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 Lesser 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 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser 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
40 #include "gdk-pixbuf-private.h"
41 #include "gdk-pixbuf-io.h"
47 These structures are actually dummies. These are according to
48 the "Windows API reference guide volume II" as written by
49 Borland International, but GCC fiddles with the alignment of
50 the internal members, so these aren't actually usable.
54 struct BitmapFileHeader {
61 struct BitmapInfoHeader {
69 guint biXPelsPerMeter;
70 guint biYPelsPerMeter;
77 DumpBIH printf's the values in a BitmapInfoHeader to the screen, for
82 static void DumpBIH(unsigned char *BIH)
84 printf("biSize = %i \n",
85 (int) (BIH[3] << 24) + (BIH[2] << 16) + (BIH[1] << 8) +
87 printf("biWidth = %i \n",
88 (int) (BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) +
90 printf("biHeight = %i \n",
91 (int) (BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) +
93 printf("biPlanes = %i \n", (int) (BIH[13] << 8) + (BIH[12]));
94 printf("biBitCount = %i \n", (int) (BIH[15] << 8) + (BIH[14]));
95 printf("biCompress = %i \n",
96 (int) (BIH[19] << 24) + (BIH[18] << 16) + (BIH[17] << 8) +
98 printf("biSizeImage = %i \n",
99 (int) (BIH[23] << 24) + (BIH[22] << 16) + (BIH[21] << 8) +
101 printf("biXPels = %i \n",
102 (int) (BIH[27] << 24) + (BIH[26] << 16) + (BIH[25] << 8) +
104 printf("biYPels = %i \n",
105 (int) (BIH[31] << 24) + (BIH[30] << 16) + (BIH[29] << 8) +
107 printf("biClrUsed = %i \n",
108 (int) (BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) +
110 printf("biClrImprtnt= %i \n",
111 (int) (BIH[39] << 24) + (BIH[38] << 16) + (BIH[37] << 8) +
115 /* struct headerpair contains the decoded width/height/depth info for
116 the current bitmap */
122 guint Negative; /* Negative = 1 -> top down BMP,
123 Negative = 0 -> bottom up BMP */
126 /* Data needed for the "state" during decompression */
127 struct bmp_compression_state {
128 gint phase; /* 0 = clean,
132 4 = Relative part 1 is next
133 5 = Relative part 2 is next
134 6 = end of image -> No more input allowed
141 /* Progressive loading */
143 struct bmp_progressive_state {
144 ModulePreparedNotifyFunc prepared_func;
145 ModuleUpdatedNotifyFunc updated_func;
148 gint HeaderSize; /* The size of the header-part (incl colormap) */
149 guchar *HeaderBuf; /* The buffer for the header (incl colormap) */
150 gint HeaderDone; /* The nr of bytes actually in HeaderBuf */
152 gint LineWidth; /* The width of a line in bytes */
153 guchar *LineBuf; /* Buffer for 1 line */
154 gint LineDone; /* # of bytes in LineBuf */
155 gint Lines; /* # of finished lines */
160 4 = 4 bpp colormapped
161 8 = 8 bpp colormapped
165 struct bmp_compression_state compr;
168 struct headerpair Header; /* Decoded (BE->CPU) header */
171 GdkPixbuf *pixbuf; /* Our "target" */
175 gdk_pixbuf__bmp_image_begin_load(ModulePreparedNotifyFunc prepared_func,
176 ModuleUpdatedNotifyFunc updated_func,
177 ModuleFrameDoneNotifyFunc frame_done_func,
178 ModuleAnimationDoneNotifyFunc
179 anim_done_func, gpointer user_data,
182 void gdk_pixbuf__bmp_image_stop_load(gpointer data);
183 gboolean gdk_pixbuf__bmp_image_load_increment(gpointer data, guchar * buf,
189 /* Shared library entry point --> This should be removed when
190 generic_image_load enters gdk-pixbuf-io. */
191 GdkPixbuf *gdk_pixbuf__bmp_image_load(FILE * f, GError **error)
195 struct bmp_progressive_state *State;
200 gdk_pixbuf__bmp_image_begin_load(NULL, NULL, NULL, NULL, NULL,
206 while (feof(f) == 0) {
207 length = fread(membuf, 1, 4096, f);
209 if (!gdk_pixbuf__bmp_image_load_increment(State,
213 gdk_pixbuf__bmp_image_stop_load (State);
218 if (State->pixbuf != NULL)
219 gdk_pixbuf_ref(State->pixbuf);
223 gdk_pixbuf__bmp_image_stop_load(State);
227 static void DecodeHeader(unsigned char *BFH, unsigned char *BIH,
228 struct bmp_progressive_state *State)
234 State->Header.width =
235 (int) (BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) +
237 State->Header.height =
238 (int) (BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) +
240 State->Header.depth = (int) (BIH[15] << 8) + (BIH[14]);;
242 State->Type = State->Header.depth; /* This may be less trivial someday */
244 (int) ((BFH[13] << 24) + (BFH[12] << 16) + (BFH[11] << 8) +
246 if (State->HeaderSize >= 14 + 40 + 1024)
248 g_realloc(State->HeaderBuf, State->HeaderSize);
250 if ((BIH[16] != 0) || (BIH[17] != 0) || (BIH[18] != 0)
252 State->Compressed = 1;
255 /* Negative heights indicates bottom-down pixelorder */
256 if (State->Header.height < 0) {
257 State->Header.height = -State->Header.height;
258 State->Header.Negative = 1;
260 if (State->Header.width < 0) {
261 State->Header.width = -State->Header.width;
262 State->Header.Negative = 0;
265 if (State->Type == 32)
266 State->LineWidth = State->Header.width * 4;
267 if (State->Type == 24)
268 State->LineWidth = State->Header.width * 3;
269 if (State->Type == 8)
270 State->LineWidth = State->Header.width * 1;
271 if (State->Type == 4)
272 State->LineWidth = (State->Header.width + 1) / 2;
273 if (State->Type == 1) {
274 State->LineWidth = State->Header.width / 8;
275 if ((State->Header.width & 7) != 0)
279 /* Pad to a 32 bit boundary */
280 if (((State->LineWidth % 4) > 0) && (State->Compressed == 0))
281 State->LineWidth = (State->LineWidth / 4) * 4 + 4;
284 if (State->LineBuf == NULL)
285 State->LineBuf = g_malloc(State->LineWidth);
287 g_assert(State->LineBuf != NULL);
290 if (State->pixbuf == NULL) {
291 if (State->Type == 32)
293 gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
294 (gint) State->Header.width,
295 (gint) State->Header.height);
298 gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
299 (gint) State->Header.width,
300 (gint) State->Header.height);
302 if (State->prepared_func != NULL)
303 /* Notify the client that we are ready to go */
304 (*State->prepared_func) (State->pixbuf, State->user_data);
311 * func - called when we have pixmap created (but no image data)
312 * user_data - passed as arg 1 to func
313 * return context (opaque to user)
317 gdk_pixbuf__bmp_image_begin_load(ModulePreparedNotifyFunc prepared_func,
318 ModuleUpdatedNotifyFunc updated_func,
319 ModuleFrameDoneNotifyFunc frame_done_func,
320 ModuleAnimationDoneNotifyFunc
321 anim_done_func, gpointer user_data,
324 struct bmp_progressive_state *context;
326 context = g_new0(struct bmp_progressive_state, 1);
327 context->prepared_func = prepared_func;
328 context->updated_func = updated_func;
329 context->user_data = user_data;
331 context->HeaderSize = 54;
332 context->HeaderBuf = g_malloc(14 + 40 + 1024);
333 /* 14 for the BitmapFileHeader, 40 for the BitmapImageHeader and
334 1024 for the colormap */
336 context->HeaderDone = 0;
338 context->LineWidth = 0;
339 context->LineBuf = NULL;
340 context->LineDone = 0;
345 memset(&context->Header, 0, sizeof(struct headerpair));
346 memset(&context->compr, 0, sizeof(struct bmp_compression_state));
349 context->pixbuf = NULL;
352 return (gpointer) context;
356 * context - returned from image_begin_load
358 * free context, unref gdk_pixbuf
360 void gdk_pixbuf__bmp_image_stop_load(gpointer data)
362 struct bmp_progressive_state *context =
363 (struct bmp_progressive_state *) data;
366 g_return_if_fail(context != NULL);
368 if (context->LineBuf != NULL)
369 g_free(context->LineBuf);
370 context->LineBuf = NULL;
372 if (context->HeaderBuf != NULL)
373 g_free(context->HeaderBuf);
374 context->LineBuf = NULL;
377 gdk_pixbuf_unref(context->pixbuf);
384 The OneLineXX functions are called when 1 line worth of data is present.
385 OneLine24 is the 24 bpp-version.
387 static void OneLine32(struct bmp_progressive_state *context)
393 if (context->Header.Negative == 0)
394 Pixels = (context->pixbuf->pixels +
395 context->pixbuf->rowstride *
396 (context->Header.height - context->Lines - 1));
398 Pixels = (context->pixbuf->pixels +
399 context->pixbuf->rowstride *
401 while (X < context->Header.width) {
402 Pixels[X * 4 + 0] = context->LineBuf[X * 4 + 2];
403 Pixels[X * 4 + 1] = context->LineBuf[X * 4 + 1];
404 Pixels[X * 4 + 2] = context->LineBuf[X * 4 + 0];
405 Pixels[X * 4 + 3] = context->LineBuf[X * 4 + 3];
411 static void OneLine24(struct bmp_progressive_state *context)
417 if (context->Header.Negative == 0)
418 Pixels = (context->pixbuf->pixels +
419 context->pixbuf->rowstride *
420 (context->Header.height - context->Lines - 1));
422 Pixels = (context->pixbuf->pixels +
423 context->pixbuf->rowstride *
425 while (X < context->Header.width) {
426 Pixels[X * 3 + 0] = context->LineBuf[X * 3 + 2];
427 Pixels[X * 3 + 1] = context->LineBuf[X * 3 + 1];
428 Pixels[X * 3 + 2] = context->LineBuf[X * 3 + 0];
434 static void OneLine8(struct bmp_progressive_state *context)
440 if (context->Header.Negative == 0)
441 Pixels = (context->pixbuf->pixels +
442 context->pixbuf->rowstride *
443 (context->Header.height - context->Lines - 1));
445 Pixels = (context->pixbuf->pixels +
446 context->pixbuf->rowstride *
448 while (X < context->Header.width) {
450 context->HeaderBuf[4 * context->LineBuf[X] + 56];
452 context->HeaderBuf[4 * context->LineBuf[X] + 55];
454 context->HeaderBuf[4 * context->LineBuf[X] + 54];
459 static void OneLine4(struct bmp_progressive_state *context)
465 if (context->Header.Negative == 0)
466 Pixels = (context->pixbuf->pixels +
467 context->pixbuf->rowstride *
468 (context->Header.height - context->Lines - 1));
470 Pixels = (context->pixbuf->pixels +
471 context->pixbuf->rowstride *
474 while (X < context->Header.width) {
477 Pix = context->LineBuf[X / 2];
480 context->HeaderBuf[4 * (Pix >> 4) + 56];
482 context->HeaderBuf[4 * (Pix >> 4) + 55];
484 context->HeaderBuf[4 * (Pix >> 4) + 54];
486 if (X < context->Header.width) {
487 /* Handle the other 4 bit pixel only when there is one */
489 context->HeaderBuf[4 * (Pix & 15) + 56];
491 context->HeaderBuf[4 * (Pix & 15) + 55];
493 context->HeaderBuf[4 * (Pix & 15) + 54];
500 static void OneLine1(struct bmp_progressive_state *context)
506 if (context->Header.Negative == 0)
507 Pixels = (context->pixbuf->pixels +
508 context->pixbuf->rowstride *
509 (context->Header.height - context->Lines - 1));
511 Pixels = (context->pixbuf->pixels +
512 context->pixbuf->rowstride *
514 while (X < context->Header.width) {
517 Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
519 Pixels[X * 3 + 0] = Bit * 255;
520 Pixels[X * 3 + 1] = Bit * 255;
521 Pixels[X * 3 + 2] = Bit * 255;
527 static void OneLine(struct bmp_progressive_state *context)
529 context->LineDone = 0;
530 if (context->Lines >= context->Header.height)
533 if (context->Type == 32)
535 if (context->Type == 24)
537 if (context->Type == 8)
539 if (context->Type == 4)
541 if (context->Type == 1)
546 if (context->updated_func != NULL) {
547 (*context->updated_func) (context->pixbuf,
550 context->Header.width,
551 context->Header.height,
557 /* DoCompressedByte handles 1 byte of incomming compressed data */
558 void DoCompressedByte(struct bmp_progressive_state *context, guchar ** buf,
562 switch (context->compr.phase) {
563 case 0: /* Neutral state */
564 if ((*buf)[0] != 0) { /* run count */
565 context->compr.phase = 1;
566 context->compr.RunCount = (*buf)[0];
568 } else { /* Escape */
569 context->compr.phase = 2;
574 case 1: /* Run count received.... */
575 while (context->compr.RunCount > 0) {
577 context->LineWidth - context->LineDone;
578 if (BytesToCopy > context->compr.RunCount)
579 BytesToCopy = context->compr.RunCount;
580 if (BytesToCopy > 0) {
581 memset(context->LineBuf +
582 context->LineDone, (*buf)[0],
585 context->compr.RunCount -= BytesToCopy;
586 context->LineDone += BytesToCopy;
588 if ((context->LineDone >= context->LineWidth)
589 && (context->LineWidth > 0)) {
593 context->compr.phase = 0;
597 case 2: /* Escape received */
598 if ((*buf)[0] == 0) { /* End of line */
599 context->compr.phase = 0;
600 if (context->LineDone > 0)
602 } else if ((*buf)[0] == 1) { /* End of image */
604 context->compr.phase = 6;
607 } else if ((*buf)[0] == 2) { /* Cursor displacement */
608 context->compr.phase = 4;
610 context->compr.phase = 3;
611 context->compr.RunCount = (*buf)[0];
612 if (context->compr.RunCount & 1)
613 context->compr.phase = 7;
621 while ((context->compr.RunCount > 0)
624 context->LineWidth - context->LineDone;
625 if (BytesToCopy > context->compr.RunCount)
626 BytesToCopy = context->compr.RunCount;
627 if (BytesToCopy > *size)
630 if (BytesToCopy > 0) {
631 memcpy(context->LineBuf +
632 context->LineDone, *buf,
635 context->compr.RunCount -= BytesToCopy;
636 (*buf) += BytesToCopy;
637 (*size) -= BytesToCopy;
638 context->LineDone += BytesToCopy;
640 if ((context->LineDone >= context->LineWidth)
641 && (context->LineWidth > 0))
644 if (context->compr.RunCount <= 0)
645 context->compr.phase = 0;
649 context->compr.phase = 5;
650 context->compr.XDelta = (*buf)[0];
655 context->compr.phase = 0;
656 context->compr.YDelta = (*buf)[0];
657 g_assert(0); /* No implementatio of this yet */
658 /* If this happens, please email me (arjan@fenrus.demon.nl)
659 the image concerned. */
666 case 7: /* Odd raw run */
667 while ((context->compr.RunCount > 0) && (*size > 0)) {
669 context->LineWidth - context->LineDone;
670 if (BytesToCopy > context->compr.RunCount)
671 BytesToCopy = context->compr.RunCount;
672 if (BytesToCopy > *size)
675 if (BytesToCopy > 0) {
676 memcpy(context->LineBuf +
677 context->LineDone, *buf,
680 context->compr.RunCount -= BytesToCopy;
681 (*buf) += BytesToCopy;
682 (*size) -= BytesToCopy;
683 context->LineDone += BytesToCopy;
685 if ((context->LineDone >= context->LineWidth)
686 && (context->LineWidth > 0))
689 if (context->compr.RunCount <= 0)
690 context->compr.phase = 8;
693 case 8: /* Eat dummy byte; */
696 context->compr.phase = 0;
702 * context - from image_begin_load
703 * buf - new image data
704 * size - length of new image data
706 * append image data onto inrecrementally built output image
708 gboolean gdk_pixbuf__bmp_image_load_increment(gpointer data,
713 struct bmp_progressive_state *context =
714 (struct bmp_progressive_state *) data;
719 g_assert(context->LineDone >= 0);
720 if (context->HeaderDone < context->HeaderSize) { /* We still
721 have headerbytes to do */
723 context->HeaderSize - context->HeaderDone;
724 if (BytesToCopy > size)
727 memmove(context->HeaderBuf + context->HeaderDone,
732 context->HeaderDone += BytesToCopy;
734 } else if (context->Compressed) {
735 /* Compression is done 1 byte at a time for now */
736 DoCompressedByte(context, &buf, &size);
739 /* Uncompressed pixeldata */
741 context->LineWidth - context->LineDone;
742 if (BytesToCopy > size)
745 if (BytesToCopy > 0) {
746 memmove(context->LineBuf +
747 context->LineDone, buf,
752 context->LineDone += BytesToCopy;
754 if ((context->LineDone >= context->LineWidth)
755 && (context->LineWidth > 0))
761 if (context->HeaderDone >= 14 + 40)
762 DecodeHeader(context->HeaderBuf,
763 context->HeaderBuf + 14, context);