Commit d6492938 authored by Carl Olsson's avatar Carl Olsson Committed by Boudewijn Rempt

Added basic dither functionality from Palettize filter to the Gradient

Map filter.
Allows dithered blending between stop colours without introducing any
new colours which is desirable for pixel art.

Moved dither functionality/ui out of Palettize filter into KisDitherUtil
and KisDitherWidget.
parent edb2c841
...@@ -161,17 +161,11 @@ bool KoStopGradient::stopsAt(KoGradientStop &leftStop, KoGradientStop &rightStop ...@@ -161,17 +161,11 @@ bool KoStopGradient::stopsAt(KoGradientStop &leftStop, KoGradientStop &rightStop
} else { } else {
// we have at least two color stops // we have at least two color stops
// -> find the two stops which frame our t // -> find the two stops which frame our t
QList<KoGradientStop>::const_iterator stop = m_stops.begin(); auto it = std::lower_bound(m_stops.begin(), m_stops.end(), KoGradientStop(t, KoColor()), [](const KoGradientStop &a, const KoGradientStop &b){
QList<KoGradientStop>::const_iterator lastStop = m_stops.end(); return a.first < b.first;
// we already checked the first stop, so we start at the second });
for (++stop; stop != lastStop; ++stop) { leftStop = *(it - 1);
// we break at the stop which is just after our t rightStop = *(it);
if (stop->first > t)
break;
}
leftStop = *(stop - 1);
rightStop = *(stop);
return true; return true;
} }
} }
...@@ -208,7 +202,6 @@ void KoStopGradient::colorAt(KoColor& dst, qreal t) const ...@@ -208,7 +202,6 @@ void KoStopGradient::colorAt(KoColor& dst, qreal t) const
colorWeights[0] = static_cast<quint8>((1.0 - localT) * 255 + 0.5); colorWeights[0] = static_cast<quint8>((1.0 - localT) * 255 + 0.5);
colorWeights[1] = 255 - colorWeights[0]; colorWeights[1] = 255 - colorWeights[0];
//check if our mixspace exists, it doesn't at startup. //check if our mixspace exists, it doesn't at startup.
if (mixSpace){ if (mixSpace){
if (*buffer.colorSpace() != *mixSpace) { if (*buffer.colorSpace() != *mixSpace) {
...@@ -221,7 +214,6 @@ void KoStopGradient::colorAt(KoColor& dst, qreal t) const ...@@ -221,7 +214,6 @@ void KoStopGradient::colorAt(KoColor& dst, qreal t) const
colorSpace()->mixColorsOp()->mixColors(colors, colorWeights, 2, buffer.data()); colorSpace()->mixColorsOp()->mixColors(colors, colorWeights, 2, buffer.data());
} }
dst.fromKoColor(buffer); dst.fromKoColor(buffer);
} }
......
...@@ -243,8 +243,8 @@ void KisLayerManager::layerProperties() ...@@ -243,8 +243,8 @@ void KisLayerManager::layerProperties()
const bool multipleLayersSelected = selectedNodes.size() > 1; const bool multipleLayersSelected = selectedNodes.size() > 1;
KisAdjustmentLayerSP adjustmentLayer = KisAdjustmentLayerSP(dynamic_cast<KisAdjustmentLayer*>(layer.data())); KisAdjustmentLayerSP adjustmentLayer = KisAdjustmentLayerSP(dynamic_cast<KisAdjustmentLayer*>(layer.data()));
KisGeneratorLayerSP groupLayer = KisGeneratorLayerSP(dynamic_cast<KisGeneratorLayer*>(layer.data())); KisGeneratorLayerSP generatorLayer = KisGeneratorLayerSP(dynamic_cast<KisGeneratorLayer*>(layer.data()));
KisFileLayerSP filterLayer = KisFileLayerSP(dynamic_cast<KisFileLayer*>(layer.data())); KisFileLayerSP fileLayer = KisFileLayerSP(dynamic_cast<KisFileLayer*>(layer.data()));
if (adjustmentLayer && !multipleLayersSelected) { if (adjustmentLayer && !multipleLayersSelected) {
...@@ -292,12 +292,12 @@ void KisLayerManager::layerProperties() ...@@ -292,12 +292,12 @@ void KisLayerManager::layerProperties()
} }
} }
} }
else if (groupLayer && !multipleLayersSelected) { else if (generatorLayer && !multipleLayersSelected) {
KisFilterConfigurationSP configBefore(groupLayer->filter()); KisFilterConfigurationSP configBefore(generatorLayer->filter());
Q_ASSERT(configBefore); Q_ASSERT(configBefore);
QString xmlBefore = configBefore->toXML(); QString xmlBefore = configBefore->toXML();
KisDlgGeneratorLayer *dlg = new KisDlgGeneratorLayer(groupLayer->name(), m_view, m_view->mainWindow(), groupLayer, configBefore); KisDlgGeneratorLayer *dlg = new KisDlgGeneratorLayer(generatorLayer->name(), m_view, m_view->mainWindow(), generatorLayer, configBefore);
dlg->setCaption(i18n("Fill Layer Properties")); dlg->setCaption(i18n("Fill Layer Properties"));
dlg->setAttribute(Qt::WA_DeleteOnClose); dlg->setAttribute(Qt::WA_DeleteOnClose);
...@@ -309,11 +309,11 @@ void KisLayerManager::layerProperties() ...@@ -309,11 +309,11 @@ void KisLayerManager::layerProperties()
dlg->show(); dlg->show();
} }
else if (filterLayer && !multipleLayersSelected){ else if (fileLayer && !multipleLayersSelected){
QString basePath = QFileInfo(m_view->document()->url().toLocalFile()).absolutePath(); QString basePath = QFileInfo(m_view->document()->url().toLocalFile()).absolutePath();
QString fileNameOld = filterLayer->fileName(); QString fileNameOld = fileLayer->fileName();
KisFileLayer::ScalingMethod scalingMethodOld = filterLayer->scalingMethod(); KisFileLayer::ScalingMethod scalingMethodOld = fileLayer->scalingMethod();
KisDlgFileLayer dlg(basePath, filterLayer->name(), m_view->mainWindow()); KisDlgFileLayer dlg(basePath, fileLayer->name(), m_view->mainWindow());
dlg.setCaption(i18n("File Layer Properties")); dlg.setCaption(i18n("File Layer Properties"));
dlg.setFileName(fileNameOld); dlg.setFileName(fileNameOld);
dlg.setScalingMethod(scalingMethodOld); dlg.setScalingMethod(scalingMethodOld);
...@@ -326,11 +326,11 @@ void KisLayerManager::layerProperties() ...@@ -326,11 +326,11 @@ void KisLayerManager::layerProperties()
QMessageBox::critical(m_view->mainWindow(), i18nc("@title:window", "Krita"), i18n("No file name specified")); QMessageBox::critical(m_view->mainWindow(), i18nc("@title:window", "Krita"), i18n("No file name specified"));
return; return;
} }
filterLayer->setName(dlg.layerName()); fileLayer->setName(dlg.layerName());
if (fileNameOld!= fileNameNew || scalingMethodOld != scalingMethodNew) { if (fileNameOld!= fileNameNew || scalingMethodOld != scalingMethodNew) {
KisChangeFileLayerCmd *cmd KisChangeFileLayerCmd *cmd
= new KisChangeFileLayerCmd(filterLayer, = new KisChangeFileLayerCmd(fileLayer,
basePath, basePath,
fileNameOld, fileNameOld,
scalingMethodOld, scalingMethodOld,
......
...@@ -10,6 +10,9 @@ ...@@ -10,6 +10,9 @@
<height>154</height> <height>154</height>
</rect> </rect>
</property> </property>
<property name="windowTitle">
<string>KisDitherWidget</string>
</property>
<layout class="QFormLayout" name="formLayout"> <layout class="QFormLayout" name="formLayout">
<property name="leftMargin"> <property name="leftMargin">
<number>0</number> <number>0</number>
......
...@@ -58,7 +58,7 @@ KritaGradientMapConfigWidget::KritaGradientMapConfigWidget(QWidget *parent, KisP ...@@ -58,7 +58,7 @@ KritaGradientMapConfigWidget::KritaGradientMapConfigWidget(QWidget *parent, KisP
connect(m_page->gradientEditor, SIGNAL(sigGradientChanged()), m_gradientChangedCompressor, SLOT(start())); connect(m_page->gradientEditor, SIGNAL(sigGradientChanged()), m_gradientChangedCompressor, SLOT(start()));
connect(m_gradientChangedCompressor, SIGNAL(timeout()), this, SIGNAL(sigConfigurationItemChanged())); connect(m_gradientChangedCompressor, SIGNAL(timeout()), this, SIGNAL(sigConfigurationItemChanged()));
QObject::connect(m_page->ditherGroupBox, &QGroupBox::toggled, this, &KisConfigWidget::sigConfigurationItemChanged); QObject::connect(m_page->colorModeComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &KisConfigWidget::sigConfigurationItemChanged);
QObject::connect(m_page->ditherWidget, &KisDitherWidget::sigConfigurationItemChanged, this, &KisConfigWidget::sigConfigurationItemChanged); QObject::connect(m_page->ditherWidget, &KisDitherWidget::sigConfigurationItemChanged, this, &KisConfigWidget::sigConfigurationItemChanged);
} }
...@@ -88,7 +88,7 @@ KisPropertiesConfigurationSP KritaGradientMapConfigWidget::configuration() const ...@@ -88,7 +88,7 @@ KisPropertiesConfigurationSP KritaGradientMapConfigWidget::configuration() const
cfg->setProperty("gradientXML", doc.toString()); cfg->setProperty("gradientXML", doc.toString());
} }
cfg->setProperty("ditherEnabled", m_page->ditherGroupBox->isChecked()); cfg->setProperty("colorMode", m_page->colorModeComboBox->currentIndex());
m_page->ditherWidget->configuration(*cfg, "dither/"); m_page->ditherWidget->configuration(*cfg, "dither/");
return cfg; return cfg;
...@@ -108,7 +108,7 @@ void KritaGradientMapConfigWidget::setConfiguration(const KisPropertiesConfigura ...@@ -108,7 +108,7 @@ void KritaGradientMapConfigWidget::setConfiguration(const KisPropertiesConfigura
} }
} }
m_page->ditherGroupBox->setChecked(config->getBool("ditherEnabled")); m_page->colorModeComboBox->setCurrentIndex(config->getInt("colorMode"));
m_page->ditherWidget->setConfiguration(*config, "dither/"); m_page->ditherWidget->setConfiguration(*config, "dither/");
} }
......
...@@ -70,9 +70,9 @@ void KritaFilterGradientMap::processImpl(KisPaintDeviceSP device, ...@@ -70,9 +70,9 @@ void KritaFilterGradientMap::processImpl(KisPaintDeviceSP device,
} }
KoStopGradient gradient = KoStopGradient::fromXML(doc.firstChildElement()); KoStopGradient gradient = KoStopGradient::fromXML(doc.firstChildElement());
const bool ditherEnabled = config->getBool("ditherEnabled"); const ColorMode colorMode = ColorMode(config->getInt("colorMode"));
KisDitherUtil ditherUtil; KisDitherUtil ditherUtil;
if (ditherEnabled) ditherUtil.setConfiguration(*config, "dither/"); if (colorMode == ColorMode::Dither) ditherUtil.setConfiguration(*config, "dither/");
KoColor outColor(Qt::white, device->colorSpace()); KoColor outColor(Qt::white, device->colorSpace());
KisSequentialIteratorProgress it(device, applyRect, progressUpdater); KisSequentialIteratorProgress it(device, applyRect, progressUpdater);
...@@ -80,7 +80,17 @@ void KritaFilterGradientMap::processImpl(KisPaintDeviceSP device, ...@@ -80,7 +80,17 @@ void KritaFilterGradientMap::processImpl(KisPaintDeviceSP device,
const int pixelSize = device->colorSpace()->pixelSize(); const int pixelSize = device->colorSpace()->pixelSize();
while (it.nextPixel()) { while (it.nextPixel()) {
grey = qreal(device->colorSpace()->intensity8(it.oldRawData())) / 255; grey = qreal(device->colorSpace()->intensity8(it.oldRawData())) / 255;
if (ditherEnabled) { if (colorMode == ColorMode::Nearest) {
KoGradientStop leftStop, rightStop;
if (!gradient.stopsAt(leftStop, rightStop, grey)) continue;
if (std::abs(grey - leftStop.first) < std::abs(grey - rightStop.first)) {
outColor = leftStop.second;
}
else {
outColor = rightStop.second;
}
}
else if (colorMode == ColorMode::Dither) {
KoGradientStop leftStop, rightStop; KoGradientStop leftStop, rightStop;
if (!gradient.stopsAt(leftStop, rightStop, grey)) continue; if (!gradient.stopsAt(leftStop, rightStop, grey)) continue;
qreal localT = (grey - leftStop.first) / (rightStop.first - leftStop.first); qreal localT = (grey - leftStop.first) / (rightStop.first - leftStop.first);
...@@ -113,7 +123,7 @@ KisFilterConfigurationSP KritaFilterGradientMap::factoryConfiguration() const ...@@ -113,7 +123,7 @@ KisFilterConfigurationSP KritaFilterGradientMap::factoryConfiguration() const
doc.appendChild(elt); doc.appendChild(elt);
config->setProperty("gradientXML", doc.toString()); config->setProperty("gradientXML", doc.toString());
config->setProperty("ditherEnabled", false); config->setProperty("colorMode", false);
KisDitherWidget::factoryConfiguration(*config, "dither/"); KisDitherWidget::factoryConfiguration(*config, "dither/");
return config; return config;
......
...@@ -34,6 +34,11 @@ class KritaFilterGradientMap : public KisFilter ...@@ -34,6 +34,11 @@ class KritaFilterGradientMap : public KisFilter
public: public:
KritaFilterGradientMap(); KritaFilterGradientMap();
public: public:
enum ColorMode {
Blend,
Nearest,
Dither,
};
static inline KoID id() { static inline KoID id() {
return KoID("gradientmap", i18n("Gradient Map")); return KoID("gradientmap", i18n("Gradient Map"));
......
...@@ -6,15 +6,15 @@ ...@@ -6,15 +6,15 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>361</width> <width>219</width>
<height>341</height> <height>133</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Gradient Map</string> <string>Gradient Map</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QFormLayout" name="formLayout">
<item> <item row="0" column="0" colspan="2">
<widget class="KoColorPopupButton" name="btnGradientChooser"> <widget class="KoColorPopupButton" name="btnGradientChooser">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed"> <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
...@@ -27,49 +27,75 @@ ...@@ -27,49 +27,75 @@
</property> </property>
</widget> </widget>
</item> </item>
<item> <item row="2" column="0">
<widget class="KisStopGradientEditor" name="gradientEditor" native="true"> <widget class="QLabel" name="colorModeLabel">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="minimumSize"> <property name="text">
<size> <string>Color Mode</string>
<width>0</width> </property>
<height>0</height> <property name="buddy">
</size> <cstring>colorModeComboBox</cstring>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1">
<widget class="QComboBox" name="colorModeComboBox">
<item> <item>
<widget class="QGroupBox" name="ditherGroupBox"> <property name="text">
<property name="title"> <string>Blend</string>
</property>
</item>
<item>
<property name="text">
<string>Nearest</string>
</property>
</item>
<item>
<property name="text">
<string>Dither</string> <string>Dither</string>
</property> </property>
<property name="checkable"> </item>
<bool>true</bool> </widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QStackedWidget" name="colorModeStackedWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property> </property>
<layout class="QFormLayout" name="formLayout"> <property name="currentIndex">
<number>1</number>
</property>
<property name="prefix" stdset="0">
<string>Amount: </string>
</property>
<widget class="QWidget" name="blendPage">
<layout class="QFormLayout" name="formLayout_7"/>
</widget>
<widget class="QWidget" name="nearestPage">
<layout class="QFormLayout" name="formLayout_6"/>
</widget>
<widget class="QWidget" name="ditherPage">
<layout class="QFormLayout" name="formLayout_5">
<item row="0" column="0" colspan="2"> <item row="0" column="0" colspan="2">
<widget class="KisDitherWidget" name="ditherWidget" native="true"/> <widget class="KisDitherWidget" name="ditherWidget" native="true"/>
</item> </item>
</layout> </layout>
</widget> </widget>
</widget>
</item> </item>
<item> <item row="1" column="0" colspan="2">
<spacer name="verticalSpacer"> <widget class="KisStopGradientEditor" name="gradientEditor" native="true"/>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item> </item>
</layout> </layout>
</widget> </widget>
...@@ -93,5 +119,38 @@ ...@@ -93,5 +119,38 @@
</customwidget> </customwidget>
</customwidgets> </customwidgets>
<resources/> <resources/>
<connections/> <connections>
<connection>
<sender>colorModeComboBox</sender>
<signal>currentIndexChanged(int)</signal>
<receiver>colorModeStackedWidget</receiver>
<slot>setCurrentIndex(int)</slot>
<hints>
<hint type="sourcelabel">
<x>227</x>
<y>101</y>
</hint>
<hint type="destinationlabel">
<x>241</x>
<y>121</y>
</hint>
</hints>
</connection>
<connection>
<sender>colorModeStackedWidget</sender>
<signal>currentChanged(int)</signal>
<receiver>colorModeComboBox</receiver>
<slot>setCurrentIndex(int)</slot>
<hints>
<hint type="sourcelabel">
<x>297</x>
<y>124</y>
</hint>
<hint type="destinationlabel">
<x>295</x>
<y>89</y>
</hint>
</hints>
</connection>
</connections>
</ui> </ui>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment