Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Gimp/libgimpwidgets/   (Gimp Bildeditor Version 3.2.0©)  Datei vom 14.2.2026 mit Größe 66 kB image not shown  

Quelle  gimppreviewarea.c   Sprache: C

 

/* 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,
  PROP_CHECK_CUSTOM_COLOR1,
  PROP_CHECK_CUSTOM_COLOR2
};


#define DEFAULT_CHECK_SIZE  GIMP_CHECK_SIZE_MEDIUM_CHECKS
#define DEFAULT_CHECK_TYPE  GIMP_CHECK_TYPE_GRAY_CHECKS

#define CHECK_R(are, row, col)        \
  (((((area)->offset_y + (row)) & size) ^  \
    (((area)->offset_x + (col)) & size)) ? rgb1[0] : rgb2[0])

#define CHECK_G(area, row, col)        \
  (((((area)->offset_y + (row)) & size) ^  \
    (((area)->offset_x + (col)) & size)) ? rgb1[1] : rgb2[1])

#define CHECK_B(area, row, col)        \
  (((((area)->offset_y + (row)) & size) ^  \
    (((area)->offset_x + (col)) & size)) ? rgb1[2] : rgb2[2])


struct _GimpPreviewArea
{
  GtkDrawingArea      parent_instance;

  GimpCheckSize       check_size;
  GimpCheckType       check_type;
  GeglColor          *check_custom_color1;
  GeglColor          *check_custom_color2;
  gint                width;
  gint                height;
  gint                rowstride;
  gint                offset_x;
  gint                offset_y;
  gint                max_width;
  gint                max_height;
  guchar             *buf;
  guchar             *colormap;

  GimpColorConfig    *config;
  GimpColorTransform *transform;
};


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_widget_draw       (GtkWidget        *widget,
                                                      cairo_t          *cr);

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 (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);
  GeglColor      *color1_default;
  GeglColor      *color2_default;

  color1_default = gegl_color_new (NULL);
  gegl_color_set_pixel (color1_default, babl_format ("R'G'B'A double"), GIMP_CHECKS_CUSTOM_COLOR1);
  color2_default = gegl_color_new (NULL);
  gegl_color_set_pixel (color2_default, babl_format ("R'G'B'A double"), GIMP_CHECKS_CUSTOM_COLOR2);

  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->draw          = gimp_preview_area_widget_draw;

  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));

  g_object_class_install_property (object_class, PROP_CHECK_CUSTOM_COLOR1,
                                   gimp_param_spec_color ("check-custom-color1",
                                                          _("Custom Checks Color 1"),
                                                          "The first color of the checkerboard pattern indicating transparency",
                                                          FALSE, color1_default,
                                                          GIMP_PARAM_READWRITE));

  g_object_class_install_property (object_class, PROP_CHECK_CUSTOM_COLOR2,
                                   gimp_param_spec_color ("check-custom-color2",
                                                          _("Custom Checks Color 2"),
                                                          "The second color of the checkerboard pattern indicating transparency",
                                                          FALSE, color2_default,
                                                          GIMP_PARAM_READWRITE));

  g_object_unref (color1_default);
  g_object_unref (color2_default);
}

static void
gimp_preview_area_init (GimpPreviewArea *area)
{
  area->check_size          = DEFAULT_CHECK_SIZE;
  area->check_type          = DEFAULT_CHECK_TYPE;
  area->check_custom_color1 = gegl_color_new (NULL);
  gegl_color_set_pixel (area->check_custom_color1, babl_format ("R'G'B'A double"), GIMP_CHECKS_CUSTOM_COLOR1);
  area->check_custom_color2 = gegl_color_new (NULL);
  gegl_color_set_pixel (area->check_custom_color2, babl_format ("R'G'B'A double"), GIMP_CHECKS_CUSTOM_COLOR2);
  area->max_width           = -1;
  area->max_height          = -1;

  gimp_widget_track_monitor (GTK_WIDGET (area),
                             G_CALLBACK (gimp_preview_area_destroy_transform),
                             NULL, 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_clear_object (&area->check_custom_color1);
  g_clear_object (&area->check_custom_color2);

  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;
    case PROP_CHECK_CUSTOM_COLOR1:
      g_clear_object (&area->check_custom_color1);
      area->check_custom_color1 = gegl_color_duplicate (g_value_get_object (value));
      break;
    case PROP_CHECK_CUSTOM_COLOR2:
      g_clear_object (&area->check_custom_color2);
      area->check_custom_color2 = gegl_color_duplicate (g_value_get_object (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;
    case PROP_CHECK_CUSTOM_COLOR1:
      g_value_set_object (value, area->check_custom_color1);
      break;
    case PROP_CHECK_CUSTOM_COLOR2:
      g_value_set_object (value, area->check_custom_color2);
      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_widget_draw (GtkWidget *widget,
                               cairo_t   *cr)
{
  GimpPreviewArea *area = GIMP_PREVIEW_AREA (widget);
  GtkAllocation    allocation;
  GdkPixbuf       *pixbuf;
  GdkRectangle     rect;

  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 (! area->transform)
    gimp_preview_area_create_transform (area);

  if (area->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 (area->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);
    }

  gdk_cairo_set_source_pixbuf (cr, pixbuf, rect.x, rect.y);
  cairo_paint (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)
{
  if (area->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 ();

      area->transform = gimp_widget_get_color_transform (GTK_WIDGET (area),
                                                         area->config,
                                                         profile,
                                                         format,
                                                         format,
                                                         NULL,
                                                         GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC,
                                                         FALSE);
    }
}

static void
gimp_preview_area_destroy_transform (GimpPreviewArea *area)
{
  if (area->transform)
    {
      g_object_unref (area->transform);
      area->transform = NULL;
    }

  gtk_widget_queue_draw (GTK_WIDGET (area));
}


/**
 * gimp_preview_area_new:
 *
 * Creates a new #GimpPreviewArea widget.
 *
 * If the preview area is used to draw an image with transparency, you
 * might want to default the checkboard size and colors to user-set
 * Preferences. To do this, you may set the following properties on the
 * newly created #GimpPreviewArea:
 *
 * |[<!-- language="C" -->
 * g_object_set (area,
 *               "check-size",          gimp_check_size (),
 *               "check-type",          gimp_check_type (),
 *               "check-custom-color1", gimp_check_custom_color1 (),
 *               "check-custom-color2", gimp_check_custom_color2 (),
 *               NULL);
 * ]|
 *
 * Returns: 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: (array): 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;
  GeglColor    *color1;
  GeglColor    *color2;
  guchar        rgb1[3];
  guchar        rgb2[3];
  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_new0 (guchar, area->rowstride * area->height);
    }

  size = 1 << (2 + area->check_size);
  color1 = area->check_custom_color1;
  color2 = area->check_custom_color2;
  gimp_checks_get_colors (area->check_type, &color1, &color2);
  gegl_color_get_pixel (color1, babl_format ("R'G'B' u8"), rgb1);
  gegl_color_get_pixel (color2, babl_format ("R'G'B' u8"), rgb2);
  g_object_unref (color1);
  g_object_unref (color2);

  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] = CHECK_R (area, row, col);
                  d[1] = CHECK_G (area, row, col);
                  d[2] = CHECK_B (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_r = CHECK_R (area, row, col);
                    register guint check_g = CHECK_G (area, row, col);
                    register guint check_b = CHECK_B (area, row, col);

                    d[0] = ((check_r << 8) + (s[0] - check_r) * alpha) >> 8;
                    d[1] = ((check_g << 8) + (s[1] - check_g) * alpha) >> 8;
                    d[2] = ((check_b << 8) + (s[2] - check_b) * 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] = CHECK_R (area, row, col);
                  d[1] = CHECK_G (area, row, col);
                  d[2] = CHECK_B (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_r = CHECK_R (area, row, col);
                    register guint check_g = CHECK_G (area, row, col);
                    register guint check_b = CHECK_B (area, row, col);

                    d[0] = ((check_r << 8) + (s[0] - check_r) * alpha) >> 8;
                    d[1] = ((check_g << 8) + (s[0] - check_g) * alpha) >> 8;
                    d[2] = ((check_b << 8) + (s[0] - check_b) * 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] = CHECK_R (area, row, col);
                  d[1] = CHECK_G (area, row, col);
                  d[2] = CHECK_B (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_r = CHECK_R (area, row, col);
                    register guint check_g = CHECK_G (area, row, col);
                    register guint check_b = CHECK_B (area, row, col);

                    d[0] = ((check_r << 8) + (colormap[0] - check_r) * alpha) >> 8;
                    d[1] = ((check_g << 8) + (colormap[1] - check_g) * alpha) >> 8;
                    d[2] = ((check_b << 8) + (colormap[2] - check_b) * 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: (array): a #guchar buffer that contains the pixel data for
 *                 the lower layer
 * @rowstride1: rowstride of @buf1
 * @buf2: (array): 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;
  GeglColor    *color1;
  GeglColor    *color2;
  guchar        rgb1[3];
  guchar        rgb2[3];
  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_new0 (guchar, area->rowstride * area->height);
    }

  size = 1 << (2 + area->check_size);
  color1 = area->check_custom_color1;
  color2 = area->check_custom_color2;
  gimp_checks_get_colors (area->check_type, &color1, &color2);
  gegl_color_get_pixel (color1, babl_format ("R'G'B' u8"), rgb1);
  gegl_color_get_pixel (color2, babl_format ("R'G'B' u8"), rgb2);
  g_object_unref (color1);
  g_object_unref (color2);

  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] = CHECK_R (area, row, col);
                  d[1] = CHECK_G (area, row, col);
                  d[2] = CHECK_B (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_r = CHECK_R (area, row, col);
                    register guint check_g = CHECK_G (area, row, col);
                    register guint check_b = CHECK_B (area, row, col);

                    d[0] = ((check_r << 8) + (inter[0] - check_r) * alpha) >> 8;
                    d[1] = ((check_g << 8) + (inter[1] - check_g) * alpha) >> 8;
                    d[2] = ((check_b << 8) + (inter[2] - check_b) * 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] = CHECK_R (area, row, col);
                  d[1] = CHECK_G (area, row, col);
                  d[2] = CHECK_B (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_r = CHECK_R (area, row, col);
                    register guint check_g = CHECK_G (area, row, col);
                    register guint check_b = CHECK_B (area, row, col);

                    d[0] = ((check_r << 8) + (inter[0] - check_r) * alpha) >> 8;
                    d[1] = ((check_g << 8) + (inter[0] - check_g) * alpha) >> 8;
                    d[2] = ((check_b << 8) + (inter[0] - check_b) * 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] = CHECK_R (area, row, col);
                  d[1] = CHECK_G (area, row, col);
                  d[2] = CHECK_B (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_r = CHECK_R (area, row, col);
                    register guint check_g = CHECK_G (area, row, col);
                    register guint check_b = CHECK_B (area, row, col);

                    d[0] = ((check_r << 8) + (inter[0] - check_r) * alpha) >> 8;
                    d[1] = ((check_g << 8) + (inter[1] - check_g) * alpha) >> 8;
                    d[2] = ((check_b << 8) + (inter[2] - check_b) * 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: (array):  a #guchar buffer that contains the pixel data for
 *                  the lower layer
 * @rowstride1:     rowstride of @buf1
 * @buf2: (array):  a #guchar buffer that contains the pixel data for
 *                  the upper layer
 * @rowstride2:     rowstride of @buf2
 * @mask: (array):  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;
  GeglColor    *color1;
  GeglColor    *color2;
  guchar        rgb1[3];
  guchar        rgb2[3];
  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_new0 (guchar, area->rowstride * area->height);
    }

  size = 1 << (2 + area->check_size);
  color1 = area->check_custom_color1;
  color2 = area->check_custom_color2;
  gimp_checks_get_colors (area->check_type, &color1, &color2);
  gegl_color_get_pixel (color1, babl_format ("R'G'B' u8"), rgb1);
  gegl_color_get_pixel (color2, babl_format ("R'G'B' u8"), rgb2);
  g_object_unref (color1);
  g_object_unref (color2);

  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] = CHECK_R (area, row, col);
                      d[1] = CHECK_G (area, row, col);
                      d[2] = CHECK_B (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_r = CHECK_R (area, row, col);
                        register guint check_g = CHECK_G (area, row, col);
                        register guint check_b = CHECK_B (area, row, col);

                        d[0] = ((check_r << 8) + (s1[0] - check_r) * alpha) >> 8;
                        d[1] = ((check_g << 8) + (s1[1] - check_g) * alpha) >> 8;
                        d[2] = ((check_b << 8) + (s1[2] - check_b) * alpha) >> 8;
                      }
                      break;
                    }
                  break;

                case 255:
                  switch (s2[3])
                    {
                    case 0:
                      d[0] = CHECK_R (area, row, col);
                      d[1] = CHECK_G (area, row, col);
                      d[2] = CHECK_B (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_r = CHECK_R (area, row, col);
                        register guint check_g = CHECK_G (area, row, col);
                        register guint check_b = CHECK_B (area, row, col);

                        d[0] = ((check_r << 8) + (s2[0] - check_r) * alpha) >> 8;
                        d[1] = ((check_g << 8) + (s2[1] - check_g) * alpha) >> 8;
                        d[2] = ((check_b << 8) + (s2[2] - check_b) * 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] = CHECK_R (area, row, col);
                        d[1] = CHECK_G (area, row, col);
                        d[2] = CHECK_B (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_r = CHECK_R (area, row, col);
                          register guint check_g = CHECK_G (area, row, col);
                          register guint check_b = CHECK_B (area, row, col);

                          d[0] = (((check_r << 8) +
                                   (inter[0] - check_r) * alpha) >> 8);
                          d[1] = (((check_g << 8) +
                                   (inter[1] - check_g) * alpha) >> 8);
                          d[2] = (((check_b << 8) +
                                   (inter[2] - check_b) * 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] = CHECK_R (area, row, col);
                      d[1] = CHECK_G (area, row, col);
                      d[2] = CHECK_B (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_r = CHECK_R (area, row, col);
                        register guint check_g = CHECK_G (area, row, col);
                        register guint check_b = CHECK_B (area, row, col);

                        d[0] = ((check_r << 8) + (s1[0] - check_r) * alpha) >> 8;
                        d[1] = ((check_g << 8) + (s1[0] - check_g) * alpha) >> 8;
                        d[2] = ((check_b << 8) + (s1[0] - check_b) * alpha) >> 8;
                      }
                      break;
                    }
                  break;

                case 255:
                  switch (s2[1])
                    {
                    case 0:
                      d[0] = CHECK_R (area, row, col);
                      d[1] = CHECK_G (area, row, col);
                      d[2] = CHECK_B (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_r = CHECK_R (area, row, col);
                        register guint check_g = CHECK_G (area, row, col);
                        register guint check_b = CHECK_B (area, row, col);

                        d[0] = ((check_r << 8) + (s2[0] - check_r) * alpha) >> 8;
                        d[1] = ((check_g << 8) + (s2[0] - check_g) * alpha) >> 8;
                        d[2] = ((check_b << 8) + (s2[0] - check_b) * 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] = CHECK_R (area, row, col);
                        d[1] = CHECK_G (area, row, col);
                        d[2] = CHECK_B (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_r = CHECK_R (area, row, col);
                          register guint check_g = CHECK_G (area, row, col);
                          register guint check_b = CHECK_B (area, row, col);

                          d[0] = ((check_r << 8) + (inter[0] - check_r) * alpha) >> 8;
                          d[1] = ((check_g << 8) + (inter[0] - check_g) * alpha) >> 8;
                          d[2] = ((check_b << 8) + (inter[0] - check_b) * 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] = CHECK_R (area, row, col);
                      d[1] = CHECK_G (area, row, col);
                      d[2] = CHECK_B (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_r = CHECK_R (area, row, col);
                        register guint check_g = CHECK_G (area, row, col);
                        register guint check_b = CHECK_B (area, row, col);

                        d[0] = ((check_r << 8) + (cmap1[0] - check_r) * alpha) >> 8;
                        d[1] = ((check_g << 8) + (cmap1[1] - check_g) * alpha) >> 8;
                        d[2] = ((check_b << 8) + (cmap1[2] - check_b) * alpha) >> 8;
                      }
                      break;
                    }
                  break;

                case 255:
                  switch (s2[1])
                    {
                    case 0:
                      d[0] = CHECK_R (area, row, col);
                      d[1] = CHECK_G (area, row, col);
                      d[2] = CHECK_B (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_r = CHECK_R (area, row, col);
                        register guint check_g = CHECK_G (area, row, col);
                        register guint check_b = CHECK_B (area, row, col);

                        d[0] = ((check_r << 8) + (cmap2[0] - check_r) * alpha) >> 8;
                        d[1] = ((check_g << 8) + (cmap2[1] - check_g) * alpha) >> 8;
                        d[2] = ((check_b << 8) + (cmap2[2] - check_b) * 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] = CHECK_R (area, row, col);
                        d[1] = CHECK_G (area, row, col);
                        d[2] = CHECK_B (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_r = CHECK_R (area, row, col);
                          register guint check_g = CHECK_G (area, row, col);
                          register guint check_b = CHECK_B (area, row, col);

                          d[0] =
                            ((check_r << 8) + (inter[0] - check_r) * alpha) >> 8;
                          d[1] =
                            ((check_g << 8) + (inter[1] - check_g) * alpha) >> 8;
                          d[2] =
                            ((check_b << 8) + (inter[2] - check_b) * 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_new0 (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_reset:
 * @area:   a #GimpPreviewArea widget.
 *
 * Reset any previous drawing done through [class@GimpUi.PreviewArea] functions.
 *
 * Since: 3.0
 **/

