Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Gimp/app/text/   (Gimp Bildeditor Version 3.2.0©)  Datei vom 14.2.2026 mit Größe 35 kB image not shown  

Quelle  gimptextlayer.c   Sprache: C

 

/* GIMP - The GNU Image Manipulation Program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * GimpTextLayer
 * Copyright (C) 2002-2004  Sven Neumann <sven@gimp.org>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 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 <https://www.gnu.org/licenses/>.
 */


#include "config.h"

#include <string.h>

#include <cairo.h>
#include <gegl.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <pango/pangocairo.h>

#include "libgimpbase/gimpbase.h"
#include "libgimpcolor/gimpcolor.h"
#include "libgimpconfig/gimpconfig.h"

#include "text-types.h"

#include "gegl/gimp-babl.h"
#include "gegl/gimp-gegl-loops.h"
#include "gegl/gimp-gegl-utils.h"

#include "core/gimp.h"
#include "core/gimp-utils.h"
#include "core/gimpcontainer.h"
#include "core/gimpcontext.h"
#include "core/gimpdatafactory.h"
#include "core/gimpimage.h"
#include "core/gimpimage-color-profile.h"
#include "core/gimpimage-undo.h"
#include "core/gimpimage-undo-push.h"
#include "core/gimpparasitelist.h"
#include "core/gimppattern.h"
#include "core/gimprasterizable.h"
#include "core/gimptempbuf.h"

#include "gimptext.h"
#include "gimptextlayer.h"
#include "gimptextlayout.h"
#include "gimptextlayout-render.h"

#include "gimp-intl.h"


enum
{
  PROP_0,
  PROP_TEXT,
};

struct _GimpTextLayerPrivate
{
  GimpTextDirection base_dir;

  /* on-canvas editor position */
  gboolean          style_overlay_positioned;
  gdouble           style_overlay_x;
  gdouble           style_overlay_y;
  gdouble           style_overlay_offset_x;
  gdouble           style_overlay_offset_y;
};

static void       gimp_text_layer_rasterizable_iface_init
                                                 (GimpRasterizableInterface *iface);

static void       gimp_text_layer_finalize       (GObject           *object);
static void       gimp_text_layer_get_property   (GObject           *object,
                                                  guint              property_id,
                                                  GValue            *value,
                                                  GParamSpec        *pspec);
static void       gimp_text_layer_set_property   (GObject           *object,
                                                  guint              property_id,
                                                  const GValue      *value,
                                                  GParamSpec        *pspec);

static void       gimp_text_layer_set_rasterized (GimpRasterizable  *rasterizable,
                                                  gboolean           rasterized);

static gint64     gimp_text_layer_get_memsize    (GimpObject        *object,
                                                  gint64            *gui_size);

static GimpItem * gimp_text_layer_duplicate      (GimpItem          *item,
                                                  GType              new_type);

static void       gimp_text_layer_set_buffer     (GimpDrawable      *drawable,
                                                  gboolean           push_undo,
                                                  const gchar       *undo_desc,
                                                  GeglBuffer        *buffer,
                                                  const GeglRectangle *bounds);
static void       gimp_text_layer_push_undo      (GimpDrawable      *drawable,
                                                  const gchar       *undo_desc,
                                                  GeglBuffer        *buffer,
                                                  gint               x,
                                                  gint               y,
                                                  gint               width,
                                                  gint               height);

static void       gimp_text_layer_convert_type   (GimpLayer         *layer,
                                                  GimpImage         *dest_image,
                                                  const Babl        *new_format,
                                                  GimpColorProfile  *src_profile,
                                                  GimpColorProfile  *dest_profile,
                                                  GeglDitherMethod   layer_dither_type,
                                                  GeglDitherMethod   mask_dither_type,
                                                  gboolean           push_undo,
                                                  GimpProgress      *progress);

static void       gimp_text_layer_text_changed   (GimpTextLayer     *layer);
static gboolean   gimp_text_layer_render         (GimpTextLayer     *layer);
static void       gimp_text_layer_render_layout  (GimpTextLayer     *layer,
                                                  GimpTextLayout    *layout);


G_DEFINE_TYPE_WITH_CODE (GimpTextLayer, gimp_text_layer, GIMP_TYPE_LAYER,
                         G_ADD_PRIVATE (GimpTextLayer)
                         G_IMPLEMENT_INTERFACE (GIMP_TYPE_RASTERIZABLE,
                                                gimp_text_layer_rasterizable_iface_init))

#define parent_class gimp_text_layer_parent_class


static void
gimp_text_layer_class_init (GimpTextLayerClass *klass)
{
  GObjectClass      *object_class      = G_OBJECT_CLASS (klass);
  GimpObjectClass   *gimp_object_class = GIMP_OBJECT_CLASS (klass);
  GimpViewableClass *viewable_class    = GIMP_VIEWABLE_CLASS (klass);
  GimpItemClass     *item_class        = GIMP_ITEM_CLASS (klass);
  GimpDrawableClass *drawable_class    = GIMP_DRAWABLE_CLASS (klass);
  GimpLayerClass    *layer_class       = GIMP_LAYER_CLASS (klass);

  object_class->finalize            = gimp_text_layer_finalize;
  object_class->get_property        = gimp_text_layer_get_property;
  object_class->set_property        = gimp_text_layer_set_property;

  gimp_object_class->get_memsize    = gimp_text_layer_get_memsize;

  viewable_class->default_icon_name = "gimp-text-layer";
  viewable_class->default_name      = _("Text Layer");

  item_class->duplicate             = gimp_text_layer_duplicate;
  item_class->rename                = gimp_rasterizable_rename;

#if 0
  item_class->scale                 = gimp_text_layer_scale;
  item_class->flip                  = gimp_text_layer_flip;
  item_class->rotate                = gimp_text_layer_rotate;
  item_class->transform             = gimp_text_layer_transform;
#endif

  item_class->rename_desc           = _("Rename Text Layer");
  item_class->translate_desc        = _("Move Text Layer");
  item_class->scale_desc            = _("Scale Text Layer");
  item_class->resize_desc           = _("Resize Text Layer");
  item_class->flip_desc             = _("Flip Text Layer");
  item_class->rotate_desc           = _("Rotate Text Layer");
  item_class->transform_desc        = _("Transform Text Layer");

  drawable_class->set_buffer        = gimp_text_layer_set_buffer;
  drawable_class->push_undo         = gimp_text_layer_push_undo;

  layer_class->convert_type         = gimp_text_layer_convert_type;

  GIMP_CONFIG_PROP_OBJECT (object_class, PROP_TEXT,
                           "text",
                           NULL, NULL,
                           GIMP_TYPE_TEXT,
                           GIMP_PARAM_STATIC_STRINGS);
}

static void
gimp_text_layer_init (GimpTextLayer *layer)
{
  layer->text                 = NULL;
  layer->text_parasite        = NULL;
  layer->text_parasite_is_old = FALSE;
  layer->private              = gimp_text_layer_get_instance_private (layer);
}

static void
gimp_text_layer_rasterizable_iface_init (GimpRasterizableInterface *iface)
{
  iface->set_rasterized = gimp_text_layer_set_rasterized;
}

static void
gimp_text_layer_finalize (GObject *object)
{
  GimpTextLayer *layer = GIMP_TEXT_LAYER (object);

  g_clear_object (&layer->text);

  G_OBJECT_CLASS (parent_class)->finalize (object);
}

