1 /* GdkPixbuf library - Windows Bitmap image loader
3 * Copyright (C) 1999 The Free Software Foundation
5 * Authors: Arjan van de Ven <arjan@fenrus.demon.nl>
6 * Federico Mena-Quintero <federico@gimp.org>
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.
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.
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.
29 * 4bpp compressed files don't work
30 * bi-tonal files aren't tested with palettes
38 #include "gdk-pixbuf-private.h"
39 #include "gdk-pixbuf-io.h"
45 These structures are actually dummies. These are according to
46 the "Windows API reference guide volume II" as written by
47 Borland International, but GCC fiddles with the alignment of
48 the internal members, so these aren't actually usable.
52 struct BitmapFileHeader {
59 struct BitmapInfoHeader {
67 guint biXPelsPerMeter;
68 guint biYPelsPerMeter;
75 DumpBIH printf's the values in a BitmapInfoHeader to the screen, for
80 static void DumpBIH(unsigned char *BIH)
82 printf("biSize = %i \n",
83 (int) (BIH[3] << 24) + (BIH[2] << 16) + (BIH[1] << 8) +
85 printf("biWidth = %i \n",
86 (int) (BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) +
88 printf("biHeight = %i \n",
89 (int) (BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) +
91 printf("biPlanes = %i \n", (int) (BIH[13] << 8) + (BIH[12]));
92 printf("biBitCount = %i \n", (int) (BIH[15] << 8) + (BIH[14]));
93 printf("biCompress = %i \n",
94 (int) (BIH[19] << 24) + (BIH[18] << 16) + (BIH[17] << 8) +
96 printf("biSizeImage = %i \n",
97 (int) (BIH[23] << 24) + (BIH[22] << 16) + (BIH[21] << 8) +
99 printf("biXPels = %i \n",
100 (int) (BIH[27] << 24) + (BIH[26] << 16) + (BIH[25] << 8) +
102 printf("biYPels = %i \n",
103 (int) (BIH[31] << 24) + (BIH[30] << 16) + (BIH[29] << 8) +
105 printf("biClrUsed = %i \n",
106 (int) (BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) +
108 printf("biClrImprtnt= %i \n",
109 (int) (BIH[39] << 24) + (BIH[38] << 16) + (BIH[37] << 8) +
113 /* struct headerpair contains the decoded width/height/depth info for
114 the current bitmap */
120 guint Negative; /* Negative = 1 -> top down BMP,
121 Negative = 0 -> bottom up BMP */
124 /* Data needed for the "state" during decompression */
125 struct bmp_compression_state {
126 gint phase; /* 0 = clean,
130 4 = Relative part 1 is next
131 5 = Relative part 2 is next
132 6 = end of image -> No more input allowed
139 /* Progressive loading */
141 struct bmp_progressive_state {
142 ModulePreparedNotifyFunc prepared_func;
143 ModuleUpdatedNotifyFunc updated_func;
146 gint HeaderSize; /* The size of the header-part (incl colormap) */
147 guchar *HeaderBuf; /* The buffer for the header (incl colormap) */
148 gint HeaderDone; /* The nr of bytes actually in HeaderBuf */
150 gint LineWidth; /* The width of a line in bytes */
151 guchar *LineBuf; /* Buffer for 1 line */
152 gint LineDone; /* # of bytes in LineBuf */
153 gint Lines; /* # of finished lines */
158 4 = 4 bpp colormapped
159 8 = 8 bpp colormapped
163 struct bmp_compression_state compr;
166 struct headerpair Header; /* Decoded (BE->CPU) header */
169 GdkPixbuf *pixbuf; /* Our "target" */
173 gdk_pixbuf__bmp_image_begin_load(ModulePreparedNotifyFunc prepared_func,
174 ModuleUpdatedNotifyFunc updated_func,
175 ModuleFrameDoneNotifyFunc frame_done_func,
176 ModuleAnimationDoneNotifyFunc
177 anim_done_func, gpointer user_data);
179 void gdk_pixbuf__bmp_image_stop_load(gpointer data);
180 gboolean gdk_pixbuf__bmp_image_load_increment(gpointer data, guchar * buf,
185 /* Shared library entry point --> This should be removed when
186 generic_image_load enters gdk-pixbuf-io. */
187 GdkPixbuf *gdk_pixbuf__bmp_image_load(FILE * f)
191 struct bmp_progressive_state *State;
196 gdk_pixbuf__bmp_image_begin_load(NULL, NULL, NULL, NULL, NULL);
197 membuf = g_malloc(4096);
199 g_assert(membuf != NULL);
202 while (feof(f) == 0) {
203 length = fread(membuf, 1, 4096, f);
207 gdk_pixbuf__bmp_image_load_increment(State,
213 if (State->pixbuf != NULL)
214 gdk_pixbuf_ref(State->pixbuf);
218 gdk_pixbuf__bmp_image_stop_load(State);
222 static void DecodeHeader(unsigned char *BFH, unsigned char *BIH,
223 struct bmp_progressive_state *State)
229 State->Header.width =
230 (int) (BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) +
232 State->Header.height =
233 (int) (BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) +
235 State->Header.depth = (int) (BIH[15] << 8) + (BIH[14]);;
237 State->Type = State->Header.depth; /* This may be less trivial someday */
239 (int) ((BFH[13] << 24) + (BFH[12] << 16) + (BFH[11] << 8) +
241 if (State->HeaderSize >= 14 + 40 + 1024)
243 g_realloc(State->HeaderBuf, State->HeaderSize);
245 if ((BIH[16] != 0) || (BIH[17] != 0) || (BIH[18] != 0)
247 State->Compressed = 1;
250 /* Negative heights indicates bottom-down pixelorder */
251 if (State->Header.height < 0) {
252 State->Header.height = -State->Header.height;
253 State->Header.Negative = 1;
255 if (State->Header.width < 0) {
256 State->Header.width = -State->Header.width;
257 State->Header.Negative = 0;
260 if (State->Type == 32)
261 State->LineWidth = State->Header.width * 4;
262 if (State->Type == 24)
263 State->LineWidth = State->Header.width * 3;
264 if (State->Type == 8)
265 State->LineWidth = State->Header.width * 1;
266 if (State->Type == 4)
267 State->LineWidth = (State->Header.width + 1) / 2;
268 if (State->Type == 1) {
269 State->LineWidth = State->Header.width / 8;
270 if ((State->Header.width & 7) != 0)
274 /* Pad to a 32 bit boundary */
275 if (((State->LineWidth % 4) > 0) && (State->Compressed == 0))
276 State->LineWidth = (State->LineWidth / 4) * 4 + 4;
279 if (State->LineBuf == NULL)
280 State->LineBuf = g_malloc(State->LineWidth);
282 g_assert(State->LineBuf != NULL);
285 if (State->pixbuf == NULL) {
286 if (State->Type == 32)
288 gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
289 (gint) State->Header.width,
290 (gint) State->Header.height);
293 gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
294 (gint) State->Header.width,
295 (gint) State->Header.height);
297 if (State->prepared_func != NULL)
298 /* Notify the client that we are ready to go */
299 (*State->prepared_func) (State->pixbuf, State->user_data);
306 * func - called when we have pixmap created (but no image data)
307 * user_data - passed as arg 1 to func
308 * return context (opaque to user)
312 gdk_pixbuf__bmp_image_begin_load(ModulePreparedNotifyFunc prepared_func,
313 ModuleUpdatedNotifyFunc updated_func,
314 ModuleFrameDoneNotifyFunc frame_done_func,
315 ModuleAnimationDoneNotifyFunc
316 anim_done_func, gpointer user_data)
318 struct bmp_progressive_state *context;
320 context = g_new0(struct bmp_progressive_state, 1);
321 context->prepared_func = prepared_func;
322 context->updated_func = updated_func;
323 context->user_data = user_data;
325 context->HeaderSize = 54;
326 context->HeaderBuf = g_malloc(14 + 40 + 1024);
327 /* 14 for the BitmapFileHeader, 40 for the BitmapImageHeader and
328 1024 for the colormap */
330 context->HeaderDone = 0;
332 context->LineWidth = 0;
333 context->LineBuf = NULL;
334 context->LineDone = 0;
339 memset(&context->Header, 0, sizeof(struct headerpair));
340 memset(&context->compr, 0, sizeof(struct bmp_compression_state));
343 context->pixbuf = NULL;
346 return (gpointer) context;
350 * context - returned from image_begin_load
352 * free context, unref gdk_pixbuf
354 void gdk_pixbuf__bmp_image_stop_load(gpointer data)
356 struct bmp_progressive_state *context =
357 (struct bmp_progressive_state *) data;
360 g_return_if_fail(context != NULL);
362 if (context->LineBuf != NULL)
363 g_free(context->LineBuf);
364 context->LineBuf = NULL;
366 if (context->HeaderBuf != NULL)
367 g_free(context->HeaderBuf);
368 context->LineBuf = NULL;
371 gdk_pixbuf_unref(context->pixbuf);
378 The OneLineXX functions are called when 1 line worth of data is present.
379 OneLine24 is the 24 bpp-version.
381 static void OneLine32(struct bmp_progressive_state *context)
387 if (context->Header.Negative == 0)
388 Pixels = (context->pixbuf->pixels +
389 context->pixbuf->rowstride *
390 (context->Header.height - context->Lines - 1));
392 Pixels = (context->pixbuf->pixels +
393 context->pixbuf->rowstride *
395 while (X < context->Header.width) {
396 Pixels[X * 4 + 0] = context->LineBuf[X * 4 + 2];
397 Pixels[X * 4 + 1] = context->LineBuf[X * 4 + 1];
398 Pixels[X * 4 + 2] = context->LineBuf[X * 4 + 0];
399 Pixels[X * 4 + 3] = context->LineBuf[X * 4 + 3];
405 static void OneLine24(struct bmp_progressive_state *context)
411 if (context->Header.Negative == 0)
412 Pixels = (context->pixbuf->pixels +
413 context->pixbuf->rowstride *
414 (context->Header.height - context->Lines - 1));
416 Pixels = (context->pixbuf->pixels +
417 context->pixbuf->rowstride *
419 while (X < context->Header.width) {
420 Pixels[X * 3 + 0] = context->LineBuf[X * 3 + 2];
421 Pixels[X * 3 + 1] = context->LineBuf[X * 3 + 1];
422 Pixels[X * 3 + 2] = context->LineBuf[X * 3 + 0];
428 static void OneLine8(struct bmp_progressive_state *context)
434 if (context->Header.Negative == 0)
435 Pixels = (context->pixbuf->pixels +
436 context->pixbuf->rowstride *
437 (context->Header.height - context->Lines - 1));
439 Pixels = (context->pixbuf->pixels +
440 context->pixbuf->rowstride *
442 while (X < context->Header.width) {
444 context->HeaderBuf[4 * context->LineBuf[X] + 56];
446 context->HeaderBuf[4 * context->LineBuf[X] + 55];
448 context->HeaderBuf[4 * context->LineBuf[X] + 54];
453 static void OneLine4(struct bmp_progressive_state *context)
459 if (context->Header.Negative == 0)
460 Pixels = (context->pixbuf->pixels +
461 context->pixbuf->rowstride *
462 (context->Header.height - context->Lines - 1));
464 Pixels = (context->pixbuf->pixels +
465 context->pixbuf->rowstride *
468 while (X < context->Header.width) {
471 Pix = context->LineBuf[X / 2];
474 context->HeaderBuf[4 * (Pix >> 4) + 56];
476 context->HeaderBuf[4 * (Pix >> 4) + 55];
478 context->HeaderBuf[4 * (Pix >> 4) + 54];
480 if (X < context->Header.width) {
481 /* Handle the other 4 bit pixel only when there is one */
483 context->HeaderBuf[4 * (Pix & 15) + 56];
485 context->HeaderBuf[4 * (Pix & 15) + 55];
487 context->HeaderBuf[4 * (Pix & 15) + 54];
494 static void OneLine1(struct bmp_progressive_state *context)
500 if (context->Header.Negative == 0)
501 Pixels = (context->pixbuf->pixels +
502 context->pixbuf->rowstride *
503 (context->Header.height - context->Lines - 1));
505 Pixels = (context->pixbuf->pixels +
506 context->pixbuf->rowstride *
508 while (X < context->Header.width) {
511 Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
513 Pixels[X * 3 + 0] = Bit * 255;
514 Pixels[X * 3 + 1] = Bit * 255;
515 Pixels[X * 3 + 2] = Bit * 255;
521 static void OneLine(struct bmp_progressive_state *context)
523 context->LineDone = 0;
524 if (context->Lines >= context->Header.height)
527 if (context->Type == 32)
529 if (context->Type == 24)
531 if (context->Type == 8)
533 if (context->Type == 4)
535 if (context->Type == 1)
540 if (context->updated_func != NULL) {
541 (*context->updated_func) (context->pixbuf,
544 context->Header.width,
545 context->Header.height,
551 /* DoCompressedByte handles 1 byte of incomming compressed data */
552 void DoCompressedByte(struct bmp_progressive_state *context, guchar ** buf,
556 switch (context->compr.phase) {
557 case 0: /* Neutral state */
558 if ((*buf)[0] != 0) { /* run count */
559 context->compr.phase = 1;
560 context->compr.RunCount = (*buf)[0];
562 } else { /* Escape */
563 context->compr.phase = 2;
568 case 1: /* Run count received.... */
569 while (context->compr.RunCount > 0) {
571 context->LineWidth - context->LineDone;
572 if (BytesToCopy > context->compr.RunCount)
573 BytesToCopy = context->compr.RunCount;
574 if (BytesToCopy > 0) {
575 memset(context->LineBuf +
576 context->LineDone, (*buf)[0],
579 context->compr.RunCount -= BytesToCopy;
580 context->LineDone += BytesToCopy;
582 if ((context->LineDone >= context->LineWidth)
583 && (context->LineWidth > 0)) {
587 context->compr.phase = 0;
591 case 2: /* Escape received */
592 if ((*buf)[0] == 0) { /* End of line */
593 context->compr.phase = 0;
594 if (context->LineDone > 0)
596 } else if ((*buf)[0] == 1) { /* End of image */
598 context->compr.phase = 6;
601 } else if ((*buf)[0] == 2) { /* Cursor displacement */
602 context->compr.phase = 4;
604 context->compr.phase = 3;
605 context->compr.RunCount = (*buf)[0];
606 if (context->compr.RunCount & 1)
607 context->compr.phase = 7;
615 while ((context->compr.RunCount > 0)
618 context->LineWidth - context->LineDone;
619 if (BytesToCopy > context->compr.RunCount)
620 BytesToCopy = context->compr.RunCount;
621 if (BytesToCopy > *size)
624 if (BytesToCopy > 0) {
625 memcpy(context->LineBuf +
626 context->LineDone, *buf,
629 context->compr.RunCount -= BytesToCopy;
630 (*buf) += BytesToCopy;
631 (*size) -= BytesToCopy;
632 context->LineDone += BytesToCopy;
634 if ((context->LineDone >= context->LineWidth)
635 && (context->LineWidth > 0))
638 if (context->compr.RunCount <= 0)
639 context->compr.phase = 0;
643 context->compr.phase = 5;
644 context->compr.XDelta = (*buf)[0];
649 context->compr.phase = 0;
650 context->compr.YDelta = (*buf)[0];
651 g_assert(0); /* No implementatio of this yet */
652 /* If this happens, please email me (arjan@fenrus.demon.nl)
653 the image concerned. */
660 case 7: /* Odd raw run */
661 while ((context->compr.RunCount > 0) && (*size > 0)) {
663 context->LineWidth - context->LineDone;
664 if (BytesToCopy > context->compr.RunCount)
665 BytesToCopy = context->compr.RunCount;
666 if (BytesToCopy > *size)
669 if (BytesToCopy > 0) {
670 memcpy(context->LineBuf +
671 context->LineDone, *buf,
674 context->compr.RunCount -= BytesToCopy;
675 (*buf) += BytesToCopy;
676 (*size) -= BytesToCopy;
677 context->LineDone += BytesToCopy;
679 if ((context->LineDone >= context->LineWidth)
680 && (context->LineWidth > 0))
683 if (context->compr.RunCount <= 0)
684 context->compr.phase = 8;
687 case 8: /* Eat dummy byte; */
690 context->compr.phase = 0;
696 * context - from image_begin_load
697 * buf - new image data
698 * size - length of new image data
700 * append image data onto inrecrementally built output image
702 gboolean gdk_pixbuf__bmp_image_load_increment(gpointer data, guchar * buf,
705 struct bmp_progressive_state *context =
706 (struct bmp_progressive_state *) data;
711 g_assert(context->LineDone >= 0);
712 if (context->HeaderDone < context->HeaderSize) { /* We still
713 have headerbytes to do */
715 context->HeaderSize - context->HeaderDone;
716 if (BytesToCopy > size)
719 memmove(context->HeaderBuf + context->HeaderDone,
724 context->HeaderDone += BytesToCopy;
726 } else if (context->Compressed) {
727 /* Compression is done 1 byte at a time for now */
728 DoCompressedByte(context, &buf, &size);
731 /* Uncompressed pixeldata */
733 context->LineWidth - context->LineDone;
734 if (BytesToCopy > size)
737 if (BytesToCopy > 0) {
738 memmove(context->LineBuf +
739 context->LineDone, buf,
744 context->LineDone += BytesToCopy;
746 if ((context->LineDone >= context->LineWidth)
747 && (context->LineWidth > 0))
753 if (context->HeaderDone >= 14 + 40)
754 DecodeHeader(context->HeaderBuf,
755 context->HeaderBuf + 14, context);