forked from qt-creator/qt-creator
QmlDesigner: Generate icons and preview for HDR images
Copied HDR texture generation algorithm from QtQuick3D and modified it to create pixmaps for asset icon and navigator preview images. Fixes: QDS-3684 Change-Id: Ia7f1240bbeb4a23c00fb8cb2685e769c2b599abb Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io> Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
@@ -213,6 +213,7 @@ extend_qtc_plugin(QmlDesigner
|
|||||||
selectioncontext.cpp selectioncontext.h
|
selectioncontext.cpp selectioncontext.h
|
||||||
theme.cpp theme.h
|
theme.cpp theme.h
|
||||||
zoomaction.cpp zoomaction.h
|
zoomaction.cpp zoomaction.h
|
||||||
|
hdrimage.cpp hdrimage.h
|
||||||
)
|
)
|
||||||
|
|
||||||
extend_qtc_plugin(QmlDesigner
|
extend_qtc_plugin(QmlDesigner
|
||||||
|
@@ -19,6 +19,7 @@ SOURCES += navigation2d.cpp
|
|||||||
SOURCES += crumblebar.cpp
|
SOURCES += crumblebar.cpp
|
||||||
SOURCES += qmldesignericonprovider.cpp
|
SOURCES += qmldesignericonprovider.cpp
|
||||||
SOURCES += zoomaction.cpp
|
SOURCES += zoomaction.cpp
|
||||||
|
SOURCES += hdrimage.cpp
|
||||||
|
|
||||||
HEADERS += modelnodecontextmenu.h
|
HEADERS += modelnodecontextmenu.h
|
||||||
HEADERS += addimagesdialog.h
|
HEADERS += addimagesdialog.h
|
||||||
@@ -41,6 +42,7 @@ HEADERS += actioninterface.h
|
|||||||
HEADERS += crumblebar.h
|
HEADERS += crumblebar.h
|
||||||
HEADERS += qmldesignericonprovider.h
|
HEADERS += qmldesignericonprovider.h
|
||||||
HEADERS += zoomaction.h
|
HEADERS += zoomaction.h
|
||||||
|
HEADERS += hdrimage.h
|
||||||
|
|
||||||
FORMS += \
|
FORMS += \
|
||||||
$$PWD/addsignalhandlerdialog.ui
|
$$PWD/addsignalhandlerdialog.ui
|
||||||
|
276
src/plugins/qmldesigner/components/componentcore/hdrimage.cpp
Normal file
276
src/plugins/qmldesigner/components/componentcore/hdrimage.cpp
Normal file
@@ -0,0 +1,276 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2021 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "hdrimage.h"
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
#include <QFileInfo>
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
// Hdr loading code was copied from QtQuick3D and adapted to create QImage instead of texture.
|
||||||
|
|
||||||
|
typedef unsigned char RGBE[4];
|
||||||
|
#define R 0
|
||||||
|
#define G 1
|
||||||
|
#define B 2
|
||||||
|
#define E 3
|
||||||
|
|
||||||
|
struct M8E8
|
||||||
|
{
|
||||||
|
quint8 m;
|
||||||
|
quint8 e;
|
||||||
|
M8E8() : m(0), e(0){
|
||||||
|
}
|
||||||
|
M8E8(const float val) {
|
||||||
|
float l2 = 1.f + std::floor(log2f(val));
|
||||||
|
float mm = val / powf(2.f, l2);
|
||||||
|
m = quint8(mm * 255.f);
|
||||||
|
e = quint8(l2 + 128);
|
||||||
|
}
|
||||||
|
M8E8(const float val, quint8 exp) {
|
||||||
|
if (val <= 0) {
|
||||||
|
m = e = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
float mm = val / powf(2.f, exp - 128);
|
||||||
|
m = quint8(mm * 255.f);
|
||||||
|
e = exp;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
QByteArray fileToByteArray(QString const &filename)
|
||||||
|
{
|
||||||
|
QFile file(filename);
|
||||||
|
QFileInfo info(file);
|
||||||
|
|
||||||
|
if (info.exists() && file.open(QFile::ReadOnly))
|
||||||
|
return file.readAll();
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int calculateLine(int width, int bitdepth) { return ((width * bitdepth) + 7) / 8; }
|
||||||
|
|
||||||
|
inline int calculatePitch(int line) { return (line + 3) & ~3; }
|
||||||
|
|
||||||
|
float convertComponent(int exponent, int val)
|
||||||
|
{
|
||||||
|
float v = val / (256.0f);
|
||||||
|
float d = powf(2.0f, (float)exponent - 128.0f);
|
||||||
|
return v * d;
|
||||||
|
}
|
||||||
|
|
||||||
|
void decrunchScanline(const char *&p, const char *pEnd, RGBE *scanline, int w)
|
||||||
|
{
|
||||||
|
scanline[0][R] = *p++;
|
||||||
|
scanline[0][G] = *p++;
|
||||||
|
scanline[0][B] = *p++;
|
||||||
|
scanline[0][E] = *p++;
|
||||||
|
|
||||||
|
if (scanline[0][R] == 2 && scanline[0][G] == 2 && scanline[0][B] < 128) {
|
||||||
|
// new rle, the first pixel was a dummy
|
||||||
|
for (int channel = 0; channel < 4; ++channel) {
|
||||||
|
for (int x = 0; x < w && p < pEnd; ) {
|
||||||
|
unsigned char c = *p++;
|
||||||
|
if (c > 128) { // run
|
||||||
|
if (p < pEnd) {
|
||||||
|
int repCount = c & 127;
|
||||||
|
c = *p++;
|
||||||
|
while (repCount--)
|
||||||
|
scanline[x++][channel] = c;
|
||||||
|
}
|
||||||
|
} else { // not a run
|
||||||
|
while (c-- && p < pEnd)
|
||||||
|
scanline[x++][channel] = *p++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// old rle
|
||||||
|
scanline[0][R] = 2;
|
||||||
|
int bitshift = 0;
|
||||||
|
int x = 1;
|
||||||
|
while (x < w && pEnd - p >= 4) {
|
||||||
|
scanline[x][R] = *p++;
|
||||||
|
scanline[x][G] = *p++;
|
||||||
|
scanline[x][B] = *p++;
|
||||||
|
scanline[x][E] = *p++;
|
||||||
|
|
||||||
|
if (scanline[x][R] == 1 && scanline[x][G] == 1 && scanline[x][B] == 1) { // run
|
||||||
|
int repCount = scanline[x][3] << bitshift;
|
||||||
|
while (repCount--) {
|
||||||
|
memcpy(scanline[x], scanline[x - 1], 4);
|
||||||
|
++x;
|
||||||
|
}
|
||||||
|
bitshift += 8;
|
||||||
|
} else { // not a run
|
||||||
|
++x;
|
||||||
|
bitshift = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void decodeScanlineToImageData(RGBE *scanline, int width, void *outBuf, quint32 offset)
|
||||||
|
{
|
||||||
|
quint8 *target = reinterpret_cast<quint8 *>(outBuf);
|
||||||
|
target += offset;
|
||||||
|
|
||||||
|
float rgbaF32[4];
|
||||||
|
for (int i = 0; i < width; ++i) {
|
||||||
|
rgbaF32[R] = convertComponent(scanline[i][E], scanline[i][R]);
|
||||||
|
rgbaF32[G] = convertComponent(scanline[i][E], scanline[i][G]);
|
||||||
|
rgbaF32[B] = convertComponent(scanline[i][E], scanline[i][B]);
|
||||||
|
rgbaF32[3] = 1.0f;
|
||||||
|
|
||||||
|
float max = qMax(rgbaF32[R], qMax(rgbaF32[G], rgbaF32[B]));
|
||||||
|
M8E8 ex(max);
|
||||||
|
M8E8 a(rgbaF32[R], ex.e);
|
||||||
|
M8E8 b(rgbaF32[G], ex.e);
|
||||||
|
M8E8 c(rgbaF32[B], ex.e);
|
||||||
|
quint8 *dst = target + i * 4;
|
||||||
|
dst[0] = c.m;
|
||||||
|
dst[1] = b.m;
|
||||||
|
dst[2] = a.m;
|
||||||
|
dst[3] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HdrImage::HdrImage(const QString &fileName)
|
||||||
|
: m_fileName(fileName)
|
||||||
|
{
|
||||||
|
loadHdr();
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap HdrImage::toPixmap() const
|
||||||
|
{
|
||||||
|
// Copy is used to ensure pixmap will be valid after HdrImage goes out of scope
|
||||||
|
return QPixmap::fromImage(m_image, Qt::NoFormatConversion).copy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HdrImage::loadHdr()
|
||||||
|
{
|
||||||
|
QByteArray buf(fileToByteArray(m_fileName));
|
||||||
|
|
||||||
|
auto handleError = [this](const QString &error) {
|
||||||
|
qWarning(QStringLiteral("Failed to load HDR image '%1': %2.").arg(m_fileName, error).toUtf8());
|
||||||
|
};
|
||||||
|
|
||||||
|
if (buf.isEmpty()) {
|
||||||
|
handleError("File open failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!buf.startsWith(QByteArrayLiteral("#?RADIANCE\n"))) {
|
||||||
|
handleError("Non-HDR file");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *p = buf.constData();
|
||||||
|
const char *pEnd = p + buf.size();
|
||||||
|
|
||||||
|
// Process lines until the empty one.
|
||||||
|
QByteArray line;
|
||||||
|
QByteArray formatTag {"FORMAT="};
|
||||||
|
QByteArray formatId {"32-bit_rle_rgbe"};
|
||||||
|
while (p < pEnd) {
|
||||||
|
char c = *p++;
|
||||||
|
if (c == '\n') {
|
||||||
|
if (line.isEmpty())
|
||||||
|
break;
|
||||||
|
if (line.startsWith(formatTag)) {
|
||||||
|
const QByteArray format = line.mid(7).trimmed();
|
||||||
|
if (format != formatId) {
|
||||||
|
handleError(QStringLiteral("Unsupported HDR format '%1'")
|
||||||
|
.arg(QString::fromUtf8(format)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
line.clear();
|
||||||
|
} else {
|
||||||
|
line.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (p == pEnd) {
|
||||||
|
handleError("Malformed HDR image data at property strings");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the resolution string.
|
||||||
|
while (p < pEnd) {
|
||||||
|
char c = *p++;
|
||||||
|
if (c == '\n')
|
||||||
|
break;
|
||||||
|
line.append(c);
|
||||||
|
}
|
||||||
|
if (p == pEnd) {
|
||||||
|
handleError("Malformed HDR image data at resolution string");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int width = 0;
|
||||||
|
int height = 0;
|
||||||
|
// We only care about the standard orientation
|
||||||
|
if (!::sscanf(line.constData(), "-Y %d +X %d", &height, &width)) {
|
||||||
|
handleError(QStringLiteral("Unsupported HDR resolution string '%1'")
|
||||||
|
.arg(QString::fromUtf8(line)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (width <= 0 || height <= 0) {
|
||||||
|
handleError("Invalid HDR resolution");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int bytesPerPixel = 4;
|
||||||
|
const int bitCount = bytesPerPixel * 8;
|
||||||
|
const int pitch = calculatePitch(calculateLine(width, bitCount));
|
||||||
|
const int dataSize = int(height * pitch);
|
||||||
|
|
||||||
|
m_buf = QByteArray {dataSize, 0};
|
||||||
|
|
||||||
|
// Allocate a scanline worth of RGBE data
|
||||||
|
RGBE *scanline = new RGBE[width];
|
||||||
|
|
||||||
|
for (int y = 0; y < height; ++y) {
|
||||||
|
quint32 byteOffset = quint32(y * width * bytesPerPixel);
|
||||||
|
if (pEnd - p < 4) {
|
||||||
|
handleError("Unexpected end of HDR data");
|
||||||
|
delete[] scanline;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
decrunchScanline(p, pEnd, scanline, width);
|
||||||
|
decodeScanlineToImageData(scanline, width, m_buf.data(), byteOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] scanline;
|
||||||
|
|
||||||
|
m_image = QImage {reinterpret_cast<const uchar *>(m_buf.constData()), width, height, pitch,
|
||||||
|
QImage::Format_ARGB32};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QmlDesigner
|
52
src/plugins/qmldesigner/components/componentcore/hdrimage.h
Normal file
52
src/plugins/qmldesigner/components/componentcore/hdrimage.h
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2021 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <qmldesignercorelib_global.h>
|
||||||
|
|
||||||
|
#include <QImage>
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
class QMLDESIGNERCORE_EXPORT HdrImage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HdrImage(const QString &fileName);
|
||||||
|
|
||||||
|
QString fileName() const { return m_fileName; }
|
||||||
|
const QImage &image() const { return m_image; }
|
||||||
|
QPixmap toPixmap() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void loadHdr();
|
||||||
|
|
||||||
|
QImage m_image;
|
||||||
|
QString m_fileName;
|
||||||
|
QByteArray m_buf;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QmlDesigner
|
@@ -27,6 +27,7 @@
|
|||||||
|
|
||||||
#include <synchronousimagecache.h>
|
#include <synchronousimagecache.h>
|
||||||
#include <theme.h>
|
#include <theme.h>
|
||||||
|
#include <hdrimage.h>
|
||||||
|
|
||||||
#include <utils/filesystemwatcher.h>
|
#include <utils/filesystemwatcher.h>
|
||||||
|
|
||||||
@@ -94,6 +95,11 @@ static QPixmap defaultPixmapForType(const QString &type, const QSize &size)
|
|||||||
return QPixmap(QStringLiteral(":/ItemLibrary/images/asset_%1_%2.png").arg(type).arg(size.width()));
|
return QPixmap(QStringLiteral(":/ItemLibrary/images/asset_%1_%2.png").arg(type).arg(size.width()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QPixmap texturePixmap(const QString &fileName)
|
||||||
|
{
|
||||||
|
return HdrImage{fileName}.toPixmap();
|
||||||
|
}
|
||||||
|
|
||||||
QString fontFamily(const QFileInfo &info)
|
QString fontFamily(const QFileInfo &info)
|
||||||
{
|
{
|
||||||
QRawFont font(info.absoluteFilePath(), 10);
|
QRawFont font(info.absoluteFilePath(), 10);
|
||||||
@@ -117,24 +123,26 @@ public:
|
|||||||
const QString suffix = info.suffix();
|
const QString suffix = info.suffix();
|
||||||
const QString filePath = info.absoluteFilePath();
|
const QString filePath = info.absoluteFilePath();
|
||||||
|
|
||||||
|
// Provide icon depending on suffix
|
||||||
|
QPixmap origPixmap;
|
||||||
|
|
||||||
if (supportedFontSuffixes().contains(suffix))
|
if (supportedFontSuffixes().contains(suffix))
|
||||||
return generateFontIcons(filePath);
|
return generateFontIcons(filePath);
|
||||||
|
else if (supportedImageSuffixes().contains(suffix))
|
||||||
|
origPixmap.load(filePath);
|
||||||
|
else if (supportedTexture3DSuffixes().contains(suffix))
|
||||||
|
origPixmap = texturePixmap(filePath);
|
||||||
|
|
||||||
for (auto iconSize : iconSizes) {
|
for (auto iconSize : iconSizes) {
|
||||||
// Provide icon depending on suffix
|
QPixmap pixmap = origPixmap;
|
||||||
QPixmap pixmap;
|
if (pixmap.isNull()) {
|
||||||
|
if (supportedAudioSuffixes().contains(suffix))
|
||||||
if (supportedImageSuffixes().contains(suffix))
|
|
||||||
pixmap.load(filePath);
|
|
||||||
else if (supportedAudioSuffixes().contains(suffix))
|
|
||||||
pixmap = defaultPixmapForType("sound", iconSize);
|
pixmap = defaultPixmapForType("sound", iconSize);
|
||||||
else if (supportedShaderSuffixes().contains(suffix))
|
else if (supportedShaderSuffixes().contains(suffix))
|
||||||
pixmap = defaultPixmapForType("shader", iconSize);
|
pixmap = defaultPixmapForType("shader", iconSize);
|
||||||
else if (supportedTexture3DSuffixes().contains(suffix))
|
|
||||||
pixmap = defaultPixmapForType("texture", iconSize);
|
|
||||||
|
|
||||||
if (pixmap.isNull())
|
if (pixmap.isNull())
|
||||||
return QFileIconProvider::icon(info);
|
return QFileIconProvider::icon(info);
|
||||||
|
}
|
||||||
|
|
||||||
if ((pixmap.width() > iconSize.width()) || (pixmap.height() > iconSize.height()))
|
if ((pixmap.width() > iconSize.width()) || (pixmap.height() > iconSize.height()))
|
||||||
pixmap = pixmap.scaled(iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
pixmap = pixmap.scaled(iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||||
|
@@ -86,6 +86,7 @@
|
|||||||
#include <coreplugin/actionmanager/actionmanager.h>
|
#include <coreplugin/actionmanager/actionmanager.h>
|
||||||
#include <coreplugin/editormanager/editormanager.h>
|
#include <coreplugin/editormanager/editormanager.h>
|
||||||
#include <coreplugin/documentmanager.h>
|
#include <coreplugin/documentmanager.h>
|
||||||
|
#include <hdrimage.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <projectexplorer/target.h>
|
#include <projectexplorer/target.h>
|
||||||
@@ -1723,6 +1724,11 @@ QVariant NodeInstanceView::previewImageDataForImageNode(const ModelNode &modelNo
|
|||||||
originalPixmap = QPixmap::fromImage(paintImage);
|
originalPixmap = QPixmap::fromImage(paintImage);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
#ifndef QMLDESIGNER_TEST
|
||||||
|
if (imageFi.suffix() == "hdr")
|
||||||
|
originalPixmap = HdrImage{imageSource}.toPixmap();
|
||||||
|
else
|
||||||
|
#endif
|
||||||
originalPixmap.load(imageSource);
|
originalPixmap.load(imageSource);
|
||||||
}
|
}
|
||||||
if (!originalPixmap.isNull()) {
|
if (!originalPixmap.isNull()) {
|
||||||
|
@@ -481,6 +481,8 @@ Project {
|
|||||||
"componentcore/componentcore.qrc",
|
"componentcore/componentcore.qrc",
|
||||||
"componentcore/zoomaction.cpp",
|
"componentcore/zoomaction.cpp",
|
||||||
"componentcore/zoomaction.h",
|
"componentcore/zoomaction.h",
|
||||||
|
"componentcore/hdrimage.cpp",
|
||||||
|
"componentcore/hdrimage.h",
|
||||||
"texteditor/texteditorstatusbar.cpp",
|
"texteditor/texteditorstatusbar.cpp",
|
||||||
"texteditor/texteditorstatusbar.h",
|
"texteditor/texteditorstatusbar.h",
|
||||||
"componentcore/changestyleaction.cpp",
|
"componentcore/changestyleaction.cpp",
|
||||||
|
Reference in New Issue
Block a user