You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
529 lines
14 KiB
C++
529 lines
14 KiB
C++
/*
|
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*
|
|
*/
|
|
#include "EditorDefs.h"
|
|
|
|
#include "QBitmapPreviewDialogImp.h"
|
|
|
|
// Cry
|
|
#include <ITexture.h>
|
|
|
|
// EditorCore
|
|
#include <Util/Image.h>
|
|
#include <Include/IImageUtil.h>
|
|
|
|
// QT
|
|
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: class '...' needs to have dll-interface to be used by clients of class '...'
|
|
#include <QEvent>
|
|
#include <QKeyEvent>
|
|
#include <QPainter>
|
|
#include <QPainterPath>
|
|
#include <qmath.h>
|
|
AZ_POP_DISABLE_WARNING
|
|
|
|
#include <Controls/ui_QBitmapPreviewDialog.h>
|
|
|
|
static const int kDefaultWidth = 256;
|
|
static const int kDefaultHeight = 256;
|
|
|
|
QBitmapPreviewDialogImp::QBitmapPreviewDialogImp(QWidget* parent)
|
|
: QBitmapPreviewDialog(parent)
|
|
, m_image(new CImageEx())
|
|
, m_showOriginalSize(false)
|
|
, m_showMode(ESHOW_RGB)
|
|
, m_histrogramMode(eHistogramMode_OverlappedRGB)
|
|
{
|
|
setMouseTracking(true);
|
|
setImage("");
|
|
ui->m_placeholderBitmap->setStyleSheet("background-color: rgba(0, 0, 0, 0);");
|
|
ui->m_placeholderHistogram->setStyleSheet("background-color: rgba(0, 0, 0, 0);");
|
|
|
|
ui->m_labelForBitmapSize->setProperty("tooltipLabel", "Content");
|
|
ui->m_labelForMean->setProperty("tooltipLabel", "Content");
|
|
ui->m_labelForMedian->setProperty("tooltipLabel", "Content");
|
|
ui->m_labelForMips->setProperty("tooltipLabel", "Content");
|
|
ui->m_labelForStdDev->setProperty("tooltipLabel", "Content");
|
|
|
|
ui->m_vBitmapSize->setProperty("tooltipLabel", "Content");
|
|
ui->m_vMean->setProperty("tooltipLabel", "Content");
|
|
ui->m_vMedian->setProperty("tooltipLabel", "Content");
|
|
ui->m_vMips->setProperty("tooltipLabel", "Content");
|
|
ui->m_vStdDev->setProperty("tooltipLabel", "Content");
|
|
|
|
setUIStyleMode(EUISTYLE_IMAGE_ONLY);
|
|
}
|
|
|
|
QBitmapPreviewDialogImp::~QBitmapPreviewDialogImp()
|
|
{
|
|
SAFE_DELETE(m_image);
|
|
}
|
|
|
|
void QBitmapPreviewDialogImp::setImage(const QString path)
|
|
{
|
|
if (path.isEmpty()
|
|
|| m_path == path
|
|
|| !GetIEditor()->GetImageUtil()->LoadImage(path.toUtf8().data(), *m_image))
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_showOriginalSize = isSizeSmallerThanDefault();
|
|
m_path = path;
|
|
refreshData();
|
|
}
|
|
|
|
void QBitmapPreviewDialogImp::setShowMode(EShowMode mode)
|
|
{
|
|
if (mode == ESHOW_NumModes)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_showMode = mode;
|
|
refreshData();
|
|
update();
|
|
}
|
|
|
|
void QBitmapPreviewDialogImp::toggleShowMode()
|
|
{
|
|
m_showMode = (EShowMode)(((int)m_showMode + 1) % ESHOW_NumModes);
|
|
refreshData();
|
|
update();
|
|
}
|
|
|
|
void QBitmapPreviewDialogImp::setUIStyleMode(EUIStyle mode)
|
|
{
|
|
if (mode >= EUISTYLE_NumModes)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_uiStyle = mode;
|
|
if (m_uiStyle == EUISTYLE_IMAGE_ONLY)
|
|
{
|
|
ui->m_placeholderHistogram->hide();
|
|
|
|
ui->m_labelForBitmapSize->hide();
|
|
ui->m_labelForMean->hide();
|
|
ui->m_labelForMedian->hide();
|
|
ui->m_labelForMips->hide();
|
|
ui->m_labelForStdDev->hide();
|
|
|
|
ui->m_vBitmapSize->hide();
|
|
ui->m_vMean->hide();
|
|
ui->m_vMedian->hide();
|
|
ui->m_vMips->hide();
|
|
ui->m_vStdDev->hide();
|
|
}
|
|
else
|
|
{
|
|
ui->m_placeholderHistogram->show();
|
|
|
|
ui->m_labelForBitmapSize->show();
|
|
ui->m_labelForMean->show();
|
|
ui->m_labelForMedian->show();
|
|
ui->m_labelForMips->show();
|
|
ui->m_labelForStdDev->show();
|
|
|
|
ui->m_vBitmapSize->show();
|
|
ui->m_vMean->show();
|
|
ui->m_vMedian->show();
|
|
ui->m_vMips->show();
|
|
ui->m_vStdDev->show();
|
|
}
|
|
}
|
|
|
|
const QBitmapPreviewDialogImp::EShowMode& QBitmapPreviewDialogImp::getShowMode() const
|
|
{
|
|
return m_showMode;
|
|
}
|
|
|
|
void QBitmapPreviewDialogImp::setHistogramMode(EHistogramMode mode)
|
|
{
|
|
if (mode == eHistogramMode_NumModes)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_histrogramMode = mode;
|
|
}
|
|
|
|
void QBitmapPreviewDialogImp::toggleHistrogramMode()
|
|
{
|
|
m_histrogramMode = (EHistogramMode)(((int)m_histrogramMode + 1) % eHistogramMode_NumModes);
|
|
update();
|
|
}
|
|
|
|
const QBitmapPreviewDialogImp::EHistogramMode& QBitmapPreviewDialogImp::getHistogramMode() const
|
|
{
|
|
return m_histrogramMode;
|
|
}
|
|
|
|
void QBitmapPreviewDialogImp::toggleOriginalSize()
|
|
{
|
|
m_showOriginalSize = !m_showOriginalSize;
|
|
|
|
refreshData();
|
|
update();
|
|
}
|
|
|
|
bool QBitmapPreviewDialogImp::isSizeSmallerThanDefault()
|
|
{
|
|
return m_image->GetWidth() < kDefaultWidth && m_image->GetHeight() < kDefaultHeight;
|
|
}
|
|
|
|
void QBitmapPreviewDialogImp::setOriginalSize(bool value)
|
|
{
|
|
m_showOriginalSize = value;
|
|
|
|
refreshData();
|
|
update();
|
|
}
|
|
|
|
|
|
const char* QBitmapPreviewDialogImp::GetShowModeDescription(EShowMode eShowMode, [[maybe_unused]] bool bShowInOriginalSize) const
|
|
{
|
|
switch (eShowMode)
|
|
{
|
|
case ESHOW_RGB:
|
|
return "RGB";
|
|
case ESHOW_RGB_ALPHA:
|
|
return "RGB+A";
|
|
case ESHOW_ALPHA:
|
|
return "Alpha";
|
|
case ESHOW_RGBA:
|
|
return "RGBA";
|
|
case ESHOW_RGBE:
|
|
return "RGBExp";
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
const char* getHistrogramModeStr(QBitmapPreviewDialogImp::EHistogramMode mode, bool shortName)
|
|
{
|
|
switch (mode)
|
|
{
|
|
case QBitmapPreviewDialogImp::eHistogramMode_Luminosity:
|
|
return shortName ? "Lum" : "Luminosity";
|
|
case QBitmapPreviewDialogImp::eHistogramMode_OverlappedRGB:
|
|
return shortName ? "Overlap" : "Overlapped RGBA";
|
|
case QBitmapPreviewDialogImp::eHistogramMode_SplitRGB:
|
|
return shortName ? "R|G|B" : "Split RGB";
|
|
case QBitmapPreviewDialogImp::eHistogramMode_RedChannel:
|
|
return shortName ? "Red" : "Red Channel";
|
|
case QBitmapPreviewDialogImp::eHistogramMode_GreenChannel:
|
|
return shortName ? "Green" : "Green Channel";
|
|
case QBitmapPreviewDialogImp::eHistogramMode_BlueChannel:
|
|
return shortName ? "Blue" : "Blue Channel";
|
|
case QBitmapPreviewDialogImp::eHistogramMode_AlphaChannel:
|
|
return shortName ? "Alpha" : "Alpha Channel";
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
void QBitmapPreviewDialogImp::refreshData()
|
|
{
|
|
// Check if we have some usefull data loaded
|
|
if (m_image->GetWidth() * m_image->GetHeight() == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int w = m_image->GetWidth();
|
|
int h = m_image->GetHeight();
|
|
|
|
int multiplier = (m_showMode == ESHOW_RGB_ALPHA ? 2 : 1);
|
|
int originalW = w * multiplier;
|
|
int originalH = h;
|
|
|
|
if (!m_showOriginalSize || (w == 0))
|
|
{
|
|
w = kDefaultWidth;
|
|
}
|
|
if (!m_showOriginalSize || (h == 0))
|
|
{
|
|
h = kDefaultHeight;
|
|
}
|
|
|
|
w *= multiplier;
|
|
|
|
CImageEx scaledImage;
|
|
|
|
if (m_showOriginalSize && (originalW < w))
|
|
{
|
|
w = originalW;
|
|
}
|
|
if (m_showOriginalSize && (originalH < h))
|
|
{
|
|
h = originalH;
|
|
}
|
|
|
|
scaledImage.Allocate(w, h);
|
|
|
|
if (m_showMode == ESHOW_RGB_ALPHA)
|
|
{
|
|
GetIEditor()->GetImageUtil()->ScaleToDoubleFit(*m_image, scaledImage);
|
|
}
|
|
else
|
|
{
|
|
GetIEditor()->GetImageUtil()->ScaleToFit(*m_image, scaledImage);
|
|
}
|
|
|
|
if (m_showMode == ESHOW_RGB || m_showMode == ESHOW_RGBE)
|
|
{
|
|
scaledImage.FillAlpha();
|
|
}
|
|
else if (m_showMode == ESHOW_ALPHA)
|
|
{
|
|
for (int h2 = 0; h2 < scaledImage.GetHeight(); h2++)
|
|
{
|
|
for (int w2 = 0; w2 < scaledImage.GetWidth(); w2++)
|
|
{
|
|
int a = scaledImage.ValueAt(w2, h2) >> 24;
|
|
scaledImage.ValueAt(w2, h2) = RGB(a, a, a) | (a << 24);
|
|
}
|
|
}
|
|
}
|
|
else if (m_showMode == ESHOW_RGB_ALPHA)
|
|
{
|
|
int halfWidth = scaledImage.GetWidth() / 2;
|
|
for (int h2 = 0; h2 < scaledImage.GetHeight(); h2++)
|
|
{
|
|
for (int w2 = 0; w2 < halfWidth; w2++)
|
|
{
|
|
int r = GetRValue(scaledImage.ValueAt(w2, h2));
|
|
int g = GetGValue(scaledImage.ValueAt(w2, h2));
|
|
int b = GetBValue(scaledImage.ValueAt(w2, h2));
|
|
int a = scaledImage.ValueAt(w2, h2) >> 24;
|
|
scaledImage.ValueAt(w2, h2) = RGB(r, g, b) | (a << 24);
|
|
scaledImage.ValueAt(w2 + halfWidth, h2) = RGB(a, a, a) | (a << 24);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
setImageRgba8888(scaledImage.GetData(), w, h, "");
|
|
setSize(QString().asprintf("%d x %d", m_image->GetWidth(), m_image->GetHeight()));
|
|
setMips(QString().asprintf("%d", m_image->GetNumberOfMipMaps()));
|
|
|
|
setFullSize(m_showOriginalSize);
|
|
|
|
// Compute histogram
|
|
m_histogram.ComputeHistogram((BYTE*)scaledImage.GetData(), w, h, CImageHistogram::eImageFormat_32BPP_RGBA);
|
|
}
|
|
|
|
void QBitmapPreviewDialogImp::paintEvent(QPaintEvent* e)
|
|
{
|
|
QBitmapPreviewDialog::paintEvent(e);
|
|
|
|
//if showing original size hide other information so it's easier to see
|
|
if (m_showOriginalSize)
|
|
{
|
|
return;
|
|
}
|
|
if (m_uiStyle == EUISTYLE_IMAGE_ONLY)
|
|
{
|
|
return;
|
|
}
|
|
|
|
QPainter p(this);
|
|
QPen pen;
|
|
QPainterPath path[4];
|
|
|
|
// Fill background color
|
|
QRect histogramRect = getHistogramArea();
|
|
p.fillRect(histogramRect, QColor(255, 255, 255));
|
|
|
|
// Draw borders
|
|
pen.setColor(QColor(0, 0, 0));
|
|
p.setPen(pen);
|
|
p.drawRect(histogramRect);
|
|
|
|
// Draw histogram
|
|
|
|
QVector<int> drawChannels;
|
|
|
|
switch (m_histrogramMode)
|
|
{
|
|
case eHistogramMode_Luminosity:
|
|
drawChannels.push_back(3);
|
|
break;
|
|
case eHistogramMode_SplitRGB:
|
|
drawChannels.push_back(0);
|
|
drawChannels.push_back(1);
|
|
drawChannels.push_back(2);
|
|
break;
|
|
case eHistogramMode_OverlappedRGB:
|
|
drawChannels.push_back(0);
|
|
drawChannels.push_back(1);
|
|
drawChannels.push_back(2);
|
|
break;
|
|
case eHistogramMode_RedChannel:
|
|
drawChannels.push_back(0);
|
|
break;
|
|
case eHistogramMode_GreenChannel:
|
|
drawChannels.push_back(1);
|
|
break;
|
|
case eHistogramMode_BlueChannel:
|
|
drawChannels.push_back(2);
|
|
break;
|
|
case eHistogramMode_AlphaChannel:
|
|
drawChannels.push_back(3);
|
|
break;
|
|
}
|
|
|
|
int graphWidth = qMax(histogramRect.width(), 1);
|
|
int graphHeight = qMax(histogramRect.height() - 2, 0);
|
|
int graphBottom = histogramRect.bottom() + 1;
|
|
int currX[4] = {0, 0, 0, 0};
|
|
int prevX[4] = {0, 0, 0, 0};
|
|
float scale = 0.0f;
|
|
static const int numSubGraphs = 3;
|
|
const int subGraph = qCeil(graphWidth / numSubGraphs);
|
|
|
|
// Fill background for Split RGB histogram
|
|
if (m_histrogramMode == eHistogramMode_SplitRGB)
|
|
{
|
|
const static QColor backgroundColor[numSubGraphs] =
|
|
{
|
|
QColor(255, 220, 220),
|
|
QColor(220, 255, 220),
|
|
QColor(220, 220, 255)
|
|
};
|
|
|
|
for (int i = 0; i < numSubGraphs; i++)
|
|
{
|
|
p.fillRect(histogramRect.left() + subGraph * i,
|
|
histogramRect.top(),
|
|
subGraph + (i == numSubGraphs - 1 ? 1 : 0),
|
|
histogramRect.height(), backgroundColor[i]);
|
|
}
|
|
}
|
|
|
|
int lastHeight[CImageHistogram::kNumChannels] = { INT_MAX, INT_MAX, INT_MAX, INT_MAX };
|
|
|
|
for (int x = 0; x < graphWidth; ++x)
|
|
{
|
|
for (int j = 0; j < drawChannels.size(); j++)
|
|
{
|
|
const int c = drawChannels[j];
|
|
int& curr_x = currX[c];
|
|
int& prev_x = prevX[c];
|
|
int& last_height = lastHeight[c];
|
|
QPainterPath& curr_path = path[c];
|
|
|
|
|
|
curr_x = histogramRect.left() + x + 1;
|
|
int i = static_cast<int>(((float)x / (graphWidth - 1)) * (CImageHistogram::kNumColorLevels - 1));
|
|
if (m_histrogramMode == eHistogramMode_SplitRGB)
|
|
{
|
|
// Filter out to area which we are interested
|
|
const int k = x / subGraph;
|
|
if (k != c)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
i = qCeil((i - (subGraph * c)) * numSubGraphs);
|
|
i = qMin(i, CImageHistogram::kNumColorLevels - 1);
|
|
i = qMax(i, 0);
|
|
}
|
|
|
|
if (m_histrogramMode == eHistogramMode_Luminosity)
|
|
{
|
|
scale = (float)m_histogram.m_lumCount[i] / m_histogram.m_maxLumCount;
|
|
}
|
|
else if (m_histogram.m_maxCount[c])
|
|
{
|
|
scale = (float)m_histogram.m_count[c][i] / m_histogram.m_maxCount[c];
|
|
}
|
|
|
|
int height = static_cast<int>(graphBottom - graphHeight * scale);
|
|
if (last_height == INT_MAX)
|
|
{
|
|
last_height = height;
|
|
}
|
|
|
|
curr_path.moveTo(prev_x, last_height);
|
|
curr_path.lineTo(curr_x, height);
|
|
last_height = height;
|
|
|
|
if (prev_x == INT_MAX)
|
|
{
|
|
prev_x = curr_x;
|
|
}
|
|
|
|
prev_x = curr_x;
|
|
}
|
|
}
|
|
|
|
static const QColor kChannelColor[4] =
|
|
{
|
|
QColor(255, 0, 0),
|
|
QColor(0, 255, 0),
|
|
QColor(0, 0, 255),
|
|
QColor(120, 120, 120)
|
|
};
|
|
|
|
for (int i = 0; i < drawChannels.size(); i++)
|
|
{
|
|
const int c = drawChannels[i];
|
|
pen.setColor(kChannelColor[c]);
|
|
p.setPen(pen);
|
|
p.drawPath(path[c]);
|
|
}
|
|
|
|
// Update histogram info
|
|
{
|
|
float mean = 0, stdDev = 0, median = 0;
|
|
|
|
switch (m_histrogramMode)
|
|
{
|
|
case eHistogramMode_Luminosity:
|
|
case eHistogramMode_SplitRGB:
|
|
case eHistogramMode_OverlappedRGB:
|
|
mean = m_histogram.m_meanAvg;
|
|
stdDev = m_histogram.m_stdDevAvg;
|
|
median = m_histogram.m_medianAvg;
|
|
break;
|
|
case eHistogramMode_RedChannel:
|
|
mean = m_histogram.m_mean[0];
|
|
stdDev = m_histogram.m_stdDev[0];
|
|
median = m_histogram.m_median[0];
|
|
break;
|
|
case eHistogramMode_GreenChannel:
|
|
mean = m_histogram.m_mean[1];
|
|
stdDev = m_histogram.m_stdDev[1];
|
|
median = m_histogram.m_median[1];
|
|
break;
|
|
case eHistogramMode_BlueChannel:
|
|
mean = m_histogram.m_mean[2];
|
|
stdDev = m_histogram.m_stdDev[2];
|
|
median = m_histogram.m_median[2];
|
|
break;
|
|
case eHistogramMode_AlphaChannel:
|
|
mean = m_histogram.m_mean[3];
|
|
stdDev = m_histogram.m_stdDev[3];
|
|
median = m_histogram.m_median[3];
|
|
break;
|
|
}
|
|
QString val;
|
|
val.setNum(mean);
|
|
setMean(val);
|
|
val.setNum(stdDev);
|
|
setStdDev(val);
|
|
val.setNum(median);
|
|
setMedian(val);
|
|
}
|
|
}
|
|
|
|
#include <Controls/moc_QBitmapPreviewDialogImp.cpp>
|