Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Gnome/gsk/   (Gnome Linux Desktop Version 4.23.2©)  Datei vom 30.5.2026 mit Größe 31 kB image not shown  

Quelle  gskoffload.c

  Sprache: C
 

/* gskoffload.c
 *
 * Copyright 2023 Red Hat, Inc.
 *
 * This file 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 file 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 General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * SPDX-License-Identifier: LGPL-2.1-or-later
 */


#include "config.h"

#include "gskoffloadprivate.h"

#include "gskclipnode.h"
#include "gskcolornodeprivate.h"
#include "gskcontainernodeprivate.h"
#include "gskdebugnode.h"
#include "gskdebugprivate.h"
#include "gskisolationnode.h"
#include "gskopacitynode.h"
#include "gskrendernode.h"
#include "gskrectprivate.h"
#include "gskrendernodeprivate.h"
#include "gskroundedclipnode.h"
#include "gskroundedrectprivate.h"
#include "gsksubsurfacenode.h"
#include "gsktexturenode.h"
#include "gsktransformnode.h"
#include "gsktransformprivate.h"

#include "gdkrgbaprivate.h"
#include "gdksurfaceprivate.h"

#include <graphene.h>

typedef struct
{
  GskRoundedRect rect;
  guint is_rectilinear     : 1;
  guint is_fully_contained : 1;
  guint is_empty           : 1;
  guint is_complex         : 1;
} Clip;

struct _GskOffload
{
  GdkSurface *surface;
  GskOffloadInfo *subsurfaces;
  gsize n_subsurfaces;

  GSList *transforms;
  GSList *clips;

  Clip *current_clip;
  const char *offload_impossible;

  GskOffloadInfo *last_info;
};

static GdkDihedral
find_texture_transform (GskTransform *transform)
{
  GdkDihedral dihedral;
  float sx, sy, dx, dy;

  g_assert (gsk_transform_get_fine_category (transform) >= GSK_FINE_TRANSFORM_CATEGORY_2D_DIHEDRAL);

  gsk_transform_to_dihedral (transform, &dihedral, &sx, &sy, &dx, &dy);

  return dihedral;
}

static gboolean
color_is_black (const GdkColor *color)
{
  return color->red < 255.f / 65535.f &&
         color->green < 255.f / 65535.f &&
         color->blue < 255.f / 65535.f &&
         color->alpha > 65280.f / 65535.f;
}

