[gimp/alxsa-outline-text-massimo: 17/17] text: Add outline options to text editor




commit af5f9a2f22b7aa6f26f36d2d82e6a5ae8f6b4942
Author: Alx Sa <cmyk student gmail com>
Date:   Mon Oct 3 19:45:52 2022 +0000

    text: Add outline options to text editor
    
    This ports Massimo’s code to work in the latest version of GIMP.
    It adds new outline-related properties to GimpText and GimpTextOptions.
    These are controlled via the Text Tool Editor.
    Cairo is currently used to draw the outline around the text.

 app/core/gimp-memsize.c      |  21 ++-
 app/text/gimptext-parasite.c |   3 +-
 app/text/gimptext-parasite.h |   1 +
 app/text/gimptext.c          | 265 +++++++++++++++++++++++++-
 app/text/gimptext.h          |  11 ++
 app/text/gimptextlayer-xcf.c |   4 +-
 app/text/gimptextlayer.c     | 172 ++++++++++++++++-
 app/text/text-enums.c        |   6 +-
 app/text/text-enums.h        |   6 +-
 app/tools/gimptextoptions.c  | 441 +++++++++++++++++++++++++++++++++++++++----
 app/tools/gimptextoptions.h  |  13 ++
 app/tools/gimptexttool.c     |   3 +-
 app/widgets/gimpfilleditor.c |   2 +-
 13 files changed, 893 insertions(+), 55 deletions(-)
---
diff --git a/app/core/gimp-memsize.c b/app/core/gimp-memsize.c
index fc674d0842..c14436b490 100644
--- a/app/core/gimp-memsize.c
+++ b/app/core/gimp-memsize.c
@@ -243,6 +243,20 @@ gimp_g_value_get_memsize (GValue *value)
                 memsize += gimp_string_get_memsize (array[i]);
             }
         }
+      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",
@@ -251,8 +265,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 bc0f7d0363..b6a113013c 100644
--- a/app/text/gimptext-parasite.c
+++ b/app/text/gimptext-parasite.c
@@ -65,6 +65,7 @@ gimp_text_to_parasite (GimpText *text)
 
 GimpText *
 gimp_text_from_parasite (const GimpParasite  *parasite,
+                         Gimp                *gimp,
                          GError             **error)
 {
   GimpText *text;
@@ -76,7 +77,7 @@ gimp_text_from_parasite (const GimpParasite  *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);
 
   parasite_data = (gchar *) gimp_parasite_get_data (parasite, &parasite_data_size);
   if (parasite_data)
diff --git a/app/text/gimptext-parasite.h b/app/text/gimptext-parasite.h
index 2e2847a497..eae1087b0d 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            (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 ed2f041294..b3ce6acc71 100644
--- a/app/text/gimptext.c
+++ b/app/text/gimptext.c
@@ -34,9 +34,14 @@
 
 #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/gimpstrokeoptions.h"
+#include "core/gimppattern.h"
 
 #include "gimptext.h"
 
@@ -68,8 +73,22 @@ 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_OFFSET,
+  PROP_OUTLINE_DASH_INFO,
   /* for backward compatibility */
-  PROP_HINTING
+  PROP_HINTING,
+
+  PROP_GIMP
 };
 
 enum
@@ -78,6 +97,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,
@@ -96,7 +127,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
 
@@ -109,8 +141,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",
@@ -128,6 +162,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,
@@ -298,12 +333,68 @@ gimp_text_class_init (GimpTextClass *klass)
                                                      G_PARAM_CONSTRUCT |
                                                      GIMP_PARAM_WRITABLE));
 
