#include "tcolorstyles.h"
#include <QVBoxLayout>
#include <QComboBox>
#include <QPushButton>
#include <QIcon>
#include <QStackedWidget>
#include <QPainter>
#include "toonzqt/histogram.h"
const int HistogramGraph::drawMargin = 10;
//*****************************************************************************
// Local namespace
//*****************************************************************************
namespace
{
void computeGreyValues(const TRasterGR8P &ras, int *greyValues)
{
int y, lx = ras->getLx(), ly = ras->getLy();
TPixelGR8 *pix, *endPix;
for (y = 0; y < ly; ++y)
for (pix = ras->pixels(y), endPix = pix + lx; pix < endPix; ++pix)
++greyValues[pix->value];
}
//-----------------------------------------------------------------------------
void computeGreyValues(const TRasterGR16P &ras, int *greyValues)
{
int y, lx = ras->getLx(), ly = ras->getLy();
TPixelGR16 *pix, *endPix;
for (y = 0; y < ly; ++y)
for (pix = ras->pixels(y), endPix = pix + lx; pix < endPix; ++pix)
++greyValues[pix->value >> 8];
}
//-----------------------------------------------------------------------------
void computeRGBMValues32(const TRaster32P &ras, int (*rgbmValues)[256])
{
int y, lx = ras->getLx(), ly = ras->getLy();
TPixel32 *pix, *endPix;
for (y = 0; y < ly; ++y)
for (pix = ras->pixels(y), endPix = pix + lx; pix < endPix; ++pix) {
//NOTE: The insertion order is related to the way channels are
//displayed inside the histogram's QStackedWidget instance
if (pix->m) {
++rgbmValues[0][pix->r];
++rgbmValues[1][pix->g];
++rgbmValues[2][pix->b];
}
++rgbmValues[3][pix->m];
}
}
//-----------------------------------------------------------------------------
void computeRGBMValues64(const TRaster64P &ras, int (*rgbmValues)[256])
{
int y, lx = ras->getLx(), ly = ras->getLy();
TPixel64 *pix, *endPix;
for (y = 0; y < ly; ++y)
for (pix = ras->pixels(y), endPix = pix + lx; pix < endPix; ++pix) {
//NOTE: The insertion order is related to the way channels are
//displayed inside the histogram's QStackedWidget instance
if (pix->m) {
++rgbmValues[0][pix->r >> 8];
++rgbmValues[1][pix->g >> 8];
++rgbmValues[2][pix->b >> 8];
}
++rgbmValues[3][pix->m >> 8];
}
}
//-----------------------------------------------------------------------------
void computeRGBMValues(const TRasterCM32P &ras, TPaletteP pal, int (*rgbmValues)[256])
{
assert(pal);
int y, lx = ras->getLx(), ly = ras->getLy();
TPixelCM32 *pix, *endPix;
int styleId;
TColorStyle *colorStyle;
TPixel32 color;
for (y = 0; y < ly; ++y)
for (pix = ras->pixels(y), endPix = pix + lx; pix < endPix; ++pix) {
styleId = (pix->getTone() < 127) ? pix->getInk() : pix->getPaint();
colorStyle = pal->getStyle(styleId);
if (!colorStyle)
continue;
color = colorStyle->getAverageColor();
if (color.m) {
++rgbmValues[0][color.r];
++rgbmValues[1][color.g];
++rgbmValues[2][color.b];
}
++rgbmValues[3][color.m];
}
}
//-----------------------------------------------------------------------------
void computeRGB(int (*channelValues)[256], int *rgbValues)
{
int i;
for (i = 0; i < 256; ++i)
rgbValues[i] = channelValues[0][i] + channelValues[1][i] + channelValues[2][i];
}
//-----------------------------------------------------------------------------
void computeRGBM(int (*channelValues)[256], int *rgbmValues)
{
int i;
for (i = 0; i < 256; ++i)
rgbmValues[i] =
channelValues[0][i] + channelValues[1][i] +
channelValues[2][i] + channelValues[3][i];
}
} //namespace
//=============================================================================
// HistogramGraph
//-----------------------------------------------------------------------------
HistogramGraph::HistogramGraph(QWidget *parent, QColor color)
: QWidget(parent), m_color(color), m_height(120), m_values(0), m_logScale(false)
{
if (m_color.alpha() == 0)
m_color = Qt::black;
setMinimumWidth(256 + drawMargin * 2 + 2);
setMinimumHeight(m_height + drawMargin);
}
//-----------------------------------------------------------------------------
HistogramGraph::~HistogramGraph()
{
m_values.clear();
}
//-----------------------------------------------------------------------------
void HistogramGraph::setAlphaMask(int value)
{
m_color.setAlpha(value);
}
//-----------------------------------------------------------------------------
void HistogramGraph::setValues(const int values[])
{
m_values.clear();
m_values.resize(256);
int i;
double maxValue = 0;
for (i = 0; i < 256; i++) {
int count = m_values[i] = values[i];
if (maxValue < count)
maxValue = count;
}
m_viewValues.clear();
m_logViewValues.clear();
m_viewValues.resize(256);
m_logViewValues.resize(256);
double logMaxValue = log(1.0 + maxValue);
for (i = 0; i < 256; i++) {
m_viewValues[i] = double(values[i]) * double(m_height) / maxValue;
m_logViewValues[i] = double(log(1.0 + values[i])) * double(m_height) / logMaxValue;
}
}
//-----------------------------------------------------------------------------
void HistogramGraph::draw(QPainter *p, QPoint translation)
{
int x0 = translation.x() + drawMargin;
int y0 = translation.y() + drawMargin - 2;
int w = width() - 2 * drawMargin;
int h = m_height + 1;
p->setPen(Qt::NoPen);
p->setBrush(Qt::white);
p->drawRect(x0, y0, w - 1, h);
p->setBrush(Qt::NoBrush);
p->setPen(Qt::gray);
int step = w * 0.25;
int deltaX = x0 + 1 + step;
int y1 = y0 + h;
p->drawLine(deltaX, y0 + 1, deltaX, y1);
deltaX += step;
p->drawLine(deltaX, y0 + 1, deltaX, y1);
deltaX += step;
p->drawLine(deltaX, y0 + 1, deltaX, y1);
p->drawRect(x0, y0, w - 1, h);
if (m_values.size() == 0)
return;
const QVector<int> &viewValues = m_logScale ? m_logViewValues : m_viewValues;
p->setPen(m_color);
w = w - 2;
x0 = x0 + 1;
y1 = y1 - 1;
int i;
double gap = double(viewValues.size()) / double(w); /*-- 1チャンネル値あたりのグラフの横幅 --*/
for (i = 0; i < w; i++) {
int j = i * gap;
assert(j >= 0 && j < viewValues.size());
int v = viewValues[j];
if (v <= 0)
continue;
int x = x0 + i;
p->drawLine(x, y1 - v + 1, x, y1);
}
}
//-----------------------------------------------------------------------------
void HistogramGraph::paintEvent(QPaintEvent *event)
{
QPainter p(this);
draw(&p);
}
//=============================================================================
// ChannelBar
//-----------------------------------------------------------------------------
ChannelBar::ChannelBar(QWidget *parent, QColor color, bool isHorizontal)
: QWidget(parent), m_color(color), m_isHorizontal(isHorizontal), m_colorBarLength(13), m_drawNumbers(true)
{
int d = 256 + HistogramGraph::drawMargin * 2 + 2;
if (m_isHorizontal)
setMinimumWidth(d);
else
setFixedHeight(d); //La barra verticale ha la size fissa
setDrawNumbers(m_drawNumbers);
if (color == Qt::black)
m_color = Qt::white;
}
//-----------------------------------------------------------------------------
ChannelBar::~ChannelBar()
{
}
//-----------------------------------------------------------------------------
void ChannelBar::setDrawNumbers(bool onOff)
{
if (m_isHorizontal)
setFixedHeight(onOff ? m_colorBarLength * 2 + 2 : m_colorBarLength + 2);
else
setFixedWidth(onOff ? m_colorBarLength + 20 + 2 : m_colorBarLength + 2);
}
//-----------------------------------------------------------------------------
void ChannelBar::draw(QPainter *p, QPoint translation)
{
//Calcolo i parametri necessari al draw a seconda del caso in cui mi trovo.
int space = HistogramGraph::drawMargin;
int w, h, x0, y0;
QRect rect;
QPoint initialPoint, finalPoint, delta;
QColor initialColor, finalColor;
if (m_isHorizontal) {
w = width() - 2 * HistogramGraph::drawMargin;
h = m_colorBarLength;
x0 = translation.x() + space;
y0 = translation.y();
initialPoint = QPoint(x0, 0);
finalPoint = QPoint(w + x0, 0);
initialColor = Qt::black;
finalColor = m_color;
rect = QRect(x0 - space, y0 + h, 20, 20);
delta = QPoint(w / 4, 0);
} else {
w = m_colorBarLength;
h = height() - 2 * HistogramGraph::drawMargin;
x0 = translation.x() + width() - w;
y0 = translation.y() + space;
initialPoint = QPoint(0, y0);
finalPoint = QPoint(0, h + y0);
initialColor = m_color;
finalColor = Qt::black;
rect = QRect(0, h + y0 - space, 20, 20);
delta = QPoint(0, -h / 4);
}
if (m_color == QColor(0, 0, 0, 0)) {
static QPixmap checkboard(":Resources/backg.png");
p->drawTiledPixmap(x0, y0, w, h, checkboard);
QColor color = initialColor;
initialColor = finalColor;
finalColor = color;
}
QLinearGradient linearGrad(initialPoint, finalPoint);
linearGrad.setColorAt(0, initialColor);
linearGrad.setColorAt(1, finalColor);
p->setBrush(QBrush(linearGrad));
p->setPen(Qt::black);
p->drawRect(x0, y0, w - 1, h - 1);
if (m_drawNumbers) {
p->drawText(rect, Qt::AlignCenter, QString::number(0));
rect.translate(delta);
p->drawText(rect, Qt::AlignCenter, QString::number(64));
rect.translate(delta);
p->drawText(rect, Qt::AlignCenter, QString::number(128));
rect.translate(delta);
p->drawText(rect, Qt::AlignCenter, QString::number(192));
rect.translate(delta);
p->drawText(rect, Qt::AlignCenter, QString::number(255));
}
}
//-----------------------------------------------------------------------------
void ChannelBar::paintEvent(QPaintEvent *event)
{
QPainter p(this);
draw(&p);
}
//=============================================================================
// HistogramView
//-----------------------------------------------------------------------------
HistogramView::HistogramView(QWidget *parent, QColor color)
: QWidget(parent), m_drawnWidget(parent)
{
setMinimumWidth(256 + 10 * 2 + 2); //10 == margin of internal widget
setMinimumHeight(120 + 10 * 2 + 2); //10 == margin of internal widget
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->setMargin(0);
mainLayout->setSpacing(7);
m_histogramGraph = new HistogramGraph(this, color);
m_colorBar = new ChannelBar(this, color);
mainLayout->addWidget(m_histogramGraph);
mainLayout->addWidget(m_colorBar);
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
setLayout(mainLayout);
}
//-----------------------------------------------------------------------------
void HistogramView::setDrawnWidget(QWidget *widget)
{
m_drawnWidget = widget;
}
//-----------------------------------------------------------------------------
void HistogramView::setValues(const int values[])
{
m_histogramGraph->setValues(values);
m_drawnWidget->update();
}
//-----------------------------------------------------------------------------
void HistogramView::draw(QPainter *painter, QPoint translation)
{
m_histogramGraph->draw(painter, translation);
m_colorBar->draw(painter, QPoint(translation.x(), translation.y() + m_histogramGraph->getHeight() + 17));
}
//-----------------------------------------------------------------------------
HistogramView::~HistogramView()
{
}
//=============================================================================
// Histograms
//-----------------------------------------------------------------------------
Histograms::Histograms(QWidget *parent, bool rgba)
: QStackedWidget(parent), m_raster(0), m_palette(0), m_computeAlsoRGBA(rgba), m_channelsCount(rgba ? 6 : 5)
{
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
HistogramView *histogramViews[6];
int h = 0;
if (m_computeAlsoRGBA)
histogramViews[h++] = new HistogramView(this);
histogramViews[h++] = new HistogramView(this);
histogramViews[h++] = new HistogramView(this, Qt::red);
histogramViews[h++] = new HistogramView(this, Qt::green);
histogramViews[h++] = new HistogramView(this, Qt::blue);
histogramViews[h++] = new HistogramView(this, QColor(0, 0, 0, 0));
int i;
for (i = 0; i < m_channelsCount; ++i)
addWidget(histogramViews[i]);
}
//-----------------------------------------------------------------------------
Histograms::~Histograms()
{
memset(m_channelValue, 0, sizeof m_channelValue);
}
//-----------------------------------------------------------------------------
void Histograms::setRaster(const TRasterP &raster, const TPaletteP &palette)
{
if (palette.getPointer())
m_palette = palette;
m_raster = raster;
computeChannelsValue();
int i;
for (i = 0; i < count(); i++)
getHistogramView(i)->setValues(m_channelValue[i]);
}
//-----------------------------------------------------------------------------
void Histograms::computeChannelsValue()
{
m_channelsCount = m_computeAlsoRGBA ? 6 : 5;
memset(m_channelValue, 0, sizeof m_channelValue);
if (!m_raster.getPointer())
return;
int(*rgbChannelValues)[256] = m_channelValue + (m_computeAlsoRGBA ? 1 : 0);
int(*channelValues)[256] = rgbChannelValues + 1;
TRasterCM32P cmRaster = m_raster;
bool isCmRaster = !!cmRaster;
{
TRaster32P ras32(m_raster);
if (ras32) {
::computeRGBMValues32(ras32, channelValues);
goto computeChannelsSums;
}
}
{
TRaster64P ras64(m_raster);
if (ras64) {
::computeRGBMValues64(ras64, channelValues);
goto computeChannelsSums;
}
}
{
TRasterGR8P rasGR8(m_raster);
if (rasGR8) {
m_channelsCount = 1;
::computeGreyValues(rasGR8, m_channelValue[0]);
return;
}
}
{
TRasterGR16P rasGR16(m_raster);
if (rasGR16) {
m_channelsCount = 1;
::computeGreyValues(rasGR16, m_channelValue[0]);
return;
}
}
return;
computeChannelsSums:
if (m_computeAlsoRGBA)
::computeRGBM(channelValues, m_channelValue[0]);
::computeRGB(channelValues, *rgbChannelValues);
}
//-----------------------------------------------------------------------------
HistogramView *Histograms::getHistogramView(int indexType) const
{
HistogramView *ch = dynamic_cast<HistogramView *>(widget(indexType));
assert(ch);
return ch;
}
//=============================================================================
// Histogram
//-----------------------------------------------------------------------------
Histogram::Histogram(QWidget *parent)
: QWidget(parent)
{
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->setMargin(0);
mainLayout->setSpacing(0);
setLayout(mainLayout);
QHBoxLayout *upperLayout = new QHBoxLayout;
mainLayout->addLayout(upperLayout);
m_channelsListBox = new QComboBox(this);
m_channelsListBox->setFixedSize(100, 20);
upperLayout->addSpacing(HistogramGraph::drawMargin);
upperLayout->addWidget(m_channelsListBox);
upperLayout->addStretch(1);
QIcon icon;
QString normal = QString(":Resources/histograms.png");
QString click = QString(":Resources/histograms_over.png");
icon.addFile(normal, QSize(), QIcon::Normal, QIcon::Off);
icon.addFile(click, QSize(), QIcon::Normal, QIcon::On);
QPushButton *logScaleButton = new QPushButton(icon, "", this);
logScaleButton->setToolTip(tr("Logarithmic Scale"));
logScaleButton->setFixedSize(20, 20);
logScaleButton->setCheckable(true);
upperLayout->addWidget(logScaleButton);
upperLayout->addSpacing(HistogramGraph::drawMargin);
m_histograms = new Histograms(this);
m_histograms->setCurrentIndex(0);
mainLayout->addWidget(m_histograms);
connect(m_channelsListBox, SIGNAL(currentIndexChanged(int)), m_histograms, SLOT(setCurrentIndex(int)));
connect(logScaleButton, SIGNAL(toggled(bool)), this, SLOT(setLogScale(bool)));
updateChannelsList();
}
//-----------------------------------------------------------------------------
void Histogram::updateChannelsList()
{
if (m_histograms->channelsCount() != m_channelsListBox->count()) {
QStringList channels;
m_channelsListBox->clear();
if (m_histograms->channelsCount() == 1)
channels << "Value";
else
channels << "RGB"
<< "Red"
<< "Green"
<< "Blue"
<< "Alpha";
m_channelsListBox->addItems(channels);
}
}
//-----------------------------------------------------------------------------
void Histogram::setRaster(const TRasterP &raster, const TPaletteP &palette)
{
m_histograms->setRaster(raster, palette);
updateChannelsList();
}
//-----------------------------------------------------------------------------
void Histogram::setLogScale(bool onOff)
{
int i, count = m_histograms->channelsCount();
for (i = 0; i < count; ++i) {
HistogramGraph *graph = m_histograms->getHistogramView(i)->histogramGraph();
graph->setLogScale(onOff);
}
update();
}