[gimp] app: implement optional dithering when converting to lower bit depth



commit 5dbcdef4777b91b1459fb92b9335110c611db300
Author: Michael Natterer <mitch gimp org>
Date:   Sat Oct 13 21:46:56 2012 +0200

    app: implement optional dithering when converting to lower bit depth
    
    Add "layer_dither_type" and "mask_dither_type" to
    GimpDrawable::convert_type(), pass around the dither type from the
    dialog, and implement dithering using gegl:color-reduction.

 app/core/gimpchannel.c                 |   63 ++++++++++++++++++++++++++++++++
 app/core/gimpdrawable.c                |   12 ++++++
 app/core/gimpdrawable.h                |    4 ++
 app/core/gimpgrouplayer.c              |    8 ++++-
 app/core/gimpgrouplayerundo.c          |    1 +
 app/core/gimpimage-convert-precision.c |    5 ++-
 app/core/gimpimage-convert-type.c      |    1 +
 app/core/gimplayer.c                   |   59 ++++++++++++++++++++++++++----
 8 files changed, 144 insertions(+), 9 deletions(-)
---
diff --git a/app/core/gimpchannel.c b/app/core/gimpchannel.c
index 9ad873b..85364d1 100644
--- a/app/core/gimpchannel.c
+++ b/app/core/gimpchannel.c
@@ -129,6 +129,13 @@ static void       gimp_channel_to_selection  (GimpItem          *item,
                                               gdouble            feather_radius_x,
                                               gdouble            feather_radius_y);
 
+static void       gimp_channel_convert_type  (GimpDrawable      *drawable,
+                                              GimpImage         *dest_image,
+                                              GimpImageBaseType  new_base_type,
+                                              GimpPrecision      new_precision,
+                                              gint               layer_dither_type,
+                                              gint               mask_dither_type,
+                                              gboolean           push_undo);
 static void gimp_channel_invalidate_boundary   (GimpDrawable       *drawable);
 static void gimp_channel_get_active_components (const GimpDrawable *drawable,
                                                 gboolean           *active);
@@ -281,6 +288,7 @@ gimp_channel_class_init (GimpChannelClass *klass)
   item_class->raise_failed         = _("Channel cannot be raised higher.");
   item_class->lower_failed         = _("Channel cannot be lowered more.");
 
+  drawable_class->convert_type          = gimp_channel_convert_type;
   drawable_class->invalidate_boundary   = gimp_channel_invalidate_boundary;
   drawable_class->get_active_components = gimp_channel_get_active_components;
   drawable_class->get_active_mask       = gimp_channel_get_active_mask;