static void
gimp_text_layer_get_property (GObject      *object,
                              guint         property_id,
                              GValue       *value,
                              GParamSpec   *pspec)
{
  GimpTextLayer *text_layer = GIMP_TEXT_LAYER (object);

  switch (property_id)
    {
    case PROP_TEXT:
      g_value_set_object (value, text_layer->text);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
gimp_text_layer_set_property (GObject      *object,
                              guint         property_id,
                              const GValue *value,
                              GParamSpec   *pspec)
{
  GimpTextLayer *text_layer = GIMP_TEXT_LAYER (object);

  switch (property_id)
    {
    case PROP_TEXT:
      gimp_text_layer_set_text (text_layer, g_value_get_object (value));
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
gimp_text_layer_set_rasterized (GimpRasterizable *rasterizable,
                                gboolean          rasterized)
{
  if (! rasterized)
    {
      if (! gimp_drawable_has_alpha (GIMP_DRAWABLE (rasterizable)))
        gimp_layer_add_alpha (GIMP_LAYER (rasterizable));

      gimp_text_layer_render (GIMP_TEXT_LAYER (rasterizable));
    }
}

static gint64
gimp_text_layer_get_memsize (GimpObject *object,
                             gint64     *gui_size)
{
  GimpTextLayer *text_layer = GIMP_TEXT_LAYER (object);
  gint64         memsize    = 0;

  memsize += gimp_object_get_memsize (GIMP_OBJECT (text_layer->text),
                                      gui_size);

  return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
                                                                  gui_size);
}

static GimpItem *
gimp_text_layer_duplicate (GimpItem *item,
                           GType     new_type)
{
  GimpItem *new_item;

  g_return_val_if_fail (g_type_is_a (new_type, GIMP_TYPE_DRAWABLE), NULL);

  new_item = GIMP_ITEM_CLASS (parent_class)->duplicate (item, new_type);

  if (GIMP_IS_TEXT_LAYER (new_item))
    {
      GimpTextLayer *layer     = GIMP_TEXT_LAYER (item);
      GimpTextLayer *new_layer = GIMP_TEXT_LAYER (new_item);

      gimp_config_sync (G_OBJECT (layer), G_OBJECT (new_layer), 0);

      if (layer->text)
        {
          GimpText *text = gimp_config_duplicate (GIMP_CONFIG (layer->text));

          gimp_text_layer_set_text (new_layer, text);

          g_object_unref (text);
        }

      /*  this is just the parasite name, not a pointer to the parasite  */
      if (layer->text_parasite)
        {
          new_layer->text_parasite        = layer->text_parasite;
          new_layer->text_parasite_is_old = layer->text_parasite_is_old;
        }

      new_layer->private->base_dir = layer->private->base_dir;
      gimp_rasterizable_set_auto_rename (GIMP_RASTERIZABLE (new_layer),
                                         gimp_rasterizable_get_auto_rename (GIMP_RASTERIZABLE (layer)));

      if (gimp_rasterizable_is_rasterized (GIMP_RASTERIZABLE (layer)))
        gimp_rasterizable_set_undo_rasterized (GIMP_RASTERIZABLE (new_layer), TRUE);
    }

  return new_item;
}

static void
gimp_text_layer_set_buffer (GimpDrawable        *drawable,
                            gboolean             push_undo,
                            const gchar         *undo_desc,
                            GeglBuffer          *buffer,
                            const GeglRectangle *bounds)
{
  GimpTextLayer *layer = GIMP_TEXT_LAYER (drawable);
  GimpImage     *image = gimp_item_get_image (GIMP_ITEM (layer));
  gboolean       is_rasterized;

  is_rasterized = gimp_rasterizable_is_rasterized (GIMP_RASTERIZABLE (layer));

  if (push_undo && ! is_rasterized)
    gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_DRAWABLE_MOD,
                                 undo_desc);

  GIMP_DRAWABLE_CLASS (parent_class)->set_buffer (drawable,
                                                  push_undo, undo_desc,
                                                  buffer, bounds);

  if (push_undo && ! is_rasterized)
    {
      gimp_rasterizable_rasterize (GIMP_RASTERIZABLE (layer), push_undo);
      gimp_image_undo_group_end (image);
    }
}

static void
gimp_text_layer_push_undo (GimpDrawable *drawable,
                           const gchar  *undo_desc,
                           GeglBuffer   *buffer,
                           gint          x,
                           gint          y,
                           gint          width,
                           gint          height)
{
  GimpTextLayer *layer = GIMP_TEXT_LAYER (drawable);
  GimpImage     *image = gimp_item_get_image (GIMP_ITEM (layer));
  gboolean       is_rasterized;

  is_rasterized = gimp_rasterizable_is_rasterized (GIMP_RASTERIZABLE (layer));

  if (! is_rasterized)
    gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_DRAWABLE, undo_desc);

  GIMP_DRAWABLE_CLASS (parent_class)->push_undo (drawable, undo_desc,
                                                 buffer,
                                                 x, y, width, height);

  if (! is_rasterized)
    {
      gimp_rasterizable_rasterize (GIMP_RASTERIZABLE (layer), TRUE);
      gimp_image_undo_group_end (image);
    }
}

static void
gimp_text_layer_convert_type (GimpLayer         *layer,
                              GimpImage         *dest_image,
                              const Babl        *new_format,
                              GimpColorProfile  *src_profile,
                              GimpColorProfile  *dest_profile,
                              GeglDitherMethod   layer_dither_type,
                              GeglDitherMethod   mask_dither_type,
                              gboolean           push_undo,
                              GimpProgress      *progress)
{
  GimpTextLayer *text_layer = GIMP_TEXT_LAYER (layer);
  GimpImage     *image      = gimp_item_get_image (GIMP_ITEM (text_layer));

  if (! text_layer->text                                          ||
      gimp_rasterizable_is_rasterized (GIMP_RASTERIZABLE (layer)) ||
      layer_dither_type != GEGL_DITHER_NONE)
    {
      GIMP_LAYER_CLASS (parent_class)->convert_type (layer, dest_image,
                                                     new_format,
                                                     src_profile,
                                                     dest_profile,
                                                     layer_dither_type,
                                                     mask_dither_type,
                                                     push_undo,
                                                     progress);
    }
  else
    {
      if (push_undo)
        gimp_image_undo_push_text_layer_convert (image, NULL, text_layer);

      text_layer->convert_format = new_format;

      gimp_text_layer_render (text_layer);

      text_layer->convert_format = NULL;
    }
}


/*  public functions  */

/**
 * gimp_text_layer_new:
 * @image: the #GimpImage the layer should belong to
 * @text: a #GimpText object
 *
 * Creates a new text layer.
 *
 * Returns: (nullable): a new #GimpTextLayer or %NULL in case of a problem
 **/

GimpLayer *
gimp_text_layer_new (GimpImage *image,
                     GimpText  *text)
{
  GimpTextLayer *layer;

  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
  g_return_val_if_fail (GIMP_IS_TEXT (text), NULL);

  if (! text->text && ! text->markup)
    return NULL;

  layer =
    GIMP_TEXT_LAYER (gimp_drawable_new (GIMP_TYPE_TEXT_LAYER,
                                        image, NULL,
                                        0, 0, 1, 1,
                                        gimp_image_get_layer_format (image,
                                                                     TRUE)));

  gimp_layer_set_mode (GIMP_LAYER (layer),
                       gimp_image_get_default_new_layer_mode (image),
                       FALSE);

  gimp_text_layer_set_text (layer, text);

  if (! gimp_text_layer_render (layer))
    {
      g_object_unref (layer);
      return NULL;
    }

  return GIMP_LAYER (layer);
}

void
gimp_text_layer_set_text (GimpTextLayer *layer,
                          GimpText      *text)
{
  g_return_if_fail (GIMP_IS_TEXT_LAYER (layer));
  g_return_if_fail (text == NULL || GIMP_IS_TEXT (text));

  if (layer->text == text)
    return;

  if (layer->text)
    {
      g_signal_handlers_disconnect_by_func (layer->text,
                                            G_CALLBACK (gimp_text_layer_text_changed),
                                            layer);

      g_clear_object (&layer->text);
    }

  if (text)
    {
      layer->text = g_object_ref (text);
      layer->private->base_dir = layer->text->base_dir;

      g_signal_connect_object (text, "changed",
                               G_CALLBACK (gimp_text_layer_text_changed),
                               layer, G_CONNECT_SWAPPED);
    }

  g_object_notify (G_OBJECT (layer), "text");
  gimp_viewable_invalidate_preview (GIMP_VIEWABLE (layer));
}

GimpText *
gimp_text_layer_get_text (GimpTextLayer *layer)
{
  g_return_val_if_fail (GIMP_IS_TEXT_LAYER (layer), NULL);

  return layer->text;
}

void
gimp_text_layer_set (GimpTextLayer *layer,
                     const gchar   *undo_desc,
                     const gchar   *first_property_name,
                     ...)
{
  GimpImage *image;
  GimpText  *text;
  va_list    var_args;

  g_return_if_fail (gimp_item_is_text_layer (GIMP_ITEM (layer)));
  g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (layer)));

  text = gimp_text_layer_get_text (layer);
  if (! text)
    return;

  image = gimp_item_get_image (GIMP_ITEM (layer));

  gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TEXT, undo_desc);

  g_object_freeze_notify (G_OBJECT (layer));

  if (gimp_rasterizable_is_rasterized (GIMP_RASTERIZABLE (layer)))
    {
      /*  pass copy_tiles = TRUE so we not only ref the tiles; after
       *  being a text layer again, undo doesn't care about the
       *  layer's pixels any longer because they are generated, so
       *  changing the text would happily overwrite the layer's
       *  pixels, changing the pixels on the undo stack too without
       *  any chance to ever undo again.
       */

      gimp_image_undo_push_drawable_mod (image, NULL,
                                         GIMP_DRAWABLE (layer), TRUE);
    }

  gimp_image_undo_push_text_layer (image, undo_desc, layer, NULL);

  va_start (var_args, first_property_name);
  g_object_set_valist (G_OBJECT (text), first_property_name, var_args);
  va_end (var_args);

  if (gimp_rasterizable_is_rasterized (GIMP_RASTERIZABLE (layer)))
    gimp_rasterizable_restore (GIMP_RASTERIZABLE (layer));

  g_object_thaw_notify (G_OBJECT (layer));

  gimp_image_undo_group_end (image);
}

