[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [krita/krita/3.1] libs/pigment: D3525: Fix floating point Lab
From:       Boudewijn Rempt <boud () valdyas ! org>
Date:       2016-12-07 9:49:55
Message-ID: E1cEYrL-0003kr-1y () code ! kde ! org
[Download RAW message or body]

Git commit 774e5b763f0c48e4185d9d47619c3e89f58a1a70 by Boudewijn Rempt.
Committed on 07/12/2016 at 09:48.
Pushed by rempt into branch 'krita/3.1'.

D3525: Fix floating point Lab

Even for floating point lab the unit value should be
 0-100, -128-127, -128-127

M  +291  -0    libs/pigment/KoLabColorSpaceTraits.h

https://commits.kde.org/krita/774e5b763f0c48e4185d9d47619c3e89f58a1a70

diff --git a/libs/pigment/KoLabColorSpaceTraits.h b/libs/pigment/KoLabColorSpaceTraits.h
index 1cc4ce0a6cd..43eb0f72a7b 100644
--- a/libs/pigment/KoLabColorSpaceTraits.h
+++ b/libs/pigment/KoLabColorSpaceTraits.h
@@ -1,5 +1,6 @@
 /*
  *  Copyright (c) 2006-2007 Cyrille Berger <cberger@cberger.net>
+ *  Copyright (c) 2016 L. E. Segovia <leo.segovia@siggraph.org>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -84,26 +85,316 @@ struct KoLabTraits : public KoColorSpaceTrait<_channels_type_, 4, 3> {
     }
 };
 
+//For quint* values must range from 0 to 1 - see KoColorSpaceMaths<double, quint*>
 
 struct KoLabU8Traits : public KoLabTraits<quint8> {
+
+    static const quint32 MAX_CHANNEL_L = 100;
+    static const quint32 MAX_CHANNEL_AB = 255;
+    static const quint32 CHANNEL_AB_ZERO_OFFSET = 128;
+
+    inline static void normalisedChannelsValue(const quint8 *pixel, QVector<float> &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;
+                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;
+                break;
+            case 3:
+                channels[i] = ((qreal)c) / UINT16_MAX;
+                break;
+            default:
+                channels[i] = ((qreal)c) / KoColorSpaceMathsTraits<channels_type>::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);
+        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));
+        case 3:
+            return QString().setNum(100.0 * ((qreal)c) / UINT16_MAX);
+        default:
+            return QString("Error");
+        }
+    }
+
+    inline static void fromNormalisedChannelsValue(quint8 *pixel, const QVector<float> &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);
+                break;
+            case a_pos:
+            case b_pos:
+                b = qBound((float)0,
+                           (float)MAX_CHANNEL_AB * values[i],
+                           (float)MAX_CHANNEL_AB);
+                break;
+            default:
+                b = qBound((float)KoColorSpaceMathsTraits<channels_type>::min,
+                           (float)KoColorSpaceMathsTraits<channels_type>::unitValue * values[i],
+                           (float)KoColorSpaceMathsTraits<channels_type>::max);
+                break;
+            }
+            c = (channels_type)b;
+            nativeArray(pixel)[i] = c;
+        }
+    }
 };
 
 struct KoLabU16Traits : public KoLabTraits<quint16> {
+    // 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<float> &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;
+                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;
+                break;
+            case 3:
+                channels[i] = ((qreal)c) / UINT16_MAX;
+                break;
+            default:
+                channels[i] = ((qreal)c) / KoColorSpaceMathsTraits<channels_type>::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);
+        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));
+        case 3:
+            return QString().setNum(100.0 * ((qreal)c) / UINT16_MAX);
+        default:
+            return QString("Error");
+        }
+    }
+
+    inline static void fromNormalisedChannelsValue(quint8 *pixel, const QVector<float> &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);
+                break;
+            case a_pos:
+            case b_pos:
+                b = qBound((float)0,
+                           (float)MAX_CHANNEL_AB * values[i],
+                           (float)MAX_CHANNEL_AB);
+                break;
+            default:
+                b = qBound((float)KoColorSpaceMathsTraits<channels_type>::min,
+                           (float)KoColorSpaceMathsTraits<channels_type>::unitValue * values[i],
+                           (float)KoColorSpaceMathsTraits<channels_type>::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?
+
 #include <KoConfig.h>
 #ifdef HAVE_OPENEXR
 #include <half.h>
 
 struct KoLabF16Traits : public KoLabTraits<half> {
+    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);
+    }
+    inline static void normalisedChannelsValue(const quint8 *pixel, QVector<float> &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;
+        }
+    }
+    inline static void fromNormalisedChannelsValue(quint8 *pixel, const QVector<float> &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<half>::unitValue * values[i],
+                           (float)MAX_CHANNEL_L);
+                break;
+            case a_pos:
+            case b_pos:
+                b = qBound((float)MIN_CHANNEL_AB,
+                           (float)KoColorSpaceMathsTraits<half>::unitValue * values[i],
+                           (float)MAX_CHANNEL_AB);
+                break;
+            case 3:
+                b = qBound((float)KoColorSpaceMathsTraits<half>::min,
+                           (float)KoColorSpaceMathsTraits<half>::unitValue * values[i],
+                           (float)KoColorSpaceMathsTraits<half>::max);
+            default:
+                break;
+            }
+            c = (channels_type)b;
+            parent::nativeArray(pixel)[i] = c;
+        }
+    }
 };
 
 #endif
 
 struct KoLabF32Traits : public KoLabTraits<float> {
+    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);
+    }
+    inline static void normalisedChannelsValue(const quint8 *pixel, QVector<float> &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;
+        }
+    }
+    inline static void fromNormalisedChannelsValue(quint8 *pixel, const QVector<float> &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<float>::unitValue * values[i],
+                           (float)MAX_CHANNEL_L);
+                break;
+            case a_pos:
+            case b_pos:
+                b = qBound((float)MIN_CHANNEL_AB,
+                           (float)KoColorSpaceMathsTraits<float>::unitValue * values[i],
+                           (float)MAX_CHANNEL_AB);
+                break;
+            case 3:
+                b = qBound((float)KoColorSpaceMathsTraits<float>::min,
+                           (float)KoColorSpaceMathsTraits<float>::unitValue * values[i],
+                           (float)KoColorSpaceMathsTraits<float>::max);
+            default:
+                break;
+            }
+            c = (channels_type)b;
+            parent::nativeArray(pixel)[i] = c;
+        }
+    }
 };
 
 struct KoLabF64Traits : public KoLabTraits<double> {
+    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);
+    }
+    inline static void normalisedChannelsValue(const quint8 *pixel, QVector<float> &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;
+        }
+    }
+    inline static void fromNormalisedChannelsValue(quint8 *pixel, const QVector<float> &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<double>::unitValue * values[i],
+                           (float)MAX_CHANNEL_L);
+                break;
+            case a_pos:
+            case b_pos:
+                b = qBound((float)MIN_CHANNEL_AB,
+                           (float)KoColorSpaceMathsTraits<double>::unitValue * values[i],
+                           (float)MAX_CHANNEL_AB);
+                break;
+            case 3:
+                b = qBound((float)KoColorSpaceMathsTraits<double>::min,
+                           (float)KoColorSpaceMathsTraits<double>::unitValue * values[i],
+                           (float)KoColorSpaceMathsTraits<double>::max);
+            default:
+                break;
+            }
+            c = (channels_type)b;
+            parent::nativeArray(pixel)[i] = c;
+        }
+    }
 };
 
 #endif
[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic