Warning, file /graphics/krita/libs/image/lazybrush/kis_multiway_cut.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002  *  SPDX-FileCopyrightText: 2016 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_multiway_cut.h"
0008 
0009 #include <KoColorSpaceRegistry.h>
0010 #include <KoColorSpace.h>
0011 #include <KoColor.h>
0012 
0013 #include "KisRegion.h"
0014 #include "kis_paint_device.h"
0015 #include "kis_painter.h"
0016 #include "kis_lazy_fill_tools.h"
0017 #include "kis_sequential_iterator.h"
0018 #include <floodfill/kis_scanline_fill.h>
0019 
0020 
0021 using namespace KisLazyFillTools;
0022 
0023 struct KisMultiwayCut::Private
0024 {
0025     KisPaintDeviceSP src;
0026     KisPaintDeviceSP dst;
0027     KisPaintDeviceSP mask;
0028     QRect boundingRect;
0029 
0030     QVector<KeyStroke> keyStrokes;
0031 
0032     static void maskOutKeyStroke(KisPaintDeviceSP keyStrokeDevice, KisPaintDeviceSP mask, const QRect &boundingRect);
0033 };
0034 
0035 KisMultiwayCut::KisMultiwayCut(KisPaintDeviceSP src,
0036                                KisPaintDeviceSP dst,
0037                                const QRect &boundingRect)
0038     : m_d(new Private)
0039 {
0040     m_d->src = src;
0041     m_d->dst = dst;
0042     m_d->mask = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
0043     m_d->boundingRect = boundingRect;
0044 }
0045 
0046 KisMultiwayCut::~KisMultiwayCut()
0047 {
0048 }
0049 
0050 void KisMultiwayCut::addKeyStroke(KisPaintDeviceSP dev, const KoColor &color)
0051 {
0052     m_d->keyStrokes << KeyStroke(dev, color);
0053 }
0054 
0055 
0056 void KisMultiwayCut::Private::maskOutKeyStroke(KisPaintDeviceSP keyStrokeDevice, KisPaintDeviceSP mask, const QRect &boundingRect)
0057 {
0058     KIS_ASSERT_RECOVER_RETURN(keyStrokeDevice->pixelSize() == 1);
0059     KIS_ASSERT_RECOVER_RETURN(mask->pixelSize() == 1);
0060 
0061     KisRegion region =
0062         keyStrokeDevice->region() &
0063         mask->exactBounds() & boundingRect;
0064 
0065     Q_FOREACH (const QRect &rc, region.rects()) {
0066         KisSequentialIterator dstIt(keyStrokeDevice, rc);
0067         KisSequentialConstIterator mskIt(mask, rc);
0068 
0069         while (dstIt.nextPixel() && mskIt.nextPixel()) {
0070             if (*mskIt.rawDataConst() > 0) {
0071                 *dstIt.rawData() = 0;
0072             }
0073         }
0074     }
0075 }
0076 
0077 bool keyStrokesOrder(const KeyStroke &a, const KeyStroke &b)
0078 {
0079     const bool aTransparent = a.color.opacityU8() == OPACITY_TRANSPARENT_U8;
0080     const bool bTransparent = b.color.opacityU8() == OPACITY_TRANSPARENT_U8;
0081 
0082     if (aTransparent && !bTransparent) return true;
0083     if (!aTransparent && bTransparent) return false;
0084 
0085     const QRect aRect = a.dev->extent();
0086     const QRect bRect = b.dev->extent();
0087 
0088     const int aArea = aRect.width() * aRect.height();
0089     const int bArea = bRect.width() * bRect.height();
0090 
0091     return aArea > bArea;
0092 }
0093 
0094 void KisMultiwayCut::run()
0095 {
0096     KisPaintDeviceSP other(new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()));
0097 
0098     /**
0099      * First sort all the key strokes in a way that all the
0100      * transparent strokes go to the beginning of the list.
0101      *
0102      * This is juat an heuristic: the transparent stroke usually
0103      * represents the background so it is the bigger one. And since
0104      * our algorithm is greedy, we should cover the biggest area
0105      * as fast as possible.
0106      */
0107 
0108     std::stable_sort(m_d->keyStrokes.begin(), m_d->keyStrokes.end(), keyStrokesOrder);
0109 
0110     while (m_d->keyStrokes.size() > 1) {
0111         KeyStroke current = m_d->keyStrokes.takeFirst();
0112 
0113         // if current scribble is empty, it just has no effect
0114         if (current.dev->exactBounds().isEmpty()) continue;
0115 
0116         KisPainter gc(other);
0117 
0118         Q_FOREACH (const KeyStroke &s, m_d->keyStrokes) {
0119             const QRect rc = s.dev->extent() & m_d->boundingRect;
0120             gc.bitBlt(rc.topLeft(), s.dev, rc);
0121         }
0122 
0123         // if other is empty, it means that *all* other strokes are
0124         // empty, so there is no reason to continue the process
0125         if (other->exactBounds().isEmpty()) {
0126             m_d->keyStrokes.clear();
0127             m_d->keyStrokes << current;
0128             break;
0129         }
0130 
0131         KisLazyFillTools::cutOneWay(current.color,
0132                                     m_d->src,
0133                                     current.dev,
0134                                     other,
0135                                     m_d->dst,
0136                                     m_d->mask,
0137                                     m_d->boundingRect);
0138 
0139         other->clear();
0140     }
0141 
0142     // TODO: check if one can use the last cut for this purpose!
0143 
0144     if (m_d->keyStrokes.size() == 1) {
0145         KeyStroke current = m_d->keyStrokes.takeLast();
0146 
0147         m_d->maskOutKeyStroke(current.dev, m_d->mask, m_d->boundingRect);
0148 
0149         QVector<QPoint> points =
0150             KisLazyFillTools::splitIntoConnectedComponents(current.dev, m_d->boundingRect);
0151 
0152         Q_FOREACH (const QPoint &pt, points) {
0153             KisScanlineFill fill(m_d->mask, pt, m_d->boundingRect);
0154             fill.fill(current.color, m_d->dst);
0155         }
0156     }
0157 }
0158 
0159 KisPaintDeviceSP KisMultiwayCut::srcDevice() const
0160 {
0161     return m_d->src;
0162 }
0163 
0164 KisPaintDeviceSP KisMultiwayCut::dstDevice() const
0165 {
0166     return m_d->dst;
0167 }