Commit 5dbcdef4 authored by Michael Natterer's avatar Michael Natterer 😴

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.
parent 56c6935a
......@@ -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);
}
......@@ -786,6 +795,60 @@ gimp_channel_to_selection (GimpItem *item,
feather, feather_radius_x, feather_radius_x);
}
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)
{
......
......@@ -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);
}
......
......@@ -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,
......
......@@ -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);
}
}
......
......@@ -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;
......
......@@ -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,
......
......@@ -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;
......
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment