File indexing completed on 2024-12-22 04:15:06

0001 /*
0002  * SPDX-FileCopyrightText: 2014 Boudewijn Rempt <boud@valdyas.org>
0003  *
0004  * SPDX-License-Identifier: LGPL-2.0-or-later
0005  */
0006 
0007 #include "layersplit.h"
0008 
0009 #include <QMap>
0010 #include <QPointer>
0011 #include <QHash>
0012 
0013 #include <klocalizedstring.h>
0014 #include <kpluginfactory.h>
0015 
0016 #include <KoColorSpace.h>
0017 #include <KoChannelInfo.h>
0018 #include <KoColor.h>
0019 
0020 #include <kis_debug.h>
0021 #include <kis_types.h>
0022 #include <KisViewManager.h>
0023 #include <kis_image.h>
0024 #include <kis_action.h>
0025 #include <KisDocument.h>
0026 #include <kis_node.h>
0027 #include <kis_painter.h>
0028 #include <kis_paint_device.h>
0029 #include <kis_paint_layer.h>
0030 #include <kis_group_layer.h>
0031 #include <kis_random_accessor_ng.h>
0032 #include "dlg_layersplit.h"
0033 #include "kis_node_manager.h"
0034 #include "kis_node_commands_adapter.h"
0035 #include "kis_undo_adapter.h"
0036 #include <KisImageBarrierLock.h>
0037 #include "kis_selection_mask.h"
0038 #include "kis_layer_utils.h"
0039 
0040 #include <KoUpdater.h>
0041 #include <KoProgressUpdater.h>
0042 #include <KisCursorOverrideLock.h>
0043 
0044 K_PLUGIN_FACTORY_WITH_JSON(LayerSplitFactory, "kritalayersplit.json", registerPlugin<LayerSplit>();)
0045 
0046 LayerSplit::LayerSplit(QObject *parent, const QVariantList &)
0047     : KisActionPlugin(parent)
0048 {
0049     KisAction *action  = createAction("layersplit");
0050     connect(action, SIGNAL(triggered()), this, SLOT(slotLayerSplit()));
0051 }
0052 
0053 LayerSplit::~LayerSplit()
0054 {
0055 }
0056 
0057 
0058 struct Layer {
0059     KoColor color;
0060     KisPaintDeviceSP device;
0061     KisRandomAccessorSP accessor;
0062     int pixelsWritten;
0063 
0064     bool operator<(const Layer& other) const
0065     {
0066         return pixelsWritten < other.pixelsWritten;
0067     }
0068 };
0069 
0070 void LayerSplit::slotLayerSplit()
0071 {
0072     DlgLayerSplit dlg;
0073 
0074     if (dlg.exec() == QDialog::Accepted) {
0075 
0076         bool modeToLayer = !dlg.m_modeToMask;
0077         dlg.hide();
0078 
0079         KisImageSP image = viewManager()->image();
0080         if (!image) return;
0081 
0082         KisNodeSP node = viewManager()->activeNode();
0083         if (!node) return;
0084 
0085         // Convert to paint layer prior to splitting if current node is a colorize mask
0086         if (node->inherits("KisColorizeMask")) {
0087             std::future<KisNodeSP> convertedNode = KisLayerUtils::convertToPaintLayer(image, node);
0088             node = convertedNode.get();
0089         }
0090 
0091         if (!node) return;
0092 
0093         KisImageBarrierLock lock(image);
0094 
0095         KisPaintDeviceSP projection = node->projection();
0096         if (!projection) return;
0097 
0098         QList<Layer> colorMap;
0099 
0100         const KoColorSpace *cs = projection->colorSpace();
0101         QRect rc = image->bounds();
0102 
0103         int fuzziness = dlg.fuzziness();
0104 
0105         KisCursorOverrideLock cursorLock(Qt::WaitCursor);
0106 
0107         QPointer<KoUpdater> updater;
0108         if( modeToLayer){
0109             updater = viewManager()->createUnthreadedUpdater(i18n("Split into Layers"));
0110         }
0111         else {
0112             updater = viewManager()->createUnthreadedUpdater(i18n("Split into Masks"));
0113         }
0114 
0115         updater->setProgress(0);
0116 
0117         KisRandomConstAccessorSP acc = projection->createRandomConstAccessorNG();
0118 
0119         for (int row = rc.y(); row < rc.height(); ++row) {
0120 
0121             for (int col = rc.x(); col < rc.width(); ++col) {
0122 
0123                 acc->moveTo(col, row);
0124 
0125                 KoColor c(cs);
0126                 c.setColor(acc->rawDataConst(), cs);
0127 
0128                 if (c.opacityU8() == OPACITY_TRANSPARENT_U8) {
0129                     continue;
0130                 }
0131 
0132                 if (dlg.disregardOpacity()) {
0133                     c.setOpacity(OPACITY_OPAQUE_U8);
0134                 }
0135 
0136                 bool found = false;
0137                 Q_FOREACH (const Layer &l, colorMap) {
0138                     if (fuzziness == 0) {
0139 
0140                         found = (l.color == c);
0141                     }
0142                     else {
0143                         quint8 match = cs->difference(l.color.data(), c.data());
0144                         found = (match <= fuzziness);
0145                     }
0146                     if (found) {
0147                         KisRandomAccessorSP dstAcc = l.accessor;
0148                         dstAcc->moveTo(col, row);
0149                         memcpy(dstAcc->rawData(), acc->rawDataConst(), cs->pixelSize());
0150                         const_cast<Layer*>(&l)->pixelsWritten++;
0151                         break;
0152                     }
0153                 }
0154 
0155                 if (!found) {
0156                     QString name = "";
0157                     if (dlg.palette()) {
0158                        name = dlg.palette()->getClosestSwatchInfo(c).swatch.name();
0159                     }
0160 
0161                     if (name.toLower() == "untitled" || name.toLower() == "none" || name.toLower() == "") {
0162                         name = KoColor::toQString(c);
0163                     }
0164                     Layer l;
0165                     l.color = c;
0166                     l.device = new KisPaintDevice(cs, name);
0167                     l.accessor = l.device->createRandomAccessorNG();
0168                     l.accessor->moveTo(col, row);
0169                     memcpy(l.accessor->rawData(), acc->rawDataConst(), cs->pixelSize());
0170                     l.pixelsWritten = 1;
0171                     colorMap << l;
0172                 }
0173             }
0174 
0175             if (updater->interrupted()) {
0176                 return;
0177             }
0178 
0179             updater->setProgress((row - rc.y()) * 100 / rc.height() - rc.y());
0180         }
0181 
0182         updater->setProgress(100);
0183 
0184         dbgKrita << "Created" << colorMap.size() << "layers";
0185 //        Q_FOREACH (const Layer &l, colorMap) {
0186 //            dbgKrita << "\t" << l.device->objectName() << ":" << l.pixelsWritten;
0187 //        }
0188 
0189         if (dlg.sortLayers()) {
0190             std::sort(colorMap.begin(), colorMap.end());
0191         }
0192 
0193         KisUndoAdapter *undo = image->undoAdapter();
0194         undo->beginMacro(kundo2_i18n("Split Layer"));
0195         KisNodeCommandsAdapter adapter(viewManager());
0196 
0197         if(modeToLayer){
0198             KisGroupLayerSP baseGroup = dynamic_cast<KisGroupLayer*>(node->parent().data());
0199             if (!baseGroup) {
0200                 // Masks are never nested
0201                 baseGroup = dynamic_cast<KisGroupLayer*>(node->parent()->parent().data());
0202             }
0203 
0204             if (dlg.hideOriginal()) {
0205                 node->setVisible(false);
0206             }
0207 
0208             if (dlg.createBaseGroup()) {
0209                 KisGroupLayerSP grp = new KisGroupLayer(image, i18n("Color"), OPACITY_OPAQUE_U8);
0210                 adapter.addNode(grp, baseGroup, 1);
0211                 baseGroup = grp;
0212             }
0213 
0214             Q_FOREACH (const Layer &l, colorMap) {
0215                 KisGroupLayerSP grp = baseGroup;
0216                 if (dlg.createSeparateGroups()) {
0217                     grp = new KisGroupLayer(image, l.device->objectName(), OPACITY_OPAQUE_U8);
0218                     adapter.addNode(grp, baseGroup, 1);
0219                 }
0220                 KisPaintLayerSP paintLayer = new KisPaintLayer(image, l.device->objectName(), OPACITY_OPAQUE_U8, l.device);
0221                 adapter.addNode(paintLayer, grp, 0);
0222                 paintLayer->setAlphaLocked(dlg.lockAlpha());
0223             }
0224         }
0225         else{
0226             KisLayerSP baseGroup = dynamic_cast<KisLayer*>(node.data());
0227             Q_FOREACH (const Layer &l, colorMap) {
0228                 KisSelectionMaskSP mask = new KisSelectionMask(image);
0229                 mask->setName( l.device->objectName());
0230 
0231                 KisPaintDeviceSP temp = KisPainter::convertToAlphaAsPureAlpha(l.device);
0232                 mask->initSelection(temp , baseGroup);
0233                 adapter.addNode(mask, baseGroup,0);
0234                 mask->setActive(true);
0235             }
0236         }
0237 
0238         undo->endMacro();
0239    }
0240 }
0241 
0242 #include "layersplit.moc"