static GdkTexture *
find_texture_to_attach (GskOffload          *self,
                        const GskRenderNode *subsurface_node,
                        graphene_rect_t     *out_texture_rect,
                        graphene_rect_t     *out_source_rect,
                        gboolean            *has_background,
                        GdkDihedral         *out_texture_transform)
{
  GdkSubsurface *subsurface;
  const GskRenderNode *node;
  gboolean has_clip = FALSE;
  graphene_rect_t clip;
  GskTransform *transform = NULL;
  GdkTexture *ret = NULL;

  *has_background = FALSE;
  *out_texture_transform = GDK_DIHEDRAL_NORMAL;

  subsurface = gsk_subsurface_node_get_subsurface (subsurface_node);
  node = subsurface_node;

  for (;;)
    {
      switch ((int) GSK_RENDER_NODE_TYPE (node))
        {
        case GSK_DEBUG_NODE:
          node = gsk_debug_node_get_child (node);
          break;

        case GSK_SUBSURFACE_NODE:
          node = gsk_subsurface_node_get_child (node);
          break;

        case GSK_OPACITY_NODE:
          if (gsk_opacity_node_get_opacity (node) < 1.0)
            goto out;
          node = gsk_opacity_node_get_child (node);
          break;

        case GSK_ISOLATION_NODE:
          node = gsk_isolation_node_get_child (node);
          break;

        case GSK_CONTAINER_NODE:
          if (gsk_container_node_get_n_children (node) == 1)
            {
              node = gsk_container_node_get_child (node, 0);
              break;
            }
          else if (gsk_container_node_get_n_children (node) == 2)
            {
              GskRenderNode *child = gsk_container_node_get_child (node, 0);
              graphene_rect_t bounds;

              gsk_transform_transform_bounds (transform, &child->bounds, &bounds);
              if (GSK_RENDER_NODE_TYPE (child) == GSK_COLOR_NODE &&
                  gsk_rect_equal (&bounds, &subsurface_node->bounds) &&
                  color_is_black (gsk_color_node_get_gdk_color (child)))
                {
                  *has_background = TRUE;
                  node = gsk_container_node_get_child (node, 1);
                  break;
                }
            }

          GDK_DISPLAY_DEBUG (gdk_surface_get_display (self->surface), OFFLOAD,
                             "[%p] �� Too much content, container with %d children",
                             subsurface, gsk_container_node_get_n_children (node));
          goto out;

        case GSK_TRANSFORM_NODE:
          {
            GskTransform *t = gsk_transform_node_get_transform (node);

            if (gsk_transform_get_fine_category (t) < GSK_FINE_TRANSFORM_CATEGORY_2D_DIHEDRAL)
              {
                char *s = gsk_transform_to_string (t);
                GDK_DISPLAY_DEBUG (gdk_surface_get_display (self->surface), OFFLOAD,
                                   "[%p] �� Transform %s is not dihedral",
                                   subsurface, s);
                g_free (s);
                goto out;
              }

            if (has_clip)
              {
                GskTransform *inv = gsk_transform_invert (gsk_transform_ref (t));
                gsk_transform_transform_bounds (inv, &clip, &clip);
                gsk_transform_unref (inv);
              }

            transform = gsk_transform_transform (transform, gsk_transform_ref (t));

            node = gsk_transform_node_get_child (node);
          }
          break;

        case GSK_CLIP_NODE:
          {
            const graphene_rect_t *c = gsk_clip_node_get_clip (node);

            if (has_clip)
              {
                if (!gsk_rect_intersection (c, &clip, &clip))
                  {
                    GDK_DISPLAY_DEBUG (gdk_surface_get_display (self->surface), OFFLOAD,
                                       "[%p] �� Empty clip", subsurface);
                    goto out;
                  }
              }
            else
              {
                gsk_transform_transform_bounds (transform, &node->bounds, out_texture_rect);
                clip = *c;
                has_clip = TRUE;
              }

            node = gsk_clip_node_get_child (node);
          }
          break;

        case GSK_TEXTURE_NODE:
          {
            GdkTexture *texture = gsk_texture_node_get_texture (node);
            int width, height;

            if (GDK_IS_MEMORY_TEXTURE (texture) &&
                !GDK_DISPLAY_DEBUG_CHECK (gdk_surface_get_display (self->surface),
                                          FORCE_OFFLOAD))
              {
                GDK_DISPLAY_DEBUG (gdk_surface_get_display (self->surface), OFFLOAD,
                                   "[%p] �� Memory texture", subsurface);
                goto out;
              }

            if (gsk_transform_get_fine_category (transform) < GSK_FINE_TRANSFORM_CATEGORY_2D_DIHEDRAL)
              {
                char *s = gsk_transform_to_string (transform);
                GDK_DISPLAY_DEBUG (gdk_surface_get_display (self->surface), OFFLOAD,
                                   "[%p] �� Transform %s is not dihedral",
                                   subsurface, s);
                g_free (s);
                goto out;
              }

            width = gdk_texture_get_width (texture);
            height = gdk_texture_get_height (texture);

            *out_texture_transform = find_texture_transform (transform);

            if (gdk_dihedral_swaps_xy (*out_texture_transform))
              {
                int tmp = width;
                width = height;
                height = tmp;
              }

            if (has_clip)
              {
                float dx = node->bounds.origin.x;
                float dy = node->bounds.origin.y;
                float sx = width / node->bounds.size.width;
                float sy = height / node->bounds.size.height;

                if (!gsk_rect_intersection (&node->bounds, &clip, &clip))
                  {
                    GDK_DISPLAY_DEBUG (gdk_surface_get_display (self->surface), OFFLOAD,
                                       "[%p] �� Empty clip", subsurface);
                    goto out;
                  }

                out_source_rect->origin.x = (clip.origin.x - dx) * sx;
                out_source_rect->origin.y = (clip.origin.y - dy) * sy;
                out_source_rect->size.width = clip.size.width * sx;
                out_source_rect->size.height = clip.size.height * sy;
              }
            else
              {
                gsk_transform_transform_bounds (transform, &node->bounds, out_texture_rect);
                out_source_rect->origin.x = 0;
                out_source_rect->origin.y = 0;
                out_source_rect->size.width = width;
                out_source_rect->size.height = height;
              }

            ret = texture;
            goto out;
          }

        default:
          GDK_DISPLAY_DEBUG (gdk_surface_get_display (self->surface), OFFLOAD,
                             "[%p] �� Only textures supported (found %s)",
                             subsurface, g_type_name_from_instance ((GTypeInstance *) node));
          goto out;
        }
    }

out:
  g_clear_pointer (&transform, gsk_transform_unref);

  return ret;
}