+   GIMP_CONFIG_PROP_ENUM (object_class, PROP_OUTLINE_STYLE,
+                          "outline-style", NULL, NULL,
+                          GIMP_TYPE_FILL_STYLE,
+                          GIMP_FILL_STYLE_SOLID,
+                          GIMP_PARAM_STATIC_STRINGS);
+   GIMP_CONFIG_PROP_OBJECT (object_class, PROP_OUTLINE_PATTERN,
+                            "outline-pattern", NULL, NULL,
+                            GIMP_TYPE_PATTERN,
+                            GIMP_PARAM_STATIC_STRINGS);
+   GIMP_CONFIG_PROP_RGB (object_class, PROP_OUTLINE_FOREGROUND,
+                         "outline-foreground", NULL, NULL,
+                         FALSE, &gray,
+                         GIMP_PARAM_STATIC_STRINGS);
+   GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_OUTLINE_WIDTH,
+                            "outline-width", NULL, NULL,
+                            0.0, 8192.0, 4.0,
+                            GIMP_PARAM_STATIC_STRINGS |
+                            GIMP_CONFIG_PARAM_DEFAULTS);
+   GIMP_CONFIG_PROP_ENUM (object_class, PROP_OUTLINE_CAP_STYLE,
+                          "outline-cap-style", NULL, NULL,
+                          GIMP_TYPE_CAP_STYLE, GIMP_CAP_BUTT,
+                          GIMP_PARAM_STATIC_STRINGS);
+   GIMP_CONFIG_PROP_ENUM (object_class, PROP_OUTLINE_JOIN_STYLE,
+                          "outline-join-style", NULL, NULL,
+                          GIMP_TYPE_JOIN_STYLE, GIMP_JOIN_MITER,
+                          GIMP_PARAM_STATIC_STRINGS);
+   GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_OUTLINE_MITER_LIMIT,
+                            "outline-miter-limit",
+                            NULL, NULL,
+                            0.0, 100.0, 10.0,
+                            GIMP_PARAM_STATIC_STRINGS);
+   GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_OUTLINE_ANTIALIAS,
+                             "outline-antialias", NULL, NULL,
+                             TRUE,
+                             GIMP_PARAM_STATIC_STRINGS);
+   GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_OUTLINE_DASH_OFFSET,
+                            "outline-dash-offset", NULL, 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
@@ -311,6 +402,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)
 {
@@ -403,10 +501,48 @@ 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_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;
@@ -527,6 +663,50 @@ gimp_text_set_property (GObject      *object,
     case PROP_OFFSET_Y:
       text->offset_y = g_value_get_double (value);
       break;
+    case PROP_OUTLINE_STYLE:
+      text->outline_style = g_value_get_enum (value);
+      break;
+    case PROP_OUTLINE_FOREGROUND:
+      color                    = g_value_get_boxed (value);
+      text->outline_foreground = *color;
+      break;
+    case PROP_OUTLINE_PATTERN:
+      {
+        GimpPattern *pattern = g_value_get_object (value);
+
+        if (text->outline_pattern != pattern)
+          {
+            if (text->outline_pattern)
+              g_object_unref (text->outline_pattern);
+
+            text->outline_pattern = pattern ? g_object_ref (pattern) : pattern;
+          }
+        break;
+      }
+    case PROP_OUTLINE_WIDTH:
+      text->outline_width = g_value_get_double (value);
+      break;
+    case PROP_OUTLINE_CAP_STYLE:
+      text->outline_cap_style = g_value_get_enum (value);
+      break;
+    case PROP_OUTLINE_JOIN_STYLE:
+      text->outline_join_style = g_value_get_enum (value);
+      break;
+    case PROP_OUTLINE_MITER_LIMIT:
+      text->outline_miter_limit = g_value_get_double (value);
+      break;
+    case PROP_OUTLINE_ANTIALIAS:
+      text->outline_antialias = g_value_get_boolean (value);
+      break;
+    case PROP_OUTLINE_DASH_OFFSET:
+      text->outline_dash_offset = g_value_get_double (value);
+      break;
+    case PROP_OUTLINE_DASH_INFO:
+      {
+        GimpValueArray *value_array = g_value_get_boxed (value);
+        text->outline_dash_info = gimp_dash_pattern_from_value_array (value_array);
+      }
+      break;
     case PROP_BORDER:
       text->border = g_value_get_int (value);
       break;
@@ -540,6 +720,8 @@ gimp_text_set_property (GObject      *object,
                             GIMP_TEXT_HINT_STYLE_MEDIUM :
                             GIMP_TEXT_HINT_STYLE_NONE);
       break;
+    case PROP_GIMP:
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       break;
@@ -592,3 +774,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..8a93d0f8e8 100644
--- a/app/text/gimptextlayer-xcf.c
+++ b/app/text/gimptextlayer-xcf.c
@@ -69,7 +69,9 @@ 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..327d76853c 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 ab9623838b..271b2065dd 100644
--- a/app/text/text-enums.c
+++ b/app/text/text-enums.c
@@ -51,9 +51,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..f032ce690b 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..f7cecfed11 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,18 @@ 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_OFFSET,
+  PROP_OUTLINE_DASH_INFO,
   PROP_USE_EDITOR,
 
   PROP_FONT_VIEW_TYPE,
@@ -71,32 +90,49 @@ enum
 };
 
 
-static void  gimp_text_options_config_iface_init (GimpConfigInterface *config_iface);
-
-static void  gimp_text_options_finalize           (GObject         *object);
-static void  gimp_text_options_set_property       (GObject         *object,
-                                                   guint            property_id,
-                                                   const GValue    *value,
-                                                   GParamSpec      *pspec);
-static void  gimp_text_options_get_property       (GObject         *object,
-                                                   guint            property_id,
-                                                   GValue          *value,
-                                                   GParamSpec      *pspec);
-
-static void  gimp_text_options_reset              (GimpConfig      *config);
+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,
+                                                     guint                property_id,
+                                                     const GValue        *value,
+                                                     GParamSpec          *pspec);
+static void  gimp_text_options_get_property         (GObject             *object,
+                                                     guint                property_id,
+                                                     GValue              *value,
+                                                     GParamSpec          *pspec);
+
+static void  gimp_text_options_reset                (GimpConfig          *config);
+
+static void  gimp_text_options_notify_font          (GimpContext         *context,
+                                                     GParamSpec          *pspec,
+                                                     GimpText            *text);
+static void  gimp_text_options_notify_text_font     (GimpText            *text,
+                                                     GParamSpec          *pspec,
+                                                     GimpContext         *context);
+static void  gimp_text_options_notify_color         (GimpContext         *context,
+                                                     GParamSpec          *pspec,
+                                                     GimpText            *text);
+static void  gimp_text_options_notify_text_color    (GimpText            *text,
+                                                     GParamSpec          *pspec,
+                                                     GimpContext         *context);
+static void  gimp_text_options_outline_changed      (GtkWidget           *combo,
+                                                     GtkWidget           *vbox);
 
