gimp/app/core/gimppalette-import.c
Sven Neumann c10b2c10b1 applied a (slightly modified) patch from Nickolay V. Shmyrev that changes
2004-11-14  Sven Neumann  <sven@gimp.org>

	* app/core/gimppalette-import.c: applied a (slightly modified)
	patch from Nickolay V. Shmyrev that changes the palette import
	function to not only read palettes in the RIFF format but also
	GIMP and Photoshop ACT palette files (bug #158297).
2004-11-14 20:15:05 +00:00

500 lines
13 KiB
C

/* The GIMP -- an image manipulation program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <string.h> /* memcpy */
#include <errno.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <glib-object.h>
#ifdef G_OS_WIN32
#include "libgimpbase/gimpwin32-io.h"
#endif
#include "libgimpbase/gimpbase.h"
#include "libgimpcolor/gimpcolor.h"
#include "core-types.h"
#include "base/pixel-region.h"
#include "gimpcontainer.h"
#include "gimpgradient.h"
#include "gimpimage.h"
#include "gimppalette.h"
#include "gimpprojection.h"
#include "gimp-intl.h"
#define MAX_IMAGE_COLORS (10000 * 2)
/* create a palette from a gradient ****************************************/
GimpPalette *
gimp_palette_import_from_gradient (GimpGradient *gradient,
gboolean reverse,
const gchar *palette_name,
gint n_colors)
{
GimpPalette *palette;
gdouble dx, cur_x;
GimpRGB color;
gint loop;
g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), NULL);
g_return_val_if_fail (palette_name != NULL, NULL);
g_return_val_if_fail (n_colors > 1, NULL);
palette = GIMP_PALETTE (gimp_palette_new (palette_name, FALSE));
dx = 1.0 / (n_colors - 1);
for (loop = 0, cur_x = 0; loop < n_colors; loop++, cur_x += dx)
{
gimp_gradient_get_color_at (gradient, cur_x, reverse, &color);
gimp_palette_add_entry (palette, NULL, &color);
}
return palette;
}
/* create a palette from a non-indexed image *******************************/
typedef struct _ImgColors ImgColors;
struct _ImgColors
{
guint count;
guint r_adj;
guint g_adj;
guint b_adj;
guchar r;
guchar g;
guchar b;
};
static gint count_color_entries = 0;
static GHashTable *
gimp_palette_import_store_colors (GHashTable *h_array,
guchar *colors,
guchar *colors_real,
gint n_colors)
{
gpointer found_color = NULL;
ImgColors *new_color;
guint key_colors = colors[0] * 256 * 256 + colors[1] * 256 + colors[2];
if (h_array == NULL)
{
h_array = g_hash_table_new (g_direct_hash, g_direct_equal);
count_color_entries = 0;
}
else
{
found_color = g_hash_table_lookup (h_array,
GUINT_TO_POINTER (key_colors));
}
if (found_color == NULL)
{
if (count_color_entries > MAX_IMAGE_COLORS)
{
/* Don't add any more new ones */
return h_array;
}
count_color_entries++;
new_color = g_new (ImgColors, 1);
new_color->count = 1;
new_color->r_adj = 0;
new_color->g_adj = 0;
new_color->b_adj = 0;
new_color->r = colors[0];
new_color->g = colors[1];
new_color->b = colors[2];
g_hash_table_insert (h_array, GUINT_TO_POINTER (key_colors), new_color);
}
else
{
new_color = (ImgColors *) found_color;
if (new_color->count < (G_MAXINT - 1))
new_color->count++;
/* Now do the adjustments ...*/
new_color->r_adj += (colors_real[0] - colors[0]);
new_color->g_adj += (colors_real[1] - colors[1]);
new_color->b_adj += (colors_real[2] - colors[2]);
/* Boundary conditions */
if(new_color->r_adj > (G_MAXINT - 255))
new_color->r_adj /= new_color->count;
if(new_color->g_adj > (G_MAXINT - 255))
new_color->g_adj /= new_color->count;
if(new_color->b_adj > (G_MAXINT - 255))
new_color->b_adj /= new_color->count;
}
return h_array;
}
static void
gimp_palette_import_create_sorted_list (gpointer key,
gpointer value,
gpointer user_data)
{
GSList **sorted_list = (GSList**) user_data;
ImgColors *color_tab = (ImgColors *) value;
*sorted_list = g_slist_prepend (*sorted_list, color_tab);
}
static gint
gimp_palette_import_sort_colors (gconstpointer a,
gconstpointer b)
{
ImgColors *s1 = (ImgColors *) a;
ImgColors *s2 = (ImgColors *) b;
if(s1->count > s2->count)
return -1;
if(s1->count < s2->count)
return 1;
return 0;
}
static void
gimp_palette_import_create_image_palette (gpointer data,
gpointer user_data)
{
GimpPalette *palette;
ImgColors *color_tab;
gint n_colors;
gchar *lab;
GimpRGB color;
palette = (GimpPalette *) user_data;
color_tab = (ImgColors *) data;
n_colors = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (palette),
"import_n_colors"));
if (palette->n_colors >= n_colors)
return;
lab = g_strdup_printf ("%s (occurs %u)", _("Untitled"), color_tab->count);
/* Adjust the colors to the mean of the the sample */
gimp_rgba_set_uchar
(&color,
(guchar) color_tab->r + (color_tab->r_adj / color_tab->count),
(guchar) color_tab->g + (color_tab->g_adj / color_tab->count),
(guchar) color_tab->b + (color_tab->b_adj / color_tab->count),
255);
gimp_palette_add_entry (palette, lab, &color);
g_free (lab);
}
static GimpPalette *
gimp_palette_import_image_make_palette (GHashTable *h_array,
const gchar *palette_name,
gint n_colors)
{
GimpPalette *palette;
GSList *sorted_list = NULL;
g_hash_table_foreach (h_array, gimp_palette_import_create_sorted_list,
&sorted_list);
sorted_list = g_slist_sort (sorted_list, gimp_palette_import_sort_colors);
palette = GIMP_PALETTE (gimp_palette_new (palette_name, FALSE));
g_object_set_data (G_OBJECT (palette), "import_n_colors",
GINT_TO_POINTER (n_colors));
g_slist_foreach (sorted_list, gimp_palette_import_create_image_palette,
palette);
g_object_set_data (G_OBJECT (palette), "import_n_colors", NULL);
/* Free up used memory
* Note the same structure is on both the hash list and the sorted
* list. So only delete it once.
*/
g_hash_table_destroy (h_array);
g_slist_foreach (sorted_list, (GFunc) g_free, NULL);
g_slist_free (sorted_list);
return palette;
}
GimpPalette *
gimp_palette_import_from_image (GimpImage *gimage,
const gchar *palette_name,
gint n_colors,
gint threshold)
{
PixelRegion imagePR;
guchar *image_data;
guchar *idata;
guchar rgb[MAX_CHANNELS];
guchar rgb_real[MAX_CHANNELS];
gboolean has_alpha, indexed;
gint width, height;
gint bytes, alpha;
gint i, j;
void *pr;
GimpImageType d_type;
GHashTable *store_array = NULL;
g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL);
g_return_val_if_fail (palette_name != NULL, NULL);
g_return_val_if_fail (n_colors > 1, NULL);
g_return_val_if_fail (threshold > 0, NULL);
/* Get the image information */
d_type = gimp_projection_get_image_type (gimage->projection);
bytes = gimp_projection_get_bytes (gimage->projection);
has_alpha = GIMP_IMAGE_TYPE_HAS_ALPHA (d_type);
indexed = GIMP_IMAGE_TYPE_IS_INDEXED (d_type);
width = gimage->width;
height = gimage->height;
pixel_region_init (&imagePR, gimp_projection_get_tiles (gimage->projection),
0, 0, width, height, FALSE);
alpha = bytes - 1;
/* iterate over the entire image */
for (pr = pixel_regions_register (1, &imagePR);
pr != NULL;
pr = pixel_regions_process (pr))
{
image_data = imagePR.data;
for (i = 0; i < imagePR.h; i++)
{
idata = image_data;
for (j = 0; j < imagePR.w; j++)
{
/* Get the rgb values for the color */
gimp_image_get_color (gimage, d_type, idata, rgb);
memcpy (rgb_real, rgb, MAX_CHANNELS); /* Structure copy */
rgb[0] = (rgb[0] / threshold) * threshold;
rgb[1] = (rgb[1] / threshold) * threshold;
rgb[2] = (rgb[2] / threshold) * threshold;
store_array =
gimp_palette_import_store_colors (store_array, rgb, rgb_real,
n_colors);
idata += bytes;
}
image_data += imagePR.rowstride;
}
}
return gimp_palette_import_image_make_palette (store_array,
palette_name,
n_colors);
}
/* create a palette from an indexed image **********************************/
GimpPalette *
gimp_palette_import_from_indexed_image (GimpImage *gimage,
const gchar *palette_name)
{
GimpPalette *palette;
gint count;
GimpRGB color;
g_return_val_if_fail (GIMP_IS_IMAGE (gimage), NULL);
g_return_val_if_fail (gimp_image_base_type (gimage) == GIMP_INDEXED, NULL);
g_return_val_if_fail (palette_name != NULL, NULL);
palette = GIMP_PALETTE (gimp_palette_new (palette_name, FALSE));
for (count= 0; count < gimage->num_cols; ++count)
{
gimp_rgba_set_uchar (&color,
gimage->cmap[count * 3],
gimage->cmap[count * 3 + 1],
gimage->cmap[count * 3 + 2],
255);
gimp_palette_add_entry (palette, NULL, &color);
}
return palette;
}
/* create a palette from a file **********************************/
typedef enum
{
GIMP_PALETTE_FILE_FORMAT_UNKNOWN,
GIMP_PALETTE_FILE_FORMAT_GPL, /* GIMP palette file */
GIMP_PALETTE_FILE_FORMAT_PAL, /* RIFF palette file */
GIMP_PALETTE_FILE_FORMAT_ACT /* Photoshop binary color palette file */
} GimpPaletteFileFormat;
static GimpPaletteFileFormat
gimp_palette_detect_file_format (const gchar *filename)
{
GimpPaletteFileFormat format = GIMP_PALETTE_FILE_FORMAT_UNKNOWN;
gint fd;
guchar header[16];
struct stat file_stat;
fd = open (filename, O_RDONLY);
if (fd)
{
if (read (fd, header, sizeof (header)) == sizeof (header))
{
if (strncmp (header + 0, "RIFF", 4) == 0 &&
strncmp (header + 8, "PAL data", 8) == 0)
{
format = GIMP_PALETTE_FILE_FORMAT_PAL;
}
if (strncmp (header, "GIMP Palette", 12) == 0)
{
format = GIMP_PALETTE_FILE_FORMAT_GPL;
}
}
if (fstat (fd, &file_stat) >= 0)
{
if (file_stat.st_size == 768)
format = GIMP_PALETTE_FILE_FORMAT_ACT;
}
close (fd);
}
return format;
}
GimpPalette *
gimp_palette_import_from_file (const gchar *filename,
const gchar *palette_name,
GError **error)
{
GimpPalette *palette = NULL;
GList *palette_list;
GimpRGB color;
gint fd;
guchar color_bytes[4];
g_return_val_if_fail (filename != NULL, NULL);
g_return_val_if_fail (palette_name != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
fd = open (filename, O_RDONLY);
if (! fd)
{
g_set_error (error,
G_FILE_ERROR, g_file_error_from_errno (errno),
_("Opening '%s' failed: %s"),
gimp_filename_to_utf8 (filename), g_strerror (errno));
return NULL;
}
switch (gimp_palette_detect_file_format (filename))
{
case GIMP_PALETTE_FILE_FORMAT_GPL:
palette_list = gimp_palette_load (filename, FALSE, error);
if (palette_list)
{
palette = palette_list->data;
g_list_free (palette_list);
}
break;
case GIMP_PALETTE_FILE_FORMAT_ACT:
palette = GIMP_PALETTE (gimp_palette_new (palette_name, FALSE));
while (read (fd, color_bytes, 3) == 3)
{
gimp_rgba_set_uchar (&color,
color_bytes[0],
color_bytes[1],
color_bytes[2],
255);
gimp_palette_add_entry (palette, NULL, &color);
}
break;
case GIMP_PALETTE_FILE_FORMAT_PAL:
palette = GIMP_PALETTE (gimp_palette_new (palette_name, FALSE));
lseek (fd, 28, SEEK_SET);
while (read (fd,
color_bytes, sizeof (color_bytes)) == sizeof (color_bytes))
{
gimp_rgba_set_uchar (&color,
color_bytes[0],
color_bytes[1],
color_bytes[2],
255);
gimp_palette_add_entry (palette, NULL, &color);
}
break;
default:
g_set_error (error,
0, 0,
_("Unknown type of palette file:\n%s"),
gimp_filename_to_utf8 (filename));
break;
}
close (fd);
return palette;
}