[gimp/wip/schumaml/issue-35-outlined-text: 20/21] Issue #35 - Outlined text



commit 8b3c8588e52ab05dc9f5b32447b4e81ee628c4c4
Author: Michael Schumacher <schumaml gmx de>
Date:   Tue Dec 24 23:50:27 2019 +0100

    Issue #35 - Outlined text
    
    Manually applied the latest patch by Massimo

 app/core/gimp-memsize.c      |  21 ++-
 app/text/gimptext-parasite.c |   7 +-
 app/text/gimptext-parasite.h |   1 +
 app/text/gimptext.c          | 224 ++++++++++++++++++++++++++++-
 app/text/gimptext.h          |  11 ++
 app/text/gimptextlayer-xcf.c |   2 +-
 app/text/gimptextlayer.c     | 172 +++++++++++++++++++++-
 app/text/text-enums.c        |   6 +-
 app/text/text-enums.h        |   6 +-
 app/tools/gimptextoptions.c  | 333 ++++++++++++++++++++++++++++++++++++++++++-
 app/tools/gimptextoptions.h  |  13 ++
 app/widgets/gimpfilleditor.c |   2 +-
 12 files changed, 781 insertions(+), 17 deletions(-)
---
diff --git a/app/core/gimp-memsize.c b/app/core/gimp-memsize.c
index 7c55c7b0d0..e45154684c 100644
--- a/app/core/gimp-memsize.c
+++ b/app/core/gimp-memsize.c
@@ -251,6 +251,20 @@ gimp_g_value_get_memsize (GValue *value)
                 }
             }
         }
+      else if (strcmp ("GimpValueArray", G_VALUE_TYPE_NAME (value)) == 0)
+        {
+          GimpValueArray *array = g_value_get_boxed (value);
+
+          if (array)
+            {
+              gint n_values = gimp_value_array_length (array), i;
+
+              memsize += /* sizeof (GimpValueArray) */ sizeof (GValue *) + 3 * sizeof (gint);
+
+              for (i = 0; i < n_values; i++)
+                memsize += gimp_g_value_get_memsize (gimp_value_array_index (array, i));
+            }
+        }
       else
         {
           g_printerr ("%s: unhandled boxed value type: %s\n",
@@ -259,8 +273,11 @@ gimp_g_value_get_memsize (GValue *value)
     }
   else if (G_VALUE_HOLDS_OBJECT (value))
     {
-      g_printerr ("%s: unhandled object value type: %s\n",
-                  G_STRFUNC, G_VALUE_TYPE_NAME (value));
+      if (strcmp ("GimpPattern", G_VALUE_TYPE_NAME (value)) == 0)
+        memsize += gimp_g_object_get_memsize (g_value_get_object (value));
+      else
+        g_printerr ("%s: unhandled object value type: %s\n",
+                    G_STRFUNC, G_VALUE_TYPE_NAME (value));
     }
 
   return memsize + sizeof (GValue);
diff --git a/app/text/gimptext-parasite.c b/app/text/gimptext-parasite.c
index 0b8c617451..c098d90f8a 100644
--- a/app/text/gimptext-parasite.c
+++ b/app/text/gimptext-parasite.c
@@ -63,18 +63,19 @@ gimp_text_to_parasite (const GimpText *text)
                                             NULL);
 }
 
+
 GimpText *
 gimp_text_from_parasite (const GimpParasite  *parasite,
+                         Gimp                *gimp,
                          GError             **error)
 {
-  GimpText *text;
-
+  GimpText    *text;
   g_return_val_if_fail (parasite != NULL, NULL);
   g_return_val_if_fail (strcmp (gimp_parasite_name (parasite),
                                 gimp_text_parasite_name ()) == 0, NULL);
   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
-  text = g_object_new (GIMP_TYPE_TEXT, NULL);
+  text = g_object_new (GIMP_TYPE_TEXT, "gimp", gimp, NULL);
 
   if (gimp_parasite_data (parasite))
     {
diff --git a/app/text/gimptext-parasite.h b/app/text/gimptext-parasite.h
index d834309e47..ddce2a8cad 100644
--- a/app/text/gimptext-parasite.h
+++ b/app/text/gimptext-parasite.h
@@ -25,6 +25,7 @@
 const gchar  * gimp_text_parasite_name          (void) G_GNUC_CONST;
 GimpParasite * gimp_text_to_parasite            (const GimpText      *text);
 GimpText     * gimp_text_from_parasite          (const GimpParasite  *parasite,
+                                                 Gimp                *gimp,
                                                  GError             **error);
 
 const gchar  * gimp_text_gdyntext_parasite_name (void) G_GNUC_CONST;
diff --git a/app/text/gimptext.c b/app/text/gimptext.c
index fe671a42ba..1cdb7e3c95 100644
--- a/app/text/gimptext.c
+++ b/app/text/gimptext.c
@@ -34,10 +34,15 @@
 
 #include "text-types.h"
 
+#include "core/gimp.h"
 #include "core/gimp-memsize.h"
 #include "core/gimp-utils.h"
+#include "core/gimpcontainer.h"
+#include "core/gimpdashpattern.h"
+#include "core/gimpdatafactory.h"
 #include "core/gimpmarshal.h"
 #include "core/gimpstrokeoptions.h"
+#include "core/gimppattern.h"
 
 #include "gimptext.h"
 
@@ -69,8 +74,24 @@ enum
   PROP_OFFSET_X,
   PROP_OFFSET_Y,
   PROP_BORDER,
+
+  PROP_OUTLINE_STYLE,       /* fill-options */
+  PROP_OUTLINE_FOREGROUND,  /* context */
+  PROP_OUTLINE_PATTERN,     /* context */
+  PROP_OUTLINE_WIDTH,       /* stroke-options */
+  PROP_OUTLINE_UNIT,
+  PROP_OUTLINE_CAP_STYLE,
+  PROP_OUTLINE_JOIN_STYLE,
+  PROP_OUTLINE_MITER_LIMIT,
+  PROP_OUTLINE_ANTIALIAS,   /* fill-options */
+  PROP_OUTLINE_DASH_UNIT, /* NOT USED */
+  PROP_OUTLINE_DASH_OFFSET,
+  PROP_OUTLINE_DASH_INFO,
+
   /* for backward compatibility */
-  PROP_HINTING
+  PROP_HINTING,
+
+  PROP_GIMP
 };
 
 enum
@@ -79,6 +100,18 @@ enum
   LAST_SIGNAL
 };
 