-static void  gimp_text_options_notify_font        (GimpContext     *context,
-                                                   GParamSpec      *pspec,
-                                                   GimpText        *text);
-static void  gimp_text_options_notify_text_font   (GimpText        *text,
-                                                   GParamSpec      *pspec,
-                                                   GimpContext     *context);
-static void  gimp_text_options_notify_color       (GimpContext     *context,
-                                                   GParamSpec      *pspec,
-                                                   GimpText        *text);
-static void  gimp_text_options_notify_text_color  (GimpText        *text,
-                                                   GParamSpec      *pspec,
-                                                   GimpContext     *context);
 
 
 G_DEFINE_TYPE_WITH_CODE (GimpTextOptions, gimp_text_options,
@@ -113,7 +149,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 +263,82 @@ gimp_text_options_class_init (GimpTextOptionsClass *klass)
                         GIMP_VIEWABLE_MAX_BUTTON_SIZE,
                         GIMP_VIEW_SIZE_SMALL,
                         GIMP_PARAM_STATIC_STRINGS);
+  GIMP_CONFIG_PROP_ENUM (object_class, PROP_OUTLINE,
+                         "outline",
+                         NULL, NULL,
+                         GIMP_TYPE_TEXT_OUTLINE,
+                         GIMP_TEXT_OUTLINE_NONE,
+                         GIMP_PARAM_STATIC_STRINGS |
+                         GIMP_CONFIG_PARAM_DEFAULTS);
+   GIMP_CONFIG_PROP_ENUM (object_class, PROP_OUTLINE_STYLE,
+                          "outline-style",
+                          NULL, NULL,
+                          GIMP_TYPE_FILL_STYLE,
+                          GIMP_FILL_STYLE_SOLID,
+                          GIMP_PARAM_STATIC_STRINGS);
+   GIMP_CONFIG_PROP_RGB (object_class, PROP_OUTLINE_FOREGROUND,
+                         "outline-foreground",
+                         NULL, NULL,
+                         FALSE, &gray,
+                         GIMP_PARAM_STATIC_STRINGS);
+   GIMP_CONFIG_PROP_OBJECT (object_class, PROP_OUTLINE_PATTERN,
+                            "outline-pattern",
+                            NULL, NULL,
+                            GIMP_TYPE_PATTERN,
+                            GIMP_PARAM_STATIC_STRINGS);
+   GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_OUTLINE_WIDTH,
+                            "outline-width",
+                            _("Outline width"),
+                            _("Adjust outline width"),
+                            0, 8192.0, 2.0,
+                            GIMP_PARAM_STATIC_STRINGS |
+                            GIMP_CONFIG_PARAM_DEFAULTS);
+   GIMP_CONFIG_PROP_UNIT (object_class, PROP_OUTLINE_UNIT,
+                          "outline-unit",
+                          _("Unit"),
+                          _("Outline width unit"),
+                          TRUE, FALSE, GIMP_UNIT_PIXEL,
+                          GIMP_PARAM_STATIC_STRINGS);
+   GIMP_CONFIG_PROP_ENUM (object_class, PROP_OUTLINE_CAP_STYLE,
+                          "outline-cap-style",
+                          NULL, NULL,
+                          GIMP_TYPE_CAP_STYLE, GIMP_CAP_BUTT,
+                          GIMP_PARAM_STATIC_STRINGS);
+   GIMP_CONFIG_PROP_ENUM (object_class, PROP_OUTLINE_JOIN_STYLE,
+                          "outline-join-style",
+                          NULL, NULL,
+                          GIMP_TYPE_JOIN_STYLE, GIMP_JOIN_MITER,
+                          GIMP_PARAM_STATIC_STRINGS);
+   GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_OUTLINE_MITER_LIMIT,
+                            "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_PROP_BOOLEAN (object_class, PROP_OUTLINE_ANTIALIAS,
+                             "outline-antialias",
+                             NULL, NULL,
+                             TRUE,
+                             GIMP_PARAM_STATIC_STRINGS);
+   GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_OUTLINE_DASH_OFFSET,
+                            "outline-dash-offset",
+                            NULL, 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));
 }
 
 static void