gboolean
gimp_item_is_text_layer (GimpItem *item)
{
  return (GIMP_IS_TEXT_LAYER (item) &&
          ! gimp_rasterizable_is_rasterized (GIMP_RASTERIZABLE (item)));
}


void
gimp_text_layer_set_style_overlay_position (GimpTextLayer *layer,
                                            gboolean       positioned,
                                            gdouble        x,
                                            gdouble        y)
{
  GimpTextLayerPrivate *priv;

  g_return_if_fail (GIMP_IS_TEXT_LAYER (layer));

  priv = layer->private;

  priv->style_overlay_positioned = positioned;

  /* We want to set "style_overlay_x" and "style_overlay_y" only
   * when "positioned" is TRUE. Otherwhise, we only want to set
   * "style_overlay_positioned" to FALSE */

  if (positioned)
    {
      priv->style_overlay_x = x;
      priv->style_overlay_y = y;
    }
}

gboolean
gimp_text_layer_get_style_overlay_position (GimpTextLayer *layer,
                                            gdouble       *x,
                                            gdouble       *y)
{
  GimpTextLayerPrivate *priv;

  g_return_val_if_fail (GIMP_IS_TEXT_LAYER (layer), FALSE);

  priv = layer->private;

  if (! priv->style_overlay_positioned)
    return FALSE;

  *x = priv->style_overlay_x;
  *y = priv->style_overlay_y;

  return TRUE;
}

gboolean
gimp_text_layer_is_style_overlay_positioned (GimpTextLayer *layer)
{
  GimpTextLayerPrivate *priv;

  g_return_val_if_fail (GIMP_IS_TEXT_LAYER (layer), FALSE);

  priv = layer->private;

  return priv->style_overlay_positioned;
}

void
gimp_text_layer_set_style_overlay_offset (GimpTextLayer *layer,
                                          gdouble        offset_x,
                                          gdouble        offset_y)
{
  GimpTextLayerPrivate *priv;

  g_return_if_fail (GIMP_IS_TEXT_LAYER (layer));

  priv = layer->private;

  priv->style_overlay_offset_x = offset_x;
  priv->style_overlay_offset_y = offset_y;
}

void
gimp_text_layer_get_style_overlay_offset (GimpTextLayer *layer,
                                          gdouble       *offset_x,
                                          gdouble       *offset_y)
{
  GimpTextLayerPrivate *priv;

  g_return_if_fail (GIMP_IS_TEXT_LAYER (layer));

  priv = layer->private;

  *offset_x = priv->style_overlay_offset_x;
  *offset_y = priv->style_overlay_offset_y;
}


/*  private functions  */

static const Babl *
gimp_text_layer_get_format (GimpTextLayer *layer)
{
  if (layer->convert_format)
    return layer->convert_format;

  return gimp_drawable_get_format (GIMP_DRAWABLE (layer));
}

static void
gimp_text_layer_text_changed (GimpTextLayer *layer)
{
  /*  If the text layer was created from a parasite, it's time to
   *  remove that parasite now.
   */

  if (layer->text_parasite)
    {
      /*  Don't push an undo because the parasite only exists temporarily
       *  while the text layer is loaded from XCF.
       */

      gimp_item_parasite_detach (GIMP_ITEM (layer), layer->text_parasite,
                                 FALSE);
      layer->text_parasite        = NULL;
      layer->text_parasite_is_old = FALSE;
    }

  if (layer->text->box_mode == GIMP_TEXT_BOX_DYNAMIC)
    {
      gint                old_width;
      gint                new_width;
      GimpItem           *item         = GIMP_ITEM (layer);
      GimpTextDirection   old_base_dir = layer->private->base_dir;
      GimpTextDirection   new_base_dir = layer->text->base_dir;

      old_width = gimp_item_get_width (item);
      gimp_text_layer_render (layer);
      new_width = gimp_item_get_width (item);

      if (old_base_dir != new_base_dir)
        {
          switch (old_base_dir)
            {
            case GIMP_TEXT_DIRECTION_LTR:
            case GIMP_TEXT_DIRECTION_RTL:
            case GIMP_TEXT_DIRECTION_TTB_LTR:
            case GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT:
              switch (new_base_dir)
                {
                case GIMP_TEXT_DIRECTION_TTB_RTL:
                case GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT:
                  gimp_item_translate (item, -new_width, 0, FALSE);
                  break;

                case GIMP_TEXT_DIRECTION_LTR:
                case GIMP_TEXT_DIRECTION_RTL:
                case GIMP_TEXT_DIRECTION_TTB_LTR:
                case GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT:
                  break;
                }
              break;

            case GIMP_TEXT_DIRECTION_TTB_RTL:
            case GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT:
              switch (new_base_dir)
                {
                case GIMP_TEXT_DIRECTION_LTR:
                case GIMP_TEXT_DIRECTION_RTL:
                case GIMP_TEXT_DIRECTION_TTB_LTR:
                case GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT:
                  gimp_item_translate (item, old_width, 0, FALSE);
                  break;

                case GIMP_TEXT_DIRECTION_TTB_RTL:
                case GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT:
                  break;
                }
              break;
            }
        }
      else if ((new_base_dir == GIMP_TEXT_DIRECTION_TTB_RTL ||
              new_base_dir == GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT))
        {
          if (old_width != new_width)
            gimp_item_translate (item, old_width - new_width, 0, FALSE);
        }
    }
  else
    gimp_text_layer_render (layer);

  layer->private->base_dir = layer->text->base_dir;
}

