gsk: Introduce GskTextureScaleNode
authorMatthias Clasen <mclasen@redhat.com>
Wed, 8 Feb 2023 03:52:06 +0000 (22:52 -0500)
committerMatthias Clasen <mclasen@redhat.com>
Sat, 11 Feb 2023 20:09:38 +0000 (15:09 -0500)
gsk/broadway/gskbroadwayrenderer.c
gsk/gl/gskglrenderjob.c
gsk/gskenums.h
gsk/gskrendernode.h
gsk/gskrendernodeimpl.c
gsk/vulkan/gskvulkanrenderpass.c
gtk/inspector/recorder.c

index bfd7270f56af1a289c731e274e0bc43c30ecad22..6fee960edf39b931bab753eff422c4340ba3160e 100644 (file)
@@ -250,6 +250,7 @@ collect_reused_child_nodes (GskRenderer *renderer,
       /* Leaf nodes */
 
     case GSK_TEXTURE_NODE:
+    case GSK_TEXTURE_SCALE_NODE:
     case GSK_CAIRO_NODE:
     case GSK_COLOR_NODE:
     case GSK_BORDER_NODE:
@@ -845,6 +846,7 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer,
       }
       break; /* Fallback */
 
+    case GSK_TEXTURE_SCALE_NODE:
     case GSK_TEXT_NODE:
     case GSK_RADIAL_GRADIENT_NODE:
     case GSK_REPEATING_LINEAR_GRADIENT_NODE:
index e30a1e13414788763f39eac598781ed463a7cecb..103f40c02989e122a17cb8453dbd971b2ce8bf65 100644 (file)
@@ -3762,6 +3762,10 @@ gsk_gl_render_job_visit_node (GskGLRenderJob      *job,
       gsk_gl_render_job_visit_texture_node (job, node);
     break;
 
+    case GSK_TEXTURE_SCALE_NODE:
+      gsk_gl_render_job_visit_as_fallback (job, node);
+    break;
+
     case GSK_TRANSFORM_NODE:
       gsk_gl_render_job_visit_transform_node (job, node);
     break;
index 0dfeb0a0ea63175f9c757d4f7f3321779abde7d6..0c92555cfd79ff2e670d469c9869e558d1e5a932 100644 (file)
@@ -35,6 +35,7 @@
  * @GSK_CONIC_GRADIENT_NODE: A node drawing a conic gradient
  * @GSK_BORDER_NODE: A node stroking a border around an area
  * @GSK_TEXTURE_NODE: A node drawing a `GdkTexture`
+ * @GSK_TEXTURE_SCALE_NODE: A node drawing a `GdkTexture` scaled and filtered
  * @GSK_INSET_SHADOW_NODE: A node drawing an inset shadow
  * @GSK_OUTSET_SHADOW_NODE: A node drawing an outset shadow
  * @GSK_TRANSFORM_NODE: A node that renders its child after applying a matrix transform
@@ -65,6 +66,7 @@ typedef enum {
   GSK_CONIC_GRADIENT_NODE,
   GSK_BORDER_NODE,
   GSK_TEXTURE_NODE,
+  GSK_TEXTURE_SCALE_NODE,
   GSK_INSET_SHADOW_NODE,
   GSK_OUTSET_SHADOW_NODE,
   GSK_TRANSFORM_NODE,
index 3fa1d057c25ceb0211fb04bad4d08f733828dc36..fb6e1f51f699e3d34937b7d73bc7332770f0b156 100644 (file)
@@ -142,6 +142,7 @@ GskRenderNode *         gsk_render_node_deserialize             (GBytes
 #define GSK_TYPE_DEBUG_NODE                     (gsk_debug_node_get_type())
 #define GSK_TYPE_COLOR_NODE                     (gsk_color_node_get_type())
 #define GSK_TYPE_TEXTURE_NODE                   (gsk_texture_node_get_type())
+#define GSK_TYPE_TEXTURE_SCALE_NODE             (gsk_texture_scale_node_get_type())
 #define GSK_TYPE_LINEAR_GRADIENT_NODE           (gsk_linear_gradient_node_get_type())
 #define GSK_TYPE_REPEATING_LINEAR_GRADIENT_NODE (gsk_repeating_linear_gradient_node_get_type())
 #define GSK_TYPE_RADIAL_GRADIENT_NODE           (gsk_radial_gradient_node_get_type())
@@ -168,6 +169,7 @@ GskRenderNode *         gsk_render_node_deserialize             (GBytes
 typedef struct _GskDebugNode                    GskDebugNode;
 typedef struct _GskColorNode                    GskColorNode;
 typedef struct _GskTextureNode                  GskTextureNode;
+typedef struct _GskTextureScaleNode             GskTextureScaleNode;
 typedef struct _GskLinearGradientNode           GskLinearGradientNode;
 typedef struct _GskRepeatingLinearGradientNode  GskRepeatingLinearGradientNode;
 typedef struct _GskRadialGradientNode           GskRadialGradientNode;
@@ -217,6 +219,17 @@ GskRenderNode *         gsk_texture_node_new                    (GdkTexture
 GDK_AVAILABLE_IN_ALL
 GdkTexture *            gsk_texture_node_get_texture            (const GskRenderNode      *node) G_GNUC_PURE;
 
+GDK_AVAILABLE_IN_4_10
+GType                   gsk_texture_scale_node_get_type         (void) G_GNUC_CONST;
+GDK_AVAILABLE_IN_4_10
+GskRenderNode *         gsk_texture_scale_node_new              (GdkTexture               *texture,
+                                                                 const graphene_rect_t    *bounds,
+                                                                 GskScalingFilter          filter);
+GDK_AVAILABLE_IN_4_10
+GdkTexture *            gsk_texture_scale_node_get_texture      (const GskRenderNode      *node) G_GNUC_PURE;
+GDK_AVAILABLE_IN_4_10
+GskScalingFilter        gsk_texture_scale_node_get_filter       (const GskRenderNode      *node) G_GNUC_PURE;
+
 GDK_AVAILABLE_IN_ALL
 GType                   gsk_linear_gradient_node_get_type           (void) G_GNUC_CONST;
 GDK_AVAILABLE_IN_ALL
index cb747802ba45ec11b9e27c07c32f51fff583f86b..376d32cb4ef62af5a5f5fbe01912fb9a52e8871f 100644 (file)
@@ -1581,6 +1581,191 @@ gsk_texture_node_new (GdkTexture            *texture,
   return node;
 }
 
+/* }}} */
+/* {{{ GSK_TEXTURE_SCALE_NODE */
+
+/**
+ * GskTextureScaleNode:
+ *
+ * A render node for a `GdkTexture`.
+ */
+struct _GskTextureScaleNode
+{
+  GskRenderNode render_node;
+
+  GdkTexture *texture;
+  GskScalingFilter filter;
+};
+
+static void
+gsk_texture_scale_node_finalize (GskRenderNode *node)
+{
+  GskTextureScaleNode *self = (GskTextureScaleNode *) node;
+  GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_TEXTURE_SCALE_NODE));
+
+  g_clear_object (&self->texture);
+
+  parent_class->finalize (node);
+}
+
+static void
+gsk_texture_scale_node_draw (GskRenderNode *node,
+                             cairo_t       *cr)
+{
+  GskTextureScaleNode *self = (GskTextureScaleNode *) node;
+  cairo_surface_t *surface;
+  cairo_pattern_t *pattern;
+  cairo_matrix_t matrix;
+  cairo_filter_t filters[] = {
+    CAIRO_FILTER_BILINEAR,
+    CAIRO_FILTER_NEAREST,
+    CAIRO_FILTER_GOOD,
+  };
+  cairo_t *cr2;
+  cairo_surface_t *surface2;
+
+  surface2 = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                         (int) ceilf (node->bounds.size.width),
+                                         (int) ceilf (node->bounds.size.height));
+  cr2 = cairo_create (surface2);
+
+  cairo_set_source_rgba (cr2, 0, 0, 0, 0);
+  cairo_paint (cr2);
+
+  surface = gdk_texture_download_surface (self->texture);
+  pattern = cairo_pattern_create_for_surface (surface);
+  cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
+
+  cairo_matrix_init_scale (&matrix,
+                           gdk_texture_get_width (self->texture) / node->bounds.size.width,
+                           gdk_texture_get_height (self->texture) / node->bounds.size.height);
+  cairo_pattern_set_matrix (pattern, &matrix);
+  cairo_pattern_set_filter (pattern, filters[self->filter]);
+
+  cairo_set_source (cr2, pattern);
+  cairo_pattern_destroy (pattern);
+  cairo_surface_destroy (surface);
+
+  cairo_rectangle (cr2, 0, 0, node->bounds.size.width, node->bounds.size.height);
+  cairo_fill (cr2);
+
+  cairo_destroy (cr2);
+
+  cairo_save (cr);
+
+  pattern = cairo_pattern_create_for_surface (surface2);
+  cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
+
+  cairo_matrix_init_identity (&matrix);
+  cairo_matrix_translate (&matrix,
+                          -node->bounds.origin.x,
+                          -node->bounds.origin.y);
+  cairo_pattern_set_matrix (pattern, &matrix);
+  cairo_set_source (cr, pattern);
+  cairo_pattern_destroy (pattern);
+  cairo_surface_destroy (surface2);
+
+  gsk_cairo_rectangle (cr, &node->bounds);
+  cairo_fill (cr);
+
+  cairo_restore (cr);
+}
+
+static void
+gsk_texture_scale_node_diff (GskRenderNode  *node1,
+                             GskRenderNode  *node2,
+                             cairo_region_t *region)
+{
+  GskTextureScaleNode *self1 = (GskTextureScaleNode *) node1;
+  GskTextureScaleNode *self2 = (GskTextureScaleNode *) node2;
+
+  if (graphene_rect_equal (&node1->bounds, &node2->bounds) &&
+      self1->texture == self2->texture &&
+      self1->filter == self2->filter)
+    return;
+
+  gsk_render_node_diff_impossible (node1, node2, region);
+}
+
+/**
+ * gsk_texture_scale_node_get_texture:
+ * @node: (type GskTextureNode): a `GskRenderNode` of type %GSK_TEXTURE_SCALE_NODE
+ *
+ * Retrieves the `GdkTexture` used when creating this `GskRenderNode`.
+ *
+ * Returns: (transfer none): the `GdkTexture`
+ *
+ * Since: 4.10
+ */
+GdkTexture *
+gsk_texture_scale_node_get_texture (const GskRenderNode *node)
+{
+  const GskTextureScaleNode *self = (const GskTextureScaleNode *) node;
+
+  return self->texture;
+}
+
+/**
+ * gsk_texture_scale_node_get_filter:
+ * @node: (type GskTextureNode): a `GskRenderNode` of type %GSK_TEXTURE_SCALE_NODE
+ *
+ * Retrieves the `GskScalingFilter` used when creating this `GskRenderNode`.
+ *
+ * Returns: (transfer none): the `GskScalingFilter`
+ *
+ * Since: 4.10
+ */
+GskScalingFilter
+gsk_texture_scale_node_get_filter (const GskRenderNode *node)
+{
+  const GskTextureScaleNode *self = (const GskTextureScaleNode *) node;
+
+  return self->filter;
+}
+
+/**
+ * gsk_texture_scale_node_new:
+ * @texture: the texture to scale
+ * @bounds: the size of the texture to scale to
+ * @filter: how to scale the texture
+ *
+ * Creates a node that scales the texture to the size given by the
+ * bounds and the filter and then places it at the bounds' position.
+ *
+ * This node is intended for tight control over scaling applied
+ * to a texture, such as in image editors and requires the
+ * application to be aware of the whole render tree as further
+ * transforms may be applied that conflict with the desired effect
+ * of this node.
+ *
+ * Returns: (transfer full) (type GskTextureScaleNode): A new `GskRenderNode`
+ *
+ * Since: 4.10
+ */
+GskRenderNode *
+gsk_texture_scale_node_new (GdkTexture            *texture,
+                            const graphene_rect_t *bounds,
+                            GskScalingFilter       filter)
+{
+  GskTextureScaleNode *self;
+  GskRenderNode *node;
+
+  g_return_val_if_fail (GDK_IS_TEXTURE (texture), NULL);
+  g_return_val_if_fail (bounds != NULL, NULL);
+
+  self = gsk_render_node_alloc (GSK_TEXTURE_SCALE_NODE);
+  node = (GskRenderNode *) self;
+  node->offscreen_for_opacity = FALSE;
+
+  self->texture = g_object_ref (texture);
+  graphene_rect_init_from_rect (&node->bounds, bounds);
+  self->filter = filter;
+
+  node->prefers_high_depth = gdk_memory_format_prefers_high_depth (gdk_texture_get_format (texture));
+
+  return node;
+}
+
 /* }}} */
 /* {{{ GSK_INSET_SHADOW_NODE */
 
@@ -5357,6 +5542,7 @@ GSK_DEFINE_RENDER_NODE_TYPE (gsk_repeating_radial_gradient_node, GSK_REPEATING_R
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_conic_gradient_node, GSK_CONIC_GRADIENT_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_border_node, GSK_BORDER_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_texture_node, GSK_TEXTURE_NODE)
+GSK_DEFINE_RENDER_NODE_TYPE (gsk_texture_scale_node, GSK_TEXTURE_SCALE_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_inset_shadow_node, GSK_INSET_SHADOW_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_outset_shadow_node, GSK_OUTSET_SHADOW_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_transform_node, GSK_TRANSFORM_NODE)
@@ -5536,6 +5722,22 @@ gsk_render_node_init_types_once (void)
     gsk_render_node_types[GSK_TEXTURE_NODE] = node_type;
   }
 
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_TEXTURE_SCALE_NODE,
+      sizeof (GskTextureScaleNode),
+      NULL,
+      gsk_texture_scale_node_finalize,
+      gsk_texture_scale_node_draw,
+      NULL,
+      gsk_texture_scale_node_diff,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskTextureScaleNode"), &node_info);
+    gsk_render_node_types[GSK_TEXTURE_SCALE_NODE] = node_type;
+  }
+
   {
     const GskRenderNodeTypeInfo node_info =
     {
index 48127e20b981d63ec0e4df2ae09a7a017c5b9deb..3008c42154ddf2e8b99a014be21e0c7577e0b489 100644 (file)
@@ -440,6 +440,9 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass           *self,
       g_array_append_val (self->render_ops, op);
       return;
 
+    case GSK_TEXTURE_SCALE_NODE:
+      goto fallback;
+
     case GSK_COLOR_NODE:
       if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
         pipeline_type = GSK_VULKAN_PIPELINE_COLOR;
index e33d058b05db407f0d7238ad793155a969357674..53528a26cbfb046023e7028849db65085dc32828 100644 (file)
@@ -264,6 +264,7 @@ create_list_model_for_render_node (GskRenderNode *node)
     case GSK_CAIRO_NODE:
     case GSK_TEXT_NODE:
     case GSK_TEXTURE_NODE:
+    case GSK_TEXTURE_SCALE_NODE:
     case GSK_COLOR_NODE:
     case GSK_LINEAR_GRADIENT_NODE:
     case GSK_REPEATING_LINEAR_GRADIENT_NODE:
@@ -402,6 +403,8 @@ node_type_name (GskRenderNodeType type)
       return "Border";
     case GSK_TEXTURE_NODE:
       return "Texture";
+    case GSK_TEXTURE_SCALE_NODE:
+      return "Scaled Texture";
     case GSK_INSET_SHADOW_NODE:
       return "Inset Shadow";
     case GSK_OUTSET_SHADOW_NODE:
@@ -476,6 +479,11 @@ node_name (GskRenderNode *node)
         GdkTexture *texture = gsk_texture_node_get_texture (node);
         return g_strdup_printf ("%dx%d Texture", gdk_texture_get_width (texture), gdk_texture_get_height (texture));
       }
+    case GSK_TEXTURE_SCALE_NODE:
+      {
+        GdkTexture *texture = gsk_texture_node_get_texture (node);
+        return g_strdup_printf ("%dx%d Texture, Filter %d", gdk_texture_get_width (texture), gdk_texture_get_height (texture), gsk_texture_scale_node_get_filter (node));
+      }
     }
 }
 
@@ -933,6 +941,18 @@ populate_render_node_properties (GListStore    *store,
       }
       break;
 
+    case GSK_TEXTURE_SCALE_NODE:
+      {
+        GdkTexture *texture = g_object_ref (gsk_texture_scale_node_get_texture (node));
+        GskScalingFilter filter = gsk_texture_scale_node_get_filter (node);
+        g_list_store_append (store, object_property_new ("Texture", "", texture));
+
+        tmp = g_enum_to_string (GSK_TYPE_SCALING_FILTER, filter);
+        add_text_row (store, "Filter", tmp);
+        g_free (tmp);
+      }
+      break;
+
     case GSK_COLOR_NODE:
       add_color_row (store, "Color", gsk_color_node_get_color (node));
       break;