io-ico.c 29.9 KB
Newer Older
1
/* -*- mode: C; c-file-style: "linux" -*- */
2
/* GdkPixbuf library - Windows Icon/Cursor image loader
3
4
5
6
7
8
9
10
11
 *
 * Copyright (C) 1999 The Free Software Foundation
 *
 * Authors: Arjan van de Ven <arjan@fenrus.demon.nl>
 *          Federico Mena-Quintero <federico@gimp.org>
 *
 * Based on io-bmp.c
 *
 * This library is free software; you can redistribute it and/or
12
 * modify it under the terms of the GNU Lesser General Public
13
14
15
16
17
18
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19
 * Lesser General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU Lesser General Public
22
23
24
25
26
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

27
#undef DUMPBIH
28
29
30
31
32
33
34
35
36
37
38
/*

Icons are just like BMP's, except for the header.
 
Known bugs:
	* bi-tonal files aren't tested 

*/

#include <config.h>
#include <stdio.h>
39
#include <stdlib.h>
Tor Lillqvist's avatar
Tor Lillqvist committed
40
#ifdef HAVE_UNISTD_H
41
#include <unistd.h>
Tor Lillqvist's avatar
Tor Lillqvist committed
42
#endif
43
#include <string.h>
44
#include "gdk-pixbuf-private.h"
45
#include "gdk-pixbuf-io.h"
46
#include <errno.h>
47

48

49
50
51
52

/* 

These structures are actually dummies. These are according to
53
54
55
the "Windows API reference guide volume II" as written by 
Borland International, but GCC fiddles with the alignment of
the internal members.
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

*/

struct BitmapFileHeader {
	gushort bfType;
	guint bfSize;
	guint reserverd;
	guint bfOffbits;
};

struct BitmapInfoHeader {
	guint biSize;
	guint biWidth;
	guint biHeight;
	gushort biPlanes;
	gushort biBitCount;
	guint biCompression;
	guint biSizeImage;
	guint biXPelsPerMeter;
	guint biYPelsPerMeter;
	guint biClrUsed;
	guint biClrImportant;
};

80
81
82
83
84
85
86
#ifdef DUMPBIH
/* 

DumpBIH printf's the values in a BitmapInfoHeader to the screen, for 
debugging purposes.

*/
87
static void DumpBIH(unsigned char *BIH)
88
{				
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) +
95
	       (BIH[8]));
96
97
	printf("biPlanes    = %i \n", (int)(BIH[13] << 8) + (BIH[12]));
	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) +
100
101
	       (BIH[16]));
	printf("biSizeImage = %i \n",
102
	       (int)(BIH[23] << 24) + (BIH[22] << 16) + (BIH[21] << 8) +
103
104
	       (BIH[20]));
	printf("biXPels     = %i \n",
105
	       (int)(BIH[27] << 24) + (BIH[26] << 16) + (BIH[25] << 8) +
106
107
	       (BIH[24]));
	printf("biYPels     = %i \n",
108
	       (int)(BIH[31] << 24) + (BIH[30] << 16) + (BIH[29] << 8) +
109
110
	       (BIH[28]));
	printf("biClrUsed   = %i \n",
111
	       (int)(BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) +
112
113
	       (BIH[32]));
	printf("biClrImprtnt= %i \n",
114
	       (int)(BIH[39] << 24) + (BIH[38] << 16) + (BIH[37] << 8) +
115
116
	       (BIH[36]));
}
117
#endif
118
119

/* Progressive loading */
120
struct headerpair {
121
122
	gint width;
	gint height;
123
124
125
126
	guint depth;
	guint Negative;		/* Negative = 1 -> top down BMP,  
				   Negative = 0 -> bottom up BMP */
};
127
128

struct ico_progressive_state {
129
	GdkPixbufModuleSizeFunc size_func;
130
131
	GdkPixbufModulePreparedFunc prepared_func;
	GdkPixbufModuleUpdatedFunc updated_func;
132
133
134
135
136
137
138
139
140
141
142
143
144
	gpointer user_data;

	gint HeaderSize;	/* The size of the header-part (incl colormap) */
	guchar *HeaderBuf;	/* The buffer for the header (incl colormap) */
	gint BytesInHeaderBuf;  /* The size of the allocated HeaderBuf */
	gint HeaderDone;	/* The nr of bytes actually in HeaderBuf */

	gint LineWidth;		/* The width of a line in bytes */
	guchar *LineBuf;	/* Buffer for 1 line */
	gint LineDone;		/* # of bytes in LineBuf */
	gint Lines;		/* # of finished lines */

	gint Type;		/*  
145
				   32 = RGBA
146
				   24 = RGB
147
				   16 = 555 RGB
148
				   8 = 8 bit colormapped
149
				   4 = 4 bpp colormapped
150
151
				   1  = 1 bit bitonal 
				 */
152
153
154
        gboolean cursor;
        gint x_hot;
        gint y_hot;
155
156
157
158
159
160
161
162
163
164

