diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:30:19 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:30:19 +0000 |
commit | 5c1676dfe6d2f3c837a5e074117b45613fd29a72 (patch) | |
tree | cbffb45144febf451e54061db2b21395faf94bfe /libgimpwidgets/gimppreviewarea.c | |
parent | Initial commit. (diff) | |
download | gimp-5c1676dfe6d2f3c837a5e074117b45613fd29a72.tar.xz gimp-5c1676dfe6d2f3c837a5e074117b45613fd29a72.zip |
Adding upstream version 2.10.34.upstream/2.10.34upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libgimpwidgets/gimppreviewarea.c')
-rw-r--r-- | libgimpwidgets/gimppreviewarea.c | 1956 |
1 files changed, 1956 insertions, 0 deletions
diff --git a/libgimpwidgets/gimppreviewarea.c b/libgimpwidgets/gimppreviewarea.c new file mode 100644 index 0000000..7715ce0 --- /dev/null +++ b/libgimpwidgets/gimppreviewarea.c @@ -0,0 +1,1956 @@ +/* LIBGIMP - The GIMP Library + * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball + * + * This library is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <https://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <string.h> + +#include <gegl.h> +#include <gtk/gtk.h> + +#include "libgimpbase/gimpbase.h" +#include "libgimpconfig/gimpconfig.h" +#include "libgimpcolor/gimpcolor.h" + +#include "gimpwidgetstypes.h" + +#include "gimppreviewarea.h" +#include "gimpwidgetsutils.h" + +#include "libgimp/libgimp-intl.h" + + +/** + * SECTION: gimppreviewarea + * @title: GimpPreviewArea + * @short_description: A general purpose preview widget which caches + * its pixel data. + * + * A general purpose preview widget which caches its pixel data. + **/ + + +enum +{ + PROP_0, + PROP_CHECK_SIZE, + PROP_CHECK_TYPE +}; + + +#define DEFAULT_CHECK_SIZE GIMP_CHECK_SIZE_MEDIUM_CHECKS +#define DEFAULT_CHECK_TYPE GIMP_CHECK_TYPE_GRAY_CHECKS + +#define CHECK_COLOR(area, row, col) \ + (((((area)->offset_y + (row)) & size) ^ \ + (((area)->offset_x + (col)) & size)) ? dark : light) + + +typedef struct _GimpPreviewAreaPrivate GimpPreviewAreaPrivate; + +struct _GimpPreviewAreaPrivate +{ + GimpColorConfig *config; + GimpColorTransform *transform; +}; + +#define GET_PRIVATE(obj) \ + ((GimpPreviewAreaPrivate *) gimp_preview_area_get_instance_private ((GimpPreviewArea *) (obj))) + + +static void gimp_preview_area_dispose (GObject *object); +static void gimp_preview_area_finalize (GObject *object); +static void gimp_preview_area_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_preview_area_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_preview_area_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); +static gboolean gimp_preview_area_expose (GtkWidget *widget, + GdkEventExpose *event); + +static void gimp_preview_area_queue_draw (GimpPreviewArea *area, + gint x, + gint y, + gint width, + gint height); +static gint gimp_preview_area_image_type_bytes (GimpImageType type); + +static void gimp_preview_area_create_transform (GimpPreviewArea *area); +static void gimp_preview_area_destroy_transform (GimpPreviewArea *area); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpPreviewArea, gimp_preview_area, + GTK_TYPE_DRAWING_AREA) + +#define parent_class gimp_preview_area_parent_class + + +static void +gimp_preview_area_class_init (GimpPreviewAreaClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = gimp_preview_area_dispose; + object_class->finalize = gimp_preview_area_finalize; + object_class->set_property = gimp_preview_area_set_property; + object_class->get_property = gimp_preview_area_get_property; + + widget_class->size_allocate = gimp_preview_area_size_allocate; + widget_class->expose_event = gimp_preview_area_expose; + + g_object_class_install_property (object_class, PROP_CHECK_SIZE, + g_param_spec_enum ("check-size", + _("Check Size"), + "The size of the checkerboard pattern indicating transparency", + GIMP_TYPE_CHECK_SIZE, + DEFAULT_CHECK_SIZE, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_CHECK_TYPE, + g_param_spec_enum ("check-type", + _("Check Style"), + "The colors of the checkerboard pattern indicating transparency", + GIMP_TYPE_CHECK_TYPE, + DEFAULT_CHECK_TYPE, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_preview_area_init (GimpPreviewArea *area) +{ + area->check_size = DEFAULT_CHECK_SIZE; + area->check_type = DEFAULT_CHECK_TYPE; + area->buf = NULL; + area->colormap = NULL; + area->offset_x = 0; + area->offset_y = 0; + area->width = 0; + area->height = 0; + area->rowstride = 0; + area->max_width = -1; + area->max_height = -1; + + gimp_widget_track_monitor (GTK_WIDGET (area), + G_CALLBACK (gimp_preview_area_destroy_transform), + NULL); +} + +static void +gimp_preview_area_dispose (GObject *object) +{ + GimpPreviewArea *area = GIMP_PREVIEW_AREA (object); + + gimp_preview_area_set_color_config (area, NULL); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_preview_area_finalize (GObject *object) +{ + GimpPreviewArea *area = GIMP_PREVIEW_AREA (object); + + g_clear_pointer (&area->buf, g_free); + g_clear_pointer (&area->colormap, g_free); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gimp_preview_area_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpPreviewArea *area = GIMP_PREVIEW_AREA (object); + + switch (property_id) + { + case PROP_CHECK_SIZE: + area->check_size = g_value_get_enum (value); + break; + case PROP_CHECK_TYPE: + area->check_type = g_value_get_enum (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_preview_area_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpPreviewArea *area = GIMP_PREVIEW_AREA (object); + + switch (property_id) + { + case PROP_CHECK_SIZE: + g_value_set_enum (value, area->check_size); + break; + case PROP_CHECK_TYPE: + g_value_set_enum (value, area->check_type); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_preview_area_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GimpPreviewArea *area = GIMP_PREVIEW_AREA (widget); + gint width; + gint height; + + if (GTK_WIDGET_CLASS (parent_class)->size_allocate) + GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation); + + width = (area->max_width > 0 ? + MIN (allocation->width, area->max_width) : allocation->width); + height = (area->max_height > 0 ? + MIN (allocation->height, area->max_height) : allocation->height); + + if (width != area->width || height != area->height) + { + if (area->buf) + { + g_free (area->buf); + + area->buf = NULL; + area->rowstride = 0; + } + + area->width = width; + area->height = height; + } +} + +static gboolean +gimp_preview_area_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + GimpPreviewArea *area = GIMP_PREVIEW_AREA (widget); + GimpPreviewAreaPrivate *priv = GET_PRIVATE (area); + GtkAllocation allocation; + GdkPixbuf *pixbuf; + GdkRectangle rect; + cairo_t *cr; + + if (! area->buf) + return FALSE; + + gtk_widget_get_allocation (widget, &allocation); + + rect.x = (allocation.width - area->width) / 2; + rect.y = (allocation.height - area->height) / 2; + rect.width = area->width; + rect.height = area->height; + + if (! priv->transform) + gimp_preview_area_create_transform (area); + + if (priv->transform) + { + const Babl *format = babl_format ("R'G'B' u8"); + gint rowstride = ((area->width * 3) + 3) & ~3; + guchar *buf = g_new (guchar, rowstride * area->height); + guchar *src = area->buf; + guchar *dest = buf; + gint i; + + for (i = 0; i < area->height; i++) + { + gimp_color_transform_process_pixels (priv->transform, + format, src, + format, dest, + area->width); + + src += area->rowstride; + dest += rowstride; + } + + pixbuf = gdk_pixbuf_new_from_data (buf, + GDK_COLORSPACE_RGB, + FALSE, + 8, + rect.width, + rect.height, + rowstride, + (GdkPixbufDestroyNotify) g_free, NULL); + } + else + { + pixbuf = gdk_pixbuf_new_from_data (area->buf, + GDK_COLORSPACE_RGB, + FALSE, + 8, + rect.width, + rect.height, + area->rowstride, + NULL, NULL); + } + + cr = gdk_cairo_create (gtk_widget_get_window (widget)); + + gdk_cairo_region (cr, event->region); + cairo_clip (cr); + + gdk_cairo_set_source_pixbuf (cr, pixbuf, rect.x, rect.y); + cairo_paint (cr); + + cairo_destroy (cr); + g_object_unref (pixbuf); + + return FALSE; +} + +static void +gimp_preview_area_queue_draw (GimpPreviewArea *area, + gint x, + gint y, + gint width, + gint height) +{ + GtkWidget *widget = GTK_WIDGET (area); + GtkAllocation allocation; + + gtk_widget_get_allocation (widget, &allocation); + + x += (allocation.width - area->width) / 2; + y += (allocation.height - area->height) / 2; + + gtk_widget_queue_draw_area (widget, x, y, width, height); +} + +static gint +gimp_preview_area_image_type_bytes (GimpImageType type) +{ + switch (type) + { + case GIMP_GRAY_IMAGE: + case GIMP_INDEXED_IMAGE: + return 1; + + case GIMP_GRAYA_IMAGE: + case GIMP_INDEXEDA_IMAGE: + return 2; + + case GIMP_RGB_IMAGE: + return 3; + + case GIMP_RGBA_IMAGE: + return 4; + + default: + g_return_val_if_reached (0); + break; + } +} + +static void +gimp_preview_area_create_transform (GimpPreviewArea *area) +{ + GimpPreviewAreaPrivate *priv = GET_PRIVATE (area); + + if (priv->config) + { + static GimpColorProfile *profile = NULL; + + const Babl *format = babl_format ("R'G'B' u8"); + + if (G_UNLIKELY (! profile)) + profile = gimp_color_profile_new_rgb_srgb (); + + priv->transform = gimp_widget_get_color_transform (GTK_WIDGET (area), + priv->config, + profile, + format, + format); + } +} + +static void +gimp_preview_area_destroy_transform (GimpPreviewArea *area) +{ + GimpPreviewAreaPrivate *priv = GET_PRIVATE (area); + + if (priv->transform) + { + g_object_unref (priv->transform); + priv->transform = NULL; + } + + gtk_widget_queue_draw (GTK_WIDGET (area)); +} + + +/** + * gimp_preview_area_new: + * + * Creates a new #GimpPreviewArea widget. + * + * Return value: a new #GimpPreviewArea widget. + * + * Since GIMP 2.2 + **/ +GtkWidget * +gimp_preview_area_new (void) +{ + return g_object_new (GIMP_TYPE_PREVIEW_AREA, NULL); +} + +/** + * gimp_preview_area_draw: + * @area: a #GimpPreviewArea widget. + * @x: x offset in preview + * @y: y offset in preview + * @width: buffer width + * @height: buffer height + * @type: the #GimpImageType of @buf + * @buf: a #guchar buffer that contains the preview pixel data. + * @rowstride: rowstride of @buf + * + * Draws @buf on @area and queues a redraw on the given rectangle. + * + * Since GIMP 2.2 + **/ +void +gimp_preview_area_draw (GimpPreviewArea *area, + gint x, + gint y, + gint width, + gint height, + GimpImageType type, + const guchar *buf, + gint rowstride) +{ + const guchar *src; + guchar *dest; + guint size; + guchar light; + guchar dark; + gint row; + gint col; + + g_return_if_fail (GIMP_IS_PREVIEW_AREA (area)); + g_return_if_fail (width >= 0 && height >= 0); + + if (width == 0 || height == 0) + return; + + g_return_if_fail (buf != NULL); + g_return_if_fail (rowstride > 0); + + if (x + width < 0 || x >= area->width) + return; + + if (y + height < 0 || y >= area->height) + return; + + if (x < 0) + { + gint bpp = gimp_preview_area_image_type_bytes (type); + + buf -= x * bpp; + width += x; + + x = 0; + } + + if (x + width > area->width) + width = area->width - x; + + if (y < 0) + { + buf -= y * rowstride; + height += y; + + y = 0; + } + + if (y + height > area->height) + height = area->height - y; + + if (! area->buf) + { + area->rowstride = ((area->width * 3) + 3) & ~3; + area->buf = g_new (guchar, area->rowstride * area->height); + } + + size = 1 << (2 + area->check_size); + gimp_checks_get_shades (area->check_type, &light, &dark); + + src = buf; + dest = area->buf + x * 3 + y * area->rowstride; + + switch (type) + { + case GIMP_RGB_IMAGE: + for (row = 0; row < height; row++) + { + memcpy (dest, src, 3 * width); + + src += rowstride; + dest += area->rowstride; + } + break; + + case GIMP_RGBA_IMAGE: + for (row = y; row < y + height; row++) + { + const guchar *s = src; + guchar *d = dest; + + for (col = x; col < x + width; col++, s += 4, d+= 3) + { + switch (s[3]) + { + case 0: + d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col); + break; + + case 255: + d[0] = s[0]; + d[1] = s[1]; + d[2] = s[2]; + break; + + default: + { + register guint alpha = s[3] + 1; + register guint check = CHECK_COLOR (area, row, col); + + d[0] = ((check << 8) + (s[0] - check) * alpha) >> 8; + d[1] = ((check << 8) + (s[1] - check) * alpha) >> 8; + d[2] = ((check << 8) + (s[2] - check) * alpha) >> 8; + } + break; + } + } + + src += rowstride; + dest += area->rowstride; + } + break; + + case GIMP_GRAY_IMAGE: + for (row = 0; row < height; row++) + { + const guchar *s = src; + guchar *d = dest; + + for (col = 0; col < width; col++, s++, d += 3) + { + d[0] = d[1] = d[2] = s[0]; + } + + src += rowstride; + dest += area->rowstride; + } + break; + + case GIMP_GRAYA_IMAGE: + for (row = y; row < y + height; row++) + { + const guchar *s = src; + guchar *d = dest; + + for (col = x; col < x + width; col++, s += 2, d+= 3) + { + switch (s[1]) + { + case 0: + d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col); + break; + + case 255: + d[0] = d[1] = d[2] = s[0]; + break; + + default: + { + register guint alpha = s[1] + 1; + register guint check = CHECK_COLOR (area, row, col); + + d[0] = d[1] = d[2] = + ((check << 8) + (s[0] - check) * alpha) >> 8; + } + break; + } + } + + src += rowstride; + dest += area->rowstride; + } + break; + + case GIMP_INDEXED_IMAGE: + g_return_if_fail (area->colormap != NULL); + for (row = 0; row < height; row++) + { + const guchar *s = src; + guchar *d = dest; + + for (col = 0; col < width; col++, s++, d += 3) + { + const guchar *colormap = area->colormap + 3 * s[0]; + + d[0] = colormap[0]; + d[1] = colormap[1]; + d[2] = colormap[2]; + } + + src += rowstride; + dest += area->rowstride; + } + break; + + case GIMP_INDEXEDA_IMAGE: + g_return_if_fail (area->colormap != NULL); + for (row = y; row < y + height; row++) + { + const guchar *s = src; + guchar *d = dest; + + for (col = x; col < x + width; col++, s += 2, d += 3) + { + const guchar *colormap = area->colormap + 3 * s[0]; + + switch (s[1]) + { + case 0: + d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col); + break; + + case 255: + d[0] = colormap[0]; + d[1] = colormap[1]; + d[2] = colormap[2]; + break; + + default: + { + register guint alpha = s[3] + 1; + register guint check = CHECK_COLOR (area, row, col); + + d[0] = ((check << 8) + (colormap[0] - check) * alpha) >> 8; + d[1] = ((check << 8) + (colormap[1] - check) * alpha) >> 8; + d[2] = ((check << 8) + (colormap[2] - check) * alpha) >> 8; + } + break; + } + } + + src += rowstride; + dest += area->rowstride; + } + break; + } + + gimp_preview_area_queue_draw (area, x, y, width, height); +} + +/** + * gimp_preview_area_blend: + * @area: a #GimpPreviewArea widget. + * @x: x offset in preview + * @y: y offset in preview + * @width: buffer width + * @height: buffer height + * @type: the #GimpImageType of @buf1 and @buf2 + * @buf1: a #guchar buffer that contains the pixel data for + * the lower layer + * @rowstride1: rowstride of @buf1 + * @buf2: a #guchar buffer that contains the pixel data for + * the upper layer + * @rowstride2: rowstride of @buf2 + * @opacity: The opacity of the first layer. + * + * Composites @buf1 on @buf2 with the given @opacity, draws the result + * to @area and queues a redraw on the given rectangle. + * + * Since GIMP 2.2 + **/ +void +gimp_preview_area_blend (GimpPreviewArea *area, + gint x, + gint y, + gint width, + gint height, + GimpImageType type, + const guchar *buf1, + gint rowstride1, + const guchar *buf2, + gint rowstride2, + guchar opacity) +{ + const guchar *src1; + const guchar *src2; + guchar *dest; + guint size; + guchar light; + guchar dark; + gint row; + gint col; + gint i; + + g_return_if_fail (GIMP_IS_PREVIEW_AREA (area)); + g_return_if_fail (width >= 0 && height >= 0); + + if (width == 0 || height == 0) + return; + + g_return_if_fail (buf1 != NULL); + g_return_if_fail (buf2 != NULL); + g_return_if_fail (rowstride1 > 0); + g_return_if_fail (rowstride2 > 0); + + switch (opacity) + { + case 0: + gimp_preview_area_draw (area, x, y, width, height, + type, buf1, rowstride1); + return; + + case 255: + gimp_preview_area_draw (area, x, y, width, height, + type, buf2, rowstride2); + return; + + default: + break; + } + + if (x + width < 0 || x >= area->width) + return; + + if (y + height < 0 || y >= area->height) + return; + + if (x < 0) + { + gint bpp = gimp_preview_area_image_type_bytes (type); + + buf1 -= x * bpp; + buf2 -= x * bpp; + width += x; + + x = 0; + } + + if (x + width > area->width) + width = area->width - x; + + if (y < 0) + { + buf1 -= y * rowstride1; + buf2 -= y * rowstride2; + height += y; + + y = 0; + } + + if (y + height > area->height) + height = area->height - y; + + if (! area->buf) + { + area->rowstride = ((area->width * 3) + 3) & ~3; + area->buf = g_new (guchar, area->rowstride * area->height); + } + + size = 1 << (2 + area->check_size); + gimp_checks_get_shades (area->check_type, &light, &dark); + + src1 = buf1; + src2 = buf2; + dest = area->buf + x * 3 + y * area->rowstride; + + switch (type) + { + case GIMP_RGB_IMAGE: + for (row = 0; row < height; row++) + { + const guchar *s1 = src1; + const guchar *s2 = src2; + guchar *d = dest; + + for (col = x; col < x + width; col++, s1 += 3, s2 += 3, d+= 3) + { + d[0] = ((s1[0] << 8) + (s2[0] - s1[0]) * opacity) >> 8; + d[1] = ((s1[1] << 8) + (s2[1] - s1[1]) * opacity) >> 8; + d[2] = ((s1[2] << 8) + (s2[2] - s1[2]) * opacity) >> 8; + } + + src1 += rowstride1; + src2 += rowstride2; + dest += area->rowstride; + } + break; + + case GIMP_RGBA_IMAGE: + for (row = y; row < y + height; row++) + { + const guchar *s1 = src1; + const guchar *s2 = src2; + guchar *d = dest; + + for (col = x; col < x + width; col++, s1 += 4, s2 += 4, d+= 3) + { + guchar inter[4]; + + if (s1[3] == s2[3]) + { + inter[0] = ((s1[0] << 8) + (s2[0] - s1[0]) * opacity) >> 8; + inter[1] = ((s1[1] << 8) + (s2[1] - s1[1]) * opacity) >> 8; + inter[2] = ((s1[2] << 8) + (s2[2] - s1[2]) * opacity) >> 8; + inter[3] = s1[3]; + } + else + { + inter[3] = ((s1[3] << 8) + (s2[3] - s1[3]) * opacity) >> 8; + + if (inter[3]) + { + for (i = 0; i < 3; i++) + { + gushort a = s1[i] * s1[3]; + gushort b = s2[i] * s2[3]; + + inter[i] = + (((a << 8) + (b - a) * opacity) >> 8) / inter[3]; + } + } + } + + switch (inter[3]) + { + case 0: + d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col); + break; + + case 255: + d[0] = inter[0]; + d[1] = inter[1]; + d[2] = inter[2]; + break; + + default: + { + register guint alpha = inter[3] + 1; + register guint check = CHECK_COLOR (area, row, col); + + d[0] = ((check << 8) + (inter[0] - check) * alpha) >> 8; + d[1] = ((check << 8) + (inter[1] - check) * alpha) >> 8; + d[2] = ((check << 8) + (inter[2] - check) * alpha) >> 8; + } + break; + } + } + + src1 += rowstride1; + src2 += rowstride2; + dest += area->rowstride; + } + break; + + case GIMP_GRAY_IMAGE: + for (row = 0; row < height; row++) + { + const guchar *s1 = src1; + const guchar *s2 = src2; + guchar *d = dest; + + for (col = 0; col < width; col++, s1++, s2++, d += 3) + { + d[0] = d[1] = d[2] = + ((s1[0] << 8) + (s2[0] - s1[0]) * opacity) >> 8; + } + + src1 += rowstride1; + src2 += rowstride2; + dest += area->rowstride; + } + break; + + case GIMP_GRAYA_IMAGE: + for (row = y; row < y + height; row++) + { + const guchar *s1 = src1; + const guchar *s2 = src2; + guchar *d = dest; + + for (col = x; col < x + width; col++, s1 += 2, s2 += 2, d+= 3) + { + guchar inter[2] = { 0, }; + + if (s1[1] == s2[1]) + { + inter[0] = ((s1[0] << 8) + (s2[0] - s1[0]) * opacity) >> 8; + inter[1] = s1[1]; + } + else + { + inter[1] = ((s1[1] << 8) + (s2[1] - s1[1]) * opacity) >> 8; + + if (inter[1]) + { + gushort a = s1[0] * s1[1]; + gushort b = s2[0] * s2[1]; + + inter[0] = + (((a << 8) + (b - a) * opacity) >> 8) / inter[1]; + } + } + + switch (inter[1]) + { + case 0: + d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col); + break; + + case 255: + d[0] = d[1] = d[2] = inter[0]; + break; + + default: + { + register guint alpha = inter[1] + 1; + register guint check = CHECK_COLOR (area, row, col); + + d[0] = d[1] = d[2] = + ((check << 8) + (inter[0] - check) * alpha) >> 8; + } + break; + } + } + + src1 += rowstride1; + src2 += rowstride2; + dest += area->rowstride; + } + break; + + case GIMP_INDEXED_IMAGE: + g_return_if_fail (area->colormap != NULL); + for (row = 0; row < height; row++) + { + const guchar *s1 = src1; + const guchar *s2 = src2; + guchar *d = dest; + + for (col = 0; col < width; col++, s1++, s2++, d += 3) + { + const guchar *cmap1 = area->colormap + 3 * s1[0]; + const guchar *cmap2 = area->colormap + 3 * s2[0]; + + d[0] = ((cmap1[0] << 8) + (cmap2[0] - cmap1[0]) * opacity) >> 8; + d[1] = ((cmap1[1] << 8) + (cmap2[1] - cmap1[1]) * opacity) >> 8; + d[2] = ((cmap1[2] << 8) + (cmap2[2] - cmap1[2]) * opacity) >> 8; + } + + src1 += rowstride1; + src2 += rowstride2; + dest += area->rowstride; + } + break; + + case GIMP_INDEXEDA_IMAGE: + g_return_if_fail (area->colormap != NULL); + for (row = y; row < y + height; row++) + { + const guchar *s1 = src1; + const guchar *s2 = src2; + guchar *d = dest; + + for (col = x; col < x + width; col++, s1 += 2, s2 += 2, d += 3) + { + const guchar *cmap1 = area->colormap + 3 * s1[0]; + const guchar *cmap2 = area->colormap + 3 * s2[0]; + guchar inter[4]; + + if (s1[1] == s2[1]) + { + inter[0] = (((cmap1[0] << 8) + + (cmap2[0] - cmap1[0]) * opacity) >> 8); + inter[1] = (((cmap1[1] << 8) + + (cmap2[1] - cmap1[1]) * opacity) >> 8); + inter[2] = (((cmap1[2] << 8) + + (cmap2[2] - cmap1[2]) * opacity) >> 8); + inter[3] = s1[1]; + } + else + { + inter[3] = ((s1[1] << 8) + (s2[1] - s1[1]) * opacity) >> 8; + + if (inter[3]) + { + for (i = 0; i < 3; i++) + { + gushort a = cmap1[i] * s1[1]; + gushort b = cmap2[i] * s2[1]; + + inter[i] = + (((a << 8) + (b - a) * opacity) >> 8) / inter[3]; + } + } + } + + switch (inter[3]) + { + case 0: + d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col); + break; + + case 255: + d[0] = inter[0]; + d[1] = inter[1]; + d[2] = inter[2]; + break; + + default: + { + register guint alpha = inter[3] + 1; + register guint check = CHECK_COLOR (area, row, col); + + d[0] = ((check << 8) + (inter[0] - check) * alpha) >> 8; + d[1] = ((check << 8) + (inter[1] - check) * alpha) >> 8; + d[2] = ((check << 8) + (inter[2] - check) * alpha) >> 8; + } + break; + } + } + + src1 += rowstride1; + src2 += rowstride2; + dest += area->rowstride; + } + break; + } + + gimp_preview_area_queue_draw (area, x, y, width, height); +} + +/** + * gimp_preview_area_mask: + * @area: a #GimpPreviewArea widget. + * @x: x offset in preview + * @y: y offset in preview + * @width: buffer width + * @height: buffer height + * @type: the #GimpImageType of @buf1 and @buf2 + * @buf1: a #guchar buffer that contains the pixel data for + * the lower layer + * @rowstride1: rowstride of @buf1 + * @buf2: a #guchar buffer that contains the pixel data for + * the upper layer + * @rowstride2: rowstride of @buf2 + * @mask: a #guchar buffer representing the mask of the second + * layer. + * @rowstride_mask: rowstride for the mask. + * + * Composites @buf1 on @buf2 with the given @mask, draws the result on + * @area and queues a redraw on the given rectangle. + * + * Since GIMP 2.2 + **/ +void +gimp_preview_area_mask (GimpPreviewArea *area, + gint x, + gint y, + gint width, + gint height, + GimpImageType type, + const guchar *buf1, + gint rowstride1, + const guchar *buf2, + gint rowstride2, + const guchar *mask, + gint rowstride_mask) +{ + const guchar *src1; + const guchar *src2; + const guchar *src_mask; + guchar *dest; + guint size; + guchar light; + guchar dark; + gint row; + gint col; + gint i; + + g_return_if_fail (GIMP_IS_PREVIEW_AREA (area)); + g_return_if_fail (width >= 0 && height >= 0); + + if (width == 0 || height == 0) + return; + + g_return_if_fail (buf1 != NULL); + g_return_if_fail (buf2 != NULL); + g_return_if_fail (mask != NULL); + g_return_if_fail (rowstride1 > 0); + g_return_if_fail (rowstride2 > 0); + g_return_if_fail (rowstride_mask > 0); + + if (x + width < 0 || x >= area->width) + return; + + if (y + height < 0 || y >= area->height) + return; + + if (x < 0) + { + gint bpp = gimp_preview_area_image_type_bytes (type); + + buf1 -= x * bpp; + buf2 -= x * bpp; + mask -= x; + width += x; + + x = 0; + } + + if (x + width > area->width) + width = area->width - x; + + if (y < 0) + { + buf1 -= y * rowstride1; + buf2 -= y * rowstride2; + mask -= y * rowstride_mask; + height += y; + + y = 0; + } + + if (y + height > area->height) + height = area->height - y; + + if (! area->buf) + { + area->rowstride = ((area->width * 3) + 3) & ~3; + area->buf = g_new (guchar, area->rowstride * area->height); + } + + size = 1 << (2 + area->check_size); + gimp_checks_get_shades (area->check_type, &light, &dark); + + src1 = buf1; + src2 = buf2; + src_mask = mask; + dest = area->buf + x * 3 + y * area->rowstride; + + switch (type) + { + case GIMP_RGB_IMAGE: + for (row = 0; row < height; row++) + { + const guchar *s1 = src1; + const guchar *s2 = src2; + const guchar *m = src_mask; + guchar *d = dest; + + for (col = x; col < x + width; col++, s1 += 3, s2 += 3, m++, d+= 3) + { + d[0] = ((s1[0] << 8) + (s2[0] - s1[0]) * m[0]) >> 8; + d[1] = ((s1[1] << 8) + (s2[1] - s1[1]) * m[0]) >> 8; + d[2] = ((s1[2] << 8) + (s2[2] - s1[2]) * m[0]) >> 8; + } + + src1 += rowstride1; + src2 += rowstride2; + src_mask += rowstride_mask; + dest += area->rowstride; + } + break; + + case GIMP_RGBA_IMAGE: + for (row = y; row < y + height; row++) + { + const guchar *s1 = src1; + const guchar *s2 = src2; + const guchar *m = src_mask; + guchar *d = dest; + + for (col = x; col < x + width; col++, s1 += 4, s2 += 4, m++, d+= 3) + { + switch (m[0]) + { + case 0: + switch (s1[3]) + { + case 0: + d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col); + break; + + case 255: + d[0] = s1[0]; + d[1] = s1[1]; + d[2] = s1[2]; + break; + + default: + { + register guint alpha = s1[3] + 1; + register guint check = CHECK_COLOR (area, row, col); + + d[0] = ((check << 8) + (s1[0] - check) * alpha) >> 8; + d[1] = ((check << 8) + (s1[1] - check) * alpha) >> 8; + d[2] = ((check << 8) + (s1[2] - check) * alpha) >> 8; + } + break; + } + break; + + case 255: + switch (s2[3]) + { + case 0: + d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col); + break; + + case 255: + d[0] = s2[0]; + d[1] = s2[1]; + d[2] = s2[2]; + break; + + default: + { + register guint alpha = s2[3] + 1; + register guint check = CHECK_COLOR (area, row, col); + + d[0] = ((check << 8) + (s2[0] - check) * alpha) >> 8; + d[1] = ((check << 8) + (s2[1] - check) * alpha) >> 8; + d[2] = ((check << 8) + (s2[2] - check) * alpha) >> 8; + } + break; + } + break; + + default: + { + guchar inter[4]; + + if (s1[3] == s2[3]) + { + inter[0] = ((s1[0] << 8) + (s2[0] - s1[0]) * m[0]) >> 8; + inter[1] = ((s1[1] << 8) + (s2[1] - s1[1]) * m[0]) >> 8; + inter[2] = ((s1[2] << 8) + (s2[2] - s1[2]) * m[0]) >> 8; + inter[3] = s1[3]; + } + else + { + inter[3] = ((s1[3] << 8) + (s2[3] - s1[3]) * m[0]) >> 8; + + if (inter[3]) + { + for (i = 0; i < 3; i++) + { + gushort a = s1[i] * s1[3]; + gushort b = s2[i] * s2[3]; + + inter[i] = + (((a << 8) + (b - a) * m[0]) >> 8) / inter[3]; + } + } + } + + switch (inter[3]) + { + case 0: + d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col); + break; + + case 255: + d[0] = inter[0]; + d[1] = inter[1]; + d[2] = inter[2]; + break; + + default: + { + register guint alpha = inter[3] + 1; + register guint check = CHECK_COLOR (area, row, col); + + d[0] = (((check << 8) + + (inter[0] - check) * alpha) >> 8); + d[1] = (((check << 8) + + (inter[1] - check) * alpha) >> 8); + d[2] = (((check << 8) + + (inter[2] - check) * alpha) >> 8); + } + break; + } + } + break; + } + } + + src1 += rowstride1; + src2 += rowstride2; + src_mask += rowstride_mask; + dest += area->rowstride; + } + break; + + case GIMP_GRAY_IMAGE: + for (row = 0; row < height; row++) + { + const guchar *s1 = src1; + const guchar *s2 = src2; + const guchar *m = src_mask; + guchar *d = dest; + + for (col = 0; col < width; col++, s1++, s2++, m++, d += 3) + d[0] = d[1] = d[2] = ((s1[0] << 8) + (s2[0] - s1[0]) * m[0]) >> 8; + + src1 += rowstride1; + src2 += rowstride2; + src_mask += rowstride_mask; + dest += area->rowstride; + } + break; + + case GIMP_GRAYA_IMAGE: + for (row = y; row < y + height; row++) + { + const guchar *s1 = src1; + const guchar *s2 = src2; + const guchar *m = src_mask; + guchar *d = dest; + + for (col = x; col < x + width; col++, s1 += 2, s2 += 2, m++, d+= 3) + { + switch (m[0]) + { + case 0: + switch (s1[1]) + { + case 0: + d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col); + break; + + case 255: + d[0] = d[1] = d[2] = s1[0]; + break; + + default: + { + register guint alpha = s1[1] + 1; + register guint check = CHECK_COLOR (area, row, col); + + d[0] = d[1] = d[2] = + ((check << 8) + (s1[0] - check) * alpha) >> 8; + } + break; + } + break; + + case 255: + switch (s2[1]) + { + case 0: + d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col); + break; + + case 255: + d[0] = d[1] = d[2] = s2[0]; + break; + + default: + { + register guint alpha = s2[1] + 1; + register guint check = CHECK_COLOR (area, row, col); + + d[0] = d[1] = d[2] = + ((check << 8) + (s2[0] - check) * alpha) >> 8; + } + break; + } + break; + + default: + { + guchar inter[2] = { 0, }; + + if (s1[1] == s2[1]) + { + inter[0] = ((s1[0] << 8) + (s2[0] - s1[0]) * m[0]) >> 8; + inter[1] = s1[1]; + } + else + { + inter[1] = ((s1[1] << 8) + (s2[1] - s1[1]) * m[0]) >> 8; + + if (inter[1]) + { + gushort a = s1[0] * s1[1]; + gushort b = s2[0] * s2[1]; + + inter[0] = + (((a << 8) + (b - a) * m[0]) >> 8) / inter[1]; + } + } + + switch (inter[1]) + { + case 0: + d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col); + break; + + case 255: + d[0] = d[1] = d[2] = inter[0]; + break; + + default: + { + register guint alpha = inter[1] + 1; + register guint check = CHECK_COLOR (area, row, col); + + d[0] = d[1] = d[2] = + ((check << 8) + (inter[0] - check) * alpha) >> 8; + } + break; + } + } + break; + } + } + + src1 += rowstride1; + src2 += rowstride2; + src_mask += rowstride_mask; + dest += area->rowstride; + } + break; + + case GIMP_INDEXED_IMAGE: + g_return_if_fail (area->colormap != NULL); + for (row = 0; row < height; row++) + { + const guchar *s1 = src1; + const guchar *s2 = src2; + const guchar *m = src_mask; + guchar *d = dest; + + for (col = 0; col < width; col++, s1++, s2++, m++, d += 3) + { + const guchar *cmap1 = area->colormap + 3 * s1[0]; + const guchar *cmap2 = area->colormap + 3 * s2[0]; + + d[0] = ((cmap1[0] << 8) + (cmap2[0] - cmap1[0]) * m[0]) >> 8; + d[1] = ((cmap1[1] << 8) + (cmap2[1] - cmap1[1]) * m[0]) >> 8; + d[2] = ((cmap1[2] << 8) + (cmap2[2] - cmap1[2]) * m[0]) >> 8; + } + + src1 += rowstride1; + src2 += rowstride2; + src_mask += rowstride_mask; + dest += area->rowstride; + } + break; + + case GIMP_INDEXEDA_IMAGE: + g_return_if_fail (area->colormap != NULL); + for (row = y; row < y + height; row++) + { + const guchar *s1 = src1; + const guchar *s2 = src2; + const guchar *m = src_mask; + guchar *d = dest; + + for (col = x; col < x + width; col++, s1 += 2, s2 += 2, m++, d += 3) + { + const guchar *cmap1 = area->colormap + 3 * s1[0]; + const guchar *cmap2 = area->colormap + 3 * s2[0]; + + switch (m[0]) + { + case 0: + switch (s1[1]) + { + case 0: + d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col); + break; + + case 255: + d[0] = cmap1[0]; + d[1] = cmap1[1]; + d[2] = cmap1[2]; + break; + + default: + { + register guint alpha = s1[1] + 1; + register guint check = CHECK_COLOR (area, row, col); + + d[0] = ((check << 8) + (cmap1[0] - check) * alpha) >> 8; + d[1] = ((check << 8) + (cmap1[1] - check) * alpha) >> 8; + d[2] = ((check << 8) + (cmap1[2] - check) * alpha) >> 8; + } + break; + } + break; + + case 255: + switch (s2[1]) + { + case 0: + d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col); + break; + + case 255: + d[0] = cmap2[0]; + d[1] = cmap2[1]; + d[2] = cmap2[2]; + break; + + default: + { + register guint alpha = s2[1] + 1; + register guint check = CHECK_COLOR (area, row, col); + + d[0] = ((check << 8) + (cmap2[0] - check) * alpha) >> 8; + d[1] = ((check << 8) + (cmap2[1] - check) * alpha) >> 8; + d[2] = ((check << 8) + (cmap2[2] - check) * alpha) >> 8; + } + break; + } + break; + + default: + { + guchar inter[4]; + + if (s1[1] == s2[1]) + { + inter[0] = (((cmap1[0] << 8) + + (cmap2[0] - cmap1[0]) * m[0]) >> 8); + inter[1] = (((cmap1[1] << 8) + + (cmap2[1] - cmap1[1]) * m[0]) >> 8); + inter[2] = (((cmap1[2] << 8) + + (cmap2[2] - cmap1[2]) * m[0]) >> 8); + inter[3] = s1[1]; + } + else + { + inter[3] = ((s1[1] << 8) + (s2[1] - s1[1]) * m[0]) >> 8; + + if (inter[3]) + { + for (i = 0 ; i < 3 ; i++) + { + gushort a = cmap1[i] * s1[1]; + gushort b = cmap2[i] * s2[1]; + + inter[i] = ((((a << 8) + (b - a) * m[0]) >> 8) + / inter[3]); + } + } + } + + switch (inter[3]) + { + case 0: + d[0] = d[1] = d[2] = CHECK_COLOR (area, row, col); + break; + + case 255: + d[0] = inter[0]; + d[1] = inter[1]; + d[2] = inter[2]; + break; + + default: + { + register guint alpha = inter[3] + 1; + register guint check = CHECK_COLOR (area, row, col); + + d[0] = + ((check << 8) + (inter[0] - check) * alpha) >> 8; + d[1] = + ((check << 8) + (inter[1] - check) * alpha) >> 8; + d[2] = + ((check << 8) + (inter[2] - check) * alpha) >> 8; + } + break; + } + } + break; + } + } + + src1 += rowstride1; + src2 += rowstride2; + src_mask += rowstride_mask; + dest += area->rowstride; + } + break; + } + + gimp_preview_area_queue_draw (area, x, y, width, height); +} + +/** + * gimp_preview_area_fill: + * @area: a #GimpPreviewArea widget. + * @x: x offset in preview + * @y: y offset in preview + * @width: width of the rectangle to fill + * @height: height of the rectangle to fill + * @red: red component of the fill color (0-255) + * @green: green component of the fill color (0-255) + * @blue: red component of the fill color (0-255) + * + * Fills the given rectangle of @area in the given color and queues a + * redraw. + * + * Since GIMP 2.2 + **/ +void +gimp_preview_area_fill (GimpPreviewArea *area, + gint x, + gint y, + gint width, + gint height, + guchar red, + guchar green, + guchar blue) +{ + guchar *dest; + guchar *d; + gint row; + gint col; + + g_return_if_fail (GIMP_IS_PREVIEW_AREA (area)); + g_return_if_fail (width >= 0 && height >= 0); + + if (width == 0 || height == 0) + return; + + if (x + width < 0 || x >= area->width) + return; + + if (y + height < 0 || y >= area->height) + return; + + if (x < 0) + { + width += x; + x = 0; + } + + if (x + width > area->width) + width = area->width - x; + + if (y < 0) + { + height += y; + y = 0; + } + + if (y + height > area->height) + height = area->height - y; + + if (! area->buf) + { + area->rowstride = ((area->width * 3) + 3) & ~3; + area->buf = g_new (guchar, area->rowstride * area->height); + } + + dest = area->buf + x * 3 + y * area->rowstride; + + /* colorize first row */ + for (col = 0, d = dest; col < width; col++, d+= 3) + { + d[0] = red; + d[1] = green; + d[2] = blue; + } + + /* copy first row to remaining rows */ + for (row = 1, d = dest; row < height; row++) + { + d += area->rowstride; + memcpy (d, dest, width * 3); + } + + gimp_preview_area_queue_draw (area, x, y, width, height); +} + +/** + * gimp_preview_area_set_offsets: + * @area: a #GimpPreviewArea + * @x: horizontal offset + * @y: vertical offset + * + * Sets the offsets of the previewed area. This information is used + * when drawing the checkerboard and to determine the dither offsets. + * + * Since: 2.2 + **/ +void +gimp_preview_area_set_offsets (GimpPreviewArea *area, + gint x, + gint y) +{ + g_return_if_fail (GIMP_IS_PREVIEW_AREA (area)); + + area->offset_x = x; + area->offset_y = y; +} + +/** + * gimp_preview_area_set_colormap: + * @area: a #GimpPreviewArea + * @colormap: a #guchar buffer that contains the colormap + * @num_colors: the number of colors in the colormap + * + * Sets the colormap for the #GimpPreviewArea widget. You need to + * call this function before you use gimp_preview_area_draw() with + * an image type of %GIMP_INDEXED_IMAGE or %GIMP_INDEXEDA_IMAGE. + * + * Since GIMP 2.2 + **/ +void +gimp_preview_area_set_colormap (GimpPreviewArea *area, + const guchar *colormap, + gint num_colors) +{ + g_return_if_fail (GIMP_IS_PREVIEW_AREA (area)); + g_return_if_fail (colormap != NULL || num_colors == 0); + g_return_if_fail (num_colors >= 0 && num_colors <= 256); + + if (num_colors > 0) + { + if (area->colormap) + memset (area->colormap, 0, 3 * 256); + else + area->colormap = g_new0 (guchar, 3 * 256); + + memcpy (area->colormap, colormap, 3 * num_colors); + } + else + { + g_free (area->colormap); + area->colormap = NULL; + } +} + +/** + * gimp_preview_area_set_color_config: + * @area: a #GimpPreviewArea widget. + * @config: a #GimpColorConfig object. + * + * Sets the color management configuration to use with this preview area. + * + * Since: 2.10 + */ +void +gimp_preview_area_set_color_config (GimpPreviewArea *area, + GimpColorConfig *config) +{ + GimpPreviewAreaPrivate *priv; + + g_return_if_fail (GIMP_IS_PREVIEW_AREA (area)); + g_return_if_fail (config == NULL || GIMP_IS_COLOR_CONFIG (config)); + + priv = GET_PRIVATE (area); + + if (config != priv->config) + { + if (priv->config) + { + g_signal_handlers_disconnect_by_func (priv->config, + gimp_preview_area_destroy_transform, + area); + + gimp_preview_area_destroy_transform (area); + } + + g_set_object (&priv->config, config); + + if (priv->config) + { + g_signal_connect_swapped (priv->config, "notify", + G_CALLBACK (gimp_preview_area_destroy_transform), + area); + } + } +} + +/** + * gimp_preview_area_set_max_size: + * @area: a #GimpPreviewArea widget + * @width: the maximum width in pixels or -1 to unset the limit + * @height: the maximum height in pixels or -1 to unset the limit + * + * Usually a #GimpPreviewArea fills the size that it is + * allocated. This function allows you to limit the preview area to a + * maximum size. If a larger size is allocated for the widget, the + * preview will draw itself centered into the allocated area. + * + * Since: 2.2 + **/ +void +gimp_preview_area_set_max_size (GimpPreviewArea *area, + gint width, + gint height) +{ + g_return_if_fail (GIMP_IS_PREVIEW_AREA (area)); + + area->max_width = width; + area->max_height = height; +} + + + +/* popup menu */ + +static void +gimp_preview_area_menu_toggled (GtkWidget *item, + GimpPreviewArea *area) +{ + gboolean active = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item)); + + if (active) + { + const gchar *name = g_object_get_data (G_OBJECT (item), + "gimp-preview-area-prop-name"); + if (name) + { + gint value = + GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), + "gimp-preview-area-prop-value")); + g_object_set (area, + name, value, + NULL); + } + } +} + +static GtkWidget * +gimp_preview_area_menu_new (GimpPreviewArea *area, + const gchar *property) +{ + GParamSpec *pspec; + GEnumClass *enum_class; + GEnumValue *enum_value; + GtkWidget *menu; + GtkWidget *item; + GSList *group = NULL; + gint value; + + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (area), property); + + g_return_val_if_fail (G_IS_PARAM_SPEC_ENUM (pspec), NULL); + + g_object_get (area, + property, &value, + NULL); + + enum_class = G_PARAM_SPEC_ENUM (pspec)->enum_class; + + menu = gtk_menu_new (); + + for (enum_value = enum_class->values; enum_value->value_name; enum_value++) + { + const gchar *name = gimp_enum_value_get_desc (enum_class, enum_value); + + item = gtk_radio_menu_item_new_with_label (group, name); + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show (item); + + g_object_set_data (G_OBJECT (item), + "gimp-preview-area-prop-name", + (gpointer) property); + + g_object_set_data (G_OBJECT (item), + "gimp-preview-area-prop-value", + GINT_TO_POINTER (enum_value->value)); + + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), + (enum_value->value == value)); + + g_signal_connect (item, "toggled", + G_CALLBACK (gimp_preview_area_menu_toggled), + area); + + group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (item)); + } + + item = gtk_menu_item_new_with_label (g_param_spec_get_nick (pspec)); + + gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu); + + gtk_widget_show (item); + + return item; +} + +/** + * gimp_preview_area_menu_popup: + * @area: a #GimpPreviewArea + * @event: the button event that causes the menu to popup or %NULL + * + * Creates a popup menu that allows one to configure the size and type of + * the checkerboard pattern that the @area uses to visualize transparency. + * + * Since: 2.2 + **/ +void +gimp_preview_area_menu_popup (GimpPreviewArea *area, + GdkEventButton *event) +{ + GtkWidget *menu; + + g_return_if_fail (GIMP_IS_PREVIEW_AREA (area)); + + menu = gtk_menu_new (); + gtk_menu_set_screen (GTK_MENU (menu), + gtk_widget_get_screen (GTK_WIDGET (area))); + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), + gimp_preview_area_menu_new (area, "check-type")); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), + gimp_preview_area_menu_new (area, "check-size")); + + if (event) + gtk_menu_popup (GTK_MENU (menu), + NULL, NULL, NULL, NULL, event->button, event->time); + else + gtk_menu_popup (GTK_MENU (menu), + NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time ()); +} |