+static void     gimp_text_config_iface_init           (GimpConfigInterface *iface);
+static gboolean gimp_text_serialize_property          (GimpConfig       *config,
+                                                       guint             property_id,
+                                                       const GValue     *value,
+                                                       GParamSpec       *pspec,
+                                                       GimpConfigWriter *writer);
+static gboolean gimp_text_deserialize_property        (GimpConfig       *config,
+                                                       guint             property_id,
+                                                       GValue           *value,
+                                                       GParamSpec       *pspec,
+                                                       GScanner         *scanner,
+                                                       GTokenType       *expected);
 
 static void     gimp_text_finalize                    (GObject      *object);
 static void     gimp_text_get_property                (GObject      *object,
@@ -97,7 +130,8 @@ static gint64   gimp_text_get_memsize                 (GimpObject   *object,
 
 
 G_DEFINE_TYPE_WITH_CODE (GimpText, gimp_text, GIMP_TYPE_OBJECT,
-                         G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG, NULL))
+                         G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG,
+                                                gimp_text_config_iface_init))
 
 #define parent_class gimp_text_parent_class
 
@@ -110,8 +144,10 @@ gimp_text_class_init (GimpTextClass *klass)
   GObjectClass    *object_class      = G_OBJECT_CLASS (klass);
   GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
   GimpRGB          black;
+  GimpRGB          gray;
   GimpMatrix2      identity;
   gchar           *language;
+  GParamSpec      *array_spec;
 
   text_signals[CHANGED] =
     g_signal_new ("changed",
@@ -130,6 +166,7 @@ gimp_text_class_init (GimpTextClass *klass)
   gimp_object_class->get_memsize            = gimp_text_get_memsize;
 
   gimp_rgba_set (&black, 0.0, 0.0, 0.0, GIMP_OPACITY_OPAQUE);
+  gimp_rgba_set (&gray, 0.75, 0.75, 0.75, GIMP_OPACITY_OPAQUE);
   gimp_matrix2_identity (&identity);
 
   GIMP_CONFIG_PROP_STRING (object_class, PROP_TEXT,
@@ -300,12 +337,68 @@ gimp_text_class_init (GimpTextClass *klass)
                                                      G_PARAM_CONSTRUCT |
                                                      GIMP_PARAM_WRITABLE));
 
+   GIMP_CONFIG_INSTALL_PROP_ENUM (object_class, PROP_OUTLINE_STYLE,
+                                  "outline-style", NULL,
+                                  GIMP_TYPE_FILL_STYLE,
+                                  GIMP_FILL_STYLE_SOLID,
+                                  GIMP_PARAM_STATIC_STRINGS);
+   GIMP_CONFIG_INSTALL_PROP_OBJECT (object_class, PROP_OUTLINE_PATTERN,
+                                    "outline-pattern", NULL,
+                                    GIMP_TYPE_PATTERN,
+                                    GIMP_PARAM_STATIC_STRINGS);
+   GIMP_CONFIG_INSTALL_PROP_RGB (object_class, PROP_OUTLINE_FOREGROUND,
+                                 "outline-foreground", NULL,
+                                 FALSE, &gray,
+                                 GIMP_PARAM_STATIC_STRINGS);
+   GIMP_CONFIG_INSTALL_PROP_DOUBLE (object_class, PROP_OUTLINE_WIDTH,
+                                    "outline-width", NULL,
+                                    0.0, 8192.0, 4.0,
+                                    GIMP_PARAM_STATIC_STRINGS |
+                                    GIMP_CONFIG_PARAM_DEFAULTS);
+   GIMP_CONFIG_INSTALL_PROP_ENUM (object_class, PROP_OUTLINE_CAP_STYLE,
+                                  "outline-cap-style", NULL,
+                                  GIMP_TYPE_CAP_STYLE, GIMP_CAP_BUTT,
+                                  GIMP_PARAM_STATIC_STRINGS);
+   GIMP_CONFIG_INSTALL_PROP_ENUM (object_class, PROP_OUTLINE_JOIN_STYLE,
+                                  "outline-join-style", NULL,
+                                  GIMP_TYPE_JOIN_STYLE, GIMP_JOIN_MITER,
+                                  GIMP_PARAM_STATIC_STRINGS);
+   GIMP_CONFIG_INSTALL_PROP_DOUBLE (object_class, PROP_OUTLINE_MITER_LIMIT,
+                                    "outline-miter-limit",
+                                    NULL,
+                                    0.0, 100.0, 10.0,
+                                    GIMP_PARAM_STATIC_STRINGS);
+   GIMP_CONFIG_INSTALL_PROP_BOOLEAN (object_class, PROP_OUTLINE_ANTIALIAS,
+                                     "outline-antialias", NULL,
+                                     TRUE,
+                                     GIMP_PARAM_STATIC_STRINGS);
+   GIMP_CONFIG_INSTALL_PROP_DOUBLE (object_class, PROP_OUTLINE_DASH_OFFSET,
+                                    "outline-dash-offset", NULL,
+                                    0.0, 2000.0, 0.0,
+                                    GIMP_PARAM_STATIC_STRINGS);
+
+   array_spec = g_param_spec_double ("outline-dash-length", NULL, NULL,
+                                     0.0, 2000.0, 1.0, GIMP_PARAM_READWRITE);
+   g_object_class_install_property (object_class, PROP_OUTLINE_DASH_INFO,
+                                    gimp_param_spec_value_array ("outline-dash-info",
+                                                                 NULL, NULL,
+                                                                 array_spec,
+                                                                 GIMP_PARAM_STATIC_STRINGS |
+                                                                 GIMP_CONFIG_PARAM_FLAGS));
+
   /*  the old hinting options have been replaced by 'hint-style'  */
   GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_HINTING,
                             "hinting",
                             NULL, NULL,
                             TRUE,
                             GIMP_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_property (object_class, PROP_GIMP,
+                                   g_param_spec_object ("gimp", NULL, NULL,
+                                                        GIMP_TYPE_GIMP,
+                                                        GIMP_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT_ONLY));
+
 }
 
 static void