void
gimp_preview_area_reset (GimpPreviewArea *area)
{
  GtkAllocation allocation;

  if (area->buf)
    {
      g_free (area->buf);

      area->buf       = NULL;
      area->rowstride = 0;
    }

  gtk_widget_get_allocation (GTK_WIDGET (area), &allocation);
  gimp_preview_area_queue_draw (area, 0, 0, allocation.width, allocation.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: (array): 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)
{
  g_return_if_fail (GIMP_IS_PREVIEW_AREA (area));
  g_return_if_fail (config == NULL || GIMP_IS_COLOR_CONFIG (config));

  if (config != area->config)
    {
      if (area->config)
        {
          g_signal_handlers_disconnect_by_func (area->config,
                                                gimp_preview_area_destroy_transform,
                                                area);

          gimp_preview_area_destroy_transform (area);
        }

      g_set_object (&area->config, config);

      if (area->config)
        {
          g_signal_connect_swapped (area->config, "notify",
                                    G_CALLBACK (gimp_preview_area_destroy_transform),
                                    area);
        }
    }
}

/**
 * gimp_preview_area_get_size:
 * @area:   a #GimpPreviewArea widget.
 * @width: (out): The preview areay width
 * @height: (out): The preview areay height
 *
 * Gets the preview area size
 */

void
gimp_preview_area_get_size (GimpPreviewArea *area,
                            gint            *width,
                            gint            *height)
{
  g_return_if_fail (GIMP_IS_PREVIEW_AREA (area));

  if (width)  *width  = area->width;
  if (height) *height = area->height;
}

/**
 * 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: (nullable): 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 0
  /* gimp_preview_area_menu_new() currently only handles enum types, and
   * in particular not color properties.
   */

  gtk_menu_shell_append (GTK_MENU_SHELL (menu),
                         gimp_preview_area_menu_new (area, "check-custom-color1"));
  gtk_menu_shell_append (GTK_MENU_SHELL (menu),
                         gimp_preview_area_menu_new (area, "check-custom-color2"));
#endif

  gtk_menu_popup_at_pointer (GTK_MENU (menu), (GdkEvent *) event);
}

Messung
CHZG
84969993

¤ Dauer der Verarbeitung: 0.30 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.