diff --git a/libs/image/lazybrush/kis_lazy_fill_capacity_map.h b/libs/image/lazybrush/kis_lazy_fill_capacity_map.h index b1ddde3f6b..5500193f52 100644 --- a/libs/image/lazybrush/kis_lazy_fill_capacity_map.h +++ b/libs/image/lazybrush/kis_lazy_fill_capacity_map.h @@ -1,180 +1,182 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * 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 __KIS_LAZY_FILL_CAPACITY_MAP_H #define __KIS_LAZY_FILL_CAPACITY_MAP_H #include #include "kis_lazy_fill_graph.h" #include "kis_paint_device.h" #include "kis_types.h" #include "kis_painter.h" #include "kis_random_accessor_ng.h" #include "kis_global.h" class KisLazyFillCapacityMap { typedef KisLazyFillCapacityMap type; typedef typename boost::graph_traits::vertex_descriptor VertexDescriptor; typedef typename boost::graph_traits::edge_descriptor EdgeDescriptor; public: typedef EdgeDescriptor key_type; - typedef float value_type; - typedef const float& reference; + typedef int value_type; + typedef const int& reference; typedef boost::readable_property_map_tag category; KisLazyFillCapacityMap(KisPaintDeviceSP mainImage, KisPaintDeviceSP aLabelImage, KisPaintDeviceSP bLabelImage, KisPaintDeviceSP maskImage, const QRect &boundingRect) : m_mainImage(mainImage), m_aLabelImage(aLabelImage), m_bLabelImage(bLabelImage), m_maskImage(maskImage), m_mainRect(boundingRect), m_aLabelRect(m_aLabelImage->exactBounds() & boundingRect), m_bLabelRect(m_bLabelImage->exactBounds() & boundingRect), m_colorSpace(mainImage->colorSpace()), m_pixelSize(m_colorSpace->pixelSize()), m_graph(m_mainRect, m_aLabelImage->regionExact() & boundingRect, m_bLabelImage->regionExact() & boundingRect) { KIS_ASSERT_RECOVER_NOOP(m_mainImage->colorSpace()->pixelSize() == 1); KIS_ASSERT_RECOVER_NOOP(m_aLabelImage->colorSpace()->pixelSize() == 1); KIS_ASSERT_RECOVER_NOOP(m_bLabelImage->colorSpace()->pixelSize() == 1); const QPoint pt = m_mainRect.topLeft(); m_mainAccessor = m_mainImage->createRandomConstAccessorNG(pt.x(), pt.y()); m_aAccessor = m_aLabelImage->createRandomConstAccessorNG(pt.x(), pt.y()); m_bAccessor = m_bLabelImage->createRandomConstAccessorNG(pt.x(), pt.y()); m_maskAccessor = m_maskImage->createRandomConstAccessorNG(pt.x(), pt.y()); m_srcPixelBuf.resize(m_pixelSize); } int maxCapacity() const { const int k = 2 * (m_mainRect.width() + m_mainRect.height()); return k + 1; } friend value_type get(type &map, const key_type &key) { VertexDescriptor src = source(key, map.m_graph); VertexDescriptor dst = target(key, map.m_graph); if (src.type == VertexDescriptor::NORMAL) { map.m_maskAccessor->moveTo(src.x, src.y); if (*map.m_maskAccessor->rawDataConst()) { return 0; } } if (dst.type == VertexDescriptor::NORMAL) { map.m_maskAccessor->moveTo(dst.x, dst.y); if (*map.m_maskAccessor->rawDataConst()) { return 0; } } bool srcLabelA = src.type == VertexDescriptor::LABEL_A; bool srcLabelB = src.type == VertexDescriptor::LABEL_B; bool dstLabelA = dst.type == VertexDescriptor::LABEL_A; bool dstLabelB = dst.type == VertexDescriptor::LABEL_B; if (srcLabelA || srcLabelB) { std::swap(src, dst); std::swap(srcLabelA, dstLabelA); std::swap(srcLabelB, dstLabelB); } Q_ASSERT(!srcLabelA && !srcLabelB); // TODO: precalculate! const int k = 2 * (map.m_mainRect.width() + map.m_mainRect.height()); - float value = 0.0; + static const int unitValue = 256; + + qreal value = 0.0; if (dstLabelA) { map.m_aAccessor->moveTo(src.x, src.y); const int i0 = *((quint8*)map.m_aAccessor->rawDataConst()); value = i0 / 255.0 * k; } else if (dstLabelB) { map.m_bAccessor->moveTo(src.x, src.y); const int i0 = *((quint8*)map.m_bAccessor->rawDataConst()); value = i0 / 255.0 * k; } else { map.m_mainAccessor->moveTo(src.x, src.y); memcpy(map.m_srcPixelBuf.data(), map.m_mainAccessor->rawDataConst(), map.m_pixelSize); map.m_mainAccessor->moveTo(dst.x, dst.y); //const quint8 diff = map.m_colorSpace->differenceA((quint8*)map.m_srcPixelBuf.data(), map.m_mainAccessor->rawDataConst()); //const quint8 i0 = map.m_colorSpace->intensity8((quint8*)map.m_srcPixelBuf.data()); //const quint8 i1 = map.m_colorSpace->intensity8(map.m_mainAccessor->rawDataConst()); const quint8 i0 = *((quint8*)map.m_srcPixelBuf.data()); const quint8 i1 = *map.m_mainAccessor->rawDataConst(); const quint8 diff = qAbs(i1 - i0); const qreal diffPenalty = qBound(0.0, qreal(diff) / 10.0, 1.0); const qreal intensityPenalty = 1.0 - i1 / 255.0; const qreal totalPenalty = qMax(0.0 * diffPenalty, intensityPenalty); value = 1.0 + k * (1.0 - pow2(totalPenalty)); } - return value; + return value * unitValue; } KisLazyFillGraph& graph() { return m_graph; } private: KisPaintDeviceSP m_mainImage; KisPaintDeviceSP m_aLabelImage; KisPaintDeviceSP m_bLabelImage; KisPaintDeviceSP m_maskImage; QRect m_mainRect; QRect m_aLabelRect; QRect m_bLabelRect; const KoColorSpace *m_colorSpace; int m_pixelSize; KisRandomConstAccessorSP m_mainAccessor; KisRandomConstAccessorSP m_aAccessor; KisRandomConstAccessorSP m_bAccessor; KisRandomConstAccessorSP m_maskAccessor; QByteArray m_srcPixelBuf; KisLazyFillGraph m_graph; }; #endif /* __KIS_LAZY_FILL_CAPACITY_MAP_H */ diff --git a/libs/image/lazybrush/kis_lazy_fill_tools.cpp b/libs/image/lazybrush/kis_lazy_fill_tools.cpp index 6b0121bcda..7524e93a55 100644 --- a/libs/image/lazybrush/kis_lazy_fill_tools.cpp +++ b/libs/image/lazybrush/kis_lazy_fill_tools.cpp @@ -1,179 +1,179 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * 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 "kis_lazy_fill_tools.h" #include #include #include #include #include #include #include // we use a forked version of the algorithm //#include #include "patched_boykov_kolmogorov_max_flow.hpp" #include #include "lazybrush/kis_lazy_fill_graph.h" #include "lazybrush/kis_lazy_fill_capacity_map.h" #include "kis_sequential_iterator.h" #include #include "krita_utils.h" namespace KisLazyFillTools { void normalizeAndInvertAlpha8Device(KisPaintDeviceSP dev, const QRect &rect) { quint8 maxPixel = std::numeric_limits::min(); quint8 minPixel = std::numeric_limits::max(); KritaUtils::applyToAlpha8Device(dev, rect, [&minPixel, &maxPixel](quint8 pixel) { if (pixel > maxPixel) { maxPixel = pixel; } if (pixel < minPixel) { minPixel = pixel; } }); const qreal scale = 255.0 / (maxPixel - minPixel); KritaUtils::filterAlpha8Device(dev, rect, [minPixel, scale](quint8 pixel) { return pow2(255 - quint8((pixel - minPixel) * scale)) / 255; }); } void cutOneWay(const KoColor &color, KisPaintDeviceSP src, KisPaintDeviceSP colorScribble, KisPaintDeviceSP backgroundScribble, KisPaintDeviceSP resultDevice, KisPaintDeviceSP maskDevice, const QRect &boundingRect) { using namespace boost; KIS_ASSERT_RECOVER_RETURN(src->pixelSize() == 1); KIS_ASSERT_RECOVER_RETURN(colorScribble->pixelSize() == 1); KIS_ASSERT_RECOVER_RETURN(backgroundScribble->pixelSize() == 1); KIS_ASSERT_RECOVER_RETURN(maskDevice->pixelSize() == 1); KIS_ASSERT_RECOVER_RETURN(*resultDevice->colorSpace() == *color.colorSpace()); KisLazyFillCapacityMap capacityMap(src, colorScribble, backgroundScribble, maskDevice, boundingRect); KisLazyFillGraph &graph = capacityMap.graph(); std::vector groups(num_vertices(graph)); - std::vector residual_capacity(num_edges(graph), 0); + std::vector residual_capacity(num_edges(graph), 0); std::vector::vertices_size_type> distance_vec(num_vertices(graph), 0); std::vector::edge_descriptor> predecessor_vec(num_vertices(graph)); auto vertexIndexMap = get(boost::vertex_index, graph); typedef KisLazyFillGraph::vertex_descriptor Vertex; Vertex s(Vertex::LABEL_A); Vertex t(Vertex::LABEL_B); float maxFlow = boykov_kolmogorov_max_flow(graph, capacityMap, make_iterator_property_map(&residual_capacity[0], get(boost::edge_index, graph)), get(boost::edge_reverse, graph), make_iterator_property_map(&predecessor_vec[0], vertexIndexMap), make_iterator_property_map(&groups[0], vertexIndexMap), make_iterator_property_map(&distance_vec[0], vertexIndexMap), vertexIndexMap, s, t); Q_UNUSED(maxFlow); KisSequentialIterator dstIt(resultDevice, graph.rect()); KisSequentialIterator mskIt(maskDevice, graph.rect()); const int pixelSize = resultDevice->pixelSize(); do { KisLazyFillGraph::vertex_descriptor v(dstIt.x(), dstIt.y()); long vertex_idx = get(boost::vertex_index, graph, v); default_color_type label = groups[vertex_idx]; if (label == black_color) { memcpy(dstIt.rawData(), color.data(), pixelSize); *mskIt.rawData() = 10 + (int(label) << 4); } } while (dstIt.nextPixel() && mskIt.nextPixel()); } QVector splitIntoConnectedComponents(KisPaintDeviceSP dev, const QRect &boundingRect) { QVector points; const KoColorSpace *cs = dev->colorSpace(); const QRect rect = dev->exactBounds() & boundingRect; if (rect.isEmpty()) return points; /** * Please note that since we modify the device inside * clearNonZeroComponent() call, we must use a *writable* * iterator, for not ending up with a lazy copied old version of a * device. */ KisSequentialIterator dstIt(dev, rect); do { if (cs->opacityU8(dstIt.rawData()) > 0) { const QPoint pt(dstIt.x(), dstIt.y()); points << pt; KisScanlineFill fill(dev, pt, rect); fill.clearNonZeroComponent(); } } while (dstIt.nextPixel()); return points; } KeyStroke::KeyStroke() : isTransparent(false) { } KeyStroke::KeyStroke(KisPaintDeviceSP _dev, const KoColor &_color, bool _isTransparent) : dev(_dev), color(_color), isTransparent(_isTransparent) { } bool operator==(const KeyStroke& t1, const KeyStroke&t2) { return t1.dev == t2.dev && t1.color == t2.color && t1.isTransparent == t2.isTransparent; } }