@@ -313,6 +406,13 @@ gimp_text_init (GimpText *text)
 {
 }
 
+static void
+gimp_text_config_iface_init (GimpConfigInterface *iface)
+{
+  iface->serialize_property   = gimp_text_serialize_property;
+  iface->deserialize_property = gimp_text_deserialize_property;
+}
+
 static void
 gimp_text_finalize (GObject *object)
 {
@@ -405,10 +505,51 @@ gimp_text_get_property (GObject      *object,
     case PROP_OFFSET_Y:
       g_value_set_double (value, text->offset_y);
       break;
+    case PROP_OUTLINE_STYLE:
+      g_value_set_enum (value, text->outline_style);
+      break;
+    case PROP_OUTLINE_FOREGROUND:
+      g_value_set_boxed (value, &text->outline_foreground);
+      break;
+    case PROP_OUTLINE_PATTERN:
+      g_value_set_object (value, text->outline_pattern);
+      break;
+    case PROP_OUTLINE_WIDTH:
+      g_value_set_double (value, text->outline_width);
+      break;
+    case PROP_OUTLINE_CAP_STYLE:
+      g_value_set_enum (value, text->outline_cap_style);
+      break;
+    case PROP_OUTLINE_JOIN_STYLE:
+      g_value_set_enum (value, text->outline_join_style);
+      break;
+    case PROP_OUTLINE_MITER_LIMIT:
+      g_value_set_double (value, text->outline_miter_limit);
+      break;
+    case PROP_OUTLINE_ANTIALIAS:
+      g_value_set_boolean (value, text->outline_antialias);
+      break;
+    case PROP_OUTLINE_DASH_UNIT:
+      /* NOT USED */
+      break;
+    case PROP_OUTLINE_DASH_OFFSET:
+      g_value_set_double (value, text->outline_dash_offset);
+      break;
+    case PROP_OUTLINE_DASH_INFO:
+      {
+        GimpValueArray *value_array;
+
+        value_array = gimp_dash_pattern_to_value_array (text->outline_dash_info);
+        g_value_take_boxed (value, value_array);
+      }
+      break;
     case PROP_HINTING:
       g_value_set_boolean (value,
                            text->hint_style != GIMP_TEXT_HINT_STYLE_NONE);
       break;
+    case PROP_GIMP:
+      g_value_set_object (value, text->gimp);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       break;
@@ -594,3 +735,82 @@ gimp_text_get_transformation (GimpText    *text,
   matrix->coeff[2][1] = 0.0;
   matrix->coeff[2][2] = 1.0;
 }
+
+static gboolean
+gimp_text_serialize_property (GimpConfig       *config,
+                              guint             property_id,
+                              const GValue     *value,
+                              GParamSpec       *pspec,
+                              GimpConfigWriter *writer)
+{
+  if (property_id == PROP_OUTLINE_PATTERN)
+    {
+      GimpObject *serialize_obj = g_value_get_object (value);
+
+      gimp_config_writer_open (writer, pspec->name);
+
+      if (serialize_obj)
+        gimp_config_writer_string (writer, gimp_object_get_name (serialize_obj));
+      else
+        gimp_config_writer_print (writer, "NULL", 4);
+
+      gimp_config_writer_close (writer);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+gimp_text_deserialize_property (GimpConfig *object,
+                                guint       property_id,
+                                GValue     *value,
+                                GParamSpec *pspec,
+                                GScanner   *scanner,
+                                GTokenType *expected)
+{
+  if (property_id == PROP_OUTLINE_PATTERN)
+    {
+      gchar *object_name;
+
+      if (gimp_scanner_parse_identifier (scanner, "NULL"))
+        {
+          g_value_set_object (value, NULL);
+        }
+      else if (gimp_scanner_parse_string (scanner, &object_name))
+        {
+          GimpText      *text = GIMP_TEXT (object);
+          GimpContainer *container;
+          GimpObject    *deserialize_obj;
+
+          if (! object_name)
+            object_name = g_strdup ("");
+
+          container = gimp_data_factory_get_container (text->gimp->pattern_factory);
+
+          deserialize_obj = gimp_container_get_child_by_name (container,
+                                                              object_name);
+
+          g_value_set_object (value, deserialize_obj);
+
+          g_free (object_name);
+        }
+      else
+        {
+          *expected = G_TOKEN_STRING;
+        }
+
+      return TRUE;
+    }
+  else if (property_id == PROP_OUTLINE_DASH_INFO)
+    {
+      if (gimp_scanner_parse_identifier (scanner, "NULL"))
+        {
+          g_value_take_boxed (value, NULL);
+          return TRUE;
+        }
+    }
+
+  return FALSE;
+}
diff --git a/app/text/gimptext.h b/app/text/gimptext.h
index 8a292be896..4ca3b333be 100644
--- a/app/text/gimptext.h
+++ b/app/text/gimptext.h
@@ -50,6 +50,16 @@ struct _GimpText
   gchar                 *language;
   GimpTextDirection      base_dir;
   GimpRGB                color;
+  GimpFillStyle          outline_style;
+  GimpPattern           *outline_pattern;
+  GimpRGB                outline_foreground;
+  gdouble                outline_width;
+  GimpCapStyle           outline_cap_style;
+  GimpJoinStyle          outline_join_style;
+  gdouble                outline_miter_limit;
+  gboolean               outline_antialias;
+  gdouble                outline_dash_offset;
+  GArray                *outline_dash_info;
   GimpTextOutline        outline;
   GimpTextJustification  justify;
   gdouble                indent;
@@ -64,6 +74,7 @@ struct _GimpText
   gdouble                offset_y;
 
   gdouble                border;
+  Gimp                  *gimp;
 };
 
 struct _GimpTextClass
diff --git a/app/text/gimptextlayer-xcf.c b/app/text/gimptextlayer-xcf.c
index d0961ee148..9a58b4865f 100644
--- a/app/text/gimptextlayer-xcf.c
+++ b/app/text/gimptextlayer-xcf.c
@@ -69,7 +69,7 @@ gimp_text_layer_xcf_load_hack (GimpLayer **layer)
     {
       GError *error = NULL;
 
-      text = gimp_text_from_parasite (parasite, &error);
+      text = gimp_text_from_parasite (parasite, gimp_item_get_image (GIMP_ITEM(*layer))->gimp, &error);
 
       if (error)
         {
diff --git a/app/text/gimptextlayer.c b/app/text/gimptextlayer.c
index 002cec1904..2d553bcf37 100644
--- a/app/text/gimptextlayer.c
+++ b/app/text/gimptextlayer.c
@@ -48,6 +48,8 @@
 #include "core/gimpimage-undo-push.h"
 #include "core/gimpitemtree.h"
 #include "core/gimpparasitelist.h"
+#include "core/gimppattern.h"
+#include "core/gimptempbuf.h"
 
 #include "gimptext.h"
 #include "gimptextlayer.h"
@@ -799,6 +801,121 @@ gimp_text_layer_render (GimpTextLayer *layer)
   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)
@@ -834,7 +951,60 @@ gimp_text_layer_render_layout (GimpTextLayer  *layer,
     }
 
   cr = cairo_create (surface);
-  gimp_text_layout_render (layout, cr, layer->text->base_dir, FALSE);
+  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;
+      GimpRGB   col = text->outline_foreground;
+
+      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_FILL_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
+        {
+          cairo_set_source_rgba (cr, col.r, col.g, col.b, col.a);
+        }
+
+      cairo_set_line_width (cr, text->outline_width * 2);
+
+      gimp_text_layout_render (layout, cr, text->base_dir, TRUE);
+      cairo_clip_preserve (cr);
+      cairo_stroke (cr);
+
+      cairo_restore (cr);
+    }
+
   cairo_destroy (cr);
 
   cairo_surface_flush (surface);
diff --git a/app/text/text-enums.c b/app/text/text-enums.c
index cc18ec9175..2a1fad01fd 100644
--- a/app/text/text-enums.c
+++ b/app/text/text-enums.c
@@ -50,9 +50,9 @@ gimp_text_outline_get_type (void)
 
   static const GimpEnumDesc descs[] =
   {
-    { GIMP_TEXT_OUTLINE_NONE, "GIMP_TEXT_OUTLINE_NONE", NULL },
-    { GIMP_TEXT_OUTLINE_STROKE_ONLY, "GIMP_TEXT_OUTLINE_STROKE_ONLY", NULL },
-    { GIMP_TEXT_OUTLINE_STROKE_FILL, "GIMP_TEXT_OUTLINE_STROKE_FILL", NULL },
+    { GIMP_TEXT_OUTLINE_NONE, NC_("text-outline", "Filled"), NULL },
+    { GIMP_TEXT_OUTLINE_STROKE_ONLY, NC_("text-outline", "Outlined"), NULL },
+    { GIMP_TEXT_OUTLINE_STROKE_FILL, NC_("text-outline", "Outlined and filled"), NULL },
     { 0, NULL, NULL }
   };
 
diff --git a/app/text/text-enums.h b/app/text/text-enums.h
index 20bd327d63..cf8ef85412 100644
--- a/app/text/text-enums.h
+++ b/app/text/text-enums.h
@@ -36,9 +36,9 @@ GType gimp_text_outline_get_type (void) G_GNUC_CONST;
 
 typedef enum
 {
-  GIMP_TEXT_OUTLINE_NONE,
-  GIMP_TEXT_OUTLINE_STROKE_ONLY,
-  GIMP_TEXT_OUTLINE_STROKE_FILL
+ GIMP_TEXT_OUTLINE_NONE,        /*< desc="Filled"              >*/
+ GIMP_TEXT_OUTLINE_STROKE_ONLY, /*< desc="Outlined"            >*/
+ GIMP_TEXT_OUTLINE_STROKE_FILL  /*< desc="Outlined and filled" >*/
 } GimpTextOutline;
 
 
diff --git a/app/tools/gimptextoptions.c b/app/tools/gimptextoptions.c
index 48a75ee187..4ffa2fd5b8 100644
--- a/app/tools/gimptextoptions.c
+++ b/app/tools/gimptextoptions.c
@@ -21,6 +21,7 @@
 #include <gtk/gtk.h>
 
 #include "libgimpbase/gimpbase.h"
+#include "libgimpcolor/gimpcolor.h"
 #include "libgimpconfig/gimpconfig.h"
 #include "libgimpwidgets/gimpwidgets.h"
 
@@ -29,6 +30,11 @@
 #include "config/gimpconfig-utils.h"
 
 #include "core/gimp.h"
+#include "core/gimpcontainer.h"
+#include "core/gimpdashpattern.h"
+#include "core/gimpdatafactory.h"
+#include "core/gimppattern.h"
+#include "core/gimpstrokeoptions.h"
 #include "core/gimpdatafactory.h"
 #include "core/gimptoolinfo.h"
 #include "core/gimpviewable.h"
@@ -38,6 +44,7 @@
 #include "widgets/gimpcolorpanel.h"
 #include "widgets/gimpmenufactory.h"
 #include "widgets/gimppropwidgets.h"
+#include "widgets/gimpstrokeeditor.h"
 #include "widgets/gimptextbuffer.h"
 #include "widgets/gimptexteditor.h"
 #include "widgets/gimpviewablebox.h"
@@ -64,6 +71,19 @@ enum
   PROP_LETTER_SPACING,
   PROP_BOX_MODE,
 
+  PROP_OUTLINE,
+  PROP_OUTLINE_STYLE,       /* fill-options */
+  PROP_OUTLINE_FOREGROUND,  /* context */
+  PROP_OUTLINE_PATTERN,     /* context */
+  PROP_OUTLINE_WIDTH,       /* stroke-options */
+  PROP_OUTLINE_UNIT,
+  PROP_OUTLINE_CAP_STYLE,
+  PROP_OUTLINE_JOIN_STYLE,
+  PROP_OUTLINE_MITER_LIMIT,
+  PROP_OUTLINE_ANTIALIAS,   /* fill-options */
+  PROP_OUTLINE_DASH_UNIT, /* NOT USED */
+  PROP_OUTLINE_DASH_OFFSET,
+  PROP_OUTLINE_DASH_INFO,
   PROP_USE_EDITOR,
 
   PROP_FONT_VIEW_TYPE,
@@ -71,7 +91,18 @@ enum
 };
 
 
-static void  gimp_text_options_config_iface_init (GimpConfigInterface *config_iface);
+static void       gimp_text_options_config_iface_init    (GimpConfigInterface *config_iface);
+static gboolean   gimp_text_options_serialize_property   (GimpConfig       *config,
+                                                          guint             property_id,
+                                                          const GValue     *value,
+                                                          GParamSpec       *pspec,
+                                                          GimpConfigWriter *writer);
+static gboolean   gimp_text_options_deserialize_property (GimpConfig       *config,
+                                                          guint             property_id,
+                                                          GValue           *value,
+                                                          GParamSpec       *pspec,
+                                                          GScanner         *scanner,
+                                                          GTokenType       *expected);
 
 static void  gimp_text_options_finalize           (GObject         *object);
 static void  gimp_text_options_set_property       (GObject         *object,
@@ -113,7 +144,10 @@ static void
 gimp_text_options_class_init (GimpTextOptionsClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GimpRGB               gray;
+  GParamSpec   *array_spec;
 
+  gimp_rgba_set (&gray, 0.75, 0.75, 0.75, GIMP_OPACITY_OPAQUE);
   object_class->finalize     = gimp_text_options_finalize;
   object_class->set_property = gimp_text_options_set_property;
   object_class->get_property = gimp_text_options_get_property;
@@ -224,6 +258,71 @@ gimp_text_options_class_init (GimpTextOptionsClass *klass)
                         GIMP_VIEWABLE_MAX_BUTTON_SIZE,
                         GIMP_VIEW_SIZE_SMALL,
                         GIMP_PARAM_STATIC_STRINGS);
+
+   GIMP_CONFIG_INSTALL_PROP_ENUM (object_class, PROP_OUTLINE,
+                                  "outline", NULL,
+                                  GIMP_TYPE_TEXT_OUTLINE,
+                                  GIMP_TEXT_OUTLINE_NONE,
+                                  GIMP_PARAM_STATIC_STRINGS |
+                                  GIMP_CONFIG_PARAM_DEFAULTS);
+   GIMP_CONFIG_INSTALL_PROP_ENUM (object_class, PROP_OUTLINE_STYLE,
+                                  "outline-style", NULL,
+                                  GIMP_TYPE_FILL_STYLE,
+                                  GIMP_FILL_STYLE_SOLID,
+                                  GIMP_PARAM_STATIC_STRINGS);
+   GIMP_CONFIG_INSTALL_PROP_RGB (object_class, PROP_OUTLINE_FOREGROUND,
+                                 "outline-foreground", NULL,
+                                 FALSE, &gray,
+                                 GIMP_PARAM_STATIC_STRINGS);
+   GIMP_CONFIG_INSTALL_PROP_OBJECT (object_class, PROP_OUTLINE_PATTERN,
+                                    "outline-pattern", NULL,
+                                    GIMP_TYPE_PATTERN,
+                                    GIMP_PARAM_STATIC_STRINGS);
+   GIMP_CONFIG_INSTALL_PROP_DOUBLE (object_class, PROP_OUTLINE_WIDTH,
+                                    "outline-width",
+                                    _("Adjust outline width"),
+                                    0, 8192.0, 2.0,
+                                    GIMP_PARAM_STATIC_STRINGS |
+                                    GIMP_CONFIG_PARAM_DEFAULTS);
+   GIMP_CONFIG_INSTALL_PROP_UNIT (object_class, PROP_OUTLINE_UNIT,
+                                  "outline-unit", NULL,
+                                  TRUE, FALSE, GIMP_UNIT_PIXEL,
+                                  GIMP_PARAM_STATIC_STRINGS);
+   GIMP_CONFIG_INSTALL_PROP_ENUM (object_class, PROP_OUTLINE_CAP_STYLE,
+                                  "outline-cap-style", NULL,
+                                  GIMP_TYPE_CAP_STYLE, GIMP_CAP_BUTT,
+                                  GIMP_PARAM_STATIC_STRINGS);
+   GIMP_CONFIG_INSTALL_PROP_ENUM (object_class, PROP_OUTLINE_JOIN_STYLE,
+                                  "outline-join-style", NULL,
+                                  GIMP_TYPE_JOIN_STYLE, GIMP_JOIN_MITER,
+                                  GIMP_PARAM_STATIC_STRINGS);
+   GIMP_CONFIG_INSTALL_PROP_DOUBLE (object_class, PROP_OUTLINE_MITER_LIMIT,
+                                    "outline-miter-limit",
+                                    _("Convert a mitered join to a bevelled "
+                                      "join if the miter would extend to a "
+                                      "distance of more than miter-limit * "
+                                      "line-width from the actual join point."),
+                                    0.0, 100.0, 10.0,
+                                    GIMP_PARAM_STATIC_STRINGS);
+   GIMP_CONFIG_INSTALL_PROP_BOOLEAN (object_class, PROP_OUTLINE_ANTIALIAS,
+                                     "outline-antialias", NULL,
+                                     TRUE,
+                                     GIMP_PARAM_STATIC_STRINGS);
+   GIMP_CONFIG_INSTALL_PROP_DOUBLE (object_class, PROP_OUTLINE_DASH_OFFSET,
+                                    "outline-dash-offset", NULL,
+                                    0.0, 2000.0, 0.0,
+                                    GIMP_PARAM_STATIC_STRINGS);
+ 
+   array_spec = g_param_spec_double ("outline-dash-length", NULL, NULL,
+                                     0.0, 2000.0, 1.0, GIMP_PARAM_READWRITE);
+   g_object_class_install_property (object_class, PROP_OUTLINE_DASH_INFO,
+                                    gimp_param_spec_value_array ("outline-dash-info",
+                                                                 NULL, NULL,
+                                                                 array_spec,
+                                                                 GIMP_PARAM_READWRITE |
+                                                                 G_PARAM_CONSTRUCT));
+ 
+
 }
 
 static void
@@ -240,6 +339,13 @@ gimp_text_options_init (GimpTextOptions *options)
   options->size_entry = NULL;
 }
 
+static void
+gimp_text_options_config_iface_init (GimpConfigInterface *iface)
+{
+  iface->serialize_property   = gimp_text_options_serialize_property;
+  iface->deserialize_property = gimp_text_options_deserialize_property;
+}
+
 static void
 gimp_text_options_finalize (GObject *object)
 {
@@ -294,6 +400,51 @@ gimp_text_options_get_property (GObject    *object,
       g_value_set_enum (value, options->box_mode);
       break;
 
+    case PROP_OUTLINE:
+      g_value_set_enum (value, options->outline);
+      break;
+    case PROP_OUTLINE_STYLE:
+      g_value_set_enum (value, options->outline_style);
+      break;
+    case PROP_OUTLINE_FOREGROUND:
+      g_value_set_boxed (value, &options->outline_foreground);
+      break;
+    case PROP_OUTLINE_PATTERN:
+      g_value_set_object (value, options->outline_pattern);
+      break;
+    case PROP_OUTLINE_WIDTH:
+      g_value_set_double (value, options->outline_width);
+      break;
+    case PROP_OUTLINE_UNIT:
+      g_value_set_int (value, options->outline_unit);
+      break;
+    case PROP_OUTLINE_CAP_STYLE:
+      g_value_set_enum (value, options->outline_cap_style);
+      break;
+    case PROP_OUTLINE_JOIN_STYLE:
+      g_value_set_enum (value, options->outline_join_style);
+      break;
+    case PROP_OUTLINE_MITER_LIMIT:
+      g_value_set_double (value, options->outline_miter_limit);
+      break;
+    case PROP_OUTLINE_ANTIALIAS:
+      g_value_set_boolean (value, options->outline_antialias);
+      break;
+    case PROP_OUTLINE_DASH_UNIT:
+      /* NOT USED */
+      break;
+    case PROP_OUTLINE_DASH_OFFSET:
+      g_value_set_double (value, options->outline_dash_offset);
+      break;
+    case PROP_OUTLINE_DASH_INFO:
+      {
+        GimpValueArray *value_array;
+
+        value_array = gimp_dash_pattern_to_value_array (options->outline_dash_info);
+        g_value_take_boxed (value, value_array);
+      }
+      break;
+
     case PROP_USE_EDITOR:
       g_value_set_boolean (value, options->use_editor);
       break;
@@ -318,6 +469,7 @@ gimp_text_options_set_property (GObject      *object,
                                 GParamSpec   *pspec)
 {
   GimpTextOptions *options = GIMP_TEXT_OPTIONS (object);
+  GimpRGB         *color;
 
   switch (property_id)
     {
@@ -356,6 +508,60 @@ gimp_text_options_set_property (GObject      *object,
       options->box_mode = g_value_get_enum (value);
       break;
 
+    case PROP_OUTLINE:
+      options->outline = g_value_get_enum (value);
+      break;
+    case PROP_OUTLINE_STYLE:
+      options->outline_style = g_value_get_enum (value);
+      break;
+    case PROP_OUTLINE_FOREGROUND:
+      color = g_value_get_boxed (value);
+      options->outline_foreground = *color;
+      break;
+    case PROP_OUTLINE_PATTERN:
+      {
+        GimpPattern *pattern = g_value_get_object (value);
+
+        if (options->outline_pattern != pattern)
+          {
+            if (options->outline_pattern)
+              g_object_unref (options->outline_pattern);
+
+            options->outline_pattern = pattern ? g_object_ref (pattern) : pattern;
+          }
+        break;
+      }
+    case PROP_OUTLINE_WIDTH:
+      options->outline_width =  g_value_get_double (value);
+      break;
+    case PROP_OUTLINE_UNIT:
+      options->outline_unit = g_value_get_int (value);
+      break;
+    case PROP_OUTLINE_CAP_STYLE:
+      options->outline_cap_style = g_value_get_enum (value);
+      break;
+    case PROP_OUTLINE_JOIN_STYLE:
+      options->outline_join_style = g_value_get_enum (value);
+      break;
+    case PROP_OUTLINE_MITER_LIMIT:
+      options->outline_miter_limit = g_value_get_double (value);
+      break;
+    case PROP_OUTLINE_ANTIALIAS:
+      options->outline_antialias = g_value_get_boolean (value);
+      break;
+    case PROP_OUTLINE_DASH_UNIT:
+      /* unused */
+      break;
+    case PROP_OUTLINE_DASH_OFFSET:
+      options->outline_dash_offset = g_value_get_double (value);
+      break;
+    case PROP_OUTLINE_DASH_INFO:
+      {
+        GimpValueArray *value_array = g_value_get_boxed (value);
+        options->outline_dash_info = gimp_dash_pattern_from_value_array (value_array);
+      }
+      break;
+
     case PROP_USE_EDITOR:
       options->use_editor = g_value_get_boolean (value);
       break;
@@ -399,6 +605,21 @@ gimp_text_options_reset (GimpConfig *config)
   gimp_config_reset_property (object, "line-spacing");
   gimp_config_reset_property (object, "letter-spacing");
   gimp_config_reset_property (object, "box-mode");
+
+  gimp_config_reset_property (object, "outline");
+  gimp_config_reset_property (object, "outline-style");
+  gimp_config_reset_property (object, "outline-foreground");
+  gimp_config_reset_property (object, "outline-pattern");
+  gimp_config_reset_property (object, "outline-width");
+  gimp_config_reset_property (object, "outline-unit");
+  gimp_config_reset_property (object, "outline-cap-style");
+  gimp_config_reset_property (object, "outline-join-style");
+  gimp_config_reset_property (object, "outline-miter-limit");
+  gimp_config_reset_property (object, "outline-antialias");
+  //gimp_config_reset_property (object, "outline-dash-unit");
+  gimp_config_reset_property (object, "outline-dash-offset");
+  gimp_config_reset_property (object, "outline-dash-info");
+
   gimp_config_reset_property (object, "use-editor");
 }
 
@@ -523,6 +744,8 @@ gimp_text_options_gui (GimpToolOptions *tool_options)
   GtkWidget       *box;
   GtkWidget       *spinbutton;
   GtkWidget       *combo;
+  GtkWidget       *editor;
+  GimpStrokeOptions *stroke_options;
   GtkSizeGroup    *size_group;
   gint             row = 0;
 
@@ -587,6 +810,12 @@ gimp_text_options_gui (GimpToolOptions *tool_options)
 
   size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
 
+  button = gimp_prop_enum_combo_box_new (config, "outline", -1, -1);
+  gimp_table_attach_aligned (GTK_TABLE (table), 0, row++,
+                             _("Outline:"), 0.0, 0.5,
+                             button, 1, TRUE);
+  gtk_size_group_add_widget (size_group, button);
+
   button = gimp_prop_enum_combo_box_new (config, "hint-style", -1, -1);
   gtk_widget_set_halign (button, GTK_ALIGN_START);
   gimp_grid_attach_aligned (GTK_GRID (grid), 0, row++,
@@ -641,6 +870,37 @@ gimp_text_options_gui (GimpToolOptions *tool_options)
                             _("Box:"), 0.0, 0.5,
                             combo, 1);
 
+  stroke_options = gimp_stroke_options_new (GIMP_CONTEXT (options)->gimp,
+                                            NULL,
+                                            FALSE);
+#define BIND(a)                                    \
+  g_object_bind_property (options, "outline-" #a,  \
+                          stroke_options, #a,                           \
+                          G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE)
+  BIND (style);
+  BIND (foreground);
+  BIND (pattern);
+  BIND (width);
+  BIND (unit);
+  BIND (cap-style);
+  BIND (join-style);
+  BIND (miter-limit);
+  BIND (antialias);
+  //  BIND (dash-unit);
+  BIND (dash-offset);
+  BIND (dash-info);
+  /*
+    {
+    GimpPattern *pattern = gimp_context_get_pattern (gimp_get_user_context (GIMP_CONTEXT (options)->gimp));
+    gimp_context_set_pattern (GIMP_CONTEXT (stroke_options), pattern);
+    }
+  */
+  editor = gimp_stroke_editor_new (stroke_options, 72.0, TRUE);
+  gtk_box_pack_start (GTK_BOX (main_vbox), editor, FALSE, FALSE, 0);
+  gtk_widget_show (editor);
+
+  g_object_unref (stroke_options);
+  
   /*  Only add the language entry if the iso-codes package is available.  */
 
 #ifdef HAVE_ISO_CODES
@@ -745,3 +1005,74 @@ gimp_text_options_editor_new (GtkWindow       *parent,
 
   return editor;
 }
+
+static gboolean
+gimp_text_options_serialize_property (GimpConfig       *config,
+                                      guint             property_id,
+                                      const GValue     *value,
+                                      GParamSpec       *pspec,
+                                      GimpConfigWriter *writer)
+{
+  if (property_id == PROP_OUTLINE_PATTERN)
+    {
+      GimpObject *serialize_obj = g_value_get_object (value);
+
+      gimp_config_writer_open (writer, pspec->name);
+
+      if (serialize_obj)
+        gimp_config_writer_string (writer, gimp_object_get_name (serialize_obj));
+      else
+        gimp_config_writer_print (writer, "NULL", 4);
+
+      gimp_config_writer_close (writer);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+gimp_text_options_deserialize_property (GimpConfig *object,
+                                        guint       property_id,
+                                        GValue     *value,
+                                        GParamSpec *pspec,
+                                        GScanner   *scanner,
+                                        GTokenType *expected)
+{
+  if (property_id == PROP_OUTLINE_PATTERN)
+    {
+      gchar *object_name;
+
+      if (gimp_scanner_parse_identifier (scanner, "NULL"))
+        {
+          g_value_set_object (value, NULL);
+        }
+      else if (gimp_scanner_parse_string (scanner, &object_name))
+        {
+          GimpContext   *context = GIMP_CONTEXT (object);
+          GimpContainer *container;
+          GimpObject    *deserialize_obj;
+
+          if (! object_name)
+            object_name = g_strdup ("");
+
+          container = gimp_data_factory_get_container (context->gimp->pattern_factory);
+
+          deserialize_obj = gimp_container_get_child_by_name (container,
+                                                              object_name);
+
+          g_value_set_object (value, deserialize_obj);
+
+          g_free (object_name);
+        }
+      else
+        {
+          *expected = G_TOKEN_STRING;
+        }
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
diff --git a/app/tools/gimptextoptions.h b/app/tools/gimptextoptions.h
index d80c21f5f8..b45d8625d8 100644
--- a/app/tools/gimptextoptions.h
+++ b/app/tools/gimptextoptions.h
@@ -49,6 +49,19 @@ struct _GimpTextOptions
   gdouble                letter_spacing;
   GimpTextBoxMode        box_mode;
 
+  GimpTextOutline        outline;
+  GimpFillStyle          outline_style;
+  GimpRGB                outline_foreground;
+  GimpPattern           *outline_pattern;
+  gdouble                outline_width;
+  GimpUnit               outline_unit;
+  GimpCapStyle           outline_cap_style;
+  GimpJoinStyle          outline_join_style;
+  gdouble                outline_miter_limit;
+  gboolean               outline_antialias;
+  gdouble                outline_dash_offset;
+  GArray                *outline_dash_info;
+
   GimpViewType           font_view_type;
   GimpViewSize           font_view_size;
 
diff --git a/app/widgets/gimpfilleditor.c b/app/widgets/gimpfilleditor.c
index 50d110e150..0f16ccf4d4 100644
--- a/app/widgets/gimpfilleditor.c
+++ b/app/widgets/gimpfilleditor.c
@@ -121,7 +121,7 @@ gimp_fill_editor_constructed (GObject *object)
       color_button = gimp_prop_color_button_new (G_OBJECT (editor->options),
                                                  "foreground",
                                                  _("Fill Color"),
-                                                 -1, 24,
+                                                 1, 24,
                                                  GIMP_COLOR_AREA_SMALL_CHECKS);
       gimp_color_panel_set_context (GIMP_COLOR_PANEL (color_button),
                                     GIMP_CONTEXT (editor->options));



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