]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/io-bmp.c
Major cleanup, added support for 32 bpp and 4 bpp (uncompressed) images,
[~andy/gtk] / gdk-pixbuf / io-bmp.c
1 /* GdkPixbuf library - Windows Bitmap image loader
2  *
3  * Copyright (C) 1999 The Free Software Foundation
4  *
5  * Authors: Arjan van de Ven <arjan@fenrus.demon.nl>
6  *          Federico Mena-Quintero <federico@gimp.org>
7  *
8  * Based on io-ras.c
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public
21  * License along with this library; if not, write to the
22  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23  * Boston, MA 02111-1307, USA.
24  */
25
26 /*
27
28 Known bugs:
29         * 4bpp compressed files don't work
30         * bi-tonal files aren't tested with palettes
31
32 */
33
34 #include <config.h>
35 #include <stdio.h>
36 #include <unistd.h>
37 #include <gdk-pixbuf/gdk-pixbuf.h>
38 #include <gdk-pixbuf/gdk-pixbuf-io.h>
39 \f
40
41
42
43
44 /* 
45
46 These structures are actually dummies. These are according to
47 the "Windows API reference guide volume II" as written by 
48 Borland International, but GCC fiddles with the alignment of 
49 the internal members, so these aren't actually usable.
50
51 */
52
53 struct BitmapFileHeader {
54         gushort bfType;
55         guint bfSize;
56         guint reserverd;
57         guint bfOffbits;
58 };
59
60 struct BitmapInfoHeader {
61         guint biSize;
62         guint biWidth;
63         guint biHeight;
64         gushort biPlanes;
65         gushort biBitCount;
66         guint biCompression;
67         guint biSizeImage;
68         guint biXPelsPerMeter;
69         guint biYPelsPerMeter;
70         guint biClrUsed;
71         guint biClrImportant;
72 };
73
74 /* 
75
76 DumpBIH printf's the values in a BitmapInfoHeader to the screen, for 
77 debugging purposes.
78
79 */
80 #if DUMPBIH
81 static void DumpBIH(unsigned char *BIH)
82 {                               
83         printf("biSize      = %i \n",
84                (int) (BIH[3] << 24) + (BIH[2] << 16) + (BIH[1] << 8) +
85                (BIH[0]));
86         printf("biWidth     = %i \n",
87                (int) (BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) +
88                (BIH[4]));
89         printf("biHeight    = %i \n",
90                (int) (BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) +
91                (BIH[8]));
92         printf("biPlanes    = %i \n", (int) (BIH[13] << 8) + (BIH[12]));
93         printf("biBitCount  = %i \n", (int) (BIH[15] << 8) + (BIH[14]));
94         printf("biCompress  = %i \n",
95                (int) (BIH[19] << 24) + (BIH[18] << 16) + (BIH[17] << 8) +
96                (BIH[16]));
97         printf("biSizeImage = %i \n",
98                (int) (BIH[23] << 24) + (BIH[22] << 16) + (BIH[21] << 8) +
99                (BIH[20]));
100         printf("biXPels     = %i \n",
101                (int) (BIH[27] << 24) + (BIH[26] << 16) + (BIH[25] << 8) +
102                (BIH[24]));
103         printf("biYPels     = %i \n",
104                (int) (BIH[31] << 24) + (BIH[30] << 16) + (BIH[29] << 8) +
105                (BIH[28]));
106         printf("biClrUsed   = %i \n",
107                (int) (BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) +
108                (BIH[32]));
109         printf("biClrImprtnt= %i \n",
110                (int) (BIH[39] << 24) + (BIH[38] << 16) + (BIH[37] << 8) +
111                (BIH[36]));
112 }
113 #endif
114 /* struct headerpair contains the decoded width/height/depth info for
115    the current bitmap */
116
117 struct headerpair {
118         guint width;
119         guint height;
120         guint depth;
121         guint Negative;         /* Negative = 1 -> top down BMP,  
122                                    Negative = 0 -> bottom up BMP */
123 };
124
125 /* Data needed for the "state" during decompression */
126 struct bmp_compression_state {
127         gint phase;             /* 0 = clean, 
128                                    1 = count received
129                                    2 = escape received
130                                    3 = in "raw" run
131                                    4 = Relative part 1 is next
132                                    5 = Relative part 2 is next
133                                    6 = end of image -> No more input allowed
134                                  */
135         gint RunCount;
136         gint XDelta;
137         gint YDelta;
138 };
139
140 /* Progressive loading */
141
142 struct bmp_progressive_state {
143         ModulePreparedNotifyFunc prepared_func;
144         ModuleUpdatedNotifyFunc updated_func;
145         gpointer user_data;
146
147         gint HeaderSize;        /* The size of the header-part (incl colormap) */
148         guchar *HeaderBuf;      /* The buffer for the header (incl colormap) */
149         gint HeaderDone;        /* The nr of bytes actually in HeaderBuf */
150
151         gint LineWidth;         /* The width of a line in bytes */
152         guchar *LineBuf;        /* Buffer for 1 line */
153         gint LineDone;          /* # of bytes in LineBuf */
154         gint Lines;             /* # of finished lines */
155
156         gint Type;              /*  
157                                    32 = RGB + alpha
158                                    24 = RGB
159                                    4  = 4 bpp colormapped
160                                    8  = 8 bpp colormapped
161                                    1  = 1 bit bitonal 
162                                  */
163         gint Compressed;
164         struct bmp_compression_state compr;
165
166
167         struct headerpair Header;       /* Decoded (BE->CPU) header */
168
169
170         GdkPixbuf *pixbuf;      /* Our "target" */
171 };
172
173 gpointer
174 image_begin_load(ModulePreparedNotifyFunc prepared_func,
175                  ModuleUpdatedNotifyFunc updated_func, gpointer user_data);
176 void image_stop_load(gpointer data);
177 gboolean image_load_increment(gpointer data, guchar * buf, guint size);
178
179
180
181 /* Shared library entry point --> This should be removed when
182    generic_image_load enters gdk-pixbuf-io. */
183 GdkPixbuf *image_load(FILE * f)
184 {
185         guchar *membuf;
186         size_t length;
187         struct bmp_progressive_state *State;
188
189         GdkPixbuf *pb;
190
191         State = image_begin_load(NULL, NULL, NULL);
192         membuf = g_malloc(4096);
193
194         g_assert(membuf != NULL);
195
196
197         while (feof(f) == 0) {
198                 length = fread(membuf, 1, 4096, f);
199                 if (length > 0)
200                         (void) image_load_increment(State, membuf, length);
201
202         }
203         g_free(membuf);
204         if (State->pixbuf != NULL)
205                 gdk_pixbuf_ref(State->pixbuf);
206
207         pb = State->pixbuf;
208
209         image_stop_load(State);
210         return pb;
211 }
212
213 static void DecodeHeader(unsigned char *BFH, unsigned char *BIH,
214                          struct bmp_progressive_state *State)
215 {
216 #if DUMPBIH
217         DumpBIH(BIH);
218 #endif
219
220         State->Header.width =
221             (int) (BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) +
222             (BIH[4]);
223         State->Header.height =
224             (int) (BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) +
225             (BIH[8]);
226         State->Header.depth = (int) (BIH[15] << 8) + (BIH[14]);;
227
228         State->Type = State->Header.depth;      /* This may be less trivial someday */
229         State->HeaderSize =
230             (int) ((BFH[13] << 24) + (BFH[12] << 16) + (BFH[11] << 8) +
231                    (BFH[10]));
232         if (State->HeaderSize >= 14 + 40 + 1024)
233                 State->HeaderBuf =
234                     g_realloc(State->HeaderBuf, State->HeaderSize);
235
236         if ((BIH[16] != 0) || (BIH[17] != 0) || (BIH[18] != 0)
237             || (BIH[19] != 0)) {
238                 State->Compressed = 1;
239         }
240
241         /* Negative heights indicates bottom-down pixelorder */
242         if (State->Header.height < 0) {
243                 State->Header.height = -State->Header.height;
244                 State->Header.Negative = 1;
245         }
246         if (State->Header.width < 0) {
247                 State->Header.width = -State->Header.width;
248                 State->Header.Negative = 0;
249         }
250
251         if (State->Type == 32)
252                 State->LineWidth = State->Header.width * 4;
253         if (State->Type == 24)
254                 State->LineWidth = State->Header.width * 3;
255         if (State->Type == 8)
256                 State->LineWidth = State->Header.width * 1;
257         if (State->Type == 4)
258                 State->LineWidth = (State->Header.width + 1) / 2;
259         if (State->Type == 1) {
260                 State->LineWidth = State->Header.width / 8;
261                 if ((State->Header.width & 7) != 0)
262                         State->LineWidth++;
263         }
264
265         /* Pad to a 32 bit boundary */
266         if (((State->LineWidth % 4) > 0) && (State->Compressed == 0))
267                 State->LineWidth = (State->LineWidth / 4) * 4 + 4;
268
269
270         if (State->LineBuf == NULL)
271                 State->LineBuf = g_malloc(State->LineWidth);
272
273         g_assert(State->LineBuf != NULL);
274
275
276         if (State->pixbuf == NULL) {
277                 if (State->Type == 32)
278                         State->pixbuf =
279                             gdk_pixbuf_new(ART_PIX_RGB, TRUE, 8,
280                                            (gint) State->Header.width,
281                                            (gint) State->Header.height);
282                 else
283                         State->pixbuf =
284                             gdk_pixbuf_new(ART_PIX_RGB, FALSE, 8,
285                                            (gint) State->Header.width,
286                                            (gint) State->Header.height);
287
288                 if (State->prepared_func != NULL)
289                         /* Notify the client that we are ready to go */
290                         (*State->prepared_func) (State->pixbuf,
291                                                  State->user_data);
292
293         }
294
295 }
296
297 /* 
298  * func - called when we have pixmap created (but no image data)
299  * user_data - passed as arg 1 to func
300  * return context (opaque to user)
301  */
302
303 gpointer
304 image_begin_load(ModulePreparedNotifyFunc prepared_func,
305                  ModuleUpdatedNotifyFunc updated_func, gpointer user_data)
306 {
307         struct bmp_progressive_state *context;
308
309         context = g_new0(struct bmp_progressive_state, 1);
310         context->prepared_func = prepared_func;
311         context->updated_func = updated_func;
312         context->user_data = user_data;
313
314         context->HeaderSize = 54;
315         context->HeaderBuf = g_malloc(14 + 40 + 1024);
316         /* 14 for the BitmapFileHeader, 40 for the BitmapImageHeader and 
317            1024 for the colormap */
318
319         context->HeaderDone = 0;
320
321         context->LineWidth = 0;
322         context->LineBuf = NULL;
323         context->LineDone = 0;
324         context->Lines = 0;
325
326         context->Type = 0;
327
328         memset(&context->Header, 0, sizeof(struct headerpair));
329         memset(&context->compr, 0, sizeof(struct bmp_compression_state));
330
331
332         context->pixbuf = NULL;
333
334
335         return (gpointer) context;
336 }
337
338 /*
339  * context - returned from image_begin_load
340  *
341  * free context, unref gdk_pixbuf
342  */
343 void image_stop_load(gpointer data)
344 {
345         struct bmp_progressive_state *context =
346             (struct bmp_progressive_state *) data;
347
348
349         g_return_if_fail(context != NULL);
350
351         if (context->LineBuf != NULL)
352                 g_free(context->LineBuf);
353         context->LineBuf = NULL;
354
355         if (context->HeaderBuf != NULL)
356                 g_free(context->HeaderBuf);
357         context->LineBuf = NULL;
358
359         if (context->pixbuf)
360                 gdk_pixbuf_unref(context->pixbuf);
361
362         g_free(context);
363 }
364
365
366 /*
367 The OneLineXX functions are called when 1 line worth of data is present.
368 OneLine24 is the 24 bpp-version.
369 */
370 static void OneLine32(struct bmp_progressive_state *context)
371 {
372         gint X;
373         guchar *Pixels;
374
375         X = 0;
376         if (context->Header.Negative == 0)
377                 Pixels = context->pixbuf->art_pixbuf->pixels +
378                     gdk_pixbuf_get_rowstride(context->pixbuf) *
379                     (context->Header.height - context->Lines - 1);
380         else
381                 Pixels = context->pixbuf->art_pixbuf->pixels +
382                     gdk_pixbuf_get_rowstride(context->pixbuf) *
383                     context->Lines;
384         while (X < context->Header.width) {
385                 Pixels[X * 4 + 0] = context->LineBuf[X * 4 + 2];
386                 Pixels[X * 4 + 1] = context->LineBuf[X * 4 + 1];
387                 Pixels[X * 4 + 2] = context->LineBuf[X * 4 + 0];
388                 Pixels[X * 4 + 3] = context->LineBuf[X * 4 + 3];
389                 X++;
390         }
391
392 }
393
394 static void OneLine24(struct bmp_progressive_state *context)
395 {
396         gint X;
397         guchar *Pixels;
398
399         X = 0;
400         if (context->Header.Negative == 0)
401                 Pixels = context->pixbuf->art_pixbuf->pixels +
402                     gdk_pixbuf_get_rowstride(context->pixbuf) *
403                     (context->Header.height - context->Lines - 1);
404         else
405                 Pixels = context->pixbuf->art_pixbuf->pixels +
406                     gdk_pixbuf_get_rowstride(context->pixbuf) *
407                     context->Lines;
408         while (X < context->Header.width) {
409                 Pixels[X * 3 + 0] = context->LineBuf[X * 3 + 2];
410                 Pixels[X * 3 + 1] = context->LineBuf[X * 3 + 1];
411                 Pixels[X * 3 + 2] = context->LineBuf[X * 3 + 0];
412                 X++;
413         }
414
415 }
416
417 static void OneLine8(struct bmp_progressive_state *context)
418 {
419         gint X;
420         guchar *Pixels;
421
422         X = 0;
423         if (context->Header.Negative == 0)
424                 Pixels = context->pixbuf->art_pixbuf->pixels +
425                     gdk_pixbuf_get_rowstride(context->pixbuf) *
426                     (context->Header.height - context->Lines - 1);
427         else
428                 Pixels = context->pixbuf->art_pixbuf->pixels +
429                     gdk_pixbuf_get_rowstride(context->pixbuf) *
430                     context->Lines;
431         while (X < context->Header.width) {
432                 Pixels[X * 3 + 0] =
433                     context->HeaderBuf[4 * context->LineBuf[X] + 56];
434                 Pixels[X * 3 + 1] =
435                     context->HeaderBuf[4 * context->LineBuf[X] + 55];
436                 Pixels[X * 3 + 2] =
437                     context->HeaderBuf[4 * context->LineBuf[X] + 54];
438                 X++;
439         }
440 }
441
442 static void OneLine4(struct bmp_progressive_state *context)
443 {
444         gint X;
445         guchar *Pixels;
446
447         X = 0;
448         if (context->Header.Negative == 0)
449                 Pixels = context->pixbuf->art_pixbuf->pixels +
450                     gdk_pixbuf_get_rowstride(context->pixbuf) *
451                     (context->Header.height - context->Lines - 1);
452         else
453                 Pixels = context->pixbuf->art_pixbuf->pixels +
454                     gdk_pixbuf_get_rowstride(context->pixbuf) *
455                     context->Lines;
456
457         while (X < context->Header.width) {
458                 guchar Pix;
459
460                 Pix = context->LineBuf[X / 2];
461
462                 Pixels[X * 3 + 0] =
463                     context->HeaderBuf[4 * (Pix >> 4) + 56];
464                 Pixels[X * 3 + 1] =
465                     context->HeaderBuf[4 * (Pix >> 4) + 55];
466                 Pixels[X * 3 + 2] =
467                     context->HeaderBuf[4 * (Pix >> 4) + 54];
468                 X++;
469                 if (X < context->Header.width) {
470                         /* Handle the other 4 bit pixel only when there is one */
471                         Pixels[X * 3 + 0] =
472                             context->HeaderBuf[4 * (Pix & 15) + 56];
473                         Pixels[X * 3 + 1] =
474                             context->HeaderBuf[4 * (Pix & 15) + 55];
475                         Pixels[X * 3 + 2] =
476                             context->HeaderBuf[4 * (Pix & 15) + 54];
477                         X++;
478                 }
479         }
480
481 }
482
483 static void OneLine1(struct bmp_progressive_state *context)
484 {
485         gint X;
486         guchar *Pixels;
487
488         X = 0;
489         if (context->Header.Negative == 0)
490                 Pixels = context->pixbuf->art_pixbuf->pixels +
491                     gdk_pixbuf_get_rowstride(context->pixbuf) *
492                     (context->Header.height - context->Lines - 1);
493         else
494                 Pixels = context->pixbuf->art_pixbuf->pixels +
495                     gdk_pixbuf_get_rowstride(context->pixbuf) *
496                     context->Lines;
497         while (X < context->Header.width) {
498                 gint Bit;
499
500                 Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
501                 Bit = Bit & 1;
502                 Pixels[X * 3 + 0] = Bit*255;
503                 Pixels[X * 3 + 1] = Bit*255;
504                 Pixels[X * 3 + 2] = Bit*255;
505                 X++;
506         }
507 }
508
509
510 static void OneLine(struct bmp_progressive_state *context)
511 {
512         context->LineDone = 0;
513         if (context->Lines >= context->Header.height)
514                 return;
515
516         if (context->Type == 32)
517                 OneLine32(context);
518         if (context->Type == 24)
519                 OneLine24(context);
520         if (context->Type == 8)
521                 OneLine8(context);
522         if (context->Type == 4)
523                 OneLine4(context);
524         if (context->Type == 1)
525                 OneLine1(context);
526
527         context->Lines++;
528
529         if (context->updated_func != NULL) {
530                 (*context->updated_func) (context->pixbuf,
531                                           context->user_data,
532                                           0,
533                                           context->Lines,
534                                           context->Header.width,
535                                           context->Header.height);
536
537         }
538 }
539
540 /* DoCompressedByte handles 1 byte of incomming compressed data */
541 void DoCompressedByte(struct bmp_progressive_state *context, guchar ** buf,
542                       gint * size)
543 {
544         gint BytesToCopy;
545         switch (context->compr.phase) {
546         case 0:         /* Neutral state */
547                 if (buf[0] != 0) {      /* run count */
548                         context->compr.phase = 1;
549                         context->compr.RunCount = (*buf)[0];
550                 } else {        /* Escape */
551                         context->compr.phase = 2;
552                 }
553                 (*buf)++;
554                 (*size)--;
555                 break;
556         case 1:         /* Run count received.... */
557                 while (context->compr.RunCount > 0) {
558                         BytesToCopy =
559                             context->LineWidth - context->LineDone;
560                         if (BytesToCopy > context->compr.RunCount)
561                                 BytesToCopy = context->compr.RunCount;
562                         if (BytesToCopy > 0) {
563                                 memset(context->LineBuf +
564                                        context->LineDone,
565                                        (*buf)[0], BytesToCopy);
566
567                                 context->compr.RunCount -= BytesToCopy;
568                                 context->LineDone += BytesToCopy;
569                         }
570                         if ((context->LineDone >= context->LineWidth)
571                             && (context->LineWidth > 0)) {
572                                 OneLine(context);
573                         }
574                 }
575                 context->compr.phase = 0;
576                 (*buf)++;
577                 (*size)--;
578                 break;
579         case 2:         /* Escape received */
580                 if ((*buf)[0] == 0) {   /* End of line */
581                         context->compr.phase = 0;
582                         if (context->LineDone > 0)
583                                 OneLine(context);
584                 } else if ((*buf)[0] == 1) {    /* End of image */
585                         OneLine(context);
586                         context->compr.phase = 6;
587                         (*size) = 0;
588                         break;
589                 } else if ((*buf)[0] == 2) {    /* Cursor displacement */
590                         context->compr.phase = 4;
591                 } else {
592                         context->compr.phase = 3;
593                         context->compr.RunCount = (*buf)[0];
594                 }
595                 (*buf)++;
596                 (*size)--;
597
598                 break;
599         case 3:
600                 while ((context->compr.RunCount > 0)
601                        && (size > 0)) {
602                         BytesToCopy =
603                             context->LineWidth - context->LineDone;
604                         if (BytesToCopy > context->compr.RunCount)
605                                 BytesToCopy = context->compr.RunCount;
606                         if (BytesToCopy > *size)
607                                 BytesToCopy = *size;
608
609                         if (BytesToCopy > 0) {
610                                 memcpy(context->LineBuf +
611                                        context->LineDone,
612                                        *buf, BytesToCopy);
613
614                                 context->compr.RunCount -= BytesToCopy;
615                                 (*buf) += BytesToCopy;
616                                 (*size) -= BytesToCopy;
617                                 context->LineDone += BytesToCopy;
618                         }
619                         if ((context->LineDone >= context->LineWidth)
620                             && (context->LineWidth > 0))
621                                 OneLine(context);
622                 }
623                 if (context->compr.RunCount <= 0)
624                         context->compr.phase = 0;
625
626                 break;
627         case 4:
628                 context->compr.phase = 5;
629                 context->compr.XDelta = (*buf)[0];
630                 (*buf)++;
631                 (*size)--;
632                 break;
633         case 5:
634                 context->compr.phase = 0;
635                 context->compr.YDelta = (*buf)[0];
636                 g_assert(0);    /* No implementatio of this yet */
637                 /* If this happens, please email me (arjan@fenrus.demon.nl)
638                    the image concerned. */
639                 (*buf)++;
640                 (*size)--;
641                 break;
642         case 6:
643                 (*size) = 0;
644         }
645 }
646
647 /*
648  * context - from image_begin_load
649  * buf - new image data
650  * size - length of new image data
651  *
652  * append image data onto inrecrementally built output image
653  */
654 gboolean image_load_increment(gpointer data, guchar * buf, guint size)
655 {
656         struct bmp_progressive_state *context =
657             (struct bmp_progressive_state *) data;
658
659         gint BytesToCopy;
660
661         while (size > 0) {
662                 g_assert(context->LineDone >= 0);
663                 if (context->HeaderDone < context->HeaderSize) {        /* We still 
664                                                                            have headerbytes to do */
665                         BytesToCopy =
666                             context->HeaderSize - context->HeaderDone;
667                         if (BytesToCopy > size)
668                                 BytesToCopy = size;
669
670                         memmove(context->HeaderBuf + context->HeaderDone,
671                                buf, BytesToCopy);
672
673                         size -= BytesToCopy;
674                         buf += BytesToCopy;
675                         context->HeaderDone += BytesToCopy;
676
677                 } else if (context->Compressed) {
678                         /* Compression is done 1 byte at a time for now */
679                         DoCompressedByte(context, &buf, &size);
680
681                 } else {
682                         /* Uncompressed pixeldata */
683                         BytesToCopy =
684                             context->LineWidth - context->LineDone;
685                         if (BytesToCopy > size)
686                                 BytesToCopy = size;
687
688                         if (BytesToCopy > 0) {
689                                 memmove(context->LineBuf +
690                                        context->LineDone, buf,
691                                        BytesToCopy);
692
693                                 size -= BytesToCopy;
694                                 buf += BytesToCopy;
695                                 context->LineDone += BytesToCopy;
696                         }
697                         if ((context->LineDone >= context->LineWidth) &&
698                             (context->LineWidth > 0))
699                                 OneLine(context);
700
701
702                 }
703
704                 if (context->HeaderDone >= 14 + 40)
705                         DecodeHeader(context->HeaderBuf,
706                                      context->HeaderBuf + 14, context);
707
708
709         }
710
711         return TRUE;
712 }