	struct headerpair Header;	/* Decoded (BE->CPU) header */
	
	gint			DIBoffset;
	gint			ImageScore;


	GdkPixbuf *pixbuf;	/* Our "target" */
};

165
static gpointer
166
167
168
gdk_pixbuf__ico_image_begin_load(GdkPixbufModuleSizeFunc size_func,
                                 GdkPixbufModulePreparedFunc prepared_func,
				 GdkPixbufModuleUpdatedFunc updated_func,
Havoc Pennington's avatar
Havoc Pennington committed
169
170
				 gpointer user_data,
                                 GError **error);
171
172
173
174
static gboolean gdk_pixbuf__ico_image_stop_load(gpointer data, GError **error);
static gboolean gdk_pixbuf__ico_image_load_increment(gpointer data,
                                                     const guchar * buf, guint size,
                                                     GError **error);
175

176
177
178
179
180
181
182
183
static void 
context_free (struct ico_progressive_state *context)
{
	if (context->LineBuf != NULL)
		g_free (context->LineBuf);
	context->LineBuf = NULL;
	if (context->HeaderBuf != NULL)
		g_free (context->HeaderBuf);
184

185
	if (context->pixbuf)
186
		g_object_unref (context->pixbuf);
187

188
189
	g_free (context);
}
190
191

static void DecodeHeader(guchar *Data, gint Bytes,
192
193
			 struct ico_progressive_state *State,
			 GError **error)
