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>
21#include <QtQuick3DTextureHelpers/private/qssgexrreader_p.h>
28QSharedPointer<QIODevice> QSSGInputUtil::getStreamForFile(
const QString &inPath,
bool inQuiet, QString *outPath)
30 QFile *file =
nullptr;
31 QString tryPath = inPath;
32 if (tryPath.startsWith(QLatin1String(
"qrc:/"))) {
33 tryPath = inPath.mid(3);
34 }
else if (tryPath.startsWith(QLatin1String(
"file://"))) {
35 tryPath = inPath.mid(7);
38 QFileInfo fi(tryPath);
39 bool found = fi.exists();
40 if (!found && fi.isNativePath()) {
41 tryPath.prepend(QLatin1String(
":/"));
46 QString filePath = fi.canonicalFilePath();
47 file =
new QFile(filePath);
48 if (file->open(QIODevice::ReadOnly)) {
56 if (!file && !inQuiet)
57 qCWarning(WARNING,
"Failed to find file: %s", qPrintable(inPath));
58 return QSharedPointer<QIODevice>(file);
61QSharedPointer<QIODevice> QSSGInputUtil::getStreamForTextureFile(
const QString &inPath,
bool inQuiet,
62 QString *outPath, FileType *outFileType)
64 static const QList<QByteArray> hdrFormats = QList<QByteArray>({
"hdr",
"exr" });
65 static const QList<QByteArray> textureFormats = QTextureFileReader::supportedFileFormats();
66 static const QList<QByteArray> imageFormats = QImageReader::supportedImageFormats();
67 static const QList<QByteArray> allFormats = textureFormats + hdrFormats + imageFormats;
71 QSharedPointer<QIODevice> stream = getStreamForFile(inPath,
true, &filePath);
73 ext = QFileInfo(filePath).suffix().toLatin1().toLower();
75 for (
const QByteArray &format : allFormats) {
76 QString tryName = inPath + QLatin1Char(
'.') + QLatin1String(format);
77 stream = getStreamForFile(tryName,
true, &filePath);
88 FileType type = UnknownFile;
89 if (hdrFormats.contains(ext))
91 else if (textureFormats.contains(ext))
93 else if (imageFormats.contains(ext))
97 }
else if (!inQuiet) {
98 qCWarning(WARNING,
"Failed to find texture file for: %s", qPrintable(inPath));
105 switch (internalFormat) {
107 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::R8);
109 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::R16);
111 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::R16F);
113 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::R32I);
115 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::R32UI);
117 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::R32F);
119 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RG8);
121 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA8);
123 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB8);
125 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8);
127 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8A8);
129 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB565);
131 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::Alpha8);
133 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::Luminance8);
135 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::Luminance16);
137 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::LuminanceAlpha8);
139 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA16F);
141 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RG16F);
143 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RG32F);
145 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB32F);
147 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA32F);
149 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::R11G11B10);
151 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB9E5);
153 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB10_A2);
155 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB16F);
157 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA32UI);
159 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB32UI);
161 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA16UI);
163 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB16UI);
165 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA8UI);
167 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB8UI);
169 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA32I);
171 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB32I);
173 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA16I);
175 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB16I);
177 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA8I);
179 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB8I);
181 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_DXT1);
183 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB_DXT1);
185 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_DXT3);
187 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_DXT5);
189 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::R11_EAC_UNorm);
191 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::R11_EAC_SNorm);
193 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RG11_EAC_UNorm);
195 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RG11_EAC_SNorm);
197 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB8_ETC2);
199 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_ETC2);
201 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB8_PunchThrough_Alpha1_ETC2);
203 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_PunchThrough_Alpha1_ETC2);
205 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA8_ETC2_EAC);
207 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ETC2_EAC);
209 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_4x4);
211 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_5x4);
213 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_5x5);
215 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_6x5);
217 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_6x6);
219 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_8x5);
221 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_8x6);
223 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_8x8);
225 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_10x5);
227 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_10x6);
229 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_10x8);
231 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_10x10);
233 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_12x10);
235 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_12x12);
237 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_4x4);
239 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_5x4);
241 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_5x5);
243 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_6x5);
245 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_6x6);
247 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_8x5);
249 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_8x6);
251 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_8x8);
253 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_10x5);
255 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_10x6);
257 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_10x8);
259 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_10x10);
261 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_12x10);
263 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_12x12);
265 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::Depth16);
267 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::Depth24);
269 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::Depth32);
271 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::Depth24Stencil8);
273 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::Unknown);
279 QImage image(inPath);
282 const QPixelFormat pixFormat = image.pixelFormat();
283 QImage::Format targetFormat = QImage::Format_RGBA8888_Premultiplied;
284 if (image.colorCount())
285 targetFormat = QImage::Format_RGBA8888;
286 else if (pixFormat.channelCount() == 1)
287 targetFormat = QImage::Format_Grayscale8;
288 else if (pixFormat.alphaUsage() == QPixelFormat::IgnoresAlpha)
289 targetFormat = QImage::Format_RGBX8888;
290 else if (pixFormat.premultiplied() == QPixelFormat::NotPremultiplied)
291 targetFormat = QImage::Format_RGBA8888;
293 image.convertTo(targetFormat);
299QSSGLoadedTexture *QSSGLoadedTexture::loadQImage(
const QString &inPath, qint32 flipVertical)
301 QImage image = loadImage(inPath, flipVertical);
304 QSSGLoadedTexture *retval =
new QSSGLoadedTexture;
305 retval->width = image.width();
306 retval->height = image.height();
307 retval->components = image.pixelFormat().channelCount();
308 retval->image = image;
309 retval->data = (
void *)retval->image.bits();
310 retval->dataSizeInBytes = image.sizeInBytes();
311 retval->setFormatFromComponents();
313 retval->isSRGB = image.colorSpace().transferFunction() != QColorSpace::TransferFunction::Linear;
318QSSGLoadedTexture *QSSGLoadedTexture::loadCompressedImage(
const QString &inPath)
320 QSSGLoadedTexture *retval =
nullptr;
323 QFile imageFile(inPath);
324 if (!imageFile.open(QIODevice::ReadOnly)) {
325 qWarning() <<
"Could not open image file: " << inPath;
328 auto reader =
new QTextureFileReader(&imageFile, inPath);
330 if (!reader->canRead()) {
331 qWarning() <<
"Unable to read image file: " << inPath;
335 retval =
new QSSGLoadedTexture;
336 retval->textureFileData = reader->read();
339 retval->width = retval->textureFileData.size().width();
340 retval->height = retval->textureFileData.size().height();
341 auto glFormat = retval->textureFileData.glInternalFormat()
342 ? retval->textureFileData.glInternalFormat()
343 : retval->textureFileData.glFormat();
344 retval->format = fromGLtoTextureFormat(glFormat);
347 if ((retval->format.format >= QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_4x4) &&
348 (retval->format.format <= QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_12x12))
349 retval->isSRGB =
true;
359typedef unsigned char RGBE[4];
360enum : size_t { R = 0, G = 1, B = 2, E = 3 };
365qsizetype calculateDataSize(qsizetype width, qsizetype height, qsizetype depth, qsizetype bytesPerPixel)
367 if (width <= 0 || height <= 0 || depth < 0 || bytesPerPixel <= 0)
371 if (qMulOverflow(width, bytesPerPixel, &line))
373 const qsizetype pitch = (line + 3) & ~qsizetype(3);
375 qsizetype dataSize = 0;
376 if (qMulOverflow(height, pitch, &dataSize))
378 if (depth > 0 && qMulOverflow(dataSize, depth, &dataSize))
381 if (quint64(dataSize) > quint64(std::numeric_limits<quint32>::max()))
387float convertComponent(
int exponent,
int val)
389 float v = val / (256.0f);
390 float d = powf(2.0f, (
float)exponent - 128.0f);
397bool decrunchScanline(
const char *&p,
const char *pEnd, RGBE *scanline,
int w)
402 scanline[0][R] = *p++;
403 scanline[0][G] = *p++;
404 scanline[0][B] = *p++;
405 scanline[0][E] = *p++;
407 if (scanline[0][R] == 2 && scanline[0][G] == 2 && scanline[0][B] < 128) {
409 for (
int channel = 0; channel < 4; ++channel) {
414 unsigned char c = *p++;
416 const int repCount = c & 127;
417 if (p >= pEnd || repCount > w - x)
420 for (
int i = 0; i < repCount; ++i)
421 scanline[x++][channel] = c;
423 if (c > w - x || pEnd - p < c)
425 for (
int i = 0; i < c; ++i)
426 scanline[x++][channel] = *p++;
437 scanline[x][R] = *p++;
438 scanline[x][G] = *p++;
439 scanline[x][B] = *p++;
440 scanline[x][E] = *p++;
442 if (scanline[x][R] == 1 && scanline[x][G] == 1 && scanline[x][B] == 1) {
446 if (bitshift > 24 || scanline[x][E] > ((w - x) >> bitshift))
448 const int repCount =
int(scanline[x][E]) << bitshift;
449 for (
int i = 0; i < repCount; ++i, ++x)
450 memcpy(scanline[x], scanline[x - 1], 4);
462void decodeScanlineToTexture(RGBE *scanline,
int width,
void *outBuf, qsizetype offset, QSSGRenderTextureFormat inFormat)
464 quint8 *target =
reinterpret_cast<quint8 *>(outBuf);
467 if (inFormat == QSSGRenderTextureFormat::RGBE8) {
468 memcpy(target, scanline, size_t(width) * 4);
471 for (
int i = 0; i < width; ++i) {
472 rgbaF32[R] = convertComponent(scanline[i][E], scanline[i][R]);
473 rgbaF32[G] = convertComponent(scanline[i][E], scanline[i][G]);
474 rgbaF32[B] = convertComponent(scanline[i][E], scanline[i][B]);
477 inFormat.encodeToPixel(rgbaF32, target, i * inFormat.getSizeofFormat());
482QSSGLoadedTexture *loadRadianceHdr(
const QSharedPointer<QIODevice> &source,
const QSSGRenderTextureFormat &format)
484 QSSGLoadedTexture *imageData =
nullptr;
488 if (source->read(sig,
sizeof(sig)) ==
sizeof(sig) && !memcmp(sig,
"#?RADIANCE\n",
sizeof(sig))) {
489 QByteArray buf = source->readAll();
490 const char *p = buf.constData();
491 const char *pEnd = p + buf.size();
500 if (line.startsWith(QByteArrayLiteral(
"FORMAT="))) {
501 const QByteArray format = line.mid(7).trimmed();
502 if (format != QByteArrayLiteral(
"32-bit_rle_rgbe")) {
503 qWarning(
"HDR format '%s' is not supported", format.constData());
513 qWarning(
"Malformed HDR image data at property strings");
525 qWarning(
"Malformed HDR image data at resolution string");
533 if (!sscanf_s(line.constData(),
"-Y %d +X %d", &height, &width)) {
535 if (!sscanf(line.constData(),
"-Y %d +X %d", &height, &width)) {
537 qWarning(
"Unsupported HDR resolution string '%s'", line.constData());
540 if (width <= 0 || height <= 0 || width > 65536 || height > 65536) {
541 qWarning(
"Invalid HDR resolution %dx%d", width, height);
545 const int bytesPerPixel = format.getSizeofFormat();
546 const qsizetype dataSize = calculateDataSize(width, height, 0, bytesPerPixel);
548 qWarning(
"HDR image dimensions %dx%d are too large", width, height);
551 void *data = ::calloc(1, size_t(dataSize));
553 qWarning(
"Failed to allocate %lld bytes for HDR image", qlonglong(dataSize));
556 imageData =
new QSSGLoadedTexture;
557 imageData->dataSizeInBytes = quint32(dataSize);
558 imageData->data = data;
559 imageData->width = width;
560 imageData->height = height;
561 imageData->format = format;
562 imageData->components = format.getNumberOfComponent();
563 imageData->isSRGB =
false;
566 RGBE *scanline =
new RGBE[width];
570 for (
int y = 0; y < height; ++y) {
571 const qsizetype byteOffset = qsizetype(height - 1 - y) * width * bytesPerPixel;
572 if (!decrunchScanline(p, pEnd, scanline, width)) {
573 qWarning(
"Malformed HDR scanline data at row %d of %d", y, height);
576 decodeScanlineToTexture(scanline, width, imageData->data, byteOffset, format);
585QSSGLoadedTexture *loadExr(
const QSharedPointer<QIODevice> &source,
const QSSGRenderTextureFormat format)
589 if (!QSSGExrReader::isExr(source->peek(4)))
591 const QByteArray buf = source->readAll();
593 QSSGExrReader reader;
594 if (!reader.parseHeader(buf)) {
595 qWarning(
"Failed to parse EXR file: %s", qPrintable(reader.errorMessage()));
599 const QSSGExrReader::ChannelMap map = qssgExrRgbaChannelMap(reader);
600 if (map.source[0] < 0 && map.source[1] < 0 && map.source[2] < 0) {
601 QByteArrayList names;
602 for (
const QSSGExrReader::Channel &channel : reader.channels())
603 names.append(channel.name);
604 qWarning(
"EXR image has no R, G or B channel; its channels are: %s", names.join(
", ").constData());
608 const qint32 width = reader.size().width();
609 const qint32 height = reader.size().height();
610 const int bytesPerPixel = format.getSizeofFormat();
611 const qsizetype dataSize = calculateDataSize(width, height, 0, bytesPerPixel);
613 qWarning(
"EXR image dimensions %dx%d are too large", width, height);
616 void *data = ::malloc(size_t(dataSize));
618 qWarning(
"Failed to allocate %lld bytes for EXR image", qlonglong(dataSize));
622 std::unique_ptr<QSSGLoadedTexture> imageData(
new QSSGLoadedTexture);
623 imageData->dataSizeInBytes = quint32(dataSize);
624 imageData->data = data;
625 imageData->width = width;
626 imageData->height = height;
627 imageData->format = format;
628 imageData->components = format.getNumberOfComponent();
629 imageData->isSRGB =
false;
631 quint8 *
const target =
reinterpret_cast<quint8 *>(data);
632 const qsizetype pitch = dataSize / height;
634 const bool ok = reader.decode(map, QSSGExrReader::FlipVertically, [&](qint32 row,
const float *rgba) {
635 quint8 *dst = target + qsizetype(row) * pitch;
636 for (qint32 x = 0; x < width; ++x) {
637 float px[4] = { rgba[x * 4], rgba[x * 4 + 1], rgba[x * 4 + 2], rgba[x * 4 + 3] };
638 format.encodeToPixel(px, dst, x * bytesPerPixel);
643 qWarning(
"Failed to decode EXR file: %s", qPrintable(reader.errorMessage()));
647 return imageData.release();
652QSSGLoadedTexture *QSSGLoadedTexture::loadLightmapImage(
const QString &inPath,
const QSSGRenderTextureFormat &inFormat,
const QString &key)
654 if (
auto stream = QSSGInputUtil::getStreamForFile(inPath))
655 return QSSGLightmapLoader::createTexture(stream, inFormat, key);
659QSSGLoadedTexture *QSSGLoadedTexture::loadHdrImage(
const QSharedPointer<QIODevice> &source,
const QSSGRenderTextureFormat &inFormat)
661 QSSGLoadedTexture *imageData =
nullptr;
663 QSSGRenderTextureFormat format = inFormat;
664 if (format.format == QSSGRenderTextureFormat::Unknown) {
669 format = QSSGRenderTextureFormat::RGBA16F;
673 imageData = loadRadianceHdr(source, format);
677 imageData = loadExr(source, format);
688 if (size.width() <= 0 || size.height() <= 0 || depth < 0 || bytesPerPixel <= 0)
691 qsizetype dataSize = bytesPerPixel;
692 if (qMulOverflow(dataSize, qsizetype(size.width()), &dataSize)
693 || qMulOverflow(dataSize, qsizetype(size.height()), &dataSize)
694 || qMulOverflow(dataSize, qsizetype(qMax(depth, 1)), &dataSize)) {
698 if (quint64(dataSize) > quint64(std::numeric_limits<quint32>::max()))
704QSSGLoadedTexture *QSSGLoadedTexture::loadTextureData(QSSGRenderTextureData *textureData)
706 QSSGLoadedTexture *imageData =
new QSSGLoadedTexture;
708 if (!textureData->format().isCompressedTextureFormat()) {
709 const int bytesPerPixel = textureData->format().getSizeofFormat();
710 const qsizetype dataSize = packedDataSize(textureData->size(), textureData->depth(), bytesPerPixel);
712 qWarning(
"TextureData dimensions %dx%dx%d are not usable",
713 textureData->size().width(),
714 textureData->size().height(),
715 textureData->depth());
720 if (dataSize > textureData->textureData().size()) {
721 qWarning(
"TextureData holds %lld bytes, but %dx%dx%d at %d bytes per pixel needs %lld",
722 qlonglong(textureData->textureData().size()),
723 textureData->size().width(),
724 textureData->size().height(),
725 textureData->depth(),
727 qlonglong(dataSize));
730 imageData->dataSizeInBytes = quint32(dataSize);
732 imageData->data =
const_cast<
void*>(
reinterpret_cast<
const void*>(textureData->textureData().data()));
733 imageData->width = textureData->size().width();
734 imageData->height = textureData->size().height();
735 imageData->depth = textureData->depth();
736 imageData->format = textureData->format();
737 imageData->components = textureData->format().getNumberOfComponent();
741 const qsizetype dataSize = textureData->textureData().size();
742 if (quint64(dataSize) > quint64(std::numeric_limits<quint32>::max())) {
743 qWarning(
"TextureData holds %lld bytes, which exceeds the 4GB limit",
744 qlonglong(dataSize));
747 imageData->data =
const_cast<
void *>(
reinterpret_cast<
const void *>(textureData->textureData().data()));
748 imageData->dataSizeInBytes = quint32(dataSize);
752 imageData->width = textureData->size().width();
753 imageData->height = textureData->size().height();
754 imageData->format = textureData->format();
761 if (imageData->format == QSSGRenderTextureFormat::RGBE8 ||
762 imageData->format == QSSGRenderTextureFormat::RGBA16F ||
763 imageData->format == QSSGRenderTextureFormat::RGBA32F ||
764 imageData->format == QSSGRenderTextureFormat::BC6H)
765 imageData->isSRGB =
false;
767 imageData->isSRGB =
true;
774bool scanImageForAlpha(
const void *inData, quint32 inWidth, quint32 inHeight, quint32 inPixelSizeInBytes, quint8 inAlphaSizeInBits)
776 const quint8 *rowPtr =
reinterpret_cast<
const quint8 *>(inData);
777 bool hasAlpha =
false;
778 if (inAlphaSizeInBits == 0)
780 if (inPixelSizeInBytes != 2 && inPixelSizeInBytes != 4) {
784 if (inAlphaSizeInBits > 8) {
789 quint32 alphaRightShift = inPixelSizeInBytes * 8 - inAlphaSizeInBits;
790 quint32 maxAlphaValue = (1 << inAlphaSizeInBits) - 1;
792 for (quint32 rowIdx = 0; rowIdx < inHeight && !hasAlpha; ++rowIdx) {
793 for (quint32 idx = 0; idx < inWidth && !hasAlpha; ++idx, rowPtr += inPixelSizeInBytes) {
794 quint32 pixelValue = 0;
795 if (inPixelSizeInBytes == 2)
796 pixelValue = *(
reinterpret_cast<
const quint16 *>(rowPtr));
798 pixelValue = *(
reinterpret_cast<
const quint32 *>(rowPtr));
799 pixelValue = pixelValue >> alphaRightShift;
800 if (pixelValue < maxAlphaValue)
808QSSGLoadedTexture::~QSSGLoadedTexture()
810 if (data && image.sizeInBytes() <= 0 && ownsData)
814bool QSSGLoadedTexture::scanForTransparency()
const
816 switch (format.format) {
817 case QSSGRenderTextureFormat::SRGB8A8:
818 case QSSGRenderTextureFormat::RGBA8:
822 return scanImageForAlpha(data, width, height, 4, 8);
824 case QSSGRenderTextureFormat::SRGB8:
825 case QSSGRenderTextureFormat::RGB8:
826 case QSSGRenderTextureFormat::RGBE8:
828 case QSSGRenderTextureFormat::RGB565:
830 case QSSGRenderTextureFormat::RGBA5551:
834 return scanImageForAlpha(data, width, height, 2, 1);
836 case QSSGRenderTextureFormat::Alpha8:
838 case QSSGRenderTextureFormat::R8:
839 case QSSGRenderTextureFormat::Luminance8:
840 case QSSGRenderTextureFormat::RG8:
842 case QSSGRenderTextureFormat::LuminanceAlpha8:
846 return scanImageForAlpha(data, width, height, 2, 8);
847 case QSSGRenderTextureFormat::RGB_DXT1:
849 case QSSGRenderTextureFormat::RGBA_DXT3:
850 case QSSGRenderTextureFormat::RGBA_DXT1:
851 case QSSGRenderTextureFormat::RGBA_DXT5:
853 case QSSGRenderTextureFormat::RGB9E5:
855 case QSSGRenderTextureFormat::RG32F:
856 case QSSGRenderTextureFormat::RGB32F:
857 case QSSGRenderTextureFormat::RGBA16F:
858 case QSSGRenderTextureFormat::RGBA32F:
872 if (img1.size() != img2.size())
874 if (img1.pixelFormat().channelCount() != img2.pixelFormat().channelCount())
880static QSSGLoadedTexture *
loadCubeMap(
const QString &inPath,
bool flipY)
882 QStringList fileNames;
883 if (inPath.contains(QStringLiteral(
"%p"))) {
884 fileNames.reserve(6);
885 const char *faces[6] = {
"posx",
"negx",
"posy",
"negy",
"posz",
"negz" };
886 for (
const auto face : faces) {
887 QString fileName = inPath;
888 fileName.replace(QStringLiteral(
"%p"), QLatin1StringView(face));
889 fileNames << fileName;
892 }
else if (inPath.contains(QStringLiteral(
";"))) {
893 fileNames = inPath.split(QChar(u';'));
895 if (fileNames.size() != 6)
897 std::unique_ptr<QTextureFileData> textureFileData = std::make_unique<QTextureFileData>(QTextureFileData::ImageMode);
898 textureFileData->setNumFaces(6);
899 textureFileData->setNumLevels(1);
900 textureFileData->setLogName(inPath.toUtf8());
902 for (
int i = 0; i < 6; ++i) {
903 QString searchName = fileNames[i];
905 auto stream = QSSGInputUtil::getStreamForFile(searchName,
true, &filePath);
909 QImage face = loadImage(filePath, !flipY);
910 if (face.isNull() || (!prevImage.isNull() && !isCompatible(prevImage, face))) {
913 textureFileData->setData(face, 0, i);
914 textureFileData->setSize(face.size());
918 const qsizetype dataSize = prevImage.sizeInBytes();
919 if (quint64(dataSize) > quint64(std::numeric_limits<quint32>::max())) {
920 qWarning(
"Cube map face %dx%d is too large to describe",
926 QSSGLoadedTexture *retval =
new QSSGLoadedTexture;
928 retval->textureFileData = *textureFileData;
930 retval->width = prevImage.width();
931 retval->height = prevImage.height();
932 retval->components = prevImage.pixelFormat().channelCount();
933 retval->image = prevImage;
934 retval->data = (
void *)retval->image.bits();
935 retval->dataSizeInBytes = quint32(dataSize);
936 retval->setFormatFromComponents();
938 retval->isSRGB = prevImage.colorSpace().transferFunction() != QColorSpace::TransferFunction::Linear;
943QSSGLoadedTexture *QSSGLoadedTexture::load(
const QString &inPath,
944 const QSSGRenderTextureFormat &inFormat,
947 if (inPath.isEmpty())
950 QSSGLoadedTexture *theLoadedImage =
nullptr;
952 QSSGInputUtil::FileType fileType = QSSGInputUtil::UnknownFile;
953 QSharedPointer<QIODevice> theStream =
954 QSSGInputUtil::getStreamForTextureFile(inPath,
true, &fileName, &fileType);
958 case QSSGInputUtil::HdrFile:
961 theLoadedImage = loadHdrImage(theStream, inFormat);
963 case QSSGInputUtil::TextureFile:
964 theLoadedImage = loadCompressedImage(fileName);
967 theLoadedImage = loadQImage(fileName, inFlipY);
972 return loadCubeMap(inPath, inFlipY);
974 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)