26#include <QMutexLocker>
30#if KIS_GAP_MAP_MEASURE_ELAPSED_TIME
31#include <QElapsedTimer>
40ALWAYS_INLINE QPoint TransformNone(
int x,
int y,
int xOffset,
int yOffset)
42 return {
x + xOffset,
y + yOffset};
45ALWAYS_INLINE QPoint TransformRotateClockwiseMirrorHorizontally(
int x,
int y,
int xOffset,
int yOffset)
47 return {
x - yOffset,
y - xOffset};
50ALWAYS_INLINE QPoint TransformRotateClockwise(
int x,
int y,
int xOffset,
int yOffset)
52 return {
x - yOffset,
y + xOffset};
55ALWAYS_INLINE QPoint TransformMirrorHorizontally(
int x,
int y,
int xOffset,
int yOffset)
57 return {
x + xOffset,
y - yOffset};
62template<
bool BoundsCheck>
65#if KIS_GAP_MAP_DEBUG_LOGGING_AND_ASSERTS
68 qDebug() <<
"ERROR: opacity at (" << x <<
"," << y <<
") not loaded";
73 if ((x >= 0) && (x <
m_size.width()) && (y >= 0) && (y <
m_size.height())) {
83template<
bool BoundsCheck>
86 return isOpaque<BoundsCheck>(
p.x(),
p.y());
90 const QRect& mapBounds,
91 const FillOpacityFunc& fillOpacityFunc)
93 , m_size(mapBounds.size())
94 , m_numTiles(qCeil(static_cast<float>(m_size.width()) / TileSize),
95 qCeil(static_cast<float>(m_size.height()) / TileSize))
96 , m_fillOpacityFunc(fillOpacityFunc)
101 KIS_ASSERT((mapBounds.x() == 0) && (mapBounds.y() == 0) &&
102 "Gap closing fill assumes x and y start at coordinate (0, 0)");
104 Data defaultPixel {};
115#if KIS_GAP_MAP_MEASURE_ELAPSED_TIME
120 for (
int ty = tileRect.top(); ty <= tileRect.bottom(); ++ty) {
121 for (
int tx = tileRect.left(); tx <= tileRect.right(); ++tx) {
129#if KIS_GAP_MAP_DEBUG_LOGGING_AND_ASSERTS
130 qDebug() <<
"loadOpacityTiles()" <<
rect;
141#if KIS_GAP_MAP_MEASURE_ELAPSED_TIME
142 m_opacityElapsedNanos += timer.nsecsElapsed();
150 for (
int x = x1; x <= x2; ++x) {
151 if (isOpaque<true>(x, y)) {
152 gapDistanceSearch<true>(x, y, TransformNone);
153 gapDistanceSearch<true>(x, y, TransformRotateClockwiseMirrorHorizontally);
154 gapDistanceSearch<true>(x, y, TransformRotateClockwise);
155 gapDistanceSearch<true>(x, y, TransformMirrorHorizontally);
159 for (
int x = x1; x <= x2; ++x) {
160 if (isOpaque<false>(x, y)) {
161 gapDistanceSearch<false>(x, y, TransformNone);
162 gapDistanceSearch<false>(x, y, TransformRotateClockwiseMirrorHorizontally);
163 gapDistanceSearch<false>(x, y, TransformRotateClockwise);
164 gapDistanceSearch<false>(x, y, TransformMirrorHorizontally);
182#if KIS_GAP_MAP_MEASURE_ELAPSED_TIME
198 const bool tileOpaqueTopLeft = (nearbyTilesRect.left() == tile.x()) || (nearbyTilesRect.top() == tile.y()) ?
false : (*
tileFlagsPtr(tile.x() - 1, tile.y() - 1) &
TILE_HAS_OPAQUE_PIXELS) != 0;
199 const bool tileOpaqueBottomLeft = (nearbyTilesRect.left() == tile.x()) || (nearbyTilesRect.bottom() == tile.y()) ?
false : (*
tileFlagsPtr(tile.x() - 1, tile.y() + 1) &
TILE_HAS_OPAQUE_PIXELS) != 0;
203 if (! (tileOpaqueTopLeft || tileOpaqueTop || tileOpaqueLeft || tileOpaque || tileOpaqueBottomLeft || tileOpaqueBottom)) {
207#if KIS_GAP_MAP_MEASURE_ELAPSED_TIME
208 m_distanceElapsedNanos += timer.nsecsElapsed();
223 const int guardBandVertical = qMin(guardBand, 31);
224 const int y1 = tileOpaqueTopLeft || tileOpaqueTop ? qMax(0,
rect.top() - guardBandVertical) :
rect.top();
225 const int y2 = tileOpaqueBottomLeft || tileOpaqueBottom ? qMin(
rect.bottom() + guardBandVertical,
m_size.height() - 1) :
rect.bottom();
226 const int x1Top = tileOpaqueTopLeft ? qMax(0,
rect.left() - guardBand) :
rect.left();
227 const int x1Middle = tileOpaqueLeft ? qMax(0,
rect.left() - guardBand) :
rect.left();
228 const int x1Bottom = tileOpaqueBottomLeft ? qMax(0,
rect.left() - guardBand) :
rect.left();
229 const int x2Top = tileOpaqueTop ?
rect.right() :
rect.left() - 1;
230 const int x2Middle = tileOpaque ?
rect.right() :
rect.left() - 1;
231 const int x2Bottom = tileOpaqueBottom ?
rect.right() :
rect.left() - 1;
234 const bool boundsCheck =
243 for (
int y = y1; y <=
rect.top() - 1; ++y) {
247 for (
int y =
rect.top(); y <=
rect.bottom(); ++y) {
251 for (
int y =
rect.bottom() + 1; y <= y2; ++y) {
255#if KIS_GAP_MAP_MEASURE_ELAPSED_TIME
256 m_distanceElapsedNanos += timer.nsecsElapsed();
280template<
bool BoundsCheck,
typename CoordinateTransform>
283 if (isOpaque<BoundsCheck>(op(x, y, 0, -1)) ||
284 isOpaque<BoundsCheck>(op(x, y, 1, -1))) {
288 for (
int yoffs = 2; yoffs <
m_gapSize + 2; ++yoffs) {
289 const int yDistanceSq = (yoffs - 1) * (yoffs - 1);
291 for (
int xoffs = 0; xoffs <= yoffs; ++xoffs) {
292 const int offsetDistance = yDistanceSq + xoffs * xoffs;
298 if (isOpaque<BoundsCheck>(op(x, y, xoffs, -yoffs))) {
299 const float dx =
static_cast<float>(xoffs) / (yoffs - 1);
303 for (
int cy = 1; cy < yoffs; ++cy) {
307 if (
static_cast<int>(tx) > cx) {
336#if KIS_GAP_MAP_DEBUG_LOGGING_AND_ASSERTS
337 qDebug() <<
"lazyDistance() at (" << x <<
"," << y <<
")";
344 const QPoint topLeft(qMax(0, tx - 1),
346 const QPoint bottomRight(qMin(tx + 1,
m_numTiles.width() - 1),
348 const QRect nearbyTiles = QRect(topLeft, bottomRight);
PythonPluginManager * instance
const QSize m_numTiles
Map size in tiles.
@ TILE_OPACITY_LOADED
Opacity data is available.
@ TILE_HAS_OPAQUE_PIXELS
Some pixels of the loaded tile are opaque.
@ TILE_DISTANCE_LOADED
Distance data is available.
const FillOpacityFunc m_fillOpacityFunc
A callback to get the opacity data from the fill class.
std::unique_ptr< KisTileOptimizedAccessor > m_accessor
An accessor for the paint device.
KisGapMap(int gapSize, const QRect &mapBounds, const FillOpacityFunc &fillOpacityFunc)
void updateDistance(const QPoint &globalPosition, quint16 newDistance)
const int m_gapSize
Gap size in pixels for this map.
ALWAYS_INLINE bool isOpaque(int x, int y)
void distanceSearchRowInnerLoop(bool boundsCheck, int y, int x1, int x2)
const QSize m_size
Size in pixels of the opacity/gap map.
QPoint m_tilePosition
The position of the currently computed tile compared to the whole region.
ALWAYS_INLINE Data * dataPtr(int x, int y)
static constexpr quint16 DISTANCE_INFINITE
static constexpr int TileSize
ALWAYS_INLINE TileFlags * tileFlagsPtr(int tileX, int tileY)
quint16 lazyDistance(int x, int y)
KisPaintDeviceSP m_deviceSp
A 32-bit per pixel paint device that holds the distance and other data.
void loadOpacityTiles(const QRect &tileRect)
void gapDistanceSearch(int x, int y, CoordinateTransform op)
Data * m_tileDataPtr
The pointer to the currently computed tile data.
void loadDistanceTile(const QPoint &tile, const QRect &nearbyTilesRect, int guardBand)
void setDefaultPixel(const KoColor &defPixel)
void fill(const QRect &rc, const KoColor &color)
#define KIS_SAFE_ASSERT_RECOVER(cond)
const quint8 MAX_SELECTED
const quint8 MIN_SELECTED
static KoColorSpaceRegistry * instance()