diff --git a/configure.ac b/configure.ac index 231f6a173d..c803fd00a5 100644 --- a/configure.ac +++ b/configure.ac @@ -3091,6 +3091,7 @@ plug-ins/file-exr/Makefile plug-ins/file-faxg3/Makefile plug-ins/file-fits/Makefile plug-ins/file-fli/Makefile +plug-ins/file-icns/Makefile plug-ins/file-ico/Makefile plug-ins/file-jpeg/Makefile plug-ins/file-psd/Makefile diff --git a/plug-ins/Makefile.am b/plug-ins/Makefile.am index 6384022430..06d5cabfeb 100644 --- a/plug-ins/Makefile.am +++ b/plug-ins/Makefile.am @@ -33,6 +33,7 @@ SUBDIRS = \ file-faxg3 \ file-fits \ file-fli \ + file-icns \ file-ico \ file-jpeg \ file-psd \ diff --git a/plug-ins/file-icns/Makefile.am b/plug-ins/file-icns/Makefile.am new file mode 100644 index 0000000000..5ece7f8f86 --- /dev/null +++ b/plug-ins/file-icns/Makefile.am @@ -0,0 +1,55 @@ +## Process this file with automake to produce Makefile.in + +libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la +libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la +libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la +libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la +libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la +libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la +libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la + +if OS_WIN32 +mwindows = -mwindows +endif + +if HAVE_WINDRES +include $(top_srcdir)/build/windows/gimprc-plug-ins.rule +file_icns_RC = file-icns.rc.o +endif + +AM_LDFLAGS = $(mwindows) + +libexecdir = $(gimpplugindir)/plug-ins/file-icns + +libexec_PROGRAMS = file-icns + +file_icns_CFLAGS = $(PNG_CFLAGS) + +file_icns_SOURCES = \ + file-icns-data.c \ + file-icns-data.h \ + file-icns-load.c \ + file-icns-load.h \ + file-icns.c \ + file-icns.h + +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + $(GTK_CFLAGS) \ + $(GEGL_CFLAGS) \ + -I$(includedir) + +LDADD = \ + $(libgimpui) \ + $(libgimpwidgets) \ + $(libgimpconfig) \ + $(libgimp) \ + $(libgimpcolor) \ + $(libgimpmath) \ + $(libgimpbase) \ + $(GTK_LIBS) \ + $(GEGL_LIBS) \ + $(PNG_LIBS) \ + $(RT_LIBS) \ + $(INTLLIBS) \ + $(file_icns_RC) diff --git a/plug-ins/file-icns/file-icns-data.c b/plug-ins/file-icns/file-icns-data.c new file mode 100644 index 0000000000..febb56ed4b --- /dev/null +++ b/plug-ins/file-icns/file-icns-data.c @@ -0,0 +1,373 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis + * + * file-icns-data.c + * Copyright (C) 2004 Brion Vibber + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include "file-icns-data.h" + +/* Ported from Brion Vibber's icnsdata.c code, under the GPL license, version 3 + * or any later version of the license */ +IconType iconTypes[] = +{ + /* 1-bit, no mask? */ + {"SICN", 16, 16, 1, 0, FALSE}, + {"ICON", 32, 32, 1, 0, FALSE}, + + /* 1-bit image and hitmask */ + {"icm#", 12, 12, 1, "icm#", FALSE}, + {"ics#", 16, 16, 1, "ics#", FALSE}, + {"ICN#", 32, 32, 1, "ICN#", FALSE}, + {"ich#", 48, 48, 1, "ich#", FALSE}, + + /* 4-bit. Use mask from binary. */ + {"icm4", 12, 12, 4, "icm#", FALSE}, + {"ics4", 16, 16, 4, "ics#", FALSE}, + {"icl4", 32, 32, 4, "ICN#", FALSE}, + {"ich4", 48, 48, 4, "ich#", FALSE}, + + /* 8-bit. Use mask from binary. */ + {"icm8", 12, 12, 8, "icm#", FALSE}, + {"ics8", 16, 16, 8, "ics#", FALSE}, + {"icl8", 32, 32, 8, "ICN#", FALSE}, + {"ich8", 48, 48, 8, "ich#", FALSE}, + + /* 32-bit color icons; separate 8-bit alpha */ + {"is32", 16, 16, 32, "s8mk", FALSE}, + {"il32", 32, 32, 32, "l8mk", FALSE}, + {"ih32", 48, 48, 32, "h8mk", FALSE}, + {"it32", 128, 128, 32, "t8mk", FALSE}, + + /* Post-MacOS 10.0 ICNS formats */ + /* PNG, JPEG 2000, or 24-bit RGB */ + {"icp4", 16, 16, 32, "N/A", TRUE}, + {"icp5", 32, 32, 32, "N/A", TRUE}, + + /* PNG or JPEG 2000 */ + {"icp6", 48, 48, 0, "N/A", TRUE}, + {"ic07", 128, 128, 0, "N/A", TRUE}, + {"ic08", 256, 256, 0, "N/A", TRUE}, + {"ic09", 512, 512, 0, "N/A", TRUE}, + {"sb24", 24, 24, 0, "N/A", TRUE}, + + /* PNG or JPEG 2000 (Retina) */ + {"ic10", 1024, 1024, 0, "N/A", TRUE}, + {"ic11", 32, 32, 0, "N/A", TRUE}, + {"ic12", 64, 64, 0, "N/A", TRUE}, + {"ic13", 256, 256, 0, "N/A", TRUE}, + {"ic14", 512, 512, 0, "N/A", TRUE}, + {"icsB", 36, 36, 0, "N/A", TRUE}, + {"SB24", 48, 48, 0, "N/A", TRUE}, + + /* ARGB, PNG, or JPEG 2000 */ + {"ic04", 16, 16, 0, "N/A", TRUE}, + {"icsb", 18, 18, 0, "N/A", TRUE}, + + {0, 0, 0, 0, 0} +}; + +IconType maskTypes[] = +{ + /* 8-bit masks */ + {"s8mk", 16, 16, 8, 0}, + {"l8mk", 32, 32, 8, 0}, + {"h8mk", 48, 48, 8, 0}, + {"t8mk", 128, 128, 8, 0}, + + {0, 0, 0, 0, 0} +}; + +guchar icns_colormap_4[] = +{ + 0xFF, 0xFF, 0xFF, + 0xFC, 0xF3, 0x05, + 0xFF, 0x64, 0x02, + 0xDD, 0x08, 0x06, + 0xF2, 0x08, 0x84, + 0x46, 0x00, 0xA5, + 0x00, 0x00, 0xD4, + 0x02, 0xAB, 0xEA, + 0x1F, 0xB7, 0x14, + 0x00, 0x64, 0x11, + 0x56, 0x2C, 0x05, + 0x90, 0x71, 0x3A, + 0xC0, 0xC0, 0xC0, + 0x80, 0x80, 0x80, + 0x40, 0x40, 0x40, + 0x00, 0x00, 0x00 +}; + +guchar icns_colormap_8[] = +{ + 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xCC, + 0xFF, 0xFF, 0x99, + 0xFF, 0xFF, 0x66, + 0xFF, 0xFF, 0x33, + 0xFF, 0xFF, 0x00, + 0xFF, 0xCC, 0xFF, + 0xFF, 0xCC, 0xCC, + 0xFF, 0xCC, 0x99, + 0xFF, 0xCC, 0x66, + 0xFF, 0xCC, 0x33, + 0xFF, 0xCC, 0x00, + 0xFF, 0x99, 0xFF, + 0xFF, 0x99, 0xCC, + 0xFF, 0x99, 0x99, + 0xFF, 0x99, 0x66, + 0xFF, 0x99, 0x33, + 0xFF, 0x99, 0x00, + 0xFF, 0x66, 0xFF, + 0xFF, 0x66, 0xCC, + 0xFF, 0x66, 0x99, + 0xFF, 0x66, 0x66, + 0xFF, 0x66, 0x33, + 0xFF, 0x66, 0x00, + 0xFF, 0x33, 0xFF, + 0xFF, 0x33, 0xCC, + 0xFF, 0x33, 0x99, + 0xFF, 0x33, 0x66, + 0xFF, 0x33, 0x33, + 0xFF, 0x33, 0x00, + 0xFF, 0x00, 0xFF, + 0xFF, 0x00, 0xCC, + 0xFF, 0x00, 0x99, + 0xFF, 0x00, 0x66, + 0xFF, 0x00, 0x33, + 0xFF, 0x00, 0x00, + 0xCC, 0xFF, 0xFF, + 0xCC, 0xFF, 0xCC, + 0xCC, 0xFF, 0x99, + 0xCC, 0xFF, 0x66, + 0xCC, 0xFF, 0x33, + 0xCC, 0xFF, 0x00, + 0xCC, 0xCC, 0xFF, + 0xCC, 0xCC, 0xCC, + 0xCC, 0xCC, 0x99, + 0xCC, 0xCC, 0x66, + 0xCC, 0xCC, 0x33, + 0xCC, 0xCC, 0x00, + 0xCC, 0x99, 0xFF, + 0xCC, 0x99, 0xCC, + 0xCC, 0x99, 0x99, + 0xCC, 0x99, 0x66, + 0xCC, 0x99, 0x33, + 0xCC, 0x99, 0x00, + 0xCC, 0x66, 0xFF, + 0xCC, 0x66, 0xCC, + 0xCC, 0x66, 0x99, + 0xCC, 0x66, 0x66, + 0xCC, 0x66, 0x33, + 0xCC, 0x66, 0x00, + 0xCC, 0x33, 0xFF, + 0xCC, 0x33, 0xCC, + 0xCC, 0x33, 0x99, + 0xCC, 0x33, 0x66, + 0xCC, 0x33, 0x33, + 0xCC, 0x33, 0x00, + 0xCC, 0x00, 0xFF, + 0xCC, 0x00, 0xCC, + 0xCC, 0x00, 0x99, + 0xCC, 0x00, 0x66, + 0xCC, 0x00, 0x33, + 0xCC, 0x00, 0x00, + 0x99, 0xFF, 0xFF, + 0x99, 0xFF, 0xCC, + 0x99, 0xFF, 0x99, + 0x99, 0xFF, 0x66, + 0x99, 0xFF, 0x33, + 0x99, 0xFF, 0x00, + 0x99, 0xCC, 0xFF, + 0x99, 0xCC, 0xCC, + 0x99, 0xCC, 0x99, + 0x99, 0xCC, 0x66, + 0x99, 0xCC, 0x33, + 0x99, 0xCC, 0x00, + 0x99, 0x99, 0xFF, + 0x99, 0x99, 0xCC, + 0x99, 0x99, 0x99, + 0x99, 0x99, 0x66, + 0x99, 0x99, 0x33, + 0x99, 0x99, 0x00, + 0x99, 0x66, 0xFF, + 0x99, 0x66, 0xCC, + 0x99, 0x66, 0x99, + 0x99, 0x66, 0x66, + 0x99, 0x66, 0x33, + 0x99, 0x66, 0x00, + 0x99, 0x33, 0xFF, + 0x99, 0x33, 0xCC, + 0x99, 0x33, 0x99, + 0x99, 0x33, 0x66, + 0x99, 0x33, 0x33, + 0x99, 0x33, 0x00, + 0x99, 0x00, 0xFF, + 0x99, 0x00, 0xCC, + 0x99, 0x00, 0x99, + 0x99, 0x00, 0x66, + 0x99, 0x00, 0x33, + 0x99, 0x00, 0x00, + 0x66, 0xFF, 0xFF, + 0x66, 0xFF, 0xCC, + 0x66, 0xFF, 0x99, + 0x66, 0xFF, 0x66, + 0x66, 0xFF, 0x33, + 0x66, 0xFF, 0x00, + 0x66, 0xCC, 0xFF, + 0x66, 0xCC, 0xCC, + 0x66, 0xCC, 0x99, + 0x66, 0xCC, 0x66, + 0x66, 0xCC, 0x33, + 0x66, 0xCC, 0x00, + 0x66, 0x99, 0xFF, + 0x66, 0x99, 0xCC, + 0x66, 0x99, 0x99, + 0x66, 0x99, 0x66, + 0x66, 0x99, 0x33, + 0x66, 0x99, 0x00, + 0x66, 0x66, 0xFF, + 0x66, 0x66, 0xCC, + 0x66, 0x66, 0x99, + 0x66, 0x66, 0x66, + 0x66, 0x66, 0x33, + 0x66, 0x66, 0x00, + 0x66, 0x33, 0xFF, + 0x66, 0x33, 0xCC, + 0x66, 0x33, 0x99, + 0x66, 0x33, 0x66, + 0x66, 0x33, 0x33, + 0x66, 0x33, 0x00, + 0x66, 0x00, 0xFF, + 0x66, 0x00, 0xCC, + 0x66, 0x00, 0x99, + 0x66, 0x00, 0x66, + 0x66, 0x00, 0x33, + 0x66, 0x00, 0x00, + 0x33, 0xFF, 0xFF, + 0x33, 0xFF, 0xCC, + 0x33, 0xFF, 0x99, + 0x33, 0xFF, 0x66, + 0x33, 0xFF, 0x33, + 0x33, 0xFF, 0x00, + 0x33, 0xCC, 0xFF, + 0x33, 0xCC, 0xCC, + 0x33, 0xCC, 0x99, + 0x33, 0xCC, 0x66, + 0x33, 0xCC, 0x33, + 0x33, 0xCC, 0x00, + 0x33, 0x99, 0xFF, + 0x33, 0x99, 0xCC, + 0x33, 0x99, 0x99, + 0x33, 0x99, 0x66, + 0x33, 0x99, 0x33, + 0x33, 0x99, 0x00, + 0x33, 0x66, 0xFF, + 0x33, 0x66, 0xCC, + 0x33, 0x66, 0x99, + 0x33, 0x66, 0x66, + 0x33, 0x66, 0x33, + 0x33, 0x66, 0x00, + 0x33, 0x33, 0xFF, + 0x33, 0x33, 0xCC, + 0x33, 0x33, 0x99, + 0x33, 0x33, 0x66, + 0x33, 0x33, 0x33, + 0x33, 0x33, 0x00, + 0x33, 0x00, 0xFF, + 0x33, 0x00, 0xCC, + 0x33, 0x00, 0x99, + 0x33, 0x00, 0x66, + 0x33, 0x00, 0x33, + 0x33, 0x00, 0x00, + 0x00, 0xFF, 0xFF, + 0x00, 0xFF, 0xCC, + 0x00, 0xFF, 0x99, + 0x00, 0xFF, 0x66, + 0x00, 0xFF, 0x33, + 0x00, 0xFF, 0x00, + 0x00, 0xCC, 0xFF, + 0x00, 0xCC, 0xCC, + 0x00, 0xCC, 0x99, + 0x00, 0xCC, 0x66, + 0x00, 0xCC, 0x33, + 0x00, 0xCC, 0x00, + 0x00, 0x99, 0xFF, + 0x00, 0x99, 0xCC, + 0x00, 0x99, 0x99, + 0x00, 0x99, 0x66, + 0x00, 0x99, 0x33, + 0x00, 0x99, 0x00, + 0x00, 0x66, 0xFF, + 0x00, 0x66, 0xCC, + 0x00, 0x66, 0x99, + 0x00, 0x66, 0x66, + 0x00, 0x66, 0x33, + 0x00, 0x66, 0x00, + 0x00, 0x33, 0xFF, + 0x00, 0x33, 0xCC, + 0x00, 0x33, 0x99, + 0x00, 0x33, 0x66, + 0x00, 0x33, 0x33, + 0x00, 0x33, 0x00, + 0x00, 0x00, 0xFF, + 0x00, 0x00, 0xCC, + 0x00, 0x00, 0x99, + 0x00, 0x00, 0x66, + 0x00, 0x00, 0x33, + 0xEE, 0x00, 0x00, + 0xDD, 0x00, 0x00, + 0xBB, 0x00, 0x00, + 0xAA, 0x00, 0x00, + 0x88, 0x00, 0x00, + 0x77, 0x00, 0x00, + 0x55, 0x00, 0x00, + 0x44, 0x00, 0x00, + 0x22, 0x00, 0x00, + 0x11, 0x00, 0x00, + 0x00, 0xEE, 0x00, + 0x00, 0xDD, 0x00, + 0x00, 0xBB, 0x00, + 0x00, 0xAA, 0x00, + 0x00, 0x88, 0x00, + 0x00, 0x77, 0x00, + 0x00, 0x55, 0x00, + 0x00, 0x44, 0x00, + 0x00, 0x22, 0x00, + 0x00, 0x11, 0x00, + 0x00, 0x00, 0xEE, + 0x00, 0x00, 0xDD, + 0x00, 0x00, 0xBB, + 0x00, 0x00, 0xAA, + 0x00, 0x00, 0x88, + 0x00, 0x00, 0x77, + 0x00, 0x00, 0x55, + 0x00, 0x00, 0x44, + 0x00, 0x00, 0x22, + 0x00, 0x00, 0x11, + 0xEE, 0xEE, 0xEE, + 0xDD, 0xDD, 0xDD, + 0xBB, 0xBB, 0xBB, + 0xAA, 0xAA, 0xAA, + 0x88, 0x88, 0x88, + 0x77, 0x77, 0x77, + 0x55, 0x55, 0x55, + 0x44, 0x44, 0x44, + 0x22, 0x22, 0x22, + 0x11, 0x11, 0x11, + 0x00, 0x00, 0x00 +}; diff --git a/plug-ins/file-icns/file-icns-data.h b/plug-ins/file-icns/file-icns-data.h new file mode 100644 index 0000000000..705bed33f1 --- /dev/null +++ b/plug-ins/file-icns/file-icns-data.h @@ -0,0 +1,39 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis + * + * file-icns-data.h + * Copyright (C) 2004 Brion Vibber + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _ICNS_DATA_H +#define _ICNS_DATA_H + +typedef struct _IconType +{ + gchar *type; + guint width; + guint height; + guint bits; + gchar *mask; + gboolean isModern; /* OSX icns files */ +} IconType; + +extern IconType iconTypes[]; +extern IconType maskTypes[]; +extern guchar icns_colormap_4[]; +extern guchar icns_colormap_8[]; + +#endif diff --git a/plug-ins/file-icns/file-icns-load.c b/plug-ins/file-icns/file-icns-load.c new file mode 100644 index 0000000000..67e775cb49 --- /dev/null +++ b/plug-ins/file-icns/file-icns-load.c @@ -0,0 +1,621 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis + * + * file-icns-load.c + * Copyright (C) 2004 Brion Vibber + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include + +#include +#include + +#include + +/* #define ICNS_DBG */ + +#include "file-icns.h" +#include "file-icns-data.h" +#include "file-icns-load.h" + +#include "libgimp/stdplugins-intl.h" + +IcnsResource * resource_load (FILE *file); + +IcnsResource * resource_find (IcnsResource *list, + gchar *type, + gint max); + +gboolean resource_get_next (IcnsResource *icns, + IcnsResource *res); + +void icns_slurp (guchar *dest, + IconType *icontype, + IcnsResource *icns, + IcnsResource *mask); + +gboolean icns_decompress (guchar *dest, + IconType *icontype, + IcnsResource *image, + IcnsResource *mask); + +void icns_attach_image (GimpImage *image, + IconType *icontype, + IcnsResource *icns, + IcnsResource *mask, + gboolean isOSX); + +GimpImage * icns_load (IcnsResource *icns, + GFile *file); + +/* Ported from Brion Vibber's icnsload.c code, under the GPL license, version 3 + * or any later version of the license */ +IcnsResource * +resource_load (FILE *file) +{ + IcnsResource *res = NULL; + + if (file) + { + IcnsResourceHeader header; + + if (1 == fread (&header, sizeof (IcnsResourceHeader), 1, file)) + { + gchar type[5]; + guint32 size; + + strncpy (type, header.type, 4); + type[4] = '\0'; + size = GUINT32_FROM_BE (header.size); + + if (! strncmp (header.type, "icns", 4) && size > sizeof (IcnsResourceHeader)) + { + res = (IcnsResource *) g_new (guchar, sizeof (IcnsResource) + size); + strncpy (res->type, header.type, 4); + res->type[4] = '\0'; + res->size = size; + res->cursor = sizeof (IcnsResourceHeader); + res->data = (guchar *) res + sizeof (IcnsResource); + fseek (file, 0, SEEK_SET); + + if (size != fread (res->data, 1, res->size, file)) + { + g_message ("** expected %d bytes\n", size); + g_free (res); + res = NULL; + } + } + } + else + { + g_message (("** couldn't read icns header.\n")); + } + fclose (file); + } + else + { + g_message (("** couldn't open file.\n")); + } + return res; +} + +IcnsResource * +resource_find (IcnsResource *list, + gchar *type, + gint max) +{ + for (gint i = 0; i < max; i++) + { + if (! strncmp (list[i].type, type, 4)) + return &list[i]; + } + return NULL; +} + +gboolean +resource_get_next (IcnsResource *icns, + IcnsResource *res) +{ + IcnsResourceHeader *header; + + if (icns->size - icns->cursor < sizeof (IcnsResourceHeader)) + return FALSE; + + header = (IcnsResourceHeader *) &(icns->data[icns->cursor]); + strncpy (res->type, header->type, 4); + res->size = GUINT32_FROM_BE (header->size); + res->cursor = sizeof (IcnsResourceHeader); + res->data = &(icns->data[icns->cursor]); + + icns->cursor += res->size; + if (icns->cursor > icns->size) + { + gchar typestring[5]; + fourcc_get_string (icns->type, typestring); + g_message ("icns resource_get_next: resource too big! type '%s', size %u\n", + typestring, icns->size); + + return FALSE; + } + return TRUE; +} + +GimpImage * +icns_load (IcnsResource *icns, + GFile *file) +{ + IcnsResource *resources; + guint nResources; + gfloat current_resources = 0; + GimpImage *image; + + resources = g_new (IcnsResource, 256); + + /* Largest .icns icon is 1024 x 1024 */ + image = gimp_image_new (1024, 1024, GIMP_RGB); + gimp_image_set_file (image, file); + + nResources = 0; + while (resource_get_next (icns, &resources[nResources++])) {} + + for (gint i = 0; iconTypes[i].type; i++) + { + IcnsResource *icns; + IcnsResource *mask = NULL; + + if ((icns = resource_find (resources, iconTypes[i].type, nResources))) + { + if (! iconTypes[i].isModern) + mask = resource_find (resources, iconTypes[i].mask, nResources); + + icns_attach_image (image, &iconTypes[i], icns, mask, iconTypes[i].isModern); + + gimp_progress_update (current_resources++ / nResources); + } + } + + gimp_image_resize_to_layers (image); + g_free (resources); + return image; +} + +void +icns_slurp (guchar *dest, + IconType *icontype, + IcnsResource *icns, + IcnsResource *mask) +{ + guint out; + guint max; + guchar bucket = 0; + guchar bit; + guint index; + + max = icontype->width * icontype->height; + icns->cursor = sizeof (IcnsResourceHeader); + + switch (icontype->bits) + { + case 1: + for (out = 0; out < max; out++) + { + if (out % 8 == 0) + bucket = icns->data[icns->cursor++]; + + bit = (bucket & 0x80) ? 0 : 255; + bucket = bucket << 1; + dest[out * 4] = bit; + dest[out * 4 + 1] = bit; + dest[out * 4 + 2] = bit; + } + break; + case 4: + for (out = 0; out < max; out++) + { + if (out % 2 == 0) + bucket = icns->data[icns->cursor++]; + + index = 3 * (bucket & 0xf0) >> 4; + bucket = bucket << 4; + dest[out * 4] = icns_colormap_4[index]; + dest[out * 4 + 1] = icns_colormap_4[index + 1]; + dest[out * 4 + 2] = icns_colormap_4[index + 2]; + } + break; + case 8: + for (out = 0; out < max; out++) + { + index = 3 * icns->data[icns->cursor++]; + dest[out * 4] = icns_colormap_8[index]; + dest[out * 4 + 1] = icns_colormap_8[index + 1]; + dest[out * 4 + 2] = icns_colormap_8[index + 2]; + dest[out * 4 + 3] = 255; + } + break; + case 32: + for (out = 0; out < max; out++) + { + dest[out * 4] = icns->data[icns->cursor++]; + dest[out * 4 + 1] = icns->data[icns->cursor++]; + dest[out * 4 + 2] = icns->data[icns->cursor++]; + /* Throw away alpha, use the mask */ + icns->cursor++; + if (mask) + dest[out * 4 + 3] = icns->data[mask->cursor++]; + else + dest[out * 4 + 3] = 255; + } + break; + } + + /* Now for the mask */ + if (mask && icontype->bits != 32) + { + mask->cursor = sizeof (IcnsResourceHeader) + icontype->width * icontype->height / 8; + for (out = 0; out < max; out++) + { + if (out % 8 == 0) + bucket = mask->data[mask->cursor++]; + + bit = (bucket & 0x80) ? 255 : 0; + bucket = bucket << 1; + dest[out * 4 + 3] = bit; + } + } +} + +gboolean +icns_decompress (guchar *dest, + IconType *icontype, + IcnsResource *image, + IcnsResource *mask) +{ + guint max; + guint channel; + guint out; + guchar run; + guchar val; + gint n_channels = 3; + + max = icontype->width * icontype->height; + memset (dest, 255, max * 4); + + /* For some reason there seem to be 4 null bytes at the start of an it32. */ + if (! strncmp (icontype->type, "it32", 4)) + image->cursor += 4; + + for (channel = 0; channel < n_channels; channel++) + { + out = 0; + while (out < max) + { + run = image->data[image->cursor++]; + + if (run & 0x80) + { + /* Compressed */ + if (image->cursor >= image->size) + { + g_message ("Corrupt icon: compressed run overflows input size."); + return FALSE; + } + + val = image->data[image->cursor++]; + + for (run -= 125; run > 0; run--) + { + if (out >= max) + { + g_message ("Corrupt icon? compressed run overflows output size."); + return FALSE; + } + dest[out++ * 4 + channel] = val; + } + } + else + { + /* Uncompressed */ + for (run += 1; run > 0; run--) + { + if (image->cursor >= image->size) + { + g_message ("Corrupt icon: uncompressed run overflows input size."); + return FALSE; + } + if (out >= max) + { + g_message ("Corrupt icon: uncompressed run overflows output size."); + return FALSE; + } + dest[out++ * 4 + channel] = image->data[image->cursor++]; + } + } + } + } + + if (mask) + { + gchar typestring[5]; + fourcc_get_string (mask->type, typestring); + + for (out = 0; out < max; out++) + dest[out * 4 + 3] = mask->data[mask->cursor++]; + } + return TRUE; +} + +void +icns_attach_image (GimpImage *image, + IconType *icontype, + IcnsResource *icns, + IcnsResource *mask, + gboolean isOSX) +{ + gchar layer_name[5]; + guchar *dest; + GimpLayer *layer; + GeglBuffer *buffer; + guint row; + guint expected_size; + gboolean layer_loaded = FALSE; + + strncpy (layer_name, icontype->type, 4); + layer_name[4] = '\0'; + + row = 4 * icontype->width; + dest = g_malloc (row * icontype->height); + + expected_size = + (icontype->width * icontype->height * icontype->bits) / 8; + + if (icns == mask) + expected_size *= 2; + + expected_size += sizeof (IcnsResourceHeader); + + if (isOSX) + { + gchar image_type[5]; + GimpImage *temp_image; + GFile *temp_file = NULL; + FILE *fp; + GimpValueArray *return_vals = NULL; + GimpLayer **layers; + GimpLayer *new_layer; + gint n_layers; + gchar *temp_file_type = NULL; + gchar *procedure_name = NULL; + + temp_image = gimp_image_new (icontype->width, icontype->height, + gimp_image_get_base_type (image)); + + strncpy (image_type, (gchar *) icns->data + 8, 4); + image_type[4] = '\0'; + + /* PNG */ + if (! strncmp (image_type, "\x89\x50\x4E\x47", 4)) + { + temp_file_type = "png"; + procedure_name = "file-png-load"; + } + /* JPEG 2000 */ + else if (! strncmp (image_type, "\x0CjP", 3)) + { + temp_file_type = "jp2"; + procedure_name = "file-jp2-load"; + } + + if (temp_file_type && procedure_name) + { + temp_file = gimp_temp_file (temp_file_type); + fp = g_fopen (g_file_peek_path (temp_file), "wb"); + + if (! fp) + { + g_message (_("Error trying to open temporary %s file '%s' " + "for icns loading: %s"), + temp_file_type, + gimp_file_get_utf8_name (temp_file), + g_strerror (errno)); + return; + } + + fwrite (icns->data + 8, sizeof (guchar), icns->size - 8, fp); + fclose (fp); + + return_vals = + gimp_pdb_run_procedure (gimp_get_pdb (), + procedure_name, + GIMP_TYPE_RUN_MODE, GIMP_RUN_NONINTERACTIVE, + G_TYPE_FILE, temp_file, + G_TYPE_NONE); + } + + if (temp_image && return_vals) + { + temp_image = g_value_get_object (gimp_value_array_index (return_vals, 1)); + + layers = gimp_image_get_layers (temp_image, &n_layers); + new_layer = gimp_layer_new_from_drawable (GIMP_DRAWABLE (layers[0]), image); + gimp_item_set_name (GIMP_ITEM (new_layer), layer_name); + gimp_image_insert_layer (image, new_layer, NULL, 0); + + layer_loaded = TRUE; + + g_file_delete (temp_file, NULL, NULL); + g_object_unref (temp_file); + gimp_value_array_unref (return_vals); + g_free (layers); + } + } + else + { + if (icontype->bits != 32 || expected_size == icns->size) + icns_slurp (dest, icontype, icns, mask); + else + icns_decompress (dest, icontype, icns, mask); + } + + if (! layer_loaded) + { + layer = gimp_layer_new (image, layer_name, icontype->width, icontype->height, + GIMP_RGBA_IMAGE, 100, + gimp_image_get_default_new_layer_mode (image)); + + buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)); + + gegl_buffer_set (buffer, + GEGL_RECTANGLE (0, 0, icontype->width, icontype->height), + 0, NULL, + dest, GEGL_AUTO_ROWSTRIDE); + + gimp_image_insert_layer (image, layer, NULL, 0); + + g_object_unref (buffer); + } + + g_free (dest); +} + +GimpImage * +icns_load_image (GFile *file, + gint32 *file_offset, + GError **error) +{ + FILE *fp; + IcnsResource *icns; + GimpImage *image; + + gegl_init (NULL, NULL); + + gimp_progress_init_printf (_("Opening '%s'"), + gimp_file_get_utf8_name (file)); + + fp = g_fopen (g_file_peek_path (file), "rb"); + + if (! fp) + { + g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), + _("Could not open '%s' for reading: %s"), + gimp_file_get_utf8_name (file), g_strerror (errno)); + return NULL; + } + + icns = resource_load (fp); + + fclose (fp); + + if (! icns) + { + g_message ("Invalid or corrupt icns resource file."); + return NULL; + } + + image = icns_load (icns, file); + + g_free (icns); + + gimp_progress_update (1.0); + + return image; +} + +GimpImage * +icns_load_thumbnail_image (GFile *file, + gint *width, + gint *height, + gint32 file_offset, + GError **error) +{ + gint w = 0; + FILE *fp; + GimpImage *image = NULL; + IcnsResource *icns; + IcnsResource *resources; + IcnsResource *mask = NULL; + guint i; + gint match = -1; + guint nResources = 0; + + gegl_init (NULL, NULL); + + gimp_progress_init_printf (_("Opening thumbnail for '%s'"), + gimp_file_get_utf8_name (file)); + + fp = g_fopen (g_file_peek_path (file), "rb"); + + if (! fp) + { + g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), + _("Could not open '%s' for reading: %s"), + gimp_file_get_utf8_name (file), g_strerror (errno)); + return NULL; + } + + icns = resource_load (fp); + fclose (fp); + + if (! icns) + { + g_message ("Invalid or corrupt icns resource file."); + return NULL; + } + + image = gimp_image_new (1024, 1024, GIMP_RGB); + + resources = g_new (IcnsResource, 256); + while (resource_get_next (icns, &resources[nResources++])) {} + + for (i = 0; iconTypes[i].type; i++) + { + if ((icns = resource_find (resources, iconTypes[i].type, nResources))) + { + if (iconTypes[i].width > w) + { + w = iconTypes[i].width; + match = i; + } + } + } + + if (match > -1) + { + icns = resource_find (resources, iconTypes[match].type, nResources); + + if (! iconTypes[match].isModern) + mask = resource_find (resources, iconTypes[match].mask, nResources); + + icns_attach_image (image, &iconTypes[match], icns, mask, iconTypes[match].isModern); + + gimp_image_resize_to_layers (image); + } + else + { + g_message ("Invalid or corrupt icns resource file."); + return NULL; + } + + g_free (resources); + + gimp_progress_update (1.0); + + return image; +} diff --git a/plug-ins/file-icns/file-icns-load.h b/plug-ins/file-icns/file-icns-load.h new file mode 100644 index 0000000000..355fffd9ae --- /dev/null +++ b/plug-ins/file-icns/file-icns-load.h @@ -0,0 +1,34 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis + * + * file-icns-load.h + * Copyright (C) 2004 Brion Vibber + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __ICNS_LOAD_H__ +#define __ICNS_LOAD_H__ + +GimpImage * icns_load_image (GFile *file, + gint32 *file_offset, + GError **error); + +GimpImage * icns_load_thumbnail_image (GFile *file, + gint *width, + gint *height, + gint32 file_offset, + GError **error); + +#endif diff --git a/plug-ins/file-icns/file-icns.c b/plug-ins/file-icns/file-icns.c new file mode 100644 index 0000000000..78f9bcf78a --- /dev/null +++ b/plug-ins/file-icns/file-icns.c @@ -0,0 +1,236 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis + * + * file-icns.c + * Copyright (C) 2004 Brion Vibber + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include + +#include +#include + +/* #define ICNS_DBG */ + +#include "file-icns.h" +#include "file-icns-load.h" +//#include "ico-save.h" + +#include "libgimp/stdplugins-intl.h" + +#define LOAD_PROC "file-icns-load" +#define LOAD_THUMB_PROC "file-icns-load-thumb" + + +typedef struct _Icns Icns; +typedef struct _IcnsClass IcnsClass; + +struct _Icns +{ + GimpPlugIn parent_instance; +}; + +struct _IcnsClass +{ + GimpPlugInClass parent_class; +}; + + +#define ICNS_TYPE (icns_get_type ()) +#define ICNS (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ICNS_TYPE, Icns)) + +GType icns_get_type (void) G_GNUC_CONST; + +static GList * icns_query_procedures (GimpPlugIn *plug_in); +static GimpProcedure * icns_create_procedure (GimpPlugIn *plug_in, + const gchar *name); + +static GimpValueArray * icns_load (GimpProcedure *procedure, + GimpRunMode run_mode, + GFile *file, + const GimpValueArray *args, + gpointer run_data); +static GimpValueArray * icns_load_thumb (GimpProcedure *procedure, + GFile *file, + gint size, + const GimpValueArray *args, + gpointer run_data); + + +G_DEFINE_TYPE (Icns, icns, GIMP_TYPE_PLUG_IN) + +GIMP_MAIN (ICNS_TYPE) +DEFINE_STD_SET_I18N + +static void +icns_class_init (IcnsClass *klass) +{ + GimpPlugInClass *plug_in_class = GIMP_PLUG_IN_CLASS (klass); + + plug_in_class->query_procedures = icns_query_procedures; + plug_in_class->create_procedure = icns_create_procedure; + plug_in_class->set_i18n = STD_SET_I18N; +} + +static void +icns_init (Icns *icns) +{ +} + +static GList * +icns_query_procedures (GimpPlugIn *plug_in) +{ + GList *list = NULL; + + list = g_list_append (list, g_strdup (LOAD_THUMB_PROC)); + list = g_list_append (list, g_strdup (LOAD_PROC)); + + return list; +} + +static GimpProcedure * +icns_create_procedure (GimpPlugIn *plug_in, + const gchar *name) +{ + GimpProcedure *procedure = NULL; + + if (! strcmp (name, LOAD_PROC)) + { + procedure = gimp_load_procedure_new (plug_in, name, + GIMP_PDB_PROC_TYPE_PLUGIN, + icns_load, NULL, NULL); + + gimp_procedure_set_menu_label (procedure, N_("Icns")); + + gimp_procedure_set_documentation (procedure, + "Loads files in Apple Icon Image format", + "Loads Apple Icon Image files.", + name); + gimp_procedure_set_attribution (procedure, + "Brion Vibber ", + "Brion Vibber ", + "2004"); + + gimp_file_procedure_set_mime_types (GIMP_FILE_PROCEDURE (procedure), + "image/x-icns"); + gimp_file_procedure_set_extensions (GIMP_FILE_PROCEDURE (procedure), + "icns"); + gimp_file_procedure_set_magics (GIMP_FILE_PROCEDURE (procedure), + "0,string,\x69\x63\x6E\x73"); + + gimp_load_procedure_set_thumbnail_loader (GIMP_LOAD_PROCEDURE (procedure), + LOAD_THUMB_PROC); + } + else if (! strcmp (name, LOAD_THUMB_PROC)) + { + procedure = gimp_thumbnail_procedure_new (plug_in, name, + GIMP_PDB_PROC_TYPE_PLUGIN, + icns_load_thumb, NULL, NULL); + + gimp_procedure_set_documentation (procedure, + "Loads a preview from an Apple Icon Image file", + "", + name); + gimp_procedure_set_attribution (procedure, + "Brion Vibber ", + "Brion Vibber ", + "2004"); + } + + return procedure; +} + +static GimpValueArray * +icns_load (GimpProcedure *procedure, + GimpRunMode run_mode, + GFile *file, + const GimpValueArray *args, + gpointer run_data) +{ + GimpValueArray *return_vals; + GimpImage *image; + GError *error = NULL; + + gegl_init (NULL, NULL); + + image = icns_load_image (file, NULL, &error); + + if (! image) + return gimp_procedure_new_return_values (procedure, + GIMP_PDB_EXECUTION_ERROR, + error); + + return_vals = gimp_procedure_new_return_values (procedure, + GIMP_PDB_SUCCESS, + NULL); + + GIMP_VALUES_SET_IMAGE (return_vals, 1, image); + + return return_vals; +} + +static GimpValueArray * +icns_load_thumb (GimpProcedure *procedure, + GFile *file, + gint size, + const GimpValueArray *args, + gpointer run_data) +{ + GimpValueArray *return_vals; + gint width; + gint height; + GimpImage *image; + GError *error = NULL; + + gegl_init (NULL, NULL); + + width = size; + height = size; + + image = icns_load_thumbnail_image (file, + &width, &height, 0, &error); + + if (! image) + return gimp_procedure_new_return_values (procedure, + GIMP_PDB_EXECUTION_ERROR, + error); + + return_vals = gimp_procedure_new_return_values (procedure, + GIMP_PDB_SUCCESS, + NULL); + + GIMP_VALUES_SET_IMAGE (return_vals, 1, image); + GIMP_VALUES_SET_INT (return_vals, 2, width); + GIMP_VALUES_SET_INT (return_vals, 3, height); + + gimp_value_array_truncate (return_vals, 4); + + return return_vals; +} + +/* Buffer should point to *at least 5 byte buffer*! */ +void +fourcc_get_string (gchar *fourcc, + gchar *buf) +{ + buf = fourcc; + buf[4] = 0; +} diff --git a/plug-ins/file-icns/file-icns.h b/plug-ins/file-icns/file-icns.h new file mode 100644 index 0000000000..872b73aa95 --- /dev/null +++ b/plug-ins/file-icns/file-icns.h @@ -0,0 +1,58 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis + * + * file-icns.h + * Copyright (C) 2004 Brion Vibber + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __ICNS_H__ +#define __ICNS_H__ + + +#ifdef ICNS_DBG +#define D(x) \ +{ \ + printf("ICNS plugin: "); \ + printf x; \ +} +#else +#define D(x) +#endif + +#define PLUG_IN_BINARY "file-icns" +#define PLUG_IN_ROLE "gimp-file-icns" + + +typedef struct _IcnsResourceHeader +{ + /* Big-endian! */ + gchar type[4]; + guint32 size; +} IcnsResourceHeader; + +typedef struct _IcnsResource +{ + gchar type[5]; + guint32 size; + guint32 cursor; + guchar *data; +} IcnsResource; + +void +fourcc_get_string (gchar *fourcc, + gchar *buf); + +#endif /* __ICNS_H__ */ diff --git a/plug-ins/file-icns/meson.build b/plug-ins/file-icns/meson.build new file mode 100644 index 0000000000..a66b342021 --- /dev/null +++ b/plug-ins/file-icns/meson.build @@ -0,0 +1,31 @@ +plugin_name = 'file-icns' + +plugin_sources = [ + 'file-icns-data.c', + 'file-icns-load.c', + 'file-icns.c', +] + +if platform_windows + plugin_sources += windows.compile_resources( + gimp_plugins_rc, + args: [ + '--define', 'ORIGINALFILENAME_STR="@0@"'.format(plugin_name+'.exe'), + '--define', 'INTERNALNAME_STR="@0@"' .format(plugin_name), + '--define', 'TOP_SRCDIR="@0@"' .format(meson.source_root()), + ], + include_directories: [ + rootInclude, appInclude, + ], + ) +endif + +executable(plugin_name, + plugin_sources, + dependencies: [ + libgimpui_dep, + libpng, + ], + install: true, + install_dir: gimpplugindir / 'plug-ins' / plugin_name, +) diff --git a/plug-ins/meson.build b/plug-ins/meson.build index 11c0553541..22556d618f 100644 --- a/plug-ins/meson.build +++ b/plug-ins/meson.build @@ -5,6 +5,7 @@ subdir('file-exr') subdir('file-faxg3') subdir('file-fits') subdir('file-fli') +subdir('file-icns') subdir('file-ico') subdir('file-jpeg') subdir('file-psd') diff --git a/po-plug-ins/POTFILES.in b/po-plug-ins/POTFILES.in index eff90c9f92..4494e8a05c 100644 --- a/po-plug-ins/POTFILES.in +++ b/po-plug-ins/POTFILES.in @@ -96,6 +96,8 @@ plug-ins/file-faxg3/faxg3.c plug-ins/file-fits/fits.c plug-ins/file-fli/fli.c plug-ins/file-fli/fli-gimp.c +plug-ins/file-icns/file-icns-load.c +plug-ins/file-icns/file-icns.c plug-ins/file-ico/ico-dialog.c plug-ins/file-ico/ico-load.c plug-ins/file-ico/ico-save.c