1
/* -*- mode: C; c-file-style: "linux" -*- */
2
/* GdkPixbuf library - Windows Icon/Cursor image loader
4
* Copyright (C) 1999 The Free Software Foundation
6
* Authors: Arjan van de Ven <arjan@fenrus.demon.nl>
7
* Federico Mena-Quintero <federico@gimp.org>
11
* This library is free software; you can redistribute it and/or
12
* modify it under the terms of the GNU Lesser General Public
13
* License as published by the Free Software Foundation; either
14
* version 2 of the License, or (at your option) any later version.
16
* This library is distributed in the hope that it will be useful,
17
* but WITHOUT ANY WARRANTY; without even the implied warranty of
18
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19
* Lesser General Public License for more details.
21
* You should have received a copy of the GNU Lesser General Public
22
* License along with this library; if not, write to the
23
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24
* Boston, MA 02111-1307, USA.
30
Icons are just like BMP's, except for the header.
33
* bi-tonal files aren't tested
44
#include "gdk-pixbuf-private.h"
45
#include "gdk-pixbuf-io.h"
52
These structures are actually dummies. These are according to
53
the "Windows API reference guide volume II" as written by
54
Borland International, but GCC fiddles with the alignment of
59
struct BitmapFileHeader {
66
struct BitmapInfoHeader {
74
guint biXPelsPerMeter;
75
guint biYPelsPerMeter;
83
DumpBIH printf's the values in a BitmapInfoHeader to the screen, for
87
static void DumpBIH(unsigned char *BIH)
89
printf("biSize = %i \n",
90
(int)(BIH[3] << 24) + (BIH[2] << 16) + (BIH[1] << 8) + (BIH[0]));
91
printf("biWidth = %i \n",
92
(int)(BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) + (BIH[4]));
93
printf("biHeight = %i \n",
94
(int)(BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) +
96
printf("biPlanes = %i \n", (int)(BIH[13] << 8) + (BIH[12]));
97
printf("biBitCount = %i \n", (int)(BIH[15] << 8) + (BIH[14]));
98
printf("biCompress = %i \n",
99
(int)(BIH[19] << 24) + (BIH[18] << 16) + (BIH[17] << 8) +
101
printf("biSizeImage = %i \n",
102
(int)(BIH[23] << 24) + (BIH[22] << 16) + (BIH[21] << 8) +
104
printf("biXPels = %i \n",
105
(int)(BIH[27] << 24) + (BIH[26] << 16) + (BIH[25] << 8) +
107
printf("biYPels = %i \n",
108
(int)(BIH[31] << 24) + (BIH[30] << 16) + (BIH[29] << 8) +
110
printf("biClrUsed = %i \n",
111
(int)(BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) +
113
printf("biClrImprtnt= %i \n",
114
(int)(BIH[39] << 24) + (BIH[38] << 16) + (BIH[37] << 8) +
119
/* Progressive loading */
124
guint Negative; /* Negative = 1 -> top down BMP,
125
Negative = 0 -> bottom up BMP */
128
struct ico_progressive_state {
129
GdkPixbufModuleSizeFunc size_func;
130
GdkPixbufModulePreparedFunc prepared_func;
131
GdkPixbufModuleUpdatedFunc updated_func;
134
gint HeaderSize; /* The size of the header-part (incl colormap) */
135
guchar *HeaderBuf; /* The buffer for the header (incl colormap) */
136
gint BytesInHeaderBuf; /* The size of the allocated HeaderBuf */
137
gint HeaderDone; /* The nr of bytes actually in HeaderBuf */
139
gint LineWidth; /* The width of a line in bytes */
140
guchar *LineBuf; /* Buffer for 1 line */
141
gint LineDone; /* # of bytes in LineBuf */
142
gint Lines; /* # of finished lines */
148
8 = 8 bit colormapped
149
4 = 4 bpp colormapped
156
struct headerpair Header; /* Decoded (BE->CPU) header */
162
GdkPixbuf *pixbuf; /* Our "target" */
166
gdk_pixbuf__ico_image_begin_load(GdkPixbufModuleSizeFunc size_func,
167
GdkPixbufModulePreparedFunc prepared_func,
168
GdkPixbufModuleUpdatedFunc updated_func,
171
static gboolean gdk_pixbuf__ico_image_stop_load(gpointer data, GError **error);
172
static gboolean gdk_pixbuf__ico_image_load_increment(gpointer data,
173
const guchar * buf, guint size,
177
context_free (struct ico_progressive_state *context)
179
g_free (context->LineBuf);
180
context->LineBuf = NULL;
181
g_free (context->HeaderBuf);
184
g_object_unref (context->pixbuf);
189
static void DecodeHeader(guchar *Data, gint Bytes,
190
struct ico_progressive_state *State,
193
/* For ICO's we have to be very clever. There are multiple images possible
194
in an .ICO. As a simple heuristic, we select the image which occupies the
195
largest number of bytes.
198
gint IconCount = 0; /* The number of icon-versions in the file */
199
guchar *BIH; /* The DIB for the used icon */
202
guint16 imgtype; /* 1 = icon, 2 = cursor */
204
/* Step 1: The ICO header */
206
/* First word should be 0 according to specs */
207
if (((Data[1] << 8) + Data[0]) != 0) {
208
g_set_error_literal (error,
210
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
211
_("Invalid header in icon"));
216
imgtype = (Data[3] << 8) + Data[2];
218
State->cursor = (imgtype == 2) ? TRUE : FALSE;
220
/* If it is not a cursor make sure it is actually an icon */
221
if (!State->cursor && imgtype != 1) {
222
g_set_error_literal (error,
224
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
225
_("Invalid header in icon"));
230
IconCount = (Data[5] << 8) + (Data[4]);
232
State->HeaderSize = 6 + IconCount*16;
234
if (State->HeaderSize>State->BytesInHeaderBuf) {
235
guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
237
g_set_error_literal (error,
239
GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
240
_("Not enough memory to load icon"));
243
State->HeaderBuf = tmp;
244
State->BytesInHeaderBuf = State->HeaderSize;
246
if (Bytes < State->HeaderSize)
249
/* We now have all the "short-specs" of the versions
250
So we iterate through them and select the best one */
252
State->ImageScore = 0;
253
State->DIBoffset = 0;
255
for (I=0;I<IconCount;I++) {
258
ThisScore = (Ptr[11] << 24) + (Ptr[10] << 16) + (Ptr[9] << 8) + (Ptr[8]);
260
if (ThisScore>=State->ImageScore) {
261
State->ImageScore = ThisScore;
262
State->x_hot = (Ptr[5] << 8) + Ptr[4];
263
State->y_hot = (Ptr[7] << 8) + Ptr[6];
264
State->DIBoffset = (Ptr[15]<<24)+(Ptr[14]<<16)+
265
(Ptr[13]<<8) + (Ptr[12]);
273
if (State->DIBoffset < 0) {
274
g_set_error_literal (error,
276
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
277
_("Invalid header in icon"));
281
/* We now have a winner, pointed to in State->DIBoffset,
282
so we know how many bytes are in the "header" part. */
284
State->HeaderSize = State->DIBoffset + 40; /* 40 = sizeof(InfoHeader) */
286
if (State->HeaderSize < 0) {
287
g_set_error_literal (error,
289
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
290
_("Invalid header in icon"));
294
if (State->HeaderSize>State->BytesInHeaderBuf) {
295
guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
297
g_set_error_literal (error,
299
GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
300
_("Not enough memory to load icon"));
303
State->HeaderBuf = tmp;
304
State->BytesInHeaderBuf = State->HeaderSize;
306
if (Bytes<State->HeaderSize)
309
BIH = Data+State->DIBoffset;
314
/* Add the palette to the headersize */
316
State->Header.width =
317
(int)(BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) + (BIH[4]);
318
if (State->Header.width == 0) {
319
g_set_error_literal (error,
321
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
322
_("Icon has zero width"));
325
State->Header.height =
326
(int)((BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) + (BIH[8]))/2;
327
/* /2 because the BIH height includes the transparency mask */
328
if (State->Header.height == 0) {
329
g_set_error_literal (error,
331
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
332
_("Icon has zero height"));
335
State->Header.depth = (BIH[15] << 8) + (BIH[14]);
337
State->Type = State->Header.depth;
338
if (State->Lines>=State->Header.height)
339
State->Type = 1; /* The transparency mask is 1 bpp */
341
/* Determine the palette size. If the header indicates 0, it
342
is actually the maximum for the bpp. You have to love the
343
guys who made the spec. */
344
I = (int)(BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) + (BIH[32]);
346
if ((I==0)&&(State->Type==1))
348
if ((I==0)&&(State->Type==4))
350
if ((I==0)&&(State->Type==8))
353
State->HeaderSize+=I;
355
if (State->HeaderSize < 0) {
356
g_set_error_literal (error,
358
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
359
_("Invalid header in icon"));
363
if (State->HeaderSize>State->BytesInHeaderBuf) {
364
guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
366
g_set_error_literal (error,
368
GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
369
_("Not enough memory to load icon"));
372
State->HeaderBuf = tmp;
373
State->BytesInHeaderBuf = State->HeaderSize;
375
if (Bytes < State->HeaderSize)
378
if ((BIH[16] != 0) || (BIH[17] != 0) || (BIH[18] != 0)
380
/* FIXME: is this the correct message? */
381
g_set_error_literal (error,
383
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
384
_("Compressed icons are not supported"));
388
/* Negative heights mean top-down pixel-order */
389
if (State->Header.height < 0) {
390
State->Header.height = -State->Header.height;
391
State->Header.Negative = 1;
393
if (State->Header.width < 0) {
394
State->Header.width = -State->Header.width;
396
g_assert (State->Header.width > 0);
397
g_assert (State->Header.height > 0);
399
if (State->Type == 32)
400
State->LineWidth = State->Header.width * 4;
401
else if (State->Type == 24)
402
State->LineWidth = State->Header.width * 3;
403
else if (State->Type == 16)
404
State->LineWidth = State->Header.width * 2;
405
else if (State->Type == 8)
406
State->LineWidth = State->Header.width * 1;
407
else if (State->Type == 4)
408
State->LineWidth = (State->Header.width+1)/2;
409
else if (State->Type == 1) {
410
State->LineWidth = State->Header.width / 8;
411
if ((State->Header.width & 7) != 0)
414
g_set_error_literal (error,
416
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
417
_("Unsupported icon type"));
421
/* Pad to a 32 bit boundary */
422
if (((State->LineWidth % 4) > 0))
423
State->LineWidth = (State->LineWidth / 4) * 4 + 4;
426
if (State->LineBuf == NULL) {
427
State->LineBuf = g_try_malloc(State->LineWidth);
428
if (!State->LineBuf) {
429
g_set_error_literal (error,
431
GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
432
_("Not enough memory to load icon"));
437
g_assert(State->LineBuf != NULL);
440
if (State->pixbuf == NULL) {
442
if (State->size_func) {
443
gint width = State->Header.width;
444
gint height = State->Header.height;
446
(*State->size_func) (&width, &height, State->user_data);
447
if (width == 0 || height == 0) {
448
State->LineWidth = 0;
455
gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
457
State->Header.height);
458
if (!State->pixbuf) {
459
g_set_error_literal (error,
461
GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
462
_("Not enough memory to load icon"));
467
g_snprintf (hot, 10, "%d", State->x_hot);
468
gdk_pixbuf_set_option (State->pixbuf, "x_hot", hot);
469
g_snprintf (hot, 10, "%d", State->y_hot);
470
gdk_pixbuf_set_option (State->pixbuf, "y_hot", hot);
473
if (State->prepared_func != NULL)
474
/* Notify the client that we are ready to go */
475
(*State->prepared_func) (State->pixbuf,
484
* func - called when we have pixmap created (but no image data)
485
* user_data - passed as arg 1 to func
486
* return context (opaque to user)
490
gdk_pixbuf__ico_image_begin_load(GdkPixbufModuleSizeFunc size_func,
491
GdkPixbufModulePreparedFunc prepared_func,
492
GdkPixbufModuleUpdatedFunc updated_func,
496
struct ico_progressive_state *context;
498
context = g_new0(struct ico_progressive_state, 1);
499
context->size_func = size_func;
500
context->prepared_func = prepared_func;
501
context->updated_func = updated_func;
502
context->user_data = user_data;
504
context->HeaderSize = 54;
505
context->HeaderBuf = g_try_malloc(14 + 40 + 4*256 + 512);
506
if (!context->HeaderBuf) {
508
g_set_error_literal (error,
510
GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
511
_("Not enough memory to load ICO file"));
514
/* 4*256 for the colormap */
515
context->BytesInHeaderBuf = 14 + 40 + 4*256 + 512 ;
516
context->HeaderDone = 0;
518
context->LineWidth = 0;
519
context->LineBuf = NULL;
520
context->LineDone = 0;
525
memset(&context->Header, 0, sizeof(struct headerpair));
528
context->pixbuf = NULL;
531
return (gpointer) context;
535
* context - returned from image_begin_load
537
* free context, unref gdk_pixbuf
540
gdk_pixbuf__ico_image_stop_load(gpointer data,
543
struct ico_progressive_state *context =
544
(struct ico_progressive_state *) data;
546
/* FIXME this thing needs to report errors if
547
* we have unused image data
550
g_return_val_if_fail(context != NULL, TRUE);
552
context_free (context);
557
OneLine32 (struct ico_progressive_state *context)
563
if (context->Header.Negative == 0)
564
Pixels = (context->pixbuf->pixels +
565
context->pixbuf->rowstride *
566
(context->Header.height - context->Lines - 1));
568
Pixels = (context->pixbuf->pixels +
569
context->pixbuf->rowstride *
571
while (X < context->Header.width) {
572
Pixels[X * 4 + 0] = context->LineBuf[X * 4 + 2];
573
Pixels[X * 4 + 1] = context->LineBuf[X * 4 + 1];
574
Pixels[X * 4 + 2] = context->LineBuf[X * 4 + 0];
575
Pixels[X * 4 + 3] = context->LineBuf[X * 4 + 3];
580
static void OneLine24(struct ico_progressive_state *context)
586
if (context->Header.Negative == 0)
587
Pixels = (context->pixbuf->pixels +
588
context->pixbuf->rowstride *
589
(context->Header.height - context->Lines - 1));
591
Pixels = (context->pixbuf->pixels +
592
context->pixbuf->rowstride *
594
while (X < context->Header.width) {
595
Pixels[X * 4 + 0] = context->LineBuf[X * 3 + 2];
596
Pixels[X * 4 + 1] = context->LineBuf[X * 3 + 1];
597
Pixels[X * 4 + 2] = context->LineBuf[X * 3 + 0];
604
OneLine16 (struct ico_progressive_state *context)
610
if (context->Header.Negative == 0)
611
pixels = (context->pixbuf->pixels +
612
context->pixbuf->rowstride * (context->Header.height - context->Lines - 1));
614
pixels = (context->pixbuf->pixels +
615
context->pixbuf->rowstride * context->Lines);
617
src = context->LineBuf;
619
for (i = 0; i < context->Header.width; i++) {
622
v = (int) src[0] | ((int) src[1] << 8);
625
/* Extract 5-bit RGB values */
627
r = (v >> 10) & 0x1f;
631
/* Fill the rightmost bits to form 8-bit values */
633
*pixels++ = (r << 3) | (r >> 2);
634
*pixels++ = (g << 3) | (g >> 2);
635
*pixels++ = (b << 3) | (b >> 2);
636
pixels++; /* skip alpha channel */
641
static void OneLine8(struct ico_progressive_state *context)
647
if (context->Header.Negative == 0)
648
Pixels = (context->pixbuf->pixels +
649
context->pixbuf->rowstride *
650
(context->Header.height - context->Lines - 1));
652
Pixels = (context->pixbuf->pixels +
653
context->pixbuf->rowstride *
655
while (X < context->Header.width) {
656
/* The joys of having a BGR byteorder */
658
context->HeaderBuf[4 * context->LineBuf[X] + 42+context->DIBoffset];
660
context->HeaderBuf[4 * context->LineBuf[X] + 41+context->DIBoffset];
662
context->HeaderBuf[4 * context->LineBuf[X] + 40+context->DIBoffset];
666
static void OneLine4(struct ico_progressive_state *context)
672
if (context->Header.Negative == 0)
673
Pixels = (context->pixbuf->pixels +
674
context->pixbuf->rowstride *
675
(context->Header.height - context->Lines - 1));
677
Pixels = (context->pixbuf->pixels +
678
context->pixbuf->rowstride *
681
while (X < context->Header.width) {
684
Pix = context->LineBuf[X/2];
687
context->HeaderBuf[4 * (Pix>>4) + 42+context->DIBoffset];
689
context->HeaderBuf[4 * (Pix>>4) + 41+context->DIBoffset];
691
context->HeaderBuf[4 * (Pix>>4) + 40+context->DIBoffset];
693
if (X<context->Header.width) {
694
/* Handle the other 4 bit pixel only when there is one */
696
context->HeaderBuf[4 * (Pix&15) + 42+context->DIBoffset];
698
context->HeaderBuf[4 * (Pix&15) + 41+context->DIBoffset];
700
context->HeaderBuf[4 * (Pix&15) + 40+context->DIBoffset];
707
static void OneLine1(struct ico_progressive_state *context)
713
if (context->Header.Negative == 0)
714
Pixels = (context->pixbuf->pixels +
715
context->pixbuf->rowstride *
716
(context->Header.height - context->Lines - 1));
718
Pixels = (context->pixbuf->pixels +
719
context->pixbuf->rowstride *
721
while (X < context->Header.width) {
724
Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
726
/* The joys of having a BGR byteorder */
727
Pixels[X * 4 + 0] = Bit*255;
728
Pixels[X * 4 + 1] = Bit*255;
729
Pixels[X * 4 + 2] = Bit*255;
734
static void OneLineTransp(struct ico_progressive_state *context)
739
/* Ignore the XOR mask for XP style 32-bpp icons with alpha */
740
if (context->Header.depth == 32)
744
if (context->Header.Negative == 0)
745
Pixels = (context->pixbuf->pixels +
746
context->pixbuf->rowstride *
747
(2*context->Header.height - context->Lines - 1));
749
Pixels = (context->pixbuf->pixels +
750
context->pixbuf->rowstride *
751
(context->Lines-context->Header.height));
752
while (X < context->Header.width) {
755
Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
757
/* The joys of having a BGR byteorder */
758
Pixels[X * 4 + 3] = 255-Bit*255;
773
static void OneLine(struct ico_progressive_state *context)
775
context->LineDone = 0;
777
if (context->Lines >= context->Header.height*2) {
781
if (context->Lines <context->Header.height) {
782
if (context->Type == 32)
784
else if (context->Type == 24)
786
else if (context->Type == 16)
788
else if (context->Type == 8)
790
else if (context->Type == 4)
792
else if (context->Type == 1)
795
g_assert_not_reached ();
797
OneLineTransp(context);
800
if (context->Lines>=context->Header.height) {
802
context->LineWidth = context->Header.width / 8;
803
if ((context->Header.width & 7) != 0)
804
context->LineWidth++;
805
/* Pad to a 32 bit boundary */
806
if (((context->LineWidth % 4) > 0))
807
context->LineWidth = (context->LineWidth / 4) * 4 + 4;
812
if (context->updated_func != NULL) {
813
(*context->updated_func) (context->pixbuf,
815
context->Lines % context->Header.height,
816
context->Header.width,
824
* context - from image_begin_load
825
* buf - new image data
826
* size - length of new image data
828
* append image data onto inrecrementally built output image
831
gdk_pixbuf__ico_image_load_increment(gpointer data,
836
struct ico_progressive_state *context =
837
(struct ico_progressive_state *) data;
842
g_assert(context->LineDone >= 0);
843
if (context->HeaderDone < context->HeaderSize) { /* We still
844
have headerbytes to do */
846
context->HeaderSize - context->HeaderDone;
847
if (BytesToCopy > size)
850
memmove(context->HeaderBuf + context->HeaderDone,
855
context->HeaderDone += BytesToCopy;
859
context->LineWidth - context->LineDone;
860
if (BytesToCopy > size)
863
if (BytesToCopy > 0) {
864
memmove(context->LineBuf +
865
context->LineDone, buf,
870
context->LineDone += BytesToCopy;
872
if ((context->LineDone >= context->LineWidth) &&
873
(context->LineWidth > 0))
879
if (context->HeaderDone >= 6 && context->pixbuf == NULL) {
880
GError *decode_err = NULL;
881
DecodeHeader(context->HeaderBuf,
882
context->HeaderDone, context, &decode_err);
883
if (context->LineBuf != NULL && context->LineWidth == 0)
887
g_propagate_error (error, decode_err);
909
bytes = fwrite ((char*) data, sizeof (char), count, f);
927
for (i = 0; i < count; i++)
928
data[i] = GUINT16_TO_LE (data[i]);
930
return write8 (f, (guint8*) data, count * 2);
940
for (i = 0; i < count; i++)
941
data[i] = GUINT32_TO_LE (data[i]);
943
return write8 (f, (guint8*) data, count * 4);
946
typedef struct _IconEntry IconEntry;
963
fill_entry (IconEntry *icon,
969
guchar *p, *pixels, *and, *xor;
970
gint n_channels, v, x, y;
972
if (icon->width > 255 || icon->height > 255) {
973
g_set_error_literal (error,
975
GDK_PIXBUF_ERROR_BAD_OPTION,
976
_("Image too large to be saved as ICO"));
980
if (hot_x > -1 && hot_y > -1) {
983
if (icon->hot_x >= icon->width || icon->hot_y >= icon->height) {
984
g_set_error_literal (error,
986
GDK_PIXBUF_ERROR_BAD_OPTION,
987
_("Cursor hotspot outside image"));
996
switch (icon->depth) {
998
icon->xor_rowstride = icon->width * 4;
1001
icon->xor_rowstride = icon->width * 3;
1004
icon->xor_rowstride = icon->width * 2;
1009
GDK_PIXBUF_ERROR_BAD_OPTION,
1010
_("Unsupported depth for ICO file: %d"), icon->depth);
1014
if ((icon->xor_rowstride % 4) != 0)
1015
icon->xor_rowstride = 4 * ((icon->xor_rowstride / 4) + 1);
1016
icon->xor = g_new0 (guchar, icon->xor_rowstride * icon->height);
1018
icon->and_rowstride = (icon->width + 7) / 8;
1019
if ((icon->and_rowstride % 4) != 0)
1020
icon->and_rowstride = 4 * ((icon->and_rowstride / 4) + 1);
1021
icon->and = g_new0 (guchar, icon->and_rowstride * icon->height);
1023
pixels = gdk_pixbuf_get_pixels (pixbuf);
1024
n_channels = gdk_pixbuf_get_n_channels (pixbuf);
1025
for (y = 0; y < icon->height; y++) {
1026
p = pixels + gdk_pixbuf_get_rowstride (pixbuf) * (icon->height - 1 - y);
1027
and = icon->and + icon->and_rowstride * y;
1028
xor = icon->xor + icon->xor_rowstride * y;
1029
for (x = 0; x < icon->width; x++) {
1030
switch (icon->depth) {
1036
if (n_channels == 4) {
1039
*and |= 1 << (7 - x % 8);
1047
if (n_channels == 4 && p[3] < 0x80)
1048
*and |= 1 << (7 - x % 8);
1052
v = ((p[0] >> 3) << 10) | ((p[1] >> 3) << 5) | (p[2] >> 3);
1055
if (n_channels == 4 && p[3] < 0x80)
1056
*and |= 1 << (7 - x % 8);
1071
free_entry (IconEntry *icon)
1073
g_free (icon->colors);
1080
write_icon (FILE *f, GSList *entries)
1092
if (((IconEntry *)entries->data)->hot_x > -1)
1096
n_entries = g_slist_length (entries);
1101
words[2] = n_entries;
1102
write16 (f, words, 3);
1104
offset = 6 + 16 * n_entries;
1106
for (entry = entries; entry; entry = entry->next) {
1107
icon = (IconEntry *)entry->data;
1108
size = 40 + icon->height * (icon->and_rowstride + icon->xor_rowstride);
1110
/* directory entry */
1111
bytes[0] = icon->width;
1112
bytes[1] = icon->height;
1113
bytes[2] = icon->n_colors;
1115
write8 (f, bytes, 4);
1118
words[1] = icon->depth;
1121
words[0] = icon->hot_x;
1122
words[1] = icon->hot_y;
1124
write16 (f, words, 2);
1127
write32 (f, dwords, 2);
1132
for (entry = entries; entry; entry = entry->next) {
1133
icon = (IconEntry *)entry->data;
1137
dwords[1] = icon->width;
1138
dwords[2] = icon->height * 2;
1139
write32 (f, dwords, 3);
1141
words[1] = icon->depth;
1142
write16 (f, words, 2);
1149
write32 (f, dwords, 6);
1152
write8 (f, icon->xor, icon->xor_rowstride * icon->height);
1153
write8 (f, icon->and, icon->and_rowstride * icon->height);
1158
gdk_pixbuf__ico_image_save (FILE *f,
1166
GSList *entries = NULL;
1168
/* support only single-image ICOs for now */
1169
icon = g_new0 (IconEntry, 1);
1170
icon->width = gdk_pixbuf_get_width (pixbuf);
1171
icon->height = gdk_pixbuf_get_height (pixbuf);
1172
icon->depth = gdk_pixbuf_get_has_alpha (pixbuf) ? 32 : 24;
1177
if (keys && *keys) {
1181
for (kiter = keys, viter = values; *kiter && *viter; kiter++, viter++) {
1183
if (strcmp (*kiter, "depth") == 0) {
1184
sscanf (*viter, "%d", &icon->depth);
1186
else if (strcmp (*kiter, "x_hot") == 0) {
1187
hot_x = strtol (*viter, &endptr, 10);
1189
else if (strcmp (*kiter, "y_hot") == 0) {
1190
hot_y = strtol (*viter, &endptr, 10);
1196
if (!fill_entry (icon, pixbuf, hot_x, hot_y, error)) {
1201
entries = g_slist_append (entries, icon);
1202
write_icon (f, entries);
1204
g_slist_foreach (entries, (GFunc)free_entry, NULL);
1205
g_slist_free (entries);
1211
#define MODULE_ENTRY(function) G_MODULE_EXPORT void function
1213
#define MODULE_ENTRY(function) void _gdk_pixbuf__ico_ ## function
1216
MODULE_ENTRY (fill_vtable) (GdkPixbufModule *module)
1218
module->begin_load = gdk_pixbuf__ico_image_begin_load;
1219
module->stop_load = gdk_pixbuf__ico_image_stop_load;
1220
module->load_increment = gdk_pixbuf__ico_image_load_increment;
1221
module->save = gdk_pixbuf__ico_image_save;
1224
MODULE_ENTRY (fill_info) (GdkPixbufFormat *info)
1226
static GdkPixbufModulePattern signature[] = {
1227
{ " \x1 ", "zz znz", 100 },
1228
{ " \x2 ", "zz znz", 100 },
1231
static gchar * mime_types[] = {
1234
"image/x-win-bitmap",
1237
static gchar * extensions[] = {
1244
info->signature = signature;
1245
info->description = N_("The ICO image format");
1246
info->mime_types = mime_types;
1247
info->extensions = extensions;
1248
info->flags = GDK_PIXBUF_FORMAT_WRITABLE | GDK_PIXBUF_FORMAT_THREADSAFE;
1249
info->license = "LGPL";