static void
push_transform (GskOffload   *self,
                GskTransform *transform)
{
  if (self->transforms)
    {
      GskTransform *t = self->transforms->data;
      t = gsk_transform_transform (gsk_transform_ref (t), transform);
      self->transforms = g_slist_prepend (self->transforms, t);
    }
  else
    self->transforms = g_slist_prepend (NULL, gsk_transform_ref (transform));
}

static void
pop_transform (GskOffload *self)
{
  GSList *l = self->transforms;
  GskTransform *t = l->data;

  g_assert (self->transforms != NULL);

  self->transforms = self->transforms->next;

  g_slist_free_1 (l);
  gsk_transform_unref (t);
}

static inline void
transform_bounds (GskOffload            *self,
                  const graphene_rect_t *bounds,
                  graphene_rect_t       *rect)
{
  GskTransform *t = self->transforms ? self->transforms->data : NULL;

  gsk_transform_transform_bounds (t, bounds, rect);
}

static inline gboolean
transform_rounded_rect (GskOffload       *self,
                        const GskRoundedRect *rect,
                        GskRoundedRect       *out_rect)
{
  GskTransform *t = self->transforms ? self->transforms->data : NULL;
  GdkDihedral dihedral;
  float sx, sy, dx, dy;
  GskRoundedRect tmp;

  if (gsk_transform_get_fine_category (t) < GSK_FINE_TRANSFORM_CATEGORY_2D_DIHEDRAL)
    return FALSE;

  gsk_transform_to_dihedral (t, &dihedral, &sx, &sy, &dx, &dy);
  gsk_rounded_rect_dihedral (&tmp, rect, dihedral);
  gsk_rounded_rect_scale_affine (out_rect, &tmp, sx, sy, dx, dy);

  return TRUE;
}

static void
push_rect_clip (GskOffload           *self,
                const GskRoundedRect *rect)
{
  Clip *clip = g_new0 (Clip, 1);
  clip->rect = *rect;
  clip->is_rectilinear = gsk_rounded_rect_is_rectilinear (rect);
  clip->is_empty = (rect->bounds.size.width == 0 || rect->bounds.size.height == 0);

  self->clips = g_slist_prepend (self->clips, clip);
  self->current_clip = self->clips->data;
}

static void
push_empty_clip (GskOffload *self)
{
  push_rect_clip (self, &GSK_ROUNDED_RECT_INIT (0000));
}

static void
push_contained_clip (GskOffload *self)
{
  Clip *current_clip = self->clips->data;
  Clip *clip = g_new0 (Clip, 1);

  clip->rect = current_clip->rect;
  clip->is_rectilinear = TRUE;
  clip->is_fully_contained = TRUE;

  self->clips = g_slist_prepend (self->clips, clip);
  self->current_clip = self->clips->data;
}

