mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-14 13:21:34 +00:00
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:
parent
9fe05a5dd5
commit
54bb39a9d3
@ -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) { }
|
||||
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user