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() < 8 && cs.colorModel() == QColorSpace::ColorModel::Gray) {
76 tmp.convertTo(f: QImage::Format_Grayscale8);
77 }
78 else if (tmp.depth() < 24 && cs.colorModel() == QColorSpace::ColorModel::Rgb) {
79 tmp.convertTo(f: tmp.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32);
80 }
81 tmp.setColorSpace(cs);
82 tmp.convertToColorSpace(colorSpace: _colorSpace);
83 }
84
85 /*
86 * Work Around for wrong RGBA64 -> 16FPx4/32FPx4 conversion on Intel architecture.
87 * Luckily convertTo() works fine with 16FPx4 images so I can use it instead convertToFormat().
88 * See also: https://bugreports.qt.io/browse/QTBUG-120614
89 */
90 tmp.convertTo(f: _targetFormat);
91 _convBuffer = tmp;
92
93 if (_convBuffer.isNull()) {
94 return nullptr;
95 }
96 return _convBuffer.constBits();
97}
98
99qsizetype ScanLineConverter::bytesPerLine() const
100{
101 if (_convBuffer.isNull()) {
102 return 0;
103 }
104 return _convBuffer.bytesPerLine();
105}
106
107bool ScanLineConverter::isColorSpaceConversionNeeded(const QImage &image, const QColorSpace &targetColorSpace, const QColorSpace &defaultColorSpace)
108{
109 auto sourceColorSpace = image.colorSpace();
110 if (!sourceColorSpace.isValid()) {
111 sourceColorSpace = defaultColorSpace;
112 }
113 if (!sourceColorSpace.isValid() || !targetColorSpace.isValid()) {
114 return false;
115 }
116
117 auto stf = sourceColorSpace.transferFunction();
118 auto spr = sourceColorSpace.primaries();
119 auto ttf = targetColorSpace.transferFunction();
120 auto tpr = targetColorSpace.primaries();
121 // clang-format off
122 if (stf == QColorSpace::TransferFunction::Custom ||
123 ttf == QColorSpace::TransferFunction::Custom ||
124 spr == QColorSpace::Primaries::Custom ||
125 tpr == QColorSpace::Primaries::Custom) {
126 return true;
127 }
128 // clang-format on
129 if (stf == ttf && spr == tpr) {
130 return false;
131 }
132 return true;
133}
134

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