Gimp/modules/display-filter-proof.c
Nils Philippsen b2c6bd7614 Backport lcms 2.x support from master, making it optional.
In order to retain support for lcms 1.x, this merges the lcms2-specific
code as a configurable alternative.

The user can specify which version to use by specifying
"--with-lcms=lcms1" or "--with-lcms=lcms2" when running configure. If no
version is specified explicitly, version 1.x will be preferred if both
versions are available. This ensures that GIMP is built with the same
lcms version as an older version, if the same configure switches are
used and the same libraries are available.

This builds on the following commits from the master branch:

    commit c59ab4d817
    Author:     Massimo Valentini <mvalentini@src.gnome.org>
    AuthorDate: Tue Sep 4 17:53:06 2012 +0200

        lcms: oversight

    commit 8bbfc9e5cf
    Author:     Michael Natterer <mitch@gimp.org>
    AuthorDate: Mon Sep 3 20:03:53 2012 +0200

        Bug 675558 - switch from lcms-1 to lcms-2

        Applied modified patch from Hartmut Kuhse which ports to lcms-2.
2013-09-18 13:47:12 +02:00

527 lines
16 KiB
C

/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995-1997 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 <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <glib.h> /* lcms.h uses the "inline" keyword */
#ifdef HAVE_LCMS1
#include <lcms.h>
typedef DWORD cmsUInt32Number;
#else
#include <lcms2.h>
#endif
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "libgimpcolor/gimpcolor.h"
#include "libgimpconfig/gimpconfig.h"
#include "libgimpmath/gimpmath.h"
#include "libgimpmodule/gimpmodule.h"
#include "libgimpwidgets/gimpwidgets.h"
#include "libgimp/libgimp-intl.h"
#define CDISPLAY_TYPE_PROOF (cdisplay_proof_get_type ())
#define CDISPLAY_PROOF(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CDISPLAY_TYPE_PROOF, CdisplayProof))
#define CDISPLAY_PROOF_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CDISPLAY_TYPE_PROOF, CdisplayProofClass))
#define CDISPLAY_IS_PROOF(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CDISPLAY_TYPE_PROOF))
#define CDISPLAY_IS_PROOF_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CDISPLAY_TYPE_PROOF))
typedef struct _CdisplayProof CdisplayProof;
typedef struct _CdisplayProofClass CdisplayProofClass;
struct _CdisplayProof
{
GimpColorDisplay parent_instance;
gint intent;
gboolean bpc;
gchar *profile;
cmsHTRANSFORM transform;
};
struct _CdisplayProofClass
{
GimpColorDisplayClass parent_instance;
};
enum
{
PROP_0,
PROP_INTENT,
PROP_BPC,
PROP_PROFILE
};
GType cdisplay_proof_get_type (void);
static void cdisplay_proof_finalize (GObject *object);
static void cdisplay_proof_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void cdisplay_proof_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void cdisplay_proof_convert_surface (GimpColorDisplay *display,
cairo_surface_t *surface);
static GtkWidget * cdisplay_proof_configure (GimpColorDisplay *display);
static void cdisplay_proof_changed (GimpColorDisplay *display);
static const GimpModuleInfo cdisplay_proof_info =
{
GIMP_MODULE_ABI_VERSION,
N_("Color proof filter using ICC color profile"),
"Banlu Kemiyatorn <id@project-ile.net>",
"v0.1",
"(c) 2002-2003, released under the GPL",
"November 14, 2003"
};
G_DEFINE_DYNAMIC_TYPE (CdisplayProof, cdisplay_proof,
GIMP_TYPE_COLOR_DISPLAY)
G_MODULE_EXPORT const GimpModuleInfo *
gimp_module_query (GTypeModule *module)
{
return &cdisplay_proof_info;
}
G_MODULE_EXPORT gboolean
gimp_module_register (GTypeModule *module)
{
cdisplay_proof_register_type (module);
return TRUE;
}
static void
cdisplay_proof_class_init (CdisplayProofClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GimpColorDisplayClass *display_class = GIMP_COLOR_DISPLAY_CLASS (klass);
object_class->finalize = cdisplay_proof_finalize;
object_class->get_property = cdisplay_proof_get_property;
object_class->set_property = cdisplay_proof_set_property;
GIMP_CONFIG_INSTALL_PROP_ENUM (object_class, PROP_INTENT,
"intent", NULL,
GIMP_TYPE_COLOR_RENDERING_INTENT,
GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL,
0);
GIMP_CONFIG_INSTALL_PROP_BOOLEAN (object_class, PROP_BPC,
"black-point-compensation", NULL,
FALSE,
0);
GIMP_CONFIG_INSTALL_PROP_PATH (object_class, PROP_PROFILE,
"profile", NULL,
GIMP_CONFIG_PATH_FILE, NULL,
0);
display_class->name = _("Color Proof");
display_class->help_id = "gimp-colordisplay-proof";
display_class->stock_id = GIMP_STOCK_DISPLAY_FILTER_PROOF;
display_class->convert_surface = cdisplay_proof_convert_surface;
display_class->configure = cdisplay_proof_configure;
display_class->changed = cdisplay_proof_changed;
#ifdef HAVE_LCMS1
cmsErrorAction (LCMS_ERROR_IGNORE);
#endif
}
static void
cdisplay_proof_class_finalize (CdisplayProofClass *klass)
{
}
static void
cdisplay_proof_init (CdisplayProof *proof)
{
proof->transform = NULL;
proof->profile = NULL;
}
static void
cdisplay_proof_finalize (GObject *object)
{
CdisplayProof *proof = CDISPLAY_PROOF (object);
if (proof->profile)
{
g_free (proof->profile);
proof->profile = NULL;
}
if (proof->transform)
{
cmsDeleteTransform (proof->transform);
proof->transform = NULL;
}
G_OBJECT_CLASS (cdisplay_proof_parent_class)->finalize (object);
}
static void
cdisplay_proof_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
CdisplayProof *proof = CDISPLAY_PROOF (object);
switch (property_id)
{
case PROP_INTENT:
g_value_set_enum (value, proof->intent);
break;
case PROP_BPC:
g_value_set_boolean (value, proof->bpc);
break;
case PROP_PROFILE:
g_value_set_string (value, proof->profile);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
cdisplay_proof_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
CdisplayProof *proof = CDISPLAY_PROOF (object);
switch (property_id)
{
case PROP_INTENT:
proof->intent = g_value_get_enum (value);
break;
case PROP_BPC:
proof->bpc = g_value_get_boolean (value);
break;
case PROP_PROFILE:
g_free (proof->profile);
proof->profile = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
gimp_color_display_changed (GIMP_COLOR_DISPLAY (proof));
}
static void
cdisplay_proof_convert_surface (GimpColorDisplay *display,
cairo_surface_t *surface)
{
CdisplayProof *proof = CDISPLAY_PROOF (display);
gint width = cairo_image_surface_get_width (surface);
gint height = cairo_image_surface_get_height (surface);
gint stride = cairo_image_surface_get_stride (surface);
guchar *buf = cairo_image_surface_get_data (surface);
cairo_format_t fmt = cairo_image_surface_get_format (surface);
guchar *rowbuf;
gint x, y;
guchar r, g, b, a;
if (fmt != CAIRO_FORMAT_ARGB32)
return;
if (! proof->transform)
return;
rowbuf = g_malloc (stride);
for (y = 0; y < height; y++, buf += stride)
{
/* Switch buf from ARGB premul to ARGB non-premul, since lcms ignores the
* alpha channel. The macro takes care of byte order.
*/
for (x = 0; x < width; x++)
{
GIMP_CAIRO_ARGB32_GET_PIXEL (buf + 4*x, r, g, b, a);
rowbuf[4*x+0] = a;
rowbuf[4*x+1] = r;
rowbuf[4*x+2] = g;
rowbuf[4*x+3] = b;
}
cmsDoTransform (proof->transform, rowbuf, rowbuf, width);
/* And back to ARGB premul */
for (x = 0; x < width; x++)
{
a = rowbuf[4*x+0];
r = rowbuf[4*x+1];
g = rowbuf[4*x+2];
b = rowbuf[4*x+3];
GIMP_CAIRO_ARGB32_SET_PIXEL (buf + 4*x, r, g, b, a);
}
}
g_free (rowbuf);
}
static void
cdisplay_proof_combo_box_set_active (GimpColorProfileComboBox *combo,
const gchar *filename)
{
cmsHPROFILE profile = NULL;
gchar *label = NULL;
if (filename)
profile = cmsOpenProfileFromFile (filename, "r");
if (profile)
{
#ifdef HAVE_LCMS1
label = gimp_any_to_utf8 (cmsTakeProductDesc (profile), -1, NULL);
#else
cmsUInt32Number descSize;
gchar *descData;
descSize = cmsGetProfileInfoASCII(profile, cmsInfoDescription,
"en", "US", NULL, 0);
if (descSize > 0)
{
descData = g_new (gchar, descSize + 1);
descSize = cmsGetProfileInfoASCII (profile, cmsInfoDescription,
"en", "US", descData, descSize);
if (descSize > 0)
label = gimp_any_to_utf8 (descData, -1, NULL);
g_free (descData);
}
#endif
if (! label)
#ifdef HAVE_LCMS1
label = gimp_any_to_utf8 (cmsTakeProductName (profile), -1, NULL);
#else
{
descSize = cmsGetProfileInfoASCII (profile, cmsInfoModel,
"en", "US", NULL, 0);
if (descSize > 0)
{
descData = g_new (gchar, descSize + 1);
descSize = cmsGetProfileInfoASCII (profile, cmsInfoModel,
"en", "US", descData, descSize);
if (descSize > 0)
label = gimp_any_to_utf8 (descData, -1, NULL);
g_free (descData);
}
#endif
}
cmsCloseProfile (profile);
}
gimp_color_profile_combo_box_set_active (combo, filename, label);
g_free (label);
}
static void
cdisplay_proof_file_chooser_dialog_response (GtkFileChooser *dialog,
gint response,
GimpColorProfileComboBox *combo)
{
if (response == GTK_RESPONSE_ACCEPT)
{
gchar *filename = gtk_file_chooser_get_filename (dialog);
if (filename)
{
cdisplay_proof_combo_box_set_active (combo, filename);
g_free (filename);
}
}
gtk_widget_hide (GTK_WIDGET (dialog));
}
static GtkWidget *
cdisplay_proof_file_chooser_dialog_new (void)
{
GtkWidget *dialog;
GtkFileFilter *filter;
dialog = gtk_file_chooser_dialog_new (_("Choose an ICC Color Profile"),
NULL,
GTK_FILE_CHOOSER_ACTION_OPEN,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
NULL);
gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
GTK_RESPONSE_ACCEPT,
GTK_RESPONSE_CANCEL,
-1);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
#ifndef G_OS_WIN32
{
const gchar folder[] = "/usr/share/color/icc";
if (g_file_test (folder, G_FILE_TEST_IS_DIR))
gtk_file_chooser_add_shortcut_folder (GTK_FILE_CHOOSER (dialog),
folder, NULL);
}
#endif
filter = gtk_file_filter_new ();
gtk_file_filter_set_name (filter, _("All files (*.*)"));
gtk_file_filter_add_pattern (filter, "*");
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
filter = gtk_file_filter_new ();
gtk_file_filter_set_name (filter, _("ICC color profile (*.icc, *.icm)"));
gtk_file_filter_add_pattern (filter, "*.[Ii][Cc][Cc]");
gtk_file_filter_add_pattern (filter, "*.[Ii][Cc][Mm]");
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter);
return dialog;
}
static void
cdisplay_proof_profile_changed (GtkWidget *combo,
CdisplayProof *proof)
{
gchar *profile;
profile = gimp_color_profile_combo_box_get_active (GIMP_COLOR_PROFILE_COMBO_BOX (combo));
g_object_set (proof,
"profile", profile,
NULL);
g_free (profile);
}
static GtkWidget *
cdisplay_proof_configure (GimpColorDisplay *display)
{
CdisplayProof *proof = CDISPLAY_PROOF (display);
GtkWidget *table;
GtkWidget *combo;
GtkWidget *toggle;
GtkWidget *dialog;
gchar *history;
table = gtk_table_new (3, 2, FALSE);
gtk_table_set_col_spacings (GTK_TABLE (table), 6);
gtk_table_set_row_spacings (GTK_TABLE (table), 6);
dialog = cdisplay_proof_file_chooser_dialog_new ();
history = gimp_personal_rc_file ("profilerc");
combo = gimp_color_profile_combo_box_new (dialog, history);
g_free (history);
g_signal_connect (dialog, "response",
G_CALLBACK (cdisplay_proof_file_chooser_dialog_response),
combo);
g_signal_connect (combo, "changed",
G_CALLBACK (cdisplay_proof_profile_changed),
proof);
if (proof->profile)
cdisplay_proof_combo_box_set_active (GIMP_COLOR_PROFILE_COMBO_BOX (combo),
proof->profile);
gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
_("_Profile:"), 0.0, 0.5,
combo, 1, FALSE);
combo = gimp_prop_enum_combo_box_new (G_OBJECT (proof), "intent", 0, 0);
gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
_("_Intent:"), 0.0, 0.5,
combo, 1, FALSE);
toggle = gimp_prop_check_button_new (G_OBJECT (proof),
"black-point-compensation",
_("_Black Point Compensation"));
gtk_table_attach_defaults (GTK_TABLE (table), toggle, 1, 2, 2, 3);
gtk_widget_show (toggle);
return table;
}
static void
cdisplay_proof_changed (GimpColorDisplay *display)
{
CdisplayProof *proof = CDISPLAY_PROOF (display);
cmsHPROFILE rgbProfile;
cmsHPROFILE proofProfile;
if (proof->transform)
{
cmsDeleteTransform (proof->transform);
proof->transform = NULL;
}
if (! proof->profile)
return;
rgbProfile = cmsCreate_sRGBProfile ();
proofProfile = cmsOpenProfileFromFile (proof->profile, "r");
if (proofProfile)
{
cmsUInt32Number flags = cmsFLAGS_SOFTPROOFING;
if (proof->bpc)
flags |= cmsFLAGS_BLACKPOINTCOMPENSATION;
proof->transform = cmsCreateProofingTransform (rgbProfile, TYPE_ARGB_8,
rgbProfile, TYPE_ARGB_8,
proofProfile,
proof->intent,
proof->intent,
flags);
cmsCloseProfile (proofProfile);
}
cmsCloseProfile (rgbProfile);
}