mirror of
https://github.com/aseprite/aseprite.git
synced 2025-02-21 12:40:34 +00:00
Fix problems using paint bucket referring to all layers and cels origin != 0,0
With this change we've simplified several portions of the ToolLoop code where the "cel origin" is added and then subtracted needlessly.
This commit is contained in:
parent
065ad4f1dc
commit
0f3dea233b
@ -66,6 +66,11 @@ namespace app {
|
||||
// Returns true if this ink is used to mark slices
|
||||
virtual bool isSlice() const { return false; }
|
||||
|
||||
// Returns true if inkHline() needs source cel coordinates
|
||||
// instead of sprite coordinates (i.e. relative to
|
||||
// ToolLoop::getCelOrigin()).
|
||||
virtual bool needsCelCoordinates() const { return true; }
|
||||
|
||||
// Returns true if this ink needs a special source area. For
|
||||
// example, blur tool needs one extra pixel to all sides of the
|
||||
// modified area, so it can use a 3x3 convolution matrix.
|
||||
|
@ -173,6 +173,8 @@ public:
|
||||
Ink* clone() override { return new SliceInk(*this); }
|
||||
|
||||
bool isSlice() const override { return true; }
|
||||
bool needsCelCoordinates() const override { return false; }
|
||||
|
||||
void prepareInk(ToolLoop* loop) override { }
|
||||
void inkHline(int x1, int y, int x2, ToolLoop* loop) override {
|
||||
// TODO show the selection-preview with a XOR color or something like that
|
||||
@ -319,21 +321,23 @@ public:
|
||||
Ink* clone() override { return new SelectionInk(*this); }
|
||||
|
||||
bool isSelection() const override { return true; }
|
||||
bool needsCelCoordinates() const override {
|
||||
return (m_modify_selection ? false: true);
|
||||
}
|
||||
|
||||
void inkHline(int x1, int y, int x2, ToolLoop* loop) override {
|
||||
if (m_modify_selection) {
|
||||
int modifiers = int(loop->getModifiers());
|
||||
Point origin = loop->getCelOrigin();
|
||||
|
||||
if ((modifiers & (int(ToolLoopModifiers::kReplaceSelection) |
|
||||
int(ToolLoopModifiers::kAddSelection))) != 0) {
|
||||
m_mask.add(gfx::Rect(x1+origin.x, y+origin.y, x2-x1+1, 1));
|
||||
m_mask.add(gfx::Rect(x1, y, x2-x1+1, 1));
|
||||
}
|
||||
else if ((modifiers & int(ToolLoopModifiers::kSubtractSelection)) != 0) {
|
||||
m_mask.subtract(gfx::Rect(x1+origin.x, y+origin.y, x2-x1+1, 1));
|
||||
m_mask.subtract(gfx::Rect(x1, y, x2-x1+1, 1));
|
||||
}
|
||||
|
||||
m_maxBounds |= gfx::Rect(x1+origin.x, y+origin.y, x2-x1+1, 1);
|
||||
m_maxBounds |= gfx::Rect(x1, y, x2-x1+1, 1);
|
||||
}
|
||||
// TODO show the selection-preview with a XOR color or something like that
|
||||
else {
|
||||
|
@ -27,12 +27,10 @@ void Intertwine::doPointshapePoint(int x, int y, ToolLoop* loop)
|
||||
{
|
||||
Symmetry* symmetry = loop->getSymmetry();
|
||||
if (symmetry) {
|
||||
Point origin(loop->getCelOrigin());
|
||||
|
||||
// Convert the point to the sprite position so we can apply the
|
||||
// symmetry transformation.
|
||||
Stroke main_stroke;
|
||||
main_stroke.addPoint(Point(x, y) + origin);
|
||||
main_stroke.addPoint(Point(x, y));
|
||||
|
||||
Strokes strokes;
|
||||
symmetry->generateStrokes(main_stroke, strokes, loop);
|
||||
@ -40,9 +38,7 @@ void Intertwine::doPointshapePoint(int x, int y, ToolLoop* loop)
|
||||
// We call transformPoint() moving back each point to the cel
|
||||
// origin.
|
||||
loop->getPointShape()->transformPoint(
|
||||
loop,
|
||||
stroke[0].x - origin.x,
|
||||
stroke[0].y - origin.y);
|
||||
loop, stroke[0].x, stroke[0].y);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -27,13 +27,22 @@ void PointShape::doInkHline(int x1, int y, int x2, ToolLoop* loop)
|
||||
TiledMode tiledMode = loop->getTiledMode();
|
||||
int x, w, size; // width or height
|
||||
|
||||
// In case the ink needs original cel coordinates, we have to
|
||||
// translate the x1/y/x2 coordinate.
|
||||
if (loop->getInk()->needsCelCoordinates()) {
|
||||
gfx::Point origin = loop->getCelOrigin();
|
||||
x1 -= origin.x;
|
||||
x2 -= origin.x;
|
||||
y -= origin.y;
|
||||
}
|
||||
|
||||
// Tiled in Y axis
|
||||
if (int(tiledMode) & int(TiledMode::Y_AXIS)) {
|
||||
size = loop->getDstImage()->height(); // size = image height
|
||||
y = wrap_value(y, size);
|
||||
}
|
||||
else if (y < 0 || y >= loop->getDstImage()->height())
|
||||
return;
|
||||
return;
|
||||
|
||||
// Tiled in X axis
|
||||
if (int(tiledMode) & int(TiledMode::X_AXIS)) {
|
||||
|
@ -52,14 +52,14 @@ public:
|
||||
if (m_brush->type() == kImageBrushType) {
|
||||
if (m_brush->pattern() == BrushPattern::ALIGNED_TO_DST ||
|
||||
m_brush->pattern() == BrushPattern::PAINT_BRUSH) {
|
||||
m_brush->setPatternOrigin(gfx::Point(x, y)+loop->getCelOrigin());
|
||||
m_brush->setPatternOrigin(gfx::Point(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (m_brush->type() == kImageBrushType &&
|
||||
m_brush->pattern() == BrushPattern::PAINT_BRUSH) {
|
||||
m_brush->setPatternOrigin(gfx::Point(x, y)+loop->getCelOrigin());
|
||||
m_brush->setPatternOrigin(gfx::Point(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,13 +98,8 @@ public:
|
||||
|
||||
private:
|
||||
gfx::Rect floodfillBounds(ToolLoop* loop, int x, int y) const {
|
||||
gfx::Point origin = loop->getCelOrigin();
|
||||
gfx::Rect bounds(-origin.x, -origin.y,
|
||||
loop->sprite()->width(),
|
||||
loop->sprite()->height());
|
||||
|
||||
bounds = bounds.createIntersection(
|
||||
loop->getFloodFillSrcImage()->bounds());
|
||||
gfx::Rect bounds = loop->sprite()->bounds();
|
||||
bounds &= loop->getFloodFillSrcImage()->bounds();
|
||||
|
||||
// Limit the flood-fill to the current tile if the grid is visible.
|
||||
if (loop->getStopAtGrid()) {
|
||||
@ -112,8 +107,8 @@ private:
|
||||
if (!grid.isEmpty()) {
|
||||
div_t d, dx, dy;
|
||||
|
||||
dx = div(grid.x-origin.x, grid.w);
|
||||
dy = div(grid.y-origin.y, grid.h);
|
||||
dx = div(grid.x, grid.w);
|
||||
dy = div(grid.y, grid.h);
|
||||
|
||||
if (dx.rem > 0) dx.rem -= grid.w;
|
||||
if (dy.rem > 0) dy.rem -= grid.h;
|
||||
|
@ -188,9 +188,6 @@ void ToolLoopManager::doLoopStep(bool last_step)
|
||||
|
||||
m_toolLoop->validateDstImage(m_dirtyArea);
|
||||
|
||||
// Move the stroke to be relative to the cel origin.
|
||||
main_stroke.offset(-m_toolLoop->getCelOrigin());
|
||||
|
||||
// Join or fill user points
|
||||
if (!m_toolLoop->getFilled() || (!last_step && !m_toolLoop->getPreviewFilled()))
|
||||
m_toolLoop->getIntertwine()->joinStroke(m_toolLoop, main_stroke);
|
||||
@ -229,8 +226,6 @@ void ToolLoopManager::calculateDirtyArea(const Strokes& strokes)
|
||||
// Start with a fresh dirty area
|
||||
m_dirtyArea.clear();
|
||||
|
||||
const Point celOrigin = m_toolLoop->getCelOrigin();
|
||||
|
||||
for (auto& stroke : strokes) {
|
||||
gfx::Rect strokeBounds = stroke.bounds();
|
||||
if (strokeBounds.isEmpty())
|
||||
@ -241,20 +236,17 @@ void ToolLoopManager::calculateDirtyArea(const Strokes& strokes)
|
||||
|
||||
m_toolLoop->getPointShape()->getModifiedArea(
|
||||
m_toolLoop,
|
||||
strokeBounds.x - celOrigin.x,
|
||||
strokeBounds.y - celOrigin.y, r1);
|
||||
strokeBounds.x,
|
||||
strokeBounds.y, r1);
|
||||
|
||||
m_toolLoop->getPointShape()->getModifiedArea(
|
||||
m_toolLoop,
|
||||
strokeBounds.x+strokeBounds.w-1 - celOrigin.x,
|
||||
strokeBounds.y+strokeBounds.h-1 - celOrigin.y, r2);
|
||||
strokeBounds.x+strokeBounds.w-1,
|
||||
strokeBounds.y+strokeBounds.h-1, r2);
|
||||
|
||||
m_dirtyArea.createUnion(m_dirtyArea, Region(r1.createUnion(r2)));
|
||||
}
|
||||
|
||||
// Make the dirty area relative to the sprite.
|
||||
m_dirtyArea.offset(celOrigin);
|
||||
|
||||
// Merge new dirty area with the previous one (for tools like line
|
||||
// or rectangle it's needed to redraw the previous position and
|
||||
// the new one)
|
||||
|
@ -209,7 +209,9 @@ void BrushPreview::show(const gfx::Point& screenPos)
|
||||
loop->getIntertwine()->prepareIntertwine();
|
||||
loop->getPointShape()->preparePointShape(loop);
|
||||
loop->getPointShape()->transformPoint(
|
||||
loop, -origBrushBounds.x, -origBrushBounds.y);
|
||||
loop,
|
||||
brushBounds.x-origBrushBounds.x,
|
||||
brushBounds.y-origBrushBounds.y);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -269,7 +269,7 @@ class ToolLoopImpl : public ToolLoopBase {
|
||||
gfx::Point m_maskOrigin;
|
||||
bool m_canceled;
|
||||
Transaction m_transaction;
|
||||
ExpandCelCanvas m_expandCelCanvas;
|
||||
ExpandCelCanvas* m_expandCelCanvas;
|
||||
Image* m_floodfillSrcImage;
|
||||
|
||||
public:
|
||||
@ -294,23 +294,58 @@ public:
|
||||
getInk()->isSlice() ||
|
||||
getInk()->isZoom()) ? DoesntModifyDocument:
|
||||
ModifyDocument))
|
||||
, m_expandCelCanvas(
|
||||
editor->getSite(),
|
||||
layer,
|
||||
m_docPref.tiled.mode(),
|
||||
m_transaction,
|
||||
ExpandCelCanvas::Flags(
|
||||
ExpandCelCanvas::NeedsSource |
|
||||
// If the tool is freehand-like, we can use the modified
|
||||
// region directly as undo information to save the modified
|
||||
// pixels (it's faster than creating a Dirty object).
|
||||
// See ExpandCelCanvas::commit() for details about this flag.
|
||||
(getController()->isFreehand() ?
|
||||
ExpandCelCanvas::UseModifiedRegionAsUndoInfo:
|
||||
ExpandCelCanvas::None)))
|
||||
, m_expandCelCanvas(nullptr)
|
||||
, m_floodfillSrcImage(nullptr)
|
||||
{
|
||||
ASSERT(m_context->activeDocument() == m_editor->document());
|
||||
|
||||
if (m_pointShape->isFloodFill()) {
|
||||
// Prepare a special image for floodfill when it's configured to
|
||||
// stop using all visible layers.
|
||||
if (m_toolPref.floodfill.referTo() == gen::FillReferTo::ALL_LAYERS) {
|
||||
m_floodfillSrcImage = Image::create(m_sprite->pixelFormat(),
|
||||
m_sprite->width(),
|
||||
m_sprite->height());
|
||||
|
||||
m_floodfillSrcImage->clear(m_sprite->transparentColor());
|
||||
|
||||
render::Render().renderSprite(
|
||||
m_floodfillSrcImage,
|
||||
m_sprite,
|
||||
m_frame,
|
||||
gfx::Clip(m_sprite->bounds()),
|
||||
render::Zoom(1, 1));
|
||||
}
|
||||
else {
|
||||
Cel* cel = m_layer->cel(m_frame);
|
||||
if (cel && (cel->x() != 0 || cel->y() != 0)) {
|
||||
m_floodfillSrcImage = Image::create(m_sprite->pixelFormat(),
|
||||
m_sprite->width(),
|
||||
m_sprite->height());
|
||||
m_floodfillSrcImage->clear(m_sprite->transparentColor());
|
||||
copy_image(m_floodfillSrcImage, cel->image(), cel->x(), cel->y());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_expandCelCanvas = new ExpandCelCanvas(
|
||||
editor->getSite(),
|
||||
layer,
|
||||
m_docPref.tiled.mode(),
|
||||
m_transaction,
|
||||
ExpandCelCanvas::Flags(
|
||||
ExpandCelCanvas::NeedsSource |
|
||||
// If the tool is freehand-like, we can use the modified
|
||||
// region directly as undo information to save the modified
|
||||
// pixels (it's faster than creating a Dirty object).
|
||||
// See ExpandCelCanvas::commit() for details about this flag.
|
||||
(getController()->isFreehand() ?
|
||||
ExpandCelCanvas::UseModifiedRegionAsUndoInfo:
|
||||
ExpandCelCanvas::None)));
|
||||
|
||||
if (!m_floodfillSrcImage)
|
||||
m_floodfillSrcImage = const_cast<Image*>(getSrcImage());
|
||||
|
||||
// Settings
|
||||
switch (tool->getFill(m_button)) {
|
||||
case tools::FillNone:
|
||||
@ -341,37 +376,17 @@ public:
|
||||
m_transaction.execute(new cmd::SetMask(m_document, &emptyMask));
|
||||
}
|
||||
|
||||
m_celOrigin = m_expandCelCanvas.getCel()->position();
|
||||
m_celOrigin = m_expandCelCanvas->getCel()->position();
|
||||
m_mask = m_document->mask();
|
||||
m_maskOrigin = (!m_mask->isEmpty() ? gfx::Point(m_mask->bounds().x-m_celOrigin.x,
|
||||
m_mask->bounds().y-m_celOrigin.y):
|
||||
gfx::Point(0, 0));
|
||||
|
||||
// Prepare a special image for floodfill when it's configured to
|
||||
// stop using all visible layers.
|
||||
if (m_pointShape->isFloodFill() &&
|
||||
m_toolPref.floodfill.referTo() == gen::FillReferTo::ALL_LAYERS) {
|
||||
m_floodfillSrcImage = Image::create(m_sprite->pixelFormat(),
|
||||
m_sprite->width(),
|
||||
m_sprite->height());
|
||||
|
||||
m_floodfillSrcImage->clear(m_sprite->transparentColor());
|
||||
|
||||
render::Render().renderSprite(
|
||||
m_floodfillSrcImage,
|
||||
m_sprite,
|
||||
m_frame,
|
||||
gfx::Clip(m_sprite->bounds()),
|
||||
render::Zoom(1, 1));
|
||||
}
|
||||
else {
|
||||
m_floodfillSrcImage = const_cast<Image*>(getSrcImage());
|
||||
}
|
||||
}
|
||||
|
||||
~ToolLoopImpl() {
|
||||
if (m_floodfillSrcImage != getSrcImage())
|
||||
delete m_floodfillSrcImage;
|
||||
delete m_expandCelCanvas;
|
||||
}
|
||||
|
||||
// IToolLoop interface
|
||||
@ -385,7 +400,7 @@ public:
|
||||
try {
|
||||
ContextReader reader(m_context, 500);
|
||||
ContextWriter writer(reader, 500);
|
||||
m_expandCelCanvas.commit();
|
||||
m_expandCelCanvas->commit();
|
||||
}
|
||||
catch (const LockedDocumentException& ex) {
|
||||
Console::showException(ex);
|
||||
@ -410,7 +425,7 @@ public:
|
||||
try {
|
||||
ContextReader reader(m_context, 500);
|
||||
ContextWriter writer(reader, 500);
|
||||
m_expandCelCanvas.rollback();
|
||||
m_expandCelCanvas->rollback();
|
||||
}
|
||||
catch (const LockedDocumentException& ex) {
|
||||
Console::showException(ex);
|
||||
@ -421,23 +436,23 @@ public:
|
||||
update_screen_for_document(m_document);
|
||||
}
|
||||
|
||||
const Image* getSrcImage() override { return m_expandCelCanvas.getSourceCanvas(); }
|
||||
const Image* getSrcImage() override { return m_expandCelCanvas->getSourceCanvas(); }
|
||||
const Image* getFloodFillSrcImage() override { return m_floodfillSrcImage; }
|
||||
Image* getDstImage() override { return m_expandCelCanvas.getDestCanvas(); }
|
||||
Image* getDstImage() override { return m_expandCelCanvas->getDestCanvas(); }
|
||||
void validateSrcImage(const gfx::Region& rgn) override {
|
||||
m_expandCelCanvas.validateSourceCanvas(rgn);
|
||||
m_expandCelCanvas->validateSourceCanvas(rgn);
|
||||
}
|
||||
void validateDstImage(const gfx::Region& rgn) override {
|
||||
m_expandCelCanvas.validateDestCanvas(rgn);
|
||||
m_expandCelCanvas->validateDestCanvas(rgn);
|
||||
}
|
||||
void invalidateDstImage() override {
|
||||
m_expandCelCanvas.invalidateDestCanvas();
|
||||
m_expandCelCanvas->invalidateDestCanvas();
|
||||
}
|
||||
void invalidateDstImage(const gfx::Region& rgn) override {
|
||||
m_expandCelCanvas.invalidateDestCanvas(rgn);
|
||||
m_expandCelCanvas->invalidateDestCanvas(rgn);
|
||||
}
|
||||
void copyValidDstToSrcImage(const gfx::Region& rgn) override {
|
||||
m_expandCelCanvas.copyValidDestToSourceCanvas(rgn);
|
||||
m_expandCelCanvas->copyValidDestToSourceCanvas(rgn);
|
||||
}
|
||||
|
||||
bool useMask() override { return m_useMask; }
|
||||
|
Loading…
x
Reference in New Issue
Block a user