From d73cfa4a24211f67889b84aeae8f70282ce33d3b Mon Sep 17 00:00:00 2001 From: Tobias Ellinghaus Date: Sun, 20 Sep 2015 23:42:24 +0200 Subject: [PATCH] plug-ins: support color managed EXR loading This generates an ICC profile from the embedded white point and chromaticities on the fly, assuming linear gamma images. --- plug-ins/file-exr/Makefile.am | 2 + plug-ins/file-exr/file-exr.c | 21 ++++++++ plug-ins/file-exr/openexr-wrapper.cc | 76 ++++++++++++++++++++++++++++ plug-ins/file-exr/openexr-wrapper.h | 5 ++ 4 files changed, 104 insertions(+) diff --git a/plug-ins/file-exr/Makefile.am b/plug-ins/file-exr/Makefile.am index 37b5f6eb6f..f3bda1b744 100644 --- a/plug-ins/file-exr/Makefile.am +++ b/plug-ins/file-exr/Makefile.am @@ -26,6 +26,7 @@ AM_CPPFLAGS = \ $(GTK_CFLAGS) \ $(GEGL_CFLAGS) \ $(OPENEXR_CFLAGS) \ + $(LCMS_CFLAGS) \ -I$(includedir) libexec_PROGRAMS = file-exr @@ -48,4 +49,5 @@ file_exr_LDADD = \ $(GEGL_LIBS) \ $(RT_LIBS) \ $(INTLLIBS) \ + $(LCMS_LIBS) \ $(file_exr_RC) diff --git a/plug-ins/file-exr/file-exr.c b/plug-ins/file-exr/file-exr.c index a155e539b8..9fda3c0648 100644 --- a/plug-ins/file-exr/file-exr.c +++ b/plug-ins/file-exr/file-exr.c @@ -280,6 +280,27 @@ load_image (const gchar *filename, gimp_progress_update ((gdouble) begin / (gdouble) height); } + // try to load an icc profile. + // this will be generated on the fly if chromaticities are given + if (image_type == GIMP_RGB) + { + cmsHPROFILE lcms_profile = NULL; + GimpColorProfile *profile = NULL; + + lcms_profile = exr_loader_icc_read_profile (loader); + if (lcms_profile) + { + profile = gimp_color_profile_new_from_lcms_profile (lcms_profile, + NULL); + if (profile) + { + gimp_image_set_color_profile (image, profile); + g_object_unref (profile); + } + cmsCloseProfile (lcms_profile); + } + } + gimp_progress_update (1.0); status = image; diff --git a/plug-ins/file-exr/openexr-wrapper.cc b/plug-ins/file-exr/openexr-wrapper.cc index 9cf1430b5d..db0ce05f58 100644 --- a/plug-ins/file-exr/openexr-wrapper.cc +++ b/plug-ins/file-exr/openexr-wrapper.cc @@ -170,6 +170,76 @@ struct _EXRLoader return has_alpha_ ? 1 : 0; } + cmsHPROFILE readICCProfile() const { + Chromaticities chromaticities; + float whiteLuminance = 1.0; + + cmsHPROFILE profile = NULL; + + if (hasChromaticities (file_.header ())) + chromaticities = Imf::chromaticities (file_.header ()); + else + return NULL; + + if (Imf::hasWhiteLuminance (file_.header ())) + whiteLuminance = Imf::whiteLuminance (file_.header ()); + else + return NULL; + +#if 0 + std::cout << "hasChromaticities: " + << hasChromaticities (file_.header ()) + << std::endl; + std::cout << "hasWhiteLuminance: " + << hasWhiteLuminance (file_.header ()) + << std::endl; + std::cout << chromaticities.red << std::endl; + std::cout << chromaticities.green << std::endl; + std::cout << chromaticities.blue << std::endl; + std::cout << chromaticities.white << std::endl; + std::cout << std::endl; +#endif + + // TODO: maybe factor this out into libgimpcolor/gimpcolorprofile.h ? + cmsCIExyY whitePoint = { chromaticities.white.x, + chromaticities.white.y, + whiteLuminance }; + cmsCIExyYTRIPLE CameraPrimaries = { { chromaticities.red.x, + chromaticities.red.y, + whiteLuminance }, + { chromaticities.green.x, + chromaticities.green.y, + whiteLuminance }, + { chromaticities.blue.x, + chromaticities.blue.y, + whiteLuminance } }; + + double Parameters[2] = { 1.0, 0.0 }; + cmsToneCurve *Gamma[3]; + Gamma[0] = Gamma[1] = Gamma[2] = cmsBuildParametricToneCurve(0, + 1, + Parameters); + profile = cmsCreateRGBProfile (&whitePoint, &CameraPrimaries, Gamma); + cmsFreeToneCurve (Gamma[0]); + if (profile == NULL) return NULL; + + cmsSetProfileVersion (profile, 2.1); + cmsMLU *mlu0 = cmsMLUalloc (NULL, 1); + cmsMLUsetASCII (mlu0, "en", "US", "(GIMP internal)"); + cmsMLU *mlu1 = cmsMLUalloc(NULL, 1); + cmsMLUsetASCII (mlu1, "en", "US", "color profile from EXR chromaticities"); + cmsMLU *mlu2 = cmsMLUalloc(NULL, 1); + cmsMLUsetASCII (mlu2, "en", "US", "color profile from EXR chromaticities"); + cmsWriteTag (profile, cmsSigDeviceMfgDescTag, mlu0); + cmsWriteTag (profile, cmsSigDeviceModelDescTag, mlu1); + cmsWriteTag (profile, cmsSigProfileDescriptionTag, mlu2); + cmsMLUfree (mlu0); + cmsMLUfree (mlu1); + cmsMLUfree (mlu2); + + return profile; + } + size_t refcount_; InputFile file_; const Box2i data_window_; @@ -289,3 +359,9 @@ exr_loader_read_pixel_row (EXRLoader *loader, return retval; } + +cmsHPROFILE +exr_loader_icc_read_profile (EXRLoader *loader) +{ + return loader->readICCProfile (); +} diff --git a/plug-ins/file-exr/openexr-wrapper.h b/plug-ins/file-exr/openexr-wrapper.h index 38276077dd..7b3cd41e10 100644 --- a/plug-ins/file-exr/openexr-wrapper.h +++ b/plug-ins/file-exr/openexr-wrapper.h @@ -8,6 +8,8 @@ extern "C" { #endif +#include + /* This is fully opaque on purpose, as the calling C code must not be * exposed to more than this. */ @@ -54,6 +56,9 @@ exr_loader_read_pixel_row (EXRLoader *loader, int bpp, int row); +cmsHPROFILE +exr_loader_icc_read_profile (EXRLoader *loader); + #ifdef __cplusplus } #endif