[gthumb] added ability load xcf images
- From: Paolo Bacchilega <paobac src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gthumb] added ability load xcf images
- Date: Wed, 13 Mar 2013 12:03:26 +0000 (UTC)
commit f2d72bec06accae82297272f0899afb23c007823
Author: Paolo Bacchilega <paobac src gnome org>
Date: Wed Mar 13 12:59:21 2013 +0100
added ability load xcf images
[new feature]
extensions/cairo_io/Makefile.am | 2 +
extensions/cairo_io/cairo-image-surface-xcf.c | 1646 ++++++++++++++++++++
extensions/cairo_io/cairo-image-surface-xcf.h | 41 +
extensions/cairo_io/main.c | 6 +
.../image_viewer/gth-metadata-provider-image.c | 34 +
po/POTFILES.in | 2 +
6 files changed, 1731 insertions(+), 0 deletions(-)
---
diff --git a/extensions/cairo_io/Makefile.am b/extensions/cairo_io/Makefile.am
index db0a956..efcdfcf 100644
--- a/extensions/cairo_io/Makefile.am
+++ b/extensions/cairo_io/Makefile.am
@@ -32,6 +32,8 @@ enum-types.c: $(HEADER_FILES)
libcairo_io_la_SOURCES = \
cairo-image-surface-png.c \
cairo-image-surface-png.h \
+ cairo-image-surface-xcf.c \
+ cairo-image-surface-xcf.h \
gth-image-saver-png.c \
gth-image-saver-png.h \
gth-image-saver-tga.c \
diff --git a/extensions/cairo_io/cairo-image-surface-xcf.c b/extensions/cairo_io/cairo-image-surface-xcf.c
new file mode 100644
index 0000000..cc6ee50
--- /dev/null
+++ b/extensions/cairo_io/cairo-image-surface-xcf.c
@@ -0,0 +1,1646 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2013 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <gio/gio.h>
+#include <gthumb.h>
+#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)))
+
+
+typedef enum {
+ GIMP_RGB,
+ GIMP_GRAY,
+ GIMP_INDEXED
+} GimpImageBaseType;
+
+
+typedef enum {
+ GIMP_COMPRESSION_NONE,
+ GIMP_COMPRESSION_RLE,
+ GIMP_COMPRESSION_ZLIB,
+ GIMP_COMPRESSION_FRACTAL
+} GimpCompression;
+
+
+typedef enum {
+ GIMP_RGB_IMAGE,
+ GIMP_RGBA_IMAGE,
+ GIMP_GRAY_IMAGE,
+ GIMP_GRAYA_IMAGE,
+ GIMP_INDEXED_IMAGE,
+ GIMP_INDEXEDA_IMAGE
+} GimpImageType;
+
+
+typedef enum {
+ GIMP_LAYER_MODE_NORMAL,
+ GIMP_LAYER_MODE_DISSOLVE, /* (random dithering to discrete alpha) */
+ GIMP_LAYER_MODE_BEHIND, /* (not selectable in the GIMP UI) */
+ GIMP_LAYER_MODE_MULTIPLY,
+ GIMP_LAYER_MODE_SCREEN,
+ GIMP_LAYER_MODE_OVERLAY,
+ GIMP_LAYER_MODE_DIFFERENCE,
+ GIMP_LAYER_MODE_ADDITION,
+ GIMP_LAYER_MODE_SUBTRACT,
+ GIMP_LAYER_MODE_DARKEN_ONLY,
+ GIMP_LAYER_MODE_LIGHTEN_ONLY,
+ GIMP_LAYER_MODE_HUE, /* (H of HSV) */
+ GIMP_LAYER_MODE_SATURATION, /* (S of HSV) */
+ GIMP_LAYER_MODE_COLOR, /* (H and S of HSL) */
+ GIMP_LAYER_MODE_VALUE, /* (V of HSV) */
+ GIMP_LAYER_MODE_DIVIDE,
+ GIMP_LAYER_MODE_DODGE,
+ GIMP_LAYER_MODE_BURN,
+ GIMP_LAYER_MODE_HARD_LIGHT,
+ GIMP_LAYER_MODE_SOFT_LIGHT, /* (XCF version >= 2 only) */
+ GIMP_LAYER_MODE_GRAIN_EXTRACT, /* (XCF version >= 2 only) */
+ GIMP_LAYER_MODE_GRAIN_MERGE /* (XCF version >= 2 only) */
+} GimpLayerMode;
+
+
+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;
+ struct {
+ gboolean dirty;
+ int rows;
+ int columns;
+ int n_tiles;
+ int last_row_height;
+ int last_col_width;
+ } tiles;
+ int stride;
+} GimpLayer;
+
+
+typedef struct {
+ guchar color1;
+ guchar color2;
+ guchar color3;
+} 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_indexed[2] = { 0, CAIRO_ALPHA };
+
+
+/* -- GDataInputStream functions -- */
+
+
+static gsize
+_g_data_input_stream_read_byte_array (GDataInputStream *stream,
+ guchar *byte_array,
+ gsize size,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GError *local_error = NULL;
+ gsize i;
+
+ for (i = 0; i < size; i++) {
+ guchar byte;
+
+ byte = g_data_input_stream_read_byte (stream, cancellable, &local_error);
+ if (local_error != NULL) {
+ /* ignore end-of-stream errors */
+ if (local_error->code != G_IO_ERROR_FAILED)
+ g_propagate_error (error, local_error);
+ else
+ g_error_free (local_error);
+ break;
+ }
+
+ byte_array[i] = byte;
+ }
+
+ return i;
+}
+
+
+static char *
+_g_data_input_stream_read_c_string (GDataInputStream *stream,
+ gsize size,
+ GCancellable *cancellable,
+ GError **error)
+{
+ char *string;
+ int i;
+
+ g_return_val_if_fail (size > 0, NULL);
+
+ string = g_new0 (char, size + 1);
+ for (i = 0; i < size; i++)
+ string[i] = (char) g_data_input_stream_read_byte (stream, cancellable, error);
+
+ return string;
+}
+
+
+static char *
+_g_data_input_stream_read_xcf_string (GDataInputStream *stream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ guint32 n_bytes;
+
+ n_bytes = g_data_input_stream_read_uint32 (stream, cancellable, error);
+ if (n_bytes == 0)
+ return NULL;
+
+ return _g_data_input_stream_read_c_string (stream, n_bytes, cancellable, error);
+}
+
+
+/* -- GimpLayer -- */
+
+
+static GimpLayer *
+gimp_layer_new (void)
+{
+ GimpLayer *layer;
+
+ layer = g_new0 (GimpLayer, 1);
+ layer->width = 0;
+ layer->height = 0;
+ layer->type = GIMP_RGBA_IMAGE;
+ layer->name = NULL;
+ layer->opacity = 255;
+ layer->visible = TRUE;
+ layer->floating_selection = FALSE;
+ layer->mode = GIMP_LAYER_MODE_NORMAL;
+ layer->apply_mask = FALSE;
+ layer->h_offset = 0;
+ layer->v_offset = 0;
+ layer->image = NULL;
+ layer->mask = NULL;
+ layer->tiles.dirty = TRUE;
+
+ return layer;
+}
+
+
+static gboolean
+gimp_layer_get_tile_size (GimpLayer *layer,
+ int n_tile,
+ goffset *offset,
+ int *width,
+ int *height)
+{
+ int tile_row, tile_column;
+ gsize tile_width;
+ gsize tile_height;
+
+ if (layer->tiles.dirty) {
+ layer->tiles.last_col_width = layer->width % TILE_WIDTH;
+ layer->tiles.last_row_height = layer->height % TILE_WIDTH;
+
+ layer->tiles.columns = layer->width / TILE_WIDTH;
+ if (layer->tiles.last_col_width > 0)
+ layer->tiles.columns++;
+ else
+ layer->tiles.last_col_width = TILE_WIDTH;
+
+ layer->tiles.rows = layer->height / TILE_WIDTH;
+ if (layer->tiles.last_row_height > 0)
+ layer->tiles.rows++;
+ else
+ layer->tiles.last_row_height = TILE_WIDTH;
+
+ 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);
+ }
+
+ if ((n_tile < 0) || (n_tile >= layer->tiles.n_tiles))
+ return FALSE;
+
+ tile_column = (n_tile % layer->tiles.columns);
+ if (tile_column == layer->tiles.columns - 1)
+ tile_width = layer->tiles.last_col_width;
+ else
+ tile_width = TILE_WIDTH;
+
+ tile_row = (n_tile / layer->tiles.columns);
+ if (tile_row == layer->tiles.rows - 1)
+ tile_height = layer->tiles.last_row_height;
+ else
+ tile_height = TILE_WIDTH;
+
+ *offset = ((tile_row * TILE_WIDTH) * layer->stride) + (tile_column * TILE_WIDTH * 4);
+ *width = tile_width;
+ *height = tile_height;
+
+ return TRUE;
+}
+
+
+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->name);
+ g_free (layer);
+}
+
+
+/* -- _cairo_image_surface_create_from_xcf -- */
+
+
+static void
+gimp_rgb_to_hsv (guchar red,
+ guchar green,
+ guchar blue,
+ guchar *hue,
+ guchar *sat,
+ guchar *val)
+{
+ guchar min, max;
+
+ min = MIN3 (red, green, blue);
+ max = MAX3 (red, green, blue);
+
+ *val = max;
+ if (*val == 0) {
+ *hue = *sat = 0;
+ return;
+ }
+
+ *sat = 255 * (long)(max - min) / *val;
+ if (*sat == 0) {
+ *hue = 0;
+ return;
+ }
+
+ 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 void
+gimp_hsv_to_rgb (guchar hue,
+ guchar sat,
+ guchar val,
+ double *red,
+ double *green,
+ double *blue)
+{
+ guchar region, remainder, p, q, t;
+
+ if (sat == 0) {
+ *red = *green = *blue = val;
+ return;
+ }
+
+ region = hue / 43;
+ remainder = (hue - (region * 43)) * 6;
+
+ p = (val * (255 - sat)) >> 8;
+ q = (val * (255 - ((sat * remainder) >> 8))) >> 8;
+ t = (val * (255 - ((sat * (255 - remainder)) >> 8))) >> 8;
+
+ switch (region) {
+ case 0:
+ *red = val;
+ *green = t;
+ *blue = p;
+ break;
+ case 1:
+ *red = q;
+ *green = val;
+ *blue = p;
+ break;
+ case 2:
+ *red = p;
+ *green = val;
+ *blue = t;
+ break;
+ case 3:
+ *red = p;
+ *green = q;
+ *blue = val;
+ break;
+ case 4:
+ *red = t;
+ *green = p;
+ *blue = val;
+ break;
+ default:
+ *red = val;
+ *green = p;
+ *blue = q;
+ break;
+ }
+}
+
+
+static void
+_cairo_image_surface_paint_layer (cairo_surface_t *image,
+ GimpLayer *layer)
+{
+ int image_width;
+ int image_height;
+ int image_row_stride;
+ guchar *image_row;
+ int layer_width;
+ int layer_height;
+ int layer_row_stride;
+ guchar *layer_row;
+ int x, y, width, height;
+ guchar *image_pixel;
+ guchar *layer_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;
+
+ if ((image == NULL) || (layer->image == NULL))
+ return;
+
+ cairo_surface_flush (image);
+
+ image_width = cairo_image_surface_get_width (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);
+
+ /* compute the layer <-> image intersection */
+
+ {
+ cairo_region_t *region;
+ cairo_rectangle_int_t rect;
+
+ rect.x = 0;
+ rect.y = 0;
+ rect.width = image_width;
+ rect.height = image_height;
+ region = cairo_region_create_rectangle (&rect);
+
+ rect.x = layer->h_offset;
+ rect.y = layer->v_offset;
+ rect.width = layer_width;
+ rect.height = layer_height;
+ cairo_region_intersect_rectangle (region, &rect);
+ cairo_region_get_extents (region, &rect);
+ cairo_region_destroy (region);
+
+ if ((rect.width == 0) || (rect.height == 0))
+ return;
+
+ x = rect.x;
+ y = rect.y;
+ width = rect.width;
+ height = rect.height;
+ }
+
+ 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);
+
+ /*g_print ("layer: (%d, %d) [%d, %d]\n", x, y, width, height);*/
+
+ if (layer->mode == GIMP_LAYER_MODE_DISSOLVE)
+ rand_gen = g_rand_new_with_seed (DISSOLVE_SEED);
+
+ for (i = 0; i < height; i++) {
+ image_pixel = image_row;
+ layer_pixel = layer_row;
+
+ for (j = 0; j < width; j++) {
+ layer_opacity = (double) layer->opacity / 255.0;
+
+ 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];
+
+ layer_a = a / 255.0;
+
+ switch (layer->mode) {
+ 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;
+ }
+ 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);
+ }
+ 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);
+ }
+ 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;
+ }
+
+ r *= layer_a;
+ g *= layer_a;
+ b *= 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);
+ }
+ 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);
+ 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);
+ 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);
+ break;
+ }
+
+ image_pixel += 4;
+ layer_pixel += 4;
+ }
+
+ image_row += image_row_stride;
+ layer_row += layer_row_stride;
+ }
+
+ if (layer->mode == GIMP_LAYER_MODE_DISSOLVE)
+ g_rand_free (rand_gen);
+
+ cairo_surface_mark_dirty (image);
+}
+
+
+static cairo_surface_t *
+_cairo_image_surface_create_from_layers (int canvas_width,
+ int canvas_height,
+ GimpImageBaseType base_type,
+ GList *layers)
+{
+ cairo_surface_t *image;
+ GList *scan;
+
+ 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;
+
+ if (layer->image == 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))
+ layer->mode = GIMP_LAYER_MODE_NORMAL;
+
+ /* indexed images only support NORMAL and DISSOLVE */
+
+ if ((base_type == GIMP_INDEXED) && (layer->mode != GIMP_LAYER_MODE_DISSOLVE))
+ layer->mode = GIMP_LAYER_MODE_NORMAL;
+
+ /* modes supported only by RGB images */
+
+ if (base_type != GIMP_RGB) {
+ if ((layer->mode == GIMP_LAYER_MODE_HUE)
+ || (layer->mode == GIMP_LAYER_MODE_SATURATION)
+ || (layer->mode == GIMP_LAYER_MODE_COLOR)
+ || (layer->mode == GIMP_LAYER_MODE_VALUE))
+ {
+ layer->mode = GIMP_LAYER_MODE_NORMAL;
+ }
+ }
+
+ /* find the cairo operation equivalent to the layer mode */
+
+ 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);
+ }
+
+ return image;
+}
+
+
+static void
+_cairo_surface_data_post_production (guchar *image_pixels,
+ int row_stride,
+ int width,
+ int height,
+ int bpp,
+ GimpImageBaseType base_type)
+{
+ 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;
+
+ /* read the hierarchy structure */
+
+ if (! g_seekable_seek (G_SEEKABLE (data_stream),
+ hierarchy_offset,
+ G_SEEK_SET,
+ cancellable,
+ error))
+ {
+ return NULL;
+ }
+
+ width = g_data_input_stream_read_uint32 (data_stream, cancellable, error);
+ if (*error != NULL)
+ goto read_error;
+
+ height = g_data_input_stream_read_uint32 (data_stream, cancellable, error);
+ if (*error != NULL)
+ goto read_error;
+
+ bpp = g_data_input_stream_read_uint32 (data_stream, cancellable, error);
+ if (*error != NULL)
+ goto read_error;
+
+ level_offset = g_data_input_stream_read_uint32 (data_stream, cancellable, error);
+ 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);
+ */
+
+ /* read the level structure */
+
+ if (! g_seekable_seek (G_SEEKABLE (data_stream),
+ level_offset,
+ G_SEEK_SET,
+ cancellable,
+ error))
+ {
+ goto read_error;
+ }
+
+ width = g_data_input_stream_read_uint32 (data_stream, cancellable, error);
+ if (*error != NULL)
+ goto read_error;
+
+ height = g_data_input_stream_read_uint32 (data_stream, cancellable, error);
+ 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);
+
+ tile_offsets = g_array_new (FALSE, FALSE, sizeof (guint32));;
+ n_tiles = 0;
+ last_tile_offset = 0;
+ while ((tile_offset = g_data_input_stream_read_uint32 (data_stream, cancellable, error)) != 0) {
+ 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);
+
+ if (*error != NULL)
+ goto read_error;
+
+ if (compression == GIMP_COMPRESSION_RLE) {
+
+ /* 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;
+ guchar *tile_data_limit;
+ gsize data_read;
+ goffset tile_pixels_offset;
+ gsize tile_pixels_size;
+ int tile_width;
+ int tile_height;
+ int c;
+
+ /* 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));*/
+ continue;
+ }
+
+ tile_data = g_malloc (tile_data_size);
+ data_read = _g_data_input_stream_read_byte_array (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,
+ &tile_pixels_offset,
+ &tile_width,
+ &tile_height))
+ {
+ /*g_print ("<tile %d: error>", t);*/
+ goto rle_error;
+ }
+
+ tile_pixels_size = tile_width * tile_height;
+ tile_data_p = tile_data;
+ tile_data_limit = tile_data + data_read - 1;
+
+ for (c = 0; c < bpp; c++) {
+ int channel_offset;
+ guchar *pixels_row;
+ guchar *pixels;
+ int size;
+ int n, p, q, v;
+ int tile_column;
+
+ if (base_type == GIMP_INDEXED)
+ channel_offset = cairo_indexed[c];
+ else if (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 */
+ channel_offset = cairo_graya[c];
+ else
+ channel_offset = 0;
+ pixels_row = image_pixels + tile_pixels_offset + channel_offset;
+ pixels = 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; \
+}
+
+ while (size > 0) {
+ if (tile_data_p > tile_data_limit)
+ goto rle_error;
+
+ n = *tile_data_p++;
+
+ if ((n >= 0) && (n <= 127)) {
+ /* byte n For 0 <= n <= 126: a short run of
identical bytes
+ * byte v Repeat this value n+1 times
+ */
+
+ /* byte 127 A long run of identical bytes
+ * byte p
+ * byte q
+ * byte v Repeat this value p*256 + q times
+ */
+
+ if (n == 127) {
+ if (tile_data_p + 2 > tile_data_limit)
+ goto rle_error;
+ p = *tile_data_p++;
+ q = *tile_data_p++;
+ v = *tile_data_p++;
+ n = (p * 256) + q;
+ }
+ else {
+ if (tile_data_p > tile_data_limit)
+ goto rle_error;
+ v = *tile_data_p++;
+ n++;
+ }
+
+ size -= n;
+ if (size < 0)
+ goto rle_error;
+
+ while (n-- > 0)
+ SET_PIXEL (v);
+ }
+ else if ((n >= 128) && (n <= 255)) {
+ /* byte 128 A long run of different bytes
+ * byte p
+ * byte q
+ * byte[p*256+q] data Copy these verbatim to the output
stream */
+
+ /* byte n For 129 <= n <= 255: a short run of
different bytes
+ * byte[256-n] data Copy these verbatim to the output
stream */
+
+ if (n == 128) {
+ if (tile_data_p + 1 > tile_data_limit)
+ goto rle_error;
+ p = *tile_data_p++;
+ q = *tile_data_p++;
+ n = (p * 256) + q;
+ }
+ else
+ n = 256 - n;
+
+ if (tile_data_p + n - 1 > tile_data_limit)
+ goto rle_error;
+
+ size -= n;
+ if (size < 0)
+ goto rle_error;
+
+ while (n-- > 0)
+ SET_PIXEL (*tile_data_p++);
+ }
+ }
+ }
+
+#undef SET_PIXEL
+
+rle_error:
+
+ g_free (tile_data);
+ if (*error != NULL)
+ goto read_error;
+ }
+ }
+ 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;
+
+ /* 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_data_input_stream_read_byte_array (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);
+
+ g_array_free (tile_offsets, TRUE);
+
+ return image;
+
+read_error:
+
+ if (image != NULL)
+ cairo_surface_destroy (image);
+ if (tile_offsets != NULL)
+ g_array_free (tile_offsets, TRUE);
+
+ return NULL;
+}
+
+
+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)
+{
+ GthImage *image = NULL;
+ cairo_surface_t *surface = NULL;
+ GDataInputStream *data_stream;
+ char *file_type;
+ char *version;
+ guint32 canvas_width;
+ guint32 canvas_height;
+ GimpImageBaseType base_type;
+ guint32 property_type;
+ guint32 payload_length;
+ GimpCompression compression;
+ GList *layers = NULL;
+ guint32 n_colors;
+ GimpColormap *colormap = NULL;
+ guint n_properties;
+ gboolean read_properties;
+ GArray *layer_offsets = NULL;
+ guint32 layer_offset;
+ guint n_layers;
+ guint32 channel_offset;
+ guint n_channels;
+ int i;
+
+ data_stream = g_data_input_stream_new (istream);
+ g_data_input_stream_set_byte_order (data_stream, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
+
+ /* file type magic */
+
+ file_type = _g_data_input_stream_read_c_string (data_stream, 9, cancellable, error);
+ if (*error != NULL)
+ goto out;
+
+ 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;
+ }
+ g_free (file_type);
+
+ /* version */
+
+ 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 */
+
+ canvas_width = g_data_input_stream_read_uint32 (data_stream, cancellable, error);
+ if (*error != NULL)
+ goto out;
+
+ canvas_height = g_data_input_stream_read_uint32 (data_stream, cancellable, error);
+ 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;
+ layers = NULL;
+ colormap = NULL;
+
+ read_properties = TRUE;
+ n_properties = 0;
+ while (read_properties) {
+ property_type = g_data_input_stream_read_uint32 (data_stream, cancellable, error);
+ if (*error != NULL)
+ goto out;
+
+ payload_length = g_data_input_stream_read_uint32 (data_stream, cancellable, error);
+ if (*error != NULL)
+ goto out;
+
+ n_properties += 1;
+
+ switch (property_type) {
+ case 0: /* PROP_END */
+ read_properties = FALSE;
+ break;
+
+ case 1: /* PROP_COLORMAP */
+ n_colors = g_data_input_stream_read_uint32 (data_stream, cancellable, error);
+ if (*error != NULL)
+ goto out;
+
+ if (base_type == GIMP_INDEXED) {
+ int i;
+ guchar *c;
+
+ colormap = g_new (GimpColormap, n_colors);
+ c = (guchar *) colormap;
+ for (i = 0; i < n_colors; i++) {
+ c[0] = g_data_input_stream_read_byte (data_stream, cancellable,
error);
+ if (*error != NULL)
+ goto out;
+
+ c[1] = g_data_input_stream_read_byte (data_stream, cancellable,
error);
+ if (*error != NULL)
+ goto out;
+
+ c[2] = g_data_input_stream_read_byte (data_stream, cancellable,
error);
+ if (*error != NULL)
+ goto out;
+
+ c += 3;
+ }
+ }
+ else { /* when skipping the colormap do not trust the payload_length value. */
+ g_input_stream_skip (G_INPUT_STREAM (data_stream), (n_colors * 3),
cancellable, error);
+ if (*error != NULL)
+ goto out;
+ }
+ break;
+
+ case 17: /* PROP_COMPRESSION */
+ 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 */
+
+ n_layers = 0;
+ layer_offsets = g_array_new (FALSE, FALSE, sizeof (guint32));
+ 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) {
+ n_channels += 1;
+ /*g_print ("channel %d: %" G_GUINT32_FORMAT "\n", n_channels, channel_offset);*/
+ }
+
+ if (*error != NULL)
+ goto out;
+
+ /* read the layers */
+
+ for (i = 0; i < n_layers; i++) {
+ GimpLayer *layer;
+ guint32 hierarchy_offset;
+ guint32 mask_offset;
+ 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,
+ G_SEEK_SET,
+ cancellable,
+ error))
+ {
+ goto out;
+ }
+
+ layer = gimp_layer_new ();
+ layers = g_list_prepend (layers, layer);
+
+ /* size */
+
+ layer->width = g_data_input_stream_read_uint32 (data_stream, cancellable, error);
+ if (*error != NULL)
+ goto out;
+
+ layer->height = g_data_input_stream_read_uint32 (data_stream, cancellable, error);
+ 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;
+ n_properties = 0;
+ while (read_properties) {
+ property_type = g_data_input_stream_read_uint32 (data_stream, cancellable, error);
+ if (*error != NULL)
+ goto out;
+
+ payload_length = g_data_input_stream_read_uint32 (data_stream, cancellable, error);
+ if (*error != NULL)
+ goto out;
+
+ n_properties += 1;
+
+ switch (property_type) {
+ case 0: /* PROP_END */
+ read_properties = FALSE;
+ break;
+
+ case 5: /* PROP_FLOATING_SELECTION */
+ layer->floating_selection = TRUE;
+ g_input_stream_skip (G_INPUT_STREAM (data_stream), payload_length,
cancellable, error);
+ break;
+
+ 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 */
+ layer->h_offset = g_data_input_stream_read_uint32 (data_stream, cancellable,
error);
+ if (*error != NULL)
+ 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:
+ g_input_stream_skip (G_INPUT_STREAM (data_stream), payload_length,
cancellable, error);
+ break;
+ }
+
+ if (*error != NULL)
+ goto out;
+ }
+ /*g_print (" properties: %d\n", n_properties);*/
+
+ /* hierarchy structure offset */
+
+ hierarchy_offset = g_data_input_stream_read_uint32 (data_stream, cancellable, error);
+ 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);
+ if (*error != NULL)
+ goto out;
+
+ /* read the mask */
+
+ if (! layer->apply_mask || (mask_offset == 0))
+ continue;
+
+ if (! g_seekable_seek (G_SEEKABLE (data_stream),
+ mask_offset,
+ G_SEEK_SET,
+ cancellable,
+ error))
+ {
+ goto out;
+ }
+
+ /* mask width, height and name */
+
+ /* 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);
+ if (*error != NULL)
+ goto out;
+
+ mask_name = _g_data_input_stream_read_xcf_string (data_stream, cancellable, error);
+ if (*error != NULL)
+ goto out;
+
+ g_free (mask_name);
+
+ /* mask properties */
+
+ read_properties = TRUE;
+ n_properties = 0;
+ while (read_properties) {
+ property_type = g_data_input_stream_read_uint32 (data_stream, cancellable, error);
+ if (*error != NULL)
+ goto out;
+
+ payload_length = g_data_input_stream_read_uint32 (data_stream, cancellable, error);
+ if (*error != NULL)
+ goto out;
+
+ n_properties += 1;
+
+ switch (property_type) {
+ case 0: /* PROP_END */
+ read_properties = FALSE;
+ break;
+
+ default:
+ g_input_stream_skip (G_INPUT_STREAM (data_stream), payload_length,
cancellable, error);
+ if (*error != NULL)
+ goto out;
+ break;
+ }
+ }
+ /*g_print (" mask properties: %d\n", n_properties);*/
+
+ /* the mask image */
+
+ hierarchy_offset = g_data_input_stream_read_uint32 (data_stream, cancellable, error);
+ 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);
+ if (*error != NULL)
+ goto out;
+ }
+
+ 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);
+
+out:
+
+ if (layers != NULL)
+ 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);
+ if (data_stream != NULL)
+ g_object_unref (data_stream);
+
+ return image;
+}
diff --git a/extensions/cairo_io/cairo-image-surface-xcf.h b/extensions/cairo_io/cairo-image-surface-xcf.h
new file mode 100644
index 0000000..3d7717e
--- /dev/null
+++ b/extensions/cairo_io/cairo-image-surface-xcf.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * GThumb
+ *
+ * Copyright (C) 2013 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CAIRO_IMAGE_SURFACE_XCF_H
+#define CAIRO_IMAGE_SURFACE_XCF_H
+
+#include <gtk/gtk.h>
+#include <gthumb.h>
+
+G_BEGIN_DECLS
+
+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);
+
+G_END_DECLS
+
+#endif /* CAIRO_IMAGE_SURFACE_XCF_H */
diff --git a/extensions/cairo_io/main.c b/extensions/cairo_io/main.c
index 27564de..7d5ece1 100644
--- a/extensions/cairo_io/main.c
+++ b/extensions/cairo_io/main.c
@@ -26,6 +26,7 @@
#include "cairo-image-surface-png.h"
#include "cairo-image-surface-svg.h"
#include "cairo-image-surface-webp.h"
+#include "cairo-image-surface-xcf.h"
#include "gth-image-saver-jpeg.h"
#include "gth-image-saver-png.h"
#include "gth-image-saver-tga.h"
@@ -69,6 +70,11 @@ gthumb_extension_activate (void)
gth_main_register_type ("image-saver", GTH_TYPE_IMAGE_SAVER_WEBP);
#endif
+ gth_main_register_image_loader_func (_cairo_image_surface_create_from_xcf,
+ GTH_IMAGE_FORMAT_CAIRO_SURFACE,
+ "image/x-xcf",
+ NULL);
+
gth_hook_add_callback ("dlg-preferences-construct", 30, G_CALLBACK
(ci__dlg_preferences_construct_cb), NULL);
gth_hook_add_callback ("dlg-preferences-apply", 10, G_CALLBACK (ci__dlg_preferences_apply_cb), NULL);
}
diff --git a/extensions/image_viewer/gth-metadata-provider-image.c
b/extensions/image_viewer/gth-metadata-provider-image.c
index 411443e..438919a 100644
--- a/extensions/image_viewer/gth-metadata-provider-image.c
+++ b/extensions/image_viewer/gth-metadata-provider-image.c
@@ -170,6 +170,40 @@ gth_metadata_provider_image_read (GthMetadataProvider *self,
}
}
#endif /* HAVE_LIBWEBP */
+
+ else if ((size >= 26)
+ && (strncmp ((char *) buffer, "gimp xcf ", 9) == 0))
+ {
+ /* XCF */
+
+ GInputStream *mem_stream;
+ GDataInputStream *data_stream;
+
+ mem_stream = g_memory_input_stream_new_from_data (buffer, BUFFER_SIZE, NULL);
+ data_stream = g_data_input_stream_new (mem_stream);
+ g_data_input_stream_set_byte_order (data_stream,
G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
+
+ if (g_seekable_seek (G_SEEKABLE (data_stream), 14, G_SEEK_SET, NULL, NULL)) {
+ int base_type;
+
+ width = g_data_input_stream_read_uint32 (data_stream, NULL, NULL);
+ height = g_data_input_stream_read_uint32 (data_stream, NULL, NULL);
+ base_type = g_data_input_stream_read_uint32 (data_stream, NULL, NULL);
+ if (base_type == 0)
+ description = "XCF RGB";
+ else if (base_type == 1)
+ description = "XCF grayscale";
+ else if (base_type == 2)
+ description = "XCF indexed";
+ else
+ description = "XCF";
+ mime_type = "image/x-xcf";
+ format_recognized = TRUE;
+ }
+
+ g_object_unref (data_stream);
+ g_object_unref (mem_stream);
+ }
}
g_free (buffer);
diff --git a/po/POTFILES.in b/po/POTFILES.in
index d746d35..26f2328 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -75,6 +75,8 @@ extensions/cairo_io/cairo-image-surface-svg.c
extensions/cairo_io/cairo-image-surface-svg.h
extensions/cairo_io/cairo-image-surface-webp.c
extensions/cairo_io/cairo-image-surface-webp.h
+extensions/cairo_io/cairo-image-surface-xcf.c
+extensions/cairo_io/cairo-image-surface-xcf.h
[type: gettext/ini]extensions/cairo_io/cairo_io.extension.in.in
[type: gettext/glade]extensions/cairo_io/data/ui/jpeg-options.ui
[type: gettext/glade]extensions/cairo_io/data/ui/png-options.ui
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]