Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_tiff_reader.h
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2006 Cyrille Berger <cberger@cberger.net>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7#ifndef _KIS_TIFF_READER_H_
8#define _KIS_TIFF_READER_H_
9
10#include <array>
11#include <cmath>
12#include <cstdint>
13#include <cstdio>
14#include <cstring>
15#include <limits>
16#include <tiffio.h>
17#include <type_traits>
18#include <utility>
19
20#include <kis_buffer_stream.h>
21#include <kis_debug.h>
22#include <kis_global.h>
23#include <kis_iterator_ng.h>
24#include <kis_paint_device.h>
25#include <kis_types.h>
26
27#include <KoConfig.h>
28#ifdef HAVE_OPENEXR
29#include <half.h>
30#endif // HAVE_OPENEXR
31
33
35{
36public:
37 KisTIFFPostProcessor(uint32_t nbcolorssamples)
38 : m_nbcolorssamples(nbcolorssamples)
39 {
40 }
41 virtual ~KisTIFFPostProcessor() = default;
42
43public:
44 virtual void postProcess(void *) const = 0;
45
46protected:
47 inline uint32_t nbColorsSamples() const
48 {
49 return m_nbcolorssamples;
50 }
51
52private:
54};
55
56template<typename T> class KisTIFFPostProcessorDummy : public KisTIFFPostProcessor
57{
58public:
59 KisTIFFPostProcessorDummy(uint32_t nbcolorssamples)
60 : KisTIFFPostProcessor(nbcolorssamples)
61 {
62 }
63 ~KisTIFFPostProcessorDummy() override = default;
64
65 void postProcess(void *) const override
66 {
67 }
68};
69
70template<typename T> class KisTIFFPostProcessorInvert : public KisTIFFPostProcessor
71{
72public:
73 KisTIFFPostProcessorInvert(uint32_t nbcolorssamples)
74 : KisTIFFPostProcessor(nbcolorssamples)
75 {
76 }
77 ~KisTIFFPostProcessorInvert() override = default;
78
79public:
80 void postProcess(void *data) const override
81 {
82 postProcessImpl(reinterpret_cast<T *>(data));
83 }
84
85private:
86 template<typename U = T,
87 typename std::enable_if<std::numeric_limits<U>::is_signed,
88 void>::type * = nullptr>
89 inline void postProcessImpl(T *data) const
90 {
91 for (uint32_t i = 0; i < this->nbColorsSamples(); i++) {
92 data[i] = -data[i];
93 }
94 }
95
96 template<typename U = T,
97 typename std::enable_if<!std::numeric_limits<U>::is_signed,
98 void>::type * = nullptr>
99 inline void postProcessImpl(T *data) const
100 {
101 for (uint32_t i = 0; i < this->nbColorsSamples(); i++) {
102 data[i] = std::numeric_limits<T>::max() - data[i];
103 }
104 }
105};
106
108{
109public:
110 KisTIFFPostProcessorCIELABtoICCLAB(uint32_t nbcolorssamples)
111 : KisTIFFPostProcessor(nbcolorssamples)
112 {
113 }
115
116public:
117 void postProcess(void *data) const override
118 {
119 postProcessImpl(reinterpret_cast<T *>(data));
120 }
121
122private:
123 template<typename U = T,
124 typename std::enable_if<!std::numeric_limits<U>::is_integer,
125 void>::type * = nullptr>
126 inline void postProcessImpl(T *data) const
127 {
128 for (uint32_t i = 1; i < this->nbColorsSamples(); i++) {
129 data[i] += 128.0f;
130 }
131 }
132
133 template<typename U = T,
134 typename std::enable_if<std::numeric_limits<U>::is_integer,
135 void>::type * = nullptr>
136 inline void postProcessImpl(T *data) const
137 {
138 for (uint32_t i = 1; i < this->nbColorsSamples(); i++) {
139 data[i] += std::numeric_limits<T>::max() / 2;
140 }
141 }
142};
143
145{
146public:
148 const std::array<quint8, 5> &poses,
149 int32_t alphapos,
150 uint16_t sourceDepth,
151 uint16_t sample_format,
152 uint16_t nbcolorssamples,
153 uint16_t extrasamplescount,
154 bool premultipliedAlpha,
155 KoColorTransformation *transformProfile,
157 : m_device(device)
158 , m_alphapos(alphapos)
160 , m_sample_format(sample_format)
161 , m_nbcolorssamples(nbcolorssamples)
162 , m_nbextrasamples(extrasamplescount)
163 , m_premultipliedAlpha(premultipliedAlpha)
164 , m_poses(poses)
165 , m_transformProfile(transformProfile)
166 , mpostProcessImpl(std::move(postprocessor))
167 {
168
169 }
170 virtual ~KisTIFFReaderBase() = default;
171
172public:
182 virtual uint32_t
184 quint32 y,
185 quint32 dataWidth,
190 virtual void finalize()
191 {
192 }
193
194protected:
196 {
197 return m_device;
198 }
199
200 inline qint32 alphaPos() const
201 {
202 return m_alphapos;
203 }
204
205 inline quint16 sourceDepth() const
206 {
207 return m_sourceDepth;
208 }
209 inline uint16_t sampleFormat() const
210 {
211 return m_sample_format;
212 }
213
214 inline quint16 nbColorsSamples() const
215 {
216 return m_nbcolorssamples;
217 }
218
219 inline quint16 nbExtraSamples() const
220 {
221 return m_nbextrasamples;
222 }
223
224 inline bool hasPremultipliedAlpha() const
225 {
227 }
228
229 inline const std::array<quint8, 5> &poses() const
230 {
231 return m_poses;
232 }
233
235 {
236 return m_transformProfile;
237 }
238
240 {
241 return mpostProcessImpl.get();
242 }
243
244private:
252 std::array<quint8, 5> m_poses;
255};
256
257template<typename T> class KisTIFFReaderTarget : public KisTIFFReaderBase
258{
259public:
260 using type = T;
261
263 const std::array<quint8, 5> &poses,
264 int32_t alphapos,
265 uint16_t sourceDepth,
266 uint16_t sample_format,
267 uint16_t nbcolorssamples,
268 uint16_t extrasamplescount,
269 bool premultipliedAlpha,
270 KoColorTransformation *transformProfile,
272 T alphaValue)
273 : KisTIFFReaderBase(device,
274 poses,
275 alphapos,
277 sample_format,
278 nbcolorssamples,
279 extrasamplescount,
280 premultipliedAlpha,
281 transformProfile,
282 postprocessor)
283 , m_alphaValue(alphaValue)
284 {
285 }
286public:
287 uint32_t
289 quint32 y,
290 quint32 dataWidth,
291 QSharedPointer<KisBufferStreamBase> tiffstream) override
292 {
293 return _copyDataToChannels(x, y, dataWidth, tiffstream);
294 }
295
296private:
297 template<typename U = T,
298 typename std::enable_if<!std::numeric_limits<U>::is_integer,
299 void>::type * = nullptr>
300 uint32_t _copyDataToChannels(quint32 x,
301 quint32 y,
302 quint32 dataWidth,
304 {
305 KisHLineIteratorSP it = this->paintDevice()->createHLineIteratorNG(x, y, dataWidth);
306 do {
307 T *d = reinterpret_cast<T *>(it->rawData());
308 quint8 i = 0;
309 for (i = 0; i < this->nbColorsSamples(); i++) {
310 // XXX: for half this should use the bit constructor (plus downcast to uint16_t) (same as in the rest accesses)
311 const uint32_t v = tiffstream->nextValue();
312 std::memcpy(&d[this->poses()[i]], &v, sizeof(T));
313 }
314 this->postProcessor()->postProcess(d);
315 if (this->transform()) {
316 this->transform()->transform(reinterpret_cast<quint8 *>(d), reinterpret_cast<quint8 *>(d), 1);
317 }
318 d[this->poses()[i]] = m_alphaValue;
319 for (quint8 k = 0; k < this->nbExtraSamples(); k++) {
320 if (k == this->alphaPos()) {
321 const uint32_t v = tiffstream->nextValue();
322 std::memcpy(&d[this->poses()[i]], &v, sizeof(T));
323 } else {
324 (void)tiffstream->nextValue();
325 }
326 }
327
328 if (this->hasPremultipliedAlpha()) {
329 auto unmultipliedColorsConsistent = [this, i](T *d) { return !(std::abs(d[this->poses()[i]]) < std::numeric_limits<T>::epsilon()); };
330
331 auto checkUnmultipliedColorsConsistent = [this, i](const T *d) {
332 const T alpha = std::abs(d[this->poses()[i]]);
333
334 if (alpha >= static_cast<T>(0.01)) {
335 return true;
336 } else {
337 for (size_t i = 0; i < this->nbColorsSamples(); i++) {
338 if (!qFuzzyCompare(T(d[i] * alpha), d[i])) {
339 return false;
340 }
341 }
342 return true;
343 }
344 };
345
346 if (!unmultipliedColorsConsistent(d)) {
347 while (true) {
348 T newAlpha = d[this->poses()[i]];
349
350 for (quint8 i = 0; i < this->nbColorsSamples(); i++) {
351 d[i] = std::lroundf(d[i] * newAlpha);
352 }
353
354 d[this->poses()[i]] = newAlpha;
355
356 if (checkUnmultipliedColorsConsistent(d)) {
357 break;
358 }
359
360 newAlpha += std::numeric_limits<T>::epsilon();
361 }
362 } else {
363 const T alpha = d[this->poses()[i]];
364 for (quint8 i = 0; i < this->nbColorsSamples(); i++) {
365 d[i] = std::lroundf(d[i] * alpha);
366 }
367 }
368 }
369 } while (it->nextPixel());
370 return 1;
371 }
372
373 template<typename U = T,
374 typename std::enable_if<std::numeric_limits<U>::is_integer,
375 void>::type * = nullptr>
376 uint32_t _copyDataToChannels(quint32 x,
377 quint32 y,
378 quint32 dataWidth,
380 {
381 KisHLineIteratorSP it = this->paintDevice()->createHLineIteratorNG(x, y, dataWidth);
382 const double coeff = std::numeric_limits<T>::max() / static_cast<double>(std::pow(2.0, this->sourceDepth()) - 1);
383 const bool no_coeff = !std::is_same<T, uint8_t>::value && this->sourceDepth() == sizeof(T) * CHAR_BIT;
384 // dbgFile <<" depth expansion coefficient :" << coeff;
385 do {
386 T *d = reinterpret_cast<T *>(it->rawData());
387 quint8 i;
388 for (i = 0; i < this->nbColorsSamples(); i++) {
389 if (sampleFormat() == SAMPLEFORMAT_INT) {
390 T value;
391 const typename std::make_signed<T>::type v =
392 static_cast<typename std::make_signed<T>::type>(
393 tiffstream->nextValue());
394 value = v + (std::numeric_limits<T>::max() / 2) + 1;
395 if (no_coeff) {
396 d[this->poses()[i]] = static_cast<T>(value);
397 } else {
398 d[this->poses()[i]] = static_cast<T>(value * coeff);
399 }
400 } else {
401 if (no_coeff) {
402 d[this->poses()[i]] = static_cast<T>(tiffstream->nextValue());
403 } else {
404 d[this->poses()[i]] = static_cast<T>(tiffstream->nextValue() * coeff);
405 }
406 }
407 }
408 this->postProcessor()->postProcess(d);
409 if (this->transform()) {
410 this->transform()->transform(reinterpret_cast<quint8 *>(d), reinterpret_cast<quint8 *>(d), 1);
411 }
412 d[this->poses()[i]] = m_alphaValue;
413 for (quint8 k = 0; k < this->nbExtraSamples(); k++) {
414 if (k == this->alphaPos()) {
415 if (sampleFormat() == SAMPLEFORMAT_INT) {
416 T value;
417 const typename std::make_signed<T>::type v =
418 static_cast<typename std::make_signed<T>::type>(
419 tiffstream->nextValue());
420 value = v + (std::numeric_limits<T>::max() / 2) + 1;
421 if (no_coeff) {
422 d[this->poses()[i]] = static_cast<T>(value);
423 } else {
424 d[this->poses()[i]] = static_cast<T>(value * coeff);
425 }
426 } else {
427 if (no_coeff) {
428 d[this->poses()[i]] = static_cast<T>(tiffstream->nextValue());
429 } else {
430 d[this->poses()[i]] = static_cast<T>(tiffstream->nextValue() * coeff);
431 }
432 }
433 } else {
434 tiffstream->nextValue();
435 }
436 }
437
438 if (hasPremultipliedAlpha()) {
439 const T alpha = d[poses()[i]];
440 const float factor = alpha == 0 ? 0 : static_cast<float>(std::numeric_limits<T>::max()) / alpha;
441
442 for (quint8 i = 0; i < nbColorsSamples(); i++) {
443 d[i] = std::lroundf(d[i] * factor);
444 }
445 }
446 } while (it->nextPixel());
447 return 1;
448 }
449
450private:
452};
453
455{
456public:
457 using type = uint16_t;
458
460 uint16_t *red,
461 uint16_t *green,
462 uint16_t *blue,
463 const std::array<quint8, 5> &poses,
464 int32_t alphapos,
465 uint16_t sourceDepth,
466 uint16_t sample_format,
467 uint16_t nbcolorssamples,
468 bool premultipliedAlpha,
469 uint8_t extrasamplescount,
470 KoColorTransformation *transformProfile,
472 : KisTIFFReaderBase(device,
473 poses,
474 alphapos,
476 sample_format,
477 nbcolorssamples,
478 extrasamplescount,
479 premultipliedAlpha,
480 transformProfile,
481 postprocessor)
482 , m_red(red)
483 , m_green(green)
484 , m_blue(blue)
485 {
486 }
487public:
488 uint32_t
490 quint32 y,
491 quint32 dataWidth,
492 QSharedPointer<KisBufferStreamBase> tiffstream) override
493 {
494 KisHLineIteratorSP it = paintDevice()->createHLineIteratorNG(static_cast<int>(x), static_cast<int>(y), static_cast<int>(dataWidth));
495 do {
497 reinterpret_cast<KisTIFFReaderFromPalette::type *>(
498 it->rawData());
499 uint32_t index = tiffstream->nextValue();
500 d[2] = m_red[index];
501 d[1] = m_green[index];
502 d[0] = m_blue[index];
503 d[3] = std::numeric_limits<KisTIFFReaderFromPalette::type>::max();
504
505 } while (it->nextPixel());
506 return 1;
507 }
508
509private:
510 uint16_t *m_red, *m_green, *m_blue;
511};
512
513#endif
float value(const T *src, size_t ch)
qreal v
KisHLineIteratorSP createHLineIteratorNG(qint32 x, qint32 y, qint32 w)
KisTIFFPostProcessorCIELABtoICCLAB(uint32_t nbcolorssamples)
~KisTIFFPostProcessorCIELABtoICCLAB() override=default
void postProcess(void *data) const override
void postProcess(void *) const override
~KisTIFFPostProcessorDummy() override=default
KisTIFFPostProcessorDummy(uint32_t nbcolorssamples)
~KisTIFFPostProcessorInvert() override=default
void postProcessImpl(T *data) const
KisTIFFPostProcessorInvert(uint32_t nbcolorssamples)
void postProcess(void *data) const override
uint32_t nbColorsSamples() const
virtual void postProcess(void *) const =0
KisTIFFPostProcessor(uint32_t nbcolorssamples)
virtual ~KisTIFFPostProcessor()=default
QSharedPointer< KisTIFFPostProcessor > mpostProcessImpl
const std::array< quint8, 5 > & poses() const
qint32 alphaPos() const
KisPaintDeviceSP m_device
virtual void finalize()
KoColorTransformation * m_transformProfile
std::array< quint8, 5 > m_poses
quint16 nbExtraSamples() const
KoColorTransformation * transform() const
quint16 nbColorsSamples() const
KisPaintDeviceSP paintDevice() const
uint16_t sampleFormat() const
virtual ~KisTIFFReaderBase()=default
KisTIFFReaderBase(KisPaintDeviceSP device, const std::array< quint8, 5 > &poses, int32_t alphapos, uint16_t sourceDepth, uint16_t sample_format, uint16_t nbcolorssamples, uint16_t extrasamplescount, bool premultipliedAlpha, KoColorTransformation *transformProfile, QSharedPointer< KisTIFFPostProcessor > postprocessor)
bool hasPremultipliedAlpha() const
virtual uint32_t copyDataToChannels(quint32 x, quint32 y, quint32 dataWidth, QSharedPointer< KisBufferStreamBase > tiffstream)=0
const KisTIFFPostProcessor * postProcessor() const
quint16 sourceDepth() const
uint32_t copyDataToChannels(quint32 x, quint32 y, quint32 dataWidth, QSharedPointer< KisBufferStreamBase > tiffstream) override
KisTIFFReaderFromPalette(KisPaintDeviceSP device, uint16_t *red, uint16_t *green, uint16_t *blue, const std::array< quint8, 5 > &poses, int32_t alphapos, uint16_t sourceDepth, uint16_t sample_format, uint16_t nbcolorssamples, bool premultipliedAlpha, uint8_t extrasamplescount, KoColorTransformation *transformProfile, QSharedPointer< KisTIFFPostProcessor > postprocessor)
KisTIFFReaderTarget(KisPaintDeviceSP device, const std::array< quint8, 5 > &poses, int32_t alphapos, uint16_t sourceDepth, uint16_t sample_format, uint16_t nbcolorssamples, uint16_t extrasamplescount, bool premultipliedAlpha, KoColorTransformation *transformProfile, QSharedPointer< KisTIFFPostProcessor > postprocessor, T alphaValue)
uint32_t _copyDataToChannels(quint32 x, quint32 y, quint32 dataWidth, QSharedPointer< KisBufferStreamBase > tiffstream)
uint32_t copyDataToChannels(quint32 x, quint32 y, quint32 dataWidth, QSharedPointer< KisBufferStreamBase > tiffstream) override
virtual void transform(const quint8 *src, quint8 *dst, qint32 nPixels) const =0
static bool qFuzzyCompare(half p1, half p2)
typedef void(QOPENGLF_APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC)(GLuint buffer)