diff --git a/libs/pigment/CMakeLists.txt b/libs/pigment/CMakeLists.txt index b1552b6da6..600511c078 100644 --- a/libs/pigment/CMakeLists.txt +++ b/libs/pigment/CMakeLists.txt @@ -1,124 +1,126 @@ project(kritapigment) # we have to repeat platform specifics from top-level if (WIN32) include_directories(${CMAKE_SOURCE_DIR}/winquirks) add_definitions(-D_USE_MATH_DEFINES) add_definitions(-DNOMINMAX) set(WIN32_PLATFORM_NET_LIBS ws2_32.lib netapi32.lib) endif () include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/resources ${CMAKE_CURRENT_SOURCE_DIR}/compositeops) include_directories(SYSTEM ${Boost_INCLUDE_DIRS} ) set(FILE_OPENEXR_SOURCES) set(LINK_OPENEXR_LIB) if(OPENEXR_FOUND) include_directories(SYSTEM ${OPENEXR_INCLUDE_DIR}) set(LINK_OPENEXR_LIB ${OPENEXR_LIBRARIES}) add_definitions(${OPENEXR_DEFINITIONS}) endif() set(LINK_VC_LIB) if(HAVE_VC) include_directories(SYSTEM ${Vc_INCLUDE_DIR}) set(LINK_VC_LIB ${Vc_LIBRARIES}) ko_compile_for_all_implementations_no_scalar(__per_arch_factory_objs compositeops/KoOptimizedCompositeOpFactoryPerArch.cpp) message("Following objects are generated from the per-arch lib") message(${__per_arch_factory_objs}) endif() add_subdirectory(tests) add_subdirectory(benchmarks) set(kritapigment_SRCS DebugPigment.cpp KoBasicHistogramProducers.cpp KoColor.cpp KoColorDisplayRendererInterface.cpp KoColorConversionAlphaTransformation.cpp KoColorConversionCache.cpp KoColorConversions.cpp KoColorConversionSystem.cpp KoColorConversionTransformation.cpp KoColorProofingConversionTransformation.cpp KoColorConversionTransformationFactory.cpp KoColorModelStandardIds.cpp KoColorProfile.cpp KoColorSpace.cpp KoColorSpaceEngine.cpp KoColorSpaceFactory.cpp KoColorSpaceMaths.cpp + KoCmykColorSpaceMaths.cpp + KoLabColorSpaceMaths.cpp KoColorSpaceRegistry.cpp KoColorProfileStorage.cpp KoColorTransformation.cpp KoColorTransformationFactory.cpp KoColorTransformationFactoryRegistry.cpp KoCompositeColorTransformation.cpp KoCompositeOp.cpp KoCompositeOpRegistry.cpp KoCopyColorConversionTransformation.cpp KoFallBackColorTransformation.cpp KoHistogramProducer.cpp KoMultipleColorConversionTransformation.cpp KoUniqueNumberForIdServer.cpp colorspaces/KoAlphaColorSpace.cpp colorspaces/KoLabColorSpace.cpp colorspaces/KoRgbU16ColorSpace.cpp colorspaces/KoRgbU8ColorSpace.cpp colorspaces/KoSimpleColorSpaceEngine.cpp compositeops/KoOptimizedCompositeOpFactory.cpp compositeops/KoOptimizedCompositeOpFactoryPerArch_Scalar.cpp ${__per_arch_factory_objs} colorprofiles/KoDummyColorProfile.cpp resources/KoAbstractGradient.cpp resources/KoColorSet.cpp resources/KoPattern.cpp resources/KoResource.cpp resources/KoMD5Generator.cpp resources/KoHashGeneratorProvider.cpp resources/KoStopGradient.cpp resources/KoSegmentGradient.cpp ) set (EXTRA_LIBRARIES ${LINK_OPENEXR_LIB} ${LINK_VC_LIB}) if(MSVC OR (WIN32 AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")) # avoid "cannot open file 'LIBC.lib'" error set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /NODEFAULTLIB:LIBC.LIB") endif() add_library(kritapigment SHARED ${kritapigment_SRCS}) generate_export_header(kritapigment) target_include_directories( kritapigment PUBLIC $ $ ) target_link_libraries( kritapigment PUBLIC kritaplugin kritastore kritaglobal ${EXTRA_LIBRARIES} KF5::I18n KF5::ConfigCore Qt5::Core Qt5::Gui Qt5::Xml ${WIN32_PLATFORM_NET_LIBS} ) set_target_properties(kritapigment PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritapigment ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/libs/pigment/KoCmykColorSpaceMaths.cpp b/libs/pigment/KoCmykColorSpaceMaths.cpp new file mode 100644 index 0000000000..5ae3fc3d5a --- /dev/null +++ b/libs/pigment/KoCmykColorSpaceMaths.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2006 Cyrille Berger + * Copyright (c) 2017 L. E. Segovia + * + * 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 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include + +#include + +#include + +#ifdef HAVE_OPENEXR +const half KoCmykColorSpaceMathsTraits::zeroValueCMYK = 0.0; +const half KoCmykColorSpaceMathsTraits::unitValueCMYK = 100.0; +const half KoCmykColorSpaceMathsTraits::halfValueCMYK = 50.5; +#endif + +const float KoCmykColorSpaceMathsTraits::zeroValueCMYK = 0.0; +const float KoCmykColorSpaceMathsTraits::unitValueCMYK = 100.0; +const float KoCmykColorSpaceMathsTraits::halfValueCMYK = 50.0; + +const double KoCmykColorSpaceMathsTraits::zeroValueCMYK = 0.0; +const double KoCmykColorSpaceMathsTraits::unitValueCMYK = 100.0; +const double KoCmykColorSpaceMathsTraits::halfValueCMYK = 50.0; diff --git a/libs/pigment/KoCmykColorSpaceMaths.h b/libs/pigment/KoCmykColorSpaceMaths.h new file mode 100644 index 0000000000..2cfc82bdc6 --- /dev/null +++ b/libs/pigment/KoCmykColorSpaceMaths.h @@ -0,0 +1,740 @@ +/* + * Copyright (c) 2006,2007,2010 Cyrille Berger + * Copyright (c) 2017 L. E. Segovia + * + * 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 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KOCMYKCOLORSPACEMATHS_H_ +#define KOCMYKCOLORSPACEMATHS_H_ + +#include +#include + +#include "kritapigment_export.h" +#include +#include "KoChannelInfo.h" +#include "KoLut.h" + +#include + +#undef _T + +/** + * This is an empty mainWindow that needs to be "specialized" for each possible + * numerical type (quint8, quint16...). + * + * It needs to defines some static constant fields : + * - zeroValue : the zero for this numerical type + * - unitValue : the maximum value of the normal dynamic range + * - max : the maximum value + * - min : the minimum value + * - epsilon : a value close to zero but different of zero + * - bits : the bit depth + * + * And some types : + * - compositetype the type used for composite operations (usually one with + * a higher bit depth) + * + * This class is specialized to handle the floating point bounds of the CMYK color space. + */ +template +class KoCmykColorSpaceMathsTraits +{ +public: +}; + +/** + * No changes for integer color spaces. + */ +template<> +class KRITAPIGMENT_EXPORT KoCmykColorSpaceMathsTraits : public KoColorSpaceMathsTraits +{ +}; + +template<> +class KRITAPIGMENT_EXPORT KoCmykColorSpaceMathsTraits : public KoColorSpaceMathsTraits +{ +}; + +template<> +class KRITAPIGMENT_EXPORT KoCmykColorSpaceMathsTraits : public KoColorSpaceMathsTraits +{ +}; + +template<> +class KRITAPIGMENT_EXPORT KoCmykColorSpaceMathsTraits : public KoColorSpaceMathsTraits +{ +}; + +#include +#ifdef HAVE_OPENEXR +#include + +template<> +class KRITAPIGMENT_EXPORT KoCmykColorSpaceMathsTraits : public KoColorSpaceMathsTraits +{ +public: + static const half zeroValueCMYK; + static const half unitValueCMYK; + static const half halfValueCMYK; +}; +#endif + +template<> +class KRITAPIGMENT_EXPORT KoCmykColorSpaceMathsTraits : public KoColorSpaceMathsTraits +{ +public: + static const float zeroValueCMYK; + static const float unitValueCMYK; + static const float halfValueCMYK; +}; + +template<> +class KRITAPIGMENT_EXPORT KoCmykColorSpaceMathsTraits : public KoColorSpaceMathsTraits +{ +public: + static const double zeroValueCMYK; + static const double unitValueCMYK; + static const double halfValueCMYK; +}; + +//template +//struct KoIntegerToFloat { +// inline float operator()(_T_ f) const +// { +// return f / float(KoColorSpaceMathsTraits<_T_>::max); +// } +//}; + +//struct KoLuts { + +// static KRITAPIGMENT_EXPORT const Ko::FullLut< KoIntegerToFloat, float, quint16> Uint16ToFloat; +// static KRITAPIGMENT_EXPORT const Ko::FullLut< KoIntegerToFloat, float, quint8> Uint8ToFloat; +//}; + +///** +// * This class defines some elementary operations used by various color +// * space. It's intended to be generic, but some specialization exists +// * either for optimization or just for being buildable. +// * +// * @param _T some numerical type with an existing trait +// * @param _Tdst some other numerical type with an existing trait, it is +// * only needed if different of _T +// */ +//template < typename _T, typename _Tdst = _T > +//class KoColorSpaceMaths +//{ +// typedef KoColorSpaceMathsTraits<_T> traits; +// typedef typename traits::compositetype src_compositetype; +// typedef typename KoColorSpaceMathsTraits<_Tdst>::compositetype dst_compositetype; + +//public: +// inline static _Tdst multiply(_T a, _Tdst b) { +// return (dst_compositetype(a)*b) / KoColorSpaceMathsTraits<_Tdst>::unitValue; +// } + +// inline static _Tdst multiply(_T a, _Tdst b, _Tdst c) { +// return (dst_compositetype(a)*b*c) / (dst_compositetype(KoColorSpaceMathsTraits<_Tdst>::unitValue) * KoColorSpaceMathsTraits<_T>::unitValue); +// } + +// /** +// * Division : (a * MAX ) / b +// * @param a +// * @param b +// */ +// inline static dst_compositetype divide(_T a, _Tdst b) { +// return (dst_compositetype(a) * KoColorSpaceMathsTraits<_Tdst>::unitValue) / b; +// } + +// /** +// * Inversion : unitValue - a +// * @param a +// */ +// inline static _T invert(_T a) { +// return traits::unitValue - a; +// } + +// /** +// * Blending : (a * alpha) + b * (1 - alpha) +// * @param a +// * @param b +// * @param alpha +// */ +// inline static _T blend(_T a, _T b, _T alpha) { +// src_compositetype c = ((src_compositetype(a) - b) * alpha) / traits::unitValue; +// return c + b; +// } + +// /** +// * This function will scale a value of type _T to fit into a _Tdst. +// */ +// inline static _Tdst scaleToA(_T a) { +// return _Tdst(dst_compositetype(a) * KoColorSpaceMathsTraits<_Tdst>::unitValue / KoColorSpaceMathsTraits<_T>::unitValue); +// } + +// inline static dst_compositetype clamp(dst_compositetype val) { +// return qBound(KoColorSpaceMathsTraits<_Tdst>::min, val, KoColorSpaceMathsTraits<_Tdst>::max); +// } + +// /** +// * Clamps the composite type on higher border only. That is a fast path +// * for scale-only transformations +// */ +// inline static _Tdst clampAfterScale(dst_compositetype val) { +// return qMin(val, KoColorSpaceMathsTraits<_Tdst>::max); +// } +//}; + +////------------------------------ double specialization ------------------------------// +//template<> +//inline quint8 KoColorSpaceMaths::scaleToA(double a) +//{ +// double v = a * 255; +// return float2int(CLAMP(v, 0, 255)); +//} + +//template<> +//inline double KoColorSpaceMaths::scaleToA(quint8 a) +//{ +// return KoLuts::Uint8ToFloat(a); +//} + +//template<> +//inline quint16 KoColorSpaceMaths::scaleToA(double a) +//{ +// double v = a * 0xFFFF; +// return float2int(CLAMP(v, 0, 0xFFFF)); +//} + +//template<> +//inline double KoColorSpaceMaths::scaleToA(quint16 a) +//{ +// return KoLuts::Uint16ToFloat(a); +//} + +//template<> +//inline double KoColorSpaceMaths::clamp(double a) +//{ +// return a; +//} + +////------------------------------ float specialization ------------------------------// + +//template<> +//inline float KoColorSpaceMaths::scaleToA(double a) +//{ +// return (float)a; +//} + +//template<> +//inline double KoColorSpaceMaths::scaleToA(float a) +//{ +// return a; +//} + +//template<> +//inline quint16 KoColorSpaceMaths::scaleToA(float a) +//{ +// float v = a * 0xFFFF; +// return (quint16)float2int(CLAMP(v, 0, 0xFFFF)); +//} + +//template<> +//inline float KoColorSpaceMaths::scaleToA(quint16 a) +//{ +// return KoLuts::Uint16ToFloat(a); +//} + +//template<> +//inline quint8 KoColorSpaceMaths::scaleToA(float a) +//{ +// float v = a * 255; +// return (quint8)float2int(CLAMP(v, 0, 255)); +//} + +//template<> +//inline float KoColorSpaceMaths::scaleToA(quint8 a) +//{ +// return KoLuts::Uint8ToFloat(a); +//} + +//template<> +//inline float KoColorSpaceMaths::blend(float a, float b, float alpha) +//{ +// return (a - b) * alpha + b; +//} + +//template<> +//inline double KoColorSpaceMaths::clamp(double a) +//{ +// return a; +//} + +////------------------------------ half specialization ------------------------------// + +//#ifdef HAVE_OPENEXR + +//template<> +//inline half KoColorSpaceMaths::scaleToA(double a) +//{ +// return (half)a; +//} + +//template<> +//inline double KoColorSpaceMaths::scaleToA(half a) +//{ +// return a; +//} + +//template<> +//inline float KoColorSpaceMaths::scaleToA(half a) +//{ +// return a; +//} + +//template<> +//inline half KoColorSpaceMaths::scaleToA(float a) +//{ +// return (half) a; +//} + +//template<> +//inline quint8 KoColorSpaceMaths::scaleToA(half a) +//{ +// half v = a * 255; +// return (quint8)(CLAMP(v, 0, 255)); +//} + +//template<> +//inline half KoColorSpaceMaths::scaleToA(quint8 a) +//{ +// return a *(1.0 / 255.0); +//} +//template<> +//inline quint16 KoColorSpaceMaths::scaleToA(half a) +//{ +// double v = a * 0xFFFF; +// return (quint16)(CLAMP(v, 0, 0xFFFF)); +//} + +//template<> +//inline half KoColorSpaceMaths::scaleToA(quint16 a) +//{ +// return a *(1.0 / 0xFFFF); +//} + +//template<> +//inline half KoColorSpaceMaths::scaleToA(half a) +//{ +// return a; +//} + +//template<> +//inline half KoColorSpaceMaths::blend(half a, half b, half alpha) +//{ +// return (a - b) * alpha + b; +//} + +//template<> +//inline double KoColorSpaceMaths::clamp(double a) +//{ +// return a; +//} + + +//#endif + +////------------------------------ quint8 specialization ------------------------------// + +//template<> +//inline quint8 KoColorSpaceMaths::multiply(quint8 a, quint8 b) +//{ +// return (quint8)UINT8_MULT(a, b); +//} + + +//template<> +//inline quint8 KoColorSpaceMaths::multiply(quint8 a, quint8 b, quint8 c) +//{ +// return (quint8)UINT8_MULT3(a, b, c); +//} + +//template<> +//inline KoColorSpaceMathsTraits::compositetype +//KoColorSpaceMaths::divide(quint8 a, quint8 b) +//{ +// return UINT8_DIVIDE(a, b); +//} + +//template<> +//inline quint8 KoColorSpaceMaths::invert(quint8 a) +//{ +// return ~a; +//} + +//template<> +//inline quint8 KoColorSpaceMaths::blend(quint8 a, quint8 b, quint8 c) +//{ +// return UINT8_BLEND(a, b, c); +//} + +////------------------------------ quint16 specialization ------------------------------// + +//template<> +//inline quint16 KoColorSpaceMaths::multiply(quint16 a, quint16 b) +//{ +// return (quint16)UINT16_MULT(a, b); +//} + +//template<> +//inline KoColorSpaceMathsTraits::compositetype +//KoColorSpaceMaths::divide(quint16 a, quint16 b) +//{ +// return UINT16_DIVIDE(a, b); +//} + +//template<> +//inline quint16 KoColorSpaceMaths::invert(quint16 a) +//{ +// return ~a; +//} + +////------------------------------ various specialization ------------------------------// + + +//// TODO: use more functions from KoIntegersMaths to do the computation + +///// This specialization is needed because the default implementation won't work when scaling up +//template<> +//inline quint16 KoColorSpaceMaths::scaleToA(quint8 a) +//{ +// return UINT8_TO_UINT16(a); +//} + +//template<> +//inline quint8 KoColorSpaceMaths::scaleToA(quint16 a) +//{ +// return UINT16_TO_UINT8(a); +//} + + +//// Due to once again a bug in gcc, there is the need for those specialized functions: + +//template<> +//inline quint8 KoColorSpaceMaths::scaleToA(quint8 a) +//{ +// return a; +//} + +//template<> +//inline quint16 KoColorSpaceMaths::scaleToA(quint16 a) +//{ +// return a; +//} + +//template<> +//inline float KoColorSpaceMaths::scaleToA(float a) +//{ +// return a; +//} + +//namespace Arithmetic +//{ +// const static qreal pi = 3.14159265358979323846; + +// template +// inline T mul(T a, T b) { return KoColorSpaceMaths::multiply(a, b); } + +// template +// inline T mul(T a, T b, T c) { return KoColorSpaceMaths::multiply(a, b, c); } + +//// template +//// inline T mul(T a, T b) { +//// typedef typename KoColorSpaceMathsTraits::compositetype composite_type; +//// return T(composite_type(a) * b / KoColorSpaceMathsTraits::unitValue); +//// } +//// +//// template +//// inline T mul(T a, T b, T c) { +//// typedef typename KoColorSpaceMathsTraits::compositetype composite_type; +//// return T((composite_type(a) * b * c) / (composite_type(KoColorSpaceMathsTraits::unitValue) * KoColorSpaceMathsTraits::unitValue)); +//// } + +// template +// inline T inv(T a) { return KoColorSpaceMaths::invert(a); } + +// template +// inline T lerp(T a, T b, T alpha) { return KoColorSpaceMaths::blend(b, a, alpha); } + +// template +// inline TRet scale(T a) { return KoColorSpaceMaths::scaleToA(a); } + +// template +// inline typename KoColorSpaceMathsTraits::compositetype +// div(T a, T b) { return KoColorSpaceMaths::divide(a, b); } + +// template +// inline T clamp(typename KoColorSpaceMathsTraits::compositetype a) { +// return KoColorSpaceMaths::clamp(a); +// } + +// template +// inline T min(T a, T b, T c) { +// b = (a < b) ? a : b; +// return (b < c) ? b : c; +// } + +// template +// inline T max(T a, T b, T c) { +// b = (a > b) ? a : b; +// return (b > c) ? b : c; +// } + +// template +// inline T zeroValue() { return KoColorSpaceMathsTraits::zeroValue; } + +// template +// inline T halfValue() { return KoColorSpaceMathsTraits::halfValue; } + +// template +// inline T unitValue() { return KoColorSpaceMathsTraits::unitValue; } + +// template +// inline T unionShapeOpacity(T a, T b) { +// typedef typename KoColorSpaceMathsTraits::compositetype composite_type; +// return T(composite_type(a) + b - mul(a,b)); +// } + +// template +// inline T blend(T src, T srcAlpha, T dst, T dstAlpha, T cfValue) { +// return mul(inv(srcAlpha), dstAlpha, dst) + mul(inv(dstAlpha), srcAlpha, src) + mul(dstAlpha, srcAlpha, cfValue); +// } +//} + +//struct HSYType +//{ +// template +// inline static TReal getLightness(TReal r, TReal g, TReal b) { +// return TReal(0.299)*r + TReal(0.587)*g + TReal(0.114)*b; +// } + +// template +// inline static TReal getSaturation(TReal r, TReal g, TReal b) { +// return Arithmetic::max(r,g,b) - Arithmetic::min(r,g,b); +// } +//}; + +//struct HSIType +//{ +// template +// inline static TReal getLightness(TReal r, TReal g, TReal b) { +// return (r + g + b) * TReal(0.33333333333333333333); // (r + g + b) / 3.0 +// } + +// template +// inline static TReal getSaturation(TReal r, TReal g, TReal b) { +// TReal max = Arithmetic::max(r, g, b); +// TReal min = Arithmetic::min(r, g, b); +// TReal chroma = max - min; + +// return (chroma > std::numeric_limits::epsilon()) ? +// (TReal(1.0) - min / getLightness(r, g, b)) : TReal(0.0); +// } +//}; + +//struct HSLType +//{ +// template +// inline static TReal getLightness(TReal r, TReal g, TReal b) { +// TReal max = Arithmetic::max(r, g, b); +// TReal min = Arithmetic::min(r, g, b); +// return (max + min) * TReal(0.5); +// } + +// template +// inline static TReal getSaturation(TReal r, TReal g, TReal b) { +// TReal max = Arithmetic::max(r, g, b); +// TReal min = Arithmetic::min(r, g, b); +// TReal chroma = max - min; +// TReal light = (max + min) * TReal(0.5); +// TReal div = TReal(1.0) - std::abs(TReal(2.0)*light - TReal(1.0)); + +// if(div > std::numeric_limits::epsilon()) +// return chroma / div; + +// return TReal(1.0); +// } +//}; + +//struct HSVType +//{ +// template +// inline static TReal getLightness(TReal r, TReal g, TReal b) { +// return Arithmetic::max(r,g,b); +// } + +// template +// inline static TReal getSaturation(TReal r, TReal g, TReal b) { +// TReal max = Arithmetic::max(r, g, b); +// TReal min = Arithmetic::min(r, g, b); +// return (max == TReal(0.0)) ? TReal(0.0) : (max - min) / max; +// } +//}; + +//template +//TReal getHue(TReal r, TReal g, TReal b) { +// TReal min = Arithmetic::min(r, g, b); +// TReal max = Arithmetic::max(r, g, b); +// TReal chroma = max - min; + +// TReal hue = TReal(-1.0); + +// if(chroma > std::numeric_limits::epsilon()) { + +//// return atan2(TReal(2.0)*r - g - b, TReal(1.73205080756887729353)*(g - b)); + +// if(max == r) // between yellow and magenta +// hue = (g - b) / chroma; +// else if(max == g) // between cyan and yellow +// hue = TReal(2.0) + (b - r) / chroma; +// else if(max == b) // between magenta and cyan +// hue = TReal(4.0) + (r - g) / chroma; + +// if(hue < -std::numeric_limits::epsilon()) +// hue += TReal(6.0); + +// hue /= TReal(6.0); +// } + +//// hue = (r == max) ? (b-g) : (g == max) ? TReal(2.0)+(r-b) : TReal(4.0)+(g-r); + +// return hue; +//} + +//template +//void getRGB(TReal& r, TReal& g, TReal& b, TReal hue) { +// // 0 red -> (1,0,0) +// // 1 yellow -> (1,1,0) +// // 2 green -> (0,1,0) +// // 3 cyan -> (0,1,1) +// // 4 blue -> (0,0,1) +// // 5 maenta -> (1,0,1) +// // 6 red -> (1,0,0) + +// if(hue < -std::numeric_limits::epsilon()) { +// r = g = b = TReal(0.0); +// return; +// } + +// int i = int(hue * TReal(6.0)); +// TReal x = hue * TReal(6.0) - i; +// TReal y = TReal(1.0) - x; + +// switch(i % 6){ +// case 0: { r=TReal(1.0), g=x , b=TReal(0.0); } break; +// case 1: { r=y , g=TReal(1.0), b=TReal(0.0); } break; +// case 2: { r=TReal(0.0), g=TReal(1.0), b=x ; } break; +// case 3: { r=TReal(0.0), g=y , b=TReal(1.0); } break; +// case 4: { r=x , g=TReal(0.0), b=TReal(1.0); } break; +// case 5: { r=TReal(1.0), g=TReal(0.0), b=y ; } break; +// } +//} + +//template +//inline static TReal getLightness(TReal r, TReal g, TReal b) { +// return HSXType::getLightness(r, g, b); +//} + +//template +//inline void addLightness(TReal& r, TReal& g, TReal& b, TReal light) +//{ +// using namespace Arithmetic; + +// r += light; +// g += light; +// b += light; + +// TReal l = HSXType::getLightness(r, g, b); +// TReal n = min(r, g, b); +// TReal x = max(r, g, b); + +// if(n < TReal(0.0)) { +// TReal iln = TReal(1.0) / (l-n); +// r = l + ((r-l) * l) * iln; +// g = l + ((g-l) * l) * iln; +// b = l + ((b-l) * l) * iln; +// } + +// if(x > TReal(1.0) && (x-l) > std::numeric_limits::epsilon()) { +// TReal il = TReal(1.0) - l; +// TReal ixl = TReal(1.0) / (x - l); +// r = l + ((r-l) * il) * ixl; +// g = l + ((g-l) * il) * ixl; +// b = l + ((b-l) * il) * ixl; +// } +//} + +//template +//inline void setLightness(TReal& r, TReal& g, TReal& b, TReal light) +//{ +// addLightness(r,g,b, light - HSXType::getLightness(r,g,b)); +//} + +//template +//inline static TReal getSaturation(TReal r, TReal g, TReal b) { +// return HSXType::getSaturation(r, g, b); +//} + +//template +//inline void setSaturation(TReal& r, TReal& g, TReal& b, TReal sat) +//{ +// int min = 0; +// int mid = 1; +// int max = 2; +// TReal rgb[3] = {r, g, b}; + +// if(rgb[mid] < rgb[min]) { +// int tmp = min; +// min = mid; +// mid = tmp; +// } + +// if(rgb[max] < rgb[mid]) { +// int tmp = mid; +// mid = max; +// max = tmp; +// } + +// if(rgb[mid] < rgb[min]) { +// int tmp = min; +// min = mid; +// mid = tmp; +// } + +// if((rgb[max] - rgb[min]) > TReal(0.0)) { +// rgb[mid] = ((rgb[mid]-rgb[min]) * sat) / (rgb[max]-rgb[min]); +// rgb[max] = sat; +// rgb[min] = TReal(0.0); + +// r = rgb[0]; +// g = rgb[1]; +// b = rgb[2]; +// } +// else r = g = b = TReal(0.0); +//} + +#endif diff --git a/libs/pigment/KoCmykColorSpaceTraits.h b/libs/pigment/KoCmykColorSpaceTraits.h index a81385468f..3304a8736a 100644 --- a/libs/pigment/KoCmykColorSpaceTraits.h +++ b/libs/pigment/KoCmykColorSpaceTraits.h @@ -1,238 +1,326 @@ /* * Copyright (c) 2006-2007 Cyrille Berger - * Copyright (c) 2016 L. E. Segovia + * Copyright (c) 2016-2017 L. E. Segovia * * 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 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef _KO_CMYK_COLORSPACE_TRAITS_H_ #define _KO_CMYK_COLORSPACE_TRAITS_H_ -#include - -#include "KoColorSpaceConstants.h" -#include "KoColorSpaceMaths.h" -#include "DebugPigment.h" - +#include /** * Base class for CMYK traits, it provides some convenient functions to * access CMYK channels through an explicit API. */ template struct KoCmykTraits : public KoColorSpaceTrait<_channels_type_, 5, 4> { typedef _channels_type_ channels_type; typedef KoColorSpaceTrait<_channels_type_, 5, 4> parent; static const qint32 c_pos = 0; static const qint32 m_pos = 1; static const qint32 y_pos = 2; static const qint32 k_pos = 3; /** * An CMYK pixel */ struct Pixel { channels_type cyan; channels_type magenta; channels_type yellow; channels_type black; channels_type alpha; }; /// @return the Cyan component inline static channels_type C(quint8* data) { channels_type* d = parent::nativeArray(data); return d[c_pos]; } /// Set the Cyan component inline static void setC(quint8* data, channels_type nv) { channels_type* d = parent::nativeArray(data); d[c_pos] = nv; } /// @return the Magenta component inline static channels_type M(quint8* data) { channels_type* d = parent::nativeArray(data); return d[m_pos]; } /// Set the Magenta component inline static void setM(quint8* data, channels_type nv) { channels_type* d = parent::nativeArray(data); d[m_pos] = nv; } /// @return the Yellow component inline static channels_type Y(quint8* data) { channels_type* d = parent::nativeArray(data); return d[y_pos]; } /// Set the Yellow component inline static void setY(quint8* data, channels_type nv) { channels_type* d = parent::nativeArray(data); d[y_pos] = nv; } /// @return the Key component inline static channels_type k(quint8* data) { channels_type* d = parent::nativeArray(data); return d[k_pos]; } /// Set the Key component inline static void setK(quint8* data, channels_type nv) { channels_type* d = parent::nativeArray(data); d[k_pos] = nv; } }; struct KoCmykU8Traits : public KoCmykTraits { }; struct KoCmykU16Traits : public KoCmykTraits { }; #include #ifdef HAVE_OPENEXR #include struct KoCmykF16Traits : public KoCmykTraits { - static constexpr float MAX_CHANNEL_CMYK = 100; inline static QString normalisedChannelValueText(const quint8 *pixel, quint32 channelIndex) { - return channelValueText(pixel, channelIndex); + if (channelIndex > parent::channels_nb) return QString("Error"); + channels_type c = nativeArray(pixel)[channelIndex]; + switch (channelIndex) { + case c_pos: + case m_pos: + case y_pos: + case k_pos: + return QString().setNum(100.0 * qBound((qreal)0, + ((qreal)c) / KoCmykColorSpaceMathsTraits::unitValueCMYK, + (qreal)KoCmykColorSpaceMathsTraits::unitValueCMYK)); + case 4: + return QString().setNum(100.0 * qBound((qreal)0, + ((qreal)c) / KoCmykColorSpaceMathsTraits::unitValue, + (qreal)KoCmykColorSpaceMathsTraits::unitValue)); + default: + return QString("Error"); + } } inline static void normalisedChannelsValue(const quint8 *pixel, QVector &channels) { Q_ASSERT((int)channels.count() == (int)parent::channels_nb); channels_type c; for (uint i = 0; i < parent::channels_nb; i++) { - c = parent::nativeArray(pixel)[i]; - channels[i] = (qreal)c; + c = nativeArray(pixel)[i]; + switch (i) { + case c_pos: + case m_pos: + case y_pos: + case k_pos: + channels[i] = qBound((qreal)0, + ((qreal)c) / KoCmykColorSpaceMathsTraits::unitValueCMYK, + (qreal)KoCmykColorSpaceMathsTraits::unitValueCMYK); + break; + // As per KoChannelInfo alpha channels are [0..1] + case 4: + default: + channels[i] = qBound((qreal)0, + ((qreal)c) / KoCmykColorSpaceMathsTraits::unitValue, + (qreal)KoCmykColorSpaceMathsTraits::unitValue); + break; + } } } inline static void fromNormalisedChannelsValue(quint8 *pixel, const QVector &values) { Q_ASSERT((int)values.count() == (int)parent::channels_nb); channels_type c; for (uint i = 0; i < parent::channels_nb; i++) { float b = 0; switch(i) { case c_pos: case m_pos: case y_pos: case k_pos: b = qBound((float)0, - (float)KoColorSpaceMathsTraits::unitValue * values[i], - (float)MAX_CHANNEL_CMYK); + (float)KoCmykColorSpaceMathsTraits::unitValueCMYK * values[i], + (float)KoCmykColorSpaceMathsTraits::unitValueCMYK); break; default: - b = qBound((float)KoColorSpaceMathsTraits::min, - (float)KoColorSpaceMathsTraits::unitValue * values[i], - (float)KoColorSpaceMathsTraits::max); + b = qBound((float)KoCmykColorSpaceMathsTraits::min, + (float)KoCmykColorSpaceMathsTraits::unitValue * values[i], + (float)KoCmykColorSpaceMathsTraits::max); break; } c = (channels_type)b; parent::nativeArray(pixel)[i] = c; } } }; #endif struct KoCmykF32Traits : public KoCmykTraits { - static constexpr float MAX_CHANNEL_CMYK = 100; inline static QString normalisedChannelValueText(const quint8 *pixel, quint32 channelIndex) { - return channelValueText(pixel, channelIndex); + if (channelIndex > parent::channels_nb) return QString("Error"); + channels_type c = nativeArray(pixel)[channelIndex]; + switch (channelIndex) { + case c_pos: + case m_pos: + case y_pos: + case k_pos: + return QString().setNum(100.0 * qBound((qreal)0, + ((qreal)c) / KoCmykColorSpaceMathsTraits::unitValueCMYK, + (qreal)KoCmykColorSpaceMathsTraits::unitValueCMYK)); + case 4: + return QString().setNum(100.0 * qBound((qreal)0, + ((qreal)c) / KoCmykColorSpaceMathsTraits::unitValue, + (qreal)KoCmykColorSpaceMathsTraits::unitValue)); + default: + return QString("Error"); + } } inline static void normalisedChannelsValue(const quint8 *pixel, QVector &channels) { Q_ASSERT((int)channels.count() == (int)parent::channels_nb); channels_type c; for (uint i = 0; i < parent::channels_nb; i++) { - c = parent::nativeArray(pixel)[i]; - channels[i] = (qreal)c; + c = nativeArray(pixel)[i]; + switch (i) { + case c_pos: + case m_pos: + case y_pos: + case k_pos: + channels[i] = qBound((qreal)0, + ((qreal)c) / KoCmykColorSpaceMathsTraits::unitValueCMYK, + (qreal)KoCmykColorSpaceMathsTraits::unitValueCMYK); + break; + // As per KoChannelInfo alpha channels are [0..1] + case 4: + default: + channels[i] = qBound((qreal)0, + ((qreal)c) / KoCmykColorSpaceMathsTraits::unitValue, + (qreal)KoCmykColorSpaceMathsTraits::unitValue); + break; + } } } inline static void fromNormalisedChannelsValue(quint8 *pixel, const QVector &values) { Q_ASSERT((int)values.count() == (int)parent::channels_nb); channels_type c; for (uint i = 0; i < parent::channels_nb; i++) { float b = 0; switch(i) { case c_pos: case m_pos: case y_pos: case k_pos: b = qBound((float)0, - (float)KoColorSpaceMathsTraits::unitValue * values[i], - (float)MAX_CHANNEL_CMYK); + (float)KoCmykColorSpaceMathsTraits::unitValueCMYK * values[i], + (float)KoCmykColorSpaceMathsTraits::unitValueCMYK); break; default: - b = qBound((float)KoColorSpaceMathsTraits::min, - (float)KoColorSpaceMathsTraits::unitValue * values[i], - (float)KoColorSpaceMathsTraits::max); + b = qBound((float)KoCmykColorSpaceMathsTraits::min, + (float)KoCmykColorSpaceMathsTraits::unitValue * values[i], + (float)KoCmykColorSpaceMathsTraits::max); break; } c = (channels_type)b; parent::nativeArray(pixel)[i] = c; } } }; struct KoCmykF64Traits : public KoCmykTraits { - static constexpr double MAX_CHANNEL_CMYK = 100; inline static QString normalisedChannelValueText(const quint8 *pixel, quint32 channelIndex) { - return channelValueText(pixel, channelIndex); + if (channelIndex > parent::channels_nb) return QString("Error"); + channels_type c = nativeArray(pixel)[channelIndex]; + switch (channelIndex) { + case c_pos: + case m_pos: + case y_pos: + case k_pos: + return QString().setNum(100.0 * qBound((qreal)0, + ((qreal)c) / KoCmykColorSpaceMathsTraits::unitValueCMYK, + (qreal)KoCmykColorSpaceMathsTraits::unitValueCMYK)); + case 4: + return QString().setNum(100.0 * qBound((qreal)0, + ((qreal)c) / KoCmykColorSpaceMathsTraits::unitValue, + (qreal)KoCmykColorSpaceMathsTraits::unitValue)); + default: + return QString("Error"); + } } inline static void normalisedChannelsValue(const quint8 *pixel, QVector &channels) { Q_ASSERT((int)channels.count() == (int)parent::channels_nb); channels_type c; for (uint i = 0; i < parent::channels_nb; i++) { - c = parent::nativeArray(pixel)[i]; - channels[i] = (qreal)c; + c = nativeArray(pixel)[i]; + switch (i) { + case c_pos: + case m_pos: + case y_pos: + case k_pos: + channels[i] = qBound((qreal)0, + ((qreal)c) / KoCmykColorSpaceMathsTraits::unitValueCMYK, + (qreal)KoCmykColorSpaceMathsTraits::unitValueCMYK); + break; + // As per KoChannelInfo alpha channels are [0..1] + case 4: + default: + channels[i] = qBound((qreal)0, + ((qreal)c) / KoCmykColorSpaceMathsTraits::unitValue, + (qreal)KoCmykColorSpaceMathsTraits::unitValue); + break; + } } } inline static void fromNormalisedChannelsValue(quint8 *pixel, const QVector &values) { Q_ASSERT((int)values.count() == (int)parent::channels_nb); channels_type c; for (uint i = 0; i < parent::channels_nb; i++) { float b = 0; switch(i) { case c_pos: case m_pos: case y_pos: case k_pos: - b = qBound((double)0, - (double)KoColorSpaceMathsTraits::unitValue * values[i], - (double)MAX_CHANNEL_CMYK); + b = qBound((float)0, + (float)KoCmykColorSpaceMathsTraits::unitValueCMYK * values[i], + (float)KoCmykColorSpaceMathsTraits::unitValueCMYK); break; default: - b = qBound((double)KoColorSpaceMathsTraits::min, - (double)KoColorSpaceMathsTraits::unitValue * values[i], - (double)KoColorSpaceMathsTraits::max); + b = qBound((float)KoCmykColorSpaceMathsTraits::min, + (float)KoCmykColorSpaceMathsTraits::unitValue * values[i], + (float)KoCmykColorSpaceMathsTraits::max); break; } c = (channels_type)b; parent::nativeArray(pixel)[i] = c; } } }; #endif diff --git a/libs/pigment/KoLabColorSpaceMaths.cpp b/libs/pigment/KoLabColorSpaceMaths.cpp new file mode 100644 index 0000000000..8147117543 --- /dev/null +++ b/libs/pigment/KoLabColorSpaceMaths.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2006 Cyrille Berger + * Copyright (c) 2017 L. E. Segovia + * + * 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 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include + +#include + +#include + +#ifdef HAVE_OPENEXR +const half KoLabColorSpaceMathsTraits::zeroValueL = 0.0; +const half KoLabColorSpaceMathsTraits::unitValueL = 100.0; +const half KoLabColorSpaceMathsTraits::halfValueL = 50.0; +const half KoLabColorSpaceMathsTraits::zeroValueAB = -128.0; +const half KoLabColorSpaceMathsTraits::unitValueAB = +127.0; +const half KoLabColorSpaceMathsTraits::halfValueAB = 0.0; +#endif + +const float KoLabColorSpaceMathsTraits::zeroValueL = 0.0; +const float KoLabColorSpaceMathsTraits::unitValueL = 100.0; +const float KoLabColorSpaceMathsTraits::halfValueL = 50.0; +const float KoLabColorSpaceMathsTraits::zeroValueAB = -128.0; +const float KoLabColorSpaceMathsTraits::unitValueAB = +127.0; +const float KoLabColorSpaceMathsTraits::halfValueAB = 0.0; + +const double KoLabColorSpaceMathsTraits::zeroValueL = 0.0; +const double KoLabColorSpaceMathsTraits::unitValueL = 100.0; +const double KoLabColorSpaceMathsTraits::halfValueL = 50.0; +const double KoLabColorSpaceMathsTraits::zeroValueAB = -128.0; +const double KoLabColorSpaceMathsTraits::unitValueAB = +127.0; +const double KoLabColorSpaceMathsTraits::halfValueAB = 0.0; diff --git a/libs/pigment/KoLabColorSpaceMaths.h b/libs/pigment/KoLabColorSpaceMaths.h new file mode 100644 index 0000000000..92e4dde8b8 --- /dev/null +++ b/libs/pigment/KoLabColorSpaceMaths.h @@ -0,0 +1,775 @@ +/* + * Copyright (c) 2006,2007,2010 Cyrille Berger + * Copyright (c) 2017 L. E. Segovia + * + * 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 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KOLABCOLORSPACEMATHS_H_ +#define KOLABCOLORSPACEMATHS_H_ + +#include +#include + +#include "kritapigment_export.h" +#include +#include "KoChannelInfo.h" +#include "KoLut.h" + +#include + +#undef _T + +/** + * This is an empty mainWindow that needs to be "specialized" for each possible + * numerical type (quint8, quint16...). + * + * It needs to defines some static constant fields : + * - zeroValue : the zero for this numerical type + * - unitValue : the maximum value of the normal dynamic range + * - max : the maximum value + * - min : the minimum value + * - epsilon : a value close to zero but different of zero + * - bits : the bit depth + * + * And some types : + * - compositetype the type used for composite operations (usually one with + * a higher bit depth) + * + * This class is specialized to handle the floating point bounds of the Lab color space. + */ + +template +class KoLabColorSpaceMathsTraits +{ +public: +}; + +template<> +class KRITAPIGMENT_EXPORT KoLabColorSpaceMathsTraits : public KoColorSpaceMathsTraits +{ +public: + static const quint8 zeroValueL = 0; + static const quint8 unitValueL = 0x00FF; + static const quint8 halfValueL = 0x00FF / 2; + static const quint8 zeroValueAB = 0; + static const quint8 unitValueAB = 0x00FF; + static const quint8 halfValueAB = 0x00FF / 2; +}; + +template<> +class KRITAPIGMENT_EXPORT KoLabColorSpaceMathsTraits : public KoColorSpaceMathsTraits +{ +public: + static const quint16 zeroValueL = 0; + static const quint16 unitValueL = 0xFFFF; + static const quint16 halfValueL = 0xFFFF / 2; + static const quint16 zeroValueAB = 0; + static const quint16 unitValueAB = 0xFFFF; + static const quint16 halfValueAB = 0xFFFF / 2; +}; + +template<> +class KRITAPIGMENT_EXPORT KoLabColorSpaceMathsTraits : public KoColorSpaceMathsTraits +{ +public: + static const qint16 zeroValueL = 0; + static const qint16 unitValueL = 32767; + static const qint16 halfValueL = 32767 / 2; + static const qint16 zeroValueAB = 0; + static const qint16 unitValueAB = 32767; + static const qint16 halfValueAB = 32767 / 2; +}; + +template<> +class KRITAPIGMENT_EXPORT KoLabColorSpaceMathsTraits : public KoColorSpaceMathsTraits +{ +public: + static const quint32 zeroValueL = 0; + static const quint32 unitValueL = 0xFFFFFFFF; + static const quint32 halfValueL = 0xFFFFFFFF / 2; + static const quint32 zeroValueAB = 0; + static const quint32 unitValueAB = 0xFFFFFFFF; + static const quint32 halfValueAB = 0xFFFFFFFF / 2; +}; + +#include +#ifdef HAVE_OPENEXR +#include + +template<> +class KRITAPIGMENT_EXPORT KoLabColorSpaceMathsTraits : public KoColorSpaceMathsTraits +{ +public: + static const half zeroValueL; + static const half unitValueL; + static const half halfValueL; + static const half zeroValueAB; + static const half unitValueAB; + static const half halfValueAB; +}; +#endif + +template<> +class KRITAPIGMENT_EXPORT KoLabColorSpaceMathsTraits : public KoColorSpaceMathsTraits +{ +public: + static const float zeroValueL; + static const float unitValueL; + static const float halfValueL; + static const float zeroValueAB; + static const float unitValueAB; + static const float halfValueAB; +}; + +template<> +class KRITAPIGMENT_EXPORT KoLabColorSpaceMathsTraits : public KoColorSpaceMathsTraits +{ +public: + static const double zeroValueL; + static const double unitValueL; + static const double halfValueL; + static const double zeroValueAB; + static const double unitValueAB; + static const double halfValueAB; +}; + +//template +//struct KoIntegerToFloat { +// inline float operator()(_T_ f) const +// { +// return f / float(KoColorSpaceMathsTraits<_T_>::max); +// } +//}; + +//struct KoLuts { + +// static KRITAPIGMENT_EXPORT const Ko::FullLut< KoIntegerToFloat, float, quint16> Uint16ToFloat; +// static KRITAPIGMENT_EXPORT const Ko::FullLut< KoIntegerToFloat, float, quint8> Uint8ToFloat; +//}; + +///** +// * This class defines some elementary operations used by various color +// * space. It's intended to be generic, but some specialization exists +// * either for optimization or just for being buildable. +// * +// * @param _T some numerical type with an existing trait +// * @param _Tdst some other numerical type with an existing trait, it is +// * only needed if different of _T +// */ +//template < typename _T, typename _Tdst = _T > +//class KoColorSpaceMaths +//{ +// typedef KoColorSpaceMathsTraits<_T> traits; +// typedef typename traits::compositetype src_compositetype; +// typedef typename KoColorSpaceMathsTraits<_Tdst>::compositetype dst_compositetype; + +//public: +// inline static _Tdst multiply(_T a, _Tdst b) { +// return (dst_compositetype(a)*b) / KoColorSpaceMathsTraits<_Tdst>::unitValue; +// } + +// inline static _Tdst multiply(_T a, _Tdst b, _Tdst c) { +// return (dst_compositetype(a)*b*c) / (dst_compositetype(KoColorSpaceMathsTraits<_Tdst>::unitValue) * KoColorSpaceMathsTraits<_T>::unitValue); +// } + +// /** +// * Division : (a * MAX ) / b +// * @param a +// * @param b +// */ +// inline static dst_compositetype divide(_T a, _Tdst b) { +// return (dst_compositetype(a) * KoColorSpaceMathsTraits<_Tdst>::unitValue) / b; +// } + +// /** +// * Inversion : unitValue - a +// * @param a +// */ +// inline static _T invert(_T a) { +// return traits::unitValue - a; +// } + +// /** +// * Blending : (a * alpha) + b * (1 - alpha) +// * @param a +// * @param b +// * @param alpha +// */ +// inline static _T blend(_T a, _T b, _T alpha) { +// src_compositetype c = ((src_compositetype(a) - b) * alpha) / traits::unitValue; +// return c + b; +// } + +// /** +// * This function will scale a value of type _T to fit into a _Tdst. +// */ +// inline static _Tdst scaleToA(_T a) { +// return _Tdst(dst_compositetype(a) * KoColorSpaceMathsTraits<_Tdst>::unitValue / KoColorSpaceMathsTraits<_T>::unitValue); +// } + +// inline static dst_compositetype clamp(dst_compositetype val) { +// return qBound(KoColorSpaceMathsTraits<_Tdst>::min, val, KoColorSpaceMathsTraits<_Tdst>::max); +// } + +// /** +// * Clamps the composite type on higher border only. That is a fast path +// * for scale-only transformations +// */ +// inline static _Tdst clampAfterScale(dst_compositetype val) { +// return qMin(val, KoColorSpaceMathsTraits<_Tdst>::max); +// } +//}; + +////------------------------------ double specialization ------------------------------// +//template<> +//inline quint8 KoColorSpaceMaths::scaleToA(double a) +//{ +// double v = a * 255; +// return float2int(CLAMP(v, 0, 255)); +//} + +//template<> +//inline double KoColorSpaceMaths::scaleToA(quint8 a) +//{ +// return KoLuts::Uint8ToFloat(a); +//} + +//template<> +//inline quint16 KoColorSpaceMaths::scaleToA(double a) +//{ +// double v = a * 0xFFFF; +// return float2int(CLAMP(v, 0, 0xFFFF)); +//} + +//template<> +//inline double KoColorSpaceMaths::scaleToA(quint16 a) +//{ +// return KoLuts::Uint16ToFloat(a); +//} + +//template<> +//inline double KoColorSpaceMaths::clamp(double a) +//{ +// return a; +//} + +////------------------------------ float specialization ------------------------------// + +//template<> +//inline float KoColorSpaceMaths::scaleToA(double a) +//{ +// return (float)a; +//} + +//template<> +//inline double KoColorSpaceMaths::scaleToA(float a) +//{ +// return a; +//} + +//template<> +//inline quint16 KoColorSpaceMaths::scaleToA(float a) +//{ +// float v = a * 0xFFFF; +// return (quint16)float2int(CLAMP(v, 0, 0xFFFF)); +//} + +//template<> +//inline float KoColorSpaceMaths::scaleToA(quint16 a) +//{ +// return KoLuts::Uint16ToFloat(a); +//} + +//template<> +//inline quint8 KoColorSpaceMaths::scaleToA(float a) +//{ +// float v = a * 255; +// return (quint8)float2int(CLAMP(v, 0, 255)); +//} + +//template<> +//inline float KoColorSpaceMaths::scaleToA(quint8 a) +//{ +// return KoLuts::Uint8ToFloat(a); +//} + +//template<> +//inline float KoColorSpaceMaths::blend(float a, float b, float alpha) +//{ +// return (a - b) * alpha + b; +//} + +//template<> +//inline double KoColorSpaceMaths::clamp(double a) +//{ +// return a; +//} + +////------------------------------ half specialization ------------------------------// + +//#ifdef HAVE_OPENEXR + +//template<> +//inline half KoColorSpaceMaths::scaleToA(double a) +//{ +// return (half)a; +//} + +//template<> +//inline double KoColorSpaceMaths::scaleToA(half a) +//{ +// return a; +//} + +//template<> +//inline float KoColorSpaceMaths::scaleToA(half a) +//{ +// return a; +//} + +//template<> +//inline half KoColorSpaceMaths::scaleToA(float a) +//{ +// return (half) a; +//} + +//template<> +//inline quint8 KoColorSpaceMaths::scaleToA(half a) +//{ +// half v = a * 255; +// return (quint8)(CLAMP(v, 0, 255)); +//} + +//template<> +//inline half KoColorSpaceMaths::scaleToA(quint8 a) +//{ +// return a *(1.0 / 255.0); +//} +//template<> +//inline quint16 KoColorSpaceMaths::scaleToA(half a) +//{ +// double v = a * 0xFFFF; +// return (quint16)(CLAMP(v, 0, 0xFFFF)); +//} + +//template<> +//inline half KoColorSpaceMaths::scaleToA(quint16 a) +//{ +// return a *(1.0 / 0xFFFF); +//} + +//template<> +//inline half KoColorSpaceMaths::scaleToA(half a) +//{ +// return a; +//} + +//template<> +//inline half KoColorSpaceMaths::blend(half a, half b, half alpha) +//{ +// return (a - b) * alpha + b; +//} + +//template<> +//inline double KoColorSpaceMaths::clamp(double a) +//{ +// return a; +//} + + +//#endif + +////------------------------------ quint8 specialization ------------------------------// + +//template<> +//inline quint8 KoColorSpaceMaths::multiply(quint8 a, quint8 b) +//{ +// return (quint8)UINT8_MULT(a, b); +//} + + +//template<> +//inline quint8 KoColorSpaceMaths::multiply(quint8 a, quint8 b, quint8 c) +//{ +// return (quint8)UINT8_MULT3(a, b, c); +//} + +//template<> +//inline KoColorSpaceMathsTraits::compositetype +//KoColorSpaceMaths::divide(quint8 a, quint8 b) +//{ +// return UINT8_DIVIDE(a, b); +//} + +//template<> +//inline quint8 KoColorSpaceMaths::invert(quint8 a) +//{ +// return ~a; +//} + +//template<> +//inline quint8 KoColorSpaceMaths::blend(quint8 a, quint8 b, quint8 c) +//{ +// return UINT8_BLEND(a, b, c); +//} + +////------------------------------ quint16 specialization ------------------------------// + +//template<> +//inline quint16 KoColorSpaceMaths::multiply(quint16 a, quint16 b) +//{ +// return (quint16)UINT16_MULT(a, b); +//} + +//template<> +//inline KoColorSpaceMathsTraits::compositetype +//KoColorSpaceMaths::divide(quint16 a, quint16 b) +//{ +// return UINT16_DIVIDE(a, b); +//} + +//template<> +//inline quint16 KoColorSpaceMaths::invert(quint16 a) +//{ +// return ~a; +//} + +////------------------------------ various specialization ------------------------------// + + +//// TODO: use more functions from KoIntegersMaths to do the computation + +///// This specialization is needed because the default implementation won't work when scaling up +//template<> +//inline quint16 KoColorSpaceMaths::scaleToA(quint8 a) +//{ +// return UINT8_TO_UINT16(a); +//} + +//template<> +//inline quint8 KoColorSpaceMaths::scaleToA(quint16 a) +//{ +// return UINT16_TO_UINT8(a); +//} + + +//// Due to once again a bug in gcc, there is the need for those specialized functions: + +//template<> +//inline quint8 KoColorSpaceMaths::scaleToA(quint8 a) +//{ +// return a; +//} + +//template<> +//inline quint16 KoColorSpaceMaths::scaleToA(quint16 a) +//{ +// return a; +//} + +//template<> +//inline float KoColorSpaceMaths::scaleToA(float a) +//{ +// return a; +//} + +//namespace Arithmetic +//{ +// const static qreal pi = 3.14159265358979323846; + +// template +// inline T mul(T a, T b) { return KoColorSpaceMaths::multiply(a, b); } + +// template +// inline T mul(T a, T b, T c) { return KoColorSpaceMaths::multiply(a, b, c); } + +//// template +//// inline T mul(T a, T b) { +//// typedef typename KoColorSpaceMathsTraits::compositetype composite_type; +//// return T(composite_type(a) * b / KoColorSpaceMathsTraits::unitValue); +//// } +//// +//// template +//// inline T mul(T a, T b, T c) { +//// typedef typename KoColorSpaceMathsTraits::compositetype composite_type; +//// return T((composite_type(a) * b * c) / (composite_type(KoColorSpaceMathsTraits::unitValue) * KoColorSpaceMathsTraits::unitValue)); +//// } + +// template +// inline T inv(T a) { return KoColorSpaceMaths::invert(a); } + +// template +// inline T lerp(T a, T b, T alpha) { return KoColorSpaceMaths::blend(b, a, alpha); } + +// template +// inline TRet scale(T a) { return KoColorSpaceMaths::scaleToA(a); } + +// template +// inline typename KoColorSpaceMathsTraits::compositetype +// div(T a, T b) { return KoColorSpaceMaths::divide(a, b); } + +// template +// inline T clamp(typename KoColorSpaceMathsTraits::compositetype a) { +// return KoColorSpaceMaths::clamp(a); +// } + +// template +// inline T min(T a, T b, T c) { +// b = (a < b) ? a : b; +// return (b < c) ? b : c; +// } + +// template +// inline T max(T a, T b, T c) { +// b = (a > b) ? a : b; +// return (b > c) ? b : c; +// } + +// template +// inline T zeroValue() { return KoColorSpaceMathsTraits::zeroValue; } + +// template +// inline T halfValue() { return KoColorSpaceMathsTraits::halfValue; } + +// template +// inline T unitValue() { return KoColorSpaceMathsTraits::unitValue; } + +// template +// inline T unionShapeOpacity(T a, T b) { +// typedef typename KoColorSpaceMathsTraits::compositetype composite_type; +// return T(composite_type(a) + b - mul(a,b)); +// } + +// template +// inline T blend(T src, T srcAlpha, T dst, T dstAlpha, T cfValue) { +// return mul(inv(srcAlpha), dstAlpha, dst) + mul(inv(dstAlpha), srcAlpha, src) + mul(dstAlpha, srcAlpha, cfValue); +// } +//} + +//struct HSYType +//{ +// template +// inline static TReal getLightness(TReal r, TReal g, TReal b) { +// return TReal(0.299)*r + TReal(0.587)*g + TReal(0.114)*b; +// } + +// template +// inline static TReal getSaturation(TReal r, TReal g, TReal b) { +// return Arithmetic::max(r,g,b) - Arithmetic::min(r,g,b); +// } +//}; + +//struct HSIType +//{ +// template +// inline static TReal getLightness(TReal r, TReal g, TReal b) { +// return (r + g + b) * TReal(0.33333333333333333333); // (r + g + b) / 3.0 +// } + +// template +// inline static TReal getSaturation(TReal r, TReal g, TReal b) { +// TReal max = Arithmetic::max(r, g, b); +// TReal min = Arithmetic::min(r, g, b); +// TReal chroma = max - min; + +// return (chroma > std::numeric_limits::epsilon()) ? +// (TReal(1.0) - min / getLightness(r, g, b)) : TReal(0.0); +// } +//}; + +//struct HSLType +//{ +// template +// inline static TReal getLightness(TReal r, TReal g, TReal b) { +// TReal max = Arithmetic::max(r, g, b); +// TReal min = Arithmetic::min(r, g, b); +// return (max + min) * TReal(0.5); +// } + +// template +// inline static TReal getSaturation(TReal r, TReal g, TReal b) { +// TReal max = Arithmetic::max(r, g, b); +// TReal min = Arithmetic::min(r, g, b); +// TReal chroma = max - min; +// TReal light = (max + min) * TReal(0.5); +// TReal div = TReal(1.0) - std::abs(TReal(2.0)*light - TReal(1.0)); + +// if(div > std::numeric_limits::epsilon()) +// return chroma / div; + +// return TReal(1.0); +// } +//}; + +//struct HSVType +//{ +// template +// inline static TReal getLightness(TReal r, TReal g, TReal b) { +// return Arithmetic::max(r,g,b); +// } + +// template +// inline static TReal getSaturation(TReal r, TReal g, TReal b) { +// TReal max = Arithmetic::max(r, g, b); +// TReal min = Arithmetic::min(r, g, b); +// return (max == TReal(0.0)) ? TReal(0.0) : (max - min) / max; +// } +//}; + +//template +//TReal getHue(TReal r, TReal g, TReal b) { +// TReal min = Arithmetic::min(r, g, b); +// TReal max = Arithmetic::max(r, g, b); +// TReal chroma = max - min; + +// TReal hue = TReal(-1.0); + +// if(chroma > std::numeric_limits::epsilon()) { + +//// return atan2(TReal(2.0)*r - g - b, TReal(1.73205080756887729353)*(g - b)); + +// if(max == r) // between yellow and magenta +// hue = (g - b) / chroma; +// else if(max == g) // between cyan and yellow +// hue = TReal(2.0) + (b - r) / chroma; +// else if(max == b) // between magenta and cyan +// hue = TReal(4.0) + (r - g) / chroma; + +// if(hue < -std::numeric_limits::epsilon()) +// hue += TReal(6.0); + +// hue /= TReal(6.0); +// } + +//// hue = (r == max) ? (b-g) : (g == max) ? TReal(2.0)+(r-b) : TReal(4.0)+(g-r); + +// return hue; +//} + +//template +//void getRGB(TReal& r, TReal& g, TReal& b, TReal hue) { +// // 0 red -> (1,0,0) +// // 1 yellow -> (1,1,0) +// // 2 green -> (0,1,0) +// // 3 cyan -> (0,1,1) +// // 4 blue -> (0,0,1) +// // 5 maenta -> (1,0,1) +// // 6 red -> (1,0,0) + +// if(hue < -std::numeric_limits::epsilon()) { +// r = g = b = TReal(0.0); +// return; +// } + +// int i = int(hue * TReal(6.0)); +// TReal x = hue * TReal(6.0) - i; +// TReal y = TReal(1.0) - x; + +// switch(i % 6){ +// case 0: { r=TReal(1.0), g=x , b=TReal(0.0); } break; +// case 1: { r=y , g=TReal(1.0), b=TReal(0.0); } break; +// case 2: { r=TReal(0.0), g=TReal(1.0), b=x ; } break; +// case 3: { r=TReal(0.0), g=y , b=TReal(1.0); } break; +// case 4: { r=x , g=TReal(0.0), b=TReal(1.0); } break; +// case 5: { r=TReal(1.0), g=TReal(0.0), b=y ; } break; +// } +//} + +//template +//inline static TReal getLightness(TReal r, TReal g, TReal b) { +// return HSXType::getLightness(r, g, b); +//} + +//template +//inline void addLightness(TReal& r, TReal& g, TReal& b, TReal light) +//{ +// using namespace Arithmetic; + +// r += light; +// g += light; +// b += light; + +// TReal l = HSXType::getLightness(r, g, b); +// TReal n = min(r, g, b); +// TReal x = max(r, g, b); + +// if(n < TReal(0.0)) { +// TReal iln = TReal(1.0) / (l-n); +// r = l + ((r-l) * l) * iln; +// g = l + ((g-l) * l) * iln; +// b = l + ((b-l) * l) * iln; +// } + +// if(x > TReal(1.0) && (x-l) > std::numeric_limits::epsilon()) { +// TReal il = TReal(1.0) - l; +// TReal ixl = TReal(1.0) / (x - l); +// r = l + ((r-l) * il) * ixl; +// g = l + ((g-l) * il) * ixl; +// b = l + ((b-l) * il) * ixl; +// } +//} + +//template +//inline void setLightness(TReal& r, TReal& g, TReal& b, TReal light) +//{ +// addLightness(r,g,b, light - HSXType::getLightness(r,g,b)); +//} + +//template +//inline static TReal getSaturation(TReal r, TReal g, TReal b) { +// return HSXType::getSaturation(r, g, b); +//} + +//template +//inline void setSaturation(TReal& r, TReal& g, TReal& b, TReal sat) +//{ +// int min = 0; +// int mid = 1; +// int max = 2; +// TReal rgb[3] = {r, g, b}; + +// if(rgb[mid] < rgb[min]) { +// int tmp = min; +// min = mid; +// mid = tmp; +// } + +// if(rgb[max] < rgb[mid]) { +// int tmp = mid; +// mid = max; +// max = tmp; +// } + +// if(rgb[mid] < rgb[min]) { +// int tmp = min; +// min = mid; +// mid = tmp; +// } + +// if((rgb[max] - rgb[min]) > TReal(0.0)) { +// rgb[mid] = ((rgb[mid]-rgb[min]) * sat) / (rgb[max]-rgb[min]); +// rgb[max] = sat; +// rgb[min] = TReal(0.0); + +// r = rgb[0]; +// g = rgb[1]; +// b = rgb[2]; +// } +// else r = g = b = TReal(0.0); +//} + +#endif diff --git a/libs/pigment/KoLabColorSpaceTraits.h b/libs/pigment/KoLabColorSpaceTraits.h index 43eb0f72a7..de2a7f54ff 100644 --- a/libs/pigment/KoLabColorSpaceTraits.h +++ b/libs/pigment/KoLabColorSpaceTraits.h @@ -1,400 +1,500 @@ /* * Copyright (c) 2006-2007 Cyrille Berger - * Copyright (c) 2016 L. E. Segovia + * Copyright (c) 2016-2017 L. E. Segovia * * 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 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef _KO_LAB_COLORSPACE_TRAITS_H_ #define _KO_LAB_COLORSPACE_TRAITS_H_ +#include /** * LAB traits, it provides some convenient functions to * access LAB channels through an explicit API. * * Use this class in conjonction with KoColorSpace::toLabA16 and * KoColorSpace::fromLabA16 data. * * Example: * quint8* p = KoLabU16Traits::allocate(1); * oneKoColorSpace->toLabA16(somepointertodata, p, 1); * KoLabU16Traits::setL( p, KoLabU16Traits::L(p) / 10 ); * oneKoColorSpace->fromLabA16(p, somepointertodata, 1); */ template struct KoLabTraits : public KoColorSpaceTrait<_channels_type_, 4, 3> { typedef _channels_type_ channels_type; typedef KoColorSpaceTrait<_channels_type_, 4, 3> parent; static const qint32 L_pos = 0; static const qint32 a_pos = 1; static const qint32 b_pos = 2; /** * An Lab pixel */ struct Pixel { channels_type L; channels_type a; channels_type b; channels_type alpha; }; /// @return the L component inline static channels_type L(quint8* data) { channels_type* d = parent::nativeArray(data); return d[L_pos]; } /// Set the L component inline static void setL(quint8* data, channels_type nv) { channels_type* d = parent::nativeArray(data); d[L_pos] = nv; } /// @return the a component inline static channels_type a(quint8* data) { channels_type* d = parent::nativeArray(data); return d[a_pos]; } /// Set the a component inline static void setA(quint8* data, channels_type nv) { channels_type* d = parent::nativeArray(data); d[a_pos] = nv; } /// @return the b component inline static channels_type b(quint8* data) { channels_type* d = parent::nativeArray(data); return d[b_pos]; } /// Set the a component inline static void setB(quint8* data, channels_type nv) { channels_type* d = parent::nativeArray(data); d[b_pos] = nv; } }; //For quint* values must range from 0 to 1 - see KoColorSpaceMaths -struct KoLabU8Traits : public KoLabTraits { +// https://github.com/mm2/Little-CMS/blob/master/src/cmspcs.c +//PCS in Lab2 is encoded as: +// 8 bit Lab PCS: +// L* 0..100 into a 0..ff byte. +// a* t + 128 range is -128.0 +127.0 +// b* +// 16 bit Lab PCS: +// L* 0..100 into a 0..ff00 word. +// a* t + 128 range is -128.0 +127.9961 +// b* +//Version 4 +//--------- +//CIELAB (16 bit) L* 0 -> 100.0 0x0000 -> 0xffff +//CIELAB (16 bit) a* -128.0 -> +127 0x0000 -> 0x8080 -> 0xffff +//CIELAB (16 bit) b* -128.0 -> +127 0x0000 -> 0x8080 -> 0xffff - static const quint32 MAX_CHANNEL_L = 100; - static const quint32 MAX_CHANNEL_AB = 255; - static const quint32 CHANNEL_AB_ZERO_OFFSET = 128; +struct KoLabU8Traits : public KoLabTraits { + // Maps each channel to the ranges above. + // It works when using integers b/c it is always 0..max -- which is not in fp + // I've redone the implementation to be consistent with the fp version (for clarity) --LES inline static void normalisedChannelsValue(const quint8 *pixel, QVector &channels) { Q_ASSERT((int)channels.count() == (int)parent::channels_nb); channels_type c; for (uint i = 0; i < parent::channels_nb; i++) { c = nativeArray(pixel)[i]; switch (i) { case L_pos: - channels[i] = ((qreal)c) / MAX_CHANNEL_L; + channels[i] = ((qreal)c) / KoLabColorSpaceMathsTraits::unitValueL; break; case a_pos: - channels[i] = (((qreal)c) - CHANNEL_AB_ZERO_OFFSET) / MAX_CHANNEL_AB; - break; case b_pos: - channels[i] = (((qreal)c) - CHANNEL_AB_ZERO_OFFSET) / MAX_CHANNEL_AB; + channels[i] = (((qreal)c) - KoLabColorSpaceMathsTraits::halfValueAB) / KoLabColorSpaceMathsTraits::unitValueAB; break; case 3: - channels[i] = ((qreal)c) / UINT16_MAX; + channels[i] = ((qreal)c) / UINT8_MAX; break; default: - channels[i] = ((qreal)c) / KoColorSpaceMathsTraits::unitValue; + channels[i] = ((qreal)c) / KoLabColorSpaceMathsTraits::unitValue; break; } } } inline static QString normalisedChannelValueText(const quint8 *pixel, quint32 channelIndex) { if (channelIndex > parent::channels_nb) return QString("Error"); channels_type c = nativeArray(pixel)[channelIndex]; switch (channelIndex) { case L_pos: - return QString().setNum(100.0 * ((qreal)c) / MAX_CHANNEL_L); + return QString().setNum(100.0 * ((qreal)c) / KoLabColorSpaceMathsTraits::unitValueL); case a_pos: - return QString().setNum(100.0 * ((((qreal)c) - CHANNEL_AB_ZERO_OFFSET) / MAX_CHANNEL_AB)); case b_pos: - return QString().setNum(100.0 * ((((qreal)c) - CHANNEL_AB_ZERO_OFFSET) / MAX_CHANNEL_AB)); + return QString().setNum(100.0 * ((((qreal)c) - KoLabColorSpaceMathsTraits::halfValueAB) / KoLabColorSpaceMathsTraits::unitValueAB)); case 3: - return QString().setNum(100.0 * ((qreal)c) / UINT16_MAX); + return QString().setNum(100.0 * ((qreal)c) / UINT8_MAX); default: return QString("Error"); } } inline static void fromNormalisedChannelsValue(quint8 *pixel, const QVector &values) { Q_ASSERT((int)values.count() == (int)parent::channels_nb); channels_type c; for (uint i = 0; i < channels_nb; i++) { float b = 0; switch (i) { case L_pos: b = qBound((float)0, - (float)MAX_CHANNEL_L * values[i], - (float)MAX_CHANNEL_L); + (float)KoLabColorSpaceMathsTraits::unitValueL * values[i], + (float)KoLabColorSpaceMathsTraits::unitValueL); break; case a_pos: case b_pos: b = qBound((float)0, - (float)MAX_CHANNEL_AB * values[i], - (float)MAX_CHANNEL_AB); + (float)KoLabColorSpaceMathsTraits::unitValueAB * values[i], + (float)KoLabColorSpaceMathsTraits::unitValueAB); break; default: - b = qBound((float)KoColorSpaceMathsTraits::min, - (float)KoColorSpaceMathsTraits::unitValue * values[i], - (float)KoColorSpaceMathsTraits::max); + b = qBound((float)KoLabColorSpaceMathsTraits::min, + (float)KoLabColorSpaceMathsTraits::unitValue * values[i], + (float)KoLabColorSpaceMathsTraits::max); break; } c = (channels_type)b; nativeArray(pixel)[i] = c; } } }; struct KoLabU16Traits : public KoLabTraits { - // https://github.com/mm2/Little-CMS/blob/master/src/cmspcs.c - static const quint32 MAX_CHANNEL_L = 0xFF00; - static const quint32 MAX_CHANNEL_AB = 0xFFFF; - static const quint32 CHANNEL_AB_ZERO_OFFSET = 0x8000; inline static void normalisedChannelsValue(const quint8 *pixel, QVector &channels) { Q_ASSERT((int)channels.count() == (int)parent::channels_nb); channels_type c; for (uint i = 0; i < parent::channels_nb; i++) { c = nativeArray(pixel)[i]; switch (i) { case L_pos: - channels[i] = ((qreal)c) / MAX_CHANNEL_L; + channels[i] = ((qreal)c) / KoLabColorSpaceMathsTraits::unitValueL; break; case a_pos: - channels[i] = (((qreal)c) - CHANNEL_AB_ZERO_OFFSET) / MAX_CHANNEL_AB; - break; case b_pos: - channels[i] = (((qreal)c) - CHANNEL_AB_ZERO_OFFSET) / MAX_CHANNEL_AB; + channels[i] = (((qreal)c) - KoLabColorSpaceMathsTraits::halfValueAB) / KoLabColorSpaceMathsTraits::unitValueAB; break; case 3: channels[i] = ((qreal)c) / UINT16_MAX; break; default: - channels[i] = ((qreal)c) / KoColorSpaceMathsTraits::unitValue; + channels[i] = ((qreal)c) / KoLabColorSpaceMathsTraits::unitValue; break; } } } inline static QString normalisedChannelValueText(const quint8 *pixel, quint32 channelIndex) { if (channelIndex > parent::channels_nb) return QString("Error"); channels_type c = nativeArray(pixel)[channelIndex]; switch (channelIndex) { case L_pos: - return QString().setNum(100.0 * ((qreal)c) / MAX_CHANNEL_L); + return QString().setNum(100.0 * ((qreal)c) / KoLabColorSpaceMathsTraits::unitValueL); case a_pos: - return QString().setNum(100.0 * ((((qreal)c) - CHANNEL_AB_ZERO_OFFSET) / MAX_CHANNEL_AB)); case b_pos: - return QString().setNum(100.0 * ((((qreal)c) - CHANNEL_AB_ZERO_OFFSET) / MAX_CHANNEL_AB)); + return QString().setNum(100.0 * ((((qreal)c) - KoLabColorSpaceMathsTraits::halfValueAB) / KoLabColorSpaceMathsTraits::unitValueAB)); case 3: return QString().setNum(100.0 * ((qreal)c) / UINT16_MAX); default: return QString("Error"); } } inline static void fromNormalisedChannelsValue(quint8 *pixel, const QVector &values) { Q_ASSERT((int)values.count() == (int)parent::channels_nb); channels_type c; for (uint i = 0; i < channels_nb; i++) { float b = 0; switch (i) { case L_pos: b = qBound((float)0, - (float)MAX_CHANNEL_L * values[i], - (float)MAX_CHANNEL_L); + (float)KoLabColorSpaceMathsTraits::unitValueL * values[i], + (float)KoLabColorSpaceMathsTraits::unitValueL); break; case a_pos: case b_pos: b = qBound((float)0, - (float)MAX_CHANNEL_AB * values[i], - (float)MAX_CHANNEL_AB); + (float)KoLabColorSpaceMathsTraits::unitValueAB * values[i], + (float)KoLabColorSpaceMathsTraits::unitValueAB); break; default: - b = qBound((float)KoColorSpaceMathsTraits::min, - (float)KoColorSpaceMathsTraits::unitValue * values[i], - (float)KoColorSpaceMathsTraits::max); + b = qBound((float)KoLabColorSpaceMathsTraits::min, + (float)KoLabColorSpaceMathsTraits::unitValue * values[i], + (float)KoLabColorSpaceMathsTraits::max); break; } c = (channels_type)b; nativeArray(pixel)[i] = c; } } }; -// Float values are not normalised -// XXX: is it really necessary to bind them to these ranges? +// Float values are normalized to [0..100], [-128..+127], [-128..+127] - out of range values are clipped #include #ifdef HAVE_OPENEXR #include struct KoLabF16Traits : public KoLabTraits { - static constexpr float MIN_CHANNEL_L = 0; - static constexpr float MAX_CHANNEL_L = 100; - static constexpr float MIN_CHANNEL_AB = -128; - static constexpr float MAX_CHANNEL_AB = +127; - // Lab has some... particulars - // For instance, float et al. are NOT normalised inline static QString normalisedChannelValueText(const quint8 *pixel, quint32 channelIndex) { - return channelValueText(pixel, channelIndex); + if (channelIndex > parent::channels_nb) return QString("Error"); + channels_type c = nativeArray(pixel)[channelIndex]; + switch (channelIndex) { + case L_pos: + return QString().setNum(100.0 * qBound((qreal)0, + ((qreal)c) / KoLabColorSpaceMathsTraits::unitValueL, + (qreal)KoLabColorSpaceMathsTraits::unitValueL)); + case a_pos: + case b_pos: + return QString().setNum(100.0 * qBound((qreal)0, + (((qreal)c) - KoLabColorSpaceMathsTraits::halfValueAB) / KoLabColorSpaceMathsTraits::unitValueAB, + (qreal)KoLabColorSpaceMathsTraits::unitValueAB)); + case 3: + return QString().setNum(100.0 * qBound((qreal)0, + ((qreal)c) / KoLabColorSpaceMathsTraits::unitValue, + (qreal)KoLabColorSpaceMathsTraits::unitValue)); + default: + return QString("Error"); + } } inline static void normalisedChannelsValue(const quint8 *pixel, QVector &channels) { Q_ASSERT((int)channels.count() == (int)parent::channels_nb); channels_type c; for (uint i = 0; i < parent::channels_nb; i++) { - c = parent::nativeArray(pixel)[i]; - channels[i] = (qreal)c; + c = nativeArray(pixel)[i]; + switch (i) { + case L_pos: + channels[i] = qBound((qreal)0, + ((qreal)c) / KoLabColorSpaceMathsTraits::unitValueL, + (qreal)KoLabColorSpaceMathsTraits::unitValueL); + break; + case a_pos: + case b_pos: + channels[i] = qBound((qreal)0, + (((qreal)c) - KoLabColorSpaceMathsTraits::halfValueAB) / KoLabColorSpaceMathsTraits::unitValueAB, + (qreal)KoLabColorSpaceMathsTraits::unitValueAB); + break; + // As per KoChannelInfo alpha channels are [0..1] + case 3: + default: + channels[i] = qBound((qreal)0, + ((qreal)c) / KoLabColorSpaceMathsTraits::unitValue, + (qreal)KoLabColorSpaceMathsTraits::unitValue); + break; + } } } inline static void fromNormalisedChannelsValue(quint8 *pixel, const QVector &values) { Q_ASSERT((int)values.count() == (int)parent::channels_nb); channels_type c; for (uint i = 0; i < parent::channels_nb; i++) { float b = 0; switch(i) { case L_pos: - b = qBound((float)MIN_CHANNEL_L, - (float)KoColorSpaceMathsTraits::unitValue * values[i], - (float)MAX_CHANNEL_L); + b = qBound((float)0, + (float)KoLabColorSpaceMathsTraits::unitValueL * values[i], + (float)KoLabColorSpaceMathsTraits::unitValueL); break; case a_pos: case b_pos: - b = qBound((float)MIN_CHANNEL_AB, - (float)KoColorSpaceMathsTraits::unitValue * values[i], - (float)MAX_CHANNEL_AB); + b = qBound((float)0, + (float)KoLabColorSpaceMathsTraits::unitValueAB * values[i], + (float)KoLabColorSpaceMathsTraits::unitValueAB); break; case 3: - b = qBound((float)KoColorSpaceMathsTraits::min, - (float)KoColorSpaceMathsTraits::unitValue * values[i], - (float)KoColorSpaceMathsTraits::max); + b = qBound((float)KoLabColorSpaceMathsTraits::min, + (float)KoLabColorSpaceMathsTraits::unitValue * values[i], + (float)KoLabColorSpaceMathsTraits::max); default: break; } c = (channels_type)b; parent::nativeArray(pixel)[i] = c; } } }; #endif struct KoLabF32Traits : public KoLabTraits { - static constexpr float MIN_CHANNEL_L = 0; - static constexpr float MAX_CHANNEL_L = 100; - static constexpr float MIN_CHANNEL_AB = -128; - static constexpr float MAX_CHANNEL_AB = +127; - // Lab has some... particulars inline static QString normalisedChannelValueText(const quint8 *pixel, quint32 channelIndex) { - return channelValueText(pixel, channelIndex); + if (channelIndex > parent::channels_nb) return QString("Error"); + channels_type c = nativeArray(pixel)[channelIndex]; + switch (channelIndex) { + case L_pos: + return QString().setNum(100.0 * qBound((qreal)0, + ((qreal)c) / KoLabColorSpaceMathsTraits::unitValueL, + (qreal)KoLabColorSpaceMathsTraits::unitValueL)); + case a_pos: + case b_pos: + return QString().setNum(100.0 * qBound((qreal)0, + (((qreal)c) - KoLabColorSpaceMathsTraits::halfValueAB) / KoLabColorSpaceMathsTraits::unitValueAB, + (qreal)KoLabColorSpaceMathsTraits::unitValueAB)); + case 3: + return QString().setNum(100.0 * qBound((qreal)0, + ((qreal)c) / KoLabColorSpaceMathsTraits::unitValue, + (qreal)KoLabColorSpaceMathsTraits::unitValue)); + default: + return QString("Error"); + } } inline static void normalisedChannelsValue(const quint8 *pixel, QVector &channels) { Q_ASSERT((int)channels.count() == (int)parent::channels_nb); channels_type c; for (uint i = 0; i < parent::channels_nb; i++) { - c = parent::nativeArray(pixel)[i]; - channels[i] = (qreal)c; + c = nativeArray(pixel)[i]; + switch (i) { + case L_pos: + channels[i] = qBound((qreal)0, + ((qreal)c) / KoLabColorSpaceMathsTraits::unitValueL, + (qreal)KoLabColorSpaceMathsTraits::unitValueL); + break; + case a_pos: + case b_pos: + channels[i] = qBound((qreal)0, + (((qreal)c) - KoLabColorSpaceMathsTraits::halfValueAB) / KoLabColorSpaceMathsTraits::unitValueAB, + (qreal)KoLabColorSpaceMathsTraits::unitValueAB); + break; + // As per KoChannelInfo alpha channels are [0..1] + case 3: + default: + channels[i] = qBound((qreal)0, + ((qreal)c) / KoLabColorSpaceMathsTraits::unitValue, + (qreal)KoLabColorSpaceMathsTraits::unitValue); + break; + } } } inline static void fromNormalisedChannelsValue(quint8 *pixel, const QVector &values) { Q_ASSERT((int)values.count() == (int)parent::channels_nb); channels_type c; for (uint i = 0; i < parent::channels_nb; i++) { float b = 0; switch(i) { case L_pos: - b = qBound((float)MIN_CHANNEL_L, - (float)KoColorSpaceMathsTraits::unitValue * values[i], - (float)MAX_CHANNEL_L); + b = qBound((float)0, + (float)KoLabColorSpaceMathsTraits::unitValueL * values[i], + (float)KoLabColorSpaceMathsTraits::unitValueL); break; case a_pos: case b_pos: - b = qBound((float)MIN_CHANNEL_AB, - (float)KoColorSpaceMathsTraits::unitValue * values[i], - (float)MAX_CHANNEL_AB); + b = qBound((float)0, + (float)KoLabColorSpaceMathsTraits::unitValueAB * values[i], + (float)KoLabColorSpaceMathsTraits::unitValueAB); break; case 3: - b = qBound((float)KoColorSpaceMathsTraits::min, - (float)KoColorSpaceMathsTraits::unitValue * values[i], - (float)KoColorSpaceMathsTraits::max); + b = qBound((float)KoLabColorSpaceMathsTraits::min, + (float)KoLabColorSpaceMathsTraits::unitValue * values[i], + (float)KoLabColorSpaceMathsTraits::max); default: break; } c = (channels_type)b; parent::nativeArray(pixel)[i] = c; } } }; struct KoLabF64Traits : public KoLabTraits { - static constexpr double MIN_CHANNEL_L = 0; - static constexpr double MAX_CHANNEL_L = 100; - static constexpr double MIN_CHANNEL_AB = -128; - static constexpr double MAX_CHANNEL_AB = +127; - // Lab has some... particulars inline static QString normalisedChannelValueText(const quint8 *pixel, quint32 channelIndex) { - return channelValueText(pixel, channelIndex); + if (channelIndex > parent::channels_nb) return QString("Error"); + channels_type c = nativeArray(pixel)[channelIndex]; + switch (channelIndex) { + case L_pos: + return QString().setNum(100.0 * qBound((qreal)0, + ((qreal)c) / KoLabColorSpaceMathsTraits::unitValueL, + (qreal)KoLabColorSpaceMathsTraits::unitValueL)); + case a_pos: + case b_pos: + return QString().setNum(100.0 * qBound((qreal)0, + (((qreal)c) - KoLabColorSpaceMathsTraits::halfValueAB) / KoLabColorSpaceMathsTraits::unitValueAB, + (qreal)KoLabColorSpaceMathsTraits::unitValueAB)); + case 3: + return QString().setNum(100.0 * qBound((qreal)0, + ((qreal)c) / KoLabColorSpaceMathsTraits::unitValue, + (qreal)KoLabColorSpaceMathsTraits::unitValue)); + default: + return QString("Error"); + } } inline static void normalisedChannelsValue(const quint8 *pixel, QVector &channels) { Q_ASSERT((int)channels.count() == (int)parent::channels_nb); channels_type c; for (uint i = 0; i < parent::channels_nb; i++) { - c = parent::nativeArray(pixel)[i]; - channels[i] = (qreal)c; + c = nativeArray(pixel)[i]; + switch (i) { + case L_pos: + channels[i] = qBound((qreal)0, + ((qreal)c) / KoLabColorSpaceMathsTraits::unitValueL, + (qreal)KoLabColorSpaceMathsTraits::unitValueL); + break; + case a_pos: + case b_pos: + channels[i] = qBound((qreal)0, + (((qreal)c) - KoLabColorSpaceMathsTraits::halfValueAB) / KoLabColorSpaceMathsTraits::unitValueAB, + (qreal)KoLabColorSpaceMathsTraits::unitValueAB); + break; + // As per KoChannelInfo alpha channels are [0..1] + case 3: + default: + channels[i] = qBound((qreal)0, + ((qreal)c) / KoLabColorSpaceMathsTraits::unitValue, + (qreal)KoLabColorSpaceMathsTraits::unitValue); + break; + } } } inline static void fromNormalisedChannelsValue(quint8 *pixel, const QVector &values) { Q_ASSERT((int)values.count() == (int)parent::channels_nb); channels_type c; for (uint i = 0; i < parent::channels_nb; i++) { float b = 0; switch(i) { case L_pos: - b = qBound((float)MIN_CHANNEL_L, - (float)KoColorSpaceMathsTraits::unitValue * values[i], - (float)MAX_CHANNEL_L); + b = qBound((float)0, + (float)KoLabColorSpaceMathsTraits::unitValueL * values[i], + (float)KoLabColorSpaceMathsTraits::unitValueL); break; case a_pos: case b_pos: - b = qBound((float)MIN_CHANNEL_AB, - (float)KoColorSpaceMathsTraits::unitValue * values[i], - (float)MAX_CHANNEL_AB); + b = qBound((float)0, + (float)KoLabColorSpaceMathsTraits::unitValueAB * values[i], + (float)KoLabColorSpaceMathsTraits::unitValueAB); break; case 3: - b = qBound((float)KoColorSpaceMathsTraits::min, - (float)KoColorSpaceMathsTraits::unitValue * values[i], - (float)KoColorSpaceMathsTraits::max); + b = qBound((float)KoLabColorSpaceMathsTraits::min, + (float)KoLabColorSpaceMathsTraits::unitValue * values[i], + (float)KoLabColorSpaceMathsTraits::max); default: break; } c = (channels_type)b; parent::nativeArray(pixel)[i] = c; } } }; #endif