194
195
{
/* For ICO's we have to be very clever. There are multiple images possible
196
197
   in an .ICO. As a simple heuristic, we select the image which occupies the 
   largest number of bytes.
198
199
200
201
202
203
204
205
 */   
 
	gint IconCount = 0; /* The number of icon-versions in the file */
	guchar *BIH; /* The DIB for the used icon */
 	guchar *Ptr;
 	gint I;
 
 	/* Step 1: The ICO header */
206

207
208
	State->cursor = ((Data[3] << 8) + Data[2] == 2) ? TRUE : FALSE;

209
 	IconCount = (Data[5] << 8) + (Data[4]);
210
	
211
 	State->HeaderSize = 6 + IconCount*16;
212

213
 	if (State->HeaderSize>State->BytesInHeaderBuf) {
214
215
 		guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
		if (!tmp) {
216
217
218
219
220
221
			g_set_error (error,
				     GDK_PIXBUF_ERROR,
				     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
				     _("Not enough memory to load icon"));
			return;
		}
222
		State->HeaderBuf = tmp;
223
224
225
226
227
228
229
230
231
232
233
234
235
236
 		State->BytesInHeaderBuf = State->HeaderSize;
 	}
 	if (Bytes < State->HeaderSize)
 		return;
 	
 	/* We now have all the "short-specs" of the versions 
 	   So we iterate through them and select the best one */
 	   
 	State->ImageScore = 0;
 	State->DIBoffset  = 0;
 	Ptr = Data + 6;
	for (I=0;I<IconCount;I++) {
		int ThisScore;
		
237
		ThisScore = (Ptr[11] << 24) + (Ptr[10] << 16) + (Ptr[9] << 8) + (Ptr[8]);
238

239
		if (ThisScore>=State->ImageScore) {
240
			State->ImageScore = ThisScore;
241
242
			State->x_hot = (Ptr[5] << 8) + Ptr[4];
			State->y_hot = (Ptr[7] << 8) + Ptr[6];
243
244
245
246
247
248
249
250
			State->DIBoffset = (Ptr[15]<<24)+(Ptr[14]<<16)+
					   (Ptr[13]<<8) + (Ptr[12]);
								 
		}
		
		
		Ptr += 16;	
	} 
251
252
253
254
255
256
257
258
259

	if (State->DIBoffset < 0) {
		g_set_error (error,
			     GDK_PIXBUF_ERROR,
			     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
			     _("Invalid header in icon"));
		return;
	}

260
261
262
263
264
265
	/* We now have a winner, pointed to in State->DIBoffset,
	   so we know how many bytes are in the "header" part. */
	      
	State->HeaderSize = State->DIBoffset + 40; /* 40 = sizeof(InfoHeader) */
	
 	if (State->HeaderSize>State->BytesInHeaderBuf) {
266
267
 		guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
		if (!tmp) {
268
269
270
271
272
273
			g_set_error (error,
				     GDK_PIXBUF_ERROR,
				     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
				     _("Not enough memory to load icon"));
			return;
		}
274
		State->HeaderBuf = tmp;
275
276
277
278
279
280
281
 		State->BytesInHeaderBuf = State->HeaderSize;
 	}
	if (Bytes<State->HeaderSize) 
		return;   
	
	BIH = Data+State->DIBoffset;

282
283
284
#ifdef DUMPBIH
	DumpBIH(BIH);
#endif	
285
286
287
	/* Add the palette to the headersize */
		
	State->Header.width =
288
	    (int)(BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) + (BIH[4]);
289
290
291
292
293
294
295
	if (State->Header.width == 0) {
		g_set_error (error,
			     GDK_PIXBUF_ERROR,
			     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
			     _("Icon has zero width"));
		return;
	}
296
	State->Header.height =
297
	    (int)((BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) + (BIH[8]))/2;
298
	    /* /2 because the BIH height includes the transparency mask */
299
300
301
302
303
304
305
	if (State->Header.height == 0) {
		g_set_error (error,
			     GDK_PIXBUF_ERROR,
			     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
			     _("Icon has zero height"));
		return;
	}
306
	State->Header.depth = (BIH[15] << 8) + (BIH[14]);
307

308
	State->Type = State->Header.depth;	
309
	if (State->Lines>=State->Header.height)
310
311
312
313
314
		State->Type = 1; /* The transparency mask is 1 bpp */
	
	/* Determine the  palette size. If the header indicates 0, it
	   is actually the maximum for the bpp. You have to love the
	   guys who made the spec. */
315
316
	I = (int)(BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) + (BIH[32]);
	I = I*4;
317
318
319
320
321
322
323
324
325
	if ((I==0)&&(State->Type==1))
		I = 2*4;
	if ((I==0)&&(State->Type==4))
		I = 16*4;
	if ((I==0)&&(State->Type==8))
		I = 256*4;
	
	State->HeaderSize+=I;
	
Matthias Clasen's avatar
Matthias Clasen committed
326
327
328
329
330
331
332
333
	if (State->HeaderSize < 0) {
		g_set_error (error,
			     GDK_PIXBUF_ERROR,
			     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
			     _("Invalid header in icon"));
		return;
	}

334
 	if (State->HeaderSize>State->BytesInHeaderBuf) {
335
336
	        guchar *tmp=g_try_realloc(State->HeaderBuf,State->HeaderSize);
		if (!tmp) {
337
338
339
340
341
342
			g_set_error (error,
				     GDK_PIXBUF_ERROR,
				     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
				     _("Not enough memory to load icon"));
			return;
		}
343
		State->HeaderBuf = tmp;
344
345
346
347
348
349
350
 		State->BytesInHeaderBuf = State->HeaderSize;
 	}
 	if (Bytes < State->HeaderSize)
 		return;

	if ((BIH[16] != 0) || (BIH[17] != 0) || (BIH[18] != 0)
	    || (BIH[19] != 0)) {
351
352
353
354
355
356
		/* FIXME: is this the correct message? */
                g_set_error (error,
                             GDK_PIXBUF_ERROR,
                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                             _("Compressed icons are not supported"));
		return;
357
358
	}

359
	/* Negative heights mean top-down pixel-order */
360
361
362
363
364
365
366
	if (State->Header.height < 0) {
		State->Header.height = -State->Header.height;
		State->Header.Negative = 1;
	}
	if (State->Header.width < 0) {
		State->Header.width = -State->Header.width;
	}
367
368
	g_assert (State->Header.width > 0);
	g_assert (State->Header.height > 0);
369

370
371
372
        if (State->Type == 32)
                State->LineWidth = State->Header.width * 4;
        else if (State->Type == 24)
373
		State->LineWidth = State->Header.width * 3;
374
        else if (State->Type == 16)
375
                State->LineWidth = State->Header.width * 2;
376
        else if (State->Type == 8)
377
		State->LineWidth = State->Header.width * 1;
378
        else if (State->Type == 4)
379
		State->LineWidth = (State->Header.width+1)/2;
380
        else if (State->Type == 1) {
381
382
383
		State->LineWidth = State->Header.width / 8;
		if ((State->Header.width & 7) != 0)
			State->LineWidth++;
384
385
386
387
388
389
        } else {
          g_set_error (error,
                       GDK_PIXBUF_ERROR,
                       GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                       _("Unsupported icon type"));
          return;
390
391
392
393
	}

	/* Pad to a 32 bit boundary */
	if (((State->LineWidth % 4) > 0))
394
		State->LineWidth = (State->LineWidth / 4) * 4 + 4;
395
396


397
398
399
400
401
402
403
	if (State->LineBuf == NULL) {
		State->LineBuf = g_try_malloc(State->LineWidth);
		if (!State->LineBuf) {
			g_set_error (error,
				     GDK_PIXBUF_ERROR,
				     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
				     _("Not enough memory to load icon"));
404
			return;
405
406
		}
	}
407
408
409
410
411

	g_assert(State->LineBuf != NULL);


	if (State->pixbuf == NULL) {
412
413
414
415
416
417
418
419
420
421
422
423
424
#if 1
		if (State->size_func) {
			gint width = State->Header.width;
			gint height = State->Header.height;

			(*State->size_func) (&width, &height, State->user_data);
			if (width == 0 || height == 0) {
				State->LineWidth = 0;
				return;
			}
		}
#endif

425
		State->pixbuf =
426
		    gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
427
428
				   State->Header.width,
				   State->Header.height);
429
430
431
432
433
434
435
		if (!State->pixbuf) {
			g_set_error (error,
				     GDK_PIXBUF_ERROR,
				     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
				     _("Not enough memory to load icon"));
			return;
		}
436
437
438
		if (State->cursor) {
			gchar hot[10];
			g_snprintf (hot, 10, "%d", State->x_hot);
439
			gdk_pixbuf_set_option (State->pixbuf, "x_hot", hot);
440
			g_snprintf (hot, 10, "%d", State->y_hot);
441
			gdk_pixbuf_set_option (State->pixbuf, "y_hot", hot);
442
		}
443
444
445
446

		if (State->prepared_func != NULL)
			/* Notify the client that we are ready to go */
			(*State->prepared_func) (State->pixbuf,
Havoc Pennington's avatar
Havoc Pennington committed
447
                                                 NULL,
448
449
450
451
452
453
454
455
456
457
458
459
						 State->user_data);

	}

}

