diff options
-rw-r--r-- | Makefile | 14 | ||||
-rw-r--r-- | io-psd.c | 251 |
2 files changed, 265 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4f667b4 --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +# ok, our Makefile slightly sucks at the moment + +CC = gcc +CFLAGS=-Wall -std=c99 + +all: + $(CC) $(CFLAGS) io-psd.c -o libpixbufloader-psd.so \ + `pkg-config --cflags gtk+-2.0` \ + -shared -fpic -DGDK_PIXBUF_ENABLE_BACKEND + +# and then run the following as root: +# gdk-pixbuf-query-loaders libpixbufloader-psd.so >> /etc/gtk-2.0/gdk-pixbuf.loaders + + diff --git a/io-psd.c b/io-psd.c new file mode 100644 index 0000000..664d42f --- /dev/null +++ b/io-psd.c @@ -0,0 +1,251 @@ +/* -*- mode: C; c-file-style: "linux" -*- */ +/* GdkPixbuf library - PSD image loader + * + * Copyright (C) 2008 Jan Dudek + * + * Authors: Jan Dudek <jd@jandudek.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <gdk-pixbuf/gdk-pixbuf-io.h> +#include <glib/gstdio.h> + + +typedef struct +{ + guchar signature[4]; /* file ID, always "8BPS" */ + guint16 version; /* version number, always 1 */ + guchar reserved[6]; + guint8 channels; /* number of color channels (1-24) */ + guint32 rows; /* height of image in pixels (1-30000) */ + guint32 columns; /* width of image in pixels (1-30000) */ + guint16 depth; /* number of bits per channel (1, 8, and 16) */ + guint16 color_mode; /* color mode as defined below */ +} PsdHeader; + +typedef enum +{ + PSD_MODE_MONO = 0, + PSD_MODE_GRAYSCALE = 1, + PSD_MODE_INDEXED = 2, + PSD_MODE_RGB = 3, + PSD_MODE_CMYK = 4, + PSD_MODE_MULTICHANNEL = 7, + PSD_MODE_DUOTONE = 8, + PSD_MODE_LAB = 9, +} PsdColorMode; + +typedef enum +{ + PSD_COMPRESSION_NONE = 0, + PSD_COMPRESSION_RLE = 1 +} PsdCompressionType; + +static guint16 +read_uint16 (FILE *fp) +{ + guint16 t; + t = fgetc(fp) << 8; + t |= fgetc(fp); + return t; +} + +static guint32 +read_uint32 (FILE *fp) +{ + guint32 t; + t = fgetc(fp) << 24; + t |= fgetc(fp) << 16; + t |= fgetc(fp) << 8; + t |= fgetc(fp); + return t; +} + +static PsdHeader +psd_read_header (FILE *fp) +{ + PsdHeader hd; + int t; + + fread(hd.signature, 1, 4, fp); + hd.version = read_uint16(fp); + fread(hd.reserved, 1, 6, fp); + hd.channels = read_uint16(fp); + hd.rows = read_uint32(fp); + hd.columns = read_uint32(fp); + hd.depth = read_uint16(fp); + hd.color_mode = read_uint16(fp); + + // skip Color Mode Data Block + t = read_uint32(fp); + fseek(fp, t, SEEK_CUR); + + // skip Image Resources Block + t = read_uint32(fp); + fseek(fp, t, SEEK_CUR); + + // skip Layer and Mask Information Block + t = read_uint32(fp); + fseek(fp, t, SEEK_CUR); + + return hd; +} + +static GdkPixbuf* +gdk_pixbuf__psd_image_load (FILE *fp, + GError **error) +{ + guint rowstride; + guint16 compression_type; + guchar *pixels; + GdkPixbuf *pixbuf; + guchar **buffers; + + PsdHeader hd = psd_read_header(fp); + pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, hd.columns, hd.rows); + + if (pixbuf == NULL) { + g_set_error (error, GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, + ("Insufficient memory to load PSD image file")); + return NULL; + } + + pixels = gdk_pixbuf_get_pixels (pixbuf); + rowstride = gdk_pixbuf_get_rowstride (pixbuf); + + compression_type = read_uint16(fp); + + if (compression_type != PSD_COMPRESSION_NONE && + compression_type != PSD_COMPRESSION_RLE) { + g_set_error (error, GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_UNKNOWN_TYPE, + ("Unsupported compression type")); + return NULL; + } + + if (hd.color_mode != PSD_MODE_RGB) { + g_set_error (error, GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_UNKNOWN_TYPE, + ("Unsupported color mode")); + return NULL; + } + + //g_message("mode=%d, channels=%d", hd.color_mode, hd.channels); + + buffers = g_malloc(sizeof(guchar*) * hd.channels); + + if (compression_type == PSD_COMPRESSION_RLE) { + guint16 *line_lengths = g_malloc(2 * hd.rows * hd.channels); + + for (int i = 0; i < hd.rows * hd.channels; ++i) { + line_lengths[i] = read_uint16(fp); + } + + for (int i = 0; i < hd.channels; ++i) { + buffers[i] = g_malloc(hd.rows * hd.columns); + gint position = 0; + + for (int j = 0; j < hd.rows; ++j) { + guint16 bytes_read = 0; + while (bytes_read < line_lengths[i * hd.rows + j]) { + gchar byte = fgetc(fp); + ++bytes_read; + + if (byte == -128) { + continue; + } else if (byte > -1) { + gint count = byte + 1; + + // copy next count bytes + for (int k = 0; k < count; ++k) { + buffers[i][position] = fgetc(fp); + ++bytes_read; + ++position; + } + } else { + gint count = -byte + 1; + + // copy next byte count times + guchar next_byte = fgetc(fp); + ++bytes_read; + for (int k = 0; k < count; ++k) { + buffers[i][position] = next_byte; + ++position; + } + } + } + } + } + + g_free(line_lengths); + } + + if (hd.color_mode == PSD_MODE_RGB) { + for (int i = 0; i < hd.rows; ++i) { + for (int j = 0; j < hd.columns; ++j) { + pixels[i * rowstride + 3 * j + 0] = buffers[0][i * hd.columns + j]; + pixels[i * rowstride + 3 * j + 1] = buffers[1][i * hd.columns + j]; + pixels[i * rowstride + 3 * j + 2] = buffers[2][i * hd.columns + j]; + } + } + } + // TODO: other color modes, CMYK at least + + return pixbuf; +} + +void +fill_vtable (GdkPixbufModule* module) +{ +/* module->load = gdk_pixbuf__xbm_image_load; + module->begin_load = gdk_pixbuf__xbm_image_begin_load; + module->stop_load = gdk_pixbuf__xbm_image_stop_loads + module->load_increment = gdk_pixbuf__xbm_image_load_increment;*/ + // TODO progressive loading + + module->load = gdk_pixbuf__psd_image_load; +} + +void +fill_info (GdkPixbufFormat *info) +{ + static GdkPixbufModulePattern signature[] = { + { "8BPS", NULL, 100 }, + { NULL, NULL, 0 } + }; + static gchar * mime_types[] = { + "image/x-psd", + NULL + }; + static gchar * extensions[] = { + "psd", + NULL + }; + + info->name = "psd"; + info->signature = signature; + //info->description = N_("Adobe Photoshop format"); + info->description = "Adobe Photoshop format"; + info->mime_types = mime_types; + info->extensions = extensions; + info->flags = GDK_PIXBUF_FORMAT_THREADSAFE; + info->flags = 0; + info->license = "LGPL"; +} + |