From a3cdd885f5bb5bff5d6901d7ec2928425a5f261b Mon Sep 17 00:00:00 2001 From: shun_iwasawa Date: Jan 16 2017 11:33:23 +0000 Subject: added color filter feature and modified composition with colorscale --- diff --git a/toonz/sources/common/trop/quickput.cpp b/toonz/sources/common/trop/quickput.cpp index d06b4de..7895288 100644 --- a/toonz/sources/common/trop/quickput.cpp +++ b/toonz/sources/common/trop/quickput.cpp @@ -53,24 +53,20 @@ inline TPixel32 applyColorScale(const TPixel32 &color, color.r * colorScale.m / 255, color.g * colorScale.m / 255, color.b * colorScale.m / 255, color.m * colorScale.m / 255); } - int r = color.r + colorScale.r; - int g = color.g + colorScale.g; - int b = color.b + colorScale.b; - - return premultiply(TPixel32(r > 255 ? 255 : r, g > 255 ? 255 : g, - b > 255 ? 255 : b, color.m * colorScale.m / 255)); + int r = 255 - (255 - color.r) * (255 - colorScale.r) / 255; + int g = 255 - (255 - color.g) * (255 - colorScale.g) / 255; + int b = 255 - (255 - color.b) * (255 - colorScale.b) / 255; + return premultiply(TPixel32(r, g, b, color.m * colorScale.m / 255)); } //------------------------------------------------------------------------------ inline TPixel32 applyColorScaleCMapped(const TPixel32 &color, const TPixel32 &colorScale) { - int r = color.r + colorScale.r; - int g = color.g + colorScale.g; - int b = color.b + colorScale.b; - - return premultiply(TPixel32(r > 255 ? 255 : r, g > 255 ? 255 : g, - b > 255 ? 255 : b, color.m * colorScale.m / 255)); + int r = 255 - (255 - color.r) * (255 - colorScale.r) / 255; + int g = 255 - (255 - color.g) * (255 - colorScale.g) / 255; + int b = 255 - (255 - color.b) * (255 - colorScale.b) / 255; + return premultiply(TPixel32(r, g, b, color.m * colorScale.m / 255)); } //------------------------------------------------------------------------------ diff --git a/toonz/sources/include/toonz/stageplayer.h b/toonz/sources/include/toonz/stageplayer.h index 1c19bae..b5379b0 100644 --- a/toonz/sources/include/toonz/stageplayer.h +++ b/toonz/sources/include/toonz/stageplayer.h @@ -102,6 +102,8 @@ public: static double m_onionSkinFrontSize; static double m_onionSkinBackSize; + TPixel32 m_filterColor; + public: Player(); diff --git a/toonz/sources/include/toonz/stagevisitor.h b/toonz/sources/include/toonz/stagevisitor.h index f9d8c4d..ec03556 100644 --- a/toonz/sources/include/toonz/stagevisitor.h +++ b/toonz/sources/include/toonz/stagevisitor.h @@ -209,11 +209,14 @@ private: bool m_doPremultiply; //!< Whether the image must be premultiplied bool m_whiteTransp; //!< Whether white must be intended as transparent + TPixel32 m_filterColor; + public: Node(const TRasterP &raster, TPalette *palette, int alpha, const TAffine &aff, const TRect &savebox, const TRectD &bbox, int frame, bool isCurrentColumn, OnionMode onionMode, - bool doPremultiply, bool whiteTransp, bool isFirstColumn) + bool doPremultiply, bool whiteTransp, bool isFirstColumn, + TPixel32 filterColor = TPixel32::Black) : m_raster(raster) , m_aff(aff) , m_savebox(savebox) @@ -225,7 +228,8 @@ private: , m_onionMode(onionMode) , m_doPremultiply(doPremultiply) , m_whiteTransp(whiteTransp) - , m_isFirstColumn(isFirstColumn) {} + , m_isFirstColumn(isFirstColumn) + , m_filterColor(filterColor) {} }; struct VisualizationOptions { diff --git a/toonz/sources/include/toonz/txshcolumn.h b/toonz/sources/include/toonz/txshcolumn.h index 6724e2d..0d01c15 100644 --- a/toonz/sources/include/toonz/txshcolumn.h +++ b/toonz/sources/include/toonz/txshcolumn.h @@ -64,6 +64,8 @@ class DVAPI TXshColumn : public TColumnHeader, public TPersist { int m_colorTag; // Usato solo in tabkids UCHAR m_opacity; + int m_filterColorId; + protected: enum { eCamstandVisible = 0x1, @@ -90,7 +92,12 @@ public: /*! Constructs a TXshColumn with default value. */ - TXshColumn() : m_status(0), m_xsheet(0), m_colorTag(0), m_opacity(255) {} + TXshColumn() + : m_status(0) + , m_xsheet(0) + , m_colorTag(0) + , m_opacity(255) + , m_filterColorId(0) {} enum ColumnType { eLevelType = 0, @@ -236,6 +243,9 @@ Set column color tag to \b colorTag. void setColorTag(int colorTag) { m_colorTag = colorTag; } // Usato solo in tabkids + + int getFilterColorId() const { return m_filterColorId; } + void setFilterColorId(int id) { m_filterColorId = id; } }; #ifdef _WIN32 diff --git a/toonz/sources/include/tpixelutils.h b/toonz/sources/include/tpixelutils.h index f8552cb..69eb988 100644 --- a/toonz/sources/include/tpixelutils.h +++ b/toonz/sources/include/tpixelutils.h @@ -126,14 +126,52 @@ DVAPI inline T quickOverPixPremultT(const T &bot, const T &top) { } //------------------------------------------------------------------------------------ /*-- Show raster images darken-blended on the viewer --*/ +/* references from ino_blend_darken.cpp */ template DVAPI inline T quickOverPixDarkenBlendedT(const T &bot, const T &top) { - UINT max = T::maxChannelValue; + struct locals { + static inline double comp(const double ch_a, const double ch_b, + const double alpha) { + return clamp(ch_b + ch_a * (1.0 - alpha)); + } + static inline double darken_ch(const double dn, const double dn_a, + const double up, const double up_a) { + return (up / up_a < dn / dn_a) ? comp(dn, up, up_a) : comp(up, dn, dn_a); + } + static inline double clamp(double val) { + return (val < 0.0) ? 0.0 : (val > 1.0) ? 1.0 : val; + } + }; // locals + if (bot.m == 0) return top; - TUINT32 r = (top.r < bot.r) ? top.r : bot.r; - TUINT32 g = (top.g < bot.g) ? top.g : bot.g; - TUINT32 b = (top.b < bot.b) ? top.b : bot.b; - return T((Q)r, (Q)g, (Q)b, max); + + if (top.m == T::maxChannelValue && bot.m == T::maxChannelValue) { + TUINT32 r = (top.r < bot.r) ? top.r : bot.r; + TUINT32 g = (top.g < bot.g) ? top.g : bot.g; + TUINT32 b = (top.b < bot.b) ? top.b : bot.b; + return T((Q)r, (Q)g, (Q)b, T::maxChannelValue); + } + + double maxi = static_cast(T::maxChannelValue); // 255or65535 + + double upr = static_cast(top.r) / maxi; + double upg = static_cast(top.g) / maxi; + double upb = static_cast(top.b) / maxi; + double upa = static_cast(top.m) / maxi; + double dnr = static_cast(bot.r) / maxi; + double dng = static_cast(bot.g) / maxi; + double dnb = static_cast(bot.b) / maxi; + double dna = static_cast(bot.m) / maxi; + dnr = locals::darken_ch(dnr, dna, upr, upa); + dng = locals::darken_ch(dng, dna, upg, upa); + dnb = locals::darken_ch(dnb, dna, upb, upa); + dna = locals::comp(dna, upa, upa); + T out; + out.r = static_cast(dnr * (maxi + 0.999999)); + out.g = static_cast(dng * (maxi + 0.999999)); + out.b = static_cast(dnb * (maxi + 0.999999)); + out.m = static_cast(dna * (maxi + 0.999999)); + return out; } //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonz/xshcolumnviewer.cpp b/toonz/sources/toonz/xshcolumnviewer.cpp index f490b8e..84caa2b 100644 --- a/toonz/sources/toonz/xshcolumnviewer.cpp +++ b/toonz/sources/toonz/xshcolumnviewer.cpp @@ -58,6 +58,7 @@ #include #include #include +#include //============================================================================= namespace { @@ -102,6 +103,14 @@ bool containsRasterLevel(TColumnSelection *selection) { } return false; } + +const QIcon getColorChipIcon(const int id) { + static QList colors = {Qt::red, Qt::green, Qt::blue, + Qt::darkYellow, Qt::darkCyan, Qt::darkMagenta}; + QPixmap pixmap(12, 12); + pixmap.fill(colors.at(id - 1)); + return QIcon(pixmap); +} } //----------------------------------------------------------------------------- @@ -763,6 +772,16 @@ void ColumnArea::drawLevelColumnHead(QPainter &p, int col) { // notify that the column icon is already shown if (levelColumn) levelColumn->setIconVisible(true); } + + // filter color + if (column->getFilterColorId() != 0) { + QRect filterColorRect(thumbnailRect.topRight().x() - 14, + thumbnailRect.topRight().y(), 14, 14); + p.fillRect(filterColorRect, Qt::white); + p.drawPixmap( + filterColorRect.adjusted(2, 2, -2, -2), + getColorChipIcon(column->getFilterColorId()).pixmap(12, 12)); + } } } } @@ -1315,13 +1334,45 @@ m_value->setFixedWidth(30); static QFont font("Helvetica", 7, QFont::Normal); m_value->setFont(font);*/ - QHBoxLayout *hlayout = new QHBoxLayout; - hlayout->setContentsMargins(0, 3, 0, 3); - hlayout->setSpacing(1); - hlayout->addWidget(m_slider); - hlayout->addWidget(m_value); - hlayout->addWidget(new QLabel("%")); - setLayout(hlayout); + m_filterColorCombo = new QComboBox(this); + m_filterColorCombo->addItem(tr("None"), 0); + m_filterColorCombo->addItem(getColorChipIcon(1), tr("Red"), 1); + m_filterColorCombo->addItem(getColorChipIcon(2), tr("Green"), 2); + m_filterColorCombo->addItem(getColorChipIcon(3), tr("Blue"), 3); + m_filterColorCombo->addItem(getColorChipIcon(4), tr("DarkYellow"), 4); + m_filterColorCombo->addItem(getColorChipIcon(5), tr("DarkCyan"), 5); + m_filterColorCombo->addItem(getColorChipIcon(6), tr("DarkMagenta"), 6); + // For now the color filter affects only for Raster and ToonzRaser levels. + // TODO: Make this property to affect vector levels as well. + m_filterColorCombo->setToolTip( + tr("N.B. Filter doesn't affect vector levels")); + + QLabel *filterLabel = new QLabel(tr("Filter:"), this); + filterLabel->setToolTip(tr("N.B. Filter doesn't affect vector levels")); + + QVBoxLayout *mainLayout = new QVBoxLayout(); + mainLayout->setMargin(3); + mainLayout->setSpacing(3); + { + QHBoxLayout *hlayout = new QHBoxLayout; + // hlayout->setContentsMargins(0, 3, 0, 3); + hlayout->setMargin(0); + hlayout->setSpacing(1); + hlayout->addWidget(m_slider); + hlayout->addWidget(m_value); + hlayout->addWidget(new QLabel("%")); + mainLayout->addLayout(hlayout, 0); + + QHBoxLayout *filterColorLay = new QHBoxLayout(); + filterColorLay->setMargin(0); + filterColorLay->setSpacing(2); + { + filterColorLay->addWidget(filterLabel, 0); + filterColorLay->addWidget(m_filterColorCombo, 1); + } + mainLayout->addLayout(filterColorLay, 0); + } + setLayout(mainLayout); bool ret = connect(m_slider, SIGNAL(sliderReleased()), this, SLOT(onSliderReleased())); @@ -1331,6 +1382,9 @@ m_value->setFont(font);*/ SLOT(onSliderValueChanged(int))); ret = ret && connect(m_value, SIGNAL(textChanged(const QString &)), this, SLOT(onValueChanged(const QString &))); + + ret = ret && connect(m_filterColorCombo, SIGNAL(activated(int)), this, + SLOT(onFilterColorChanged(int))); assert(ret); } @@ -1372,6 +1426,15 @@ void ColumnTransparencyPopup::onValueChanged(const QString &str) { //---------------------------------------------------------------- +void ColumnTransparencyPopup::onFilterColorChanged(int id) { + m_column->setFilterColorId(id); + TApp::instance()->getCurrentScene()->notifySceneChanged(); + TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); + ((ColumnArea *)parent())->update(); +} + +//---------------------------------------------------------------- + void ColumnTransparencyPopup::setColumn(TXshColumn *column) { m_column = column; assert(m_column); @@ -1381,6 +1444,8 @@ void ColumnTransparencyPopup::setColumn(TXshColumn *column) { m_value->setText(QString::number(val)); connect(m_value, SIGNAL(textChanged(const QString &)), this, SLOT(onValueChanged(const QString &))); + + m_filterColorCombo->setCurrentIndex(m_column->getFilterColorId()); } /*void ColumnTransparencyPopup::mouseMoveEvent ( QMouseEvent * e ) diff --git a/toonz/sources/toonz/xshcolumnviewer.h b/toonz/sources/toonz/xshcolumnviewer.h index 282ef9b..e4dd076 100644 --- a/toonz/sources/toonz/xshcolumnviewer.h +++ b/toonz/sources/toonz/xshcolumnviewer.h @@ -13,6 +13,7 @@ class TObjectHandle; class TXsheetHandle; class TStageObjectId; class TXshColumn; +class QComboBox; //============================================================================= namespace XsheetGUI { @@ -153,6 +154,8 @@ class ColumnTransparencyPopup final : public QWidget { QLineEdit *m_value; TXshColumn *m_column; + QComboBox *m_filterColorCombo; + public: ColumnTransparencyPopup(QWidget *parent); void setColumn(TXshColumn *column); @@ -166,6 +169,8 @@ protected slots: void onSliderChange(int val); void onSliderValueChanged(int); void onValueChanged(const QString &); + + void onFilterColorChanged(int id); }; //! La classe si occupa della visualizzazione dell'area che gestisce le colonne. diff --git a/toonz/sources/toonzlib/stage.cpp b/toonz/sources/toonzlib/stage.cpp index ad340da..22146b8 100644 --- a/toonz/sources/toonzlib/stage.cpp +++ b/toonz/sources/toonzlib/stage.cpp @@ -96,6 +96,35 @@ void updateOnionSkinSize(const PlayerSet &players) { bool descending(int i, int j) { return (i > j); } //---------------------------------------------------------------- + +TPixel32 getFilterColorFromId(int id) { + switch (id) { + case 0: + return TPixel::Black; + break; + case 1: + return TPixel::Red; + break; + case 2: + return TPixel::Green; + break; + case 3: + return TPixel::Blue; + break; + case 4: + return TPixel(128, 128, 0); + break; + case 5: + return TPixel(0, 128, 128); + break; + case 6: + return TPixel(128, 0, 128); + break; + } + return TPixel::Black; +} + +//---------------------------------------------------------------- } //============================================================================= @@ -367,6 +396,7 @@ void StageBuilder::addCell(PlayerSet &players, ToonzScene *scene, TXsheet *xsh, player.m_ancestorColumnIndex = m_ancestorColumnIndex; player.m_masks = m_masks; player.m_opacity = column->getOpacity(); + player.m_filterColor = getFilterColorFromId(column->getFilterColorId()); if (m_subXSheetStack.empty()) { player.m_z = columnZ; diff --git a/toonz/sources/toonzlib/stagevisitor.cpp b/toonz/sources/toonzlib/stagevisitor.cpp index dc256db..87339fc 100644 --- a/toonz/sources/toonzlib/stagevisitor.cpp +++ b/toonz/sources/toonzlib/stagevisitor.cpp @@ -520,8 +520,13 @@ void RasterPainter::flushRasterImages() { else if (m_nodes[i].m_onionMode == Node::eOnionSkinBack) colorscale = TPixel32(backOnionColor.r, backOnionColor.g, backOnionColor.b, m_nodes[i].m_alpha); - } else + } else { + if (m_nodes[i].m_filterColor != TPixel32::Black) { + colorscale = m_nodes[i].m_filterColor; + colorscale.m = m_nodes[i].m_alpha; + } inksOnly = tc & ToonzCheck::eInksOnly; + } if (TRaster32P src32 = m_nodes[i].m_raster) TRop::quickPut(viewedRaster, src32, aff, colorscale, @@ -924,7 +929,8 @@ void RasterPainter::onRasterImage(TRasterImage *ri, m_nodes.push_back(Node(r, 0, alpha, aff, ri->getSavebox(), bbox, player.m_frame, player.m_isCurrentColumn, onionMode, - doPremultiply, whiteTransp, ignoreAlpha)); + doPremultiply, whiteTransp, ignoreAlpha, + player.m_filterColor)); } //----------------------------------------------------------------------------- @@ -964,7 +970,7 @@ void RasterPainter::onToonzImage(TToonzImage *ti, const Stage::Player &player) { m_nodes.push_back(Node(r, ti->getPalette(), alpha, aff, ti->getSavebox(), bbox, player.m_frame, player.m_isCurrentColumn, - onionMode, false, false, false)); + onionMode, false, false, false, player.m_filterColor)); } //********************************************************************************************** diff --git a/toonz/sources/toonzlib/txshlevelcolumn.cpp b/toonz/sources/toonzlib/txshlevelcolumn.cpp index 790544d..98dba0a 100644 --- a/toonz/sources/toonzlib/txshlevelcolumn.cpp +++ b/toonz/sources/toonzlib/txshlevelcolumn.cpp @@ -113,6 +113,10 @@ void TXshLevelColumn::loadData(TIStream &is) { int opacity; is >> opacity; setOpacity((UCHAR)opacity); + } else if (tagName == "filter_color_id") { + int id; + is >> id; + setFilterColorId(id); } else if (tagName == "cells") { while (is.openChild(tagName)) { if (tagName == "cell") { @@ -164,6 +168,8 @@ void TXshLevelColumn::loadData(TIStream &is) { void TXshLevelColumn::saveData(TOStream &os) { os.child("status") << getStatusWord(); if (getOpacity() < 255) os.child("camerastand_opacity") << (int)getOpacity(); + if (getFilterColorId() != 0) + os.child("filter_color_id") << (int)getFilterColorId(); int r0, r1; if (getRange(r0, r1)) { os.openChild("cells");