]> git.imager.perl.org - imager.git/blob - ICO/msicon.c
- correctly blend a rotated (or matrix_transformed()) image when
[imager.git] / ICO / msicon.c
1 #include "msicon.h"
2 #include <string.h>
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <stdarg.h>
6 #include <assert.h>
7
8 static
9 int read_packed(io_glue *ig, const char *format, ...);
10 static int 
11 read_palette(ico_reader_t *file, ico_image_t *image, int *error);
12 static int 
13 read_32bit_data(ico_reader_t *file, ico_image_t *image, int *error);
14 static int 
15 read_8bit_data(ico_reader_t *file, ico_image_t *image, int *error);
16 static int 
17 read_4bit_data(ico_reader_t *file, ico_image_t *image, int *error);
18 static int 
19 read_1bit_data(ico_reader_t *file, ico_image_t *image, int *error);
20 static int 
21 read_mask(ico_reader_t *file, ico_image_t *image, int *error);
22 static int
23 ico_write_validate(ico_image_t const *images, int image_count, int *error);
24 static int
25 ico_image_size(ico_image_t const *image, int *bits, int *colors);
26 static int
27 write_packed(i_io_glue_t *ig, char const *format, ...);
28 static int
29 write_palette(i_io_glue_t *ig, ico_image_t const *image, int *error);
30 static int
31 write_32_bit(i_io_glue_t *ig, ico_image_t const *image, int *error);
32 static int
33 write_8_bit(i_io_glue_t *ig, ico_image_t const *image, int *error);
34 static int
35 write_4_bit(i_io_glue_t *ig, ico_image_t const *image, int *error);
36 static int
37 write_1_bit(i_io_glue_t *ig, ico_image_t const *image, int *error);
38 static int
39 write_mask(i_io_glue_t *ig, ico_image_t const *image, int *error);
40
41 typedef struct {
42   int width;
43   int height;
44   long offset;
45   long size;
46   int hotspot_x, hotspot_y;
47 } ico_reader_image_entry;
48
49 /* this was previously declared, now define it */
50 struct ico_reader_tag {
51   /* the file we're dealing with */
52   i_io_glue_t *ig;
53
54   /* number of images in the file */
55   int count;
56
57   /* type of resource - 1=icon, 2=cursor */
58   int type;
59
60   /* image information from the header */
61   ico_reader_image_entry *images;
62 };
63
64 /*
65 =head1 NAME 
66
67 msicon.c - functions for working with .ICO files.
68
69 =head1 SYNOPSIS
70
71   // reading
72   int error;
73   ico_reader_t *file = ico_reader_open(ig, &error);
74   if (!file) {
75     char buffer[100];
76     ico_error_message(error, buffer, sizeof(buffer));
77     fputs(buffer, stderr);
78     exit(1);
79   }
80   int count = ico_image_count(file);
81   for (i = 0; i < count; ++i) {
82     ico_image_t *im = ico_image_read(file, index);
83     printf("%d x %d image %d\n", im->width, im->height, 
84            im->direct ? "direct" : "paletted");
85     ico_image_release(im);
86   }
87   ico_reader_close(file);
88
89 =head1 DESCRIPTION
90
91 This is intended as a general interface to reading MS Icon files, and
92 is written to be independent of Imager, even though it is part of
93 Imager.  You just need to supply something that acts like Imager's
94 io_glue.
95
96 It relies on icon images being generally small, and reads the entire
97 image into memory when reading.
98
99 =head1 READING ICON FILES
100
101 =over
102
103 =item ico_reader_open(ig, &error)
104
105 Parameters:
106
107 =over
108
109 =item *
110
111 io_glue *ig - an Imager IO object.  This must be seekable.
112
113 =item *
114
115 int *error - pointer to an integer which an error code will be
116 returned in on failure.
117
118 =back
119
120 =cut
121 */
122
123 ico_reader_t *
124 ico_reader_open(i_io_glue_t *ig, int *error) {
125   long res1, type, count;
126   ico_reader_t *file = NULL;
127   int i;
128
129   if (!read_packed(ig, "www", &res1, &type, &count)) {
130     *error = ICOERR_Short_File;
131     return NULL;
132   }
133   if (res1 != 0 || (type != 1 && type != 2) || count == 0) {
134     *error = ICOERR_Invalid_File;
135     return NULL;
136   }
137
138   file = malloc(sizeof(ico_reader_t));
139   if (!file) {
140     *error = ICOERR_Out_Of_Memory;
141     return NULL;
142   }
143   file->count = count;
144   file->type = type;
145   file->ig = ig;
146   file->images = malloc(sizeof(ico_reader_image_entry) * count);
147   if (file->images == NULL) {
148     *error = ICOERR_Out_Of_Memory;
149     free(file);
150     return NULL;
151   }
152
153   for (i = 0; i < count; ++i) {
154     long width, height, bytes_in_res, image_offset;
155
156     ico_reader_image_entry *image = file->images + i;
157     if (type == ICON_ICON) {
158       if (!read_packed(ig, "bb xxxxxx dd", &width, &height, &bytes_in_res, 
159                        &image_offset)) {
160         free(file->images);
161         free(file);
162         *error = ICOERR_Short_File;
163         return NULL;
164       }
165       image->hotspot_x = image->hotspot_y = 0;
166     }
167     else {
168       long hotspot_x, hotspot_y;
169
170       if (!read_packed(ig, "bb xx ww dd", &width, &height, 
171                        &hotspot_x, &hotspot_y, &bytes_in_res, 
172                        &image_offset)) {
173         free(file->images);
174         free(file);
175         *error = ICOERR_Short_File;
176         return NULL;
177       }
178       image->hotspot_x = hotspot_x;
179       image->hotspot_y = hotspot_y;
180     }
181
182     image->width = width;
183     image->height = height;
184     image->offset = image_offset;
185     image->size = bytes_in_res;
186   }
187
188   return file;
189 }
190
191 /*
192 =item ico_image_count
193
194   // number of images in the file
195   count = ico_image_count(file);
196
197 =cut
198 */
199
200 int
201 ico_image_count(ico_reader_t *file) {
202   return file->count;
203 }
204
205 /*
206 =item ico_type
207
208   // type of file - ICON_ICON for icon, ICON_CURSOR for cursor
209   type = ico_type(file);
210
211 =cut
212 */
213
214 int
215 ico_type(ico_reader_t *file) {
216   return file->type;
217 }
218
219 /*
220 =item ico_image_read
221
222 Read an image from the file given it's index.
223
224 =cut
225 */
226
227 ico_image_t *
228 ico_image_read(ico_reader_t *file, int index, int *error) {
229   io_glue *ig = file->ig;
230   ico_reader_image_entry *im;
231   long bi_size, width, height, planes, bit_count;
232   ico_image_t *result;
233
234   if (index < 0 || index >= file->count) {
235     *error = ICOERR_Bad_Image_Index;
236     return NULL;
237   }
238
239   im = file->images + index;
240   if (i_io_seek(ig, im->offset, SEEK_SET) != im->offset) {
241     *error = ICOERR_File_Error;
242     return NULL;
243   }
244
245   if (!read_packed(ig, "dddww xxxx xxxx xxxx xxxx xxxx xxxx", &bi_size, 
246                    &width, &height, &planes, &bit_count)) {
247     *error = ICOERR_Short_File;
248     return NULL;
249   }
250
251   /* the bitmapinfoheader height includes the height of 
252      the and and xor masks */
253   if (bi_size != 40 || width != im->width || height != im->height * 2
254       || planes != 1) { /* don't know how to handle planes != 1 */
255     *error = ICOERR_Invalid_File;
256     return NULL;
257   }
258
259   if (bit_count != 1 && bit_count != 4 && bit_count != 8 && bit_count != 32) {
260     *error = ICOERR_Unknown_Bits;
261     return 0;
262   }
263
264   result = malloc(sizeof(ico_image_t));
265   if (!result) {
266     *error = ICOERR_Out_Of_Memory;
267     return NULL;
268   }
269   result->width = width;
270   result->height = im->height;
271   result->direct = bit_count > 8;
272   result->bit_count = bit_count;
273   result->palette = NULL;
274   result->image_data = NULL;
275   result->mask_data = NULL;
276   result->hotspot_x = im->hotspot_x;
277   result->hotspot_y = im->hotspot_y;
278     
279   if (bit_count == 32) {
280     result->palette_size = 0;
281
282     result->image_data = malloc(result->width * result->height * sizeof(ico_color_t));
283     if (!result->image_data) {
284       free(result);
285       *error = ICOERR_Out_Of_Memory;
286       return NULL;
287     }
288     if (!read_32bit_data(file, result, error)) {
289       free(result->image_data);
290       free(result);
291       return NULL;
292     }
293   }
294   else {
295     int read_result;
296
297     result->palette_size = 1 << bit_count;
298     result->palette = malloc(sizeof(ico_color_t) * result->palette_size);
299     if (!result->palette) {
300       free(result);
301       *error = ICOERR_Out_Of_Memory;
302       return NULL;
303     }
304
305     result->image_data = malloc(result->width * result->height);
306     if (!result->image_data) {
307       *error = ICOERR_Out_Of_Memory;
308       free(result->palette);
309       free(result);
310       return 0;
311     }      
312     
313     if (!read_palette(file, result, error)) {
314       free(result->palette);
315       free(result->image_data);
316       free(result);
317       return 0;
318     }
319
320     switch (bit_count) {
321     case 1:
322       read_result = read_1bit_data(file, result, error);
323       break;
324
325     case 4:
326       read_result = read_4bit_data(file, result, error);
327       break;
328       
329     case 8:
330       read_result = read_8bit_data(file, result, error);
331       break;
332
333     default:
334       assert(0); /* this can't happen in theory */
335       read_result = 0;
336       break;
337     }
338
339     if (!read_result) {
340       free(result->palette);
341       free(result->image_data);
342       free(result);
343       return 0;
344     }
345   }
346
347   result->mask_data = malloc(result->width * result->height);
348   if (!result->mask_data) {
349     *error = ICOERR_Out_Of_Memory;
350     free(result->palette);
351     free(result->image_data);
352     free(result);
353     return 0;
354   }
355
356   if (!read_mask(file, result, error)) {
357     free(result->mask_data);
358     free(result->palette);
359     free(result->image_data);
360     free(result);
361     return 0;
362   }
363
364   return result;
365 }
366
367 /*
368 =item ico_image_release
369
370 Release an image structure returned by ico_image_read.
371
372 =cut
373 */
374
375 void
376 ico_image_release(ico_image_t *image) {
377   free(image->mask_data);
378   free(image->palette);
379   free(image->image_data);
380   free(image);
381 }
382
383 /*
384 =item ico_reader_close
385
386 Releases the read file structure.
387
388 =cut
389 */
390
391 void
392 ico_reader_close(ico_reader_t *file) {
393   i_io_close(file->ig);
394   free(file->images);
395   free(file);
396 }
397
398 /*
399 =back
400
401 =head1 WRITING ICON FILES
402
403 =over
404
405 =item ico_write(ig, images, image_count, type, &error)
406
407 Parameters:
408
409 =over
410
411 =item *
412
413 io_glue *ig - an Imager IO object.  This only needs to implement
414 writing for ico_write()
415
416 =item *
417
418 ico_image_t *images - array of images to be written.
419
420 =item *
421
422 int image_count - number of images
423
424 =item *
425
426 int type - must be ICON_ICON or ICON_CURSOR
427
428 =item *
429
430 int *error - set to an error code on failure.
431
432 =back
433
434 Returns non-zero on success.
435
436 =cut
437 */
438
439 int
440 ico_write(i_io_glue_t *ig, ico_image_t const *images, int image_count,
441           int type, int *error) {
442   int i;
443   int start_offset = 6 + 16 * image_count;
444   int current_offset = start_offset;
445
446   if (type != ICON_ICON && type != ICON_CURSOR) {
447     *error = ICOERR_Bad_File_Type;
448     return 0;
449   }
450
451   /* validate the images */
452   if (!ico_write_validate(images, image_count, error))
453     return 0;
454
455   /* write the header */
456   if (!write_packed(ig, "www", 0, type, image_count)) {
457     *error = ICOERR_Write_Failure;
458     return 0;
459   }
460
461   /* work out the offsets of each image */
462   for (i = 0; i < image_count; ++i) {
463     ico_image_t const *image = images + i;
464     int bits, colors;
465     int size = ico_image_size(image, &bits, &colors);
466
467     if (type == ICON_ICON) {
468       if (!write_packed(ig, "bbbbwwdd", image->width, image->height,
469                         colors, 0, 1, bits, (unsigned long)size, 
470                         (unsigned long)current_offset)) {
471         *error = ICOERR_Write_Failure;
472         return 0;
473       }
474     }
475     else {
476       int hotspot_x = image->hotspot_x;
477       int hotspot_y = image->hotspot_y;
478
479       if (hotspot_x < 0)
480         hotspot_x = 0;
481       else if (hotspot_x >= image->width)
482         hotspot_x = image->width - 1;
483       if (hotspot_y < 0)
484         hotspot_y = 0;
485       else if (hotspot_y >= image->height)
486         hotspot_y = image->height - 1;
487
488       if (!write_packed(ig, "bbbbwwdd", image->width, image->height,
489                         colors, 0, hotspot_x, hotspot_y, (unsigned long)size, 
490                         (unsigned long)current_offset)) {
491         *error = ICOERR_Write_Failure;
492         return 0;
493       }
494     }
495     current_offset += size;
496   }
497   
498   /* write out each image */
499   for (i = 0; i < image_count; ++i) {
500     ico_image_t const *image = images + i;
501
502     if (image->direct) {
503       if (!write_32_bit(ig, image, error))
504         return 0;
505     }
506     else {
507       if (image->palette_size <= 2) {
508         if (!write_1_bit(ig, image, error))
509           return 0;
510       }
511       else if (image->palette_size <= 16) {
512         if (!write_4_bit(ig, image, error))
513           return 0;
514       }
515       else {
516         if (!write_8_bit(ig, image, error))
517           return 0;
518       }
519     }
520     if (!write_mask(ig, image, error))
521       return 0;
522   }
523
524   return 1;
525 }
526
527 /*
528 =back
529
530 =head1 ERROR MESSAGES
531
532 =over
533
534 =item ico_error_message
535
536 Converts an error code into an error message.
537
538 =cut
539 */
540
541 size_t
542 ico_error_message(int error, char *buffer, size_t buffer_size) {
543   char const *msg;
544   size_t size;
545
546   switch (error) {
547   case ICOERR_Short_File:
548     msg = "Short read";
549     break;
550
551   case ICOERR_File_Error:
552     msg = "I/O error";
553     break;
554
555   case ICOERR_Write_Failure:
556     msg = "Write failure";
557     break;
558
559   case ICOERR_Invalid_File:
560     msg = "Not an icon file";
561     break;
562
563   case ICOERR_Unknown_Bits:
564     msg = "Unknown value for bits/pixel";
565     break;
566
567   case ICOERR_Bad_Image_Index:
568     msg = "Image index out of range";
569     break;
570
571   case ICOERR_Bad_File_Type:
572     msg = "Bad file type parameter";
573     break;
574
575   case ICOERR_Invalid_Width:
576     msg = "Invalid image width";
577     break;
578
579   case ICOERR_Invalid_Height:
580     msg = "Invalid image height";
581     break;
582     
583   case ICOERR_Invalid_Palette:
584     msg = "Invalid Palette";
585     break;
586
587   case ICOERR_No_Data:
588     msg = "No image data in image supplied to ico_write";
589     break;
590
591   case ICOERR_Out_Of_Memory:
592     msg = "Out of memory";
593     break;
594
595   default:
596     msg = "Unknown error code";
597     break;
598   }
599
600   size = strlen(msg) + 1;
601   if (size > buffer_size)
602     size = buffer_size;
603   memcpy(buffer, msg, size);
604   buffer[size-1] = '\0';
605
606   return size;
607 }
608
609 /*
610 =back
611
612 =head1 PRIVATE FUNCTIONS
613
614 =over
615
616 =item read_packed
617
618 Reads packed data from a stream, unpacking it.
619
620 =cut
621 */
622
623 static
624 int read_packed(io_glue *ig, const char *format, ...) {
625   unsigned char buffer[100];
626   va_list ap;
627   long *p;
628   int size;
629   const char *formatp;
630   unsigned char *bufp;
631
632   /* read efficiently, work out the size of the buffer */
633   size = 0;
634   formatp = format;
635   while (*formatp) {
636     switch (*formatp++) {
637     case 'b': 
638     case 'x': size += 1; break;
639     case 'w': size += 2; break;
640     case 'd': size += 4; break;
641     case ' ': break; /* space to separate components */
642     default:
643       fprintf(stderr, "invalid unpack char in %s\n", format);
644       exit(1);
645     }
646   }
647
648   if (size > sizeof(buffer)) {
649     /* catch if we need a bigger buffer, but 100 is plenty */
650     fprintf(stderr, "format %s too long for buffer\n", format);
651     exit(1);
652   }
653
654   if (i_io_read(ig, buffer, size) != size) {
655     return 0;
656   }
657
658   va_start(ap, format);
659
660   bufp = buffer;
661   while (*format) {
662
663     switch (*format) {
664     case 'b':
665       p = va_arg(ap, long *);
666       *p = *bufp++;
667       break;
668
669     case 'w':
670       p = va_arg(ap, long *);
671       *p = bufp[0] + (bufp[1] << 8);
672       bufp += 2;
673       break;
674
675     case 'd':
676       p = va_arg(ap, long *);
677       *p = bufp[0] + (bufp[1] << 8) + (bufp[2] << 16) + (bufp[3] << 24);
678       bufp += 4;
679       break;
680
681     case 'x':
682       ++bufp; /* skip a byte */
683       break;
684
685     case ' ':
686       /* nothing to do */
687       break;
688     }
689     ++format;
690   }
691   return 1;
692 }
693
694 /*
695 =item read_palette
696
697 Reads the palette data for an icon image.
698
699 =cut
700 */
701
702 static
703 int
704 read_palette(ico_reader_t *file, ico_image_t *image, int *error) {
705   int palette_bytes = image->palette_size * 4;
706   unsigned char *read_buffer = malloc(palette_bytes);
707   unsigned char *inp;
708   ico_color_t *outp;
709   int i;
710
711   if (!read_buffer) {
712     *error = ICOERR_Out_Of_Memory;
713     return 0;
714   }
715
716   if (i_io_read(file->ig, read_buffer, palette_bytes) != palette_bytes) {
717     *error = ICOERR_Short_File;
718     free(read_buffer);
719     return 0;
720   }
721
722   inp = read_buffer;
723   outp = image->palette;
724   for (i = 0; i < image->palette_size; ++i) {
725     outp->b = *inp++;
726     outp->g = *inp++;
727     outp->r = *inp++;
728     outp->a = 255;
729     ++inp;
730     ++outp;
731   }
732   free(read_buffer);
733
734   return 1;
735 }
736
737 /*
738 =item read_32bit_data
739
740 Reads 32 bit image data.
741
742 =cut
743 */
744
745 static
746 int
747 read_32bit_data(ico_reader_t *file, ico_image_t *image, int *error) {
748   int line_bytes = image->width * 4;
749   unsigned char *buffer = malloc(line_bytes);
750   int y;
751   int x;
752   unsigned char *inp;
753   ico_color_t *outp;
754
755   if (!buffer) {
756     *error = ICOERR_Out_Of_Memory;
757     return 0;
758   }
759
760   for (y = image->height - 1; y >= 0; --y) {
761     if (i_io_read(file->ig, buffer, line_bytes) != line_bytes) {
762       free(buffer);
763       *error = ICOERR_Short_File;
764       return 0;
765     }
766     outp = image->image_data;
767     outp += y * image->width;
768     inp = buffer;
769     for (x = 0; x < image->width; ++x) {
770       outp->b = inp[0];
771       outp->g = inp[1];
772       outp->r = inp[2];
773       outp->a = inp[3];
774       ++outp;
775       inp += 4;
776     }
777   }
778   free(buffer);
779
780   return 1;
781 }
782
783 /*
784 =item read_8bit_data
785
786 Reads 8 bit image data.
787
788 =cut
789 */
790
791 static
792 int
793 read_8bit_data(ico_reader_t *file, ico_image_t *image, int *error) {
794   int line_bytes = (image->width + 3) / 4 * 4;
795   unsigned char *buffer = malloc(line_bytes);
796   int y;
797   int x;
798   unsigned char *inp, *outp;
799
800   if (!buffer) {
801     *error = ICOERR_Out_Of_Memory;
802     return 0;
803   }
804
805   for (y = image->height - 1; y >= 0; --y) {
806     outp = image->image_data;
807     outp += y * image->width;
808     if (i_io_read(file->ig, buffer, line_bytes) != line_bytes) {
809       free(buffer);
810       *error = ICOERR_Short_File;
811       return 0;
812     }
813     for (x = 0, inp = buffer; x < image->width; ++x) {
814       *outp++ = *inp++;
815     }
816   }
817   free(buffer);
818
819   return 1;
820 }
821
822 /*
823 =item read_4bit_data
824
825 Reads 4 bit image data.
826
827 =cut
828 */
829
830 static
831 int
832 read_4bit_data(ico_reader_t *file, ico_image_t *image, int *error) {
833   /* 2 pixels per byte, rounded up to the nearest dword */
834   int line_bytes = ((image->width + 1) / 2 + 3) / 4 * 4;
835   unsigned char *read_buffer = malloc(line_bytes);
836   int y;
837   int x;
838   unsigned char *inp, *outp;
839
840   if (!read_buffer) {
841     *error = ICOERR_Out_Of_Memory;
842     return 0;
843   }
844
845   for (y = image->height - 1; y >= 0; --y) {
846     if (i_io_read(file->ig, read_buffer, line_bytes) != line_bytes) {
847       free(read_buffer);
848       *error = ICOERR_Short_File;
849       return 0;
850     }
851     
852     outp = image->image_data;
853     outp += y * image->width;
854     inp = read_buffer;
855     for (x = 0; x < image->width; ++x) {
856       /* yes, this is kind of ugly */
857       if (x & 1) {
858         *outp++ = *inp++ & 0x0F;
859       }
860       else {
861         *outp++ = *inp >> 4;
862       }
863     }
864   }
865   free(read_buffer);
866
867   return 1;
868 }
869
870 /*
871 =item read_1bit_data
872
873 Reads 1 bit image data.
874
875 =cut
876 */
877
878 static
879 int
880 read_1bit_data(ico_reader_t *file, ico_image_t *image, int *error) {
881   /* 8 pixels per byte, rounded up to the nearest dword */
882   int line_bytes = ((image->width + 7) / 8 + 3) / 4 * 4;
883   unsigned char *read_buffer = malloc(line_bytes);
884   int y;
885   int x;
886   unsigned char *inp, *outp;
887
888   if (!read_buffer) {
889     *error = ICOERR_Out_Of_Memory;
890     return 0;
891   }
892
893   for (y = image->height - 1; y >= 0; --y) {
894     if (i_io_read(file->ig, read_buffer, line_bytes) != line_bytes) {
895       free(read_buffer);
896       *error = ICOERR_Short_File;
897       return 0;
898     }
899     
900     outp = image->image_data;
901     outp += y * image->width;
902     inp = read_buffer;
903     for (x = 0; x < image->width; ++x) {
904       *outp++ = (*inp >> (7 - (x & 7))) & 1;
905       if ((x & 7) == 7)
906         ++inp;
907     }
908   }
909   free(read_buffer);
910
911   return 1;
912 }
913
914 /* this is very similar to the 1 bit reader <sigh> */
915 /*
916 =item read_mask
917
918 Reads the AND mask from an icon image.
919
920 =cut
921 */
922
923 static
924 int
925 read_mask(ico_reader_t *file, ico_image_t *image, int *error) {
926   /* 8 pixels per byte, rounded up to the nearest dword */
927   int line_bytes = ((image->width + 7) / 8 + 3) / 4 * 4;
928   unsigned char *read_buffer = malloc(line_bytes);
929   int y;
930   int x;
931   int mask;
932   unsigned char *inp, *outp;
933
934   if (!read_buffer) {
935     *error = ICOERR_Out_Of_Memory;
936     return 0;
937   }
938
939   for (y = image->height - 1; y >= 0; --y) {
940     if (i_io_read(file->ig, read_buffer, line_bytes) != line_bytes) {
941       free(read_buffer);
942       *error = ICOERR_Short_File;
943       return 0;
944     }
945     
946     outp = image->mask_data + y * image->width;
947     inp = read_buffer;
948     mask = 0x80;
949     for (x = 0; x < image->width; ++x) {
950       *outp++ = (*inp & mask) ? 1 : 0;
951       mask >>= 1;
952       if (!mask) {
953         mask = 0x80;
954         ++inp;
955       }
956     }
957   }
958   free(read_buffer);
959
960   return 1;
961 }
962
963 /*
964 =item ico_write_validate
965
966 Check each image to make sure it can go into an icon file.
967
968 =cut
969 */
970
971 static int
972 ico_write_validate(ico_image_t const *images, int image_count, int *error) {
973   int i;
974
975   for (i = 0; i < image_count; ++i) {
976     ico_image_t const *image = images + i;
977
978     if (image->width < 1 || image->width > 255) {
979       *error = ICOERR_Invalid_Width;
980       return 0;
981     }
982     if (image->height < 1 || image->height > 255) {
983       *error = ICOERR_Invalid_Height;
984       return 0;
985     }
986     if (!image->image_data) {
987       *error = ICOERR_No_Data;
988       return 0;
989     }
990     if (!image->direct) {
991       if (image->palette_size < 0 || image->palette_size > 256 
992           || !image->palette) {
993         *error = ICOERR_Invalid_Palette;
994         return 0;
995       }
996     }
997   }
998
999   return 1;
1000 }
1001
1002 /*
1003 =item ico_image_size
1004
1005 Calculate how much space the icon takes up in the file.
1006
1007 =cut
1008 */
1009
1010 static int
1011 ico_image_size(ico_image_t const *image, int *bits, int *colors) {
1012   int size = 40; /* start with the BITMAPINFOHEADER */
1013
1014   /* add in the image area */
1015   if (image->direct) {
1016     *bits = 32;
1017     *colors = 0;
1018     size += image->width * 4 * image->height;
1019   }
1020   else {
1021     if (image->palette_size <= 2) {
1022       *bits = 1;
1023       *colors = 2;
1024     }
1025     else if (image->palette_size <= 16) {
1026       *bits = 4;
1027       *colors = 16;
1028     }
1029     else {
1030       *bits = 8;
1031       *colors = 0;
1032     }
1033
1034     /* palette size */
1035     size += *colors * 4;
1036
1037     /* image data size */
1038     size += (image->width * *bits + 31) / 32 * 4 * image->height;
1039   }
1040
1041   /* add in the mask */
1042   size += (image->width + 31) / 32 * 4 * image->height;
1043
1044   return size;
1045 }
1046
1047 /*
1048 =item write_packed
1049
1050 Pack numbers given a format to a stream.
1051
1052 =cut
1053 */
1054
1055 static int 
1056 write_packed(i_io_glue_t *ig, char const *format, ...) {
1057   unsigned char buffer[100];
1058   va_list ap;
1059   unsigned long p;
1060   int size;
1061   const char *formatp;
1062   unsigned char *bufp;
1063
1064   /* write efficiently, work out the size of the buffer */
1065   size = 0;
1066   formatp = format;
1067   while (*formatp) {
1068     switch (*formatp++) {
1069     case 'b': size++; break;
1070     case 'w': size += 2; break;
1071     case 'd': size += 4; break;
1072     case ' ': break; /* space to separate components */
1073     default:
1074       fprintf(stderr, "invalid unpack char in %s\n", format);
1075       exit(1);
1076     }
1077   }
1078
1079   if (size > sizeof(buffer)) {
1080     /* catch if we need a bigger buffer, but 100 is plenty */
1081     fprintf(stderr, "format %s too long for buffer\n", format);
1082     exit(1);
1083   }
1084
1085   va_start(ap, format);
1086
1087   bufp = buffer;
1088   while (*format) {
1089
1090     switch (*format) {
1091     case 'b':
1092       p = va_arg(ap, int);
1093       *bufp++ = p;
1094       break;
1095
1096     case 'w':
1097       p = va_arg(ap, int);
1098       *bufp++ = p & 0xFF;
1099       *bufp++  = (p >> 8) & 0xFF;
1100       break;
1101
1102     case 'd':
1103       p = va_arg(ap, unsigned long);
1104       *bufp++ = p & 0xFF;
1105       *bufp++ = (p >> 8) & 0xFF;
1106       *bufp++ = (p >> 16) & 0xFF;
1107       *bufp++ = (p >> 24) & 0xFF;
1108       break;
1109
1110     case ' ':
1111       /* nothing to do */
1112       break;
1113     }
1114     ++format;
1115   }
1116
1117   if (i_io_write(ig, buffer, size) != size)
1118     return 0;
1119   
1120   return 1;
1121 }
1122
1123 /*
1124 =item write_palette
1125
1126 Write the palette for an icon.
1127
1128 =cut
1129 */
1130
1131 static int
1132 write_palette(i_io_glue_t *ig, ico_image_t const *image, int *error) {
1133   int full_size = image->palette_size;
1134   unsigned char *writebuf, *outp;
1135   ico_color_t *colorp;
1136   int i;
1137
1138   if (image->palette_size <= 2)
1139     full_size = 2;
1140   else if (image->palette_size <= 16)
1141     full_size = 16;
1142   else
1143     full_size = 256;
1144
1145   writebuf = calloc(full_size, 4);
1146   if (!writebuf) {
1147     *error = ICOERR_Out_Of_Memory;
1148     return 0;
1149   }
1150   outp = writebuf;
1151   colorp = image->palette;
1152   for (i = 0; i < image->palette_size; ++i) {
1153     *outp++ = colorp->b;
1154     *outp++ = colorp->g;
1155     *outp++ = colorp->r;
1156     *outp++ = 0xFF;
1157     ++colorp;
1158   }
1159   for (; i < full_size; ++i) {
1160     *outp++ = 0;
1161     *outp++ = 0;
1162     *outp++ = 0;
1163     *outp++ = 0;
1164   }
1165
1166   if (i_io_write(ig, writebuf, full_size * 4) != full_size * 4) {
1167     *error = ICOERR_Write_Failure;
1168     free(writebuf);
1169     return 0;
1170   }
1171
1172   free(writebuf);
1173
1174   return 1;
1175 }
1176
1177 /*
1178 =item write_bitmapinfoheader
1179
1180 Write the BITMAPINFOHEADER for an icon image.
1181
1182 =cut
1183 */
1184
1185 static int
1186 write_bitmapinfoheader(i_io_glue_t *ig, ico_image_t const *image, int *error,
1187                         int bit_count, int clr_used) {
1188   if (!write_packed(ig, "d dd w w d d dd dd", 
1189                     40UL, /* biSize */
1190                     (unsigned long)image->width, 
1191                     (unsigned long)2 * image->height, /* biWidth/biHeight */
1192                     1, bit_count, /* biPlanes, biBitCount */
1193                     0UL, 0UL, /* biCompression, biSizeImage */
1194                     0UL, 0UL, /* bi(X|Y)PetsPerMeter */
1195                     (unsigned long)clr_used, /* biClrUsed */
1196                     0UL)) { /* biClrImportant */
1197     *error = ICOERR_Write_Failure;
1198     return 0;
1199   }
1200
1201   return 1;
1202 }
1203
1204 /*
1205 =item write_32_bit
1206
1207 Write 32-bit image data to the icon.
1208
1209 =cut
1210 */
1211
1212 static int
1213 write_32_bit(i_io_glue_t *ig, ico_image_t const *image, int *error) {
1214   unsigned char *writebuf;
1215   ico_color_t *data = image->image_data, *colorp;
1216   unsigned char *writep;
1217   int x, y;
1218
1219   if (!write_bitmapinfoheader(ig, image, error, 32, 0)) {
1220     return 0;
1221   }
1222
1223   writebuf = malloc(image->width * 4);
1224   if (!writebuf) {
1225     *error = ICOERR_Out_Of_Memory;
1226     return 0;
1227   }
1228
1229   for (y = image->height-1; y >= 0; --y) {
1230     writep = writebuf;
1231     colorp = data + y * image->width;
1232     for (x = 0; x < image->width; ++x) {
1233       *writep++ = colorp->b;
1234       *writep++ = colorp->g;
1235       *writep++ = colorp->r;
1236       *writep++ = colorp->a;
1237       ++colorp;
1238     }
1239     if (i_io_write(ig, writebuf, image->width * 4) != image->width * 4) {
1240       *error = ICOERR_Write_Failure;
1241       free(writebuf);
1242       return 0;
1243     }
1244   }
1245
1246   free(writebuf);
1247
1248   return 1;
1249 }
1250
1251 /*
1252 =item write_8_bit
1253
1254 Write 8 bit image data.
1255
1256 =cut
1257 */
1258
1259 static int
1260 write_8_bit(i_io_glue_t *ig, ico_image_t const *image, int *error) {
1261   static const unsigned char zeros[3] = { '\0' };
1262   int y;
1263   const unsigned char *data = image->image_data;
1264   int zero_count = (0U - (unsigned)image->width) & 3;
1265
1266   if (!write_bitmapinfoheader(ig, image, error, 8, 256)) {
1267     return 0;
1268   }
1269
1270   if (!write_palette(ig, image, error))
1271     return 0;
1272
1273   for (y = image->height-1; y >= 0; --y) {
1274     if (i_io_write(ig, data + y * image->width, 
1275                    image->width) != image->width) {
1276       *error = ICOERR_Write_Failure;
1277       return 0;
1278     }
1279     if (zero_count) {
1280       if (i_io_write(ig, zeros, zero_count) != zero_count) {
1281         *error = ICOERR_Write_Failure;
1282         return 0;
1283       }
1284     }
1285   }
1286
1287   return 1;
1288 }
1289
1290 /*
1291 =item write_4_bit
1292
1293 Write 4 bit image data.
1294
1295 =cut
1296 */
1297
1298 static int
1299 write_4_bit(i_io_glue_t *ig, ico_image_t const *image, int *error) {
1300   int line_size = ((image->width + 1) / 2 + 3) / 4 * 4;
1301   unsigned char *writebuf, *outp;
1302   int x, y;
1303   unsigned char const *data = image->image_data;
1304   unsigned char const *pixelp;
1305   
1306   if (!write_bitmapinfoheader(ig, image, error, 4, 16)) {
1307     return 0;
1308   }
1309
1310   if (!write_palette(ig, image, error))
1311     return 0;
1312
1313   writebuf = malloc(line_size);
1314   if (!writebuf) {
1315     *error = ICOERR_Out_Of_Memory;
1316     return 0;
1317   }
1318
1319   for (y = image->height-1; y >= 0; --y) {
1320     pixelp = data + y * image->width;
1321     outp = writebuf;
1322     memset(writebuf, 0, line_size);
1323     for (x = 0; x < image->width; ++x) {
1324       if (x & 1) {
1325         *outp |= *pixelp++ & 0x0F;
1326         ++outp;
1327       }
1328       else {
1329         *outp |= *pixelp++ << 4;
1330       }
1331     }
1332
1333     if (i_io_write(ig, writebuf, line_size) != line_size) {
1334       *error = ICOERR_Write_Failure;
1335       free(writebuf);
1336       return 0;
1337     }
1338   }
1339
1340   free(writebuf);
1341
1342   return 1;
1343 }
1344
1345 /*
1346 =item write_1_bit
1347
1348 Write 1 bit image data.
1349
1350 =cut
1351 */
1352
1353 static int
1354 write_1_bit(i_io_glue_t *ig, ico_image_t const *image, int *error) {
1355   int line_size = (image->width + 31) / 32 * 4;
1356   unsigned char *writebuf = malloc(line_size);
1357   unsigned char *outp;
1358   unsigned char const *data, *pixelp;
1359   int x,y;
1360   unsigned mask;
1361
1362   if (!write_bitmapinfoheader(ig, image, error, 1, 2)) {
1363     return 0;
1364   }
1365
1366   if (!write_palette(ig, image, error))
1367     return 0;
1368
1369   if (!writebuf) {
1370     *error = ICOERR_Out_Of_Memory;
1371     return 0;
1372   }
1373   
1374   data = image->image_data;
1375   for (y = image->height-1; y >= 0; --y) {
1376     memset(writebuf, 0, line_size);
1377     pixelp = data + y * image->width;
1378     outp = writebuf;
1379     mask = 0x80;
1380     for (x = 0; x < image->width; ++x) {
1381       if (*pixelp)
1382         *outp |= mask;
1383       mask >>= 1;
1384       if (!mask) {
1385         mask = 0x80;
1386         outp++;
1387       }
1388     }
1389     if (i_io_write(ig, writebuf, line_size) != line_size) {
1390       *error = ICOERR_Write_Failure;
1391       free(writebuf);
1392       return 0;
1393     }
1394   }
1395
1396   free(writebuf);
1397
1398   return 1;
1399 }
1400
1401 /*
1402 =item write_mask
1403
1404 Write the AND mask.
1405
1406 =cut
1407 */
1408
1409 static int
1410 write_mask(i_io_glue_t *ig, ico_image_t const *image, int *error) {
1411   int line_size = (image->width + 31) / 32 * 4;
1412   unsigned char *writebuf = malloc(line_size);
1413   unsigned char *outp;
1414   unsigned char const *data, *pixelp;
1415   int x,y;
1416   unsigned mask;
1417
1418   if (!writebuf) {
1419     *error = ICOERR_Out_Of_Memory;
1420     return 0;
1421   }
1422   
1423   data = image->mask_data;
1424   if (data) {
1425     for (y = image->height-1; y >= 0; --y) {
1426       memset(writebuf, 0, line_size);
1427       pixelp = data + y * image->width;
1428       outp = writebuf;
1429       mask = 0x80;
1430       for (x = 0; x < image->width; ++x) {
1431         if (*pixelp)
1432           *outp |= mask;
1433         mask >>= 1;
1434         if (!mask) {
1435           mask = 0x80;
1436           outp++;
1437         }
1438         ++pixelp;
1439       }
1440       if (i_io_write(ig, writebuf, line_size) != line_size) {
1441         *error = ICOERR_Write_Failure;
1442         free(writebuf);
1443         return 0;
1444       }
1445     }
1446   }
1447   else {
1448     memset(writebuf, 0, line_size);
1449     for (y = image->height-1; y >= 0; --y) {
1450       if (i_io_write(ig, writebuf, line_size) != line_size) {
1451         *error = ICOERR_Write_Failure;
1452         free(writebuf);
1453         return 0;
1454       }
1455     }
1456   }
1457
1458   free(writebuf);
1459
1460   return 1;
1461 }
1462
1463 /*
1464 =back
1465
1466 =head1 AUTHOR
1467
1468 Tony Cook <tony@imager.perl.org>
1469
1470 =head1 REVISION
1471
1472 $Revision$
1473
1474 =cut
1475 */