static gboolean
gimp_text_layer_render (GimpTextLayer *layer)
{
  GimpDrawable   *drawable;
  GimpItem       *item;
  GimpImage      *image;
  GimpContainer  *container;
  GimpTextLayout *layout;
  gchar          *name = NULL;
  gdouble         xres;
  gdouble         yres;
  gint            width;
  gint            height;
  GError         *error = NULL;

  if (! layer->text)
    return FALSE;

  drawable  = GIMP_DRAWABLE (layer);
  item      = GIMP_ITEM (layer);
  image     = gimp_item_get_image (item);
  container = gimp_data_factory_get_container (image->gimp->font_factory);

  gimp_data_factory_data_wait (image->gimp->font_factory);

  if (gimp_container_is_empty (container))
    {
      gimp_message_literal (image->gimp, NULL, GIMP_MESSAGE_ERROR,
                            _("Due to lack of any fonts, "
                              "text functionality is not available."));
      return FALSE;
    }

  gimp_image_get_resolution (image, &xres, &yres);

  layout = gimp_text_layout_new (layer->text, image, xres, yres, &error);
  if (error)
    {
      gimp_message_literal (image->gimp, NULL, GIMP_MESSAGE_ERROR, error->message);
      g_error_free (error);
    }

  g_object_freeze_notify (G_OBJECT (drawable));

  if (gimp_text_layout_get_size (layout, &width, &height) &&
      (width  != gimp_item_get_width  (item) ||
       height != gimp_item_get_height (item) ||
       gimp_text_layer_get_format (layer) !=
       gimp_drawable_get_format (drawable)))
    {
      GeglBuffer *new_buffer;

      new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height),
                                    gimp_text_layer_get_format (layer));
      gimp_drawable_set_buffer (drawable, FALSE, NULL, new_buffer);
      g_object_unref (new_buffer);

      if (gimp_layer_get_mask (GIMP_LAYER (layer)))
        {
          GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (layer));

          static GimpContext *unused_eek = NULL;

          if (! unused_eek)
            unused_eek = gimp_context_new (image->gimp, "eek", NULL);

          gimp_item_resize (GIMP_ITEM (mask),
                            unused_eek, GIMP_FILL_TRANSPARENT,
                            width, height, 0, 0);
        }
    }

  if (layer->text->text)
    {
      name = gimp_utf8_strtrim (layer->text->text, 30);
    }
  else if (layer->text->markup)
    {
      gchar *tmp = gimp_markup_extract_text (layer->text->markup);
      name = gimp_utf8_strtrim (tmp, 30);
      g_free (tmp);
    }

  gimp_rasterizable_auto_rename (GIMP_RASTERIZABLE (layer), NULL, name);
  g_free (name);

  if (width > 0 && height > 0)
    gimp_text_layer_render_layout (layer, layout);

  g_object_unref (layout);

  g_object_thaw_notify (G_OBJECT (drawable));

  return (width > 0 && height > 0);
}

