- implement reading MS Windows icon files
authorTony Cook <tony@develop=help.com>
Fri, 24 Mar 2006 09:49:57 +0000 (09:49 +0000)
committerTony Cook <tony@develop=help.com>
Fri, 24 Mar 2006 09:49:57 +0000 (09:49 +0000)
- add t/Pod/Coverage/Imager.pm to MANIFEST

- skip some more trash in MANIFEST.SKIP

20 files changed:
ICO/ICO.pm [new file with mode: 0644]
ICO/ICO.xs [new file with mode: 0644]
ICO/Makefile.PL [new file with mode: 0644]
ICO/imicon.c [new file with mode: 0644]
ICO/imicon.h [new file with mode: 0644]
ICO/msicon.c [new file with mode: 0644]
ICO/msicon.h [new file with mode: 0644]
ICO/t/t10icon.t [new file with mode: 0644]
ICO/t/t20readone.t [new file with mode: 0644]
ICO/t/t21readmult.t [new file with mode: 0644]
ICO/testimg/combo.ico [new file with mode: 0644]
ICO/testimg/pal13232.ico [new file with mode: 0644]
ICO/testimg/pal43232.ico [new file with mode: 0644]
ICO/testimg/pal43232.ppm [new file with mode: 0644]
ICO/testimg/pal83232.ico [new file with mode: 0644]
ICO/testimg/pal83232.ppm [new file with mode: 0644]
ICO/testimg/rgba3232.ico [new file with mode: 0644]
ICO/testimg/rgba3232.ppm [new file with mode: 0644]
MANIFEST
MANIFEST.SKIP

diff --git a/ICO/ICO.pm b/ICO/ICO.pm
new file mode 100644 (file)
index 0000000..6f038d6
--- /dev/null
@@ -0,0 +1,78 @@
+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
diff --git a/ICO/ICO.xs b/ICO/ICO.xs
new file mode 100644 (file)
index 0000000..d33bd2b
--- /dev/null
@@ -0,0 +1,40 @@
+#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;
diff --git a/ICO/Makefile.PL b/ICO/Makefile.PL
new file mode 100644 (file)
index 0000000..aa037ae
--- /dev/null
@@ -0,0 +1,17 @@
+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);
+
+
diff --git a/ICO/imicon.c b/ICO/imicon.c
new file mode 100644 (file)
index 0000000..5655536
--- /dev/null
@@ -0,0 +1,182 @@
+#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;
+}
diff --git a/ICO/imicon.h b/ICO/imicon.h
new file mode 100644 (file)
index 0000000..855e7c9
--- /dev/null
@@ -0,0 +1,11 @@
+#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
diff --git a/ICO/msicon.c b/ICO/msicon.c
new file mode 100644 (file)
index 0000000..5e07fde
--- /dev/null
@@ -0,0 +1,767 @@
+#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
+*/
diff --git a/ICO/msicon.h b/ICO/msicon.h
new file mode 100644 (file)
index 0000000..0b26b60
--- /dev/null
@@ -0,0 +1,47 @@
+#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
diff --git a/ICO/t/t10icon.t b/ICO/t/t10icon.t
new file mode 100644 (file)
index 0000000..c89ae9a
--- /dev/null
@@ -0,0 +1,161 @@
+#!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)");
diff --git a/ICO/t/t20readone.t b/ICO/t/t20readone.t
new file mode 100644 (file)
index 0000000..9fc04c8
--- /dev/null
@@ -0,0 +1,10 @@
+#!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";
diff --git a/ICO/t/t21readmult.t b/ICO/t/t21readmult.t
new file mode 100644 (file)
index 0000000..1a62c6e
--- /dev/null
@@ -0,0 +1,10 @@
+#!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");
diff --git a/ICO/testimg/combo.ico b/ICO/testimg/combo.ico
new file mode 100644 (file)
index 0000000..bffefaa
Binary files /dev/null and b/ICO/testimg/combo.ico differ
diff --git a/ICO/testimg/pal13232.ico b/ICO/testimg/pal13232.ico
new file mode 100644 (file)
index 0000000..701e14f
Binary files /dev/null and b/ICO/testimg/pal13232.ico differ
diff --git a/ICO/testimg/pal43232.ico b/ICO/testimg/pal43232.ico
new file mode 100644 (file)
index 0000000..47b6ccc
Binary files /dev/null and b/ICO/testimg/pal43232.ico differ
diff --git a/ICO/testimg/pal43232.ppm b/ICO/testimg/pal43232.ppm
new file mode 100644 (file)
index 0000000..7a0204f
--- /dev/null
@@ -0,0 +1,5 @@
+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¡\e\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¡\e\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¡\e\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¡\e\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¡\e\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¡\e\e\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¡\e\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¡\e\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¡\e\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¡\e\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¡\e\eÁµ(Áµ(Áµ(ï\9a\9a\9a\9a\9a*ëA#ëA#ëA#ëA#ëA#ëA#ëA#ëA#ëA#ÕO*ÕO*ÕO*ÕO*ÕO*ÕO*\94OB3|     3|      `¿4r¡\e\e\eÁµ(Áµ(Áµ(Áµ(ï\9a\9a\9a\9a*ëA#ï\9a*ëA#ëA#ëA#ëA#ëA#ëA#ëA#ÕO*ÕO*ÕO*ÕO*ÕO*\94OBÕO*\94OB\94OB3|    r¡\e\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¡\e\e\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¡\e\e\e\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¡\e\e\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¡\e\e\e\e\e\e3|       Áµ(\ 69\ 4\e\86\82ô\86\82ô\86\82ô\91°ù\91°ù\91°ù\9dæö\9dæö\9dæö\94öÄ\94öÄ\94öÄ`¿4`¿4`¿4`¿4`¿4`¿4`¿4`¿4r¡\e\e\e\e\e3|  Áµ(\ 69\ 43|        r¡\e\e\e
\ No newline at end of file
diff --git a/ICO/testimg/pal83232.ico b/ICO/testimg/pal83232.ico
new file mode 100644 (file)
index 0000000..37ae693
Binary files /dev/null and b/ICO/testimg/pal83232.ico differ
diff --git a/ICO/testimg/pal83232.ppm b/ICO/testimg/pal83232.ppm
new file mode 100644 (file)
index 0000000..f4b3083
Binary files /dev/null and b/ICO/testimg/pal83232.ppm differ
diff --git a/ICO/testimg/rgba3232.ico b/ICO/testimg/rgba3232.ico
new file mode 100644 (file)
index 0000000..5e6fc54
Binary files /dev/null and b/ICO/testimg/rgba3232.ico differ
diff --git a/ICO/testimg/rgba3232.ppm b/ICO/testimg/rgba3232.ppm
new file mode 100644 (file)
index 0000000..47dceca
Binary files /dev/null and b/ICO/testimg/rgba3232.ppm differ
index 2fb5b74..6bee1b1 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -13,6 +13,24 @@ Flines/Flines.pm
 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
@@ -158,6 +176,7 @@ samples/tk-photo.pl
 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
index 3fb55bf..2f31f94 100644 (file)
@@ -4,6 +4,7 @@
 
 # editor trash
 ~$
+^\#.*\#$
 
 # stuff we don't distribute
 ^TODO$
@@ -40,3 +41,6 @@ Makefile\.old
 ^meta\.tmp$
 /meta\.tmp$
 ^imconfig\.h$
+^ICO/ICO\.c$
+^ICO/testout
+