gimp/libgimpcolor/gimplcms.c
Michael Natterer b3395d989e libgimpcolor: return an optional MD5 digest from gimp_lcms_create_srgb_profile()
pass NULL in most places, use the feature in the lcms.c plu-gin.
2014-03-23 23:34:47 +01:00

369 lines
9.9 KiB
C

/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
*
* gimplcms.c
* Copyright (C) 2014 Michael Natterer <mitch@gimp.org>
* Elle Stone <ellestone@ninedegreesbelow.com>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <glib.h> /* lcms.h uses the "inline" keyword */
#include <lcms2.h>
#include <gio/gio.h>
#include <gegl.h>
#include "libgimpbase/gimpbase.h"
#include "gimpcolortypes.h"
#include "gimplcms.h"
#include "libgimp/libgimp-intl.h"
/**
* SECTION: gimplcms
* @title: GimpLcms
* @short_description: Definitions and Functions relating to LCMS.
*
* Definitions and Functions relating to LCMS.
**/
static GQuark
gimp_lcms_error_quark (void)
{
static GQuark quark = 0;
if (G_UNLIKELY (quark == 0))
quark = g_quark_from_static_string ("gimp-lcms-error-quark");
return quark;
}
static void
gimp_lcms_calculate_checksum (const guint8 *data,
gsize length,
guint8 *md5_digest)
{
GChecksum *md5 = g_checksum_new (G_CHECKSUM_MD5);
g_checksum_update (md5,
(const guchar *) data + sizeof (cmsICCHeader),
length - sizeof (cmsICCHeader));
length = GIMP_LCMS_MD5_DIGEST_LENGTH;
g_checksum_get_digest (md5, md5_digest, &length);
g_checksum_free (md5);
}
GimpColorProfile
gimp_lcms_profile_open_from_file (const gchar *filename,
guint8 *md5_digest,
GError **error)
{
GimpColorProfile profile;
GMappedFile *file;
const guint8 *data;
gsize length;
g_return_val_if_fail (filename != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
file = g_mapped_file_new (filename, FALSE, error);
if (! file)
return NULL;
data = (const guint8 *) g_mapped_file_get_contents (file);
length = g_mapped_file_get_length (file);
profile = cmsOpenProfileFromMem (data, length);
if (! profile)
{
g_set_error (error, gimp_lcms_error_quark (), 0,
_("'%s' does not appear to be an ICC color profile"),
gimp_filename_to_utf8 (filename));
}
else if (md5_digest)
{
gimp_lcms_calculate_checksum (data, length, md5_digest);
}
g_mapped_file_unref (file);
return profile;
}
GimpColorProfile
gimp_lcms_profile_open_from_data (const guint8 *data,
gsize length,
guint8 *md5_digest,
GError **error)
{
GimpColorProfile profile;
g_return_val_if_fail (data != NULL, NULL);
g_return_val_if_fail (length > 0, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
profile = cmsOpenProfileFromMem (data, length);
if (! profile)
{
g_set_error_literal (error, gimp_lcms_error_quark (), 0,
_("Data does not appear to be an ICC color profile"));
}
else if (md5_digest)
{
gimp_lcms_calculate_checksum (data, length, md5_digest);
}
return profile;
}
static gchar *
gimp_lcms_profile_get_info (GimpColorProfile profile,
cmsInfoType info)
{
cmsUInt32Number size;
gchar *text = NULL;
g_return_val_if_fail (profile != NULL, NULL);
size = cmsGetProfileInfoASCII (profile, info,
"en", "US", NULL, 0);
if (size > 0)
{
gchar *data = g_new (gchar, size + 1);
size = cmsGetProfileInfoASCII (profile, info,
"en", "US", data, size);
if (size > 0)
text = gimp_any_to_utf8 (data, -1, NULL);
g_free (data);
}
return text;
}
gchar *
gimp_lcms_profile_get_description (GimpColorProfile profile)
{
return gimp_lcms_profile_get_info (profile, cmsInfoDescription);
}
gchar *
gimp_lcms_profile_get_manufacturer (GimpColorProfile profile)
{
return gimp_lcms_profile_get_info (profile, cmsInfoManufacturer);
}
gchar *
gimp_lcms_profile_get_model (GimpColorProfile profile)
{
return gimp_lcms_profile_get_info (profile, cmsInfoModel);
}
gchar *
gimp_lcms_profile_get_copyright (GimpColorProfile profile)
{
return gimp_lcms_profile_get_info (profile, cmsInfoCopyright);
}
gchar *
gimp_lcms_profile_get_summary (GimpColorProfile profile)
{
GString *string;
gchar *text;
g_return_val_if_fail (profile != NULL, NULL);
string = g_string_new (NULL);
text = gimp_lcms_profile_get_description (profile);
if (text)
{
g_string_append (string, text);
g_free (text);
}
text = gimp_lcms_profile_get_model (profile);
if (text)
{
if (string->len > 0)
g_string_append (string, "\n");
g_string_append (string, text);
}
text = gimp_lcms_profile_get_manufacturer (profile);
if (text)
{
if (string->len > 0)
g_string_append (string, "\n");
g_string_append (string, text);
}
text = gimp_lcms_profile_get_copyright (profile);
if (text)
{
if (string->len > 0)
g_string_append (string, "\n");
g_string_append (string, text);
}
return g_string_free (string, FALSE);
}
gboolean
gimp_lcms_profile_is_rgb (GimpColorProfile profile)
{
g_return_val_if_fail (profile != NULL, FALSE);
return (cmsGetColorSpace (profile) == cmsSigRgbData);
}
gboolean
gimp_lcms_profile_is_cmyk (GimpColorProfile profile)
{
g_return_val_if_fail (profile != NULL, FALSE);
return (cmsGetColorSpace (profile) == cmsSigCmykData);
}
static void
gimp_lcms_profile_set_tag (cmsHPROFILE profile,
cmsTagSignature sig,
const gchar *tag)
{
cmsMLU *mlu;
mlu = cmsMLUalloc (NULL, 1);
cmsMLUsetASCII (mlu, "en", "US", tag);
cmsWriteTag (profile, sig, mlu);
cmsMLUfree (mlu);
}
/**
* gimp_lcms_create_srgb_profile:
*
* This function is a replacement for cmsCreate_sRGBProfile() and
* returns an sRGB profile that is functionally the same as the
* ArgyllCMS sRGB.icm profile. "Functionally the same" means it has
* the same red, green, and blue colorants and the V4 "chad"
* equivalent of the ArgyllCMS V2 white point. The profile TRC is also
* functionally equivalent to the ArgyllCMS sRGB.icm TRC and is the
* same as the LCMS sRGB built-in profile TRC.
*
* The actual primaries in the sRGB specification are
* red xy: {0.6400, 0.3300, 1.0}
* green xy: {0.3000, 0.6000, 1.0}
* blue xy: {0.1500, 0.0600, 1.0}
*
* The sRGB primaries given below are "pre-quantized" to compensate
* for hexadecimal quantization during the profile-making process.
* Unless the profile-making code compensates for this quantization,
* the resulting profile's red, green, and blue colorants will deviate
* slightly from the correct XYZ values.
*
* LCMS2 doesn't compensate for hexadecimal quantization. The
* "pre-quantized" primaries below were back-calculated from the
* ArgyllCMS sRGB.icm profile. The resulting sRGB profile's colorants
* exactly matches the ArgyllCMS sRGB.icm profile colorants.
*
* Return value: the sRGB cmsHPROFILE.
*
* Since: GIMP 2.10
**/
GimpColorProfile
gimp_lcms_create_srgb_profile (guint8 *md5_digest)
{
cmsHPROFILE srgb_profile;
cmsCIExyY d65_srgb_specs = { 0.3127, 0.3290, 1.0 };
cmsCIExyYTRIPLE srgb_primaries_pre_quantized =
{
{ 0.639998686, 0.330010138, 1.0 },
{ 0.300003784, 0.600003357, 1.0 },
{ 0.150002046, 0.059997204, 1.0 }
};
cmsFloat64Number srgb_parameters[5] =
{ 2.4, 1.0 / 1.055, 0.055 / 1.055, 1.0 / 12.92, 0.04045 };
cmsToneCurve *srgb_parametric_curve =
cmsBuildParametricToneCurve (NULL, 4, srgb_parameters);
cmsToneCurve *tone_curve[3];
tone_curve[0] = tone_curve[1] = tone_curve[2] = srgb_parametric_curve;
srgb_profile = cmsCreateRGBProfile (&d65_srgb_specs,
&srgb_primaries_pre_quantized,
tone_curve);
cmsFreeToneCurve (srgb_parametric_curve);
gimp_lcms_profile_set_tag (srgb_profile, cmsSigProfileDescriptionTag,
"GIMP built-in sRGB");
gimp_lcms_profile_set_tag (srgb_profile, cmsSigDeviceMfgDescTag,
"GIMP");
gimp_lcms_profile_set_tag (srgb_profile, cmsSigDeviceModelDescTag,
"sRGB");
gimp_lcms_profile_set_tag (srgb_profile, cmsSigCopyrightTag,
"Public Domain");
/**
* The following line produces a V2 profile with a point curve TRC.
* Profiles with point curve TRCs can't be used in LCMS2 unbounded
* mode ICC profile conversions. A V2 profile might be appropriate
* for embedding in sRGB images saved to disk, if the image is to be
* opened by an image editing application that doesn't understand V4
* profiles.
*
* cmsSetProfileVersion (srgb_profile, 2.1);
**/
if (md5_digest)
{
md5_digest[0] = 0xcb;
md5_digest[1] = 0x63;
md5_digest[2] = 0x14;
md5_digest[3] = 0x56;
md5_digest[4] = 0xd4;
md5_digest[5] = 0x0a;
md5_digest[6] = 0x01;
md5_digest[7] = 0x62;
md5_digest[8] = 0xa0;
md5_digest[9] = 0xdb;
md5_digest[10] = 0xe6;
md5_digest[11] = 0x32;
md5_digest[12] = 0x8b;
md5_digest[13] = 0xea;
md5_digest[14] = 0x1a;
md5_digest[15] = 0x89;
}
return srgb_profile;
}