Compare commits
54 Commits
wip/smcv/t
...
heif-demo
Author | SHA1 | Date | |
---|---|---|---|
|
0806fafbab | ||
|
c58606d116 | ||
|
76d0edf64a | ||
|
9e48af467d | ||
|
d52552b480 | ||
|
a0c72902f8 | ||
|
3d9d762998 | ||
|
7a1fc3f259 | ||
|
e623007759 | ||
|
e9d54b4938 | ||
|
007777d891 | ||
|
49fc1b5440 | ||
|
5877711717 | ||
|
5ca041c137 | ||
|
1d9af571a5 | ||
|
bf0f7dddc5 | ||
|
13cdd37aa6 | ||
|
3d52678635 | ||
|
6d42ad564a | ||
|
c068bd2452 | ||
|
3f496dbfd3 | ||
|
b46724de0f | ||
|
8fd17568f0 | ||
|
c4a5b72a5f | ||
|
4ada2fe69c | ||
|
4ec1fa0a25 | ||
|
06db24b4e1 | ||
|
41527b50ab | ||
|
f8157abd4b | ||
|
46a4a31cbc | ||
|
5750de1e46 | ||
|
aef5f3b837 | ||
|
6d8f12ae48 | ||
|
46e7509cf7 | ||
|
2b5cbf5369 | ||
|
73bed6adc3 | ||
|
c8fce2c35d | ||
|
feb6721848 | ||
|
cd13109f85 | ||
|
d79c7b14ff | ||
|
007631fe1b | ||
|
15cce98a31 | ||
|
7aaaf65d6c | ||
|
2c96391bd2 | ||
|
03ed9592a7 | ||
|
85437edf3d | ||
|
dbc0574d2e | ||
|
5eda3eee87 | ||
|
4ac6fb72fc | ||
|
7cd6256add | ||
|
4dda8bfb58 | ||
|
2e09436483 | ||
|
b32262128c | ||
|
b867b425a0 |
@@ -25,7 +25,7 @@ variables:
|
||||
BACKEND_FLAGS: "-Dx11-backend=true -Dwayland-backend=true -Dbroadway-backend=true"
|
||||
FEATURE_FLAGS: "-Dvulkan=enabled -Dcloudproviders=enabled"
|
||||
MESON_TEST_TIMEOUT_MULTIPLIER: 3
|
||||
FEDORA_IMAGE: "registry.gitlab.gnome.org/gnome/gtk/fedora:v36"
|
||||
FEDORA_IMAGE: "registry.gitlab.gnome.org/gnome/gtk/fedora:v37"
|
||||
FLATPAK_IMAGE: "registry.gitlab.gnome.org/gnome/gnome-runtime-images/gnome:master"
|
||||
|
||||
.only-default:
|
||||
|
@@ -42,6 +42,7 @@ RUN dnf -y install \
|
||||
iso-codes \
|
||||
itstool \
|
||||
json-glib-devel \
|
||||
lcms2-devel \
|
||||
lcov \
|
||||
libasan \
|
||||
libattr-devel \
|
||||
@@ -71,6 +72,7 @@ RUN dnf -y install \
|
||||
mesa-libEGL-devel \
|
||||
mesa-libGLES-devel \
|
||||
meson \
|
||||
mutter \
|
||||
ninja-build \
|
||||
pango-devel \
|
||||
pcre-devel \
|
||||
|
@@ -33,6 +33,10 @@ pacman --noconfirm -S --needed \
|
||||
mingw-w64-$MSYS2_ARCH-gst-plugins-bad-libs \
|
||||
mingw-w64-$MSYS2_ARCH-shared-mime-info \
|
||||
mingw-w64-$MSYS2_ARCH-python-gobject
|
||||
mingw-w64-$MSYS2_ARCH-libpng \
|
||||
mingw-w64-$MSYS2_ARCH-libjpeg-turbo \
|
||||
mingw-w64-$MSYS2_ARCH-libtiff \
|
||||
mingw-w64-$MSYS2_ARCH-lcms2
|
||||
|
||||
mkdir -p _ccache
|
||||
export CCACHE_BASEDIR="$(pwd)"
|
||||
|
After Width: | Height: | Size: 24 KiB |
BIN
demos/gtk-demo/ICC-Rendering-Intent-Test.jpg
Normal file
After Width: | Height: | Size: 290 KiB |
BIN
demos/gtk-demo/ICC-Rendering-Intent-Test.png
Normal file
After Width: | Height: | Size: 120 KiB |
BIN
demos/gtk-demo/ICC-Rendering-Intent-Test.tif
Normal file
86
demos/gtk-demo/colorprofiles.c
Normal 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;
|
||||
}
|
94
demos/gtk-demo/colorprofiles.ui
Normal 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>
|
@@ -19,6 +19,17 @@
|
||||
<file>demoimage.c</file>
|
||||
<file>demoimage.h</file>
|
||||
</gresource>
|
||||
<gresource prefix="/colorprofiles">
|
||||
<file>colorprofiles.ui</file>
|
||||
<file>sRGB_Gray.jpg</file>
|
||||
<file>sRGB_Gray.png</file>
|
||||
<file>sRGB_Gray.tif</file>
|
||||
<file>sRGB_Gray-expected-result-no-cm.png</file>
|
||||
<file>ICC-Rendering-Intent-Test.png</file>
|
||||
<file>ICC-Rendering-Intent-Test.jpg</file>
|
||||
<file>ICC-Rendering-Intent-Test.tif</file>
|
||||
<file>ICC-Rendering-Intent-Test-expected-result-no-cm.png</file>
|
||||
</gresource>
|
||||
<gresource prefix="/constraints_builder">
|
||||
<file>constraints_builder.ui</file>
|
||||
</gresource>
|
||||
@@ -255,6 +266,7 @@
|
||||
<file>assistant.c</file>
|
||||
<file>builder.c</file>
|
||||
<file>clipboard.c</file>
|
||||
<file>colorprofiles.c</file>
|
||||
<file>combobox.c</file>
|
||||
<file>constraints.c</file>
|
||||
<file>constraints_interactive.c</file>
|
||||
|
@@ -5,6 +5,7 @@ demos = files([
|
||||
'assistant.c',
|
||||
'builder.c',
|
||||
'clipboard.c',
|
||||
'colorprofiles.c',
|
||||
'combobox.c',
|
||||
'constraints.c',
|
||||
'constraints_interactive.c',
|
||||
|
BIN
demos/gtk-demo/sRGB_Gray-expected-result-no-cm.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
demos/gtk-demo/sRGB_Gray.jpg
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
demos/gtk-demo/sRGB_Gray.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
demos/gtk-demo/sRGB_Gray.tif
Normal file
BIN
demos/widget-factory/color-profile-check.jpeg
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
demos/widget-factory/color-profile-check.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
demos/widget-factory/color-profile-check.tiff
Normal file
BIN
demos/widget-factory/hdr-example.heif
Executable file
257
demos/widget-factory/heif-loader.c
Normal file
@@ -0,0 +1,257 @@
|
||||
#include "heif-loader.h"
|
||||
#include <gtk/gtk.h>
|
||||
#include <libheif/heif.h>
|
||||
|
||||
static void
|
||||
describe_nclx_color_profile (const struct heif_color_profile_nclx *nclx,
|
||||
GString *s)
|
||||
{
|
||||
if (nclx->color_primaries == heif_color_primaries_unspecified)
|
||||
return;
|
||||
|
||||
if (nclx->color_primaries == heif_color_primaries_ITU_R_BT_709_5)
|
||||
{
|
||||
if (nclx->transfer_characteristics == heif_transfer_characteristic_IEC_61966_2_1 &&
|
||||
(nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_470_6_System_B_G ||
|
||||
nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_601_6))
|
||||
{
|
||||
g_string_append (s, "sRGB");
|
||||
return;
|
||||
}
|
||||
|
||||
if (nclx->transfer_characteristics == heif_transfer_characteristic_linear &&
|
||||
(nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_470_6_System_B_G ||
|
||||
nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_601_6))
|
||||
{
|
||||
g_string_append (s, "sRGB linear");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (nclx->color_primaries == heif_color_primaries_ITU_R_BT_2020_2_and_2100_0)
|
||||
{
|
||||
if (nclx->transfer_characteristics == heif_transfer_characteristic_ITU_R_BT_2100_0_PQ &&
|
||||
nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_2020_2_non_constant_luminance)
|
||||
{
|
||||
g_string_append (s, "BT.2020 PQ");
|
||||
return;
|
||||
}
|
||||
|
||||
if (nclx->transfer_characteristics == heif_transfer_characteristic_ITU_R_BT_2100_0_HLG &&
|
||||
nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_2020_2_non_constant_luminance)
|
||||
{
|
||||
g_string_append (s, "BT.2020 HLG");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (nclx->color_primaries == heif_color_primaries_SMPTE_EG_432_1)
|
||||
{
|
||||
if (nclx->transfer_characteristics == heif_transfer_characteristic_ITU_R_BT_2100_0_PQ)
|
||||
{
|
||||
g_string_append (s, "P3 PQ");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
g_string_append_printf (s, "%d/%d/%d",
|
||||
nclx->color_primaries,
|
||||
nclx->matrix_coefficients,
|
||||
nclx->transfer_characteristics);
|
||||
}
|
||||
|
||||
GdkTexture *
|
||||
load_heif_image (const char *resource_path,
|
||||
GString *details,
|
||||
GError **error)
|
||||
{
|
||||
struct heif_context *ctx;
|
||||
struct heif_error err;
|
||||
struct heif_image_handle *handle = NULL;
|
||||
struct heif_image *image = NULL;
|
||||
enum heif_chroma chroma;
|
||||
GdkMemoryFormat format;
|
||||
const char *format_name;
|
||||
uint8_t *data = NULL;
|
||||
gsize size;
|
||||
int width, height, bpp, stride, bits;
|
||||
GBytes *bytes;
|
||||
GdkTexture *texture = NULL;
|
||||
GdkColorProfile *profile = NULL;
|
||||
char *profile_type = NULL;
|
||||
|
||||
ctx = heif_context_alloc ();
|
||||
|
||||
bytes = g_resources_lookup_data (resource_path, 0, NULL);
|
||||
data = (uint8_t *) g_bytes_get_data (bytes, &size);
|
||||
|
||||
err = heif_context_read_from_memory (ctx, data, size, NULL);
|
||||
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
if (err.code != heif_error_Ok)
|
||||
{
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, err.message);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = heif_context_get_primary_image_handle (ctx, &handle);
|
||||
if (err.code != heif_error_Ok)
|
||||
{
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, err.message);
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (heif_image_handle_get_color_profile_type (handle))
|
||||
{
|
||||
case heif_color_profile_type_not_present:
|
||||
break;
|
||||
case heif_color_profile_type_rICC:
|
||||
case heif_color_profile_type_prof:
|
||||
{
|
||||
guchar *icc_data;
|
||||
gsize icc_size;
|
||||
|
||||
profile_type = g_strdup ("icc");
|
||||
icc_size = heif_image_handle_get_raw_color_profile_size (handle);
|
||||
icc_data = g_new0 (guchar, icc_size);
|
||||
err = heif_image_handle_get_raw_color_profile (handle, icc_data);
|
||||
if (err.code == heif_error_Ok)
|
||||
{
|
||||
bytes = g_bytes_new (icc_data, icc_size);
|
||||
profile = gdk_color_profile_new_from_icc_bytes (bytes, NULL);
|
||||
g_bytes_unref (bytes);
|
||||
}
|
||||
g_free (icc_data);
|
||||
}
|
||||
break;
|
||||
case heif_color_profile_type_nclx:
|
||||
{
|
||||
struct heif_color_profile_nclx *nclx = NULL;
|
||||
|
||||
err = heif_image_handle_get_nclx_color_profile (handle, &nclx);
|
||||
if (err.code == heif_error_Ok)
|
||||
{
|
||||
GString *s;
|
||||
|
||||
s = g_string_new ("");
|
||||
describe_nclx_color_profile (nclx, s);
|
||||
profile_type = g_string_free (s, FALSE);
|
||||
profile = gdk_color_profile_new_from_cicp (nclx->color_primaries,
|
||||
nclx->transfer_characteristics,
|
||||
0,
|
||||
TRUE,
|
||||
NULL);
|
||||
heif_nclx_color_profile_free (nclx);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
g_string_append_printf (details, "%d × %d pixels\n%d bits of luma, %d bits of chroma%s\n",
|
||||
heif_image_handle_get_width (handle),
|
||||
heif_image_handle_get_height (handle),
|
||||
heif_image_handle_get_luma_bits_per_pixel (handle),
|
||||
heif_image_handle_get_chroma_bits_per_pixel (handle),
|
||||
heif_image_handle_has_alpha_channel (handle) ? ", with alpha" : "");
|
||||
|
||||
if (profile_type)
|
||||
{
|
||||
g_string_append_printf (details, "color profile: %s\n", profile_type);
|
||||
g_free (profile_type);
|
||||
}
|
||||
|
||||
if (profile == NULL)
|
||||
profile = g_object_ref (gdk_color_profile_get_srgb ());
|
||||
|
||||
if (heif_image_handle_has_alpha_channel (handle))
|
||||
{
|
||||
if (heif_image_handle_get_luma_bits_per_pixel (handle) > 8 ||
|
||||
heif_image_handle_get_chroma_bits_per_pixel (handle) > 8)
|
||||
{
|
||||
chroma = heif_chroma_interleaved_RRGGBBAA_BE;
|
||||
format = GDK_MEMORY_R16G16B16A16;
|
||||
format_name = "R<sub>16</sub>G<sub>16</sub>B<sub>16</sub>A<sub>16</sub>";
|
||||
}
|
||||
else
|
||||
{
|
||||
chroma = heif_chroma_interleaved_RGBA;
|
||||
format = GDK_MEMORY_R8G8B8A8;
|
||||
format_name = "R<sub>8</sub>G<sub>8</sub>B<sub>8</sub>A<sub>8</sub>";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (heif_image_handle_get_luma_bits_per_pixel (handle) > 8 ||
|
||||
heif_image_handle_get_chroma_bits_per_pixel (handle) > 8)
|
||||
{
|
||||
chroma = heif_chroma_interleaved_RRGGBB_BE;
|
||||
format = GDK_MEMORY_R16G16B16;
|
||||
format_name = "R<sub>16</sub>G<sub>16</sub>B<sub>16</sub>";
|
||||
}
|
||||
else
|
||||
{
|
||||
chroma = heif_chroma_interleaved_RGB;
|
||||
format = GDK_MEMORY_R8G8B8;
|
||||
format_name = "R<sub>8</sub>G<sub>8</sub>B<sub>8</sub>";
|
||||
}
|
||||
}
|
||||
|
||||
err = heif_decode_image (handle, &image, heif_colorspace_RGB, chroma, NULL);
|
||||
if (err.code != heif_error_Ok)
|
||||
{
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, err.message);
|
||||
goto out;
|
||||
}
|
||||
|
||||
width = heif_image_get_width (image, heif_channel_interleaved);
|
||||
height = heif_image_get_height (image, heif_channel_interleaved);
|
||||
bpp = heif_image_get_bits_per_pixel (image, heif_channel_interleaved);
|
||||
data = (uint8_t*)heif_image_get_plane_readonly (image, heif_channel_interleaved, &stride);
|
||||
bits = heif_image_get_bits_per_pixel_range (image, heif_channel_interleaved);
|
||||
|
||||
g_string_append_printf (details, "texture format %s", format_name);
|
||||
|
||||
if (format == GDK_MEMORY_R16G16B16A16 || format == GDK_MEMORY_R16G16B16)
|
||||
{
|
||||
/* Shift image data to full 16bit range */
|
||||
int shift = 16 - bits;
|
||||
if (shift > 0)
|
||||
{
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
uint8_t *row = data + y * stride;
|
||||
|
||||
for (int x = 0; x < stride; x += 2)
|
||||
{
|
||||
uint8_t* p = &row[x];
|
||||
int v = (p[0] << 8) | p[1];
|
||||
v = (v << shift) | (v >> (16 - shift));
|
||||
p[1] = (uint8_t) (v >> 8);
|
||||
p[0] = (uint8_t) (v & 0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bytes = g_bytes_new_with_free_func (data, height * stride * bpp, (GDestroyNotify)heif_image_release, image);
|
||||
|
||||
image = NULL;
|
||||
|
||||
texture = gdk_memory_texture_new_with_color_profile (width, height,
|
||||
format, profile,
|
||||
bytes, stride);
|
||||
g_bytes_unref (bytes);
|
||||
g_object_unref (profile);
|
||||
|
||||
out:
|
||||
heif_context_free (ctx);
|
||||
if (handle)
|
||||
heif_image_handle_release (handle);
|
||||
if (image)
|
||||
heif_image_release (image);
|
||||
|
||||
return texture;
|
||||
}
|
7
demos/widget-factory/heif-loader.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
GdkTexture *load_heif_image (const char *resource_path,
|
||||
GString *details,
|
||||
GError **error);
|
BIN
demos/widget-factory/linear-gradient-color.png
Normal file
After Width: | Height: | Size: 213 B |
BIN
demos/widget-factory/linear-gradient-monochrome.png
Normal file
After Width: | Height: | Size: 220 B |
@@ -73,10 +73,12 @@ else
|
||||
)
|
||||
endif
|
||||
|
||||
libheif_dep = dependency('libheif')
|
||||
|
||||
executable('gtk4-widget-factory',
|
||||
sources: ['widget-factory.c', widgetfactory_resources],
|
||||
sources: ['widget-factory.c', 'heif-loader.c', widgetfactory_resources],
|
||||
c_args: common_cflags,
|
||||
dependencies: [ libgtk_dep, demo_conf_h ],
|
||||
dependencies: [ libgtk_dep, libheif_dep, demo_conf_h ],
|
||||
include_directories: confinc,
|
||||
win_subsystem: 'windows',
|
||||
link_args: extra_demo_ldflags,
|
||||
|
BIN
demos/widget-factory/srgb-gradient-color.png
Normal file
After Width: | Height: | Size: 154 B |
BIN
demos/widget-factory/srgb-gradient-monochrome.png
Normal file
After Width: | Height: | Size: 164 B |
@@ -27,6 +27,8 @@
|
||||
|
||||
#include "demo_conf.h"
|
||||
|
||||
#include "heif-loader.h"
|
||||
|
||||
static void
|
||||
change_dark_state (GSimpleAction *action,
|
||||
GVariant *state,
|
||||
@@ -2360,6 +2362,15 @@ activate (GApplication *app)
|
||||
model = (GMenuModel *)gtk_builder_get_object (builder, "new_style_context_menu_model");
|
||||
set_up_context_popover (widget, model);
|
||||
|
||||
widget = (GtkWidget *)gtk_builder_get_object (builder, "hdr_picture");
|
||||
widget2 = (GtkWidget *)gtk_builder_get_object (builder, "hdr_label");
|
||||
GString *details = g_string_new ("");
|
||||
GdkTexture *texture = load_heif_image ("/org/gtk/WidgetFactory4/hdr-example.heif", details, NULL);
|
||||
gtk_picture_set_paintable (GTK_PICTURE (widget), GDK_PAINTABLE (texture));
|
||||
gtk_label_set_label (GTK_LABEL (widget2), details->str);
|
||||
g_string_free (details, TRUE);
|
||||
g_object_unref (texture);
|
||||
|
||||
gtk_window_present (window);
|
||||
|
||||
g_object_unref (builder);
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -110,6 +110,7 @@
|
||||
<file>icons/scalable/status/weather-severe-alert-symbolic.svg</file>
|
||||
<file>icons/scalable/status/weather-showers-symbolic.svg</file>
|
||||
<file>icons/scalable/status/weather-snow-symbolic.svg</file>
|
||||
<file>hdr-example.heif</file>
|
||||
|
||||
</gresource>
|
||||
<gresource prefix="/org/gtk/WidgetFactory4">
|
||||
@@ -118,5 +119,12 @@
|
||||
<file>portland-rose.jpg</file>
|
||||
<file>nyc.jpg</file>
|
||||
<file>beach.jpg</file>
|
||||
<file>linear-gradient-color.png</file>
|
||||
<file>linear-gradient-monochrome.png</file>
|
||||
<file>srgb-gradient-color.png</file>
|
||||
<file>srgb-gradient-monochrome.png</file>
|
||||
<file>color-profile-check.png</file>
|
||||
<file>color-profile-check.jpeg</file>
|
||||
<file>color-profile-check.tiff</file>
|
||||
</gresource>
|
||||
</gresources>
|
||||
|
@@ -1346,13 +1346,173 @@ Suspendisse feugiat quam quis dolor accumsan cursus.</property>
|
||||
<child>
|
||||
<object class="GtkNotebookPage">
|
||||
<property name="child">
|
||||
<object class="GtkBox" id="box8">
|
||||
<property name="orientation">1</property>
|
||||
<object class="GtkGrid">
|
||||
<property name="hexpand">0</property>
|
||||
<property name="row-spacing">6</property>
|
||||
<property name="column-spacing">6</property>
|
||||
<property name="margin-start">6</property>
|
||||
<property name="margin-end">6</property>
|
||||
<property name="margin-top">6</property>
|
||||
<property name="margin-bottom">6</property>
|
||||
<property name="row-homogeneous">1</property>
|
||||
<property name="valign">start</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="label" translatable="yes">color</property>
|
||||
<style>
|
||||
<class name="caption-heading"/>
|
||||
</style>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="row">0</property>
|
||||
<property name="column-span">2</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="label">sRGB</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="row">1</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkPicture">
|
||||
<property name="file">resource:///org/gtk/WidgetFactory4/srgb-gradient-color.png</property>
|
||||
<property name="can-shrink">0</property>
|
||||
<property name="keep-aspect-ratio">0</property>
|
||||
<property name="hexpand">1</property>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="row">1</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="label">GTK</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="row">2</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkPicture">
|
||||
<property name="width-request">128</property>
|
||||
<property name="keep-aspect-ratio">0</property>
|
||||
<style>
|
||||
<class name="gtk-gradient-color"/>
|
||||
</style>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="row">2</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="label">linear</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="row">3</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkPicture">
|
||||
<property name="file">resource:///org/gtk/WidgetFactory4/linear-gradient-color.png</property>
|
||||
<property name="can-shrink">0</property>
|
||||
<property name="keep-aspect-ratio">0</property>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="row">3</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="label" translatable="yes">monochrome</property>
|
||||
<style>
|
||||
<class name="caption-heading"/>
|
||||
</style>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="row">4</property>
|
||||
<property name="column-span">2</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="label">sRGB</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="row">5</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkPicture">
|
||||
<property name="file">resource:///org/gtk/WidgetFactory4/srgb-gradient-monochrome.png</property>
|
||||
<property name="can-shrink">0</property>
|
||||
<property name="keep-aspect-ratio">0</property>
|
||||
<property name="hexpand">1</property>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="row">5</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="label">GTK</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="row">6</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkPicture">
|
||||
<property name="width-request">128</property>
|
||||
<property name="keep-aspect-ratio">0</property>
|
||||
<style>
|
||||
<class name="gtk-gradient-monochrome"/>
|
||||
</style>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="row">6</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="label">linear</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="row">7</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkPicture">
|
||||
<property name="file">resource:///org/gtk/WidgetFactory4/linear-gradient-monochrome.png</property>
|
||||
<property name="can-shrink">0</property>
|
||||
<property name="keep-aspect-ratio">0</property>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="row">7</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
<property name="tab">
|
||||
<object class="GtkLabel" id="label8">
|
||||
<property name="label" translatable="1">page 1</property>
|
||||
<property name="label" translatable="1">Gradients</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
@@ -1361,13 +1521,92 @@ Suspendisse feugiat quam quis dolor accumsan cursus.</property>
|
||||
<object class="GtkNotebookPage">
|
||||
<property name="position">1</property>
|
||||
<property name="child">
|
||||
<object class="GtkBox" id="box10">
|
||||
<property name="orientation">1</property>
|
||||
<object class="GtkGrid">
|
||||
<property name="hexpand">0</property>
|
||||
<property name="row-spacing">6</property>
|
||||
<property name="column-spacing">6</property>
|
||||
<property name="margin-start">6</property>
|
||||
<property name="margin-end">6</property>
|
||||
<property name="margin-top">6</property>
|
||||
<property name="margin-bottom">6</property>
|
||||
<property name="row-homogeneous">1</property>
|
||||
<property name="valign">start</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="label" translatable="yes">image loading</property>
|
||||
<style>
|
||||
<class name="caption-heading"/>
|
||||
</style>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="row">0</property>
|
||||
<property name="column-span">2</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="paintable">resource:///org/gtk/WidgetFactory4/color-profile-check.png</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="row">1</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="label">PNG</property>
|
||||
<property name="hexpand">1</property>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="row">1</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="paintable">resource:///org/gtk/WidgetFactory4/color-profile-check.jpeg</property>
|
||||
<layout>
|
||||
<property name="column">0</property>
|
||||
<property name="row">2</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="label">JPEG</property>
|
||||
<property name="hexpand">1</property>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="row">2</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="paintable">resource:///org/gtk/WidgetFactory4/color-profile-check.tiff</property>
|
||||
<layout>
|
||||
|
||||
<property name="column">0</property>
|
||||
<property name="row">3</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="label">TIFF</property>
|
||||
<property name="hexpand">1</property>
|
||||
<layout>
|
||||
<property name="column">1</property>
|
||||
<property name="row">3</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
<property name="tab">
|
||||
<object class="GtkLabel" id="label9">
|
||||
<property name="label" translatable="1">page 2</property>
|
||||
<property name="label" translatable="1">Images</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
@@ -1377,12 +1616,26 @@ Suspendisse feugiat quam quis dolor accumsan cursus.</property>
|
||||
<property name="position">2</property>
|
||||
<property name="child">
|
||||
<object class="GtkBox" id="box11">
|
||||
<property name="orientation">1</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkPicture" id="hdr_picture">
|
||||
<property name="can-shrink">1</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="hdr_label">
|
||||
<property name="margin-start">10</property>
|
||||
<property name="margin-end">10</property>
|
||||
<property name="margin-top">10</property>
|
||||
<property name="margin-bottom">10</property>
|
||||
<property name="use-markup">1</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
<property name="tab">
|
||||
<object class="GtkLabel" id="label10">
|
||||
<property name="label" translatable="1">page 3</property>
|
||||
<property name="label" translatable="1">HDR</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
|
@@ -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
|
||||
|
@@ -19,7 +19,7 @@
|
||||
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
|
||||
* file for a list of people on the GTK+ Team. See the ChangeLog
|
||||
* files for a list of changes. These files are distributed with
|
||||
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
|
||||
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
@@ -130,6 +130,7 @@ static const GdkDebugKey gdk_debug_keys[] = {
|
||||
{ "vulkan-validate", GDK_DEBUG_VULKAN_VALIDATE, "Load the Vulkan validation layer" },
|
||||
{ "default-settings",GDK_DEBUG_DEFAULT_SETTINGS, "Force default values for xsettings", TRUE },
|
||||
{ "high-depth", GDK_DEBUG_HIGH_DEPTH, "Use high bit depth rendering if possible", TRUE },
|
||||
{ "srgb", GDK_DEBUG_SRGB, "Force sRRGB rendering and turn off color profiles" },
|
||||
};
|
||||
|
||||
|
||||
|
@@ -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>
|
||||
|
114
gdk/gdkcairo.c
@@ -1,5 +1,5 @@
|
||||
/* GDK - The GIMP Drawing Kit
|
||||
* Copyright (C) 2005 Red Hat, Inc.
|
||||
* Copyright (C) 2005 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@@ -19,6 +19,10 @@
|
||||
|
||||
#include "gdkcairoprivate.h"
|
||||
|
||||
#include "gdkcolorprivate.h"
|
||||
#include "gdkmemoryformatprivate.h"
|
||||
#include "gdkcolorprofile.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/**
|
||||
@@ -32,14 +36,20 @@ void
|
||||
gdk_cairo_set_source_rgba (cairo_t *cr,
|
||||
const GdkRGBA *rgba)
|
||||
{
|
||||
GdkColor color;
|
||||
const float *components;
|
||||
|
||||
g_return_if_fail (cr != NULL);
|
||||
g_return_if_fail (rgba != NULL);
|
||||
|
||||
gdk_color_convert_rgba (&color, gdk_cairo_get_color_profile (cr), rgba);
|
||||
components = gdk_color_get_components (&color);
|
||||
cairo_set_source_rgba (cr,
|
||||
rgba->red,
|
||||
rgba->green,
|
||||
rgba->blue,
|
||||
rgba->alpha);
|
||||
components[0],
|
||||
components[1],
|
||||
components[2],
|
||||
gdk_color_get_alpha (&color));
|
||||
gdk_color_finish (&color);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -335,3 +345,97 @@ gdk_cairo_region_from_clip (cairo_t *cr)
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
static cairo_user_data_key_t color_profile_key;
|
||||
|
||||
/**
|
||||
* gdk_cairo_surface_set_color_profile:
|
||||
* @surface: a surface
|
||||
* @profile: the profile to attach to the surface
|
||||
*
|
||||
* Attaches a `GdkColorProfile` to the Cairo surface.
|
||||
*
|
||||
* This is just auxiliary data for use by GTK, no Cairo functions
|
||||
* do interact with this information.
|
||||
*
|
||||
* Note that all Cairo compositing operations are assumed to happen
|
||||
* in a linear RGB color space, so if you want to use the surface
|
||||
* as a target for rendering in a color managed way, you should use
|
||||
* such a color profile.
|
||||
*
|
||||
* The default color profile is assumed to be sRGB, which is not
|
||||
* linear.
|
||||
*
|
||||
* Since: 4.8
|
||||
*/
|
||||
void
|
||||
gdk_cairo_surface_set_color_profile (cairo_surface_t *surface,
|
||||
GdkColorProfile *profile)
|
||||
{
|
||||
g_return_if_fail (surface != NULL);
|
||||
g_return_if_fail (GDK_IS_COLOR_PROFILE (profile));
|
||||
|
||||
cairo_surface_set_user_data (surface,
|
||||
&color_profile_key,
|
||||
g_object_ref (profile),
|
||||
g_object_unref);
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_cairo_surface_get_color_profile:
|
||||
* @surface: a surface
|
||||
*
|
||||
* Gets the color profile GTK assumes for the surface. See
|
||||
* gdk_cairo_surface_set_color_profile() for details.
|
||||
*
|
||||
* Returns: (transfer none): the assumed profile
|
||||
*
|
||||
* Since: 4.8
|
||||
*/
|
||||
GdkColorProfile *
|
||||
gdk_cairo_surface_get_color_profile (cairo_surface_t *surface)
|
||||
{
|
||||
GdkColorProfile *profile;
|
||||
|
||||
g_return_val_if_fail (surface != NULL, gdk_color_profile_get_srgb ());
|
||||
|
||||
profile = cairo_surface_get_user_data (surface, &color_profile_key);
|
||||
if (profile == NULL)
|
||||
profile = gdk_color_profile_get_srgb ();
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_cairo_get_color_profile:
|
||||
* @cr: a cairo context
|
||||
*
|
||||
* Gets the color profile GTK assumes for the cairo context.
|
||||
*
|
||||
* Returns: (transfer none): the assumed profile
|
||||
*
|
||||
* Since: 4.8
|
||||
*/
|
||||
GdkColorProfile *
|
||||
gdk_cairo_get_color_profile (cairo_t *cr)
|
||||
{
|
||||
GdkColorProfile *profile;
|
||||
cairo_surface_t *surface;
|
||||
|
||||
g_return_val_if_fail (cr != NULL, gdk_color_profile_get_srgb ());
|
||||
|
||||
surface = cairo_get_group_target (cr);
|
||||
profile = cairo_surface_get_user_data (surface, &color_profile_key);
|
||||
if (profile != NULL)
|
||||
return profile;
|
||||
|
||||
/* theoretically, we should walk the whole group stack, but I don't
|
||||
* think Cairo lets us do that
|
||||
*/
|
||||
surface = cairo_get_target (cr);
|
||||
profile = cairo_surface_get_user_data (surface, &color_profile_key);
|
||||
if (profile != NULL)
|
||||
return profile;
|
||||
|
||||
return gdk_color_profile_get_srgb ();
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/* GDK - The GIMP Drawing Kit
|
||||
* Copyright (C) 2005 Red Hat, Inc.
|
||||
* Copyright (C) 2005 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@@ -46,9 +46,15 @@ void gdk_cairo_region (cairo_t *cr,
|
||||
const cairo_region_t *region);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
cairo_region_t *
|
||||
gdk_cairo_region_create_from_surface
|
||||
(cairo_surface_t *surface);
|
||||
cairo_region_t * gdk_cairo_region_create_from_surface (cairo_surface_t *surface);
|
||||
|
||||
GDK_AVAILABLE_IN_4_8
|
||||
void gdk_cairo_surface_set_color_profile (cairo_surface_t *surface,
|
||||
GdkColorProfile *profile);
|
||||
GDK_AVAILABLE_IN_4_8
|
||||
GdkColorProfile * gdk_cairo_surface_get_color_profile (cairo_surface_t *surface);
|
||||
GDK_AVAILABLE_IN_4_8
|
||||
GdkColorProfile * gdk_cairo_get_color_profile (cairo_t *cr);
|
||||
|
||||
GDK_DEPRECATED_IN_4_6_FOR(gdk_gl_texture_new)
|
||||
void gdk_cairo_draw_from_gl (cairo_t *cr,
|
||||
|
121
gdk/gdkcolor.c
Normal file
@@ -0,0 +1,121 @@
|
||||
/* GDK - The GIMP Drawing Kit
|
||||
*
|
||||
* Copyright (C) 2021 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gdkcolorprivate.h"
|
||||
#include "gdkcolorprofileprivate.h"
|
||||
|
||||
static inline cmsHTRANSFORM *
|
||||
gdk_color_get_transform (GdkColorProfile *src,
|
||||
GdkColorProfile *dest)
|
||||
{
|
||||
cmsHPROFILE *src_profile, *dest_profile;
|
||||
|
||||
src_profile = gdk_color_profile_get_lcms_profile (src);
|
||||
dest_profile = gdk_color_profile_get_lcms_profile (dest);
|
||||
|
||||
return gdk_color_profile_lookup_transform (src,
|
||||
cmsFormatterForColorspaceOfProfile (src_profile, 4, 1),
|
||||
dest,
|
||||
cmsFormatterForColorspaceOfProfile (dest_profile, 4, 1));
|
||||
}
|
||||
|
||||
void
|
||||
gdk_color_convert (GdkColor *self,
|
||||
GdkColorProfile *profile,
|
||||
const GdkColor *other)
|
||||
{
|
||||
gdk_color_init (self,
|
||||
profile,
|
||||
other->alpha,
|
||||
NULL,
|
||||
gdk_color_profile_get_n_components (profile));
|
||||
|
||||
cmsDoTransform (gdk_color_get_transform (other->profile, profile),
|
||||
gdk_color_get_components (other),
|
||||
(float *) gdk_color_get_components (self),
|
||||
1);
|
||||
}
|
||||
|
||||
void
|
||||
gdk_color_convert_rgba (GdkColor *self,
|
||||
GdkColorProfile *profile,
|
||||
const GdkRGBA *rgba)
|
||||
{
|
||||
gdk_color_init (self,
|
||||
profile,
|
||||
rgba->alpha,
|
||||
NULL,
|
||||
gdk_color_profile_get_n_components (profile));
|
||||
|
||||
cmsDoTransform (gdk_color_get_transform (gdk_color_profile_get_srgb (), profile),
|
||||
(float[3]) { rgba->red, rgba->green, rgba->blue },
|
||||
(float *) gdk_color_get_components (self),
|
||||
1);
|
||||
}
|
||||
|
||||
void
|
||||
gdk_color_mix (GdkColor *self,
|
||||
GdkColorProfile *color_profile,
|
||||
const GdkColor *src1,
|
||||
const GdkColor *src2,
|
||||
double progress)
|
||||
{
|
||||
if (src1->profile != color_profile)
|
||||
{
|
||||
GdkColor tmp;
|
||||
gdk_color_convert (&tmp, color_profile, src1);
|
||||
gdk_color_mix (self, color_profile, &tmp, src2, progress);
|
||||
}
|
||||
else if (src2->profile != color_profile)
|
||||
{
|
||||
GdkColor tmp;
|
||||
gdk_color_convert (&tmp, color_profile, src2);
|
||||
gdk_color_mix (self, color_profile, src1, &tmp, progress);
|
||||
}
|
||||
else
|
||||
{
|
||||
gsize i, n;
|
||||
const float *s1, *s2;
|
||||
float *d;
|
||||
|
||||
n = gdk_color_profile_get_n_components (color_profile);
|
||||
|
||||
gdk_color_init (self,
|
||||
color_profile,
|
||||
src1->alpha * (1.0 - progress) + src2->alpha * progress,
|
||||
NULL, n);
|
||||
|
||||
d = (float *) gdk_color_get_components (self);
|
||||
s1 = gdk_color_get_components (src1);
|
||||
s2 = gdk_color_get_components (src2);
|
||||
|
||||
if (self->alpha == 0)
|
||||
{
|
||||
for (i = 0; i < n; i++)
|
||||
d[i] = s1[i] * (1.0 - progress) + s2[i] * progress;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < n; i++)
|
||||
d[i] = (s1[i] * src1->alpha * (1.0 - progress) + s2[i] * src2->alpha * progress) / self->alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
73
gdk/gdkcolorprivate.h
Normal file
@@ -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
@@ -0,0 +1,97 @@
|
||||
/* GDK - The GIMP Drawing Kit
|
||||
*
|
||||
* Copyright (C) 2021 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
static inline gboolean
|
||||
gdk_color_is_allocated (const GdkColor *self)
|
||||
{
|
||||
return GPOINTER_TO_SIZE (self->profile) & 1;
|
||||
}
|
||||
|
||||
static inline void
|
||||
gdk_color_init (GdkColor *self,
|
||||
GdkColorProfile *profile,
|
||||
float alpha,
|
||||
float *components,
|
||||
gsize n_components)
|
||||
{
|
||||
gboolean allocated = n_components > GDK_COLOR_MAX_NATIVE_COMPONENTS;
|
||||
|
||||
g_assert (n_components == gdk_color_profile_get_n_components (profile));
|
||||
|
||||
self->profile = GSIZE_TO_POINTER (GPOINTER_TO_SIZE (g_object_ref (profile)) | allocated);
|
||||
self->alpha = alpha;
|
||||
if (allocated)
|
||||
{
|
||||
if (components)
|
||||
self->components = g_memdup (components, sizeof (float) * n_components);
|
||||
else
|
||||
self->components = g_new (float, n_components);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (components)
|
||||
memcpy (self->values, components, sizeof (float) * n_components);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
gdk_color_init_from_rgba (GdkColor *self,
|
||||
const GdkRGBA *rgba)
|
||||
{
|
||||
gdk_color_init (self,
|
||||
gdk_color_profile_get_srgb(),
|
||||
rgba->alpha,
|
||||
(float[3]) { rgba->red, rgba->green, rgba->blue },
|
||||
3);
|
||||
}
|
||||
|
||||
static inline void
|
||||
gdk_color_finish (GdkColor *self)
|
||||
{
|
||||
if (gdk_color_is_allocated (self))
|
||||
g_free (self->components);
|
||||
|
||||
g_object_unref (gdk_color_get_color_profile (self));
|
||||
}
|
||||
|
||||
static inline GdkColorProfile *
|
||||
gdk_color_get_color_profile (const GdkColor *self)
|
||||
{
|
||||
return GSIZE_TO_POINTER (GPOINTER_TO_SIZE (self->profile) & ~1);
|
||||
}
|
||||
|
||||
static inline float
|
||||
gdk_color_get_alpha (const GdkColor *self)
|
||||
{
|
||||
return self->alpha;
|
||||
}
|
||||
|
||||
static inline const float *
|
||||
gdk_color_get_components (const GdkColor *self)
|
||||
{
|
||||
if (gdk_color_is_allocated (self))
|
||||
return self->components;
|
||||
else
|
||||
return self->values;
|
||||
}
|
||||
|
||||
static inline gsize
|
||||
gdk_color_get_n_components (const GdkColor *self)
|
||||
{
|
||||
return gdk_color_profile_get_n_components (gdk_color_get_color_profile (self));
|
||||
}
|
742
gdk/gdkcolorprofile.c
Normal file
@@ -0,0 +1,742 @@
|
||||
/* gdkcolor_profile.c
|
||||
*
|
||||
* Copyright 2021 (c) Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* GdkColorProfile:
|
||||
*
|
||||
* `GdkColorProfile` is used to describe color spaces.
|
||||
*
|
||||
* It is used to associate color profiles defined by the [International
|
||||
* Color Consortium (ICC)](https://color.org/) with texture and color data.
|
||||
*
|
||||
* Each `GdkColorProfile` encapsulates an
|
||||
* [ICC profile](https://en.wikipedia.org/wiki/ICC_profile). That profile
|
||||
* can be queried via the [property@Gdk.ColorProfile:icc-profile] property.
|
||||
*
|
||||
* GDK provides a predefined color profile for the standard sRGB profile,
|
||||
* which can be obtained with [func@Gdk.ColorProfile.get_srgb].
|
||||
*
|
||||
* The main source for color profiles in GTK is loading images which contain
|
||||
* ICC profile information. GTK's image loaders will attach the resulting
|
||||
* color profiles to the textures they produce.
|
||||
*
|
||||
* `GdkColorProfile` objects are immutable and therefore threadsafe.
|
||||
*
|
||||
* Since: 4.8
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gdkcolorprofileprivate.h"
|
||||
|
||||
#include "gdkintl.h"
|
||||
|
||||
struct _GdkColorProfile
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GBytes *icc_profile;
|
||||
cmsHPROFILE lcms_profile;
|
||||
|
||||
int color_primaries;
|
||||
int matrix_coefficients;
|
||||
int transfer_characteristics;
|
||||
gboolean full_range;
|
||||
};
|
||||
|
||||
struct _GdkColorProfileClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_ICC_PROFILE,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
static GParamSpec *properties[N_PROPS];
|
||||
|
||||
static gboolean
|
||||
gdk_color_profile_real_init (GInitable *initable,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GdkColorProfile *self = GDK_COLOR_PROFILE (initable);
|
||||
|
||||
if (self->lcms_profile == NULL)
|
||||
{
|
||||
const guchar *data;
|
||||
gsize size;
|
||||
|
||||
if (self->icc_profile == NULL)
|
||||
self->icc_profile = g_bytes_new (NULL, 0);
|
||||
|
||||
data = g_bytes_get_data (self->icc_profile, &size);
|
||||
|
||||
self->lcms_profile = cmsOpenProfileFromMem (data, size);
|
||||
if (self->lcms_profile == NULL)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Failed to load ICC profile"));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME: can we determine these values from icc */
|
||||
self->color_primaries = 2;
|
||||
self->transfer_characteristics = 2;
|
||||
self->matrix_coefficients = 0;
|
||||
self->full_range = TRUE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_color_profile_initable_init (GInitableIface *iface)
|
||||
{
|
||||
iface->init = gdk_color_profile_real_init;
|
||||
}
|
||||
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GdkColorProfile, gdk_color_profile, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gdk_color_profile_initable_init))
|
||||
|
||||
static void
|
||||
gdk_color_profile_set_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GdkColorProfile *self = GDK_COLOR_PROFILE (gobject);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_ICC_PROFILE:
|
||||
self->icc_profile = g_value_dup_boxed (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_color_profile_get_property (GObject *gobject,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GdkColorProfile *self = GDK_COLOR_PROFILE (gobject);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_ICC_PROFILE:
|
||||
g_value_set_boxed (value, self->icc_profile);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_color_profile_dispose (GObject *object)
|
||||
{
|
||||
GdkColorProfile *self = GDK_COLOR_PROFILE (object);
|
||||
|
||||
g_clear_pointer (&self->icc_profile, g_bytes_unref);
|
||||
g_clear_pointer (&self->lcms_profile, cmsCloseProfile);
|
||||
|
||||
G_OBJECT_CLASS (gdk_color_profile_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_color_profile_class_init (GdkColorProfileClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->set_property = gdk_color_profile_set_property;
|
||||
gobject_class->get_property = gdk_color_profile_get_property;
|
||||
gobject_class->dispose = gdk_color_profile_dispose;
|
||||
|
||||
/**
|
||||
* GdkColorProfile:icc-profile: (attributes org.gtk.Property.get=gdk_color_profile_get_icc_profile)
|
||||
*
|
||||
* the ICC profile for this color profile
|
||||
*/
|
||||
properties[PROP_ICC_PROFILE] =
|
||||
g_param_spec_boxed ("icc-profile",
|
||||
P_("ICC profile"),
|
||||
P_("ICC profile for this color profile"),
|
||||
G_TYPE_BYTES,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS |
|
||||
G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_color_profile_init (GdkColorProfile *self)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_color_profile_get_srgb:
|
||||
*
|
||||
* Returns the color profile representing the sRGB color space.
|
||||
*
|
||||
* If you don't know anything about color profiles but need one for
|
||||
* use with some function, this one is most likely the right one.
|
||||
*
|
||||
* Returns: (transfer none): the color profile for the sRGB
|
||||
* color space.
|
||||
*
|
||||
* Since: 4.8
|
||||
*/
|
||||
GdkColorProfile *
|
||||
gdk_color_profile_get_srgb (void)
|
||||
{
|
||||
static GdkColorProfile *srgb_profile;
|
||||
|
||||
if (g_once_init_enter (&srgb_profile))
|
||||
{
|
||||
GdkColorProfile *new_profile;
|
||||
|
||||
new_profile = gdk_color_profile_new_from_lcms_profile (cmsCreate_sRGBProfile (), NULL);
|
||||
g_assert (new_profile);
|
||||
|
||||
new_profile->color_primaries = 0;
|
||||
new_profile->transfer_characteristics = 13;
|
||||
new_profile->matrix_coefficients = 0;
|
||||
new_profile->full_range = TRUE;
|
||||
|
||||
g_once_init_leave (&srgb_profile, new_profile);
|
||||
}
|
||||
|
||||
return srgb_profile;
|
||||
}
|
||||
|
||||
/*<private>
|
||||
* gdk_color_profile_get_srgb_linear:
|
||||
*
|
||||
* Returns the linear color profile corresponding to the sRGB
|
||||
* color space.
|
||||
*
|
||||
* It can display the same colors, but it does not have a gamma curve.
|
||||
*
|
||||
* Returns: (transfer none): the color profile for the linear sRGB
|
||||
* color space.
|
||||
*
|
||||
* Since: 4.8
|
||||
*/
|
||||
GdkColorProfile *
|
||||
gdk_color_profile_get_srgb_linear (void)
|
||||
{
|
||||
static GdkColorProfile *srgb_linear_profile;
|
||||
|
||||
if (g_once_init_enter (&srgb_linear_profile))
|
||||
{
|
||||
cmsToneCurve *curve;
|
||||
cmsHPROFILE lcms_profile;
|
||||
GdkColorProfile *new_profile;
|
||||
|
||||
curve = cmsBuildGamma (NULL, 1.0);
|
||||
lcms_profile = cmsCreateRGBProfile (&(cmsCIExyY) {
|
||||
0.3127, 0.3290, 1.0
|
||||
},
|
||||
&(cmsCIExyYTRIPLE) {
|
||||
{ 0.6400, 0.3300, 1.0 },
|
||||
{ 0.3000, 0.6000, 1.0 },
|
||||
{ 0.1500, 0.0600, 1.0 }
|
||||
},
|
||||
(cmsToneCurve*[3]) { curve, curve, curve });
|
||||
cmsFreeToneCurve (curve);
|
||||
|
||||
new_profile = gdk_color_profile_new_from_lcms_profile (lcms_profile, NULL);
|
||||
g_assert (new_profile);
|
||||
|
||||
new_profile->color_primaries = 0;
|
||||
new_profile->transfer_characteristics = 8;
|
||||
new_profile->matrix_coefficients = 0;
|
||||
new_profile->full_range = TRUE;
|
||||
|
||||
g_once_init_leave (&srgb_linear_profile, new_profile);
|
||||
}
|
||||
|
||||
return srgb_linear_profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_color_profile_new_from_icc_bytes:
|
||||
* @bytes: The ICC profiles given as a `GBytes`
|
||||
* @error: Return location for an error
|
||||
*
|
||||
* Creates a new color profile for the given ICC profile data.
|
||||
*
|
||||
* if the profile is not valid, %NULL is returned and an error
|
||||
* is raised.
|
||||
*
|
||||
* Returns: a new `GdkColorProfile` or %NULL on error
|
||||
*
|
||||
* Since: 4.8
|
||||
*/
|
||||
GdkColorProfile *
|
||||
gdk_color_profile_new_from_icc_bytes (GBytes *bytes,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (bytes != NULL, NULL);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
||||
|
||||
return g_initable_new (GDK_TYPE_COLOR_PROFILE,
|
||||
NULL,
|
||||
error,
|
||||
"icc-profile", bytes,
|
||||
NULL);
|
||||
}
|
||||
|
||||
GdkColorProfile *
|
||||
gdk_color_profile_new_from_lcms_profile (cmsHPROFILE lcms_profile,
|
||||
GError **error)
|
||||
{
|
||||
GdkColorProfile *result;
|
||||
cmsUInt32Number size;
|
||||
guchar *data;
|
||||
|
||||
size = 0;
|
||||
if (!cmsSaveProfileToMem (lcms_profile, NULL, &size))
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Could not prepare ICC profile"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data = g_malloc (size);
|
||||
if (!cmsSaveProfileToMem (lcms_profile, data, &size))
|
||||
{
|
||||
g_free (data);
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Failed to save ICC profile"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = g_object_new (GDK_TYPE_COLOR_PROFILE, NULL);
|
||||
result->lcms_profile = lcms_profile;
|
||||
result->icc_profile = g_bytes_new_take (data, size);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_color_profile_get_icc_profile: (attributes org.gtk.Method.get_property=icc-profile)
|
||||
* @self: a `GdkColorProfile`
|
||||
*
|
||||
* Returns the serialized ICC profile of @self as %GBytes.
|
||||
*
|
||||
* Returns: (transfer none): the ICC profile
|
||||
*
|
||||
* Since: 4.8
|
||||
*/
|
||||
GBytes *
|
||||
gdk_color_profile_get_icc_profile (GdkColorProfile *self)
|
||||
{
|
||||
g_return_val_if_fail (GDK_IS_COLOR_PROFILE (self), NULL);
|
||||
|
||||
return self->icc_profile;
|
||||
}
|
||||
|
||||
cmsHPROFILE *
|
||||
gdk_color_profile_get_lcms_profile (GdkColorProfile *self)
|
||||
{
|
||||
g_return_val_if_fail (GDK_IS_COLOR_PROFILE (self), NULL);
|
||||
|
||||
return self->lcms_profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_color_profile_is_linear:
|
||||
* @self: a `GdkColorProfile`
|
||||
*
|
||||
* Checks if the given profile is linear.
|
||||
*
|
||||
* GTK tries to do compositing in a linear profile.
|
||||
*
|
||||
* Some profiles may be linear, but it is not possible to
|
||||
* determine this easily. In those cases %FALSE will be returned.
|
||||
*
|
||||
* Returns: %TRUE if the profile can be proven linear
|
||||
*
|
||||
* Since: 4.8
|
||||
*/
|
||||
gboolean
|
||||
gdk_color_profile_is_linear (GdkColorProfile *self)
|
||||
{
|
||||
g_return_val_if_fail (GDK_IS_COLOR_PROFILE (self), FALSE);
|
||||
|
||||
/* FIXME: Make this useful for lcms profiles */
|
||||
if (self->transfer_characteristics == 8)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_color_profile_get_n_components:
|
||||
* @self: a `GdkColorProfile
|
||||
*
|
||||
* Gets the number of color components - also called channels - for
|
||||
* the given profile.
|
||||
*
|
||||
* Note that this does not consider an alpha channel because color
|
||||
* profiles have no alpha information. So for any form of RGB profile,
|
||||
* this returned number will be 3, but for CMYK, it will be 4.
|
||||
*
|
||||
* Returns: The number of components
|
||||
*
|
||||
* Since: 4.8
|
||||
*/
|
||||
gsize
|
||||
gdk_color_profile_get_n_components (GdkColorProfile *self)
|
||||
{
|
||||
g_return_val_if_fail (GDK_IS_COLOR_PROFILE (self), 3);
|
||||
|
||||
return cmsChannelsOf (cmsGetColorSpace (self->lcms_profile));
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_color_profile_equal:
|
||||
* @profile1: (type GdkColorProfile): a `GdkColorProfile`
|
||||
* @profile2: (type GdkColorProfile): another `GdkColorProfile`
|
||||
*
|
||||
* Compares two `GdkColorProfile`s for equality.
|
||||
*
|
||||
* Note that this function is not guaranteed to be perfect and two equal
|
||||
* profiles may compare not equal. However, different profiles will
|
||||
* never compare equal.
|
||||
*
|
||||
* Returns: %TRUE if the two color profiles compare equal
|
||||
*
|
||||
* Since: 4.8
|
||||
*/
|
||||
gboolean
|
||||
gdk_color_profile_equal (gconstpointer profile1,
|
||||
gconstpointer profile2)
|
||||
{
|
||||
return profile1 == profile2 ||
|
||||
g_bytes_equal (GDK_COLOR_PROFILE (profile1)->icc_profile,
|
||||
GDK_COLOR_PROFILE (profile2)->icc_profile);
|
||||
}
|
||||
|
||||
/* Check if the color profile and the memory format have the
|
||||
* same color components.
|
||||
*/
|
||||
gboolean
|
||||
gdk_color_profile_supports_memory_format (GdkColorProfile *profile,
|
||||
GdkMemoryFormat format)
|
||||
{
|
||||
/* Currently, all our memory formats are RGB (with or without alpha).
|
||||
* Update this when that changes.
|
||||
*/
|
||||
return cmsGetColorSpace (profile->lcms_profile) == cmsSigRgbData;
|
||||
}
|
||||
|
||||
typedef struct _GdkColorTransformCache GdkColorTransformCache;
|
||||
|
||||
struct _GdkColorTransformCache
|
||||
{
|
||||
GdkColorProfile *source;
|
||||
guint source_type;
|
||||
GdkColorProfile *dest;
|
||||
guint dest_type;
|
||||
};
|
||||
|
||||
static void
|
||||
gdk_color_transform_cache_free (gpointer data)
|
||||
{
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
static guint
|
||||
gdk_color_transform_cache_hash (gconstpointer data)
|
||||
{
|
||||
const GdkColorTransformCache *cache = data;
|
||||
|
||||
return g_direct_hash (cache->source) ^
|
||||
(g_direct_hash (cache->dest) >> 2) ^
|
||||
((cache->source_type << 16) | (cache->source_type >> 16)) ^
|
||||
cache->dest_type;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gdk_color_transform_cache_equal (gconstpointer data1,
|
||||
gconstpointer data2)
|
||||
{
|
||||
const GdkColorTransformCache *cache1 = data1;
|
||||
const GdkColorTransformCache *cache2 = data2;
|
||||
|
||||
return cache1->source == cache2->source &&
|
||||
cache1->source_type == cache2->source_type &&
|
||||
cache1->dest == cache2->dest &&
|
||||
cache1->dest_type == cache2->dest_type;
|
||||
}
|
||||
|
||||
cmsHTRANSFORM *
|
||||
gdk_color_profile_lookup_transform (GdkColorProfile *source,
|
||||
guint source_type,
|
||||
GdkColorProfile *dest,
|
||||
guint dest_type)
|
||||
{
|
||||
GdkColorTransformCache *entry;
|
||||
static GHashTable *cache = NULL;
|
||||
cmsHTRANSFORM *transform;
|
||||
|
||||
if (cache == NULL)
|
||||
cache = g_hash_table_new_full (gdk_color_transform_cache_hash,
|
||||
gdk_color_transform_cache_equal,
|
||||
gdk_color_transform_cache_free,
|
||||
cmsDeleteTransform);
|
||||
|
||||
transform = g_hash_table_lookup (cache,
|
||||
&(GdkColorTransformCache) {
|
||||
source, source_type,
|
||||
dest, dest_type
|
||||
});
|
||||
if (G_UNLIKELY (transform == NULL))
|
||||
{
|
||||
transform = cmsCreateTransform (gdk_color_profile_get_lcms_profile (source),
|
||||
source_type,
|
||||
gdk_color_profile_get_lcms_profile (dest),
|
||||
dest_type,
|
||||
INTENT_PERCEPTUAL,
|
||||
cmsFLAGS_COPY_ALPHA);
|
||||
entry = g_new (GdkColorTransformCache, 1);
|
||||
*entry = (GdkColorTransformCache) {
|
||||
source, source_type,
|
||||
dest, dest_type
|
||||
};
|
||||
g_hash_table_insert (cache, entry, transform);
|
||||
}
|
||||
|
||||
return transform;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_color_profile_new_from_cicp:
|
||||
* @color_primaries: the color primaries to use
|
||||
* @transfer_characteristics: the transfer function to use
|
||||
* @matrix_coefficients: the matrix coefficients
|
||||
* @full_range: whether the data is full-range or not
|
||||
*
|
||||
* Creates a new color profile from CICP parameters.
|
||||
*
|
||||
* This function only supports a subset of possible combinations
|
||||
* of @color_primaries and @transfer_characteristics.
|
||||
*
|
||||
* @matrix_coefficients and @full_range must have the values
|
||||
* 0 and `TRUE`, since only full-range RGB profiles are
|
||||
* supported.
|
||||
*
|
||||
* Returns: a new `GdkColorProfile` or %NULL on error
|
||||
*
|
||||
* Since: 4.8
|
||||
*/
|
||||
GdkColorProfile *
|
||||
gdk_color_profile_new_from_cicp (int color_primaries,
|
||||
int transfer_characteristics,
|
||||
int matrix_coefficients,
|
||||
gboolean full_range,
|
||||
GError **error)
|
||||
{
|
||||
cmsHPROFILE profile = NULL;
|
||||
cmsCIExyY whitepoint;
|
||||
cmsCIExyYTRIPLE primaries;
|
||||
cmsToneCurve *curve[3];
|
||||
cmsCIExyY whiteD65 = (cmsCIExyY) { 0.3127, 0.3290, 1.0 };
|
||||
cmsCIExyY whiteC = (cmsCIExyY) { 0.310, 0.316, 1.0 };
|
||||
cmsFloat64Number srgb_parameters[5] =
|
||||
{ 2.4, 1.0 / 1.055, 0.055 / 1.055, 1.0 / 12.92, 0.04045 };
|
||||
cmsFloat64Number rec709_parameters[5] =
|
||||
{ 2.2, 1.0 / 1.099, 0.099 / 1.099, 1.0 / 4.5, 0.081 };
|
||||
|
||||
/* We only support full-range RGB profiles */
|
||||
g_assert (matrix_coefficients == 0);
|
||||
g_assert (full_range);
|
||||
|
||||
if (color_primaries == 0 /* ITU_R_BT_709_5 */)
|
||||
{
|
||||
if (transfer_characteristics == 13 /* IEC_61966_2_1 */)
|
||||
return g_object_ref (gdk_color_profile_get_srgb ());
|
||||
else if (transfer_characteristics == 8 /* linear */)
|
||||
return g_object_ref (gdk_color_profile_get_srgb_linear());
|
||||
}
|
||||
|
||||
switch (color_primaries)
|
||||
{
|
||||
case 1:
|
||||
primaries.Green = (cmsCIExyY) { 0.300, 0.600, 1.0 };
|
||||
primaries.Blue = (cmsCIExyY) { 0.150, 0.060, 1.0 };
|
||||
primaries.Red = (cmsCIExyY) { 0.640, 0.330, 1.0 };
|
||||
whitepoint = whiteD65;
|
||||
break;
|
||||
case 4:
|
||||
primaries.Green = (cmsCIExyY) { 0.21, 0.71, 1.0 };
|
||||
primaries.Blue = (cmsCIExyY) { 0.14, 0.08, 1.0 };
|
||||
primaries.Red = (cmsCIExyY) { 0.67, 0.33, 1.0 };
|
||||
whitepoint = whiteC;
|
||||
break;
|
||||
case 5:
|
||||
primaries.Green = (cmsCIExyY) { 0.29, 0.60, 1.0 };
|
||||
primaries.Blue = (cmsCIExyY) { 0.15, 0.06, 1.0 };
|
||||
primaries.Red = (cmsCIExyY) { 0.64, 0.33, 1.0 };
|
||||
whitepoint = whiteD65;
|
||||
break;
|
||||
case 6:
|
||||
case 7:
|
||||
primaries.Green = (cmsCIExyY) { 0.310, 0.595, 1.0 };
|
||||
primaries.Blue = (cmsCIExyY) { 0.155, 0.070, 1.0 };
|
||||
primaries.Red = (cmsCIExyY) { 0.630, 0.340, 1.0 };
|
||||
whitepoint = whiteD65;
|
||||
break;
|
||||
case 8:
|
||||
primaries.Green = (cmsCIExyY) { 0.243, 0.692, 1.0 };
|
||||
primaries.Blue = (cmsCIExyY) { 0.145, 0.049, 1.0 };
|
||||
primaries.Red = (cmsCIExyY) { 0.681, 0.319, 1.0 };
|
||||
whitepoint = whiteC;
|
||||
break;
|
||||
case 9:
|
||||
primaries.Green = (cmsCIExyY) { 0.170, 0.797, 1.0 };
|
||||
primaries.Blue = (cmsCIExyY) { 0.131, 0.046, 1.0 };
|
||||
primaries.Red = (cmsCIExyY) { 0.708, 0.292, 1.0 };
|
||||
whitepoint = whiteD65;
|
||||
break;
|
||||
case 10:
|
||||
primaries.Green = (cmsCIExyY) { 0.0, 1.0, 1.0 };
|
||||
primaries.Blue = (cmsCIExyY) { 0.0, 0.0, 1.0 };
|
||||
primaries.Red = (cmsCIExyY) { 1.0, 0.0, 1.0 };
|
||||
whitepoint = (cmsCIExyY) { 0.333333, 0.333333, 1.0 };
|
||||
break;
|
||||
case 11:
|
||||
primaries.Green = (cmsCIExyY) { 0.265, 0.690, 1.0 };
|
||||
primaries.Blue = (cmsCIExyY) { 0.150, 0.060, 1.0 };
|
||||
primaries.Red = (cmsCIExyY) { 0.680, 0.320, 1.0 };
|
||||
whitepoint = (cmsCIExyY) { 0.314, 0.351, 1.0 };
|
||||
break;
|
||||
case 12:
|
||||
primaries.Green = (cmsCIExyY) { 0.265, 0.690, 1.0 };
|
||||
primaries.Blue = (cmsCIExyY) { 0.150, 0.060, 1.0 };
|
||||
primaries.Red = (cmsCIExyY) { 0.680, 0.320, 1.0 };
|
||||
whitepoint = whiteD65;
|
||||
break;
|
||||
case 22:
|
||||
primaries.Green = (cmsCIExyY) { 0.295, 0.605, 1.0 };
|
||||
primaries.Blue = (cmsCIExyY) { 0.155, 0.077, 1.0 };
|
||||
primaries.Red = (cmsCIExyY) { 0.630, 0.340, 1.0 };
|
||||
whitepoint = whiteD65;
|
||||
break;
|
||||
default:
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unsupported color primaries (%d)", color_primaries);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (transfer_characteristics)
|
||||
{
|
||||
case 1: /* ITU_R_BT_709_5 */
|
||||
curve[0] = curve[1] = curve[2] = cmsBuildParametricToneCurve (NULL, 4,
|
||||
rec709_parameters);
|
||||
profile = cmsCreateRGBProfile (&whitepoint, &primaries, curve);
|
||||
cmsFreeToneCurve (curve[0]);
|
||||
break;
|
||||
case 4: /* ITU_R_BT_470_6_System_M */
|
||||
curve[0] = curve[1] = curve[2] = cmsBuildGamma (NULL, 2.2f);
|
||||
profile = cmsCreateRGBProfile (&whitepoint, &primaries, curve);
|
||||
cmsFreeToneCurve (curve[0]);
|
||||
break;
|
||||
case 5: /* ITU_R_BT_470_6_System_B_G */
|
||||
curve[0] = curve[1] = curve[2] = cmsBuildGamma (NULL, 2.8f);
|
||||
profile = cmsCreateRGBProfile (&whitepoint, &primaries, curve);
|
||||
cmsFreeToneCurve (curve[0]);
|
||||
break;
|
||||
case 8: /* linear */
|
||||
curve[0] = curve[1] = curve[2] = cmsBuildGamma (NULL, 1.0f);
|
||||
profile = cmsCreateRGBProfile (&whitepoint, &primaries, curve);
|
||||
cmsFreeToneCurve (curve[0]);
|
||||
break;
|
||||
/* FIXME
|
||||
* We need to handle at least 16 (PQ) here.
|
||||
* Problem is: lcms can't do it
|
||||
*/
|
||||
case 13: /* IEC_61966_2_1 */
|
||||
default:
|
||||
curve[0] = curve[1] = curve[2] = cmsBuildParametricToneCurve (NULL, 4,
|
||||
srgb_parameters);
|
||||
profile = cmsCreateRGBProfile (&whitepoint, &primaries, curve);
|
||||
cmsFreeToneCurve (curve[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
if (profile)
|
||||
{
|
||||
GdkColorProfile *color_profile;
|
||||
|
||||
color_profile = gdk_color_profile_new_from_lcms_profile (profile, error);
|
||||
|
||||
color_profile->color_primaries = color_primaries;
|
||||
color_profile->transfer_characteristics = transfer_characteristics;
|
||||
color_profile->matrix_coefficients = matrix_coefficients;
|
||||
color_profile->full_range = full_range;
|
||||
|
||||
return color_profile;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_color_profile_get_cicp_data:
|
||||
* @self: a `GdkColorProfile`
|
||||
* @color_primaries: (out): return location for color primaries
|
||||
* @transfer_characteristics: (out): return location for transfer characteristics
|
||||
* @matrix_coefficients: (out): return location for matrix coefficients
|
||||
* @full_range: (out): return location for the full range flag
|
||||
*
|
||||
* Gets the CICP parameters of a color profile.
|
||||
*
|
||||
* This is mainly useful for color profiles created via
|
||||
* [ctor@Gdk.ColorProfile.new_for_cicp].
|
||||
*
|
||||
* Note that @color_primaries and @transfer_characteristics will be set to
|
||||
* 2 (unspecified) if the color profile does not contain CICP information.
|
||||
*
|
||||
* Since: 4.8
|
||||
*/
|
||||
void
|
||||
gdk_color_profile_get_cicp_data (GdkColorProfile *self,
|
||||
int *color_primaries,
|
||||
int *transfer_characteristics,
|
||||
int *matrix_coefficients,
|
||||
gboolean *full_range)
|
||||
{
|
||||
g_return_if_fail (GDK_IS_COLOR_PROFILE (self));
|
||||
|
||||
*color_primaries = self->color_primaries;
|
||||
*transfer_characteristics = self->transfer_characteristics;
|
||||
*matrix_coefficients = self->matrix_coefficients;
|
||||
*full_range = self->full_range;
|
||||
}
|
82
gdk/gdkcolorprofile.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/* gdkcolor_profile.h
|
||||
*
|
||||
* Copyright 2021 (c) Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __GDK_COLOR_PROFILE_H__
|
||||
#define __GDK_COLOR_PROFILE_H__
|
||||
|
||||
#if !defined (__GDK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gdk/gdk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gdk/gdkversionmacros.h>
|
||||
#include <gdk/gdktypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GDK_TYPE_COLOR_PROFILE (gdk_color_profile_get_type ())
|
||||
|
||||
#define GDK_COLOR_PROFILE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_COLOR_PROFILE, GdkColorProfile))
|
||||
#define GDK_IS_COLOR_PROFILE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_COLOR_PROFILE))
|
||||
#define GDK_COLOR_PROFILE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_COLOR_PROFILE, GdkColorProfileClass))
|
||||
#define GDK_IS_COLOR_PROFILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_COLOR_PROFILE))
|
||||
#define GDK_COLOR_PROFILE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_COLOR_PROFILE, GdkColorProfileClass))
|
||||
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkColorProfile, g_object_unref)
|
||||
|
||||
typedef struct _GdkColorProfileClass GdkColorProfileClass;
|
||||
|
||||
GDK_AVAILABLE_IN_4_8
|
||||
GType gdk_color_profile_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_4_8
|
||||
GdkColorProfile * gdk_color_profile_get_srgb (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_4_8
|
||||
GdkColorProfile * gdk_color_profile_new_from_icc_bytes (GBytes *bytes,
|
||||
GError **error);
|
||||
GDK_AVAILABLE_IN_4_8
|
||||
GBytes * gdk_color_profile_get_icc_profile (GdkColorProfile *self);
|
||||
|
||||
GDK_AVAILABLE_IN_4_8
|
||||
GdkColorProfile * gdk_color_profile_new_from_cicp (int color_primaries,
|
||||
int transfer_characteristics,
|
||||
int matrix_coefficients,
|
||||
gboolean full_range,
|
||||
GError **error);
|
||||
GDK_AVAILABLE_IN_4_8
|
||||
void gdk_color_profile_get_cicp_data (GdkColorProfile *self,
|
||||
int *color_primaries,
|
||||
int *transfer_characteristics,
|
||||
int *matrix_coefficients,
|
||||
gboolean *full_range);
|
||||
|
||||
GDK_AVAILABLE_IN_4_8
|
||||
gboolean gdk_color_profile_is_linear (GdkColorProfile *self) G_GNUC_PURE;
|
||||
|
||||
GDK_AVAILABLE_IN_4_8
|
||||
gsize gdk_color_profile_get_n_components (GdkColorProfile *self) G_GNUC_PURE;
|
||||
|
||||
|
||||
GDK_AVAILABLE_IN_4_8
|
||||
gboolean gdk_color_profile_equal (gconstpointer profile1,
|
||||
gconstpointer profile2);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GDK_COLOR_PROFILE_H__ */
|
30
gdk/gdkcolorprofileprivate.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef __GDK_COLOR_PROFILE_PRIVATE_H__
|
||||
#define __GDK_COLOR_PROFILE_PRIVATE_H__
|
||||
|
||||
#include "gdkcolorprofile.h"
|
||||
#include "gdkmemorytexture.h"
|
||||
|
||||
#include <lcms2.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GdkColorProfile * gdk_color_profile_get_srgb_linear (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GdkColorProfile * gdk_color_profile_new_from_lcms_profile (cmsHPROFILE lcms_profile,
|
||||
GError **error);
|
||||
|
||||
cmsHPROFILE * gdk_color_profile_get_lcms_profile (GdkColorProfile *self);
|
||||
|
||||
gboolean gdk_color_profile_supports_memory_format (GdkColorProfile *profile,
|
||||
GdkMemoryFormat format);
|
||||
|
||||
cmsHTRANSFORM * gdk_color_profile_lookup_transform (GdkColorProfile *source,
|
||||
guint source_type,
|
||||
GdkColorProfile *dest,
|
||||
guint dest_type);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GDK_COLOR_PROFILE_PRIVATE_H__ */
|
@@ -51,6 +51,7 @@ typedef enum {
|
||||
GDK_DEBUG_VULKAN_VALIDATE = 1 << 22,
|
||||
GDK_DEBUG_DEFAULT_SETTINGS= 1 << 23,
|
||||
GDK_DEBUG_HIGH_DEPTH = 1 << 24,
|
||||
GDK_DEBUG_SRGB = 1 << 25,
|
||||
} GdkDebugFlags;
|
||||
|
||||
extern guint _gdk_debug_flags;
|
||||
|
@@ -25,6 +25,7 @@
|
||||
#include "gdkmemoryformatprivate.h"
|
||||
#include "gdkmemorytextureprivate.h"
|
||||
#include "gdktextureprivate.h"
|
||||
#include "gdkcolorprofile.h"
|
||||
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
@@ -38,6 +39,7 @@ struct _GdkGLTexture {
|
||||
GdkTexture parent_instance;
|
||||
|
||||
GdkGLContext *context;
|
||||
GdkGLTextureFlags flags;
|
||||
guint id;
|
||||
|
||||
GdkTexture *saved;
|
||||
@@ -115,6 +117,7 @@ typedef struct _Download Download;
|
||||
struct _Download
|
||||
{
|
||||
GdkMemoryFormat format;
|
||||
GdkColorProfile *profile;
|
||||
guchar *data;
|
||||
gsize stride;
|
||||
};
|
||||
@@ -157,7 +160,7 @@ gdk_gl_texture_do_download (gpointer texture_,
|
||||
expected_stride = texture->width * gdk_memory_format_bytes_per_pixel (download->format);
|
||||
|
||||
if (download->stride == expected_stride &&
|
||||
!gdk_gl_context_get_use_es (self->context) &&
|
||||
!gdk_gl_context_get_use_es (self->context) &&
|
||||
gdk_memory_format_gl_format (download->format, TRUE, &gl_internal_format, &gl_format, &gl_type))
|
||||
{
|
||||
glGetTexImage (GL_TEXTURE_2D,
|
||||
@@ -193,7 +196,7 @@ gdk_gl_texture_do_download (gpointer texture_,
|
||||
(download->stride == expected_stride))
|
||||
{
|
||||
glReadPixels (0, 0,
|
||||
texture->width, texture->height,
|
||||
texture->width, texture->height,
|
||||
gl_read_format,
|
||||
gl_read_type,
|
||||
download->data);
|
||||
@@ -204,7 +207,7 @@ gdk_gl_texture_do_download (gpointer texture_,
|
||||
guchar *pixels = g_malloc_n (texture->width * actual_bpp, texture->height);
|
||||
|
||||
glReadPixels (0, 0,
|
||||
texture->width, texture->height,
|
||||
texture->width, texture->height,
|
||||
gl_read_format,
|
||||
gl_read_type,
|
||||
pixels);
|
||||
@@ -212,9 +215,11 @@ gdk_gl_texture_do_download (gpointer texture_,
|
||||
gdk_memory_convert (download->data,
|
||||
download->stride,
|
||||
download->format,
|
||||
download->profile,
|
||||
pixels,
|
||||
texture->width * actual_bpp,
|
||||
actual_format,
|
||||
gdk_color_profile_get_srgb (),
|
||||
texture->width,
|
||||
texture->height);
|
||||
|
||||
@@ -228,6 +233,7 @@ gdk_gl_texture_do_download (gpointer texture_,
|
||||
static void
|
||||
gdk_gl_texture_download (GdkTexture *texture,
|
||||
GdkMemoryFormat format,
|
||||
GdkColorProfile *profile,
|
||||
guchar *data,
|
||||
gsize stride)
|
||||
{
|
||||
@@ -236,11 +242,12 @@ gdk_gl_texture_download (GdkTexture *texture,
|
||||
|
||||
if (self->saved)
|
||||
{
|
||||
gdk_texture_do_download (self->saved, format, data, stride);
|
||||
gdk_texture_do_download (self->saved, format, profile, data, stride);
|
||||
return;
|
||||
}
|
||||
|
||||
download.format = format;
|
||||
download.profile = profile;
|
||||
download.data = data;
|
||||
download.stride = stride;
|
||||
|
||||
@@ -275,6 +282,12 @@ gdk_gl_texture_get_id (GdkGLTexture *self)
|
||||
return self->id;
|
||||
}
|
||||
|
||||
GdkGLTextureFlags
|
||||
gdk_gl_texture_get_flags (GdkGLTexture *self)
|
||||
{
|
||||
return self->flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_gl_texture_release:
|
||||
* @self: a `GdkTexture` wrapping a GL texture
|
||||
@@ -295,7 +308,8 @@ gdk_gl_texture_release (GdkGLTexture *self)
|
||||
|
||||
texture = GDK_TEXTURE (self);
|
||||
self->saved = GDK_TEXTURE (gdk_memory_texture_from_texture (texture,
|
||||
gdk_texture_get_format (texture)));
|
||||
gdk_texture_get_format (texture),
|
||||
gdk_texture_get_color_profile (texture)));
|
||||
|
||||
if (self->destroy)
|
||||
{
|
||||
@@ -428,6 +442,10 @@ gdk_gl_texture_determine_format (GdkGLTexture *self)
|
||||
* which will happen when the GdkTexture object is finalized, or due to
|
||||
* an explicit call of [method@Gdk.GLTexture.release].
|
||||
*
|
||||
* The texture data is assumed to be premultiplied, not flipped, and in the
|
||||
* sRGB colorspace, see [ctor@Gdk.GLTexture.new_with_color_profile] to override
|
||||
* this.
|
||||
*
|
||||
* Return value: (transfer full) (type GdkGLTexture): A newly-created
|
||||
* `GdkTexture`
|
||||
*/
|
||||
@@ -438,6 +456,46 @@ gdk_gl_texture_new (GdkGLContext *context,
|
||||
int height,
|
||||
GDestroyNotify destroy,
|
||||
gpointer data)
|
||||
{
|
||||
return gdk_gl_texture_new_with_color_profile (context, id,
|
||||
width, height,
|
||||
GDK_GL_TEXTURE_PREMULTIPLIED,
|
||||
gdk_color_profile_get_srgb (),
|
||||
destroy, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_gl_texture_new_with_color_profile:
|
||||
* @context: a `GdkGLContext`
|
||||
* @id: the ID of a texture that was created with @context
|
||||
* @width: the nominal width of the texture
|
||||
* @height: the nominal height of the texture
|
||||
* @flags: flags that describe the content of the texture
|
||||
* @color_profile: the `GdkColorProfile` for the content of the texture
|
||||
* @destroy: a destroy notify that will be called when the GL resources
|
||||
* are released
|
||||
* @data: data that gets passed to @destroy
|
||||
*
|
||||
* Creates a new texture for an existing GL texture with a given color profile
|
||||
* and flags.
|
||||
*
|
||||
* Note that the GL texture must not be modified until @destroy is called,
|
||||
* which will happen when the GdkTexture object is finalized, or due to
|
||||
* an explicit call of [method@Gdk.GLTexture.release].
|
||||
*
|
||||
* Return value: (transfer full): A newly-created `GdkTexture`
|
||||
*
|
||||
* Since: 4.8
|
||||
*/
|
||||
GdkTexture *
|
||||
gdk_gl_texture_new_with_color_profile (GdkGLContext *context,
|
||||
guint id,
|
||||
int width,
|
||||
int height,
|
||||
GdkGLTextureFlags flags,
|
||||
GdkColorProfile *color_profile,
|
||||
GDestroyNotify destroy,
|
||||
gpointer data)
|
||||
{
|
||||
GdkGLTexture *self;
|
||||
|
||||
@@ -449,10 +507,12 @@ gdk_gl_texture_new (GdkGLContext *context,
|
||||
self = g_object_new (GDK_TYPE_GL_TEXTURE,
|
||||
"width", width,
|
||||
"height", height,
|
||||
"color-profile", color_profile,
|
||||
NULL);
|
||||
|
||||
self->context = g_object_ref (context);
|
||||
self->id = id;
|
||||
self->flags = flags;
|
||||
self->destroy = destroy;
|
||||
self->data = data;
|
||||
|
||||
|
@@ -49,6 +49,29 @@ GdkTexture * gdk_gl_texture_new (GdkGLContext
|
||||
GDestroyNotify destroy,
|
||||
gpointer data);
|
||||
|
||||
/**
|
||||
* GdkGLTextureFlags:
|
||||
* @GDK_GL_TEXTURE_FLAGS_PREMULTIPLIED: The alpha in the data is premultiplied
|
||||
* @GDK_GL_TEXTURE_FLAGS_FLIPPED: The data has the origin at the bottom (this is usually
|
||||
* th case for textures that are produced by GL rendering)
|
||||
*
|
||||
* Flags that describe the content of a GL texture.
|
||||
*/
|
||||
typedef enum {
|
||||
GDK_GL_TEXTURE_PREMULTIPLIED = 1 << 0,
|
||||
GDK_GL_TEXTURE_FLIPPED = 1 << 1,
|
||||
} GdkGLTextureFlags;
|
||||
|
||||
GDK_AVAILABLE_IN_4_8
|
||||
GdkTexture * gdk_gl_texture_new_with_color_profile (GdkGLContext *context,
|
||||
guint id,
|
||||
int width,
|
||||
int height,
|
||||
GdkGLTextureFlags flags,
|
||||
GdkColorProfile *color_profile,
|
||||
GDestroyNotify destroy,
|
||||
gpointer data);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gdk_gl_texture_release (GdkGLTexture *self);
|
||||
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -21,6 +21,8 @@
|
||||
|
||||
#include "gdkmemoryformatprivate.h"
|
||||
|
||||
#include "gdkcolorprofileprivate.h"
|
||||
#include "gdkprofilerprivate.h"
|
||||
#include "gsk/gl/fp16private.h"
|
||||
|
||||
#include <epoxy/gl.h>
|
||||
@@ -72,6 +74,223 @@ TYPED_FUNCS (b8g8r8, guchar, 2, 1, 0, -1, 3, 255)
|
||||
TYPED_FUNCS (r16g16b16, guint16, 0, 1, 2, -1, 6, 65535)
|
||||
TYPED_FUNCS (r16g16b16a16, guint16, 0, 1, 2, 3, 8, 65535)
|
||||
|
||||
#define PREMULTIPLY_FUNCS(type, A, scale) \
|
||||
static void \
|
||||
type ## _premultiply_ ## A (guchar *dest_data, \
|
||||
const guchar *src_data, \
|
||||
gsize n) \
|
||||
{ \
|
||||
type *dest = (type *) dest_data; \
|
||||
const type *src = (const type *) src_data; \
|
||||
for (gsize i = 0; i < n; i++) \
|
||||
{ \
|
||||
unsigned a = src[A]; \
|
||||
if (A != 0) dest[0] = src[0] * a / scale; \
|
||||
if (A != 1) dest[1] = src[1] * a / scale; \
|
||||
if (A != 2) dest[2] = src[2] * a / scale; \
|
||||
if (A != 3) dest[3] = src[3] * a / scale; \
|
||||
dest[A] = a; \
|
||||
src += 4; \
|
||||
dest += 4; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
static void \
|
||||
type ## _unpremultiply_ ## A (guchar *dest_data, \
|
||||
const guchar *src_data, \
|
||||
gsize n) \
|
||||
{ \
|
||||
type *dest = (type *) dest_data; \
|
||||
const type *src = (const type *) src_data; \
|
||||
for (gsize i = 0; i < n; i++) \
|
||||
{ \
|
||||
unsigned a = src[A]; \
|
||||
if (a == 0) \
|
||||
{ \
|
||||
dest[0] = dest[1] = dest[2] = dest[3] = 0; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
if (A != 0) dest[0] = (src[0] * scale + a / 2) / a; \
|
||||
if (A != 1) dest[1] = (src[1] * scale + a / 2) / a; \
|
||||
if (A != 2) dest[2] = (src[2] * scale + a / 2) / a; \
|
||||
if (A != 3) dest[3] = (src[3] * scale + a / 2) / a; \
|
||||
dest[A] = a; \
|
||||
} \
|
||||
src += 4; \
|
||||
dest += 4; \
|
||||
} \
|
||||
}
|
||||
|
||||
PREMULTIPLY_FUNCS (guint8, 0, G_MAXUINT8)
|
||||
PREMULTIPLY_FUNCS (guint8, 3, G_MAXUINT8)
|
||||
PREMULTIPLY_FUNCS (guint16, 3, G_MAXUINT16)
|
||||
|
||||
static void
|
||||
half_float_premultiply (guchar *dest_data,
|
||||
const guchar *src_data,
|
||||
gsize n)
|
||||
{
|
||||
guint16 *dest = (guint16 *) dest_data;
|
||||
const float *src = (const float *) src_data;
|
||||
for (gsize i = 0; i < n; i++)
|
||||
{
|
||||
float tmp[4];
|
||||
tmp[0] = src[0] * src[3];
|
||||
tmp[1] = src[1] * src[3];
|
||||
tmp[2] = src[2] * src[3];
|
||||
tmp[3] = src[3];
|
||||
float_to_half4 (tmp, dest);
|
||||
dest += 4;
|
||||
src += 4;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
half_float_unpremultiply (guchar *dest_data,
|
||||
const guchar *src_data,
|
||||
gsize n)
|
||||
{
|
||||
float *dest = (float *) dest_data;
|
||||
const guint16 *src = (const guint16 *) src_data;
|
||||
for (gsize i = 0; i < n; i++)
|
||||
{
|
||||
half_to_float4 (src, dest);
|
||||
if (dest[3] <= 1/255.0)
|
||||
{
|
||||
memset (dest, 0, sizeof (guint) * 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
dest[0] = dest[0] / dest[3];
|
||||
dest[1] = dest[1] / dest[3];
|
||||
dest[2] = dest[2] / dest[3];
|
||||
}
|
||||
dest += 4;
|
||||
src += 4;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
float_premultiply (guchar *dest_data,
|
||||
const guchar *src_data,
|
||||
gsize n)
|
||||
{
|
||||
float *dest = (float *) dest_data;
|
||||
const float *src = (const float *) src_data;
|
||||
for (gsize i = 0; i < n; i++)
|
||||
{
|
||||
float a = src[3];
|
||||
dest[0] = src[0] * a;
|
||||
dest[1] = src[1] * a;
|
||||
dest[2] = src[2] * a;
|
||||
dest[3] = a;
|
||||
dest += 4;
|
||||
src += 4;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
float_unpremultiply (guchar *dest_data,
|
||||
const guchar *src_data,
|
||||
gsize n)
|
||||
{
|
||||
float *dest = (float *) dest_data;
|
||||
const float *src = (const float *) src_data;
|
||||
for (gsize i = 0; i < n; i++)
|
||||
{
|
||||
float a = src[3];
|
||||
if (a <= 1/255.0)
|
||||
{
|
||||
memset (dest, 0, sizeof (float) * 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
dest[0] = src[0] / a;
|
||||
dest[1] = src[1] / a;
|
||||
dest[2] = src[2] / a;
|
||||
dest[3] = a;
|
||||
}
|
||||
dest += 4;
|
||||
src += 4;
|
||||
}
|
||||
}
|
||||
|
||||
#define COMPRESS_FUNCS(type, scale) \
|
||||
static void \
|
||||
type ## _expand (guchar *dest_data, \
|
||||
const guchar *src_data, \
|
||||
gsize n) \
|
||||
{ \
|
||||
type *dest = (type *) dest_data; \
|
||||
const type *src = (const type *) src_data; \
|
||||
for (gsize i = 0; i < n; i++) \
|
||||
{ \
|
||||
dest[0] = src[0]; \
|
||||
dest[1] = src[1]; \
|
||||
dest[2] = src[2]; \
|
||||
dest[3] = scale; \
|
||||
dest += 4; \
|
||||
src += 3; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
static void \
|
||||
type ## _compress (guchar *dest_data, \
|
||||
const guchar *src_data, \
|
||||
gsize n) \
|
||||
{ \
|
||||
type *dest = (type *) dest_data; \
|
||||
const type *src = (const type *) src_data; \
|
||||
for (gsize i = 0; i < n; i++) \
|
||||
{ \
|
||||
dest[0] = src[0] * src[3] / scale; \
|
||||
dest[1] = src[1] * src[3] / scale; \
|
||||
dest[2] = src[2] * src[3] / scale; \
|
||||
dest += 3; \
|
||||
src += 4; \
|
||||
} \
|
||||
}
|
||||
|
||||
COMPRESS_FUNCS (guint8, G_MAXUINT8)
|
||||
COMPRESS_FUNCS (guint16, G_MAXUINT16)
|
||||
COMPRESS_FUNCS (float, 1.0f)
|
||||
|
||||
static void
|
||||
half_float_expand (guchar *dest_data,
|
||||
const guchar *src_data,
|
||||
gsize n)
|
||||
{
|
||||
float *dest = (float *) dest_data;
|
||||
const guint16 *src = (const guint16 *) src_data;
|
||||
for (gsize i = 0; i < n; i++)
|
||||
{
|
||||
half_to_float (src, dest, 3);
|
||||
dest[3] = 1.0;
|
||||
dest += 4;
|
||||
src += 3;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
half_float_compress (guchar *dest_data,
|
||||
const guchar *src_data,
|
||||
gsize n)
|
||||
{
|
||||
guint16 *dest = (guint16 *) dest_data;
|
||||
const float *src = (const float *) src_data;
|
||||
for (gsize i = 0; i < n; i++)
|
||||
{
|
||||
float tmp[3];
|
||||
tmp[0] = src[0] * src[3];
|
||||
tmp[1] = src[1] * src[3];
|
||||
tmp[2] = src[2] * src[3];
|
||||
float_to_half (tmp, dest, 3);
|
||||
dest += 3;
|
||||
src += 4;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
r16g16b16_float_to_float (float *dest,
|
||||
const guchar *src_data,
|
||||
@@ -166,56 +385,9 @@ r32g32b32a32_float_from_float (guchar *dest,
|
||||
memcpy (dest, src, sizeof (float) * n * 4);
|
||||
}
|
||||
|
||||
#define PREMULTIPLY_FUNC(name, R1, G1, B1, A1, R2, G2, B2, A2) \
|
||||
static void \
|
||||
name (guchar *dest, \
|
||||
const guchar *src, \
|
||||
gsize n) \
|
||||
{ \
|
||||
for (; n > 0; n--) \
|
||||
{ \
|
||||
guchar a = src[A1]; \
|
||||
guint16 r = (guint16)src[R1] * a + 127; \
|
||||
guint16 g = (guint16)src[G1] * a + 127; \
|
||||
guint16 b = (guint16)src[B1] * a + 127; \
|
||||
dest[R2] = (r + (r >> 8) + 1) >> 8; \
|
||||
dest[G2] = (g + (g >> 8) + 1) >> 8; \
|
||||
dest[B2] = (b + (b >> 8) + 1) >> 8; \
|
||||
dest[A2] = a; \
|
||||
dest += 4; \
|
||||
src += 4; \
|
||||
} \
|
||||
}
|
||||
|
||||
PREMULTIPLY_FUNC(r8g8b8a8_to_r8g8b8a8_premultiplied, 0, 1, 2, 3, 0, 1, 2, 3)
|
||||
PREMULTIPLY_FUNC(r8g8b8a8_to_b8g8r8a8_premultiplied, 0, 1, 2, 3, 2, 1, 0, 3)
|
||||
PREMULTIPLY_FUNC(r8g8b8a8_to_a8r8g8b8_premultiplied, 0, 1, 2, 3, 1, 2, 3, 0)
|
||||
PREMULTIPLY_FUNC(r8g8b8a8_to_a8b8g8r8_premultiplied, 0, 1, 2, 3, 3, 2, 1, 0)
|
||||
|
||||
#define ADD_ALPHA_FUNC(name, R1, G1, B1, R2, G2, B2, A2) \
|
||||
static void \
|
||||
name (guchar *dest, \
|
||||
const guchar *src, \
|
||||
gsize n) \
|
||||
{ \
|
||||
for (; n > 0; n--) \
|
||||
{ \
|
||||
dest[R2] = src[R1]; \
|
||||
dest[G2] = src[G1]; \
|
||||
dest[B2] = src[B1]; \
|
||||
dest[A2] = 255; \
|
||||
dest += 4; \
|
||||
src += 3; \
|
||||
} \
|
||||
}
|
||||
|
||||
ADD_ALPHA_FUNC(r8g8b8_to_r8g8b8a8, 0, 1, 2, 0, 1, 2, 3)
|
||||
ADD_ALPHA_FUNC(r8g8b8_to_b8g8r8a8, 0, 1, 2, 2, 1, 0, 3)
|
||||
ADD_ALPHA_FUNC(r8g8b8_to_a8r8g8b8, 0, 1, 2, 1, 2, 3, 0)
|
||||
ADD_ALPHA_FUNC(r8g8b8_to_a8b8g8r8, 0, 1, 2, 3, 2, 1, 0)
|
||||
|
||||
struct _GdkMemoryFormatDescription
|
||||
{
|
||||
const char *name;
|
||||
GdkMemoryAlpha alpha;
|
||||
gsize bytes_per_pixel;
|
||||
gsize alignment;
|
||||
@@ -229,6 +401,12 @@ struct _GdkMemoryFormatDescription
|
||||
/* no premultiplication going on here */
|
||||
void (* to_float) (float *, const guchar*, gsize);
|
||||
void (* from_float) (guchar *, const float *, gsize);
|
||||
struct {
|
||||
guint type;
|
||||
gsize bpp;
|
||||
} lcms;
|
||||
void (* to_lcms) (guchar *, const guchar *, gsize);
|
||||
void (* from_lcms) (guchar *, const guchar *, gsize);
|
||||
};
|
||||
|
||||
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
|
||||
@@ -241,6 +419,7 @@ struct _GdkMemoryFormatDescription
|
||||
|
||||
static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
|
||||
[GDK_MEMORY_B8G8R8A8_PREMULTIPLIED] = {
|
||||
"B8G8R8A8_PREMULTIPLIED",
|
||||
GDK_MEMORY_ALPHA_PREMULTIPLIED,
|
||||
4,
|
||||
G_ALIGNOF (guchar),
|
||||
@@ -249,8 +428,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
|
||||
{ GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE },
|
||||
b8g8r8a8_premultiplied_to_float,
|
||||
b8g8r8a8_premultiplied_from_float,
|
||||
{ TYPE_BGRA_8, 4 },
|
||||
guint8_unpremultiply_3,
|
||||
guint8_premultiply_3,
|
||||
},
|
||||
[GDK_MEMORY_A8R8G8B8_PREMULTIPLIED] = {
|
||||
"A8R8G8B8_PREMULTIPLIED",
|
||||
GDK_MEMORY_ALPHA_PREMULTIPLIED,
|
||||
4,
|
||||
G_ALIGNOF (guchar),
|
||||
@@ -259,8 +442,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
|
||||
{ GL_RGBA8, GL_BGRA, GDK_GL_UNSIGNED_BYTE_FLIPPED },
|
||||
a8r8g8b8_premultiplied_to_float,
|
||||
a8r8g8b8_premultiplied_from_float,
|
||||
{ TYPE_ARGB_8, 4 },
|
||||
guint8_unpremultiply_0,
|
||||
guint8_premultiply_0,
|
||||
},
|
||||
[GDK_MEMORY_R8G8B8A8_PREMULTIPLIED] = {
|
||||
"R8G8B8A8_PREMULTIPLIED",
|
||||
GDK_MEMORY_ALPHA_PREMULTIPLIED,
|
||||
4,
|
||||
G_ALIGNOF (guchar),
|
||||
@@ -269,8 +456,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
|
||||
{ GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE },
|
||||
r8g8b8a8_premultiplied_to_float,
|
||||
r8g8b8a8_premultiplied_from_float,
|
||||
{ TYPE_RGBA_8, 4 },
|
||||
guint8_unpremultiply_3,
|
||||
guint8_premultiply_3,
|
||||
},
|
||||
[GDK_MEMORY_B8G8R8A8] = {
|
||||
"B8G8R8A8",
|
||||
GDK_MEMORY_ALPHA_STRAIGHT,
|
||||
4,
|
||||
G_ALIGNOF (guchar),
|
||||
@@ -279,8 +470,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
|
||||
{ GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE },
|
||||
b8g8r8a8_to_float,
|
||||
b8g8r8a8_from_float,
|
||||
{ TYPE_BGRA_8, 4 },
|
||||
NULL,
|
||||
NULL,
|
||||
},
|
||||
[GDK_MEMORY_A8R8G8B8] = {
|
||||
"A8R8G8B8",
|
||||
GDK_MEMORY_ALPHA_STRAIGHT,
|
||||
4,
|
||||
G_ALIGNOF (guchar),
|
||||
@@ -289,8 +484,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
|
||||
{ GL_RGBA8, GL_RGBA, GDK_GL_UNSIGNED_BYTE_FLIPPED },
|
||||
a8r8g8b8_to_float,
|
||||
a8r8g8b8_from_float,
|
||||
{ TYPE_ARGB_8, 4 },
|
||||
NULL,
|
||||
NULL,
|
||||
},
|
||||
[GDK_MEMORY_R8G8B8A8] = {
|
||||
"R8G8B8A8",
|
||||
GDK_MEMORY_ALPHA_STRAIGHT,
|
||||
4,
|
||||
G_ALIGNOF (guchar),
|
||||
@@ -299,8 +498,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
|
||||
{ GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE },
|
||||
r8g8b8a8_to_float,
|
||||
r8g8b8a8_from_float,
|
||||
{ TYPE_RGBA_8, 4 },
|
||||
NULL,
|
||||
NULL,
|
||||
},
|
||||
[GDK_MEMORY_A8B8G8R8] = {
|
||||
"A8B8G8R8",
|
||||
GDK_MEMORY_ALPHA_STRAIGHT,
|
||||
4,
|
||||
G_ALIGNOF (guchar),
|
||||
@@ -309,8 +512,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
|
||||
{ GL_RGBA8, GL_BGRA, GDK_GL_UNSIGNED_BYTE_FLIPPED },
|
||||
a8b8g8r8_to_float,
|
||||
a8b8g8r8_from_float,
|
||||
{ TYPE_ABGR_8, 4 },
|
||||
NULL,
|
||||
NULL,
|
||||
},
|
||||
[GDK_MEMORY_R8G8B8] = {
|
||||
"R8G8B8",
|
||||
GDK_MEMORY_ALPHA_OPAQUE,
|
||||
3,
|
||||
G_ALIGNOF (guchar),
|
||||
@@ -319,8 +526,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
|
||||
{ GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE },
|
||||
r8g8b8_to_float,
|
||||
r8g8b8_from_float,
|
||||
{ TYPE_RGBA_8, 4 },
|
||||
guint8_expand,
|
||||
guint8_compress,
|
||||
},
|
||||
[GDK_MEMORY_B8G8R8] = {
|
||||
"B8G8R8",
|
||||
GDK_MEMORY_ALPHA_OPAQUE,
|
||||
3,
|
||||
G_ALIGNOF (guchar),
|
||||
@@ -329,8 +540,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
|
||||
{ GL_RGB8, GL_BGR, GL_UNSIGNED_BYTE },
|
||||
b8g8r8_to_float,
|
||||
b8g8r8_from_float,
|
||||
{ TYPE_BGRA_8, 4 },
|
||||
guint8_expand,
|
||||
guint8_compress,
|
||||
},
|
||||
[GDK_MEMORY_R16G16B16] = {
|
||||
"R16G16B16",
|
||||
GDK_MEMORY_ALPHA_OPAQUE,
|
||||
6,
|
||||
G_ALIGNOF (guint16),
|
||||
@@ -339,8 +554,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
|
||||
{ GL_RGB16, GL_RGB, GL_UNSIGNED_SHORT },
|
||||
r16g16b16_to_float,
|
||||
r16g16b16_from_float,
|
||||
{ TYPE_RGBA_16, 8 },
|
||||
guint16_expand,
|
||||
guint16_compress,
|
||||
},
|
||||
[GDK_MEMORY_R16G16B16A16_PREMULTIPLIED] = {
|
||||
"R16G16B16A16_PREMULTIPLIED",
|
||||
GDK_MEMORY_ALPHA_PREMULTIPLIED,
|
||||
8,
|
||||
G_ALIGNOF (guint16),
|
||||
@@ -349,8 +568,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
|
||||
{ GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT },
|
||||
r16g16b16a16_to_float,
|
||||
r16g16b16a16_from_float,
|
||||
{ TYPE_RGBA_16, 8 },
|
||||
guint16_unpremultiply_3,
|
||||
guint16_premultiply_3,
|
||||
},
|
||||
[GDK_MEMORY_R16G16B16A16] = {
|
||||
"R16G16B16A16",
|
||||
GDK_MEMORY_ALPHA_STRAIGHT,
|
||||
8,
|
||||
G_ALIGNOF (guint16),
|
||||
@@ -359,8 +582,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
|
||||
{ GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT },
|
||||
r16g16b16a16_to_float,
|
||||
r16g16b16a16_from_float,
|
||||
{ TYPE_RGBA_16, 8 },
|
||||
NULL,
|
||||
NULL,
|
||||
},
|
||||
[GDK_MEMORY_R16G16B16_FLOAT] = {
|
||||
"R16G16B16_FLOAT",
|
||||
GDK_MEMORY_ALPHA_OPAQUE,
|
||||
6,
|
||||
G_ALIGNOF (guint16),
|
||||
@@ -369,8 +596,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
|
||||
{ GL_RGB16F, GL_RGB, GL_HALF_FLOAT },
|
||||
r16g16b16_float_to_float,
|
||||
r16g16b16_float_from_float,
|
||||
{ TYPE_RGBA_FLT, 16 },
|
||||
half_float_expand,
|
||||
half_float_compress,
|
||||
},
|
||||
[GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED] = {
|
||||
"R16G16B16A16_FLOAT_PREMULTIPLIED",
|
||||
GDK_MEMORY_ALPHA_PREMULTIPLIED,
|
||||
8,
|
||||
G_ALIGNOF (guint16),
|
||||
@@ -379,8 +610,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
|
||||
{ GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT },
|
||||
r16g16b16a16_float_to_float,
|
||||
r16g16b16a16_float_from_float,
|
||||
{ TYPE_RGBA_FLT, 16 },
|
||||
half_float_unpremultiply,
|
||||
half_float_premultiply,
|
||||
},
|
||||
[GDK_MEMORY_R16G16B16A16_FLOAT] = {
|
||||
"R16G16B16A16_FLOAT",
|
||||
GDK_MEMORY_ALPHA_STRAIGHT,
|
||||
8,
|
||||
G_ALIGNOF (guint16),
|
||||
@@ -389,8 +624,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
|
||||
{ GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT },
|
||||
r16g16b16a16_float_to_float,
|
||||
r16g16b16a16_float_from_float,
|
||||
{ TYPE_RGBA_FLT, 16 },
|
||||
NULL,
|
||||
NULL
|
||||
},
|
||||
[GDK_MEMORY_R32G32B32_FLOAT] = {
|
||||
"R32G32B32_FLOAT",
|
||||
GDK_MEMORY_ALPHA_OPAQUE,
|
||||
12,
|
||||
G_ALIGNOF (float),
|
||||
@@ -399,8 +638,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
|
||||
{ GL_RGB32F, GL_RGB, GL_FLOAT },
|
||||
r32g32b32_float_to_float,
|
||||
r32g32b32_float_from_float,
|
||||
{ TYPE_RGBA_FLT, 16 },
|
||||
float_expand,
|
||||
float_compress,
|
||||
},
|
||||
[GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED] = {
|
||||
"R32G32B32A32_FLOAT_PREMULTIPLIED",
|
||||
GDK_MEMORY_ALPHA_PREMULTIPLIED,
|
||||
16,
|
||||
G_ALIGNOF (float),
|
||||
@@ -409,8 +652,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
|
||||
{ GL_RGBA32F, GL_RGBA, GL_FLOAT },
|
||||
r32g32b32a32_float_to_float,
|
||||
r32g32b32a32_float_from_float,
|
||||
{ TYPE_RGBA_FLT, 16 },
|
||||
float_unpremultiply,
|
||||
float_premultiply,
|
||||
},
|
||||
[GDK_MEMORY_R32G32B32A32_FLOAT] = {
|
||||
"R32G32B32A32_FLOAT",
|
||||
GDK_MEMORY_ALPHA_STRAIGHT,
|
||||
16,
|
||||
G_ALIGNOF (float),
|
||||
@@ -419,6 +666,9 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
|
||||
{ GL_RGBA32F, GL_RGBA, GL_FLOAT },
|
||||
r32g32b32a32_float_to_float,
|
||||
r32g32b32a32_float_from_float,
|
||||
{ TYPE_RGBA_FLT, 16 },
|
||||
NULL,
|
||||
NULL,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -509,86 +759,164 @@ unpremultiply (float *rgba,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gdk_memory_convert (guchar *dest_data,
|
||||
gsize dest_stride,
|
||||
GdkMemoryFormat dest_format,
|
||||
const guchar *src_data,
|
||||
gsize src_stride,
|
||||
GdkMemoryFormat src_format,
|
||||
gsize width,
|
||||
gsize height)
|
||||
static void
|
||||
gdk_memory_convert_same_format (guchar *dest_data,
|
||||
gsize dest_stride,
|
||||
const guchar *src_data,
|
||||
gsize src_stride,
|
||||
gsize width,
|
||||
gsize height,
|
||||
GdkMemoryFormat format)
|
||||
{
|
||||
const GdkMemoryFormatDescription *dest_desc = &memory_formats[dest_format];
|
||||
const GdkMemoryFormatDescription *src_desc = &memory_formats[src_format];
|
||||
float *tmp;
|
||||
gsize y;
|
||||
void (*func) (guchar *, const guchar *, gsize) = NULL;
|
||||
const GdkMemoryFormatDescription *desc = &memory_formats[format];
|
||||
gsize y, stride;
|
||||
|
||||
g_assert (dest_format < GDK_MEMORY_N_FORMATS);
|
||||
g_assert (src_format < GDK_MEMORY_N_FORMATS);
|
||||
|
||||
if (src_format == GDK_MEMORY_R8G8B8A8 && dest_format == GDK_MEMORY_R8G8B8A8_PREMULTIPLIED)
|
||||
func = r8g8b8a8_to_r8g8b8a8_premultiplied;
|
||||
else if (src_format == GDK_MEMORY_B8G8R8A8 && dest_format == GDK_MEMORY_R8G8B8A8_PREMULTIPLIED)
|
||||
func = r8g8b8a8_to_b8g8r8a8_premultiplied;
|
||||
else if (src_format == GDK_MEMORY_R8G8B8A8 && dest_format == GDK_MEMORY_B8G8R8A8_PREMULTIPLIED)
|
||||
func = r8g8b8a8_to_b8g8r8a8_premultiplied;
|
||||
else if (src_format == GDK_MEMORY_B8G8R8A8 && dest_format == GDK_MEMORY_B8G8R8A8_PREMULTIPLIED)
|
||||
func = r8g8b8a8_to_r8g8b8a8_premultiplied;
|
||||
else if (src_format == GDK_MEMORY_R8G8B8A8 && dest_format == GDK_MEMORY_A8R8G8B8_PREMULTIPLIED)
|
||||
func = r8g8b8a8_to_a8r8g8b8_premultiplied;
|
||||
else if (src_format == GDK_MEMORY_B8G8R8A8 && dest_format == GDK_MEMORY_A8R8G8B8_PREMULTIPLIED)
|
||||
func = r8g8b8a8_to_a8b8g8r8_premultiplied;
|
||||
else if (src_format == GDK_MEMORY_R8G8B8 && dest_format == GDK_MEMORY_R8G8B8A8_PREMULTIPLIED)
|
||||
func = r8g8b8_to_r8g8b8a8;
|
||||
else if (src_format == GDK_MEMORY_B8G8R8 && dest_format == GDK_MEMORY_R8G8B8A8_PREMULTIPLIED)
|
||||
func = r8g8b8_to_b8g8r8a8;
|
||||
else if (src_format == GDK_MEMORY_R8G8B8 && dest_format == GDK_MEMORY_B8G8R8A8_PREMULTIPLIED)
|
||||
func = r8g8b8_to_b8g8r8a8;
|
||||
else if (src_format == GDK_MEMORY_B8G8R8 && dest_format == GDK_MEMORY_B8G8R8A8_PREMULTIPLIED)
|
||||
func = r8g8b8_to_r8g8b8a8;
|
||||
else if (src_format == GDK_MEMORY_R8G8B8 && dest_format == GDK_MEMORY_A8R8G8B8_PREMULTIPLIED)
|
||||
func = r8g8b8_to_a8r8g8b8;
|
||||
else if (src_format == GDK_MEMORY_B8G8R8 && dest_format == GDK_MEMORY_A8R8G8B8_PREMULTIPLIED)
|
||||
func = r8g8b8_to_a8b8g8r8;
|
||||
else if (src_format == GDK_MEMORY_R8G8B8 && dest_format == GDK_MEMORY_R8G8B8A8)
|
||||
func = r8g8b8_to_r8g8b8a8;
|
||||
else if (src_format == GDK_MEMORY_B8G8R8 && dest_format == GDK_MEMORY_R8G8B8A8)
|
||||
func = r8g8b8_to_b8g8r8a8;
|
||||
else if (src_format == GDK_MEMORY_R8G8B8 && dest_format == GDK_MEMORY_B8G8R8A8)
|
||||
func = r8g8b8_to_b8g8r8a8;
|
||||
else if (src_format == GDK_MEMORY_B8G8R8 && dest_format == GDK_MEMORY_B8G8R8A8)
|
||||
func = r8g8b8_to_r8g8b8a8;
|
||||
else if (src_format == GDK_MEMORY_R8G8B8 && dest_format == GDK_MEMORY_A8R8G8B8)
|
||||
func = r8g8b8_to_a8r8g8b8;
|
||||
else if (src_format == GDK_MEMORY_B8G8R8 && dest_format == GDK_MEMORY_A8R8G8B8)
|
||||
func = r8g8b8_to_a8b8g8r8;
|
||||
|
||||
if (func != NULL)
|
||||
stride = desc->bytes_per_pixel * width;
|
||||
if (stride == src_stride && stride == dest_stride)
|
||||
{
|
||||
memcpy (dest_data, src_data, stride * height);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
func (dest_data, src_data, width);
|
||||
memcpy (dest_data + y * dest_stride,
|
||||
src_data + y * src_stride,
|
||||
stride);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_memory_convert_no_transform (guchar *dest_data,
|
||||
gsize dest_stride,
|
||||
GdkMemoryFormat dest_format,
|
||||
const guchar *src_data,
|
||||
gsize src_stride,
|
||||
GdkMemoryFormat src_format,
|
||||
gsize width,
|
||||
gsize height)
|
||||
{
|
||||
if (dest_format == src_format)
|
||||
{
|
||||
gdk_memory_convert_same_format (dest_data, dest_stride,
|
||||
src_data, src_stride,
|
||||
width, height,
|
||||
dest_format);
|
||||
}
|
||||
else
|
||||
{
|
||||
const GdkMemoryFormatDescription *dest_desc = &memory_formats[dest_format];
|
||||
const GdkMemoryFormatDescription *src_desc = &memory_formats[src_format];
|
||||
float *tmp;
|
||||
gsize y;
|
||||
|
||||
tmp = g_new (float, width * 4);
|
||||
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
src_desc->to_float (tmp, src_data, width);
|
||||
if (src_desc->alpha == GDK_MEMORY_ALPHA_PREMULTIPLIED && dest_desc->alpha == GDK_MEMORY_ALPHA_STRAIGHT)
|
||||
unpremultiply (tmp, width);
|
||||
else if (src_desc->alpha == GDK_MEMORY_ALPHA_STRAIGHT && dest_desc->alpha != GDK_MEMORY_ALPHA_STRAIGHT)
|
||||
premultiply (tmp, width);
|
||||
dest_desc->from_float (dest_data, tmp, width);
|
||||
src_data += src_stride;
|
||||
dest_data += dest_stride;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
tmp = g_new (float, width * 4);
|
||||
g_free (tmp);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_memory_convert_transform (guchar *dest_data,
|
||||
gsize dest_stride,
|
||||
GdkMemoryFormat dest_format,
|
||||
GdkColorProfile *dest_profile,
|
||||
const guchar *src_data,
|
||||
gsize src_stride,
|
||||
GdkMemoryFormat src_format,
|
||||
GdkColorProfile *src_profile,
|
||||
gsize width,
|
||||
gsize height)
|
||||
{
|
||||
const GdkMemoryFormatDescription *dest_desc = &memory_formats[dest_format];
|
||||
const GdkMemoryFormatDescription *src_desc = &memory_formats[src_format];
|
||||
cmsHTRANSFORM transform;
|
||||
guchar *src_tmp, *dest_tmp;
|
||||
gsize y;
|
||||
|
||||
transform = gdk_color_profile_lookup_transform (src_profile,
|
||||
src_desc->lcms.type,
|
||||
dest_profile,
|
||||
dest_desc->lcms.type);
|
||||
|
||||
if (src_desc->to_lcms)
|
||||
src_tmp = g_malloc_n (src_desc->lcms.bpp, width);
|
||||
else
|
||||
src_tmp = NULL;
|
||||
if (dest_desc->from_lcms)
|
||||
dest_tmp = g_malloc_n (dest_desc->lcms.bpp, width);
|
||||
else
|
||||
dest_tmp = NULL;
|
||||
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
src_desc->to_float (tmp, src_data, width);
|
||||
if (src_desc->alpha == GDK_MEMORY_ALPHA_PREMULTIPLIED && dest_desc->alpha == GDK_MEMORY_ALPHA_STRAIGHT)
|
||||
unpremultiply (tmp, width);
|
||||
else if (src_desc->alpha == GDK_MEMORY_ALPHA_STRAIGHT && dest_desc->alpha != GDK_MEMORY_ALPHA_STRAIGHT)
|
||||
premultiply (tmp, width);
|
||||
dest_desc->from_float (dest_data, tmp, width);
|
||||
if (src_desc->to_lcms)
|
||||
src_desc->to_lcms (src_tmp, src_data, width);
|
||||
|
||||
cmsDoTransform (transform,
|
||||
src_tmp ? src_tmp : src_data,
|
||||
dest_tmp ? dest_tmp : dest_data,
|
||||
width);
|
||||
|
||||
if (dest_desc->from_lcms)
|
||||
dest_desc->from_lcms (dest_data, dest_tmp, width);
|
||||
|
||||
src_data += src_stride;
|
||||
dest_data += dest_stride;
|
||||
}
|
||||
|
||||
g_free (tmp);
|
||||
g_free (src_tmp);
|
||||
g_free (dest_tmp);
|
||||
}
|
||||
|
||||
void
|
||||
gdk_memory_convert (guchar *dest_data,
|
||||
gsize dest_stride,
|
||||
GdkMemoryFormat dest_format,
|
||||
GdkColorProfile *dest_profile,
|
||||
const guchar *src_data,
|
||||
gsize src_stride,
|
||||
GdkMemoryFormat src_format,
|
||||
GdkColorProfile *src_profile,
|
||||
gsize width,
|
||||
gsize height)
|
||||
{
|
||||
G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME;
|
||||
gboolean need_transform;
|
||||
|
||||
g_assert (dest_format < GDK_MEMORY_N_FORMATS);
|
||||
g_assert (src_format < GDK_MEMORY_N_FORMATS);
|
||||
|
||||
need_transform = !gdk_color_profile_equal (src_profile, dest_profile);
|
||||
if (!need_transform)
|
||||
{
|
||||
gdk_memory_convert_no_transform (dest_data, dest_stride, dest_format,
|
||||
src_data, src_stride, src_format,
|
||||
width, height);
|
||||
}
|
||||
else
|
||||
{
|
||||
gdk_memory_convert_transform (dest_data, dest_stride, dest_format, dest_profile,
|
||||
src_data, src_stride, src_format, src_profile,
|
||||
width, height);
|
||||
}
|
||||
|
||||
gdk_profiler_end_markf (start_time, "memory convert", "%zu pixels %s => %s%s",
|
||||
width * height,
|
||||
memory_formats[src_format].name,
|
||||
memory_formats[dest_format].name,
|
||||
need_transform ? " transformed" : "");
|
||||
}
|
||||
|
@@ -21,6 +21,7 @@
|
||||
#define __GDK_MEMORY_CONVERT_PRIVATE_H__
|
||||
|
||||
#include "gdkenums.h"
|
||||
#include "gdkcolorprofile.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@@ -43,9 +44,11 @@ gboolean gdk_memory_format_gl_format (GdkMemoryFormat
|
||||
void gdk_memory_convert (guchar *dest_data,
|
||||
gsize dest_stride,
|
||||
GdkMemoryFormat dest_format,
|
||||
GdkColorProfile *dest_profile,
|
||||
const guchar *src_data,
|
||||
gsize src_stride,
|
||||
GdkMemoryFormat src_format,
|
||||
GdkColorProfile *src_profile,
|
||||
gsize width,
|
||||
gsize height);
|
||||
|
||||
|
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "gdkmemorytextureprivate.h"
|
||||
|
||||
#include "gdkcolorprofileprivate.h"
|
||||
#include "gdkmemoryformatprivate.h"
|
||||
#include "gsk/gl/fp16private.h"
|
||||
|
||||
@@ -58,6 +59,7 @@ gdk_memory_texture_dispose (GObject *object)
|
||||
static void
|
||||
gdk_memory_texture_download (GdkTexture *texture,
|
||||
GdkMemoryFormat format,
|
||||
GdkColorProfile *profile,
|
||||
guchar *data,
|
||||
gsize stride)
|
||||
{
|
||||
@@ -65,9 +67,11 @@ gdk_memory_texture_download (GdkTexture *texture,
|
||||
|
||||
gdk_memory_convert (data, stride,
|
||||
format,
|
||||
profile,
|
||||
(guchar *) g_bytes_get_data (self->bytes, NULL),
|
||||
self->stride,
|
||||
texture->format,
|
||||
gdk_texture_get_color_profile (texture),
|
||||
gdk_texture_get_width (texture),
|
||||
gdk_texture_get_height (texture));
|
||||
}
|
||||
@@ -136,6 +140,9 @@ gdk_memory_sanitize (GBytes *bytes,
|
||||
* The `GBytes` must contain @stride × @height pixels
|
||||
* in the given format.
|
||||
*
|
||||
* This function calls [ctor@Gdk.MemoryTexture.new_with_color_profile]
|
||||
* with the sRGB profile.
|
||||
*
|
||||
* Returns: (type GdkMemoryTexture): A newly-created `GdkTexture`
|
||||
*/
|
||||
GdkTexture *
|
||||
@@ -145,18 +152,58 @@ gdk_memory_texture_new (int width,
|
||||
GBytes *bytes,
|
||||
gsize stride)
|
||||
{
|
||||
GdkMemoryTexture *self;
|
||||
|
||||
g_return_val_if_fail (width > 0, NULL);
|
||||
g_return_val_if_fail (height > 0, NULL);
|
||||
g_return_val_if_fail (bytes != NULL, NULL);
|
||||
g_return_val_if_fail (stride >= width * gdk_memory_format_bytes_per_pixel (format), NULL);
|
||||
|
||||
return gdk_memory_texture_new_with_color_profile (width, height,
|
||||
format,
|
||||
gdk_color_profile_get_srgb (),
|
||||
bytes, stride);
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_memory_texture_new_with_color_profile:
|
||||
* @width: the width of the texture
|
||||
* @height: the height of the texture
|
||||
* @format: the format of the data
|
||||
* @profile: a `GdkColorProfile`
|
||||
* @bytes: the `GBytes` containing the pixel data
|
||||
* @stride: rowstride for the data
|
||||
*
|
||||
* Creates a new texture for a blob of image data with a given color profile.
|
||||
*
|
||||
* The `GBytes` must contain @stride x @height pixels
|
||||
* in the given format.
|
||||
*
|
||||
* Returns: A newly-created `GdkTexture`
|
||||
*
|
||||
* Since: 4.8
|
||||
*/
|
||||
GdkTexture *
|
||||
gdk_memory_texture_new_with_color_profile (int width,
|
||||
int height,
|
||||
GdkMemoryFormat format,
|
||||
GdkColorProfile *profile,
|
||||
GBytes *bytes,
|
||||
gsize stride)
|
||||
{
|
||||
GdkMemoryTexture *self;
|
||||
|
||||
g_return_val_if_fail (width > 0, NULL);
|
||||
g_return_val_if_fail (height > 0, NULL);
|
||||
g_return_val_if_fail (GDK_IS_COLOR_PROFILE (profile), NULL);
|
||||
g_return_val_if_fail (bytes != NULL, NULL);
|
||||
g_return_val_if_fail (stride >= width * gdk_memory_format_bytes_per_pixel (format), NULL);
|
||||
g_return_val_if_fail (gdk_color_profile_supports_memory_format (profile, format), NULL);
|
||||
|
||||
bytes = gdk_memory_sanitize (bytes, width, height, format, stride, &stride);
|
||||
|
||||
self = g_object_new (GDK_TYPE_MEMORY_TEXTURE,
|
||||
"width", width,
|
||||
"height", height,
|
||||
"color-profile", profile,
|
||||
NULL);
|
||||
|
||||
GDK_TEXTURE (self)->format = format;
|
||||
@@ -178,15 +225,15 @@ gdk_memory_texture_new_subtexture (GdkMemoryTexture *source,
|
||||
GBytes *bytes;
|
||||
|
||||
g_return_val_if_fail (GDK_IS_MEMORY_TEXTURE (source), NULL);
|
||||
g_return_val_if_fail (x < 0 || x >= GDK_TEXTURE (source)->width, NULL);
|
||||
g_return_val_if_fail (y < 0 || y >= GDK_TEXTURE (source)->height, NULL);
|
||||
g_return_val_if_fail (width <= 0 || x + width > GDK_TEXTURE (source)->width, NULL);
|
||||
g_return_val_if_fail (height <= 0 || y + height > GDK_TEXTURE (source)->height, NULL);
|
||||
g_return_val_if_fail (x >= 0 || x < GDK_TEXTURE (source)->width, NULL);
|
||||
g_return_val_if_fail (y >= 0 || y < GDK_TEXTURE (source)->height, NULL);
|
||||
g_return_val_if_fail (width > 0 || x + width <= GDK_TEXTURE (source)->width, NULL);
|
||||
g_return_val_if_fail (height > 0 || y + height <= GDK_TEXTURE (source)->height, NULL);
|
||||
|
||||
texture = GDK_TEXTURE (source);
|
||||
bpp = gdk_memory_format_bytes_per_pixel (texture->format);
|
||||
offset = y * source->stride + x * bpp;
|
||||
size = source->stride * (height - 1) + x * bpp;
|
||||
size = source->stride * (height - 1) + width * bpp;
|
||||
bytes = g_bytes_new_from_bytes (source->bytes, offset, size);
|
||||
|
||||
result = gdk_memory_texture_new (texture->width,
|
||||
@@ -201,7 +248,8 @@ gdk_memory_texture_new_subtexture (GdkMemoryTexture *source,
|
||||
|
||||
GdkMemoryTexture *
|
||||
gdk_memory_texture_from_texture (GdkTexture *texture,
|
||||
GdkMemoryFormat format)
|
||||
GdkMemoryFormat format,
|
||||
GdkColorProfile *profile)
|
||||
{
|
||||
GdkTexture *result;
|
||||
GBytes *bytes;
|
||||
@@ -214,20 +262,22 @@ gdk_memory_texture_from_texture (GdkTexture *texture,
|
||||
{
|
||||
GdkMemoryTexture *memtex = GDK_MEMORY_TEXTURE (texture);
|
||||
|
||||
if (gdk_texture_get_format (texture) == format)
|
||||
if (gdk_texture_get_format (texture) == format &&
|
||||
gdk_texture_get_color_profile (texture) == profile)
|
||||
return g_object_ref (memtex);
|
||||
}
|
||||
|
||||
stride = texture->width * gdk_memory_format_bytes_per_pixel (format);
|
||||
data = g_malloc_n (stride, texture->height);
|
||||
|
||||
gdk_texture_do_download (texture, format, data, stride);
|
||||
gdk_texture_do_download (texture, format, profile, data, stride);
|
||||
bytes = g_bytes_new_take (data, stride);
|
||||
result = gdk_memory_texture_new (texture->width,
|
||||
texture->height,
|
||||
format,
|
||||
bytes,
|
||||
stride);
|
||||
result = gdk_memory_texture_new_with_color_profile (texture->width,
|
||||
texture->height,
|
||||
format,
|
||||
profile,
|
||||
bytes,
|
||||
stride);
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
return GDK_MEMORY_TEXTURE (result);
|
||||
|
@@ -26,6 +26,7 @@
|
||||
|
||||
#include <gdk/gdkenums.h>
|
||||
#include <gdk/gdktexture.h>
|
||||
#include <gdk/gdkcolorprofile.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@@ -68,6 +69,14 @@ GdkTexture * gdk_memory_texture_new (int
|
||||
GdkMemoryFormat format,
|
||||
GBytes *bytes,
|
||||
gsize stride);
|
||||
GDK_AVAILABLE_IN_4_8
|
||||
GdkTexture * gdk_memory_texture_new_with_color_profile
|
||||
(int width,
|
||||
int height,
|
||||
GdkMemoryFormat format,
|
||||
GdkColorProfile *profile,
|
||||
GBytes *bytes,
|
||||
gsize stride);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
@@ -30,7 +30,8 @@ G_BEGIN_DECLS
|
||||
#define GDK_MEMORY_GDK_PIXBUF_ALPHA GDK_MEMORY_R8G8B8A8
|
||||
|
||||
GdkMemoryTexture * gdk_memory_texture_from_texture (GdkTexture *texture,
|
||||
GdkMemoryFormat format);
|
||||
GdkMemoryFormat format,
|
||||
GdkColorProfile *profile);
|
||||
GdkTexture * gdk_memory_texture_new_subtexture (GdkMemoryTexture *texture,
|
||||
int x,
|
||||
int y,
|
||||
|
@@ -245,7 +245,8 @@ gdk_pixbuf_get_from_texture (GdkTexture *texture)
|
||||
|
||||
memtex = gdk_memory_texture_from_texture (texture,
|
||||
alpha ? GDK_MEMORY_GDK_PIXBUF_ALPHA
|
||||
: GDK_MEMORY_GDK_PIXBUF_OPAQUE);
|
||||
: GDK_MEMORY_GDK_PIXBUF_OPAQUE,
|
||||
gdk_color_profile_get_srgb ());
|
||||
|
||||
return gdk_pixbuf_new_from_data (gdk_memory_texture_get_data (memtex),
|
||||
GDK_COLORSPACE_RGB,
|
||||
|
@@ -30,6 +30,8 @@
|
||||
#include "gdksurface.h"
|
||||
|
||||
#include "gdk-private.h"
|
||||
#include "gdkcairo.h"
|
||||
#include "gdkcolorprofile.h"
|
||||
#include "gdkcontentprovider.h"
|
||||
#include "gdkdeviceprivate.h"
|
||||
#include "gdkdisplayprivate.h"
|
||||
@@ -88,13 +90,14 @@ enum {
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_COLOR_PROFILE,
|
||||
PROP_CURSOR,
|
||||
PROP_DISPLAY,
|
||||
PROP_FRAME_CLOCK,
|
||||
PROP_MAPPED,
|
||||
PROP_WIDTH,
|
||||
PROP_HEIGHT,
|
||||
PROP_MAPPED,
|
||||
PROP_SCALE_FACTOR,
|
||||
PROP_WIDTH,
|
||||
LAST_PROP
|
||||
};
|
||||
|
||||
@@ -485,6 +488,8 @@ gdk_surface_init (GdkSurface *surface)
|
||||
|
||||
surface->alpha = 255;
|
||||
|
||||
surface->color_profile = g_object_ref (gdk_color_profile_get_srgb ());
|
||||
|
||||
surface->device_cursor = g_hash_table_new_full (NULL, NULL,
|
||||
NULL, g_object_unref);
|
||||
}
|
||||
@@ -500,6 +505,26 @@ gdk_surface_class_init (GdkSurfaceClass *klass)
|
||||
|
||||
klass->beep = gdk_surface_real_beep;
|
||||
|
||||
/**
|
||||
* GdkSurface:color-profile: (attributes org.gtk.Property.get=gdk_surface_get_color_profile)
|
||||
*
|
||||
* The preferred color profile for rendering to the surface
|
||||
*
|
||||
* This profile is negotiated between GTK and the compositor.
|
||||
*
|
||||
* The profile may change as the surface gets moved around - for example to different
|
||||
* monitors or when the compositor gets reconfigured. As long as the surface isn't show, the
|
||||
* profile may not represent the actual color profile that is going to be used.
|
||||
*
|
||||
* Since: 4.8
|
||||
*/
|
||||
properties[PROP_COLOR_PROFILE] =
|
||||
g_param_spec_object ("color-profile",
|
||||
P_("Color profile"),
|
||||
P_("The preferred color profile of the surface"),
|
||||
GDK_TYPE_COLOR_PROFILE,
|
||||
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GdkSurface:cursor: (attributes org.gtk.Property.get=gdk_surface_get_cursor org.gtk.Property.set=gdk_surface_set_cursor)
|
||||
*
|
||||
@@ -732,6 +757,7 @@ gdk_surface_finalize (GObject *object)
|
||||
g_clear_object (&surface->cursor);
|
||||
g_clear_pointer (&surface->device_cursor, g_hash_table_destroy);
|
||||
g_clear_pointer (&surface->devices_inside, g_list_free);
|
||||
g_clear_object (&surface->color_profile);
|
||||
|
||||
g_clear_object (&surface->display);
|
||||
|
||||
@@ -786,6 +812,10 @@ gdk_surface_get_property (GObject *object,
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_COLOR_PROFILE:
|
||||
g_value_set_object (value, gdk_surface_get_color_profile (surface));
|
||||
break;
|
||||
|
||||
case PROP_CURSOR:
|
||||
g_value_set_object (value, gdk_surface_get_cursor (surface));
|
||||
break;
|
||||
@@ -2054,6 +2084,40 @@ gdk_surface_get_height (GdkSurface *surface)
|
||||
return surface->height;
|
||||
}
|
||||
|
||||
void
|
||||
gdk_surface_set_color_profile (GdkSurface *self,
|
||||
GdkColorProfile *color_profile)
|
||||
{
|
||||
/* This way we support unsetting, too */
|
||||
if (GDK_DISPLAY_DEBUG_CHECK (self->display, SRGB))
|
||||
color_profile = gdk_color_profile_get_srgb();
|
||||
|
||||
if (gdk_color_profile_equal (self->color_profile, color_profile))
|
||||
return;
|
||||
|
||||
g_set_object (&self->color_profile, color_profile);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_COLOR_PROFILE]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_surface_get_color_profile: (attributes org.gtk.Method.get_property=color-profile)
|
||||
* @self: a `GdkSurface`
|
||||
*
|
||||
* Returns the preferred color profile for rendering to the given @surface.
|
||||
*
|
||||
* Returns: (transfer none): The color profile of @surface
|
||||
*
|
||||
* Since: 4.8
|
||||
*/
|
||||
GdkColorProfile *
|
||||
gdk_surface_get_color_profile (GdkSurface *self)
|
||||
{
|
||||
g_return_val_if_fail (GDK_IS_SURFACE (self), gdk_color_profile_get_srgb ());
|
||||
|
||||
return self->color_profile;
|
||||
}
|
||||
|
||||
/*
|
||||
* gdk_surface_get_origin:
|
||||
* @surface: a `GdkSurface`
|
||||
@@ -2374,7 +2438,7 @@ _gdk_windowing_got_event (GdkDisplay *display,
|
||||
* with it.
|
||||
*/
|
||||
cairo_surface_t *
|
||||
gdk_surface_create_similar_surface (GdkSurface * surface,
|
||||
gdk_surface_create_similar_surface (GdkSurface * surface,
|
||||
cairo_content_t content,
|
||||
int width,
|
||||
int height)
|
||||
@@ -2390,6 +2454,7 @@ gdk_surface_create_similar_surface (GdkSurface * surface,
|
||||
content == CAIRO_CONTENT_ALPHA ? CAIRO_FORMAT_A8 : CAIRO_FORMAT_ARGB32,
|
||||
width * scale, height * scale);
|
||||
cairo_surface_set_device_scale (similar_surface, scale, scale);
|
||||
gdk_cairo_surface_set_color_profile (similar_surface, gdk_surface_get_color_profile (surface));
|
||||
|
||||
return similar_surface;
|
||||
}
|
||||
|
@@ -86,17 +86,19 @@ GDK_AVAILABLE_IN_ALL
|
||||
GdkCursor *gdk_surface_get_device_cursor (GdkSurface *surface,
|
||||
GdkDevice *device);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
int gdk_surface_get_width (GdkSurface *surface);
|
||||
int gdk_surface_get_width (GdkSurface *surface);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
int gdk_surface_get_height (GdkSurface *surface);
|
||||
int gdk_surface_get_height (GdkSurface *surface);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gdk_surface_translate_coordinates (GdkSurface *from,
|
||||
GdkSurface *to,
|
||||
double *x,
|
||||
double *y);
|
||||
int gdk_surface_get_scale_factor (GdkSurface *surface);
|
||||
GDK_AVAILABLE_IN_4_8
|
||||
GdkColorProfile * gdk_surface_get_color_profile (GdkSurface *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gdk_surface_translate_coordinates (GdkSurface *from,
|
||||
GdkSurface *to,
|
||||
double *x,
|
||||
double *y);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
int gdk_surface_get_scale_factor (GdkSurface *surface);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gdk_surface_get_device_position (GdkSurface *surface,
|
||||
|
@@ -52,6 +52,7 @@ struct _GdkSurface
|
||||
int y;
|
||||
|
||||
GdkGLContext *gl_paint_context;
|
||||
GdkColorProfile *color_profile;
|
||||
|
||||
cairo_region_t *update_area;
|
||||
guint update_freeze_count;
|
||||
@@ -171,6 +172,8 @@ void gdk_surface_set_state (GdkSurface *surface,
|
||||
|
||||
void gdk_surface_set_is_mapped (GdkSurface *surface,
|
||||
gboolean is_mapped);
|
||||
void gdk_surface_set_color_profile (GdkSurface *self,
|
||||
GdkColorProfile *color_profile);
|
||||
|
||||
GdkMonitor * gdk_surface_get_layout_monitor (GdkSurface *surface,
|
||||
GdkPopupLayout *layout,
|
||||
|
183
gdk/gdktexture.c
@@ -39,7 +39,10 @@
|
||||
|
||||
#include "gdktextureprivate.h"
|
||||
|
||||
#include "gdkcairo.h"
|
||||
#include "gdkcolorprofile.h"
|
||||
#include "gdkintl.h"
|
||||
#include "gdkmemoryformatprivate.h"
|
||||
#include "gdkmemorytextureprivate.h"
|
||||
#include "gdkpaintable.h"
|
||||
#include "gdksnapshot.h"
|
||||
@@ -59,8 +62,9 @@ gtk_snapshot_append_texture (GdkSnapshot *snapshot,
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_WIDTH,
|
||||
PROP_COLOR_PROFILE,
|
||||
PROP_HEIGHT,
|
||||
PROP_WIDTH,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
@@ -223,6 +227,7 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GdkTexture, gdk_texture, G_TYPE_OBJECT,
|
||||
static void
|
||||
gdk_texture_default_download (GdkTexture *texture,
|
||||
GdkMemoryFormat format,
|
||||
GdkColorProfile *profile,
|
||||
guchar *data,
|
||||
gsize stride)
|
||||
{
|
||||
@@ -239,14 +244,20 @@ gdk_texture_set_property (GObject *gobject,
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_WIDTH:
|
||||
self->width = g_value_get_int (value);
|
||||
case PROP_COLOR_PROFILE:
|
||||
self->color_profile = g_value_dup_object (value);
|
||||
if (self->color_profile == NULL)
|
||||
self->color_profile = g_object_ref (gdk_color_profile_get_srgb ());
|
||||
break;
|
||||
|
||||
case PROP_HEIGHT:
|
||||
self->height = g_value_get_int (value);
|
||||
break;
|
||||
|
||||
case PROP_WIDTH:
|
||||
self->width = g_value_get_int (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
break;
|
||||
@@ -263,14 +274,18 @@ gdk_texture_get_property (GObject *gobject,
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_WIDTH:
|
||||
g_value_set_int (value, self->width);
|
||||
case PROP_COLOR_PROFILE:
|
||||
g_value_set_object (value, self->color_profile);
|
||||
break;
|
||||
|
||||
case PROP_HEIGHT:
|
||||
g_value_set_int (value, self->height);
|
||||
break;
|
||||
|
||||
case PROP_WIDTH:
|
||||
g_value_set_int (value, self->width);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||
break;
|
||||
@@ -283,6 +298,7 @@ gdk_texture_dispose (GObject *object)
|
||||
GdkTexture *self = GDK_TEXTURE (object);
|
||||
|
||||
gdk_texture_clear_render_data (self);
|
||||
g_clear_object (&self->color_profile);
|
||||
|
||||
G_OBJECT_CLASS (gdk_texture_parent_class)->dispose (object);
|
||||
}
|
||||
@@ -299,14 +315,31 @@ gdk_texture_class_init (GdkTextureClass *klass)
|
||||
gobject_class->dispose = gdk_texture_dispose;
|
||||
|
||||
/**
|
||||
* GdkTexture:width: (attributes org.gtk.Property.get=gdk_texture_get_width)
|
||||
* GdkTexture:color-profile: (attributes org.gtk.Property.get=gdk_texture_get_color_profile)
|
||||
*
|
||||
* The width of the texture, in pixels.
|
||||
* The color profile associated with texture.
|
||||
*
|
||||
* Since: 4.8
|
||||
*/
|
||||
properties[PROP_WIDTH] =
|
||||
g_param_spec_int ("width",
|
||||
"Width",
|
||||
"The width of the texture",
|
||||
properties[PROP_COLOR_PROFILE] =
|
||||
g_param_spec_object ("color-profile",
|
||||
P_("Color Profile"),
|
||||
P_("The associated color profile"),
|
||||
GDK_TYPE_COLOR_PROFILE,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS |
|
||||
G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GdkTexture:height: (attributes org.gtk.Property.get=gdk_texture_get_height)
|
||||
*
|
||||
* The height of the texture, in pixels.
|
||||
*/
|
||||
properties[PROP_HEIGHT] =
|
||||
g_param_spec_int ("height",
|
||||
P_("Height"),
|
||||
P_("The height of the texture"),
|
||||
1,
|
||||
G_MAXINT,
|
||||
1,
|
||||
@@ -316,14 +349,14 @@ gdk_texture_class_init (GdkTextureClass *klass)
|
||||
G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GdkTexture:height: (attributes org.gtk.Property.get=gdk_texture_get_height)
|
||||
* GdkTexture:width: (attributes org.gtk.Property.get=gdk_texture_get_width)
|
||||
*
|
||||
* The height of the texture, in pixels.
|
||||
* The width of the texture, in pixels.
|
||||
*/
|
||||
properties[PROP_HEIGHT] =
|
||||
g_param_spec_int ("height",
|
||||
"Height",
|
||||
"The height of the texture",
|
||||
properties[PROP_WIDTH] =
|
||||
g_param_spec_int ("width",
|
||||
P_("Width"),
|
||||
P_("The width of the texture"),
|
||||
1,
|
||||
G_MAXINT,
|
||||
1,
|
||||
@@ -365,18 +398,43 @@ gdk_texture_new_for_surface (cairo_surface_t *surface)
|
||||
* cairo_image_surface_get_stride (surface),
|
||||
(GDestroyNotify) cairo_surface_destroy,
|
||||
cairo_surface_reference (surface));
|
||||
|
||||
texture = gdk_memory_texture_new (cairo_image_surface_get_width (surface),
|
||||
cairo_image_surface_get_height (surface),
|
||||
GDK_MEMORY_DEFAULT,
|
||||
bytes,
|
||||
cairo_image_surface_get_stride (surface));
|
||||
|
||||
texture = gdk_memory_texture_new_with_color_profile (cairo_image_surface_get_width (surface),
|
||||
cairo_image_surface_get_height (surface),
|
||||
GDK_MEMORY_DEFAULT,
|
||||
gdk_cairo_surface_get_color_profile (surface),
|
||||
bytes,
|
||||
cairo_image_surface_get_stride (surface));
|
||||
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
static GdkColorProfile *
|
||||
gdk_color_profile_from_pixbuf (GdkPixbuf *pixbuf)
|
||||
{
|
||||
const char *icc_profile_base64;
|
||||
GdkColorProfile *profile = NULL;
|
||||
|
||||
icc_profile_base64 = gdk_pixbuf_get_option (pixbuf, "icc-profile");
|
||||
if (icc_profile_base64)
|
||||
{
|
||||
guchar *icc_data;
|
||||
gsize icc_len;
|
||||
GBytes *bytes;
|
||||
|
||||
icc_data = g_base64_decode (icc_profile_base64, &icc_len);
|
||||
bytes = g_bytes_new_take (icc_data, icc_len);
|
||||
profile = gdk_color_profile_new_from_icc_bytes (bytes, NULL);
|
||||
g_bytes_unref (bytes);
|
||||
}
|
||||
if (!profile)
|
||||
profile = g_object_ref (gdk_color_profile_get_srgb ());
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_texture_new_for_pixbuf:
|
||||
* @pixbuf: a `GdkPixbuf`
|
||||
@@ -394,24 +452,31 @@ gdk_texture_new_for_pixbuf (GdkPixbuf *pixbuf)
|
||||
{
|
||||
GdkTexture *texture;
|
||||
GBytes *bytes;
|
||||
GdkColorProfile *profile;
|
||||
|
||||
g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
|
||||
|
||||
profile = gdk_color_profile_from_pixbuf (pixbuf);
|
||||
|
||||
bytes = g_bytes_new_with_free_func (gdk_pixbuf_get_pixels (pixbuf),
|
||||
gdk_pixbuf_get_height (pixbuf)
|
||||
* gdk_pixbuf_get_rowstride (pixbuf),
|
||||
g_object_unref,
|
||||
g_object_ref (pixbuf));
|
||||
texture = gdk_memory_texture_new (gdk_pixbuf_get_width (pixbuf),
|
||||
gdk_pixbuf_get_height (pixbuf),
|
||||
gdk_pixbuf_get_has_alpha (pixbuf)
|
||||
? GDK_MEMORY_GDK_PIXBUF_ALPHA
|
||||
: GDK_MEMORY_GDK_PIXBUF_OPAQUE,
|
||||
bytes,
|
||||
gdk_pixbuf_get_rowstride (pixbuf));
|
||||
|
||||
texture = gdk_memory_texture_new_with_color_profile (gdk_pixbuf_get_width (pixbuf),
|
||||
gdk_pixbuf_get_height (pixbuf),
|
||||
gdk_pixbuf_get_has_alpha (pixbuf)
|
||||
? GDK_MEMORY_GDK_PIXBUF_ALPHA
|
||||
: GDK_MEMORY_GDK_PIXBUF_OPAQUE,
|
||||
profile,
|
||||
bytes,
|
||||
gdk_pixbuf_get_rowstride (pixbuf));
|
||||
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
g_object_unref (profile);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
@@ -669,32 +734,73 @@ gdk_texture_get_height (GdkTexture *texture)
|
||||
return texture->height;
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_texture_get_color_profile: (attributes org.gtk.Method.get_property=color-profile)
|
||||
* @texture: a `GdkTexture`
|
||||
*
|
||||
* Returns the color profile associsated with @texture.
|
||||
*
|
||||
* Returns: (transfer none): the color profile of the `GdkTexture`
|
||||
*
|
||||
* Since: 4.8
|
||||
*/
|
||||
GdkColorProfile *
|
||||
gdk_texture_get_color_profile (GdkTexture *texture)
|
||||
{
|
||||
g_return_val_if_fail (GDK_IS_TEXTURE (texture), 0);
|
||||
|
||||
return texture->color_profile;
|
||||
}
|
||||
|
||||
void
|
||||
gdk_texture_do_download (GdkTexture *texture,
|
||||
GdkMemoryFormat format,
|
||||
GdkColorProfile *profile,
|
||||
guchar *data,
|
||||
gsize stride)
|
||||
{
|
||||
GDK_TEXTURE_GET_CLASS (texture)->download (texture, format, data,stride);
|
||||
GDK_TEXTURE_GET_CLASS (texture)->download (texture, format, profile, data, stride);
|
||||
}
|
||||
|
||||
/*<private>
|
||||
* gdk_texture_download_surface:
|
||||
* @texture: the texture to download
|
||||
* @profile: (nullable): The target color profile or %NULL for the default sRGB
|
||||
*
|
||||
* Downloads the texture into a newly created Cairo surface.
|
||||
*
|
||||
* Returns: A new Cairo surface.
|
||||
**/
|
||||
cairo_surface_t *
|
||||
gdk_texture_download_surface (GdkTexture *texture)
|
||||
gdk_texture_download_surface (GdkTexture *texture,
|
||||
GdkColorProfile *profile)
|
||||
{
|
||||
cairo_surface_t *surface;
|
||||
cairo_status_t surface_status;
|
||||
|
||||
|
||||
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
|
||||
texture->width, texture->height);
|
||||
if (profile != NULL)
|
||||
gdk_cairo_surface_set_color_profile (surface, profile);
|
||||
else
|
||||
profile = gdk_color_profile_get_srgb ();
|
||||
|
||||
surface_status = cairo_surface_status (surface);
|
||||
if (surface_status != CAIRO_STATUS_SUCCESS)
|
||||
g_warning ("%s: surface error: %s", __FUNCTION__,
|
||||
cairo_status_to_string (surface_status));
|
||||
{
|
||||
g_warning ("%s: surface error: %s", __FUNCTION__,
|
||||
cairo_status_to_string (surface_status));
|
||||
}
|
||||
else
|
||||
{
|
||||
gdk_texture_do_download (texture,
|
||||
GDK_MEMORY_DEFAULT,
|
||||
profile,
|
||||
cairo_image_surface_get_data (surface),
|
||||
cairo_image_surface_get_stride (surface));
|
||||
}
|
||||
|
||||
gdk_texture_download (texture,
|
||||
cairo_image_surface_get_data (surface),
|
||||
cairo_image_surface_get_stride (surface));
|
||||
cairo_surface_mark_dirty (surface);
|
||||
|
||||
return surface;
|
||||
@@ -738,6 +844,7 @@ gdk_texture_download (GdkTexture *texture,
|
||||
|
||||
gdk_texture_do_download (texture,
|
||||
GDK_MEMORY_DEFAULT,
|
||||
gdk_color_profile_get_srgb (),
|
||||
data,
|
||||
stride);
|
||||
}
|
||||
@@ -755,7 +862,7 @@ gdk_texture_set_render_data (GdkTexture *self,
|
||||
GDestroyNotify notify)
|
||||
{
|
||||
g_return_val_if_fail (data != NULL, FALSE);
|
||||
|
||||
|
||||
if (self->render_key != NULL)
|
||||
return FALSE;
|
||||
|
||||
|
@@ -84,6 +84,8 @@ GDK_AVAILABLE_IN_ALL
|
||||
int gdk_texture_get_width (GdkTexture *texture) G_GNUC_PURE;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
int gdk_texture_get_height (GdkTexture *texture) G_GNUC_PURE;
|
||||
GDK_AVAILABLE_IN_4_8
|
||||
GdkColorProfile * gdk_texture_get_color_profile (GdkTexture *texture) G_GNUC_PURE;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gdk_texture_download (GdkTexture *texture,
|
||||
|
@@ -18,6 +18,7 @@ struct _GdkTexture
|
||||
GdkMemoryFormat format;
|
||||
int width;
|
||||
int height;
|
||||
GdkColorProfile *color_profile;
|
||||
|
||||
gpointer render_key;
|
||||
gpointer render_data;
|
||||
@@ -30,6 +31,7 @@ struct _GdkTextureClass {
|
||||
/* mandatory: Download in the given format into data */
|
||||
void (* download) (GdkTexture *texture,
|
||||
GdkMemoryFormat format,
|
||||
GdkColorProfile *profile,
|
||||
guchar *data,
|
||||
gsize stride);
|
||||
};
|
||||
@@ -37,10 +39,12 @@ struct _GdkTextureClass {
|
||||
gboolean gdk_texture_can_load (GBytes *bytes);
|
||||
|
||||
GdkTexture * gdk_texture_new_for_surface (cairo_surface_t *surface);
|
||||
cairo_surface_t * gdk_texture_download_surface (GdkTexture *texture);
|
||||
cairo_surface_t * gdk_texture_download_surface (GdkTexture *texture,
|
||||
GdkColorProfile *profile);
|
||||
|
||||
void gdk_texture_do_download (GdkTexture *texture,
|
||||
GdkMemoryFormat format,
|
||||
GdkColorProfile *profile,
|
||||
guchar *data,
|
||||
gsize stride);
|
||||
GdkMemoryFormat gdk_texture_get_format (GdkTexture *self);
|
||||
|
@@ -73,6 +73,7 @@ typedef cairo_rectangle_int_t GdkRectangle;
|
||||
|
||||
/* Forward declarations of commonly used types */
|
||||
typedef struct _GdkRGBA GdkRGBA;
|
||||
typedef struct _GdkColorProfile GdkColorProfile;
|
||||
typedef struct _GdkContentFormats GdkContentFormats;
|
||||
typedef struct _GdkContentProvider GdkContentProvider;
|
||||
typedef struct _GdkCursor GdkCursor;
|
||||
|
@@ -21,9 +21,10 @@
|
||||
|
||||
#include "gdkjpegprivate.h"
|
||||
|
||||
#include "gdkcolorprofile.h"
|
||||
#include "gdkintl.h"
|
||||
#include "gdktexture.h"
|
||||
#include "gdkmemorytextureprivate.h"
|
||||
#include "gdktexture.h"
|
||||
|
||||
#include "gdkprofilerprivate.h"
|
||||
|
||||
@@ -143,9 +144,12 @@ gdk_load_jpeg (GBytes *input_bytes,
|
||||
guint width, height, stride;
|
||||
unsigned char *data;
|
||||
unsigned char *row[1];
|
||||
JOCTET *icc_data;
|
||||
unsigned int icc_len;
|
||||
GBytes *bytes;
|
||||
GdkTexture *texture;
|
||||
GdkMemoryFormat format;
|
||||
GdkColorProfile *color_profile;
|
||||
G_GNUC_UNUSED guint64 before = GDK_PROFILER_CURRENT_TIME;
|
||||
|
||||
info.err = jpeg_std_error (&jerr.pub);
|
||||
@@ -165,6 +169,9 @@ gdk_load_jpeg (GBytes *input_bytes,
|
||||
g_bytes_get_data (input_bytes, NULL),
|
||||
g_bytes_get_size (input_bytes));
|
||||
|
||||
/* save color profile */
|
||||
jpeg_save_markers (&info, JPEG_APP0 + 2, 0xFFFF);
|
||||
|
||||
jpeg_read_header (&info, TRUE);
|
||||
jpeg_start_decompress (&info);
|
||||
|
||||
@@ -222,19 +229,34 @@ gdk_load_jpeg (GBytes *input_bytes,
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
if (jpeg_read_icc_profile (&info, &icc_data, &icc_len))
|
||||
{
|
||||
GBytes *icc_bytes = g_bytes_new_with_free_func (icc_data, icc_len, free, icc_data);
|
||||
color_profile = gdk_color_profile_new_from_icc_bytes (icc_bytes, error);
|
||||
g_bytes_unref (icc_bytes);
|
||||
}
|
||||
else
|
||||
color_profile = g_object_ref (gdk_color_profile_get_srgb ());
|
||||
|
||||
jpeg_finish_decompress (&info);
|
||||
jpeg_destroy_decompress (&info);
|
||||
|
||||
bytes = g_bytes_new_take (data, stride * height);
|
||||
|
||||
texture = gdk_memory_texture_new (width, height,
|
||||
format,
|
||||
bytes, stride);
|
||||
if (color_profile)
|
||||
{
|
||||
texture = gdk_memory_texture_new_with_color_profile (width, height,
|
||||
format,
|
||||
color_profile,
|
||||
bytes, stride);
|
||||
}
|
||||
else
|
||||
texture = NULL;
|
||||
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
gdk_profiler_end_mark (before, "jpeg load", NULL);
|
||||
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
@@ -252,9 +274,12 @@ gdk_save_jpeg (GdkTexture *texture)
|
||||
gsize texstride;
|
||||
guchar *row;
|
||||
int width, height;
|
||||
GdkColorProfile *color_profile;
|
||||
GBytes *bytes;
|
||||
|
||||
width = gdk_texture_get_width (texture);
|
||||
height = gdk_texture_get_height (texture);
|
||||
color_profile = gdk_texture_get_color_profile (texture);
|
||||
|
||||
info.err = jpeg_std_error (&jerr.pub);
|
||||
jerr.pub.error_exit = fatal_error_handler;
|
||||
@@ -283,12 +308,19 @@ gdk_save_jpeg (GdkTexture *texture)
|
||||
jpeg_mem_dest (&info, &data, &size);
|
||||
|
||||
memtex = gdk_memory_texture_from_texture (texture,
|
||||
GDK_MEMORY_R8G8B8);
|
||||
GDK_MEMORY_R8G8B8,
|
||||
gdk_color_profile_get_srgb ());
|
||||
texdata = gdk_memory_texture_get_data (memtex);
|
||||
texstride = gdk_memory_texture_get_stride (memtex);
|
||||
|
||||
jpeg_start_compress (&info, TRUE);
|
||||
|
||||
bytes = gdk_color_profile_get_icc_profile (color_profile);
|
||||
jpeg_write_icc_profile (&info,
|
||||
g_bytes_get_data (bytes, NULL),
|
||||
g_bytes_get_size (bytes));
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
while (info.next_scanline < info.image_height)
|
||||
{
|
||||
row = (guchar *) texdata + info.next_scanline * texstride;
|
||||
|
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "gdkpngprivate.h"
|
||||
|
||||
#include "gdkcolorprofileprivate.h"
|
||||
#include "gdkintl.h"
|
||||
#include "gdkmemoryformatprivate.h"
|
||||
#include "gdkmemorytextureprivate.h"
|
||||
@@ -127,8 +128,102 @@ png_simple_warning_callback (png_structp png,
|
||||
{
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ Public API */
|
||||
static GdkColorProfile *
|
||||
gdk_png_get_color_profile (png_struct *png,
|
||||
png_info *info)
|
||||
{
|
||||
GdkColorProfile *profile;
|
||||
guchar *icc_data;
|
||||
png_uint_32 icc_len;
|
||||
char *name;
|
||||
double gamma;
|
||||
cmsCIExyY whitepoint;
|
||||
cmsCIExyYTRIPLE primaries;
|
||||
cmsToneCurve *curve;
|
||||
cmsHPROFILE lcms_profile;
|
||||
int intent;
|
||||
|
||||
if (png_get_iCCP (png, info, &name, NULL, &icc_data, &icc_len))
|
||||
{
|
||||
GBytes *bytes = g_bytes_new (icc_data, icc_len);
|
||||
|
||||
profile = gdk_color_profile_new_from_icc_bytes (bytes, NULL);
|
||||
g_bytes_unref (bytes);
|
||||
if (profile)
|
||||
return profile;
|
||||
}
|
||||
|
||||
if (png_get_sRGB (png, info, &intent))
|
||||
return g_object_ref (gdk_color_profile_get_srgb ());
|
||||
|
||||
/* If neither of those is valid, the result is sRGB */
|
||||
if (!png_get_valid (png, info, PNG_INFO_gAMA) &&
|
||||
!png_get_valid (png, info, PNG_INFO_cHRM))
|
||||
return g_object_ref (gdk_color_profile_get_srgb ());
|
||||
|
||||
if (!png_get_gAMA (png, info, &gamma))
|
||||
gamma = 2.4;
|
||||
|
||||
if (!png_get_cHRM (png, info,
|
||||
&whitepoint.x, &whitepoint.y,
|
||||
&primaries.Red.x, &primaries.Red.y,
|
||||
&primaries.Green.x, &primaries.Green.y,
|
||||
&primaries.Blue.x, &primaries.Blue.y))
|
||||
{
|
||||
if (gamma == 2.4)
|
||||
return g_object_ref (gdk_color_profile_get_srgb ());
|
||||
|
||||
whitepoint = (cmsCIExyY) { 0.3127, 0.3290, 1.0 };
|
||||
primaries = (cmsCIExyYTRIPLE) {
|
||||
{ 0.6400, 0.3300, 1.0 },
|
||||
{ 0.3000, 0.6000, 1.0 },
|
||||
{ 0.1500, 0.0600, 1.0 }
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
primaries.Red.Y = 1.0;
|
||||
primaries.Green.Y = 1.0;
|
||||
primaries.Blue.Y = 1.0;
|
||||
}
|
||||
|
||||
curve = cmsBuildGamma (NULL, 1.0 / gamma);
|
||||
lcms_profile = cmsCreateRGBProfile (&whitepoint,
|
||||
&primaries,
|
||||
(cmsToneCurve*[3]) { curve, curve, curve });
|
||||
profile = gdk_color_profile_new_from_lcms_profile (lcms_profile, NULL);
|
||||
/* FIXME: errors? */
|
||||
if (profile == NULL)
|
||||
profile = g_object_ref (gdk_color_profile_get_srgb ());
|
||||
cmsFreeToneCurve (curve);
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_png_set_color_profile (png_struct *png,
|
||||
png_info *info,
|
||||
GdkColorProfile *profile)
|
||||
{
|
||||
/* FIXME: allow deconstructing RGB profiles into gAMA and cHRM instead of
|
||||
* falling back to iCCP */
|
||||
if (profile == gdk_color_profile_get_srgb ())
|
||||
{
|
||||
png_set_sRGB_gAMA_and_cHRM (png, info, /* FIXME */ PNG_sRGB_INTENT_PERCEPTUAL);
|
||||
}
|
||||
else
|
||||
{
|
||||
GBytes *bytes = gdk_color_profile_get_icc_profile (profile);
|
||||
png_set_iCCP (png, info,
|
||||
"ICC profile",
|
||||
0,
|
||||
g_bytes_get_data (bytes, NULL),
|
||||
g_bytes_get_size (bytes));
|
||||
}
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ Public API */
|
||||
|
||||
GdkTexture *
|
||||
gdk_load_png (GBytes *bytes,
|
||||
@@ -144,6 +239,7 @@ gdk_load_png (GBytes *bytes,
|
||||
guchar *buffer = NULL;
|
||||
guchar **row_pointers = NULL;
|
||||
GBytes *out_bytes;
|
||||
GdkColorProfile *color_profile;
|
||||
GdkTexture *texture;
|
||||
int bpp;
|
||||
G_GNUC_UNUSED gint64 before = GDK_PROFILER_CURRENT_TIME;
|
||||
@@ -247,6 +343,8 @@ gdk_load_png (GBytes *bytes,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
color_profile = gdk_png_get_color_profile (png, info);
|
||||
|
||||
bpp = gdk_memory_format_bytes_per_pixel (format);
|
||||
stride = width * bpp;
|
||||
if (stride % 8)
|
||||
@@ -257,6 +355,7 @@ gdk_load_png (GBytes *bytes,
|
||||
|
||||
if (!buffer || !row_pointers)
|
||||
{
|
||||
g_object_unref (color_profile);
|
||||
g_free (buffer);
|
||||
g_free (row_pointers);
|
||||
png_destroy_read_struct (&png, &info, NULL);
|
||||
@@ -273,8 +372,11 @@ gdk_load_png (GBytes *bytes,
|
||||
png_read_end (png, info);
|
||||
|
||||
out_bytes = g_bytes_new_take (buffer, height * stride);
|
||||
texture = gdk_memory_texture_new (width, height, format, out_bytes, stride);
|
||||
texture = gdk_memory_texture_new_with_color_profile (width, height,
|
||||
format, color_profile,
|
||||
out_bytes, stride);
|
||||
g_bytes_unref (out_bytes);
|
||||
g_object_unref (color_profile);
|
||||
|
||||
g_free (row_pointers);
|
||||
png_destroy_read_struct (&png, &info, NULL);
|
||||
@@ -301,13 +403,17 @@ gdk_save_png (GdkTexture *texture)
|
||||
int y;
|
||||
GdkMemoryTexture *memtex;
|
||||
GdkMemoryFormat format;
|
||||
GdkColorProfile *color_profile;
|
||||
int png_format;
|
||||
int depth;
|
||||
|
||||
width = gdk_texture_get_width (texture);
|
||||
height = gdk_texture_get_height (texture);
|
||||
color_profile = gdk_texture_get_color_profile (texture);
|
||||
format = gdk_texture_get_format (texture);
|
||||
|
||||
memtex = gdk_memory_texture_from_texture (texture, format, color_profile);
|
||||
|
||||
switch (format)
|
||||
{
|
||||
case GDK_MEMORY_B8G8R8A8_PREMULTIPLIED:
|
||||
@@ -317,14 +423,22 @@ gdk_save_png (GdkTexture *texture)
|
||||
case GDK_MEMORY_A8R8G8B8:
|
||||
case GDK_MEMORY_R8G8B8A8:
|
||||
case GDK_MEMORY_A8B8G8R8:
|
||||
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
|
||||
format = GDK_MEMORY_R8G8B8A8;
|
||||
#elif G_BYTE_ORDER == G_BIG_ENDIAN
|
||||
format = GDK_MEMORY_A8B8G8R8;
|
||||
#endif
|
||||
png_format = PNG_COLOR_TYPE_RGB_ALPHA;
|
||||
depth = 8;
|
||||
break;
|
||||
|
||||
case GDK_MEMORY_R8G8B8:
|
||||
case GDK_MEMORY_B8G8R8:
|
||||
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
|
||||
format = GDK_MEMORY_R8G8B8;
|
||||
#elif G_BYTE_ORDER == G_BIG_ENDIAN
|
||||
format = GDK_MEMORY_B8G8R8;
|
||||
#endif
|
||||
png_format = PNG_COLOR_TYPE_RGB;
|
||||
depth = 8;
|
||||
break;
|
||||
@@ -369,8 +483,6 @@ gdk_save_png (GdkTexture *texture)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memtex = gdk_memory_texture_from_texture (texture, format);
|
||||
|
||||
if (sigsetjmp (png_jmpbuf (png), 1))
|
||||
{
|
||||
g_object_unref (memtex);
|
||||
@@ -387,6 +499,8 @@ gdk_save_png (GdkTexture *texture)
|
||||
PNG_COMPRESSION_TYPE_DEFAULT,
|
||||
PNG_FILTER_TYPE_DEFAULT);
|
||||
|
||||
gdk_png_set_color_profile (png, info, color_profile);
|
||||
|
||||
png_write_info (png, info);
|
||||
|
||||
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
|
||||
|
@@ -220,6 +220,44 @@ tiff_open_write (GBytes **result)
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ Color profile handling */
|
||||
|
||||
static GdkColorProfile *
|
||||
gdk_tiff_get_color_profile (TIFF *tiff)
|
||||
{
|
||||
const char *icc_data;
|
||||
guint icc_len;
|
||||
|
||||
if (TIFFGetField (tiff, TIFFTAG_ICCPROFILE, &icc_len, &icc_data))
|
||||
{
|
||||
GBytes *icc_bytes;
|
||||
GdkColorProfile *profile;
|
||||
|
||||
icc_bytes = g_bytes_new (icc_data, icc_len);
|
||||
profile = gdk_color_profile_new_from_icc_bytes (icc_bytes, NULL);
|
||||
g_bytes_unref (icc_bytes);
|
||||
|
||||
if (profile)
|
||||
return profile;
|
||||
}
|
||||
|
||||
return g_object_ref (gdk_color_profile_get_srgb ());
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_tiff_set_color_profile (TIFF *tiff,
|
||||
GdkColorProfile *profile)
|
||||
{
|
||||
GBytes *bytes = gdk_color_profile_get_icc_profile (profile);
|
||||
|
||||
TIFFSetField (tiff, TIFFTAG_ICCPROFILE,
|
||||
g_bytes_get_size (bytes),
|
||||
g_bytes_get_data (bytes, NULL));
|
||||
|
||||
g_bytes_unref (bytes);
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ Public API */
|
||||
|
||||
@@ -266,6 +304,7 @@ gdk_save_tiff (GdkTexture *texture)
|
||||
GBytes *result = NULL;
|
||||
GdkMemoryTexture *memtex;
|
||||
GdkMemoryFormat format;
|
||||
GdkColorProfile *color_profile;
|
||||
const FormatData *fdata = NULL;
|
||||
|
||||
tif = tiff_open_write (&result);
|
||||
@@ -273,6 +312,7 @@ gdk_save_tiff (GdkTexture *texture)
|
||||
width = gdk_texture_get_width (texture);
|
||||
height = gdk_texture_get_height (texture);
|
||||
format = gdk_texture_get_format (texture);
|
||||
color_profile = gdk_texture_get_color_profile (texture);
|
||||
fdata = &format_data[format];
|
||||
|
||||
if (fdata == NULL)
|
||||
@@ -292,7 +332,9 @@ gdk_save_tiff (GdkTexture *texture)
|
||||
TIFFSetField (tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
|
||||
TIFFSetField (tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
|
||||
|
||||
memtex = gdk_memory_texture_from_texture (texture, fdata->format);
|
||||
gdk_tiff_set_color_profile (tif, color_profile);
|
||||
|
||||
memtex = gdk_memory_texture_from_texture (texture, fdata->format, color_profile);
|
||||
data = gdk_memory_texture_get_data (memtex);
|
||||
stride = gdk_memory_texture_get_stride (memtex);
|
||||
|
||||
@@ -326,6 +368,7 @@ load_fallback (TIFF *tif,
|
||||
int width, height;
|
||||
guchar *data;
|
||||
GBytes *bytes;
|
||||
GdkColorProfile *profile;
|
||||
GdkTexture *texture;
|
||||
|
||||
TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &width);
|
||||
@@ -342,12 +385,15 @@ load_fallback (TIFF *tif,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
profile = gdk_tiff_get_color_profile (tif);
|
||||
|
||||
bytes = g_bytes_new_take (data, width * height * 4);
|
||||
|
||||
texture = gdk_memory_texture_new (width, height,
|
||||
GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
|
||||
bytes,
|
||||
width * 4);
|
||||
texture = gdk_memory_texture_new_with_color_profile (width, height,
|
||||
GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
|
||||
profile,
|
||||
bytes,
|
||||
width * 4);
|
||||
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
@@ -372,6 +418,7 @@ gdk_load_tiff (GBytes *input_bytes,
|
||||
gsize stride;
|
||||
int bpp;
|
||||
GBytes *bytes;
|
||||
GdkColorProfile *profile;
|
||||
GdkTexture *texture;
|
||||
G_GNUC_UNUSED gint64 before = GDK_PROFILER_CURRENT_TIME;
|
||||
|
||||
@@ -471,12 +518,14 @@ gdk_load_tiff (GBytes *input_bytes,
|
||||
line += stride;
|
||||
}
|
||||
|
||||
profile = gdk_tiff_get_color_profile (tif);
|
||||
|
||||
bpp = gdk_memory_format_bytes_per_pixel (format);
|
||||
bytes = g_bytes_new_take (data, width * height * bpp);
|
||||
|
||||
texture = gdk_memory_texture_new (width, height,
|
||||
format,
|
||||
bytes, width * bpp);
|
||||
texture = gdk_memory_texture_new_with_color_profile (width, height,
|
||||
format, profile,
|
||||
bytes, width * bpp);
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
TIFFClose (tif);
|
||||
|
@@ -4,6 +4,8 @@ gdk_public_sources = files([
|
||||
'gdkcairo.c',
|
||||
'gdkcairocontext.c',
|
||||
'gdkclipboard.c',
|
||||
'gdkcolor.c',
|
||||
'gdkcolorprofile.c',
|
||||
'gdkcontentdeserializer.c',
|
||||
'gdkcontentformats.c',
|
||||
'gdkcontentprovider.c',
|
||||
@@ -64,6 +66,7 @@ gdk_public_headers = files([
|
||||
'gdkcairo.h',
|
||||
'gdkcairocontext.h',
|
||||
'gdkclipboard.h',
|
||||
'gdkcolorprofile.h',
|
||||
'gdkcontentdeserializer.h',
|
||||
'gdkcontentformats.h',
|
||||
'gdkcontentprovider.h',
|
||||
@@ -205,6 +208,7 @@ gdk_deps = [
|
||||
fontconfig_dep,
|
||||
platform_gio_dep,
|
||||
pangocairo_dep,
|
||||
lcms2_dep,
|
||||
vulkan_dep,
|
||||
png_dep,
|
||||
tiff_dep,
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -1403,6 +1403,71 @@ gdk_x11_display_init_leader_surface (GdkX11Display *self)
|
||||
self->leader_window_title_set = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
voidXFree (gpointer data)
|
||||
{
|
||||
XFree (data);
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_x11_display_check_color_profile (GdkX11Display *self)
|
||||
{
|
||||
GdkDisplay *display = GDK_DISPLAY (self);
|
||||
GdkX11Screen *screen;
|
||||
char *atom_name;
|
||||
Atom type;
|
||||
int result;
|
||||
int format;
|
||||
gulong nitems;
|
||||
gulong bytes_after;
|
||||
guchar *data;
|
||||
GBytes *bytes;
|
||||
|
||||
screen = self->screen;
|
||||
if (screen->screen_num > 0)
|
||||
atom_name = g_strdup_printf ("_ICC_PROFILE_%d", screen->screen_num);
|
||||
else
|
||||
atom_name = g_strdup ("_ICC_PROFILE");
|
||||
|
||||
g_clear_object (&self->color_profile);
|
||||
self->color_profile = g_object_ref (gdk_color_profile_get_srgb ());
|
||||
|
||||
gdk_x11_display_error_trap_push (display);
|
||||
result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
|
||||
screen->xroot_window,
|
||||
gdk_x11_get_xatom_by_name_for_display (display, atom_name),
|
||||
0, G_MAXLONG, False, XA_CARDINAL, &type,
|
||||
&format, &nitems,
|
||||
&bytes_after, &data);
|
||||
gdk_x11_display_error_trap_pop_ignored (display);
|
||||
|
||||
g_free (atom_name);
|
||||
|
||||
if (result != Success || type != XA_CARDINAL || nitems <= 0)
|
||||
return;
|
||||
|
||||
switch (format)
|
||||
{
|
||||
case 8:
|
||||
bytes = g_bytes_new_with_free_func (data, nitems, voidXFree, data);
|
||||
break;
|
||||
case 16:
|
||||
bytes = g_bytes_new_with_free_func (data, sizeof (short) * nitems, voidXFree, data);
|
||||
break;
|
||||
case 32:
|
||||
bytes = g_bytes_new_with_free_func (data, sizeof (long) * nitems, voidXFree, data);
|
||||
break;
|
||||
default:
|
||||
XFree (data);
|
||||
return;
|
||||
}
|
||||
|
||||
g_clear_object (&self->color_profile);
|
||||
self->color_profile = gdk_color_profile_new_from_icc_bytes (bytes, NULL);
|
||||
if (!self->color_profile)
|
||||
self->color_profile = g_object_ref (gdk_color_profile_get_srgb ());
|
||||
}
|
||||
|
||||
/**
|
||||
* gdk_x11_display_open:
|
||||
* @display_name: (nullable): name of the X display.
|
||||
@@ -1470,6 +1535,9 @@ gdk_x11_display_open (const char *display_name)
|
||||
/* initialize the display's screens */
|
||||
display_x11->screen = _gdk_x11_screen_new (display, DefaultScreen (display_x11->xdisplay));
|
||||
|
||||
/* We want this for the leader surface already */
|
||||
gdk_x11_display_check_color_profile (display_x11);
|
||||
|
||||
/* If GL is available we want to pick better default/rgba visuals,
|
||||
* as we care about GLX details such as alpha/depth/stencil depth,
|
||||
* stereo and double buffering
|
||||
@@ -1915,15 +1983,17 @@ gdk_x11_display_ungrab (GdkDisplay *display)
|
||||
static void
|
||||
gdk_x11_display_dispose (GObject *object)
|
||||
{
|
||||
GdkX11Display *display_x11 = GDK_X11_DISPLAY (object);
|
||||
GdkX11Display *self = GDK_X11_DISPLAY (object);
|
||||
|
||||
if (display_x11->event_source)
|
||||
if (self->event_source)
|
||||
{
|
||||
g_source_destroy (display_x11->event_source);
|
||||
g_source_unref (display_x11->event_source);
|
||||
display_x11->event_source = NULL;
|
||||
g_source_destroy (self->event_source);
|
||||
g_source_unref (self->event_source);
|
||||
self->event_source = NULL;
|
||||
}
|
||||
|
||||
g_clear_object (&self->color_profile);
|
||||
|
||||
G_OBJECT_CLASS (gdk_x11_display_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -1011,6 +1011,8 @@ setup_toplevel_window (GdkSurface *surface,
|
||||
|
||||
/* This will set WM_CLIENT_MACHINE and WM_LOCALE_NAME */
|
||||
XSetWMProperties (xdisplay, xid, NULL, NULL, NULL, 0, NULL, NULL, NULL);
|
||||
|
||||
gdk_surface_set_color_profile (surface, GDK_X11_DISPLAY (display)->color_profile);
|
||||
|
||||
if (!gdk_running_in_sandbox ())
|
||||
{
|
||||
@@ -3180,7 +3182,7 @@ gdk_surface_update_icon (GdkSurface *surface,
|
||||
|
||||
toplevel->icon_pixmap = gdk_x11_surface_create_pixmap_surface (surface, width, height);
|
||||
|
||||
cairo_surface = gdk_texture_download_surface (best_icon);
|
||||
cairo_surface = gdk_texture_download_surface (best_icon, NULL);
|
||||
|
||||
cr = cairo_create (toplevel->icon_pixmap);
|
||||
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
|
||||
|
@@ -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;
|
||||
|
@@ -28,6 +28,7 @@
|
||||
#include <gdk/gdkglcontextprivate.h>
|
||||
#include <gdk/gdkmemoryformatprivate.h>
|
||||
#include <gdk/gdkmemorytextureprivate.h>
|
||||
#include <gdk/gdkcolorprofileprivate.h>
|
||||
#include <gdk/gdkprofilerprivate.h>
|
||||
#include <gsk/gskdebugprivate.h>
|
||||
#include <gsk/gskroundedrectprivate.h>
|
||||
@@ -529,7 +530,7 @@ gsk_gl_command_queue_begin_draw (GskGLCommandQueue *self,
|
||||
GskGLCommandBatch *batch;
|
||||
|
||||
g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
|
||||
g_assert (self->in_draw == FALSE);
|
||||
g_assert (!self->in_draw);
|
||||
g_assert (width <= G_MAXUINT16);
|
||||
g_assert (height <= G_MAXUINT16);
|
||||
|
||||
@@ -567,6 +568,7 @@ gsk_gl_command_queue_end_draw (GskGLCommandQueue *self)
|
||||
GskGLCommandBatch *batch;
|
||||
|
||||
g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
|
||||
g_assert (self->in_draw);
|
||||
g_assert (self->batches.len > 0);
|
||||
|
||||
if (will_ignore_batch (self))
|
||||
@@ -1351,23 +1353,35 @@ gsk_gl_command_queue_create_framebuffer (GskGLCommandQueue *self)
|
||||
|
||||
static void
|
||||
gsk_gl_command_queue_do_upload_texture (GskGLCommandQueue *self,
|
||||
GdkTexture *texture)
|
||||
GdkTexture *texture,
|
||||
GskConversion *conversion)
|
||||
{
|
||||
GdkGLContext *context;
|
||||
const guchar *data;
|
||||
gsize stride;
|
||||
GdkMemoryTexture *memtex;
|
||||
GdkMemoryFormat data_format;
|
||||
GdkColorProfile *data_profile;
|
||||
int width, height;
|
||||
GLenum gl_internalformat;
|
||||
GLenum gl_format;
|
||||
GLenum gl_type;
|
||||
gsize bpp;
|
||||
gboolean use_es;
|
||||
gboolean convert_locally = FALSE;
|
||||
|
||||
context = gdk_gl_context_get_current ();
|
||||
use_es = gdk_gl_context_get_use_es (context);
|
||||
data_format = gdk_texture_get_format (texture);
|
||||
data_profile = gdk_texture_get_color_profile (texture);
|
||||
|
||||
if (data_profile == gdk_color_profile_get_srgb ())
|
||||
*conversion = GSK_CONVERSION_LINEARIZE;
|
||||
else if (data_profile == gdk_color_profile_get_srgb_linear ())
|
||||
*conversion = 0;
|
||||
else /* FIXME: do colorspace conversion in a shader */
|
||||
convert_locally = TRUE;
|
||||
|
||||
width = gdk_texture_get_width (texture);
|
||||
height = gdk_texture_get_height (texture);
|
||||
|
||||
@@ -1377,21 +1391,38 @@ gsk_gl_command_queue_do_upload_texture (GskGLCommandQueue *self,
|
||||
&gl_format,
|
||||
&gl_type))
|
||||
{
|
||||
if (gdk_memory_format_prefers_high_depth (data_format))
|
||||
data_format = GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED;
|
||||
else
|
||||
data_format = GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
|
||||
if (!gdk_memory_format_gl_format (data_format,
|
||||
use_es,
|
||||
&gl_internalformat,
|
||||
&gl_format,
|
||||
&gl_type))
|
||||
if (gdk_gl_context_get_api (context) == GDK_GL_API_GL)
|
||||
{
|
||||
g_assert_not_reached ();
|
||||
*conversion |= GSK_CONVERSION_PREMULTIPLY;
|
||||
}
|
||||
else
|
||||
{
|
||||
convert_locally = TRUE;
|
||||
if (gdk_memory_format_prefers_high_depth (data_format))
|
||||
data_format = GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED;
|
||||
else
|
||||
data_format = GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
|
||||
if (!gdk_memory_format_gl_format (data_format,
|
||||
use_es,
|
||||
&gl_internalformat,
|
||||
&gl_format,
|
||||
&gl_type))
|
||||
{
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memtex = gdk_memory_texture_from_texture (texture, data_format);
|
||||
if (convert_locally)
|
||||
{
|
||||
memtex = gdk_memory_texture_from_texture (texture, data_format, gdk_color_profile_get_srgb_linear ());
|
||||
*conversion = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
memtex = gdk_memory_texture_from_texture (texture, gdk_texture_get_format (texture), gdk_texture_get_color_profile (texture));
|
||||
}
|
||||
|
||||
data = gdk_memory_texture_get_data (memtex);
|
||||
stride = gdk_memory_texture_get_stride (memtex);
|
||||
bpp = gdk_memory_format_bytes_per_pixel (data_format);
|
||||
@@ -1430,11 +1461,13 @@ int
|
||||
gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
|
||||
GdkTexture *texture,
|
||||
int min_filter,
|
||||
int mag_filter)
|
||||
int mag_filter,
|
||||
GskConversion *conversion)
|
||||
{
|
||||
G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME;
|
||||
cairo_surface_t *surface = NULL;
|
||||
int width, height;
|
||||
int format;
|
||||
int texture_id;
|
||||
|
||||
g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
|
||||
@@ -1444,6 +1477,9 @@ gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
|
||||
|
||||
width = gdk_texture_get_width (texture);
|
||||
height = gdk_texture_get_height (texture);
|
||||
|
||||
*conversion = 0;
|
||||
|
||||
if (width > self->max_texture_size || height > self->max_texture_size)
|
||||
{
|
||||
g_warning ("Attempt to create texture of size %ux%u but max size is %d. "
|
||||
@@ -1452,7 +1488,9 @@ gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
|
||||
width = MAX (width, self->max_texture_size);
|
||||
height = MAX (height, self->max_texture_size);
|
||||
}
|
||||
texture_id = gsk_gl_command_queue_create_texture (self, width, height, GL_RGBA8, min_filter, mag_filter);
|
||||
|
||||
format = gdk_memory_format_prefers_high_depth (gdk_texture_get_format (texture)) ? GL_RGBA16F : GL_RGBA8;
|
||||
texture_id = gsk_gl_command_queue_create_texture (self, width, height, format, min_filter, mag_filter);
|
||||
if (texture_id == -1)
|
||||
return texture_id;
|
||||
|
||||
@@ -1462,7 +1500,7 @@ gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
|
||||
glActiveTexture (GL_TEXTURE0);
|
||||
glBindTexture (GL_TEXTURE_2D, texture_id);
|
||||
|
||||
gsk_gl_command_queue_do_upload_texture (self, texture);
|
||||
gsk_gl_command_queue_do_upload_texture (self, texture, conversion);
|
||||
|
||||
/* Restore previous texture state if any */
|
||||
if (self->attachments->textures[0].id > 0)
|
||||
|
@@ -280,10 +280,18 @@ void gsk_gl_command_queue_execute (GskGLCommandQueue
|
||||
guint scale_factor,
|
||||
const cairo_region_t *scissor,
|
||||
guint default_framebuffer);
|
||||
|
||||
typedef enum {
|
||||
GSK_CONVERSION_LINEARIZE = 1 << 0,
|
||||
GSK_CONVERSION_PREMULTIPLY = 1 << 1,
|
||||
GSK_CONVERSION_FLIP = 1 << 2,
|
||||
} GskConversion;
|
||||
|
||||
int gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
|
||||
GdkTexture *texture,
|
||||
int min_filter,
|
||||
int mag_filter);
|
||||
int mag_filter,
|
||||
GskConversion *remaining);
|
||||
int gsk_gl_command_queue_create_texture (GskGLCommandQueue *self,
|
||||
int width,
|
||||
int height,
|
||||
|
@@ -40,7 +40,9 @@
|
||||
|
||||
#include <gdk/gdkglcontextprivate.h>
|
||||
#include <gdk/gdkdisplayprivate.h>
|
||||
#include <gdk/gdkmemoryformatprivate.h>
|
||||
#include <gdk/gdkmemorytextureprivate.h>
|
||||
#include <gdk/gdkcolorprofileprivate.h>
|
||||
#include <gdk/gdkprofilerprivate.h>
|
||||
#include <gdk/gdktextureprivate.h>
|
||||
|
||||
@@ -684,6 +686,140 @@ gsk_gl_driver_cache_texture (GskGLDriver *self,
|
||||
g_hash_table_insert (self->texture_id_to_key, GUINT_TO_POINTER (texture_id), k);
|
||||
}
|
||||
|
||||
static void
|
||||
draw_rect (GskGLCommandQueue *command_queue,
|
||||
float min_x,
|
||||
float min_y,
|
||||
float max_x,
|
||||
float max_y,
|
||||
gboolean flip)
|
||||
{
|
||||
GskGLDrawVertex *vertices;
|
||||
float min_u = 0;
|
||||
float max_u = 1;
|
||||
float min_v = flip ? 0 : 1;
|
||||
float max_v = flip ? 1 : 0;
|
||||
guint16 c = FP16_ZERO;
|
||||
|
||||
vertices = gsk_gl_command_queue_add_vertices (command_queue);
|
||||
|
||||
vertices[0] = (GskGLDrawVertex) { .position = { min_x, min_y }, .uv = { min_u, min_v }, .color = { c, c, c, c } };
|
||||
vertices[1] = (GskGLDrawVertex) { .position = { min_x, max_y }, .uv = { min_u, max_v }, .color = { c, c, c, c } };
|
||||
vertices[2] = (GskGLDrawVertex) { .position = { max_x, min_y }, .uv = { max_u, min_v }, .color = { c, c, c, c } };
|
||||
vertices[3] = (GskGLDrawVertex) { .position = { max_x, max_y }, .uv = { max_u, max_v }, .color = { c, c, c, c } };
|
||||
vertices[4] = (GskGLDrawVertex) { .position = { min_x, max_y }, .uv = { min_u, max_v }, .color = { c, c, c, c } };
|
||||
vertices[5] = (GskGLDrawVertex) { .position = { max_x, min_y }, .uv = { max_u, min_v }, .color = { c, c, c, c } };
|
||||
}
|
||||
|
||||
static void
|
||||
set_viewport_for_size (GskGLDriver *self,
|
||||
GskGLProgram *program,
|
||||
float width,
|
||||
float height)
|
||||
{
|
||||
float viewport[4] = { 0, 0, width, height };
|
||||
|
||||
gsk_gl_uniform_state_set4fv (program->uniforms,
|
||||
program->program_info,
|
||||
UNIFORM_SHARED_VIEWPORT, 0,
|
||||
1,
|
||||
(const float *)&viewport);
|
||||
self->stamps[UNIFORM_SHARED_VIEWPORT]++;
|
||||
}
|
||||
|
||||
#define ORTHO_NEAR_PLANE -10000
|
||||
#define ORTHO_FAR_PLANE 10000
|
||||
|
||||
static void
|
||||
set_projection_for_size (GskGLDriver *self,
|
||||
GskGLProgram *program,
|
||||
float width,
|
||||
float height)
|
||||
{
|
||||
graphene_matrix_t projection;
|
||||
|
||||
graphene_matrix_init_ortho (&projection, 0, width, 0, height, ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE);
|
||||
graphene_matrix_scale (&projection, 1, -1, 1);
|
||||
|
||||
gsk_gl_uniform_state_set_matrix (program->uniforms,
|
||||
program->program_info,
|
||||
UNIFORM_SHARED_PROJECTION, 0,
|
||||
&projection);
|
||||
self->stamps[UNIFORM_SHARED_PROJECTION]++;
|
||||
}
|
||||
|
||||
static void
|
||||
reset_modelview (GskGLDriver *self,
|
||||
GskGLProgram *program)
|
||||
{
|
||||
graphene_matrix_t modelview;
|
||||
|
||||
graphene_matrix_init_identity (&modelview);
|
||||
|
||||
gsk_gl_uniform_state_set_matrix (program->uniforms,
|
||||
program->program_info,
|
||||
UNIFORM_SHARED_MODELVIEW, 0,
|
||||
&modelview);
|
||||
self->stamps[UNIFORM_SHARED_MODELVIEW]++;
|
||||
}
|
||||
|
||||
static GskGLTexture *
|
||||
gsk_gl_driver_convert_texture (GskGLDriver *self,
|
||||
int texture_id,
|
||||
int width,
|
||||
int height,
|
||||
int format,
|
||||
int min_filter,
|
||||
int max_filter,
|
||||
GskConversion conversion)
|
||||
{
|
||||
GskGLRenderTarget *target;
|
||||
int prev_fbo;
|
||||
GskGLProgram *program;
|
||||
|
||||
if ((conversion & (GSK_CONVERSION_LINEARIZE | GSK_CONVERSION_PREMULTIPLY)) == (GSK_CONVERSION_LINEARIZE | GSK_CONVERSION_PREMULTIPLY))
|
||||
program = self->linearize_premultiply_no_clip;
|
||||
else if (conversion & GSK_CONVERSION_LINEARIZE)
|
||||
program = self->linearize_no_clip;
|
||||
else if (conversion & GSK_CONVERSION_PREMULTIPLY)
|
||||
program = self->premultiply_no_clip;
|
||||
else
|
||||
g_assert_not_reached ();
|
||||
|
||||
gdk_gl_context_make_current (self->command_queue->context);
|
||||
|
||||
gsk_gl_driver_create_render_target (self,
|
||||
width, height,
|
||||
format,
|
||||
min_filter, max_filter,
|
||||
&target);
|
||||
|
||||
prev_fbo = gsk_gl_command_queue_bind_framebuffer (self->command_queue, target->framebuffer_id);
|
||||
gsk_gl_command_queue_clear (self->command_queue, 0, &GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||
|
||||
gsk_gl_command_queue_begin_draw (self->command_queue,
|
||||
program->program_info,
|
||||
width, height);
|
||||
|
||||
set_projection_for_size (self, program, width, height);
|
||||
set_viewport_for_size (self, program, width, height);
|
||||
reset_modelview (self, program);
|
||||
|
||||
gsk_gl_program_set_uniform_texture (program,
|
||||
UNIFORM_SHARED_SOURCE, 0,
|
||||
GL_TEXTURE_2D, GL_TEXTURE0, texture_id);
|
||||
|
||||
draw_rect (self->command_queue, 0, 0, width, height, (conversion & GSK_CONVERSION_FLIP) != 0);
|
||||
|
||||
gsk_gl_command_queue_end_draw (self->command_queue);
|
||||
|
||||
gsk_gl_command_queue_bind_framebuffer (self->command_queue, prev_fbo);
|
||||
|
||||
texture_id = gsk_gl_driver_release_render_target (self, target, FALSE);
|
||||
|
||||
return g_hash_table_lookup (self->textures, GUINT_TO_POINTER (texture_id));
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_gl_driver_load_texture:
|
||||
* @self: a `GdkTexture`
|
||||
@@ -714,20 +850,29 @@ gsk_gl_driver_load_texture (GskGLDriver *self,
|
||||
int mag_filter)
|
||||
{
|
||||
GdkGLContext *context;
|
||||
GdkMemoryTexture *downloaded_texture;
|
||||
GdkMemoryTexture *downloaded_texture = NULL;
|
||||
guint texture_id = 0;
|
||||
GskGLTexture *t;
|
||||
guint texture_id;
|
||||
int height;
|
||||
int width;
|
||||
int format;
|
||||
GskConversion conversion;
|
||||
|
||||
g_return_val_if_fail (GSK_IS_GL_DRIVER (self), 0);
|
||||
g_return_val_if_fail (GDK_IS_TEXTURE (texture), 0);
|
||||
g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (self->command_queue), 0);
|
||||
|
||||
context = self->command_queue->context;
|
||||
width = gdk_texture_get_width (texture);
|
||||
height = gdk_texture_get_height (texture);
|
||||
|
||||
format = GL_RGBA8;
|
||||
format = gdk_memory_format_prefers_high_depth (gdk_texture_get_format (texture)) ? GL_RGBA16F : GL_RGBA8;
|
||||
|
||||
if ((t = gdk_texture_get_render_data (texture, self)))
|
||||
{
|
||||
if (t->min_filter == min_filter && t->mag_filter == mag_filter)
|
||||
return t->texture_id;
|
||||
}
|
||||
|
||||
if (GDK_IS_GL_TEXTURE (texture))
|
||||
{
|
||||
@@ -736,35 +881,62 @@ gsk_gl_driver_load_texture (GskGLDriver *self,
|
||||
|
||||
if (gdk_gl_context_is_shared (context, texture_context))
|
||||
{
|
||||
/* A GL texture from the same GL context is a simple task... */
|
||||
return gdk_gl_texture_get_id (gl_texture);
|
||||
}
|
||||
else
|
||||
{
|
||||
downloaded_texture = gdk_memory_texture_from_texture (texture, gdk_texture_get_format (texture));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((t = gdk_texture_get_render_data (texture, self)))
|
||||
{
|
||||
if (t->min_filter == min_filter && t->mag_filter == mag_filter)
|
||||
return t->texture_id;
|
||||
}
|
||||
GdkColorProfile *profile;
|
||||
guint gl_texture_id;
|
||||
GdkGLTextureFlags flags;
|
||||
|
||||
downloaded_texture = gdk_memory_texture_from_texture (texture, gdk_texture_get_format (texture));
|
||||
gl_texture_id = gdk_gl_texture_get_id (gl_texture);
|
||||
profile = gdk_texture_get_color_profile (texture);
|
||||
flags = gdk_gl_texture_get_flags (gl_texture);
|
||||
|
||||
/* A GL texture from the same GL context is a simple task... */
|
||||
if (profile == gdk_color_profile_get_srgb_linear () &&
|
||||
flags == GDK_GL_TEXTURE_PREMULTIPLIED)
|
||||
{
|
||||
return gl_texture_id;
|
||||
}
|
||||
else if (profile == gdk_color_profile_get_srgb () ||
|
||||
profile == gdk_color_profile_get_srgb_linear ())
|
||||
{
|
||||
conversion = 0;
|
||||
|
||||
if (profile == gdk_color_profile_get_srgb ())
|
||||
conversion |= GSK_CONVERSION_LINEARIZE;
|
||||
|
||||
if ((flags & GDK_GL_TEXTURE_PREMULTIPLIED) == 0)
|
||||
conversion |= GSK_CONVERSION_PREMULTIPLY;
|
||||
|
||||
if ((flags & GDK_GL_TEXTURE_FLIPPED) != 0)
|
||||
conversion |= GSK_CONVERSION_FLIP;
|
||||
|
||||
t = gsk_gl_driver_convert_texture (self,
|
||||
gl_texture_id,
|
||||
width, height,
|
||||
format,
|
||||
min_filter, mag_filter,
|
||||
conversion);
|
||||
if (gdk_texture_set_render_data (texture, self, t, gsk_gl_texture_destroyed))
|
||||
t->user = texture;
|
||||
|
||||
return t->texture_id;
|
||||
}
|
||||
/* FIXME: do colorspace conversion in a shader */
|
||||
}
|
||||
}
|
||||
|
||||
downloaded_texture = gdk_memory_texture_from_texture (texture,
|
||||
gdk_texture_get_format (texture),
|
||||
gdk_texture_get_color_profile (texture));
|
||||
|
||||
/* The download_texture() call may have switched the GL context. Make sure
|
||||
* the right context is at work again. */
|
||||
* the right context is at work again.
|
||||
*/
|
||||
gdk_gl_context_make_current (context);
|
||||
|
||||
width = gdk_texture_get_width (texture);
|
||||
height = gdk_texture_get_height (texture);
|
||||
texture_id = gsk_gl_command_queue_upload_texture (self->command_queue,
|
||||
GDK_TEXTURE (downloaded_texture),
|
||||
min_filter,
|
||||
mag_filter);
|
||||
min_filter, mag_filter,
|
||||
&conversion);
|
||||
|
||||
t = gsk_gl_texture_new (texture_id,
|
||||
width, height, format, min_filter, mag_filter,
|
||||
@@ -772,6 +944,15 @@ gsk_gl_driver_load_texture (GskGLDriver *self,
|
||||
|
||||
g_hash_table_insert (self->textures, GUINT_TO_POINTER (texture_id), t);
|
||||
|
||||
g_clear_object (&downloaded_texture);
|
||||
|
||||
if (conversion)
|
||||
t = gsk_gl_driver_convert_texture (self,
|
||||
texture_id,
|
||||
width, height, format,
|
||||
min_filter, mag_filter,
|
||||
conversion);
|
||||
|
||||
if (gdk_texture_set_render_data (texture, self, t, gsk_gl_texture_destroyed))
|
||||
t->user = texture;
|
||||
|
||||
@@ -830,6 +1011,8 @@ gsk_gl_driver_create_texture (GskGLDriver *self,
|
||||
GUINT_TO_POINTER (texture->texture_id),
|
||||
texture);
|
||||
|
||||
texture->last_used_in_frame = self->current_frame_id;
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
@@ -1197,6 +1380,7 @@ gsk_gl_driver_add_texture_slices (GskGLDriver *self,
|
||||
GskGLTextureSlice *slices;
|
||||
GskGLTexture *t;
|
||||
guint n_slices;
|
||||
int format;
|
||||
guint cols;
|
||||
guint rows;
|
||||
int tex_width;
|
||||
@@ -1214,6 +1398,9 @@ gsk_gl_driver_add_texture_slices (GskGLDriver *self,
|
||||
|
||||
tex_width = texture->width;
|
||||
tex_height = texture->height;
|
||||
|
||||
format = gdk_memory_format_prefers_high_depth (gdk_texture_get_format (texture)) ? GL_RGBA16F : GL_RGBA8;
|
||||
|
||||
cols = (texture->width / max_texture_size) + 1;
|
||||
rows = (texture->height / max_texture_size) + 1;
|
||||
|
||||
@@ -1227,7 +1414,8 @@ gsk_gl_driver_add_texture_slices (GskGLDriver *self,
|
||||
n_slices = cols * rows;
|
||||
slices = g_new0 (GskGLTextureSlice, n_slices);
|
||||
memtex = gdk_memory_texture_from_texture (texture,
|
||||
gdk_texture_get_format (texture));
|
||||
gdk_texture_get_format (texture),
|
||||
gdk_texture_get_color_profile (texture));
|
||||
|
||||
for (guint col = 0; col < cols; col ++)
|
||||
{
|
||||
@@ -1239,13 +1427,38 @@ gsk_gl_driver_add_texture_slices (GskGLDriver *self,
|
||||
int slice_index = (col * rows) + row;
|
||||
GdkTexture *subtex;
|
||||
guint texture_id;
|
||||
GskConversion conversion;
|
||||
|
||||
subtex = gdk_memory_texture_new_subtexture (memtex,
|
||||
x, y,
|
||||
slice_width, slice_height);
|
||||
texture_id = gsk_gl_command_queue_upload_texture (self->command_queue,
|
||||
subtex,
|
||||
GL_NEAREST, GL_NEAREST);
|
||||
GL_NEAREST, GL_NEAREST,
|
||||
&conversion);
|
||||
|
||||
if (conversion)
|
||||
{
|
||||
t = gsk_gl_texture_new (texture_id,
|
||||
slice_width, slice_height,
|
||||
format,
|
||||
GL_NEAREST, GL_NEAREST,
|
||||
0);
|
||||
g_hash_table_insert (self->textures, GUINT_TO_POINTER (texture_id), t);
|
||||
|
||||
t = gsk_gl_driver_convert_texture (self,
|
||||
texture_id,
|
||||
slice_width, slice_height,
|
||||
format,
|
||||
GL_NEAREST, GL_NEAREST,
|
||||
conversion);
|
||||
|
||||
texture_id = t->texture_id;
|
||||
t->texture_id = 0;
|
||||
g_hash_table_steal (self->textures, GUINT_TO_POINTER (texture_id));
|
||||
gsk_gl_texture_free (t);
|
||||
}
|
||||
|
||||
g_object_unref (subtex);
|
||||
|
||||
slices[slice_index].rect.x = x;
|
||||
@@ -1331,8 +1544,10 @@ create_texture_from_texture_destroy (gpointer data)
|
||||
}
|
||||
|
||||
GdkTexture *
|
||||
gsk_gl_driver_create_gdk_texture (GskGLDriver *self,
|
||||
guint texture_id)
|
||||
gsk_gl_driver_create_gdk_texture (GskGLDriver *self,
|
||||
guint texture_id,
|
||||
GdkGLTextureFlags flags,
|
||||
GdkColorProfile *profile)
|
||||
{
|
||||
GskGLTextureState *state;
|
||||
GskGLTexture *texture;
|
||||
@@ -1360,10 +1575,12 @@ gsk_gl_driver_create_gdk_texture (GskGLDriver *self,
|
||||
texture->texture_id = 0;
|
||||
gsk_gl_texture_free (texture);
|
||||
|
||||
return gdk_gl_texture_new (self->command_queue->context,
|
||||
texture_id,
|
||||
width,
|
||||
height,
|
||||
create_texture_from_texture_destroy,
|
||||
state);
|
||||
return gdk_gl_texture_new_with_color_profile (self->command_queue->context,
|
||||
texture_id,
|
||||
width,
|
||||
height,
|
||||
flags,
|
||||
profile,
|
||||
create_texture_from_texture_destroy,
|
||||
state);
|
||||
}
|
||||
|
@@ -155,7 +155,9 @@ void gsk_gl_driver_begin_frame (GskGLDriver *s
|
||||
void gsk_gl_driver_end_frame (GskGLDriver *self);
|
||||
void gsk_gl_driver_after_frame (GskGLDriver *self);
|
||||
GdkTexture * gsk_gl_driver_create_gdk_texture (GskGLDriver *self,
|
||||
guint texture_id);
|
||||
guint texture_id,
|
||||
GdkGLTextureFlags flags,
|
||||
GdkColorProfile *profile);
|
||||
void gsk_gl_driver_cache_texture (GskGLDriver *self,
|
||||
const GskTextureKey *key,
|
||||
guint texture_id);
|
||||
|
@@ -22,12 +22,21 @@
|
||||
|
||||
#include <gdk/gdkglcontextprivate.h>
|
||||
#include <gdk/gdkmemoryformatprivate.h>
|
||||
#include <gdk/gdkcolorprofileprivate.h>
|
||||
#include <gdk/gdkprofilerprivate.h>
|
||||
|
||||
#include "gskglcommandqueueprivate.h"
|
||||
#include "gskgldriverprivate.h"
|
||||
#include "gskglglyphlibraryprivate.h"
|
||||
|
||||
#ifdef HAVE_PANGOFT
|
||||
#include <pango/pangofc-font.h>
|
||||
#include <cairo-ft.h>
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_PARAMETER_TAGS_H
|
||||
#endif
|
||||
|
||||
#define MAX_GLYPH_SIZE 128
|
||||
|
||||
G_DEFINE_TYPE (GskGLGlyphLibrary, gsk_gl_glyph_library, GSK_TYPE_GL_TEXTURE_LIBRARY)
|
||||
@@ -209,6 +218,8 @@ gsk_gl_glyph_library_create_surface (GskGLGlyphLibrary *self,
|
||||
return surface;
|
||||
}
|
||||
|
||||
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
|
||||
|
||||
static void
|
||||
render_glyph (cairo_surface_t *surface,
|
||||
const GskGLGlyphKey *key,
|
||||
@@ -217,6 +228,14 @@ render_glyph (cairo_surface_t *surface,
|
||||
cairo_t *cr;
|
||||
PangoGlyphString glyph_string;
|
||||
PangoGlyphInfo glyph_info;
|
||||
#ifdef HAVE_PANGOFT
|
||||
FT_Face face;
|
||||
FT_Bool darken = 1;
|
||||
FT_Parameter property = { FT_PARAM_TAG_STEM_DARKENING, &darken };
|
||||
|
||||
face = pango_fc_font_lock_face (PANGO_FC_FONT (key->font));
|
||||
FT_Face_Properties (face, 1, &property);
|
||||
#endif
|
||||
|
||||
g_assert (surface != NULL);
|
||||
|
||||
@@ -235,8 +254,14 @@ render_glyph (cairo_surface_t *surface,
|
||||
cairo_destroy (cr);
|
||||
|
||||
cairo_surface_flush (surface);
|
||||
|
||||
#ifdef HAVE_PANGOFT
|
||||
pango_fc_font_unlock_face (PANGO_FC_FONT (key->font));
|
||||
#endif
|
||||
}
|
||||
|
||||
G_GNUC_END_IGNORE_DEPRECATIONS
|
||||
|
||||
static void
|
||||
gsk_gl_glyph_library_upload_glyph (GskGLGlyphLibrary *self,
|
||||
const GskGLGlyphKey *key,
|
||||
@@ -284,9 +309,11 @@ gsk_gl_glyph_library_upload_glyph (GskGLGlyphLibrary *self,
|
||||
gdk_memory_convert (pixel_data,
|
||||
width * 4,
|
||||
GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
|
||||
gdk_color_profile_get_srgb_linear (),
|
||||
cairo_image_surface_get_data (surface),
|
||||
width * 4,
|
||||
GDK_MEMORY_DEFAULT,
|
||||
gdk_color_profile_get_srgb_linear (),
|
||||
width, height);
|
||||
gl_format = GL_RGBA;
|
||||
gl_type = GL_UNSIGNED_BYTE;
|
||||
|
@@ -23,7 +23,9 @@
|
||||
#include <gdk/gdkglcontextprivate.h>
|
||||
#include <gdk/gdkmemoryformatprivate.h>
|
||||
#include <gdk/gdkprofilerprivate.h>
|
||||
#include <gdk/gdkcolorprofileprivate.h>
|
||||
#include <gdk/gdktextureprivate.h>
|
||||
#include <gdk/gdkmemorytextureprivate.h>
|
||||
|
||||
#include "gskglcommandqueueprivate.h"
|
||||
#include "gskgldriverprivate.h"
|
||||
@@ -71,25 +73,107 @@ gsk_gl_icon_library_init (GskGLIconLibrary *self)
|
||||
gsk_gl_icon_data_free);
|
||||
}
|
||||
|
||||
static GdkMemoryTexture *
|
||||
gsk_ngl_texture_prepare_upload (GdkGLContext *context,
|
||||
GdkTexture *texture,
|
||||
GLenum *gl_internalformat,
|
||||
GLenum *gl_format,
|
||||
GLenum *gl_type)
|
||||
{
|
||||
GdkMemoryTexture *memtex;
|
||||
GdkColorProfile *profile;
|
||||
GdkMemoryFormat format;
|
||||
|
||||
format = gdk_texture_get_format (texture);
|
||||
profile = gdk_texture_get_color_profile (texture);
|
||||
|
||||
memtex = gdk_memory_texture_from_texture (texture, format, profile);
|
||||
|
||||
if (!gdk_memory_format_gl_format (format,
|
||||
gdk_gl_context_get_use_es (context),
|
||||
gl_internalformat,
|
||||
gl_format,
|
||||
gl_type))
|
||||
{
|
||||
g_object_unref (memtex);
|
||||
|
||||
format = GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
|
||||
if (!gdk_memory_format_gl_format (format,
|
||||
gdk_gl_context_get_use_es (context),
|
||||
gl_internalformat,
|
||||
gl_format,
|
||||
gl_type))
|
||||
{
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
return gdk_memory_texture_from_texture (texture, format, gdk_color_profile_get_srgb_linear ());
|
||||
}
|
||||
|
||||
static void
|
||||
straightTexSubImage2D (int tex,
|
||||
int level,
|
||||
int x,
|
||||
int y,
|
||||
int width,
|
||||
int height,
|
||||
GLenum gl_format,
|
||||
GLenum gl_type,
|
||||
const guchar *data,
|
||||
gsize stride)
|
||||
{
|
||||
glTexSubImage2D (tex, level,
|
||||
x, y,
|
||||
width, height,
|
||||
gl_format, gl_type,
|
||||
data);
|
||||
}
|
||||
|
||||
static void
|
||||
strideTexSubImage2D (int tex,
|
||||
int level,
|
||||
int x,
|
||||
int y,
|
||||
int width,
|
||||
int height,
|
||||
GLenum gl_format,
|
||||
GLenum gl_type,
|
||||
const guchar *data,
|
||||
gsize stride)
|
||||
{
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
glTexSubImage2D (tex, level,
|
||||
x, y + i,
|
||||
width, height,
|
||||
gl_format, gl_type,
|
||||
data + i * stride);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gsk_gl_icon_library_add (GskGLIconLibrary *self,
|
||||
GdkTexture *key,
|
||||
const GskGLIconData **out_value)
|
||||
{
|
||||
GskGLTextureLibrary *tl = (GskGLTextureLibrary *)self;
|
||||
GdkGLContext *context = gdk_gl_context_get_current ();
|
||||
G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME;
|
||||
cairo_surface_t *surface;
|
||||
GdkMemoryTexture *memtex;
|
||||
GskGLIconData *icon_data;
|
||||
guint8 *pixel_data;
|
||||
guint8 *surface_data;
|
||||
guint8 *free_data = NULL;
|
||||
guint gl_format;
|
||||
guint gl_type;
|
||||
const guchar *pixel_data;
|
||||
gsize stride, bpp;
|
||||
GdkMemoryFormat format;
|
||||
GLenum gl_internalformat;
|
||||
GLenum gl_format;
|
||||
GLenum gl_type;
|
||||
guint packed_x;
|
||||
guint packed_y;
|
||||
int width;
|
||||
int height;
|
||||
guint texture_id;
|
||||
void (* upload_func) (int, int, int, int, int, int, GLenum, GLenum, const guchar *, gsize);
|
||||
|
||||
g_assert (GSK_IS_GL_ICON_LIBRARY (self));
|
||||
g_assert (GDK_IS_TEXTURE (key));
|
||||
@@ -106,104 +190,114 @@ gsk_gl_icon_library_add (GskGLIconLibrary *self,
|
||||
icon_data->source_texture = g_object_ref (key);
|
||||
|
||||
/* actually upload the texture */
|
||||
surface = gdk_texture_download_surface (key);
|
||||
surface_data = cairo_image_surface_get_data (surface);
|
||||
gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (),
|
||||
"Uploading texture");
|
||||
gdk_gl_context_push_debug_group_printf (context, "Uploading texture");
|
||||
memtex = gsk_ngl_texture_prepare_upload (context,
|
||||
key,
|
||||
&gl_internalformat,
|
||||
&gl_format,
|
||||
&gl_type);
|
||||
|
||||
if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
|
||||
{
|
||||
pixel_data = free_data = g_malloc (width * height * 4);
|
||||
gdk_memory_convert (pixel_data, width * 4,
|
||||
GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
|
||||
surface_data, cairo_image_surface_get_stride (surface),
|
||||
GDK_MEMORY_DEFAULT, width, height);
|
||||
gl_format = GL_RGBA;
|
||||
gl_type = GL_UNSIGNED_BYTE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pixel_data = surface_data;
|
||||
gl_format = GL_BGRA;
|
||||
gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
|
||||
}
|
||||
pixel_data = gdk_memory_texture_get_data (memtex);
|
||||
stride = gdk_memory_texture_get_stride (memtex);
|
||||
format = gdk_texture_get_format (GDK_TEXTURE (memtex));
|
||||
bpp = gdk_memory_format_bytes_per_pixel (format);
|
||||
|
||||
texture_id = GSK_GL_TEXTURE_ATLAS_ENTRY_TEXTURE (icon_data);
|
||||
|
||||
glBindTexture (GL_TEXTURE_2D, texture_id);
|
||||
|
||||
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
||||
packed_x + 1, packed_y + 1,
|
||||
width, height,
|
||||
gl_format, gl_type,
|
||||
pixel_data);
|
||||
glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
|
||||
/* GL_UNPACK_ROW_LENGTH is available on desktop GL, OpenGL ES >= 3.0, or if
|
||||
* the GL_EXT_unpack_subimage extension for OpenGL ES 2.0 is available
|
||||
*/
|
||||
if ((stride % bpp == 0) &&
|
||||
(gdk_gl_context_get_api (context) == GDK_GL_API_GL ||
|
||||
gdk_gl_context_check_version (context, 3, 0, 3, 0) ||
|
||||
gdk_gl_context_has_unpack_subimage (context)))
|
||||
{
|
||||
upload_func = straightTexSubImage2D;
|
||||
glPixelStorei (GL_UNPACK_ROW_LENGTH, stride / bpp);
|
||||
}
|
||||
else
|
||||
{
|
||||
upload_func = strideTexSubImage2D;
|
||||
}
|
||||
|
||||
upload_func (GL_TEXTURE_2D, 0,
|
||||
packed_x + 1, packed_y + 1,
|
||||
width, height,
|
||||
gl_format, gl_type,
|
||||
pixel_data,
|
||||
stride);
|
||||
/* Padding top */
|
||||
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
||||
packed_x + 1, packed_y,
|
||||
width, 1,
|
||||
gl_format, gl_type,
|
||||
pixel_data);
|
||||
upload_func (GL_TEXTURE_2D, 0,
|
||||
packed_x + 1, packed_y,
|
||||
width, 1,
|
||||
gl_format, gl_type,
|
||||
pixel_data,
|
||||
stride);
|
||||
/* Padding left */
|
||||
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
||||
packed_x, packed_y + 1,
|
||||
1, height,
|
||||
gl_format, gl_type,
|
||||
pixel_data);
|
||||
upload_func (GL_TEXTURE_2D, 0,
|
||||
packed_x, packed_y + 1,
|
||||
1, height,
|
||||
gl_format, gl_type,
|
||||
pixel_data,
|
||||
stride);
|
||||
/* Padding top left */
|
||||
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
||||
packed_x, packed_y,
|
||||
1, 1,
|
||||
gl_format, gl_type,
|
||||
pixel_data);
|
||||
|
||||
upload_func (GL_TEXTURE_2D, 0,
|
||||
packed_x, packed_y,
|
||||
1, 1,
|
||||
gl_format, gl_type,
|
||||
pixel_data,
|
||||
stride);
|
||||
/* Padding right */
|
||||
glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
|
||||
glPixelStorei (GL_UNPACK_SKIP_PIXELS, width - 1);
|
||||
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
||||
packed_x + width + 1, packed_y + 1,
|
||||
1, height,
|
||||
gl_format, gl_type,
|
||||
pixel_data);
|
||||
upload_func (GL_TEXTURE_2D, 0,
|
||||
packed_x + width + 1, packed_y + 1,
|
||||
1, height,
|
||||
gl_format, gl_type,
|
||||
pixel_data + (width - 1) * bpp,
|
||||
stride);
|
||||
/* Padding top right */
|
||||
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
||||
packed_x + width + 1, packed_y,
|
||||
1, 1,
|
||||
gl_format, gl_type,
|
||||
pixel_data);
|
||||
upload_func (GL_TEXTURE_2D, 0,
|
||||
packed_x + width + 1, packed_y,
|
||||
1, 1,
|
||||
gl_format, gl_type,
|
||||
pixel_data + (width - 1) * bpp,
|
||||
stride);
|
||||
/* Padding bottom */
|
||||
glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
|
||||
glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
|
||||
glPixelStorei (GL_UNPACK_SKIP_ROWS, height - 1);
|
||||
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
||||
packed_x + 1, packed_y + 1 + height,
|
||||
width, 1,
|
||||
gl_format, gl_type,
|
||||
pixel_data);
|
||||
upload_func (GL_TEXTURE_2D, 0,
|
||||
packed_x + 1, packed_y + 1 + height,
|
||||
width, 1,
|
||||
gl_format, gl_type,
|
||||
pixel_data + (height - 1) * stride,
|
||||
stride);
|
||||
/* Padding bottom left */
|
||||
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
||||
packed_x, packed_y + 1 + height,
|
||||
1, 1,
|
||||
gl_format, gl_type,
|
||||
pixel_data);
|
||||
upload_func (GL_TEXTURE_2D, 0,
|
||||
packed_x, packed_y + 1 + height,
|
||||
1, 1,
|
||||
gl_format, gl_type,
|
||||
pixel_data + (height - 1) * stride,
|
||||
stride);
|
||||
/* Padding bottom right */
|
||||
glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
|
||||
glPixelStorei (GL_UNPACK_SKIP_PIXELS, width - 1);
|
||||
glTexSubImage2D (GL_TEXTURE_2D, 0,
|
||||
packed_x + 1 + width, packed_y + 1 + height,
|
||||
1, 1,
|
||||
gl_format, gl_type,
|
||||
pixel_data);
|
||||
upload_func (GL_TEXTURE_2D, 0,
|
||||
packed_x + 1 + width, packed_y + 1 + height,
|
||||
1, 1,
|
||||
gl_format, gl_type,
|
||||
pixel_data + (width - 1) * bpp + (height - 1) * stride,
|
||||
stride);
|
||||
/* Reset this */
|
||||
glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
|
||||
glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
|
||||
glPixelStorei (GL_UNPACK_SKIP_ROWS, 0);
|
||||
glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
|
||||
if ((stride % bpp == 0) &&
|
||||
(gdk_gl_context_get_api (context) == GDK_GL_API_GL ||
|
||||
gdk_gl_context_check_version (context, 3, 0, 3, 0) ||
|
||||
gdk_gl_context_has_unpack_subimage (context)))
|
||||
glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
|
||||
|
||||
gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ());
|
||||
gdk_gl_context_pop_debug_group (context);
|
||||
|
||||
*out_value = icon_data;
|
||||
|
||||
cairo_surface_destroy (surface);
|
||||
g_free (free_data);
|
||||
g_object_unref (memtex);
|
||||
|
||||
tl->driver->command_queue->n_uploads++;
|
||||
|
||||
|
@@ -82,3 +82,19 @@ GSK_GL_DEFINE_PROGRAM (unblurred_outset_shadow,
|
||||
GSK_GL_ADD_UNIFORM (1, UNBLURRED_OUTSET_SHADOW_SPREAD, u_spread)
|
||||
GSK_GL_ADD_UNIFORM (2, UNBLURRED_OUTSET_SHADOW_OFFSET, u_offset)
|
||||
GSK_GL_ADD_UNIFORM (3, UNBLURRED_OUTSET_SHADOW_OUTLINE_RECT, u_outline_rect))
|
||||
|
||||
GSK_GL_DEFINE_PROGRAM (postprocessing,
|
||||
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("postprocessing.glsl")),
|
||||
GSK_GL_NO_UNIFORMS)
|
||||
|
||||
GSK_GL_DEFINE_PROGRAM (linearize,
|
||||
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("linearize.glsl")),
|
||||
GSK_GL_NO_UNIFORMS)
|
||||
|
||||
GSK_GL_DEFINE_PROGRAM (premultiply,
|
||||
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("premultiply.glsl")),
|
||||
GSK_GL_NO_UNIFORMS)
|
||||
|
||||
GSK_GL_DEFINE_PROGRAM (linearize_premultiply,
|
||||
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("linearize_premultiply.glsl")),
|
||||
GSK_GL_NO_UNIFORMS)
|
||||
|
@@ -24,6 +24,7 @@
|
||||
#include <gdk/gdkdisplayprivate.h>
|
||||
#include <gdk/gdkglcontextprivate.h>
|
||||
#include <gdk/gdksurfaceprivate.h>
|
||||
#include <gdk/gdkcolorprofileprivate.h>
|
||||
#include <gdk/gdkintl.h>
|
||||
#include <gsk/gskdebugprivate.h>
|
||||
#include <gsk/gskrendererprivate.h>
|
||||
@@ -386,7 +387,10 @@ gsk_gl_renderer_render_texture (GskRenderer *renderer,
|
||||
#endif
|
||||
gsk_gl_render_job_render_flipped (job, root);
|
||||
texture_id = gsk_gl_driver_release_render_target (self->driver, render_target, FALSE);
|
||||
texture = gsk_gl_driver_create_gdk_texture (self->driver, texture_id);
|
||||
texture = gsk_gl_driver_create_gdk_texture (self->driver,
|
||||
texture_id,
|
||||
GDK_GL_TEXTURE_PREMULTIPLIED,
|
||||
gdk_color_profile_get_srgb_linear ());
|
||||
gsk_gl_driver_end_frame (self->driver);
|
||||
gsk_gl_render_job_free (job);
|
||||
|
||||
|
@@ -26,6 +26,8 @@
|
||||
#include <gdk/gdkglcontextprivate.h>
|
||||
#include <gdk/gdkprofilerprivate.h>
|
||||
#include <gdk/gdkrgbaprivate.h>
|
||||
#include <gdk/gdkcolorprofileprivate.h>
|
||||
#include <gdk/gdkmemoryformatprivate.h>
|
||||
#include <gsk/gskrendernodeprivate.h>
|
||||
#include <gsk/gskglshaderprivate.h>
|
||||
#include <gdk/gdktextureprivate.h>
|
||||
@@ -988,11 +990,23 @@ gsk_gl_render_job_update_clip (GskGLRenderJob *job,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Convert from sRGB floats to linear sRGB half-floats */
|
||||
static inline void
|
||||
rgba_to_half (const GdkRGBA *rgba,
|
||||
guint16 h[4])
|
||||
{
|
||||
float_to_half4 ((const float *)rgba, h);
|
||||
GdkRGBA d;
|
||||
gdk_memory_convert ((guchar *) &d,
|
||||
sizeof (float) * 4,
|
||||
GDK_MEMORY_R32G32B32A32_FLOAT,
|
||||
gdk_color_profile_get_srgb_linear (),
|
||||
(guchar *) rgba,
|
||||
sizeof (float) * 4,
|
||||
GDK_MEMORY_R32G32B32A32_FLOAT,
|
||||
gdk_color_profile_get_srgb (),
|
||||
1,
|
||||
1);
|
||||
float_to_half4 ((const float *)&d, h);
|
||||
}
|
||||
|
||||
/* fill_vertex_data */
|
||||
@@ -1226,6 +1240,8 @@ gsk_gl_render_job_visit_as_fallback (GskGLRenderJob *job,
|
||||
surface_width,
|
||||
surface_height);
|
||||
|
||||
gdk_cairo_surface_set_color_profile (rendered_surface, gdk_color_profile_get_srgb_linear ());
|
||||
|
||||
cairo_surface_set_device_scale (rendered_surface, scale_x, scale_y);
|
||||
cr = cairo_create (rendered_surface);
|
||||
|
||||
@@ -4050,6 +4066,21 @@ gsk_gl_render_job_render_flipped (GskGLRenderJob *job,
|
||||
glDeleteTextures (1, &texture_id);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_gl_render_job_postprocess (GskGLRenderJob *job,
|
||||
guint texture_id)
|
||||
{
|
||||
gsk_gl_command_queue_bind_framebuffer (job->command_queue, job->framebuffer);
|
||||
gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport);
|
||||
|
||||
gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, postprocessing));
|
||||
gsk_gl_program_set_uniform_texture (job->current_program,
|
||||
UNIFORM_SHARED_SOURCE, 0,
|
||||
GL_TEXTURE_2D, GL_TEXTURE0, texture_id);
|
||||
gsk_gl_render_job_draw_offscreen_rect (job, &job->viewport);
|
||||
gsk_gl_render_job_end_draw (job);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_gl_render_job_render (GskGLRenderJob *job,
|
||||
GskRenderNode *root)
|
||||
@@ -4057,6 +4088,8 @@ gsk_gl_render_job_render (GskGLRenderJob *job,
|
||||
G_GNUC_UNUSED gint64 start_time;
|
||||
guint scale_factor;
|
||||
guint surface_height;
|
||||
GskGLRenderTarget *render_target;
|
||||
guint texture_id;
|
||||
|
||||
g_return_if_fail (job != NULL);
|
||||
g_return_if_fail (root != NULL);
|
||||
@@ -4067,16 +4100,28 @@ gsk_gl_render_job_render (GskGLRenderJob *job,
|
||||
|
||||
gsk_gl_command_queue_make_current (job->command_queue);
|
||||
|
||||
if (!gsk_gl_driver_create_render_target (job->driver,
|
||||
job->viewport.size.width,
|
||||
job->viewport.size.height,
|
||||
job->target_format,
|
||||
GL_NEAREST, GL_NEAREST,
|
||||
&render_target))
|
||||
g_assert_not_reached ();
|
||||
|
||||
/* Build the command queue using the shared GL context for all renderers
|
||||
* on the same display.
|
||||
*/
|
||||
start_time = GDK_PROFILER_CURRENT_TIME;
|
||||
gdk_gl_context_push_debug_group (job->command_queue->context, "Building command queue");
|
||||
gsk_gl_command_queue_bind_framebuffer (job->command_queue, job->framebuffer);
|
||||
gsk_gl_command_queue_bind_framebuffer (job->command_queue, render_target->framebuffer_id);
|
||||
if (job->clear_framebuffer)
|
||||
gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport);
|
||||
gsk_gl_render_job_visit_node (job, root);
|
||||
gdk_gl_context_pop_debug_group (job->command_queue->context);
|
||||
|
||||
texture_id = gsk_gl_driver_release_render_target (job->driver, render_target, FALSE);
|
||||
gsk_gl_render_job_postprocess (job, texture_id);
|
||||
|
||||
gdk_profiler_add_mark (start_time, GDK_PROFILER_CURRENT_TIME-start_time, "Build GL command queue", "");
|
||||
|
||||
#if 0
|
||||
|
22
gsk/gl/resources/linearize.glsl
Normal 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);
|
||||
}
|
21
gsk/gl/resources/linearize_premultiply.glsl
Normal 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);
|
||||
}
|
22
gsk/gl/resources/postprocessing.glsl
Normal 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);
|
||||
}
|
@@ -42,10 +42,31 @@ gsk_get_bounds(vec4[3] data)
|
||||
return vec4(data[0].xy, data[0].xy + data[0].zw);
|
||||
}
|
||||
|
||||
vec4 gsk_premultiply(vec4 c) {
|
||||
vec4 gsk_premultiply(vec4 c)
|
||||
{
|
||||
return vec4(c.rgb * c.a, c.a);
|
||||
}
|
||||
|
||||
vec4 gsk_unpremultiply(vec4 c)
|
||||
{
|
||||
if (c.a != 0)
|
||||
return vec4(c.rgb / c.a, c.a);
|
||||
else
|
||||
return c;
|
||||
}
|
||||
|
||||
vec4 gsk_srgb_to_linear(vec4 srgb)
|
||||
{
|
||||
vec3 linear_rgb = pow(srgb.rgb, vec3(2.2));
|
||||
return vec4(linear_rgb, srgb.a);
|
||||
}
|
||||
|
||||
vec4 gsk_linear_to_srgb(vec4 linear_rgba)
|
||||
{
|
||||
vec3 srgb = pow(linear_rgba.rgb, vec3(1/2.2));
|
||||
return vec4(srgb, linear_rgba.a);
|
||||
}
|
||||
|
||||
vec4 gsk_scaled_premultiply(vec4 c, float s) {
|
||||
// Fast version of gsk_premultiply(c) * s
|
||||
// 4 muls instead of 7
|
||||
|
20
gsk/gl/resources/premultiply.glsl
Normal 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);
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2016 Endless
|
||||
* Copyright © 2016 Endless
|
||||
* 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
@@ -25,6 +25,9 @@
|
||||
#include "gskdebugprivate.h"
|
||||
#include "gskrendererprivate.h"
|
||||
#include "gskrendernodeprivate.h"
|
||||
|
||||
#include "gdk/gdkcolorprofileprivate.h"
|
||||
#include "gdk/gdkmemorytextureprivate.h"
|
||||
#include "gdk/gdktextureprivate.h"
|
||||
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
@@ -40,6 +43,7 @@ struct _GskCairoRenderer
|
||||
|
||||
GdkCairoContext *cairo_context;
|
||||
|
||||
gboolean color_managed;
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
ProfileTimers profile_timers;
|
||||
#endif
|
||||
@@ -104,6 +108,7 @@ gsk_cairo_renderer_render_texture (GskRenderer *renderer,
|
||||
GskRenderNode *root,
|
||||
const graphene_rect_t *viewport)
|
||||
{
|
||||
GskCairoRenderer *self = GSK_CAIRO_RENDERER (renderer);
|
||||
GdkTexture *texture;
|
||||
cairo_surface_t *surface;
|
||||
cairo_t *cr;
|
||||
@@ -127,7 +132,7 @@ gsk_cairo_renderer_render_texture (GskRenderer *renderer,
|
||||
{
|
||||
for (x = 0; x < width; x += MAX_IMAGE_SIZE)
|
||||
{
|
||||
texture = gsk_cairo_renderer_render_texture (renderer, root,
|
||||
texture = gsk_cairo_renderer_render_texture (renderer, root,
|
||||
&GRAPHENE_RECT_INIT (x, y,
|
||||
MIN (MAX_IMAGE_SIZE, viewport->size.width - x),
|
||||
MIN (MAX_IMAGE_SIZE, viewport->size.height - y)));
|
||||
@@ -145,6 +150,9 @@ gsk_cairo_renderer_render_texture (GskRenderer *renderer,
|
||||
}
|
||||
|
||||
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
|
||||
if (self->color_managed)
|
||||
gdk_cairo_surface_set_color_profile (surface, gdk_color_profile_get_srgb_linear ());
|
||||
|
||||
cr = cairo_create (surface);
|
||||
|
||||
cairo_translate (cr, - viewport->origin.x, - viewport->origin.y);
|
||||
@@ -189,7 +197,64 @@ gsk_cairo_renderer_render (GskRenderer *renderer,
|
||||
}
|
||||
#endif
|
||||
|
||||
gsk_cairo_renderer_do_render (renderer, cr, root);
|
||||
if (!self->color_managed ||
|
||||
gdk_color_profile_is_linear (gdk_cairo_get_color_profile (cr)))
|
||||
{
|
||||
gsk_cairo_renderer_do_render (renderer, cr, root);
|
||||
}
|
||||
else
|
||||
{
|
||||
GdkSurface *surface = gsk_renderer_get_surface (renderer);
|
||||
GdkColorProfile *target_profile = gdk_cairo_get_color_profile (cr);
|
||||
cairo_surface_t *cairo_surface;
|
||||
cairo_t *cr2;
|
||||
GdkTexture *color_correct;
|
||||
const cairo_region_t *frame_region;
|
||||
cairo_rectangle_int_t extents;
|
||||
guint i, n;
|
||||
|
||||
frame_region = gdk_draw_context_get_frame_region (GDK_DRAW_CONTEXT (self->cairo_context));
|
||||
cairo_region_get_extents (frame_region, &extents);
|
||||
/* We can't use cairo_push_group() here, because we'd lose the
|
||||
* color profile information.
|
||||
*/
|
||||
cairo_surface = gdk_surface_create_similar_surface (surface,
|
||||
CAIRO_CONTENT_COLOR_ALPHA,
|
||||
extents.width,
|
||||
extents.height);
|
||||
gdk_cairo_surface_set_color_profile (cairo_surface,
|
||||
gdk_color_profile_get_srgb_linear ());
|
||||
|
||||
cr2 = cairo_create (cairo_surface);
|
||||
cairo_translate (cr2, -extents.x, -extents.y);
|
||||
gdk_cairo_region (cr2, frame_region);
|
||||
cairo_clip (cr2);
|
||||
gsk_cairo_renderer_do_render (renderer, cr2, root);
|
||||
cairo_destroy (cr2);
|
||||
|
||||
color_correct = gdk_texture_new_for_surface (cairo_surface);
|
||||
cairo_surface_destroy (cairo_surface);
|
||||
n = cairo_region_num_rectangles (frame_region);
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
cairo_rectangle_int_t rect;
|
||||
GdkTexture *sub;
|
||||
|
||||
cairo_region_get_rectangle (frame_region, i, &rect);
|
||||
rect.x -= extents.x;
|
||||
rect.y -= extents.y;
|
||||
|
||||
sub = gdk_memory_texture_new_subtexture (GDK_MEMORY_TEXTURE (color_correct),
|
||||
rect.x, rect.y, rect.width, rect.height);
|
||||
cairo_surface = gdk_texture_download_surface (sub, target_profile);
|
||||
cairo_set_source_surface (cr, cairo_surface, rect.x + extents.x, rect.y + extents.y);
|
||||
cairo_paint (cr);
|
||||
cairo_surface_destroy (cairo_surface);
|
||||
g_object_unref (sub);
|
||||
}
|
||||
g_object_unref (color_correct);
|
||||
|
||||
}
|
||||
|
||||
cairo_destroy (cr);
|
||||
|
||||
@@ -210,6 +275,8 @@ gsk_cairo_renderer_class_init (GskCairoRendererClass *klass)
|
||||
static void
|
||||
gsk_cairo_renderer_init (GskCairoRenderer *self)
|
||||
{
|
||||
self->color_managed = TRUE;
|
||||
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
GskProfiler *profiler = gsk_renderer_get_profiler (GSK_RENDERER (self));
|
||||
|
||||
|
@@ -27,12 +27,20 @@
|
||||
#include "gskroundedrectprivate.h"
|
||||
#include "gsktransformprivate.h"
|
||||
|
||||
#include "gdk/gdkcolorprivate.h"
|
||||
#include "gdk/gdktextureprivate.h"
|
||||
#include "gdk/gdkmemoryformatprivate.h"
|
||||
#include "gdk/gdk-private.h"
|
||||
|
||||
#include <hb-ot.h>
|
||||
|
||||
#ifdef HAVE_PANGOFT
|
||||
#include <pango/pangofc-font.h>
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_PARAMETER_TAGS_H
|
||||
#endif
|
||||
|
||||
/* maximal number of rectangles we keep in a diff region before we throw
|
||||
* the towel and just use the bounding box of the parent node.
|
||||
* Meant to avoid performance corner cases.
|
||||
@@ -187,6 +195,7 @@ gsk_linear_gradient_node_draw (GskRenderNode *node,
|
||||
cairo_t *cr)
|
||||
{
|
||||
GskLinearGradientNode *self = (GskLinearGradientNode *) node;
|
||||
GdkColorProfile *profile;
|
||||
cairo_pattern_t *pattern;
|
||||
gsize i;
|
||||
|
||||
@@ -196,14 +205,20 @@ gsk_linear_gradient_node_draw (GskRenderNode *node,
|
||||
if (gsk_render_node_get_node_type (node) == GSK_REPEATING_LINEAR_GRADIENT_NODE)
|
||||
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
|
||||
|
||||
profile = gdk_cairo_get_color_profile (cr);
|
||||
for (i = 0; i < self->n_stops; i++)
|
||||
{
|
||||
GdkColor color;
|
||||
const float *components;
|
||||
gdk_color_convert_rgba (&color, profile, &self->stops[i].color);
|
||||
components = gdk_color_get_components (&color);
|
||||
cairo_pattern_add_color_stop_rgba (pattern,
|
||||
self->stops[i].offset,
|
||||
self->stops[i].color.red,
|
||||
self->stops[i].color.green,
|
||||
self->stops[i].color.blue,
|
||||
self->stops[i].color.alpha);
|
||||
components[0],
|
||||
components[1],
|
||||
components[2],
|
||||
gdk_color_get_alpha (&color));
|
||||
gdk_color_finish (&color);
|
||||
}
|
||||
|
||||
cairo_set_source (cr, pattern);
|
||||
@@ -464,6 +479,7 @@ gsk_radial_gradient_node_draw (GskRenderNode *node,
|
||||
cairo_t *cr)
|
||||
{
|
||||
GskRadialGradientNode *self = (GskRadialGradientNode *) node;
|
||||
GdkColorProfile *profile;
|
||||
cairo_pattern_t *pattern;
|
||||
gsize i;
|
||||
|
||||
@@ -483,13 +499,21 @@ gsk_radial_gradient_node_draw (GskRenderNode *node,
|
||||
else
|
||||
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
|
||||
|
||||
profile = gdk_cairo_get_color_profile (cr);
|
||||
for (i = 0; i < self->n_stops; i++)
|
||||
cairo_pattern_add_color_stop_rgba (pattern,
|
||||
self->stops[i].offset,
|
||||
self->stops[i].color.red,
|
||||
self->stops[i].color.green,
|
||||
self->stops[i].color.blue,
|
||||
self->stops[i].color.alpha);
|
||||
{
|
||||
GdkColor color;
|
||||
const float *components;
|
||||
gdk_color_convert_rgba (&color, profile, &self->stops[i].color);
|
||||
components = gdk_color_get_components (&color);
|
||||
cairo_pattern_add_color_stop_rgba (pattern,
|
||||
self->stops[i].offset,
|
||||
components[0],
|
||||
components[1],
|
||||
components[2],
|
||||
gdk_color_get_alpha (&color));
|
||||
gdk_color_finish (&color);
|
||||
}
|
||||
|
||||
gsk_cairo_rectangle (cr, &node->bounds);
|
||||
cairo_translate (cr, self->center.x, self->center.y);
|
||||
@@ -824,11 +848,17 @@ gsk_conic_gradient_node_finalize (GskRenderNode *node)
|
||||
#define DEG_TO_RAD(x) ((x) * (G_PI / 180.f))
|
||||
|
||||
static void
|
||||
_cairo_mesh_pattern_set_corner_rgba (cairo_pattern_t *pattern,
|
||||
guint corner_num,
|
||||
const GdkRGBA *rgba)
|
||||
_cairo_mesh_pattern_set_corner_color (cairo_pattern_t *pattern,
|
||||
guint corner_num,
|
||||
const GdkColor *color)
|
||||
{
|
||||
cairo_mesh_pattern_set_corner_color_rgba (pattern, corner_num, rgba->red, rgba->green, rgba->blue, rgba->alpha);
|
||||
const float *components = gdk_color_get_components (color);
|
||||
cairo_mesh_pattern_set_corner_color_rgba (pattern,
|
||||
corner_num,
|
||||
components[0],
|
||||
components[1],
|
||||
components[2],
|
||||
gdk_color_get_alpha (color));
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -853,9 +883,9 @@ static void
|
||||
gsk_conic_gradient_node_add_patch (cairo_pattern_t *pattern,
|
||||
float radius,
|
||||
float start_angle,
|
||||
const GdkRGBA *start_color,
|
||||
const GdkColor *start_color,
|
||||
float end_angle,
|
||||
const GdkRGBA *end_color)
|
||||
const GdkColor *end_color)
|
||||
{
|
||||
double x, y;
|
||||
|
||||
@@ -868,47 +898,27 @@ gsk_conic_gradient_node_add_patch (cairo_pattern_t *pattern,
|
||||
cairo_mesh_pattern_line_to (pattern, x, y);
|
||||
cairo_mesh_pattern_line_to (pattern, 0, 0);
|
||||
|
||||
_cairo_mesh_pattern_set_corner_rgba (pattern, 0, start_color);
|
||||
_cairo_mesh_pattern_set_corner_rgba (pattern, 1, start_color);
|
||||
_cairo_mesh_pattern_set_corner_rgba (pattern, 2, end_color);
|
||||
_cairo_mesh_pattern_set_corner_rgba (pattern, 3, end_color);
|
||||
_cairo_mesh_pattern_set_corner_color (pattern, 0, start_color);
|
||||
_cairo_mesh_pattern_set_corner_color (pattern, 1, start_color);
|
||||
_cairo_mesh_pattern_set_corner_color (pattern, 2, end_color);
|
||||
_cairo_mesh_pattern_set_corner_color (pattern, 3, end_color);
|
||||
|
||||
cairo_mesh_pattern_end_patch (pattern);
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_rgba_color_interpolate (GdkRGBA *dest,
|
||||
const GdkRGBA *src1,
|
||||
const GdkRGBA *src2,
|
||||
double progress)
|
||||
{
|
||||
double alpha = src1->alpha * (1.0 - progress) + src2->alpha * progress;
|
||||
|
||||
dest->alpha = alpha;
|
||||
if (alpha == 0)
|
||||
{
|
||||
dest->red = src1->red * (1.0 - progress) + src2->red * progress;
|
||||
dest->green = src1->green * (1.0 - progress) + src2->green * progress;
|
||||
dest->blue = src1->blue * (1.0 - progress) + src2->blue * progress;
|
||||
}
|
||||
else
|
||||
{
|
||||
dest->red = (src1->red * src1->alpha * (1.0 - progress) + src2->red * src2->alpha * progress) / alpha;
|
||||
dest->green = (src1->green * src1->alpha * (1.0 - progress) + src2->green * src2->alpha * progress) / alpha;
|
||||
dest->blue = (src1->blue * src1->alpha * (1.0 - progress) + src2->blue * src2->alpha * progress) / alpha;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_conic_gradient_node_draw (GskRenderNode *node,
|
||||
cairo_t *cr)
|
||||
{
|
||||
GskConicGradientNode *self = (GskConicGradientNode *) node;
|
||||
GdkColorProfile *profile;
|
||||
cairo_pattern_t *pattern;
|
||||
graphene_point_t corner;
|
||||
float radius;
|
||||
gsize i;
|
||||
|
||||
profile = gdk_cairo_get_color_profile (cr);
|
||||
|
||||
pattern = cairo_pattern_create_mesh ();
|
||||
graphene_rect_get_top_right (&node->bounds, &corner);
|
||||
radius = graphene_point_distance (&self->center, &corner, NULL, NULL);
|
||||
@@ -923,26 +933,31 @@ gsk_conic_gradient_node_draw (GskRenderNode *node,
|
||||
{
|
||||
GskColorStop *stop1 = &self->stops[MAX (i, 1) - 1];
|
||||
GskColorStop *stop2 = &self->stops[MIN (i, self->n_stops - 1)];
|
||||
GdkColor stop1_color, stop2_color;
|
||||
double offset1 = i > 0 ? stop1->offset : 0;
|
||||
double offset2 = i < self->n_stops ? stop2->offset : 1;
|
||||
double start_angle, end_angle;
|
||||
|
||||
offset1 = offset1 * 360 + self->rotation - 90;
|
||||
offset2 = offset2 * 360 + self->rotation - 90;
|
||||
gdk_color_convert_rgba (&stop1_color, profile, &stop1->color);
|
||||
gdk_color_convert_rgba (&stop2_color, profile, &stop1->color);
|
||||
|
||||
for (start_angle = offset1; start_angle < offset2; start_angle = end_angle)
|
||||
{
|
||||
GdkRGBA start_color, end_color;
|
||||
GdkColor start_color, end_color;
|
||||
end_angle = (floor (start_angle / 45) + 1) * 45;
|
||||
end_angle = MIN (end_angle, offset2);
|
||||
gdk_rgba_color_interpolate (&start_color,
|
||||
&stop1->color,
|
||||
&stop2->color,
|
||||
(start_angle - offset1) / (offset2 - offset1));
|
||||
gdk_rgba_color_interpolate (&end_color,
|
||||
&stop1->color,
|
||||
&stop2->color,
|
||||
(end_angle - offset1) / (offset2 - offset1));
|
||||
gdk_color_mix (&start_color,
|
||||
profile,
|
||||
&stop1_color,
|
||||
&stop2_color,
|
||||
(start_angle - offset1) / (offset2 - offset1));
|
||||
gdk_color_mix (&end_color,
|
||||
profile,
|
||||
&stop1_color,
|
||||
&stop2_color,
|
||||
(end_angle - offset1) / (offset2 - offset1));
|
||||
|
||||
gsk_conic_gradient_node_add_patch (pattern,
|
||||
radius,
|
||||
@@ -951,6 +966,9 @@ gsk_conic_gradient_node_draw (GskRenderNode *node,
|
||||
DEG_TO_RAD (end_angle),
|
||||
&end_color);
|
||||
}
|
||||
|
||||
gdk_color_finish (&stop2_color);
|
||||
gdk_color_finish (&stop1_color);
|
||||
}
|
||||
|
||||
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
|
||||
@@ -1167,7 +1185,8 @@ struct _GskBorderNode
|
||||
|
||||
static void
|
||||
gsk_border_node_mesh_add_patch (cairo_pattern_t *pattern,
|
||||
const GdkRGBA *color,
|
||||
GdkColorProfile *profile,
|
||||
const GdkRGBA *rgba,
|
||||
double x0,
|
||||
double y0,
|
||||
double x1,
|
||||
@@ -1177,15 +1196,23 @@ gsk_border_node_mesh_add_patch (cairo_pattern_t *pattern,
|
||||
double x3,
|
||||
double y3)
|
||||
{
|
||||
GdkColor color;
|
||||
const float *components;
|
||||
float alpha;
|
||||
|
||||
gdk_color_convert_rgba (&color, profile, rgba);
|
||||
components = gdk_color_get_components (&color);
|
||||
alpha = gdk_color_get_alpha (&color);
|
||||
|
||||
cairo_mesh_pattern_begin_patch (pattern);
|
||||
cairo_mesh_pattern_move_to (pattern, x0, y0);
|
||||
cairo_mesh_pattern_line_to (pattern, x1, y1);
|
||||
cairo_mesh_pattern_line_to (pattern, x2, y2);
|
||||
cairo_mesh_pattern_line_to (pattern, x3, y3);
|
||||
cairo_mesh_pattern_set_corner_color_rgba (pattern, 0, color->red, color->green, color->blue, color->alpha);
|
||||
cairo_mesh_pattern_set_corner_color_rgba (pattern, 1, color->red, color->green, color->blue, color->alpha);
|
||||
cairo_mesh_pattern_set_corner_color_rgba (pattern, 2, color->red, color->green, color->blue, color->alpha);
|
||||
cairo_mesh_pattern_set_corner_color_rgba (pattern, 3, color->red, color->green, color->blue, color->alpha);
|
||||
cairo_mesh_pattern_set_corner_color_rgba (pattern, 0, components[0], components[1], components[2], alpha);
|
||||
cairo_mesh_pattern_set_corner_color_rgba (pattern, 1, components[0], components[1], components[2], alpha);
|
||||
cairo_mesh_pattern_set_corner_color_rgba (pattern, 2, components[0], components[1], components[2], alpha);
|
||||
cairo_mesh_pattern_set_corner_color_rgba (pattern, 3, components[0], components[1], components[2], alpha);
|
||||
cairo_mesh_pattern_end_patch (pattern);
|
||||
}
|
||||
|
||||
@@ -1231,11 +1258,13 @@ gsk_border_node_draw (GskRenderNode *node,
|
||||
* Note that the call to cairo_fill() will add the potential final
|
||||
* segment by closing the path, so we don't have to care.
|
||||
*/
|
||||
GdkColorProfile *profile;
|
||||
cairo_pattern_t *mesh;
|
||||
cairo_matrix_t mat;
|
||||
graphene_point_t tl, br;
|
||||
float scale;
|
||||
|
||||
profile = gdk_cairo_get_color_profile (cr);
|
||||
mesh = cairo_pattern_create_mesh ();
|
||||
cairo_matrix_init_translate (&mat, -bounds->origin.x, -bounds->origin.y);
|
||||
cairo_pattern_set_matrix (mesh, &mat);
|
||||
@@ -1253,6 +1282,7 @@ gsk_border_node_draw (GskRenderNode *node,
|
||||
if (self->border_width[0] > 0)
|
||||
{
|
||||
gsk_border_node_mesh_add_patch (mesh,
|
||||
profile,
|
||||
&self->border_color[0],
|
||||
0, 0,
|
||||
tl.x, tl.y,
|
||||
@@ -1264,6 +1294,7 @@ gsk_border_node_draw (GskRenderNode *node,
|
||||
if (self->border_width[1] > 0)
|
||||
{
|
||||
gsk_border_node_mesh_add_patch (mesh,
|
||||
profile,
|
||||
&self->border_color[1],
|
||||
bounds->size.width, 0,
|
||||
br.x, tl.y,
|
||||
@@ -1275,6 +1306,7 @@ gsk_border_node_draw (GskRenderNode *node,
|
||||
if (self->border_width[2] > 0)
|
||||
{
|
||||
gsk_border_node_mesh_add_patch (mesh,
|
||||
profile,
|
||||
&self->border_color[2],
|
||||
0, bounds->size.height,
|
||||
tl.x, br.y,
|
||||
@@ -1286,6 +1318,7 @@ gsk_border_node_draw (GskRenderNode *node,
|
||||
if (self->border_width[3] > 0)
|
||||
{
|
||||
gsk_border_node_mesh_add_patch (mesh,
|
||||
profile,
|
||||
&self->border_color[3],
|
||||
0, 0,
|
||||
tl.x, tl.y,
|
||||
@@ -1493,7 +1526,8 @@ gsk_texture_node_draw (GskRenderNode *node,
|
||||
cairo_pattern_t *pattern;
|
||||
cairo_matrix_t matrix;
|
||||
|
||||
surface = gdk_texture_download_surface (self->texture);
|
||||
surface = gdk_texture_download_surface (self->texture,
|
||||
gdk_cairo_get_color_profile (cr));
|
||||
pattern = cairo_pattern_create_for_surface (surface);
|
||||
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
|
||||
|
||||
@@ -4428,12 +4462,22 @@ gsk_text_node_finalize (GskRenderNode *node)
|
||||
parent_class->finalize (node);
|
||||
}
|
||||
|
||||
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
|
||||
|
||||
static void
|
||||
gsk_text_node_draw (GskRenderNode *node,
|
||||
cairo_t *cr)
|
||||
{
|
||||
GskTextNode *self = (GskTextNode *) node;
|
||||
PangoGlyphString glyphs;
|
||||
#ifdef HAVE_PANGOFT
|
||||
FT_Face face;
|
||||
FT_Bool darken = 1;
|
||||
FT_Parameter property = { FT_PARAM_TAG_STEM_DARKENING, &darken };
|
||||
|
||||
face = pango_fc_font_lock_face (PANGO_FC_FONT (self->font));
|
||||
FT_Face_Properties (face, 1, &property);
|
||||
#endif
|
||||
|
||||
glyphs.num_glyphs = self->num_glyphs;
|
||||
glyphs.glyphs = self->glyphs;
|
||||
@@ -4446,8 +4490,14 @@ gsk_text_node_draw (GskRenderNode *node,
|
||||
pango_cairo_show_glyph_string (cr, self->font, &glyphs);
|
||||
|
||||
cairo_restore (cr);
|
||||
|
||||
#ifdef HAVE_PANGOFT
|
||||
pango_fc_font_unlock_face (PANGO_FC_FONT (self->font));
|
||||
#endif
|
||||
}
|
||||
|
||||
G_GNUC_END_IGNORE_DEPRECATIONS
|
||||
|
||||
static void
|
||||
gsk_text_node_diff (GskRenderNode *node1,
|
||||
GskRenderNode *node2,
|
||||
|
@@ -1102,7 +1102,7 @@ parse_conic_gradient_node (GtkCssParser *parser)
|
||||
g_array_append_val (stops, to);
|
||||
}
|
||||
|
||||
result = gsk_conic_gradient_node_new (&bounds, ¢er, rotation,
|
||||
result = gsk_conic_gradient_node_new (&bounds, ¢er, rotation,
|
||||
(GskColorStop *) stops->data, stops->len);
|
||||
|
||||
g_array_free (stops, TRUE);
|
||||
@@ -1388,7 +1388,7 @@ parse_cairo_node (GtkCssParser *parser)
|
||||
parse_declarations (parser, declarations, G_N_ELEMENTS(declarations));
|
||||
|
||||
node = gsk_cairo_node_new (&bounds);
|
||||
|
||||
|
||||
if (surface != NULL)
|
||||
{
|
||||
cairo_t *cr = gsk_cairo_node_get_draw_context (node);
|
||||
@@ -1399,7 +1399,8 @@ parse_cairo_node (GtkCssParser *parser)
|
||||
else if (pixels != NULL)
|
||||
{
|
||||
cairo_t *cr = gsk_cairo_node_get_draw_context (node);
|
||||
surface = gdk_texture_download_surface (pixels);
|
||||
surface = gdk_texture_download_surface (pixels,
|
||||
gdk_texture_get_color_profile (pixels));
|
||||
cairo_set_source_surface (cr, surface, 0, 0);
|
||||
cairo_paint (cr);
|
||||
cairo_destroy (cr);
|
||||
@@ -2697,10 +2698,10 @@ render_node_print (Printer *p,
|
||||
start_node (p, "texture");
|
||||
append_rect_param (p, "bounds", &node->bounds);
|
||||
|
||||
bytes = gdk_texture_save_to_png_bytes (texture);
|
||||
bytes = gdk_texture_save_to_tiff_bytes (texture);
|
||||
|
||||
_indent (p);
|
||||
g_string_append (p->str, "texture: url(\"data:image/png;base64,");
|
||||
g_string_append (p->str, "texture: url(\"data:image/tiff;base64,");
|
||||
b64 = base64_encode_with_linebreaks (g_bytes_get_data (bytes, NULL),
|
||||
g_bytes_get_size (bytes));
|
||||
append_escaping_newlines (p->str, b64);
|
||||
|
@@ -19,6 +19,10 @@ gsk_private_gl_shaders = [
|
||||
'gl/resources/repeat.glsl',
|
||||
'gl/resources/custom.glsl',
|
||||
'gl/resources/filled_border.glsl',
|
||||
'gl/resources/postprocessing.glsl',
|
||||
'gl/resources/linearize.glsl',
|
||||
'gl/resources/premultiply.glsl',
|
||||
'gl/resources/linearize_premultiply.glsl',
|
||||
]
|
||||
|
||||
gsk_public_sources = files([
|
||||
|
@@ -342,7 +342,7 @@ gsk_vulkan_renderer_ref_texture_image (GskVulkanRenderer *self,
|
||||
if (data)
|
||||
return g_object_ref (data->image);
|
||||
|
||||
surface = gdk_texture_download_surface (texture);
|
||||
surface = gdk_texture_download_surface (texture,NULL);
|
||||
image = gsk_vulkan_image_new_from_data (uploader,
|
||||
cairo_image_surface_get_data (surface),
|
||||
cairo_image_surface_get_width (surface),
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@ fribidi_req = '>= 0.19.7'
|
||||
cairo_req = '>= 1.14.0'
|
||||
gdk_pixbuf_req = '>= 2.30.0'
|
||||
introspection_req = '>= 1.39.0'
|
||||
lcms2_req = '>= 2.8'
|
||||
wayland_proto_req = '>= 1.23'
|
||||
wayland_req = '>= 1.20.0'
|
||||
graphene_req = '>= 1.9.1'
|
||||
@@ -393,6 +394,8 @@ pango_dep = dependency('pango', version: pango_req,
|
||||
fallback : ['pango', 'libpango_dep'])
|
||||
fribidi_dep = dependency('fribidi', version: fribidi_req,
|
||||
fallback : ['fribidi', 'libfribidi_dep'])
|
||||
lcms2_dep = dependency('lcms2', version: lcms2_req,
|
||||
fallback : ['lcms2', 'liblcms2_dep'])
|
||||
|
||||
# Require PangoFT2 if on X11 or wayland
|
||||
require_pangoft2 = wayland_enabled or x11_enabled
|
||||
|
@@ -291,17 +291,20 @@ gtk_gst_sink_texture_from_buffer (GtkGstSink *self,
|
||||
GstGLSyncMeta *sync_meta;
|
||||
|
||||
sync_meta = gst_buffer_get_gl_sync_meta (buffer);
|
||||
if (sync_meta) {
|
||||
gst_gl_sync_meta_set_sync_point (sync_meta, self->gst_context);
|
||||
gst_gl_sync_meta_wait (sync_meta, self->gst_context);
|
||||
}
|
||||
if (sync_meta)
|
||||
{
|
||||
gst_gl_sync_meta_set_sync_point (sync_meta, self->gst_context);
|
||||
gst_gl_sync_meta_wait (sync_meta, self->gst_context);
|
||||
}
|
||||
|
||||
texture = gdk_gl_texture_new (self->gdk_context,
|
||||
*(guint *) frame->data[0],
|
||||
frame->info.width,
|
||||
frame->info.height,
|
||||
(GDestroyNotify) video_frame_free,
|
||||
frame);
|
||||
texture = gdk_gl_texture_new_with_color_profile (self->gdk_context,
|
||||
*(guint *) frame->data[0],
|
||||
frame->info.width,
|
||||
frame->info.height,
|
||||
0, /* Neither premultiplied nor flipped */
|
||||
gdk_color_profile_get_srgb (),
|
||||
(GDestroyNotify) video_frame_free,
|
||||
frame);
|
||||
|
||||
*pixel_aspect_ratio = ((double) frame->info.par_n) / ((double) frame->info.par_d);
|
||||
}
|
||||
|
6
subprojects/lcms2.wrap
Normal file
@@ -0,0 +1,6 @@
|
||||
[wrap-git]
|
||||
directory=lcms2
|
||||
url=https://github.com/mm2/Little-CMS.git
|
||||
revision=master
|
||||
patch_directory=lcms2
|
||||
depth=1
|
14
subprojects/packagefiles/lcms2/meson.build
Normal 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')
|
@@ -148,3 +148,12 @@ if libsysprof_dep.found()
|
||||
dependencies: [libsysprof_dep, platform_gio_dep, libm],
|
||||
)
|
||||
endif
|
||||
|
||||
libheif_dep = dependency('libheif')
|
||||
|
||||
if libheif_dep.found()
|
||||
executable('testheif',
|
||||
sources: 'testheif.c',
|
||||
dependencies: [libgtk_dep, libheif_dep],
|
||||
)
|
||||
endif
|
||||
|
290
tests/testheif.c
Normal file
@@ -0,0 +1,290 @@
|
||||
#include <gtk/gtk.h>
|
||||
#include <libheif/heif.h>
|
||||
#include <lcms2.h>
|
||||
|
||||
static void
|
||||
describe_nclx_color_profile (const struct heif_color_profile_nclx *nclx,
|
||||
GString *s)
|
||||
{
|
||||
if (nclx->color_primaries == heif_color_primaries_unspecified)
|
||||
return;
|
||||
|
||||
if (nclx->color_primaries == heif_color_primaries_ITU_R_BT_709_5)
|
||||
{
|
||||
if (nclx->transfer_characteristics == heif_transfer_characteristic_IEC_61966_2_1 &&
|
||||
(nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_470_6_System_B_G ||
|
||||
nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_601_6))
|
||||
{
|
||||
g_string_append (s, "sRGB");
|
||||
return;
|
||||
}
|
||||
|
||||
if (nclx->transfer_characteristics == heif_transfer_characteristic_linear &&
|
||||
(nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_470_6_System_B_G ||
|
||||
nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_601_6))
|
||||
{
|
||||
g_string_append (s, "sRGB linear");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (nclx->color_primaries == heif_color_primaries_ITU_R_BT_2020_2_and_2100_0)
|
||||
{
|
||||
if (nclx->transfer_characteristics == heif_transfer_characteristic_ITU_R_BT_2100_0_PQ &&
|
||||
nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_2020_2_non_constant_luminance)
|
||||
{
|
||||
g_string_append (s, "BT.2020 PQ");
|
||||
return;
|
||||
}
|
||||
|
||||
if (nclx->transfer_characteristics == heif_transfer_characteristic_ITU_R_BT_2100_0_HLG &&
|
||||
nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_2020_2_non_constant_luminance)
|
||||
{
|
||||
g_string_append (s, "BT.2020 HLG");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (nclx->color_primaries == heif_color_primaries_SMPTE_EG_432_1)
|
||||
{
|
||||
if (nclx->transfer_characteristics == heif_transfer_characteristic_ITU_R_BT_2100_0_PQ)
|
||||
{
|
||||
g_string_append (s, "P3 PQ");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
g_string_append_printf (s, "%d/%d/%d",
|
||||
nclx->color_primaries,
|
||||
nclx->matrix_coefficients,
|
||||
nclx->transfer_characteristics);
|
||||
}
|
||||
|
||||
static GdkTexture *
|
||||
load_heif_image (const char *filename,
|
||||
GString *details,
|
||||
GError **error)
|
||||
{
|
||||
struct heif_context *ctx;
|
||||
struct heif_error err;
|
||||
struct heif_image_handle *handle = NULL;
|
||||
struct heif_image *image = NULL;
|
||||
enum heif_chroma chroma;
|
||||
GdkMemoryFormat format;
|
||||
const char *format_name;
|
||||
uint8_t *data = NULL;
|
||||
int width, height, bpp, stride, bits;
|
||||
GBytes *bytes;
|
||||
GdkTexture *texture = NULL;
|
||||
GdkColorProfile *profile = NULL;
|
||||
char *profile_type = NULL;
|
||||
|
||||
ctx = heif_context_alloc ();
|
||||
|
||||
err = heif_context_read_from_file (ctx, filename, NULL);
|
||||
|
||||
if (err.code != heif_error_Ok)
|
||||
{
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, err.message);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = heif_context_get_primary_image_handle (ctx, &handle);
|
||||
if (err.code != heif_error_Ok)
|
||||
{
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, err.message);
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (heif_image_handle_get_color_profile_type (handle))
|
||||
{
|
||||
case heif_color_profile_type_not_present:
|
||||
break;
|
||||
case heif_color_profile_type_rICC:
|
||||
case heif_color_profile_type_prof:
|
||||
{
|
||||
guchar *icc_data;
|
||||
gsize icc_size;
|
||||
|
||||
profile_type = g_strdup ("icc");
|
||||
icc_size = heif_image_handle_get_raw_color_profile_size (handle);
|
||||
icc_data = g_new0 (guchar, icc_size);
|
||||
err = heif_image_handle_get_raw_color_profile (handle, icc_data);
|
||||
if (err.code == heif_error_Ok)
|
||||
{
|
||||
bytes = g_bytes_new (icc_data, icc_size);
|
||||
profile = gdk_color_profile_new_from_icc_bytes (bytes, NULL);
|
||||
g_bytes_unref (bytes);
|
||||
}
|
||||
g_free (icc_data);
|
||||
}
|
||||
break;
|
||||
case heif_color_profile_type_nclx:
|
||||
{
|
||||
struct heif_color_profile_nclx *nclx = NULL;
|
||||
|
||||
err = heif_image_handle_get_nclx_color_profile (handle, &nclx);
|
||||
if (err.code == heif_error_Ok)
|
||||
{
|
||||
GString *s;
|
||||
|
||||
s = g_string_new ("");
|
||||
describe_nclx_color_profile (nclx, s);
|
||||
profile_type = g_string_free (s, FALSE);
|
||||
profile = gdk_color_profile_new_from_cicp (nclx->color_primaries,
|
||||
nclx->transfer_characteristics,
|
||||
0,
|
||||
TRUE,
|
||||
NULL);
|
||||
heif_nclx_color_profile_free (nclx);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
g_string_append_printf (details, "%d × %d pixels\n%d bits of luma, %d bits of chroma%s\n",
|
||||
heif_image_handle_get_width (handle),
|
||||
heif_image_handle_get_height (handle),
|
||||
heif_image_handle_get_luma_bits_per_pixel (handle),
|
||||
heif_image_handle_get_chroma_bits_per_pixel (handle),
|
||||
heif_image_handle_has_alpha_channel (handle) ? ", with alpha" : "");
|
||||
|
||||
if (profile_type)
|
||||
{
|
||||
g_string_append_printf (details, "color profile: %s\n", profile_type);
|
||||
g_free (profile_type);
|
||||
}
|
||||
|
||||
if (profile == NULL)
|
||||
profile = g_object_ref (gdk_color_profile_get_srgb ());
|
||||
|
||||
if (heif_image_handle_has_alpha_channel (handle))
|
||||
{
|
||||
if (heif_image_handle_get_luma_bits_per_pixel (handle) > 8 ||
|
||||
heif_image_handle_get_chroma_bits_per_pixel (handle) > 8)
|
||||
{
|
||||
chroma = heif_chroma_interleaved_RRGGBBAA_BE;
|
||||
format = GDK_MEMORY_R16G16B16A16;
|
||||
format_name = "R<sub>16</sub>G<sub>16</sub>B<sub>16</sub>A<sub>16</sub>";
|
||||
}
|
||||
else
|
||||
{
|
||||
chroma = heif_chroma_interleaved_RGBA;
|
||||
format = GDK_MEMORY_R8G8B8A8;
|
||||
format_name = "R<sub>8</sub>G<sub>8</sub>B<sub>8</sub>A<sub>8</sub>";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (heif_image_handle_get_luma_bits_per_pixel (handle) > 8 ||
|
||||
heif_image_handle_get_chroma_bits_per_pixel (handle) > 8)
|
||||
{
|
||||
chroma = heif_chroma_interleaved_RRGGBB_BE;
|
||||
format = GDK_MEMORY_R16G16B16;
|
||||
format_name = "R<sub>16</sub>G<sub>16</sub>B<sub>16</sub>";
|
||||
}
|
||||
else
|
||||
{
|
||||
chroma = heif_chroma_interleaved_RGB;
|
||||
format = GDK_MEMORY_R8G8B8;
|
||||
format_name = "R<sub>8</sub>G<sub>8</sub>B<sub>8</sub>";
|
||||
}
|
||||
}
|
||||
|
||||
err = heif_decode_image (handle, &image, heif_colorspace_RGB, chroma, NULL);
|
||||
if (err.code != heif_error_Ok)
|
||||
{
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, err.message);
|
||||
goto out;
|
||||
}
|
||||
|
||||
width = heif_image_get_width (image, heif_channel_interleaved);
|
||||
height = heif_image_get_height (image, heif_channel_interleaved);
|
||||
bpp = heif_image_get_bits_per_pixel (image, heif_channel_interleaved);
|
||||
data = (uint8_t*)heif_image_get_plane_readonly (image, heif_channel_interleaved, &stride);
|
||||
bits = heif_image_get_bits_per_pixel_range (image, heif_channel_interleaved);
|
||||
|
||||
g_string_append_printf (details, "texture format %s", format_name);
|
||||
|
||||
g_print ("%s\n", details->str);
|
||||
|
||||
if (format == GDK_MEMORY_R16G16B16A16 || format == GDK_MEMORY_R16G16B16)
|
||||
{
|
||||
/* Shift image data to full 16bit range */
|
||||
int shift = 16 - bits;
|
||||
if (shift > 0)
|
||||
{
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
uint8_t *row = data + y * stride;
|
||||
|
||||
for (int x = 0; x < stride; x += 2)
|
||||
{
|
||||
uint8_t* p = &row[x];
|
||||
int v = (p[0] << 8) | p[1];
|
||||
v = (v << shift) | (v >> (16 - shift));
|
||||
p[1] = (uint8_t) (v >> 8);
|
||||
p[0] = (uint8_t) (v & 0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bytes = g_bytes_new_with_free_func (data, height * stride * bpp, (GDestroyNotify)heif_image_release, image);
|
||||
|
||||
image = NULL;
|
||||
|
||||
texture = gdk_memory_texture_new_with_color_profile (width, height,
|
||||
format, profile,
|
||||
bytes, stride);
|
||||
g_bytes_unref (bytes);
|
||||
g_object_unref (profile);
|
||||
|
||||
out:
|
||||
heif_context_free (ctx);
|
||||
if (handle)
|
||||
heif_image_handle_release (handle);
|
||||
if (image)
|
||||
heif_image_release (image);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
GdkTexture *texture;
|
||||
GError *error = NULL;
|
||||
GtkWidget *window, *picture, *box, *label;
|
||||
GString *details;
|
||||
|
||||
gtk_init ();
|
||||
|
||||
details = g_string_new ("");
|
||||
|
||||
texture = load_heif_image (argv[1], details, &error);
|
||||
if (!texture)
|
||||
g_error ("%s", error->message);
|
||||
|
||||
window = gtk_window_new ();
|
||||
|
||||
gtk_window_set_title (GTK_WINDOW (window), argv[1]);
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 1024, 768);
|
||||
|
||||
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10);
|
||||
picture = gtk_picture_new_for_paintable (GDK_PAINTABLE (texture));
|
||||
gtk_box_append (GTK_BOX (box), picture);
|
||||
label = gtk_label_new (details->str);
|
||||
gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
|
||||
gtk_widget_set_margin_bottom (label, 10);
|
||||
gtk_box_append (GTK_BOX (box), label);
|
||||
gtk_window_set_child (GTK_WINDOW (window), box);
|
||||
|
||||
g_string_free (details, TRUE);
|
||||
|
||||
gtk_window_present (GTK_WINDOW (window));
|
||||
|
||||
while (1)
|
||||
g_main_context_iteration (NULL, TRUE);
|
||||
}
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 507 B After Width: | Height: | Size: 860 B |
Before Width: | Height: | Size: 221 B After Width: | Height: | Size: 573 B |
Before Width: | Height: | Size: 115 B After Width: | Height: | Size: 472 B |
Before Width: | Height: | Size: 407 B After Width: | Height: | Size: 761 B |