Commit a4b7ed89 authored by Boudewijn Rempt's avatar Boudewijn Rempt

BUG:339594 Add a plugin to split a layer into its color components

Warning: this can potentially generate thousands and thousands of
layers which is slow and will cause out-of-memory problems.
parent 9e686707
......@@ -15,6 +15,7 @@ add_subdirectory( shearimage )
add_subdirectory( dockers )
add_subdirectory( layergroupswitcher )
add_subdirectory( resourcemanager )
add_subdirectory( layersplit )
# Allow to skip building GMIC plugin
option(WITH_GMIC "Build the G'Mic plugin" ON)
......
set(kritalayersplit_PART_SRCS
layersplit.cpp
dlg_layersplit.cpp
wdg_layersplit.cpp
)
kde4_add_ui_files(kritalayersplit_PART_SRCS
wdg_layersplit.ui
)
kde4_add_plugin(kritalayersplit ${kritalayersplit_PART_SRCS})
target_link_libraries(kritalayersplit kritaui)
install(TARGETS kritalayersplit DESTINATION ${PLUGIN_INSTALL_DIR})
install( FILES layersplit.rc DESTINATION ${DATA_INSTALL_DIR}/kritaplugins)
install( FILES kritalayersplit.desktop DESTINATION ${SERVICES_INSTALL_DIR}/calligra)
/*
* dlg_layersplit.cc - part of KimageShop^WKrayon^WKrita
*
* Copyright (c) 2009 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
*
* 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 "dlg_layersplit.h"
#include <klocale.h>
#include <kis_debug.h>
#include <KisViewManager.h>
#include <kis_image.h>
#include <kis_paint_device.h>
#include "kis_slider_spin_box.h"
#include <QCheckBox>
#include <QSpinBox>
#include <kis_config.h>
DlgLayerSplit::DlgLayerSplit()
: KDialog()
{
m_page = new WdgLayerSplit(this);
setCaption(i18n("Split Layer"));
setButtons(Apply | Cancel);
setDefaultButton(Apply);
m_page->intFuzziness->setRange(0, 200);
m_page->intFuzziness->setSingleStep(1);
KisConfig cfg;
m_page->intFuzziness->setValue(cfg.readEntry<int>("layersplit/fuzziness", 20));
m_page->chkCreateGroupLayer->setChecked(cfg.readEntry<bool>("layerspit/createmastergroup", true));
m_page->chkSeparateGroupLayers->setChecked(cfg.readEntry<bool>("layerspit/separategrouplayers", false));
m_page->chkAlphaLock->setChecked(cfg.readEntry<bool>("layerspit/alphalock", true));
m_page->chkHideOriginal->setChecked(cfg.readEntry<bool>("layerspit/hideoriginal", false));
m_page->chkSortLayers->setChecked(cfg.readEntry<bool>("layerspit/sortlayers", true));
m_page->chkDisregardOpacity->setChecked(cfg.readEntry<bool>("layerspit/disregardopacity", true));
connect(this, SIGNAL(applyClicked()), this, SLOT(applyClicked()));
setMainWidget(m_page);
}
DlgLayerSplit::~DlgLayerSplit()
{
}
void DlgLayerSplit::applyClicked()
{
KisConfig cfg;
cfg.writeEntry("layersplit/fuzziness", m_page->intFuzziness->value());
cfg.writeEntry("layerspit/createmastergroup", m_page->chkCreateGroupLayer->isChecked());
cfg.writeEntry("layerspit/separategrouplayers", m_page->chkSeparateGroupLayers->isChecked());
cfg.writeEntry("layerspit/alphalock", m_page->chkAlphaLock->isChecked());
cfg.writeEntry("layerspit/hideoriginal", m_page->chkHideOriginal->isChecked());
cfg.writeEntry("layerspit/sortlayers", m_page->chkSortLayers->isChecked());
cfg.writeEntry("layerspit/disregardopacity", m_page->chkDisregardOpacity->isChecked());
accept();
}
bool DlgLayerSplit::createBaseGroup() const
{
return m_page->chkCreateGroupLayer->isChecked();
}
bool DlgLayerSplit::createSeparateGroups() const
{
return m_page->chkSeparateGroupLayers->isChecked();
}
bool DlgLayerSplit::lockAlpha() const
{
return m_page->chkAlphaLock->isChecked();
}
bool DlgLayerSplit::hideOriginal() const
{
return m_page->chkHideOriginal->isChecked();
}
bool DlgLayerSplit::sortLayers() const
{
return m_page->chkSortLayers->isChecked();
}
bool DlgLayerSplit::disregardOpacity() const
{
return m_page->chkDisregardOpacity->isChecked();
}
int DlgLayerSplit::fuzziness() const
{
return m_page->intFuzziness->value();
}
#include "dlg_layersplit.moc"
/*
* dlg_layersplit.h -- part of KimageShop^WKrayon^WKrita
*
* Copyright (c) 2009 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
*
* 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 DLG_LAYERSPLIT
#define DLG_LAYERSPLIT
#include <kdialog.h>
#include <kis_types.h>
#include "wdg_layersplit.h"
class KisViewManager;
/**
* This dialog allows the user to create a selection mask based
* on a (range of) colors.
*/
class DlgLayerSplit: public KDialog
{
Q_OBJECT
public:
DlgLayerSplit();
~DlgLayerSplit();
bool createBaseGroup() const;
bool createSeparateGroups() const;
bool lockAlpha() const;
bool hideOriginal() const;
bool sortLayers() const;
bool disregardOpacity() const;
int fuzziness() const;
private slots:
void applyClicked();
private:
WdgLayerSplit* m_page;
};
#endif // DLG_LAYERSPLIT
[Desktop Entry]
Name=Layer Splitter
X-KDE-ServiceTypes=Krita/ViewPlugin
Type=Service
X-KDE-Library=kritalayersplit
X-Krita-Version=28
/*
* layersplit.cc -- Part of Krita
*
* Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org)
* Copyright (c) 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
*
* 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 "layersplit.h"
#include <QMap>
#include <QPointer>
#include <QHash>
#include <klocale.h>
#include <kpluginfactory.h>
#include <KoColorSpace.h>
#include <KoChannelInfo.h>
#include <kis_debug.h>
#include <kis_types.h>
#include <KisViewManager.h>
#include <kis_image.h>
#include <kis_action.h>
#include <KisDocument.h>
#include <kis_node.h>
#include <kis_painter.h>
#include <kis_paint_device.h>
#include <kis_paint_layer.h>
#include <kis_group_layer.h>
#include <kis_random_accessor_ng.h>
#include "dlg_layersplit.h"
#include "kis_node_manager.h"
#include "kis_node_commands_adapter.h"
#include "kis_undo_adapter.h"
#include <KoUpdater.h>
#include <KoProgressUpdater.h>
K_PLUGIN_FACTORY(LayerSplitFactory, registerPlugin<LayerSplit>();)
K_EXPORT_PLUGIN(LayerSplitFactory("krita"))
LayerSplit::LayerSplit(QObject *parent, const QVariantList &)
: KisViewPlugin(parent, "kritaplugins/layersplit.rc")
{
KisAction *action = new KisAction(i18n("Split Layer..."), this);
addAction("layersplit", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotLayerSplit()));
}
LayerSplit::~LayerSplit()
{
}
struct Layer {
KoColor color;
KisPaintDeviceSP device;
KisRandomAccessorSP accessor;
int pixelsWritten;
bool operator<(const Layer& other) const
{
return pixelsWritten < other.pixelsWritten;
}
};
void LayerSplit::slotLayerSplit()
{
DlgLayerSplit dlg;
if (dlg.exec() == QDialog::Accepted) {
dlg.hide();
QApplication::setOverrideCursor(Qt::WaitCursor);
KoProgressUpdater* pu = m_view->createProgressUpdater(KoProgressUpdater::Unthreaded);
pu->start(100, i18n("Split into Layers"));
QPointer<KoUpdater> updater = pu->startSubtask();
KisImageSP image = m_view->image();
if (!image) return;
image->lock();
KisNodeSP node = m_view->activeNode();
if (!node) return;
KisPaintDeviceSP projection = node->projection();
if (!projection) return;
QList<Layer> colorMap;
const KoColorSpace *cs = projection->colorSpace();
QRect rc = image->bounds();
int fuzziness = dlg.fuzziness();
updater->setProgress(0);
KisRandomConstAccessorSP acc = projection->createRandomConstAccessorNG(rc.x(), rc.y());
for (int row = rc.y(); row < rc.height(); ++row) {
for (int col = rc.x(); col < rc.width(); ++col) {
acc->moveTo(col, row);
KoColor c(cs);
c.setColor(acc->rawDataConst(), cs);
if (dlg.disregardOpacity()) {
c.setOpacity(OPACITY_OPAQUE_U8);
}
bool found = false;
foreach(const Layer &l, colorMap) {
if (fuzziness == 0) {
found = (l.color == c);
}
else {
quint8 match = cs->difference(l.color.data(), c.data());
found = (match <= fuzziness);
}
if (found) {
KisRandomAccessorSP dstAcc = l.accessor;
dstAcc->moveTo(col, row);
memcpy(dstAcc->rawData(), acc->rawDataConst(), cs->pixelSize());
const_cast<Layer*>(&l)->pixelsWritten++;
break;
}
}
if (!found) {
Layer l;
l.color = c;
l.device = new KisPaintDevice(cs, KoColor::toQString(c));
l.accessor = l.device->createRandomAccessorNG(col, row);
l.accessor->moveTo(col, row);
memcpy(l.accessor->rawData(), acc->rawDataConst(), cs->pixelSize());
l.pixelsWritten = 1;
colorMap << l;
}
}
if (updater->interrupted()) {
return;
}
updater->setProgress((row - rc.y()) * 100 / rc.height() - rc.y());
}
updater->setProgress(100);
qDebug() << "Created" << colorMap.size() << "layers";
// foreach(const Layer &l, colorMap) {
// qDebug() << "\t" << l.device->objectName() << ":" << l.pixelsWritten;
// }
if (dlg.sortLayers()) {
qSort(colorMap);
}
KisUndoAdapter *undo = image->undoAdapter();
undo->beginMacro(kundo2_i18n("Split Layer"));
KisNodeCommandsAdapter adapter(m_view);
KisGroupLayerSP baseGroup = dynamic_cast<KisGroupLayer*>(node->parent().data());
if (!baseGroup) {
// Masks are never nested
baseGroup = dynamic_cast<KisGroupLayer*>(node->parent()->parent().data());
}
if (dlg.hideOriginal()) {
node->setVisible(false);
}
if (dlg.createBaseGroup()) {
KisGroupLayerSP grp = new KisGroupLayer(image, i18n("Color"), OPACITY_OPAQUE_U8);
adapter.addNode(grp, baseGroup, 1);
baseGroup = grp;
}
foreach(const Layer &l, colorMap) {
KisGroupLayerSP grp = baseGroup;
if (dlg.createSeparateGroups()) {
grp = new KisGroupLayer(image, l.device->objectName(), OPACITY_OPAQUE_U8);
adapter.addNode(grp, baseGroup, 1);
}
KisPaintLayerSP paintLayer = new KisPaintLayer(image, l.device->objectName(), OPACITY_OPAQUE_U8, l.device);
adapter.addNode(paintLayer, grp, 0);
paintLayer->setAlphaLocked(dlg.lockAlpha());
}
undo->endMacro();
image->unlock();
image->setModified();
}
QApplication::restoreOverrideCursor();
}
#include "layersplit.moc"
/*
* layersplit.h -- Part of Krita
*
* Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org)
* Copyright (c) 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
*
* 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 LAYERSPLIT_H
#define LAYERSPLIT_H
#include <QVariant>
#include <kurl.h>
#include <kis_view_plugin.h>
class LayerSplit : public KisViewPlugin
{
Q_OBJECT
public:
LayerSplit(QObject *parent, const QVariantList &);
virtual ~LayerSplit();
private slots:
void slotLayerSplit();
};
#endif // LAYERSPLIT_H
<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
<kpartgui library="kritaimagesize" version="6">
<MenuBar>
<Menu name="Layer"><text>&amp;Layer</text>
<Action name="layersplit"/>
</Menu>
</MenuBar>
</kpartgui>
/*
* dlg_layersplit.cc - part of KimageShop^WKrayon^WKrita
*
* Copyright (c) 2009 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
*
* 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 "wdg_layersplit.h"
#include <QPainter>
#include <QStringList>
#include <QImage>
#include <QListWidgetItem>
#include <QDebug>
#include <kcomponentdata.h>
#include "kis_factory2.h"
#include "kis_config.h"
WdgLayerSplit::WdgLayerSplit(QWidget* parent)
: QWidget(parent)
{
setupUi(this);
}
#include "wdg_layersplit.moc"
/*
* wdg_layersplit.h -- part of KimageShop^WKrayon^WKrita
*
* Copyright (c) 2009 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
*
* 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 WDG_LAYERSPLIT_H
#define WDG_LAYERSPLIT_H
#include <QWidget>
#include "ui_wdg_layersplit.h"
class WdgLayerSplit : public QWidget, public Ui::WdgLayerSplit
{
Q_OBJECT
public:
WdgLayerSplit(QWidget* parent);
};
#endif // WDG_LAYERSPLIT_H
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WdgLayerSplit</class>
<widget class="QWidget" name="WdgLayerSplit">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>449</width>
<height>271</height>
</rect>
</property>
<property name="windowTitle">
<string>Image Size</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QCheckBox" name="chkCreateGroupLayer">
<property name="text">
<string>Put all new layers in a group layer</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QCheckBox" name="chkSeparateGroupLayers">
<property name="text">
<string>Put every layer in its own, separate group layer.</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="chkAlphaLock">
<property name="text">
<string>Alpha-lock every new layer</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="chkHideOriginal">
<property name="text">
<string>Hide the original layer</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QCheckBox" name="chkSortLayers">
<property name="text">
<string>Sort layers by amount of non-transparent pixels</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Fuzziness:</string>
</property>
<property name="buddy">
<cstring>intFuzziness</cstring>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="KisSliderSpinBox" name="intFuzziness" native="true"/>
</item>
<item row="5" column="0">
<widget class="QCheckBox" name="chkDisregardOpacity">
<property name="text">
<string>Disregard opacity</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label">
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Split a layer according to color&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Creates a new layer for every color in the active layer.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>