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:
David Capello 2016-05-04 00:02:56 -03:00
parent 065ad4f1dc
commit 0f3dea233b
8 changed files with 99 additions and 81 deletions

View File

@ -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.

View File

@ -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 {

View File

@ -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 {

View File

@ -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)) {

View File

@ -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;

View File

@ -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)

View File

@ -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);
}
}

View File

@ -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; }