static void
push_complex_clip (GskOffload *self)
{
  Clip *current_clip = self->clips->data;
  Clip *clip = g_new0 (Clip, 1);

  clip->rect = current_clip->rect;
  clip->is_complex = TRUE;

  self->clips = g_slist_prepend (self->clips, clip);
  self->current_clip = self->clips->data;
}

static void
pop_clip (GskOffload *self)
{
  GSList *l = self->clips;
  Clip *clip = l->data;

  g_assert (self->clips != NULL);

  self->clips = self->clips->next;
  if (self->clips)
    self->current_clip = self->clips->data;

  g_slist_free_1 (l);
  g_free (clip);
}

static inline void
rounded_rect_get_inner (const GskRoundedRect *rect,
                        graphene_rect_t      *inner)
{
  float left = MAX (rect->corner[GSK_CORNER_TOP_LEFT].width, rect->corner[GSK_CORNER_BOTTOM_LEFT].width);
  float right = MAX (rect->corner[GSK_CORNER_TOP_RIGHT].width, rect->corner[GSK_CORNER_BOTTOM_RIGHT].width);
  float top = MAX (rect->corner[GSK_CORNER_TOP_LEFT].height, rect->corner[GSK_CORNER_TOP_RIGHT].height);
  float bottom = MAX (rect->corner[GSK_CORNER_BOTTOM_LEFT].height, rect->corner[GSK_CORNER_BOTTOM_RIGHT].height);

  inner->origin.x = rect->bounds.origin.x + left;
  inner->size.width = rect->bounds.size.width - (left + right);

  inner->origin.y = rect->bounds.origin.y + top;
  inner->size.height = rect->bounds.size.height - (top + bottom);
}

static inline gboolean
interval_contains (float p1, float w1,
                   float p2, float w2)
{
  if (p2 < p1)
    return FALSE;

  if (p2 + w2 > p1 + w1)
    return FALSE;

  return TRUE;
}

static gboolean
update_clip (GskOffload            *self,
             const graphene_rect_t *transformed_bounds)
{
  gboolean no_clip = FALSE;
  gboolean rect_clip = FALSE;

  if (self->current_clip->is_fully_contained ||
      self->current_clip->is_empty ||
      self->current_clip->is_complex)
    return FALSE;

  if (!gsk_rect_intersects (&self->current_clip->rect.bounds, transformed_bounds))
    {
      push_empty_clip (self);
      return TRUE;
    }

  if (self->current_clip->is_rectilinear)
    {
      if (gsk_rect_contains_rect (&self->current_clip->rect.bounds, transformed_bounds))
        no_clip = TRUE;
      else
        rect_clip = TRUE;
    }
  else if (gsk_rounded_rect_contains_rect (&self->current_clip->rect, transformed_bounds))
    {
      no_clip = TRUE;
    }
  else
    {
      graphene_rect_t inner;

      rounded_rect_get_inner (&self->current_clip->rect, &inner);

      if (interval_contains (inner.origin.x, inner.size.width,
                             transformed_bounds->origin.x, transformed_bounds->size.width) ||
          interval_contains (inner.origin.y, inner.size.height,
                             transformed_bounds->origin.y, transformed_bounds->size.height))
        rect_clip = TRUE;
    }

  if (no_clip)
    {
      /* This node is completely contained inside the clip.
       * Record this fact on the clip stack, so we don't do
       * more work for child nodes.
       */

      push_contained_clip (self);
      return TRUE;
    }
  else if (rect_clip && !self->current_clip->is_rectilinear)
    {
      graphene_rect_t rect;

      /* The clip gets simpler for this node */

      if (gsk_rect_intersection (&self->current_clip->rect.bounds, transformed_bounds, &rect))
        push_rect_clip (self, &GSK_ROUNDED_RECT_INIT_FROM_RECT (rect));
      else
        push_empty_clip (self);
      return TRUE;
    }

  return FALSE;
}

