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