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

Quelle  gdktextureutils.c

  Sprache: C
 

/* Copyright (C) 2016 Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 */


#include "config.h"

#include <gsk/gsk.h>
#include "gdktextureutilsprivate.h"
#include "gdk/gdktextureprivate.h"
#include "gtksnapshot.h"
#include "svg/gtksvg.h"
#include "gtksymbolicpaintable.h"
#include "gtkprivate.h"

/* {{{ svg helpers */

static GdkTexture *
svg_to_texture (GtkSvg  *svg,
                int      width,
                int      height,
                GdkRGBA *colors,
                size_t   n_colors)
{
  GtkSnapshot *snapshot;
  GskRenderNode *node;
  cairo_surface_t *surface;
  cairo_t *cr;
  GdkTexture *texture;

  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);

  snapshot = gtk_snapshot_new ();

  gtk_symbolic_paintable_snapshot_symbolic (GTK_SYMBOLIC_PAINTABLE (svg),
                                            snapshot,
                                            width, height,
                                            colors, n_colors);

  node = gtk_snapshot_free_to_node (snapshot);

  if (node)
    {
      cr = cairo_create (surface);
      gsk_render_node_draw (node, cr);
      cairo_destroy (cr);

      gsk_render_node_unref (node);
    }

  texture = gdk_texture_new_for_surface (surface);
  cairo_surface_destroy (surface);

  return texture;
}

static void
svg_load_error (GtkSvg       *svg,
                const GError *error,
                gpointer      user_data)
{
  char **unsupported = user_data;

  if (g_error_matches (error, GTK_SVG_ERROR, GTK_SVG_ERROR_NOT_IMPLEMENTED))
    {
      if (unsupported && !*unsupported)
        *unsupported = g_strdup (error->message);
    }
}

static GtkSvg *
svg_from_bytes (GBytes     *bytes,
                gboolean    is_symbolic,
                char      **unsupported)
{
  GtkSvg *svg;
  gulong handler_id;

  svg = gtk_svg_new ();

  if (is_symbolic)
    gtk_svg_set_features (svg, GTK_SVG_DEFAULT_FEATURES | GTK_SVG_TRADITIONAL_SYMBOLIC);

  handler_id = g_signal_connect (svg, "error", G_CALLBACK (svg_load_error), unsupported);
  gtk_svg_load_from_bytes (svg, bytes);
  g_signal_handler_disconnect (svg, handler_id);

  return svg;
}

/* }}} */
/* {{{ Symbolic processing */

static GdkTexture *
load_symbolic_svg (GtkSvg      *svg,
                   int          width,
                   int          height,
                   const char  *fg_color,
                   const char  *success_color,
                   const char  *warning_color,
                   const char  *error_color,
                   GError     **error)
{
  GdkRGBA colors[4];

  gdk_rgba_parse (&colors[GTK_SYMBOLIC_COLOR_FOREGROUND], fg_color);
  gdk_rgba_parse (&colors[GTK_SYMBOLIC_COLOR_SUCCESS], success_color);
  gdk_rgba_parse (&colors[GTK_SYMBOLIC_COLOR_WARNING], warning_color);
  gdk_rgba_parse (&colors[GTK_SYMBOLIC_COLOR_ERROR], error_color);

  return svg_to_texture (svg, width, height, colors, 4);
}

static gboolean
extract_plane (GdkTexture *src,
               guchar     *dst_data,
               gsize       dst_width,
               gsize       dst_height,
               int         from_plane,
               int         to_plane)
{
  const guchar *src_data, *src_row;
  int width, height;
  gsize src_stride, dst_stride;
  guchar *dst_row;
  int x, y;
  gboolean all_clear = TRUE;
  GdkTextureDownloader *downloader;
  GBytes *bytes;

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

  g_assert (width <= dst_width);
  g_assert (height <= dst_height);

  downloader = gdk_texture_downloader_new (src);
  gdk_texture_downloader_set_format (downloader, GDK_MEMORY_R8G8B8A8);
  bytes = gdk_texture_downloader_download_bytes (downloader, &src_stride);

  src_data = g_bytes_get_data (bytes, NULL);

  dst_stride = dst_width * 4;

  for (y = 0; y < height; y++)
    {
      src_row = src_data + src_stride * y;
      dst_row = dst_data + dst_stride * y;
      for (x = 0; x < width; x++)
        {
          if (src_row[from_plane] != 0)
            all_clear = FALSE;

          dst_row[to_plane] = src_row[from_plane];
          src_row += 4;
          dst_row += 4;
        }
    }

  gdk_texture_downloader_free (downloader);
  g_bytes_unref (bytes);

  return all_clear;
}

static GdkTexture *
keep_alpha (GdkTexture *src)
{
  GdkTextureDownloader *downloader;
  GBytes *bytes;
  gsize stride;
  gsize width, height;
  guchar *data;
  gsize size;
  GdkTexture *res;

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

  downloader = gdk_texture_downloader_new (src);
  gdk_texture_downloader_set_format (downloader, GDK_MEMORY_R8G8B8A8);
  data = g_bytes_unref_to_data (gdk_texture_downloader_download_bytes (downloader, &stride), ;&size);

  for (gsize y = 0; y < height; y++)
    {
      guchar *row = data + stride * y;
      for (gsize x = 0; x < width; x++)
        {
          row[0] = row[1] = row[2] = 0;
          row += 4;
        }
    }

  bytes = g_bytes_new_take (data, size);
  res = gdk_memory_texture_new (width, height, GDK_MEMORY_R8G8B8A8, bytes, stride);
  g_bytes_unref (bytes);
  g_object_unref (src);

  gdk_texture_downloader_free (downloader);

  return res;
}

static gboolean
svg_has_symbolic_classes (GBytes *bytes)
{
#ifdef HAVE_MEMMEM
  const char *data;
  gsize len;

  data = g_bytes_get_data (bytes, &len);

  /* Not super precise, but good enough */
  return memmem (data, len, "class=\"", strlen ("class=\"")) != NULL;
#else
  return TRUE;
#endif
}

static GdkTexture *
gdk_texture_new_from_bytes_symbolic (GBytes    *bytes,
                                     int        width,
                                     int        height,
                                     gboolean  *out_only_fg,
                                     GError   **error)

{
  GtkSvg *svg;
  const char *r_string = "rgb(255,0,0)";
  const char *g_string = "rgb(0,255,0)";
  gboolean only_fg;
  guchar *data;
  GBytes *data_bytes;
  GdkTexture *texture;

  svg = gtk_svg_new_from_bytes (bytes);

  if (width == 0 && height == 0)
    {
      width = gdk_paintable_get_intrinsic_width (GDK_PAINTABLE (svg));
      height = gdk_paintable_get_intrinsic_height (GDK_PAINTABLE (svg));
      if (width == 0 || height == 0)
        {
          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                       "Symbolic icon has no intrinsic size; please set one in its SVG");
          g_object_unref (svg);
          return NULL;
        }
    }

  if (!svg_has_symbolic_classes (bytes))
    {
      texture = svg_to_texture (svg, width, height, NULL, 0);

      if (texture)
        texture = keep_alpha (texture);

      if (out_only_fg)
        *out_only_fg = TRUE;

      g_object_unref (svg);

      return texture;
    }

  only_fg = TRUE;
  texture = NULL;

  data = NULL;
  data_bytes = NULL;

  for (int plane = 0; plane < 3; plane++)
    {
      GdkTexture *loaded;

      /* Here we render the svg with all colors solid, this should
       * always make the alpha channel the same and it should match
       * the final alpha channel for all possible renderings. We
       * Just use it as-is for final alpha.
       *
       * For the 3 non-fg colors, we render once each with that
       * color as red, and every other color as green. The resulting
       * red will describe the amount of that color is in the
       * opaque part of the color. We store these as the rgb
       * channels, with the color of the fg being implicitly
       * the "rest", as all color fractions should add up to 1.
       */

      loaded = load_symbolic_svg (svg, width, height,
                                  g_string,
                                  plane == 0 ? r_string : g_string,
                                  plane == 1 ? r_string : g_string,
                                  plane == 2 ? r_string : g_string,
                                  error);
      if (loaded == NULL)
        goto out;

      if (plane == 0)
        {
          data = g_new0 (guchar, 4 * width * height);
          data_bytes = g_bytes_new_take (data, 4 * width * height);

          extract_plane (loaded, data, width, height, 33);
        }

      only_fg &= extract_plane (loaded, data, width, height, 0, plane);

      g_object_unref (loaded);
    }

  texture = gdk_memory_texture_new (width, height,
                                    GDK_MEMORY_R8G8B8A8,
                                    data_bytes,
                                    4 * width);

out:
  g_bytes_unref (data_bytes);
  g_object_unref (svg);

  if (out_only_fg)
    *out_only_fg = only_fg;

  return texture;
}

/* }}} */
/* {{{ Texture-from-stream API */

static GBytes *
input_stream_get_bytes (GInputStream  *stream,
                        GError       **error)
{
  GOutputStream *out;
  gssize res;
  GBytes *bytes;

  out = g_memory_output_stream_new_resizable ();
  res = g_output_stream_splice (out, stream, G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, NULL, error);

  if (res == -1)
    {
      g_object_unref (out);
      return NULL;
    }

  bytes = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (out));
  g_object_unref (out);

  return bytes;
}

