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.
28 Icons are just like BMP's, except for the header.
31 * bi-tonal files aren't tested
42 #include "gdk-pixbuf/gdk-pixbuf.h"
43 #include "gdk-pixbuf/gdk-pixbuf-io.h"
51 guint Negative; /* Negative = 1 -> top down BMP,
52 Negative = 0 -> bottom up BMP */
57 These structures are actually dummies. These are according to
58 the "Windows API reference guide volume II" as written by Borland,
59 but GCC fiddles with the alignment of the internal members.
63 struct BitmapFileHeader {
70 struct BitmapInfoHeader {
78 guint biXPelsPerMeter;
79 guint biYPelsPerMeter;
84 static void DumpBIH(unsigned char *BIH)
86 printf("biSize = %i \n",
87 (BIH[3] << 24) + (BIH[2] << 16) + (BIH[1] << 8) + (BIH[0]));
88 printf("biWidth = %i \n",
89 (BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) + (BIH[4]));
90 printf("biHeight = %i \n",
91 (BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) +
93 printf("biPlanes = %i \n", (BIH[13] << 8) + (BIH[12]));
94 printf("biBitCount = %i \n", (BIH[15] << 8) + (BIH[14]));
95 printf("biCompress = %i \n",
96 (BIH[19] << 24) + (BIH[18] << 16) + (BIH[17] << 8) +
98 printf("biSizeImage = %i \n",
99 (BIH[23] << 24) + (BIH[22] << 16) + (BIH[21] << 8) +
101 printf("biXPels = %i \n",
102 (BIH[27] << 24) + (BIH[26] << 16) + (BIH[25] << 8) +
104 printf("biYPels = %i \n",
105 (BIH[31] << 24) + (BIH[30] << 16) + (BIH[29] << 8) +
107 printf("biClrUsed = %i \n",
108 (BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) +
110 printf("biClrImprtnt= %i \n",
111 (BIH[39] << 24) + (BIH[38] << 16) + (BIH[37] << 8) +
116 This does a byte-order swap. Does glib have something like
120 static unsigned int le32_to_cpu(guint i)
127 Destroy notification function for the libart pixbuf
130 static void free_buffer(gpointer user_data, gpointer data)
136 /* Progressive loading */
138 struct ico_progressive_state {
139 ModulePreparedNotifyFunc prepared_func;
140 ModuleUpdatedNotifyFunc updated_func;
143 gint HeaderSize; /* The size of the header-part (incl colormap) */
144 guchar *HeaderBuf; /* The buffer for the header (incl colormap) */
145 gint BytesInHeaderBuf; /* The size of the allocated HeaderBuf */
146 gint HeaderDone; /* The nr of bytes actually in HeaderBuf */
148 gint LineWidth; /* The width of a line in bytes */
149 guchar *LineBuf; /* Buffer for 1 line */
150 gint LineDone; /* # of bytes in LineBuf */
151 gint Lines; /* # of finished lines */
155 8 = 8 bit colormapped
160 struct headerpair Header; /* Decoded (BE->CPU) header */
166 GdkPixbuf *pixbuf; /* Our "target" */
170 image_begin_load(ModulePreparedNotifyFunc prepared_func,
171 ModuleUpdatedNotifyFunc updated_func, gpointer user_data);
172 void image_stop_load(gpointer data);
173 gboolean image_load_increment(gpointer data, guchar * buf, guint size);
177 /* Shared library entry point */
178 GdkPixbuf *image_load(FILE * f)
182 struct ico_progressive_state *State;
187 State = image_begin_load(NULL, NULL, NULL);
188 membuf = g_malloc(4096);
190 g_assert(membuf != NULL);
193 while (feof(f) == 0) {
194 length = fread(membuf, 1, 4096, f);
196 image_load_increment(State, membuf, length);
200 if (State->pixbuf != NULL)
201 gdk_pixbuf_ref(State->pixbuf);
205 image_stop_load(State);
206 return State->pixbuf;
209 static void DecodeHeader(guchar *Data, gint Bytes,
210 struct ico_progressive_state *State)
212 /* For ICO's we have to be very clever. There are multiple images possible
213 in an .ICO. For now, we select (in order of priority):
214 1) The one with the highest number of colors
218 gint IconCount = 0; /* The number of icon-versions in the file */
219 guchar *BIH; /* The DIB for the used icon */
223 /* Step 1: The ICO header */
225 IconCount = (Data[5] << 8) + (Data[4]);
227 printf("There are %i icons in this file \n",IconCount);
228 State->HeaderSize = 6 + IconCount*16;
230 if (State->HeaderSize>State->BytesInHeaderBuf) {
231 State->HeaderBuf=g_realloc(State->HeaderBuf,State->HeaderSize);
232 State->BytesInHeaderBuf = State->HeaderSize;
234 if (Bytes < State->HeaderSize)
237 /* We now have all the "short-specs" of the versions
238 So we iterate through them and select the best one */
240 State->ImageScore = 0;
241 State->DIBoffset = 0;
243 for (I=0;I<IconCount;I++) {
244 int ThisWidth, ThisHeight,ThisColors;
249 ThisColors = (Ptr[2]);
251 ThisColors=256; /* Yes, this is in the spec */
253 printf("Option: %ix%ix%i ",ThisWidth,ThisHeight,ThisColors);
255 ThisScore = ThisColors*1024+ThisWidth*ThisHeight;
257 if (ThisScore>State->ImageScore) {
258 State->ImageScore = ThisScore;
259 State->DIBoffset = (Ptr[15]<<24)+(Ptr[14]<<16)+
260 (Ptr[13]<<8) + (Ptr[12]);
270 /* We now have a winner, pointed to in State->DIBoffset,
271 so we know how many bytes are in the "header" part. */
273 State->HeaderSize = State->DIBoffset + 40; /* 40 = sizeof(InfoHeader) */
275 if (State->HeaderSize>State->BytesInHeaderBuf) {
276 State->HeaderBuf=g_realloc(State->HeaderBuf,State->HeaderSize);
277 State->BytesInHeaderBuf = State->HeaderSize;
279 if (Bytes<State->HeaderSize)
282 BIH = Data+State->DIBoffset;
286 /* Add the palette to the headersize */
288 State->Header.width =
289 (BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) + (BIH[4]);
290 State->Header.height =
291 (BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) + (BIH[8])/2;
292 /* /2 because the BIH height includes the transparency mask */
293 State->Header.depth = (BIH[15] << 8) + (BIH[14]);;
295 State->Type = State->Header.depth; /* This may be less trivial someday */
296 if (State->Lines>State->Header.height)
299 I =(BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) + (BIH[32]);
300 if ((I==0)&&(State->Type==1))
302 if ((I==0)&&(State->Type==4))
304 if ((I==0)&&(State->Type==8))
307 State->HeaderSize+=I;
309 if (State->HeaderSize>State->BytesInHeaderBuf) {
310 State->HeaderBuf=g_realloc(State->HeaderBuf,State->HeaderSize);
311 State->BytesInHeaderBuf = State->HeaderSize;
313 if (Bytes < State->HeaderSize)
316 if ((BIH[16] != 0) || (BIH[17] != 0) || (BIH[18] != 0)
318 g_assert(0); /* Compressed icons aren't allowed */
321 if (State->Header.height < 0) {
322 State->Header.height = -State->Header.height;
323 State->Header.Negative = 1;
325 if (State->Header.width < 0) {
326 State->Header.width = -State->Header.width;
327 State->Header.Negative = 0;
330 if (State->Type == 24)
331 State->LineWidth = State->Header.width * 3;
332 if (State->Type == 8)
333 State->LineWidth = State->Header.width * 1;
334 if (State->Type == 4) {
335 State->LineWidth = State->Header.width/2;
336 if ((State->Header.width & 1) != 0)
339 if (State->Type == 1) {
340 State->LineWidth = State->Header.width / 8;
341 if ((State->Header.width & 7) != 0)
345 /* Pad to a 32 bit boundary */
346 if (((State->LineWidth % 4) > 0))
347 State->LineWidth = (State->LineWidth / 3) * 3 + 3;
350 if (State->LineBuf == NULL)
351 State->LineBuf = g_malloc(State->LineWidth);
353 g_assert(State->LineBuf != NULL);
356 if (State->pixbuf == NULL) {
358 gdk_pixbuf_new(ART_PIX_RGB, TRUE, 8,
359 (gint) State->Header.width,
360 (gint) State->Header.height);
362 if (State->prepared_func != NULL)
363 /* Notify the client that we are ready to go */
364 (*State->prepared_func) (State->pixbuf,
372 * func - called when we have pixmap created (but no image data)
373 * user_data - passed as arg 1 to func
374 * return context (opaque to user)
378 image_begin_load(ModulePreparedNotifyFunc prepared_func,
379 ModuleUpdatedNotifyFunc updated_func, gpointer user_data)
381 struct ico_progressive_state *context;
383 context = g_new0(struct ico_progressive_state, 1);
384 context->prepared_func = prepared_func;
385 context->updated_func = updated_func;
386 context->user_data = user_data;
388 context->HeaderSize = 54;
389 context->HeaderBuf = g_malloc(14 + 40 + 4*256 + 512);
390 /* 4*256 for the colormap */
391 context->BytesInHeaderBuf = 14 + 40 + 4*256 + 512 ;
392 context->HeaderDone = 0;
394 context->LineWidth = 0;
395 context->LineBuf = NULL;
396 context->LineDone = 0;
401 memset(&context->Header, 0, sizeof(struct headerpair));
404 context->pixbuf = NULL;
407 return (gpointer) context;
411 * context - returned from image_begin_load
413 * free context, unref gdk_pixbuf
415 void image_stop_load(gpointer data)
417 struct ico_progressive_state *context =
418 (struct ico_progressive_state *) data;
421 g_return_if_fail(context != NULL);
423 if (context->LineBuf != NULL)
424 g_free(context->LineBuf);
425 context->LineBuf = NULL;
426 if (context->HeaderBuf != NULL)
427 g_free(context->HeaderBuf);
430 gdk_pixbuf_unref(context->pixbuf);
436 static void OneLine24(struct ico_progressive_state *context)
442 if (context->Header.Negative == 0)
443 Pixels = context->pixbuf->art_pixbuf->pixels +
444 gdk_pixbuf_get_rowstride(context->pixbuf) *
445 (context->Header.height - context->Lines - 1);
447 Pixels = context->pixbuf->art_pixbuf->pixels +
448 gdk_pixbuf_get_rowstride(context->pixbuf) *
450 while (X < context->Header.width) {
451 Pixels[X * 4 + 0] = context->LineBuf[X * 3 + 2];
452 Pixels[X * 4 + 1] = context->LineBuf[X * 3 + 1];
453 Pixels[X * 4 + 2] = context->LineBuf[X * 3 + 0];
459 static void OneLine8(struct ico_progressive_state *context)
465 if (context->Header.Negative == 0)
466 Pixels = context->pixbuf->art_pixbuf->pixels +
467 gdk_pixbuf_get_rowstride(context->pixbuf) *
468 (context->Header.height - context->Lines - 1);
470 Pixels = context->pixbuf->art_pixbuf->pixels +
471 gdk_pixbuf_get_rowstride(context->pixbuf) *
473 while (X < context->Header.width) {
474 /* The joys of having a BGR byteorder */
476 context->HeaderBuf[4 * context->LineBuf[X] + 42+context->DIBoffset];
478 context->HeaderBuf[4 * context->LineBuf[X] + 41+context->DIBoffset];
480 context->HeaderBuf[4 * context->LineBuf[X] + 40+context->DIBoffset];
484 static void OneLine4(struct ico_progressive_state *context)
490 if (context->Header.Negative == 0)
491 Pixels = context->pixbuf->art_pixbuf->pixels +
492 gdk_pixbuf_get_rowstride(context->pixbuf) *
493 (context->Header.height - context->Lines - 1);
495 Pixels = context->pixbuf->art_pixbuf->pixels +
496 gdk_pixbuf_get_rowstride(context->pixbuf) *
499 while (X < context->Header.width) {
502 Pix = context->LineBuf[X/2];
505 context->HeaderBuf[4 * (Pix>>4) + 42+context->DIBoffset];
507 context->HeaderBuf[4 * (Pix>>4) + 41+context->DIBoffset];
509 context->HeaderBuf[4 * (Pix>>4) + 40+context->DIBoffset];
511 if (X<context->Header.width) {
512 /* Handle the other 4 bit pixel only when there is one */
514 context->HeaderBuf[4 * (Pix&15) + 42+context->DIBoffset];
516 context->HeaderBuf[4 * (Pix&15) + 41+context->DIBoffset];
518 context->HeaderBuf[4 * (Pix&15) + 40+context->DIBoffset];
525 static void OneLine1(struct ico_progressive_state *context)
531 if (context->Header.Negative == 0)
532 Pixels = context->pixbuf->art_pixbuf->pixels +
533 gdk_pixbuf_get_rowstride(context->pixbuf) *
534 (context->Header.height - context->Lines - 1);
536 Pixels = context->pixbuf->art_pixbuf->pixels +
537 gdk_pixbuf_get_rowstride(context->pixbuf) *
539 while (X < context->Header.width) {
542 Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
544 /* The joys of having a BGR byteorder */
545 Pixels[X * 4 + 0] = context->HeaderBuf[Bit + 32];
546 Pixels[X * 4 + 1] = context->HeaderBuf[Bit + 2 + 32];
547 Pixels[X * 4 + 2] = context->HeaderBuf[Bit + 4 + 32];
552 static void OneLineTransp(struct ico_progressive_state *context)
558 if (context->Header.Negative == 0)
559 Pixels = context->pixbuf->art_pixbuf->pixels +
560 gdk_pixbuf_get_rowstride(context->pixbuf) *
561 (2*context->Header.height - context->Lines - 1);
563 Pixels = context->pixbuf->art_pixbuf->pixels +
564 gdk_pixbuf_get_rowstride(context->pixbuf) *
565 (context->Lines-context->Header.height);
566 while (X < context->Header.width) {
569 Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
571 /* The joys of having a BGR byteorder */
572 Pixels[X * 4 + 3] = 255-Bit*255;
589 static void OneLine(struct ico_progressive_state *context)
591 context->LineDone = 0;
593 if (context->Lines >= context->Header.height*2) {
597 if (context->Lines <context->Header.height) {
599 if (context->Type == 24)
601 if (context->Type == 8)
603 if (context->Type == 4)
605 if (context->Type == 1)
609 OneLineTransp(context);
613 if (context->Lines>=context->Header.height) {
615 context->LineWidth = context->Header.width / 8;
616 if ((context->Header.width & 7) != 0)
617 context->LineWidth++;
618 /* Pad to a 32 bit boundary */
619 if (((context->LineWidth % 4) > 0))
620 context->LineWidth = (context->LineWidth / 4) * 4 + 4;
625 if (context->updated_func != NULL) {
626 (*context->updated_func) (context->pixbuf,
630 context->Header.width,
631 context->Header.height);
637 * context - from image_begin_load
638 * buf - new image data
639 * size - length of new image data
641 * append image data onto inrecrementally built output image
643 gboolean image_load_increment(gpointer data, guchar * buf, guint size)
645 struct ico_progressive_state *context =
646 (struct ico_progressive_state *) data;
651 printf("Y=%i C=%i H=%i\n",context->Lines,context->Type,context->Header.height);
652 g_assert(context->LineDone >= 0);
653 if (context->HeaderDone < context->HeaderSize) { /* We still
654 have headerbytes to do */
656 context->HeaderSize - context->HeaderDone;
657 if (BytesToCopy > size)
660 memcpy(context->HeaderBuf + context->HeaderDone,
665 context->HeaderDone += BytesToCopy;
670 context->LineWidth - context->LineDone;
671 if (BytesToCopy > size)
674 if (BytesToCopy > 0) {
675 memcpy(context->LineBuf +
676 context->LineDone, buf,
681 context->LineDone += BytesToCopy;
683 if ((context->LineDone >= context->LineWidth) &&
684 (context->LineWidth > 0))
690 if (context->HeaderDone >= 6)
691 DecodeHeader(context->HeaderBuf,
692 context->HeaderDone, context);