5#include <QtQuick3DRuntimeRender/private/qssgrenderloadedtexture_p.h>
6#include <QtQuick3DRuntimeRender/private/qssgrendererutil_p.h>
7#include <QtQuick3DRuntimeRender/private/qssgruntimerenderlogging_p.h>
8#include <QtQuick3DRuntimeRender/private/qssgrendertexturedata_p.h>
9#include <QtGui/QImageReader>
10#include <QtGui/QColorSpace>
13#include <QtQuick3DUtils/private/qssgutils_p.h>
14#include <QtQuick3DUtils/private/qssgassert_p.h>
16#include <private/qtexturefilereader_p.h>
18#define TINYEXR_IMPLEMENTATION
19#define TINYEXR_USE_MINIZ 0
20#define TINYEXR_USE_THREAD 1
32 bool found = fi.exists();
33 if (!found && fi.isNativePath()) {
39 QString filePath = fi.canonicalFilePath();
49 if (!
file && !inQuiet)
51 return QSharedPointer<QIODevice>(
file);
57 static const QList<QByteArray> hdrFormats = QList<QByteArray>({
"hdr",
"exr" });
60 static const QList<QByteArray> allFormats = textureFormats + hdrFormats + imageFormats;
82 if (hdrFormats.contains(ext))
84 else if (textureFormats.contains(ext))
86 else if (imageFormats.contains(ext))
90 }
else if (!inQuiet) {
277 if (
image.colorCount())
279 else if (pixFormat.channelCount() == 1)
286 image.convertTo(targetFormat);
299 retval->height =
image.height();
300 retval->components =
image.pixelFormat().channelCount();
301 retval->image =
image;
302 retval->data = (
void *)retval->image.
bits();
303 retval->dataSizeInBytes =
image.sizeInBytes();
304 retval->setFormatFromComponents();
316 QFile imageFile(inPath);
318 qWarning() <<
"Could not open image file: " << inPath;
324 qWarning() <<
"Unable to read image file: " << inPath;
332 retval->
width = retval->textureFileData.size().width();
333 retval->height = retval->textureFileData.size().height();
334 auto glFormat = retval->textureFileData.glInternalFormat()
335 ? retval->textureFileData.glInternalFormat()
336 : retval->textureFileData.glFormat();
347typedef unsigned char RGBE[4];
354#define MAXELEN 0x7fff
358inline int calculateLine(
int width,
int bitdepth) {
return ((
width * bitdepth) + 7) / 8; }
360inline int calculatePitch(
int line) {
return (
line + 3) & ~3; }
364 float v =
val / (256.0f);
365 float d = powf(2.0f, (
float)
exponent - 128.0f);
369void decrunchScanline(
const char *&
p,
const char *pEnd, RGBE *scanline,
int w)
371 scanline[0][
R] = *
p++;
372 scanline[0][
G] = *
p++;
373 scanline[0][
B] = *
p++;
374 scanline[0][
E] = *
p++;
376 if (scanline[0][
R] == 2 && scanline[0][
G] == 2 && scanline[0][
B] < 128) {
379 for (
int x = 0;
x <
w &&
p < pEnd; ) {
380 unsigned char c = *
p++;
383 int repCount =
c & 127;
389 while (
c-- &&
p < pEnd)
399 while (x < w && pEnd - p >= 4) {
400 scanline[
x][
R] = *
p++;
401 scanline[
x][
G] = *
p++;
402 scanline[
x][
B] = *
p++;
403 scanline[
x][
E] = *
p++;
405 if (scanline[
x][
R] == 1 && scanline[
x][
G] == 1 && scanline[
x][
B] == 1) {
406 int repCount = scanline[
x][3] << bitshift;
408 memcpy(scanline[
x], scanline[
x - 1], 4);
430 rgbaF32[
R] = convertComponent(scanline[
i][
E], scanline[
i][
R]);
431 rgbaF32[
G] = convertComponent(scanline[
i][
E], scanline[
i][
G]);
432 rgbaF32[
B] = convertComponent(scanline[
i][
E], scanline[
i][
B]);
435 inFormat.encodeToPixel(rgbaF32,
target,
i * inFormat.getSizeofFormat());
447 if (!strncmp(sig,
"#?RADIANCE\n", 11)) {
450 const char *pEnd =
p +
buf.size();
472 qWarning(
"Malformed HDR image data at property strings");
484 qWarning(
"Malformed HDR image data at resolution string");
504 const int bytesPerPixel =
format.getSizeofFormat();
505 const int bitCount = bytesPerPixel * 8;
506 const int pitch = calculatePitch(calculateLine(
width, bitCount));
508 QSSG_CHECK_X(
dataSize <= std::numeric_limits<quint32>::max(),
"Requested data size exceeds 4GB limit!");
519 RGBE *scanline =
new RGBE[
width];
526 qWarning(
"Unexpected end of HDR data");
530 decrunchScanline(
p, pEnd, scanline,
width);
544 char versionBuffer[tinyexr::kEXRVersionSize];
546 auto size =
source->read(versionBuffer, tinyexr::kEXRVersionSize);
548 if (
size != tinyexr::kEXRVersionSize)
551 EXRVersion exrVersion;
552 if (ParseEXRVersionFromMemory(&exrVersion,
reinterpret_cast<unsigned char *
>(versionBuffer), tinyexr::kEXRVersionSize) != TINYEXR_SUCCESS)
556 if (exrVersion.multipart)
562 const char *err =
nullptr;
565 InitEXRHeader(&exrHeader);
566 if (ParseEXRHeaderFromMemory(&exrHeader,
568 reinterpret_cast<const unsigned char *
>(
buf.constData()),
570 &err) != TINYEXR_SUCCESS) {
571 qWarning(
"Failed to parse EXR Header with error: '%s'", err);
572 FreeEXRErrorMessage(err);
577 for (
int i = 0;
i < exrHeader.num_channels;
i++) {
578 if (exrHeader.pixel_types[
i] == TINYEXR_PIXELTYPE_HALF)
579 exrHeader.requested_pixel_types[
i] = TINYEXR_PIXELTYPE_FLOAT;
585 InitEXRImage(&exrImage);
586 if (LoadEXRImageFromMemory(&exrImage,
588 reinterpret_cast<const unsigned char *
>(
buf.constData()),
590 &err) != TINYEXR_SUCCESS) {
591 qWarning(
"Failed to load EXR Image with error: '%s'", err);
592 FreeEXRHeader(&exrHeader);
593 FreeEXRErrorMessage(err);
598 const int bytesPerPixel =
format.getSizeofFormat();
599 const int bitCount = bytesPerPixel * 8;
600 const int pitch = calculatePitch(calculateLine(exrImage.width, bitCount));
601 const size_t dataSize = exrImage.height * pitch;
602 QSSG_CHECK_X(
dataSize <= std::numeric_limits<quint32>::max(),
"Requested data size exceeds 4GB limit!");
620 for (
int c = 0;
c < exrHeader.num_channels;
c++) {
621 if (strcmp(exrHeader.channels[
c].name,
"R") == 0)
623 else if (strcmp(exrHeader.channels[
c].name,
"G") == 0)
625 else if (strcmp(exrHeader.channels[
c].name,
"B") == 0)
627 else if (strcmp(exrHeader.channels[
c].name,
"A") == 0)
630 const bool isSingleChannel = exrHeader.num_channels == 1;
633 if (exrHeader.tiled) {
634 for (
int it = 0;
it < exrImage.num_tiles;
it++) {
635 for (
int j = 0;
j < exrHeader.tile_size_y;
j++)
636 for (
int i = 0;
i < exrHeader.tile_size_x;
i++) {
638 exrImage.tiles[
it].offset_x * exrHeader.tile_size_x +
i;
640 exrImage.tiles[
it].offset_y * exrHeader.tile_size_y +
j;
641 const int inverseJJ = std::abs(jj - (exrImage.height - 1));
642 const int idx = ii + inverseJJ * exrImage.width;
645 if (ii >= exrImage.width) {
648 if (jj >= exrImage.height) {
651 const int srcIdx =
i +
j * exrHeader.tile_size_x;
652 unsigned char **
src = exrImage.tiles[
it].images;
653 if (isSingleChannel) {
654 rgbaF32[
R] =
reinterpret_cast<float **
>(
src)[0][srcIdx];
655 rgbaF32[
G] = rgbaF32[
R];
656 rgbaF32[
B] = rgbaF32[
R];
657 rgbaF32[3] = rgbaF32[
R];
659 rgbaF32[
R] =
reinterpret_cast<float **
>(
src)[idxR][srcIdx];
660 rgbaF32[
G] =
reinterpret_cast<float **
>(
src)[idxG][srcIdx];
661 rgbaF32[
B] =
reinterpret_cast<float **
>(
src)[idxB][srcIdx];
663 rgbaF32[3] =
reinterpret_cast<float **
>(
src)[idxA][srcIdx];
667 format.encodeToPixel(rgbaF32,
target, idx * bytesPerPixel);
672 for (
int y = exrImage.height - 1;
y >= 0; --
y) {
673 for (
int x = 0;
x < exrImage.width;
x++) {
674 const int i =
y * exrImage.width +
x;
675 if (isSingleChannel) {
676 rgbaF32[
R] =
reinterpret_cast<float **
>(exrImage.images)[0][
i];
677 rgbaF32[
G] = rgbaF32[
R];
678 rgbaF32[
B] = rgbaF32[
R];
679 rgbaF32[3] = rgbaF32[
R];
681 rgbaF32[
R] =
reinterpret_cast<float **
>(exrImage.images)[idxR][
i];
682 rgbaF32[
G] =
reinterpret_cast<float **
>(exrImage.images)[idxG][
i];
683 rgbaF32[
B] =
reinterpret_cast<float **
>(exrImage.images)[idxB][
i];
685 rgbaF32[3] =
reinterpret_cast<float **
>(exrImage.images)[idxA][
i];
689 format.encodeToPixel(rgbaF32,
target, idx * bytesPerPixel);
696 FreeEXRImage(&exrImage);
697 FreeEXRHeader(&exrHeader);
733 const int bitCount = bytesPerPixel * 8;
734 const int pitch = calculatePitch(calculateLine(textureData->
size().
width(), bitCount));
736 if (textureData->
depth() > 0)
738 QSSG_CHECK_X(
dataSize <= std::numeric_limits<quint32>::max(),
"Requested data size exceeds 4GB limit!");
752 QSSG_CHECK_X(
dataSize <= std::numeric_limits<quint32>::max(),
"Requested data size exceeds 4GB limit!");
781 const quint8 *rowPtr =
reinterpret_cast<const quint8 *
>(inData);
783 if (inAlphaSizeInBits == 0)
785 if (inPixelSizeInBytes != 2 && inPixelSizeInBytes != 4) {
789 if (inAlphaSizeInBits > 8) {
794 quint32 alphaRightShift = inPixelSizeInBytes * 8 - inAlphaSizeInBits;
795 quint32 maxAlphaValue = (1 << inAlphaSizeInBits) - 1;
798 for (
quint32 idx = 0; idx < inWidth && !
hasAlpha; ++idx, rowPtr += inPixelSizeInBytes) {
800 if (inPixelSizeInBytes == 2)
801 pixelValue = *(
reinterpret_cast<const quint16 *
>(rowPtr));
803 pixelValue = *(
reinterpret_cast<const quint32 *
>(rowPtr));
804 pixelValue = pixelValue >> alphaRightShift;
805 if (pixelValue < maxAlphaValue)
877 if (img1.size() != img2.size())
879 if (img1.pixelFormat().channelCount() != img2.pixelFormat().channelCount())
890 const char *faces[6] = {
"posx",
"negx",
"posy",
"negy",
"posz",
"negz" };
891 for (
const auto face : faces) {
903 textureFileData->setNumFaces(6);
904 textureFileData->setNumLevels(1);
905 textureFileData->setLogName(inPath.toUtf8());
907 for (
int i = 0;
i < 6; ++
i) {
918 textureFileData->setData(
face, 0,
i);
919 textureFileData->setSize(
face.size());
927 retval->width = prevImage.width();
928 retval->height = prevImage.height();
929 retval->components = prevImage.pixelFormat().channelCount();
930 retval->image = prevImage;
931 retval->data = (
void *)retval->image.bits();
932 const size_t dataSize = prevImage.sizeInBytes();
933 QSSG_CHECK_X(
dataSize <= std::numeric_limits<quint32>::max(),
"Requested data size exceeds 4GB limit!");
935 retval->setFormatFromComponents();
946 if (inPath.isEmpty())
952 QSharedPointer<QIODevice> theStream =
973 return theLoadedImage;
IOBluetoothL2CAPChannel * channel
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
QByteArray toLower() const &
QString suffix() const
Returns the suffix (extension) of the file.
QFILE_MAYBE_NODISCARD bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
bool canRead() const
Returns true if an image can be read for the device (i.e., the image format is supported,...
static QList< QByteArray > supportedImageFormats()
Returns the list of image formats supported by QImageReader.
QImage read()
Reads an image from the device.
int width() const
Returns the width of the image.
uchar * bits()
Returns a pointer to the first pixel data.
Format
The following image formats are available in Qt.
@ Format_RGBA8888_Premultiplied
const QByteArray & textureData() const
QSSGRenderTextureFormat format() const
constexpr int height() const noexcept
Returns the height.
constexpr int width() const noexcept
Returns the width.
\macro QT_RESTRICTED_CAST_FROM_ASCII
QByteArray toLatin1() const &
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
QString & replace(qsizetype i, qsizetype len, QChar after)
QString mid(qsizetype position, qsizetype n=-1) const &
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
void clear()
Clears the contents of the string and makes it null.
const QChar * constData() const
Returns a pointer to the data stored in the QString.
QString & append(QChar c)
QString trimmed() const &
static QList< QByteArray > supportedFileFormats()
QSet< QString >::iterator it
Combined button and popup list for selecting options.
#define QByteArrayLiteral(str)
#define qCWarning(category,...)
GLsizei const GLfloat * v
[13]
GLint GLint GLint GLint GLint x
[0]
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint GLenum GLsizei const GLchar * buf
GLenum GLuint GLintptr offset
GLint GLsizei GLsizei GLenum format
GLsizei GLenum internalFormat
GLsizei GLsizei GLchar * source
static bool hasAlpha(const QImage &image)
#define QSSG_CHECK_X(cond, msg)
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)
#define qPrintable(string)
QLatin1StringView QLatin1String
#define QStringLiteral(str)
static FileType fileType(const QFileInfo &fi)
\inmodule QtCore \reentrant
QTextureFileData textureFileData
static QSSGLoadedTexture * loadHdrImage(const QSharedPointer< QIODevice > &source, const QSSGRenderTextureFormat &inFormat)
static QSSGLoadedTexture * loadCompressedImage(const QString &inPath)
static QSSGLoadedTexture * load(const QString &inPath, const QSSGRenderTextureFormat &inFormat, bool inFlipY=true)
static QSSGLoadedTexture * loadTextureData(QSSGRenderTextureData *textureData)
bool scanForTransparency() const
static QSSGLoadedTexture * loadQImage(const QString &inPath, qint32 flipVertical)
@ SRGB8_Alpha8_ASTC_12x10
@ SRGB8_Alpha8_ASTC_12x12
@ SRGB8_Alpha8_ASTC_10x10
@ SRGB8_PunchThrough_Alpha1_ETC2
@ RGB8_PunchThrough_Alpha1_ETC2
qint32 getNumberOfComponent() const noexcept
constexpr bool isCompressedTextureFormat() const noexcept
qint32 getSizeofFormat() const noexcept