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