DDS: improved read/write support
The following changes have been made: - Improved writing speed by using scanLine() instead of pixel() - Optimized memory usage on writing by using ScanlineConverter class - Added native write support for RGBA32FPx4, RGBA16FPx4, RGB8, Grayscale8 and Indexed8 uncompressed formats - Grayscale DDS without alpha are loaded in a Grayscale8 image - Fixed warnings about wrong PITCH reported by GIMP on DDSs saved by this plugin - Initial support for loading DX10 formats (R16F, RG16F, RGBA16F, RGBPreMulA16F, R32F, RG32F, RGBA32F, RGBPreMulA32F) - Fixed alignment issues and A8P8 format support of the attached images* Tested using GIMP and [NVIDIA Texture Tools](https://developer.nvidia.com/texture-tools-exporter) plugin for Photoshop. (*) The following images (taken from [here](https://github.com/walbourn/directxtexmedia)) cannot be added to read tests due to license issue: [test8_DWORD.dds](/uploads/449b5a0d886aaf6764af554fe38e2b09/test8_DWORD.dds) [dx5_logo.dds](/uploads/6f5f27df752890b227ef07e0195435d4/dx5_logo.dds) [test888_DWORD.dds](/uploads/c8bc355c5749cf203d47e0b3073ad419/test888_DWORD.dds)
16
README.md
@ -66,8 +66,8 @@ removed.
|
||||
|
||||
### The DDS plugin
|
||||
|
||||
The DDS plugin is a fork from Qt 5.6. It will be activated once the
|
||||
security issues are resolved.
|
||||
The DDS plugin is a fork of Qt 5.6 with bug fixes and improvements. It
|
||||
will be activated once the security issues are resolved.
|
||||
|
||||
## License
|
||||
|
||||
@ -79,16 +79,17 @@ The CMake code in this framework is licensed under the
|
||||
|
||||
## Plugin status
|
||||
|
||||
The current implementation of a plugin may not be complete or may have limitations
|
||||
of various kinds. Typically the limitations are on maximum size and color depth.
|
||||
The current implementation of a plugin may not be complete or may have
|
||||
limitations of various kinds. Typically the limitations are on maximum size
|
||||
and color depth.
|
||||
|
||||
The various plugins are also limited by the formats natively supported by Qt.
|
||||
For example, native support for CMYK images is only available since Qt 6.8.
|
||||
|
||||
### HDR images
|
||||
|
||||
HDR images are supported via floating point image formats from EXR, HDR, JXL,
|
||||
JXR, PFM and PSD plugins.
|
||||
HDR images are supported via floating point image formats from DDS, EXR, HDR,
|
||||
JXL, JXR, PFM and PSD plugins.
|
||||
It is important to note that in the past these plugins stripped away HDR
|
||||
information, returning SDR images.
|
||||
|
||||
@ -176,6 +177,9 @@ with Qt 6.8+.
|
||||
**This plugin is disabled by default. It can be enabled with the
|
||||
`KIMAGEFORMATS_DDS` build option in the cmake file.**
|
||||
|
||||
The following defines can be defined in cmake to modify the behavior of the plugin:
|
||||
- `DDS_DISABLE_STRIDE_ALIGNMENT`: disable the stride aligment based on DDS pitch: it is known that some writers do not set it correctly.
|
||||
|
||||
### The HEIF plugin
|
||||
|
||||
**This plugin is disabled by default. It can be enabled with the
|
||||
|
@ -78,7 +78,7 @@ kimageformats_read_tests(
|
||||
|
||||
if(KIMAGEFORMATS_DDS)
|
||||
kimageformats_read_tests(dds)
|
||||
kimageformats_write_tests(dds)
|
||||
kimageformats_write_tests(dds-nodatacheck-lossless)
|
||||
endif()
|
||||
|
||||
if (KF6Archive_FOUND)
|
||||
|
BIN
autotests/read/dds/gray8.dds
Normal file
BIN
autotests/read/dds/gray8.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
autotests/read/dds/r16.dds
Normal file
BIN
autotests/read/dds/r16.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
autotests/read/dds/r16dx10.dds
Normal file
BIN
autotests/read/dds/r16dx10.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
autotests/read/dds/r32.dds
Normal file
BIN
autotests/read/dds/r32.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
autotests/read/dds/r32dx10.dds
Normal file
BIN
autotests/read/dds/r32dx10.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
autotests/read/dds/rg16.dds
Normal file
BIN
autotests/read/dds/rg16.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
autotests/read/dds/rg16dx10.dds
Normal file
BIN
autotests/read/dds/rg16dx10.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
autotests/read/dds/rg32.dds
Normal file
BIN
autotests/read/dds/rg32.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
autotests/read/dds/rg32dx10.dds
Normal file
BIN
autotests/read/dds/rg32dx10.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
autotests/read/dds/rgba16dx10.dds
Normal file
BIN
autotests/read/dds/rgba16dx10.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
autotests/read/dds/rgba32dx10.dds
Normal file
BIN
autotests/read/dds/rgba32dx10.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
autotests/read/dds/rgbpa16dx10.dds
Normal file
BIN
autotests/read/dds/rgbpa16dx10.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
autotests/read/dds/rgbpa32dx10.dds
Normal file
BIN
autotests/read/dds/rgbpa32dx10.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
@ -32,7 +32,7 @@ endif()
|
||||
##################################
|
||||
|
||||
if(KIMAGEFORMATS_DDS)
|
||||
kimageformats_add_plugin(kimg_dds SOURCES dds.cpp)
|
||||
kimageformats_add_plugin(kimg_dds SOURCES dds.cpp scanlineconverter.cpp)
|
||||
endif()
|
||||
|
||||
##################################
|
||||
|
@ -2,6 +2,7 @@
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2015 The Qt Company Ltd
|
||||
SPDX-FileCopyrightText: 2013 Ivan Komissarov
|
||||
SPDX-FileCopyrightText: 2024 Mirco Miranda
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only
|
||||
*/
|
||||
@ -10,6 +11,7 @@
|
||||
|
||||
#include "dds_p.h"
|
||||
#include "util_p.h"
|
||||
#include "scanlineconverter_p.h"
|
||||
|
||||
#include <QColorSpace>
|
||||
#include <QDataStream>
|
||||
@ -17,6 +19,11 @@
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#ifndef DDS_DISABLE_STRIDE_ALIGNMENT
|
||||
// Disable the stride aligment based on DDS pitch: it is known that some writers do not set it correctly
|
||||
// #define DDS_DISABLE_STRIDE_ALIGNMENT
|
||||
#endif
|
||||
|
||||
enum Format {
|
||||
FormatUnknown = 0,
|
||||
|
||||
@ -108,6 +115,141 @@ enum Format {
|
||||
FormatLast = 0x7fffffff
|
||||
};
|
||||
|
||||
enum DXGIFormat {
|
||||
DXGIFormatUNKNOWN = 0,
|
||||
DXGIFormatR32G32B32A32_TYPELESS = 1,
|
||||
DXGIFormatR32G32B32A32_FLOAT = 2,
|
||||
DXGIFormatR32G32B32A32_UINT = 3,
|
||||
DXGIFormatR32G32B32A32_SINT = 4,
|
||||
DXGIFormatR32G32B32_TYPELESS = 5,
|
||||
DXGIFormatR32G32B32_FLOAT = 6,
|
||||
DXGIFormatR32G32B32_UINT = 7,
|
||||
DXGIFormatR32G32B32_SINT = 8,
|
||||
DXGIFormatR16G16B16A16_TYPELESS = 9,
|
||||
DXGIFormatR16G16B16A16_FLOAT = 10,
|
||||
DXGIFormatR16G16B16A16_UNORM = 11,
|
||||
DXGIFormatR16G16B16A16_UINT = 12,
|
||||
DXGIFormatR16G16B16A16_SNORM = 13,
|
||||
DXGIFormatR16G16B16A16_SINT = 14,
|
||||
DXGIFormatR32G32_TYPELESS = 15,
|
||||
DXGIFormatR32G32_FLOAT = 16,
|
||||
DXGIFormatR32G32_UINT = 17,
|
||||
DXGIFormatR32G32_SINT = 18,
|
||||
DXGIFormatR32G8X24_TYPELESS = 19,
|
||||
DXGIFormatD32_FLOAT_S8X24_UINT = 20,
|
||||
DXGIFormatR32_FLOAT_X8X24_TYPELESS = 21,
|
||||
DXGIFormatX32_TYPELESS_G8X24_UINT = 22,
|
||||
DXGIFormatR10G10B10A2_TYPELESS = 23,
|
||||
DXGIFormatR10G10B10A2_UNORM = 24,
|
||||
DXGIFormatR10G10B10A2_UINT = 25,
|
||||
DXGIFormatR11G11B10_FLOAT = 26,
|
||||
DXGIFormatR8G8B8A8_TYPELESS = 27,
|
||||
DXGIFormatR8G8B8A8_UNORM = 28,
|
||||
DXGIFormatR8G8B8A8_UNORM_SRGB = 29,
|
||||
DXGIFormatR8G8B8A8_UINT = 30,
|
||||
DXGIFormatR8G8B8A8_SNORM = 31,
|
||||
DXGIFormatR8G8B8A8_SINT = 32,
|
||||
DXGIFormatR16G16_TYPELESS = 33,
|
||||
DXGIFormatR16G16_FLOAT = 34,
|
||||
DXGIFormatR16G16_UNORM = 35,
|
||||
DXGIFormatR16G16_UINT = 36,
|
||||
DXGIFormatR16G16_SNORM = 37,
|
||||
DXGIFormatR16G16_SINT = 38,
|
||||
DXGIFormatR32_TYPELESS = 39,
|
||||
DXGIFormatD32_FLOAT = 40,
|
||||
DXGIFormatR32_FLOAT = 41,
|
||||
DXGIFormatR32_UINT = 42,
|
||||
DXGIFormatR32_SINT = 43,
|
||||
DXGIFormatR24G8_TYPELESS = 44,
|
||||
DXGIFormatD24_UNORM_S8_UINT = 45,
|
||||
DXGIFormatR24_UNORM_X8_TYPELESS = 46,
|
||||
DXGIFormatX24_TYPELESS_G8_UINT = 47,
|
||||
DXGIFormatR8G8_TYPELESS = 48,
|
||||
DXGIFormatR8G8_UNORM = 49,
|
||||
DXGIFormatR8G8_UINT = 50,
|
||||
DXGIFormatR8G8_SNORM = 51,
|
||||
DXGIFormatR8G8_SINT = 52,
|
||||
DXGIFormatR16_TYPELESS = 53,
|
||||
DXGIFormatR16_FLOAT = 54,
|
||||
DXGIFormatD16_UNORM = 55,
|
||||
DXGIFormatR16_UNORM = 56,
|
||||
DXGIFormatR16_UINT = 57,
|
||||
DXGIFormatR16_SNORM = 58,
|
||||
DXGIFormatR16_SINT = 59,
|
||||
DXGIFormatR8_TYPELESS = 60,
|
||||
DXGIFormatR8_UNORM = 61,
|
||||
DXGIFormatR8_UINT = 62,
|
||||
DXGIFormatR8_SNORM = 63,
|
||||
DXGIFormatR8_SINT = 64,
|
||||
DXGIFormatA8_UNORM = 65,
|
||||
DXGIFormatR1_UNORM = 66,
|
||||
DXGIFormatR9G9B9E5_SHAREDEXP = 67,
|
||||
DXGIFormatR8G8_B8G8_UNORM = 68,
|
||||
DXGIFormatG8R8_G8B8_UNORM = 69,
|
||||
DXGIFormatBC1_TYPELESS = 70,
|
||||
DXGIFormatBC1_UNORM = 71,
|
||||
DXGIFormatBC1_UNORM_SRGB = 72,
|
||||
DXGIFormatBC2_TYPELESS = 73,
|
||||
DXGIFormatBC2_UNORM = 74,
|
||||
DXGIFormatBC2_UNORM_SRGB = 75,
|
||||
DXGIFormatBC3_TYPELESS = 76,
|
||||
DXGIFormatBC3_UNORM = 77,
|
||||
DXGIFormatBC3_UNORM_SRGB = 78,
|
||||
DXGIFormatBC4_TYPELESS = 79,
|
||||
DXGIFormatBC4_UNORM = 80,
|
||||
DXGIFormatBC4_SNORM = 81,
|
||||
DXGIFormatBC5_TYPELESS = 82,
|
||||
DXGIFormatBC5_UNORM = 83,
|
||||
DXGIFormatBC5_SNORM = 84,
|
||||
DXGIFormatB5G6R5_UNORM = 85,
|
||||
DXGIFormatB5G5R5A1_UNORM = 86,
|
||||
DXGIFormatB8G8R8A8_UNORM = 87,
|
||||
DXGIFormatB8G8R8X8_UNORM = 88,
|
||||
DXGIFormatR10G10B10_XR_BIAS_A2_UNORM = 89,
|
||||
DXGIFormatB8G8R8A8_TYPELESS = 90,
|
||||
DXGIFormatB8G8R8A8_UNORM_SRGB = 91,
|
||||
DXGIFormatB8G8R8X8_TYPELESS = 92,
|
||||
DXGIFormatB8G8R8X8_UNORM_SRGB = 93,
|
||||
DXGIFormatBC6H_TYPELESS = 94,
|
||||
DXGIFormatBC6H_UF16 = 95,
|
||||
DXGIFormatBC6H_SF16 = 96,
|
||||
DXGIFormatBC7_TYPELESS = 97,
|
||||
DXGIFormatBC7_UNORM = 98,
|
||||
DXGIFormatBC7_UNORM_SRGB = 99,
|
||||
DXGIFormatAYUV = 100,
|
||||
DXGIFormatY410 = 101,
|
||||
DXGIFormatY416 = 102,
|
||||
DXGIFormatNV12 = 103,
|
||||
DXGIFormatP010 = 104,
|
||||
DXGIFormatP016 = 105,
|
||||
DXGIFormat420_OPAQUE = 106,
|
||||
DXGIFormatYUY2 = 107,
|
||||
DXGIFormatY210 = 108,
|
||||
DXGIFormatY216 = 109,
|
||||
DXGIFormatNV11 = 110,
|
||||
DXGIFormatAI44 = 111,
|
||||
DXGIFormatIA44 = 112,
|
||||
DXGIFormatP8 = 113,
|
||||
DXGIFormatA8P8 = 114,
|
||||
DXGIFormatB4G4R4A4_UNORM = 115,
|
||||
DXGIFormatP208 = 130,
|
||||
DXGIFormatV208 = 131,
|
||||
DXGIFormatV408 = 132,
|
||||
DXGIFormatSAMPLER_FEEDBACK_MIN_MIP_OPAQUE,
|
||||
DXGIFormatSAMPLER_FEEDBACK_MIP_REGION_USED_OPAQUE,
|
||||
DXGIFormatFORCE_UINT = 0xffffffff
|
||||
};
|
||||
|
||||
enum DXGIMiscFlags2
|
||||
{
|
||||
// not really flags...
|
||||
DXGIAlphaModeUnknow = 0,
|
||||
DXGIAlphaModeStraight = 1,
|
||||
DXGIAlphaModePremultiplied = 2,
|
||||
DXGIAlphaModeOpaque = 3,
|
||||
DXGIAlphaModeCustom = 4
|
||||
};
|
||||
|
||||
enum Colors {
|
||||
Red = 0,
|
||||
Green,
|
||||
@ -221,6 +363,22 @@ static const Format knownFourCCs[] = {
|
||||
};
|
||||
static const size_t knownFourCCsSize = sizeof(knownFourCCs)/sizeof(Format);
|
||||
|
||||
struct DXGIFormatToFormat
|
||||
{
|
||||
DXGIFormat dxgiFormat;
|
||||
Format format;
|
||||
};
|
||||
|
||||
static const DXGIFormatToFormat knownDXGIFormat[] = {
|
||||
{ DXGIFormatR16G16B16A16_FLOAT, FormatA16B16G16R16F },
|
||||
{ DXGIFormatR32G32B32A32_FLOAT, FormatA32B32G32R32F },
|
||||
{ DXGIFormatR16G16_FLOAT, FormatG16R16F },
|
||||
{ DXGIFormatR32G32_FLOAT, FormatG32R32F },
|
||||
{ DXGIFormatR16_FLOAT, FormatR16F },
|
||||
{ DXGIFormatR32_FLOAT, FormatR32F }
|
||||
};
|
||||
static const size_t knownDXGIFormatSize = sizeof(knownDXGIFormat)/sizeof(DXGIFormatToFormat);
|
||||
|
||||
struct FormatName
|
||||
{
|
||||
Format format;
|
||||
@ -342,6 +500,26 @@ QDataStream &operator<<(QDataStream &s, const DDSPixelFormat &pixelFormat)
|
||||
return s;
|
||||
}
|
||||
|
||||
QDataStream &operator>>(QDataStream &s, DDSHeaderDX10 &header)
|
||||
{
|
||||
s >> header.dxgiFormat;
|
||||
s >> header.resourceDimension;
|
||||
s >> header.miscFlag;
|
||||
s >> header.arraySize;
|
||||
s >> header.miscFlags2;
|
||||
return s;
|
||||
}
|
||||
|
||||
QDataStream &operator<<(QDataStream &s, const DDSHeaderDX10 &header)
|
||||
{
|
||||
s << header.dxgiFormat;
|
||||
s << header.resourceDimension;
|
||||
s << header.miscFlag;
|
||||
s << header.arraySize;
|
||||
s << header.miscFlags2;
|
||||
return s;
|
||||
}
|
||||
|
||||
QDataStream &operator>>(QDataStream &s, DDSHeader &header)
|
||||
{
|
||||
s >> header.magic;
|
||||
@ -360,6 +538,9 @@ QDataStream &operator>>(QDataStream &s, DDSHeader &header)
|
||||
s >> header.caps3;
|
||||
s >> header.caps4;
|
||||
s >> header.reserved2;
|
||||
if (header.pixelFormat.fourCC == dx10Magic)
|
||||
s >> header.header10;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@ -381,26 +562,9 @@ QDataStream &operator<<(QDataStream &s, const DDSHeader &header)
|
||||
s << header.caps3;
|
||||
s << header.caps4;
|
||||
s << header.reserved2;
|
||||
return s;
|
||||
}
|
||||
if (header.pixelFormat.fourCC == dx10Magic)
|
||||
s << header.header10;
|
||||
|
||||
QDataStream &operator>>(QDataStream &s, DDSHeaderDX10 &header)
|
||||
{
|
||||
s >> header.dxgiFormat;
|
||||
s >> header.resourceDimension;
|
||||
s >> header.miscFlag;
|
||||
s >> header.arraySize;
|
||||
s >> header.reserved;
|
||||
return s;
|
||||
}
|
||||
|
||||
QDataStream &operator<<(QDataStream &s, const DDSHeaderDX10 &header)
|
||||
{
|
||||
s << header.dxgiFormat;
|
||||
s << header.resourceDimension;
|
||||
s << header.miscFlag;
|
||||
s << header.arraySize;
|
||||
s << header.reserved;
|
||||
return s;
|
||||
}
|
||||
|
||||
@ -461,6 +625,24 @@ static inline QRgb yuv2rgb(quint8 Y, quint8 U, quint8 V)
|
||||
quint8(Y + 2.03211 * (U - 128)));
|
||||
}
|
||||
|
||||
static void strideAlignment(QDataStream &s, const DDSHeader &dds, quint32 width)
|
||||
{
|
||||
#ifdef DDS_DISABLE_STRIDE_ALIGNMENT
|
||||
Q_UNUSED(s)
|
||||
Q_UNUSED(dds)
|
||||
Q_UNUSED(width)
|
||||
#else
|
||||
if (dds.flags & DDSHeader::FlagPitch) {
|
||||
if (auto alignBytes = qint64(dds.pitchOrLinearSize) - (width * dds.pixelFormat.rgbBitCount + 7) / 8) {
|
||||
quint8 tmp;
|
||||
for (; alignBytes > 0 && alignBytes < 4; --alignBytes) {
|
||||
s >> tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static Format getFormat(const DDSHeader &dds)
|
||||
{
|
||||
const DDSPixelFormat &format = dds.pixelFormat;
|
||||
@ -469,9 +651,16 @@ static Format getFormat(const DDSHeader &dds)
|
||||
} else if (format.flags & DDSPixelFormat::FlagPaletteIndexed8) {
|
||||
return FormatP8;
|
||||
} else if (format.flags & DDSPixelFormat::FlagFourCC) {
|
||||
for (size_t i = 0; i < knownFourCCsSize; ++i) {
|
||||
if (dds.pixelFormat.fourCC == knownFourCCs[i])
|
||||
return knownFourCCs[i];
|
||||
if (dds.pixelFormat.fourCC == dx10Magic) {
|
||||
for (size_t i = 0; i < knownDXGIFormatSize; ++i) {
|
||||
if (dds.header10.dxgiFormat == knownDXGIFormat[i].dxgiFormat)
|
||||
return knownDXGIFormat[i].format;
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i < knownFourCCsSize; ++i) {
|
||||
if (dds.pixelFormat.fourCC == knownFourCCs[i])
|
||||
return knownFourCCs[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i < formatInfosSize; ++i) {
|
||||
@ -784,8 +973,9 @@ static QImage readUnsignedImage(QDataStream &s, const DDSHeader &dds, quint32 wi
|
||||
masks[i] = (masks[i] >> shifts[i]) << (8 - bits[i]);
|
||||
}
|
||||
|
||||
const QImage::Format format = hasAlpha ? QImage::Format_ARGB32 : QImage::Format_RGB32;
|
||||
|
||||
QImage::Format format = hasAlpha ? QImage::Format_ARGB32 : QImage::Format_RGB32;
|
||||
if (!hasAlpha && (flags & DDSPixelFormat::FlagLuminance))
|
||||
format = QImage::Format_Grayscale8;
|
||||
QImage image = imageAlloc(width, height, format);
|
||||
if (image.isNull()) {
|
||||
return image;
|
||||
@ -793,7 +983,8 @@ static QImage readUnsignedImage(QDataStream &s, const DDSHeader &dds, quint32 wi
|
||||
|
||||
for (quint32 y = 0; y < height; y++) {
|
||||
for (quint32 x = 0; x < width; x++) {
|
||||
QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
|
||||
quint8 *byteLine = reinterpret_cast<quint8 *>(image.scanLine(y));
|
||||
QRgb *line = reinterpret_cast<QRgb *>(byteLine);
|
||||
|
||||
quint32 value = readValue(s, dds.pixelFormat.rgbBitCount);
|
||||
quint8 colors[ColorCount];
|
||||
@ -808,20 +999,27 @@ static QImage readUnsignedImage(QDataStream &s, const DDSHeader &dds, quint32 wi
|
||||
if (masks[c])
|
||||
colors[c] = color * 0xff / masks[c];
|
||||
else
|
||||
colors[c] = 0;
|
||||
colors[c] = c == Alpha ? 0xff : 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & DDSPixelFormat::FlagLuminance)
|
||||
line[x] = qRgba(colors[Red], colors[Red], colors[Red], colors[Alpha]);
|
||||
else if (flags & DDSPixelFormat::FlagYUV)
|
||||
if (flags & DDSPixelFormat::FlagLuminance) {
|
||||
if (hasAlpha)
|
||||
line[x] = qRgba(colors[Red], colors[Red], colors[Red], colors[Alpha]);
|
||||
else
|
||||
byteLine[x] = colors[Red];
|
||||
}
|
||||
else if (flags & DDSPixelFormat::FlagYUV) {
|
||||
line[x] = yuv2rgb(colors[Red], colors[Green], colors[Blue]);
|
||||
else
|
||||
}
|
||||
else {
|
||||
line[x] = qRgba(colors[Red], colors[Green], colors[Blue], colors[Alpha]);
|
||||
}
|
||||
|
||||
if (s.status() != QDataStream::Ok)
|
||||
return QImage();
|
||||
}
|
||||
strideAlignment(s, dds, width); // some dds seems aligned to 32 bits
|
||||
}
|
||||
|
||||
return image;
|
||||
@ -896,9 +1094,9 @@ static QImage readRG16F(QDataStream &s, const quint32 width, const quint32 heigh
|
||||
return image;
|
||||
}
|
||||
|
||||
static QImage readARGB16F(QDataStream &s, const quint32 width, const quint32 height)
|
||||
static QImage readARGB16F(QDataStream &s, const quint32 width, const quint32 height, bool alphaPremul)
|
||||
{
|
||||
QImage image = imageAlloc(width, height, QImage::Format_RGBA16FPx4);
|
||||
QImage image = imageAlloc(width, height, alphaPremul ? QImage::Format_RGBA16FPx4_Premultiplied : QImage::Format_RGBA16FPx4);
|
||||
if (image.isNull()) {
|
||||
return image;
|
||||
}
|
||||
@ -965,9 +1163,9 @@ static QImage readRG32F(QDataStream &s, const quint32 width, const quint32 heigh
|
||||
return image;
|
||||
}
|
||||
|
||||
static QImage readARGB32F(QDataStream &s, const quint32 width, const quint32 height)
|
||||
static QImage readARGB32F(QDataStream &s, const quint32 width, const quint32 height, bool alphaPremul)
|
||||
{
|
||||
QImage image = imageAlloc(width, height, QImage::Format_RGBA32FPx4);
|
||||
QImage image = imageAlloc(width, height, alphaPremul ? QImage::Format_RGBA32FPx4_Premultiplied : QImage::Format_RGBA32FPx4);
|
||||
if (image.isNull()) {
|
||||
return image;
|
||||
}
|
||||
@ -1040,7 +1238,7 @@ static QImage readCxV8U8(QDataStream &s, const quint32 width, const quint32 heig
|
||||
return image;
|
||||
}
|
||||
|
||||
static QImage readPalette8Image(QDataStream &s, quint32 width, quint32 height)
|
||||
static QImage readPalette8Image(QDataStream &s, const DDSHeader &dds, quint32 width, quint32 height)
|
||||
{
|
||||
QImage image = imageAlloc(width, height, QImage::Format_Indexed8);
|
||||
if (image.isNull()) {
|
||||
@ -1054,12 +1252,12 @@ static QImage readPalette8Image(QDataStream &s, quint32 width, quint32 height)
|
||||
}
|
||||
|
||||
for (quint32 y = 0; y < height; y++) {
|
||||
quint8 *scanLine = reinterpret_cast<quint8 *>(image.scanLine(y));
|
||||
for (quint32 x = 0; x < width; x++) {
|
||||
quint8 index;
|
||||
s >> index;
|
||||
image.setPixel(x, y, index);
|
||||
quint32 value = readValue(s, dds.pixelFormat.rgbBitCount);
|
||||
if (s.status() != QDataStream::Ok)
|
||||
return QImage();
|
||||
scanLine[x] = (value & 0xff); // any alpha channel discarded
|
||||
}
|
||||
}
|
||||
|
||||
@ -1401,6 +1599,8 @@ static QImage readLayer(QDataStream &s, const DDSHeader &dds, const int format,
|
||||
if (width * height == 0)
|
||||
return QImage();
|
||||
|
||||
bool alphaPremul = dds.header10.miscFlags2 == DXGIAlphaModePremultiplied;
|
||||
|
||||
switch (format) {
|
||||
case FormatR8G8B8:
|
||||
case FormatX8R8G8B8:
|
||||
@ -1427,7 +1627,7 @@ static QImage readLayer(QDataStream &s, const DDSHeader &dds, const int format,
|
||||
return readA2R10G10B10(s, dds, width, height);
|
||||
case FormatP8:
|
||||
case FormatA8P8:
|
||||
return readPalette8Image(s, width, height);
|
||||
return readPalette8Image(s, dds, width, height);
|
||||
case FormatP4:
|
||||
case FormatA4P4:
|
||||
return readPalette4Image(s, width, height);
|
||||
@ -1472,13 +1672,13 @@ static QImage readLayer(QDataStream &s, const DDSHeader &dds, const int format,
|
||||
case FormatG16R16F:
|
||||
return readRG16F(s, width, height);
|
||||
case FormatA16B16G16R16F:
|
||||
return readARGB16F(s, width, height);
|
||||
return readARGB16F(s, width, height, alphaPremul);
|
||||
case FormatR32F:
|
||||
return readR32F(s, width, height);
|
||||
case FormatG32R32F:
|
||||
return readRG32F(s, width, height);
|
||||
case FormatA32B32G32R32F:
|
||||
return readARGB32F(s, width, height);
|
||||
return readARGB32F(s, width, height, alphaPremul);
|
||||
case FormatD16Lockable:
|
||||
case FormatD32:
|
||||
case FormatD15S1:
|
||||
@ -1692,8 +1892,7 @@ static int formatByName(const QByteArray &name)
|
||||
|
||||
QDDSHandler::QDDSHandler() :
|
||||
m_header(),
|
||||
m_format(FormatA8R8G8B8),
|
||||
m_header10(),
|
||||
m_format(FormatUnknown),
|
||||
m_currentImage(0),
|
||||
m_scanState(ScanNotScanned)
|
||||
{
|
||||
@ -1717,7 +1916,10 @@ bool QDDSHandler::read(QImage *outImage)
|
||||
if (!ensureScanned() || device()->isSequential())
|
||||
return false;
|
||||
|
||||
qint64 pos = headerSize + mipmapOffset(m_header, m_format, m_currentImage);
|
||||
qint64 pos = headerSize;
|
||||
if (m_header.pixelFormat.fourCC == dx10Magic)
|
||||
pos += 20;
|
||||
pos += mipmapOffset(m_header, m_format, m_currentImage);
|
||||
if (!device()->seek(pos))
|
||||
return false;
|
||||
QDataStream s(device());
|
||||
@ -1736,33 +1938,18 @@ bool QDDSHandler::read(QImage *outImage)
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool QDDSHandler::write(const QImage &outImage)
|
||||
bool writeA8R8G8B8(const QImage &outImage, QDataStream &s)
|
||||
{
|
||||
if (m_format != FormatA8R8G8B8) {
|
||||
qWarning() << "Format" << formatName(m_format) << "is not supported";
|
||||
return false;
|
||||
}
|
||||
|
||||
QImage image = outImage;
|
||||
if(image.colorSpace().isValid())
|
||||
image = image.convertedToColorSpace(QColorSpace(QColorSpace::SRgb));
|
||||
image.convertTo(QImage::Format_ARGB32);
|
||||
if (image.isNull()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QDataStream s(device());
|
||||
s.setByteOrder(QDataStream::LittleEndian);
|
||||
|
||||
DDSHeader dds;
|
||||
// Filling header
|
||||
dds.magic = ddsMagic;
|
||||
dds.size = 124;
|
||||
dds.flags = DDSHeader::FlagCaps | DDSHeader::FlagHeight |
|
||||
DDSHeader::FlagWidth | DDSHeader::FlagPixelFormat;
|
||||
dds.height = image.height();
|
||||
dds.width = image.width();
|
||||
dds.pitchOrLinearSize = 128;
|
||||
DDSHeader::FlagWidth | DDSHeader::FlagPixelFormat |
|
||||
DDSHeader::FlagPitch;
|
||||
dds.height = outImage.height();
|
||||
dds.width = outImage.width();
|
||||
dds.pitchOrLinearSize = dds.width * 32 / 8;
|
||||
dds.depth = 0;
|
||||
dds.mipMapCount = 0;
|
||||
for (int i = 0; i < DDSHeader::ReservedCount; i++)
|
||||
@ -1784,22 +1971,417 @@ bool QDDSHandler::write(const QImage &outImage)
|
||||
dds.pixelFormat.bBitMask = 0x000000ff;
|
||||
|
||||
s << dds;
|
||||
for (int height = 0; height < image.height(); height++) {
|
||||
for (int width = 0; width < image.width(); width++) {
|
||||
QRgb pixel = image.pixel(width, height);
|
||||
quint32 color;
|
||||
quint8 alpha = qAlpha(pixel);
|
||||
quint8 red = qRed(pixel);
|
||||
quint8 green = qGreen(pixel);
|
||||
quint8 blue = qBlue(pixel);
|
||||
color = (alpha << 24) + (red << 16) + (green << 8) + blue;
|
||||
s << color;
|
||||
if (s.status() != QDataStream::Ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ScanLineConverter slc(QImage::Format_ARGB32);
|
||||
if(outImage.colorSpace().isValid())
|
||||
slc.setTargetColorSpace(QColorSpace(QColorSpace::SRgb));
|
||||
|
||||
for (int y = 0, h = outImage.height(); y < h; ++y) {
|
||||
const QRgb *scanLine = reinterpret_cast<const QRgb*>(slc.convertedScanLine(outImage, y));
|
||||
if (scanLine == nullptr) {
|
||||
return false;
|
||||
}
|
||||
for (int x = 0, w = outImage.width(); x < w; ++x) {
|
||||
s << quint32(scanLine[x]);
|
||||
}
|
||||
if (s.status() != QDataStream::Ok) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool writeR8G8B8(const QImage &outImage, QDataStream &s)
|
||||
{
|
||||
DDSHeader dds;
|
||||
// Filling header
|
||||
dds.magic = ddsMagic;
|
||||
dds.size = 124;
|
||||
dds.flags = DDSHeader::FlagCaps | DDSHeader::FlagHeight |
|
||||
DDSHeader::FlagWidth | DDSHeader::FlagPixelFormat |
|
||||
DDSHeader::FlagPitch;
|
||||
dds.height = outImage.height();
|
||||
dds.width = outImage.width();
|
||||
dds.pitchOrLinearSize = dds.width * 24 / 8;
|
||||
dds.depth = 1;
|
||||
dds.mipMapCount = 0;
|
||||
for (int i = 0; i < DDSHeader::ReservedCount; i++)
|
||||
dds.reserved1[i] = 0;
|
||||
dds.caps = DDSHeader::CapsTexture;
|
||||
dds.caps2 = 0;
|
||||
dds.caps3 = 0;
|
||||
dds.caps4 = 0;
|
||||
dds.reserved2 = 0;
|
||||
|
||||
// Filling pixelformat
|
||||
dds.pixelFormat.size = 32;
|
||||
dds.pixelFormat.flags = DDSPixelFormat::FlagRGB;
|
||||
dds.pixelFormat.fourCC = 0;
|
||||
dds.pixelFormat.rgbBitCount = 24;
|
||||
dds.pixelFormat.aBitMask = 0x00000000;
|
||||
dds.pixelFormat.rBitMask = 0x00ff0000;
|
||||
dds.pixelFormat.gBitMask = 0x0000ff00;
|
||||
dds.pixelFormat.bBitMask = 0x000000ff;
|
||||
|
||||
s << dds;
|
||||
if (s.status() != QDataStream::Ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ScanLineConverter slc(QImage::Format_RGB888);
|
||||
if(outImage.colorSpace().isValid())
|
||||
slc.setTargetColorSpace(QColorSpace(QColorSpace::SRgb));
|
||||
|
||||
for (int y = 0, h = outImage.height(); y < h; ++y) {
|
||||
const quint8 *scanLine = reinterpret_cast<const quint8*>(slc.convertedScanLine(outImage, y));
|
||||
if (scanLine == nullptr) {
|
||||
return false;
|
||||
}
|
||||
for (int x = 0, w = outImage.width(); x < w; ++x) {
|
||||
size_t x3 = size_t(x) * 3;
|
||||
s << scanLine[x3 + 2];
|
||||
s << scanLine[x3 + 1];
|
||||
s << scanLine[x3];
|
||||
}
|
||||
if (s.status() != QDataStream::Ok) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool writeL8(const QImage &outImage, QDataStream &s)
|
||||
{
|
||||
DDSHeader dds;
|
||||
// Filling header
|
||||
dds.magic = ddsMagic;
|
||||
dds.size = 124;
|
||||
dds.flags = DDSHeader::FlagCaps | DDSHeader::FlagHeight |
|
||||
DDSHeader::FlagWidth | DDSHeader::FlagPixelFormat |
|
||||
DDSHeader::FlagPitch;
|
||||
dds.height = outImage.height();
|
||||
dds.width = outImage.width();
|
||||
dds.pitchOrLinearSize = dds.width;
|
||||
dds.depth = 1;
|
||||
dds.mipMapCount = 0;
|
||||
for (int i = 0; i < DDSHeader::ReservedCount; i++)
|
||||
dds.reserved1[i] = 0;
|
||||
dds.caps = DDSHeader::CapsTexture;
|
||||
dds.caps2 = 0;
|
||||
dds.caps3 = 0;
|
||||
dds.caps4 = 0;
|
||||
dds.reserved2 = 0;
|
||||
|
||||
// Filling pixelformat
|
||||
dds.pixelFormat.size = 32;
|
||||
dds.pixelFormat.flags = DDSPixelFormat::FlagLuminance | DDSPixelFormat::FlagRGB;
|
||||
dds.pixelFormat.fourCC = 0;
|
||||
dds.pixelFormat.rgbBitCount = 8;
|
||||
dds.pixelFormat.aBitMask = 0x00000000;
|
||||
dds.pixelFormat.rBitMask = 0x000000ff;
|
||||
dds.pixelFormat.gBitMask = 0x00000000;
|
||||
dds.pixelFormat.bBitMask = 0x00000000;
|
||||
|
||||
s << dds;
|
||||
if (s.status() != QDataStream::Ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ScanLineConverter slc(QImage::Format_Grayscale8);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
if(outImage.colorSpace().isValid())
|
||||
slc.setTargetColorSpace(QColorSpace(QPointF(0.3127, 0.3291), QColorSpace::TransferFunction::SRgb));
|
||||
#endif
|
||||
|
||||
for (int y = 0, h = outImage.height(); y < h; ++y) {
|
||||
const quint8 *scanLine = reinterpret_cast<const quint8*>(slc.convertedScanLine(outImage, y));
|
||||
if (scanLine == nullptr) {
|
||||
return false;
|
||||
}
|
||||
for (int x = 0, w = outImage.width(); x < w; ++x) {
|
||||
s << scanLine[x];
|
||||
}
|
||||
if (s.status() != QDataStream::Ok) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool writeP8(const QImage &image, QDataStream &s)
|
||||
{
|
||||
QImage outImage = image;
|
||||
// indexed not supported by ScanlineConverter class
|
||||
if (image.format() != QImage::Format_Indexed8) {
|
||||
if (image.colorSpace().isValid())
|
||||
outImage.convertToColorSpace(QColorSpace(QColorSpace::SRgb));
|
||||
outImage = outImage.convertToFormat(QImage::Format_Indexed8);
|
||||
}
|
||||
|
||||
DDSHeader dds;
|
||||
// Filling header
|
||||
dds.magic = ddsMagic;
|
||||
dds.size = 124;
|
||||
dds.flags = DDSHeader::FlagCaps | DDSHeader::FlagHeight |
|
||||
DDSHeader::FlagWidth | DDSHeader::FlagPixelFormat |
|
||||
DDSHeader::FlagPitch;
|
||||
dds.height = outImage.height();
|
||||
dds.width = outImage.width();
|
||||
dds.pitchOrLinearSize = dds.width;
|
||||
dds.depth = 1;
|
||||
dds.mipMapCount = 0;
|
||||
for (int i = 0; i < DDSHeader::ReservedCount; i++)
|
||||
dds.reserved1[i] = 0;
|
||||
dds.caps = DDSHeader::CapsTexture;
|
||||
dds.caps2 = 0;
|
||||
dds.caps3 = 0;
|
||||
dds.caps4 = 0;
|
||||
dds.reserved2 = 0;
|
||||
|
||||
// Filling pixelformat
|
||||
dds.pixelFormat.size = 32;
|
||||
dds.pixelFormat.flags = DDSPixelFormat::FlagPaletteIndexed8;
|
||||
dds.pixelFormat.fourCC = 0;
|
||||
dds.pixelFormat.rgbBitCount = 8;
|
||||
dds.pixelFormat.aBitMask = 0x00000000;
|
||||
dds.pixelFormat.rBitMask = 0x00000000;
|
||||
dds.pixelFormat.gBitMask = 0x00000000;
|
||||
dds.pixelFormat.bBitMask = 0x00000000;
|
||||
|
||||
s << dds;
|
||||
|
||||
QList<QRgb> palette = outImage.colorTable();
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
quint8 r = 0, g = 0, b = 0, a = 0xff;
|
||||
if (i < palette.size()) {
|
||||
auto&& rgba = palette.at(i);
|
||||
r = qRed(rgba);
|
||||
g = qGreen(rgba);
|
||||
b = qBlue(rgba);
|
||||
a = qAlpha(rgba);
|
||||
}
|
||||
s << r;
|
||||
s << g;
|
||||
s << b;
|
||||
s << a;
|
||||
}
|
||||
|
||||
if (s.status() != QDataStream::Ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int y = 0, h = outImage.height(); y < h; ++y) {
|
||||
const quint8 *scanLine = reinterpret_cast<const quint8*>(outImage.constScanLine(y));
|
||||
if (scanLine == nullptr) {
|
||||
return false;
|
||||
}
|
||||
for (int x = 0, w = outImage.width(); x < w; ++x) {
|
||||
s << scanLine[x];
|
||||
}
|
||||
if (s.status() != QDataStream::Ok) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool writeA16B16G16R16F(const QImage &outImage, QDataStream &s)
|
||||
{
|
||||
DDSHeader dds;
|
||||
// Filling header
|
||||
dds.magic = ddsMagic;
|
||||
dds.size = 124;
|
||||
dds.flags = DDSHeader::FlagCaps | DDSHeader::FlagHeight |
|
||||
DDSHeader::FlagWidth | DDSHeader::FlagPitch |
|
||||
DDSHeader::FlagPixelFormat;
|
||||
dds.height = outImage.height();
|
||||
dds.width = outImage.width();
|
||||
dds.pitchOrLinearSize = dds.width * 64 / 8;
|
||||
dds.depth = 1;
|
||||
dds.mipMapCount = 0;
|
||||
for (int i = 0; i < DDSHeader::ReservedCount; i++)
|
||||
dds.reserved1[i] = 0;
|
||||
dds.caps = DDSHeader::CapsTexture;
|
||||
dds.caps2 = 0;
|
||||
dds.caps3 = 0;
|
||||
dds.caps4 = 0;
|
||||
dds.reserved2 = 0;
|
||||
|
||||
// Filling pixelformat
|
||||
dds.pixelFormat.size = 32;
|
||||
dds.pixelFormat.flags = DDSPixelFormat::FlagFourCC;
|
||||
dds.pixelFormat.fourCC = 113;
|
||||
dds.pixelFormat.rgbBitCount = 0;
|
||||
dds.pixelFormat.aBitMask = 0;
|
||||
dds.pixelFormat.rBitMask = 0;
|
||||
dds.pixelFormat.gBitMask = 0;
|
||||
dds.pixelFormat.bBitMask = 0;
|
||||
|
||||
s << dds;
|
||||
if (s.status() != QDataStream::Ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ScanLineConverter slc(QImage::Format_RGBA16FPx4);
|
||||
if(outImage.colorSpace().isValid())
|
||||
slc.setTargetColorSpace(QColorSpace(QColorSpace::SRgbLinear));
|
||||
|
||||
for (int y = 0, h = outImage.height(); y < h; ++y) {
|
||||
const quint16 *scanLine = reinterpret_cast<const quint16*>(slc.convertedScanLine(outImage, y));
|
||||
if (scanLine == nullptr) {
|
||||
return false;
|
||||
}
|
||||
for (int x = 0, w = outImage.width(); x < w; ++x) {
|
||||
size_t x4 = size_t(x) * 4;
|
||||
s << scanLine[x4];
|
||||
s << scanLine[x4 + 1];
|
||||
s << scanLine[x4 + 2];
|
||||
s << scanLine[x4 + 3];
|
||||
}
|
||||
if (s.status() != QDataStream::Ok) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool writeA32B32G32R32F(const QImage &outImage, QDataStream &s)
|
||||
{
|
||||
DDSHeader dds;
|
||||
// Filling header
|
||||
dds.magic = ddsMagic;
|
||||
dds.size = 124;
|
||||
dds.flags = DDSHeader::FlagCaps | DDSHeader::FlagHeight |
|
||||
DDSHeader::FlagWidth | DDSHeader::FlagPitch |
|
||||
DDSHeader::FlagPixelFormat;
|
||||
dds.height = outImage.height();
|
||||
dds.width = outImage.width();
|
||||
dds.pitchOrLinearSize = dds.width * 128 / 8;
|
||||
dds.depth = 1;
|
||||
dds.mipMapCount = 0;
|
||||
for (int i = 0; i < DDSHeader::ReservedCount; i++)
|
||||
dds.reserved1[i] = 0;
|
||||
dds.caps = DDSHeader::CapsTexture;
|
||||
dds.caps2 = 0;
|
||||
dds.caps3 = 0;
|
||||
dds.caps4 = 0;
|
||||
dds.reserved2 = 0;
|
||||
|
||||
// Filling pixelformat
|
||||
dds.pixelFormat.size = 32;
|
||||
dds.pixelFormat.flags = DDSPixelFormat::FlagFourCC;
|
||||
dds.pixelFormat.fourCC = 116;
|
||||
dds.pixelFormat.rgbBitCount = 0;
|
||||
dds.pixelFormat.aBitMask = 0;
|
||||
dds.pixelFormat.rBitMask = 0;
|
||||
dds.pixelFormat.gBitMask = 0;
|
||||
dds.pixelFormat.bBitMask = 0;
|
||||
|
||||
s << dds;
|
||||
if (s.status() != QDataStream::Ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ScanLineConverter slc(QImage::Format_RGBA32FPx4);
|
||||
if(outImage.colorSpace().isValid())
|
||||
slc.setTargetColorSpace(QColorSpace(QColorSpace::SRgbLinear));
|
||||
|
||||
for (int y = 0, h = outImage.height(); y < h; ++y) {
|
||||
const quint32 *scanLine = reinterpret_cast<const quint32*>(slc.convertedScanLine(outImage, y));
|
||||
if (scanLine == nullptr) {
|
||||
return false;
|
||||
}
|
||||
for (int x = 0, w = outImage.width(); x < w; ++x) {
|
||||
size_t x4 = size_t(x) * 4;
|
||||
s << scanLine[x4];
|
||||
s << scanLine[x4 + 1];
|
||||
s << scanLine[x4 + 2];
|
||||
s << scanLine[x4 + 3];
|
||||
}
|
||||
if (s.status() != QDataStream::Ok) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QDDSHandler::write(const QImage &outImage)
|
||||
{
|
||||
if (outImage.isNull() || device() == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QDataStream s(device());
|
||||
s.setByteOrder(QDataStream::LittleEndian);
|
||||
|
||||
int format = m_format;
|
||||
if (format == FormatUnknown) {
|
||||
switch (outImage.format()) {
|
||||
case QImage::Format_RGBX16FPx4:
|
||||
case QImage::Format_RGBA16FPx4:
|
||||
case QImage::Format_RGBA16FPx4_Premultiplied:
|
||||
format = FormatA16B16G16R16F;
|
||||
break;
|
||||
|
||||
case QImage::Format_RGBX32FPx4:
|
||||
case QImage::Format_RGBA32FPx4:
|
||||
case QImage::Format_RGBA32FPx4_Premultiplied:
|
||||
format = FormatA32B32G32R32F;
|
||||
break;
|
||||
|
||||
case QImage::Format_Grayscale16:
|
||||
case QImage::Format_Grayscale8:
|
||||
case QImage::Format_Mono:
|
||||
case QImage::Format_MonoLSB:
|
||||
format = FormatL8;
|
||||
break;
|
||||
|
||||
case QImage::Format_Indexed8:
|
||||
format = FormatP8;
|
||||
break;
|
||||
|
||||
default:
|
||||
format = outImage.hasAlphaChannel() ? FormatA8R8G8B8 : FormatR8G8B8;
|
||||
}
|
||||
}
|
||||
|
||||
if (format == FormatA8R8G8B8) {
|
||||
return writeA8R8G8B8(outImage, s);
|
||||
}
|
||||
|
||||
if (format == FormatR8G8B8) {
|
||||
return writeR8G8B8(outImage, s);
|
||||
}
|
||||
|
||||
if (format == FormatL8) {
|
||||
return writeL8(outImage, s);
|
||||
}
|
||||
|
||||
if (format == FormatP8) {
|
||||
return writeP8(outImage, s);
|
||||
}
|
||||
|
||||
if (format == FormatA16B16G16R16F) {
|
||||
return writeA16B16G16R16F(outImage, s);
|
||||
}
|
||||
|
||||
if (format == FormatA32B32G32R32F) {
|
||||
return writeA32B32G32R32F(outImage, s);
|
||||
}
|
||||
|
||||
qWarning() << "Format" << formatName(format) << "is not supported";
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariant QDDSHandler::option(QImageIOHandler::ImageOption option) const
|
||||
{
|
||||
if (!supportsOption(option)) {
|
||||
@ -1808,7 +2390,14 @@ QVariant QDDSHandler::option(QImageIOHandler::ImageOption option) const
|
||||
|
||||
// *** options that do not require a valid stream ***
|
||||
if (option == QImageIOHandler::SupportedSubTypes) {
|
||||
return QVariant::fromValue(QList<QByteArray>() << formatName(FormatA8R8G8B8));
|
||||
return QVariant::fromValue(QList<QByteArray>()
|
||||
<< QByteArrayLiteral("Automatic")
|
||||
<< formatName(FormatA8R8G8B8)
|
||||
<< formatName(FormatR8G8B8)
|
||||
<< formatName(FormatL8)
|
||||
<< formatName(FormatP8)
|
||||
<< formatName(FormatA16B16G16R16F)
|
||||
<< formatName(FormatA32B32G32R32F));
|
||||
}
|
||||
|
||||
// *** options that require a valid stream ***
|
||||
@ -1821,7 +2410,7 @@ QVariant QDDSHandler::option(QImageIOHandler::ImageOption option) const
|
||||
}
|
||||
|
||||
if (option == QImageIOHandler::SubType) {
|
||||
return formatName(m_format);
|
||||
return m_format == FormatUnknown ? QByteArrayLiteral("Automatic") : formatName(m_format);
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
@ -1832,8 +2421,6 @@ void QDDSHandler::setOption(QImageIOHandler::ImageOption option, const QVariant
|
||||
if (option == QImageIOHandler::SubType) {
|
||||
const QByteArray subType = value.toByteArray();
|
||||
m_format = formatByName(subType.toUpper());
|
||||
if (m_format == FormatUnknown)
|
||||
qWarning() << "unknown format" << subType;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1899,8 +2486,6 @@ bool QDDSHandler::ensureScanned() const
|
||||
QDataStream s(device());
|
||||
s.setByteOrder(QDataStream::LittleEndian);
|
||||
s >> that->m_header;
|
||||
if (m_header.pixelFormat.fourCC == dx10Magic)
|
||||
s >> that->m_header10;
|
||||
|
||||
device()->seek(oldPos);
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2015 The Qt Company Ltd
|
||||
SPDX-FileCopyrightText: 2013 Ivan Komissarov
|
||||
SPDX-FileCopyrightText: 2024 Mirco Miranda
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only
|
||||
*/
|
||||
@ -37,6 +38,15 @@ struct DDSPixelFormat
|
||||
quint32 aBitMask;
|
||||
};
|
||||
|
||||
struct DDSHeaderDX10
|
||||
{
|
||||
quint32 dxgiFormat = 0;
|
||||
quint32 resourceDimension = 0;
|
||||
quint32 miscFlag = 0;
|
||||
quint32 arraySize = 0;
|
||||
quint32 miscFlags2 = 0;
|
||||
};
|
||||
|
||||
struct DDSHeader
|
||||
{
|
||||
enum DDSFlags {
|
||||
@ -84,15 +94,7 @@ struct DDSHeader
|
||||
quint32 caps3;
|
||||
quint32 caps4;
|
||||
quint32 reserved2;
|
||||
};
|
||||
|
||||
struct DDSHeaderDX10
|
||||
{
|
||||
quint32 dxgiFormat;
|
||||
quint32 resourceDimension;
|
||||
quint32 miscFlag;
|
||||
quint32 arraySize;
|
||||
quint32 reserved;
|
||||
DDSHeaderDX10 header10;
|
||||
};
|
||||
|
||||
class QDDSHandler : public QImageIOHandler
|
||||
@ -126,7 +128,6 @@ private:
|
||||
|
||||
DDSHeader m_header;
|
||||
int m_format;
|
||||
DDSHeaderDX10 m_header10;
|
||||
int m_currentImage;
|
||||
mutable ScanState m_scanState;
|
||||
};
|
||||
@ -136,8 +137,8 @@ class QDDSPlugin : public QImageIOPlugin
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QImageIOHandlerFactoryInterface" FILE "dds.json")
|
||||
public:
|
||||
Capabilities capabilities(QIODevice *device, const QByteArray &format) const;
|
||||
QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const;
|
||||
Capabilities capabilities(QIODevice *device, const QByteArray &format) const override;
|
||||
QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const override;
|
||||
};
|
||||
|
||||
#endif // QDDSHANDLER_H
|
||||
|