1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "lightmapimageprovider.h"
5
6#include "lightmapviewerhelpers.h"
7
8#include <QUrlQuery>
9#include <QtQuick3DRuntimeRender/private/qssglightmapio_p.h>
10
11LightmapImageProvider::LightmapImageProvider() : QQuickImageProvider(QQuickImageProvider::Image)
12{
13 // Generate error image
14 constexpr int width = 100;
15 m_errorImage = QImage(QSize(width, width), QImage::Format::Format_RGB888);
16 m_errorImage.fill(color: QColorConstants::White);
17 for (int i = 0; i < width; i++) {
18 m_errorImage.setPixelColor(x: i, y: i, c: QColorConstants::Red);
19 m_errorImage.setPixelColor(x: width - i - 1, y: i, c: QColorConstants::Red);
20 }
21}
22
23enum class LightmapDataType { Unset, F32, U32 };
24
25QImage LightmapImageProvider::requestImage(const QString &id, QSize *size, const QSize & /*requestedSize*/)
26{
27 if (size)
28 *size = QSize(100, 100);
29 const QUrl url = QUrl("lightmap://?" + id, QUrl::StrictMode);
30 const QUrlQuery query(url);
31 const QString keyWithTag = query.queryItemValue(key: "key");
32
33 QString key = keyWithTag;
34 QSSGLightmapIODataTag tag = QSSGLightmapIODataTag::Unset;
35 if (int sep = keyWithTag.indexOf(ch: '$'); sep >= 0) {
36 key = keyWithTag.left(n: sep);
37 const QString tagString = keyWithTag.mid(position: sep + 1);
38 tag = LightmapViewerHelpers::stringToLightmapTag(tag: tagString);
39 }
40
41 const QUrl filePath = query.queryItemValue(key: "file");
42 const bool useAlpha = query.queryItemValue(key: "alpha") == QStringLiteral("true");
43
44 LightmapDataType dataType = LightmapDataType::Unset;
45 switch (tag) {
46 case QSSGLightmapIODataTag::Mask:
47 dataType = LightmapDataType::U32;
48 break;
49 case QSSGLightmapIODataTag::Texture_Final:
50 case QSSGLightmapIODataTag::Texture_Direct:
51 case QSSGLightmapIODataTag::Texture_Indirect:
52 dataType = LightmapDataType::F32;
53 break;
54 case QSSGLightmapIODataTag::Metadata:
55 case QSSGLightmapIODataTag::Mesh:
56 case QSSGLightmapIODataTag::Unset:
57 default:
58 break;
59 }
60
61 QSharedPointer<QSSGLightmapLoader> loader = QSSGLightmapLoader::open(path: filePath.toLocalFile());
62
63 if (!loader)
64 return m_errorImage;
65
66 QVariantMap metadata = loader->readMetadata(key);
67 if (metadata.isEmpty())
68 return m_errorImage;
69 bool ok = false;
70 const int width = metadata[QStringLiteral("width")].toInt(ok: &ok);
71 if (!ok)
72 return m_errorImage;
73 const int height = metadata[QStringLiteral("height")].toInt(ok: &ok);
74 if (!ok)
75 return m_errorImage;
76
77 QImage::Format format = QImage::Format_Invalid;
78 QByteArray array;
79 if (dataType == LightmapDataType::U32) {
80 array = loader->readU32Image(key, tag);
81 if (array.size() != qsizetype(sizeof(quint32) * width * height))
82 return m_errorImage;
83 format = QImage::Format_RGBA8888;
84 LightmapViewerHelpers::maskToBBGRColor(array, useAlpha);
85 } else if (dataType == LightmapDataType::F32) {
86 array = loader->readF32Image(key, tag);
87 if (array.size() != qsizetype(4 * sizeof(float) * width * height))
88 return m_errorImage;
89 if (!useAlpha) {
90 std::array<float, 4> *rgbas = reinterpret_cast<std::array<float, 4> *>(array.data());
91 for (int i = 0, n = array.size() / (4 * sizeof(float)); i < n; ++i)
92 rgbas[i][3] = 1.0f;
93 }
94 format = QImage::Format_RGBA32FPx4;
95 } else {
96 return m_errorImage;
97 }
98
99 // Everything should be OK here
100 if (size)
101 *size = QSize(width, height);
102 QImage result(width, height, format);
103 memcpy(dest: result.bits(), src: array.data(), n: array.size());
104 return result;
105}
106

source code of qtquick3d/tools/lightmapviewer/lightmapimageprovider.cpp