File indexing completed on 2024-05-26 04:33:43

0001 /*
0002  *  SPDX-FileCopyrightText: 2006 Cyrille Berger <cberger@cberger.net>
0003  *  SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #ifndef _KIS_TIFF_YCBCR_READER_H_
0009 #define _KIS_TIFF_YCBCR_READER_H_
0010 
0011 #include <cmath>
0012 #include <cstdint>
0013 #include <memory>
0014 
0015 #include <QSharedPointer>
0016 
0017 #include <kis_buffer_stream.h>
0018 #include <kis_global.h>
0019 #include <kis_iterator_ng.h>
0020 #include <kis_paint_device.h>
0021 
0022 #include "kis_tiff_reader.h"
0023 
0024 namespace KisTIFFYCbCr
0025 {
0026 enum Position { POSITION_CENTERED = 1, POSITION_COSITED = 2 };
0027 }
0028 
0029 template<typename T> class KisTIFFYCbCrReader : public KisTIFFReaderBase
0030 {
0031 public:
0032     using type = T;
0033 
0034     /**
0035      * @param hsub horizontal subsampling of Cb and Cr
0036      * @param hsub vertical subsampling of Cb and Cr
0037      */
0038     KisTIFFYCbCrReader(KisPaintDeviceSP device,
0039                        quint32 width,
0040                        quint32 height,
0041                        const std::array<quint8, 5> &poses,
0042                        int32_t alphapos,
0043                        uint16_t sourceDepth,
0044                        uint16_t sampleformat,
0045                        uint16_t nbcolorssamples,
0046                        uint16_t extrasamplescount,
0047                        bool premultipliedAlpha,
0048                        KoColorTransformation *transformProfile,
0049                        QSharedPointer<KisTIFFPostProcessor> postprocessor,
0050                        uint16_t hsub,
0051                        uint16_t vsub)
0052         : KisTIFFReaderBase(device,
0053                             poses,
0054                             alphapos,
0055                             sourceDepth,
0056                             sampleformat,
0057                             nbcolorssamples,
0058                             extrasamplescount,
0059                             premultipliedAlpha,
0060                             transformProfile,
0061                             postprocessor)
0062         , m_hsub(hsub)
0063         , m_vsub(vsub)
0064         , m_imageWidth(width)
0065     {
0066         // Initialize the buffer
0067         if (2 * (m_imageWidth / 2) != m_imageWidth)
0068             m_imageWidth++;
0069         m_bufferWidth = m_imageWidth / m_hsub;
0070         m_imageHeight = height;
0071         if (2 * (m_imageHeight / 2) != m_imageHeight)
0072             m_imageHeight++;
0073         m_bufferHeight = m_imageHeight / m_vsub;
0074         m_bufferCb = std::make_unique<T[]>(m_bufferWidth * m_bufferHeight);
0075         m_bufferCr = std::make_unique<T[]>(m_bufferWidth * m_bufferHeight);
0076     }
0077 
0078     ~KisTIFFYCbCrReader() override = default;
0079 
0080     uint32_t
0081     copyDataToChannels(quint32 x,
0082                        quint32 y,
0083                        quint32 dataWidth,
0084                        QSharedPointer<KisBufferStreamBase> tiffstream) override
0085     {
0086         return copyDataToChannelsImpl(x, y, dataWidth, tiffstream);
0087     }
0088 
0089     void finalize() override
0090     {
0091         return finalizeImpl();
0092     }
0093 
0094 private:
0095     template<typename U = T,
0096              typename std::enable_if<!std::numeric_limits<U>::is_integer,
0097                                      void>::type * = nullptr>
0098     uint32_t
0099     copyDataToChannelsImpl(quint32 x,
0100                            quint32 y,
0101                            quint32 dataWidth,
0102                            QSharedPointer<KisBufferStreamBase> tiffstream)
0103     {
0104         quint32 numcols = dataWidth / m_hsub;
0105         quint32 buffPos = y / m_vsub * m_bufferWidth + x / m_hsub;
0106         for (quint32 index = 0; index < numcols; index++) {
0107             KisHLineIteratorSP it = paintDevice()->createHLineIteratorNG(x + m_hsub * index, y, m_hsub);
0108             for (int vindex = 0; vindex < m_vsub; vindex++) {
0109                 do {
0110                     T *d = reinterpret_cast<T *>(it->rawData());
0111                     d[0] = static_cast<T>(tiffstream->nextValue());
0112                     d[3] = std::numeric_limits<T>::max();
0113                     for (int k = 0; k < nbExtraSamples(); k++) {
0114                         if (k == alphaPos())
0115                             d[3] = static_cast<T>(tiffstream->nextValue());
0116                         else
0117                             tiffstream->nextValue();
0118                     }
0119                 } while (it->nextPixel());
0120                 it->nextRow();
0121             }
0122             m_bufferCb[buffPos] = static_cast<T>(tiffstream->nextValue());
0123             m_bufferCr[buffPos] = static_cast<T>(tiffstream->nextValue());
0124             buffPos++;
0125         }
0126         return m_vsub;
0127     }
0128 
0129     template<typename U = T,
0130              typename std::enable_if<std::numeric_limits<U>::is_integer,
0131                                      void>::type * = nullptr>
0132     uint32_t
0133     copyDataToChannelsImpl(quint32 x,
0134                            quint32 y,
0135                            quint32 dataWidth,
0136                            QSharedPointer<KisBufferStreamBase> tiffstream)
0137     {
0138         quint32 numcols = dataWidth / m_hsub;
0139         double coeff = std::numeric_limits<T>::max() / (double)(std::pow(2.0, this->sourceDepth()) - 1);
0140         //     dbgFile <<" depth expansion coefficient :" << coeff;
0141         //     dbgFile <<" y =" << y;
0142         size_t buffPos = y / m_vsub * m_bufferWidth + x / m_hsub;
0143         for (quint32 index = 0; index < numcols; index++) {
0144             KisHLineIteratorSP it = paintDevice()->createHLineIteratorNG(x + m_hsub * index, y, m_hsub);
0145             for (int vindex = 0; vindex < m_vsub; vindex++) {
0146                 do {
0147                     T *d = reinterpret_cast<T *>(it->rawData());
0148                     d[0] = static_cast<T>(tiffstream->nextValue() * coeff);
0149                     d[3] = std::numeric_limits<T>::max();
0150                     for (int k = 0; k < nbExtraSamples(); k++) {
0151                         if (k == alphaPos())
0152                             d[3] = static_cast<T>(tiffstream->nextValue() * coeff);
0153                         else
0154                             tiffstream->nextValue();
0155                     }
0156                 } while (it->nextPixel());
0157                 it->nextRow();
0158             }
0159             m_bufferCb[buffPos] = static_cast<T>(tiffstream->nextValue() * coeff);
0160             m_bufferCr[buffPos] = static_cast<T>(tiffstream->nextValue() * coeff);
0161             buffPos++;
0162         }
0163         return m_vsub;
0164     }
0165 
0166     template<typename U = T, typename std::enable_if<!std::numeric_limits<U>::is_integer, void>::type * = nullptr> void finalizeImpl()
0167     {
0168         KisHLineIteratorSP it = paintDevice()->createHLineIteratorNG(0, 0, m_imageWidth);
0169         for (size_t y = 0; y < m_imageHeight; y++) {
0170             size_t x = 0;
0171             do {
0172                 T *d = reinterpret_cast<T *>(it->rawData());
0173                 size_t index = x / m_hsub + y / m_vsub * m_bufferWidth;
0174                 d[1] = m_bufferCb[index];
0175                 d[2] = m_bufferCr[index];
0176                 ++x;
0177 
0178                 if (this->hasPremultipliedAlpha()) {
0179                     auto unmultipliedColorsConsistent = [](T *d) { return !(std::abs(d[3]) < std::numeric_limits<T>::epsilon()); };
0180 
0181                     auto checkUnmultipliedColorsConsistent = [this](const T *d) {
0182                         const T alpha = std::abs(d[3]);
0183 
0184                         if (alpha >= static_cast<T>(0.01)) {
0185                             return true;
0186                         } else {
0187                             for (size_t i = 0; i < this->nbColorsSamples(); i++) {
0188                                 if (!qFuzzyCompare(T(d[i] * alpha), d[i])) {
0189                                     return false;
0190                                 }
0191                             }
0192                             return true;
0193                         }
0194                     };
0195 
0196                     if (!unmultipliedColorsConsistent(d)) {
0197                         while (1) {
0198                             T newAlpha = d[3];
0199 
0200                             for (quint8 i = 0; i < this->nbColorsSamples(); i++) {
0201                                 d[i] = std::lroundf(d[i] * newAlpha);
0202                             }
0203 
0204                             d[3] = newAlpha;
0205 
0206                             if (checkUnmultipliedColorsConsistent(d)) {
0207                                 break;
0208                             }
0209 
0210                             newAlpha += std::numeric_limits<T>::epsilon();
0211                         }
0212                     } else {
0213                         const T alpha = d[3];
0214                         for (quint8 i = 0; i < this->nbColorsSamples(); i++) {
0215                             d[i] = std::lroundf(d[i] * alpha);
0216                         }
0217                     }
0218                 }
0219             } while (it->nextPixel());
0220             it->nextRow();
0221         }
0222     }
0223 
0224     template<typename U = T, typename std::enable_if<std::numeric_limits<U>::is_integer, void>::type * = nullptr> void finalizeImpl()
0225     {
0226         KisHLineIteratorSP it = paintDevice()->createHLineIteratorNG(0, 0, m_imageWidth);
0227         for (size_t y = 0; y < m_imageHeight; y++) {
0228             size_t x = 0;
0229             do {
0230                 T *d = reinterpret_cast<T *>(it->rawData());
0231                 size_t index = x / m_hsub + y / m_vsub * m_bufferWidth;
0232                 d[1] = m_bufferCb[index];
0233                 d[2] = m_bufferCr[index];
0234                 ++x;
0235 
0236                 if (this->hasPremultipliedAlpha()) {
0237                     const T alpha = d[3];
0238                     const float factor = alpha == 0 ? 0 : static_cast<float>(std::numeric_limits<T>::max()) / alpha;
0239 
0240                     for (quint8 i = 0; i < this->nbColorsSamples(); i++) {
0241                         d[i] = std::lroundf(d[i] * factor);
0242                     }
0243                 }
0244             } while (it->nextPixel());
0245             it->nextRow();
0246         }
0247     }
0248 
0249 private:
0250     std::unique_ptr<T[]> m_bufferCb;
0251     std::unique_ptr<T[]> m_bufferCr;
0252     quint32 m_bufferWidth, m_bufferHeight;
0253     uint16_t m_hsub;
0254     uint16_t m_vsub;
0255     quint32 m_imageWidth, m_imageHeight;
0256 };
0257 
0258 #endif