GdkTexture *
gdk_texture_new_from_filename_at_scale (const char  *filename,
                                        int          width,
                                        int          height,
                                        GError     **error)
{
  GFile *file;
  GInputStream *stream;
  GBytes *bytes;
  GtkSvg *svg;
  GdkTexture *texture;
  char *unsupported = NULL;

  file = g_file_new_for_path (filename);
  stream = G_INPUT_STREAM (g_file_read (file, NULL, error));
  g_object_unref (file);
  if (!stream)
    return NULL;

  bytes = input_stream_get_bytes (stream, error);
  g_object_unref (stream);
  if (!bytes)
    return NULL;

  svg = svg_from_bytes (bytes, FALSE, &unsupported);
  g_bytes_unref (bytes);
  if (unsupported)
    {
      g_clear_object (&svg);
      g_free (unsupported);
      return NULL;
    }

  texture = svg_to_texture (svg, width, height, NULL, 0);
  g_object_unref (svg);

  return texture;
}

/* }}} */
/* {{{ Paintable API */

static gboolean
is_symbolic (const char *path)
{
  return g_str_has_suffix (path, "-symbolic.svg") ||
         g_str_has_suffix (path, "-symbolic-ltr.svg") ||
         g_str_has_suffix (path, "-symbolic-rtl.svg");
}

static GdkPaintable *
gdk_paintable_new_from_bytes (GBytes     *bytes,
                              const char *path,
                              gboolean    is_symbolic)
{
  GdkPaintable *paintable;

  if (gdk_texture_can_load (bytes))
    paintable = GDK_PAINTABLE (gdk_texture_new_from_bytes (bytes, NULL));
  else
    {
      GtkSvg *svg;
      char *unsupported = NULL;

      svg = svg_from_bytes (bytes, is_symbolic, &unsupported);
      if (unsupported)
        {
          gboolean only_fg;

          if (is_symbolic)
            paintable = GDK_PAINTABLE (gdk_texture_new_from_bytes_symbolic (bytes, 00, &only_fg, NULL));
          else
            paintable = GDK_PAINTABLE (gdk_texture_new_from_bytes (bytes, NULL));
          if (paintable)
            {
              GTK_DEBUG (ICONFALLBACK, "Falling back to a texture for %s: %s", path, unsupported);
              g_clear_object (&svg);
              g_free (unsupported);
            }
          else
            {
              GTK_DEBUG (ICONFALLBACK, "Expect misrendering; %s uses unsupported features: %s", path, unsupported);
              g_free (unsupported);
              paintable = GDK_PAINTABLE (svg);
            }
        }
      else
        paintable = GDK_PAINTABLE (svg);
    }

  return paintable;
}

GdkPaintable *
gdk_paintable_new_from_filename (const char  *filename,
                                 GError     **error)
{
  char *contents;
  gsize length;
  GBytes *bytes;
  GdkPaintable *paintable;

  if (!g_file_get_contents (filename, &contents, &length, error))
    return NULL;

  bytes = g_bytes_new_take (contents, length);
  paintable = gdk_paintable_new_from_bytes (bytes, filename, is_symbolic (filename));
  g_bytes_unref (bytes);

  return paintable;
}

GdkPaintable *
gdk_paintable_new_from_resource (const char *path)
{
  GBytes *bytes;
  GdkPaintable *paintable;

  bytes = g_resources_lookup_data (path, G_RESOURCE_LOOKUP_FLAGS_NONE, NULL);
  if (!bytes)
    return NULL;

  paintable = gdk_paintable_new_from_bytes (bytes, path, is_symbolic (path));
  g_bytes_unref (bytes);

  return paintable;
}

GdkPaintable *
gdk_paintable_new_from_file (GFile   *file,
                             GError **error)
{
  GBytes *bytes;
  GdkPaintable *paintable;
  char *uri;

  bytes = g_file_load_bytes (file, NULL, NULL, error);
  if (!bytes)
    return NULL;

  uri = g_file_get_uri (file);
  paintable = gdk_paintable_new_from_bytes (bytes, uri, is_symbolic (uri));
  g_bytes_unref (bytes);
  g_free (uri);

  return paintable;
}

GdkPaintable *
gdk_paintable_new_from_stream (GInputStream  *stream,
                               GCancellable  *cancellable,
                               GError       **error)
{
  GBytes *bytes;
  GdkPaintable *paintable;

  bytes = input_stream_get_bytes (stream, error);
  if (!bytes)
    return NULL;

  paintable = gdk_paintable_new_from_bytes (bytes, "?"FALSE);
  g_bytes_unref (bytes);

  return paintable;
}

/* }}} */

/* vim:set foldmethod=marker: */

Messung V0.5 in Prozent
C=98 H=94 G=95

¤ Dauer der Verarbeitung: 0.10 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.