Fix tile mode glitch with image brushes

This fix solves u,v set calculation when 'image brush' are used.

The cause of this bug is that we considered the set m_u, m_v as constants.

We have several cases when we are working on X axis:
- Scanline entirely contained in a tile, but OUTSIDE of the reference tile.
- Scanline entirely contained in a tile, but INSIDE of the reference tile.
- The left side of a scanline which was sliced (by the tile limit).
- The right side of a scanline which was sliced (by the tile limit).
Each one has its own m_u, so we need to recalculate it before every loop->getInk()->inkHline(...).

We have to take similar considerations when we are working in the Y axis.
We need recalculate m_v according if 'y+scanline.y' is INSIDE or OUTSIDE of the reference tile, each case has its own m_v.

Bug reports:
* https://community.aseprite.org/t/1183
* https://community.aseprite.org/t/4695
This commit is contained in:
Gaspar Capello 2020-02-18 12:54:58 -03:00 committed by David Capello
parent 9fe05a5dd5
commit 54bb39a9d3
5 changed files with 96 additions and 9 deletions

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018 Igara Studio S.A.
// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
@ -102,6 +102,9 @@ namespace app {
// Called for each point shape.
virtual void prepareForPointShape(ToolLoop* loop, bool firstPoint, int x, int y) { }
virtual void prepareVForPointShape(ToolLoop* loop, int y) { }
virtual void prepareUForPointShapeWholeScanline(ToolLoop* loop, int x1) { }
virtual void prepareUForPointShapeSlicedScanline(ToolLoop* loop, bool leftSlice, int x1) { }
};

View File

