6#include <QtQuick3DRuntimeRender/private/qssgrenderloadedtexture_p.h>
7#include <QtQuick3DRuntimeRender/private/qssgrendererutil_p.h>
8#include <QtQuick3DRuntimeRender/private/qssgruntimerenderlogging_p.h>
9#include <QtQuick3DRuntimeRender/private/qssgrendertexturedata_p.h>
10#include <QtGui/QImageReader>
11#include <QtGui/QColorSpace>
12#include <QtCore/qnumeric.h>
15#include <QtQuick3DUtils/private/qssgutils_p.h>
17#include <private/qtexturefilereader_p.h>
19#define TINYEXR_IMPLEMENTATION
20#define TINYEXR_USE_MINIZ 0
21#define TINYEXR_USE_THREAD 1
24QT_WARNING_DISABLE_CLANG(
"-Wunused-parameter")
25QT_WARNING_DISABLE_CLANG(
"-Wunused-function")
26QT_WARNING_DISABLE_GCC(
"-Wunused-parameter")
27QT_WARNING_DISABLE_GCC(
"-Wunused-function")
35QSharedPointer<QIODevice> QSSGInputUtil::getStreamForFile(
const QString &inPath,
bool inQuiet, QString *outPath)
37 QFile *file =
nullptr;
38 QString tryPath = inPath;
39 if (tryPath.startsWith(QLatin1String(
"qrc:/"))) {
40 tryPath = inPath.mid(3);
41 }
else if (tryPath.startsWith(QLatin1String(
"file://"))) {
42 tryPath = inPath.mid(7);
45 QFileInfo fi(tryPath);
46 bool found = fi.exists();
47 if (!found && fi.isNativePath()) {
48 tryPath.prepend(QLatin1String(
":/"));
53 QString filePath = fi.canonicalFilePath();
54 file =
new QFile(filePath);
55 if (file->open(QIODevice::ReadOnly)) {
63 if (!file && !inQuiet)
64 qCWarning(WARNING,
"Failed to find file: %s", qPrintable(inPath));
65 return QSharedPointer<QIODevice>(file);
68QSharedPointer<QIODevice> QSSGInputUtil::getStreamForTextureFile(
const QString &inPath,
bool inQuiet,
69 QString *outPath, FileType *outFileType)
71 static const QList<QByteArray> hdrFormats = QList<QByteArray>({
"hdr",
"exr" });
72 static const QList<QByteArray> textureFormats = QTextureFileReader::supportedFileFormats();
73 static const QList<QByteArray> imageFormats = QImageReader::supportedImageFormats();
74 static const QList<QByteArray> allFormats = textureFormats + hdrFormats + imageFormats;
78 QSharedPointer<QIODevice> stream = getStreamForFile(inPath,
true, &filePath);
80 ext = QFileInfo(filePath).suffix().toLatin1().toLower();
82 for (
const QByteArray &format : allFormats) {
83 QString tryName = inPath + QLatin1Char(
'.') + QLatin1String(format);
84 stream = getStreamForFile(tryName,
true, &filePath);
95 FileType type = UnknownFile;
96 if (hdrFormats.contains(ext))
98 else if (textureFormats.contains(ext))
100 else if (imageFormats.contains(ext))
104 }
else if (!inQuiet) {
105 qCWarning(WARNING,
"Failed to find texture file for: %s", qPrintable(inPath));
112 switch (internalFormat) {
114 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::R8);
116 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::R16);
118 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::R16F);
120 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::R32I);
122 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::R32UI);
124 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::R32F);
126 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RG8);
128 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA8);
130 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB8);
132 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8);
134 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8A8);
136 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB565);
138 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::Alpha8);
140 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::Luminance8);
142 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::Luminance16);
144 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::LuminanceAlpha8);
146 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA16F);
148 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RG16F);
150 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RG32F);
152 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB32F);
154 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA32F);
156 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::R11G11B10);
158 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB9E5);
160 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB10_A2);
162 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB16F);
164 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA32UI);
166 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB32UI);
168 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA16UI);
170 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB16UI);
172 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA8UI);
174 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB8UI);
176 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA32I);
178 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB32I);
180 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA16I);
182 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB16I);
184 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA8I);
186 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB8I);
188 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_DXT1);
190 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB_DXT1);
192 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_DXT3);
194 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_DXT5);
196 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::R11_EAC_UNorm);
198 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::R11_EAC_SNorm);
200 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RG11_EAC_UNorm);
202 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RG11_EAC_SNorm);
204 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB8_ETC2);
206 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_ETC2);
208 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB8_PunchThrough_Alpha1_ETC2);
210 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_PunchThrough_Alpha1_ETC2);
212 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA8_ETC2_EAC);
214 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ETC2_EAC);
216 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_4x4);
218 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_5x4);
220 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_5x5);
222 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_6x5);
224 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_6x6);
226 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_8x5);
228 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_8x6);
230 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_8x8);
232 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_10x5);
234 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_10x6);
236 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_10x8);
238 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_10x10);
240 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_12x10);
242 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_12x12);
244 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_4x4);
246 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_5x4);
248 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_5x5);
250 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_6x5);
252 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_6x6);
254 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_8x5);
256 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_8x6);
258 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_8x8);
260 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_10x5);
262 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_10x6);
264 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_10x8);
266 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_10x10);
268 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_12x10);
270 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_12x12);
272 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::Depth16);
274 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::Depth24);
276 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::Depth32);
278 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::Depth24Stencil8);
280 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::Unknown);
286 QImage image(inPath);
289 const QPixelFormat pixFormat = image.pixelFormat();
290 QImage::Format targetFormat = QImage::Format_RGBA8888_Premultiplied;
291 if (image.colorCount())
292 targetFormat = QImage::Format_RGBA8888;
293 else if (pixFormat.channelCount() == 1)
294 targetFormat = QImage::Format_Grayscale8;
295 else if (pixFormat.alphaUsage() == QPixelFormat::IgnoresAlpha)
296 targetFormat = QImage::Format_RGBX8888;
297 else if (pixFormat.premultiplied() == QPixelFormat::NotPremultiplied)
298 targetFormat = QImage::Format_RGBA8888;
300 image.convertTo(targetFormat);
306QSSGLoadedTexture *QSSGLoadedTexture::loadQImage(
const QString &inPath, qint32 flipVertical)
308 QImage image = loadImage(inPath, flipVertical);
311 QSSGLoadedTexture *retval =
new QSSGLoadedTexture;
312 retval->width = image.width();
313 retval->height = image.height();
314 retval->components = image.pixelFormat().channelCount();
315 retval->image = image;
316 retval->data = (
void *)retval->image.bits();
317 retval->dataSizeInBytes = image.sizeInBytes();
318 retval->setFormatFromComponents();
320 retval->isSRGB = image.colorSpace().transferFunction() != QColorSpace::TransferFunction::Linear;
325QSSGLoadedTexture *QSSGLoadedTexture::loadCompressedImage(
const QString &inPath)
327 QSSGLoadedTexture *retval =
nullptr;
330 QFile imageFile(inPath);
331 if (!imageFile.open(QIODevice::ReadOnly)) {
332 qWarning() <<
"Could not open image file: " << inPath;
335 auto reader =
new QTextureFileReader(&imageFile, inPath);
337 if (!reader->canRead()) {
338 qWarning() <<
"Unable to read image file: " << inPath;
342 retval =
new QSSGLoadedTexture;
343 retval->textureFileData = reader->read();
346 retval->width = retval->textureFileData.size().width();
347 retval->height = retval->textureFileData.size().height();
348 auto glFormat = retval->textureFileData.glInternalFormat()
349 ? retval->textureFileData.glInternalFormat()
350 : retval->textureFileData.glFormat();
351 retval->format = fromGLtoTextureFormat(glFormat);
354 if ((retval->format.format >= QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_4x4) &&
355 (retval->format.format <= QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_12x12))
356 retval->isSRGB =
true;
366typedef unsigned char RGBE[4];
373#define MAXELEN 0x7fff
378qsizetype calculateDataSize(qsizetype width, qsizetype height, qsizetype depth, qsizetype bytesPerPixel)
380 if (width <= 0 || height <= 0 || depth < 0 || bytesPerPixel <= 0)
384 if (qMulOverflow(width, bytesPerPixel, &line))
386 const qsizetype pitch = (line + 3) & ~qsizetype(3);
388 qsizetype dataSize = 0;
389 if (qMulOverflow(height, pitch, &dataSize))
391 if (depth > 0 && qMulOverflow(dataSize, depth, &dataSize))
394 if (dataSize > qsizetype(std::numeric_limits<quint32>::max()))
400float convertComponent(
int exponent,
int val)
402 float v = val / (256.0f);
403 float d = powf(2.0f, (
float)exponent - 128.0f);
410bool decrunchScanline(
const char *&p,
const char *pEnd, RGBE *scanline,
int w)
415 scanline[0][
R] = *p++;
416 scanline[0][
G] = *p++;
417 scanline[0][
B] = *p++;
418 scanline[0][
E] = *p++;
420 if (scanline[0][
R] == 2 && scanline[0][
G] == 2 && scanline[0][
B] < 128) {
422 for (
int channel = 0; channel < 4; ++channel) {
427 unsigned char c = *p++;
429 const int repCount = c & 127;
430 if (p >= pEnd || repCount > w - x)
433 for (
int i = 0; i < repCount; ++i)
434 scanline[x++][channel] = c;
436 if (c > w - x || pEnd - p < c)
438 for (
int i = 0; i < c; ++i)
439 scanline[x++][channel] = *p++;
450 scanline[x][
R] = *p++;
451 scanline[x][
G] = *p++;
452 scanline[x][
B] = *p++;
453 scanline[x][
E] = *p++;
455 if (scanline[x][
R] == 1 && scanline[x][
G] == 1 && scanline[x][
B] == 1) {
459 if (bitshift > 24 || scanline[x][
E] > ((w - x) >> bitshift))
461 const int repCount =
int(scanline[x][
E]) << bitshift;
462 for (
int i = 0; i < repCount; ++i, ++x)
463 memcpy(scanline[x], scanline[x - 1], 4);
475void decodeScanlineToTexture(RGBE *scanline,
int width,
void *outBuf, qsizetype offset, QSSGRenderTextureFormat inFormat)
477 quint8 *target =
reinterpret_cast<quint8 *>(outBuf);
480 if (inFormat == QSSGRenderTextureFormat::RGBE8) {
481 memcpy(target, scanline, size_t(width) * 4);
484 for (
int i = 0; i < width; ++i) {
485 rgbaF32[
R] = convertComponent(scanline[i][
E], scanline[i][
R]);
486 rgbaF32[
G] = convertComponent(scanline[i][
E], scanline[i][
G]);
487 rgbaF32[
B] = convertComponent(scanline[i][
E], scanline[i][
B]);
490 inFormat.encodeToPixel(rgbaF32, target, i * inFormat.getSizeofFormat());
495QSSGLoadedTexture *loadRadianceHdr(
const QSharedPointer<QIODevice> &source,
const QSSGRenderTextureFormat &format)
497 QSSGLoadedTexture *imageData =
nullptr;
501 if (source->read(sig,
sizeof(sig)) ==
sizeof(sig) && !memcmp(sig,
"#?RADIANCE\n",
sizeof(sig))) {
502 QByteArray buf = source->readAll();
503 const char *p = buf.constData();
504 const char *pEnd = p + buf.size();
513 if (line.startsWith(QByteArrayLiteral(
"FORMAT="))) {
514 const QByteArray format = line.mid(7).trimmed();
515 if (format != QByteArrayLiteral(
"32-bit_rle_rgbe")) {
516 qWarning(
"HDR format '%s' is not supported", format.constData());
526 qWarning(
"Malformed HDR image data at property strings");
538 qWarning(
"Malformed HDR image data at resolution string");
546 if (!sscanf_s(line.constData(),
"-Y %d +X %d", &height, &width)) {
548 if (!sscanf(line.constData(),
"-Y %d +X %d", &height, &width)) {
550 qWarning(
"Unsupported HDR resolution string '%s'", line.constData());
553 if (width <= 0 || height <= 0 || width > 65536 || height > 65536) {
554 qWarning(
"Invalid HDR resolution %dx%d", width, height);
558 const int bytesPerPixel = format.getSizeofFormat();
559 const qsizetype dataSize = calculateDataSize(width, height, 0, bytesPerPixel);
561 qWarning(
"HDR image dimensions %dx%d are too large", width, height);
564 void *data = ::calloc(1, size_t(dataSize));
566 qWarning(
"Failed to allocate %lld bytes for HDR image", qlonglong(dataSize));
569 imageData =
new QSSGLoadedTexture;
570 imageData->dataSizeInBytes = quint32(dataSize);
571 imageData->data = data;
572 imageData->width = width;
573 imageData->height = height;
574 imageData->format = format;
575 imageData->components = format.getNumberOfComponent();
576 imageData->isSRGB =
false;
579 RGBE *scanline =
new RGBE[width];
583 for (
int y = 0; y < height; ++y) {
584 const qsizetype byteOffset = qsizetype(height - 1 - y) * width * bytesPerPixel;
585 if (!decrunchScanline(p, pEnd, scanline, width)) {
586 qWarning(
"Malformed HDR scanline data at row %d of %d", y, height);
589 decodeScanlineToTexture(scanline, width, imageData->data, byteOffset, format);
598QSSGLoadedTexture *loadExr(
const QSharedPointer<QIODevice> &source,
const QSSGRenderTextureFormat format)
600 QSSGLoadedTexture *imageData =
nullptr;
602 char versionBuffer[tinyexr::kEXRVersionSize];
604 auto size = source->read(versionBuffer, tinyexr::kEXRVersionSize);
606 if (size != tinyexr::kEXRVersionSize)
609 EXRVersion exrVersion;
610 if (ParseEXRVersionFromMemory(&exrVersion,
reinterpret_cast<
unsigned char *>(versionBuffer), tinyexr::kEXRVersionSize) != TINYEXR_SUCCESS)
614 if (exrVersion.multipart)
619 QByteArray buf = source->readAll();
620 const char *err =
nullptr;
623 InitEXRHeader(&exrHeader);
624 if (ParseEXRHeaderFromMemory(&exrHeader,
626 reinterpret_cast<
const unsigned char *>(buf.constData()),
628 &err) != TINYEXR_SUCCESS) {
629 qWarning(
"Failed to parse EXR Header with error: '%s'", err);
630 FreeEXRErrorMessage(err);
635 for (
int i = 0; i < exrHeader.num_channels; i++) {
636 if (exrHeader.pixel_types[i] == TINYEXR_PIXELTYPE_HALF)
637 exrHeader.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT;
643 InitEXRImage(&exrImage);
644 if (LoadEXRImageFromMemory(&exrImage,
646 reinterpret_cast<
const unsigned char *>(buf.constData()),
648 &err) != TINYEXR_SUCCESS) {
649 qWarning(
"Failed to load EXR Image with error: '%s'", err);
650 FreeEXRHeader(&exrHeader);
651 FreeEXRErrorMessage(err);
661 for (
int c = 0; c < exrHeader.num_channels; c++) {
662 if (strcmp(exrHeader.channels[c].name,
"R") == 0)
664 else if (strcmp(exrHeader.channels[c].name,
"G") == 0)
666 else if (strcmp(exrHeader.channels[c].name,
"B") == 0)
668 else if (strcmp(exrHeader.channels[c].name,
"A") == 0)
671 const bool isSingleChannel = exrHeader.num_channels == 1;
677 if (!isSingleChannel && (idxR == -1 || idxG == -1 || idxB == -1)) {
678 qWarning(
"EXR image has no R, G and B channels");
679 FreeEXRImage(&exrImage);
680 FreeEXRHeader(&exrHeader);
685 const int bytesPerPixel = format.getSizeofFormat();
686 const qsizetype dataSize = calculateDataSize(exrImage.width, exrImage.height, 0, bytesPerPixel);
688 qWarning(
"EXR image dimensions %dx%d are too large", exrImage.width, exrImage.height);
689 FreeEXRImage(&exrImage);
690 FreeEXRHeader(&exrHeader);
693 void *data = ::malloc(size_t(dataSize));
695 qWarning(
"Failed to allocate %lld bytes for EXR image", qlonglong(dataSize));
696 FreeEXRImage(&exrImage);
697 FreeEXRHeader(&exrHeader);
700 imageData =
new QSSGLoadedTexture;
701 imageData->dataSizeInBytes = quint32(dataSize);
702 imageData->data = data;
703 imageData->width = exrImage.width;
704 imageData->height = exrImage.height;
705 imageData->format = format;
706 imageData->components = format.getNumberOfComponent();
707 imageData->isSRGB =
false;
709 quint8 *target =
reinterpret_cast<quint8 *>(imageData->data);
713 if (exrHeader.tiled) {
714 for (
int it = 0; it < exrImage.num_tiles; it++) {
715 for (
int j = 0; j < exrHeader.tile_size_y; j++)
716 for (
int i = 0; i < exrHeader.tile_size_x; i++) {
718 exrImage.tiles[it].offset_x * exrHeader.tile_size_x + i;
720 exrImage.tiles[it].offset_y * exrHeader.tile_size_y + j;
721 const int inverseJJ = std::abs(jj - (exrImage.height - 1));
722 const int idx = ii + inverseJJ * exrImage.width;
725 if (ii >= exrImage.width) {
728 if (jj >= exrImage.height) {
731 const int srcIdx = i + j * exrHeader.tile_size_x;
732 unsigned char **src = exrImage.tiles[it].images;
733 if (isSingleChannel) {
734 rgbaF32[
R] =
reinterpret_cast<
float **>(src)[0][srcIdx];
735 rgbaF32[
G] = rgbaF32[
R];
736 rgbaF32[
B] = rgbaF32[
R];
737 rgbaF32[3] = rgbaF32[
R];
739 rgbaF32[
R] =
reinterpret_cast<
float **>(src)[idxR][srcIdx];
740 rgbaF32[
G] =
reinterpret_cast<
float **>(src)[idxG][srcIdx];
741 rgbaF32[
B] =
reinterpret_cast<
float **>(src)[idxB][srcIdx];
743 rgbaF32[3] =
reinterpret_cast<
float **>(src)[idxA][srcIdx];
747 format.encodeToPixel(rgbaF32, target + qsizetype(idx) * bytesPerPixel, 0);
752 for (
int y = exrImage.height - 1; y >= 0; --y) {
753 for (
int x = 0; x < exrImage.width; x++) {
754 const int i = y * exrImage.width + x;
755 if (isSingleChannel) {
756 rgbaF32[
R] =
reinterpret_cast<
float **>(exrImage.images)[0][i];
757 rgbaF32[
G] = rgbaF32[
R];
758 rgbaF32[
B] = rgbaF32[
R];
759 rgbaF32[3] = rgbaF32[
R];
761 rgbaF32[
R] =
reinterpret_cast<
float **>(exrImage.images)[idxR][i];
762 rgbaF32[
G] =
reinterpret_cast<
float **>(exrImage.images)[idxG][i];
763 rgbaF32[
B] =
reinterpret_cast<
float **>(exrImage.images)[idxB][i];
765 rgbaF32[3] =
reinterpret_cast<
float **>(exrImage.images)[idxA][i];
769 format.encodeToPixel(rgbaF32, target + qsizetype(idx) * bytesPerPixel, 0);
776 FreeEXRImage(&exrImage);
777 FreeEXRHeader(&exrHeader);
784QSSGLoadedTexture *QSSGLoadedTexture::loadLightmapImage(
const QString &inPath,
const QSSGRenderTextureFormat &inFormat,
const QString &key)
786 if (
auto stream = QSSGInputUtil::getStreamForFile(inPath))
787 return QSSGLightmapLoader::createTexture(stream, inFormat, key);
791QSSGLoadedTexture *QSSGLoadedTexture::loadHdrImage(
const QSharedPointer<QIODevice> &source,
const QSSGRenderTextureFormat &inFormat)
793 QSSGLoadedTexture *imageData =
nullptr;
795 QSSGRenderTextureFormat format = inFormat;
796 if (format.format == QSSGRenderTextureFormat::Unknown) {
801 format = QSSGRenderTextureFormat::RGBA16F;
805 imageData = loadRadianceHdr(source, format);
809 imageData = loadExr(source, format);
820 if (size.width() <= 0 || size.height() <= 0 || depth < 0 || bytesPerPixel <= 0)
823 qsizetype dataSize = bytesPerPixel;
824 if (qMulOverflow(dataSize, qsizetype(size.width()), &dataSize)
825 || qMulOverflow(dataSize, qsizetype(size.height()), &dataSize)
826 || qMulOverflow(dataSize, qsizetype(qMax(depth, 1)), &dataSize)) {
830 if (dataSize > qsizetype(std::numeric_limits<quint32>::max()))
836QSSGLoadedTexture *QSSGLoadedTexture::loadTextureData(QSSGRenderTextureData *textureData)
838 QSSGLoadedTexture *imageData =
new QSSGLoadedTexture;
840 if (!textureData->format().isCompressedTextureFormat()) {
841 const int bytesPerPixel = textureData->format().getSizeofFormat();
842 const qsizetype dataSize = packedDataSize(textureData->size(), textureData->depth(), bytesPerPixel);
844 qWarning(
"TextureData dimensions %dx%dx%d are not usable",
845 textureData->size().width(),
846 textureData->size().height(),
847 textureData->depth());
852 if (dataSize > textureData->textureData().size()) {
853 qWarning(
"TextureData holds %lld bytes, but %dx%dx%d at %d bytes per pixel needs %lld",
854 qlonglong(textureData->textureData().size()),
855 textureData->size().width(),
856 textureData->size().height(),
857 textureData->depth(),
859 qlonglong(dataSize));
862 imageData->dataSizeInBytes = quint32(dataSize);
864 imageData->data =
const_cast<
void*>(
reinterpret_cast<
const void*>(textureData->textureData().data()));
865 imageData->width = textureData->size().width();
866 imageData->height = textureData->size().height();
867 imageData->depth = textureData->depth();
868 imageData->format = textureData->format();
869 imageData->components = textureData->format().getNumberOfComponent();
873 const qsizetype dataSize = textureData->textureData().size();
874 if (dataSize > qsizetype(std::numeric_limits<quint32>::max())) {
875 qWarning(
"TextureData holds %lld bytes, which exceeds the 4GB limit",
876 qlonglong(dataSize));
879 imageData->data =
const_cast<
void *>(
reinterpret_cast<
const void *>(textureData->textureData().data()));
880 imageData->dataSizeInBytes = quint32(dataSize);
884 imageData->width = textureData->size().width();
885 imageData->height = textureData->size().height();
886 imageData->format = textureData->format();
893 if (imageData->format == QSSGRenderTextureFormat::RGBE8 ||
894 imageData->format == QSSGRenderTextureFormat::RGBA16F ||
895 imageData->format == QSSGRenderTextureFormat::RGBA32F ||
896 imageData->format == QSSGRenderTextureFormat::BC6H)
897 imageData->isSRGB =
false;
899 imageData->isSRGB =
true;
906bool scanImageForAlpha(
const void *inData, quint32 inWidth, quint32 inHeight, quint32 inPixelSizeInBytes, quint8 inAlphaSizeInBits)
908 const quint8 *rowPtr =
reinterpret_cast<
const quint8 *>(inData);
909 bool hasAlpha =
false;
910 if (inAlphaSizeInBits == 0)
912 if (inPixelSizeInBytes != 2 && inPixelSizeInBytes != 4) {
916 if (inAlphaSizeInBits > 8) {
921 quint32 alphaRightShift = inPixelSizeInBytes * 8 - inAlphaSizeInBits;
922 quint32 maxAlphaValue = (1 << inAlphaSizeInBits) - 1;
924 for (quint32 rowIdx = 0; rowIdx < inHeight && !hasAlpha; ++rowIdx) {
925 for (quint32 idx = 0; idx < inWidth && !hasAlpha; ++idx, rowPtr += inPixelSizeInBytes) {
926 quint32 pixelValue = 0;
927 if (inPixelSizeInBytes == 2)
928 pixelValue = *(
reinterpret_cast<
const quint16 *>(rowPtr));
930 pixelValue = *(
reinterpret_cast<
const quint32 *>(rowPtr));
931 pixelValue = pixelValue >> alphaRightShift;
932 if (pixelValue < maxAlphaValue)
940QSSGLoadedTexture::~QSSGLoadedTexture()
942 if (data && image.sizeInBytes() <= 0 && ownsData)
946bool QSSGLoadedTexture::scanForTransparency()
const
948 switch (format.format) {
949 case QSSGRenderTextureFormat::SRGB8A8:
950 case QSSGRenderTextureFormat::RGBA8:
954 return scanImageForAlpha(data, width, height, 4, 8);
956 case QSSGRenderTextureFormat::SRGB8:
957 case QSSGRenderTextureFormat::RGB8:
958 case QSSGRenderTextureFormat::RGBE8:
960 case QSSGRenderTextureFormat::RGB565:
962 case QSSGRenderTextureFormat::RGBA5551:
966 return scanImageForAlpha(data, width, height, 2, 1);
968 case QSSGRenderTextureFormat::Alpha8:
970 case QSSGRenderTextureFormat::R8:
971 case QSSGRenderTextureFormat::Luminance8:
972 case QSSGRenderTextureFormat::RG8:
974 case QSSGRenderTextureFormat::LuminanceAlpha8:
978 return scanImageForAlpha(data, width, height, 2, 8);
979 case QSSGRenderTextureFormat::RGB_DXT1:
981 case QSSGRenderTextureFormat::RGBA_DXT3:
982 case QSSGRenderTextureFormat::RGBA_DXT1:
983 case QSSGRenderTextureFormat::RGBA_DXT5:
985 case QSSGRenderTextureFormat::RGB9E5:
987 case QSSGRenderTextureFormat::RG32F:
988 case QSSGRenderTextureFormat::RGB32F:
989 case QSSGRenderTextureFormat::RGBA16F:
990 case QSSGRenderTextureFormat::RGBA32F:
1004 if (img1.size() != img2.size())
1006 if (img1.pixelFormat().channelCount() != img2.pixelFormat().channelCount())
1014 QStringList fileNames;
1015 if (inPath.contains(QStringLiteral(
"%p"))) {
1016 fileNames.reserve(6);
1017 const char *faces[6] = {
"posx",
"negx",
"posy",
"negy",
"posz",
"negz" };
1018 for (
const auto face : faces) {
1019 QString fileName = inPath;
1020 fileName.replace(QStringLiteral(
"%p"), QLatin1StringView(face));
1021 fileNames << fileName;
1024 }
else if (inPath.contains(QStringLiteral(
";"))) {
1025 fileNames = inPath.split(QChar(u';'));
1027 if (fileNames.size() != 6)
1029 std::unique_ptr<QTextureFileData> textureFileData = std::make_unique<QTextureFileData>(QTextureFileData::ImageMode);
1030 textureFileData->setNumFaces(6);
1031 textureFileData->setNumLevels(1);
1032 textureFileData->setLogName(inPath.toUtf8());
1034 for (
int i = 0; i < 6; ++i) {
1035 QString searchName = fileNames[i];
1037 auto stream = QSSGInputUtil::getStreamForFile(searchName,
true, &filePath);
1041 QImage face = loadImage(filePath, !flipY);
1042 if (face.isNull() || (!prevImage.isNull() && !isCompatible(prevImage, face))) {
1045 textureFileData->setData(face, 0, i);
1046 textureFileData->setSize(face.size());
1050 const qsizetype dataSize = prevImage.sizeInBytes();
1051 if (dataSize > qsizetype(std::numeric_limits<quint32>::max())) {
1052 qWarning(
"Cube map face %dx%d is too large to describe",
1054 prevImage.height());
1058 QSSGLoadedTexture *retval =
new QSSGLoadedTexture;
1060 retval->textureFileData = *textureFileData;
1062 retval->width = prevImage.width();
1063 retval->height = prevImage.height();
1064 retval->components = prevImage.pixelFormat().channelCount();
1065 retval->image = prevImage;
1066 retval->data = (
void *)retval->image.bits();
1067 retval->dataSizeInBytes = quint32(dataSize);
1068 retval->setFormatFromComponents();
1070 retval->isSRGB = prevImage.colorSpace().transferFunction() != QColorSpace::TransferFunction::Linear;
1075QSSGLoadedTexture *QSSGLoadedTexture::load(
const QString &inPath,
1076 const QSSGRenderTextureFormat &inFormat,
1079 if (inPath.isEmpty())
1082 QSSGLoadedTexture *theLoadedImage =
nullptr;
1084 QSSGInputUtil::FileType fileType = QSSGInputUtil::UnknownFile;
1085 QSharedPointer<QIODevice> theStream =
1086 QSSGInputUtil::getStreamForTextureFile(inPath,
true, &fileName, &fileType);
1090 case QSSGInputUtil::HdrFile:
1093 theLoadedImage = loadHdrImage(theStream, inFormat);
1095 case QSSGInputUtil::TextureFile:
1096 theLoadedImage = loadCompressedImage(fileName);
1099 theLoadedImage = loadQImage(fileName, inFlipY);
1104 return loadCubeMap(inPath, inFlipY);
1106 return theLoadedImage;
Combined button and popup list for selecting options.
static qsizetype packedDataSize(const QSize &size, int depth, int bytesPerPixel)
static bool isCompatible(const QImage &img1, const QImage &img2)
static QSSGRenderTextureFormat fromGLtoTextureFormat(quint32 internalFormat)
static QImage loadImage(const QString &inPath, bool flipVertical)
static QSSGLoadedTexture * loadCubeMap(const QString &inPath, bool flipY)