diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:23:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:23:22 +0000 |
commit | e42129241681dde7adae7d20697e7b421682fbb4 (patch) | |
tree | af1fe815a5e639e68e59fabd8395ec69458b3e5e /plug-ins/file-ico/ico-load.c | |
parent | Initial commit. (diff) | |
download | gimp-upstream.tar.xz gimp-upstream.zip |
Adding upstream version 2.10.22.upstream/2.10.22upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | plug-ins/file-ico/ico-load.c | 813 |
1 files changed, 813 insertions, 0 deletions
diff --git a/plug-ins/file-ico/ico-load.c b/plug-ins/file-ico/ico-load.c new file mode 100644 index 0000000..f44b805 --- /dev/null +++ b/plug-ins/file-ico/ico-load.c @@ -0,0 +1,813 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * GIMP Plug-in for Windows Icon files. + * Copyright (C) 2002 Christian Kreibich <christian@whoop.org>. + * + * 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 <https://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <errno.h> +#include <string.h> + +#include <glib/gstdio.h> + +#include <libgimp/gimp.h> +#include <libgimp/gimpui.h> + +#include <png.h> + +/* #define ICO_DBG */ + +#include "ico.h" +#include "ico-load.h" + +#include "libgimp/stdplugins-intl.h" + + +#define A_VAL(p) ((guchar *)(p))[3] +#define R_VAL(p) ((guchar *)(p))[2] +#define G_VAL(p) ((guchar *)(p))[1] +#define B_VAL(p) ((guchar *)(p))[0] + +#define A_VAL_GIMP(p) ((guchar *)(p))[3] +#define R_VAL_GIMP(p) ((guchar *)(p))[0] +#define G_VAL_GIMP(p) ((guchar *)(p))[1] +#define B_VAL_GIMP(p) ((guchar *)(p))[2] + + +static gint ico_read_int8 (FILE *fp, + guint8 *data, + gint count); +static gint ico_read_int16 (FILE *fp, + guint16 *data, + gint count); +static gint ico_read_int32 (FILE *fp, + guint32 *data, + gint count); + +static gint +ico_read_int32 (FILE *fp, + guint32 *data, + gint count) +{ + gint i, total; + + total = count; + if (count > 0) + { + ico_read_int8 (fp, (guint8 *) data, count * 4); + for (i = 0; i < count; i++) + data[i] = GUINT32_FROM_LE (data[i]); + } + + return total * 4; +} + + +static gint +ico_read_int16 (FILE *fp, + guint16 *data, + gint count) +{ + gint i, total; + + total = count; + if (count > 0) + { + ico_read_int8 (fp, (guint8 *) data, count * 2); + for (i = 0; i < count; i++) + data[i] = GUINT16_FROM_LE (data[i]); + } + + return total * 2; +} + + +static gint +ico_read_int8 (FILE *fp, + guint8 *data, + gint count) +{ + gint total; + gint bytes; + + total = count; + while (count > 0) + { + bytes = fread ((gchar *) data, sizeof (gchar), count, fp); + if (bytes <= 0) /* something bad happened */ + break; + + count -= bytes; + data += bytes; + } + + return total; +} + + +static guint32 +ico_read_init (FILE *fp) +{ + IcoFileHeader header; + + /* read and check file header */ + if (! ico_read_int16 (fp, &header.reserved, 1) || + ! ico_read_int16 (fp, &header.resource_type, 1) || + ! ico_read_int16 (fp, &header.icon_count, 1) || + header.reserved != 0 || + header.resource_type != 1) + { + return 0; + } + + return header.icon_count; +} + + +static gboolean +ico_read_size (FILE *fp, + IcoLoadInfo *info) +{ + png_structp png_ptr; + png_infop info_ptr; + png_uint_32 w, h; + gint32 bpp; + gint32 color_type; + guint32 magic; + + if (fseek (fp, info->offset, SEEK_SET) < 0) + return FALSE; + + ico_read_int32 (fp, &magic, 1); + + if (magic == ICO_PNG_MAGIC) + { + png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, + NULL); + if (! png_ptr) + return FALSE; + + info_ptr = png_create_info_struct (png_ptr); + if (! info_ptr) + { + png_destroy_read_struct (&png_ptr, NULL, NULL); + return FALSE; + } + + if (setjmp (png_jmpbuf (png_ptr))) + { + png_destroy_read_struct (&png_ptr, NULL, NULL); + return FALSE; + } + png_init_io (png_ptr, fp); + png_set_sig_bytes (png_ptr, 4); + png_read_info (png_ptr, info_ptr); + png_get_IHDR (png_ptr, info_ptr, &w, &h, &bpp, &color_type, + NULL, NULL, NULL); + png_destroy_read_struct (&png_ptr, &info_ptr, NULL); + info->width = w; + info->height = h; + D(("ico_read_size: PNG: %ix%i\n", info->width, info->height)); + return TRUE; + } + else if (magic == 40) + { + if (ico_read_int32 (fp, &info->width, 1) && + ico_read_int32 (fp, &info->height, 1)) + { + info->height /= 2; + D(("ico_read_size: ICO: %ix%i\n", info->width, info->height)); + return TRUE; + } + else + { + info->width = 0; + info->height = 0; + return FALSE; + } + } + return FALSE; +} + +static IcoLoadInfo* +ico_read_info (FILE *fp, + gint icon_count, + GError **error) +{ + gint i; + IcoFileEntry *entries; + IcoLoadInfo *info; + + /* read icon entries */ + entries = g_new (IcoFileEntry, icon_count); + if (fread (entries, sizeof (IcoFileEntry), icon_count, fp) <= 0) + { + g_set_error (error, G_FILE_ERROR, 0, + _("Could not read '%lu' bytes"), + sizeof (IcoFileEntry)); + g_free (entries); + return NULL; + } + + info = g_new (IcoLoadInfo, icon_count); + for (i = 0; i < icon_count; i++) + { + info[i].width = entries[i].width; + info[i].height = entries[i].height; + info[i].bpp = GUINT16_FROM_LE (entries[i].bpp); + info[i].size = GUINT32_FROM_LE (entries[i].size); + info[i].offset = GUINT32_FROM_LE (entries[i].offset); + + if (info[i].width == 0 || info[i].height == 0) + { + ico_read_size (fp, info + i); + } + + D(("ico_read_info: %ix%i (%i bits, size: %i, offset: %i)\n", + info[i].width, info[i].height, info[i].bpp, + info[i].size, info[i].offset)); + + if (info[i].width == 0 || info[i].height == 0) + { + g_set_error (error, G_FILE_ERROR, 0, + _("Icon #%d has zero width or height"), i); + g_free (info); + g_free (entries); + return NULL; + } + } + + g_free (entries); + + return info; +} + +static gboolean +ico_read_png (FILE *fp, + guint32 header, + guchar *buf, + gint maxsize, + gint *width, + gint *height) +{ + png_structp png_ptr; + png_infop info; + png_uint_32 w; + png_uint_32 h; + gint32 bit_depth; + gint32 color_type; + guint32 **rows; + gint i; + + png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (! png_ptr) + return FALSE; + info = png_create_info_struct (png_ptr); + if (! info) + { + png_destroy_read_struct (&png_ptr, NULL, NULL); + return FALSE; + } + + if (setjmp (png_jmpbuf (png_ptr))) + { + png_destroy_read_struct (&png_ptr, &info, NULL); + return FALSE; + } + + png_init_io (png_ptr, fp); + png_set_sig_bytes (png_ptr, 4); + png_read_info (png_ptr, info); + png_get_IHDR (png_ptr, info, &w, &h, &bit_depth, &color_type, + NULL, NULL, NULL); + if (w*h*4 > maxsize) + { + png_destroy_read_struct (&png_ptr, &info, NULL); + return FALSE; + } + D(("ico_read_png: %ix%i, %i bits, %i type\n", (gint)w, (gint)h, + bit_depth, color_type)); + switch (color_type) + { + case PNG_COLOR_TYPE_GRAY: + png_set_expand_gray_1_2_4_to_8 (png_ptr); + if (bit_depth == 16) + png_set_strip_16 (png_ptr); + png_set_gray_to_rgb (png_ptr); + png_set_add_alpha (png_ptr, 0xff, PNG_FILLER_AFTER); + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: + png_set_expand_gray_1_2_4_to_8 (png_ptr); + if (bit_depth == 16) + png_set_strip_16 (png_ptr); + png_set_gray_to_rgb (png_ptr); + break; + case PNG_COLOR_TYPE_PALETTE: + png_set_palette_to_rgb (png_ptr); + if (png_get_valid (png_ptr, info, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha (png_ptr); + else + png_set_add_alpha (png_ptr, 0xff, PNG_FILLER_AFTER); + break; + case PNG_COLOR_TYPE_RGB: + if (bit_depth == 16) + png_set_strip_16 (png_ptr); + png_set_add_alpha (png_ptr, 0xff, PNG_FILLER_AFTER); + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + if (bit_depth == 16) + png_set_strip_16 (png_ptr); + break; + } + + *width = w; + *height = h; + rows = g_new (guint32*, h); + rows[0] = (guint32*) buf; + for (i = 1; i < h; i++) + rows[i] = rows[i-1] + w; + png_read_image (png_ptr, (png_bytepp) rows); + png_destroy_read_struct (&png_ptr, &info, NULL); + g_free (rows); + return TRUE; +} + +gint +ico_get_bit_from_data (const guint8 *data, + gint line_width, + gint bit) +{ + gint line; + gint width32; + gint offset; + gint result; + + /* width per line in multiples of 32 bits */ + width32 = (line_width % 32 == 0 ? line_width/32 : line_width/32 + 1); + line = bit / line_width; + offset = bit % line_width; + + result = (data[line * width32 * 4 + offset/8] & (1 << (7 - (offset % 8)))); + + return (result ? 1 : 0); +} + + +gint +ico_get_nibble_from_data (const guint8 *data, + gint line_width, + gint nibble) +{ + gint line; + gint width32; + gint offset; + gint result; + + /* width per line in multiples of 32 bits */ + width32 = (line_width % 8 == 0 ? line_width/8 : line_width/8 + 1); + line = nibble / line_width; + offset = nibble % line_width; + + result = + (data[line * width32 * 4 + offset/2] & (0x0F << (4 * (1 - offset % 2)))); + + if (offset % 2 == 0) + result = result >> 4; + + return result; +} + +gint +ico_get_byte_from_data (const guint8 *data, + gint line_width, + gint byte) +{ + gint line; + gint width32; + gint offset; + + /* width per line in multiples of 32 bits */ + width32 = (line_width % 4 == 0 ? line_width / 4 : line_width / 4 + 1); + line = byte / line_width; + offset = byte % line_width; + + return data[line * width32 * 4 + offset]; +} + +static gboolean +ico_read_icon (FILE *fp, + guint32 header_size, + guchar *buf, + gint maxsize, + gint *width, + gint *height) +{ + IcoFileDataHeader data; + gint length; + gint x, y, w, h; + guchar *xor_map, *and_map; + guint32 *palette; + guint32 *dest_vec; + guchar *row; + gint rowstride; + + palette = NULL; + + data.header_size = header_size; + ico_read_int32 (fp, &data.width, 1); + ico_read_int32 (fp, &data.height, 1); + ico_read_int16 (fp, &data.planes, 1); + ico_read_int16 (fp, &data.bpp, 1); + ico_read_int32 (fp, &data.compression, 1); + ico_read_int32 (fp, &data.image_size, 1); + ico_read_int32 (fp, &data.x_res, 1); + ico_read_int32 (fp, &data.y_res, 1); + ico_read_int32 (fp, &data.used_clrs, 1); + ico_read_int32 (fp, &data.important_clrs, 1); + + D((" header size %i, " + "w %i, h %i, planes %i, size %i, bpp %i, used %i, imp %i.\n", + data.header_size, data.width, data.height, + data.planes, data.image_size, data.bpp, + data.used_clrs, data.important_clrs)); + + if (data.planes != 1 || + data.compression != 0) + { + D(("skipping image: invalid header\n")); + return FALSE; + } + + if (data.bpp != 1 && + data.bpp != 4 && + data.bpp != 8 && + data.bpp != 24 && + data.bpp != 32) + { + D(("skipping image: invalid depth: %i\n", data.bpp)); + return FALSE; + } + + if (data.width * data.height * 2 > maxsize) + { + D(("skipping image: too large\n")); + return FALSE; + } + + w = data.width; + h = data.height / 2; + + if (data.bpp <= 8) + { + if (data.used_clrs == 0) + data.used_clrs = (1 << data.bpp); + + D((" allocating a %i-slot palette for %i bpp.\n", + data.used_clrs, data.bpp)); + + palette = g_new0 (guint32, data.used_clrs); + ico_read_int8 (fp, (guint8 *) palette, data.used_clrs * 4); + } + + xor_map = ico_alloc_map (w, h, data.bpp, &length); + ico_read_int8 (fp, xor_map, length); + D((" length of xor_map: %i\n", length)); + + /* Read in and_map. It's padded out to 32 bits per line: */ + and_map = ico_alloc_map (w, h, 1, &length); + ico_read_int8 (fp, and_map, length); + D((" length of and_map: %i\n", length)); + + dest_vec = (guint32 *) buf; + switch (data.bpp) + { + case 1: + for (y = 0; y < h; y++) + for (x = 0; x < w; x++) + { + guint32 color = palette[ico_get_bit_from_data (xor_map, + w, y * w + x)]; + guint32 *dest = dest_vec + (h - 1 - y) * w + x; + + R_VAL_GIMP (dest) = R_VAL (&color); + G_VAL_GIMP (dest) = G_VAL (&color); + B_VAL_GIMP (dest) = B_VAL (&color); + + if (ico_get_bit_from_data (and_map, w, y * w + x)) + A_VAL_GIMP (dest) = 0; + else + A_VAL_GIMP (dest) = 255; + } + break; + + case 4: + for (y = 0; y < h; y++) + for (x = 0; x < w; x++) + { + guint32 color = palette[ico_get_nibble_from_data (xor_map, + w, y * w + x)]; + guint32 *dest = dest_vec + (h - 1 - y) * w + x; + + R_VAL_GIMP (dest) = R_VAL (&color); + G_VAL_GIMP (dest) = G_VAL (&color); + B_VAL_GIMP (dest) = B_VAL (&color); + + if (ico_get_bit_from_data (and_map, w, y * w + x)) + A_VAL_GIMP (dest) = 0; + else + A_VAL_GIMP (dest) = 255; + } + break; + + case 8: + for (y = 0; y < h; y++) + for (x = 0; x < w; x++) + { + guint32 color = palette[ico_get_byte_from_data (xor_map, + w, y * w + x)]; + guint32 *dest = dest_vec + (h - 1 - y) * w + x; + + R_VAL_GIMP (dest) = R_VAL (&color); + G_VAL_GIMP (dest) = G_VAL (&color); + B_VAL_GIMP (dest) = B_VAL (&color); + + if (ico_get_bit_from_data (and_map, w, y * w + x)) + A_VAL_GIMP (dest) = 0; + else + A_VAL_GIMP (dest) = 255; + } + break; + + default: + { + gint bytespp = data.bpp / 8; + + rowstride = ico_rowstride (w, data.bpp); + + for (y = 0; y < h; y++) + { + row = xor_map + rowstride * y; + + for (x = 0; x < w; x++) + { + guint32 *dest = dest_vec + (h - 1 - y) * w + x; + + B_VAL_GIMP (dest) = row[0]; + G_VAL_GIMP (dest) = row[1]; + R_VAL_GIMP (dest) = row[2]; + + if (data.bpp < 32) + { + if (ico_get_bit_from_data (and_map, w, y * w + x)) + A_VAL_GIMP (dest) = 0; + else + A_VAL_GIMP (dest) = 255; + } + else + { + A_VAL_GIMP (dest) = row[3]; + } + + row += bytespp; + } + } + } + } + if (palette) + g_free (palette); + g_free (xor_map); + g_free (and_map); + *width = w; + *height = h; + return TRUE; +} + +static gint32 +ico_load_layer (FILE *fp, + gint32 image, + gint32 icon_num, + guchar *buf, + gint maxsize, + IcoLoadInfo *info) +{ + gint width, height; + gint32 layer; + guint32 first_bytes; + GeglBuffer *buffer; + gchar name[ICO_MAXBUF]; + + if (fseek (fp, info->offset, SEEK_SET) < 0 || + ! ico_read_int32 (fp, &first_bytes, 1)) + return -1; + + if (first_bytes == ICO_PNG_MAGIC) + { + if (!ico_read_png (fp, first_bytes, buf, maxsize, &width, &height)) + return -1; + } + else if (first_bytes == 40) + { + if (!ico_read_icon (fp, first_bytes, buf, maxsize, &width, &height)) + return -1; + } + else + { + return -1; + } + + /* read successfully. add to image */ + g_snprintf (name, sizeof (name), _("Icon #%i"), icon_num+1); + layer = gimp_layer_new (image, name, width, height, + GIMP_RGBA_IMAGE, + 100, + gimp_image_get_default_new_layer_mode (image)); + gimp_image_insert_layer (image, layer, -1, icon_num); + + buffer = gimp_drawable_get_buffer (layer); + + gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, width, height), 0, + NULL, buf, GEGL_AUTO_ROWSTRIDE); + + g_object_unref (buffer); + + return layer; +} + + +gint32 +ico_load_image (const gchar *filename, + GError **error) +{ + FILE *fp; + IcoLoadInfo *info; + gint max_width, max_height; + gint i; + gint32 image; + guchar *buf; + guint icon_count; + gint maxsize; + + gimp_progress_init_printf (_("Opening '%s'"), + gimp_filename_to_utf8 (filename)); + + fp = g_fopen (filename, "rb"); + if (! fp) + { + g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), + _("Could not open '%s' for reading: %s"), + gimp_filename_to_utf8 (filename), g_strerror (errno)); + return -1; + } + + icon_count = ico_read_init (fp); + if (!icon_count) + { + fclose (fp); + return -1; + } + + info = ico_read_info (fp, icon_count, error); + if (! info) + { + fclose (fp); + return -1; + } + + /* find width and height of image */ + max_width = 0; + max_height = 0; + for (i = 0; i < icon_count; i++) + { + if (info[i].width > max_width) + max_width = info[i].width; + if (info[i].height > max_height) + max_height = info[i].height; + } + if (max_width <= 0 || max_height <= 0) + { + g_free (info); + fclose (fp); + return -1; + } + D(("image size: %ix%i\n", max_width, max_height)); + + image = gimp_image_new (max_width, max_height, GIMP_RGB); + gimp_image_set_filename (image, filename); + + maxsize = max_width * max_height * 4; + buf = g_new (guchar, max_width * max_height * 4); + for (i = 0; i < icon_count; i++) + { + ico_load_layer (fp, image, i, buf, maxsize, info+i); + } + g_free (buf); + g_free (info); + fclose (fp); + + gimp_progress_update (1.0); + + return image; +} + +gint32 +ico_load_thumbnail_image (const gchar *filename, + gint *width, + gint *height, + GError **error) +{ + FILE *fp; + IcoLoadInfo *info; + gint32 image; + gint w = 0; + gint h = 0; + gint bpp = 0; + gint match = 0; + gint i, icon_count; + guchar *buf; + + gimp_progress_init_printf (_("Opening thumbnail for '%s'"), + gimp_filename_to_utf8 (filename)); + + fp = g_fopen (filename, "rb"); + if (! fp) + { + g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), + _("Could not open '%s' for reading: %s"), + gimp_filename_to_utf8 (filename), g_strerror (errno)); + return -1; + } + + icon_count = ico_read_init (fp); + if (! icon_count) + { + fclose (fp); + return -1; + } + + D(("*** %s: Microsoft icon file, containing %i icon(s)\n", + filename, icon_count)); + + info = ico_read_info (fp, icon_count, error); + if (! info) + { + fclose (fp); + return -1; + } + + /* Do a quick scan of the icons in the file to find the best match */ + for (i = 0; i < icon_count; i++) + { + if ((info[i].width > w && w < *width) || + (info[i].height > h && h < *height)) + { + w = info[i].width; + h = info[i].height; + bpp = info[i].bpp; + + match = i; + } + else if (w == info[i].width && + h == info[i].height && + info[i].bpp > bpp) + { + /* better quality */ + bpp = info[i].bpp; + match = i; + } + } + + if (w <= 0 || h <= 0) + return -1; + + image = gimp_image_new (w, h, GIMP_RGB); + buf = g_new (guchar, w*h*4); + ico_load_layer (fp, image, match, buf, w*h*4, info+match); + g_free (buf); + + *width = w; + *height = h; + + D(("*** thumbnail successfully loaded.\n\n")); + + gimp_progress_update (1.0); + + g_free (info); + fclose (fp); + + return image; +} |