diff --git a/plugins/filters/colorsfilters/kis_cross_channel_filter.cpp b/plugins/filters/colorsfilters/kis_cross_channel_filter.cpp index f1f05dd48f..02438aec1a 100644 --- a/plugins/filters/colorsfilters/kis_cross_channel_filter.cpp +++ b/plugins/filters/colorsfilters/kis_cross_channel_filter.cpp @@ -1,269 +1,292 @@ /* * This file is part of Krita * * Copyright (c) 2018 Jouni Pentikainen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_cross_channel_filter.h" #include #include #include #include #include #include #include #include #include "KoChannelInfo.h" #include "KoBasicHistogramProducers.h" #include "KoColorModelStandardIds.h" #include "KoColorSpace.h" #include "KoColorTransformation.h" #include "KoCompositeColorTransformation.h" #include "KoCompositeOp.h" #include "KoID.h" #include "kis_signals_blocker.h" #include "kis_bookmarked_configuration_manager.h" #include "kis_config_widget.h" #include #include #include #include #include #include "kis_histogram.h" #include "kis_painter.h" #include "widgets/kis_curve_widget.h" #include "../../color/colorspaceextensions/kis_hsv_adjustment.h" // KisCrossChannelFilterConfiguration -KisCrossChannelFilterConfiguration::KisCrossChannelFilterConfiguration(int channelCount) - : KisMultiChannelFilterConfiguration(channelCount, "crosschannel", 1) +KisCrossChannelFilterConfiguration::KisCrossChannelFilterConfiguration(int channelCount, const KoColorSpace *cs) + : KisMultiChannelFilterConfiguration(channelCount, "crosschannel", 1) { init(); - m_driverChannels.resize(channelCount); + + int defaultDriver = 0; + + if (cs) { + QVector virtualChannels = KisMultiChannelFilter::getVirtualChannels(cs); + defaultDriver = qMax(0, KisMultiChannelFilter::findChannel(virtualChannels, VirtualChannelInfo::LIGHTNESS)); + } + + m_driverChannels.fill(defaultDriver, channelCount); } KisCrossChannelFilterConfiguration::~KisCrossChannelFilterConfiguration() {} const QVector KisCrossChannelFilterConfiguration::driverChannels() const { return m_driverChannels; } void KisCrossChannelFilterConfiguration::setDriverChannels(QVector driverChannels) { KIS_SAFE_ASSERT_RECOVER_RETURN(driverChannels.size() == m_curves.size()); m_driverChannels = driverChannels; } void KisCrossChannelFilterConfiguration::fromXML(const QDomElement& root) { KisMultiChannelFilterConfiguration::fromXML(root); m_driverChannels.resize(m_curves.size()); QRegExp rx("driver(\\d+)"); for (QDomElement e = root.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) { const QString attributeName = e.attribute("name"); if (rx.exactMatch(attributeName)) { int channel = rx.cap(1).toUShort(); int driver = KisDomUtils::toInt(e.text()); if (0 <= channel && channel < m_driverChannels.size()) { m_driverChannels[channel] = driver; } } } } void KisCrossChannelFilterConfiguration::toXML(QDomDocument& doc, QDomElement& root) const { KisMultiChannelFilterConfiguration::toXML(doc, root); for (int i = 0; i < m_driverChannels.size(); i++) { QDomElement param = doc.createElement("param"); param.setAttribute("name", QString("driver%1").arg(i)); QDomText text = doc.createTextNode(KisDomUtils::toString(m_driverChannels[i])); param.appendChild(text); root.appendChild(param); } } KisCubicCurve KisCrossChannelFilterConfiguration::getDefaultCurve() { const QList points { QPointF(0.0f, 0.5f), QPointF(1.0f, 0.5f) }; return KisCubicCurve(points); } - KisCrossChannelConfigWidget::KisCrossChannelConfigWidget(QWidget * parent, KisPaintDeviceSP dev, Qt::WindowFlags f) : KisMultiChannelConfigWidget(parent, dev, f) { const int virtualChannelCount = m_virtualChannels.size(); m_driverChannels.resize(virtualChannelCount); init(); for (int i = 0; i < virtualChannelCount; i++) { const VirtualChannelInfo &info = m_virtualChannels[i]; if (info.type() == VirtualChannelInfo::ALL_COLORS) { continue; } m_page->cmbDriverChannel->addItem(info.name(), i); } connect(m_page->cmbDriverChannel, SIGNAL(activated(int)), this, SLOT(slotDriverChannelSelected(int))); } // KisCrossChannelConfigWidget KisCrossChannelConfigWidget::~KisCrossChannelConfigWidget() {} void KisCrossChannelConfigWidget::setConfiguration(const KisPropertiesConfigurationSP config) { const auto *cfg = dynamic_cast(config.data()); m_driverChannels = cfg->driverChannels(); KisMultiChannelConfigWidget::setConfiguration(config); + + // Show the first channel with a curve, or saturation by default + + int initialChannel = -1; + for (int i = 0; i < m_virtualChannels.size(); i++) { + if (!m_curves[i].isConstant(0.5)) { + initialChannel = i; + break; + } + } + + if (initialChannel < 0) { + initialChannel = qMax(0, KisMultiChannelFilter::findChannel(m_virtualChannels, VirtualChannelInfo::SATURATION)); + } + + setActiveChannel(initialChannel); } KisPropertiesConfigurationSP KisCrossChannelConfigWidget::configuration() const { - auto *cfg = new KisCrossChannelFilterConfiguration(m_virtualChannels.count()); + auto *cfg = new KisCrossChannelFilterConfiguration(m_virtualChannels.count(), m_dev->colorSpace()); KisPropertiesConfigurationSP cfgSP = cfg; m_curves[m_activeVChannel] = m_page->curveWidget->curve(); cfg->setCurves(m_curves); cfg->setDriverChannels(m_driverChannels); return cfgSP; } void KisCrossChannelConfigWidget::updateChannelControls() { m_page->curveWidget->setupInOutControls(m_page->intIn, m_page->intOut, 0, 100, -100, 100); const int index = m_page->cmbDriverChannel->findData(m_driverChannels[m_activeVChannel]); m_page->cmbDriverChannel->setCurrentIndex(index); } KisPropertiesConfigurationSP KisCrossChannelConfigWidget::getDefaultConfiguration() { - return new KisCrossChannelFilterConfiguration(m_virtualChannels.size()); + return new KisCrossChannelFilterConfiguration(m_virtualChannels.size(), m_dev->colorSpace()); } void KisCrossChannelConfigWidget::slotDriverChannelSelected(int index) { const int channel = m_page->cmbDriverChannel->itemData(index).toInt(); KIS_SAFE_ASSERT_RECOVER_RETURN(0 <= channel && channel < m_virtualChannels.size()); m_driverChannels[m_activeVChannel] = channel; updateChannelControls(); } // KisCrossChannelFilter KisCrossChannelFilter::KisCrossChannelFilter() : KisMultiChannelFilter(id(), i18n("&Cross-channel adjustment curves...")) {} KisCrossChannelFilter::~KisCrossChannelFilter() {} KisConfigWidget * KisCrossChannelFilter::createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev) const { return new KisCrossChannelConfigWidget(parent, dev); } KisFilterConfigurationSP KisCrossChannelFilter::factoryConfiguration() const { - return new KisCrossChannelFilterConfiguration(0); + return new KisCrossChannelFilterConfiguration(0, nullptr); } int mapChannel(const VirtualChannelInfo &channel) { switch (channel.type()) { case VirtualChannelInfo::REAL: { int pixelIndex = channel.pixelIndex(); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(0 <= pixelIndex && pixelIndex < 4, 0); return pixelIndex; } case VirtualChannelInfo::ALL_COLORS: return KisHSVCurve::AllColors; case VirtualChannelInfo::HUE: return KisHSVCurve::Hue; case VirtualChannelInfo::SATURATION: return KisHSVCurve::Saturation; case VirtualChannelInfo::LIGHTNESS: return KisHSVCurve::Value; }; KIS_SAFE_ASSERT_RECOVER_NOOP(false); return 0; } KoColorTransformation* KisCrossChannelFilter::createTransformation(const KoColorSpace* cs, const KisFilterConfigurationSP config) const { const KisCrossChannelFilterConfiguration* configBC = dynamic_cast(config.data()); Q_ASSERT(configBC); const QVector > &originalTransfers = configBC->transfers(); const QList &curves = configBC->curves(); const QVector &drivers = configBC->driverChannels(); const QVector virtualChannels = KisMultiChannelFilter::getVirtualChannels(cs); if (originalTransfers.size() != int(virtualChannels.size())) { // We got an illegal number of colorchannels :( return 0; } QVector transforms; // Channel order reversed in order to adjust saturation before hue. This allows mapping grays to colors. for (int i = virtualChannels.size() - 1; i >= 0; i--) { if (!curves[i].isConstant(0.5)) { int channel = mapChannel(virtualChannels[i]); int driverChannel = mapChannel(virtualChannels[drivers[i]]); QHash params; params["channel"] = channel; params["driverChannel"] = driverChannel; params["curve"] = QVariant::fromValue(originalTransfers[i]); params["relative"] = true; params["lumaRed"] = cs->lumaCoefficients()[0]; params["lumaGreen"] = cs->lumaCoefficients()[1]; params["lumaBlue"] = cs->lumaCoefficients()[2]; transforms << cs->createColorTransformation("hsv_curve_adjustment", params); } } return KoCompositeColorTransformation::createOptimizedCompositeTransform(transforms); } diff --git a/plugins/filters/colorsfilters/kis_cross_channel_filter.h b/plugins/filters/colorsfilters/kis_cross_channel_filter.h index c7b69b0242..37da142778 100644 --- a/plugins/filters/colorsfilters/kis_cross_channel_filter.h +++ b/plugins/filters/colorsfilters/kis_cross_channel_filter.h @@ -1,101 +1,102 @@ /* * This file is part of Krita * * Copyright (c) 2018 Jouni Pentikainen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_CROSSCHANNEL_FILTER_H_ #define _KIS_CROSSCHANNEL_FILTER_H_ #include #include #include #include #include #include #include "ui_wdg_perchannel.h" #include "virtual_channel_info.h" #include "kis_multichannel_filter_base.h" /** * Filter which applies a relative adjustment to a (virtual) color channel based on the value of another. * The amount of adjustment for a given input is controlled by a user-defined curve. */ class KisCrossChannelFilter : public KisMultiChannelFilter { public: KisCrossChannelFilter(); ~KisCrossChannelFilter() override; KisConfigWidget * createConfigurationWidget(QWidget *parent, const KisPaintDeviceSP dev) const override; KisFilterConfigurationSP factoryConfiguration() const override; KoColorTransformation* createTransformation(const KoColorSpace *cs, const KisFilterConfigurationSP config) const override; static inline KoID id() { return KoID("crosschannel", i18n("Cross-channel color adjustment")); } }; class KisCrossChannelFilterConfiguration : public KisMultiChannelFilterConfiguration { public: - KisCrossChannelFilterConfiguration(int n); + KisCrossChannelFilterConfiguration(int channelCount, const KoColorSpace *cs); + ~KisCrossChannelFilterConfiguration() override; const QVector driverChannels() const; void setDriverChannels(QVector driverChannels); using KisFilterConfiguration::fromXML; using KisFilterConfiguration::toXML; void fromXML(const QDomElement& e) override; void toXML(QDomDocument& doc, QDomElement& root) const override; KisCubicCurve getDefaultCurve() override; private: QVector m_driverChannels; }; class KisCrossChannelConfigWidget : public KisMultiChannelConfigWidget { Q_OBJECT public: KisCrossChannelConfigWidget(QWidget * parent, KisPaintDeviceSP dev, Qt::WindowFlags f = 0); ~KisCrossChannelConfigWidget() override; void setConfiguration(const KisPropertiesConfigurationSP config) override; KisPropertiesConfigurationSP configuration() const override; protected: void updateChannelControls() override; virtual KisPropertiesConfigurationSP getDefaultConfiguration() override; private Q_SLOTS: void slotDriverChannelSelected(int index); private: QVector m_driverChannels; }; #endif diff --git a/plugins/filters/colorsfilters/kis_multichannel_filter_base.cpp b/plugins/filters/colorsfilters/kis_multichannel_filter_base.cpp index 91fc2ad928..9ab392d467 100644 --- a/plugins/filters/colorsfilters/kis_multichannel_filter_base.cpp +++ b/plugins/filters/colorsfilters/kis_multichannel_filter_base.cpp @@ -1,496 +1,507 @@ /* * This file is part of Krita * * Copyright (c) 2018 Jouni Pentikainen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_multichannel_filter_base.h" #include #include #include #include #include #include #include #include #include "KoChannelInfo.h" #include "KoBasicHistogramProducers.h" #include "KoColorModelStandardIds.h" #include "KoColorSpace.h" #include "KoColorTransformation.h" #include "KoCompositeColorTransformation.h" #include "KoCompositeOp.h" #include "KoID.h" #include "kis_signals_blocker.h" #include "kis_bookmarked_configuration_manager.h" #include "kis_config_widget.h" #include #include #include #include #include "kis_histogram.h" #include "kis_painter.h" #include "widgets/kis_curve_widget.h" KisMultiChannelFilter::KisMultiChannelFilter(const KoID& id, const QString &entry) : KisColorTransformationFilter(id, categoryAdjust(), entry) { setSupportsPainting(true); setColorSpaceIndependence(TO_LAB16); } bool KisMultiChannelFilter::needsTransparentPixels(const KisFilterConfigurationSP config, const KoColorSpace *cs) const { Q_UNUSED(config); return cs->colorModelId() == AlphaColorModelID; } QVector KisMultiChannelFilter::getVirtualChannels(const KoColorSpace *cs) { const bool supportsLightness = cs->colorModelId() != LABAColorModelID && cs->colorModelId() != GrayAColorModelID && cs->colorModelId() != GrayColorModelID && cs->colorModelId() != AlphaColorModelID; const bool supportsHue = supportsLightness; const bool supportSaturation = supportsLightness; QVector vchannels; QList sortedChannels = KoChannelInfo::displayOrderSorted(cs->channels()); if (supportsLightness) { vchannels << VirtualChannelInfo(VirtualChannelInfo::ALL_COLORS, -1, 0, cs); } Q_FOREACH (KoChannelInfo *channel, sortedChannels) { int pixelIndex = KoChannelInfo::displayPositionToChannelIndex(channel->displayPosition(), sortedChannels); vchannels << VirtualChannelInfo(VirtualChannelInfo::REAL, pixelIndex, channel, cs); } if (supportsHue) { vchannels << VirtualChannelInfo(VirtualChannelInfo::HUE, -1, 0, cs); } if (supportSaturation) { vchannels << VirtualChannelInfo(VirtualChannelInfo::SATURATION, -1, 0, cs); } if (supportsLightness) { vchannels << VirtualChannelInfo(VirtualChannelInfo::LIGHTNESS, -1, 0, cs); } return vchannels; } +int KisMultiChannelFilter::findChannel(const QVector &virtualChannels, + const VirtualChannelInfo::Type &channelType) +{ + for (int i = 0; i < virtualChannels.size(); i++) { + if (virtualChannels[i].type() == channelType) { + return i; + } + } + return -1; +} + KisMultiChannelFilterConfiguration::KisMultiChannelFilterConfiguration(int channelCount, const QString & name, qint32 version) : KisColorTransformationConfiguration(name, version) , m_channelCount(channelCount) { m_transfers.resize(m_channelCount); } KisMultiChannelFilterConfiguration::~KisMultiChannelFilterConfiguration() {} void KisMultiChannelFilterConfiguration::init() { m_curves.clear(); for (int i = 0; i < m_channelCount; ++i) { m_curves.append(getDefaultCurve()); } updateTransfers(); } bool KisMultiChannelFilterConfiguration::isCompatible(const KisPaintDeviceSP dev) const { return (int)dev->compositionSourceColorSpace()->channelCount() == m_channelCount; } void KisMultiChannelFilterConfiguration::setCurves(QList &curves) { m_curves.clear(); m_curves = curves; m_channelCount = curves.size(); updateTransfers(); } void KisMultiChannelFilterConfiguration::updateTransfers() { m_transfers.resize(m_channelCount); for (int i = 0; i < m_channelCount; i++) { m_transfers[i] = m_curves[i].uint16Transfer(); } } const QVector >& KisMultiChannelFilterConfiguration::transfers() const { return m_transfers; } const QList& KisMultiChannelFilterConfiguration::curves() const { return m_curves; } void KisMultiChannelFilterConfiguration::fromLegacyXML(const QDomElement& root) { fromXML(root); } void KisMultiChannelFilterConfiguration::fromXML(const QDomElement& root) { QList curves; quint16 numTransfers = 0; int version; version = root.attribute("version").toInt(); QDomElement e = root.firstChild().toElement(); QString attributeName; KisCubicCurve curve; quint16 index; while (!e.isNull()) { if ((attributeName = e.attribute("name")) == "nTransfers") { numTransfers = e.text().toUShort(); } else { QRegExp rx("curve(\\d+)"); if (rx.indexIn(attributeName, 0) != -1) { index = rx.cap(1).toUShort(); index = qMin(index, quint16(curves.count())); if (!e.text().isEmpty()) { curve.fromString(e.text()); } curves.insert(index, curve); } } e = e.nextSiblingElement(); } //prepend empty curves for the brightness contrast filter. if(getString("legacy") == "brightnesscontrast") { if (getString("colorModel") == LABAColorModelID.id()) { curves.append(KisCubicCurve()); curves.append(KisCubicCurve()); curves.append(KisCubicCurve()); } else { int extraChannels = 5; if (getString("colorModel") == CMYKAColorModelID.id()) { extraChannels = 6; } else if (getString("colorModel") == GrayAColorModelID.id()) { extraChannels = 0; } for(int c = 0; c < extraChannels; c ++) { curves.insert(0, KisCubicCurve()); } } } if (!numTransfers) return; setVersion(version); setCurves(curves); } /** * Inherited from KisPropertiesConfiguration */ //void KisMultiChannelFilterConfiguration::fromXML(const QString& s) void addParamNode(QDomDocument& doc, QDomElement& root, const QString &name, const QString &value) { QDomText text = doc.createTextNode(value); QDomElement t = doc.createElement("param"); t.setAttribute("name", name); t.appendChild(text); root.appendChild(t); } void KisMultiChannelFilterConfiguration::toXML(QDomDocument& doc, QDomElement& root) const { /** * * 3 * 0,0;0.5,0.5;1,1; * 0,0;1,1; * 0,0;1,1; * */ root.setAttribute("version", version()); QDomText text; QDomElement t; addParamNode(doc, root, "nTransfers", QString::number(m_channelCount)); KisCubicCurve curve; QString paramName; for (int i = 0; i < m_curves.size(); ++i) { QString name = QLatin1String("curve") + QString::number(i); QString value = m_curves[i].toString(); addParamNode(doc, root, name, value); } } KisMultiChannelConfigWidget::KisMultiChannelConfigWidget(QWidget * parent, KisPaintDeviceSP dev, Qt::WindowFlags f) : KisConfigWidget(parent, f) , m_dev(dev) , m_page(new WdgPerChannel(this)) { Q_ASSERT(m_dev); const KoColorSpace *targetColorSpace = dev->compositionSourceColorSpace(); m_virtualChannels = KisMultiChannelFilter::getVirtualChannels(targetColorSpace); } /** * Initialize the dialog. * Note: m_virtualChannels must be populated before calling this */ void KisMultiChannelConfigWidget::init() { QHBoxLayout * layout = new QHBoxLayout(this); Q_CHECK_PTR(layout); layout->setContentsMargins(0,0,0,0); layout->addWidget(m_page); resetCurves(); const int virtualChannelCount = m_virtualChannels.size(); for (int i = 0; i < virtualChannelCount; i++) { const VirtualChannelInfo &info = m_virtualChannels[i]; m_page->cmbChannel->addItem(info.name(), i); } connect(m_page->cmbChannel, SIGNAL(activated(int)), this, SLOT(slotChannelSelected(int))); connect((QObject*)(m_page->chkLogarithmic), SIGNAL(toggled(bool)), this, SLOT(logHistView())); connect((QObject*)(m_page->resetButton), SIGNAL(clicked()), this, SLOT(resetCurve())); // create the horizontal and vertical gradient labels m_page->hgradient->setPixmap(createGradient(Qt::Horizontal)); m_page->vgradient->setPixmap(createGradient(Qt::Vertical)); // init histogram calculator const KoColorSpace *targetColorSpace = m_dev->compositionSourceColorSpace(); QList keys = KoHistogramProducerFactoryRegistry::instance()->keysCompatibleWith(targetColorSpace); if (keys.size() > 0) { KoHistogramProducerFactory *hpf; hpf = KoHistogramProducerFactoryRegistry::instance()->get(keys.at(0)); m_histogram = new KisHistogram(m_dev, m_dev->exactBounds(), hpf->generate(), LINEAR); } connect(m_page->curveWidget, SIGNAL(modified()), this, SIGNAL(sigConfigurationItemChanged())); { KisSignalsBlocker b(m_page->curveWidget); m_page->curveWidget->setCurve(m_curves[0]); setActiveChannel(0); } } KisMultiChannelConfigWidget::~KisMultiChannelConfigWidget() { delete m_histogram; } void KisMultiChannelConfigWidget::resetCurves() { const KisPropertiesConfigurationSP &defaultConfiguration = getDefaultConfiguration(); const auto *defaults = dynamic_cast(defaultConfiguration.data()); KIS_SAFE_ASSERT_RECOVER_RETURN(defaults); m_curves = defaults->curves(); const int virtualChannelCount = m_virtualChannels.size(); for (int i = 0; i < virtualChannelCount; i++) { const VirtualChannelInfo &info = m_virtualChannels[i]; m_curves[i].setName(info.name()); } } void KisMultiChannelConfigWidget::setConfiguration(const KisPropertiesConfigurationSP config) { const KisMultiChannelFilterConfiguration * cfg = dynamic_cast(config.data()); if (!cfg) { return; } if (cfg->curves().empty()) { /** * HACK ALERT: our configuration factory generates * default configuration with nTransfers==0. * Catching it here. Just set everything to defaults instead. */ const KisPropertiesConfigurationSP &defaultConfiguration = getDefaultConfiguration(); const auto *defaults = dynamic_cast(defaultConfiguration.data()); KIS_SAFE_ASSERT_RECOVER_RETURN(defaults); if (!defaults->curves().isEmpty()) { setConfiguration(defaultConfiguration); return; } } else if (cfg->curves().size() != int(m_virtualChannels.size())) { warnKrita << "WARNING: trying to load a curve with incorrect number of channels!"; warnKrita << "WARNING: expected:" << m_virtualChannels.size(); warnKrita << "WARNING: got:" << cfg->curves().size(); return; } else { for (int ch = 0; ch < cfg->curves().size(); ch++) { m_curves[ch] = cfg->curves()[ch]; } } // HACK: we save the previous curve in setActiveChannel, so just copy it m_page->curveWidget->setCurve(m_curves[m_activeVChannel]); setActiveChannel(0); } inline QPixmap KisMultiChannelConfigWidget::createGradient(Qt::Orientation orient /*, int invert (not used yet) */) { int width; int height; int *i, inc, col; int x = 0, y = 0; if (orient == Qt::Horizontal) { i = &x; inc = 1; col = 0; width = 256; height = 1; } else { i = &y; inc = -1; col = 255; width = 1; height = 256; } QPixmap gradientpix(width, height); QPainter p(&gradientpix); p.setPen(QPen(QColor(0, 0, 0), 1, Qt::SolidLine)); for (; *i < 256; (*i)++, col += inc) { p.setPen(QColor(col, col, col)); p.drawPoint(x, y); } return gradientpix; } inline QPixmap KisMultiChannelConfigWidget::getHistogram() { int i; int height = 256; QPixmap pix(256, height); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(m_histogram, pix); bool logarithmic = m_page->chkLogarithmic->isChecked(); if (logarithmic) m_histogram->setHistogramType(LOGARITHMIC); else m_histogram->setHistogramType(LINEAR); QPalette appPalette = QApplication::palette(); pix.fill(QColor(appPalette.color(QPalette::Base))); QPainter p(&pix); p.setPen(QColor(appPalette.color(QPalette::Text))); p.save(); p.setOpacity(0.2); const VirtualChannelInfo &info = m_virtualChannels[m_activeVChannel]; if (info.type() == VirtualChannelInfo::REAL) { m_histogram->setChannel(info.pixelIndex()); double highest = (double)m_histogram->calculations().getHighest(); qint32 bins = m_histogram->producer()->numberOfBins(); if (m_histogram->getHistogramType() == LINEAR) { double factor = (double)height / highest; for (i = 0; i < bins; ++i) { p.drawLine(i, height, i, height - int(m_histogram->getValue(i) * factor)); } } else { double factor = (double)height / (double)log(highest); for (i = 0; i < bins; ++i) { p.drawLine(i, height, i, height - int(log((double)m_histogram->getValue(i)) * factor)); } } } p.restore(); return pix; } void KisMultiChannelConfigWidget::slotChannelSelected(int index) { const int virtualChannel = m_page->cmbChannel->itemData(index).toInt(); setActiveChannel(virtualChannel); } void KisMultiChannelConfigWidget::setActiveChannel(int ch) { m_curves[m_activeVChannel] = m_page->curveWidget->curve(); m_activeVChannel = ch; m_page->curveWidget->setCurve(m_curves[m_activeVChannel]); m_page->curveWidget->setPixmap(getHistogram()); const int index = m_page->cmbChannel->findData(m_activeVChannel); m_page->cmbChannel->setCurrentIndex(index); updateChannelControls(); } void KisMultiChannelConfigWidget::logHistView() { m_page->curveWidget->setPixmap(getHistogram()); } void KisMultiChannelConfigWidget::resetCurve() { const KisPropertiesConfigurationSP &defaultConfiguration = getDefaultConfiguration(); const auto *defaults = dynamic_cast(defaultConfiguration.data()); KIS_SAFE_ASSERT_RECOVER_RETURN(defaults); auto defaultCurves = defaults->curves(); KIS_SAFE_ASSERT_RECOVER_RETURN(defaultCurves.size() > m_activeVChannel); m_page->curveWidget->setCurve(defaultCurves[m_activeVChannel]); } diff --git a/plugins/filters/colorsfilters/kis_multichannel_filter_base.h b/plugins/filters/colorsfilters/kis_multichannel_filter_base.h index 773c926304..ffbdcaa26f 100644 --- a/plugins/filters/colorsfilters/kis_multichannel_filter_base.h +++ b/plugins/filters/colorsfilters/kis_multichannel_filter_base.h @@ -1,134 +1,133 @@ /* * This file is part of Krita * * Copyright (c) 2018 Jouni Pentikainen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_MULTICHANNEL_FILTER_BASE_H_ #define _KIS_MULTICHANNEL_FILTER_BASE_H_ #include #include #include #include #include #include #include "ui_wdg_perchannel.h" #include "virtual_channel_info.h" /** * Base class for filters which use curves to operate on multiple channels. */ class KisMultiChannelFilter : public KisColorTransformationFilter { public: bool needsTransparentPixels(const KisFilterConfigurationSP config, const KoColorSpace *cs) const override; static QVector getVirtualChannels(const KoColorSpace *cs); + static int findChannel(const QVector &virtualChannels, const VirtualChannelInfo::Type &channelType); protected: KisMultiChannelFilter(const KoID &id, const QString &entry); }; /** * Base class for configurations of KisMultiChannelFilter subclasses */ class KisMultiChannelFilterConfiguration : public KisColorTransformationConfiguration { public: KisMultiChannelFilterConfiguration(int channelCount, const QString & name, qint32 version); ~KisMultiChannelFilterConfiguration() override; using KisFilterConfiguration::fromXML; using KisFilterConfiguration::toXML; using KisFilterConfiguration::fromLegacyXML; void fromLegacyXML(const QDomElement& root) override; void fromXML(const QDomElement& e) override; void toXML(QDomDocument& doc, QDomElement& root) const override; void setCurves(QList &curves) override; bool isCompatible(const KisPaintDeviceSP) const override; const QVector >& transfers() const; const QList& curves() const override; protected: int m_channelCount; QList m_curves; QVector> m_transfers; void init(); void updateTransfers(); virtual KisCubicCurve getDefaultCurve() = 0; }; class WdgPerChannel : public QWidget, public Ui::WdgPerChannel { Q_OBJECT public: WdgPerChannel(QWidget *parent) : QWidget(parent) { setupUi(this); } }; /** * Base class for configuration widgets of KisMultiChannelFilter subclasses */ class KisMultiChannelConfigWidget : public KisConfigWidget { Q_OBJECT public: KisMultiChannelConfigWidget(QWidget * parent, KisPaintDeviceSP dev, Qt::WindowFlags f = 0); ~KisMultiChannelConfigWidget() override; void setConfiguration(const KisPropertiesConfigurationSP config) override; protected Q_SLOTS: void logHistView(); void resetCurve(); void slotChannelSelected(int index); protected: void init(); void resetCurves(); + void setActiveChannel(int ch); virtual void updateChannelControls() = 0; virtual KisPropertiesConfigurationSP getDefaultConfiguration() = 0; inline QPixmap getHistogram(); inline QPixmap createGradient(Qt::Orientation orient /*, int invert (not used now) */); QVector m_virtualChannels; int m_activeVChannel = 0; mutable QList m_curves; - WdgPerChannel * m_page; KisPaintDeviceSP m_dev; + WdgPerChannel * m_page; KisHistogram *m_histogram; - -private: - void setActiveChannel(int ch); }; #endif