1/*
2 SPDX-FileCopyrightText: 2023 Mirco Miranda <mircomir@outlook.com>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "scanlineconverter_p.h"
8#include <cstring>
9
10ScanLineConverter::ScanLineConverter(const QImage::Format &targetFormat)
11 : _targetFormat(targetFormat)
12{
13}
14
15ScanLineConverter::ScanLineConverter(const ScanLineConverter &other)
16 : _targetFormat(other._targetFormat)
17 , _colorSpace(other._colorSpace)
18 , _defaultColorSpace(other._defaultColorSpace)
19{
20}
21
22ScanLineConverter &ScanLineConverter::operator=(const ScanLineConverter &other)
23{
24 _targetFormat = other._targetFormat;
25 _colorSpace = other._colorSpace;
26 _defaultColorSpace = other._defaultColorSpace;
27 return (*this);
28}
29
30QImage::Format ScanLineConverter::targetFormat() const
31{
32 return _targetFormat;
33}
34
35void ScanLineConverter::setTargetColorSpace(const QColorSpace &colorSpace)
36{
37 _colorSpace = colorSpace;
38}
39
40QColorSpace ScanLineConverter::targetColorSpace() const
41{
42 return _colorSpace;
43}
44
45void ScanLineConverter::setDefaultSourceColorSpace(const QColorSpace &colorSpace)
46{
47 _defaultColorSpace = colorSpace;
48}
49
50QColorSpace ScanLineConverter::defaultSourceColorSpace() const
51{
52 return _defaultColorSpace;
53}
54
55const uchar *ScanLineConverter::convertedScanLine(const QImage &image, qint32 y)
56{
57 auto colorSpaceConversion = isColorSpaceConversionNeeded(image);
58 if (image.format() == _targetFormat && !colorSpaceConversion) {
59 return image.constScanLine(y);
60 }
61 if (image.width() != _tmpBuffer.width() || image.format() != _tmpBuffer.format()) {
62 _tmpBuffer = QImage(image.width(), 1, image.format());
63 _tmpBuffer.setColorTable(image.colorTable());
64 }
65 if (_tmpBuffer.isNull()) {
66 return nullptr;
67 }
68 std::memcpy(dest: _tmpBuffer.bits(), src: image.constScanLine(y), n: std::min(a: _tmpBuffer.bytesPerLine(), b: image.bytesPerLine()));
69 auto tmp = _tmpBuffer;
70 if (colorSpaceConversion) {
71 auto cs = image.colorSpace();
72 if (!cs.isValid()) {
73 cs = _defaultColorSpace;
74 }
75 if (tmp.depth() < 24) {
76 tmp.convertTo(f: tmp.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32);
77 }
78 tmp.setColorSpace(cs);
79 tmp.convertToColorSpace(_colorSpace);
80 }
81
82 /*
83 * Work Around for wrong RGBA64 -> 16FPx4/32FPx4 conversion on Intel architecture.
84 * Luckily convertTo() works fine with 16FPx4 images so I can use it instead convertToFormat().
85 * See also: https://bugreports.qt.io/browse/QTBUG-120614
86 */
87 tmp.convertTo(f: _targetFormat);
88 _convBuffer = tmp;
89
90 if (_convBuffer.isNull()) {
91 return nullptr;
92 }
93 return _convBuffer.constBits();
94}
95
96qsizetype ScanLineConverter::bytesPerLine() const
97{
98 if (_convBuffer.isNull()) {
99 return 0;
100 }
101 return _convBuffer.bytesPerLine();
102}
103
104bool ScanLineConverter::isColorSpaceConversionNeeded(const QImage &image, const QColorSpace &targetColorSpace, const QColorSpace &defaultColorSpace)
105{
106 auto sourceColorSpace = image.colorSpace();
107 if (!sourceColorSpace.isValid()) {
108 sourceColorSpace = defaultColorSpace;
109 }
110 if (!sourceColorSpace.isValid() || !targetColorSpace.isValid()) {
111 return false;
112 }
113
114 auto stf = sourceColorSpace.transferFunction();
115 auto spr = sourceColorSpace.primaries();
116 auto ttf = targetColorSpace.transferFunction();
117 auto tpr = targetColorSpace.primaries();
118 // clang-format off
119 if (stf == QColorSpace::TransferFunction::Custom ||
120 ttf == QColorSpace::TransferFunction::Custom ||
121 spr == QColorSpace::Primaries::Custom ||
122 tpr == QColorSpace::Primaries::Custom) {
123 return true;
124 }
125 // clang-format on
126 if (stf == ttf && spr == tpr) {
127 return false;
128 }
129 return true;
130}
131

source code of kimageformats/src/imageformats/scanlineconverter.cpp