@@ -232,6 +347,9 @@ gimp_text_options_config_iface_init (GimpConfigInterface *config_iface)
   parent_config_iface = g_type_interface_peek_parent (config_iface);
 
   config_iface->reset = gimp_text_options_reset;
+
+  config_iface->serialize_property   = gimp_text_options_serialize_property;
+  config_iface->deserialize_property = gimp_text_options_deserialize_property;
 }
 
 static void
@@ -294,6 +412,48 @@ 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_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 +478,7 @@ gimp_text_options_set_property (GObject      *object,
                                 GParamSpec   *pspec)
 {
   GimpTextOptions *options = GIMP_TEXT_OPTIONS (object);
+  GimpRGB         *color;
 
   switch (property_id)
     {
@@ -356,6 +517,57 @@ 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_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 +611,20 @@ 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-offset");
+  gimp_config_reset_property (object, "outline-dash-info");
+
   gimp_config_reset_property (object, "use-editor");
 }
 
@@ -510,21 +736,24 @@ gimp_text_options_connect_text (GimpTextOptions *options,
 GtkWidget *
 gimp_text_options_gui (GimpToolOptions *tool_options)
 {
-  GObject         *config    = G_OBJECT (tool_options);
-  GimpTextOptions *options   = GIMP_TEXT_OPTIONS (tool_options);
-  GtkWidget       *main_vbox = gimp_tool_options_gui (tool_options);
-  GimpAsyncSet    *async_set;
-  GtkWidget       *options_vbox;
-  GtkWidget       *grid;
-  GtkWidget       *vbox;
-  GtkWidget       *hbox;
-  GtkWidget       *button;
-  GtkWidget       *entry;
-  GtkWidget       *box;
-  GtkWidget       *spinbutton;
-  GtkWidget       *combo;
-  GtkSizeGroup    *size_group;
-  gint             row = 0;
+  GObject           *config    = G_OBJECT (tool_options);
+  GimpTextOptions   *options   = GIMP_TEXT_OPTIONS (tool_options);
+  GtkWidget         *main_vbox = gimp_tool_options_gui (tool_options);
+  GimpAsyncSet      *async_set;
+  GtkWidget         *options_vbox;
+  GtkWidget         *grid;
+  GtkWidget         *vbox;
+  GtkWidget         *hbox;
+  GtkWidget         *button;
+  GtkWidget         *entry;
+  GtkWidget         *box;
+  GtkWidget         *spinbutton;
+  GtkWidget         *combo;
+  GtkWidget         *editor;
+  GimpStrokeOptions *stroke_options;
+  GtkWidget         *outline_vbox;
+  GtkSizeGroup      *size_group;
+  gint               row = 0;
 
   async_set =
     gimp_data_factory_get_async_set (tool_options->tool_info->gimp->font_factory);
@@ -605,6 +834,30 @@ gimp_text_options_gui (GimpToolOptions *tool_options)
                             button, 1);
   gtk_size_group_add_widget (size_group, button);
 
+  button = gimp_prop_enum_combo_box_new (config, "outline", -1, -1);
+  gimp_grid_attach_aligned (GTK_GRID (grid), 0, row++,
+                             _("Style:"), 0.0, 0.5,
+                             button, 1);
+  gtk_size_group_add_widget (size_group, button);
+
+  outline_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL,
+                              gtk_box_get_spacing (GTK_BOX (options_vbox)));
+  gtk_box_pack_start (GTK_BOX (options_vbox), outline_vbox, FALSE, FALSE, 0);
+  gtk_widget_show (outline_vbox);
+
+  g_signal_connect (button, "changed",
+                    G_CALLBACK (gimp_text_options_outline_changed),
+                    outline_vbox);
+  gimp_text_options_outline_changed (button, outline_vbox);
+
+  grid = gtk_grid_new ();
+  gtk_grid_set_column_spacing (GTK_GRID (grid), 2);
+  gtk_grid_set_row_spacing (GTK_GRID (grid), 2);
+  gtk_box_pack_start (GTK_BOX (options_vbox), grid, FALSE, FALSE, 0);
+  gtk_widget_show (grid);
+
+  row = 0;
+
   box = gimp_prop_enum_icon_box_new (config, "justify", "format-justify", 0, 0);
   gtk_widget_set_halign (box, GTK_ALIGN_START);
   gimp_grid_attach_aligned (GTK_GRID (grid), 0, row++,
@@ -640,6 +893,29 @@ gimp_text_options_gui (GimpToolOptions *tool_options)
   gimp_grid_attach_aligned (GTK_GRID (grid), 0, row++,
                             _("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-offset);
+  BIND (dash-info);
+
+  editor = gimp_stroke_editor_new (stroke_options, 72.0, TRUE);
+  gtk_box_pack_start (GTK_BOX (outline_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.  */
 
@@ -702,6 +978,20 @@ gimp_text_options_editor_notify_font (GimpTextOptions *options,
   gimp_text_editor_set_font_name (editor, font_name);
 }
 
+static void
+gimp_text_options_outline_changed (GtkWidget *combo,
+                                   GtkWidget *vbox)
+{
+  GimpTextOutline active;
+
+  active = (GimpTextOutline) gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
+
+  if (active == GIMP_TEXT_OUTLINE_NONE)
+    gtk_widget_hide (vbox);
+  else
+    gtk_widget_show (vbox);
+}
+
 GtkWidget *
 gimp_text_options_editor_new (GtkWindow       *parent,
                               Gimp            *gimp,
@@ -745,3 +1035,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/tools/gimptexttool.c b/app/tools/gimptexttool.c
index bc8309aa3e..0202d4cfa0 100644
--- a/app/tools/gimptexttool.c
+++ b/app/tools/gimptexttool.c
@@ -2117,7 +2117,8 @@ gimp_text_tool_apply (GimpTextTool *text_tool,
                                              GIMP_DRAWABLE (layer), TRUE);
         }
 
-      gimp_image_undo_push_text_layer (image, NULL, layer, pspec);
+      if (pspec)
+        gimp_image_undo_push_text_layer (image, NULL, layer, pspec);
     }
 
   gimp_text_tool_apply_list (text_tool, list);
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]