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
37 #include <gdk-pixbuf/gdk-pixbuf.h>
38 #include <gdk-pixbuf/gdk-pixbuf-io.h>
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.
53 struct BitmapFileHeader {
60 struct BitmapInfoHeader {
68 guint biXPelsPerMeter;
69 guint biYPelsPerMeter;
76 DumpBIH printf's the values in a BitmapInfoHeader to the screen, for
81 static void DumpBIH(unsigned char *BIH)
83 printf("biSize = %i \n",
84 (int) (BIH[3] << 24) + (BIH[2] << 16) + (BIH[1] << 8) +
86 printf("biWidth = %i \n",
87 (int) (BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) +
89 printf("biHeight = %i \n",
90 (int) (BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 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) +
97 printf("biSizeImage = %i \n",
98 (int) (BIH[23] << 24) + (BIH[22] << 16) + (BIH[21] << 8) +
100 printf("biXPels = %i \n",
101 (int) (BIH[27] << 24) + (BIH[26] << 16) + (BIH[25] << 8) +
103 printf("biYPels = %i \n",
104 (int) (BIH[31] << 24) + (BIH[30] << 16) + (BIH[29] << 8) +
106 printf("biClrUsed = %i \n",
107 (int) (BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) +
109 printf("biClrImprtnt= %i \n",
110 (int) (BIH[39] << 24) + (BIH[38] << 16) + (BIH[37] << 8) +
114 /* struct headerpair contains the decoded width/height/depth info for
115 the current bitmap */
121 guint Negative; /* Negative = 1 -> top down BMP,
122 Negative = 0 -> bottom up BMP */
125 /* Data needed for the "state" during decompression */
126 struct bmp_compression_state {
127 gint phase; /* 0 = clean,
131 4 = Relative part 1 is next
132 5 = Relative part 2 is next
133 6 = end of image -> No more input allowed
140 /* Progressive loading */
142 struct bmp_progressive_state {
143 ModulePreparedNotifyFunc prepared_func;
144 ModuleUpdatedNotifyFunc updated_func;
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 */
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 */
159 4 = 4 bpp colormapped
160 8 = 8 bpp colormapped
164 struct bmp_compression_state compr;
167 struct headerpair Header; /* Decoded (BE->CPU) header */
170 GdkPixbuf *pixbuf; /* Our "target" */
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);
181 /* Shared library entry point --> This should be removed when
182 generic_image_load enters gdk-pixbuf-io. */
183 GdkPixbuf *image_load(FILE * f)
187 struct bmp_progressive_state *State;
191 State = image_begin_load(NULL, NULL, NULL);
192 membuf = g_malloc(4096);
194 g_assert(membuf != NULL);
197 while (feof(f) == 0) {
198 length = fread(membuf, 1, 4096, f);
200 (void) image_load_increment(State, membuf, length);
204 if (State->pixbuf != NULL)
205 gdk_pixbuf_ref(State->pixbuf);
209 image_stop_load(State);
213 static void DecodeHeader(unsigned char *BFH, unsigned char *BIH,
214 struct bmp_progressive_state *State)
220 State->Header.width =
221 (int) (BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) +
223 State->Header.height =
224 (int) (BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) +
226 State->Header.depth = (int) (BIH[15] << 8) + (BIH[14]);;
228 State->Type = State->Header.depth; /* This may be less trivial someday */
230 (int) ((BFH[13] << 24) + (BFH[12] << 16) + (BFH[11] << 8) +
232 if (State->HeaderSize >= 14 + 40 + 1024)
234 g_realloc(State->HeaderBuf, State->HeaderSize);
236 if ((BIH[16] != 0) || (BIH[17] != 0) || (BIH[18] != 0)
238 State->Compressed = 1;
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;
246 if (State->Header.width < 0) {
247 State->Header.width = -State->Header.width;
248 State->Header.Negative = 0;
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)
265 /* Pad to a 32 bit boundary */
266 if (((State->LineWidth % 4) > 0) && (State->Compressed == 0))
267 State->LineWidth = (State->LineWidth / 4) * 4 + 4;
270 if (State->LineBuf == NULL)
271 State->LineBuf = g_malloc(State->LineWidth);
273 g_assert(State->LineBuf != NULL);
276 if (State->pixbuf == NULL) {
277 if (State->Type == 32)
279 gdk_pixbuf_new(ART_PIX_RGB, TRUE, 8,
280 (gint) State->Header.width,
281 (gint) State->Header.height);
284 gdk_pixbuf_new(ART_PIX_RGB, FALSE, 8,
285 (gint) State->Header.width,
286 (gint) State->Header.height);
288 if (State->prepared_func != NULL)
289 /* Notify the client that we are ready to go */
290 (*State->prepared_func) (State->pixbuf,
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)
304 image_begin_load(ModulePreparedNotifyFunc prepared_func,
305 ModuleUpdatedNotifyFunc updated_func, gpointer user_data)
307 struct bmp_progressive_state *context;
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;
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 */
319 context->HeaderDone = 0;
321 context->LineWidth = 0;
322 context->LineBuf = NULL;
323 context->LineDone = 0;
328 memset(&context->Header, 0, sizeof(struct headerpair));
329 memset(&context->compr, 0, sizeof(struct bmp_compression_state));
332 context->pixbuf = NULL;
335 return (gpointer) context;
339 * context - returned from image_begin_load
341 * free context, unref gdk_pixbuf
343 void image_stop_load(gpointer data)
345 struct bmp_progressive_state *context =
346 (struct bmp_progressive_state *) data;
349 g_return_if_fail(context != NULL);
351 if (context->LineBuf != NULL)
352 g_free(context->LineBuf);
353 context->LineBuf = NULL;
355 if (context->HeaderBuf != NULL)
356 g_free(context->HeaderBuf);
357 context->LineBuf = NULL;
360 gdk_pixbuf_unref(context->pixbuf);
367 The OneLineXX functions are called when 1 line worth of data is present.
368 OneLine24 is the 24 bpp-version.
370 static void OneLine32(struct bmp_progressive_state *context)
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);
381 Pixels = context->pixbuf->art_pixbuf->pixels +
382 gdk_pixbuf_get_rowstride(context->pixbuf) *
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];
394 static void OneLine24(struct bmp_progressive_state *context)
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);
405 Pixels = context->pixbuf->art_pixbuf->pixels +
406 gdk_pixbuf_get_rowstride(context->pixbuf) *
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];
417 static void OneLine8(struct bmp_progressive_state *context)
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);
428 Pixels = context->pixbuf->art_pixbuf->pixels +
429 gdk_pixbuf_get_rowstride(context->pixbuf) *
431 while (X < context->Header.width) {
433 context->HeaderBuf[4 * context->LineBuf[X] + 56];
435 context->HeaderBuf[4 * context->LineBuf[X] + 55];
437 context->HeaderBuf[4 * context->LineBuf[X] + 54];
442 static void OneLine4(struct bmp_progressive_state *context)
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);
453 Pixels = context->pixbuf->art_pixbuf->pixels +
454 gdk_pixbuf_get_rowstride(context->pixbuf) *
457 while (X < context->Header.width) {
460 Pix = context->LineBuf[X / 2];
463 context->HeaderBuf[4 * (Pix >> 4) + 56];
465 context->HeaderBuf[4 * (Pix >> 4) + 55];
467 context->HeaderBuf[4 * (Pix >> 4) + 54];
469 if (X < context->Header.width) {
470 /* Handle the other 4 bit pixel only when there is one */
472 context->HeaderBuf[4 * (Pix & 15) + 56];
474 context->HeaderBuf[4 * (Pix & 15) + 55];
476 context->HeaderBuf[4 * (Pix & 15) + 54];
483 static void OneLine1(struct bmp_progressive_state *context)
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);
494 Pixels = context->pixbuf->art_pixbuf->pixels +
495 gdk_pixbuf_get_rowstride(context->pixbuf) *
497 while (X < context->Header.width) {
500 Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
502 Pixels[X * 3 + 0] = Bit*255;
503 Pixels[X * 3 + 1] = Bit*255;
504 Pixels[X * 3 + 2] = Bit*255;
510 static void OneLine(struct bmp_progressive_state *context)
512 context->LineDone = 0;
513 if (context->Lines >= context->Header.height)
516 if (context->Type == 32)
518 if (context->Type == 24)
520 if (context->Type == 8)
522 if (context->Type == 4)
524 if (context->Type == 1)
529 if (context->updated_func != NULL) {
530 (*context->updated_func) (context->pixbuf,
534 context->Header.width,
535 context->Header.height);
540 /* DoCompressedByte handles 1 byte of incomming compressed data */
541 void DoCompressedByte(struct bmp_progressive_state *context, guchar ** buf,
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;
556 case 1: /* Run count received.... */
557 while (context->compr.RunCount > 0) {
559 context->LineWidth - context->LineDone;
560 if (BytesToCopy > context->compr.RunCount)
561 BytesToCopy = context->compr.RunCount;
562 if (BytesToCopy > 0) {
563 memset(context->LineBuf +
565 (*buf)[0], BytesToCopy);
567 context->compr.RunCount -= BytesToCopy;
568 context->LineDone += BytesToCopy;
570 if ((context->LineDone >= context->LineWidth)
571 && (context->LineWidth > 0)) {
575 context->compr.phase = 0;
579 case 2: /* Escape received */
580 if ((*buf)[0] == 0) { /* End of line */
581 context->compr.phase = 0;
582 if (context->LineDone > 0)
584 } else if ((*buf)[0] == 1) { /* End of image */
586 context->compr.phase = 6;
589 } else if ((*buf)[0] == 2) { /* Cursor displacement */
590 context->compr.phase = 4;
592 context->compr.phase = 3;
593 context->compr.RunCount = (*buf)[0];
600 while ((context->compr.RunCount > 0)
603 context->LineWidth - context->LineDone;
604 if (BytesToCopy > context->compr.RunCount)
605 BytesToCopy = context->compr.RunCount;
606 if (BytesToCopy > *size)
609 if (BytesToCopy > 0) {
610 memcpy(context->LineBuf +
614 context->compr.RunCount -= BytesToCopy;
615 (*buf) += BytesToCopy;
616 (*size) -= BytesToCopy;
617 context->LineDone += BytesToCopy;
619 if ((context->LineDone >= context->LineWidth)
620 && (context->LineWidth > 0))
623 if (context->compr.RunCount <= 0)
624 context->compr.phase = 0;
628 context->compr.phase = 5;
629 context->compr.XDelta = (*buf)[0];
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. */
648 * context - from image_begin_load
649 * buf - new image data
650 * size - length of new image data
652 * append image data onto inrecrementally built output image
654 gboolean image_load_increment(gpointer data, guchar * buf, guint size)
656 struct bmp_progressive_state *context =
657 (struct bmp_progressive_state *) data;
662 g_assert(context->LineDone >= 0);
663 if (context->HeaderDone < context->HeaderSize) { /* We still
664 have headerbytes to do */
666 context->HeaderSize - context->HeaderDone;
667 if (BytesToCopy > size)
670 memmove(context->HeaderBuf + context->HeaderDone,
675 context->HeaderDone += BytesToCopy;
677 } else if (context->Compressed) {
678 /* Compression is done 1 byte at a time for now */
679 DoCompressedByte(context, &buf, &size);
682 /* Uncompressed pixeldata */
684 context->LineWidth - context->LineDone;
685 if (BytesToCopy > size)
688 if (BytesToCopy > 0) {
689 memmove(context->LineBuf +
690 context->LineDone, buf,
695 context->LineDone += BytesToCopy;
697 if ((context->LineDone >= context->LineWidth) &&
698 (context->LineWidth > 0))
704 if (context->HeaderDone >= 14 + 40)
705 DecodeHeader(context->HeaderBuf,
706 context->HeaderBuf + 14, context);