diff --git a/ChangeLog b/ChangeLog index 77fdc49a51..90c8eca0f0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -23,6 +23,8 @@ Tue May 20 21:58:00 2003 Hidetoshi Tajima 2003-05-21 Matthias Clasen + * configure.in (all_loaders): Add pcx. + * gtk/gtkcalendar.c: Make calendar controls spin. (#112540) Tue May 20 17:27:24 2003 Owen Taylor diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index 77fdc49a51..90c8eca0f0 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -23,6 +23,8 @@ Tue May 20 21:58:00 2003 Hidetoshi Tajima 2003-05-21 Matthias Clasen + * configure.in (all_loaders): Add pcx. + * gtk/gtkcalendar.c: Make calendar controls spin. (#112540) Tue May 20 17:27:24 2003 Owen Taylor diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4 index 77fdc49a51..90c8eca0f0 100644 --- a/ChangeLog.pre-2-4 +++ b/ChangeLog.pre-2-4 @@ -23,6 +23,8 @@ Tue May 20 21:58:00 2003 Hidetoshi Tajima 2003-05-21 Matthias Clasen + * configure.in (all_loaders): Add pcx. + * gtk/gtkcalendar.c: Make calendar controls spin. (#112540) Tue May 20 17:27:24 2003 Owen Taylor diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index 77fdc49a51..90c8eca0f0 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -23,6 +23,8 @@ Tue May 20 21:58:00 2003 Hidetoshi Tajima 2003-05-21 Matthias Clasen + * configure.in (all_loaders): Add pcx. + * gtk/gtkcalendar.c: Make calendar controls spin. (#112540) Tue May 20 17:27:24 2003 Owen Taylor diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index 77fdc49a51..90c8eca0f0 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -23,6 +23,8 @@ Tue May 20 21:58:00 2003 Hidetoshi Tajima 2003-05-21 Matthias Clasen + * configure.in (all_loaders): Add pcx. + * gtk/gtkcalendar.c: Make calendar controls spin. (#112540) Tue May 20 17:27:24 2003 Owen Taylor diff --git a/configure.in b/configure.in index 32173c0953..817742d30d 100644 --- a/configure.in +++ b/configure.in @@ -827,7 +827,7 @@ else fi fi -all_loaders="png,bmp,wbmp,gif,ico,ani,jpeg,pnm,ras,tiff,xpm,tga" +all_loaders="png,bmp,wbmp,gif,ico,ani,jpeg,pnm,ras,tiff,xpm,tga,pcx" included_loaders="" # If no loaders specified, include all if test "x$with_included_loaders" = xyes ; then diff --git a/gdk-pixbuf/ChangeLog b/gdk-pixbuf/ChangeLog index 905eb23784..7286682db6 100644 --- a/gdk-pixbuf/ChangeLog +++ b/gdk-pixbuf/ChangeLog @@ -1,3 +1,11 @@ +2003-05-21 Matthias Clasen + + * pixbufloader_pcx.def: + * io-pcx.c: A loader for PCX files. (#113035, Josh Beam) + + * gdk-pixbuf-io.c: + * Makefile.am: Add PCX loader. + 2003-05-19 Matthias Clasen Improve progressive loading from slow sources: (#107368) diff --git a/gdk-pixbuf/Makefile.am b/gdk-pixbuf/Makefile.am index 1031ce8e59..b1c214df4c 100644 --- a/gdk-pixbuf/Makefile.am +++ b/gdk-pixbuf/Makefile.am @@ -150,6 +150,14 @@ libpixbufloader_tga_la_SOURCES = io-tga.c libpixbufloader_tga_la_LDFLAGS = -avoid-version -module $(no_undefined) libpixbufloader_tga_la_LIBADD = $(module_libs) +# +# The PCX loader +# +libpixbufloader_static_pcx_la_SOURCES = io-pcx.c +libpixbufloader_pcx_la_SOURCES = io-pcx.c +libpixbufloader_pcx_la_LDFLAGS = -avoid-version -module $(no_undefined) +libpixbufloader_pcx_la_LIBADD = $(module_libs) + if HAVE_PNG PNG_LIB = libpixbufloader-png.la STATIC_PNG_LIB = libpixbufloader-static-png.la @@ -195,6 +203,9 @@ STATIC_XBM_LIB = libpixbufloader-static-xbm.la TGA_LIB = libpixbufloader-tga.la STATIC_TGA_LIB = libpixbufloader-static-tga.la +PCX_LIB = libpixbufloader-pcx.la +STATIC_PCX_LIB = libpixbufloader-static-pcx.la + if BUILD_DYNAMIC_MODULES loader_LTLIBRARIES = \ @@ -210,7 +221,8 @@ loader_LTLIBRARIES = \ $(BMP_LIB) \ $(WBMP_LIB) \ $(XBM_LIB) \ - $(TGA_LIB) + $(TGA_LIB) \ + $(PCX_LIB) extra_sources = @@ -231,7 +243,8 @@ noinst_LTLIBRARIES = \ $(STATIC_BMP_LIB) \ $(STATIC_WBMP_LIB) \ $(STATIC_XBM_LIB) \ - $(STATIC_TGA_LIB) + $(STATIC_TGA_LIB) \ + $(STATIC_PCX_LIB) builtin_objs = @INCLUDED_LOADER_OBJ@ diff --git a/gdk-pixbuf/gdk-pixbuf-io.c b/gdk-pixbuf/gdk-pixbuf-io.c index 218c44c29d..f3f6f4b320 100644 --- a/gdk-pixbuf/gdk-pixbuf-io.c +++ b/gdk-pixbuf/gdk-pixbuf-io.c @@ -413,6 +413,7 @@ module (tiff); module (xpm); module (xbm); module (tga); +module (pcx); gboolean _gdk_pixbuf_load_module (GdkPixbufModule *image_module, @@ -518,6 +519,13 @@ _gdk_pixbuf_load_module (GdkPixbufModule *image_module, } #endif +#ifdef INCLUDE_pcx + else if (strcmp (image_module->module_name, "pcx") == 0) { + fill_info = MODULE_ENTRY (pcx, fill_info); + fill_vtable = MODULE_ENTRY (pcx, fill_vtable); + } +#endif + if (fill_vtable) { (* fill_vtable) (image_module); image_module->info = g_new0 (GdkPixbufFormat, 1); @@ -541,7 +549,7 @@ gdk_pixbuf_io_init () gchar *included_formats[] = { "ani", "png", "bmp", "wbmp", "gif", "ico", "jpeg", "pnm", "ras", "tiff", - "xpm", "xbm", "tga", + "xpm", "xbm", "tga", "pcx", NULL }; gchar **name; diff --git a/gdk-pixbuf/io-pcx.c b/gdk-pixbuf/io-pcx.c new file mode 100644 index 0000000000..10c26f3c24 --- /dev/null +++ b/gdk-pixbuf/io-pcx.c @@ -0,0 +1,757 @@ +/* + * GdkPixbuf library - PCX image loader + * + * Copyright (C) 2003 Josh A. Beam + * + * Authors: Josh A. Beam + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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 +#include +#include "gdk-pixbuf-private.h" +#include "gdk-pixbuf-io.h" + +#define PCX_DEBUG + +#define PCX_TASK_DONE 0 +#define PCX_TASK_LOAD_HEADER 1 +#define PCX_TASK_LOAD_DATA 2 +#define PCX_TASK_LOAD_PALETTE 3 + +struct pcx_header { + guint8 manufacturer; + guint8 version; + guint8 encoding; + guint8 bitsperpixel; + gint16 xmin; + gint16 ymin; + gint16 xmax; + gint16 ymax; + guint16 horizdpi; + guint16 vertdpi; + guint8 palette[48]; + guint8 reserved; + guint8 colorplanes; + guint16 bytesperline; + guint16 palettetype; + guint16 hscrsize; + guint16 vscrsize; + guint8 filler[54]; +}; + +struct pcx_context { + GdkPixbuf *pixbuf; + gint rowstride; + + GdkPixbufModuleSizeFunc size_func; + GdkPixbufModuleUpdatedFunc updated_func; + GdkPixbufModulePreparedFunc prepared_func; + gpointer user_data; + + guchar current_task; + + gboolean header_loaded; + struct pcx_header *header; + guint bpp; + gint width, height; + guint num_planes; + guint bytesperline; + + guchar *buf; + guint buf_size; + guint buf_pos; + guchar *data; + guchar *line; + guint current_line; + guchar *p_data; +}; + +/* + * set context's image information based on the header + */ +static void +fill_pcx_context(struct pcx_context *context) +{ + struct pcx_header *header = context->header; + + context->bpp = header->bitsperpixel; + context->width = header->xmax - header->xmin + 1; + context->height = header->ymax - header->ymin + 1; + context->num_planes = header->colorplanes; + context->bytesperline = header->bytesperline; + + if(header->version == 5 && context->bpp == 8 && context->num_planes == 3) + context->bpp = 24; +} + +static void +free_pcx_context(struct pcx_context *context, gboolean unref_pixbuf) +{ + if(context->header) + g_free(context->header); + if(context->buf) + g_free(context->buf); + if(unref_pixbuf && context->pixbuf) + g_object_unref(context->pixbuf); + if(context->line) + g_free(context->line); + if(context->p_data) + g_free(context->p_data); + + g_free(context); +} + +/* + * read each plane of a single scanline. store_planes is + * the number of planes that can be stored in the planes array. + * data is the pointer to the block of memory to read + * from, size is the length of that data, and line_bytes + * is where the number of bytes read will be stored. + */ +static gboolean +read_scanline_data(guchar *data, guint size, guchar *planes[], + guint store_planes, guint num_planes, guint bytesperline, + guint *line_bytes) +{ + guint i, j; + guint p, count; + guint d = 0; + guint8 byte; + + for(p = 0; p < num_planes; p++) { + for(i = 0; i < bytesperline;) { /* i incremented when line byte set */ + if(d >= size) + return FALSE; + byte = data[d++]; + + if(byte >> 6 == 0x3) { + count = byte & ~(0x3 << 6); + if(count == 0) + return FALSE; + if(d >= size) + return FALSE; + byte = data[d++]; + } else { + count = 1; + } + + for(j = 0; j < count; j++) { + if(p < store_planes) + planes[p][i++] = byte; + else + i++; + + if(i >= bytesperline) { + p++; + if(p >= num_planes) { + *line_bytes = d; + return TRUE; + } + i = 0; + } + } + } + } + + *line_bytes = d; /* number of bytes read for scanline */ + return TRUE; +} + +static gpointer +gdk_pixbuf__pcx_begin_load(GdkPixbufModuleSizeFunc size_func, + GdkPixbufModulePreparedFunc prepared_func, + GdkPixbufModuleUpdatedFunc updated_func, + gpointer user_data, GError **error) +{ + struct pcx_context *context; + + context = g_new0(struct pcx_context, 1); + if(!context) + return NULL; + + context->header = g_try_malloc(sizeof(struct pcx_header)); + if(!context->header) { + g_free(context); + g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't allocate memory for header")); + return NULL; + } + + context->size_func = size_func; + context->updated_func = updated_func; + context->prepared_func = prepared_func; + context->user_data = user_data; + + context->current_task = PCX_TASK_LOAD_HEADER; + + context->buf = g_try_malloc(sizeof(guchar) * 512); + if(!context->buf) { + g_free(context->header); + g_free(context); + g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't allocate memory for context buffer")); + return NULL; + } + context->buf_size = 512; + + context->header_loaded = FALSE; + + return context; +} + +static gboolean +pcx_resize_context_buf(struct pcx_context *context, guint size) +{ + guchar *new_buf; + + new_buf = g_try_realloc(context->buf, size); + if(!new_buf) + return FALSE; + + context->buf = new_buf; + context->buf_size = size; + return TRUE; +} + +/* + * remove a number of bytes (specified by size) from the + * beginning of a context's buf + */ +static gboolean +pcx_chop_context_buf(struct pcx_context *context, guint size) +{ + guint i, j; + + if(size > context->buf_pos) + return FALSE; + else if(size < 0) + return FALSE; + else if(size == 0) + return TRUE; + + for(i = 0, j = size; j < context->buf_pos; i++, j++) + context->buf[i] = context->buf[j]; + + context->buf_pos -= size; + + return TRUE; +} + +static guchar +read_pixel_1(guchar *data, guint offset) +{ + guchar retval; + guint bit_offset; + + if(!(offset % 8)) { + offset /= 8; + retval = data[offset] >> 7; + } else { + bit_offset = offset % 8; + offset -= bit_offset; + offset /= 8; + retval = (data[offset] >> (7 - bit_offset)) & 0x1; + } + + return retval; +} + +static guchar +read_pixel_4(guchar *data, guint offset) +{ + guchar retval; + + if(!(offset % 2)) { + offset /= 2; + retval = data[offset] >> 4; + } else { + offset--; + offset /= 2; + retval = data[offset] & 0xf; + } + + return retval; +} + +static gboolean +pcx_increment_load_data_1(struct pcx_context *context) +{ + guint i; + guchar *planes[4]; + guint line_bytes; + guint store_planes; + + if(context->num_planes == 4) { + planes[0] = context->line; + planes[1] = planes[0] + context->bytesperline; + planes[2] = planes[1] + context->bytesperline; + planes[3] = planes[2] + context->bytesperline; + store_planes = 4; + } else if(context->num_planes == 3) { + planes[0] = context->line; + planes[1] = planes[0] + context->bytesperline; + planes[2] = planes[1] + context->bytesperline; + store_planes = 3; + } else if(context->num_planes == 2) { + planes[0] = context->line; + planes[1] = planes[0] + context->bytesperline; + store_planes = 2; + } else if(context->num_planes == 1) { + planes[0] = context->line; + store_planes = 1; + } else { + return FALSE; + } + + while(read_scanline_data(context->buf, context->buf_pos, planes, store_planes, context->num_planes, context->bytesperline, &line_bytes)) { + pcx_chop_context_buf(context, line_bytes); + + for(i = 0; i < context->width; i++) { + guchar p; + + if(context->num_planes == 4) { + p = read_pixel_1(planes[3], i); + p <<= 1; + p |= read_pixel_1(planes[2], i); + p <<= 1; + p |= read_pixel_1(planes[1], i); + p <<= 1; + p |= read_pixel_1(planes[0], i); + } else if(context->num_planes == 3) { + p = read_pixel_1(planes[2], i); + p <<= 1; + p |= read_pixel_1(planes[1], i); + p <<= 1; + p |= read_pixel_1(planes[0], i); + } else if(context->num_planes == 2) { + p = read_pixel_1(planes[1], i); + p <<= 1; + p |= read_pixel_1(planes[0], i); + } else if(context->num_planes == 1) { + p = read_pixel_1(planes[0], i); + } else { + return FALSE; + } + p &= 0xf; + context->data[context->current_line * context->rowstride + i * 3 + 0] = context->header->palette[p * 3 + 0]; + context->data[context->current_line * context->rowstride + i * 3 + 1] = context->header->palette[p * 3 + 1]; + context->data[context->current_line * context->rowstride + i * 3 + 2] = context->header->palette[p * 3 + 2]; + } + + if(context->updated_func) + context->updated_func(context->pixbuf, 0, context->current_line, context->width, 1, context->user_data); + + context->current_line++; + + if(context->current_line == context->height) { + context->current_task = PCX_TASK_DONE; + return TRUE; + } + } + + return TRUE; +} + +static gboolean +pcx_increment_load_data_2(struct pcx_context *context) +{ + guint i; + guchar *planes[1]; + guint line_bytes; + guint shift, h; + + planes[0] = context->line; + + while(read_scanline_data(context->buf, context->buf_pos, planes, 1, context->num_planes, context->bytesperline, &line_bytes)) { + pcx_chop_context_buf(context, line_bytes); + + for(i = 0; i < context->width; i++) { + shift = 6 - 2 * (i % 4); + h = (planes[0][i / 4] >> shift) & 0x3; + context->data[context->current_line * context->rowstride + i * 3 + 0] = context->header->palette[h * 3 + 0]; + context->data[context->current_line * context->rowstride + i * 3 + 1] = context->header->palette[h * 3 + 1]; + context->data[context->current_line * context->rowstride + i * 3 + 2] = context->header->palette[h * 3 + 2]; + } + + if(context->updated_func) + context->updated_func(context->pixbuf, 0, context->current_line, context->width, 1, context->user_data); + + context->current_line++; + + if(context->current_line == context->height) { + context->current_task = PCX_TASK_DONE; + return TRUE; + } + } + + return TRUE; +} + +static gboolean +pcx_increment_load_data_4(struct pcx_context *context) +{ + guint i; + guchar *planes[1]; + guint line_bytes; + + planes[0] = context->line; + + while(read_scanline_data(context->buf, context->buf_pos, planes, 1, context->num_planes, context->bytesperline, &line_bytes)) { + pcx_chop_context_buf(context, line_bytes); + + for(i = 0; i < context->width; i++) { + guchar p; + + p = read_pixel_4(planes[0], i) & 0xf; + context->data[context->current_line * context->rowstride + i * 3 + 0] = context->header->palette[p * 3 + 0]; + context->data[context->current_line * context->rowstride + i * 3 + 1] = context->header->palette[p * 3 + 1]; + context->data[context->current_line * context->rowstride + i * 3 + 2] = context->header->palette[p * 3 + 2]; + } + + if(context->updated_func) + context->updated_func(context->pixbuf, 0, context->current_line, context->width, 1, context->user_data); + + context->current_line++; + + if(context->current_line == context->height) { + context->current_task = PCX_TASK_DONE; + return TRUE; + } + } + + return TRUE; +} + +/* + * for loading 8-bit pcx images, we keep a buffer containing + * each pixel's palette number; once we've loaded each scanline, + * we wait for loading to stop and call pcx_load_palette_8, + * which finds the palette at the end of the pcx data and sets the + * RGB data. + */ +static gboolean +pcx_increment_load_data_8(struct pcx_context *context) +{ + guint i; + guchar *planes[1]; + guint line_bytes; + + planes[0] = context->line; + + while(read_scanline_data(context->buf, context->buf_pos, planes, 1, context->num_planes, context->bytesperline, &line_bytes)) { + pcx_chop_context_buf(context, line_bytes); + + for(i = 0; i < context->width; i++) + context->p_data[context->current_line * context->width + i + 0] = planes[0][i]; + + context->current_line++; + + if(context->current_line == context->height) { + context->current_task = PCX_TASK_LOAD_PALETTE; + return TRUE; + } + } + + return TRUE; +} + +/* + * read the palette and set the RGB data + */ +static gboolean +pcx_load_palette_8(struct pcx_context *context) +{ + guint i, j; + + if(context->current_line < context->height) + return FALSE; + + if(context->buf_pos >= 769) { + guchar *palette = context->buf + (context->buf_pos - 769); + + if(palette[0] == 12) { + palette++; + for(i = 0; i < context->height; i++) { + for(j = 0; j < context->width; j++) { + context->data[i * context->rowstride + j * 3 + 0] = palette[(context->p_data[i * context->width + j]) * 3 + 0]; + context->data[i * context->rowstride + j * 3 + 1] = palette[(context->p_data[i * context->width + j]) * 3 + 1]; + context->data[i * context->rowstride + j * 3 + 2] = palette[(context->p_data[i * context->width + j]) * 3 + 2]; + } + + if(context->updated_func) + context->updated_func(context->pixbuf, 0, context->current_line, context->width, 1, context->user_data); + } + +#ifdef PCX_DEBUG + g_print("read palette\n"); +#endif + + context->current_task = PCX_TASK_DONE; + return TRUE; + } else { +#ifdef PCX_DEBUG + g_print("this ain't a palette\n"); +#endif + return FALSE; + } + } + + return FALSE; +} + +/* + * in 24-bit images, each scanline has three color planes + * for red, green, and blue, respectively. + */ +static gboolean +pcx_increment_load_data_24(struct pcx_context *context) +{ + guint i; + guchar *planes[3]; + guint line_bytes; + + planes[0] = context->line; + planes[1] = planes[0] + context->bytesperline; + planes[2] = planes[1] + context->bytesperline; + + while(read_scanline_data(context->buf, context->buf_pos, planes, 3, context->num_planes, context->bytesperline, &line_bytes)) { + pcx_chop_context_buf(context, line_bytes); + + for(i = 0; i < context->width; i++) { + context->data[context->current_line * context->rowstride + i * 3 + 0] = planes[0][i]; + context->data[context->current_line * context->rowstride + i * 3 + 1] = planes[1][i]; + context->data[context->current_line * context->rowstride + i * 3 + 2] = planes[2][i]; + } + + if(context->updated_func) + context->updated_func(context->pixbuf, 0, context->current_line, context->width, 1, context->user_data); + + context->current_line++; + + if(context->current_line == context->height) { + context->current_task = PCX_TASK_DONE; + return TRUE; + } + } + + return TRUE; +} + +static gboolean +gdk_pixbuf__pcx_load_increment(gpointer data, const guchar *buf, guint size, + GError **error) +{ + struct pcx_context *context = (struct pcx_context *)data; + struct pcx_header *header; + guint i; + gboolean retval = TRUE; + + /* if context's buf isn't large enough to hold its current data plus the passed buf, increase its size */ + if(context->buf_pos + size > context->buf_size) { + if(!pcx_resize_context_buf(context, sizeof(guchar) * (context->buf_pos + size))) { + g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't allocate memory for context buffer")); + return FALSE; + } + } + + for(i = 0; i < size; i++) + context->buf[context->buf_pos++] = buf[i]; + + if(context->current_task == PCX_TASK_LOAD_HEADER) { + if(!context->header_loaded && context->buf_pos > sizeof(struct pcx_header)) { /* set header */ + gint width, height; + + memcpy(context->header, context->buf, sizeof(struct pcx_header)); + pcx_chop_context_buf(context, sizeof(struct pcx_header)); + header = context->header; + + /* convert the multi-byte header variables that will be used */ + header->xmin = GINT16_FROM_LE(header->xmin); + header->ymin = GINT16_FROM_LE(header->ymin); + header->xmax = GINT16_FROM_LE(header->xmax); + header->ymax = GINT16_FROM_LE(header->ymax); + header->bytesperline = GUINT16_FROM_LE(header->bytesperline); + +#ifdef PCX_DEBUG + g_print ("Manufacturer %d\n" + "Version %d\n" + "Encoding %d\n" + "Bits/Pixel %d\n" + "Planes %d\n" + "Palette %d\n", + header->manufacturer, header->version, + header->encoding, header->bitsperpixel, + header->colorplanes, header->palettetype); +#endif + + context->header_loaded = TRUE; + fill_pcx_context(context); + + width = context->width; + height = context->height; + if(width <= 0 || height <= 0) { + g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("Image has invalid width and/or height")); + return FALSE; + } + if(context->size_func) + context->size_func(&width, &height, context->user_data); + + switch(context->bpp) { + default: + g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_UNKNOWN_TYPE, _("Image has unsupported bpp")); + return FALSE; + break; + case 1: + if(context->num_planes < 1 || context->num_planes > 4) { + g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_UNKNOWN_TYPE, _("Image has unsupported number of 1-bit planes")); + return FALSE; + } + break; + case 2: + case 4: + case 8: + if(context->num_planes != 1) { + g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_UNKNOWN_TYPE, _("Image has unsupported number of %d-bit planes"), context->bpp); + return FALSE; + } + break; + case 24: + break; /* context's bpp is set to 24 if there are three 8-bit planes */ + } + +#ifdef PCX_DEBUG + g_print("io-pcx: header loaded\n"); + g_print("bpp: %u\n", context->bpp); + g_print("dimensions: %ux%u\n", context->width, context->height); +#endif + + context->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, context->width, context->height); + if(!context->pixbuf) { + g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't create new pixbuf")); + return FALSE; + } + context->data = gdk_pixbuf_get_pixels(context->pixbuf); + context->rowstride = gdk_pixbuf_get_rowstride(context->pixbuf); + + context->line = g_try_malloc(sizeof(guchar) * context->bytesperline * context->num_planes); + if(!context->line) { + g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't allocate memory for line data")); + return FALSE; + } + + if(context->bpp == 8) { + context->p_data = g_try_malloc(sizeof(guchar) * context->width * context->height); + if(!context->p_data) { + g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't allocate memory for paletted data")); + return FALSE; + } + } + + if(context->prepared_func) + context->prepared_func(context->pixbuf, NULL, context->user_data); + + context->current_task = PCX_TASK_LOAD_DATA; + } + + retval = TRUE; + } + + if(context->current_task == PCX_TASK_LOAD_DATA) { + switch(context->bpp) { + default: + g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_UNKNOWN_TYPE, _("Image has unsupported bpp")); + retval = FALSE; + break; + case 1: + retval = pcx_increment_load_data_1(context); + break; + case 2: + retval = pcx_increment_load_data_2(context); + break; + case 4: + retval = pcx_increment_load_data_4(context); + break; + case 8: + retval = pcx_increment_load_data_8(context); + break; + case 24: + retval = pcx_increment_load_data_24(context); + break; + } + } + + return retval; +} + +static gboolean +gdk_pixbuf__pcx_stop_load(gpointer data, GError **error) +{ + struct pcx_context *context = (struct pcx_context *)data; + + if(context->current_line != context->height) { + g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, _("Didn't get all lines of PCX image")); + free_pcx_context(context, FALSE); + return FALSE; + } + + if(context->current_task == PCX_TASK_LOAD_PALETTE) { + if(!pcx_load_palette_8(context)) { + g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, _("No palette found at end of PCX data")); + free_pcx_context(context, FALSE); + return FALSE; + } + } + + free_pcx_context(context, FALSE); + + return TRUE; +} + +void +MODULE_ENTRY (pcx, fill_vtable) (GdkPixbufModule *module) +{ + module->begin_load = gdk_pixbuf__pcx_begin_load; + module->stop_load = gdk_pixbuf__pcx_stop_load; + module->load_increment = gdk_pixbuf__pcx_load_increment; +} + +void +MODULE_ENTRY (pcx, fill_info) (GdkPixbufFormat *info) +{ + static GdkPixbufModulePattern signature[] = { + { "\x0a \x01", NULL, 100 }, + { "\x0a\x02\x01", NULL, 100 }, + { "\x0a\x03\x01", NULL, 100 }, + { "\x0a\x04\x01", NULL, 100 }, + { "\x0a\x05\x01", NULL, 100 }, + { NULL, NULL, 0 } + }; + static gchar *mime_types[] = { + "image/x-pcx", + NULL, + }; + static gchar *extensions[] = { + "pcx", + NULL, + }; + + info->name = "pcx"; + info->signature = signature; + info->description = N_("The PCX image format"); + info->mime_types = mime_types; + info->extensions = extensions; + info->flags = 0; +} diff --git a/gdk-pixbuf/pixbufloader_pcx.def b/gdk-pixbuf/pixbufloader_pcx.def new file mode 100644 index 0000000000..9044f08971 --- /dev/null +++ b/gdk-pixbuf/pixbufloader_pcx.def @@ -0,0 +1,3 @@ +EXPORTS + fill_vtable + fill_info