mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2025-07-18 20:04:16 -04:00
qoi: write support
As a base I used the reference implementation found on the official site at https://qoiformat.org/ (MIT license). I added a class to convert scan lines in scanlineconverter.cpp. The class takes advantage of the QImage conversion and contrary to what one might expect, with large images it improves performance (compared to converting the whole image) 😄 In progressive mode, for each line, the following conversions (only if needed) are made before saving: 1. If the icc profile is set, the line is converted to sRGB or sRGB Linear. 2. The line is scaled to 8 bits with RGBA order.
This commit is contained in:
committed by
Daniel Novomeský
parent
4bd9d5baec
commit
8dc685df26
100
src/imageformats/scanlineconverter.cpp
Normal file
100
src/imageformats/scanlineconverter.cpp
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2023 Mirco Miranda <mircomir@outlook.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "scanlineconverter_p.h"
|
||||
|
||||
ScanLineConverter::ScanLineConverter(const QImage::Format &targetFormat)
|
||||
: _targetFormat(targetFormat)
|
||||
{
|
||||
}
|
||||
|
||||
ScanLineConverter::ScanLineConverter(const ScanLineConverter &other)
|
||||
: _targetFormat(other._targetFormat)
|
||||
, _colorSpace(other._colorSpace)
|
||||
{
|
||||
}
|
||||
|
||||
ScanLineConverter &ScanLineConverter::operator=(const ScanLineConverter &other)
|
||||
{
|
||||
this->_targetFormat = other._targetFormat;
|
||||
this->_colorSpace = other._colorSpace;
|
||||
return (*this);
|
||||
}
|
||||
|
||||
QImage::Format ScanLineConverter::targetFormat() const
|
||||
{
|
||||
return _targetFormat;
|
||||
}
|
||||
|
||||
void ScanLineConverter::setTargetColorSpace(const QColorSpace &colorSpace)
|
||||
{
|
||||
_colorSpace = colorSpace;
|
||||
}
|
||||
|
||||
QColorSpace ScanLineConverter::targetColorSpace() const
|
||||
{
|
||||
return _colorSpace;
|
||||
}
|
||||
|
||||
const uchar *ScanLineConverter::convertedScanLine(const QImage &image, qint32 y)
|
||||
{
|
||||
auto colorSpaceConversion = isColorSpaceConversionNeeded(image, _colorSpace);
|
||||
if (image.format() == _targetFormat && !colorSpaceConversion) {
|
||||
return image.constScanLine(y);
|
||||
}
|
||||
if (image.width() != _tmpBuffer.width() || image.format() != _tmpBuffer.format()) {
|
||||
_tmpBuffer = QImage(image.width(), 1, image.format());
|
||||
}
|
||||
if (_tmpBuffer.isNull()) {
|
||||
return nullptr;
|
||||
}
|
||||
std::memcpy(_tmpBuffer.bits(), image.constScanLine(y), std::min(_tmpBuffer.bytesPerLine(), image.bytesPerLine()));
|
||||
if (colorSpaceConversion) {
|
||||
_tmpBuffer.setColorSpace(image.colorSpace());
|
||||
_tmpBuffer.convertToColorSpace(_colorSpace);
|
||||
}
|
||||
_convBuffer = _tmpBuffer.convertToFormat(_targetFormat);
|
||||
if (_convBuffer.isNull()) {
|
||||
return nullptr;
|
||||
}
|
||||
return _convBuffer.constBits();
|
||||
}
|
||||
|
||||
qsizetype ScanLineConverter::bytesPerLine() const
|
||||
{
|
||||
if (_convBuffer.isNull()) {
|
||||
return 0;
|
||||
}
|
||||
return _convBuffer.bytesPerLine();
|
||||
}
|
||||
|
||||
bool ScanLineConverter::isColorSpaceConversionNeeded(const QImage &image, const QColorSpace &targetColorSpace) const
|
||||
{
|
||||
if (image.depth() < 24) { // RGB 8 bit or grater only
|
||||
return false;
|
||||
}
|
||||
auto sourceColorSpace = image.colorSpace();
|
||||
if (!sourceColorSpace.isValid() || !targetColorSpace.isValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto stf = sourceColorSpace.transferFunction();
|
||||
auto spr = sourceColorSpace.primaries();
|
||||
auto ttf = targetColorSpace.transferFunction();
|
||||
auto tpr = targetColorSpace.primaries();
|
||||
// clang-format off
|
||||
if (stf == QColorSpace::TransferFunction::Custom ||
|
||||
ttf == QColorSpace::TransferFunction::Custom ||
|
||||
spr == QColorSpace::Primaries::Custom ||
|
||||
tpr == QColorSpace::Primaries::Custom) {
|
||||
return true;
|
||||
}
|
||||
// clang-format on
|
||||
if (stf == ttf && spr == tpr) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
Reference in New Issue
Block a user