--- /dev/null
+package Imager::File::ICO;
+use strict;
+use Imager;
+use vars qw($VERSION @ISA);
+
+BEGIN {
+ $VERSION = "0.01";
+
+ eval {
+ require XSLoader;
+ XSLoader::load('Imager::File::ICO', $VERSION);
+ 1;
+ } or do {
+ require DynaLoader;
+ push @ISA, 'DynaLoader';
+ bootstrap Imager::File::ICO $VERSION;
+ };
+}
+
+Imager->register_reader
+ (
+ type=>'ico',
+ single =>
+ sub {
+ my ($im, $io, %hsh) = @_;
+ $im->{IMG} = i_readico_single($io, $hsh{page} || 0);
+
+ unless ($im->{IMG}) {
+ $im->_set_error(Imager->_error_as_msg);
+ return;
+ }
+ return $im;
+ },
+ multiple =>
+ sub {
+ my ($io, %hsh) = @_;
+
+ my @imgs = i_readico_multi($io);
+ unless (@imgs) {
+ Imager->_set_error(Imager->_error_as_msg);
+ return;
+ }
+ return map {
+ bless { IMG => $_, DEBUG => $Imager::DEBUG, ERRSTR => undef }, 'Imager'
+ } @imgs;
+ },
+ );
+
+1;
+
+__END__
+
+=head1 NAME
+
+Imager::File::ICO - read MS Icon files
+
+=head1 SYNOPSIS
+
+ use Imager;
+
+ my $img = Imager->new;
+ $img->read(file=>"foo.ico")
+ or die $img->errstr;
+
+ my @imgs = Imager->read_multi(file => "foo.ico")
+ or die Imager->errstr;
+
+=head1 DESCRIPTION
+
+
+
+=head1 AUTHOR
+
+=head1 SEE ALSO
+
+Imager, Imager::Files.
+
+=cut
--- /dev/null
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+#include "imext.h"
+#include "imperl.h"
+#include "imicon.h"
+
+DEFINE_IMAGER_CALLBACKS;
+
+MODULE = Imager::File::ICO PACKAGE = Imager::File::ICO
+
+PROTOTYPES: DISABLE
+
+Imager::ImgRaw
+i_readico_single(ig, index)
+ Imager::IO ig
+ int index
+
+void
+i_readico_multi(ig)
+ Imager::IO ig
+ PREINIT:
+ i_img **imgs;
+ int count;
+ int i;
+ PPCODE:
+ imgs = i_readico_multi(ig, &count);
+ if (imgs) {
+ EXTEND(SP, count);
+ for (i = 0; i < count; ++i) {
+ SV *sv = sv_newmortal();
+ sv_setref_pv(sv, "Imager::ImgRaw", (void *)imgs[i]);
+ PUSHs(sv);
+ }
+ myfree(imgs);
+ }
+
+
+BOOT:
+ PERL_INITIALIZE_IMAGER_CALLBACKS;
--- /dev/null
+use ExtUtils::MakeMaker;
+
+my %opts =
+ (
+ NAME => 'Imager::File::ICO',
+ VERSION_FROM => 'ICO.pm',
+ OBJECT => 'ICO.o msicon.o imicon.o',
+ INC => '-I..'
+ );
+if ($ExtUtils::MakeMaker::VERSION > 6.06) {
+ $opts{AUTHOR} = 'Tony Cook <tony@imager.perl.org>';
+ $opts{ABSTRACT} = 'Icon Image file support';
+}
+
+WriteMakefile(%opts);
+
+
--- /dev/null
+#include "imext.h"
+#include "imicon.h"
+#include "msicon.h"
+
+static void
+ico_push_error(int error) {
+ char error_buf[ICO_MAX_MESSAGE];
+
+ ico_error_message(error, error_buf, sizeof(error_buf));
+ i_push_error(error, error_buf);
+}
+
+static
+i_img *
+read_one_icon(ico_reader_t *file, int index) {
+ ico_image_t *image;
+ int error;
+ i_img *result;
+
+ image = ico_image_read(file, index, &error);
+ if (!image) {
+ ico_reader_close(file);
+ ico_push_error(error);
+ return NULL;
+ }
+
+ if (image->direct) {
+ int x, y;
+ i_color *line_buf = mymalloc(image->width * sizeof(i_color));
+ i_color *outp;
+ ico_color_t *inp = image->image_data;
+
+ result = i_img_8_new(image->width, image->height, 4);
+ if (!result) {
+ ico_image_release(image);
+ ico_reader_close(file);
+ return NULL;
+ }
+
+ for (y = 0; y < image->height; ++y) {
+ outp = line_buf;
+ for (x = 0; x < image->width; ++x) {
+ outp->rgba.r = inp->r;
+ outp->rgba.g = inp->g;
+ outp->rgba.b = inp->b;
+ outp->rgba.a = inp->a;
+ ++outp;
+ ++inp;
+ }
+ i_plin(result, 0, image->width, y, line_buf);
+ }
+
+ myfree(line_buf);
+ }
+ else {
+ int pal_index;
+ int y;
+ unsigned char *image_data;
+
+ result = i_img_pal_new(image->width, image->height, 3, 256);
+ if (!result) {
+ ico_image_release(image);
+ ico_reader_close(file);
+ return NULL;
+ }
+
+ /* fill in the palette */
+ for (pal_index = 0; pal_index < image->palette_size; ++pal_index) {
+ i_color c;
+ c.rgba.r = image->palette[pal_index].r;
+ c.rgba.g = image->palette[pal_index].g;
+ c.rgba.b = image->palette[pal_index].b;
+ c.rgba.a = 255; /* so as to not confuse some code */
+
+ if (i_addcolors(result, &c, 1) < 0) {
+ i_push_error(0, "could not add color to palette");
+ ico_image_release(image);
+ ico_reader_close(file);
+ i_img_destroy(result);
+ return NULL;
+ }
+ }
+
+ /* fill in the image data */
+ image_data = image->image_data;
+ for (y = 0; y < image->height; ++y) {
+ i_ppal(result, 0, image->width, y, image_data);
+ image_data += image->width;
+ }
+ }
+
+ {
+ unsigned char *inp = image->mask_data;
+ char *outp;
+ int x, y;
+ char *mask;
+ /* fill in the mask tag */
+ /* space for " .\n", width + 1 chars per line and NUL */
+ mask = mymalloc(3 + (image->width + 1) * image->height + 1);
+
+ outp = mask;
+ *outp++ = '.';
+ *outp++ = '*';
+ *outp++ = '\n';
+ for (y = 0; y < image->height; ++y) {
+ for (x = 0; x < image->width; ++x) {
+ *outp++ = *inp++ ? '*' : '.';
+ }
+ if (y != image->height - 1) /* not on the last line */
+ *outp++ = '\n';
+ }
+ *outp++ = '\0';
+
+ i_tags_set(&result->tags, "ico_mask", mask, (outp-mask)-1);
+
+ myfree(mask);
+ }
+ i_tags_setn(&result->tags, "ico_bits", image->bit_count);
+ i_tags_set(&result->tags, "ico_type", ico_type(file) == ICON_ICON ? "icon" : "cursor", -1);
+ i_tags_set(&result->tags, "i_format", "ico", 3);
+
+ ico_image_release(image);
+
+ return result;
+}
+
+i_img *
+i_readico_single(io_glue *ig, int index) {
+ ico_reader_t *file;
+ i_img *result;
+ int error;
+
+ file = ico_reader_open(ig, &error);
+ if (!file) {
+ ico_push_error(error);
+ return NULL;
+ }
+
+ if (index < 0 && index >= ico_image_count(file)) {
+ i_push_error(0, "page out of range");
+ return NULL;
+ }
+
+ result = read_one_icon(file, index);
+ ico_reader_close(file);
+
+ return result;
+}
+
+i_img **
+i_readico_multi(io_glue *ig, int *count) {
+ ico_reader_t *file;
+ int index;
+ int error;
+ i_img **imgs;
+
+ file = ico_reader_open(ig, &error);
+ if (!file) {
+ ico_push_error(error);
+ return NULL;
+ }
+
+ imgs = mymalloc(sizeof(i_img *) * ico_image_count(file));
+
+ *count = 0;
+ for (index = 0; index < ico_image_count(file); ++index) {
+ i_img *im = read_one_icon(file, index);
+ if (!im)
+ break;
+
+ imgs[(*count)++] = im;
+ }
+
+ ico_reader_close(file);
+
+ if (*count == 0) {
+ myfree(imgs);
+ return NULL;
+ }
+
+ return imgs;
+}
--- /dev/null
+#ifndef IMAGER_IMICON_H
+#define IMAGER_IMICON_H
+
+#include "imext.h"
+
+extern i_img *
+i_readico_single(io_glue *ig, int index);
+extern i_img **
+i_readico_multi(io_glue *ig, int *count);
+
+#endif
--- /dev/null
+#include "msicon.h"
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+static int
+read_palette(ico_reader_t *file, ico_image_t *image, int *error);
+static int
+read_32bit_data(ico_reader_t *file, ico_image_t *image, int *error);
+static int
+read_8bit_data(ico_reader_t *file, ico_image_t *image, int *error);
+static int
+read_4bit_data(ico_reader_t *file, ico_image_t *image, int *error);
+static int
+read_1bit_data(ico_reader_t *file, ico_image_t *image, int *error);
+static int
+read_mask(ico_reader_t *file, ico_image_t *image, int *error);
+
+typedef struct {
+ int width;
+ int height;
+ long offset;
+ long size;
+} ico_reader_image_entry;
+
+/* this was previously declared, now define it */
+struct ico_reader_tag {
+ /* the file we're dealing with */
+ i_io_glue_t *ig;
+
+ /* number of images in the file */
+ int count;
+
+ /* type of resource - 1=icon, 2=cursor */
+ int type;
+
+ /* image information from the header */
+ ico_reader_image_entry *images;
+};
+
+static
+int read_packed(io_glue *ig, const char *format, ...);
+
+/*
+=head1 NAME
+
+msicon.c - functions for working with .ICO files.
+
+=head1 SYNOPSIS
+
+ // reading
+ int error;
+ ico_reader_t *file = ico_reader_open(ig, &error);
+ if (!file) {
+ char buffer[100];
+ ico_error_message(error, buffer, sizeof(buffer));
+ fputs(buffer, stderr);
+ exit(1);
+ }
+ int count = ico_image_count(file);
+ for (i = 0; i < count; ++i) {
+ ico_image_t *im = ico_image_read(file, index);
+ printf("%d x %d image %d\n", im->width, im->height,
+ im->direct ? "direct" : "paletted");
+ ico_image_release(im);
+ }
+ ico_reader_close(file);
+
+=head1 DESCRIPTION
+
+This is intended as a general interface to reading MS Icon files, and
+is written to be independent of Imager, even though it is part of
+Imager. You just need to supply something that acts like Imager's
+io_glue.
+
+It relies on icon images being generally small, and reads the entire
+image into memory when reading.
+
+=head1 READING ICON FILES
+
+=over
+
+=item ico_reader_open(ig, &error)
+
+Parameters:
+
+=over
+
+=item *
+
+io_glue *ig - an Imager IO object. This must be seekable.
+
+=item *
+
+int *error - pointer to an integer which an error code will be
+returned in on failure.
+
+=back
+
+=cut
+*/
+
+ico_reader_t *
+ico_reader_open(i_io_glue_t *ig, int *error) {
+ long res1, type, count;
+ ico_reader_t *file = NULL;
+ int i;
+
+ if (!read_packed(ig, "www", &res1, &type, &count)) {
+ *error = ICOERR_Short_File;
+ return NULL;
+ }
+ if (res1 != 0 || (type != 1 && type != 2) || count == 0) {
+ *error = ICOERR_Invalid_File;
+ return NULL;
+ }
+
+ file = malloc(sizeof(ico_reader_t));
+ if (!file) {
+ *error = ICOERR_Out_Of_Memory;
+ return NULL;
+ }
+ file->count = count;
+ file->type = type;
+ file->ig = ig;
+ file->images = malloc(sizeof(ico_reader_image_entry) * count);
+ if (file->images == NULL) {
+ *error = ICOERR_Out_Of_Memory;
+ free(file);
+ return NULL;
+ }
+
+ for (i = 0; i < count; ++i) {
+ long width, height, bytes_in_res, image_offset;
+ ico_reader_image_entry *image = file->images + i;
+ if (!read_packed(ig, "bbxxxxxxdd", &width, &height, &bytes_in_res,
+ &image_offset)) {
+ free(file->images);
+ free(file);
+ *error = ICOERR_Short_File;
+ return NULL;
+ }
+
+ image->width = width;
+ image->height = height;
+ image->offset = image_offset;
+ image->size = bytes_in_res;
+ }
+
+ return file;
+}
+
+/*
+=item ico_image_count
+
+ // number of images in the file
+ count = ico_image_count(file);
+
+=cut
+*/
+
+int
+ico_image_count(ico_reader_t *file) {
+ return file->count;
+}
+
+/*
+=item ico_type
+
+ // type of file - 1 for icon, 2 for cursor
+ type = ico_type(file);
+
+=cut
+*/
+
+int
+ico_type(ico_reader_t *file) {
+ return file->type;
+}
+
+/*
+=item ico_image_read
+
+Read an image from the file given it's index.
+
+=cut
+*/
+
+ico_image_t *
+ico_image_read(ico_reader_t *file, int index, int *error) {
+ io_glue *ig = file->ig;
+ ico_reader_image_entry *im;
+ long bi_size, width, height, planes, bit_count;
+ ico_image_t *result;
+
+ if (index < 0 || index >= file->count) {
+ *error = ICOERR_Bad_Image_Index;
+ return NULL;
+ }
+
+ im = file->images + index;
+ if (i_io_seek(ig, im->offset, SEEK_SET) != im->offset) {
+ *error = ICOERR_File_Error;
+ return NULL;
+ }
+
+ if (!read_packed(ig, "dddww xxxx xxxx xxxx xxxx xxxx xxxx", &bi_size,
+ &width, &height, &planes, &bit_count)) {
+ *error = ICOERR_Short_File;
+ return NULL;
+ }
+
+ /* the bitmapinfoheader height includes the height of
+ the and and xor masks */
+ if (bi_size != 40 || width != im->width || height != im->height * 2
+ || planes != 1) { /* don't know how to handle planes != 1 */
+ *error = ICOERR_Invalid_File;
+ return NULL;
+ }
+
+ result = malloc(sizeof(ico_image_t));
+ if (!result) {
+ *error = ICOERR_Out_Of_Memory;
+ return NULL;
+ }
+ result->width = width;
+ result->height = im->height;
+ result->direct = bit_count > 8;
+ result->bit_count = bit_count;
+ result->palette = NULL;
+ result->image_data = NULL;
+ result->mask_data = NULL;
+
+ if (result->direct) {
+ result->palette_size = 0;
+
+ result->image_data = malloc(result->width * result->height * sizeof(ico_color_t));
+ if (!result->image_data) {
+ free(result);
+ *error = ICOERR_Out_Of_Memory;
+ return NULL;
+ }
+ if (bit_count == 32) {
+ if (!read_32bit_data(file, result, error)) {
+ free(result->image_data);
+ free(result);
+ return NULL;
+ }
+ }
+ else {
+ *error = ICOERR_Unknown_Bits;
+ free(result->image_data);
+ free(result);
+ return NULL;
+ }
+ }
+ else {
+ int read_result;
+ result->palette_size = 1 << bit_count;
+ result->palette = malloc(sizeof(ico_color_t) * result->palette_size);
+ result->image_data = malloc(result->width * result->height);
+ if (!result->palette) {
+ free(result);
+ *error = ICOERR_Out_Of_Memory;
+ return NULL;
+ }
+
+ if (!read_palette(file, result, error)) {
+ free(result->palette);
+ free(result->image_data);
+ free(result);
+ return 0;
+ }
+
+ switch (bit_count) {
+ case 1:
+ read_result = read_1bit_data(file, result, error);
+ break;
+
+ case 4:
+ read_result = read_4bit_data(file, result, error);
+ break;
+
+ case 8:
+ read_result = read_8bit_data(file, result, error);
+ break;
+
+ default:
+ read_result = 0;
+ *error = ICOERR_Unknown_Bits;
+ break;
+ }
+
+ if (!read_result) {
+ free(result->palette);
+ free(result->image_data);
+ free(result);
+ return 0;
+ }
+ }
+
+ result->mask_data = malloc(result->width * result->height);
+ if (!result->mask_data) {
+ *error = ICOERR_Out_Of_Memory;
+ free(result->palette);
+ free(result->image_data);
+ free(result);
+ return 0;
+ }
+
+ if (!read_mask(file, result, error)) {
+ free(result->mask_data);
+ free(result->palette);
+ free(result->image_data);
+ free(result);
+ return 0;
+ }
+
+ return result;
+}
+
+/*
+=item ico_image_release
+
+Release an image structure returned by ico_image_read.
+
+=cut
+*/
+
+void
+ico_image_release(ico_image_t *image) {
+ free(image->mask_data);
+ free(image->palette);
+ free(image->image_data);
+ free(image);
+}
+
+/*
+=item ico_reader_close
+
+Releases the file structure.
+
+=cut
+*/
+
+void
+ico_reader_close(ico_reader_t *file) {
+ i_io_close(file->ig);
+ free(file->images);
+ free(file);
+}
+
+/*
+=item ico_error_message
+
+Converts an error code into an error message.
+
+=cut
+*/
+
+size_t
+ico_error_message(int error, char *buffer, size_t buffer_size) {
+ char const *msg;
+ size_t size;
+
+ switch (error) {
+ case ICOERR_Short_File:
+ msg = "Short read";
+ break;
+
+ case ICOERR_File_Error:
+ msg = "I/O error";
+ break;
+
+ case ICOERR_Invalid_File:
+ msg = "Not an icon file";
+ break;
+
+ case ICOERR_Unknown_Bits:
+ msg = "Unknown value for bits/pixel";
+ break;
+
+ case ICOERR_Bad_Image_Index:
+ msg = "Image index out of range";
+ break;
+
+ case ICOERR_Out_Of_Memory:
+ msg = "Out of memory";
+ break;
+
+ default:
+ msg = "Unknown error code";
+ break;
+ }
+
+ size = strlen(msg) + 1;
+ if (size > buffer_size)
+ size = buffer_size;
+ memcpy(buffer, msg, size);
+ buffer[size-1] = '\0';
+
+ return size;
+}
+
+/*
+=back
+
+=head1 PRIVATE FUNCTIONS
+
+=over
+
+=item read_packed
+
+Reads packed data from a stream, unpacking it.
+
+=cut
+*/
+
+static
+int read_packed(io_glue *ig, const char *format, ...) {
+ unsigned char buffer[100];
+ va_list ap;
+ long *p;
+ int size;
+ const char *formatp;
+ unsigned char *bufp;
+
+ /* read efficiently, work out the size of the buffer */
+ size = 0;
+ formatp = format;
+ while (*formatp) {
+ switch (*formatp++) {
+ case 'b':
+ case 'x': size += 1; break;
+ case 'w': size += 2; break;
+ case 'd': size += 4; break;
+ case ' ': break; /* space to separate components */
+ default:
+ fprintf(stderr, "invalid unpack char in %s\n", format);
+ exit(1);
+ }
+ }
+
+ if (size > sizeof(buffer)) {
+ /* catch if we need a bigger buffer, but 100 is plenty */
+ fprintf(stderr, "format %s too long for buffer\n", format);
+ exit(1);
+ }
+
+ if (i_io_read(ig, buffer, size) != size) {
+ return 0;
+ }
+
+ va_start(ap, format);
+
+ bufp = buffer;
+ while (*format) {
+
+ switch (*format) {
+ case 'b':
+ p = va_arg(ap, long *);
+ *p = *bufp++;
+ break;
+
+ case 'w':
+ p = va_arg(ap, long *);
+ *p = bufp[0] + (bufp[1] << 8);
+ bufp += 2;
+ break;
+
+ case 'd':
+ p = va_arg(ap, long *);
+ *p = bufp[0] + (bufp[1] << 8) + (bufp[2] << 16) + (bufp[3] << 24);
+ bufp += 4;
+ break;
+
+ case 'x':
+ ++bufp; /* skip a byte */
+ break;
+
+ case ' ':
+ /* nothing to do */
+ break;
+ }
+ ++format;
+ }
+ return 1;
+}
+
+/*
+=item read_palette
+
+Reads the palette data for an icon image.
+
+=cut
+*/
+
+static
+int
+read_palette(ico_reader_t *file, ico_image_t *image, int *error) {
+ int palette_bytes = image->palette_size * 4;
+ unsigned char *read_buffer = malloc(palette_bytes);
+ unsigned char *inp;
+ ico_color_t *outp;
+ int i;
+
+ if (!read_buffer) {
+ *error = ICOERR_Out_Of_Memory;
+ return 0;
+ }
+
+ if (i_io_read(file->ig, read_buffer, palette_bytes) != palette_bytes) {
+ *error = ICOERR_Short_File;
+ free(read_buffer);
+ return 0;
+ }
+
+ inp = read_buffer;
+ outp = image->palette;
+ for (i = 0; i < image->palette_size; ++i) {
+ outp->b = *inp++;
+ outp->g = *inp++;
+ outp->r = *inp++;
+ outp->a = 255;
+ ++inp;
+ ++outp;
+ }
+ free(read_buffer);
+
+ return 1;
+}
+
+/*
+=item read_32bit_data
+
+Reads 32 bit image data.
+
+=cut
+*/
+
+static
+int
+read_32bit_data(ico_reader_t *file, ico_image_t *image, int *error) {
+ int line_bytes = image->width * 4;
+ unsigned char *buffer = malloc(line_bytes);
+ int y;
+ int x;
+ unsigned char *inp;
+ ico_color_t *outp;
+
+ if (!buffer) {
+ *error = ICOERR_Out_Of_Memory;
+ return 0;
+ }
+
+ for (y = image->height - 1; y >= 0; --y) {
+ if (i_io_read(file->ig, buffer, line_bytes) != line_bytes) {
+ free(buffer);
+ *error = ICOERR_Short_File;
+ return 0;
+ }
+ outp = image->image_data;
+ outp += y * image->width;
+ inp = buffer;
+ for (x = 0; x < image->width; ++x) {
+ outp->b = inp[0];
+ outp->g = inp[1];
+ outp->r = inp[2];
+ outp->a = inp[3];
+ ++outp;
+ inp += 4;
+ }
+ }
+ free(buffer);
+
+ return 1;
+}
+
+/*
+=item read_8bit_data
+
+Reads 8 bit image data.
+
+=cut
+*/
+
+static
+int
+read_8bit_data(ico_reader_t *file, ico_image_t *image, int *error) {
+ int line_bytes = (image->width + 3) / 4 * 4;
+ unsigned char *buffer = malloc(line_bytes);
+ int y;
+ int x;
+ unsigned char *inp, *outp;
+
+ if (!buffer) {
+ *error = ICOERR_Out_Of_Memory;
+ return 0;
+ }
+
+ for (y = image->height - 1; y >= 0; --y) {
+ outp = image->image_data;
+ outp += y * image->width;
+ if (i_io_read(file->ig, buffer, line_bytes) != line_bytes) {
+ free(buffer);
+ *error = ICOERR_Short_File;
+ return 0;
+ }
+ for (x = 0, inp = buffer; x < image->width; ++x) {
+ *outp++ = *inp++;
+ }
+ }
+ free(buffer);
+
+ return 1;
+}
+
+/*
+=item read_4bit_data
+
+Reads 4 bit image data.
+
+=cut
+*/
+
+static
+int
+read_4bit_data(ico_reader_t *file, ico_image_t *image, int *error) {
+ /* 2 pixels per byte, rounded up to the nearest dword */
+ int line_bytes = ((image->width + 1) / 2 + 3) / 4 * 4;
+ unsigned char *read_buffer = malloc(line_bytes);
+ int y;
+ int x;
+ unsigned char *inp, *outp;
+
+ if (!read_buffer) {
+ *error = ICOERR_Out_Of_Memory;
+ return 0;
+ }
+
+ for (y = image->height - 1; y >= 0; --y) {
+ if (i_io_read(file->ig, read_buffer, line_bytes) != line_bytes) {
+ free(read_buffer);
+ *error = ICOERR_Short_File;
+ return 0;
+ }
+
+ outp = image->image_data;
+ outp += y * image->width;
+ inp = read_buffer;
+ for (x = 0; x < image->width; ++x) {
+ /* yes, this is kind of ugly */
+ if (x & 1) {
+ *outp++ = *inp++ & 0x0F;
+ }
+ else {
+ *outp++ = *inp >> 4;
+ }
+ }
+ }
+ free(read_buffer);
+
+ return 1;
+}
+
+/*
+=item read_1bit_data
+
+Reads 1 bit image data.
+
+=cut
+*/
+
+static
+int
+read_1bit_data(ico_reader_t *file, ico_image_t *image, int *error) {
+ /* 8 pixels per byte, rounded up to the nearest dword */
+ int line_bytes = ((image->width + 7) / 8 + 3) / 4 * 4;
+ unsigned char *read_buffer = malloc(line_bytes);
+ int y;
+ int x;
+ unsigned char *inp, *outp;
+
+ if (!read_buffer) {
+ *error = ICOERR_Out_Of_Memory;
+ return 0;
+ }
+
+ for (y = image->height - 1; y >= 0; --y) {
+ if (i_io_read(file->ig, read_buffer, line_bytes) != line_bytes) {
+ free(read_buffer);
+ *error = ICOERR_Short_File;
+ return 0;
+ }
+
+ outp = image->image_data;
+ outp += y * image->width;
+ inp = read_buffer;
+ for (x = 0; x < image->width; ++x) {
+ *outp++ = (*inp >> (7 - (x & 7))) & 1;
+ if ((x & 7) == 7)
+ ++inp;
+ }
+ }
+ free(read_buffer);
+
+ return 1;
+}
+
+/* this is very similar to the 1 bit reader <sigh> */
+/*
+=item read_mask
+
+Reads the AND mask from an icon image.
+
+=cut
+*/
+
+static
+int
+read_mask(ico_reader_t *file, ico_image_t *image, int *error) {
+ /* 8 pixels per byte, rounded up to the nearest dword */
+ int line_bytes = ((image->width + 7) / 8 + 3) / 4 * 4;
+ unsigned char *read_buffer = malloc(line_bytes);
+ int y;
+ int x;
+ unsigned char *inp, *outp;
+
+ if (!read_buffer) {
+ *error = ICOERR_Out_Of_Memory;
+ return 0;
+ }
+
+ for (y = image->height - 1; y >= 0; --y) {
+ if (i_io_read(file->ig, read_buffer, line_bytes) != line_bytes) {
+ free(read_buffer);
+ *error = ICOERR_Short_File;
+ return 0;
+ }
+
+ outp = image->mask_data + y * image->width;
+ inp = read_buffer;
+ for (x = 0; x < image->width; ++x) {
+ *outp++ = (*inp >> (7 - (x & 7))) & 1;
+ if ((x & 7) == 7)
+ ++inp;
+ }
+ }
+ free(read_buffer);
+
+ return 1;
+}
+
+/*
+=back
+
+=head1 AUTHOR
+
+Tony Cook <tony@imager.perl.org>
+
+=head1 REVISION
+
+$Revision$
+
+=cut
+*/
--- /dev/null
+#ifndef IMAGER_MSICON_H_
+#define IMAGER_MSICON_H_
+
+#include "iolayer.h"
+
+typedef struct ico_reader_tag ico_reader_t;
+
+#define ICON_ICON 1
+#define ICON_CURSOR 2
+
+typedef struct {
+ unsigned char r, g, b, a;
+} ico_color_t;
+
+typedef struct {
+ int width;
+ int height;
+ int direct;
+ int bit_count;
+ void *image_data;
+ int palette_size;
+ ico_color_t *palette;
+ unsigned char *mask_data;
+} ico_image_t;
+
+extern ico_reader_t *ico_reader_open(i_io_glue_t *ig, int *error);
+extern int ico_image_count(ico_reader_t *file);
+extern int ico_type(ico_reader_t *file);
+extern ico_image_t *ico_image_read(ico_reader_t *file, int index, int *error);
+extern void ico_image_release(ico_image_t *image);
+extern void ico_reader_close(ico_reader_t *file);
+
+extern size_t ico_error_message(int error, char *buffer, size_t buffer_size);
+
+#define ICO_MAX_MESSAGE 80
+
+#define ICOERR_Short_File 100
+#define ICOERR_File_Error 101
+
+#define ICOERR_Invalid_File 200
+#define ICOERR_Unknown_Bits 201
+
+#define ICOERR_Bad_Image_Index 300
+
+#define ICOERR_Out_Of_Memory 400
+
+#endif
--- /dev/null
+#!perl -w
+use strict;
+use Test::More tests => 60;
+
+BEGIN { use_ok('Imager::File::ICO'); }
+
+-d 'testout' or mkdir 'testout';
+
+my $im = Imager->new;
+ok($im->read(file => "testimg/rgba3232.ico", type=>"ico"),
+ "read 32 bit")
+ or print "# ", $im->errstr, "\n";
+is($im->getwidth, 32, "check width");
+is($im->getwidth, 32, "check height");
+is($im->type, 'direct', "check type");
+is($im->tags(name => 'ico_bits'), 32, "check ico_bits tag");
+is($im->tags(name => 'i_format'), 'ico', "check i_format tag");
+is($im->tags(name => 'ico_type'), 'icon', "check ico_type tag");
+my $mask = '.*
+..........................******
+..........................******
+..........................******
+..........................******
+...........................*****
+............................****
+............................****
+.............................***
+.............................***
+.............................***
+.............................***
+..............................**
+..............................**
+...............................*
+...............................*
+................................
+................................
+................................
+................................
+................................
+................................
+*...............................
+**..............................
+**..............................
+***.............................
+***.............................
+****............................
+****............................
+*****...........................
+*****...........................
+*****...........................
+*****...........................';
+is($im->tags(name => 'ico_mask'), $mask, "check ico_mask_tag");
+
+# compare the pixels
+# ppm can't store 4 channels
+SKIP:
+{
+ my $work = $im->convert(preset=>'noalpha');
+ my $comp = Imager->new;
+ $comp->read(file => "testimg/rgba3232.ppm")
+ or skip "could not read 24-bit comparison file:". $comp->errstr, 1;
+ is(Imager::i_img_diff($comp->{IMG}, $work->{IMG}), 0,
+ "compare image data");
+}
+
+ok($im->read(file => 'testimg/pal83232.ico', type=>'ico'),
+ "read 8 bit")
+ or print "# ", $im->errstr, "\n";
+is($im->getwidth, 32, "check width");
+is($im->getwidth, 32, "check height");
+is($im->type, 'paletted', "check type");
+is($im->colorcount, 256, "color count");
+is($im->tags(name => 'ico_bits'), 8, "check ico_bits tag");
+is($im->tags(name => 'i_format'), 'ico', "check i_format tag");
+is($im->tags(name => 'ico_type'), 'icon', "check ico_type tag");
+SKIP:
+{
+ my $comp = Imager->new;
+ $comp->read(file => "testimg/pal83232.ppm")
+ or skip "could not read 8-bit comparison file:". $comp->errstr, 1;
+ is(Imager::i_img_diff($comp->{IMG}, $im->{IMG}), 0,
+ "compare image data");
+}
+$im->write(file=>'testout/pal83232.ppm');
+
+ok($im->read(file => 'testimg/pal43232.ico', type=>'ico'),
+ "read 4 bit")
+ or print "# ", $im->errstr, "\n";
+is($im->getwidth, 32, "check width");
+is($im->getwidth, 32, "check height");
+is($im->type, 'paletted', "check type");
+is($im->colorcount, 16, "color count");
+is($im->tags(name => 'ico_bits'), 4, "check ico_bits tag");
+is($im->tags(name => 'i_format'), 'ico', "check i_format tag");
+is($im->tags(name => 'ico_type'), 'icon', "check ico_type tag");
+SKIP:
+{
+ my $comp = Imager->new;
+ $comp->read(file => "testimg/pal43232.ppm")
+ or skip "could not read 4-bit comparison file:". $comp->errstr, 1;
+ is(Imager::i_img_diff($comp->{IMG}, $im->{IMG}), 0,
+ "compare image data");
+}
+
+$im->write(file=>'testout/pal43232.ppm');
+ok($im->read(file => 'testimg/pal13232.ico', type=>'ico'),
+ "read 1 bit")
+ or print "# ", $im->errstr, "\n";
+is($im->getwidth, 32, "check width");
+is($im->getwidth, 32, "check height");
+is($im->type, 'paletted', "check type");
+is($im->colorcount, 2, "color count");
+is($im->tags(name => 'ico_bits'), 1, "check ico_bits tag");
+is($im->tags(name => 'i_format'), 'ico', "check i_format tag");
+is($im->tags(name => 'ico_type'), 'cursor', "check ico_type tag");
+$im->write(file=>'testout/pal13232.ppm');
+
+# combo was created with the GIMP, which has a decent mechanism for selecting
+# the output format
+# you get different size icon images by making different size layers.
+my @imgs = Imager->read_multi(file => 'testimg/combo.ico', type=>'ico');
+is(scalar(@imgs), 3, "read multiple");
+is($imgs[0]->getwidth, 48, "image 0 width");
+is($imgs[0]->getheight, 48, "image 0 height");
+is($imgs[1]->getwidth, 32, "image 1 width");
+is($imgs[1]->getheight, 32, "image 1 height");
+is($imgs[2]->getwidth, 16, "image 2 width");
+is($imgs[2]->getheight, 16, "image 2 height");
+is($imgs[0]->type, 'direct', "image 0 type");
+is($imgs[1]->type, 'paletted', "image 1 type");
+is($imgs[2]->type, 'paletted', "image 2 type");
+is($imgs[1]->colorcount, 256, "image 1 colorcount");
+is($imgs[2]->colorcount, 16, "image 2 colorcount");
+
+is_deeply([ $imgs[0]->getpixel(x=>0, 'y'=>0)->rgba ], [ 231, 17, 67, 255 ],
+ "check image data 0(0,0)");
+is_deeply([ $imgs[1]->getpixel(x=>0, 'y'=>0)->rgba ], [ 231, 17, 67, 255 ],
+ "check image data 1(0,0)");
+is_deeply([ $imgs[2]->getpixel(x=>0, 'y'=>0)->rgba ], [ 231, 17, 67, 255 ],
+ "check image data 2(0,0)");
+
+is_deeply([ $imgs[0]->getpixel(x=>47, 'y'=>0)->rgba ], [ 131, 231, 17, 255 ],
+ "check image data 0(47,0)");
+is_deeply([ $imgs[1]->getpixel(x=>31, 'y'=>0)->rgba ], [ 131, 231, 17, 255 ],
+ "check image data 1(31,0)");
+is_deeply([ $imgs[2]->getpixel(x=>15, 'y'=>0)->rgba ], [ 131, 231, 17, 255 ],
+ "check image data 2(15,0)");
+
+is_deeply([ $imgs[0]->getpixel(x=>0, 'y'=>47)->rgba ], [ 17, 42, 231, 255 ],
+ "check image data 0(0,47)");
+is_deeply([ $imgs[1]->getpixel(x=>0, 'y'=>31)->rgba ], [ 17, 42, 231, 255 ],
+ "check image data 1(0,31)");
+is_deeply([ $imgs[2]->getpixel(x=>0, 'y'=>15)->rgba ], [ 17, 42, 231, 255 ],
+ "check image data 2(0,15)");
+
+is_deeply([ $imgs[0]->getpixel(x=>47, 'y'=>47)->rgba ], [ 17, 231, 177, 255 ],
+ "check image data 0(47,47)");
+is_deeply([ $imgs[1]->getpixel(x=>31, 'y'=>31)->rgba ], [ 17, 231, 177, 255 ],
+ "check image data 1(31,31)");
+is_deeply([ $imgs[2]->getpixel(x=>15, 'y'=>15)->rgba ], [ 17, 231, 177, 255 ],
+ "check image data 2(15,15)");
--- /dev/null
+#!perl -w
+use strict;
+use Test::More tests => 1;
+use Imager;
+
+# checks that we load the ICO handler automatically
+my $im = Imager->new;
+ok($im->read(file => 'testimg/rgba3232.ico'),
+ "check that icon reader loaded correctly for singles")
+ or print "# ", $im->errstr, "\n";
--- /dev/null
+#!perl -w
+use strict;
+use Test::More tests => 1;
+use Imager;
+
+# checks that we load the ICO handler automatically
+my @imgs = Imager->read_multi(file => 'testimg/combo.ico')
+ or print "# ",Imager->errstr,"\n";
+is(@imgs, 3,
+ "check that icon reader loaded correctly for multiples");
--- /dev/null
+P6
+#CREATOR: Imager
+32 32
+255
+\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 43| \ 69\ 43| 3| 3| 3| 3| 3| 3| 3| `¿4r¡\er¡\eõ(\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 43| \ 69\ 43| \ 69\ 43| 3| 3| 3| `¿43| `¿4r¡\er¡\eõ(õ(õ(\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 43| 3| 3| 3| 3| 3| 3| 3| 3| 3| `¿4r¡\er¡\eõ(õ(õ(ï\9a*ï\9a*\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 43| \ 69\ 43| \ 69\ 43| 3| 3| 3| 3| `¿4r¡\er¡\eõ(õ(õ(ï\9a*õ(ï\9a*ï\9a*ï\9a*\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 43| 3| 3| 3| 3| 3| 3| 3| 3| `¿43| r¡\er¡\eõ(õ(õ(õ(ï\9a*ï\9a*ï\9a*ï\9a*ÃO*ÃO*\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 43| \ 69\ 43| 3| 3| 3| `¿43| r¡\er¡\er¡\eõ(õ(õ(ï\9a*ï\9a*ï\9a*ï\9a*ï\9a*ëA#ï\9a*ëA#ëA#\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 4\ 69\ 43| 3| 3| 3| 3| 3| 3| 3| 3| 3| `¿4r¡\er¡\eõ(õ(õ(ï\9a*ï\9a*ï\9a*ï\9a*ï\9a*ëA#ëA#ëA#ëA#ëA#ëA#\ 69\ 4\ 69\ 4\ 69\ 43| \ 69\ 43| 3| 3| 3| 3| 3| 3| `¿4r¡\er¡\eõ(õ(ï\9a*õ(ï\9a*ï\9a*ï\9a*ï\9a*ï\9a*ÃO*ëA#ëA#ëA#ëA#ëA#ëA#ëA#3| 3| 3| 3| 3| 3| 3| 3| 3| `¿43| r¡\er¡\eõ(õ(õ(ï\9a*õ(ï\9a*ï\9a*ï\9a*ÃO*ëA#ëA#ëA#ëA#ëA#ëA#ëA#ëA#ÃO*ÃO*\ 69\ 43| 3| 3| 3| 3| 3| 3| r¡\er¡\eõ(õ(õ(õ(ï\9a*ï\9a*ï\9a*ï\9a*ï\9a*ëA#ëA#ëA#ëA#ëA#ëA#ëA#ëA#ëA#ÃO*ÃO*ÃO*ÃO*3| 3| 3| 3| 3| `¿4r¡\er¡\eõ(õ(õ(ï\9a*ï\9a*ï\9a*ï\9a*ï\9a*ëA#ëA#ëA#ëA#ëA#ëA#ëA#ëA#ëA#ÃO*ÃO*ÃO*ÃO*ÃO*ÃO*\94OB3| 3| `¿4r¡\er¡\er¡\eõ(õ(õ(õ(ï\9a*ï\9a*ï\9a*ï\9a*ëA#ï\9a*ëA#ëA#ëA#ëA#ëA#ëA#ëA#ÃO*ÃO*ÃO*ÃO*ÃO*\94OBÃO*\94OB\94OB3| r¡\er¡\eõ(õ(õ(ï\9a*ï\9a*ï\9a*ï\9a*ï\9a*ï\9a*ëA#ëA#ëA#ëA#ëA#ëA#ëA#ëA#ÃO*ÃO*ÃO*ÃO*ÃO*ÃO*\94OBÃO*\94OB\94OB\94OB\94OBr¡\eõ(õ(õ(õ(ï\9a*ï\9a*ï\9a*ï\9a*ÃO*ëA#ëA#ëA#ëA#ëA#ëA#ëA#ÃO*ëA#ÃO*ÃO*ÃO*ÃO*ÃO*ÃO*\94OB\94OB\94OB\94OB\94OB\83O\88\83O\88õ(õ(ï\9a*ï\9a*ï\9a*ï\9a*ï\9a*ëA#ëA#ëA#ëA#ëA#ëA#ëA#ëA#ëA#ëA#ÃO*ÃO*ÃO*ÃO*\94OBÃO*\94OB\94OB\94OB\94OB\83O\88\83O\88\83O\88\83O\88}VÃï\9a*ï\9a*ï\9a*ï\9a*ï\9a*ëA#ï\9a*ëA#ëA#ëA#ëA#ëA#ëA#ëA#ÃO*ÃO*ÃO*ÃO*ÃO*\94OBÃO*\94OB\94OB\94OB\94OB\83O\88\83O\88\83O\88\83O\88}VÃ}VÃ}VÃï\9a*ï\9a*ÃO*ëA#ëA#ëA#ëA#ëA#ëA#ëA#ëA#ÃO*ÃO*ÃO*ÃO*ÃO*ÃO*\94OB\94OB\94OB\94OB\94OB\83O\88\83O\88\83O\88\83O\88\83O\88}VÃ}VÃ}VÃ}VÃ}VÃï\9a*ëA#ëA#ëA#ëA#ëA#ëA#ëA#ëA#ÃO*ÃO*ÃO*ÃO*ÃO*\94OBÃO*\94OB\94OB\94OB\94OB\83O\88\83O\88\83O\88\83O\88}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ}VÃëA#ëA#ëA#ëA#ëA#ëA#ÃO*ÃO*ÃO*ÃO*ÃO*ÃO*\94OBÃO*\94OB\94OB\94OB\94OB\83O\88\83O\88\83O\88\83O\88}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ\86\82ô\86\82ôëA#ëA#ëA#ëA#ëA#ÃO*ÃO*ÃO*ÃO*\94OBÃO*\94OB\94OB\94OB\94OB\83O\88\83O\88\83O\88\83O\88\83O\88}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ\86\82ô\86\82ô\86\82ô\86\82ô\91°ùëA#ëA#ÃO*ÃO*ÃO*ÃO*ÃO*ÃO*\94OBÃO*\94OB\94OB\94OB\83O\88\83O\88\83O\88\83O\88}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ\86\82ô\86\82ô\86\82ô\86\82ô\91°ù\91°ù\91°ùÃO*ÃO*ÃO*ÃO*\94OBÃO*\94OB\94OB\94OB\94OB\94OB\83O\88\83O\88\83O\88\83O\88}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ\86\82ô\86\82ô\86\82ô\86\82ô\91°ù\91°ù\9dæö\9dæö\9dæöÃO*ÃO*\94OBÃO*\94OB\94OB\94OB\94OB\83O\88\83O\88\83O\88\83O\88\86\82ô}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ\86\82ô\86\82ô\86\82ô\86\82ô\91°ù\91°ù\91°ù\9dæö\9dæö\9dæö\94öÃ\94öÃÃO*ÃO*\94OB\94OB\94OB\94OB\83O\88\83O\88\83O\88\83O\88}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ\86\82ô\86\82ô\86\82ô\91°ù\91°ù\91°ù\9dæö\9dæö\9dæö\94öÃ\94öÃ\94öÃ`¿4\94OB\94OB\94OB\94OB\83O\88\83O\88\83O\88\86\82ô}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ\86\82ô\86\82ô\91°ù\86\82ô\91°ù\91°ù\9dæö\9dæö\9dæö\94öÃ\94öÃ\94öÃ\94öÃ`¿4`¿4`¿4\94OB\83O\88\83O\88\83O\88\83O\88\83O\88}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ\86\82ô\86\82ô\86\82ô\86\82ô\91°ù\91°ù\91°ù\9dæö\9dæö\9dæö\94öÃ\94öÃ\94öÃ`¿4`¿4`¿4`¿4`¿4`¿4\83O\88\83O\88\83O\88}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ\86\82ô\86\82ô\86\82ô\86\82ô\91°ù\91°ù\91°ù\9dæö\9dæö\94öÃ\94öÃ\94öÃ\94öÃ`¿4`¿4`¿4`¿4`¿4`¿4`¿4`¿4\83O\88}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ\86\82ô\86\82ô\86\82ô\91°ù\91°ù\91°ù\9dæö\9dæö\9dæö\94öÃ\94öÃ\94öÃ\94öÃ`¿4`¿4`¿4`¿4`¿4`¿4`¿4r¡\er¡\er¡\e}VÃ}VÃ}VÃ}VÃ}VÃ}VÃ\86\82ô\86\82ô\86\82ô\86\82ô\91°ù\91°ù\91°ù\9dæö\9dæö\94öÃ\94öÃ\94öÃ\94öÃ`¿4`¿4`¿4`¿4`¿4`¿4`¿4`¿4r¡\er¡\er¡\er¡\e3| }VÃ}VÃ}VÃ}VÃ\86\82ô\86\82ô\86\82ô\86\82ô\91°ù\91°ù\91°ù\9dæö\9dæö\9dæö\94öÃ\94öÃ\94öÃ`¿4`¿4`¿4`¿4`¿4`¿4`¿4`¿4r¡\er¡\er¡\e3| r¡\e3| r¡\e}VÃ\86\82ô\86\82ô\86\82ô\86\82ô\91°ù\91°ù\91°ù\9dæö\9dæö\9dæö\94öÃ\94öÃ\94öÃ\94öÃ`¿4`¿4`¿4`¿4`¿4`¿4`¿4r¡\er¡\er¡\er¡\er¡\er¡\e3| õ(\ 69\ 4r¡\e\86\82ô\86\82ô\86\82ô\91°ù\91°ù\91°ù\9dæö\9dæö\9dæö\94öÃ\94öÃ\94öÃ`¿4`¿4`¿4`¿4`¿4`¿4`¿4`¿4r¡\er¡\er¡\er¡\er¡\e3| õ(\ 69\ 43| r¡\er¡\er¡\e
\ No newline at end of file
Flines/Flines.xs
Flines/Makefile.PL
Flines/t/t00flines.t
+ICO/ICO.pm Windows Icon file support
+ICO/ICO.xs
+ICO/Makefile.PL
+ICO/imicon.c
+ICO/imicon.h
+ICO/msicon.c
+ICO/msicon.h
+ICO/t/t10icon.t
+ICO/t/t20readone.t
+ICO/t/t21readmult.t
+ICO/testimg/combo.ico
+ICO/testimg/pal13232.ico
+ICO/testimg/pal43232.ico
+ICO/testimg/pal43232.ppm
+ICO/testimg/pal83232.ico
+ICO/testimg/pal83232.ppm
+ICO/testimg/rgba3232.ico
+ICO/testimg/rgba3232.ppm
Imager.pm
Imager.xs
MANIFEST
spot.perl For making an ordered dither matrix from a spot function
stackmach.c
stackmach.h
+t/Pod/Coverage/Imager.pm
t/Test/Builder.pm
t/Test/More.pm
t/t00basic.t
# editor trash
~$
+^\#.*\#$
# stuff we don't distribute
^TODO$
^meta\.tmp$
/meta\.tmp$
^imconfig\.h$
+^ICO/ICO\.c$
+^ICO/testout
+