gimp/app/core/gimppalette-load.c

996 lines
29 KiB
C
Raw Normal View History

/* GIMP - The GNU 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 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 <stdlib.h>
#include <cairo.h>
#include <gegl.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include "libgimpbase/gimpbase.h"
Makefile.am configure.in added stuff for the new library below. 2001-01-23 Michael Natterer <mitch@gimp.org> * Makefile.am * configure.in * gimptool.in: added stuff for the new library below. * libgimpcolor/.cvsignore * libgimpcolor/Makefile.am * libgimpcolor/gimpcolor.h * libgimpcolor/gimpcolorspace.c * libgimpcolor/gimpcolorspace.h * libgimpcolor/gimpcolortypes.h * libgimpcolor/gimphsv.c * libgimpcolor/gimphsv.h * libgimpcolor/gimprgb.c * libgimpcolor/gimprgb.h: new shared library which both the app and plug-ins link against. The library depends only on glib. * libgimpcolor/gimpcolor.def * libgimpcolor/makefile.mingw.in * libgimpcolor/makefile.msc: added Win32 build files which definitely don't work. * libgimp/Makefile.am * libgimp/gimpcolor.[ch] * libgimp/gimpcolorspace.[ch]: removed. * libgimp/gimp.h * libgimp/gimpadaptivesupersample.c * libgimp/gimpbilinear.c * libgimp/gimppalette.c * libgimp/gimptypes.h: include the stuff from libgimpcolor. Plug-Ins don't need to include <libgimpcolor/gimpcolor.h> explicitely. LibGimp depends on libgimpcolor and thus also includes it's headers. * libgimp/gimp.def * libgimp/makefile.mingw.in: fiddled around with Win32 stuff... * app/Makefile.am: link against libgimpcolor.la * app/apptypes.h: include "libgimpcolor/gimpcolortypes.h" * app/asupsample.c * app/channels_dialog.c * app/colormap_dialog.c * app/commands.c * app/convert.c * app/devices.c * app/disp_callbacks.c * app/drawable.c * app/gimpcontext.c * app/gimpdnd.c * app/gimpimage.c * app/gimppalette.c * app/gimprc.c * app/gradient.c * app/libgimp_glue.c * app/palette.c * app/palette_import.c * app/qmask.c * app/xcf.c * app/tools/paint_core.c * app/tools/paintbrush.c * app/tools/pencil.c: include "libgimpcolor/gimpcolor.h" before all gimp includes because it's a standalone library. * plug-ins/FractalExplorer/Makefile.am * plug-ins/Lighting/Makefile.am * plug-ins/MapObject/Makefile.am * plug-ins/bmp/Makefile.am * plug-ins/common/Makefile.am * plug-ins/common/mkgen.pl * plug-ins/dbbrowser/Makefile.am * plug-ins/faxg3/Makefile.am * plug-ins/fits/Makefile.am * plug-ins/flame/Makefile.am * plug-ins/fp/Makefile.am * plug-ins/gap/Makefile.am * plug-ins/gdyntext/Makefile.am * plug-ins/gfig/Makefile.am * plug-ins/gflare/Makefile.am * plug-ins/gfli/Makefile.am * plug-ins/gimpressionist/Makefile.am * plug-ins/helpbrowser/Makefile.am * plug-ins/ifscompose/Makefile.am * plug-ins/imagemap/Makefile.am * plug-ins/maze/Makefile.am * plug-ins/mosaic/Makefile.am * plug-ins/pagecurl/Makefile.am * plug-ins/print/Makefile.am * plug-ins/rcm/Makefile.am * plug-ins/script-fu/Makefile.am * plug-ins/sel2path/Makefile.am * plug-ins/sgi/Makefile.am * plug-ins/webbrowser/Makefile.am * plug-ins/xjt/Makefile.am: add libgimpcolor.la to LDADD. * INSTALL: don't recommend to --disable-shared for development. * TODO.xml: increased some percentages, added plug-in help stuff.
2001-01-24 02:49:44 +08:00
#include "libgimpcolor/gimpcolor.h"
#include "core-types.h"
#include "gimp-utils.h"
#include "gimppalette.h"
#include "gimppalette-load.h"
#include "gimp-intl.h"
static gchar * gimp_palette_load_ase_block_name (GInputStream *input,
goffset file_size,
GError **error);
GList *
gimp_palette_load (GimpContext *context,
GFile *file,
GInputStream *input,
GError **error)
{
GimpPalette *palette = NULL;
GimpPaletteEntry *entry;
GDataInputStream *data_input;
gchar *str;
gsize str_len;
gchar *tok;
gint r, g, b;
gint linenum;
g_return_val_if_fail (G_IS_FILE (file), NULL);
g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
data_input = g_data_input_stream_new (input);
r = g = b = 0;
linenum = 1;
str_len = 1024;
str = gimp_data_input_stream_read_line_always (data_input, &str_len,
NULL, error);
if (! str)
goto failed;
if (! g_str_has_prefix (str, "GIMP Palette"))
{
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
_("Missing magic header."));
g_free (str);
goto failed;
}
g_free (str);
palette = g_object_new (GIMP_TYPE_PALETTE,
"mime-type", "application/x-gimp-palette",
NULL);
Made a GimpContainer out of the palette list: 2001-02-11 Michael Natterer <mitch@gimp.org> Made a GimpContainer out of the palette list: * app/Makefile.am * app/palettes.[ch]: new files for the global palette list. * app/gimpgradientpreview.[ch] * app/gimppalettepreview.[ch]: new widgets. * app/gimppalette.[ch]: derive it from GimpData to get all the preview etc. stuff. * app/datafiles.[ch]: new function datafiles_check_extension(), added a "loader_data" parameter to datafiles_read_directories() and pass it to the loader function. * app/gimpcontext.[ch]: added the palette (not really used yet except by the test dialogs). * app/gimpdatalist.[ch]: new function gimp_data_list_save_and_clear() which does everything needed for patterns_free(), brushes_free() ... * app/gimpdnd.c: added palette DND. * app/app_procs.c * app/brushes.c * app/color_notebook.h * app/commands.c * app/convert.c * app/gimpbrush.h * app/gimpbrushpipe.h * app/gimpgradient.c * app/gimppattern.h * app/gimppreview.c * app/gradients.c * app/module_db.c * app/palette.[ch] * app/paletteP.h * app/palette_import.c * app/palette_select.[ch] * app/patterns.c * app/plug_in.c * app/pdb/convert_cmds.c * app/pdb/palette_cmds.c * tools/pdbgen/pdb/convert.pdb * tools/pdbgen/pdb/palette.pdb: lotsa stuff: changed due to the above API changes, #define the file extensions in the GimpData subclasses' header files instead of hardcoding them in several places, ... * data/palettes/*: The same file format change as for the gradient files: - Save the palette name in a parsable form (as part of the file format, not in a comment. - Removed unserscores from the palette names. - Added an extension (Gimp PaLettes are ".gpl" files now ;-)
2001-02-12 00:14:25 +08:00
linenum++;
str_len = 1024;
str = gimp_data_input_stream_read_line_always (data_input, &str_len,
NULL, error);
if (! str)
goto failed;
Made a GimpContainer out of the palette list: 2001-02-11 Michael Natterer <mitch@gimp.org> Made a GimpContainer out of the palette list: * app/Makefile.am * app/palettes.[ch]: new files for the global palette list. * app/gimpgradientpreview.[ch] * app/gimppalettepreview.[ch]: new widgets. * app/gimppalette.[ch]: derive it from GimpData to get all the preview etc. stuff. * app/datafiles.[ch]: new function datafiles_check_extension(), added a "loader_data" parameter to datafiles_read_directories() and pass it to the loader function. * app/gimpcontext.[ch]: added the palette (not really used yet except by the test dialogs). * app/gimpdatalist.[ch]: new function gimp_data_list_save_and_clear() which does everything needed for patterns_free(), brushes_free() ... * app/gimpdnd.c: added palette DND. * app/app_procs.c * app/brushes.c * app/color_notebook.h * app/commands.c * app/convert.c * app/gimpbrush.h * app/gimpbrushpipe.h * app/gimpgradient.c * app/gimppattern.h * app/gimppreview.c * app/gradients.c * app/module_db.c * app/palette.[ch] * app/paletteP.h * app/palette_import.c * app/palette_select.[ch] * app/patterns.c * app/plug_in.c * app/pdb/convert_cmds.c * app/pdb/palette_cmds.c * tools/pdbgen/pdb/convert.pdb * tools/pdbgen/pdb/palette.pdb: lotsa stuff: changed due to the above API changes, #define the file extensions in the GimpData subclasses' header files instead of hardcoding them in several places, ... * data/palettes/*: The same file format change as for the gradient files: - Save the palette name in a parsable form (as part of the file format, not in a comment. - Removed unserscores from the palette names. - Added an extension (Gimp PaLettes are ".gpl" files now ;-)
2001-02-12 00:14:25 +08:00
if (g_str_has_prefix (str, "Name: "))
Made a GimpContainer out of the palette list: 2001-02-11 Michael Natterer <mitch@gimp.org> Made a GimpContainer out of the palette list: * app/Makefile.am * app/palettes.[ch]: new files for the global palette list. * app/gimpgradientpreview.[ch] * app/gimppalettepreview.[ch]: new widgets. * app/gimppalette.[ch]: derive it from GimpData to get all the preview etc. stuff. * app/datafiles.[ch]: new function datafiles_check_extension(), added a "loader_data" parameter to datafiles_read_directories() and pass it to the loader function. * app/gimpcontext.[ch]: added the palette (not really used yet except by the test dialogs). * app/gimpdatalist.[ch]: new function gimp_data_list_save_and_clear() which does everything needed for patterns_free(), brushes_free() ... * app/gimpdnd.c: added palette DND. * app/app_procs.c * app/brushes.c * app/color_notebook.h * app/commands.c * app/convert.c * app/gimpbrush.h * app/gimpbrushpipe.h * app/gimpgradient.c * app/gimppattern.h * app/gimppreview.c * app/gradients.c * app/module_db.c * app/palette.[ch] * app/paletteP.h * app/palette_import.c * app/palette_select.[ch] * app/patterns.c * app/plug_in.c * app/pdb/convert_cmds.c * app/pdb/palette_cmds.c * tools/pdbgen/pdb/convert.pdb * tools/pdbgen/pdb/palette.pdb: lotsa stuff: changed due to the above API changes, #define the file extensions in the GimpData subclasses' header files instead of hardcoding them in several places, ... * data/palettes/*: The same file format change as for the gradient files: - Save the palette name in a parsable form (as part of the file format, not in a comment. - Removed unserscores from the palette names. - Added an extension (Gimp PaLettes are ".gpl" files now ;-)
2001-02-12 00:14:25 +08:00
{
gchar *utf8;
utf8 = gimp_any_to_utf8 (g_strstrip (str + strlen ("Name: ")), -1,
_("Invalid UTF-8 string in palette file '%s'"),
gimp_file_get_utf8_name (file));
gimp_object_take_name (GIMP_OBJECT (palette), utf8);
g_free (str);
linenum++;
str_len = 1024;
str = gimp_data_input_stream_read_line_always (data_input, &str_len,
NULL, error);
if (! str)
goto failed;
if (g_str_has_prefix (str, "Columns: "))
{
gint columns;
if (! gimp_ascii_strtoi (g_strstrip (str + strlen ("Columns: ")),
NULL, 10, &columns))
{
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
_("Invalid column count."));
g_free (str);
goto failed;
}
if (columns < 0 || columns > 256)
{
g_message (_("Reading palette file '%s': "
"Invalid number of columns in line %d. "
libgimpwidgets/gimpquerybox.c configure the labels in the message dialog 2003-11-14 Michael Natterer <mitch@gimp.org> * libgimpwidgets/gimpquerybox.c * app/widgets/gimpwidgets-utils.c: configure the labels in the message dialog and the query boxes to do automatic word wrapping to be HIG compliant. * app/app_procs.c * app/batch.c * app/config/gimpconfig-deserialize.c * app/config/gimpconfig-path.c * app/config/gimpconfig-utils.c * app/config/gimpconfigwriter.c * app/config/gimpscanner.c * app/core/gimpbrush.c * app/core/gimpbrushgenerated.c * app/core/gimpbrushpipe.c * app/core/gimpdatafactory.c * app/core/gimpgradient.c * app/core/gimpimage-merge.c * app/core/gimpimage.c * app/core/gimpimagefile.c * app/core/gimplayer-floating-sel.c * app/core/gimppalette.c * app/core/gimppattern.c * app/core/gimpselection.c * app/display/gimpdisplayshell.c * app/file/file-utils.c * app/gui/brush-select.c * app/gui/dialogs-commands.c * app/gui/drawable-commands.c * app/gui/edit-commands.c * app/gui/file-commands.c * app/gui/file-new-dialog.c * app/gui/font-select.c * app/gui/gradient-select.c * app/gui/gui.c * app/gui/image-commands.c * app/gui/layers-commands.c * app/gui/palette-select.c * app/gui/palettes-commands.c * app/gui/pattern-select.c * app/gui/preferences-dialog.c * app/gui/select-commands.c * app/gui/stroke-dialog.c * app/gui/tool-options-menu.c * app/gui/vectors-commands.c * app/gui/view-commands.c * app/plug-in/plug-in-message.c * app/plug-in/plug-in.c * app/plug-in/plug-ins.c * app/text/gimptextlayer-xcf.c * app/text/gimptextlayer.c * app/tools/gimpcurvestool.c * app/tools/gimphuesaturationtool.c * app/tools/gimplevelstool.c * app/tools/gimptransformtool.c * app/vectors/gimpvectors-export.c * app/widgets/gimpdatafactoryview.c * app/widgets/gimphelp.c * app/widgets/gimptemplateview.c * app/widgets/gimptooloptionseditor.c * app/xcf/xcf.c * tools/pdbgen/pdb/image.pdb: removed explicit newlines from messages. Reduced number of translatable strings by making many file error messages the same. Quote single words and filenames with 'foo', not "foo". Replaced some more "drawable" by "layer". General message cleanup and consistency check. * app/pdb/image_cmds.c: regenerated.
2003-11-14 23:33:40 +08:00
"Using default value."),
gimp_file_get_utf8_name (file), linenum);
columns = 0;
}
gimp_palette_set_columns (palette, columns);
g_free (str);
linenum++;
str_len = 1024;
str = gimp_data_input_stream_read_line_always (data_input, &str_len,
NULL, error);
if (! str)
goto failed;
}
Made a GimpContainer out of the palette list: 2001-02-11 Michael Natterer <mitch@gimp.org> Made a GimpContainer out of the palette list: * app/Makefile.am * app/palettes.[ch]: new files for the global palette list. * app/gimpgradientpreview.[ch] * app/gimppalettepreview.[ch]: new widgets. * app/gimppalette.[ch]: derive it from GimpData to get all the preview etc. stuff. * app/datafiles.[ch]: new function datafiles_check_extension(), added a "loader_data" parameter to datafiles_read_directories() and pass it to the loader function. * app/gimpcontext.[ch]: added the palette (not really used yet except by the test dialogs). * app/gimpdatalist.[ch]: new function gimp_data_list_save_and_clear() which does everything needed for patterns_free(), brushes_free() ... * app/gimpdnd.c: added palette DND. * app/app_procs.c * app/brushes.c * app/color_notebook.h * app/commands.c * app/convert.c * app/gimpbrush.h * app/gimpbrushpipe.h * app/gimpgradient.c * app/gimppattern.h * app/gimppreview.c * app/gradients.c * app/module_db.c * app/palette.[ch] * app/paletteP.h * app/palette_import.c * app/palette_select.[ch] * app/patterns.c * app/plug_in.c * app/pdb/convert_cmds.c * app/pdb/palette_cmds.c * tools/pdbgen/pdb/convert.pdb * tools/pdbgen/pdb/palette.pdb: lotsa stuff: changed due to the above API changes, #define the file extensions in the GimpData subclasses' header files instead of hardcoding them in several places, ... * data/palettes/*: The same file format change as for the gradient files: - Save the palette name in a parsable form (as part of the file format, not in a comment. - Removed unserscores from the palette names. - Added an extension (Gimp PaLettes are ".gpl" files now ;-)
2001-02-12 00:14:25 +08:00
}
else /* old palette format */
{
gimp_object_take_name (GIMP_OBJECT (palette),
g_path_get_basename (gimp_file_get_utf8_name (file)));
Made a GimpContainer out of the palette list: 2001-02-11 Michael Natterer <mitch@gimp.org> Made a GimpContainer out of the palette list: * app/Makefile.am * app/palettes.[ch]: new files for the global palette list. * app/gimpgradientpreview.[ch] * app/gimppalettepreview.[ch]: new widgets. * app/gimppalette.[ch]: derive it from GimpData to get all the preview etc. stuff. * app/datafiles.[ch]: new function datafiles_check_extension(), added a "loader_data" parameter to datafiles_read_directories() and pass it to the loader function. * app/gimpcontext.[ch]: added the palette (not really used yet except by the test dialogs). * app/gimpdatalist.[ch]: new function gimp_data_list_save_and_clear() which does everything needed for patterns_free(), brushes_free() ... * app/gimpdnd.c: added palette DND. * app/app_procs.c * app/brushes.c * app/color_notebook.h * app/commands.c * app/convert.c * app/gimpbrush.h * app/gimpbrushpipe.h * app/gimpgradient.c * app/gimppattern.h * app/gimppreview.c * app/gradients.c * app/module_db.c * app/palette.[ch] * app/paletteP.h * app/palette_import.c * app/palette_select.[ch] * app/patterns.c * app/plug_in.c * app/pdb/convert_cmds.c * app/pdb/palette_cmds.c * tools/pdbgen/pdb/convert.pdb * tools/pdbgen/pdb/palette.pdb: lotsa stuff: changed due to the above API changes, #define the file extensions in the GimpData subclasses' header files instead of hardcoding them in several places, ... * data/palettes/*: The same file format change as for the gradient files: - Save the palette name in a parsable form (as part of the file format, not in a comment. - Removed unserscores from the palette names. - Added an extension (Gimp PaLettes are ".gpl" files now ;-)
2001-02-12 00:14:25 +08:00
}
while (str)
Made a GimpContainer out of the palette list: 2001-02-11 Michael Natterer <mitch@gimp.org> Made a GimpContainer out of the palette list: * app/Makefile.am * app/palettes.[ch]: new files for the global palette list. * app/gimpgradientpreview.[ch] * app/gimppalettepreview.[ch]: new widgets. * app/gimppalette.[ch]: derive it from GimpData to get all the preview etc. stuff. * app/datafiles.[ch]: new function datafiles_check_extension(), added a "loader_data" parameter to datafiles_read_directories() and pass it to the loader function. * app/gimpcontext.[ch]: added the palette (not really used yet except by the test dialogs). * app/gimpdatalist.[ch]: new function gimp_data_list_save_and_clear() which does everything needed for patterns_free(), brushes_free() ... * app/gimpdnd.c: added palette DND. * app/app_procs.c * app/brushes.c * app/color_notebook.h * app/commands.c * app/convert.c * app/gimpbrush.h * app/gimpbrushpipe.h * app/gimpgradient.c * app/gimppattern.h * app/gimppreview.c * app/gradients.c * app/module_db.c * app/palette.[ch] * app/paletteP.h * app/palette_import.c * app/palette_select.[ch] * app/patterns.c * app/plug_in.c * app/pdb/convert_cmds.c * app/pdb/palette_cmds.c * tools/pdbgen/pdb/convert.pdb * tools/pdbgen/pdb/palette.pdb: lotsa stuff: changed due to the above API changes, #define the file extensions in the GimpData subclasses' header files instead of hardcoding them in several places, ... * data/palettes/*: The same file format change as for the gradient files: - Save the palette name in a parsable form (as part of the file format, not in a comment. - Removed unserscores from the palette names. - Added an extension (Gimp PaLettes are ".gpl" files now ;-)
2001-02-12 00:14:25 +08:00
{
GError *my_error = NULL;
if (str[0] != '#' && str[0] != '\0')
{
tok = strtok (str, " \t");
if (tok)
r = atoi (tok);
else
g_message (_("Reading palette file '%s': "
"Missing RED component in line %d."),
gimp_file_get_utf8_name (file), linenum);
tok = strtok (NULL, " \t");
if (tok)
g = atoi (tok);
else
g_message (_("Reading palette file '%s': "
"Missing GREEN component in line %d."),
gimp_file_get_utf8_name (file), linenum);
tok = strtok (NULL, " \t");
if (tok)
b = atoi (tok);
else
g_message (_("Reading palette file '%s': "
"Missing BLUE component in line %d."),
gimp_file_get_utf8_name (file), linenum);
/* optional name */
tok = strtok (NULL, "\n");
if (r < 0 || r > 255 ||
g < 0 || g > 255 ||
b < 0 || b > 255)
g_message (_("Reading palette file '%s': "
"RGB value out of range in line %d."),
gimp_file_get_utf8_name (file), linenum);
/* don't call gimp_palette_add_entry here, it's rather inefficient */
entry = g_slice_new0 (GimpPaletteEntry);
gimp_rgba_set_uchar (&entry->color,
(guchar) r,
(guchar) g,
(guchar) b,
255);
entry->name = g_strdup (tok ? tok : _("Untitled"));
palette->colors = g_list_prepend (palette->colors, entry);
palette->n_colors++;
}
Made a GimpContainer out of the palette list: 2001-02-11 Michael Natterer <mitch@gimp.org> Made a GimpContainer out of the palette list: * app/Makefile.am * app/palettes.[ch]: new files for the global palette list. * app/gimpgradientpreview.[ch] * app/gimppalettepreview.[ch]: new widgets. * app/gimppalette.[ch]: derive it from GimpData to get all the preview etc. stuff. * app/datafiles.[ch]: new function datafiles_check_extension(), added a "loader_data" parameter to datafiles_read_directories() and pass it to the loader function. * app/gimpcontext.[ch]: added the palette (not really used yet except by the test dialogs). * app/gimpdatalist.[ch]: new function gimp_data_list_save_and_clear() which does everything needed for patterns_free(), brushes_free() ... * app/gimpdnd.c: added palette DND. * app/app_procs.c * app/brushes.c * app/color_notebook.h * app/commands.c * app/convert.c * app/gimpbrush.h * app/gimpbrushpipe.h * app/gimpgradient.c * app/gimppattern.h * app/gimppreview.c * app/gradients.c * app/module_db.c * app/palette.[ch] * app/paletteP.h * app/palette_import.c * app/palette_select.[ch] * app/patterns.c * app/plug_in.c * app/pdb/convert_cmds.c * app/pdb/palette_cmds.c * tools/pdbgen/pdb/convert.pdb * tools/pdbgen/pdb/palette.pdb: lotsa stuff: changed due to the above API changes, #define the file extensions in the GimpData subclasses' header files instead of hardcoding them in several places, ... * data/palettes/*: The same file format change as for the gradient files: - Save the palette name in a parsable form (as part of the file format, not in a comment. - Removed unserscores from the palette names. - Added an extension (Gimp PaLettes are ".gpl" files now ;-)
2001-02-12 00:14:25 +08:00
g_free (str);
linenum++;
str_len = 1024;
str = g_data_input_stream_read_line (data_input, &str_len,
NULL, &my_error);
if (! str && my_error)
{
g_message (_("Reading palette file '%s': "
"Read %d colors from truncated file: %s"),
gimp_file_get_utf8_name (file),
g_list_length (palette->colors),
my_error->message);
g_clear_error (&my_error);
}
}
palette->colors = g_list_reverse (palette->colors);
g_object_unref (data_input);
return g_list_prepend (NULL, palette);
failed:
g_object_unref (data_input);
if (palette)
g_object_unref (palette);
g_prefix_error (error, _("In line %d of palette file: "), linenum);
return NULL;
}
GList *
gimp_palette_load_act (GimpContext *context,
GFile *file,
GInputStream *input,
GError **error)
{
GimpPalette *palette;
gchar *palette_name;
guchar color_bytes[3];
gsize bytes_read;
g_return_val_if_fail (G_IS_FILE (file), NULL);
g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
palette_name = g_path_get_basename (gimp_file_get_utf8_name (file));
palette = GIMP_PALETTE (gimp_palette_new (context, palette_name));
g_free (palette_name);
while (g_input_stream_read_all (input, color_bytes, sizeof (color_bytes),
&bytes_read, NULL, NULL) &&
bytes_read == sizeof (color_bytes))
{
GimpRGB color;
gimp_rgba_set_uchar (&color,
color_bytes[0],
color_bytes[1],
color_bytes[2],
255);
gimp_palette_add_entry (palette, -1, NULL, &color);
}
return g_list_prepend (NULL, palette);
}
GList *
gimp_palette_load_riff (GimpContext *context,
GFile *file,
GInputStream *input,
GError **error)
{
GimpPalette *palette;
gchar *palette_name;
guchar color_bytes[4];
gsize bytes_read;
g_return_val_if_fail (G_IS_FILE (file), NULL);
g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
palette_name = g_path_get_basename (gimp_file_get_utf8_name (file));
palette = GIMP_PALETTE (gimp_palette_new (context, palette_name));
g_free (palette_name);
if (! g_seekable_seek (G_SEEKABLE (input), 28, G_SEEK_SET, NULL, error))
{
g_object_unref (palette);
return NULL;
}
while (g_input_stream_read_all (input, color_bytes, sizeof (color_bytes),
&bytes_read, NULL, NULL) &&
bytes_read == sizeof (color_bytes))
{
GimpRGB color;
gimp_rgba_set_uchar (&color,
color_bytes[0],
color_bytes[1],
color_bytes[2],
255);
gimp_palette_add_entry (palette, -1, NULL, &color);
}
return g_list_prepend (NULL, palette);
}
GList *
gimp_palette_load_psp (GimpContext *context,
GFile *file,
GInputStream *input,
GError **error)
{
GimpPalette *palette;
gchar *palette_name;
guchar color_bytes[4];
gint number_of_colors;
gsize bytes_read;
gint i, j;
gboolean color_ok;
gchar buffer[4096];
/*Maximum valid file size: 256 * 4 * 3 + 256 * 2 ~= 3650 bytes */
gchar **lines;
gchar **ascii_colors;
g_return_val_if_fail (G_IS_FILE (file), NULL);
g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
palette_name = g_path_get_basename (gimp_file_get_utf8_name (file));
palette = GIMP_PALETTE (gimp_palette_new (context, palette_name));
g_free (palette_name);
if (! g_seekable_seek (G_SEEKABLE (input), 16, G_SEEK_SET, NULL, error))
{
g_object_unref (palette);
return NULL;
}
if (! g_input_stream_read_all (input, buffer, sizeof (buffer) - 1,
&bytes_read, NULL, error))
{
g_object_unref (palette);
return NULL;
}
buffer[bytes_read] = '\0';
lines = g_strsplit (buffer, "\x0d\x0a", -1);
number_of_colors = atoi (lines[0]);
for (i = 0; i < number_of_colors; i++)
{
if (lines[i + 1] == NULL)
{
g_printerr ("Premature end of file reading %s.",
gimp_file_get_utf8_name (file));
break;
}
ascii_colors = g_strsplit (lines[i + 1], " ", 3);
color_ok = TRUE;
for (j = 0 ; j < 3; j++)
{
if (ascii_colors[j] == NULL)
{
g_printerr ("Corrupted palette file %s.",
gimp_file_get_utf8_name (file));
color_ok = FALSE;
break;
}
color_bytes[j] = atoi (ascii_colors[j]);
}
if (color_ok)
{
GimpRGB color;
gimp_rgba_set_uchar (&color,
color_bytes[0],
color_bytes[1],
color_bytes[2],
255);
gimp_palette_add_entry (palette, -1, NULL, &color);
}
g_strfreev (ascii_colors);
}
g_strfreev (lines);
return g_list_prepend (NULL, palette);
}
GList *
gimp_palette_load_aco (GimpContext *context,
GFile *file,
GInputStream *input,
GError **error)
{
GimpPalette *palette;
gchar *palette_name;
gint format_version;
gint number_of_colors;
gint i;
gchar header[4];
gsize bytes_read;
g_return_val_if_fail (G_IS_FILE (file), NULL);
g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
if (! g_input_stream_read_all (input, header, sizeof (header),
&bytes_read, NULL, error) ||
bytes_read != sizeof (header))
{
g_prefix_error (error,
_("Could not read header from palette file '%s': "),
gimp_file_get_utf8_name (file));
return NULL;
}
palette_name = g_path_get_basename (gimp_file_get_utf8_name (file));
palette = GIMP_PALETTE (gimp_palette_new (context, palette_name));
g_free (palette_name);
format_version = header[1] + (header[0] << 8);
number_of_colors = header[3] + (header[2] << 8);
for (i = 0; i < number_of_colors; i++)
{
gchar color_info[10];
gint color_space;
gint w, x, y, z;
gint xLab;
gint yLab;
void *pixels = NULL;
const Babl *src_format = NULL;
const Babl *dst_format = babl_format ("R'G'B' double");
gboolean color_ok = FALSE;
GimpRGB color;
GError *my_error = NULL;
if (! g_input_stream_read_all (input, color_info, sizeof (color_info),
&bytes_read, NULL, &my_error) ||
bytes_read != sizeof (color_info))
{
if (palette->colors)
{
g_message (_("Reading palette file '%s': "
"Read %d colors from truncated file: %s"),
gimp_file_get_utf8_name (file),
g_list_length (palette->colors),
my_error ?
my_error->message : _("Premature end of file."));
g_clear_error (&my_error);
break;
}
g_propagate_error (error, my_error);
g_object_unref (palette);
return NULL;
}
color_space = color_info[1] + (color_info[0] << 8);
w = (guchar) color_info[3] + ((guchar) color_info[2] << 8);
x = (guchar) color_info[5] + ((guchar) color_info[4] << 8);
y = (guchar) color_info[7] + ((guchar) color_info[6] << 8);
z = (guchar) color_info[9] + ((guchar) color_info[8] << 8);
/* Lab-specific values */
xLab = (gint) color_info[5] | ((gint) color_info[4] << 8);
yLab = (gint) color_info[7] | ((gint) color_info[6] << 8);
if (xLab >= 32768)
xLab -= 65536;
if (yLab >= 32768)
yLab -= 65536;
if (color_space == 0) /* RGB */
{
gdouble rgb[3] = { ((gdouble) w) / 65536.0,
((gdouble) x) / 65536.0,
((gdouble) y) / 65536.0 };
src_format = babl_format ("R'G'B' double");
pixels = rgb;
color_ok = TRUE;
}
else if (color_space == 1) /* HSV */
{
gdouble hsv[3] = { ((gdouble) w) / 65536.0,
((gdouble) x) / 65536.0,
((gdouble) y) / 65536.0};
src_format = babl_format ("HSV double");
pixels = hsv;
color_ok = TRUE;
}
else if (color_space == 2) /* CMYK */
{
gdouble cmyk[4] = { 1.0 - ((gdouble) w) / 65536.0,
1.0 - ((gdouble) x) / 65536.0,
1.0 - ((gdouble) y) / 65536.0,
1.0 - ((gdouble) z) / 65536.0 };
src_format = babl_format ("CMYK double");
pixels = cmyk;
color_ok = TRUE;
}
else if (color_space == 7) /* CIE Lab */
{
gdouble lab[3] = { ((gdouble) w) / 10000.0,
((gdouble) xLab) / 25500.0,
((gdouble) yLab) / 25500.0 };
src_format = babl_format ("CIE Lab float");
pixels = lab;
color_ok = TRUE;
}
else if (color_space == 8) /* Grayscale */
{
gdouble k[1] = { 1.0 - (((gdouble) w) / 10000.0) };
src_format = babl_format ("Y' double");
pixels = k;
color_ok = TRUE;
}
else if (color_space == 9) /* Wide? CMYK */
{
gdouble cmyk[4] = { 1.0 - ((gdouble) w) / 10000.0,
1.0 - ((gdouble) x) / 10000.0,
1.0 - ((gdouble) y) / 10000.0,
1.0 - ((gdouble) z) / 10000.0 };
src_format = babl_format ("CMYK double");
pixels = cmyk;
color_ok = TRUE;
}
else
{
g_printerr ("Unsupported color space (%d) in ACO file %s\n",
color_space, gimp_file_get_utf8_name (file));
}
if (color_ok)
babl_process (babl_fish (src_format, dst_format), (gdouble *) pixels,
&color, 1);
if (format_version == 2)
{
gchar format2_preamble[4];
gint number_of_chars;
if (! g_input_stream_read_all (input,
format2_preamble,
sizeof (format2_preamble),
&bytes_read, NULL, error) ||
bytes_read != sizeof (format2_preamble))
{
g_object_unref (palette);
return NULL;
}
number_of_chars = format2_preamble[3] + (format2_preamble[2] << 8);
if (! g_seekable_seek (G_SEEKABLE (input), number_of_chars * 2,
G_SEEK_SET, NULL, error))
{
g_object_unref (palette);
return NULL;
}
}
if (color_ok)
gimp_palette_add_entry (palette, -1, NULL, &color);
}
return g_list_prepend (NULL, palette);
}
GList *
gimp_palette_load_ase (GimpContext *context,
GFile *file,
GInputStream *input,
GError **error)
{
GimpPalette *palette;
gchar *palette_name;
goffset file_size;
gint num_cols;
gint i;
gchar header[8];
gshort group;
gsize bytes_read;
gboolean skip_first = FALSE;
const Babl *src_format = NULL;
const Babl *dst_format = babl_format ("R'G'B' double");
g_return_val_if_fail (G_IS_FILE (file), NULL);
g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
/* Get file size */
g_seekable_seek (G_SEEKABLE (input), 0, G_SEEK_END, NULL, error);
file_size = g_seekable_tell (G_SEEKABLE (input));
g_seekable_seek (G_SEEKABLE (input), 0, G_SEEK_SET, NULL, error);
if (! g_input_stream_read_all (input, header, sizeof (header),
&bytes_read, NULL, error) ||
bytes_read != sizeof (header))
{
g_prefix_error (error,
_("Could not read header from palette file '%s': "),
gimp_file_get_utf8_name (file));
return NULL;
}
/* Checking header values */
if (! g_str_has_prefix (header + 0, "ASEF") ||
header[5] != 0x01)
{
g_prefix_error (error, _("Invalid ASE header: %s"),
gimp_file_get_utf8_name (file));
return NULL;
}
if (! g_input_stream_read_all (input, &num_cols, sizeof (num_cols),
&bytes_read, NULL, error))
{
g_prefix_error (error,
_("Invalid number of colors in palette."));
return NULL;
}
num_cols = GINT32_FROM_BE (num_cols);
if (num_cols <= 1)
{
g_prefix_error (error, _("Invalid number of colors: %s."),
gimp_file_get_utf8_name (file));
return NULL;
}
/* First block contains the palette name if
* one is defined. */
if (! g_input_stream_read_all (input, &group, sizeof (group),
&bytes_read, NULL, error))
{
g_prefix_error (error, _("Invalid ASE file: %s."),
gimp_file_get_utf8_name (file));
return NULL;
}
group = GINT16_FROM_BE (group);
/* If first marker is 0x01, then the palette has no group name */
if (group != 1)
{
palette_name = gimp_palette_load_ase_block_name (input, file_size,
error);
palette = GIMP_PALETTE (gimp_palette_new (context, palette_name));
num_cols -= 1;
}
else
{
palette_name = g_path_get_basename (gimp_file_get_utf8_name (file));
palette = GIMP_PALETTE (gimp_palette_new (context, palette_name));
skip_first = TRUE;
}
g_free (palette_name);
/* Header blocks are considered a "color" so we offset the count here */
num_cols -= 1;
for (i = 0; i < num_cols; i++)
{
gchar color_space[4];
gchar *color_name;
GimpRGB color;
gshort spot_color;
gfloat *pixels;
gint components = 0;
gboolean valid_color = TRUE;
if (! skip_first)
{
if (! g_input_stream_read_all (input, &group, sizeof (group),
&bytes_read, NULL, error))
{
g_printerr ("Invalid ASE color entry: %s.",
gimp_file_get_utf8_name (file));
break;
}
}
skip_first = FALSE;
color_name = gimp_palette_load_ase_block_name (input, file_size, error);
if (! color_name)
break;
g_input_stream_read_all (input, color_space, sizeof (color_space),
&bytes_read, NULL, error);
/* Color formats */
if (g_str_has_prefix (color_space, "RGB"))
{
components = 3;
src_format = babl_format ("R'G'B' float");
}
else if (g_str_has_prefix (color_space, "GRAY"))
{
components = 1;
src_format = babl_format ("Y' float");
}
else if (g_str_has_prefix (color_space, "CMYK"))
{
components = 4;
src_format = babl_format ("CMYK float");
}
else if (g_str_has_prefix (color_space, "LAB"))
{
components = 3;
src_format = babl_format ("CIE Lab float");
}
if (components == 0)
{
g_printerr (_("Invalid color components: %s."), color_space);
g_free (color_name);
break;
}
pixels = g_malloc (sizeof (gfloat) * components);
for (gint j = 0; j < components; j++)
{
gint tmp;
if (! g_input_stream_read_all (input, &tmp, sizeof (tmp),
&bytes_read, NULL, error))
{
g_printerr (_("Invalid ASE color entry: %s."),
gimp_file_get_utf8_name (file));
g_free (color_name);
valid_color = FALSE;
break;
}
/* Convert 4 bytes to a 32bit float value */
tmp = GINT32_FROM_BE (tmp);
pixels[j] = *(gfloat *) &tmp;
}
if (! valid_color)
break;
/* The L component of LAB goes from 0 to 100 percent */
if (g_str_has_prefix (color_space, "LAB"))
pixels[0] *= 100;
babl_process (babl_fish (src_format, dst_format), pixels, &color, 1);
g_free (pixels);
/* TODO: When GIMP supports spot colors, use this information in
* the palette. */
if (! g_input_stream_read_all (input, &spot_color, sizeof (spot_color),
&bytes_read, NULL, error))
{
g_printerr (_("Invalid ASE color entry: %s."),
gimp_file_get_utf8_name (file));
g_free (color_name);
break;
}
gimp_palette_add_entry (palette, -1, color_name, &color);
g_free (color_name);
}
return g_list_prepend (NULL, palette);
}
static gchar *
gimp_palette_load_ase_block_name (GInputStream *input,
goffset file_size,
GError **error)
{
gint block_length;
gushort pal_name_len;
gunichar2 *pal_name = NULL;
gchar *pal_name_utf8;
goffset current_pos;
gsize bytes_read;
if (! g_input_stream_read_all (input, &block_length, sizeof (block_length),
&bytes_read, NULL, error))
{
g_printerr (_("Invalid ASE palette name."));
return NULL;
}
block_length = GINT32_FROM_BE (block_length);
current_pos = g_seekable_tell (G_SEEKABLE (input));
if (block_length <= 0 || block_length > (file_size - current_pos))
{
g_printerr (_("Invalid ASE block size."));
return NULL;
}
if (! g_input_stream_read_all (input, &pal_name_len, sizeof (pal_name_len),
&bytes_read, NULL, error))
{
g_printerr (_("Invalid ASE palette name."));
return NULL;
}
pal_name_len = GUINT16_FROM_BE (pal_name_len);
current_pos = g_seekable_tell (G_SEEKABLE (input));
if (pal_name_len <= 0 || pal_name_len > (file_size - current_pos))
{
g_printerr (_("Invalid ASE name size."));
return NULL;
}
pal_name = g_malloc (pal_name_len * 2);
for (gint i = 0; i < pal_name_len; i++)
{
if (! g_input_stream_read_all (input, &pal_name[i], 2,
&bytes_read, NULL, error))
{
g_printerr (_("Invalid ASE palette name."));
g_free (pal_name);
return NULL;
}
pal_name[i] = GUINT16_FROM_BE (pal_name[i]);
}
pal_name_utf8 = g_utf16_to_utf8 (pal_name, -1, NULL, NULL, NULL);
g_free (pal_name);
return pal_name_utf8;
}
GList *
gimp_palette_load_css (GimpContext *context,
GFile *file,
GInputStream *input,
GError **error)
{
GimpPalette *palette;
GDataInputStream *data_input;
gchar *name;
GRegex *regex;
gchar *buf;
g_return_val_if_fail (G_IS_FILE (file), NULL);
g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
regex = g_regex_new (".*color.*:(?P<param>.*)", G_REGEX_CASELESS, 0, error);
if (! regex)
return NULL;
name = g_path_get_basename (gimp_file_get_utf8_name (file));
palette = GIMP_PALETTE (gimp_palette_new (context, name));
g_free (name);
data_input = g_data_input_stream_new (input);
do
{
gsize buf_len = 1024;
buf = g_data_input_stream_read_line (data_input, &buf_len, NULL, NULL);
if (buf)
{
GMatchInfo *matches;
if (g_regex_match (regex, buf, 0, &matches))
{
GimpRGB color;
gchar *word = g_match_info_fetch_named (matches, "param");
if (gimp_rgb_parse_css (&color, word, -1))
{
if (! gimp_palette_find_entry (palette, &color, NULL))
{
gimp_palette_add_entry (palette, -1, NULL, &color);
}
}
g_free (word);
}
g_match_info_free (matches);
g_free (buf);
}
}
while (buf);
g_regex_unref (regex);
g_object_unref (data_input);
return g_list_prepend (NULL, palette);
}
GimpPaletteFileFormat
gimp_palette_load_detect_format (GFile *file,
GInputStream *input)
{
GimpPaletteFileFormat format = GIMP_PALETTE_FILE_FORMAT_UNKNOWN;
gchar header[16];
gsize bytes_read;
if (g_input_stream_read_all (input, &header, sizeof (header),
&bytes_read, NULL, NULL) &&
bytes_read == sizeof (header))
{
if (g_str_has_prefix (header + 0, "RIFF") &&
g_str_has_prefix (header + 8, "PAL data"))
{
format = GIMP_PALETTE_FILE_FORMAT_RIFF_PAL;
}
else if (g_str_has_prefix (header, "GIMP Palette"))
{
format = GIMP_PALETTE_FILE_FORMAT_GPL;
}
else if (g_str_has_prefix (header, "JASC-PAL"))
{
format = GIMP_PALETTE_FILE_FORMAT_PSP_PAL;
}
}
if (format == GIMP_PALETTE_FILE_FORMAT_UNKNOWN)
{
gchar *lower = g_ascii_strdown (gimp_file_get_utf8_name (file), -1);
if (g_str_has_suffix (lower, ".aco"))
{
format = GIMP_PALETTE_FILE_FORMAT_ACO;
}
if (g_str_has_suffix (lower, ".ase"))
{
format = GIMP_PALETTE_FILE_FORMAT_ASE;
}
else if (g_str_has_suffix (lower, ".css"))
{
format = GIMP_PALETTE_FILE_FORMAT_CSS;
}
g_free (lower);
}
if (format == GIMP_PALETTE_FILE_FORMAT_UNKNOWN)
{
GFileInfo *info = g_file_query_info (file,
G_FILE_ATTRIBUTE_STANDARD_SIZE,
G_FILE_QUERY_INFO_NONE,
NULL, NULL);
if (info)
{
goffset size = g_file_info_get_size (info);
if (size == 768)
format = GIMP_PALETTE_FILE_FORMAT_ACT;
g_object_unref (info);
}
}
g_seekable_seek (G_SEEKABLE (input), 0, G_SEEK_SET, NULL, NULL);
return format;
}