diff --git a/demos/gtk-demo/demo.gresource.xml b/demos/gtk-demo/demo.gresource.xml
index 2998560b02..295c056f0b 100644
--- a/demos/gtk-demo/demo.gresource.xml
+++ b/demos/gtk-demo/demo.gresource.xml
@@ -454,6 +454,9 @@
icons/16x16/categories/applications-other.png
icons/48x48/status/starred.png
data/scalable/apps/org.gtk.Demo4.svg
+ portland-rose-thumbnail.png
+ large-image-thumbnail.png
+ large-image.png
help-overlay.ui
diff --git a/demos/gtk-demo/image_scaling.c b/demos/gtk-demo/image_scaling.c
index 65c344f8bf..7a541292b9 100644
--- a/demos/gtk-demo/image_scaling.c
+++ b/demos/gtk-demo/image_scaling.c
@@ -14,6 +14,103 @@
#include
#include "demo3widget.h"
+static GtkWidget *window = NULL;
+static GCancellable *cancellable = NULL;
+
+static void
+load_texture (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cable)
+{
+ GFile *file = task_data;
+ GdkTexture *texture;
+ GError *error = NULL;
+
+ texture = gdk_texture_new_from_file (file, &error);
+
+ if (texture)
+ g_task_return_pointer (task, texture, g_object_unref);
+ else
+ g_task_return_error (task, error);
+}
+
+static void
+set_wait_cursor (GtkWidget *widget)
+{
+ gtk_widget_set_cursor_from_name (GTK_WIDGET (gtk_widget_get_root (widget)), "wait");
+}
+
+static void
+unset_wait_cursor (GtkWidget *widget)
+{
+ gtk_widget_set_cursor (GTK_WIDGET (gtk_widget_get_root (widget)), NULL);
+}
+
+static void
+texture_loaded (GObject *source,
+ GAsyncResult *result,
+ gpointer data)
+{
+ GdkTexture *texture;
+ GError *error = NULL;
+
+ texture = g_task_propagate_pointer (G_TASK (result), &error);
+
+ if (!texture)
+ {
+ g_print ("%s\n", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ if (!window)
+ {
+ g_object_unref (texture);
+ return;
+ }
+
+ unset_wait_cursor (GTK_WIDGET (data));
+
+ g_object_set (G_OBJECT (data), "texture", texture, NULL);
+}
+
+static void
+open_file_async (GFile *file,
+ GtkWidget *demo)
+{
+ GTask *task;
+
+ set_wait_cursor (demo);
+
+ task = g_task_new (demo, cancellable, texture_loaded, demo);
+ g_task_set_task_data (task, g_object_ref (file), g_object_unref);
+ g_task_run_in_thread (task, load_texture);
+ g_object_unref (task);
+}
+
+static void
+open_portland_rose (GtkWidget *button,
+ GtkWidget *demo)
+{
+ GFile *file;
+
+ file = g_file_new_for_uri ("resource:///transparent/portland-rose.jpg");
+ open_file_async (file, demo);
+ g_object_unref (file);
+}
+
+static void
+open_large_image (GtkWidget *button,
+ GtkWidget *demo)
+{
+ GFile *file;
+
+ file = g_file_new_for_uri ("resource:///org/gtk/Demo4/large-image.png");
+ open_file_async (file, demo);
+ g_object_unref (file);
+}
+
static void
file_opened (GObject *source,
GAsyncResult *result,
@@ -21,7 +118,6 @@ file_opened (GObject *source,
{
GFile *file;
GError *error = NULL;
- GdkTexture *texture;
file = gtk_file_dialog_open_finish (GTK_FILE_DIALOG (source), result, &error);
@@ -32,17 +128,9 @@ file_opened (GObject *source,
return;
}
- texture = gdk_texture_new_from_file (file, &error);
- g_object_unref (file);
- if (!texture)
- {
- g_print ("%s\n", error->message);
- g_error_free (error);
- return;
- }
+ open_file_async (file, data);
- g_object_set (G_OBJECT (data), "texture", texture, NULL);
- g_object_unref (texture);
+ g_object_unref (file);
}
static void
@@ -116,11 +204,26 @@ transform_from (GBinding *binding,
return TRUE;
}
+static void
+free_cancellable (gpointer data)
+{
+ g_cancellable_cancel (cancellable);
+ g_clear_object (&cancellable);
+}
+
+static gboolean
+cancel_load (GtkWidget *widget,
+ GVariant *args,
+ gpointer data)
+{
+ unset_wait_cursor (widget);
+ g_cancellable_cancel (G_CANCELLABLE (data));
+ return TRUE;
+}
+
GtkWidget *
do_image_scaling (GtkWidget *do_widget)
{
- static GtkWidget *window = NULL;
-
if (!window)
{
GtkWidget *box;
@@ -130,6 +233,7 @@ do_image_scaling (GtkWidget *do_widget)
GtkWidget *scale;
GtkWidget *dropdown;
GtkWidget *button;
+ GtkEventController *controller;
window = gtk_window_new ();
gtk_window_set_title (GTK_WINDOW (window), "Image Scaling");
@@ -138,6 +242,20 @@ do_image_scaling (GtkWidget *do_widget)
gtk_widget_get_display (do_widget));
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
+ cancellable = g_cancellable_new ();
+ g_object_set_data_full (G_OBJECT (window), "cancellable",
+ cancellable, free_cancellable);
+
+ controller = gtk_shortcut_controller_new ();
+ gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (controller),
+ gtk_shortcut_new (
+ gtk_keyval_trigger_new (GDK_KEY_Escape, 0),
+ gtk_callback_action_new (cancel_load, cancellable, NULL)
+ ));
+ gtk_shortcut_controller_set_scope (GTK_SHORTCUT_CONTROLLER (controller),
+ GTK_SHORTCUT_SCOPE_GLOBAL);
+ gtk_widget_add_controller (window, controller);
+
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_window_set_child (GTK_WINDOW (window), box);
@@ -156,6 +274,22 @@ do_image_scaling (GtkWidget *do_widget)
g_signal_connect (button, "clicked", G_CALLBACK (open_file), widget);
gtk_box_append (GTK_BOX (box2), button);
+ button = gtk_button_new ();
+ gtk_button_set_child (GTK_BUTTON (button),
+ gtk_image_new_from_resource ("/org/gtk/Demo4/portland-rose-thumbnail.png"));
+ gtk_widget_add_css_class (button, "image-button");
+ gtk_widget_set_tooltip_text (button, "Portland Rose");
+ g_signal_connect (button, "clicked", G_CALLBACK (open_portland_rose), widget);
+ gtk_box_append (GTK_BOX (box2), button);
+
+ button = gtk_button_new ();
+ gtk_button_set_child (GTK_BUTTON (button),
+ gtk_image_new_from_resource ("/org/gtk/Demo4/large-image-thumbnail.png"));
+ gtk_widget_add_css_class (button, "image-button");
+ gtk_widget_set_tooltip_text (button, "Large image");
+ g_signal_connect (button, "clicked", G_CALLBACK (open_large_image), widget);
+ gtk_box_append (GTK_BOX (box2), button);
+
button = gtk_button_new_from_icon_name ("object-rotate-right-symbolic");
gtk_widget_set_tooltip_text (button, "Rotate");
g_signal_connect (button, "clicked", G_CALLBACK (rotate), widget);
@@ -191,7 +325,9 @@ do_image_scaling (GtkWidget *do_widget)
if (!gtk_widget_get_visible (window))
gtk_widget_set_visible (window, TRUE);
else
- gtk_window_destroy (GTK_WINDOW (window));
+ {
+ gtk_window_destroy (GTK_WINDOW (window));
+ }
return window;
}
diff --git a/demos/gtk-demo/large-image-thumbnail.png b/demos/gtk-demo/large-image-thumbnail.png
new file mode 100644
index 0000000000..d383d8bbf4
Binary files /dev/null and b/demos/gtk-demo/large-image-thumbnail.png differ
diff --git a/demos/gtk-demo/large-image.png b/demos/gtk-demo/large-image.png
new file mode 100644
index 0000000000..0ddeaef501
Binary files /dev/null and b/demos/gtk-demo/large-image.png differ
diff --git a/demos/gtk-demo/portland-rose-thumbnail.png b/demos/gtk-demo/portland-rose-thumbnail.png
new file mode 100644
index 0000000000..d3838ddb17
Binary files /dev/null and b/demos/gtk-demo/portland-rose-thumbnail.png differ
diff --git a/gdk/gdkmemoryformat.c b/gdk/gdkmemoryformat.c
index 6f513d8737..01b1859224 100644
--- a/gdk/gdkmemoryformat.c
+++ b/gdk/gdkmemoryformat.c
@@ -22,14 +22,26 @@
#include "gdkmemoryformatprivate.h"
#include "gdkdmabuffourccprivate.h"
-#include "gdkglcontextprivate.h"
#include "gdkcolorstateprivate.h"
+#include "gdkparalleltaskprivate.h"
#include "gtk/gtkcolorutilsprivate.h"
+#include "gdkprofilerprivate.h"
#include "gsk/gl/fp16private.h"
#include
+/* Don't report quick (< 0.5 msec) runs */
+#define MIN_MARK_DURATION 500000
+
+#define ADD_MARK(before,name,fmt,...) \
+ if (GDK_PROFILER_IS_RUNNING) \
+ { \
+ gint64 duration = GDK_PROFILER_CURRENT_TIME - before; \
+ if (duration > MIN_MARK_DURATION) \
+ gdk_profiler_add_markf (before, duration, name, fmt, __VA_ARGS__); \
+ }
+
G_STATIC_ASSERT ((1 << GDK_MEMORY_DEPTH_BITS) > GDK_N_DEPTHS);
typedef struct _GdkMemoryFormatDescription GdkMemoryFormatDescription;
@@ -313,6 +325,88 @@ 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)
+#define MIPMAP_FUNC(SumType, DataType, n_units) \
+static void \
+gdk_mipmap_ ## DataType ## _ ## n_units ## _nearest (guchar *dest, \
+ gsize dest_stride, \
+ const guchar *src, \
+ gsize src_stride, \
+ gsize src_width, \
+ gsize src_height, \
+ guint lod_level) \
+{ \
+ gsize y, x, i; \
+ gsize n = 1 << lod_level; \
+\
+ for (y = 0; y < src_height; y += n) \
+ { \
+ DataType *dest_data = (DataType *) dest; \
+ for (x = 0; x < src_width; x += n) \
+ { \
+ const DataType *src_data = (const DataType *) (src + (y + MIN (n / 2, src_height - y)) * src_stride); \
+\
+ for (i = 0; i < n_units; i++) \
+ *dest_data++ = src_data[n_units * (x + MIN (n / 2, src_width - n_units)) + i]; \
+ } \
+ dest += dest_stride; \
+ src += src_stride * n; \
+ } \
+} \
+\
+static void \
+gdk_mipmap_ ## DataType ## _ ## n_units ## _linear (guchar *dest, \
+ gsize dest_stride, \
+ const guchar *src, \
+ gsize src_stride, \
+ gsize src_width, \
+ gsize src_height, \
+ guint lod_level) \
+{ \
+ gsize y_dest, y, x_dest, x, i; \
+ gsize n = 1 << lod_level; \
+\
+ for (y_dest = 0; y_dest < src_height; y_dest += n) \
+ { \
+ DataType *dest_data = (DataType *) dest; \
+ for (x_dest = 0; x_dest < src_width; x_dest += n) \
+ { \
+ SumType tmp[n_units] = { 0, }; \
+\
+ for (y = 0; y < MIN (n, src_height - y_dest); y++) \
+ { \
+ const DataType *src_data = (const DataType *) (src + y * src_stride); \
+ for (x = 0; x < MIN (n, src_width - x_dest); x++) \
+ { \
+ for (i = 0; i < n_units; i++) \
+ tmp[i] += src_data[n_units * (x_dest + x) + i]; \
+ } \
+ } \
+\
+ for (i = 0; i < n_units; i++) \
+ *dest_data++ = tmp[i] / (x * y); \
+ } \
+ dest += dest_stride; \
+ src += src_stride * n; \
+ } \
+}
+
+MIPMAP_FUNC(guint32, guint8, 1)
+MIPMAP_FUNC(guint32, guint8, 2)
+MIPMAP_FUNC(guint32, guint8, 3)
+MIPMAP_FUNC(guint32, guint8, 4)
+MIPMAP_FUNC(guint32, guint16, 1)
+MIPMAP_FUNC(guint32, guint16, 2)
+MIPMAP_FUNC(guint32, guint16, 3)
+MIPMAP_FUNC(guint32, guint16, 4)
+MIPMAP_FUNC(float, float, 1)
+MIPMAP_FUNC(float, float, 3)
+MIPMAP_FUNC(float, float, 4)
+#define half_float guint16
+MIPMAP_FUNC(float, half_float, 1)
+MIPMAP_FUNC(float, half_float, 3)
+MIPMAP_FUNC(float, half_float, 4)
+#undef half_float
+
struct _GdkMemoryFormatDescription
{
const char *name;
@@ -346,6 +440,8 @@ struct _GdkMemoryFormatDescription
/* no premultiplication going on here */
void (* to_float) (float (*)[4], const guchar*, gsize);
void (* from_float) (guchar *, const float (*)[4], gsize);
+ void (* mipmap_nearest) (guchar *, gsize, const guchar *, gsize, gsize, gsize, guint);
+ void (* mipmap_linear) (guchar *, gsize, const guchar *, gsize, gsize, gsize, guint);
};
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
@@ -387,6 +483,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = b8g8r8a8_premultiplied_to_float,
.from_float = b8g8r8a8_premultiplied_from_float,
+ .mipmap_nearest = gdk_mipmap_guint8_4_nearest,
+ .mipmap_linear = gdk_mipmap_guint8_4_linear,
},
[GDK_MEMORY_A8R8G8B8_PREMULTIPLIED] = {
.name = "ARGB8(p)",
@@ -418,6 +516,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = a8r8g8b8_premultiplied_to_float,
.from_float = a8r8g8b8_premultiplied_from_float,
+ .mipmap_nearest = gdk_mipmap_guint8_4_nearest,
+ .mipmap_linear = gdk_mipmap_guint8_4_linear,
},
[GDK_MEMORY_R8G8B8A8_PREMULTIPLIED] = {
.name = "RGBA8(p)",
@@ -448,6 +548,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = r8g8b8a8_premultiplied_to_float,
.from_float = r8g8b8a8_premultiplied_from_float,
+ .mipmap_nearest = gdk_mipmap_guint8_4_nearest,
+ .mipmap_linear = gdk_mipmap_guint8_4_linear,
},
[GDK_MEMORY_A8B8G8R8_PREMULTIPLIED] = {
.name = "ABGR8(p)",
@@ -479,6 +581,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = a8b8g8r8_premultiplied_to_float,
.from_float = a8b8g8r8_premultiplied_from_float,
+ .mipmap_nearest = gdk_mipmap_guint8_4_nearest,
+ .mipmap_linear = gdk_mipmap_guint8_4_linear,
},
[GDK_MEMORY_B8G8R8A8] = {
.name = "BGRA8",
@@ -510,6 +614,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = b8g8r8a8_to_float,
.from_float = b8g8r8a8_from_float,
+ .mipmap_nearest = gdk_mipmap_guint8_4_nearest,
+ .mipmap_linear = gdk_mipmap_guint8_4_linear,
},
[GDK_MEMORY_A8R8G8B8] = {
.name = "ARGB8",
@@ -541,6 +647,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = a8r8g8b8_to_float,
.from_float = a8r8g8b8_from_float,
+ .mipmap_nearest = gdk_mipmap_guint8_4_nearest,
+ .mipmap_linear = gdk_mipmap_guint8_4_linear,
},
[GDK_MEMORY_R8G8B8A8] = {
.name = "RGBA8",
@@ -571,6 +679,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = r8g8b8a8_to_float,
.from_float = r8g8b8a8_from_float,
+ .mipmap_nearest = gdk_mipmap_guint8_4_nearest,
+ .mipmap_linear = gdk_mipmap_guint8_4_linear,
},
[GDK_MEMORY_A8B8G8R8] = {
.name = "ABGR8",
@@ -602,6 +712,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = a8b8g8r8_to_float,
.from_float = a8b8g8r8_from_float,
+ .mipmap_nearest = gdk_mipmap_guint8_4_nearest,
+ .mipmap_linear = gdk_mipmap_guint8_4_linear,
},
[GDK_MEMORY_B8G8R8X8] = {
.name = "BGRX8",
@@ -634,6 +746,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = b8g8r8x8_to_float,
.from_float = b8g8r8x8_from_float,
+ .mipmap_nearest = gdk_mipmap_guint8_4_nearest,
+ .mipmap_linear = gdk_mipmap_guint8_4_linear,
},
[GDK_MEMORY_X8R8G8B8] = {
.name = "XRGB8",
@@ -666,6 +780,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = x8r8g8b8_to_float,
.from_float = x8r8g8b8_from_float,
+ .mipmap_nearest = gdk_mipmap_guint8_4_nearest,
+ .mipmap_linear = gdk_mipmap_guint8_4_linear,
},
[GDK_MEMORY_R8G8B8X8] = {
.name = "RGBX8",
@@ -697,6 +813,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = r8g8b8x8_to_float,
.from_float = r8g8b8x8_from_float,
+ .mipmap_nearest = gdk_mipmap_guint8_4_nearest,
+ .mipmap_linear = gdk_mipmap_guint8_4_linear,
},
[GDK_MEMORY_X8B8G8R8] = {
.name = "XBGR8",
@@ -729,6 +847,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = x8b8g8r8_to_float,
.from_float = x8b8g8r8_from_float,
+ .mipmap_nearest = gdk_mipmap_guint8_4_nearest,
+ .mipmap_linear = gdk_mipmap_guint8_4_linear,
},
[GDK_MEMORY_R8G8B8] = {
.name = "RGB8",
@@ -760,6 +880,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = r8g8b8_to_float,
.from_float = r8g8b8_from_float,
+ .mipmap_nearest = gdk_mipmap_guint8_3_nearest,
+ .mipmap_linear = gdk_mipmap_guint8_3_linear,
},
[GDK_MEMORY_B8G8R8] = {
.name = "BGR8",
@@ -792,6 +914,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = b8g8r8_to_float,
.from_float = b8g8r8_from_float,
+ .mipmap_nearest = gdk_mipmap_guint8_3_nearest,
+ .mipmap_linear = gdk_mipmap_guint8_3_linear,
},
[GDK_MEMORY_R16G16B16] = {
.name = "RGB16",
@@ -826,6 +950,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = r16g16b16_to_float,
.from_float = r16g16b16_from_float,
+ .mipmap_nearest = gdk_mipmap_guint16_3_nearest,
+ .mipmap_linear = gdk_mipmap_guint16_3_linear,
},
[GDK_MEMORY_R16G16B16A16_PREMULTIPLIED] = {
.name = "RGBA16(p)",
@@ -859,6 +985,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = r16g16b16a16_to_float,
.from_float = r16g16b16a16_from_float,
+ .mipmap_nearest = gdk_mipmap_guint16_4_nearest,
+ .mipmap_linear = gdk_mipmap_guint16_4_linear,
},
[GDK_MEMORY_R16G16B16A16] = {
.name = "RGBA16",
@@ -892,6 +1020,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = r16g16b16a16_to_float,
.from_float = r16g16b16a16_from_float,
+ .mipmap_nearest = gdk_mipmap_guint16_4_nearest,
+ .mipmap_linear = gdk_mipmap_guint16_4_linear,
},
[GDK_MEMORY_R16G16B16_FLOAT] = {
.name = "RGBA16f",
@@ -925,6 +1055,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = r16g16b16_float_to_float,
.from_float = r16g16b16_float_from_float,
+ .mipmap_nearest = gdk_mipmap_half_float_3_nearest,
+ .mipmap_linear = gdk_mipmap_half_float_3_linear,
},
[GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED] = {
.name = "RGBA16f(p)",
@@ -957,6 +1089,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = r16g16b16a16_float_to_float,
.from_float = r16g16b16a16_float_from_float,
+ .mipmap_nearest = gdk_mipmap_half_float_4_nearest,
+ .mipmap_linear = gdk_mipmap_half_float_4_linear,
},
[GDK_MEMORY_R16G16B16A16_FLOAT] = {
.name = "RGBA16f",
@@ -989,6 +1123,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = r16g16b16a16_float_to_float,
.from_float = r16g16b16a16_float_from_float,
+ .mipmap_nearest = gdk_mipmap_half_float_4_nearest,
+ .mipmap_linear = gdk_mipmap_half_float_4_linear,
},
[GDK_MEMORY_R32G32B32_FLOAT] = {
.name = "RGB32f",
@@ -1022,6 +1158,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = r32g32b32_float_to_float,
.from_float = r32g32b32_float_from_float,
+ .mipmap_nearest = gdk_mipmap_float_3_nearest,
+ .mipmap_linear = gdk_mipmap_float_3_linear,
},
[GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED] = {
.name = "RGBA32f(p)",
@@ -1054,6 +1192,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = r32g32b32a32_float_to_float,
.from_float = r32g32b32a32_float_from_float,
+ .mipmap_nearest = gdk_mipmap_float_4_nearest,
+ .mipmap_linear = gdk_mipmap_float_4_linear,
},
[GDK_MEMORY_R32G32B32A32_FLOAT] = {
.name = "RGBA32f",
@@ -1086,6 +1226,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = r32g32b32a32_float_to_float,
.from_float = r32g32b32a32_float_from_float,
+ .mipmap_nearest = gdk_mipmap_float_4_nearest,
+ .mipmap_linear = gdk_mipmap_float_4_linear,
},
[GDK_MEMORY_G8A8_PREMULTIPLIED] = {
.name = "GA8(p)",
@@ -1117,6 +1259,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = g8a8_premultiplied_to_float,
.from_float = g8a8_premultiplied_from_float,
+ .mipmap_nearest = gdk_mipmap_guint8_2_nearest,
+ .mipmap_linear = gdk_mipmap_guint8_2_linear,
},
[GDK_MEMORY_G8A8] = {
.name = "GA8",
@@ -1148,6 +1292,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = g8a8_to_float,
.from_float = g8a8_from_float,
+ .mipmap_nearest = gdk_mipmap_guint8_2_nearest,
+ .mipmap_linear = gdk_mipmap_guint8_2_linear,
},
[GDK_MEMORY_G8] = {
.name = "G8",
@@ -1179,6 +1325,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = g8_to_float,
.from_float = g8_from_float,
+ .mipmap_nearest = gdk_mipmap_guint8_1_nearest,
+ .mipmap_linear = gdk_mipmap_guint8_1_linear,
},
[GDK_MEMORY_G16A16_PREMULTIPLIED] = {
.name = "GA16(p)",
@@ -1213,6 +1361,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = g16a16_premultiplied_to_float,
.from_float = g16a16_premultiplied_from_float,
+ .mipmap_nearest = gdk_mipmap_guint16_2_nearest,
+ .mipmap_linear = gdk_mipmap_guint16_2_linear,
},
[GDK_MEMORY_G16A16] = {
.name = "GA16",
@@ -1247,6 +1397,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = g16a16_to_float,
.from_float = g16a16_from_float,
+ .mipmap_nearest = gdk_mipmap_guint16_2_nearest,
+ .mipmap_linear = gdk_mipmap_guint16_2_linear,
},
[GDK_MEMORY_G16] = {
.name = "G16",
@@ -1281,6 +1433,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = g16_to_float,
.from_float = g16_from_float,
+ .mipmap_nearest = gdk_mipmap_guint16_1_nearest,
+ .mipmap_linear = gdk_mipmap_guint16_1_linear,
},
[GDK_MEMORY_A8] = {
.name = "A8",
@@ -1312,6 +1466,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = a8_to_float,
.from_float = a8_from_float,
+ .mipmap_nearest = gdk_mipmap_guint8_1_nearest,
+ .mipmap_linear = gdk_mipmap_guint8_1_linear,
},
[GDK_MEMORY_A16] = {
.name = "A16",
@@ -1346,6 +1502,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = a16_to_float,
.from_float = a16_from_float,
+ .mipmap_nearest = gdk_mipmap_guint16_1_nearest,
+ .mipmap_linear = gdk_mipmap_guint16_1_linear,
},
[GDK_MEMORY_A16_FLOAT] = {
.name = "A16f",
@@ -1379,6 +1537,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = a16_float_to_float,
.from_float = a16_float_from_float,
+ .mipmap_nearest = gdk_mipmap_half_float_1_nearest,
+ .mipmap_linear = gdk_mipmap_half_float_1_linear,
},
[GDK_MEMORY_A32_FLOAT] = {
.name = "A32f",
@@ -1412,6 +1572,8 @@ static const GdkMemoryFormatDescription memory_formats[] = {
#endif
.to_float = a32_float_to_float,
.from_float = a32_float_from_float,
+ .mipmap_nearest = gdk_mipmap_float_1_nearest,
+ .mipmap_linear = gdk_mipmap_float_1_linear,
}
};
@@ -1850,119 +2012,98 @@ unpremultiply (float (*rgba)[4],
}
}
-void
-gdk_memory_convert (guchar *dest_data,
- gsize dest_stride,
- GdkMemoryFormat dest_format,
- GdkColorState *dest_cs,
- const guchar *src_data,
- gsize src_stride,
- GdkMemoryFormat src_format,
- GdkColorState *src_cs,
- gsize width,
- gsize height)
+typedef void (* FastConversionFunc) (guchar *dest,
+ const guchar *src,
+ gsize n);
+
+static FastConversionFunc
+get_fast_conversion_func (GdkMemoryFormat dest_format,
+ GdkMemoryFormat src_format)
{
- const GdkMemoryFormatDescription *dest_desc = &memory_formats[dest_format];
- const GdkMemoryFormatDescription *src_desc = &memory_formats[src_format];
+ if (src_format == GDK_MEMORY_R8G8B8A8 && dest_format == GDK_MEMORY_R8G8B8A8_PREMULTIPLIED)
+ return r8g8b8a8_to_r8g8b8a8_premultiplied;
+ else if (src_format == GDK_MEMORY_B8G8R8A8 && dest_format == GDK_MEMORY_R8G8B8A8_PREMULTIPLIED)
+ return r8g8b8a8_to_b8g8r8a8_premultiplied;
+ else if (src_format == GDK_MEMORY_R8G8B8A8 && dest_format == GDK_MEMORY_B8G8R8A8_PREMULTIPLIED)
+ return r8g8b8a8_to_b8g8r8a8_premultiplied;
+ else if (src_format == GDK_MEMORY_B8G8R8A8 && dest_format == GDK_MEMORY_B8G8R8A8_PREMULTIPLIED)
+ return r8g8b8a8_to_r8g8b8a8_premultiplied;
+ else if (src_format == GDK_MEMORY_R8G8B8A8 && dest_format == GDK_MEMORY_A8R8G8B8_PREMULTIPLIED)
+ return r8g8b8a8_to_a8r8g8b8_premultiplied;
+ else if (src_format == GDK_MEMORY_B8G8R8A8 && dest_format == GDK_MEMORY_A8R8G8B8_PREMULTIPLIED)
+ return r8g8b8a8_to_a8b8g8r8_premultiplied;
+ else if (src_format == GDK_MEMORY_R8G8B8 && dest_format == GDK_MEMORY_R8G8B8A8_PREMULTIPLIED)
+ return r8g8b8_to_r8g8b8a8;
+ else if (src_format == GDK_MEMORY_B8G8R8 && dest_format == GDK_MEMORY_R8G8B8A8_PREMULTIPLIED)
+ return r8g8b8_to_b8g8r8a8;
+ else if (src_format == GDK_MEMORY_R8G8B8 && dest_format == GDK_MEMORY_B8G8R8A8_PREMULTIPLIED)
+ return r8g8b8_to_b8g8r8a8;
+ else if (src_format == GDK_MEMORY_B8G8R8 && dest_format == GDK_MEMORY_B8G8R8A8_PREMULTIPLIED)
+ return r8g8b8_to_r8g8b8a8;
+ else if (src_format == GDK_MEMORY_R8G8B8 && dest_format == GDK_MEMORY_A8R8G8B8_PREMULTIPLIED)
+ return r8g8b8_to_a8r8g8b8;
+ else if (src_format == GDK_MEMORY_B8G8R8 && dest_format == GDK_MEMORY_A8R8G8B8_PREMULTIPLIED)
+ return r8g8b8_to_a8b8g8r8;
+ else if (src_format == GDK_MEMORY_R8G8B8 && dest_format == GDK_MEMORY_R8G8B8A8)
+ return r8g8b8_to_r8g8b8a8;
+ else if (src_format == GDK_MEMORY_B8G8R8 && dest_format == GDK_MEMORY_R8G8B8A8)
+ return r8g8b8_to_b8g8r8a8;
+ else if (src_format == GDK_MEMORY_R8G8B8 && dest_format == GDK_MEMORY_B8G8R8A8)
+ return r8g8b8_to_b8g8r8a8;
+ else if (src_format == GDK_MEMORY_B8G8R8 && dest_format == GDK_MEMORY_B8G8R8A8)
+ return r8g8b8_to_r8g8b8a8;
+ else if (src_format == GDK_MEMORY_R8G8B8 && dest_format == GDK_MEMORY_A8R8G8B8)
+ return r8g8b8_to_a8r8g8b8;
+ else if (src_format == GDK_MEMORY_B8G8R8 && dest_format == GDK_MEMORY_A8R8G8B8)
+ return r8g8b8_to_a8b8g8r8;
+
+ return NULL;
+}
+
+typedef struct _MemoryConvert MemoryConvert;
+
+struct _MemoryConvert
+{
+ guchar *dest_data;
+ gsize dest_stride;
+ GdkMemoryFormat dest_format;
+ GdkColorState *dest_cs;
+ const guchar *src_data;
+ gsize src_stride;
+ GdkMemoryFormat src_format;
+ GdkColorState *src_cs;
+ gsize width;
+ gsize height;
+
+ /* atomic */ int rows_done;
+};
+
+static void
+gdk_memory_convert_generic (gpointer data)
+{
+ MemoryConvert *mc = data;
+ const GdkMemoryFormatDescription *dest_desc = &memory_formats[mc->dest_format];
+ const GdkMemoryFormatDescription *src_desc = &memory_formats[mc->src_format];
float (*tmp)[4];
- gsize y;
GdkFloatColorConvert convert_func = NULL;
GdkFloatColorConvert convert_func2 = NULL;
- void (*func) (guchar *, const guchar *, gsize) = NULL;
gboolean needs_premultiply, needs_unpremultiply;
+ gsize y, n;
+ gint64 before = GDK_PROFILER_CURRENT_TIME;
+ gsize rows;
- g_assert (dest_format < GDK_MEMORY_N_FORMATS);
- g_assert (src_format < GDK_MEMORY_N_FORMATS);
- /* We don't allow overlap here. If you want to do in-place color state conversions,
- * use gdk_memory_convert_color_state.
- */
- g_assert (dest_data + gdk_memory_format_min_buffer_size (dest_format, dest_stride, width, height) <= src_data ||
- src_data + gdk_memory_format_min_buffer_size (src_format, src_stride, width, height) <= dest_data);
+ convert_func = gdk_color_state_get_convert_to (mc->src_cs, mc->dest_cs);
- if (src_format == dest_format && gdk_color_state_equal (dest_cs, src_cs))
+ if (!convert_func)
+ convert_func2 = gdk_color_state_get_convert_from (mc->dest_cs, mc->src_cs);
+
+ if (!convert_func && !convert_func2)
{
- gsize bytes_per_row = src_desc->bytes_per_pixel * width;
-
- if (bytes_per_row == src_stride && bytes_per_row == dest_stride)
- {
- memcpy (dest_data, src_data, bytes_per_row * height);
- }
- else
- {
- for (y = 0; y < height; y++)
- {
- memcpy (dest_data, src_data, bytes_per_row);
- src_data += src_stride;
- dest_data += dest_stride;
- }
- }
- return;
+ GdkColorState *connection = GDK_COLOR_STATE_REC2100_LINEAR;
+ convert_func = gdk_color_state_get_convert_to (mc->src_cs, connection);
+ convert_func2 = gdk_color_state_get_convert_from (mc->dest_cs, connection);
}
- if (!gdk_color_state_equal (dest_cs, src_cs))
- {
- convert_func = gdk_color_state_get_convert_to (src_cs, dest_cs);
-
- if (!convert_func)
- convert_func2 = gdk_color_state_get_convert_from (dest_cs, src_cs);
-
- if (!convert_func && !convert_func2)
- {
- GdkColorState *connection = GDK_COLOR_STATE_REC2100_LINEAR;
- convert_func = gdk_color_state_get_convert_to (src_cs, connection);
- convert_func2 = gdk_color_state_get_convert_from (dest_cs, connection);
- }
- }
- else 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)
- {
- for (y = 0; y < height; y++)
- {
- func (dest_data, src_data, width);
- src_data += src_stride;
- dest_data += dest_stride;
- }
- return;
- }
-
- tmp = g_malloc (sizeof (*tmp) * width);
-
if (convert_func)
{
needs_unpremultiply = src_desc->alpha == GDK_MEMORY_ALPHA_PREMULTIPLIED;
@@ -1974,31 +2115,134 @@ gdk_memory_convert (guchar *dest_data,
needs_premultiply = src_desc->alpha == GDK_MEMORY_ALPHA_STRAIGHT && dest_desc->alpha != GDK_MEMORY_ALPHA_STRAIGHT;
}
- for (y = 0; y < height; y++)
+ tmp = g_malloc (sizeof (*tmp) * mc->width);
+ n = 1;
+
+ for (y = g_atomic_int_add (&mc->rows_done, n), rows = 0;
+ y < mc->height;
+ y = g_atomic_int_add (&mc->rows_done, n), rows++)
{
- src_desc->to_float (tmp, src_data, width);
+ const guchar *src_data = mc->src_data + y * mc->src_stride;
+ guchar *dest_data = mc->dest_data + y * mc->dest_stride;
+
+ src_desc->to_float (tmp, src_data, mc->width);
if (needs_unpremultiply)
- unpremultiply (tmp, width);
+ unpremultiply (tmp, mc->width);
if (convert_func)
- convert_func (src_cs, tmp, width);
+ convert_func (mc->src_cs, tmp, mc->width);
if (convert_func2)
- convert_func2 (dest_cs, tmp, width);
+ convert_func2 (mc->dest_cs, tmp, mc->width);
if (needs_premultiply)
- premultiply (tmp, width);
+ premultiply (tmp, mc->width);
- dest_desc->from_float (dest_data, tmp, width);
-
- src_data += src_stride;
- dest_data += dest_stride;
+ dest_desc->from_float (dest_data, tmp, mc->width);
}
g_free (tmp);
+
+ ADD_MARK (before,
+ "Memory convert (thread)", "size %lux%lu, %lu rows",
+ mc->width, mc->height, rows);
}
+void
+gdk_memory_convert (guchar *dest_data,
+ gsize dest_stride,
+ GdkMemoryFormat dest_format,
+ GdkColorState *dest_cs,
+ const guchar *src_data,
+ gsize src_stride,
+ GdkMemoryFormat src_format,
+ GdkColorState *src_cs,
+ gsize width,
+ gsize height)
+{
+ MemoryConvert mc = {
+ .dest_data = dest_data,
+ .dest_stride = dest_stride,
+ .dest_format = dest_format,
+ .dest_cs = dest_cs,
+ .src_data = src_data,
+ .src_stride = src_stride,
+ .src_format = src_format,
+ .src_cs = src_cs,
+ .width = width,
+ .height = height,
+ };
+
+ g_assert (dest_format < GDK_MEMORY_N_FORMATS);
+ g_assert (src_format < GDK_MEMORY_N_FORMATS);
+ /* We don't allow overlap here. If you want to do in-place color state conversions,
+ * use gdk_memory_convert_color_state.
+ */
+ g_assert (dest_data + gdk_memory_format_min_buffer_size (dest_format, dest_stride, width, height) <= src_data ||
+ src_data + gdk_memory_format_min_buffer_size (src_format, src_stride, width, height) <= dest_data);
+
+ if (src_format == dest_format && gdk_color_state_equal (dest_cs, src_cs))
+ {
+ const GdkMemoryFormatDescription *src_desc = &memory_formats[src_format];
+ gsize bytes_per_row = src_desc->bytes_per_pixel * width;
+
+ if (bytes_per_row == src_stride && bytes_per_row == dest_stride)
+ {
+ memcpy (dest_data, src_data, bytes_per_row * height);
+ }
+ else
+ {
+ gsize y;
+
+ for (y = 0; y < height; y++)
+ {
+ memcpy (dest_data, src_data, bytes_per_row);
+ src_data += src_stride;
+ dest_data += dest_stride;
+ }
+ }
+ return;
+ }
+
+ if (gdk_color_state_equal (dest_cs, src_cs))
+ {
+ FastConversionFunc func;
+
+ func = get_fast_conversion_func (dest_format, src_format);
+
+ if (func != NULL)
+ {
+ gsize y;
+
+ for (y = 0; y < height; y++)
+ {
+ func (dest_data, src_data, width);
+ src_data += src_stride;
+ dest_data += dest_stride;
+ }
+ return;
+ }
+ }
+
+ gdk_parallel_task_run (gdk_memory_convert_generic, &mc);
+}
+
+typedef struct _MemoryConvertColorState MemoryConvertColorState;
+
+struct _MemoryConvertColorState
+{
+ guchar *data;
+ gsize stride;
+ GdkMemoryFormat format;
+ GdkColorState *src_cs;
+ GdkColorState *dest_cs;
+ gsize width;
+ gsize height;
+
+ /* atomic */ int rows_done;
+};
+
static const guchar srgb_lookup[] = {
0, 12, 21, 28, 33, 38, 42, 46, 49, 52, 55, 58, 61, 63, 66, 68,
70, 73, 75, 77, 79, 81, 82, 84, 86, 88, 89, 91, 93, 94, 96, 97,
@@ -2103,43 +2347,101 @@ convert_srgb_linear_to_srgb (guchar *data,
}
static void
-convert_srgb_to_srgb_linear_in_place (guchar *data,
- gsize stride,
- gsize width,
- gsize height)
+gdk_memory_convert_color_state_srgb_to_srgb_linear (gpointer data)
{
- if (stride == width * 4)
+ MemoryConvertColorState *mc = data;
+ int y;
+ guint64 before = GDK_PROFILER_CURRENT_TIME;
+ gsize rows;
+
+ for (y = g_atomic_int_add (&mc->rows_done, 1), rows = 0;
+ y < mc->height;
+ y = g_atomic_int_add (&mc->rows_done, 1), rows++)
{
- convert_srgb_to_srgb_linear (data, height * width);
- }
- else
- {
- for (gsize y = 0; y < height; y++)
- {
- convert_srgb_to_srgb_linear (data, width);
- data += stride;
- }
+ convert_srgb_to_srgb_linear (mc->data + y * mc->stride, mc->width);
}
+
+ ADD_MARK (before,
+ "Color state convert srgb->srgb-linear (thread)", "size %lux%lu, %lu rows",
+ mc->width, mc->height, rows);
}
static void
-convert_srgb_linear_to_srgb_in_place (guchar *data,
- gsize stride,
- gsize width,
- gsize height)
+gdk_memory_convert_color_state_srgb_linear_to_srgb (gpointer data)
{
- if (stride == width * 4)
+ MemoryConvertColorState *mc = data;
+ int y;
+ guint64 before = GDK_PROFILER_CURRENT_TIME;
+ gsize rows;
+
+ for (y = g_atomic_int_add (&mc->rows_done, 1), rows = 0;
+ y < mc->height;
+ y = g_atomic_int_add (&mc->rows_done, 1), rows++)
{
- convert_srgb_linear_to_srgb (data, height * width);
+ convert_srgb_linear_to_srgb (mc->data + y * mc->stride, mc->width);
}
- else
+
+ ADD_MARK (before,
+ "Color state convert srgb-linear->srgb (thread)", "size %lux%lu, %lu rows",
+ mc->width, mc->height, rows);
+}
+
+static void
+gdk_memory_convert_color_state_generic (gpointer user_data)
+{
+ MemoryConvertColorState *mc = user_data;
+ const GdkMemoryFormatDescription *desc = &memory_formats[mc->format];
+ GdkFloatColorConvert convert_func = NULL;
+ GdkFloatColorConvert convert_func2 = NULL;
+ float (*tmp)[4];
+ int y;
+ guint64 before = GDK_PROFILER_CURRENT_TIME;
+ gsize rows;
+
+ convert_func = gdk_color_state_get_convert_to (mc->src_cs, mc->dest_cs);
+
+ if (!convert_func)
{
- for (gsize y = 0; y < height; y++)
- {
- convert_srgb_linear_to_srgb (data, width);
- data += stride;
- }
+ convert_func2 = gdk_color_state_get_convert_from (mc->dest_cs, mc->src_cs);
}
+
+ if (!convert_func && !convert_func2)
+ {
+ GdkColorState *connection = GDK_COLOR_STATE_REC2100_LINEAR;
+ convert_func = gdk_color_state_get_convert_to (mc->src_cs, connection);
+ convert_func2 = gdk_color_state_get_convert_from (mc->dest_cs, connection);
+ }
+
+ tmp = g_malloc (sizeof (*tmp) * mc->width);
+
+ for (y = g_atomic_int_add (&mc->rows_done, 1), rows = 0;
+ y < mc->height;
+ y = g_atomic_int_add (&mc->rows_done, 1), rows++)
+ {
+ guchar *data = mc->data + y * mc->stride;
+
+ desc->to_float (tmp, data, mc->width);
+
+ if (desc->alpha == GDK_MEMORY_ALPHA_PREMULTIPLIED)
+ unpremultiply (tmp, mc->width);
+
+ if (convert_func)
+ convert_func (mc->src_cs, tmp, mc->width);
+
+ if (convert_func2)
+ convert_func2 (mc->dest_cs, tmp, mc->width);
+
+ if (desc->alpha == GDK_MEMORY_ALPHA_PREMULTIPLIED)
+ premultiply (tmp, mc->width);
+
+ desc->from_float (data, tmp, mc->width);
+ }
+
+ g_free (tmp);
+
+ ADD_MARK (before,
+ "Color state convert (thread)", "size %lux%lu, %lu rows",
+ mc->width, mc->height, rows);
}
void
@@ -2151,10 +2453,15 @@ gdk_memory_convert_color_state (guchar *data,
gsize width,
gsize height)
{
- const GdkMemoryFormatDescription *desc = &memory_formats[format];
- GdkFloatColorConvert convert_func = NULL;
- GdkFloatColorConvert convert_func2 = NULL;
- float (*tmp)[4];
+ MemoryConvertColorState mc = {
+ .data = data,
+ .stride = stride,
+ .format = format,
+ .src_cs = src_cs,
+ .dest_cs = dest_cs,
+ .width = width,
+ .height = height,
+ };
if (gdk_color_state_equal (src_cs, dest_cs))
return;
@@ -2163,52 +2470,185 @@ gdk_memory_convert_color_state (guchar *data,
src_cs == GDK_COLOR_STATE_SRGB &&
dest_cs == GDK_COLOR_STATE_SRGB_LINEAR)
{
- convert_srgb_to_srgb_linear_in_place (data, stride, width, height);
- return;
+ gdk_parallel_task_run (gdk_memory_convert_color_state_srgb_to_srgb_linear, &mc);
}
else if (format == GDK_MEMORY_B8G8R8A8_PREMULTIPLIED &&
src_cs == GDK_COLOR_STATE_SRGB_LINEAR &&
dest_cs == GDK_COLOR_STATE_SRGB)
{
- convert_srgb_linear_to_srgb_in_place (data, stride, width, height);
- return;
+ gdk_parallel_task_run (gdk_memory_convert_color_state_srgb_linear_to_srgb, &mc);
+ }
+ else
+ {
+ gdk_parallel_task_run (gdk_memory_convert_color_state_generic, &mc);
+ }
+}
+
+typedef struct _MipmapData MipmapData;
+
+struct _MipmapData
+{
+ guchar *dest;
+ gsize dest_stride;
+ GdkMemoryFormat dest_format;
+ const guchar *src;
+ gsize src_stride;
+ GdkMemoryFormat src_format;
+ gsize src_width;
+ gsize src_height;
+ guint lod_level;
+ gboolean linear;
+
+ gint rows_done;
+};
+
+static void
+gdk_memory_mipmap_same_format_nearest (gpointer data)
+{
+ MipmapData *mipmap = data;
+ const GdkMemoryFormatDescription *desc = &memory_formats[mipmap->src_format];
+ gsize n, y;
+ guint64 before = GDK_PROFILER_CURRENT_TIME;
+ gsize rows;
+
+ n = 1 << mipmap->lod_level;
+
+ for (y = g_atomic_int_add (&mipmap->rows_done, n), rows = 0;
+ y < mipmap->src_height;
+ y = g_atomic_int_add (&mipmap->rows_done, n), rows++)
+ {
+ guchar *dest = mipmap->dest + (y >> mipmap->lod_level) * mipmap->dest_stride;
+ const guchar *src = mipmap->src + y * mipmap->src_stride;
+
+ desc->mipmap_nearest (dest, mipmap->dest_stride,
+ src, mipmap->src_stride,
+ mipmap->src_width, MIN (n, mipmap->src_height - y),
+ mipmap->lod_level);
}
- convert_func = gdk_color_state_get_convert_to (src_cs, dest_cs);
+ ADD_MARK (before,
+ "Mipmap nearest (thread)", "size %lux%lu, lod %u, %lu rows",
+ mipmap->src_width, mipmap->src_height, mipmap->lod_level, rows);
+}
- if (!convert_func)
+static void
+gdk_memory_mipmap_same_format_linear (gpointer data)
+{
+ MipmapData *mipmap = data;
+ const GdkMemoryFormatDescription *desc = &memory_formats[mipmap->src_format];
+ gsize n, y;
+ guint64 before = GDK_PROFILER_CURRENT_TIME;
+ gsize rows;
+
+ n = 1 << mipmap->lod_level;
+
+ for (y = g_atomic_int_add (&mipmap->rows_done, n), rows = 0;
+ y < mipmap->src_height;
+ y = g_atomic_int_add (&mipmap->rows_done, n), rows++)
{
- convert_func2 = gdk_color_state_get_convert_from (dest_cs, src_cs);
+ guchar *dest = mipmap->dest + (y >> mipmap->lod_level) * mipmap->dest_stride;
+ const guchar *src = mipmap->src + y * mipmap->src_stride;
+
+ desc->mipmap_linear (dest, mipmap->dest_stride,
+ src, mipmap->src_stride,
+ mipmap->src_width, MIN (n, mipmap->src_height - y),
+ mipmap->lod_level);
}
- if (!convert_func && !convert_func2)
+ ADD_MARK (before,
+ "Mipmap linear (thread)", "size %lux%lu, lod %u, %lu rows",
+ mipmap->src_width, mipmap->src_height, mipmap->lod_level, rows);
+}
+
+static void
+gdk_memory_mipmap_generic (gpointer data)
+{
+ MipmapData *mipmap = data;
+ const GdkMemoryFormatDescription *desc = &memory_formats[mipmap->src_format];
+ FastConversionFunc func;
+ gsize dest_width;
+ gsize size;
+ guchar *tmp;
+ gsize n, y;
+ guint64 before = GDK_PROFILER_CURRENT_TIME;
+ gsize rows;
+
+ n = 1 << mipmap->lod_level;
+ dest_width = (mipmap->src_width + n - 1) >> mipmap->lod_level;
+ size = gdk_memory_format_bytes_per_pixel (mipmap->src_format) * dest_width;
+ tmp = g_malloc (size);
+ func = get_fast_conversion_func (mipmap->dest_format, mipmap->src_format);
+
+ for (y = g_atomic_int_add (&mipmap->rows_done, n), rows = 0;
+ y < mipmap->src_height;
+ y = g_atomic_int_add (&mipmap->rows_done, n), rows++)
{
- GdkColorState *connection = GDK_COLOR_STATE_REC2100_LINEAR;
- convert_func = gdk_color_state_get_convert_to (src_cs, connection);
- convert_func2 = gdk_color_state_get_convert_from (dest_cs, connection);
- }
+ guchar *dest = mipmap->dest + (y >> mipmap->lod_level) * mipmap->dest_stride;
+ const guchar *src = mipmap->src + y * mipmap->src_stride;
- tmp = g_malloc (sizeof (*tmp) * width);
-
- for (gsize y = 0; y < height; y++)
- {
- desc->to_float (tmp, data, width);
-
- if (desc->alpha == GDK_MEMORY_ALPHA_PREMULTIPLIED)
- unpremultiply (tmp, width);
-
- if (convert_func)
- convert_func (src_cs, tmp, width);
-
- if (convert_func2)
- convert_func2 (dest_cs, tmp, width);
-
- if (desc->alpha == GDK_MEMORY_ALPHA_PREMULTIPLIED)
- premultiply (tmp, width);
-
- desc->from_float (data, tmp, width);
- data += stride;
+ if (mipmap->linear)
+ desc->mipmap_linear (tmp, (size + 7) & 7,
+ src, mipmap->src_stride,
+ mipmap->src_width, MIN (n, mipmap->src_height - y),
+ mipmap->lod_level);
+ else
+ desc->mipmap_nearest (tmp, (size + 7) & 7,
+ src, mipmap->src_stride,
+ mipmap->src_width, MIN (n, mipmap->src_height - y),
+ mipmap->lod_level);
+ if (func)
+ func (dest, tmp, dest_width);
+ else
+ gdk_memory_convert (dest, mipmap->dest_stride, mipmap->dest_format, GDK_COLOR_STATE_SRGB,
+ tmp, (size + 7) & 7, mipmap->src_format, GDK_COLOR_STATE_SRGB,
+ dest_width, 1);
}
g_free (tmp);
+
+ ADD_MARK (before,
+ "Mipmap generic (thread)", "size %lux%lu, lod %u, %lu rows",
+ mipmap->src_width, mipmap->src_height, mipmap->lod_level, rows);
}
+
+void
+gdk_memory_mipmap (guchar *dest,
+ gsize dest_stride,
+ GdkMemoryFormat dest_format,
+ const guchar *src,
+ gsize src_stride,
+ GdkMemoryFormat src_format,
+ gsize src_width,
+ gsize src_height,
+ guint lod_level,
+ gboolean linear)
+{
+ MipmapData mipmap = {
+ .dest = dest,
+ .dest_stride = dest_stride,
+ .dest_format = dest_format,
+ .src = src,
+ .src_stride = src_stride,
+ .src_format = src_format,
+ .src_width = src_width,
+ .src_height = src_height,
+ .lod_level = lod_level,
+ .linear = linear,
+ .rows_done = 0,
+ };
+
+ g_assert (lod_level > 0);
+
+ if (dest_format == src_format)
+ {
+ if (linear)
+ gdk_parallel_task_run (gdk_memory_mipmap_same_format_linear, &mipmap);
+ else
+ gdk_parallel_task_run (gdk_memory_mipmap_same_format_nearest, &mipmap);
+ }
+ else
+ {
+ gdk_parallel_task_run (gdk_memory_mipmap_generic, &mipmap);
+ }
+}
+
diff --git a/gdk/gdkmemoryformatprivate.h b/gdk/gdkmemoryformatprivate.h
index 57f55bd5fd..323ac927ab 100644
--- a/gdk/gdkmemoryformatprivate.h
+++ b/gdk/gdkmemoryformatprivate.h
@@ -110,6 +110,16 @@ void gdk_memory_convert_color_state (guchar
GdkColorState *dest_color_state,
gsize width,
gsize height);
+void gdk_memory_mipmap (guchar *dest,
+ gsize dest_stride,
+ GdkMemoryFormat dest_format,
+ const guchar *src,
+ gsize src_stride,
+ GdkMemoryFormat src_format,
+ gsize src_width,
+ gsize src_height,
+ guint lod_level,
+ gboolean linear);
G_END_DECLS
diff --git a/gdk/gdkparalleltask.c b/gdk/gdkparalleltask.c
new file mode 100644
index 0000000000..4b9c49e609
--- /dev/null
+++ b/gdk/gdkparalleltask.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright © 2024 Benjamin Otte
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see .
+ *
+ * Authors: Benjamin Otte
+ */
+
+#include "config.h"
+
+#include "gdkparalleltaskprivate.h"
+
+typedef struct _TaskData TaskData;
+
+struct _TaskData
+{
+ GdkTaskFunc task_func;
+ gpointer task_data;
+ int n_running_tasks;
+};
+
+static void
+gdk_parallel_task_thread_func (gpointer data,
+ gpointer unused)
+{
+ TaskData *task = data;
+
+ task->task_func (task->task_data);
+
+ g_atomic_int_add (&task->n_running_tasks, -1);
+}
+
+/**
+ * gdk_parallel_task_run:
+ * @task_func: the function to spawn
+ * @task_data: data to pass to the function
+ *
+ * Spawns the given function in many threads.
+ * Once all functions have exited, this function returns.
+ **/
+void
+gdk_parallel_task_run (GdkTaskFunc task_func,
+ gpointer task_data)
+{
+ static GThreadPool *pool;
+ TaskData task = {
+ .task_func = task_func,
+ .task_data = task_data,
+ };
+ int i, n_tasks;
+
+ if (g_once_init_enter (&pool))
+ {
+ GThreadPool *the_pool = g_thread_pool_new (gdk_parallel_task_thread_func,
+ NULL,
+ MAX (2, g_get_num_processors ()) - 1,
+ FALSE,
+ NULL);
+ g_once_init_leave (&pool, the_pool);
+ }
+
+ n_tasks = g_get_num_processors ();
+ task.n_running_tasks = n_tasks;
+ /* Start with 1 because we run 1 task ourselves */
+ for (i = 1; i < n_tasks; i++)
+ {
+ g_thread_pool_push (pool, &task, NULL);
+ }
+
+ gdk_parallel_task_thread_func (&task, NULL);
+
+ while (g_atomic_int_get (&task.n_running_tasks) > 0)
+ g_thread_yield ();
+}
+
diff --git a/gdk/gdkparalleltaskprivate.h b/gdk/gdkparalleltaskprivate.h
new file mode 100644
index 0000000000..a20fb72f95
--- /dev/null
+++ b/gdk/gdkparalleltaskprivate.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright © 2024 Benjamin Otte
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see .
+ *
+ * Authors: Benjamin Otte
+ */
+
+#pragma once
+
+#include
+
+G_BEGIN_DECLS
+
+typedef void (* GdkTaskFunc) (gpointer user_data);
+
+void gdk_parallel_task_run (GdkTaskFunc task_func,
+ gpointer task_data);
+
+G_END_DECLS
+
diff --git a/gdk/gdktexture.c b/gdk/gdktexture.c
index e4e7baa001..318265efd7 100644
--- a/gdk/gdktexture.c
+++ b/gdk/gdktexture.c
@@ -33,7 +33,18 @@
*
* `GdkTexture` is an immutable object: That means you cannot change
* anything about it other than increasing the reference count via
- * [method@GObject.Object.ref], and consequently, it is a thread-safe object.
+ * [method@GObject.Object.ref], and consequently, it is a threadsafe object.
+ *
+ * GDK provides a number of threadsafe texture loading functions:
+ * [ctor@Gdk.Texture.new_from_resource],
+ * [ctor@Gdk.Texture.new_from_bytes],
+ * [ctor@Gdk.Texture.new_from_file],
+ * [ctor@Gdk.Texture.new_from_filename],
+ * [ctor@Gdk.Texture.new_for_pixbuf]. Note that these are meant for loading
+ * icons and resources that are shipped with the toolkit or application. It
+ * is recommended that you use a dedicated image loading framework such as
+ * [glycin](https://lib.rs/crates/glycin), if you need to load untrusted image
+ * data.
*/
#include "config.h"
diff --git a/gdk/meson.build b/gdk/meson.build
index 78609a9870..b75349f291 100644
--- a/gdk/meson.build
+++ b/gdk/meson.build
@@ -52,23 +52,24 @@ gdk_public_sources = files([
'gdkmonitor.c',
'gdkpaintable.c',
'gdkpango.c',
+ 'gdkparalleltask.c',
'gdkpipeiostream.c',
+ 'gdkpopup.c',
+ 'gdkpopuplayout.c',
+ 'gdkprofiler.c',
'gdkrectangle.c',
'gdkrgba.c',
'gdkseat.c',
'gdkseatdefault.c',
'gdksnapshot.c',
- 'gdktexture.c',
- 'gdktexturedownloader.c',
- 'gdkvulkancontext.c',
'gdksubsurface.c',
'gdksurface.c',
- 'gdkpopuplayout.c',
- 'gdkprofiler.c',
- 'gdkpopup.c',
+ 'gdktexture.c',
+ 'gdktexturedownloader.c',
'gdktoplevellayout.c',
'gdktoplevelsize.c',
'gdktoplevel.c',
+ 'gdkvulkancontext.c',
'loaders/gdkpng.c',
'loaders/gdktiff.c',
'loaders/gdkjpeg.c',
diff --git a/gsk/gpu/gskgpucache.c b/gsk/gpu/gskgpucache.c
index 724b9ecf57..4c26210393 100644
--- a/gsk/gpu/gskgpucache.c
+++ b/gsk/gpu/gskgpucache.c
@@ -537,6 +537,8 @@ struct _GskGpuCachedTile
GskGpuCached parent;
GdkTexture *texture;
+ guint lod_level;
+ gboolean lod_linear;
gsize tile_id;
/* atomic */ int use_count; /* We count the use by the cache (via the linked
@@ -630,7 +632,10 @@ gsk_gpu_cached_tile_hash (gconstpointer data)
{
const GskGpuCachedTile *self = data;
- return g_direct_hash (self->texture) ^ self->tile_id;
+ return g_direct_hash (self->texture) ^
+ self->tile_id ^
+ (self->lod_level << 24) ^
+ (self->lod_linear << 31);
}
static gboolean
@@ -641,12 +646,16 @@ gsk_gpu_cached_tile_equal (gconstpointer data_a,
const GskGpuCachedTile *b = data_b;
return a->texture == b->texture &&
+ a->lod_level == b->lod_level &&
+ a->lod_linear == b->lod_linear &&
a->tile_id == b->tile_id;
}
static GskGpuCachedTile *
gsk_gpu_cached_tile_new (GskGpuCache *cache,
GdkTexture *texture,
+ guint lod_level,
+ gboolean lod_linear,
guint tile_id,
GskGpuImage *image,
GdkColorState *color_state)
@@ -655,6 +664,8 @@ gsk_gpu_cached_tile_new (GskGpuCache *cache,
self = gsk_gpu_cached_new (cache, &GSK_GPU_CACHED_TILE_CLASS);
self->texture = texture;
+ self->lod_level = lod_level;
+ self->lod_linear = lod_linear;
self->tile_id = tile_id;
self->image = g_object_ref (image);
self->color_state = gdk_color_state_ref (color_state);
@@ -673,14 +684,18 @@ gsk_gpu_cached_tile_new (GskGpuCache *cache,
}
GskGpuImage *
-gsk_gpu_cache_lookup_tile (GskGpuCache *self,
- GdkTexture *texture,
- gsize tile_id,
- GdkColorState **out_color_state)
+gsk_gpu_cache_lookup_tile (GskGpuCache *self,
+ GdkTexture *texture,
+ guint lod_level,
+ GskScalingFilter lod_filter,
+ gsize tile_id,
+ GdkColorState **out_color_state)
{
GskGpuCachedTile *tile;
GskGpuCachedTile lookup = {
.texture = texture,
+ .lod_level = lod_level,
+ .lod_linear = lod_filter == GSK_SCALING_FILTER_TRILINEAR,
.tile_id = tile_id
};
@@ -699,15 +714,23 @@ gsk_gpu_cache_lookup_tile (GskGpuCache *self,
}
void
-gsk_gpu_cache_cache_tile (GskGpuCache *self,
- GdkTexture *texture,
- guint tile_id,
- GskGpuImage *image,
- GdkColorState *color_state)
+gsk_gpu_cache_cache_tile (GskGpuCache *self,
+ GdkTexture *texture,
+ guint lod_level,
+ GskScalingFilter lod_filter,
+ gsize tile_id,
+ GskGpuImage *image,
+ GdkColorState *color_state)
{
GskGpuCachedTile *tile;
- tile = gsk_gpu_cached_tile_new (self, texture, tile_id, image, color_state);
+ tile = gsk_gpu_cached_tile_new (self,
+ texture,
+ lod_level,
+ lod_filter == GSK_SCALING_FILTER_TRILINEAR,
+ tile_id,
+ image,
+ color_state);
gsk_gpu_cached_use (self, (GskGpuCached *) tile);
}
diff --git a/gsk/gpu/gskgpucacheprivate.h b/gsk/gpu/gskgpucacheprivate.h
index 9164504c76..517cbbe824 100644
--- a/gsk/gpu/gskgpucacheprivate.h
+++ b/gsk/gpu/gskgpucacheprivate.h
@@ -77,11 +77,15 @@ void gsk_gpu_cache_cache_texture_image (GskGpuC
GdkColorState *color_state);
GskGpuImage * gsk_gpu_cache_lookup_tile (GskGpuCache *self,
GdkTexture *texture,
+ guint lod_level,
+ GskScalingFilter lod_filter,
gsize tile_id,
GdkColorState **out_color_state);
void gsk_gpu_cache_cache_tile (GskGpuCache *self,
GdkTexture *texture,
- guint tile_id,
+ guint lod_level,
+ GskScalingFilter lod_filter,
+ gsize tile_id,
GskGpuImage *image,
GdkColorState *color_state);
diff --git a/gsk/gpu/gskgpuframe.c b/gsk/gpu/gskgpuframe.c
index 6659063797..6403ad982d 100644
--- a/gsk/gpu/gskgpuframe.c
+++ b/gsk/gpu/gskgpuframe.c
@@ -107,7 +107,7 @@ gsk_gpu_frame_default_upload_texture (GskGpuFrame *self,
{
GskGpuImage *image;
- image = gsk_gpu_upload_texture_op_try (self, with_mipmap, texture);
+ image = gsk_gpu_upload_texture_op_try (self, with_mipmap, 0, GSK_SCALING_FILTER_NEAREST, texture);
return image;
}
diff --git a/gsk/gpu/gskgpunodeprocessor.c b/gsk/gpu/gskgpunodeprocessor.c
index 6ab7c120fb..52a4fe90ea 100644
--- a/gsk/gpu/gskgpunodeprocessor.c
+++ b/gsk/gpu/gskgpunodeprocessor.c
@@ -1964,18 +1964,26 @@ gsk_gpu_node_processor_draw_texture_tiles (GskGpuNodeProcessor *self,
gboolean need_mipmap;
GdkMemoryTexture *memtex;
GdkTexture *subtex;
- float scaled_tile_width, scaled_tile_height;
+ float scale_factor, scaled_tile_width, scaled_tile_height;
gsize tile_size, width, height, n_width, n_height, x, y;
graphene_rect_t clip_bounds;
+ guint lod_level;
device = gsk_gpu_frame_get_device (self->frame);
cache = gsk_gpu_device_get_cache (device);
sampler = gsk_gpu_sampler_for_scaling_filter (scaling_filter);
need_mipmap = scaling_filter == GSK_SCALING_FILTER_TRILINEAR;
gsk_gpu_node_processor_get_clip_bounds (self, &clip_bounds);
- tile_size = gsk_gpu_device_get_tile_size (device);
width = gdk_texture_get_width (texture);
height = gdk_texture_get_height (texture);
+ tile_size = gsk_gpu_device_get_tile_size (device);
+ scale_factor = MIN (width / MAX (tile_size, texture_bounds->size.width),
+ height / MAX (tile_size, texture_bounds->size.height));
+ if (scale_factor <= 1.0)
+ lod_level = 0;
+ else
+ lod_level = floor (log2f (scale_factor));
+ tile_size <<= lod_level;
n_width = (width + tile_size - 1) / tile_size;
n_height = (height + tile_size - 1) / tile_size;
scaled_tile_width = texture_bounds->size.width * tile_size / width;
@@ -1994,7 +2002,7 @@ gsk_gpu_node_processor_draw_texture_tiles (GskGpuNodeProcessor *self,
!gsk_rect_intersects (&clip_bounds, &tile_rect))
continue;
- tile = gsk_gpu_cache_lookup_tile (cache, texture, y * n_width + x, &tile_cs);
+ tile = gsk_gpu_cache_lookup_tile (cache, texture, lod_level, scaling_filter, y * n_width + x, &tile_cs);
if (tile == NULL)
{
@@ -2005,7 +2013,7 @@ gsk_gpu_node_processor_draw_texture_tiles (GskGpuNodeProcessor *self,
y * tile_size,
MIN (tile_size, width - x * tile_size),
MIN (tile_size, height - y * tile_size));
- tile = gsk_gpu_upload_texture_op_try (self->frame, need_mipmap, subtex);
+ tile = gsk_gpu_upload_texture_op_try (self->frame, need_mipmap, lod_level, scaling_filter, subtex);
g_object_unref (subtex);
if (tile == NULL)
{
@@ -2021,7 +2029,7 @@ gsk_gpu_node_processor_draw_texture_tiles (GskGpuNodeProcessor *self,
g_assert (tile_cs);
}
- gsk_gpu_cache_cache_tile (cache, texture, y * n_width + x, tile, tile_cs);
+ gsk_gpu_cache_cache_tile (cache, texture, lod_level, scaling_filter, y * n_width + x, tile, tile_cs);
}
if (need_mipmap &&
@@ -2029,7 +2037,7 @@ gsk_gpu_node_processor_draw_texture_tiles (GskGpuNodeProcessor *self,
{
tile = gsk_gpu_copy_image (self->frame, self->ccs, tile, tile_cs, TRUE);
tile_cs = self->ccs;
- gsk_gpu_cache_cache_tile (cache, texture, y * n_width + x, tile, tile_cs);
+ gsk_gpu_cache_cache_tile (cache, texture, lod_level, scaling_filter, y * n_width + x, tile, tile_cs);
}
if (need_mipmap && !(gsk_gpu_image_get_flags (tile) & GSK_GPU_IMAGE_MIPMAP))
gsk_gpu_mipmap_op (self->frame, tile);
diff --git a/gsk/gpu/gskgpuuploadop.c b/gsk/gpu/gskgpuuploadop.c
index ad4b9aa02d..6f788f738e 100644
--- a/gsk/gpu/gskgpuuploadop.c
+++ b/gsk/gpu/gskgpuuploadop.c
@@ -214,6 +214,8 @@ struct _GskGpuUploadTextureOp
GskGpuImage *image;
GskGpuBuffer *buffer;
GdkTexture *texture;
+ guint lod_level;
+ GskScalingFilter lod_filter;
};
static void
@@ -236,6 +238,10 @@ gsk_gpu_upload_texture_op_print (GskGpuOp *op,
gsk_gpu_print_op (string, indent, "upload-texture");
gsk_gpu_print_image (string, self->image);
+ if (self->lod_level > 0)
+ g_string_append_printf (string, " @%ux %s",
+ 1 << self->lod_level,
+ self->lod_filter == GSK_SCALING_FILTER_TRILINEAR ? "linear" : "nearest");
gsk_gpu_print_newline (string);
}
@@ -248,9 +254,31 @@ gsk_gpu_upload_texture_op_draw (GskGpuOp *op,
GdkTextureDownloader *downloader;
downloader = gdk_texture_downloader_new (self->texture);
- gdk_texture_downloader_set_format (downloader, gsk_gpu_image_get_format (self->image));
gdk_texture_downloader_set_color_state (downloader, gdk_texture_get_color_state (self->texture));
- gdk_texture_downloader_download_into (downloader, data, stride);
+ if (self->lod_level == 0)
+ {
+ gdk_texture_downloader_set_format (downloader, gsk_gpu_image_get_format (self->image));
+ gdk_texture_downloader_download_into (downloader, data, stride);
+ }
+ else
+ {
+ GBytes *bytes;
+ gsize src_stride;
+
+ gdk_texture_downloader_set_format (downloader, gdk_texture_get_format (self->texture));
+ bytes = gdk_texture_downloader_download_bytes (downloader, &src_stride);
+ gdk_memory_mipmap (data,
+ stride,
+ gsk_gpu_image_get_format (self->image),
+ g_bytes_get_data (bytes, NULL),
+ src_stride,
+ gdk_texture_get_format (self->texture),
+ gdk_texture_get_width (self->texture),
+ gdk_texture_get_height (self->texture),
+ self->lod_level,
+ self->lod_filter == GSK_SCALING_FILTER_TRILINEAR ? TRUE : FALSE);
+ g_bytes_unref (bytes);
+ }
gdk_texture_downloader_free (downloader);
}
@@ -296,9 +324,11 @@ static const GskGpuOpClass GSK_GPU_UPLOAD_TEXTURE_OP_CLASS = {
};
GskGpuImage *
-gsk_gpu_upload_texture_op_try (GskGpuFrame *frame,
- gboolean with_mipmap,
- GdkTexture *texture)
+gsk_gpu_upload_texture_op_try (GskGpuFrame *frame,
+ gboolean with_mipmap,
+ guint lod_level,
+ GskScalingFilter lod_filter,
+ GdkTexture *texture)
{
GskGpuUploadTextureOp *self;
GskGpuImage *image;
@@ -311,8 +341,8 @@ gsk_gpu_upload_texture_op_try (GskGpuFrame *frame,
format,
gdk_memory_format_alpha (format) != GDK_MEMORY_ALPHA_PREMULTIPLIED &&
gdk_color_state_get_no_srgb_tf (gdk_texture_get_color_state (texture)) != NULL,
- gdk_texture_get_width (texture),
- gdk_texture_get_height (texture));
+ (gdk_texture_get_width (texture) + (1 << lod_level) - 1) >> lod_level,
+ (gdk_texture_get_height (texture) + (1 << lod_level) - 1) >> lod_level);
if (image == NULL)
return NULL;
@@ -343,6 +373,8 @@ gsk_gpu_upload_texture_op_try (GskGpuFrame *frame,
self = (GskGpuUploadTextureOp *) gsk_gpu_op_alloc (frame, &GSK_GPU_UPLOAD_TEXTURE_OP_CLASS);
self->texture = g_object_ref (texture);
+ self->lod_level = lod_level;
+ self->lod_filter = lod_filter;
self->image = image;
return g_object_ref (self->image);
diff --git a/gsk/gpu/gskgpuuploadopprivate.h b/gsk/gpu/gskgpuuploadopprivate.h
index f29ca24250..452e8bd896 100644
--- a/gsk/gpu/gskgpuuploadopprivate.h
+++ b/gsk/gpu/gskgpuuploadopprivate.h
@@ -11,6 +11,8 @@ typedef void (* GskGpuCairoFunc) (gpointe
GskGpuImage * gsk_gpu_upload_texture_op_try (GskGpuFrame *frame,
gboolean with_mipmap,
+ guint lod_level,
+ GskScalingFilter lod_filter,
GdkTexture *texture);
GskGpuImage * gsk_gpu_upload_cairo_op (GskGpuFrame *frame,