@@ -448,6 +456,7 @@ gimp_channel_convert (GimpItem  *item,
     {
       gimp_drawable_convert_type (drawable, dest_image, GIMP_GRAY,
                                   gimp_image_get_precision (dest_image),
+                                  0, 0,
                                   FALSE);
     }
 
@@ -787,6 +796,60 @@ gimp_channel_to_selection (GimpItem       *item,
 }
 
 static void
+gimp_channel_convert_type (GimpDrawable      *drawable,
+                           GimpImage         *dest_image,
+                           GimpImageBaseType  new_base_type,
+                           GimpPrecision      new_precision,
+                           gint               layer_dither_type,
+                           gint               mask_dither_type,
+                           gboolean           push_undo)
+{
+  GeglBuffer *dest_buffer;
+  const Babl *format;
+
+  format = gimp_image_get_format (dest_image,
+                                  new_base_type,
+                                  new_precision,
+                                  gimp_drawable_has_alpha (drawable));
+
+  dest_buffer =
+    gegl_buffer_new (GEGL_RECTANGLE (0, 0,
+                                     gimp_item_get_width  (GIMP_ITEM (drawable)),
+                                     gimp_item_get_height (GIMP_ITEM (drawable))),
+                     format);
+
+  if (mask_dither_type == 0)
+    {
+      gegl_buffer_copy (gimp_drawable_get_buffer (drawable), NULL,
+                        dest_buffer, NULL);
+    }
+  else
+    {
+      GeglNode *dither;
+      gint      bits;
+
+      bits = (babl_format_get_bytes_per_pixel (format) * 8 /
+              babl_format_get_n_components (format));
+
+      dither = gegl_node_new_child (NULL,
+                                    "operation",       "gegl:color-reduction",
+                                    "red-bits",        bits,
+                                    "green-bits",      bits,
+                                    "blue-bits",       bits,
+                                    "alpha-bits",      bits,
+                                    "dither-strategy", mask_dither_type,
+                                    NULL);
+
+      gimp_drawable_apply_operation_to_buffer (drawable, NULL, NULL,
+                                               dither, dest_buffer);
+      g_object_unref (dither);
+    }
+
+  gimp_drawable_set_buffer (drawable, push_undo, NULL, dest_buffer);
+  g_object_unref (dest_buffer);
+}
+
+static void
 gimp_channel_invalidate_boundary (GimpDrawable *drawable)
 {
   GIMP_CHANNEL (drawable)->boundary_known = FALSE;
diff --git a/app/core/gimpdrawable.c b/app/core/gimpdrawable.c
index 58cd405..5423fbf 100644
--- a/app/core/gimpdrawable.c
+++ b/app/core/gimpdrawable.c
@@ -132,6 +132,8 @@ static void       gimp_drawable_real_convert_type  (GimpDrawable      *drawable,
                                                     GimpImage         *dest_image,
                                                     GimpImageBaseType  new_base_type,
                                                     GimpPrecision      new_precision,
+                                                    gint               layer_dither_type,
+                                                    gint               mask_dither_type,
                                                     gboolean           push_undo);
 
 static GeglBuffer * gimp_drawable_real_get_buffer  (GimpDrawable      *drawable);
@@ -670,11 +672,17 @@ gimp_drawable_real_estimate_memsize (const GimpDrawable *drawable,
   return (gint64) babl_format_get_bytes_per_pixel (format) * width * height;
 }
 
+/* FIXME: this default impl is currently unused because no subclass
+ * chins up. the goal is to handle the almost identical subclass code
+ * here again.
+ */
 static void
 gimp_drawable_real_convert_type (GimpDrawable      *drawable,
                                  GimpImage         *dest_image,
                                  GimpImageBaseType  new_base_type,
                                  GimpPrecision      new_precision,
+                                 gint               layer_dither_type,
+                                 gint               mask_dither_type,
                                  gboolean           push_undo)
 {
   GeglBuffer *dest_buffer;
@@ -1137,6 +1145,8 @@ gimp_drawable_convert_type (GimpDrawable      *drawable,
                             GimpImage         *dest_image,
                             GimpImageBaseType  new_base_type,
                             GimpPrecision      new_precision,
+                            gint               layer_dither_type,
+                            gint               mask_dither_type,
                             gboolean           push_undo)
 {
   g_return_if_fail (GIMP_IS_DRAWABLE (drawable));
@@ -1150,6 +1160,8 @@ gimp_drawable_convert_type (GimpDrawable      *drawable,
   GIMP_DRAWABLE_GET_CLASS (drawable)->convert_type (drawable, dest_image,
                                                     new_base_type,
                                                     new_precision,
+                                                    layer_dither_type,
+                                                    mask_dither_type,
                                                     push_undo);
 }
 
diff --git a/app/core/gimpdrawable.h b/app/core/gimpdrawable.h
index cd4c148..eb2236a 100644
--- a/app/core/gimpdrawable.h
+++ b/app/core/gimpdrawable.h
@@ -64,6 +64,8 @@ struct _GimpDrawableClass
                                            GimpImage            *dest_image,
                                            GimpImageBaseType     new_base_type,
                                            GimpPrecision         new_precision,
+                                           gint                  layer_dither_type,
+                                           gint                  mask_dither_type,
                                            gboolean              push_undo);
   void          (* apply_buffer)          (GimpDrawable         *drawable,
                                            GeglBuffer           *buffer,
@@ -137,6 +139,8 @@ void            gimp_drawable_convert_type       (GimpDrawable       *drawable,
                                                   GimpImage          *dest_image,
                                                   GimpImageBaseType   new_base_type,
                                                   GimpPrecision       new_precision,
+                                                  gint                layer_dither_type,
+                                                  gint                mask_dither_type,
                                                   gboolean            push_undo);
 
 void            gimp_drawable_apply_buffer       (GimpDrawable        *drawable,
diff --git a/app/core/gimpgrouplayer.c b/app/core/gimpgrouplayer.c
index 1cfb5f3..70b0600 100644
--- a/app/core/gimpgrouplayer.c
+++ b/app/core/gimpgrouplayer.c
@@ -135,6 +135,8 @@ static void            gimp_group_layer_convert_type (GimpDrawable      *drawabl
                                                       GimpImage         *dest_image,
                                                       GimpImageBaseType  new_base_type,
                                                       GimpPrecision      new_precision,
+                                                      gint               layer_dither_type,
+                                                      gint               mask_dither_type,
                                                       gboolean           push_undo);
 
 static const Babl    * gimp_group_layer_get_format   (GimpProjectable *projectable);
@@ -847,6 +849,8 @@ gimp_group_layer_convert_type (GimpDrawable      *drawable,
                                GimpImage         *dest_image,
                                GimpImageBaseType  new_base_type,
                                GimpPrecision      new_precision,
+                               gint               layer_dither_type,
+                               gint               mask_dither_type,
                                gboolean           push_undo)
 {
   GimpGroupLayer        *group   = GIMP_GROUP_LAYER (drawable);
@@ -887,7 +891,9 @@ gimp_group_layer_convert_type (GimpDrawable      *drawable,
       new_precision != gimp_drawable_get_precision (GIMP_DRAWABLE (mask)))
     {
       gimp_drawable_convert_type (GIMP_DRAWABLE (mask), dest_image,
-                                  GIMP_GRAY, new_precision, push_undo);
+                                  GIMP_GRAY, new_precision,
+                                  layer_dither_type, mask_dither_type,
+                                  push_undo);
     }
 }
 
diff --git a/app/core/gimpgrouplayerundo.c b/app/core/gimpgrouplayerundo.c
index 728ecfb..83f3fb2 100644
--- a/app/core/gimpgrouplayerundo.c
+++ b/app/core/gimpgrouplayerundo.c
@@ -128,6 +128,7 @@ gimp_group_layer_undo_pop (GimpUndo            *undo,
                                     gimp_item_get_image (GIMP_ITEM (group)),
                                     group_layer_undo->prev_type,
                                     group_layer_undo->prev_precision,
+                                    0, 0,
                                     FALSE);
 
         group_layer_undo->prev_type      = type;
diff --git a/app/core/gimpimage-convert-precision.c b/app/core/gimpimage-convert-precision.c
index 682f891..327d75c 100644
--- a/app/core/gimpimage-convert-precision.c
+++ b/app/core/gimpimage-convert-precision.c
@@ -104,7 +104,10 @@ gimp_image_convert_precision (GimpImage     *image,
 
       gimp_drawable_convert_type (drawable, image,
                                   gimp_drawable_get_base_type (drawable),
-                                  precision, TRUE);
+                                  precision,
+                                  layer_dither_type,
+                                  mask_dither_type,
+                                  TRUE);
 
       if (progress)
         gimp_progress_set_value (progress,
diff --git a/app/core/gimpimage-convert-type.c b/app/core/gimpimage-convert-type.c
index 0ef38aa..e7d814e 100644
--- a/app/core/gimpimage-convert-type.c
+++ b/app/core/gimpimage-convert-type.c
@@ -978,6 +978,7 @@ gimp_image_convert_type (GimpImage               *image,
         case GIMP_GRAY:
           gimp_drawable_convert_type (GIMP_DRAWABLE (layer), image, new_type,
                                       gimp_drawable_get_precision (GIMP_DRAWABLE (layer)),
+                                      0, 0,
                                       TRUE);
           break;
 
diff --git a/app/core/gimplayer.c b/app/core/gimplayer.c
index bcf5113..652a1cb 100644
--- a/app/core/gimplayer.c
+++ b/app/core/gimplayer.c
@@ -159,6 +159,8 @@ static void    gimp_layer_convert_type          (GimpDrawable       *drawable,
                                                  GimpImage          *dest_image,
                                                  GimpImageBaseType   new_base_type,
                                                  GimpPrecision       new_precision,
+                                                 gint                layer_dither_type,
+                                                 gint                mask_dither_type,
                                                  gboolean            push_undo);
 static void    gimp_layer_invalidate_boundary   (GimpDrawable       *drawable);
 static void    gimp_layer_get_active_components (const GimpDrawable *drawable,
@@ -631,6 +633,7 @@ gimp_layer_convert (GimpItem  *item,
     {
       gimp_drawable_convert_type (drawable, dest_image,
                                   new_base_type, new_precision,
+                                  0, 0,
                                   FALSE);
     }
 
@@ -950,21 +953,63 @@ gimp_layer_convert_type (GimpDrawable      *drawable,
                          GimpImage         *dest_image,
                          GimpImageBaseType  new_base_type,
                          GimpPrecision      new_precision,
+                         gint               layer_dither_type,
+                         gint               mask_dither_type,
                          gboolean           push_undo)
 {
-  GimpLayer *layer = GIMP_LAYER (drawable);
+  GimpLayer  *layer = GIMP_LAYER (drawable);
+  GeglBuffer *dest_buffer;
+  const Babl *format;
+
+  format = gimp_image_get_format (dest_image,
+                                  new_base_type,
+                                  new_precision,
+                                  gimp_drawable_has_alpha (drawable));
+
+  dest_buffer =
+    gegl_buffer_new (GEGL_RECTANGLE (0, 0,
+                                     gimp_item_get_width  (GIMP_ITEM (drawable)),
+                                     gimp_item_get_height (GIMP_ITEM (drawable))),
+                     format);
+
+  if (layer_dither_type == 0)
+    {
+      gegl_buffer_copy (gimp_drawable_get_buffer (drawable), NULL,
+                        dest_buffer, NULL);
+    }
+  else
+    {
+      GeglNode *dither;
+      gint      bits;
+
+      bits = (babl_format_get_bytes_per_pixel (format) * 8 /
+              babl_format_get_n_components (format));
+
+      dither = gegl_node_new_child (NULL,
+                                    "operation",       "gegl:color-reduction",
+                                    "red-bits",        bits,
+                                    "green-bits",      bits,
+                                    "blue-bits",       bits,
+                                    "alpha-bits",      bits,
+                                    "dither-strategy", layer_dither_type,
+                                    NULL);
+
+      gimp_drawable_apply_operation_to_buffer (drawable, NULL, NULL,
+                                               dither, dest_buffer);
+      g_object_unref (dither);
+    }
+
+  gimp_drawable_set_buffer (drawable, push_undo, NULL, dest_buffer);
+  g_object_unref (dest_buffer);
 
   if (layer->mask &&
       new_precision != gimp_drawable_get_precision (GIMP_DRAWABLE (layer->mask)))
     {
       gimp_drawable_convert_type (GIMP_DRAWABLE (layer->mask), dest_image,
-                                  GIMP_GRAY, new_precision, push_undo);
+                                  GIMP_GRAY, new_precision,
+                                  layer_dither_type, mask_dither_type,
+                                  push_undo);
     }
-
-  GIMP_DRAWABLE_CLASS (parent_class)->convert_type (drawable, dest_image,
-                                                    new_base_type,
-                                                    new_precision,
-                                                    push_undo);
 }
 
 static void



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