/* 
 * func - called when we have pixmap created (but no image data)
 * user_data - passed as arg 1 to func
 * return context (opaque to user)
 */

460
static gpointer
461
462
463
gdk_pixbuf__ico_image_begin_load(GdkPixbufModuleSizeFunc size_func,
                                 GdkPixbufModulePreparedFunc prepared_func,
				 GdkPixbufModuleUpdatedFunc updated_func,
Havoc Pennington's avatar
Havoc Pennington committed
464
465
				 gpointer user_data,
                                 GError **error)
466
467
468
469
{
	struct ico_progressive_state *context;

	context = g_new0(struct ico_progressive_state, 1);
470
	context->size_func = size_func;
471
472
473
474
475
	context->prepared_func = prepared_func;
	context->updated_func = updated_func;
	context->user_data = user_data;

	context->HeaderSize = 54;
476
477
	context->HeaderBuf = g_try_malloc(14 + 40 + 4*256 + 512);
	if (!context->HeaderBuf) {
478
		g_free (context);
479
480
481
482
483
484
		g_set_error (error,
			     GDK_PIXBUF_ERROR,
			     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
			     _("Not enough memory to load ICO file"));
		return NULL;
	}
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
	/* 4*256 for the colormap */
	context->BytesInHeaderBuf = 14 + 40 + 4*256 + 512 ;
	context->HeaderDone = 0;

	context->LineWidth = 0;
	context->LineBuf = NULL;
	context->LineDone = 0;
	context->Lines = 0;

	context->Type = 0;

	memset(&context->Header, 0, sizeof(struct headerpair));


	context->pixbuf = NULL;


	return (gpointer) context;
}

/*
 * context - returned from image_begin_load
 *
 * free context, unref gdk_pixbuf
 */
510
511
512
static gboolean 
gdk_pixbuf__ico_image_stop_load(gpointer data,
				GError **error)
513
514
515
516
{
	struct ico_progressive_state *context =
	    (struct ico_progressive_state *) data;

517
518
519
        /* FIXME this thing needs to report errors if
         * we have unused image data
         */
520

521
	g_return_val_if_fail(context != NULL, TRUE);
522

523
	context_free (context);
524
        return TRUE;
525
526
}

527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
static void
OneLine32 (struct ico_progressive_state *context)
{
        gint X;
        guchar *Pixels;

        X = 0;
        if (context->Header.Negative == 0)
                Pixels = (context->pixbuf->pixels +
                          context->pixbuf->rowstride *
                          (context->Header.height - context->Lines - 1));
        else
                Pixels = (context->pixbuf->pixels +
                          context->pixbuf->rowstride *
                          context->Lines);
        while (X < context->Header.width) {
                Pixels[X * 4 + 0] = context->LineBuf[X * 4 + 2];
                Pixels[X * 4 + 1] = context->LineBuf[X * 4 + 1];
                Pixels[X * 4 + 2] = context->LineBuf[X * 4 + 0];
                Pixels[X * 4 + 3] = context->LineBuf[X * 4 + 3];
                X++;
        }
}
550
551
552
553
554
555
556
557

static void OneLine24(struct ico_progressive_state *context)
{
	gint X;
	guchar *Pixels;

	X = 0;
	if (context->Header.Negative == 0)
558
559
560
		Pixels = (context->pixbuf->pixels +
			  context->pixbuf->rowstride *
			  (context->Header.height - context->Lines - 1));
561
	else
562
563
564
		Pixels = (context->pixbuf->pixels +
			  context->pixbuf->rowstride *
			  context->Lines);
565
566
567
568
569
570
571
572
573
	while (X < context->Header.width) {
		Pixels[X * 4 + 0] = context->LineBuf[X * 3 + 2];
		Pixels[X * 4 + 1] = context->LineBuf[X * 3 + 1];
		Pixels[X * 4 + 2] = context->LineBuf[X * 3 + 0];
		X++;
	}

}

