#include "toonzqt/combohistogram.h" #include "tcolorstyles.h" #include #include #include #include #include #include #include #include "toonz/preferences.h" #include "toonzqt/lutcalibrator.h" #include "toonzqt/gutil.h" #include "timagecache.h" #include "trasterimage.h" // TnzBase includes #include "tenv.h" #include #include TEnv::IntVar HistogramChannelDisplayMode( "HistogramChannelDisplayMode", (int)ComboHistoRGBLabel::Display_8bit); // 8bit display by default namespace { inline int idx(int y, int x) { return y * COMBOHIST_RESOLUTION_W + x; } } // namespace //============================================================================= // ChannelHistoGraph //----------------------------------------------------------------------------- ChannelHistoGraph::ChannelHistoGraph(int index, QWidget *parent, bool *showComparePtr) : QWidget(parent) , m_showComparePtr(showComparePtr) , m_channelIndex(index) , m_pickedValue(-1) { // width 256+2 pixels, height 100+2 pixels (with margin) setFixedSize(COMBOHIST_RESOLUTION_W + 2, COMBOHIST_RESOLUTION_H + 2); m_values[0].reserve(COMBOHIST_RESOLUTION_W); m_values[1].reserve(COMBOHIST_RESOLUTION_W); } //----------------------------------------------------------------------------- ChannelHistoGraph::~ChannelHistoGraph() { m_values[0].clear(); m_values[1].clear(); } //----------------------------------------------------------------------------- void ChannelHistoGraph::setValues(int *buf, bool isComp) { int id = (isComp) ? 1 : 0; m_values[id].clear(); m_values[id].resize(COMBOHIST_RESOLUTION_W); m_maxValue[id] = 1; int i; // normalize with the maximum value int maxValue = 1; for (i = 0; i < COMBOHIST_RESOLUTION_W; i++) { m_values[id][i] = buf[i]; if (m_maxValue[id] < buf[i]) m_maxValue[id] = buf[i]; } // for (i = 0; i < COMBOHIST_RESOLUTION_W; i++) { // int v = buf[i]; // m_values[id][i] = // tround((double)(v * COMBOHIST_RESOLUTION_H) / (double)maxValue); //} } //----------------------------------------------------------------------------- void ChannelHistoGraph::paintEvent(QPaintEvent *event) { QPainter p(this); p.setPen(QColor(144, 144, 144)); p.setBrush(QColor(214, 214, 214)); p.drawRect(rect().x(), rect().y(), rect().width() - 1, rect().height() - 1); p.setBrush(Qt::NoBrush); int i; // draw scale marks p.setPen(QColor(144, 144, 144)); for (i = 1; i < 10; i++) { int posx = rect().width() * i / 10; p.drawLine(posx, 1, posx, COMBOHIST_RESOLUTION_H); } QColor compColor = (m_channelIndex == 0) ? Qt::red : (m_channelIndex == 1) ? Qt::green : (m_channelIndex == 2) ? Qt::blue : Qt::magenta; compColor.setAlpha(120); int maxValue = m_maxValue[0]; if (!m_values[1].isEmpty() && m_showComparePtr && *m_showComparePtr && m_maxValue[0] < m_maxValue[1]) maxValue = m_maxValue[1]; p.translate(0, COMBOHIST_RESOLUTION_H); p.scale(1.0, -(double)COMBOHIST_RESOLUTION_H / (double)maxValue); for (int id = 0; id < 2; id++) { if (m_values[id].isEmpty()) continue; if (id == 1 && (!m_showComparePtr || !(*m_showComparePtr))) continue; p.setPen((id == 0) ? Qt::black : compColor); // draw each histogram for (i = 0; i < COMBOHIST_RESOLUTION_W; i++) { int v = m_values[id][i]; if (v <= 0) continue; int x = 1 + i; p.drawLine(x, 0, x, v); } // draw picked color's channel value if (m_pickedValue > -1) { p.setPen(Qt::white); int x = 1 + m_pickedValue; p.drawLine(x, 1, x, maxValue); } } } //----------------------------------------------------------------------------- void ChannelHistoGraph::showCurrentChannelValue(int val) { m_pickedValue = val; update(); } //============================================================================= // ChannelHistoGraph //----------------------------------------------------------------------------- RGBHistoGraph::RGBHistoGraph(int index, QWidget *parent) : ChannelHistoGraph(index, parent) { m_histoImg = QImage(COMBOHIST_RESOLUTION_W, COMBOHIST_RESOLUTION_H, QImage::Format_ARGB32_Premultiplied); } //----------------------------------------------------------------------------- RGBHistoGraph::~RGBHistoGraph() { for (int i = 0; i < 3; i++) m_rgbValues[i].clear(); } //----------------------------------------------------------------------------- void RGBHistoGraph::setValues(int *buf, bool) { for (int chan = 0; chan < 3; chan++) { m_rgbValues[chan].clear(); m_rgbValues[chan].resize(COMBOHIST_RESOLUTION_W); int i; // normalize with the maximum value int maxValue = 1; for (i = 0; i < COMBOHIST_RESOLUTION_W; i++) { int count = buf[COMBOHIST_RESOLUTION_W * chan + i]; if (maxValue < count) maxValue = count; } for (i = 0; i < COMBOHIST_RESOLUTION_W; i++) { int v = buf[COMBOHIST_RESOLUTION_W * chan + i]; m_rgbValues[chan][i] = tround((double)(v * COMBOHIST_RESOLUTION_H) / (double)maxValue); } } QPainter imgPainter(&m_histoImg); imgPainter.fillRect(m_histoImg.rect(), Qt::black); if (m_rgbValues[0].size() == 0 || m_rgbValues[1].size() == 0 || m_rgbValues[2].size() == 0) { imgPainter.end(); return; } imgPainter.setCompositionMode(QPainter::CompositionMode_Plus); for (int chan = 0; chan < 3; chan++) { imgPainter.setPen((chan == 0) ? Qt::red : (chan == 1) ? Qt::green : Qt::blue); for (int i = 0; i < COMBOHIST_RESOLUTION_W; i++) { int v = m_rgbValues[chan][i]; if (v <= 0) continue; int x = 1 + i; imgPainter.drawLine(x, COMBOHIST_RESOLUTION_H + 1 - v, x, COMBOHIST_RESOLUTION_H); } } imgPainter.setCompositionMode(QPainter::CompositionMode_SourceOver); imgPainter.end(); } //----------------------------------------------------------------------------- void RGBHistoGraph::paintEvent(QPaintEvent *event) { QPainter p(this); p.setCompositionMode(QPainter::CompositionMode_SourceOver); p.setPen(QColor(144, 144, 144)); p.setBrush(QColor(64, 64, 64)); p.drawRect(rect().x(), rect().y(), rect().width() - 1, rect().height() - 1); p.setBrush(Qt::NoBrush); p.drawImage(1, 1, m_histoImg); } //============================================================================= // ChannelColorBar //----------------------------------------------------------------------------- ChannelColorBar::ChannelColorBar(QWidget *parent, QColor color) : QWidget(parent), m_color(color) { // 2 pixels margin for width setFixedSize(COMBOHIST_RESOLUTION_W + 2, 6); } //----------------------------------------------------------------------------- void ChannelColorBar::paintEvent(QPaintEvent *event) { QPainter p(this); QLinearGradient linearGrad(QPoint(1, 0), QPoint(COMBOHIST_RESOLUTION_W, 0)); if (m_color == QColor(0, 0, 0, 0)) { linearGrad.setColorAt(0, m_color); linearGrad.setColorAt(1, Qt::white); } else { linearGrad.setColorAt(0, Qt::black); linearGrad.setColorAt(1, m_color); } p.setBrush(QBrush(linearGrad)); p.setPen(Qt::NoPen); p.drawRect(rect()); } //============================================================================= // ChannelHisto //----------------------------------------------------------------------------- ChannelHisto::ChannelHisto(int channelIndex, bool *showComparePtr, QWidget *parent) { QString label; QColor color; switch (channelIndex) { case 0: label = tr("Red"); color = Qt::red; break; case 1: label = tr("Green"); color = Qt::green; break; case 2: label = tr("Blue"); color = Qt::blue; break; case 3: label = tr("Alpha"); color = QColor(0, 0, 0, 0); break; case 4: label = tr("RGB"); color = Qt::white; break; } if (channelIndex != 4) m_histogramGraph = new ChannelHistoGraph(channelIndex, this, showComparePtr); else m_histogramGraph = new RGBHistoGraph(channelIndex, this); m_colorBar = new ChannelColorBar(this, color); // show/hide the alpha channel QPushButton *showAlphaChannelButton = 0; if (channelIndex == 3) { showAlphaChannelButton = new QPushButton("", this); showAlphaChannelButton->setObjectName("menuToggleButton"); showAlphaChannelButton->setFixedSize(15, 15); showAlphaChannelButton->setIcon(createQIcon("menu_toggle")); showAlphaChannelButton->setCheckable(true); showAlphaChannelButton->setChecked(false); showAlphaChannelButton->setFocusPolicy(Qt::NoFocus); } // layout QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->setMargin(0); mainLayout->setSpacing(2); { QHBoxLayout *titleLay = new QHBoxLayout(); titleLay->setMargin(0); titleLay->setSpacing(2); { titleLay->addWidget(new QLabel(label, this), 0); if (channelIndex == 3) titleLay->addWidget(showAlphaChannelButton, 0); titleLay->addStretch(1); } mainLayout->addLayout(titleLay); mainLayout->addSpacing(3); mainLayout->addWidget(m_histogramGraph); mainLayout->addWidget(m_colorBar); } setLayout(mainLayout); if (channelIndex == 3) { connect(showAlphaChannelButton, SIGNAL(toggled(bool)), this, SLOT(onShowAlphaButtonToggled(bool))); onShowAlphaButtonToggled(false); } } //----------------------------------------------------------------------------- /*! update the picked color's channel value */ void ChannelHisto::showCurrentChannelValue(int val) { m_histogramGraph->showCurrentChannelValue(val); } //----------------------------------------------------------------------------- void ChannelHisto::onShowAlphaButtonToggled(bool visible) { m_histogramGraph->setVisible(visible); m_colorBar->setVisible(visible); } //============================================================================= // ComboHistoRGBLabel //----------------------------------------------------------------------------- ComboHistoRGBLabel::ComboHistoRGBLabel(QColor color, QWidget *parent) : QWidget(parent), m_color(color), m_mode(DisplayMode::Display_8bit) { setFixedSize(COMBOHIST_RESOLUTION_W, 30); } void ComboHistoRGBLabel::setColorAndUpdate(QColor color) { if (m_color == color) return; m_color = color; update(); } void ComboHistoRGBLabel::paintEvent(QPaintEvent *pe) { QPainter p(this); p.setPen(Qt::black); QRect bgRect = QRect(rect().left(), rect().top(), rect().width() - 1, rect().height() - 1); if (m_color.alpha() == 0) { p.setBrush(Qt::gray); p.drawRect(bgRect); return; } if (LutManager::instance()->isValid()) { QColor convertedColor(m_color); LutManager::instance()->convert(convertedColor); p.setBrush(convertedColor); } else p.setBrush(m_color); p.drawRect(bgRect); // white text for dark color, black text for light color int val = m_color.red() * 30 + m_color.green() * 59 + m_color.blue() * 11; if (val < 12800) p.setPen(Qt::white); else p.setPen(Qt::black); p.setBrush(Qt::NoBrush); QFont font = p.font(); const int pixelSizes[3] = {18, 14, 14}; font.setPixelSize(pixelSizes[(int)m_mode]); p.setFont(font); QString colorStr; switch (m_mode) { case Display_8bit: { colorStr = tr("R:%1 G:%2 B:%3") .arg(m_color.red()) .arg(m_color.green()) .arg(m_color.blue()); break; } case Display_16bit: { QRgba64 rgba64 = m_color.rgba64(); colorStr = tr("R:%1 G:%2 B:%3") .arg(rgba64.red()) .arg(rgba64.green()) .arg(rgba64.blue()); break; } case Display_0_1: { colorStr = tr("R:%1 G:%2 B:%3") .arg(m_color.redF()) .arg(m_color.greenF()) .arg(m_color.blueF()); break; } } p.drawText(rect(), Qt::AlignCenter, colorStr); } //============================================================================= // ComboHistogram //----------------------------------------------------------------------------- ComboHistogram::ComboHistogram(QWidget *parent) : QWidget(parent) , m_raster(0) , m_palette(0) , m_showCompare(false) , m_compHistoIsValid(true) { for (int chan = 0; chan < 4; chan++) m_histograms[chan] = new ChannelHisto(chan, &m_showCompare, this); m_histograms[4] = new ChannelHisto(4, &m_showCompare, this); // RGB label m_rgbLabel = new ComboHistoRGBLabel(QColor(128, 128, 128), this); // m_rgbLabel->setStyleSheet("font-size: 18px;"); m_rectAverageRgbLabel = new ComboHistoRGBLabel(QColor(128, 128, 128), this); // m_rectAverageRgbLabel->setStyleSheet("font-size: 18px;"); m_xPosLabel = new QLabel("", this); m_yPosLabel = new QLabel("", this); m_displayModeCombo = new QComboBox(this); m_displayModeCombo->addItem( tr("8bit (0-255)"), (int)ComboHistoRGBLabel::DisplayMode::Display_8bit); m_displayModeCombo->addItem( tr("16bit (0-65535)"), (int)ComboHistoRGBLabel::DisplayMode::Display_16bit); m_displayModeCombo->addItem( tr("0.0-1.0"), (int)ComboHistoRGBLabel::DisplayMode::Display_0_1); // layout QVBoxLayout *mainLayout = new QVBoxLayout(); mainLayout->setMargin(5); mainLayout->setSpacing(5); { mainLayout->addWidget(m_histograms[4]); // RGB QHBoxLayout *labelLay = new QHBoxLayout(); labelLay->setMargin(0); labelLay->setSpacing(0); { labelLay->addWidget(new QLabel(tr("Picked Color"), this), 0); labelLay->addStretch(1); labelLay->addWidget(m_displayModeCombo, 0); } mainLayout->addLayout(labelLay, 0); mainLayout->addWidget(m_rgbLabel, 0, Qt::AlignCenter); mainLayout->addWidget(new QLabel(tr("Average Color (Ctrl + Drag)"), this), 0, Qt::AlignLeft | Qt::AlignVCenter); mainLayout->addWidget(m_rectAverageRgbLabel, 0, Qt::AlignCenter); QGridLayout *infoParamLay = new QGridLayout(); infoParamLay->setHorizontalSpacing(3); infoParamLay->setVerticalSpacing(5); { infoParamLay->addWidget(new QLabel(tr("X:"), this), 0, 0, Qt::AlignRight | Qt::AlignVCenter); infoParamLay->addWidget(m_xPosLabel, 0, 1, Qt::AlignLeft | Qt::AlignVCenter); infoParamLay->addWidget(new QLabel(tr("Y:"), this), 1, 0, Qt::AlignRight | Qt::AlignVCenter); infoParamLay->addWidget(m_yPosLabel, 1, 1, Qt::AlignLeft | Qt::AlignVCenter); } mainLayout->addLayout(infoParamLay, 0); mainLayout->addWidget(m_histograms[0]); // R mainLayout->addWidget(m_histograms[1]); // G mainLayout->addWidget(m_histograms[2]); // B mainLayout->addWidget(m_histograms[3]); // A mainLayout->addStretch(1); } setLayout(mainLayout); m_rectAverageRgbLabel->setColorAndUpdate(Qt::transparent); connect(m_displayModeCombo, SIGNAL(activated(int)), this, SLOT(onDisplayModeChanged())); } //----------------------------------------------------------------------------- ComboHistogram::~ComboHistogram() { memset(m_channelValue, 0, sizeof m_channelValue); memset(m_channelValueComp, 0, sizeof m_channelValueComp); } //----------------------------------------------------------------------------- void ComboHistogram::setRaster(const TRasterP &raster, const TPaletteP &palette) { if (palette.getPointer()) m_palette = palette; m_raster = raster; computeChannelsValue(&m_channelValue[0][0], sizeof(m_channelValue), m_raster); for (int chan = 0; chan < 4; chan++) m_histograms[chan]->refleshValue(&m_channelValue[chan][0]); m_histograms[4]->refleshValue(&m_channelValue[0][0]); update(); } //----------------------------------------------------------------------------- void ComboHistogram::computeChannelsValue(int *buf, size_t size, TRasterP ras, TPalette *extPlt) { memset(buf, 0, size); if (!ras.getPointer()) return; TRasterCM32P cmRaster = ras; bool isCmRaster = !!cmRaster; TRaster64P raster64 = ras; bool is64bit = !!raster64; int lx = ras->getLx(); int ly = ras->getLy(); if (lx > 1 && ly > 1) { int i, j; if (is64bit) { for (j = 0; j < ly; j++) { TPixel64 *pix_64 = raster64->pixels(j); for (i = 0; i < lx; i++, pix_64++) { int mValue = (int)byteFromUshort(pix_64->m); if (mValue != 0) { ++buf[idx(0, (int)byteFromUshort(pix_64->r))]; ++buf[idx(1, (int)byteFromUshort(pix_64->g))]; ++buf[idx(2, (int)byteFromUshort(pix_64->b))]; } ++buf[idx(3, mValue)]; } } } else if (isCmRaster) { TPalette *plt = (extPlt) ? extPlt : m_palette.getPointer(); assert(plt); for (j = 0; j < ly; j++) { TPixelCM32 *pix_cm = cmRaster->pixels(j); for (i = 0; i < lx; i++, pix_cm++) { int styleId = pix_cm->getTone() < 127 ? pix_cm->getInk() : pix_cm->getPaint(); TColorStyle *colorStyle = plt->getStyle(styleId); if (!colorStyle) continue; TPixel color = colorStyle->getAverageColor(); int mValue = color.m; if (mValue != 0) { ++buf[idx(0, color.r)]; ++buf[idx(1, color.g)]; ++buf[idx(2, color.b)]; } ++buf[idx(3, color.m)]; } } } else // 8bpc raster { for (j = 0; j < ly; j++) { TPixel *pix = (TPixel *)ras->getRawData(0, j); for (i = 0; i < lx; i++, pix++) { int mValue = pix->m; if (mValue != 0) { ++buf[idx(0, pix->r)]; ++buf[idx(1, pix->g)]; ++buf[idx(2, pix->b)]; } ++buf[idx(3, pix->m)]; } } } } } //----------------------------------------------------------------------------- void ComboHistogram::updateInfo(const TPixel32 &pix, const TPointD &imagePos) { if (pix == TPixel32::Transparent) { m_histograms[0]->showCurrentChannelValue(-1); m_histograms[1]->showCurrentChannelValue(-1); m_histograms[2]->showCurrentChannelValue(-1); m_rgbLabel->setColorAndUpdate(Qt::transparent); m_xPosLabel->setText(""); m_yPosLabel->setText(""); } else { // show picked color's channel values m_histograms[0]->showCurrentChannelValue((int)pix.r); m_histograms[1]->showCurrentChannelValue((int)pix.g); m_histograms[2]->showCurrentChannelValue((int)pix.b); m_rgbLabel->setColorAndUpdate(QColor((int)pix.r, (int)pix.g, (int)pix.b)); m_xPosLabel->setText(QString::number(tround(imagePos.x))); m_yPosLabel->setText(QString::number(tround(imagePos.y))); } } //----------------------------------------------------------------------------- void ComboHistogram::updateInfo(const TPixel64 &pix, const TPointD &imagePos) { if (pix == TPixel64::Transparent) { m_histograms[0]->showCurrentChannelValue(-1); m_histograms[1]->showCurrentChannelValue(-1); m_histograms[2]->showCurrentChannelValue(-1); m_rgbLabel->setColorAndUpdate(Qt::transparent); m_xPosLabel->setText(""); m_yPosLabel->setText(""); } else { TPixel32 pix32 = toPixel32(pix); // show picked color's channel values m_histograms[0]->showCurrentChannelValue((int)pix32.r); m_histograms[1]->showCurrentChannelValue((int)pix32.g); m_histograms[2]->showCurrentChannelValue((int)pix32.b); m_rgbLabel->setColorAndUpdate( QColor::fromRgba64((ushort)pix.r, (ushort)pix.g, (ushort)pix.b)); m_xPosLabel->setText(QString::number(tround(imagePos.x))); m_yPosLabel->setText(QString::number(tround(imagePos.y))); } } //----------------------------------------------------------------------------- void ComboHistogram::updateAverageColor(const TPixel32 &pix) { if (pix == TPixel32::Transparent) { m_rectAverageRgbLabel->setColorAndUpdate(Qt::transparent); } else { m_rectAverageRgbLabel->setColorAndUpdate( QColor((int)pix.r, (int)pix.g, (int)pix.b)); } } //----------------------------------------------------------------------------- void ComboHistogram::updateAverageColor(const TPixel64 &pix) { if (pix == TPixel64::Transparent) { m_rectAverageRgbLabel->setColorAndUpdate(Qt::transparent); } else { m_rectAverageRgbLabel->setColorAndUpdate( QColor::fromRgba64((ushort)pix.r, (ushort)pix.g, (ushort)pix.b)); } } //----------------------------------------------------------------------------- void ComboHistogram::onDisplayModeChanged() { ComboHistoRGBLabel::DisplayMode mode = static_cast( m_displayModeCombo->currentData().toInt()); m_rgbLabel->setDisplayMode(mode); m_rectAverageRgbLabel->setDisplayMode(mode); HistogramChannelDisplayMode = (int)mode; update(); } //----------------------------------------------------------------------------- void ComboHistogram::updateCompHistogram() { assert(!m_compHistoIsValid); m_compHistoIsValid = true; TImageP refimg = TImageCache::instance()->get(QString("TnzCompareImg"), false); if (!(TToonzImageP)refimg && !(TRasterImageP)refimg) return; computeChannelsValue(&m_channelValueComp[0][0], sizeof m_channelValueComp, refimg->raster(), refimg->getPalette()); for (int chan = 0; chan < 4; chan++) m_histograms[chan]->refleshValue(&m_channelValueComp[chan][0], true); } //----------------------------------------------------------------------------- void ComboHistogram::showEvent(QShowEvent *) { if (!m_compHistoIsValid && m_showCompare) updateCompHistogram(); int envMode = HistogramChannelDisplayMode; m_displayModeCombo->setCurrentIndex(m_displayModeCombo->findData(envMode)); ComboHistoRGBLabel::DisplayMode mode = static_cast(envMode); m_rgbLabel->setDisplayMode(mode); m_rectAverageRgbLabel->setDisplayMode(mode); }