diff options
-rw-r--r-- | io-psd.c | 301 |
1 files changed, 75 insertions, 226 deletions
@@ -26,6 +26,7 @@ * - report errors from parse_psd_header * - other color modes * - ... + * - MODULE_ENTRY stuff */ #include <stdlib.h> @@ -39,7 +40,7 @@ typedef struct { guchar signature[4]; /* file ID, always "8BPS" */ guint16 version; /* version number, always 1 */ - guchar reserved[6]; + guchar resetved[6]; guint16 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) */ @@ -95,7 +96,6 @@ typedef struct guint32 bytes_to_skip; gboolean bytes_to_skip_known; - //PsdHeader hd; guint32 width; /* width of image in pixels (1-30000) */ guint32 height; /* height of image in pixels (1-30000) */ guint16 channels; /* number of color channels (1-24) */ @@ -103,79 +103,31 @@ typedef struct PsdColorMode color_mode; PsdCompressionType compression; - guchar** channels_buffers; - guint current_channel; - guint current_row; - guint position; // ? redundant? + guchar** ch_bufs; /* channels buffers */ + guint curr_ch; /* current channel */ + guint curr_row; + guint pos; // redundant? guint16* lines_lengths; gboolean finalized; } PsdContext; -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 guint16 -parse_uint16 (guchar* buf) +read_uint16 (guchar* buf) { return (buf[0] << 8) | buf[1]; } static guint32 -parse_uint32 (guchar* buf) +read_uint32 (guchar* buf) { return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; } -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; -} - /* * Parse Psdheader from buffer + * * str is expected to be at least PSD_HEADER_SIZE long */ static PsdHeader @@ -184,130 +136,22 @@ psd_parse_header (guchar* str) PsdHeader hd; memcpy(hd.signature, str, 4); - hd.version = parse_uint16(str + 4); - hd.channels = parse_uint16(str + 12); - hd.rows = parse_uint32(str + 14); - hd.columns = parse_uint32(str + 18); - hd.depth = parse_uint16(str + 22); - hd.color_mode = parse_uint16(str + 24); + hd.version = read_uint16(str + 4); + hd.channels = read_uint16(str + 12); + hd.rows = read_uint32(str + 14); + hd.columns = read_uint32(str + 18); + hd.depth = read_uint16(str + 22); + hd.color_mode = read_uint16(str + 24); return hd; } -// -- non-progressive loading -------------------------------------------------- - -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; -} - - -// --- progressive loading ----------------------------------------------------- - -// Attempts to read bytes_needed bytes from data and stores them -// in buffer. -// Returns true if there were enough bytes and false otherwise -// (which means we need to call feed_buffer again) - +/* + * Attempts to read bytes_needed bytes from data and stores them in buffer. + * + * Returns true if there were enough bytes and false otherwise + * (which means we need to call feed_buffer again) + */ static gboolean feed_buffer (guchar* buffer, guint* bytes_read, @@ -326,10 +170,12 @@ feed_buffer (guchar* buffer, return (*bytes_read == bytes_needed); } -// Attempts to read size of the block and then skip this block. -// Returns true when finishes consuming block data, otherwise false -// (false means we must call skip_block once again) - +/* + * Attempts to read size of the block and then skip this block. + * + * Returns true when finishes consuming block data, otherwise false + * (false means we must call skip_block once again) + */ static gboolean skip_block (PsdContext* context, const guchar** data, guint* size) { @@ -338,7 +184,7 @@ skip_block (PsdContext* context, const guchar** data, guint* size) if (!context->bytes_to_skip_known) { context->bytes_read = 0; if (feed_buffer(context->buffer, &context->bytes_read, data, size, 4)) { - context->bytes_to_skip = parse_uint32(context->buffer); + context->bytes_to_skip = read_uint32(context->buffer); context->bytes_to_skip_known = TRUE; counter = 0; } else { @@ -359,7 +205,9 @@ skip_block (PsdContext* context, const guchar** data, guint* size) } } -// Decodes RLE-compressed data +/* + * Decodes RLE-compressed data + */ static void decompress_line(const guchar* src, guint line_length, guchar* dest) { @@ -394,7 +242,7 @@ decompress_line(const guchar* src, guint line_length, guchar* dest) } static void -reset_context(PsdContext* ctx) +reset_context_buffer(PsdContext* ctx) { ctx->bytes_read = 0; ctx->bytes_to_skip = 0; @@ -426,12 +274,12 @@ gdk_pixbuf__psd_image_begin_load (GdkPixbufModuleSizeFunc size_func, // we'll allocate larger buffer once we know image size context->buffer = g_malloc(PSD_HEADER_SIZE); - reset_context(context); + reset_context_buffer(context); - context->channels_buffers = NULL; - context->current_channel = 0; - context->current_row = 0; - context->position = 0; + context->ch_bufs = NULL; + context->curr_ch = 0; + context->curr_row = 0; + context->pos = 0; context->lines_lengths = NULL; context->finalized = FALSE; @@ -449,12 +297,16 @@ gdk_pixbuf__psd_image_stop_load (gpointer context_ptr, GError **error) error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, - ("PSD file was corrupted or incomplete. (not PSD_STATE_DONE)")); + ("PSD file was corrupted or incomplete.")); retval = FALSE; } - g_free (ctx->buffer); // TODO a few more buffers need freeing - g_free (ctx); + g_free(ctx->buffer); + g_free(ctx->lines_lengths); + for (int i = 0; i < ctx->channels; i++) { + g_free(ctx->ch_bufs[i]); + } + g_free(ctx); return retval; } @@ -467,14 +319,13 @@ gdk_pixbuf__psd_image_load_increment (gpointer context_ptr, GError **error) { - PsdContext* context = (PsdContext*) context_ptr; - PsdContext* ctx = context; + PsdContext* ctx = (PsdContext*) context_ptr; while (size > 0) { - switch (context->state) { + switch (ctx->state) { case PSD_STATE_HEADER: if (feed_buffer( - context->buffer, &context->bytes_read, + ctx->buffer, &ctx->bytes_read, &data, &size, PSD_HEADER_SIZE)) { PsdHeader hd = psd_parse_header(ctx->buffer); @@ -485,8 +336,8 @@ gdk_pixbuf__psd_image_load_increment (gpointer context_ptr, ctx->depth = hd.depth; ctx->color_mode = hd.color_mode; - g_message("color_mode=%d, channels=%d, depth=%d", - ctx->color_mode, ctx->channels, ctx->depth); + //g_message("color_mode=%d, channels=%d, depth=%d", + // ctx->color_mode, ctx->channels, ctx->depth); if (ctx->color_mode != PSD_MODE_RGB/* && ctx->color_mode != PSD_MODE_CMYK*/ @@ -535,13 +386,12 @@ gdk_pixbuf__psd_image_load_increment (gpointer context_ptr, } // create separate buffers for each channel - context->channels_buffers = - g_malloc(sizeof(guchar*) * ctx->channels); + ctx->ch_bufs = g_malloc(sizeof(guchar*) * ctx->channels); for (int i = 0; i < ctx->channels; i++) { - ctx->channels_buffers[i] = + ctx->ch_bufs[i] = g_malloc(ctx->width * ctx->height); - if (ctx->channels_buffers[i] == NULL) { + if (ctx->ch_bufs[i] == NULL) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, ("Insufficient memory to load PSD image file")); @@ -552,35 +402,35 @@ gdk_pixbuf__psd_image_load_increment (gpointer context_ptr, ctx->prepared_func(ctx->pixbuf, NULL, ctx->user_data); ctx->state = PSD_STATE_COLOR_MODE_BLOCK; - reset_context(ctx); + reset_context_buffer(ctx); } break; case PSD_STATE_COLOR_MODE_BLOCK: if (skip_block(ctx, &data, &size)) { ctx->state = PSD_STATE_RESOURCES_BLOCK; - reset_context(ctx); + reset_context_buffer(ctx); } break; case PSD_STATE_RESOURCES_BLOCK: if (skip_block(ctx, &data, &size)) { ctx->state = PSD_STATE_LAYERS_BLOCK; - reset_context(ctx); + reset_context_buffer(ctx); } break; case PSD_STATE_LAYERS_BLOCK: if (skip_block(ctx, &data, &size)) { ctx->state = PSD_STATE_COMPRESSION; - reset_context(ctx); + reset_context_buffer(ctx); } break; case PSD_STATE_COMPRESSION: if (feed_buffer(ctx->buffer, &ctx->bytes_read, &data, &size, 2)) { - ctx->compression = parse_uint16(ctx->buffer); + ctx->compression = read_uint16(ctx->buffer); if (ctx->compression == PSD_COMPRESSION_RLE) { ctx->state = PSD_STATE_LINES_LENGTHS; - reset_context(ctx); + reset_context_buffer(ctx); } else if (ctx->compression == PSD_COMPRESSION_NONE) { ctx->state = PSD_STATE_CHANNEL_DATA; } else { @@ -598,11 +448,11 @@ gdk_pixbuf__psd_image_load_increment (gpointer context_ptr, { // convert from different endianness for (int i = 0; i < ctx->height * ctx->channels; i++) { - ctx->lines_lengths[i] = parse_uint16( + ctx->lines_lengths[i] = read_uint16( (guchar*) &ctx->lines_lengths[i]); } ctx->state = PSD_STATE_CHANNEL_DATA; - reset_context(ctx); + reset_context_buffer(ctx); } break; case PSD_STATE_CHANNEL_DATA: @@ -610,32 +460,31 @@ gdk_pixbuf__psd_image_load_increment (gpointer context_ptr, guint line_length = ctx->width; if (ctx->compression == PSD_COMPRESSION_RLE) { line_length = ctx->lines_lengths[ - ctx->current_channel * ctx->height + ctx->current_row]; + ctx->curr_ch * ctx->height + ctx->curr_row]; } if (feed_buffer(ctx->buffer, &ctx->bytes_read, &data, &size, line_length)) { - ctx->bytes_read = 0; + reset_context_buffer(ctx); if (ctx->compression == PSD_COMPRESSION_RLE) { decompress_line(ctx->buffer, line_length, - ctx->channels_buffers[ctx->current_channel] - + ctx->position + ctx->ch_bufs[ctx->curr_ch] + ctx->pos ); } else { - memcpy(ctx->channels_buffers[ctx->current_channel] - + ctx->position, ctx->buffer, ctx->width); + memcpy(ctx->ch_bufs[ctx->curr_ch] + ctx->pos, + ctx->buffer, ctx->width); } - ctx->position += ctx->width; - ++ctx->current_row; + ctx->pos += ctx->width; + ++ctx->curr_row; - if (ctx->current_row >= ctx->height) { - ++ctx->current_channel; - ctx->current_row = 0; - ctx->position = 0; - if (ctx->current_channel >= ctx->channels) { + if (ctx->curr_row >= ctx->height) { + ++ctx->curr_ch; + ctx->curr_row = 0; + ctx->pos = 0; + if (ctx->curr_ch >= ctx->channels) { ctx->state = PSD_STATE_DONE; } } @@ -655,9 +504,9 @@ gdk_pixbuf__psd_image_load_increment (gpointer context_ptr, guchar* pixels = gdk_pixbuf_get_pixels(ctx->pixbuf); for (int i = 0; i < ctx->height; i++) { for (int j = 0; j < ctx->width; j++) { - pixels[3*j+0] = ctx->channels_buffers[0][ctx->width*i + j]; - pixels[3*j+1] = ctx->channels_buffers[1][ctx->width*i + j]; - pixels[3*j+2] = ctx->channels_buffers[2][ctx->width*i + j]; + pixels[3*j+0] = ctx->ch_bufs[0][ctx->width*i + j]; + pixels[3*j+1] = ctx->ch_bufs[1][ctx->width*i + j]; + pixels[3*j+2] = ctx->ch_bufs[2][ctx->width*i + j]; } pixels += gdk_pixbuf_get_rowstride(ctx->pixbuf); } |