@ -34,6 +34,9 @@ public:
virtual void processScanline(int x1, int y, int x2, ToolLoop* loop) = 0;
virtual void prepareForStrokes(ToolLoop* loop, Strokes& strokes) { }
virtual void prepareForPointShape(ToolLoop* loop, bool firstPoint, int x, int y) { }
virtual void prepareVForPointShape(ToolLoop* loop, int y) { }
virtual void prepareUForPointShapeWholeScanline(ToolLoop* loop, int x1) { }
virtual void prepareUForPointShapeSlicedScanline(ToolLoop* loop, bool leftSlice, int x1) { }
};
typedef std::unique_ptr<BaseInkProcessing> InkProcessingPtr;
@ -1124,8 +1127,51 @@ public:
void prepareForPointShape(ToolLoop* loop, bool firstPoint, int x, int y) override {
if ((m_brush->pattern() == BrushPattern::ALIGNED_TO_DST && firstPoint) ||
(m_brush->pattern() == BrushPattern::PAINT_BRUSH)) {
m_u = (m_brush->patternOrigin().x - loop->getCelOrigin().x) % m_width;
m_u = ((m_brush->patternOrigin().x % loop->sprite()->width()) - loop->getCelOrigin().x) % m_width;
m_v = ((m_brush->patternOrigin().y % loop->sprite()->height()) - loop->getCelOrigin().y) % m_height;
}
}
void prepareVForPointShape(ToolLoop* loop, int y) override {
if (m_brush->pattern() == doc::BrushPattern::ALIGNED_TO_SRC) {
m_v = (m_brush->patternOrigin().y - loop->getCelOrigin().y) % m_height;
if (m_v < 0) m_v += m_height;
}
else {
int spriteH = loop->sprite()->height();
if (y/spriteH > 0)
// 'y' is outside of the center tile.
m_v = (m_brush->patternOrigin().y + m_height - (y/spriteH) * spriteH) % m_height;
else
// 'y' is inside of the center tile.
m_v = ((m_brush->patternOrigin().y % spriteH) - loop->getCelOrigin().y) % m_height;
}
}
void prepareUForPointShapeWholeScanline(ToolLoop* loop, int x1) override {
if (m_brush->pattern() == doc::BrushPattern::ALIGNED_TO_SRC) {
m_u = (m_brush->patternOrigin().x - loop->getCelOrigin().x) % m_width;
if (m_u < 0) m_u += m_height;
}
else {
m_u = ((m_brush->patternOrigin().x % loop->sprite()->width()) - loop->getCelOrigin().x ) % m_width;
if (x1/loop->sprite()->width() > 0)
m_u = (m_brush->patternOrigin().x + m_width - (x1/loop->sprite()->width()) * loop->sprite()->width()) % m_width;
}
}
void prepareUForPointShapeSlicedScanline(ToolLoop* loop, bool leftSlice, int x1) override {
if (loop->getBrush()->pattern() == doc::BrushPattern::ALIGNED_TO_SRC) {
m_u = (m_brush->patternOrigin().x - loop->getCelOrigin().x) % m_width;
if (m_u < 0) m_u += m_height;
return;
}
else {
if (leftSlice)
m_u = ((m_brush->patternOrigin().x % loop->sprite()->width()) - loop->getCelOrigin().x ) % m_width;
else
m_u = (m_brush->patternOrigin().x + m_width - (x1/loop->sprite()->width() + 1) * loop->sprite()->width()) % m_width;
}
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2019 Igara Studio S.A.
// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -34,6 +34,21 @@ public:
m_proc->prepareForPointShape(loop, firstPoint, x, y);
}
void prepareVForPointShape(ToolLoop* loop, int y) override {
ASSERT(m_proc);
m_proc->prepareVForPointShape(loop, y);
}
void prepareUForPointShapeWholeScanline(ToolLoop* loop, int x1) override {
ASSERT(m_proc);
m_proc->prepareUForPointShapeWholeScanline(loop, x1);
}
void prepareUForPointShapeSlicedScanline(ToolLoop* loop, bool leftSlice, int x1) override {
ASSERT(m_proc);
m_proc->prepareUForPointShapeSlicedScanline(loop, leftSlice, x1);
}
protected:
void setProc(BaseInkProcessing* proc) {
m_proc.reset(proc);

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2020 Igara Studio S.A.
// Copyright (C) 2001-2015 David Capello
//
// This program is distributed under the terms of
@ -13,7 +14,9 @@
#include "app/tools/ink.h"
#include "app/tools/tool_loop.h"
#include "app/util/wrap_value.h"
#include "doc/brush.h"
#include "doc/image.h"
#include "doc/sprite.h"
namespace app {
namespace tools {
@ -53,13 +56,20 @@ void PointShape::doInkHline(int x1, int y, int x2, ToolLoop* loop)
if (w >= size)
loop->getInk()->inkHline(0, y, size-1, loop);
else {
x = x1;
x = wrap_value(x, size);
if (x+w-1 <= size-1)
x = wrap_value(x1, loop->sprite()->width());
if (x+w <= loop->sprite()->width()) {
// Here we asure that tile limit line does not bisect the current
// scanline, i.e. the scanline is enterely contained inside the tile.
loop->getInk()->prepareUForPointShapeWholeScanline(loop, x1);
loop->getInk()->inkHline(x, y, x+w-1, loop);
}
else {
// Here the tile limit line bisect the current scanline.
// So we need to execute TWO times the inkHline function, each one with a different m_u.
loop->getInk()->prepareUForPointShapeSlicedScanline(loop, true, x1);// true = left slice
loop->getInk()->inkHline(x, y, size-1, loop);
loop->getInk()->prepareUForPointShapeSlicedScanline(loop, false, x1);// false = right slice
loop->getInk()->inkHline(0, y, w-(size-x)-1, loop);
}
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
@ -7,6 +7,8 @@
#include "app/util/wrap_point.h"
#include "app/tools/ink.h"
namespace app {
namespace tools {
@ -67,13 +69,24 @@ public:
}
}
if (int(loop->getTiledMode()) & int(TiledMode::X_AXIS)) {
int wrappedPatternOriginX = wrap_value(m_brush->patternOrigin().x, loop->sprite()->width()) % m_brush->bounds().w;
m_brush->setPatternOrigin(gfx::Point(wrappedPatternOriginX, m_brush->patternOrigin().y));
x = wrap_value(x, loop->sprite()->width());
}
if (int(loop->getTiledMode()) & int(TiledMode::Y_AXIS)) {
int wrappedPatternOriginY = wrap_value(m_brush->patternOrigin().y, loop->sprite()->height()) % m_brush->bounds().h;
m_brush->setPatternOrigin(gfx::Point(m_brush->patternOrigin().x, wrappedPatternOriginY));
y = wrap_value(y, loop->sprite()->height());
}
loop->getInk()->prepareForPointShape(loop, m_firstPoint, x, y);
for (auto scanline : *m_compressedImage) {
int u = x+scanline.x;
loop->getInk()->prepareVForPointShape(loop, y+scanline.y);
doInkHline(u, y+scanline.y, u+scanline.w-1, loop);
}
m_firstPoint = false;
}