static void
gimp_text_layer_set_dash_info (cairo_t      *cr,
                               gdouble       width,
                               gdouble       dash_offset,
                               const GArray *dash_info)
{
  if (dash_info && dash_info->len >= 2)
    {
      gint     n_dashes = dash_info->len;
      gdouble *dashes   = g_new (gdouble, dash_info->len);
      gint     i;

      dash_offset = dash_offset * MAX (width, 1.0);

      for (i = 0; i < n_dashes; i++)
        dashes[i] = MAX (width, 1.0) * g_array_index (dash_info, gdouble, i);

      /* correct 0.0 in the first element (starts with a gap) */

      if (dashes[0] == 0.0)
        {
          gdouble first;

          first = dashes[1];

          /* shift the pattern to really starts with a dash and
           * use the offset to skip into it.
           */

          for (i = 0; i < n_dashes - 2; i++)
            {
              dashes[i] = dashes[i + 2];
              dash_offset += dashes[i];
            }

          if (n_dashes % 2 == 1)
            {
              dashes[n_dashes - 2] = first;
              n_dashes--;
            }
          else if (dash_info->len > 2)
            {
              dashes[n_dashes - 3] += first;
              n_dashes -= 2;
            }
        }

      /* correct odd number of dash specifiers */

      if (n_dashes % 2 == 1)
        {
          gdouble last = dashes[n_dashes - 1];

          dashes[0] += last;
          dash_offset += last;
          n_dashes--;
        }

      if (n_dashes >= 2)
        cairo_set_dash (cr,
                        dashes,
                        n_dashes,
                        dash_offset);

      g_free (dashes);
    }
}

static cairo_surface_t *
gimp_temp_buf_create_cairo_surface (GimpTempBuf *temp_buf)
{
  cairo_surface_t *surface;
  gboolean         has_alpha;
  const Babl      *format;
  const Babl      *fish = NULL;
  const guchar    *data;
  gint             width;
  gint             height;
  gint             bpp;
  guchar          *pixels;
  gint             rowstride;
  gint             i;

  g_return_val_if_fail (temp_buf != NULL, NULL);

  data      = gimp_temp_buf_get_data (temp_buf);
  format    = gimp_temp_buf_get_format (temp_buf);
  width     = gimp_temp_buf_get_width (temp_buf);
  height    = gimp_temp_buf_get_height (temp_buf);
  bpp       = babl_format_get_bytes_per_pixel (format);
  has_alpha = babl_format_has_alpha (format);

  surface = cairo_image_surface_create (has_alpha ?
                                        CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24,
                                        width, height);

  pixels    = cairo_image_surface_get_data (surface);
  rowstride = cairo_image_surface_get_stride (surface);

  if (format != babl_format (has_alpha ? "cairo-ARGB32" : "cairo-RGB24"))
    fish = babl_fish (format, babl_format (has_alpha ? "cairo-ARGB32" : "cairo-RGB24"));

  for (i = 0; i < height; i++)
    {
      if (fish)
        babl_process (fish, data, pixels, width);
      else
        memcpy (pixels, data, width * bpp);

      data += width * bpp;
      pixels += rowstride;
    }

  return surface;
}

