2 * GdkPixbuf library - PCX image loader
4 * Copyright (C) 2003 Josh A. Beam
6 * Authors: Josh A. Beam <josh@joshbeam.com>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
27 #include "gdk-pixbuf-private.h"
28 #include "gdk-pixbuf-io.h"
32 #define PCX_TASK_DONE 0
33 #define PCX_TASK_LOAD_HEADER 1
34 #define PCX_TASK_LOAD_DATA 2
35 #define PCX_TASK_LOAD_PALETTE 3
62 GdkPixbufModuleSizeFunc size_func;
63 GdkPixbufModuleUpdatedFunc updated_func;
64 GdkPixbufModulePreparedFunc prepared_func;
69 gboolean header_loaded;
70 struct pcx_header *header;
86 * set context's image information based on the header
89 fill_pcx_context(struct pcx_context *context)
91 struct pcx_header *header = context->header;
93 context->bpp = header->bitsperpixel;
94 context->width = header->xmax - header->xmin + 1;
95 context->height = header->ymax - header->ymin + 1;
96 context->num_planes = header->colorplanes;
97 context->bytesperline = header->bytesperline;
99 if(header->version == 5 && context->bpp == 8 && context->num_planes == 3)
104 free_pcx_context(struct pcx_context *context, gboolean unref_pixbuf)
106 g_free(context->header);
107 g_free(context->buf);
108 if(unref_pixbuf && context->pixbuf)
109 g_object_unref(context->pixbuf);
110 g_free(context->line);
111 g_free(context->p_data);
117 * read each plane of a single scanline. store_planes is
118 * the number of planes that can be stored in the planes array.
119 * data is the pointer to the block of memory to read
120 * from, size is the length of that data, and line_bytes
121 * is where the number of bytes read will be stored.
124 read_scanline_data(guchar *data, guint size, guchar *planes[],
125 guint store_planes, guint num_planes, guint bytesperline,
133 for(p = 0; p < num_planes; p++) {
134 for(i = 0; i < bytesperline;) { /* i incremented when line byte set */
139 if(byte >> 6 == 0x3) {
140 count = byte & ~(0x3 << 6);
150 for(j = 0; j < count; j++) {
152 planes[p][i++] = byte;
156 if(i >= bytesperline) {
158 if(p >= num_planes) {
168 *line_bytes = d; /* number of bytes read for scanline */
173 gdk_pixbuf__pcx_begin_load(GdkPixbufModuleSizeFunc size_func,
174 GdkPixbufModulePreparedFunc prepared_func,
175 GdkPixbufModuleUpdatedFunc updated_func,
176 gpointer user_data, GError **error)
178 struct pcx_context *context;
180 context = g_new0(struct pcx_context, 1);
184 context->header = g_try_malloc(sizeof(struct pcx_header));
185 if(!context->header) {
187 g_set_error_literal(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't allocate memory for header"));
191 context->size_func = size_func;
192 context->updated_func = updated_func;
193 context->prepared_func = prepared_func;
194 context->user_data = user_data;
196 context->current_task = PCX_TASK_LOAD_HEADER;
198 context->buf = g_try_malloc(sizeof(guchar) * 512);
200 g_free(context->header);
202 g_set_error_literal(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't allocate memory for context buffer"));
205 context->buf_size = 512;
207 context->header_loaded = FALSE;
213 pcx_resize_context_buf(struct pcx_context *context, guint size)
217 new_buf = g_try_realloc(context->buf, size);
221 context->buf = new_buf;
222 context->buf_size = size;
227 * remove a number of bytes (specified by size) from the
228 * beginning of a context's buf
231 pcx_chop_context_buf(struct pcx_context *context, guint size)
235 if (size > context->buf_pos)
240 for (i = 0, j = size; j < context->buf_pos; i++, j++)
241 context->buf[i] = context->buf[j];
243 context->buf_pos -= size;
249 read_pixel_1(guchar *data, guint offset)
256 retval = data[offset] >> 7;
258 bit_offset = offset % 8;
259 offset -= bit_offset;
261 retval = (data[offset] >> (7 - bit_offset)) & 0x1;
268 read_pixel_4(guchar *data, guint offset)
274 retval = data[offset] >> 4;
278 retval = data[offset] & 0xf;
285 pcx_increment_load_data_1(struct pcx_context *context)
292 if(context->num_planes == 4) {
293 planes[0] = context->line;
294 planes[1] = planes[0] + context->bytesperline;
295 planes[2] = planes[1] + context->bytesperline;
296 planes[3] = planes[2] + context->bytesperline;
298 } else if(context->num_planes == 3) {
299 planes[0] = context->line;
300 planes[1] = planes[0] + context->bytesperline;
301 planes[2] = planes[1] + context->bytesperline;
303 } else if(context->num_planes == 2) {
304 planes[0] = context->line;
305 planes[1] = planes[0] + context->bytesperline;
307 } else if(context->num_planes == 1) {
308 planes[0] = context->line;
314 while(read_scanline_data(context->buf, context->buf_pos, planes, store_planes, context->num_planes, context->bytesperline, &line_bytes)) {
315 pcx_chop_context_buf(context, line_bytes);
317 for(i = 0; i < context->width; i++) {
320 if(context->num_planes == 4) {
321 p = read_pixel_1(planes[3], i);
323 p |= read_pixel_1(planes[2], i);
325 p |= read_pixel_1(planes[1], i);
327 p |= read_pixel_1(planes[0], i);
328 } else if(context->num_planes == 3) {
329 p = read_pixel_1(planes[2], i);
331 p |= read_pixel_1(planes[1], i);
333 p |= read_pixel_1(planes[0], i);
334 } else if(context->num_planes == 2) {
335 p = read_pixel_1(planes[1], i);
337 p |= read_pixel_1(planes[0], i);
338 } else if(context->num_planes == 1) {
339 p = read_pixel_1(planes[0], i);
344 context->data[context->current_line * context->rowstride + i * 3 + 0] = context->header->palette[p * 3 + 0];
345 context->data[context->current_line * context->rowstride + i * 3 + 1] = context->header->palette[p * 3 + 1];
346 context->data[context->current_line * context->rowstride + i * 3 + 2] = context->header->palette[p * 3 + 2];
349 if(context->updated_func)
350 context->updated_func(context->pixbuf, 0, context->current_line, context->width, 1, context->user_data);
352 context->current_line++;
354 if(context->current_line == context->height) {
355 context->current_task = PCX_TASK_DONE;
364 pcx_increment_load_data_2(struct pcx_context *context)
371 planes[0] = context->line;
373 while(read_scanline_data(context->buf, context->buf_pos, planes, 1, context->num_planes, context->bytesperline, &line_bytes)) {
374 pcx_chop_context_buf(context, line_bytes);
376 for(i = 0; i < context->width; i++) {
377 shift = 6 - 2 * (i % 4);
378 h = (planes[0][i / 4] >> shift) & 0x3;
379 context->data[context->current_line * context->rowstride + i * 3 + 0] = context->header->palette[h * 3 + 0];
380 context->data[context->current_line * context->rowstride + i * 3 + 1] = context->header->palette[h * 3 + 1];
381 context->data[context->current_line * context->rowstride + i * 3 + 2] = context->header->palette[h * 3 + 2];
384 if(context->updated_func)
385 context->updated_func(context->pixbuf, 0, context->current_line, context->width, 1, context->user_data);
387 context->current_line++;
389 if(context->current_line == context->height) {
390 context->current_task = PCX_TASK_DONE;
399 pcx_increment_load_data_4(struct pcx_context *context)
405 planes[0] = context->line;
407 while(read_scanline_data(context->buf, context->buf_pos, planes, 1, context->num_planes, context->bytesperline, &line_bytes)) {
408 pcx_chop_context_buf(context, line_bytes);
410 for(i = 0; i < context->width; i++) {
413 p = read_pixel_4(planes[0], i) & 0xf;
414 context->data[context->current_line * context->rowstride + i * 3 + 0] = context->header->palette[p * 3 + 0];
415 context->data[context->current_line * context->rowstride + i * 3 + 1] = context->header->palette[p * 3 + 1];
416 context->data[context->current_line * context->rowstride + i * 3 + 2] = context->header->palette[p * 3 + 2];
419 if(context->updated_func)
420 context->updated_func(context->pixbuf, 0, context->current_line, context->width, 1, context->user_data);
422 context->current_line++;
424 if(context->current_line == context->height) {
425 context->current_task = PCX_TASK_DONE;
434 * for loading 8-bit pcx images, we keep a buffer containing
435 * each pixel's palette number; once we've loaded each scanline,
436 * we wait for loading to stop and call pcx_load_palette_8,
437 * which finds the palette at the end of the pcx data and sets the
441 pcx_increment_load_data_8(struct pcx_context *context)
447 planes[0] = context->line;
449 while(read_scanline_data(context->buf, context->buf_pos, planes, 1, context->num_planes, context->bytesperline, &line_bytes)) {
450 pcx_chop_context_buf(context, line_bytes);
452 for(i = 0; i < context->width; i++)
453 context->p_data[context->current_line * context->width + i + 0] = planes[0][i];
455 context->current_line++;
457 if(context->current_line == context->height) {
458 context->current_task = PCX_TASK_LOAD_PALETTE;
467 * read the palette and set the RGB data
470 pcx_load_palette_8(struct pcx_context *context)
474 if(context->current_line < context->height)
477 if(context->buf_pos >= 769) {
478 guchar *palette = context->buf + (context->buf_pos - 769);
480 if(palette[0] == 12) {
482 for(i = 0; i < context->height; i++) {
483 for(j = 0; j < context->width; j++) {
484 context->data[i * context->rowstride + j * 3 + 0] = palette[(context->p_data[i * context->width + j]) * 3 + 0];
485 context->data[i * context->rowstride + j * 3 + 1] = palette[(context->p_data[i * context->width + j]) * 3 + 1];
486 context->data[i * context->rowstride + j * 3 + 2] = palette[(context->p_data[i * context->width + j]) * 3 + 2];
489 if(context->updated_func)
490 context->updated_func(context->pixbuf, 0, i, context->width, 1, context->user_data);
494 g_print("read palette\n");
497 context->current_task = PCX_TASK_DONE;
501 g_print("this ain't a palette\n");
511 * in 24-bit images, each scanline has three color planes
512 * for red, green, and blue, respectively.
515 pcx_increment_load_data_24(struct pcx_context *context)
521 planes[0] = context->line;
522 planes[1] = planes[0] + context->bytesperline;
523 planes[2] = planes[1] + context->bytesperline;
525 while(read_scanline_data(context->buf, context->buf_pos, planes, 3, context->num_planes, context->bytesperline, &line_bytes)) {
526 pcx_chop_context_buf(context, line_bytes);
528 for(i = 0; i < context->width; i++) {
529 context->data[context->current_line * context->rowstride + i * 3 + 0] = planes[0][i];
530 context->data[context->current_line * context->rowstride + i * 3 + 1] = planes[1][i];
531 context->data[context->current_line * context->rowstride + i * 3 + 2] = planes[2][i];
534 if(context->updated_func)
535 context->updated_func(context->pixbuf, 0, context->current_line, context->width, 1, context->user_data);
537 context->current_line++;
539 if(context->current_line == context->height) {
540 context->current_task = PCX_TASK_DONE;
549 gdk_pixbuf__pcx_load_increment(gpointer data, const guchar *buf, guint size,
552 struct pcx_context *context = (struct pcx_context *)data;
553 struct pcx_header *header;
555 gboolean retval = TRUE;
557 /* if context's buf isn't large enough to hold its current data plus the passed buf, increase its size */
558 if(context->buf_pos + size > context->buf_size) {
559 if(!pcx_resize_context_buf(context, sizeof(guchar) * (context->buf_pos + size))) {
560 g_set_error_literal(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't allocate memory for context buffer"));
565 for(i = 0; i < size; i++)
566 context->buf[context->buf_pos++] = buf[i];
568 if(context->current_task == PCX_TASK_LOAD_HEADER) {
569 if(!context->header_loaded && context->buf_pos > sizeof(struct pcx_header)) { /* set header */
572 memcpy(context->header, context->buf, sizeof(struct pcx_header));
573 pcx_chop_context_buf(context, sizeof(struct pcx_header));
574 header = context->header;
576 /* convert the multi-byte header variables that will be used */
577 header->xmin = GINT16_FROM_LE(header->xmin);
578 header->ymin = GINT16_FROM_LE(header->ymin);
579 header->xmax = GINT16_FROM_LE(header->xmax);
580 header->ymax = GINT16_FROM_LE(header->ymax);
581 header->bytesperline = GUINT16_FROM_LE(header->bytesperline);
584 g_print ("Manufacturer %d\n"
590 header->manufacturer, header->version,
591 header->encoding, header->bitsperpixel,
592 header->colorplanes, header->palettetype);
595 context->header_loaded = TRUE;
596 fill_pcx_context(context);
598 width = context->width;
599 height = context->height;
600 if(width <= 0 || height <= 0) {
601 g_set_error_literal(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("Image has invalid width and/or height"));
604 if (context->size_func)
606 (*context->size_func) (&width, &height, context->user_data);
607 if (width == 0 || height == 0)
611 switch(context->bpp) {
613 g_set_error_literal(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_UNKNOWN_TYPE, _("Image has unsupported bpp"));
617 if(context->num_planes < 1 || context->num_planes > 4) {
618 g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_UNKNOWN_TYPE, _("Image has unsupported number of %d-bit planes"), 1);
625 if(context->num_planes != 1) {
626 g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_UNKNOWN_TYPE, _("Image has unsupported number of %d-bit planes"), (gint)context->bpp);
631 break; /* context's bpp is set to 24 if there are three 8-bit planes */
635 g_print("io-pcx: header loaded\n");
636 g_print("bpp: %u\n", context->bpp);
637 g_print("dimensions: %ux%u\n", context->width, context->height);
640 context->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, context->width, context->height);
641 if(!context->pixbuf) {
642 g_set_error_literal(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't create new pixbuf"));
645 context->data = gdk_pixbuf_get_pixels(context->pixbuf);
646 context->rowstride = gdk_pixbuf_get_rowstride(context->pixbuf);
648 context->line = g_try_malloc(sizeof(guchar) * context->bytesperline * context->num_planes);
650 g_set_error_literal(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't allocate memory for line data"));
654 if(context->bpp == 8) {
655 context->p_data = g_try_malloc(sizeof(guchar) * context->width * context->height);
656 if(!context->p_data) {
657 g_set_error_literal(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't allocate memory for paletted data"));
662 if(context->prepared_func)
663 context->prepared_func(context->pixbuf, NULL, context->user_data);
665 context->current_task = PCX_TASK_LOAD_DATA;
671 if(context->current_task == PCX_TASK_LOAD_DATA) {
672 switch(context->bpp) {
674 g_set_error_literal(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_UNKNOWN_TYPE, _("Image has unsupported bpp"));
678 retval = pcx_increment_load_data_1(context);
681 retval = pcx_increment_load_data_2(context);
684 retval = pcx_increment_load_data_4(context);
687 retval = pcx_increment_load_data_8(context);
690 retval = pcx_increment_load_data_24(context);
699 gdk_pixbuf__pcx_stop_load(gpointer data, GError **error)
701 struct pcx_context *context = (struct pcx_context *)data;
703 if(context->current_line != context->height) {
704 g_set_error_literal(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, _("Didn't get all lines of PCX image"));
705 free_pcx_context(context, FALSE);
709 if(context->current_task == PCX_TASK_LOAD_PALETTE) {
710 if(!pcx_load_palette_8(context)) {
711 g_set_error_literal(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, _("No palette found at end of PCX data"));
712 free_pcx_context(context, FALSE);
717 free_pcx_context(context, FALSE);
723 #define MODULE_ENTRY(function) G_MODULE_EXPORT void function
725 #define MODULE_ENTRY(function) void _gdk_pixbuf__pcx_ ## function
728 MODULE_ENTRY (fill_vtable) (GdkPixbufModule *module)
730 module->begin_load = gdk_pixbuf__pcx_begin_load;
731 module->stop_load = gdk_pixbuf__pcx_stop_load;
732 module->load_increment = gdk_pixbuf__pcx_load_increment;
735 MODULE_ENTRY (fill_info) (GdkPixbufFormat *info)
737 static GdkPixbufModulePattern signature[] = {
738 { "\x0a \x01", NULL, 100 },
739 { "\x0a\x02\x01", NULL, 100 },
740 { "\x0a\x03\x01", NULL, 100 },
741 { "\x0a\x04\x01", NULL, 100 },
742 { "\x0a\x05\x01", NULL, 100 },
745 static gchar *mime_types[] = {
749 static gchar *extensions[] = {
755 info->signature = signature;
756 info->description = N_("The PCX image format");
757 info->mime_types = mime_types;
758 info->extensions = extensions;
759 info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
760 info->license = "LGPL";