static GskOffloadInfo *
find_subsurface_info (GskOffload    *self,
                      GdkSubsurface *subsurface)
{
  for (gsize i = 0; i < self->n_subsurfaces; i++)
    {
      GskOffloadInfo *info = &self->subsurfaces[i];
      if (info->subsurface == subsurface)
        return info;
    }

  return NULL;
}

static void
visit_node (GskOffload    *self,
            GskRenderNode *node);

static void
visit_children (GskOffload    *self,
                GskRenderNode *node)
{
  GskRenderNode **children;
  gsize i, n_children;

  children = gsk_render_node_get_children (node, &n_children);

  for (i = 0; i < n_children; i++)
    {
      visit_node (self, children[i]);
    }
}

static void
visit_node (GskOffload    *self,
            GskRenderNode *node)
{
  gboolean has_clip;
  graphene_rect_t transformed_bounds;

  transform_bounds (self, &node->bounds, &transformed_bounds);

  for (gsize i = 0; i < self->n_subsurfaces; i++)
    {
      GskOffloadInfo *info = &self->subsurfaces[i];

      if (info->can_offload && info->can_raise)
        {
          if (gsk_rect_intersects (&transformed_bounds, &info->texture_rect) ||
              gsk_rect_intersects (&transformed_bounds, &info->background_rect))
            {
              GskRenderNodeType type = GSK_RENDER_NODE_TYPE (node);

              if (!gsk_render_node_contains_subsurface_node (node) ||
                  (type != GSK_CONTAINER_NODE &&
                   type != GSK_TRANSFORM_NODE &&
                   type != GSK_CLIP_NODE &&
                   type != GSK_ROUNDED_CLIP_NODE &&
                   type != GSK_DEBUG_NODE))
                {
                  if (gsk_render_node_clears_background (node))
                    {
                      GDK_DISPLAY_DEBUG (gdk_surface_get_display (self->surface), OFFLOAD,
                                         "[%p]   Disabling because a %s clears the background",
                                         info->subsurface,
                                         g_type_name_from_instance ((GTypeInstance *) node));
                      info->can_offload = FALSE;
                    }
                  else
                    {
                      GDK_DISPLAY_DEBUG (gdk_surface_get_display (self->surface), OFFLOAD,
                                         "[%p]   Lowering because a %s overlaps",
                                         info->subsurface,
                                         g_type_name_from_instance ((GTypeInstance *) node));
                      info->can_raise = FALSE;
                    }
                }
            }
        }
    }

  if (!gsk_render_node_contains_subsurface_node (node))
    return;

  has_clip = update_clip (self, &transformed_bounds);

  switch (GSK_RENDER_NODE_TYPE (node))
    {
    case GSK_BORDER_NODE:
    case GSK_CONIC_GRADIENT_NODE:
    case GSK_LINEAR_GRADIENT_NODE:
    case GSK_REPEATING_LINEAR_GRADIENT_NODE:
    case GSK_RADIAL_GRADIENT_NODE:
    case GSK_REPEATING_RADIAL_GRADIENT_NODE:
    case GSK_TEXT_NODE:
    case GSK_TEXTURE_NODE:
    case GSK_TEXTURE_SCALE_NODE:
    case GSK_CAIRO_NODE:
    case GSK_COLOR_NODE:
    case GSK_INSET_SHADOW_NODE:
    case GSK_OUTSET_SHADOW_NODE:
    case GSK_PASTE_NODE:
    case GSK_TURBULENCE_NODE:
      /* no children */
      break;

    case GSK_CONTAINER_NODE:
      {
        GskRenderNode **children;
        gsize i, okay, n_children;
        const char *was_impossible = self->offload_impossible;

        children = gsk_render_node_get_children (node, &n_children);

        if (!was_impossible &&
            (gsk_render_node_clears_background (node) ||
             gsk_render_node_get_copy_mode (node) != GSK_COPY_NONE))
          {
            for (okay = n_children; okay > 0; okay--)
              {
                if (gsk_render_node_clears_background (children[okay - 1]))
                  {
                    self->offload_impossible = "Composite operations";
                    break;
                  }

                if (gsk_render_node_get_copy_mode (children[okay - 1]) != GSK_COPY_NONE)
                  {
                    self->offload_impossible = "Copied contents";
                    okay--;
                    break;
                  }
              }
          }
        else
          okay = 0;

        for (i = 0; i < n_children ; i++)
          {
            if (i == okay)
              self->offload_impossible = was_impossible;

            visit_node (self, children[i]);
          }
      }
      break;

    case GSK_DEBUG_NODE:
    case GSK_COPY_NODE:
    case GSK_ISOLATION_NODE:
      /* keep going */
      visit_children (self, node);
      break;

    case GSK_GL_SHADER_NODE:
    case GSK_BLEND_NODE:
    case GSK_BLUR_NODE:
    case GSK_COLOR_MATRIX_NODE:
    case GSK_OPACITY_NODE:
    case GSK_CROSS_FADE_NODE:
    case GSK_SHADOW_NODE:
    case GSK_REPEAT_NODE:
    case GSK_MASK_NODE:
    case GSK_FILL_NODE:
    case GSK_STROKE_NODE:
    case GSK_COMPONENT_TRANSFER_NODE:
    case GSK_COMPOSITE_NODE:
    case GSK_DISPLACEMENT_NODE:
    case GSK_ARITHMETIC_NODE:
      /* cannot offload */
      {
        const char *was_impossible = self->offload_impossible;
        self->offload_impossible = g_type_name_from_instance ((GTypeInstance *) node);
        visit_children (self, node);
        self->offload_impossible = was_impossible;
      }
      break;

    case GSK_CLIP_NODE:
      {
        const graphene_rect_t *clip = gsk_clip_node_get_clip (node);
        graphene_rect_t transformed_clip;
        GskRoundedRect intersection;

        transform_bounds (self, clip, &transformed_clip);

        if (self->current_clip->is_rectilinear)
          {
            memset (&intersection.corner, 0sizeof intersection.corner);
            if (gsk_rect_intersection (&transformed_clip,
                                       &self->current_clip->rect.bounds,
                                       &intersection.bounds))
              push_rect_clip (self, &intersection);
            else
              push_empty_clip (self);
            visit_node (self, gsk_clip_node_get_child (node));
            pop_clip (self);
          }
        else
          {
            GskRoundedRectIntersection intersect;

            intersect = gsk_rounded_rect_intersect_with_rect (&self->current_clip->rect,
                                                              &transformed_clip,
                                                              &intersection);

            if (intersect == GSK_INTERSECTION_EMPTY)
              push_empty_clip (self);
            else if (intersect == GSK_INTERSECTION_NONEMPTY)
              push_rect_clip (self, &intersection);
            else
              push_complex_clip (self);
            visit_node (self, gsk_clip_node_get_child (node));
            pop_clip (self);
          }
      }
      break;

    case GSK_ROUNDED_CLIP_NODE:
      {
        const GskRoundedRect *clip = gsk_rounded_clip_node_get_clip (node);
        GskRoundedRect transformed_clip;

        if (!transform_rounded_rect (self, clip, &transformed_clip))
          {
            const char *was_impossible = self->offload_impossible;
            self->offload_impossible = "Non-dihedral transform";
            visit_node (self, gsk_rounded_clip_node_get_child (node));
            self->offload_impossible = was_impossible;
          }
        else if (self->current_clip->is_rectilinear)
          {
            GskRoundedRect intersection;
            GskRoundedRectIntersection intersect;

            intersect = gsk_rounded_rect_intersect_with_rect (&transformed_clip,
                                                              &self->current_clip->rect.bounds,
                                                              &intersection);

            if (intersect == GSK_INTERSECTION_EMPTY)
              push_empty_clip (self);
            else if (intersect == GSK_INTERSECTION_NONEMPTY)
              push_rect_clip (self, &intersection);
            else
              goto complex_clip;
            visit_node (self, gsk_rounded_clip_node_get_child (node));
            pop_clip (self);
          }
        else
          {
complex_clip:
            if (gsk_rounded_rect_contains_rect (&self->current_clip->rect, &transformed_clip.bounds))
              push_rect_clip (self, &transformed_clip);
            else
              push_complex_clip (self);
            visit_node (self, gsk_rounded_clip_node_get_child (node));
            pop_clip (self);
          }
      }
      break;

    case GSK_TRANSFORM_NODE:
      push_transform (self, gsk_transform_node_get_transform (node));
      visit_node (self, gsk_transform_node_get_child (node));
      pop_transform (self);
      break;

    case GSK_SUBSURFACE_NODE:
      {
        GdkSubsurface *subsurface = gsk_subsurface_node_get_subsurface (node);
        GskTransform *transform;

        GskOffloadInfo *info = find_subsurface_info (self, subsurface);

        transform = self->transforms ? (GskTransform *) self->transforms->data : NULL;

        if (info == NULL)
          {
            GDK_DISPLAY_DEBUG (gdk_surface_get_display (self->surface), OFFLOAD,
                               "[%p] �� Unknown subsurface",
                               subsurface);
          }
        else
          {
            info->n_nodes++;

            if (info->n_nodes > 1)
              {
                /* only print this message once, not once per node */
                if (info->n_nodes == 2)
                  GDK_DISPLAY_DEBUG (gdk_surface_get_display (self->surface), OFFLOAD,
                                     "[%p] �� Multiple nodes referring to subsurface",
                                     subsurface);
                if (info->can_offload)
                  {
                    info->can_offload = FALSE;
                    info->can_raise = FALSE;
                  }
              }
            else if (self->offload_impossible)
              {
                GDK_DISPLAY_DEBUG (gdk_surface_get_display (self->surface), OFFLOAD,
                                   "[%p] �� %s",
                                   subsurface, self->offload_impossible);
              }
            else if (!self->current_clip->is_fully_contained)
              {
                GDK_DISPLAY_DEBUG (gdk_surface_get_display (self->surface), OFFLOAD,
                                   "[%p] �� Clipped",
                                   subsurface);
              }
            else if (gsk_transform_get_fine_category (transform) < GSK_FINE_TRANSFORM_CATEGORY_2D_DIHEDRAL)
              {
                GDK_DISPLAY_DEBUG (gdk_surface_get_display (self->surface), OFFLOAD,
                                   "[%p] �� Non-dihedral transform",
                                   subsurface);
              }
            else
              {
                gboolean has_background;
                float sx, sy, dx, dy;
                GdkDihedral context_transform;
                GdkDihedral inner_transform;

                gsk_transform_to_dihedral (transform, &context_transform, &sx, &sy, &dx, &dy);

                info->texture = find_texture_to_attach (self, node, &info->texture_rect, &info->source_rect, &has_background,&nbsp;&inner_transform);
                if (info->texture)
                  {
                    info->transform = gdk_dihedral_combine (context_transform, inner_transform);
                    info->can_offload = TRUE;
                    info->can_raise = TRUE;
                    transform_bounds (self, &info->texture_rect, &info->texture_rect);
                    info->has_background = has_background;
                    transform_bounds (self, &node->bounds, &info->background_rect);
                    if (self->last_info && self->last_info->subsurface != info->subsurface)
                      info->place_above = self->last_info->subsurface;
                    else
                      info->place_above = NULL;
                    self->last_info = info;
                  }
              }
          }
      }
      break;

    case GSK_NOT_A_RENDER_NODE:
    default:
      g_assert_not_reached ();
    break;
    }

  if (has_clip)
    pop_clip (self);
}