574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
static void
OneLine16 (struct ico_progressive_state *context)
{
        int i;
        guchar *pixels;
        guchar *src;

        if (context->Header.Negative == 0)
                pixels = (context->pixbuf->pixels +
                          context->pixbuf->rowstride * (context->Header.height - context->Lines - 1));
        else
                pixels = (context->pixbuf->pixels +
                          context->pixbuf->rowstride * context->Lines);

        src = context->LineBuf;

        for (i = 0; i < context->Header.width; i++) {
                int v, r, g, b;

                v = (int) src[0] | ((int) src[1] << 8);
                src += 2;

                /* Extract 5-bit RGB values */

                r = (v >> 10) & 0x1f;
                g = (v >> 5) & 0x1f;
                b = v & 0x1f;

                /* Fill the rightmost bits to form 8-bit values */

                *pixels++ = (r << 3) | (r >> 2);
                *pixels++ = (g << 3) | (g >> 2);
                *pixels++ = (b << 3) | (b >> 2);
                pixels++; /* skip alpha channel */
        }
}


612
613
614
615
616
617
618
static void OneLine8(struct ico_progressive_state *context)
{
	gint X;
	guchar *Pixels;

	X = 0;
	if (context->Header.Negative == 0)
619
620
621
		Pixels = (context->pixbuf->pixels +
			  context->pixbuf->rowstride *
			  (context->Header.height - context->Lines - 1));
622
	else
623
624
625
		Pixels = (context->pixbuf->pixels +
			  context->pixbuf->rowstride *
			  context->Lines);
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
	while (X < context->Header.width) {
		/* The joys of having a BGR byteorder */
		Pixels[X * 4 + 0] =
		    context->HeaderBuf[4 * context->LineBuf[X] + 42+context->DIBoffset];
		Pixels[X * 4 + 1] =
		    context->HeaderBuf[4 * context->LineBuf[X] + 41+context->DIBoffset];
		Pixels[X * 4 + 2] =
		    context->HeaderBuf[4 * context->LineBuf[X] + 40+context->DIBoffset];
		X++;
	}
}
static void OneLine4(struct ico_progressive_state *context)
{
	gint X;
	guchar *Pixels;

	X = 0;
	if (context->Header.Negative == 0)
644
645
646
		Pixels = (context->pixbuf->pixels +
			  context->pixbuf->rowstride *
			  (context->Header.height - context->Lines - 1));
647
	else
648
649
650
		Pixels = (context->pixbuf->pixels +
			  context->pixbuf->rowstride *
			  context->Lines);
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
	
	while (X < context->Header.width) {
		guchar Pix;
		
		Pix = context->LineBuf[X/2];

		Pixels[X * 4 + 0] =
		    context->HeaderBuf[4 * (Pix>>4) + 42+context->DIBoffset];
		Pixels[X * 4 + 1] =
		    context->HeaderBuf[4 * (Pix>>4) + 41+context->DIBoffset];
		Pixels[X * 4 + 2] =
		    context->HeaderBuf[4 * (Pix>>4) + 40+context->DIBoffset];
		X++;
		if (X<context->Header.width) { 
			/* Handle the other 4 bit pixel only when there is one */
			Pixels[X * 4 + 0] =
			    context->HeaderBuf[4 * (Pix&15) + 42+context->DIBoffset];
			Pixels[X * 4 + 1] =
			    context->HeaderBuf[4 * (Pix&15) + 41+context->DIBoffset];
			Pixels[X * 4 + 2] =
			    context->HeaderBuf[4 * (Pix&15) + 40+context->DIBoffset];
			X++;
		}
	}
	
}

static void OneLine1(struct ico_progressive_state *context)
{
	gint X;
	guchar *Pixels;

	X = 0;
	if (context->Header.Negative == 0)
685
686
687
		Pixels = (context->pixbuf->pixels +
			  context->pixbuf->rowstride *
			  (context->Header.height - context->Lines - 1));
688
	else
689
690
691
		Pixels = (context->pixbuf->pixels +
			  context->pixbuf->rowstride *
			  context->Lines);
692
693
694
695
696
697
	while (X < context->Header.width) {
		int Bit;

		Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
		Bit = Bit & 1;
		/* The joys of having a BGR byteorder */
698
699
700
		Pixels[X * 4 + 0] = Bit*255;
		Pixels[X * 4 + 1] = Bit*255;
		Pixels[X * 4 + 2] = Bit*255;
701
702
703
704
705
706
707
708
709
		X++;
	}
}

