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
} else {
// we have at least two color stops
// -> find the two stops which frame our t
QList<KoGradientStop>::const_iterator stop = m_stops.begin();
QList<KoGradientStop>::const_iterator lastStop = m_stops.end();
// we already checked the first stop, so we start at the second
for (++stop; stop != lastStop; ++stop) {
// we break at the stop which is just after our t
if (stop->first > t)
break;
}
leftStop = *(stop - 1);
rightStop = *(stop);
auto it = std::lower_bound(m_stops.begin(), m_stops.end(), KoGradientStop(t, KoColor()), [](const KoGradientStop &a, const KoGradientStop &b){
return a.first < b.first;
});
leftStop = *(it - 1);
rightStop = *(it);
return true;
}
}
......@@ -208,7 +202,6 @@ void KoStopGradient::colorAt(KoColor& dst, qreal t) const
colorWeights[0] = static_cast<quint8>((1.0 - localT) * 255 + 0.5);
colorWeights[1] = 255 - colorWeights[0];
//check if our mixspace exists, it doesn't at startup.
if (mixSpace){
if (*buffer.colorSpace() != *mixSpace) {
......@@ -221,7 +214,6 @@ void KoStopGradient::colorAt(KoColor& dst, qreal t) const
colorSpace()->mixColorsOp()->mixColors(colors, colorWeights, 2, buffer.data());
}
dst.fromKoColor(buffer);
}
......
......@@ -243,8 +243,8 @@ void KisLayerManager::layerProperties()
const bool multipleLayersSelected = selectedNodes.size() > 1;
KisAdjustmentLayerSP adjustmentLayer = KisAdjustmentLayerSP(dynamic_cast<KisAdjustmentLayer*>(layer.data()));
KisGeneratorLayerSP groupLayer = KisGeneratorLayerSP(dynamic_cast<KisGeneratorLayer*>(layer.data()));
KisFileLayerSP filterLayer = KisFileLayerSP(dynamic_cast<KisFileLayer*>(layer.data()));
KisGeneratorLayerSP generatorLayer = KisGeneratorLayerSP(dynamic_cast<KisGeneratorLayer*>(layer.data()));
KisFileLayerSP fileLayer = KisFileLayerSP(dynamic_cast<KisFileLayer*>(layer.data()));
if (adjustmentLayer && !multipleLayersSelected) {
......@@ -292,12 +292,12 @@ void KisLayerManager::layerProperties()
}
}
}
else if (groupLayer && !multipleLayersSelected) {
KisFilterConfigurationSP configBefore(groupLayer->filter());
else if (generatorLayer && !multipleLayersSelected) {
KisFilterConfigurationSP configBefore(generatorLayer->filter());
Q_ASSERT(configBefore);
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->setAttribute(Qt::WA_DeleteOnClose);
......@@ -309,11 +309,11 @@ void KisLayerManager::layerProperties()
dlg->show();
}
else if (filterLayer && !multipleLayersSelected){
else if (fileLayer && !multipleLayersSelected){
QString basePath = QFileInfo(m_view->document()->url().toLocalFile()).absolutePath();
QString fileNameOld = filterLayer->fileName();
KisFileLayer::ScalingMethod scalingMethodOld = filterLayer->scalingMethod();
KisDlgFileLayer dlg(basePath, filterLayer->name(), m_view->mainWindow());
QString fileNameOld = fileLayer->fileName();
KisFileLayer::ScalingMethod scalingMethodOld = fileLayer->scalingMethod();
KisDlgFileLayer dlg(basePath, fileLayer->name(), m_view->mainWindow());
dlg.setCaption(i18n("File Layer Properties"));
dlg.setFileName(fileNameOld);
dlg.setScalingMethod(scalingMethodOld);
......@@ -326,11 +326,11 @@ void KisLayerManager::layerProperties()
QMessageBox::critical(m_view->mainWindow(), i18nc("@title:window", "Krita"), i18n("No file name specified"));
return;
}
filterLayer->setName(dlg.layerName());
fileLayer->setName(dlg.layerName());
if (fileNameOld!= fileNameNew || scalingMethodOld != scalingMethodNew) {
KisChangeFileLayerCmd *cmd
= new KisChangeFileLayerCmd(filterLayer,
= new KisChangeFileLayerCmd(fileLayer,
basePath,
fileNameOld,
scalingMethodOld,
......
......@@ -10,6 +10,9 @@
<height>154</height>
</rect>
</property>
<property name="windowTitle">
<string>KisDitherWidget</string>
</property>
<layout class="QFormLayout" name="formLayout">
<property name="leftMargin">
<number>0</number>
......
......@@ -58,7 +58,7 @@ KritaGradientMapConfigWidget::KritaGradientMapConfigWidget(QWidget *parent, KisP
connect(m_page->gradientEditor, SIGNAL(sigGradientChanged()), m_gradientChangedCompressor, SLOT(start()));
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);
}
......@@ -88,7 +88,7 @@ KisPropertiesConfigurationSP KritaGradientMapConfigWidget::configuration() const
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/");
return cfg;
......@@ -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/");
}
......
......@@ -70,9 +70,9 @@ void KritaFilterGradientMap::processImpl(KisPaintDeviceSP device,
}
KoStopGradient gradient = KoStopGradient::fromXML(doc.firstChildElement());
const bool ditherEnabled = config->getBool("ditherEnabled");
const ColorMode colorMode = ColorMode(config->getInt("colorMode"));
KisDitherUtil ditherUtil;
if (ditherEnabled) ditherUtil.setConfiguration(*config, "dither/");
if (colorMode == ColorMode::Dither) ditherUtil.setConfiguration(*config, "dither/");
KoColor outColor(Qt::white, device->colorSpace());
KisSequentialIteratorProgress it(device, applyRect, progressUpdater);
......@@ -80,7 +80,17 @@ void KritaFilterGradientMap::processImpl(KisPaintDeviceSP device,
const int pixelSize = device->colorSpace()->pixelSize();
while (it.nextPixel()) {
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;
if (!gradient.stopsAt(leftStop, rightStop, grey)) continue;
qreal localT = (grey - leftStop.first) / (rightStop.first - leftStop.first);
......@@ -113,7 +123,7 @@ KisFilterConfigurationSP KritaFilterGradientMap::factoryConfiguration() const
doc.appendChild(elt);
config->setProperty("gradientXML", doc.toString());
config->setProperty("ditherEnabled", false);
config->setProperty("colorMode", false);
KisDitherWidget::factoryConfiguration(*config, "dither/");
return config;
......
......@@ -34,6 +34,11 @@ class KritaFilterGradientMap : public KisFilter
public:
KritaFilterGradientMap();
public:
enum ColorMode {
Blend,
Nearest,
Dither,
};
static inline KoID id() {
return KoID("gradientmap", i18n("Gradient Map"));
......
......@@ -6,15 +6,15 @@
<rect>
<x>0</x>
<y>0</y>
<width>361</width>
<height>341</height>
<width>219</width>
<height>133</height>
</rect>
</property>
<property name="windowTitle">
<string>Gradient Map</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0" colspan="2">
<widget class="KoColorPopupButton" name="btnGradientChooser">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
......@@ -27,49 +27,75 @@
</property>
</widget>
</item>
<item>
<widget class="KisStopGradientEditor" name="gradientEditor" native="true">
<item row="2" column="0">
<widget class="QLabel" name="colorModeLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
<property name="text">
<string>Color Mode</string>
</property>
<property name="buddy">
<cstring>colorModeComboBox</cstring>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="ditherGroupBox">
<property name="title">
<string>Dither</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0" colspan="2">
<widget class="KisDitherWidget" name="ditherWidget" native="true"/>
</item>
</layout>
<item row="2" column="1">
<widget class="QComboBox" name="colorModeComboBox">
<item>
<property name="text">
<string>Blend</string>
</property>
</item>
<item>
<property name="text">
<string>Nearest</string>
</property>
</item>
<item>
<property name="text">
<string>Dither</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
<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 name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
<property name="currentIndex">
<number>1</number>
</property>
</spacer>
<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">
<widget class="KisDitherWidget" name="ditherWidget" native="true"/>
</item>
</layout>
</widget>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="KisStopGradientEditor" name="gradientEditor" native="true"/>
</item>
</layout>
</widget>
......@@ -93,5 +119,38 @@
</customwidget>
</customwidgets>
<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>
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