]> git.imager.perl.org - imager.git/blame_incremental - ICO/msicon.c
most numeric parameters to the XS implementation now throw an exception if supplied...
[imager.git] / ICO / msicon.c
... / ...
CommitLineData
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
9static
10int read_packed(io_glue *ig, const char *format, ...);
11static int
12read_palette(ico_reader_t *file, ico_image_t *image, int *error);
13static int
14read_24bit_data(ico_reader_t *file, ico_image_t *image, int *error);
15static int
16read_32bit_data(ico_reader_t *file, ico_image_t *image, int *error);
17static int
18read_8bit_data(ico_reader_t *file, ico_image_t *image, int *error);
19static int
20read_4bit_data(ico_reader_t *file, ico_image_t *image, int *error);
21static int
22read_1bit_data(ico_reader_t *file, ico_image_t *image, int *error);
23static int
24read_mask(ico_reader_t *file, ico_image_t *image, int *error);
25static int
26ico_write_validate(ico_image_t const *images, int image_count, int *error);
27static int
28ico_image_size(ico_image_t const *image, int *bits, int *colors);
29static int
30write_packed(i_io_glue_t *ig, char const *format, ...);
31static int
32write_palette(i_io_glue_t *ig, ico_image_t const *image, int *error);
33static int
34write_32_bit(i_io_glue_t *ig, ico_image_t const *image, int *error);
35static int
36write_8_bit(i_io_glue_t *ig, ico_image_t const *image, int *error);
37static int
38write_4_bit(i_io_glue_t *ig, ico_image_t const *image, int *error);
39static int
40write_1_bit(i_io_glue_t *ig, ico_image_t const *image, int *error);
41static int
42write_mask(i_io_glue_t *ig, ico_image_t const *image, int *error);
43
44typedef 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 */
53struct 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
70msicon.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
94This is intended as a general interface to reading MS Icon files, and
95is written to be independent of Imager, even though it is part of
96Imager. You just need to supply something that acts like Imager's
97io_glue.
98
99It relies on icon images being generally small, and reads the entire
100image into memory when reading.
101
102=head1 READING ICON FILES
103
104=over
105
106=item ico_reader_open(ig, &error)
107
108Parameters:
109
110=over
111
112=item *
113
114io_glue *ig - an Imager IO object. This must be seekable.
115
116=item *
117
118int *error - pointer to an integer which an error code will be
119returned in on failure.
120
121=back
122
123=cut
124*/
125
126ico_reader_t *
127ico_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
204int
205ico_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
218int
219ico_type(ico_reader_t *file) {
220 return file->type;
221}
222
223/*
224=item ico_image_read
225
226Read an image from the file given it's index.
227
228=cut
229*/
230
231ico_image_t *
232ico_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
390Release an image structure returned by ico_image_read.
391
392=cut
393*/
394
395void
396ico_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
406Releases the read file structure.
407
408=cut
409*/
410
411void
412ico_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
427Parameters:
428
429=over
430
431=item *
432
433io_glue *ig - an Imager IO object. This only needs to implement
434writing for ico_write()
435
436=item *
437
438ico_image_t *images - array of images to be written.
439
440=item *
441
442int image_count - number of images
443
444=item *
445
446int type - must be ICON_ICON or ICON_CURSOR
447
448=item *
449
450int *error - set to an error code on failure.
451
452=back
453
454Returns non-zero on success.
455
456=cut
457*/
458
459int
460ico_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
558Converts an error code into an error message.
559
560=cut
561*/
562
563size_t
564ico_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
640Reads packed data from a stream, unpacking it.
641
642=cut
643*/
644
645static
646int 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
719Reads the palette data for an icon image.
720
721=cut
722*/
723
724static
725int
726read_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
762Reads 32 bit image data.
763
764=cut
765*/
766
767static
768int
769read_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
808Reads 24 bit image data.
809
810=cut
811*/
812
813static
814int
815read_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
858Reads 8 bit image data.
859
860=cut
861*/
862
863static
864int
865read_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
897Reads 4 bit image data.
898
899=cut
900*/
901
902static
903int
904read_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
945Reads 1 bit image data.
946
947=cut
948*/
949
950static
951int
952read_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
990Reads the AND mask from an icon image.
991
992=cut
993*/
994
995static
996int
997read_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
1038Check each image to make sure it can go into an icon file.
1039
1040=cut
1041*/
1042
1043static int
1044ico_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
1077Calculate how much space the icon takes up in the file.
1078
1079=cut
1080*/
1081
1082static int
1083ico_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
1122Pack numbers given a format to a stream.
1123
1124=cut
1125*/
1126
1127static int
1128write_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
1198Write the palette for an icon.
1199
1200=cut
1201*/
1202
1203static int
1204write_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
1252Write the BITMAPINFOHEADER for an icon image.
1253
1254=cut
1255*/
1256
1257static int
1258write_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
1279Write 32-bit image data to the icon.
1280
1281=cut
1282*/
1283
1284static int
1285write_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
1326Write 8 bit image data.
1327
1328=cut
1329*/
1330
1331static int
1332write_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
1365Write 4 bit image data.
1366
1367=cut
1368*/
1369
1370static int
1371write_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
1420Write 1 bit image data.
1421
1422=cut
1423*/
1424
1425static int
1426write_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
1477Write the AND mask.
1478
1479=cut
1480*/
1481
1482static int
1483write_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
1541Tony Cook <tonyc@cpan.org>
1542
1543=head1 REVISION
1544
1545$Revision$
1546
1547=cut
1548*/