]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/io-ras.c
Support for separately installed loaders. (#77486)
[~andy/gtk] / gdk-pixbuf / io-ras.c
1 /* -*- mode: C; c-file-style: "linux" -*- */
2 /* GdkPixbuf library - SUNRAS image loader
3  *
4  * Copyright (C) 1999 The Free Software Foundation
5  *
6  * Authors: Arjan van de Ven <arjan@fenrus.demon.nl>
7  *          Federico Mena-Quintero <federico@gimp.org>
8  *
9  * Based on io-gif.c, io-tiff.c and io-png.c
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the
23  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24  * Boston, MA 02111-1307, USA.
25  */
26
27 /*
28
29 Known bugs:
30         * Compressed rasterfiles don't work yet
31
32 */
33
34 #include <config.h>
35 #include <stdio.h>
36 #ifdef HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
39 #include <string.h>
40 #include "gdk-pixbuf-private.h"
41 #include "gdk-pixbuf-io.h"
42
43 \f
44
45 /* 
46    Header structure for sunras files.
47    All values are in big-endian order on disk
48    
49    Note: Every scanline is padded to be a multiple of 16 bits
50  */
51
52 struct rasterfile {
53         guint magic;
54         guint width;
55         guint height;
56         guint depth;
57         guint length;
58         guint type;
59         guint maptype;
60         guint maplength;
61 };
62
63 /* 
64         This does a byte-order swap. Does glib have something like
65         be32_to_cpu() ??
66 */
67
68 /* Progressive loading */
69
70 struct ras_progressive_state {
71         GdkPixbufModulePreparedFunc prepared_func;
72         GdkPixbufModuleUpdatedFunc updated_func;
73         gpointer user_data;
74
75         gint HeaderSize;        /* The size of the header-part (incl colormap) */
76         guchar *HeaderBuf;      /* The buffer for the header (incl colormap) */
77         gint HeaderDone;        /* The nr of bytes actually in HeaderBuf */
78
79         gint LineWidth;         /* The width of a line in bytes */
80         guchar *LineBuf;        /* Buffer for 1 line */
81         gint LineDone;          /* # of bytes in LineBuf */
82         gint Lines;             /* # of finished lines */
83
84         gint RasType;           /*  32 = BGRA
85                                    24 = BGR
86                                    8 = 8 bit colormapped
87                                    1  = 1 bit bitonal 
88                                  */
89         gint DecoderState;        
90
91         struct rasterfile Header;       /* Decoded (BE->CPU) header */
92
93
94         GdkPixbuf *pixbuf;      /* Our "target" */
95 };
96
97 static gpointer
98 gdk_pixbuf__ras_image_begin_load(GdkPixbufModuleSizeFunc size_func,
99                                  GdkPixbufModulePreparedFunc prepared_func,
100                                  GdkPixbufModuleUpdatedFunc updated_func,
101                                  gpointer user_data,
102                                  GError **error);
103 static gboolean gdk_pixbuf__ras_image_stop_load(gpointer data, GError **error);
104 static gboolean gdk_pixbuf__ras_image_load_increment(gpointer data,
105                                                      const guchar * buf, guint size,
106                                                      GError **error);
107
108 static gboolean RAS2State(struct rasterfile *RAS,
109                           struct ras_progressive_state *State,
110                           GError **error)
111 {
112         State->Header.width = GUINT32_FROM_BE(RAS->width);
113         State->Header.height = GUINT32_FROM_BE(RAS->height);
114         State->Header.depth = GUINT32_FROM_BE(RAS->depth);
115         State->Header.type = GUINT32_FROM_BE(RAS->type);
116         State->Header.maptype = GUINT32_FROM_BE(RAS->maptype);
117         State->Header.maplength = GUINT32_FROM_BE(RAS->maplength);
118
119         if ((gint)State->Header.width <= 0 ||
120             (gint)State->Header.height <= 0 || 
121             State->Header.maplength > 768) {
122                 g_set_error (error,
123                              GDK_PIXBUF_ERROR,
124                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
125                              _("RAS image has bogus header data")); 
126                 return FALSE;
127         }
128
129         State->RasType = State->Header.depth;   /* This may be less trivial someday */
130         State->HeaderSize = 32 + State->Header.maplength;
131
132         if (State->RasType == 32)
133                 State->LineWidth = State->Header.width * 4;
134         else if (State->RasType == 24)
135                 State->LineWidth = State->Header.width * 3;
136         else if (State->RasType == 8)
137                 State->LineWidth = State->Header.width * 1;
138         else if (State->RasType == 1) {
139                 State->LineWidth = State->Header.width / 8;
140                 if ((State->Header.width & 7) != 0)
141                         State->LineWidth++;
142         }
143         else {
144                 g_set_error (error,
145                              GDK_PIXBUF_ERROR,
146                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
147                              _("RAS image has unknown type")); 
148                 return FALSE;
149         }
150
151         if (State->Header.type > 2 || State->Header.maptype > 1) {
152                 g_set_error (error,
153                              GDK_PIXBUF_ERROR,
154                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
155                              _("unsupported RAS image variation")); 
156                 return FALSE;
157         }
158
159         /* Now pad the line to be a multiple of 16 bits */
160         if ((State->LineWidth & 1) != 0)
161                 State->LineWidth++;
162
163         if (!State->LineBuf) {
164                 State->LineBuf = g_try_malloc (State->LineWidth);
165
166                 if (!State->LineBuf) {
167                         g_set_error (error,
168                                      GDK_PIXBUF_ERROR,
169                                      GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
170                                      _("Not enough memory to load RAS image")); 
171                         return FALSE;
172                 }
173         }
174
175
176         if (!State->pixbuf) {
177                 if (State->RasType == 32)
178                         State->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
179                                                         (gint) State->Header.width,
180                                                         (gint) State->Header.height);
181                 else
182                         State->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8,
183                                                         (gint) State->Header.width,
184                                                         (gint) State->Header.height);
185                 
186                 if (!State->pixbuf) {
187                         g_set_error (error,
188                                      GDK_PIXBUF_ERROR,
189                                      GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
190                                      _("Not enough memory to load RAS image"));
191                         return FALSE;
192                 }
193
194                 if (State->prepared_func != NULL)
195                         /* Notify the client that we are ready to go */
196                         (*State->prepared_func) (State->pixbuf,
197                                                  NULL,
198                                                  State->user_data);
199
200         }
201         
202         if ((State->Header.maplength == 0) && (State->RasType == 1)) {
203                 State->HeaderBuf[32] = 255;
204                 State->HeaderBuf[33] = 0;
205                 State->HeaderBuf[34] = 255;
206                 State->HeaderBuf[35] = 0;
207                 State->HeaderBuf[36] = 255;
208                 State->HeaderBuf[37] = 0;
209         }
210
211         return TRUE;
212 }
213
214 /* 
215  * func - called when we have pixmap created (but no image data)
216  * user_data - passed as arg 1 to func
217  * return context (opaque to user)
218  */
219
220 static gpointer
221 gdk_pixbuf__ras_image_begin_load(GdkPixbufModuleSizeFunc size_func, 
222                                  GdkPixbufModulePreparedFunc prepared_func,
223                                  GdkPixbufModuleUpdatedFunc updated_func,
224                                  gpointer user_data,
225                                  GError **error)
226 {
227         struct ras_progressive_state *context;
228
229         context = g_new0(struct ras_progressive_state, 1);
230         context->prepared_func = prepared_func;
231         context->updated_func = updated_func;
232         context->user_data = user_data;
233
234         context->HeaderSize = 32;
235         context->HeaderBuf = g_malloc(32 + 768);        /* 32 for rasheader,
236                                                            768 for the colormap */
237         context->HeaderDone = 0;
238
239         context->LineWidth = 0;
240         context->LineBuf = NULL;
241         context->LineDone = 0;
242         context->Lines = 0;
243
244         context->RasType = 0;
245         context->DecoderState = 0;
246         
247         memset(&context->Header, 0, sizeof(struct rasterfile));
248
249
250         context->pixbuf = NULL;
251
252
253         return (gpointer) context;
254 }
255
256 /*
257  * context - returned from image_begin_load
258  *
259  * free context, unref gdk_pixbuf
260  */
261 static gboolean
262 gdk_pixbuf__ras_image_stop_load(gpointer data, GError **error)
263 {
264         struct ras_progressive_state *context =
265             (struct ras_progressive_state *) data;
266
267         /* FIXME this thing needs to report errors if
268          * we have unused image data
269          */
270
271         g_return_val_if_fail(context != NULL, TRUE);
272
273         if (context->LineBuf != NULL)
274                 g_free(context->LineBuf);
275         if (context->HeaderBuf != NULL)
276                 g_free(context->HeaderBuf);
277
278         if (context->pixbuf)
279                 g_object_unref(context->pixbuf);
280
281         g_free(context);
282
283         return TRUE;
284 }
285
286 /* 
287  OneLine is called when enough data is received to process 1 line 
288  of pixels 
289  */
290
291 static void OneLine32(struct ras_progressive_state *context)
292 {
293         gint X;
294         guchar *Pixels;
295
296         X = 0;
297         Pixels = context->pixbuf->pixels + context->pixbuf->rowstride * context->Lines;
298         while (X < context->Header.width) {
299                 /* The joys of having a BGR byteorder */
300                 Pixels[X * 4 + 0] = context->LineBuf[X * 4 + 2];
301                 Pixels[X * 4 + 1] = context->LineBuf[X * 4 + 1];
302                 Pixels[X * 4 + 2] = context->LineBuf[X * 4 + 0];
303                 Pixels[X * 4 + 3] = context->LineBuf[X * 4 + 3];
304                 X++;
305         }
306 }
307
308 static void OneLine24(struct ras_progressive_state *context)
309 {
310         gint X;
311         guchar *Pixels;
312
313         X = 0;
314         Pixels = context->pixbuf->pixels + context->pixbuf->rowstride * context->Lines;
315         while (X < context->Header.width) {
316                 /* The joys of having a BGR byteorder */
317                 Pixels[X * 3 + 0] = context->LineBuf[X * 3 + 2];
318                 Pixels[X * 3 + 1] = context->LineBuf[X * 3 + 1];
319                 Pixels[X * 3 + 2] = context->LineBuf[X * 3 + 0];
320                 X++;
321         }
322
323 }
324
325 static void OneLine8(struct ras_progressive_state *context)
326 {
327         gint X;
328         guchar *Pixels;
329         int offset = context->Header.maplength / 3;
330
331         X = 0;
332         Pixels = context->pixbuf->pixels + context->pixbuf->rowstride * context->Lines;
333         while (X < context->Header.width) {
334                 /* The joys of having a BGR byteorder */
335                 Pixels[X * 3 + 0] =
336                     context->HeaderBuf[context->LineBuf[X] + 32];
337                 Pixels[X * 3 + 1] =
338                     context->HeaderBuf[context->LineBuf[X] + offset + 32];
339                 Pixels[X * 3 + 2] =
340                     context->HeaderBuf[context->LineBuf[X] + 2*offset + 32];
341                 X++;
342         }
343 }
344
345 static void OneLine1(struct ras_progressive_state *context)
346 {
347         gint X;
348         guchar *Pixels;
349
350         X = 0;
351         Pixels = context->pixbuf->pixels + context->pixbuf->rowstride * context->Lines;
352         while (X < context->Header.width) {
353                 int Bit;
354                 
355                 Bit = (context->LineBuf[X/8])>>(7-(X&7));
356                 Bit = Bit & 1;
357                 /* The joys of having a BGR byteorder */
358                 Pixels[X * 3 + 0] =
359                     context->HeaderBuf[Bit + 32];
360                 Pixels[X * 3 + 1] =
361                     context->HeaderBuf[Bit + 2 + 32];
362                 Pixels[X * 3 + 2] =
363                     context->HeaderBuf[Bit + 4 + 32];
364                 X++;
365         }
366 }
367
368
369 static void OneLine(struct ras_progressive_state *context)
370 {
371         context->LineDone = 0;
372         if (context->Lines >= context->Header.height)
373                 return;
374         if (context->RasType == 32)
375                 OneLine32(context);
376         if (context->RasType == 24)
377                 OneLine24(context);
378         if (context->RasType == 8)
379                 OneLine8(context);
380         if (context->RasType == 1)
381                 OneLine1(context);
382
383         context->LineDone = 0;
384         context->Lines++;
385
386         if (context->updated_func != NULL) {
387                 (*context->updated_func) (context->pixbuf,
388                                           0,
389                                           context->Lines,
390                                           context->Header.width,
391                                           1,
392                                           context->user_data);
393
394         }
395 }
396
397 static gboolean
398 DoCompressed (struct ras_progressive_state *context,
399               const guchar * buf, guint size,
400               GError **error)
401 {
402         int i;
403
404         for (i = 0; i < size; i++) {
405                 switch (context->DecoderState) {
406                     case 0:
407                             if (buf[i] == 0x80)
408                                     context->DecoderState = 1;
409                             else
410                                     context->LineBuf[context->LineDone++] = buf[i];
411                             break;
412                     case 1:
413                             if (buf[i] == 0) {
414                                     context->LineBuf[context->LineDone++] = 0x80;
415                                     context->DecoderState = 0;
416                             }
417                             else
418                                     context->DecoderState = buf[i] + 1;
419                             break;
420                     default:
421                             for (; context->DecoderState; context->DecoderState--) {
422                                     context->LineBuf[context->LineDone++] = buf[i];
423                                     if ((context->LineDone >= context->LineWidth) && (context->LineWidth > 0))
424                                             OneLine(context);
425                             }
426                 }
427                 if ((context->LineDone >= context->LineWidth) && (context->LineWidth > 0))
428                         OneLine(context);
429         }
430         return TRUE;
431 }
432
433 /*
434  * context - from image_begin_load
435  * buf - new image data
436  * size - length of new image data
437  *
438  * append image data onto incrementally built output image
439  */
440 static gboolean
441 gdk_pixbuf__ras_image_load_increment(gpointer data,
442                                      const guchar * buf, guint size,
443                                      GError **error)
444 {
445         struct ras_progressive_state *context =
446             (struct ras_progressive_state *) data;
447
448         gint BytesToCopy;
449
450         while (size > 0) {
451                 if (context->HeaderDone < context->HeaderSize) {        /* We still 
452                                                                            have headerbytes to do */
453                         BytesToCopy =
454                             context->HeaderSize - context->HeaderDone;
455                         if (BytesToCopy > size)
456                                 BytesToCopy = size;
457
458                         memmove(context->HeaderBuf + context->HeaderDone,
459                                buf, BytesToCopy);
460
461                         size -= BytesToCopy;
462                         buf += BytesToCopy;
463                         context->HeaderDone += BytesToCopy;
464
465                 } else if (context->Header.type == 2) {
466                         if (!DoCompressed (context, buf, size, error)) {
467                                 return FALSE;
468                         }
469                         size = 0;
470                 }
471                 else {
472                         BytesToCopy =
473                             context->LineWidth - context->LineDone;
474                         if (BytesToCopy > size)
475                                 BytesToCopy = size;
476
477                         if (BytesToCopy > 0) {
478                                 memmove(context->LineBuf +
479                                        context->LineDone, buf,
480                                        BytesToCopy);
481
482                                 size -= BytesToCopy;
483                                 buf += BytesToCopy;
484                                 context->LineDone += BytesToCopy;
485                         }
486                         if ((context->LineDone >= context->LineWidth) &&
487                             (context->LineWidth > 0))
488                                 OneLine(context);
489
490
491                 }
492
493                 if (context->HeaderDone >= 32)
494                         if (!RAS2State((struct rasterfile *) context->HeaderBuf,
495                                        context, error)) {
496                                 return FALSE;
497                         }
498
499
500         }
501
502         return TRUE;
503 }
504
505 void
506 MODULE_ENTRY (ras, fill_vtable) (GdkPixbufModule *module)
507 {
508         module->begin_load = gdk_pixbuf__ras_image_begin_load;
509         module->stop_load = gdk_pixbuf__ras_image_stop_load;
510         module->load_increment = gdk_pixbuf__ras_image_load_increment;
511 }
512
513 void
514 MODULE_ENTRY (ras, fill_info) (GdkPixbufFormat *info)
515 {
516         static GdkPixbufModulePattern signature[] = {
517                 { "\x59\xa6\x6a\x95", NULL, 100 },
518                 { NULL, NULL, 0 }
519         };
520         static gchar * mime_types[] = {
521                 "image/x-cmu-raster",
522                 "image/x-sun-raster",
523                 NULL
524         };
525         static gchar * extensions[] = {
526                 "ras",
527                 NULL
528         };
529
530         info->name = "ras";
531         info->signature = signature;
532         info->description = N_("The Sun raster image format");
533         info->mime_types = mime_types;
534         info->extensions = extensions;
535         info->flags = 0;
536 }
537