static void OneLineTransp(struct ico_progressive_state *context)
{
	gint X;
	guchar *Pixels;

710
711
712
713
	/* Ignore the XOR mask for XP style 32-bpp icons with alpha */ 
	if (context->Header.depth == 32)
		return;

714
715
	X = 0;
	if (context->Header.Negative == 0)
716
717
718
		Pixels = (context->pixbuf->pixels +
			  context->pixbuf->rowstride *
			  (2*context->Header.height - context->Lines - 1));
719
	else
720
721
722
		Pixels = (context->pixbuf->pixels +
			  context->pixbuf->rowstride *
			  (context->Lines-context->Header.height));
723
724
725
726
727
728
729
	while (X < context->Header.width) {
		int Bit;

		Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7));
		Bit = Bit & 1;
		/* The joys of having a BGR byteorder */
		Pixels[X * 4 + 3] = 255-Bit*255;
730
#if 0
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
		if (Bit){
		  Pixels[X*4+0] = 255;
		  Pixels[X*4+1] = 255;
		} else {
		  Pixels[X*4+0] = 0;
		  Pixels[X*4+1] = 0;
		}
#endif		
		X++;
	}
}


static void OneLine(struct ico_progressive_state *context)
{
	context->LineDone = 0;
	
	if (context->Lines >= context->Header.height*2) {
		return;
	}
		
	if (context->Lines <context->Header.height) {		
753
754
755
                if (context->Type == 32)
                        OneLine32 (context);
		else if (context->Type == 24)
756
			OneLine24(context);
757
758
759
                else if (context->Type == 16)
                        OneLine16 (context);
		else if (context->Type == 8)
760
			OneLine8(context);
761
		else if (context->Type == 4)
762
			OneLine4(context);
763
		else if (context->Type == 1)
764
			OneLine1(context);
765
766
		else 
			g_assert_not_reached ();
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
	} else
		OneLineTransp(context);
	
	context->Lines++;
	if (context->Lines>=context->Header.height) {
	 	context->Type = 1;
		context->LineWidth = context->Header.width / 8;
		if ((context->Header.width & 7) != 0)
			context->LineWidth++;
		/* Pad to a 32 bit boundary */
		if (((context->LineWidth % 4) > 0))
			context->LineWidth = (context->LineWidth / 4) * 4 + 4;
			
	}
	  

	if (context->updated_func != NULL) {
		(*context->updated_func) (context->pixbuf,
					  0,
786
					  context->Lines % context->Header.height,
787
					  context->Header.width,
788
					  1,
789
					  context->user_data);
790
791
792
793
794
795
796
797
798
799
800

	}
}

/*
 * context - from image_begin_load
 * buf - new image data
 * size - length of new image data
 *
 * append image data onto inrecrementally built output image
 */
801
802
803
804
static gboolean
gdk_pixbuf__ico_image_load_increment(gpointer data,
                                     const guchar * buf,
                                     guint size,
Havoc Pennington's avatar
Havoc Pennington committed
805
                                     GError **error)
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
{
	struct ico_progressive_state *context =
	    (struct ico_progressive_state *) data;

	gint BytesToCopy;

	while (size > 0) {
		g_assert(context->LineDone >= 0);
		if (context->HeaderDone < context->HeaderSize) {	/* We still 
									   have headerbytes to do */
			BytesToCopy =
			    context->HeaderSize - context->HeaderDone;
			if (BytesToCopy > size)
				BytesToCopy = size;

821
			memmove(context->HeaderBuf + context->HeaderDone,
822
823
824
825
826
			       buf, BytesToCopy);

			size -= BytesToCopy;
			buf += BytesToCopy;
			context->HeaderDone += BytesToCopy;
827
828
		} 
		else {
829
830
831
832
833
834
			BytesToCopy =
			    context->LineWidth - context->LineDone;
			if (BytesToCopy > size)
				BytesToCopy = size;

			if (BytesToCopy > 0) {
835
				memmove(context->LineBuf +
836
837
838
839
840
841
842
843
844
845
846
847
848
849
				       context->LineDone, buf,
				       BytesToCopy);

				size -= BytesToCopy;
				buf += BytesToCopy;
				context->LineDone += BytesToCopy;
			}
			if ((context->LineDone >= context->LineWidth) &&
			    (context->LineWidth > 0))
				OneLine(context);


		}

850
		if (context->HeaderDone >= 6 && context->pixbuf == NULL) {
851
			GError *decode_err = NULL;
852
			DecodeHeader(context->HeaderBuf,
853
				     context->HeaderDone, context, &decode_err);
854
855
856
			if (context->LineBuf != NULL && context->LineWidth == 0)
				return TRUE;

857
858
859
860
861
			if (decode_err) {
				g_propagate_error (error, decode_err);
				return FALSE;
			}
		}
862
863
864
865
	}

	return TRUE;
}
866

867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
/* saving ICOs */ 

static gint
write8 (FILE     *f,
	guint8   *data,
	gint      count)
{
  gint bytes;
  gint written;

  written = 0;
  while (count > 0)
    {
      bytes = fwrite ((char*) data, sizeof (char), count, f);
      if (bytes <= 0)
        break;
      count -= bytes;
      data += bytes;
      written += bytes;
    }

  return written;
}

