9 int read_packed(io_glue *ig, const char *format, ...);
11 read_palette(ico_reader_t *file, ico_image_t *image, int *error);
13 read_32bit_data(ico_reader_t *file, ico_image_t *image, int *error);
15 read_8bit_data(ico_reader_t *file, ico_image_t *image, int *error);
17 read_4bit_data(ico_reader_t *file, ico_image_t *image, int *error);
19 read_1bit_data(ico_reader_t *file, ico_image_t *image, int *error);
21 read_mask(ico_reader_t *file, ico_image_t *image, int *error);
23 ico_write_validate(ico_image_t const *images, int image_count, int *error);
25 ico_image_size(ico_image_t const *image, int *bits, int *colors);
27 write_packed(i_io_glue_t *ig, char const *format, ...);
29 write_palette(i_io_glue_t *ig, ico_image_t const *image, int *error);
31 write_32_bit(i_io_glue_t *ig, ico_image_t const *image, int *error);
33 write_8_bit(i_io_glue_t *ig, ico_image_t const *image, int *error);
35 write_4_bit(i_io_glue_t *ig, ico_image_t const *image, int *error);
37 write_1_bit(i_io_glue_t *ig, ico_image_t const *image, int *error);
39 write_mask(i_io_glue_t *ig, ico_image_t const *image, int *error);
46 int hotspot_x, hotspot_y;
47 } ico_reader_image_entry;
49 /* this was previously declared, now define it */
50 struct ico_reader_tag {
51 /* the file we're dealing with */
54 /* number of images in the file */
57 /* type of resource - 1=icon, 2=cursor */
60 /* image information from the header */
61 ico_reader_image_entry *images;
67 msicon.c - functions for working with .ICO files.
73 ico_reader_t *file = ico_reader_open(ig, &error);
76 ico_error_message(error, buffer, sizeof(buffer));
77 fputs(buffer, stderr);
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);
87 ico_reader_close(file);
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
96 It relies on icon images being generally small, and reads the entire
97 image into memory when reading.
99 =head1 READING ICON FILES
103 =item ico_reader_open(ig, &error)
111 io_glue *ig - an Imager IO object. This must be seekable.
115 int *error - pointer to an integer which an error code will be
116 returned in on failure.
124 ico_reader_open(i_io_glue_t *ig, int *error) {
125 long res1, type, count;
126 ico_reader_t *file = NULL;
129 if (!read_packed(ig, "www", &res1, &type, &count)) {
130 *error = ICOERR_Short_File;
133 if (res1 != 0 || (type != 1 && type != 2) || count == 0) {
134 *error = ICOERR_Invalid_File;
138 file = malloc(sizeof(ico_reader_t));
140 *error = ICOERR_Out_Of_Memory;
146 file->images = malloc(sizeof(ico_reader_image_entry) * count);
147 if (file->images == NULL) {
148 *error = ICOERR_Out_Of_Memory;
153 for (i = 0; i < count; ++i) {
154 long width, height, bytes_in_res, image_offset;
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,
162 *error = ICOERR_Short_File;
165 image->hotspot_x = image->hotspot_y = 0;
168 long hotspot_x, hotspot_y;
170 if (!read_packed(ig, "bb xx ww dd", &width, &height,
171 &hotspot_x, &hotspot_y, &bytes_in_res,
175 *error = ICOERR_Short_File;
178 image->hotspot_x = hotspot_x;
179 image->hotspot_y = hotspot_y;
182 image->width = width;
183 image->height = height;
184 image->offset = image_offset;
185 image->size = bytes_in_res;
192 =item ico_image_count
194 // number of images in the file
195 count = ico_image_count(file);
201 ico_image_count(ico_reader_t *file) {
208 // type of file - ICON_ICON for icon, ICON_CURSOR for cursor
209 type = ico_type(file);
215 ico_type(ico_reader_t *file) {
222 Read an image from the file given it's index.
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;
234 if (index < 0 || index >= file->count) {
235 *error = ICOERR_Bad_Image_Index;
239 im = file->images + index;
240 if (i_io_seek(ig, im->offset, SEEK_SET) != im->offset) {
241 *error = ICOERR_File_Error;
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;
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;
259 if (bit_count != 1 && bit_count != 4 && bit_count != 8 && bit_count != 32) {
260 *error = ICOERR_Unknown_Bits;
264 result = malloc(sizeof(ico_image_t));
266 *error = ICOERR_Out_Of_Memory;
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;
279 if (bit_count == 32) {
280 result->palette_size = 0;
282 result->image_data = malloc(result->width * result->height * sizeof(ico_color_t));
283 if (!result->image_data) {
285 *error = ICOERR_Out_Of_Memory;
288 if (!read_32bit_data(file, result, error)) {
289 free(result->image_data);
297 result->palette_size = 1 << bit_count;
298 result->palette = malloc(sizeof(ico_color_t) * result->palette_size);
299 if (!result->palette) {
301 *error = ICOERR_Out_Of_Memory;
305 result->image_data = malloc(result->width * result->height);
306 if (!result->image_data) {
307 *error = ICOERR_Out_Of_Memory;
308 free(result->palette);
313 if (!read_palette(file, result, error)) {
314 free(result->palette);
315 free(result->image_data);
322 read_result = read_1bit_data(file, result, error);
326 read_result = read_4bit_data(file, result, error);
330 read_result = read_8bit_data(file, result, error);
334 assert(0); /* this can't happen in theory */
340 free(result->palette);
341 free(result->image_data);
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);
356 if (!read_mask(file, result, error)) {
357 free(result->mask_data);
358 free(result->palette);
359 free(result->image_data);
368 =item ico_image_release
370 Release an image structure returned by ico_image_read.
376 ico_image_release(ico_image_t *image) {
377 free(image->mask_data);
378 free(image->palette);
379 free(image->image_data);
384 =item ico_reader_close
386 Releases the read file structure.
392 ico_reader_close(ico_reader_t *file) {
393 i_io_close(file->ig);
401 =head1 WRITING ICON FILES
405 =item ico_write(ig, images, image_count, type, &error)
413 io_glue *ig - an Imager IO object. This only needs to implement
414 writing for ico_write()
418 ico_image_t *images - array of images to be written.
422 int image_count - number of images
426 int type - must be ICON_ICON or ICON_CURSOR
430 int *error - set to an error code on failure.
434 Returns non-zero on success.
440 ico_write(i_io_glue_t *ig, ico_image_t const *images, int image_count,
441 int type, int *error) {
443 int start_offset = 6 + 16 * image_count;
444 int current_offset = start_offset;
446 if (type != ICON_ICON && type != ICON_CURSOR) {
447 *error = ICOERR_Bad_File_Type;
451 /* validate the images */
452 if (!ico_write_validate(images, image_count, error))
455 /* write the header */
456 if (!write_packed(ig, "www", 0, type, image_count)) {
457 *error = ICOERR_Write_Failure;
461 /* work out the offsets of each image */
462 for (i = 0; i < image_count; ++i) {
463 ico_image_t const *image = images + i;
465 int size = ico_image_size(image, &bits, &colors);
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;
476 int hotspot_x = image->hotspot_x;
477 int hotspot_y = image->hotspot_y;
481 else if (hotspot_x >= image->width)
482 hotspot_x = image->width - 1;
485 else if (hotspot_y >= image->height)
486 hotspot_y = image->height - 1;
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;
495 current_offset += size;
498 /* write out each image */
499 for (i = 0; i < image_count; ++i) {
500 ico_image_t const *image = images + i;
503 if (!write_32_bit(ig, image, error))
507 if (image->palette_size <= 2) {
508 if (!write_1_bit(ig, image, error))
511 else if (image->palette_size <= 16) {
512 if (!write_4_bit(ig, image, error))
516 if (!write_8_bit(ig, image, error))
520 if (!write_mask(ig, image, error))
530 =head1 ERROR MESSAGES
534 =item ico_error_message
536 Converts an error code into an error message.
542 ico_error_message(int error, char *buffer, size_t buffer_size) {
547 case ICOERR_Short_File:
551 case ICOERR_File_Error:
555 case ICOERR_Write_Failure:
556 msg = "Write failure";
559 case ICOERR_Invalid_File:
560 msg = "Not an icon file";
563 case ICOERR_Unknown_Bits:
564 msg = "Unknown value for bits/pixel";
567 case ICOERR_Bad_Image_Index:
568 msg = "Image index out of range";
571 case ICOERR_Bad_File_Type:
572 msg = "Bad file type parameter";
575 case ICOERR_Invalid_Width:
576 msg = "Invalid image width";
579 case ICOERR_Invalid_Height:
580 msg = "Invalid image height";
583 case ICOERR_Invalid_Palette:
584 msg = "Invalid Palette";
588 msg = "No image data in image supplied to ico_write";
591 case ICOERR_Out_Of_Memory:
592 msg = "Out of memory";
596 msg = "Unknown error code";
600 size = strlen(msg) + 1;
601 if (size > buffer_size)
603 memcpy(buffer, msg, size);
604 buffer[size-1] = '\0';
612 =head1 PRIVATE FUNCTIONS
618 Reads packed data from a stream, unpacking it.
624 int read_packed(io_glue *ig, const char *format, ...) {
625 unsigned char buffer[100];
632 /* read efficiently, work out the size of the buffer */
636 switch (*formatp++) {
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 */
643 fprintf(stderr, "invalid unpack char in %s\n", format);
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);
654 if (i_io_read(ig, buffer, size) != size) {
658 va_start(ap, format);
665 p = va_arg(ap, long *);
670 p = va_arg(ap, long *);
671 *p = bufp[0] + (bufp[1] << 8);
676 p = va_arg(ap, long *);
677 *p = bufp[0] + (bufp[1] << 8) + (bufp[2] << 16) + (bufp[3] << 24);
682 ++bufp; /* skip a byte */
697 Reads the palette data for an icon image.
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);
712 *error = ICOERR_Out_Of_Memory;
716 if (i_io_read(file->ig, read_buffer, palette_bytes) != palette_bytes) {
717 *error = ICOERR_Short_File;
723 outp = image->palette;
724 for (i = 0; i < image->palette_size; ++i) {
738 =item read_32bit_data
740 Reads 32 bit image data.
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);
756 *error = ICOERR_Out_Of_Memory;
760 for (y = image->height - 1; y >= 0; --y) {
761 if (i_io_read(file->ig, buffer, line_bytes) != line_bytes) {
763 *error = ICOERR_Short_File;
766 outp = image->image_data;
767 outp += y * image->width;
769 for (x = 0; x < image->width; ++x) {
786 Reads 8 bit image data.
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);
798 unsigned char *inp, *outp;
801 *error = ICOERR_Out_Of_Memory;
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) {
810 *error = ICOERR_Short_File;
813 for (x = 0, inp = buffer; x < image->width; ++x) {
825 Reads 4 bit image data.
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);
838 unsigned char *inp, *outp;
841 *error = ICOERR_Out_Of_Memory;
845 for (y = image->height - 1; y >= 0; --y) {
846 if (i_io_read(file->ig, read_buffer, line_bytes) != line_bytes) {
848 *error = ICOERR_Short_File;
852 outp = image->image_data;
853 outp += y * image->width;
855 for (x = 0; x < image->width; ++x) {
856 /* yes, this is kind of ugly */
858 *outp++ = *inp++ & 0x0F;
873 Reads 1 bit image data.
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);
886 unsigned char *inp, *outp;
889 *error = ICOERR_Out_Of_Memory;
893 for (y = image->height - 1; y >= 0; --y) {
894 if (i_io_read(file->ig, read_buffer, line_bytes) != line_bytes) {
896 *error = ICOERR_Short_File;
900 outp = image->image_data;
901 outp += y * image->width;
903 for (x = 0; x < image->width; ++x) {
904 *outp++ = (*inp >> (7 - (x & 7))) & 1;
914 /* this is very similar to the 1 bit reader <sigh> */
918 Reads the AND mask from an icon image.
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);
932 unsigned char *inp, *outp;
935 *error = ICOERR_Out_Of_Memory;
939 for (y = image->height - 1; y >= 0; --y) {
940 if (i_io_read(file->ig, read_buffer, line_bytes) != line_bytes) {
942 *error = ICOERR_Short_File;
946 outp = image->mask_data + y * image->width;
949 for (x = 0; x < image->width; ++x) {
950 *outp++ = (*inp & mask) ? 1 : 0;
964 =item ico_write_validate
966 Check each image to make sure it can go into an icon file.
972 ico_write_validate(ico_image_t const *images, int image_count, int *error) {
975 for (i = 0; i < image_count; ++i) {
976 ico_image_t const *image = images + i;
978 if (image->width < 1 || image->width > 255) {
979 *error = ICOERR_Invalid_Width;
982 if (image->height < 1 || image->height > 255) {
983 *error = ICOERR_Invalid_Height;
986 if (!image->image_data) {
987 *error = ICOERR_No_Data;
990 if (!image->direct) {
991 if (image->palette_size < 0 || image->palette_size > 256
992 || !image->palette) {
993 *error = ICOERR_Invalid_Palette;
1003 =item ico_image_size
1005 Calculate how much space the icon takes up in the file.
1011 ico_image_size(ico_image_t const *image, int *bits, int *colors) {
1012 int size = 40; /* start with the BITMAPINFOHEADER */
1014 /* add in the image area */
1015 if (image->direct) {
1018 size += image->width * 4 * image->height;
1021 if (image->palette_size <= 2) {
1025 else if (image->palette_size <= 16) {
1035 size += *colors * 4;
1037 /* image data size */
1038 size += (image->width * *bits + 31) / 32 * 4 * image->height;
1041 /* add in the mask */
1042 size += (image->width + 31) / 32 * 4 * image->height;
1050 Pack numbers given a format to a stream.
1056 write_packed(i_io_glue_t *ig, char const *format, ...) {
1057 unsigned char buffer[100];
1061 const char *formatp;
1062 unsigned char *bufp;
1064 /* write efficiently, work out the size of the buffer */
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 */
1074 fprintf(stderr, "invalid unpack char in %s\n", format);
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);
1085 va_start(ap, format);
1092 p = va_arg(ap, int);
1097 p = va_arg(ap, int);
1099 *bufp++ = (p >> 8) & 0xFF;
1103 p = va_arg(ap, unsigned long);
1105 *bufp++ = (p >> 8) & 0xFF;
1106 *bufp++ = (p >> 16) & 0xFF;
1107 *bufp++ = (p >> 24) & 0xFF;
1117 if (i_io_write(ig, buffer, size) != size)
1126 Write the palette for an icon.
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;
1138 if (image->palette_size <= 2)
1140 else if (image->palette_size <= 16)
1145 writebuf = calloc(full_size, 4);
1147 *error = ICOERR_Out_Of_Memory;
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;
1159 for (; i < full_size; ++i) {
1166 if (i_io_write(ig, writebuf, full_size * 4) != full_size * 4) {
1167 *error = ICOERR_Write_Failure;
1178 =item write_bitmapinfoheader
1180 Write the BITMAPINFOHEADER for an icon image.
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",
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;
1207 Write 32-bit image data to the icon.
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;
1219 if (!write_bitmapinfoheader(ig, image, error, 32, 0)) {
1223 writebuf = malloc(image->width * 4);
1225 *error = ICOERR_Out_Of_Memory;
1229 for (y = image->height-1; y >= 0; --y) {
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;
1239 if (i_io_write(ig, writebuf, image->width * 4) != image->width * 4) {
1240 *error = ICOERR_Write_Failure;
1254 Write 8 bit image data.
1260 write_8_bit(i_io_glue_t *ig, ico_image_t const *image, int *error) {
1261 static const unsigned char zeros[3] = { '\0' };
1263 const unsigned char *data = image->image_data;
1264 int zero_count = (0U - (unsigned)image->width) & 3;
1266 if (!write_bitmapinfoheader(ig, image, error, 8, 256)) {
1270 if (!write_palette(ig, image, error))
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;
1280 if (i_io_write(ig, zeros, zero_count) != zero_count) {
1281 *error = ICOERR_Write_Failure;
1293 Write 4 bit image data.
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;
1303 unsigned char const *data = image->image_data;
1304 unsigned char const *pixelp;
1306 if (!write_bitmapinfoheader(ig, image, error, 4, 16)) {
1310 if (!write_palette(ig, image, error))
1313 writebuf = malloc(line_size);
1315 *error = ICOERR_Out_Of_Memory;
1319 for (y = image->height-1; y >= 0; --y) {
1320 pixelp = data + y * image->width;
1322 memset(writebuf, 0, line_size);
1323 for (x = 0; x < image->width; ++x) {
1325 *outp |= *pixelp++ & 0x0F;
1329 *outp |= *pixelp++ << 4;
1333 if (i_io_write(ig, writebuf, line_size) != line_size) {
1334 *error = ICOERR_Write_Failure;
1348 Write 1 bit image data.
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;
1362 if (!write_bitmapinfoheader(ig, image, error, 1, 2)) {
1366 if (!write_palette(ig, image, error))
1370 *error = ICOERR_Out_Of_Memory;
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;
1380 for (x = 0; x < image->width; ++x) {
1389 if (i_io_write(ig, writebuf, line_size) != line_size) {
1390 *error = ICOERR_Write_Failure;
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;
1419 *error = ICOERR_Out_Of_Memory;
1423 data = image->mask_data;
1425 for (y = image->height-1; y >= 0; --y) {
1426 memset(writebuf, 0, line_size);
1427 pixelp = data + y * image->width;
1430 for (x = 0; x < image->width; ++x) {
1440 if (i_io_write(ig, writebuf, line_size) != line_size) {
1441 *error = ICOERR_Write_Failure;
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;
1468 Tony Cook <tony@imager.perl.org>