static void
gimp_text_layer_render_layout (GimpTextLayer  *layer,
                               GimpTextLayout *layout)
{
  GimpDrawable       *drawable = GIMP_DRAWABLE (layer);
  GimpItem           *item     = GIMP_ITEM (layer);
  const Babl         *format;
  GeglBuffer         *buffer;
  cairo_t            *cr;
  cairo_surface_t    *surface;
  gint                width;
  gint                height;
  cairo_status_t      status;

  g_return_if_fail (gimp_drawable_has_alpha (drawable));

  width  = gimp_item_get_width  (item);
  height = gimp_item_get_height (item);

#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 17, 2)
  surface = cairo_image_surface_create (CAIRO_FORMAT_RGBA128F, width, height);
#else
  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
#endif
  status = cairo_surface_status (surface);

  if (status != CAIRO_STATUS_SUCCESS)
    {
      GimpImage *image = gimp_item_get_image (item);

      gimp_message_literal (image->gimp, NULL, GIMP_MESSAGE_ERROR,
                            _("Your text cannot be rendered. It is likely too big. "
                              "Please make it shorter or use a smaller font."));
      cairo_surface_destroy (surface);
      return;
    }

  cr = cairo_create (surface);
  if (layer->text->outline != GIMP_TEXT_OUTLINE_STROKE_ONLY)
    {
      cairo_save (cr);

      gimp_text_layout_render (layout, cr, layer->text->base_dir, FALSE);

      cairo_restore (cr);
    }

  if (layer->text->outline != GIMP_TEXT_OUTLINE_NONE)
    {
      GimpText *text = layer->text;

      cairo_save (cr);

      cairo_set_antialias (cr, text->outline_antialias ?
                           CAIRO_ANTIALIAS_GRAY : CAIRO_ANTIALIAS_NONE);
      cairo_set_line_cap (cr,
                          text->outline_cap_style == GIMP_CAP_BUTT ? CAIRO_LINE_CAP_BUTT :
                          text->outline_cap_style == GIMP_CAP_ROUND ? CAIRO_LINE_CAP_ROUND :
                          CAIRO_LINE_CAP_SQUARE);
      cairo_set_line_join (cr, text->outline_join_style == GIMP_JOIN_MITER ? CAIRO_LINE_JOIN_MITER :
                               text->outline_join_style == GIMP_JOIN_ROUND ? CAIRO_LINE_JOIN_ROUND :
                               CAIRO_LINE_JOIN_BEVEL);
      cairo_set_miter_limit (cr, text->outline_miter_limit);

      if (text->outline_dash_info)
        gimp_text_layer_set_dash_info (cr, text->outline_width, text->outline_dash_offset, text->outline_dash_info);

      if (text->outline_style == GIMP_CUSTOM_STYLE_PATTERN && text->outline_pattern)
        {
          GimpTempBuf     *tempbuf = gimp_pattern_get_mask (text->outline_pattern);
          cairo_surface_t *surface = gimp_temp_buf_create_cairo_surface (tempbuf);

          cairo_set_source_surface (cr, surface, 0.0, 0.0);
          cairo_surface_destroy (surface);

          cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
        }
      else
        {
          GeglColor  *col = text->outline_foreground;
          gdouble     color[3];

          format = gimp_text_layout_get_format (layout, "double");
          gegl_color_get_pixel (col, format, color);
          /* Text layout can be either grayscale or RGB without alpha. */
          if (! babl_space_is_gray (babl_format_get_space (format)))
            cairo_set_source_rgba (cr, color[0], color[1], color[2], 1.0);
          else
            cairo_set_source_rgba (cr, color[0], color[0], color[0], 1.0);
        }

      cairo_set_line_width (cr, text->outline_width * 2);

      gimp_text_layout_render (layout, cr, text->base_dir, TRUE);

      if (text->outline_direction == GIMP_TEXT_OUTLINE_DIRECTION_INNER)
        cairo_clip_preserve (cr);

      cairo_stroke_preserve (cr);

      /* Clears inner outline if outline direction is outward */
      if (text->outline_direction == GIMP_TEXT_OUTLINE_DIRECTION_OUTER &&
          text->outline == GIMP_TEXT_OUTLINE_STROKE_ONLY)
        {
          cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
          cairo_fill_preserve (cr);
        }

      cairo_restore (cr);

      if (text->outline_direction == GIMP_TEXT_OUTLINE_DIRECTION_OUTER &&
          text->outline != GIMP_TEXT_OUTLINE_STROKE_ONLY)
        {
          cairo_save (cr);

          gimp_text_layout_render (layout, cr, layer->text->base_dir, FALSE);

          cairo_restore (cr);
        }
    }

  cairo_destroy (cr);

  cairo_surface_flush (surface);

#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 17, 2)
  /* The CAIRO_FORMAT_RGBA128F surface maps to the layout TRC and space. */
  switch (gimp_text_layout_get_trc (layout))
    {
    case GIMP_TRC_LINEAR:
      format = babl_format_with_space ("RaGaBaA float", gimp_text_layout_get_space (layout));
      break;
    case GIMP_TRC_NON_LINEAR:
      format = babl_format_with_space ("R'aG'aB'aA float", gimp_text_layout_get_space (layout));
      break;
    case GIMP_TRC_PERCEPTUAL:
      format = babl_format_with_space ("R~aG~aB~aA float", gimp_text_layout_get_space (layout));
      break;
    default:
      g_return_if_reached ();
    }
#else
  format = babl_format_with_space ("cairo-ARGB32", gimp_text_layout_get_space (layout));
#endif
  buffer = gimp_cairo_surface_get_buffer (surface, format, FALSE);

  gimp_gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE,
                         gimp_drawable_get_buffer (drawable), NULL);

  g_object_unref (buffer);
  cairo_surface_destroy (surface);

  gimp_drawable_update (drawable, 0, 0, width, height);
}

Messung
CHZG
839710093

¤ Dauer der Verarbeitung: 0.7 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


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.