static gint
write16 (FILE     *f,
	 guint16  *data,
	 gint      count)
{
  gint i;

  for (i = 0; i < count; i++)
	  data[i] = GUINT16_TO_LE (data[i]);

  return write8 (f, (guint8*) data, count * 2);
}

static gint
write32 (FILE     *f,
	 guint32  *data,
	 gint      count)
{
  gint i;

  for (i = 0; i < count; i++)
	  data[i] = GUINT32_TO_LE (data[i]);
  
  return write8 (f, (guint8*) data, count * 4);
}

typedef struct _IconEntry IconEntry;
struct _IconEntry {
	gint width;
	gint height;
	gint depth;
	gint hot_x;
	gint hot_y;

	guint8 n_colors;
	guint32 *colors;
	guint xor_rowstride;
	guint8 *xor;
	guint and_rowstride;
	guint8 *and;
};

static gboolean
fill_entry (IconEntry *icon, 
	    GdkPixbuf *pixbuf, 
	    gint       hot_x, 
	    gint       hot_y, 
	    GError   **error) 
 {
	guchar *p, *pixels, *and, *xor;
	gint n_channels, v, x, y;

	if (icon->width > 255 || icon->height > 255) {
		g_set_error (error,
			     GDK_PIXBUF_ERROR,
			     GDK_PIXBUF_ERROR_BAD_OPTION,
			     _("Image too large to be saved as ICO"));
		return FALSE;
	} 
	
	if (hot_x > -1 && hot_y > -1) {
		icon->hot_x = hot_x;
953
		icon->hot_y = hot_y;
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
		if (icon->hot_x >= icon->width || icon->hot_y >= icon->height) {
			g_set_error (error,
				     GDK_PIXBUF_ERROR,
				     GDK_PIXBUF_ERROR_BAD_OPTION,
				     _("Cursor hotspot outside image"));
			return FALSE;
		}
	}
	else {
		icon->hot_x = -1;
		icon->hot_y = -1;
	}
	
	switch (icon->depth) {
	case 32:
		icon->xor_rowstride = icon->width * 4;
		break;
	case 24:
		icon->xor_rowstride = icon->width * 3;
		break;
	case 16:
		icon->xor_rowstride = icon->width * 2;
		break;
	default:
		g_set_error (error,
			     GDK_PIXBUF_ERROR,
			     GDK_PIXBUF_ERROR_BAD_OPTION,
			     _("Unsupported depth for ICO file: %d"), icon->depth);
		return FALSE;
	}

	if ((icon->xor_rowstride % 4) != 0) 		
		icon->xor_rowstride = 4 * ((icon->xor_rowstride / 4) + 1);
	icon->xor = g_new0 (guchar, icon->xor_rowstride * icon->height);

	icon->and_rowstride = icon->width / 8;
	if ((icon->and_rowstride % 4) != 0) 		
		icon->and_rowstride = 4 * ((icon->and_rowstride / 4) + 1);
	icon->and = g_new0 (guchar, icon->and_rowstride * icon->height);

	pixels = gdk_pixbuf_get_pixels (pixbuf);
	n_channels = gdk_pixbuf_get_n_channels (pixbuf);
	for (y = 0; y < icon->height; y++) {
		p = pixels + gdk_pixbuf_get_rowstride (pixbuf) * (icon->height - 1 - y);
		and = icon->and + icon->and_rowstride * y;
		xor = icon->xor + icon->xor_rowstride * y;
		for (x = 0; x < icon->width; x++) {
			switch (icon->depth) {
			case 32:
				xor[0] = p[2];
				xor[1] = p[1];
				xor[2] = p[0];
				xor[3] = 0xff;
				if (n_channels == 4) {
					xor[3] = p[3];
					if (p[3] < 0x80)
						*and |= 1 << (7 - x % 8);
				}
				xor += 4;
				break;
			case 24:
				xor[0] = p[2];
				xor[1] = p[1];
				xor[2] = p[0];
				if (n_channels == 4 && p[3] < 0x80)
					*and |= 1 << (7 - x % 8);
				xor += 3;
				break;
			case 16:
				v = ((p[0] >> 3) << 10) | ((p[1] >> 3) << 5) | (p[2] >> 3);
				xor[0] = v & 0xff;
				xor[1] = v >> 8;
				if (n_channels == 4 && p[3] < 0x80)
					*and |= 1 << (7 - x % 8);
				xor += 2;
				break;
			}
			
			p += n_channels;
			if (x % 8 == 7) 
				and++;
		}
	}

	return TRUE;
}

static void
free_entry (IconEntry *icon)
{
	g_free (icon->colors);
	g_free (icon->and);
	g_free (icon->xor);
	g_free (icon);
}