GskOffload *
gsk_offload_new (GdkSurface     *surface,
                 GskRenderNode  *root,
                 cairo_region_t *diff)
{
  GskOffload *self;

  self = g_new0 (GskOffload, 1);

  self->surface = surface;
  self->transforms = NULL;
  self->clips = NULL;

  self->last_info = NULL;

  self->n_subsurfaces = gdk_surface_get_n_subsurfaces (self->surface);
  self->subsurfaces = g_new0 (GskOffloadInfo, self->n_subsurfaces);

  for (gsize i = 0; i < self->n_subsurfaces; i++)
    {
      GskOffloadInfo *info = &self->subsurfaces[i];
      graphene_rect_t rect;

      info->subsurface = gdk_surface_get_subsurface (self->surface, i);
      info->was_offloaded = gdk_subsurface_get_texture (info->subsurface) != NULL;
      info->was_above = gdk_subsurface_is_above_parent (info->subsurface);
      info->had_background = gdk_subsurface_get_background_rect (info->subsurface, &rect);
    }

  if (self->n_subsurfaces > 0 && gsk_render_node_contains_subsurface_node (root))
    {
      push_rect_clip (self, &GSK_ROUNDED_RECT_INIT (00,
                                                    gdk_surface_get_width (surface),
                                                    gdk_surface_get_height (surface)));
      visit_node (self, root);
      pop_clip (self);
    }

  for (gsize i = 0; i < self->n_subsurfaces; i++)
    {
      GskOffloadInfo *info = &self->subsurfaces[i];
      graphene_rect_t old_bounds;
      graphene_rect_t bounds;

      gdk_subsurface_get_bounds (info->subsurface, &old_bounds);

      if (info->can_offload)
        {
          if (info->can_raise)
            info->is_offloaded = gdk_subsurface_attach (info->subsurface,
                                                        info->texture,
                                                        &info->source_rect,
                                                        &info->texture_rect,
                                                        info->transform,
                                                        info->has_background ? &info->background_rect : NULL,
                                                        TRUE, NULL);
          else
            info->is_offloaded = gdk_subsurface_attach (info->subsurface,
                                                        info->texture,
                                                        &info->source_rect,
                                                        &info->texture_rect,
                                                        info->transform,
                                                        info->has_background ? &info->background_rect : NULL,
                                                        info->place_above != NULL,
                                                        info->place_above);
        }
      else
        {
          info->is_offloaded = FALSE;
          if (info->was_offloaded)
            gdk_subsurface_detach (info->subsurface);
        }

      info->is_above = info->is_offloaded && gdk_subsurface_is_above_parent (info->subsurface);

      gdk_subsurface_get_bounds (info->subsurface, &bounds);

      if (info->is_offloaded != info->was_offloaded ||
          info->is_above != info->was_above ||
          (info->is_offloaded && !gsk_rect_equal (&bounds, &old_bounds)))
        {
          /* We changed things, need to invalidate everything */
          cairo_rectangle_int_t rect;

          if (info->is_offloaded)
            {
              gsk_rect_to_cairo_grow (&bounds, &rect);
              cairo_region_union_rectangle (diff, &rect);
            }
          if (info->was_offloaded)
            {
              gsk_rect_to_cairo_grow (&old_bounds, &rect);
              cairo_region_union_rectangle (diff, &rect);
            }
        }

    }

  return self;
}

void
gsk_offload_free (GskOffload *self)
{
  g_free (self->subsurfaces);
  g_free (self);
}

GskOffloadInfo *
gsk_offload_get_subsurface_info (GskOffload    *self,
                                 GdkSubsurface *subsurface)
{
  return find_subsurface_info (self, subsurface);
}

Messung V0.5 in Prozent
C=98 H=96 G=96

¤ Dauer der Verarbeitung: 0.15 Sekunden  (vorverarbeitet am  2026-07-03) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.