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>
14#include <QtQuick3DUtils/private/qssgutils_p.h>
15#include <QtQuick3DUtils/private/qssgassert_p.h>
17#include <private/qtexturefilereader_p.h>
19#define TINYEXR_IMPLEMENTATION
20#define TINYEXR_USE_MINIZ 0
21#define TINYEXR_USE_THREAD 1
29QSharedPointer<QIODevice> QSSGInputUtil::getStreamForFile(
const QString &inPath,
bool inQuiet, QString *outPath)
31 QFile *file =
nullptr;
32 QString tryPath = inPath;
33 if (tryPath.startsWith(QLatin1String(
"qrc:/"))) {
34 tryPath = inPath.mid(3);
35 }
else if (tryPath.startsWith(QLatin1String(
"file://"))) {
36 tryPath = inPath.mid(7);
39 QFileInfo fi(tryPath);
40 bool found = fi.exists();
41 if (!found && fi.isNativePath()) {
42 tryPath.prepend(QLatin1String(
":/"));
47 QString filePath = fi.canonicalFilePath();
48 file =
new QFile(filePath);
49 if (file->open(QIODevice::ReadOnly)) {
57 if (!file && !inQuiet)
58 qCWarning(WARNING,
"Failed to find file: %s", qPrintable(inPath));
59 return QSharedPointer<QIODevice>(file);
62QSharedPointer<QIODevice> QSSGInputUtil::getStreamForTextureFile(
const QString &inPath,
bool inQuiet,
63 QString *outPath, FileType *outFileType)
65 static const QList<QByteArray> hdrFormats = QList<QByteArray>({
"hdr",
"exr" });
66 static const QList<QByteArray> textureFormats = QTextureFileReader::supportedFileFormats();
67 static const QList<QByteArray> imageFormats = QImageReader::supportedImageFormats();
68 static const QList<QByteArray> allFormats = textureFormats + hdrFormats + imageFormats;
72 QSharedPointer<QIODevice> stream = getStreamForFile(inPath,
true, &filePath);
74 ext = QFileInfo(filePath).suffix().toLatin1().toLower();
76 for (
const QByteArray &format : allFormats) {
77 QString tryName = inPath + QLatin1Char(
'.') + QLatin1String(format);
78 stream = getStreamForFile(tryName,
true, &filePath);
89 FileType type = UnknownFile;
90 if (hdrFormats.contains(ext))
92 else if (textureFormats.contains(ext))
94 else if (imageFormats.contains(ext))
98 }
else if (!inQuiet) {
99 qCWarning(WARNING,
"Failed to find texture file for: %s", qPrintable(inPath));
106 switch (internalFormat) {
108 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::R8);
110 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::R16);
112 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::R16F);
114 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::R32I);
116 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::R32UI);
118 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::R32F);
120 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RG8);
122 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA8);
124 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB8);
126 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8);
128 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8A8);
130 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB565);
132 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::Alpha8);
134 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::Luminance8);
136 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::Luminance16);
138 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::LuminanceAlpha8);
140 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA16F);
142 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RG16F);
144 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RG32F);
146 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB32F);
148 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA32F);
150 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::R11G11B10);
152 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB9E5);
154 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB10_A2);
156 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB16F);
158 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA32UI);
160 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB32UI);
162 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA16UI);
164 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB16UI);
166 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA8UI);
168 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB8UI);
170 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA32I);
172 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB32I);
174 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA16I);
176 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB16I);
178 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA8I);
180 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB8I);
182 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_DXT1);
184 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB_DXT1);
186 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_DXT3);
188 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_DXT5);
190 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::R11_EAC_UNorm);
192 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::R11_EAC_SNorm);
194 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RG11_EAC_UNorm);
196 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RG11_EAC_SNorm);
198 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB8_ETC2);
200 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_ETC2);
202 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGB8_PunchThrough_Alpha1_ETC2);
204 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_PunchThrough_Alpha1_ETC2);
206 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA8_ETC2_EAC);
208 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ETC2_EAC);
210 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_4x4);
212 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_5x4);
214 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_5x5);
216 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_6x5);
218 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_6x6);
220 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_8x5);
222 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_8x6);
224 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_8x8);
226 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_10x5);
228 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_10x6);
230 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_10x8);
232 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_10x10);
234 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_12x10);
236 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::RGBA_ASTC_12x12);
238 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_4x4);
240 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_5x4);
242 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_5x5);
244 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_6x5);
246 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_6x6);
248 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_8x5);
250 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_8x6);
252 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_8x8);
254 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_10x5);
256 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_10x6);
258 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_10x8);
260 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_10x10);
262 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_12x10);
264 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_12x12);
266 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::Depth16);
268 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::Depth24);
270 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::Depth32);
272 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::Depth24Stencil8);
274 return QSSGRenderTextureFormat(QSSGRenderTextureFormat::Unknown);
280 QImage image(inPath);
283 const QPixelFormat pixFormat = image.pixelFormat();
284 QImage::Format targetFormat = QImage::Format_RGBA8888_Premultiplied;
285 if (image.colorCount())
286 targetFormat = QImage::Format_RGBA8888;
287 else if (pixFormat.channelCount() == 1)
288 targetFormat = QImage::Format_Grayscale8;
289 else if (pixFormat.alphaUsage() == QPixelFormat::IgnoresAlpha)
290 targetFormat = QImage::Format_RGBX8888;
291 else if (pixFormat.premultiplied() == QPixelFormat::NotPremultiplied)
292 targetFormat = QImage::Format_RGBA8888;
294 image.convertTo(targetFormat);
300QSSGLoadedTexture *QSSGLoadedTexture::loadQImage(
const QString &inPath, qint32 flipVertical)
302 QImage image = loadImage(inPath, flipVertical);
305 QSSGLoadedTexture *retval =
new QSSGLoadedTexture;
306 retval->width = image.width();
307 retval->height = image.height();
308 retval->components = image.pixelFormat().channelCount();
309 retval->image = image;
310 retval->data = (
void *)retval->image.bits();
311 retval->dataSizeInBytes = image.sizeInBytes();
312 retval->setFormatFromComponents();
314 retval->isSRGB = image.colorSpace().transferFunction() != QColorSpace::TransferFunction::Linear;
319QSSGLoadedTexture *QSSGLoadedTexture::loadCompressedImage(
const QString &inPath)
321 QSSGLoadedTexture *retval =
nullptr;
324 QFile imageFile(inPath);
325 if (!imageFile.open(QIODevice::ReadOnly)) {
326 qWarning() <<
"Could not open image file: " << inPath;
329 auto reader =
new QTextureFileReader(&imageFile, inPath);
331 if (!reader->canRead()) {
332 qWarning() <<
"Unable to read image file: " << inPath;
336 retval =
new QSSGLoadedTexture;
337 retval->textureFileData = reader->read();
340 retval->width = retval->textureFileData.size().width();
341 retval->height = retval->textureFileData.size().height();
342 auto glFormat = retval->textureFileData.glInternalFormat()
343 ? retval->textureFileData.glInternalFormat()
344 : retval->textureFileData.glFormat();
345 retval->format = fromGLtoTextureFormat(glFormat);
348 if ((retval->format.format >= QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_4x4) &&
349 (retval->format.format <= QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_12x12))
350 retval->isSRGB =
true;
360typedef unsigned char RGBE[4];
367#define MAXELEN 0x7fff
371inline int calculateLine(
int width,
int bitdepth) {
return ((width * bitdepth) + 7) / 8; }
373inline int calculatePitch(
int line) {
return (line + 3) & ~3; }
375float convertComponent(
int exponent,
int val)
377 float v = val / (256.0f);
378 float d = powf(2.0f, (
float)exponent - 128.0f);
382void decrunchScanline(
const char *&p,
const char *pEnd, RGBE *scanline,
int w)
384 scanline[0][
R] = *p++;
385 scanline[0][
G] = *p++;
386 scanline[0][
B] = *p++;
387 scanline[0][
E] = *p++;
389 if (scanline[0][
R] == 2 && scanline[0][
G] == 2 && scanline[0][
B] < 128) {
391 for (
int channel = 0; channel < 4; ++channel) {
392 for (
int x = 0; x < w && p < pEnd; ) {
393 unsigned char c = *p++;
396 int repCount = c & 127;
399 scanline[x++][channel] = c;
402 while (c-- && p < pEnd)
403 scanline[x++][channel] = *p++;
412 while (x < w && pEnd - p >= 4) {
413 scanline[x][
R] = *p++;
414 scanline[x][
G] = *p++;
415 scanline[x][
B] = *p++;
416 scanline[x][
E] = *p++;
418 if (scanline[x][
R] == 1 && scanline[x][
G] == 1 && scanline[x][
B] == 1) {
419 int repCount = scanline[x][3] << bitshift;
421 memcpy(scanline[x], scanline[x - 1], 4);
433void decodeScanlineToTexture(RGBE *scanline,
int width,
void *outBuf, quint32 offset, QSSGRenderTextureFormat inFormat)
435 quint8 *target =
reinterpret_cast<quint8 *>(outBuf);
438 if (inFormat == QSSGRenderTextureFormat::RGBE8) {
439 memcpy(target, scanline, size_t(width) * 4);
442 for (
int i = 0; i < width; ++i) {
443 rgbaF32[
R] = convertComponent(scanline[i][
E], scanline[i][
R]);
444 rgbaF32[
G] = convertComponent(scanline[i][
E], scanline[i][
G]);
445 rgbaF32[
B] = convertComponent(scanline[i][
E], scanline[i][
B]);
448 inFormat.encodeToPixel(rgbaF32, target, i * inFormat.getSizeofFormat());
453QSSGLoadedTexture *loadRadianceHdr(
const QSharedPointer<QIODevice> &source,
const QSSGRenderTextureFormat &format)
455 QSSGLoadedTexture *imageData =
nullptr;
459 source->read(sig, 11);
460 if (!strncmp(sig,
"#?RADIANCE\n", 11)) {
461 QByteArray buf = source->readAll();
462 const char *p = buf.constData();
463 const char *pEnd = p + buf.size();
472 if (line.startsWith(QByteArrayLiteral(
"FORMAT="))) {
473 const QByteArray format = line.mid(7).trimmed();
474 if (format != QByteArrayLiteral(
"32-bit_rle_rgbe")) {
475 qWarning(
"HDR format '%s' is not supported", format.constData());
485 qWarning(
"Malformed HDR image data at property strings");
497 qWarning(
"Malformed HDR image data at resolution string");
505 if (!sscanf_s(line.constData(),
"-Y %d +X %d", &height, &width)) {
507 if (!sscanf(line.constData(),
"-Y %d +X %d", &height, &width)) {
509 qWarning(
"Unsupported HDR resolution string '%s'", line.constData());
512 if (width <= 0 || height <= 0) {
513 qWarning(
"Invalid HDR resolution");
517 const int bytesPerPixel = format.getSizeofFormat();
518 const int bitCount = bytesPerPixel * 8;
519 const int pitch = calculatePitch(calculateLine(width, bitCount));
520 const size_t dataSize = height * pitch;
521 QSSG_CHECK_X(dataSize <= std::numeric_limits<quint32>::max(),
"Requested data size exceeds 4GB limit!");
522 imageData =
new QSSGLoadedTexture;
523 imageData->dataSizeInBytes = quint32(dataSize);
524 imageData->data = ::malloc(dataSize);
525 imageData->width = width;
526 imageData->height = height;
527 imageData->format = format;
528 imageData->components = format.getNumberOfComponent();
529 imageData->isSRGB =
false;
532 RGBE *scanline =
new RGBE[width];
536 for (
int y = 0; y < height; ++y) {
537 quint32 byteOffset = quint32((height - 1 - y) * width * bytesPerPixel);
539 qWarning(
"Unexpected end of HDR data");
543 decrunchScanline(p, pEnd, scanline, width);
544 decodeScanlineToTexture(scanline, width, imageData->data, byteOffset, format);
553QSSGLoadedTexture *loadExr(
const QSharedPointer<QIODevice> &source,
const QSSGRenderTextureFormat format)
555 QSSGLoadedTexture *imageData =
nullptr;
557 char versionBuffer[tinyexr::kEXRVersionSize];
559 auto size = source->read(versionBuffer, tinyexr::kEXRVersionSize);
561 if (size != tinyexr::kEXRVersionSize)
564 EXRVersion exrVersion;
565 if (ParseEXRVersionFromMemory(&exrVersion,
reinterpret_cast<
unsigned char *>(versionBuffer), tinyexr::kEXRVersionSize) != TINYEXR_SUCCESS)
569 if (exrVersion.multipart)
574 QByteArray buf = source->readAll();
575 const char *err =
nullptr;
578 InitEXRHeader(&exrHeader);
579 if (ParseEXRHeaderFromMemory(&exrHeader,
581 reinterpret_cast<
const unsigned char *>(buf.constData()),
583 &err) != TINYEXR_SUCCESS) {
584 qWarning(
"Failed to parse EXR Header with error: '%s'", err);
585 FreeEXRErrorMessage(err);
590 for (
int i = 0; i < exrHeader.num_channels; i++) {
591 if (exrHeader.pixel_types[i] == TINYEXR_PIXELTYPE_HALF)
592 exrHeader.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT;
598 InitEXRImage(&exrImage);
599 if (LoadEXRImageFromMemory(&exrImage,
601 reinterpret_cast<
const unsigned char *>(buf.constData()),
603 &err) != TINYEXR_SUCCESS) {
604 qWarning(
"Failed to load EXR Image with error: '%s'", err);
605 FreeEXRHeader(&exrHeader);
606 FreeEXRErrorMessage(err);
611 const int bytesPerPixel = format.getSizeofFormat();
612 const int bitCount = bytesPerPixel * 8;
613 const int pitch = calculatePitch(calculateLine(exrImage.width, bitCount));
614 const size_t dataSize = exrImage.height * pitch;
615 QSSG_CHECK_X(dataSize <= std::numeric_limits<quint32>::max(),
"Requested data size exceeds 4GB limit!");
616 imageData =
new QSSGLoadedTexture;
617 imageData->dataSizeInBytes = quint32(dataSize);
618 imageData->data = ::malloc(imageData->dataSizeInBytes);
619 imageData->width = exrImage.width;
620 imageData->height = exrImage.height;
621 imageData->format = format;
622 imageData->components = format.getNumberOfComponent();
623 imageData->isSRGB =
false;
625 quint8 *target =
reinterpret_cast<quint8 *>(imageData->data);
633 for (
int c = 0; c < exrHeader.num_channels; c++) {
634 if (strcmp(exrHeader.channels[c].name,
"R") == 0)
636 else if (strcmp(exrHeader.channels[c].name,
"G") == 0)
638 else if (strcmp(exrHeader.channels[c].name,
"B") == 0)
640 else if (strcmp(exrHeader.channels[c].name,
"A") == 0)
643 const bool isSingleChannel = exrHeader.num_channels == 1;
646 if (exrHeader.tiled) {
647 for (
int it = 0; it < exrImage.num_tiles; it++) {
648 for (
int j = 0; j < exrHeader.tile_size_y; j++)
649 for (
int i = 0; i < exrHeader.tile_size_x; i++) {
651 exrImage.tiles[it].offset_x * exrHeader.tile_size_x + i;
653 exrImage.tiles[it].offset_y * exrHeader.tile_size_y + j;
654 const int inverseJJ = std::abs(jj - (exrImage.height - 1));
655 const int idx = ii + inverseJJ * exrImage.width;
658 if (ii >= exrImage.width) {
661 if (jj >= exrImage.height) {
664 const int srcIdx = i + j * exrHeader.tile_size_x;
665 unsigned char **src = exrImage.tiles[it].images;
666 if (isSingleChannel) {
667 rgbaF32[
R] =
reinterpret_cast<
float **>(src)[0][srcIdx];
668 rgbaF32[
G] = rgbaF32[
R];
669 rgbaF32[
B] = rgbaF32[
R];
670 rgbaF32[3] = rgbaF32[
R];
672 rgbaF32[
R] =
reinterpret_cast<
float **>(src)[idxR][srcIdx];
673 rgbaF32[
G] =
reinterpret_cast<
float **>(src)[idxG][srcIdx];
674 rgbaF32[
B] =
reinterpret_cast<
float **>(src)[idxB][srcIdx];
676 rgbaF32[3] =
reinterpret_cast<
float **>(src)[idxA][srcIdx];
680 format.encodeToPixel(rgbaF32, target, idx * bytesPerPixel);
685 for (
int y = exrImage.height - 1; y >= 0; --y) {
686 for (
int x = 0; x < exrImage.width; x++) {
687 const int i = y * exrImage.width + x;
688 if (isSingleChannel) {
689 rgbaF32[
R] =
reinterpret_cast<
float **>(exrImage.images)[0][i];
690 rgbaF32[
G] = rgbaF32[
R];
691 rgbaF32[
B] = rgbaF32[
R];
692 rgbaF32[3] = rgbaF32[
R];
694 rgbaF32[
R] =
reinterpret_cast<
float **>(exrImage.images)[idxR][i];
695 rgbaF32[
G] =
reinterpret_cast<
float **>(exrImage.images)[idxG][i];
696 rgbaF32[
B] =
reinterpret_cast<
float **>(exrImage.images)[idxB][i];
698 rgbaF32[3] =
reinterpret_cast<
float **>(exrImage.images)[idxA][i];
702 format.encodeToPixel(rgbaF32, target, idx * bytesPerPixel);
709 FreeEXRImage(&exrImage);
710 FreeEXRHeader(&exrHeader);
717QSSGLoadedTexture *QSSGLoadedTexture::loadLightmapImage(
const QString &inPath,
const QSSGRenderTextureFormat &inFormat,
const QString &key)
719 if (
auto stream = QSSGInputUtil::getStreamForFile(inPath))
720 return QSSGLightmapLoader::createTexture(stream, inFormat, key);
724QSSGLoadedTexture *QSSGLoadedTexture::loadHdrImage(
const QSharedPointer<QIODevice> &source,
const QSSGRenderTextureFormat &inFormat)
726 QSSGLoadedTexture *imageData =
nullptr;
728 QSSGRenderTextureFormat format = inFormat;
729 if (format.format == QSSGRenderTextureFormat::Unknown) {
734 format = QSSGRenderTextureFormat::RGBA16F;
738 imageData = loadRadianceHdr(source, format);
742 imageData = loadExr(source, format);
747QSSGLoadedTexture *QSSGLoadedTexture::loadTextureData(QSSGRenderTextureData *textureData)
749 QSSGLoadedTexture *imageData =
new QSSGLoadedTexture;
751 if (!textureData->format().isCompressedTextureFormat()) {
752 const int bytesPerPixel = textureData->format().getSizeofFormat();
753 const int bitCount = bytesPerPixel * 8;
754 const int pitch = calculatePitch(calculateLine(textureData->size().width(), bitCount));
755 size_t dataSize = size_t(textureData->size().height()) * pitch;
756 if (textureData->depth() > 0)
757 dataSize *= textureData->depth();
758 QSSG_CHECK_X(dataSize <= std::numeric_limits<quint32>::max(),
"Requested data size exceeds 4GB limit!");
759 imageData->dataSizeInBytes = quint32(dataSize);
761 imageData->data =
const_cast<
void*>(
reinterpret_cast<
const void*>(textureData->textureData().data()));
762 imageData->width = textureData->size().width();
763 imageData->height = textureData->size().height();
764 imageData->depth = textureData->depth();
765 imageData->format = textureData->format();
766 imageData->components = textureData->format().getNumberOfComponent();
770 imageData->data =
const_cast<
void*>(
reinterpret_cast<
const void*>(textureData->textureData().data()));
771 const size_t dataSize = textureData->textureData().size();
772 QSSG_CHECK_X(dataSize <= std::numeric_limits<quint32>::max(),
"Requested data size exceeds 4GB limit!");
773 imageData->dataSizeInBytes = quint32(dataSize);
777 imageData->width = textureData->size().width();
778 imageData->height = textureData->size().height();
779 imageData->format = textureData->format();
786 if (imageData->format == QSSGRenderTextureFormat::RGBE8 ||
787 imageData->format == QSSGRenderTextureFormat::RGBA16F ||
788 imageData->format == QSSGRenderTextureFormat::RGBA32F ||
789 imageData->format == QSSGRenderTextureFormat::BC6H)
790 imageData->isSRGB =
false;
792 imageData->isSRGB =
true;
799bool scanImageForAlpha(
const void *inData, quint32 inWidth, quint32 inHeight, quint32 inPixelSizeInBytes, quint8 inAlphaSizeInBits)
801 const quint8 *rowPtr =
reinterpret_cast<
const quint8 *>(inData);
802 bool hasAlpha =
false;
803 if (inAlphaSizeInBits == 0)
805 if (inPixelSizeInBytes != 2 && inPixelSizeInBytes != 4) {
809 if (inAlphaSizeInBits > 8) {
814 quint32 alphaRightShift = inPixelSizeInBytes * 8 - inAlphaSizeInBits;
815 quint32 maxAlphaValue = (1 << inAlphaSizeInBits) - 1;
817 for (quint32 rowIdx = 0; rowIdx < inHeight && !hasAlpha; ++rowIdx) {
818 for (quint32 idx = 0; idx < inWidth && !hasAlpha; ++idx, rowPtr += inPixelSizeInBytes) {
819 quint32 pixelValue = 0;
820 if (inPixelSizeInBytes == 2)
821 pixelValue = *(
reinterpret_cast<
const quint16 *>(rowPtr));
823 pixelValue = *(
reinterpret_cast<
const quint32 *>(rowPtr));
824 pixelValue = pixelValue >> alphaRightShift;
825 if (pixelValue < maxAlphaValue)
833QSSGLoadedTexture::~QSSGLoadedTexture()
835 if (data && image.sizeInBytes() <= 0 && ownsData)
839bool QSSGLoadedTexture::scanForTransparency()
const
841 switch (format.format) {
842 case QSSGRenderTextureFormat::SRGB8A8:
843 case QSSGRenderTextureFormat::RGBA8:
847 return scanImageForAlpha(data, width, height, 4, 8);
849 case QSSGRenderTextureFormat::SRGB8:
850 case QSSGRenderTextureFormat::RGB8:
851 case QSSGRenderTextureFormat::RGBE8:
853 case QSSGRenderTextureFormat::RGB565:
855 case QSSGRenderTextureFormat::RGBA5551:
859 return scanImageForAlpha(data, width, height, 2, 1);
861 case QSSGRenderTextureFormat::Alpha8:
863 case QSSGRenderTextureFormat::R8:
864 case QSSGRenderTextureFormat::Luminance8:
865 case QSSGRenderTextureFormat::RG8:
867 case QSSGRenderTextureFormat::LuminanceAlpha8:
871 return scanImageForAlpha(data, width, height, 2, 8);
872 case QSSGRenderTextureFormat::RGB_DXT1:
874 case QSSGRenderTextureFormat::RGBA_DXT3:
875 case QSSGRenderTextureFormat::RGBA_DXT1:
876 case QSSGRenderTextureFormat::RGBA_DXT5:
878 case QSSGRenderTextureFormat::RGB9E5:
880 case QSSGRenderTextureFormat::RG32F:
881 case QSSGRenderTextureFormat::RGB32F:
882 case QSSGRenderTextureFormat::RGBA16F:
883 case QSSGRenderTextureFormat::RGBA32F:
897 if (img1.size() != img2.size())
899 if (img1.pixelFormat().channelCount() != img2.pixelFormat().channelCount())
905static QSSGLoadedTexture *
loadCubeMap(
const QString &inPath,
bool flipY)
907 QStringList fileNames;
908 if (inPath.contains(QStringLiteral(
"%p"))) {
909 fileNames.reserve(6);
910 const char *faces[6] = {
"posx",
"negx",
"posy",
"negy",
"posz",
"negz" };
911 for (
const auto face : faces) {
912 QString fileName = inPath;
913 fileName.replace(QStringLiteral(
"%p"), QLatin1StringView(face));
914 fileNames << fileName;
917 }
else if (inPath.contains(QStringLiteral(
";"))) {
918 fileNames = inPath.split(QChar(u';'));
920 if (fileNames.size() != 6)
922 std::unique_ptr<QTextureFileData> textureFileData = std::make_unique<QTextureFileData>(QTextureFileData::ImageMode);
923 textureFileData->setNumFaces(6);
924 textureFileData->setNumLevels(1);
925 textureFileData->setLogName(inPath.toUtf8());
927 for (
int i = 0; i < 6; ++i) {
928 QString searchName = fileNames[i];
930 auto stream = QSSGInputUtil::getStreamForFile(searchName,
true, &filePath);
934 QImage face = loadImage(filePath, !flipY);
935 if (face.isNull() || (!prevImage.isNull() && !isCompatible(prevImage, face))) {
938 textureFileData->setData(face, 0, i);
939 textureFileData->setSize(face.size());
943 QSSGLoadedTexture *retval =
new QSSGLoadedTexture;
945 retval->textureFileData = *textureFileData;
947 retval->width = prevImage.width();
948 retval->height = prevImage.height();
949 retval->components = prevImage.pixelFormat().channelCount();
950 retval->image = prevImage;
951 retval->data = (
void *)retval->image.bits();
952 const size_t dataSize = prevImage.sizeInBytes();
953 QSSG_CHECK_X(dataSize <= std::numeric_limits<quint32>::max(),
"Requested data size exceeds 4GB limit!");
954 retval->dataSizeInBytes = quint32(dataSize);
955 retval->setFormatFromComponents();
957 retval->isSRGB = prevImage.colorSpace().transferFunction() != QColorSpace::TransferFunction::Linear;
962QSSGLoadedTexture *QSSGLoadedTexture::load(
const QString &inPath,
963 const QSSGRenderTextureFormat &inFormat,
966 if (inPath.isEmpty())
969 QSSGLoadedTexture *theLoadedImage =
nullptr;
971 QSSGInputUtil::FileType fileType = QSSGInputUtil::UnknownFile;
972 QSharedPointer<QIODevice> theStream =
973 QSSGInputUtil::getStreamForTextureFile(inPath,
true, &fileName, &fileType);
977 case QSSGInputUtil::HdrFile:
980 theLoadedImage = loadHdrImage(theStream, inFormat);
982 case QSSGInputUtil::TextureFile:
983 theLoadedImage = loadCompressedImage(fileName);
986 theLoadedImage = loadQImage(fileName, inFlipY);
991 return loadCubeMap(inPath, inFlipY);
993 return theLoadedImage;
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)