static void
write_icon (FILE *f, GSList *entries)
{
	IconEntry *icon;
	GSList *entry;
	guint8 bytes[4];
	guint16 words[4];
	guint32 dwords[6];
	gint type;
	gint n_entries;
	gint offset;
	gint size;

	if (((IconEntry *)entries->data)->hot_x > -1)
		type = 2;
	else 
		type = 1;
	n_entries = g_slist_length (entries);

	/* header */
	words[0] = 0;
	words[1] = type;
	words[2] = n_entries;
	write16 (f, words, 3);
	
	offset = 6 + 16 * n_entries;

	for (entry = entries; entry; entry = entry->next) {
		icon = (IconEntry *)entry->data;
		size = 40 + icon->height * (icon->and_rowstride + icon->xor_rowstride);
		
		/* directory entry */
		bytes[0] = icon->width;
		bytes[1] = icon->height;
		bytes[2] = icon->n_colors;
		bytes[3] = 0;
		write8 (f, bytes, 4);
		if (type == 1) {
			words[0] = 1;
			words[1] = icon->depth;
		}
		else {
			words[0] = icon->hot_x;
			words[1] = icon->hot_y;
		}
		write16 (f, words, 2);
		dwords[0] = size;
		dwords[1] = offset;
		write32 (f, dwords, 2);

		offset += size;
	}

	for (entry = entries; entry; entry = entry->next) {
		icon = (IconEntry *)entry->data;

		/* bitmap header */
		dwords[0] = 40;
		dwords[1] = icon->width;
		dwords[2] = icon->height * 2;
		write32 (f, dwords, 3);
		words[0] = 1;
		words[1] = icon->depth;
		write16 (f, words, 2);
		dwords[0] = 0;
		dwords[1] = 0;
		dwords[2] = 0;
		dwords[3] = 0;
		dwords[4] = 0;
		dwords[5] = 0;
		write32 (f, dwords, 6);

		/* image data */
		write8 (f, icon->xor, icon->xor_rowstride * icon->height);
		write8 (f, icon->and, icon->and_rowstride * icon->height);
	}
}

static gboolean
gdk_pixbuf__ico_image_save (FILE          *f, 
                            GdkPixbuf     *pixbuf, 
                            gchar        **keys,
                            gchar        **values,
                            GError       **error)
{
	gint hot_x, hot_y;
	IconEntry *icon;
	GSList *entries = NULL;

	/* support only single-image ICOs for now */
	icon = g_new0 (IconEntry, 1);
	icon->width = gdk_pixbuf_get_width (pixbuf);
	icon->height = gdk_pixbuf_get_height (pixbuf);
	icon->depth = gdk_pixbuf_get_has_alpha (pixbuf) ? 32 : 24;
	hot_x = -1;
	hot_y = -1;
	
	/* parse options */
	if (keys && *keys) {
		gchar **kiter;
		gchar **viter;
		
		for (kiter = keys, viter = values; *kiter && *viter; kiter++, viter++) {
			char *endptr;
			if (strcmp (*kiter, "depth") == 0) {
				sscanf (*viter, "%d", &icon->depth);
			}
			else if (strcmp (*kiter, "x_hot") == 0) {
				hot_x = strtol (*viter, &endptr, 10);
			}
			else if (strcmp (*kiter, "y_hot") == 0) {
				hot_y = strtol (*viter, &endptr, 10);
			}

		}
	}

	if (!fill_entry (icon, pixbuf, hot_x, hot_y, error)) {
		free_entry (icon);
		return FALSE;
	}

	entries = g_slist_append (entries, icon); 
	write_icon (f, entries);

	g_slist_foreach (entries, (GFunc)free_entry, NULL);
	g_slist_free (entries);

	return TRUE;
}

1181
void
1182
MODULE_ENTRY (ico, fill_vtable) (GdkPixbufModule *module)
1183
{
1184
1185
1186
	module->begin_load = gdk_pixbuf__ico_image_begin_load;
	module->stop_load = gdk_pixbuf__ico_image_stop_load;
	module->load_increment = gdk_pixbuf__ico_image_load_increment;
1187
        module->save = gdk_pixbuf__ico_image_save;
1188
}
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212

void
MODULE_ENTRY (ico, fill_info) (GdkPixbufFormat *info)
{
	static GdkPixbufModulePattern signature[] = {
		{ "  \x1   ", "zz znz", 100 }, 
		{ "  \x2   ", "zz znz", 100 },
		{ NULL, NULL, 0 }
	};
	static gchar * mime_types[] = {
		"image/x-icon",
		NULL
	};
	static gchar * extensions[] = {
		"ico",
		"cur",
		NULL
	};

	info->name = "ico";
	info->signature = signature;
	info->description = N_("The ICO image format");
	info->mime_types = mime_types;
	info->extensions = extensions;
1213
	info->flags = GDK_PIXBUF_FORMAT_WRITABLE | GDK_PIXBUF_FORMAT_THREADSAFE;
1214
	info->license = "LGPL";
1215
1216
1217
1218
1219
}