]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/io-pcx.c
e6de063161751256ecc2b6383eab364a60f98d4c
[~andy/gtk] / gdk-pixbuf / io-pcx.c
1 /*
2  * GdkPixbuf library - PCX image loader
3  *
4  * Copyright (C) 2003 Josh A. Beam
5  *
6  * Authors: Josh A. Beam <josh@joshbeam.com>
7  *
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.
12  *
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.
17  *
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.
22  */
23
24 #include <config.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include "gdk-pixbuf-private.h"
28 #include "gdk-pixbuf-io.h"
29
30 #undef PCX_DEBUG
31
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
36
37 struct pcx_header {
38         guint8 manufacturer;
39         guint8 version;
40         guint8 encoding;
41         guint8 bitsperpixel;
42         gint16 xmin;
43         gint16 ymin;
44         gint16 xmax;
45         gint16 ymax;
46         guint16 horizdpi;
47         guint16 vertdpi;
48         guint8 palette[48];
49         guint8 reserved;
50         guint8 colorplanes;
51         guint16 bytesperline;
52         guint16 palettetype;
53         guint16 hscrsize;
54         guint16 vscrsize;
55         guint8 filler[54];
56 };
57
58 struct pcx_context {
59         GdkPixbuf *pixbuf;
60         gint rowstride;
61
62         GdkPixbufModuleSizeFunc size_func;
63         GdkPixbufModuleUpdatedFunc updated_func;
64         GdkPixbufModulePreparedFunc prepared_func;
65         gpointer user_data;
66
67         guchar current_task;
68
69         gboolean header_loaded;
70         struct pcx_header *header;
71         guint bpp;
72         gint width, height;
73         guint num_planes;
74         guint bytesperline;
75
76         guchar *buf;
77         guint buf_size;
78         guint buf_pos;
79         guchar *data;
80         guchar *line;
81         guint current_line;
82         guchar *p_data;
83 };
84
85 /*
86  * set context's image information based on the header
87  */
88 static void
89 fill_pcx_context(struct pcx_context *context)
90 {
91         struct pcx_header *header = context->header;
92
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;
98
99         if(header->version == 5 && context->bpp == 8 && context->num_planes == 3)
100                 context->bpp = 24;
101 }
102
103 static void
104 free_pcx_context(struct pcx_context *context, gboolean unref_pixbuf)
105 {
106         if(context->header)
107                 g_free(context->header);
108         if(context->buf)
109                 g_free(context->buf);
110         if(unref_pixbuf && context->pixbuf)
111                 g_object_unref(context->pixbuf);
112         if(context->line)
113                 g_free(context->line);
114         if(context->p_data)
115                 g_free(context->p_data);
116
117         g_free(context);
118 }
119
120 /*
121  * read each plane of a single scanline. store_planes is
122  * the number of planes that can be stored in the planes array.
123  * data is the pointer to the block of memory to read
124  * from, size is the length of that data, and line_bytes
125  * is where the number of bytes read will be stored.
126  */
127 static gboolean
128 read_scanline_data(guchar *data, guint size, guchar *planes[],
129                    guint store_planes, guint num_planes, guint bytesperline,
130                    guint *line_bytes)
131 {
132         guint i, j;
133         guint p, count;
134         guint d = 0;
135         guint8 byte;
136
137         for(p = 0; p < num_planes; p++) {
138                 for(i = 0; i < bytesperline;) { /* i incremented when line byte set */
139                         if(d >= size)
140                                 return FALSE;
141                         byte = data[d++];
142
143                         if(byte >> 6 == 0x3) {
144                                 count = byte & ~(0x3 << 6);
145                                 if(count == 0)
146                                         return FALSE;
147                                 if(d >= size)
148                                         return FALSE;
149                                 byte = data[d++];
150                         } else {
151                                 count = 1;
152                         }
153
154                         for(j = 0; j < count; j++) {
155                                 if(p < store_planes)
156                                         planes[p][i++] = byte;
157                                 else
158                                         i++;
159
160                                 if(i >= bytesperline) {
161                                         p++;
162                                         if(p >= num_planes) {
163                                                 *line_bytes = d;
164                                                 return TRUE;
165                                         }
166                                         i = 0;
167                                 }
168                         }
169                 }
170         }
171
172         *line_bytes = d; /* number of bytes read for scanline */
173         return TRUE;
174 }
175
176 static gpointer
177 gdk_pixbuf__pcx_begin_load(GdkPixbufModuleSizeFunc size_func,
178                            GdkPixbufModulePreparedFunc prepared_func,
179                            GdkPixbufModuleUpdatedFunc updated_func,
180                            gpointer user_data, GError **error)
181 {
182         struct pcx_context *context;
183
184         context = g_new0(struct pcx_context, 1);
185         if(!context)
186                 return NULL;
187
188         context->header = g_try_malloc(sizeof(struct pcx_header));
189         if(!context->header) {
190                 g_free(context);
191                 g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't allocate memory for header"));
192                 return NULL;
193         }
194
195         context->size_func = size_func;
196         context->updated_func = updated_func;
197         context->prepared_func = prepared_func;
198         context->user_data = user_data;
199
200         context->current_task = PCX_TASK_LOAD_HEADER;
201
202         context->buf = g_try_malloc(sizeof(guchar) * 512);
203         if(!context->buf) {
204                 g_free(context->header);
205                 g_free(context);
206                 g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't allocate memory for context buffer"));
207                 return NULL;
208         }
209         context->buf_size = 512;
210
211         context->header_loaded = FALSE;
212
213         return context;
214 }
215
216 static gboolean
217 pcx_resize_context_buf(struct pcx_context *context, guint size)
218 {
219         guchar *new_buf;
220
221         new_buf = g_try_realloc(context->buf, size);
222         if(!new_buf)
223                 return FALSE;
224
225         context->buf = new_buf;
226         context->buf_size = size;
227         return TRUE;
228 }
229
230 /*
231  * remove a number of bytes (specified by size) from the
232  * beginning of a context's buf
233  */
234 static gboolean
235 pcx_chop_context_buf(struct pcx_context *context, guint size)
236 {
237         guint i, j;
238
239         if(size > context->buf_pos)
240                 return FALSE;
241         else if(size < 0)
242                 return FALSE;
243         else if(size == 0)
244                 return TRUE;
245
246         for(i = 0, j = size; j < context->buf_pos; i++, j++)
247                 context->buf[i] = context->buf[j];
248
249         context->buf_pos -= size;
250
251         return TRUE;
252 }
253
254 static guchar
255 read_pixel_1(guchar *data, guint offset)
256 {
257         guchar retval;
258         guint bit_offset;
259
260         if(!(offset % 8)) {
261                 offset /= 8;
262                 retval = data[offset] >> 7;
263         } else {
264                 bit_offset = offset % 8;
265                 offset -= bit_offset;
266                 offset /= 8;
267                 retval = (data[offset] >> (7 - bit_offset)) & 0x1;
268         }
269
270         return retval;
271 }
272
273 static guchar
274 read_pixel_4(guchar *data, guint offset)
275 {
276         guchar retval;
277
278         if(!(offset % 2)) {
279                 offset /= 2;
280                 retval = data[offset] >> 4;
281         } else {
282                 offset--;
283                 offset /= 2;
284                 retval = data[offset] & 0xf;
285         }
286
287         return retval;
288 }
289
290 static gboolean
291 pcx_increment_load_data_1(struct pcx_context *context)
292 {
293         guint i;
294         guchar *planes[4];
295         guint line_bytes;
296         guint store_planes;
297
298         if(context->num_planes == 4) {
299                 planes[0] = context->line;
300                 planes[1] = planes[0] + context->bytesperline;
301                 planes[2] = planes[1] + context->bytesperline;
302                 planes[3] = planes[2] + context->bytesperline;
303                 store_planes = 4;
304         } else if(context->num_planes == 3) {
305                 planes[0] = context->line;
306                 planes[1] = planes[0] + context->bytesperline;
307                 planes[2] = planes[1] + context->bytesperline;
308                 store_planes = 3;
309         } else if(context->num_planes == 2) {
310                 planes[0] = context->line;
311                 planes[1] = planes[0] + context->bytesperline;
312                 store_planes = 2;
313         } else if(context->num_planes == 1) {
314                 planes[0] = context->line;
315                 store_planes = 1;
316         } else {
317                 return FALSE;
318         }
319
320         while(read_scanline_data(context->buf, context->buf_pos, planes, store_planes, context->num_planes, context->bytesperline, &line_bytes)) {
321                 pcx_chop_context_buf(context, line_bytes);
322
323                 for(i = 0; i < context->width; i++) {
324                         guchar p;
325
326                         if(context->num_planes == 4) {
327                                 p = read_pixel_1(planes[3], i);
328                                 p <<= 1;
329                                 p |= read_pixel_1(planes[2], i);
330                                 p <<= 1;
331                                 p |= read_pixel_1(planes[1], i);
332                                 p <<= 1;
333                                 p |= read_pixel_1(planes[0], i);
334                         } else if(context->num_planes == 3) {
335                                 p = read_pixel_1(planes[2], i);
336                                 p <<= 1;
337                                 p |= read_pixel_1(planes[1], i);
338                                 p <<= 1;
339                                 p |= read_pixel_1(planes[0], i);
340                         } else if(context->num_planes == 2) {
341                                 p = read_pixel_1(planes[1], i);
342                                 p <<= 1;
343                                 p |= read_pixel_1(planes[0], i);
344                         } else if(context->num_planes == 1) {
345                                 p = read_pixel_1(planes[0], i);
346                         } else {
347                                 return FALSE;
348                         }
349                         p &= 0xf;
350                         context->data[context->current_line * context->rowstride + i * 3 + 0] = context->header->palette[p * 3 + 0];
351                         context->data[context->current_line * context->rowstride + i * 3 + 1] = context->header->palette[p * 3 + 1];
352                         context->data[context->current_line * context->rowstride + i * 3 + 2] = context->header->palette[p * 3 + 2];
353                 }
354
355                 if(context->updated_func)
356                         context->updated_func(context->pixbuf, 0, context->current_line, context->width, 1, context->user_data);
357
358                 context->current_line++;
359
360                 if(context->current_line == context->height) {
361                         context->current_task = PCX_TASK_DONE;
362                         return TRUE;
363                 }
364         }
365
366         return TRUE;
367 }
368
369 static gboolean
370 pcx_increment_load_data_2(struct pcx_context *context)
371 {
372         guint i;
373         guchar *planes[1];
374         guint line_bytes;
375         guint shift, h;
376
377         planes[0] = context->line;
378
379         while(read_scanline_data(context->buf, context->buf_pos, planes, 1, context->num_planes, context->bytesperline, &line_bytes)) {
380                 pcx_chop_context_buf(context, line_bytes);
381
382                 for(i = 0; i < context->width; i++) {
383                         shift = 6 - 2 * (i % 4);
384                         h = (planes[0][i / 4] >> shift) & 0x3;
385                         context->data[context->current_line * context->rowstride + i * 3 + 0] = context->header->palette[h * 3 + 0];
386                         context->data[context->current_line * context->rowstride + i * 3 + 1] = context->header->palette[h * 3 + 1];
387                         context->data[context->current_line * context->rowstride + i * 3 + 2] = context->header->palette[h * 3 + 2];
388                 }
389
390                 if(context->updated_func)
391                         context->updated_func(context->pixbuf, 0, context->current_line, context->width, 1, context->user_data);
392
393                 context->current_line++;
394
395                 if(context->current_line == context->height) {
396                         context->current_task = PCX_TASK_DONE;
397                         return TRUE;
398                 }
399         }
400
401         return TRUE;
402 }
403
404 static gboolean
405 pcx_increment_load_data_4(struct pcx_context *context)
406 {
407         guint i;
408         guchar *planes[1];
409         guint line_bytes;
410
411         planes[0] = context->line;
412
413         while(read_scanline_data(context->buf, context->buf_pos, planes, 1, context->num_planes, context->bytesperline, &line_bytes)) {
414                 pcx_chop_context_buf(context, line_bytes);
415
416                 for(i = 0; i < context->width; i++) {
417                         guchar p;
418
419                         p = read_pixel_4(planes[0], i) & 0xf;
420                         context->data[context->current_line * context->rowstride + i * 3 + 0] = context->header->palette[p * 3 + 0];
421                         context->data[context->current_line * context->rowstride + i * 3 + 1] = context->header->palette[p * 3 + 1];
422                         context->data[context->current_line * context->rowstride + i * 3 + 2] = context->header->palette[p * 3 + 2];
423                 }
424
425                 if(context->updated_func)
426                         context->updated_func(context->pixbuf, 0, context->current_line, context->width, 1, context->user_data);
427
428                 context->current_line++;
429
430                 if(context->current_line == context->height) {
431                         context->current_task = PCX_TASK_DONE;
432                         return TRUE;
433                 }
434         }
435
436         return TRUE;
437 }
438
439 /*
440  * for loading 8-bit pcx images, we keep a buffer containing
441  * each pixel's palette number; once we've loaded each scanline,
442  * we wait for loading to stop and call pcx_load_palette_8,
443  * which finds the palette at the end of the pcx data and sets the
444  * RGB data.
445  */
446 static gboolean
447 pcx_increment_load_data_8(struct pcx_context *context)
448 {
449         guint i;
450         guchar *planes[1];
451         guint line_bytes;
452
453         planes[0] = context->line;
454
455         while(read_scanline_data(context->buf, context->buf_pos, planes, 1, context->num_planes, context->bytesperline, &line_bytes)) {
456                 pcx_chop_context_buf(context, line_bytes);
457
458                 for(i = 0; i < context->width; i++)
459                         context->p_data[context->current_line * context->width + i + 0] = planes[0][i];
460
461                 context->current_line++;
462
463                 if(context->current_line == context->height) {
464                         context->current_task = PCX_TASK_LOAD_PALETTE;
465                         return TRUE;
466                 }
467         }
468
469         return TRUE;
470 }
471
472 /*
473  * read the palette and set the RGB data
474  */
475 static gboolean
476 pcx_load_palette_8(struct pcx_context *context)
477 {
478         guint i, j;
479
480         if(context->current_line < context->height)
481                 return FALSE;
482
483         if(context->buf_pos >= 769) {
484                 guchar *palette = context->buf + (context->buf_pos - 769);
485
486                 if(palette[0] == 12) {
487                         palette++;
488                         for(i = 0; i < context->height; i++) {
489                                 for(j = 0; j < context->width; j++) {
490                                         context->data[i * context->rowstride + j * 3 + 0] = palette[(context->p_data[i * context->width + j]) * 3 + 0];
491                                         context->data[i * context->rowstride + j * 3 + 1] = palette[(context->p_data[i * context->width + j]) * 3 + 1];
492                                         context->data[i * context->rowstride + j * 3 + 2] = palette[(context->p_data[i * context->width + j]) * 3 + 2];
493                                 }
494
495                                 if(context->updated_func)
496                                         context->updated_func(context->pixbuf, 0, i, context->width, 1, context->user_data);
497                         }
498
499 #ifdef PCX_DEBUG
500                         g_print("read palette\n");
501 #endif
502
503                         context->current_task = PCX_TASK_DONE;
504                         return TRUE;
505                 } else {
506 #ifdef PCX_DEBUG
507                         g_print("this ain't a palette\n");
508 #endif
509                         return FALSE;
510                 }
511         }
512
513         return FALSE;
514 }
515
516 /*
517  * in 24-bit images, each scanline has three color planes
518  * for red, green, and blue, respectively.
519  */
520 static gboolean
521 pcx_increment_load_data_24(struct pcx_context *context)
522 {
523         guint i;
524         guchar *planes[3];
525         guint line_bytes;
526
527         planes[0] = context->line;
528         planes[1] = planes[0] + context->bytesperline;
529         planes[2] = planes[1] + context->bytesperline;
530
531         while(read_scanline_data(context->buf, context->buf_pos, planes, 3, context->num_planes, context->bytesperline, &line_bytes)) {
532                 pcx_chop_context_buf(context, line_bytes);
533
534                 for(i = 0; i < context->width; i++) {
535                         context->data[context->current_line * context->rowstride + i * 3 + 0] = planes[0][i];
536                         context->data[context->current_line * context->rowstride + i * 3 + 1] = planes[1][i];
537                         context->data[context->current_line * context->rowstride + i * 3 + 2] = planes[2][i];
538                 }
539
540                 if(context->updated_func)
541                         context->updated_func(context->pixbuf, 0, context->current_line, context->width, 1, context->user_data);
542
543                 context->current_line++;
544
545                 if(context->current_line == context->height) {
546                         context->current_task = PCX_TASK_DONE;
547                         return TRUE;
548                 }
549         }
550
551         return TRUE;
552 }
553
554 static gboolean
555 gdk_pixbuf__pcx_load_increment(gpointer data, const guchar *buf, guint size,
556                                GError **error)
557 {
558         struct pcx_context *context = (struct pcx_context *)data;
559         struct pcx_header *header;
560         guint i;
561         gboolean retval = TRUE;
562
563         /* if context's buf isn't large enough to hold its current data plus the passed buf, increase its size */
564         if(context->buf_pos + size > context->buf_size) {
565                 if(!pcx_resize_context_buf(context, sizeof(guchar) * (context->buf_pos + size))) {
566                         g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't allocate memory for context buffer"));
567                         return FALSE;
568                 }
569         }
570
571         for(i = 0; i < size; i++)
572                 context->buf[context->buf_pos++] = buf[i];
573
574         if(context->current_task == PCX_TASK_LOAD_HEADER) {
575                 if(!context->header_loaded && context->buf_pos > sizeof(struct pcx_header)) { /* set header */
576                         gint width, height;
577
578                         memcpy(context->header, context->buf, sizeof(struct pcx_header));
579                         pcx_chop_context_buf(context, sizeof(struct pcx_header));
580                         header = context->header;
581
582                         /* convert the multi-byte header variables that will be used */
583                         header->xmin = GINT16_FROM_LE(header->xmin);
584                         header->ymin = GINT16_FROM_LE(header->ymin);
585                         header->xmax = GINT16_FROM_LE(header->xmax);
586                         header->ymax = GINT16_FROM_LE(header->ymax);
587                         header->bytesperline = GUINT16_FROM_LE(header->bytesperline);
588
589 #ifdef PCX_DEBUG
590                         g_print ("Manufacturer %d\n"
591                                  "Version %d\n"
592                                  "Encoding %d\n"
593                                  "Bits/Pixel %d\n"
594                                  "Planes %d\n"
595                                  "Palette %d\n", 
596                                  header->manufacturer, header->version, 
597                                  header->encoding, header->bitsperpixel,
598                                  header->colorplanes, header->palettetype);
599 #endif
600
601                         context->header_loaded = TRUE;
602                         fill_pcx_context(context);
603
604                         width = context->width;
605                         height = context->height;
606                         if(width <= 0 || height <= 0) {
607                                 g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("Image has invalid width and/or height"));
608                                 return FALSE;
609                         }
610                         if (context->size_func)
611                           {
612                             (*context->size_func) (&width, &height, context->user_data);
613                             if (width == 0 || height == 0)
614                               return TRUE;
615                           }
616
617                         switch(context->bpp) {
618                                 default:
619                                         g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_UNKNOWN_TYPE, _("Image has unsupported bpp"));
620                                         return FALSE;
621                                         break;
622                                 case 1:
623                                         if(context->num_planes < 1 || context->num_planes > 4) {
624                                                 g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_UNKNOWN_TYPE, _("Image has unsupported number of %d-bit planes"), 1);
625                                                 return FALSE;
626                                         }
627                                         break;
628                                 case 2:
629                                 case 4:
630                                 case 8:
631                                         if(context->num_planes != 1) {
632                                                 g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_UNKNOWN_TYPE, _("Image has unsupported number of %d-bit planes"), context->bpp);
633                                                 return FALSE;
634                                         }
635                                         break;
636                                 case 24:
637                                         break; /* context's bpp is set to 24 if there are three 8-bit planes */
638                         }
639
640 #ifdef PCX_DEBUG
641                         g_print("io-pcx: header loaded\n");
642                         g_print("bpp: %u\n", context->bpp);
643                         g_print("dimensions: %ux%u\n", context->width, context->height);
644 #endif
645
646                         context->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, context->width, context->height);
647                         if(!context->pixbuf) {
648                                 g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't create new pixbuf"));
649                                 return FALSE;
650                         }
651                         context->data = gdk_pixbuf_get_pixels(context->pixbuf);
652                         context->rowstride = gdk_pixbuf_get_rowstride(context->pixbuf);
653
654                         context->line = g_try_malloc(sizeof(guchar) * context->bytesperline * context->num_planes);
655                         if(!context->line) {
656                                 g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't allocate memory for line data"));
657                                 return FALSE;
658                         }
659
660                         if(context->bpp == 8) {
661                                 context->p_data = g_try_malloc(sizeof(guchar) * context->width * context->height);
662                                 if(!context->p_data) {
663                                         g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't allocate memory for paletted data"));
664                                         return FALSE;
665                                 }
666                         }
667
668                         if(context->prepared_func)
669                                 context->prepared_func(context->pixbuf, NULL, context->user_data);
670
671                         context->current_task = PCX_TASK_LOAD_DATA;
672                 }
673
674                 retval = TRUE;
675         }
676
677         if(context->current_task == PCX_TASK_LOAD_DATA) {
678                 switch(context->bpp) {
679                         default:
680                                 g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_UNKNOWN_TYPE, _("Image has unsupported bpp"));
681                                 retval = FALSE;
682                                 break;
683                         case 1:
684                                 retval = pcx_increment_load_data_1(context);
685                                 break;
686                         case 2:
687                                 retval = pcx_increment_load_data_2(context);
688                                 break;
689                         case 4:
690                                 retval = pcx_increment_load_data_4(context);
691                                 break;
692                         case 8:
693                                 retval = pcx_increment_load_data_8(context);
694                                 break;
695                         case 24:
696                                 retval = pcx_increment_load_data_24(context);
697                                 break;
698                 }
699         }
700
701         return retval;
702 }
703
704 static gboolean
705 gdk_pixbuf__pcx_stop_load(gpointer data, GError **error)
706 {
707         struct pcx_context *context = (struct pcx_context *)data;
708
709         if(context->current_line != context->height) {
710                 g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, _("Didn't get all lines of PCX image"));
711                 free_pcx_context(context, FALSE);
712                 return FALSE;
713         }
714
715         if(context->current_task == PCX_TASK_LOAD_PALETTE) {
716                 if(!pcx_load_palette_8(context)) {
717                         g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, _("No palette found at end of PCX data"));
718                         free_pcx_context(context, FALSE);
719                         return FALSE;
720                 }
721         }
722
723         free_pcx_context(context, FALSE);
724
725         return TRUE;
726 }
727
728 void
729 MODULE_ENTRY (pcx, fill_vtable) (GdkPixbufModule *module)
730 {
731         module->begin_load = gdk_pixbuf__pcx_begin_load;
732         module->stop_load = gdk_pixbuf__pcx_stop_load;
733         module->load_increment = gdk_pixbuf__pcx_load_increment;
734 }
735
736 void
737 MODULE_ENTRY (pcx, fill_info) (GdkPixbufFormat *info)
738 {
739         static GdkPixbufModulePattern signature[] = {
740                 { "\x0a \x01", NULL, 100 },
741                 { "\x0a\x02\x01", NULL, 100 },
742                 { "\x0a\x03\x01", NULL, 100 },
743                 { "\x0a\x04\x01", NULL, 100 },
744                 { "\x0a\x05\x01", NULL, 100 },
745                 { NULL, NULL, 0 }
746         };
747         static gchar *mime_types[] = {
748                 "image/x-pcx",
749                 NULL,
750         };
751         static gchar *extensions[] = {
752                 "pcx",
753                 NULL,
754         };
755
756         info->name = "pcx";
757         info->signature = signature;
758         info->description = N_("The PCX image format");
759         info->mime_types = mime_types;
760         info->extensions = extensions;
761         info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
762         info->license = "LGPL";
763 }