[gthumb] xcf loader: simplified the code removing the use of cairo to draw layers



commit f071f7d250bede84656db5a7046d41407fe42c5e
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Sat Mar 16 17:38:58 2013 +0100

    xcf loader: simplified the code removing the use of cairo to draw layers
    
    implemented all the layer modes instead of using cairo operations
    for some modes, this allows to remove some time-consuming functions
    such as gimp_layer_apply_mask and _cairo_surface_data_post_production,
    and allows to avoid some alpha channel multiplications.

 extensions/cairo_io/cairo-image-surface-xcf.c | 1073 ++++++++++++-------------
 1 files changed, 493 insertions(+), 580 deletions(-)
---
diff --git a/extensions/cairo_io/cairo-image-surface-xcf.c b/extensions/cairo_io/cairo-image-surface-xcf.c
index 77f80b3..1038a7d 100644
--- a/extensions/cairo_io/cairo-image-surface-xcf.c
+++ b/extensions/cairo_io/cairo-image-surface-xcf.c
@@ -26,18 +26,38 @@
 #include "cairo-image-surface-xcf.h"
 
 
-#define TILE_WIDTH                             64
-#define MAX_TILE_SIZE                          (TILE_WIDTH * TILE_WIDTH * 4 * 1.5)
-#define MIN3(x,y,z)                            ((y) <= (z) ? MIN ((x), (y)) : MIN ((x), (z)))
-#define MAX3(x,y,z)                            ((y) >= (z) ? MAX ((x), (y)) : MAX ((x), (z)))
-#define DISSOLVE_SEED                          737893334
-#define CLAMP_TEMP(x, min, max)                 (temp = (x), CLAMP (temp, min, max))
-#define CLAMP_PIXEL(x)                         CLAMP_TEMP(x, 0, 255)
-#define GIMP_OP_NORMAL(xaA, xaB, aA)           (CLAMP_PIXEL (xaA + (double) xaB * (1.0 - aA)))
-#define GIMP_OP_SUBTRACT(xaA, xaB)             (CLAMP_PIXEL (xaB - xaA))
-#define GIMP_OP_DIVIDE(xaA, xaB, aA)           (CLAMP_PIXEL ((xaA > 0) ? (double) (xaB / xaA) * aA * 255.0 : 
xaB))
-#define GIMP_OP_GRAIN_EXTRACT(xaA, xaB, aA)    (CLAMP_PIXEL (xaB - xaA + (127.0 * aA)))
-#define GIMP_OP_GRAIN_MERGE(xaA, xaB, aA)      (CLAMP_PIXEL (xaB + xaA - (127.0 * aA)))
+/* Optimizations taken from xcftools 1.0.7 written by Henning Makholm
+ *
+ * xL : Layer color
+ * xI : Image color
+ * aL : Layer alpha
+ * */
+
+
+#define TILE_WIDTH                     64
+#define MAX_TILE_SIZE                  (TILE_WIDTH * TILE_WIDTH * 4 * 1.5)
+#define ADD_ALPHA(v, a)                        (add_alpha_table[v][a])
+#define MIN3(x,y,z)                    ((y) <= (z) ? MIN ((x), (y)) : MIN ((x), (z)))
+#define MAX3(x,y,z)                    ((y) >= (z) ? MAX ((x), (y)) : MAX ((x), (z)))
+#define DISSOLVE_SEED                  737893334
+#define CLAMP_TEMP(x, min, max)                (temp = (x), CLAMP (temp, min, max))
+#define ABS_TEMP2(x)                   (temp2 = (x), (temp2 < 0) ? -temp2: temp2)
+#define CLAMP_PIXEL(x)                 CLAMP_TEMP (x, 0, 255)
+#define GIMP_OP_NORMAL(xL, xI, aL)     CLAMP_PIXEL (ADD_ALPHA (xL, aL) + ADD_ALPHA (xI, 255 - aL))
+#define GIMP_OP_LIGHTEN_ONLY(xL, xI)   MAX (xI, xL)
+#define GIMP_OP_SCREEN(xL, xI)         CLAMP_PIXEL (255 ^ ADD_ALPHA (255 - xI, 255 - xL))
+#define GIMP_OP_DODGE(xL, xI)          GIMP_OP_DIVIDE (255-xL, xI)
+#define GIMP_OP_ADDITION(xL, xI)       CLAMP_PIXEL (xI + xL)
+#define GIMP_OP_DARKEN_ONLY(xL, xI)    MIN (xI, xL)
+#define GIMP_OP_MULTIPLY(xL, xI)       CLAMP_PIXEL (ADD_ALPHA (xL, xI))
+#define GIMP_OP_BURN(xL, xI)           CLAMP_PIXEL (255 - GIMP_OP_DIVIDE (xL, 255 - xI))
+#define GIMP_OP_SOFT_LIGHT(xL, xI)     CLAMP_PIXEL (ADD_ALPHA (xI, xI) + 2 * ADD_ALPHA (xL, ADD_ALPHA (xI, 
255 - xI)))
+#define GIMP_OP_HARD_LIGHT(xL, xI)     CLAMP_PIXEL (xL > 128 ? 255 ^ ADD_ALPHA (255 - xI, 2 * (255 - xL)) : 
ADD_ALPHA (xI, 2 * xL))
+#define GIMP_OP_DIFFERENCE(xL, xI)     CLAMP_PIXEL (ABS_TEMP2 (xI - xL))
+#define GIMP_OP_SUBTRACT(xL, xI)       CLAMP_PIXEL (xI - xL)
+#define GIMP_OP_GRAIN_EXTRACT(xL, xI)  CLAMP_PIXEL ((int) xI - xL + 128)
+#define GIMP_OP_GRAIN_MERGE(xL, xI)    CLAMP_PIXEL ((int) xI + xL - 128)
+#define GIMP_OP_DIVIDE(xL, xI)         CLAMP_PIXEL ((int) (xI) * 256 / (1 + (xL)))
 
 
 typedef enum {
@@ -92,28 +112,30 @@ typedef enum {
 
 
 typedef struct {
-       guint            width;
-       guint            height;
-       GimpImageType    type;
-       char            *name;
-       guint32          opacity;
-       gboolean         visible;
-       gboolean         floating_selection;
-       GimpLayerMode    mode;
-       gboolean         apply_mask;
-       gint32           h_offset;
-       gint32           v_offset;
-       cairo_surface_t *image;
-       cairo_surface_t *mask;
+       int             n;
+       guint           width;
+       guint           height;
+       GimpImageType   type;
+       char           *name;
+       guint32         opacity;
+       gboolean        visible;
+       gboolean        floating_selection;
+       GimpLayerMode   mode;
+       gboolean        apply_mask;
+       gint32          h_offset;
+       gint32          v_offset;
+       int             stride;
+       guchar         *pixels;
+       guchar         *alpha_mask;
+       int             bpp;
        struct {
-               gboolean dirty;
-               int      rows;
-               int      columns;
-               int      n_tiles;
-               int      last_row_height;
-               int      last_col_width;
+               gboolean   dirty;
+               int        rows;
+               int        columns;
+               int        n_tiles;
+               int        last_row_height;
+               int        last_col_width;
        } tiles;
-       int              stride;
 } GimpLayer;
 
 
@@ -124,9 +146,34 @@ typedef struct {
 } GimpColormap;
 
 
-static int cairo_rgba[4]  =   { CAIRO_RED, CAIRO_GREEN, CAIRO_BLUE, CAIRO_ALPHA };
-static int cairo_graya[2] =   { CAIRO_RED, CAIRO_ALPHA };
+static int cairo_rgba[4]  = { CAIRO_RED, CAIRO_GREEN, CAIRO_BLUE, CAIRO_ALPHA };
+static int cairo_graya[2] = { 0, CAIRO_ALPHA };
 static int cairo_indexed[2] = { 0, CAIRO_ALPHA };
+static guchar add_alpha_table[256][256];
+static GOnce  xcf_init_once = G_ONCE_INIT;
+
+
+static gpointer
+xcf_init (gpointer data)
+{
+       int v;
+       int a;
+       int r;
+
+       /* add_alpha_table[v][a] = v * a / 255 */
+
+       for (v = 0; v < 128; v++) {
+               for (a = 0; a <= v; a++) {
+                       r = (v * a + 127) / 255;
+                       add_alpha_table[v][a] = add_alpha_table[a][v] = r;
+                       add_alpha_table[255-v][a] = add_alpha_table[a][255-v] = a - r;
+                       add_alpha_table[v][255-a] = add_alpha_table[255-a][v] = v - r;
+                       add_alpha_table[255-v][255-a] = add_alpha_table[255-a][255-v] = (255 - a) - (v - r);
+               }
+       }
+
+       return NULL;
+}
 
 
 /* -- GDataInputStream functions -- */
@@ -169,11 +216,12 @@ _g_data_input_stream_read_xcf_string (GDataInputStream  *stream,
 
 
 static GimpLayer *
-gimp_layer_new (void)
+gimp_layer_new (int n)
 {
        GimpLayer *layer;
 
        layer = g_new0 (GimpLayer, 1);
+       layer->n = n;
        layer->width = 0;
        layer->height = 0;
        layer->type = GIMP_RGBA_IMAGE;
@@ -185,9 +233,10 @@ gimp_layer_new (void)
        layer->apply_mask = FALSE;
        layer->h_offset = 0;
        layer->v_offset = 0;
-       layer->image = NULL;
-       layer->mask = NULL;
        layer->tiles.dirty = TRUE;
+       layer->tiles.n_tiles = 0;
+       layer->pixels = NULL;
+       layer->alpha_mask = NULL;
 
        return layer;
 }
@@ -196,6 +245,7 @@ gimp_layer_new (void)
 static gboolean
 gimp_layer_get_tile_size (GimpLayer *layer,
                          int        n_tile,
+                         int        bpp,
                          goffset   *offset,
                          int       *width,
                          int       *height)
@@ -222,7 +272,7 @@ gimp_layer_get_tile_size (GimpLayer *layer,
 
                layer->tiles.n_tiles = layer->tiles.columns * layer->tiles.rows;
                layer->tiles.dirty = FALSE;
-               layer->stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, layer->width);
+               layer->stride = layer->width * bpp;
        }
 
        if ((n_tile < 0) || (n_tile >= layer->tiles.n_tiles))
@@ -240,7 +290,7 @@ gimp_layer_get_tile_size (GimpLayer *layer,
        else
                tile_height = TILE_WIDTH;
 
-       *offset = ((tile_row * TILE_WIDTH) * layer->stride) + (tile_column * TILE_WIDTH * 4);
+       *offset = ((tile_row * TILE_WIDTH) * layer->stride) + (tile_column * TILE_WIDTH * bpp);
        *width = tile_width;
        *height = tile_height;
 
@@ -249,62 +299,12 @@ gimp_layer_get_tile_size (GimpLayer *layer,
 
 
 static void
-gimp_layer_apply_mask (GimpLayer *layer)
-{
-       int     width;
-       int     height;
-       int     row_stride;
-       guchar *image_row;
-       guchar *mask_row;
-       guchar *image_pixel;
-       guchar *mask_pixel;
-       int     i, j;
-
-       if ((layer->image == NULL) || (layer->mask == NULL))
-               return;
-
-       cairo_surface_flush (layer->image);
-
-       width = cairo_image_surface_get_width (layer->image);
-       height = cairo_image_surface_get_height (layer->image);
-       row_stride = cairo_image_surface_get_stride (layer->image);
-       image_row = cairo_image_surface_get_data (layer->image);
-       mask_row = cairo_image_surface_get_data (layer->mask);
-
-       for (i = 0; i < height; i++) {
-               image_pixel = image_row;
-               mask_pixel = mask_row;
-
-               for (j = 0; j < width; j++) {
-
-                       if (mask_pixel[0] != 0xff) {
-                               double factor = (double) mask_pixel[0] / 0xff;
-                               image_pixel[CAIRO_RED] *= factor;
-                               image_pixel[CAIRO_GREEN] *= factor;
-                               image_pixel[CAIRO_BLUE] *= factor;
-                               image_pixel[CAIRO_ALPHA] *= factor;
-                       }
-
-                       image_pixel += 4;
-                       mask_pixel += 4;
-               }
-               image_row += row_stride;
-               mask_row += row_stride;
-       }
-
-       cairo_surface_mark_dirty (layer->image);
-}
-
-
-static void
 gimp_layer_free (GimpLayer *layer)
 {
        if (layer == NULL)
                return;
-       if (layer->image != NULL)
-               cairo_surface_destroy (layer->image);
-       if (layer->mask != NULL)
-               cairo_surface_destroy (layer->mask);
+       g_free (layer->pixels);
+       g_free (layer->alpha_mask);
        g_free (layer->name);
        g_free (layer);
 }
@@ -313,6 +313,9 @@ gimp_layer_free (GimpLayer *layer)
 /* -- _cairo_image_surface_create_from_xcf -- */
 
 
+/* RGB <-> HSV */
+
+
 static void
 gimp_rgb_to_hsv (guchar  red,
                 guchar  green,
@@ -338,7 +341,9 @@ gimp_rgb_to_hsv (guchar  red,
                return;
        }
 
-       if (max == red)
+       if (max == min)
+               *hue = 0;
+       else if (max == red)
                *hue = 0 + 43 * (green - blue) / (max - min);
        else if (max == green)
                *hue = 85 + 43 * (blue - red) / (max - min);
@@ -351,9 +356,9 @@ static void
 gimp_hsv_to_rgb (guchar  hue,
                 guchar  sat,
                 guchar  val,
-                double *red,
-                double *green,
-                double *blue)
+                guchar *red,
+                guchar *green,
+                guchar *blue)
 {
        guchar region, remainder, p, q, t;
 
@@ -404,6 +409,104 @@ gimp_hsv_to_rgb (guchar  hue,
 }
 
 
+/* RGB <-> HSL */
+
+
+static void
+gimp_rgb_to_hsl (guchar  red,
+                guchar  green,
+                guchar  blue,
+                guchar *hue,
+                guchar *sat,
+                guchar *lum)
+{
+       guchar min, max;
+
+       min = MIN3 (red, green, blue);
+       max = MAX3 (red, green, blue);
+
+       *lum = (max + min) / 2;
+
+       if (max == min) {
+               *hue = *sat = 0;
+               return;
+       }
+
+       if (*lum < 128)
+               *sat = 255 * (long) (max - min) / (max + min);
+       else
+               *sat = 255 * (long) (max - min) / (512 - max - min);
+
+       if (max == min)
+               *hue = 0;
+       else if (max == red)
+               *hue = 0 + 43 * (green - blue) / (max - min);
+       else if (max == green)
+               *hue = 85 + 43 * (blue - red) / (max - min);
+       else if (max == blue)
+               *hue = 171 + 43 * (red - green) / (max - min);
+}
+
+
+static inline gint
+gimp_hsl_value (gdouble n1,
+                gdouble n2,
+                gdouble hue)
+{
+       gdouble value;
+
+       if (hue > 255)
+               hue -= 255;
+       else if (hue < 0)
+               hue += 255;
+
+       if (hue < 42.5)
+               value = n1 + (n2 - n1) * (hue / 42.5);
+       else if (hue < 127.5)
+               value = n2;
+       else if (hue < 170)
+               value = n1 + (n2 - n1) * ((170 - hue) / 42.5);
+       else
+               value = n1;
+
+       return value * 255.0;
+}
+
+
+static void
+gimp_hsl_to_rgb (guchar  hue,
+                guchar  sat,
+                guchar  lum,
+                guchar *red,
+                guchar *green,
+                guchar *blue)
+{
+       if (sat == 0) {
+               *red = lum;
+               *green = lum;
+               *blue = lum;
+       }
+       else {
+               gdouble h, s, l, m1, m2;
+
+               h = hue;
+               s = sat;
+               l = lum;
+
+               if (l < 128)
+                       m2 = (l * (255 + s)) / 65025.0;
+               else
+                       m2 = (l + s - (l * s) / 255.0) / 255.0;
+
+               m1 = (l / 127.5) - m2;
+
+               *red = gimp_hsl_value (m1, m2, h + 85);
+               *green = gimp_hsl_value (m1, m2, h);
+               *blue  = gimp_hsl_value (m1, m2, h - 85);
+       }
+}
+
+
 static void
 _cairo_image_surface_paint_layer (cairo_surface_t *image,
                                  GimpLayer       *layer)
@@ -416,16 +519,19 @@ _cairo_image_surface_paint_layer (cairo_surface_t *image,
        int     layer_height;
        int     layer_row_stride;
        guchar *layer_row;
+       guchar *mask_row;
        int     x, y, width, height;
        guchar *image_pixel;
        guchar *layer_pixel;
+       guchar *mask_pixel;
        GRand  *rand_gen;
        int     i, j;
-       double  layer_opacity, layer_a, r, g, b, a, temp;
-       guchar  image_hue, image_sat, image_val;
-       guchar  layer_hue, layer_sat, layer_val;
+       guchar  r, g, b, a;
+       int     temp, temp2;
+       guchar  image_hue, image_sat, image_val, image_lum;
+       guchar  layer_hue, layer_sat, layer_val, layer_lum;
 
-       if ((image == NULL) || (layer->image == NULL))
+       if ((image == NULL) || (layer->pixels == NULL))
                return;
 
        cairo_surface_flush (image);
@@ -434,9 +540,9 @@ _cairo_image_surface_paint_layer (cairo_surface_t *image,
        image_height = cairo_image_surface_get_height (image);
        image_row_stride = cairo_image_surface_get_stride (image);
 
-       layer_width = cairo_image_surface_get_width (layer->image);
-       layer_height = cairo_image_surface_get_height (layer->image);
-       layer_row_stride = cairo_image_surface_get_stride (layer->image);
+       layer_width = layer->width;
+       layer_height = layer->height;
+       layer_row_stride = layer->width * 4;
 
        /* compute the layer <-> image intersection */
 
@@ -469,13 +575,11 @@ _cairo_image_surface_paint_layer (cairo_surface_t *image,
 
        image_row = cairo_image_surface_get_data (image) + (y * image_row_stride) + (x * 4);
 
-       /*g_print ("image: (%d, %d) [%d, %d]\n", x, y, width, height);*/
-
        x = (layer->h_offset < 0) ? -layer->h_offset : 0;
        y = (layer->v_offset < 0) ? -layer->v_offset : 0;
-       layer_row = cairo_image_surface_get_data (layer->image) + (y * layer_row_stride) + (x * 4);
+       layer_row = layer->pixels + (y * layer_row_stride) + (x * 4);
 
-       /*g_print ("layer: (%d, %d) [%d, %d]\n", x, y, width, height);*/
+       mask_row = layer->alpha_mask + (y * layer_width) + x;
 
        if (layer->mode == GIMP_LAYER_MODE_DISSOLVE)
                rand_gen = g_rand_new_with_seed (DISSOLVE_SEED);
@@ -483,139 +587,204 @@ _cairo_image_surface_paint_layer (cairo_surface_t *image,
        for (i = 0; i < height; i++) {
                image_pixel = image_row;
                layer_pixel = layer_row;
+               mask_pixel = mask_row;
 
                for (j = 0; j < width; j++) {
-                       layer_opacity = (double) layer->opacity / 255.0;
+                       a = ((layer->bpp == 2) || (layer->bpp == 4)) ? layer_pixel[CAIRO_ALPHA] : 255;
 
-                       r = layer_opacity * layer_pixel[CAIRO_RED];
-                       g = layer_opacity * layer_pixel[CAIRO_GREEN];
-                       b = layer_opacity * layer_pixel[CAIRO_BLUE];
-                       a = layer_opacity * layer_pixel[CAIRO_ALPHA];
+                       a = ADD_ALPHA (a, layer->opacity);
+                       if (layer->alpha_mask && (layer->alpha_mask != NULL))
+                               a = ADD_ALPHA (a, mask_pixel[0]);
 
-                       layer_a = a / 255.0;
+                       if (a == 0)
+                               goto next_pixel;
 
                        switch (layer->mode) {
+                       case GIMP_LAYER_MODE_NORMAL:
+                       default:
+                               r = layer_pixel[CAIRO_RED];
+                               g = layer_pixel[CAIRO_GREEN];
+                               b = layer_pixel[CAIRO_BLUE];
+                               break;
+
                        case GIMP_LAYER_MODE_DISSOLVE:
-                               if ((g_rand_double (rand_gen) <= layer_a) && (layer_a > 0)) {
-                                       image_pixel[CAIRO_RED] = CLAMP_PIXEL (r / layer_a);
-                                       image_pixel[CAIRO_GREEN] = CLAMP_PIXEL (g / layer_a);
-                                       image_pixel[CAIRO_BLUE] = CLAMP_PIXEL (b / layer_a);
-                                       image_pixel[CAIRO_ALPHA] = 0xff;
-                               }
+                               if (g_rand_int_range (rand_gen, 0, 256) > a)
+                                       goto next_pixel;
+                               r = layer_pixel[CAIRO_RED];
+                               g = layer_pixel[CAIRO_GREEN];
+                               b = layer_pixel[CAIRO_BLUE];
+                               a = 255;
                                break;
 
-                       case GIMP_LAYER_MODE_SUBTRACT:
-                               if (layer_a > 0.0) {
-                                       r = layer_a * GIMP_OP_SUBTRACT (r / layer_a, image_pixel[CAIRO_RED]);
-                                       g = layer_a * GIMP_OP_SUBTRACT (g / layer_a, 
image_pixel[CAIRO_GREEN]);
-                                       b = layer_a * GIMP_OP_SUBTRACT (b / layer_a, image_pixel[CAIRO_BLUE]);
-
-                                       image_pixel[CAIRO_RED] = GIMP_OP_NORMAL (r, image_pixel[CAIRO_RED], 
layer_a);
-                                       image_pixel[CAIRO_GREEN] = GIMP_OP_NORMAL (g, 
image_pixel[CAIRO_GREEN], layer_a);
-                                       image_pixel[CAIRO_BLUE] = GIMP_OP_NORMAL (b, image_pixel[CAIRO_BLUE], 
layer_a);
-                                       image_pixel[CAIRO_ALPHA] = GIMP_OP_NORMAL (a, 
image_pixel[CAIRO_ALPHA], layer_a);
-                               }
+                       case GIMP_LAYER_MODE_LIGHTEN_ONLY:
+                               r = GIMP_OP_LIGHTEN_ONLY (layer_pixel[CAIRO_RED], image_pixel[CAIRO_RED]);
+                               g = GIMP_OP_LIGHTEN_ONLY (layer_pixel[CAIRO_GREEN], image_pixel[CAIRO_GREEN]);
+                               b = GIMP_OP_LIGHTEN_ONLY (layer_pixel[CAIRO_BLUE], image_pixel[CAIRO_BLUE]);
                                break;
 
-                       case GIMP_LAYER_MODE_DIVIDE:
-                               if (layer_a > 0.0) {
-                                       r = layer_a * GIMP_OP_DIVIDE (r, image_pixel[CAIRO_RED], layer_a);
-                                       g = layer_a * GIMP_OP_DIVIDE (g, image_pixel[CAIRO_GREEN], layer_a);
-                                       b = layer_a * GIMP_OP_DIVIDE (b, image_pixel[CAIRO_BLUE], layer_a);
-
-                                       image_pixel[CAIRO_RED] = GIMP_OP_NORMAL (r, image_pixel[CAIRO_RED], 
layer_a);
-                                       image_pixel[CAIRO_GREEN] = GIMP_OP_NORMAL (g, 
image_pixel[CAIRO_GREEN], layer_a);
-                                       image_pixel[CAIRO_BLUE] = GIMP_OP_NORMAL (b, image_pixel[CAIRO_BLUE], 
layer_a);
-                                       image_pixel[CAIRO_ALPHA] = GIMP_OP_NORMAL (a, 
image_pixel[CAIRO_ALPHA], layer_a);
-                               }
+                       case GIMP_LAYER_MODE_SCREEN:
+                               r = GIMP_OP_SCREEN (layer_pixel[CAIRO_RED], image_pixel[CAIRO_RED]);
+                               g = GIMP_OP_SCREEN (layer_pixel[CAIRO_GREEN], image_pixel[CAIRO_GREEN]);
+                               b = GIMP_OP_SCREEN (layer_pixel[CAIRO_BLUE], image_pixel[CAIRO_BLUE]);
                                break;
 
-                       case GIMP_LAYER_MODE_HUE:
-                       case GIMP_LAYER_MODE_SATURATION:
-                       case GIMP_LAYER_MODE_VALUE:
-                               if (layer_a > 0.0) {
-                                       gimp_rgb_to_hsv (image_pixel[CAIRO_RED],
-                                                        image_pixel[CAIRO_GREEN],
-                                                        image_pixel[CAIRO_BLUE],
-                                                        &image_hue,
-                                                        &image_sat,
-                                                        &image_val);
-                                       gimp_rgb_to_hsv (r / layer_a,
-                                                        g / layer_a,
-                                                        b / layer_a,
-                                                        &layer_hue,
-                                                        &layer_sat,
-                                                        &layer_val);
-
-                                       switch (layer->mode) {
-                                       case GIMP_LAYER_MODE_HUE:
-                                               gimp_hsv_to_rgb (layer_hue,
-                                                                image_sat,
-                                                                image_val,
-                                                                &r,
-                                                                &g,
-                                                                &b);
-                                               break;
-                                       case GIMP_LAYER_MODE_SATURATION:
-                                               gimp_hsv_to_rgb (image_hue,
-                                                                layer_sat,
-                                                                image_val,
-                                                                &r,
-                                                                &g,
-                                                                &b);
-                                               break;
-                                       case GIMP_LAYER_MODE_VALUE:
-                                               gimp_hsv_to_rgb (image_hue,
-                                                                image_sat,
-                                                                layer_val,
-                                                                &r,
-                                                                &g,
-                                                                &b);
-                                               break;
-                                       default:
-                                               break;
-                                       }
+                       case GIMP_LAYER_MODE_DODGE:
+                               r = GIMP_OP_DODGE (layer_pixel[CAIRO_RED], image_pixel[CAIRO_RED]);
+                               g = GIMP_OP_DODGE (layer_pixel[CAIRO_GREEN], image_pixel[CAIRO_GREEN]);
+                               b = GIMP_OP_DODGE (layer_pixel[CAIRO_BLUE], image_pixel[CAIRO_BLUE]);
+                               break;
 
-                                       r *= layer_a;
-                                       g *= layer_a;
-                                       b *= layer_a;
+                       case GIMP_LAYER_MODE_ADDITION:
+                               r = GIMP_OP_ADDITION (layer_pixel[CAIRO_RED], image_pixel[CAIRO_RED]);
+                               g = GIMP_OP_ADDITION (layer_pixel[CAIRO_GREEN], image_pixel[CAIRO_GREEN]);
+                               b = GIMP_OP_ADDITION (layer_pixel[CAIRO_BLUE], image_pixel[CAIRO_BLUE]);
+                               break;
 
-                                       image_pixel[CAIRO_RED] = GIMP_OP_NORMAL (r, image_pixel[CAIRO_RED], 
layer_a);
-                                       image_pixel[CAIRO_GREEN] = GIMP_OP_NORMAL (g, 
image_pixel[CAIRO_GREEN], layer_a);
-                                       image_pixel[CAIRO_BLUE] = GIMP_OP_NORMAL (b, image_pixel[CAIRO_BLUE], 
layer_a);
-                                       image_pixel[CAIRO_ALPHA] = GIMP_OP_NORMAL (a, 
image_pixel[CAIRO_ALPHA], layer_a);
-                               }
+                       case GIMP_LAYER_MODE_DARKEN_ONLY:
+                               r = GIMP_OP_DARKEN_ONLY (layer_pixel[CAIRO_RED], image_pixel[CAIRO_RED]);
+                               g = GIMP_OP_DARKEN_ONLY (layer_pixel[CAIRO_GREEN], image_pixel[CAIRO_GREEN]);
+                               b = GIMP_OP_DARKEN_ONLY (layer_pixel[CAIRO_BLUE], image_pixel[CAIRO_BLUE]);
+                               break;
+
+                       case GIMP_LAYER_MODE_MULTIPLY:
+                               r = GIMP_OP_MULTIPLY (layer_pixel[CAIRO_RED], image_pixel[CAIRO_RED]);
+                               g = GIMP_OP_MULTIPLY (layer_pixel[CAIRO_GREEN], image_pixel[CAIRO_GREEN]);
+                               b = GIMP_OP_MULTIPLY (layer_pixel[CAIRO_BLUE], image_pixel[CAIRO_BLUE]);
+                               break;
+
+                       case GIMP_LAYER_MODE_BURN:
+                               r = GIMP_OP_BURN (layer_pixel[CAIRO_RED], image_pixel[CAIRO_RED]);
+                               g = GIMP_OP_BURN (layer_pixel[CAIRO_GREEN], image_pixel[CAIRO_GREEN]);
+                               b = GIMP_OP_BURN (layer_pixel[CAIRO_BLUE], image_pixel[CAIRO_BLUE]);
+                               break;
+
+                       case GIMP_LAYER_MODE_OVERLAY:
+                       case GIMP_LAYER_MODE_SOFT_LIGHT:
+                               r = GIMP_OP_SOFT_LIGHT (layer_pixel[CAIRO_RED], image_pixel[CAIRO_RED]);
+                               g = GIMP_OP_SOFT_LIGHT (layer_pixel[CAIRO_GREEN], image_pixel[CAIRO_GREEN]);
+                               b = GIMP_OP_SOFT_LIGHT (layer_pixel[CAIRO_BLUE], image_pixel[CAIRO_BLUE]);
+                               break;
+
+                       case GIMP_LAYER_MODE_HARD_LIGHT:
+                               r = GIMP_OP_HARD_LIGHT (layer_pixel[CAIRO_RED], image_pixel[CAIRO_RED]);
+                               g = GIMP_OP_HARD_LIGHT (layer_pixel[CAIRO_GREEN], image_pixel[CAIRO_GREEN]);
+                               b = GIMP_OP_HARD_LIGHT (layer_pixel[CAIRO_BLUE], image_pixel[CAIRO_BLUE]);
+                               break;
+
+                       case GIMP_LAYER_MODE_DIFFERENCE:
+                               r = GIMP_OP_DIFFERENCE (layer_pixel[CAIRO_RED], image_pixel[CAIRO_RED]);
+                               g = GIMP_OP_DIFFERENCE (layer_pixel[CAIRO_GREEN], image_pixel[CAIRO_GREEN]);
+                               b = GIMP_OP_DIFFERENCE (layer_pixel[CAIRO_BLUE], image_pixel[CAIRO_BLUE]);
+                               break;
+
+                       case GIMP_LAYER_MODE_SUBTRACT:
+                               r = GIMP_OP_SUBTRACT (layer_pixel[CAIRO_RED], image_pixel[CAIRO_RED]);
+                               g = GIMP_OP_SUBTRACT (layer_pixel[CAIRO_GREEN], image_pixel[CAIRO_GREEN]);
+                               b = GIMP_OP_SUBTRACT (layer_pixel[CAIRO_BLUE], image_pixel[CAIRO_BLUE]);
                                break;
 
                        case GIMP_LAYER_MODE_GRAIN_EXTRACT:
-                               image_pixel[CAIRO_RED] = GIMP_OP_GRAIN_EXTRACT (r, image_pixel[CAIRO_RED], 
layer_a);
-                               image_pixel[CAIRO_GREEN] = GIMP_OP_GRAIN_EXTRACT (g, 
image_pixel[CAIRO_GREEN], layer_a);
-                               image_pixel[CAIRO_BLUE] = GIMP_OP_GRAIN_EXTRACT (b, image_pixel[CAIRO_BLUE], 
layer_a);
-                               image_pixel[CAIRO_ALPHA] = GIMP_OP_NORMAL (a, image_pixel[CAIRO_ALPHA], 
layer_a);
+                               r = GIMP_OP_GRAIN_EXTRACT (layer_pixel[CAIRO_RED], image_pixel[CAIRO_RED]);
+                               g = GIMP_OP_GRAIN_EXTRACT (layer_pixel[CAIRO_GREEN], 
image_pixel[CAIRO_GREEN]);
+                               b = GIMP_OP_GRAIN_EXTRACT (layer_pixel[CAIRO_BLUE], image_pixel[CAIRO_BLUE]);
                                break;
 
                        case GIMP_LAYER_MODE_GRAIN_MERGE:
-                               image_pixel[CAIRO_RED] = GIMP_OP_GRAIN_MERGE (r, image_pixel[CAIRO_RED], 
layer_a);
-                               image_pixel[CAIRO_GREEN] = GIMP_OP_GRAIN_MERGE (g, image_pixel[CAIRO_GREEN], 
layer_a);
-                               image_pixel[CAIRO_BLUE] = GIMP_OP_GRAIN_MERGE (b, image_pixel[CAIRO_BLUE], 
layer_a);
-                               image_pixel[CAIRO_ALPHA] = GIMP_OP_NORMAL (a, image_pixel[CAIRO_ALPHA], 
layer_a);
+                               r = GIMP_OP_GRAIN_MERGE (layer_pixel[CAIRO_RED], image_pixel[CAIRO_RED]);
+                               g = GIMP_OP_GRAIN_MERGE (layer_pixel[CAIRO_GREEN], image_pixel[CAIRO_GREEN]);
+                               b = GIMP_OP_GRAIN_MERGE (layer_pixel[CAIRO_BLUE], image_pixel[CAIRO_BLUE]);
                                break;
 
-                       case GIMP_LAYER_MODE_NORMAL:
-                       default:
-                               image_pixel[CAIRO_RED] = GIMP_OP_NORMAL (r, image_pixel[CAIRO_RED], layer_a);
-                               image_pixel[CAIRO_GREEN] = GIMP_OP_NORMAL (g, image_pixel[CAIRO_GREEN], 
layer_a);
-                               image_pixel[CAIRO_BLUE] = GIMP_OP_NORMAL (b, image_pixel[CAIRO_BLUE], 
layer_a);
-                               image_pixel[CAIRO_ALPHA] = GIMP_OP_NORMAL (a, image_pixel[CAIRO_ALPHA], 
layer_a);
+                       case GIMP_LAYER_MODE_DIVIDE:
+                               r = GIMP_OP_DIVIDE (layer_pixel[CAIRO_RED], image_pixel[CAIRO_RED]);
+                               g = GIMP_OP_DIVIDE (layer_pixel[CAIRO_GREEN], image_pixel[CAIRO_GREEN]);
+                               b = GIMP_OP_DIVIDE (layer_pixel[CAIRO_BLUE], image_pixel[CAIRO_BLUE]);
+                               break;
+
+                       case GIMP_LAYER_MODE_HUE:
+                       case GIMP_LAYER_MODE_SATURATION:
+                       case GIMP_LAYER_MODE_VALUE:
+                               gimp_rgb_to_hsv (image_pixel[CAIRO_RED],
+                                                image_pixel[CAIRO_GREEN],
+                                                image_pixel[CAIRO_BLUE],
+                                                &image_hue,
+                                                &image_sat,
+                                                &image_val);
+                               gimp_rgb_to_hsv (layer_pixel[CAIRO_RED],
+                                                layer_pixel[CAIRO_GREEN],
+                                                layer_pixel[CAIRO_BLUE],
+                                                &layer_hue,
+                                                &layer_sat,
+                                                &layer_val);
+
+                               switch (layer->mode) {
+                               case GIMP_LAYER_MODE_HUE:
+                                       gimp_hsv_to_rgb (layer_hue,
+                                                        image_sat,
+                                                        image_val,
+                                                        &r,
+                                                        &g,
+                                                        &b);
+                                       break;
+                               case GIMP_LAYER_MODE_SATURATION:
+                                       gimp_hsv_to_rgb (image_hue,
+                                                        layer_sat,
+                                                        image_val,
+                                                        &r,
+                                                        &g,
+                                                        &b);
+                                       break;
+                               case GIMP_LAYER_MODE_VALUE:
+                                       gimp_hsv_to_rgb (image_hue,
+                                                        image_sat,
+                                                        layer_val,
+                                                        &r,
+                                                        &g,
+                                                        &b);
+                                       break;
+                               default:
+                                       g_assert_not_reached ();
+                                       break;
+                               }
+                               break;
+
+                       case GIMP_LAYER_MODE_COLOR:
+                               gimp_rgb_to_hsl (image_pixel[CAIRO_RED],
+                                                image_pixel[CAIRO_GREEN],
+                                                image_pixel[CAIRO_BLUE],
+                                                &image_hue,
+                                                &image_sat,
+                                                &image_lum);
+                               gimp_rgb_to_hsl (layer_pixel[CAIRO_RED],
+                                                layer_pixel[CAIRO_GREEN],
+                                                layer_pixel[CAIRO_BLUE],
+                                                &layer_hue,
+                                                &layer_sat,
+                                                &layer_lum);
+                               gimp_hsl_to_rgb (layer_hue,
+                                                layer_sat,
+                                                image_lum,
+                                                &r,
+                                                &g,
+                                                &b);
                                break;
                        }
 
+                       image_pixel[CAIRO_RED] = GIMP_OP_NORMAL (r, image_pixel[CAIRO_RED], a);
+                       image_pixel[CAIRO_GREEN] = GIMP_OP_NORMAL (g, image_pixel[CAIRO_GREEN], a);
+                       image_pixel[CAIRO_BLUE] = GIMP_OP_NORMAL (b, image_pixel[CAIRO_BLUE], a);
+                       image_pixel[CAIRO_ALPHA] = GIMP_OP_NORMAL (255, image_pixel[CAIRO_ALPHA], a);
+
+next_pixel:
+
                        image_pixel += 4;
                        layer_pixel += 4;
+                       mask_pixel += 1;
                }
 
                image_row += image_row_stride;
                layer_row += layer_row_stride;
+               mask_row += layer_width;
        }
 
        if (layer->mode == GIMP_LAYER_MODE_DISSOLVE)
@@ -637,15 +806,11 @@ _cairo_image_surface_create_from_layers (int                canvas_width,
        image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, canvas_width, canvas_height);
 
        for (scan = layers; scan; scan = scan->next) {
-               GimpLayer        *layer = scan->data;
-               cairo_operator_t  op = -1;
+               GimpLayer *layer = scan->data;
 
-               if (layer->image == NULL)
+               if (layer->pixels == NULL)
                        continue;
 
-               if ((layer->mask != NULL) && layer->apply_mask)
-                       gimp_layer_apply_mask (layer);
-
                /* the bottommost layer only supports NORMAL and DISSOLVE */
 
                if ((scan == layers) && (layer->mode != GIMP_LAYER_MODE_DISSOLVE))
@@ -668,162 +833,38 @@ _cairo_image_surface_create_from_layers (int                canvas_width,
                        }
                }
 
-               /* find the cairo operation equivalent to the layer mode */
+               _cairo_image_surface_paint_layer (image, layer);
 
-               switch (layer->mode) {
-               case GIMP_LAYER_MODE_NORMAL:
-                       op = CAIRO_OPERATOR_OVER;
-                       break;
-               case GIMP_LAYER_MODE_DISSOLVE:
-                       break;
-               case GIMP_LAYER_MODE_BEHIND:
-                       break;
-               case GIMP_LAYER_MODE_MULTIPLY:
-                       op = CAIRO_OPERATOR_MULTIPLY;
-                       break;
-               case GIMP_LAYER_MODE_SCREEN:
-                       op = CAIRO_OPERATOR_SCREEN;
-                       break;
-               case GIMP_LAYER_MODE_OVERLAY:
-                       /* In Gimp OVERLAY and SOFT_LIGHT are identical */
-                       op = CAIRO_OPERATOR_SOFT_LIGHT;
-                       break;
-               case GIMP_LAYER_MODE_DIFFERENCE:
-                       op = CAIRO_OPERATOR_DIFFERENCE;
-                       break;
-               case GIMP_LAYER_MODE_ADDITION:
-                       op = CAIRO_OPERATOR_ADD;
-                       break;
-               case GIMP_LAYER_MODE_SUBTRACT:
-                       break;
-               case GIMP_LAYER_MODE_DARKEN_ONLY:
-                       op = CAIRO_OPERATOR_DARKEN;
-                       break;
-               case GIMP_LAYER_MODE_LIGHTEN_ONLY:
-                       op = CAIRO_OPERATOR_LIGHTEN;
-                       break;
-               case GIMP_LAYER_MODE_HUE:
-                       break;
-               case GIMP_LAYER_MODE_SATURATION:
-                       break;
-               case GIMP_LAYER_MODE_COLOR:
-                       op = CAIRO_OPERATOR_HSL_COLOR;
-                       break;
-               case GIMP_LAYER_MODE_VALUE:
-                       break;
-               case GIMP_LAYER_MODE_DIVIDE:
-                       break;
-               case GIMP_LAYER_MODE_DODGE:
-                       op = CAIRO_OPERATOR_COLOR_DODGE;
-                       break;
-               case GIMP_LAYER_MODE_BURN:
-                       op = CAIRO_OPERATOR_COLOR_BURN;
-                       break;
-               case GIMP_LAYER_MODE_HARD_LIGHT:
-                       op = CAIRO_OPERATOR_HARD_LIGHT;
-                       break;
-               case GIMP_LAYER_MODE_SOFT_LIGHT:
-                       op = CAIRO_OPERATOR_SOFT_LIGHT;
-                       break;
-               case GIMP_LAYER_MODE_GRAIN_EXTRACT:
-                       break;
-               case GIMP_LAYER_MODE_GRAIN_MERGE:
-                       break;
-               }
-
-               if (op != -1) {
-
-                       /* layer mode supported directly by cairo */
-
-                       cairo_t *cr;
-
-                       cr = cairo_create (image);
-                       cairo_set_source_surface (cr, layer->image, layer->h_offset, layer->v_offset);
-                       cairo_set_operator (cr, op);
-                       cairo_rectangle (cr, 0, 0, canvas_width, canvas_height);
-                       cairo_paint_with_alpha (cr, (double) layer->opacity / 255.0);
-
-                       cairo_destroy (cr);
-               }
-               else
-                       _cairo_image_surface_paint_layer (image, layer);
+               performance (DEBUG_INFO, "end paint layer %d, mode %d", layer->n, layer->mode);
        }
 
        return image;
 }
 
 
-static void
-_cairo_surface_data_post_production (guchar            *image_pixels,
-                                    int                 row_stride,
-                                    int                 width,
-                                    int                 height,
-                                    int                 bpp,
-                                    GimpImageBaseType   base_type)
+static guchar *
+read_pixels_from_hierarchy (GDataInputStream  *data_stream,
+                           guint32            hierarchy_offset,
+                           GimpLayer         *layer,
+                           GimpColormap      *colormap,
+                           GimpImageBaseType  base_type,
+                           GimpCompression    compression,
+                           gboolean           is_gimp_channel,
+                           GCancellable      *cancellable,
+                           GError           **error)
 {
-       int     i, j;
-       guchar *row, *pixel;
-
-       row = image_pixels;
-       for (i = 0; i < height; i++) {
-               pixel = row;
-               for (j = 0; j < width; j++) {
-
-                       if ((bpp <= 2) && (base_type != GIMP_INDEXED)) {
-
-                               /* copy the red channel value to the green and blue channels */
-
-                               pixel[CAIRO_GREEN] = pixel[CAIRO_RED];
-                               pixel[CAIRO_BLUE] = pixel[CAIRO_RED];
-                       }
-
-                       if ((bpp == 4) || (bpp == 2)) {
-
-                               /* multiply for the alpha channel */
-
-                               if (pixel[CAIRO_ALPHA] < 0xff) {
-                                       double factor = (double) pixel[CAIRO_ALPHA] / 0xff;
-                                       pixel[CAIRO_RED] *= factor;
-                                       pixel[CAIRO_GREEN] *= factor;
-                                       pixel[CAIRO_BLUE] *= factor;
-                               }
-                       }
-                       else if ((bpp == 3) || (bpp == 1)) {
-
-                               /* set the alpha channel to opaque */
-
-                               pixel[CAIRO_ALPHA] = 0xff;
-                       }
-
-                       pixel += 4;
-               }
-               row += row_stride;
-       }
-}
-
-
-static cairo_surface_t *
-_cairo_image_surface_create_from_hierarchy (GDataInputStream  *data_stream,
-                                           guint32            hierarchy_offset,
-                                           GimpLayer         *layer,
-                                           GimpColormap      *colormap,
-                                           GimpImageBaseType  base_type,
-                                           GimpCompression    compression,
-                                           GCancellable      *cancellable,
-                                           GError           **error)
-{
-       cairo_surface_t *image = NULL;
-       guint32          width;
-       guint32          height;
-       guint32          bpp;
-       guint32          level_offset;
-       GArray          *tile_offsets = NULL;
-       guint32          tile_offset;
-       guint32          last_tile_offset;
-       int              n_tiles;
-       int              t;
-       guchar          *image_pixels;
-       int              row_stride;
+       guchar   *image_pixels = NULL;
+       guint32   width;
+       guint32   height;
+       guint32   in_bpp;
+       guint32   out_bpp;
+       int       row_stride;
+       guint32   level_offset;
+       GArray   *tile_offsets = NULL;
+       guint32   tile_offset;
+       guint32   last_tile_offset;
+       int       n_tiles;
+       int       t;
 
        /* read the hierarchy structure */
 
@@ -844,7 +885,7 @@ _cairo_image_surface_create_from_hierarchy (GDataInputStream  *data_stream,
        if (*error != NULL)
                goto read_error;
 
-       bpp = g_data_input_stream_read_uint32 (data_stream, cancellable, error);
+       in_bpp = g_data_input_stream_read_uint32 (data_stream, cancellable, error);
        if (*error != NULL)
                goto read_error;
 
@@ -852,12 +893,13 @@ _cairo_image_surface_create_from_hierarchy (GDataInputStream  *data_stream,
        if (*error != NULL)
                goto read_error;
 
-       /*
-       g_print ("  hierarchy width: %" G_GUINT32_FORMAT "\n", width);
-       g_print ("  hierarchy height: %" G_GUINT32_FORMAT "\n", height);
-       g_print ("  hierarchy bpp: %" G_GUINT32_FORMAT "\n", bpp);
-       g_print ("  level offset: %" G_GUINT32_FORMAT "\n", level_offset);
-       */
+       if (is_gimp_channel)
+               g_assert (in_bpp == 1);
+
+       if (! is_gimp_channel)
+               layer->bpp = in_bpp;
+
+       layer->tiles.dirty = TRUE;
 
        /* read the level structure */
 
@@ -878,17 +920,11 @@ _cairo_image_surface_create_from_hierarchy (GDataInputStream  *data_stream,
        if (*error != NULL)
                goto read_error;
 
-       /*
-       g_print ("  level width: %" G_GUINT32_FORMAT "\n", width);
-       g_print ("  level height: %" G_GUINT32_FORMAT "\n", height);
-       */
-
        /* tiles */
 
-       image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
-       cairo_surface_flush (image);
-       image_pixels = cairo_image_surface_get_data (image);
-       row_stride = cairo_image_surface_get_stride (image);
+       out_bpp = is_gimp_channel ? 1 : 4;
+       row_stride = width * out_bpp;
+       image_pixels = g_new (guchar, row_stride * height);
 
        tile_offsets = g_array_new (FALSE, FALSE, sizeof (guint32));;
        n_tiles = 0;
@@ -897,7 +933,6 @@ _cairo_image_surface_create_from_hierarchy (GDataInputStream  *data_stream,
                n_tiles += 1;
                last_tile_offset = tile_offset;
                g_array_append_val (tile_offsets, tile_offset);
-               /*g_print ("    tile %d: %" G_GUINT32_FORMAT "\n", n_tiles, tile_offset);*/
        }
        tile_offset = last_tile_offset + MAX_TILE_SIZE;
        g_array_append_val (tile_offsets, tile_offset);
@@ -906,11 +941,11 @@ _cairo_image_surface_create_from_hierarchy (GDataInputStream  *data_stream,
                goto read_error;
 
        if (compression == GIMP_COMPRESSION_RLE) {
+               guchar *tile_data = NULL;
 
                /* go to the first tile */
 
                tile_offset = g_array_index (tile_offsets, guint32, 0);
-               /*g_print ("    <seek: %" G_GUINT32_FORMAT ">\n", tile_offset);*/
                if (! g_seekable_seek (G_SEEKABLE (data_stream),
                                       tile_offset,
                                       G_SEEK_SET,
@@ -922,9 +957,10 @@ _cairo_image_surface_create_from_hierarchy (GDataInputStream  *data_stream,
 
                /* decompress the tile data */
 
+               tile_data = g_malloc (MAX_TILE_SIZE);
+
                for (t = 0; t < n_tiles; t++) {
                        goffset  tile_data_size;
-                       guchar  *tile_data;
                        guchar  *tile_data_p;
                        guchar  *tile_data_limit;
                        gsize    data_read;
@@ -937,27 +973,22 @@ _cairo_image_surface_create_from_hierarchy (GDataInputStream  *data_stream,
                        /* read the tile data */
 
                        tile_data_size = g_array_index (tile_offsets, guint32, t + 1) - g_array_index 
(tile_offsets, guint32, t);
-                       if (tile_data_size <= 0) {
-                               /*g_print ("<tile %d: size error %" G_GUINT32_FORMAT " = %" G_GUINT32_FORMAT 
" - %" G_GUINT32_FORMAT ">", t, tile_data_size, g_array_index (tile_offsets, guint32, t + 1), g_array_index 
(tile_offsets, guint32, t));*/
+                       if (tile_data_size <= 0)
                                continue;
-                       }
 
-                       tile_data = g_malloc (tile_data_size);
                        data_read = g_input_stream_read (G_INPUT_STREAM (data_stream), tile_data, 
tile_data_size, cancellable, error);
                        if (*error != NULL)
                                goto rle_error;
 
-                       /*g_print ("    tile size %d: %" G_GSIZE_FORMAT " (%" G_GSIZE_FORMAT ")\n", t, 
data_read, tile_data_size);*/
-
                        /* decompress the channel streams */
 
                        if (! gimp_layer_get_tile_size (layer,
                                                        t,
+                                                       out_bpp,
                                                        &tile_pixels_offset,
                                                        &tile_width,
                                                        &tile_height))
                        {
-                               /*g_print ("<tile %d: error>", t);*/
                                goto rle_error;
                        }
 
@@ -965,47 +996,51 @@ _cairo_image_surface_create_from_hierarchy (GDataInputStream  *data_stream,
                        tile_data_p = tile_data;
                        tile_data_limit = tile_data + data_read - 1;
 
-                       for (c = 0; c < bpp; c++) {
+                       for (c = 0; c < in_bpp; c++) {
                                int     channel_offset;
                                guchar *pixels_row;
-                               guchar *pixels;
+                               guchar *pixel;
                                int     size;
                                int     n, p, q, v;
                                int     tile_column;
 
-                               if (base_type == GIMP_INDEXED)
+                               if (is_gimp_channel)
+                                       channel_offset = 0;
+                               else if (base_type == GIMP_INDEXED)
                                        channel_offset = cairo_indexed[c];
-                               else if (bpp >= 3)
+                               else if (in_bpp >= 3)
                                        channel_offset = cairo_rgba[c];
-                               else if (bpp <= 2)
-                                       /* save the value in the red channel,
-                                        * it will be copied to the other channels in
-                                        * _cairo_surface_data_post_production */
+                               else if (in_bpp <= 2)
                                        channel_offset = cairo_graya[c];
                                else
                                        channel_offset = 0;
                                pixels_row = image_pixels + tile_pixels_offset + channel_offset;
-                               pixels = pixels_row;
+                               pixel = pixels_row;
 
                                size = tile_pixels_size;
                                tile_column = 0;
 
-#define SET_PIXEL(v) {                                         \
-       tile_column++;                                          \
-       if (tile_column > tile_width) {                         \
-               pixels_row += row_stride;                       \
-               pixels = pixels_row;                            \
-               tile_column = 1;                                \
-       }                                                       \
-       if ((base_type == GIMP_INDEXED) && (c == 0)) {          \
-               guchar *color = (guchar *) (colormap + (v));    \
-               pixels[CAIRO_RED] = color[0];                   \
-               pixels[CAIRO_GREEN] = color[1];                 \
-               pixels[CAIRO_BLUE] = color[2];                  \
-       }                                                       \
-       else                                                    \
-               *pixels = (v);                                  \
-       pixels += 4;                                            \
+#define SET_PIXEL(v) {                                                 \
+        tile_column++;                                                 \
+        if (tile_column > tile_width) {                                        \
+                pixels_row += row_stride;                              \
+                pixel = pixels_row;                                    \
+                tile_column = 1;                                       \
+        }                                                              \
+       if ((base_type == GIMP_INDEXED) && (c == 0)) {                  \
+               guchar *color = (guchar *) (colormap + (v));            \
+               pixel[CAIRO_RED] = color[0];                            \
+               pixel[CAIRO_GREEN] = color[1];                          \
+               pixel[CAIRO_BLUE] = color[2];                           \
+       }                                                               \
+       else if (! is_gimp_channel && (in_bpp <= 2) && (c == 0)) {      \
+               pixel[CAIRO_RED] = (v);                                 \
+               pixel[CAIRO_GREEN] = (v);                               \
+               pixel[CAIRO_BLUE] = (v);                                \
+       }                                                               \
+       else                                                            \
+               *pixel = (v);                                           \
+       pixel += out_bpp;                                               \
 }
 
                                while (size > 0) {
@@ -1073,151 +1108,56 @@ _cairo_image_surface_create_from_hierarchy (GDataInputStream  *data_stream,
                                                if (size < 0)
                                                        goto rle_error;
 
-                                               while (n-- > 0)
-                                                       SET_PIXEL (*tile_data_p++);
+                                               while (n-- > 0) {
+                                                       v = *tile_data_p++;
+                                                       SET_PIXEL (v);
+                                               }
                                        }
                                }
                        }
 
-#undef SET_PIXEL
+               }
 
 rle_error:
 
-                       g_free (tile_data);
-                       if (*error != NULL)
-                               goto read_error;
-               }
+               g_free (tile_data);
        }
        else if (compression == GIMP_COMPRESSION_NONE) {
 
-               /* This is untested because Gimp doesn't save in uncompressed
-                * mode any more. */
-
-               /* go to the first tile */
-
-               tile_offset = g_array_index (tile_offsets, guint32, 0);
-               /*g_print ("    <seek: %" G_GUINT32_FORMAT ">\n", tile_offset);*/
-               if (! g_seekable_seek (G_SEEKABLE (data_stream),
-                                      tile_offset,
-                                      G_SEEK_SET,
-                                      cancellable,
-                                      error))
-               {
-                       goto read_error;
-               }
-
-               /* decompress the tile data */
-
-               for (t = 0; t < n_tiles; t++) {
-                       goffset  tile_data_size;
-                       guchar  *tile_data;
-                       guchar  *tile_data_p;
-                       gsize    data_read;
-                       goffset  tile_pixels_offset;
-                       int      tile_width;
-                       int      tile_height;
-                       guchar  *pixels_row;
-                       guchar  *pixels;
-                       int      i, j;
+               /* Gimp doesn't save in uncompressed mode. */
 
-                       /* get the tile size and position */
-
-                       if (! gimp_layer_get_tile_size (layer,
-                                                       t,
-                                                       &tile_pixels_offset,
-                                                       &tile_width,
-                                                       &tile_height))
-                       {
-                               /*g_print ("<tile %d: error>", t);*/
-                               continue;
-                       }
-
-                       /* read the tile data */
-
-                       tile_data_size = tile_width * tile_height * bpp;
-                       if (tile_data_size <= 0) {
-                               /*g_print ("<tile %d: size error %" G_GUINT32_FORMAT " = %" G_GUINT32_FORMAT 
" - %" G_GUINT32_FORMAT ">", t, tile_data_size, g_array_index (tile_offsets, guint32, t + 1), g_array_index 
(tile_offsets, guint32, t));*/
-                               continue;
-                       }
-
-                       tile_data = g_malloc (tile_data_size);
-                       data_read = g_input_stream_read (G_INPUT_STREAM (data_stream), tile_data, 
tile_data_size, cancellable, error);
-                       if (*error != NULL) {
-                               g_free (tile_data);
-                               goto read_error;
-                       }
-
-                       if (data_read != tile_data_size) {
-                               /*g_print ("    tile size %d: %" G_GSIZE_FORMAT " (%" G_GSIZE_FORMAT ")\n", 
t, data_read, tile_data_size);*/
-                               g_free (tile_data);
-                               continue;
-                       }
-
-                       tile_data_p = tile_data;
-                       pixels_row = image_pixels + tile_pixels_offset;
-
-                       for (i = 0; i < tile_height; i++) {
-                               pixels = pixels_row;
-
-                               for (j = 0; j < tile_width; j++) {
-                                       if (base_type == GIMP_INDEXED) {
-                                               guchar *color = (guchar *) (colormap + (*tile_data_p++));
-                                               pixels[CAIRO_RED] = color[0];
-                                               pixels[CAIRO_GREEN] = color[1];
-                                               pixels[CAIRO_BLUE] = color[2];
-                                       }
-                                       else if (bpp >= 3) {
-                                               pixels[CAIRO_RED] = *tile_data_p++;
-                                               pixels[CAIRO_GREEN] = *tile_data_p++;
-                                               pixels[CAIRO_BLUE] = *tile_data_p++;
-
-                                       }
-                                       else if (bpp <= 2)
-                                               pixels[CAIRO_RED] = *tile_data_p++;
-
-                                       if ((bpp == 2) || (bpp == 4))
-                                               pixels[CAIRO_ALPHA] = *tile_data_p++;
-
-                                       pixels += 4;
-                               }
-
-                               pixels_row += row_stride;
-                       }
-
-                       g_free (tile_data);
-               }
        }
 
-       _cairo_surface_data_post_production (image_pixels, row_stride, width, height, bpp, base_type);
-       cairo_surface_mark_dirty (image);
+       performance (DEBUG_INFO, "end read hierarchy");
 
        g_array_free (tile_offsets, TRUE);
 
-       return image;
+       return image_pixels;
 
 read_error:
 
-       if (image != NULL)
-               cairo_surface_destroy (image);
-       if (tile_offsets != NULL)
-               g_array_free (tile_offsets, TRUE);
+       g_free (image_pixels);
+       g_array_free (tile_offsets, TRUE);
 
        return NULL;
 }
 
 
+#undef SET_PIXEL
+
+
 GthImage *
 _cairo_image_surface_create_from_xcf (GInputStream  *istream,
-                                     GthFileData   *file_data,
-                                     int            requested_size,
-                                     int           *original_width,
-                                     int           *original_height,
-                                     gpointer       user_data,
-                                     GCancellable  *cancellable,
-                                     GError       **error)
+                                     GthFileData   *file_data,
+                                     int            requested_size,
+                                     int           *original_width,
+                                     int           *original_height,
+                                     gpointer       user_data,
+                                     GCancellable  *cancellable,
+                                     GError       **error)
 {
        GthImage          *image = NULL;
-       cairo_surface_t   *surface = NULL;
+       cairo_surface_t   *surface;
        GDataInputStream  *data_stream;
        char              *file_type;
        char              *version;
@@ -1227,18 +1167,24 @@ _cairo_image_surface_create_from_xcf (GInputStream  *istream,
        guint32            property_type;
        guint32            payload_length;
        GimpCompression    compression;
-       GList             *layers = NULL;
+       GList             *layers;
        guint32            n_colors;
-       GimpColormap      *colormap = NULL;
+       GimpColormap      *colormap;
        guint              n_properties;
        gboolean           read_properties;
-       GArray            *layer_offsets = NULL;
+       GArray            *layer_offsets;
        guint32            layer_offset;
        guint              n_layers;
        guint32            channel_offset;
        guint              n_channels;
        int                i;
 
+       performance (DEBUG_INFO, "start loading");
+
+       g_once (&xcf_init_once, xcf_init, NULL);
+
+       performance (DEBUG_INFO, "end init");
+
        data_stream = g_data_input_stream_new (istream);
        g_data_input_stream_set_byte_order (data_stream, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
 
@@ -1250,8 +1196,7 @@ _cairo_image_surface_create_from_xcf (GInputStream  *istream,
 
        if (g_strcmp0 (file_type, "gimp xcf ") != 0) {
                g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Invalid format");
-               g_free (file_type);
-               goto out;
+               return NULL;
        }
        g_free (file_type);
 
@@ -1260,8 +1205,6 @@ _cairo_image_surface_create_from_xcf (GInputStream  *istream,
        version = _g_data_input_stream_read_c_string (data_stream, 5, cancellable, error);
        if (*error != NULL)
                goto out;
-
-       /*g_print ("version: %s\n", version);*/
        g_free (version);
 
        /* canvas size */
@@ -1274,16 +1217,12 @@ _cairo_image_surface_create_from_xcf (GInputStream  *istream,
        if (*error != NULL)
                goto out;
 
-       /*g_print ("canvas size: %" G_GUINT32_FORMAT " x %" G_GUINT32_FORMAT "\n", canvas_width, 
canvas_height);*/
-
        /* base type */
 
        base_type = g_data_input_stream_read_uint32 (data_stream, cancellable, error);
        if (*error != NULL)
                goto out;
 
-       /*g_print ("base type: %d\n", base_type);*/
-
        /* properties */
 
        compression = GIMP_COMPRESSION_RLE;
@@ -1346,19 +1285,15 @@ _cairo_image_surface_create_from_xcf (GInputStream  *istream,
                        compression = g_data_input_stream_read_byte (data_stream, cancellable, error);
                        if (*error != NULL)
                                goto out;
-
-                       /*g_print ("compression: %d\n", compression);*/
                        break;
 
                default:
                        g_input_stream_skip (G_INPUT_STREAM (data_stream), payload_length, cancellable, 
error);
                        if (*error != NULL)
                                goto out;
-
                        break;
                }
        }
-       /*g_print ("properties: %d\n", n_properties);*/
 
        /* layers */
 
@@ -1367,25 +1302,21 @@ _cairo_image_surface_create_from_xcf (GInputStream  *istream,
        while ((layer_offset = g_data_input_stream_read_uint32 (data_stream, cancellable, error)) != 0) {
                n_layers += 1;
                g_array_append_val (layer_offsets, layer_offset);
-               /*g_print ("layer %d: %" G_GUINT32_FORMAT "\n", n_layers, layer_offset);*/
        }
 
-       if (*error != NULL)
-               goto out;
-
        /* channels */
 
        n_channels = 0;
-       while ((channel_offset = g_data_input_stream_read_uint32 (data_stream, cancellable, error)) != 0) {
+       while ((channel_offset = g_data_input_stream_read_uint32 (data_stream, cancellable, error)) != 0)
                n_channels += 1;
-               /*g_print ("channel %d: %" G_GUINT32_FORMAT "\n", n_channels, channel_offset);*/
-       }
 
        if (*error != NULL)
                goto out;
 
        /* read the layers */
 
+       performance (DEBUG_INFO, "start read layers");
+
        for (i = 0; i < n_layers; i++) {
                GimpLayer *layer;
                guint32    hierarchy_offset;
@@ -1393,7 +1324,6 @@ _cairo_image_surface_create_from_xcf (GInputStream  *istream,
                char      *mask_name;
 
                layer_offset = g_array_index (layer_offsets, guint32, i);
-               /*g_print ("layer %d: %" G_GUINT32_FORMAT "\n", i, layer_offset);*/
 
                if (! g_seekable_seek (G_SEEKABLE (data_stream),
                                       layer_offset,
@@ -1404,7 +1334,7 @@ _cairo_image_surface_create_from_xcf (GInputStream  *istream,
                        goto out;
                }
 
-               layer = gimp_layer_new ();
+               layer = gimp_layer_new (i);
                layers = g_list_prepend (layers, layer);
 
                /* size */
@@ -1417,24 +1347,18 @@ _cairo_image_surface_create_from_xcf (GInputStream  *istream,
                if (*error != NULL)
                        goto out;
 
-               /*g_print ("  size: %" G_GUINT32_FORMAT " x %" G_GUINT32_FORMAT "\n", layer->width, 
layer->height);*/
-
                /* type  */
 
                layer->type = g_data_input_stream_read_uint32 (data_stream, cancellable, error);
                if (*error != NULL)
                        goto out;
 
-               /*g_print ("  type: %d\n", layer->type);*/
-
                /* name */
 
                layer->name = _g_data_input_stream_read_xcf_string (data_stream, cancellable, error);
                if (*error != NULL)
                        goto out;
 
-               /*g_print ("  name: %s\n", layer->name);*/
-
                /* properties */
 
                read_properties = TRUE;
@@ -1462,22 +1386,18 @@ _cairo_image_surface_create_from_xcf (GInputStream  *istream,
 
                        case 6: /* PROP_OPACITY */
                                layer->opacity = g_data_input_stream_read_uint32 (data_stream, cancellable, 
error);
-                               /*g_print ("  opacity: %d\n", layer->opacity);*/
                                break;
 
                        case 7: /* PROP_MODE */
                                layer->mode = g_data_input_stream_read_uint32 (data_stream, cancellable, 
error);
-                               /*g_print ("  mode: %d\n", layer->mode);*/
                                break;
 
                        case 8: /* PROP_VISIBLE */
                                layer->visible = g_data_input_stream_read_uint32 (data_stream, cancellable, 
error);
-                               /*g_print ("  visible: %d\n", layer->visible);*/
                                break;
 
                        case 11: /* PROP_APPLY_MASK */
                                layer->apply_mask = (g_data_input_stream_read_uint32 (data_stream, 
cancellable, error) == 1);
-                               /*g_print ("  apply mask: %d\n", layer->apply_mask);*/
                                break;
 
                        case 15: /* PROP_OFFSETS */
@@ -1486,7 +1406,6 @@ _cairo_image_surface_create_from_xcf (GInputStream  *istream,
                                        goto out;
 
                                layer->v_offset = g_data_input_stream_read_uint32 (data_stream, cancellable, 
error);
-                               /*g_print ("  coordinates: %d, %d\n", layer->h_offset, layer->v_offset);*/
                                break;
 
                        default:
@@ -1497,7 +1416,6 @@ _cairo_image_surface_create_from_xcf (GInputStream  *istream,
                        if (*error != NULL)
                                goto out;
                }
-               /*g_print ("  properties: %d\n", n_properties);*/
 
                /* hierarchy structure offset */
 
@@ -1505,22 +1423,18 @@ _cairo_image_surface_create_from_xcf (GInputStream  *istream,
                if (*error != NULL)
                        goto out;
 
-               /*g_print ("  hierarchy offset: %" G_GUINT32_FORMAT "\n", hierarchy_offset);*/
-
                /* layer mask offset */
 
                mask_offset = g_data_input_stream_read_uint32 (data_stream, cancellable, error);
                if (*error != NULL)
                        goto out;
 
-               /*g_print ("  mask offset: %" G_GUINT32_FORMAT "\n", mask_offset);*/
-
                /* the layer image */
 
                if (layer->floating_selection || ! layer->visible || (layer->opacity == 0))
                        continue;
 
-               layer->image = _cairo_image_surface_create_from_hierarchy (data_stream, hierarchy_offset, 
layer, colormap, base_type, compression, cancellable, error);
+               layer->pixels = read_pixels_from_hierarchy (data_stream, hierarchy_offset, layer, colormap, 
base_type, compression, FALSE, cancellable, error);
                if (*error != NULL)
                        goto out;
 
@@ -1540,11 +1454,11 @@ _cairo_image_surface_create_from_xcf (GInputStream  *istream,
 
                /* mask width, height and name */
 
-               /* mask_width = */ g_data_input_stream_read_uint32 (data_stream, cancellable, error);
+               /*mask_width = */ g_data_input_stream_read_uint32 (data_stream, cancellable, error);
                if (*error != NULL)
                        goto out;
 
-               /* mask_height = */ g_data_input_stream_read_uint32 (data_stream, cancellable, error);
+               /*mask_height = */ g_data_input_stream_read_uint32 (data_stream, cancellable, error);
                if (*error != NULL)
                        goto out;
 
@@ -1581,7 +1495,6 @@ _cairo_image_surface_create_from_xcf (GInputStream  *istream,
                                break;
                        }
                }
-               /*g_print ("  mask properties: %d\n", n_properties);*/
 
                /* the mask image */
 
@@ -1589,25 +1502,25 @@ _cairo_image_surface_create_from_xcf (GInputStream  *istream,
                if (*error != NULL)
                        goto out;
 
-               /*g_print ("  mask hierarchy offset: %" G_GUINT32_FORMAT "\n", hierarchy_offset);*/
-
-               layer->mask = _cairo_image_surface_create_from_hierarchy (data_stream, hierarchy_offset, 
layer, colormap, base_type, compression, cancellable, error);
+               layer->alpha_mask = read_pixels_from_hierarchy (data_stream, hierarchy_offset, layer, 
colormap, base_type, compression, TRUE, cancellable, error);
                if (*error != NULL)
                        goto out;
        }
 
+       performance (DEBUG_INFO, "end read layers");
+
        surface = _cairo_image_surface_create_from_layers (canvas_width, canvas_height, base_type, layers);
        image = gth_image_new_for_surface (surface);
        cairo_surface_destroy (surface);
 
+       performance (DEBUG_INFO, "end rendering");
+
 out:
 
-       if (layers != NULL)
-               g_list_free_full (layers, (GDestroyNotify) gimp_layer_free);
+       g_list_free_full (layers, (GDestroyNotify) gimp_layer_free);
        if (layer_offsets != NULL)
                g_array_free (layer_offsets, TRUE);
-       if (colormap != NULL)
-               g_free (colormap);
+       g_free (colormap);
        if (data_stream != NULL)
                g_object_unref (data_stream);
 


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