Compare commits
14 Commits
v5.101.0
...
v5.106.0-r
Author | SHA1 | Date | |
---|---|---|---|
14770318a3 | |||
9b1fafe29b | |||
fa673b5df8 | |||
e96b43aef5 | |||
64f3303ef0 | |||
63056c52f9 | |||
2997f7ae8d | |||
0b4741f4b7 | |||
bc52c03981 | |||
c1c57d9a11 | |||
4c6d2b92b6 | |||
05bd9397b3 | |||
f4ca3f6783 | |||
a30f043e5d |
@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.16)
|
|||||||
project(KImageFormats)
|
project(KImageFormats)
|
||||||
|
|
||||||
include(FeatureSummary)
|
include(FeatureSummary)
|
||||||
find_package(ECM 5.101.0 NO_MODULE)
|
find_package(ECM 5.106.0 NO_MODULE)
|
||||||
set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://commits.kde.org/extra-cmake-modules")
|
set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://commits.kde.org/extra-cmake-modules")
|
||||||
feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 983 B After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 94 KiB |
BIN
autotests/read/psd/birthday.tif
Normal file
Before Width: | Height: | Size: 189 KiB After Width: | Height: | Size: 189 KiB |
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 114 KiB |
@ -159,17 +159,22 @@ int main(int argc, char **argv)
|
|||||||
QTextStream(stdout) << "* Run on RANDOM ACCESS device\n";
|
QTextStream(stdout) << "* Run on RANDOM ACCESS device\n";
|
||||||
}
|
}
|
||||||
for (const QFileInfo &fi : lstImgDir) {
|
for (const QFileInfo &fi : lstImgDir) {
|
||||||
if (!fi.suffix().compare("png", Qt::CaseInsensitive)) {
|
if (!fi.suffix().compare("png", Qt::CaseInsensitive) || !fi.suffix().compare("tif", Qt::CaseInsensitive)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int suffixPos = fi.filePath().count() - suffix.count();
|
int suffixPos = fi.filePath().count() - suffix.count();
|
||||||
QString inputfile = fi.filePath();
|
QString inputfile = fi.filePath();
|
||||||
QString expfile = fi.filePath().replace(suffixPos, suffix.count(), QStringLiteral("png"));
|
QString fmt = QStringLiteral("png");
|
||||||
|
QString expfile = fi.filePath().replace(suffixPos, suffix.count(), fmt);
|
||||||
|
if (!QFile::exists(expfile)) { // try with tiff
|
||||||
|
fmt = QStringLiteral("tif");
|
||||||
|
expfile = fi.filePath().replace(suffixPos, suffix.count(), fmt);
|
||||||
|
}
|
||||||
QString expfilename = QFileInfo(expfile).fileName();
|
QString expfilename = QFileInfo(expfile).fileName();
|
||||||
|
|
||||||
std::unique_ptr<QIODevice> inputDevice(seq ? new SequentialFile(inputfile) : new QFile(inputfile));
|
std::unique_ptr<QIODevice> inputDevice(seq ? new SequentialFile(inputfile) : new QFile(inputfile));
|
||||||
QImageReader inputReader(inputDevice.get(), format);
|
QImageReader inputReader(inputDevice.get(), format);
|
||||||
QImageReader expReader(expfile, "png");
|
QImageReader expReader(expfile, fmt.toLatin1());
|
||||||
|
|
||||||
QImage inputImage;
|
QImage inputImage;
|
||||||
QImage expImage;
|
QImage expImage;
|
||||||
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
@ -93,7 +93,6 @@ endif()
|
|||||||
if (LibHeif_FOUND)
|
if (LibHeif_FOUND)
|
||||||
kimageformats_add_plugin(kimg_heif SOURCES heif.cpp)
|
kimageformats_add_plugin(kimg_heif SOURCES heif.cpp)
|
||||||
target_link_libraries(kimg_heif PkgConfig::LibHeif)
|
target_link_libraries(kimg_heif PkgConfig::LibHeif)
|
||||||
kde_target_enable_exceptions(kimg_heif PRIVATE)
|
|
||||||
|
|
||||||
if (QT_MAJOR_VERSION STREQUAL "5")
|
if (QT_MAJOR_VERSION STREQUAL "5")
|
||||||
install(FILES heif.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
|
install(FILES heif.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
|
||||||
|
35
src/imageformats/fastmath_p.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
Approximated math functions used into conversions.
|
||||||
|
|
||||||
|
SPDX-FileCopyrightText: Edward Kmett
|
||||||
|
SPDX-FileCopyrightText: 2023 Mirco Miranda <mircomir@outlook.com>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
#ifndef FASTMATH_P_H
|
||||||
|
#define FASTMATH_P_H
|
||||||
|
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief fastPow
|
||||||
|
* Based on Edward Kmett code released into the public domain.
|
||||||
|
* See also: https://github.com/ekmett/approximate
|
||||||
|
*/
|
||||||
|
inline double fastPow(double x, double y)
|
||||||
|
{
|
||||||
|
union {
|
||||||
|
double d;
|
||||||
|
qint32 i[2];
|
||||||
|
} u = {x};
|
||||||
|
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
|
||||||
|
u.i[1] = qint32(y * (u.i[1] - 1072632447) + 1072632447);
|
||||||
|
u.i[0] = 0;
|
||||||
|
#else // never tested
|
||||||
|
u.i[0] = qint32(y * (u.i[0] - 1072632447) + 1072632447);
|
||||||
|
u.i[1] = 0;
|
||||||
|
#endif
|
||||||
|
return u.d;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // FASTMATH_P_H
|
@ -3,14 +3,14 @@
|
|||||||
|
|
||||||
SPDX-FileCopyrightText: 2003 Ignacio Castaño <castano@ludicon.com>
|
SPDX-FileCopyrightText: 2003 Ignacio Castaño <castano@ludicon.com>
|
||||||
SPDX-FileCopyrightText: 2015 Alex Merry <alex.merry@kde.org>
|
SPDX-FileCopyrightText: 2015 Alex Merry <alex.merry@kde.org>
|
||||||
SPDX-FileCopyrightText: 2022 Mirco Miranda <mircomir@outlook.com>
|
SPDX-FileCopyrightText: 2022-2023 Mirco Miranda <mircomir@outlook.com>
|
||||||
|
|
||||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This code is based on Thacher Ulrich PSD loading code released
|
* The early version of this code was based on Thacher Ulrich PSD loading code
|
||||||
* into the public domain. See: http://tulrich.com/geekstuff/
|
* released into the public domain. See: http://tulrich.com/geekstuff/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -21,7 +21,6 @@
|
|||||||
/*
|
/*
|
||||||
* Limitations of the current code:
|
* Limitations of the current code:
|
||||||
* - 32-bit float image are converted to 16-bit integer image.
|
* - 32-bit float image are converted to 16-bit integer image.
|
||||||
* NOTE: Qt 6.2 allow 32-bit float images (RGB only)
|
|
||||||
* - Other color spaces cannot directly be read due to lack of QImage support for
|
* - Other color spaces cannot directly be read due to lack of QImage support for
|
||||||
* color spaces other than RGB (and Grayscale). Where possible, a conversion
|
* color spaces other than RGB (and Grayscale). Where possible, a conversion
|
||||||
* to RGB is done:
|
* to RGB is done:
|
||||||
@ -33,6 +32,7 @@
|
|||||||
* color management engine (e.g. LittleCMS).
|
* color management engine (e.g. LittleCMS).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "fastmath_p.h"
|
||||||
#include "psd_p.h"
|
#include "psd_p.h"
|
||||||
#include "util_p.h"
|
#include "util_p.h"
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ typedef quint8 uchar;
|
|||||||
* This should not be a problem because the Qt's QColorSpace supports the linear
|
* This should not be a problem because the Qt's QColorSpace supports the linear
|
||||||
* sRgb colorspace.
|
* sRgb colorspace.
|
||||||
*
|
*
|
||||||
* Using linear conversion, the loading speed is improved by 4x. Anyway, if you are using
|
* Using linear conversion, the loading speed is slightly improved. Anyway, if you are using
|
||||||
* an software that discard color info, you should comment it.
|
* an software that discard color info, you should comment it.
|
||||||
*
|
*
|
||||||
* At the time I'm writing (07/2022), Gwenview and Krita supports linear sRgb but KDE
|
* At the time I'm writing (07/2022), Gwenview and Krita supports linear sRgb but KDE
|
||||||
@ -733,9 +733,9 @@ static QImage::Format imageFormat(const PSDHeader &header, bool alpha)
|
|||||||
switch(header.color_mode) {
|
switch(header.color_mode) {
|
||||||
case CM_RGB:
|
case CM_RGB:
|
||||||
if (header.depth == 16 || header.depth == 32)
|
if (header.depth == 16 || header.depth == 32)
|
||||||
format = header.channel_count < 4 || !alpha ? QImage::Format_RGBX64 : QImage::Format_RGBA64;
|
format = header.channel_count < 4 || !alpha ? QImage::Format_RGBX64 : QImage::Format_RGBA64_Premultiplied;
|
||||||
else
|
else
|
||||||
format = header.channel_count < 4 || !alpha ? QImage::Format_RGB888 : QImage::Format_RGBA8888;
|
format = header.channel_count < 4 || !alpha ? QImage::Format_RGB888 : QImage::Format_RGBA8888_Premultiplied;
|
||||||
break;
|
break;
|
||||||
case CM_MULTICHANNEL: // Treat MCH as CMYK (number of channel check is done in IsSupported())
|
case CM_MULTICHANNEL: // Treat MCH as CMYK (number of channel check is done in IsSupported())
|
||||||
case CM_CMYK: // Photoshop supports CMYK/MCH 8-bits and 16-bits only
|
case CM_CMYK: // Photoshop supports CMYK/MCH 8-bits and 16-bits only
|
||||||
@ -814,7 +814,7 @@ inline void planarToChunchy(uchar *target, const char *source, qint32 width, qin
|
|||||||
auto s = reinterpret_cast<const T*>(source);
|
auto s = reinterpret_cast<const T*>(source);
|
||||||
auto t = reinterpret_cast<T*>(target);
|
auto t = reinterpret_cast<T*>(target);
|
||||||
for (qint32 x = 0; x < width; ++x) {
|
for (qint32 x = 0; x < width; ++x) {
|
||||||
t[x*cn+c] = xchg(s[x]);
|
t[x * cn + c] = xchg(s[x]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -826,7 +826,44 @@ inline void planarToChunchyFloat(uchar *target, const char *source, qint32 width
|
|||||||
for (qint32 x = 0; x < width; ++x) {
|
for (qint32 x = 0; x < width; ++x) {
|
||||||
auto tmp = xchg(s[x]);
|
auto tmp = xchg(s[x]);
|
||||||
auto ftmp = (*reinterpret_cast<float*>(&tmp) - double(min)) / (double(max) - double(min));
|
auto ftmp = (*reinterpret_cast<float*>(&tmp) - double(min)) / (double(max) - double(min));
|
||||||
t[x*cn+c] = quint16(std::min(ftmp * std::numeric_limits<quint16>::max() + 0.5, double(std::numeric_limits<quint16>::max())));
|
t[x * cn + c] = quint16(std::min(ftmp * std::numeric_limits<quint16>::max() + 0.5, double(std::numeric_limits<quint16>::max())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class PremulConversion {
|
||||||
|
PS2P, // Photoshop premul to qimage premul (required by RGB)
|
||||||
|
PS2A, // Photoshop premul to unassociated alpha (required by RGB, CMYK and L* components of LAB)
|
||||||
|
PSLab2A // Photoshop premul to unassociated alpha (required by a* and b* components of LAB)
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
inline void premulConversion(char *stride, qint32 width, qint32 ac, qint32 cn, const PremulConversion &conv)
|
||||||
|
{
|
||||||
|
auto s = reinterpret_cast<T *>(stride);
|
||||||
|
auto max = qint64(std::numeric_limits<T>::max());
|
||||||
|
|
||||||
|
for (qint32 c = 0; c < ac; ++c) {
|
||||||
|
if (conv == PremulConversion::PS2P) {
|
||||||
|
for (qint32 x = 0; x < width; ++x) {
|
||||||
|
auto xcn = x * cn;
|
||||||
|
auto alpha = *(s + xcn + ac);
|
||||||
|
*(s + xcn + c) = *(s + xcn + c) + alpha - max;
|
||||||
|
}
|
||||||
|
} else if (conv == PremulConversion::PS2A || (conv == PremulConversion::PSLab2A && c == 0)) {
|
||||||
|
for (qint32 x = 0; x < width; ++x) {
|
||||||
|
auto xcn = x * cn;
|
||||||
|
auto alpha = *(s + xcn + ac);
|
||||||
|
if (alpha > 0)
|
||||||
|
*(s + xcn + c) = ((*(s + xcn + c) + alpha - max) * max + alpha / 2) / alpha;
|
||||||
|
}
|
||||||
|
} else if (conv == PremulConversion::PSLab2A) {
|
||||||
|
for (qint32 x = 0; x < width; ++x) {
|
||||||
|
auto xcn = x * cn;
|
||||||
|
auto alpha = *(s + xcn + ac);
|
||||||
|
if (alpha > 0)
|
||||||
|
*(s + xcn + c) = ((*(s + xcn + c) + (alpha - max + 1) / 2) * max + alpha / 2) / alpha;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -839,12 +876,25 @@ inline void monoInvert(uchar *target, const char* source, qint32 bytes)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
inline void rawChannelsCopy(uchar *target, qint32 targetChannels, const char *source, qint32 sourceChannels, qint32 width)
|
||||||
|
{
|
||||||
|
auto s = reinterpret_cast<const T *>(source);
|
||||||
|
auto t = reinterpret_cast<T *>(target);
|
||||||
|
for (qint32 c = 0, cs = std::min(targetChannels, sourceChannels); c < cs; ++c) {
|
||||||
|
for (qint32 x = 0; x < width; ++x) {
|
||||||
|
t[x * targetChannels + c] = s[x * sourceChannels + c];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
inline void cmykToRgb(uchar *target, qint32 targetChannels, const char *source, qint32 sourceChannels, qint32 width, bool alpha = false)
|
inline void cmykToRgb(uchar *target, qint32 targetChannels, const char *source, qint32 sourceChannels, qint32 width, bool alpha = false)
|
||||||
{
|
{
|
||||||
auto s = reinterpret_cast<const T*>(source);
|
auto s = reinterpret_cast<const T*>(source);
|
||||||
auto t = reinterpret_cast<T*>(target);
|
auto t = reinterpret_cast<T*>(target);
|
||||||
auto max = double(std::numeric_limits<T>::max());
|
auto max = double(std::numeric_limits<T>::max());
|
||||||
|
auto invmax = 1.0 / max; // speed improvements by ~10%
|
||||||
|
|
||||||
if (sourceChannels < 4) {
|
if (sourceChannels < 4) {
|
||||||
qDebug() << "cmykToRgb: image is not a valid CMYK!";
|
qDebug() << "cmykToRgb: image is not a valid CMYK!";
|
||||||
@ -853,10 +903,10 @@ inline void cmykToRgb(uchar *target, qint32 targetChannels, const char *source,
|
|||||||
|
|
||||||
for (qint32 w = 0; w < width; ++w) {
|
for (qint32 w = 0; w < width; ++w) {
|
||||||
auto ps = s + sourceChannels * w;
|
auto ps = s + sourceChannels * w;
|
||||||
auto C = 1 - *(ps + 0) / max;
|
auto C = 1 - *(ps + 0) * invmax;
|
||||||
auto M = 1 - *(ps + 1) / max;
|
auto M = 1 - *(ps + 1) * invmax;
|
||||||
auto Y = 1 - *(ps + 2) / max;
|
auto Y = 1 - *(ps + 2) * invmax;
|
||||||
auto K = 1 - *(ps + 3) / max;
|
auto K = 1 - *(ps + 3) * invmax;
|
||||||
|
|
||||||
auto pt = t + targetChannels * w;
|
auto pt = t + targetChannels * w;
|
||||||
*(pt + 0) = T(std::min(max - (C * (1 - K) + K) * max + 0.5, max));
|
*(pt + 0) = T(std::min(max - (C * (1 - K) + K) * max + 0.5, max));
|
||||||
@ -881,8 +931,9 @@ inline double gammaCorrection(double linear)
|
|||||||
#ifdef PSD_FAST_LAB_CONVERSION
|
#ifdef PSD_FAST_LAB_CONVERSION
|
||||||
return linear;
|
return linear;
|
||||||
#else
|
#else
|
||||||
// NOTE: pow() slow down the performance by a 4 factor :(
|
// Replacing fastPow with std::pow the conversion time is 2/3 times longer: using fastPow
|
||||||
return (linear > 0.0031308 ? 1.055 * std::pow(linear, 1.0 / 2.4) - 0.055 : 12.92 * linear);
|
// there are minimal differences in the conversion that are not visually noticeable.
|
||||||
|
return (linear > 0.0031308 ? 1.055 * fastPow(linear, 1.0 / 2.4) - 0.055 : 12.92 * linear);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -892,6 +943,7 @@ inline void labToRgb(uchar *target, qint32 targetChannels, const char *source, q
|
|||||||
auto s = reinterpret_cast<const T*>(source);
|
auto s = reinterpret_cast<const T*>(source);
|
||||||
auto t = reinterpret_cast<T*>(target);
|
auto t = reinterpret_cast<T*>(target);
|
||||||
auto max = double(std::numeric_limits<T>::max());
|
auto max = double(std::numeric_limits<T>::max());
|
||||||
|
auto invmax = 1.0 / max;
|
||||||
|
|
||||||
if (sourceChannels < 3) {
|
if (sourceChannels < 3) {
|
||||||
qDebug() << "labToRgb: image is not a valid LAB!";
|
qDebug() << "labToRgb: image is not a valid LAB!";
|
||||||
@ -900,14 +952,14 @@ inline void labToRgb(uchar *target, qint32 targetChannels, const char *source, q
|
|||||||
|
|
||||||
for (qint32 w = 0; w < width; ++w) {
|
for (qint32 w = 0; w < width; ++w) {
|
||||||
auto ps = s + sourceChannels * w;
|
auto ps = s + sourceChannels * w;
|
||||||
auto L = (*(ps + 0) / max) * 100.0;
|
auto L = (*(ps + 0) * invmax) * 100.0;
|
||||||
auto A = (*(ps + 1) / max) * 255.0 - 128.0;
|
auto A = (*(ps + 1) * invmax) * 255.0 - 128.0;
|
||||||
auto B = (*(ps + 2) / max) * 255.0 - 128.0;
|
auto B = (*(ps + 2) * invmax) * 255.0 - 128.0;
|
||||||
|
|
||||||
// converting LAB to XYZ (D65 illuminant)
|
// converting LAB to XYZ (D65 illuminant)
|
||||||
auto Y = (L + 16.0) / 116.0;
|
auto Y = (L + 16.0) * (1.0 / 116.0);
|
||||||
auto X = A / 500.0 + Y;
|
auto X = A * (1.0 / 500.0) + Y;
|
||||||
auto Z = Y - B / 200.0;
|
auto Z = Y - B * (1.0 / 200.0);
|
||||||
|
|
||||||
// NOTE: use the constants of the illuminant of the target RGB color space
|
// NOTE: use the constants of the illuminant of the target RGB color space
|
||||||
X = finv(X) * 0.9504; // D50: * 0.9642
|
X = finv(X) * 0.9504; // D50: * 0.9642
|
||||||
@ -1057,7 +1109,15 @@ static bool LoadPSD(QDataStream &stream, const PSDHeader &header, QImage &img)
|
|||||||
QByteArray rawStride;
|
QByteArray rawStride;
|
||||||
rawStride.resize(raw_count);
|
rawStride.resize(raw_count);
|
||||||
|
|
||||||
if (header.color_mode == CM_CMYK || header.color_mode == CM_LABCOLOR || header.color_mode == CM_MULTICHANNEL) {
|
// clang-format off
|
||||||
|
// checks the need of color conversion (that requires random access to the image)
|
||||||
|
auto randomAccess = (header.color_mode == CM_CMYK) ||
|
||||||
|
(header.color_mode == CM_LABCOLOR) ||
|
||||||
|
(header.color_mode == CM_MULTICHANNEL) ||
|
||||||
|
(header.color_mode != CM_INDEXED && img.hasAlphaChannel());
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
if (randomAccess) {
|
||||||
// In order to make a colorspace transformation, we need all channels of a scanline
|
// In order to make a colorspace transformation, we need all channels of a scanline
|
||||||
QByteArray psdScanline;
|
QByteArray psdScanline;
|
||||||
psdScanline.resize(qsizetype(header.width * std::min(header.depth, quint16(16)) * header.channel_count + 7) / 8);
|
psdScanline.resize(qsizetype(header.width * std::min(header.depth, quint16(16)) * header.channel_count + 7) / 8);
|
||||||
@ -1077,31 +1137,56 @@ static bool LoadPSD(QDataStream &stream, const PSDHeader &header, QImage &img)
|
|||||||
auto scanLine = reinterpret_cast<unsigned char*>(psdScanline.data());
|
auto scanLine = reinterpret_cast<unsigned char*>(psdScanline.data());
|
||||||
if (header.depth == 8) {
|
if (header.depth == 8) {
|
||||||
planarToChunchy<quint8>(scanLine, rawStride.data(), header.width, c, header.channel_count);
|
planarToChunchy<quint8>(scanLine, rawStride.data(), header.width, c, header.channel_count);
|
||||||
}
|
} else if (header.depth == 16) {
|
||||||
else if (header.depth == 16) {
|
|
||||||
planarToChunchy<quint16>(scanLine, rawStride.data(), header.width, c, header.channel_count);
|
planarToChunchy<quint16>(scanLine, rawStride.data(), header.width, c, header.channel_count);
|
||||||
}
|
} else if (header.depth == 32) {
|
||||||
else if (header.depth == 32) { // Not currently used
|
|
||||||
planarToChunchyFloat<quint32>(scanLine, rawStride.data(), header.width, c, header.channel_count);
|
planarToChunchyFloat<quint32>(scanLine, rawStride.data(), header.width, c, header.channel_count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert premultiplied data to unassociated data
|
||||||
|
if (img.hasAlphaChannel()) {
|
||||||
|
if (header.color_mode == CM_CMYK) {
|
||||||
|
if (header.depth == 8)
|
||||||
|
premulConversion<quint8>(psdScanline.data(), header.width, 4, header.channel_count, PremulConversion::PS2A);
|
||||||
|
else if (header.depth == 16)
|
||||||
|
premulConversion<quint16>(psdScanline.data(), header.width, 4, header.channel_count, PremulConversion::PS2A);
|
||||||
|
}
|
||||||
|
if (header.color_mode == CM_LABCOLOR) {
|
||||||
|
if (header.depth == 8)
|
||||||
|
premulConversion<quint8>(psdScanline.data(), header.width, 3, header.channel_count, PremulConversion::PSLab2A);
|
||||||
|
else if (header.depth == 16)
|
||||||
|
premulConversion<quint16>(psdScanline.data(), header.width, 3, header.channel_count, PremulConversion::PSLab2A);
|
||||||
|
}
|
||||||
|
if (header.color_mode == CM_RGB) {
|
||||||
|
if (header.depth == 8)
|
||||||
|
premulConversion<quint8>(psdScanline.data(), header.width, 3, header.channel_count, PremulConversion::PS2P);
|
||||||
|
else if (header.depth == 16 || header.depth == 32)
|
||||||
|
premulConversion<quint16>(psdScanline.data(), header.width, 3, header.channel_count, PremulConversion::PS2P);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Conversion to RGB
|
// Conversion to RGB
|
||||||
if (header.color_mode == CM_CMYK || header.color_mode == CM_MULTICHANNEL) {
|
if (header.color_mode == CM_CMYK || header.color_mode == CM_MULTICHANNEL) {
|
||||||
if (header.depth == 8)
|
if (header.depth == 8)
|
||||||
cmykToRgb<quint8>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width, alpha);
|
cmykToRgb<quint8>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width, alpha);
|
||||||
else
|
else if (header.depth == 16)
|
||||||
cmykToRgb<quint16>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width, alpha);
|
cmykToRgb<quint16>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width, alpha);
|
||||||
}
|
}
|
||||||
if (header.color_mode == CM_LABCOLOR) {
|
if (header.color_mode == CM_LABCOLOR) {
|
||||||
if (header.depth == 8)
|
if (header.depth == 8)
|
||||||
labToRgb<quint8>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width, alpha);
|
labToRgb<quint8>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width, alpha);
|
||||||
else
|
else if (header.depth == 16)
|
||||||
labToRgb<quint16>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width, alpha);
|
labToRgb<quint16>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width, alpha);
|
||||||
}
|
}
|
||||||
|
if (header.color_mode == CM_RGB) {
|
||||||
|
if (header.depth == 8)
|
||||||
|
rawChannelsCopy<quint8>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width);
|
||||||
|
else if (header.depth == 16 || header.depth == 32)
|
||||||
|
rawChannelsCopy<quint16>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// Linear read (no position jumps): optimized code usable only for the colorspaces supported by QImage
|
// Linear read (no position jumps): optimized code usable only for the colorspaces supported by QImage
|
||||||
for (qint32 c = 0; c < channel_num; ++c) {
|
for (qint32 c = 0; c < channel_num; ++c) {
|
||||||
for (qint32 y = 0, h = header.height; y < h; ++y) {
|
for (qint32 y = 0, h = header.height; y < h; ++y) {
|
||||||
@ -1112,16 +1197,13 @@ static bool LoadPSD(QDataStream &stream, const PSDHeader &header, QImage &img)
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto scanLine = img.scanLine(y);
|
auto scanLine = img.scanLine(y);
|
||||||
if (header.depth == 1) { // Bitmap
|
if (header.depth == 1) { // Bitmap
|
||||||
monoInvert(scanLine, rawStride.data(), std::min(rawStride.size(), img.bytesPerLine()));
|
monoInvert(scanLine, rawStride.data(), std::min(rawStride.size(), img.bytesPerLine()));
|
||||||
}
|
} else if (header.depth == 8) { // 8-bits images: Indexed, Grayscale, RGB/RGBA
|
||||||
else if (header.depth == 8) { // 8-bits images: Indexed, Grayscale, RGB/RGBA
|
|
||||||
planarToChunchy<quint8>(scanLine, rawStride.data(), header.width, c, imgChannels);
|
planarToChunchy<quint8>(scanLine, rawStride.data(), header.width, c, imgChannels);
|
||||||
}
|
} else if (header.depth == 16) { // 16-bits integer images: Grayscale, RGB/RGBA
|
||||||
else if (header.depth == 16) { // 16-bits integer images: Grayscale, RGB/RGBA
|
|
||||||
planarToChunchy<quint16>(scanLine, rawStride.data(), header.width, c, imgChannels);
|
planarToChunchy<quint16>(scanLine, rawStride.data(), header.width, c, imgChannels);
|
||||||
}
|
} else if (header.depth == 32) { // 32-bits float images: Grayscale, RGB/RGBA (coverted to equivalent integer 16-bits)
|
||||||
else if (header.depth == 32) { // 32-bits float images: Grayscale, RGB/RGBA (coverted to equivalent integer 16-bits)
|
|
||||||
planarToChunchyFloat<quint32>(scanLine, rawStride.data(), header.width, c, imgChannels);
|
planarToChunchyFloat<quint32>(scanLine, rawStride.data(), header.width, c, imgChannels);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1264,6 +1346,9 @@ bool PSDHandler::canRead(QIODevice *device)
|
|||||||
if (header.color_mode == CM_CMYK || header.color_mode == CM_LABCOLOR || header.color_mode == CM_MULTICHANNEL) {
|
if (header.color_mode == CM_CMYK || header.color_mode == CM_LABCOLOR || header.color_mode == CM_MULTICHANNEL) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (header.color_mode == CM_RGB && header.channel_count > 3) {
|
||||||
|
return false; // supposing extra channel as alpha
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return IsSupported(header);
|
return IsSupported(header);
|
||||||
|
@ -45,7 +45,6 @@ const auto supported_formats = QSet<QByteArray>{
|
|||||||
"dcs", "dc2", "dcr", "dng", "drf", "dxo",
|
"dcs", "dc2", "dcr", "dng", "drf", "dxo",
|
||||||
"eip", "erf",
|
"eip", "erf",
|
||||||
"fff",
|
"fff",
|
||||||
"hdr",
|
|
||||||
"iiq",
|
"iiq",
|
||||||
"k25", "kc2", "kdc",
|
"k25", "kc2", "kdc",
|
||||||
"mdc", "mef", "mfw", "mos", "mrw",
|
"mdc", "mef", "mfw", "mos", "mrw",
|
||||||
@ -141,7 +140,7 @@ public:
|
|||||||
if (whence == SEEK_END) {
|
if (whence == SEEK_END) {
|
||||||
pos = size + o;
|
pos = size + o;
|
||||||
}
|
}
|
||||||
if (pos < 0 || pos > size || m_device->isSequential()) {
|
if (pos < 0 || m_device->isSequential()) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return m_device->seek(pos) ? 0 : -1;
|
return m_device->seek(pos) ? 0 : -1;
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
"dcs", "dc2", "dcr", "dng", "drf", "dxo",
|
"dcs", "dc2", "dcr", "dng", "drf", "dxo",
|
||||||
"eip", "erf",
|
"eip", "erf",
|
||||||
"fff",
|
"fff",
|
||||||
"hdr",
|
|
||||||
"iiq",
|
"iiq",
|
||||||
"k25", "kdc", "kc2",
|
"k25", "kdc", "kc2",
|
||||||
"mdc", "mef", "mfw", "mos", "mrw",
|
"mdc", "mef", "mfw", "mos", "mrw",
|
||||||
@ -27,7 +26,6 @@
|
|||||||
"image/x-kodak-dcs", "image/x-dc2", "image/x-kodak-dcr", "image/x-adobe-dng", "image/x-drf", "image/x-dxo",
|
"image/x-kodak-dcs", "image/x-dc2", "image/x-kodak-dcr", "image/x-adobe-dng", "image/x-drf", "image/x-dxo",
|
||||||
"image/x-epson-eip", "image/x-epson-erf",
|
"image/x-epson-eip", "image/x-epson-erf",
|
||||||
"image/x-fff",
|
"image/x-fff",
|
||||||
"image/x-hdr",
|
|
||||||
"image/x-iiq",
|
"image/x-iiq",
|
||||||
"image/x-kodak-k25", "image/x-kodak-kdc", "image/x-kodak-kc2",
|
"image/x-kodak-k25", "image/x-kodak-kdc", "image/x-kodak-kc2",
|
||||||
"image/x-minolta-mdc", "image/x-mamiya-mef", "image/x-mfw", "image/x-aptus-mos", "image/x-minolta-mrw",
|
"image/x-minolta-mdc", "image/x-mamiya-mef", "image/x-mfw", "image/x-aptus-mos", "image/x-minolta-mrw",
|
||||||
|
@ -430,6 +430,9 @@ bool TGAHandler::write(const QImage &image)
|
|||||||
|
|
||||||
const QImage &img = image;
|
const QImage &img = image;
|
||||||
const bool hasAlpha = (img.format() == QImage::Format_ARGB32);
|
const bool hasAlpha = (img.format() == QImage::Format_ARGB32);
|
||||||
|
static constexpr quint8 originTopLeft = TGA_ORIGIN_UPPER + TGA_ORIGIN_LEFT; // 0x20
|
||||||
|
static constexpr quint8 alphaChannel8Bits = 0x08;
|
||||||
|
|
||||||
for (int i = 0; i < 12; i++) {
|
for (int i = 0; i < 12; i++) {
|
||||||
s << targaMagic[i];
|
s << targaMagic[i];
|
||||||
}
|
}
|
||||||
@ -438,7 +441,7 @@ bool TGAHandler::write(const QImage &image)
|
|||||||
s << quint16(img.width()); // width
|
s << quint16(img.width()); // width
|
||||||
s << quint16(img.height()); // height
|
s << quint16(img.height()); // height
|
||||||
s << quint8(hasAlpha ? 32 : 24); // depth (24 bit RGB + 8 bit alpha)
|
s << quint8(hasAlpha ? 32 : 24); // depth (24 bit RGB + 8 bit alpha)
|
||||||
s << quint8(hasAlpha ? 0x24 : 0x20); // top left image (0x20) + 8 bit alpha (0x4)
|
s << quint8(hasAlpha ? originTopLeft + alphaChannel8Bits : originTopLeft); // top left image (0x20) + 8 bit alpha (0x8)
|
||||||
|
|
||||||
for (int y = 0; y < img.height(); y++) {
|
for (int y = 0; y < img.height(); y++) {
|
||||||
for (int x = 0; x < img.width(); x++) {
|
for (int x = 0; x < img.width(); x++) {
|
||||||
|