[gtk/gl-linear: 19/21] Check for pixel-alignedness for interpolation




commit e0cc7b5d861232cbaa20d97101093a9e61f40edc
Author: Matthias Clasen <mclasen redhat com>
Date:   Wed Sep 2 18:01:06 2020 -0400

    Check for pixel-alignedness for interpolation
    
    When rendering to an offscreen because of transforms,
    check if transforming the bounds of the node results
    in a non-axis-aligned quad. If it doesn't, we want
    GL_NEAREST interpolation to get sharp edges. Otherwise,
    we use GL_LINEAR to get better results for things
    that are actually transformed.

 gsk/gl/gskgldriver.c        |  4 +-
 gsk/gl/gskgldriverprivate.h |  2 +
 gsk/gl/gskglrenderer.c      | 90 +++++++++++++++++++++++++++++++++++----------
 3 files changed, 76 insertions(+), 20 deletions(-)
---
diff --git a/gsk/gl/gskgldriver.c b/gsk/gl/gskgldriver.c
index 8cd32d72ab..08e19a980f 100644
--- a/gsk/gl/gskgldriver.c
+++ b/gsk/gl/gskgldriver.c
@@ -594,6 +594,8 @@ void
 gsk_gl_driver_create_render_target (GskGLDriver *self,
                                     int          width,
                                     int          height,
+                                    int          min_filter,
+                                    int          mag_filter,
                                     int         *out_texture_id,
                                     int         *out_render_target_id)
 {
@@ -604,7 +606,7 @@ gsk_gl_driver_create_render_target (GskGLDriver *self,
 
   texture = create_texture (self, width, height);
   gsk_gl_driver_bind_source_texture (self, texture->texture_id);
-  gsk_gl_driver_init_texture_empty (self, texture->texture_id, GL_NEAREST, GL_NEAREST);
+  gsk_gl_driver_init_texture_empty (self, texture->texture_id, min_filter, mag_filter);
 
   glGenFramebuffers (1, &fbo_id);
   glBindFramebuffer (GL_FRAMEBUFFER, fbo_id);
diff --git a/gsk/gl/gskgldriverprivate.h b/gsk/gl/gskgldriverprivate.h
index 83d0c46af9..22c5ef8cb3 100644
--- a/gsk/gl/gskgldriverprivate.h
+++ b/gsk/gl/gskgldriverprivate.h
@@ -45,6 +45,8 @@ int             gsk_gl_driver_create_texture            (GskGLDriver     *driver
 void            gsk_gl_driver_create_render_target      (GskGLDriver     *driver,
                                                          int              width,
                                                          int              height,
+                                                         int              min_filter,
+                                                         int              mag_filter,
                                                          int             *out_texture_id,
                                                          int             *out_render_target_id);
 void            gsk_gl_driver_mark_texture_permanent    (GskGLDriver     *self,
diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c
index 47ec75e21f..27d1c48cbd 100644
--- a/gsk/gl/gskglrenderer.c
+++ b/gsk/gl/gskglrenderer.c
@@ -71,6 +71,7 @@ typedef enum
   RESET_OPACITY    = 1 << 2,
   DUMP_FRAMEBUFFER = 1 << 3,
   NO_CACHE_PLZ     = 1 << 5,
+  LINEAR_FILTER    = 1 << 6,
 } OffscreenFlags;
 
 static inline void
@@ -1000,6 +1001,38 @@ render_texture_node (GskGLRenderer       *self,
     }
 }
 
+/* Returns TRUE is applying transform to bounds
+ * yields an axis-aligned rectangle
+ */
+static gboolean
+result_is_axis_aligned (GskTransform          *transform,
+                        const graphene_rect_t *bounds)
+{
+  graphene_matrix_t m;
+  graphene_quad_t q;
+  graphene_rect_t b;
+  graphene_point_t b1, b2;
+  const graphene_point_t *p;
+  int i;
+
+  gsk_transform_to_matrix (transform, &m);
+  gsk_matrix_transform_rect (&m, bounds, &q);
+  graphene_quad_bounds (&q, &b);
+  graphene_rect_get_top_left (&b, &b1);
+  graphene_rect_get_bottom_right (&b, &b2);
+
+  for (i = 0; i < 4; i++)
+    {
+      p = graphene_quad_get_point (&q, i);
+      if (fabs (p->x - b1.x) > FLT_EPSILON && fabs (p->x - b2.x) > FLT_EPSILON)
+        return FALSE;
+      if (fabs (p->y - b1.y) > FLT_EPSILON && fabs (p->y - b2.y) > FLT_EPSILON)
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
 static inline void
 render_transform_node (GskGLRenderer   *self,
                        GskRenderNode   *node,
@@ -1051,27 +1084,33 @@ render_transform_node (GskGLRenderer   *self,
           }
         else
           {
+            int filter_flag = 0;
+
+            if (!result_is_axis_aligned (node_transform, &child->bounds))
+              filter_flag = LINEAR_FILTER;
+
             if (add_offscreen_ops (self, builder,
                                    &child->bounds,
                                    child,
                                    &region, &is_offscreen,
-                                   RESET_CLIP | RESET_OPACITY))
-          {
-            /* For non-trivial transforms, we draw everything on a texture and then
-             * draw the texture transformed. */
-            /* TODO: We should compute a modelview containing only the "non-trivial"
-             *       part (e.g. the rotation) and use that. We want to keep the scale
-             *       for the texture.
-             */
-            ops_push_modelview (builder, node_transform);
-            ops_set_texture (builder, region.texture_id);
-            ops_set_program (builder, &self->programs->blit_program);
-
-            load_vertex_data_with_region (ops_draw (builder, NULL),
-                                          child, builder,
-                                          &region,
-                                          is_offscreen);
-            ops_pop_modelview (builder);
+                                   RESET_CLIP | RESET_OPACITY | filter_flag))
+              {
+                /* For non-trivial transforms, we draw everything on a texture and then
+                 * draw the texture transformed. */
+                /* TODO: We should compute a modelview containing only the "non-trivial"
+                 *       part (e.g. the rotation) and use that. We want to keep the scale
+                 *       for the texture.
+                 */
+                ops_push_modelview (builder, node_transform);
+                ops_set_texture (builder, region.texture_id);
+                ops_set_program (builder, &self->programs->blit_program);
+
+                load_vertex_data_with_region (ops_draw (builder, NULL),
+                                              child, builder,
+                                              &region,
+                                              is_offscreen);
+                ops_pop_modelview (builder);
+              }
           }
       }
       break;
@@ -1457,10 +1496,12 @@ blur_texture (GskGLRenderer       *self,
 
   gsk_gl_driver_create_render_target (self->gl_driver,
                                       texture_to_blur_width, texture_to_blur_height,
+                                      GL_NEAREST, GL_NEAREST,
                                       &pass1_texture_id, &pass1_render_target);
 
   gsk_gl_driver_create_render_target (self->gl_driver,
                                       texture_to_blur_width, texture_to_blur_height,
+                                      GL_NEAREST, GL_NEAREST,
                                       &pass2_texture_id, &pass2_render_target);
 
   graphene_matrix_init_ortho (&item_proj,
@@ -1702,6 +1743,7 @@ render_inset_shadow_node (GskGLRenderer   *self,
 
       gsk_gl_driver_create_render_target (self->gl_driver,
                                           texture_width, texture_height,
+                                          GL_NEAREST, GL_NEAREST,
                                           &texture_id, &render_target);
 
       graphene_matrix_init_ortho (&item_proj,
@@ -1875,7 +1917,9 @@ render_outset_shadow_node (GskGLRenderer   *self,
       graphene_rect_t prev_viewport;
       graphene_matrix_t item_proj;
 
-      gsk_gl_driver_create_render_target (self->gl_driver, texture_width, texture_height,
+      gsk_gl_driver_create_render_target (self->gl_driver,
+                                          texture_width, texture_height,
+                                          GL_NEAREST, GL_NEAREST,
                                           &texture_id, &render_target);
       if (gdk_gl_context_has_debug (self->gl_context))
         {
@@ -3355,6 +3399,7 @@ add_offscreen_ops (GskGLRenderer         *self,
   float prev_opacity = 1.0;
   int texture_id = 0;
   int max_texture_size;
+  int filter;
 
   if (node_is_invisible (child_node))
     {
@@ -3407,7 +3452,14 @@ add_offscreen_ops (GskGLRenderer         *self,
   width  = ceilf (width * scale);
   height = ceilf (height * scale);
 
-  gsk_gl_driver_create_render_target (self->gl_driver, width, height, &texture_id, &render_target);
+  if (flags & LINEAR_FILTER)
+    filter = GL_LINEAR;
+  else
+    filter = GL_NEAREST;
+  gsk_gl_driver_create_render_target (self->gl_driver,
+                                      width, height,
+                                      filter, filter,
+                                      &texture_id, &render_target);
   if (gdk_gl_context_has_debug (self->gl_context))
     {
       gdk_gl_context_label_object_printf (self->gl_context, GL_TEXTURE, texture_id,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]