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 }