diff --git a/plugins/color/lcms2engine/LcmsRGBP2020PQColorSpace.h b/plugins/color/lcms2engine/LcmsRGBP2020PQColorSpace.h index a6985450a1..736b0fbc04 100644 --- a/plugins/color/lcms2engine/LcmsRGBP2020PQColorSpace.h +++ b/plugins/color/lcms2engine/LcmsRGBP2020PQColorSpace.h @@ -1,127 +1,141 @@ /* * Copyright (c) 2019 Dmitry Kazakov * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef LCMSRGBP2020PQCOLORSPACE_H #define LCMSRGBP2020PQCOLORSPACE_H + #include #include + +#ifdef HAVE_OPENEXR #include +#endif + #include #include "KoColorConversionTransformationFactory.h" #include template struct ColorSpaceFromFactory { }; template<> struct ColorSpaceFromFactory { typedef RgbU8ColorSpace type; }; template<> struct ColorSpaceFromFactory { typedef RgbU16ColorSpace type; }; +#ifdef HAVE_OPENEXR template<> struct ColorSpaceFromFactory { typedef RgbF16ColorSpace type; }; +#endif template<> struct ColorSpaceFromFactory { typedef RgbF32ColorSpace type; }; /** * Define a singly linked list of supported bit depth traits */ template struct NextTrait { using type = void; }; template<> struct NextTrait { using type = KoBgrU16Traits; }; + +#ifdef HAVE_OPENEXR template<> struct NextTrait { using type = KoRgbF16Traits; }; template<> struct NextTrait { using type = KoRgbF32Traits; }; +#else +template<> struct NextTrait { using type = KoRgbF32Traits; }; +#endif /** * Recursively add bit-depths conversions to the color space. We add only * **outgoing** conversions for every RGB color space. That is, every color * space has exactly three outgoing edges for color conversion. */ template void addInternalConversion(QList &list, CurrentTraits*) { // general case: add a converter and recurse for the next traits list << new LcmsScaleRGBP2020PQTransformationFactory(); using NextTraits = typename NextTrait::type; addInternalConversion(list, static_cast(0)); } template void addInternalConversion(QList &list, typename ParentColorSpace::ColorSpaceTraits*) { // exception: skip adding an edge to the same bit depth using CurrentTraits = typename ParentColorSpace::ColorSpaceTraits; using NextTraits = typename NextTrait::type; addInternalConversion(list, static_cast(0)); } template void addInternalConversion(QList &, void*) { // stop recursion } template class LcmsRGBP2020PQColorSpaceFactoryWrapper : public BaseColorSpaceFactory { typedef typename ColorSpaceFromFactory::type RelatedColorSpaceType; KoColorSpace *createColorSpace(const KoColorProfile *p) const override { return new RelatedColorSpaceType(this->name(), p->clone()); } QList colorConversionLinks() const override { QList list; /** * We explicitly disable direct conversions to/from integer color spaces, because * they may cause the the conversion system to choose them as an intermediate * color space for the conversion chain, e.g. * p709-g10 F32 -> p2020-g10 U16 -> Rec2020-pq U16, which is incorrect and loses * all the HDR data */ list << new LcmsFromRGBP2020PQTransformationFactory(); list << new LcmsFromRGBP2020PQTransformationFactory(); +#ifdef HAVE_OPENEXR list << new LcmsToRGBP2020PQTransformationFactory(); +#endif list << new LcmsToRGBP2020PQTransformationFactory(); // internally, we can convert to RGB U8 if needed addInternalConversion(list, static_cast(0)); return list; } }; #endif // LCMSRGBP2020PQCOLORSPACE_H diff --git a/plugins/color/lcms2engine/LcmsRGBP2020PQColorSpaceTransformation.h b/plugins/color/lcms2engine/LcmsRGBP2020PQColorSpaceTransformation.h index 7620cecfab..a340c5fc36 100644 --- a/plugins/color/lcms2engine/LcmsRGBP2020PQColorSpaceTransformation.h +++ b/plugins/color/lcms2engine/LcmsRGBP2020PQColorSpaceTransformation.h @@ -1,269 +1,276 @@ /* * Copyright (c) 2019 Dmitry Kazakov * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef LCMSRGBP2020PQCOLORSPACETRANSFORMATION_H #define LCMSRGBP2020PQCOLORSPACETRANSFORMATION_H #include "KoAlwaysInline.h" #include "KoColorModelStandardIds.h" #include "KoColorSpaceMaths.h" #include "KoColorModelStandardIdsUtils.h" #include "KoColorConversionTransformationFactory.h" #include #include +#ifdef HAVE_OPENEXR #include +#endif #include namespace { ALWAYS_INLINE float applySmpte2048Curve(float x) { const float m1 = 2610.0 / 4096.0 / 4.0; const float m2 = 2523.0 / 4096.0 * 128.0; const float a1 = 3424.0 / 4096.0; const float c2 = 2413.0 / 4096.0 * 32.0; const float c3 = 2392.0 / 4096.0 * 32.0; const float a4 = 1.0; const float x_p = powf(0.008 * std::max(0.0f, x), m1); const float res = powf((a1 + c2 * x_p) / (a4 + c3 * x_p), m2); return res; } ALWAYS_INLINE float removeSmpte2048Curve(float x) { const float m1_r = 4096.0 * 4.0 / 2610.0; const float m2_r = 4096.0 / 2523.0 / 128.0; const float a1 = 3424.0 / 4096.0; const float c2 = 2413.0 / 4096.0 * 32.0; const float c3 = 2392.0 / 4096.0 * 32.0; const float x_p = powf(x, m2_r); const float res = powf(qMax(0.0f, x_p - a1) / (c2 - c3 * x_p), m1_r); return res * 125.0f; } template struct DstTraitsForSource { typedef KoRgbF32Traits result; }; +/** + * If half format is present, we use it instead + */ +#ifdef HAVE_OPENEXR template <> struct DstTraitsForSource { typedef KoRgbF16Traits result; }; template <> struct DstTraitsForSource { typedef KoRgbF16Traits result; }; +#endif template struct RemoveSmpte2048Policy { static ALWAYS_INLINE dst_channel_type process(src_channel_type value) { return KoColorSpaceMaths::scaleToA( removeSmpte2048Curve( KoColorSpaceMaths::scaleToA( value))); } }; template struct ApplySmpte2048Policy { static ALWAYS_INLINE dst_channel_type process(src_channel_type value) { return KoColorSpaceMaths::scaleToA( applySmpte2048Curve( KoColorSpaceMaths::scaleToA( value))); } }; template struct NoopPolicy { static ALWAYS_INLINE dst_channel_type process(src_channel_type value) { return KoColorSpaceMaths::scaleToA(value); } }; } template class Policy> struct ApplyRgbShaper : public KoColorConversionTransformation { ApplyRgbShaper(const KoColorSpace* srcCs, const KoColorSpace* dstCs, Intent renderingIntent, ConversionFlags conversionFlags) : KoColorConversionTransformation(srcCs, dstCs, renderingIntent, conversionFlags) { } void transform(const quint8 *src, quint8 *dst, qint32 nPixels) const override { KIS_ASSERT(src != dst); const typename SrcCSTraits::Pixel *srcPixel = reinterpret_cast(src); typename DstCSTraits::Pixel *dstPixel = reinterpret_cast(dst); typedef typename SrcCSTraits::channels_type src_channel_type; typedef typename DstCSTraits::channels_type dst_channel_type; typedef Policy ConcretePolicy; for (int i = 0; i < nPixels; i++) { dstPixel->red = ConcretePolicy::process(srcPixel->red); dstPixel->green = ConcretePolicy::process(srcPixel->green); dstPixel->blue = ConcretePolicy::process(srcPixel->blue); dstPixel->alpha = KoColorSpaceMaths::scaleToA( srcPixel->alpha); srcPixel++; dstPixel++; } } }; template::result> class LcmsFromRGBP2020PQTransformationFactory : public KoColorConversionTransformationFactory { public: LcmsFromRGBP2020PQTransformationFactory() : KoColorConversionTransformationFactory(RGBAColorModelID.id(), colorDepthIdForChannelType().id(), "High Dynamic Range UHDTV Wide Color Gamut Display (Rec. 2020) - SMPTE ST 2084 PQ EOTF", RGBAColorModelID.id(), colorDepthIdForChannelType().id(), "Rec2020-elle-V4-g10.icc") { } bool conserveColorInformation() const override { return true; } bool conserveDynamicRange() const override { return dstColorDepthId() == Float16BitsColorDepthID.id() || dstColorDepthId() == Float32BitsColorDepthID.id() || dstColorDepthId() == Float64BitsColorDepthID.id(); } KoColorConversionTransformation* createColorTransformation(const KoColorSpace* srcColorSpace, const KoColorSpace* dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const override { return new ApplyRgbShaper< typename ParentColorSpace::ColorSpaceTraits, DstColorSpaceTraits, RemoveSmpte2048Policy>(srcColorSpace, dstColorSpace, renderingIntent, conversionFlags); } }; template::result> class LcmsToRGBP2020PQTransformationFactory : public KoColorConversionTransformationFactory { public: LcmsToRGBP2020PQTransformationFactory() : KoColorConversionTransformationFactory(RGBAColorModelID.id(), colorDepthIdForChannelType().id(), "Rec2020-elle-V4-g10.icc", RGBAColorModelID.id(), colorDepthIdForChannelType().id(), "High Dynamic Range UHDTV Wide Color Gamut Display (Rec. 2020) - SMPTE ST 2084 PQ EOTF") { } bool conserveColorInformation() const override { return true; } bool conserveDynamicRange() const override { return true; } KoColorConversionTransformation* createColorTransformation(const KoColorSpace* srcColorSpace, const KoColorSpace* dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const override { return new ApplyRgbShaper< DstColorSpaceTraits, typename ParentColorSpace::ColorSpaceTraits, ApplySmpte2048Policy>(srcColorSpace, dstColorSpace, renderingIntent, conversionFlags); } }; template class LcmsScaleRGBP2020PQTransformationFactory : public KoColorConversionTransformationFactory { public: LcmsScaleRGBP2020PQTransformationFactory() : KoColorConversionTransformationFactory(RGBAColorModelID.id(), colorDepthIdForChannelType().id(), "High Dynamic Range UHDTV Wide Color Gamut Display (Rec. 2020) - SMPTE ST 2084 PQ EOTF", RGBAColorModelID.id(), colorDepthIdForChannelType().id(), "High Dynamic Range UHDTV Wide Color Gamut Display (Rec. 2020) - SMPTE ST 2084 PQ EOTF") { KIS_SAFE_ASSERT_RECOVER_NOOP(srcColorDepthId() != dstColorDepthId()); } bool conserveColorInformation() const override { return true; } bool conserveDynamicRange() const override { return srcColorDepthId() == Float16BitsColorDepthID.id() || srcColorDepthId() == Float32BitsColorDepthID.id() || srcColorDepthId() == Float64BitsColorDepthID.id(); } KoColorConversionTransformation* createColorTransformation(const KoColorSpace* srcColorSpace, const KoColorSpace* dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const override { return new ApplyRgbShaper< typename ParentColorSpace::ColorSpaceTraits, DstColorSpaceTraits, NoopPolicy>(srcColorSpace, dstColorSpace, renderingIntent, conversionFlags); } }; #endif // LCMSRGBP2020PQCOLORSPACETRANSFORMATION_H