mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-13 01:13:22 +00:00
Minor refactor: Move logic related to pixel perfect to IntertwineAsPixelPerfect
This commit is contained in:
parent
b980e386fa
commit
47c836fde1
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2021 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -26,7 +26,7 @@ namespace app {
|
||||
public:
|
||||
virtual ~Intertwine() { }
|
||||
virtual bool snapByAngle() { return false; }
|
||||
virtual void prepareIntertwine() { }
|
||||
virtual void prepareIntertwine(ToolLoop* loop) { }
|
||||
|
||||
// The given stroke must be relative to the cel origin.
|
||||
virtual void joinStroke(ToolLoop* loop, const Stroke& stroke) = 0;
|
||||
@ -34,6 +34,11 @@ namespace app {
|
||||
|
||||
virtual gfx::Rect getStrokeBounds(ToolLoop* loop, const Stroke& stroke);
|
||||
|
||||
// Special region to force when the modify_tilemap_cel_region()
|
||||
// is called to restore the m_dstTileset from the m_dstImage
|
||||
// in ExpandCelCanvas::validateDestTileset.
|
||||
virtual gfx::Region forceTilemapRegionToValidate() { return gfx::Region(); }
|
||||
|
||||
struct LineData {
|
||||
ToolLoop* loop;
|
||||
Stroke::Pt a, b, pt;
|
||||
|
@ -6,6 +6,7 @@
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#include "base/pi.h"
|
||||
#include "doc/layer_tilemap.h"
|
||||
|
||||
namespace app {
|
||||
namespace tools {
|
||||
@ -94,7 +95,7 @@ class IntertwineAsLines : public Intertwine {
|
||||
public:
|
||||
bool snapByAngle() override { return true; }
|
||||
|
||||
void prepareIntertwine() override {
|
||||
void prepareIntertwine(ToolLoop* loop) override {
|
||||
m_retainedTracePolicyLast = false;
|
||||
m_firstStroke = true;
|
||||
}
|
||||
@ -431,14 +432,48 @@ class IntertwineAsPixelPerfect : public Intertwine {
|
||||
Stroke m_pts;
|
||||
bool m_saveStrokeArea = false;
|
||||
|
||||
// Helper struct to store an image's area that will be affected by the stroke
|
||||
// point at the specified position of the original image.
|
||||
struct SavedArea {
|
||||
doc::ImageRef img;
|
||||
// Original stroke point position.
|
||||
tools::Stroke::Pt pos;
|
||||
// Area of the original image that was saved into img.
|
||||
gfx::Rect r;
|
||||
};
|
||||
// Holds the areas saved by savePointshapeStrokePtArea method and restored by
|
||||
// restoreLastPts method.
|
||||
std::vector<SavedArea> m_savedAreas;
|
||||
// When a SavedArea is restored we add its Rect to this Region, then we use
|
||||
// this to expand the modified region when editing a tilemap manually.
|
||||
gfx::Region m_restoredRegion;
|
||||
// Last point index.
|
||||
int m_lastPti;
|
||||
|
||||
// Temporal tileset with latest changes to be used by pixel perfect only when
|
||||
// modifying a tilemap in Manual mode.
|
||||
std::unique_ptr<Tileset> m_tempTileset;
|
||||
doc::Grid m_grid;
|
||||
|
||||
public:
|
||||
// Useful for Shift+Ctrl+pencil to draw straight lines and snap
|
||||
// angle when "pixel perfect" is selected.
|
||||
bool snapByAngle() override { return true; }
|
||||
|
||||
void prepareIntertwine() override {
|
||||
void prepareIntertwine(ToolLoop* loop) override {
|
||||
m_pts.reset();
|
||||
m_retainedTracePolicyLast = false;
|
||||
m_grid = loop->getGrid();
|
||||
|
||||
if (loop->getLayer()->isTilemap() &&
|
||||
!loop->isTilemapMode() &&
|
||||
loop->isManualTilesetMode()) {
|
||||
const Tileset* srcTileset = static_cast<LayerTilemap*>(loop->getLayer())->tileset();
|
||||
m_tempTileset.reset(Tileset::MakeCopyCopyingImages(srcTileset));
|
||||
}
|
||||
else {
|
||||
m_tempTileset.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void joinStroke(ToolLoop* loop, const Stroke& stroke) override {
|
||||
@ -500,7 +535,7 @@ public:
|
||||
&& (m_pts[c+1].x == m_pts[c].x || m_pts[c+1].y == m_pts[c].y)
|
||||
&& m_pts[c-1].x != m_pts[c+1].x
|
||||
&& m_pts[c-1].y != m_pts[c+1].y) {
|
||||
loop->restoreLastPts(c, m_pts[c]);
|
||||
restoreLastPts(loop, c, m_pts[c]);
|
||||
if (c == nextPt-1)
|
||||
nextPt--;
|
||||
m_pts.erase(c);
|
||||
@ -522,8 +557,8 @@ public:
|
||||
// use it in doTransformPoint.
|
||||
m_saveStrokeArea = (c == m_pts.size() - 1 && !m_retainedTracePolicyLast);
|
||||
if (m_saveStrokeArea) {
|
||||
loop->clearPointshapeStrokePtAreas();
|
||||
loop->setLastPtIndex(c);
|
||||
clearPointshapeStrokePtAreas();
|
||||
setLastPtIndex(c);
|
||||
}
|
||||
doPointshapeStrokePt(m_pts[c], loop);
|
||||
}
|
||||
@ -542,17 +577,172 @@ public:
|
||||
loop, (AlgoHLine)doPointshapeHline);
|
||||
}
|
||||
|
||||
gfx::Region forceTilemapRegionToValidate() override {
|
||||
return m_restoredRegion;
|
||||
}
|
||||
|
||||
protected:
|
||||
void doTransformPoint(const Stroke::Pt& pt, ToolLoop* loop) override {
|
||||
if (m_saveStrokeArea)
|
||||
loop->savePointshapeStrokePtArea(pt);
|
||||
savePointshapeStrokePtArea(loop, pt);
|
||||
|
||||
Intertwine::doTransformPoint(pt, loop);
|
||||
|
||||
if (loop->getLayer()->isTilemap()) {
|
||||
loop->updateTempTileset(pt);
|
||||
updateTempTileset(loop, pt);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void clearPointshapeStrokePtAreas() {
|
||||
m_savedAreas.clear();
|
||||
}
|
||||
|
||||
void setLastPtIndex(const int pti) {
|
||||
m_lastPti = pti;
|
||||
}
|
||||
|
||||
// Saves the destination image's area that will be updated by the point
|
||||
// passed. The idea is to have the state of the image (only the
|
||||
// portion modified by the stroke's point shape) before drawing the last
|
||||
// point of the stroke, then if that point has to be deleted by the
|
||||
// pixel-perfect algorithm, we can use this image to restore the image to the
|
||||
// state previous to the deletion. This method is used by
|
||||
// IntertwineAsPixelPerfect.joinStroke() method.
|
||||
void savePointshapeStrokePtArea(ToolLoop* loop, const tools::Stroke::Pt& pt) {
|
||||
gfx::Rect r;
|
||||
loop->getPointShape()->getModifiedArea(loop, pt.x, pt.y, r);
|
||||
|
||||
gfx::Region rgn(r);
|
||||
// By wrapping the modified area's position when tiled mode is active, the
|
||||
// user can draw outside the canvas and still get the pixel-perfect
|
||||
// effect.
|
||||
loop->getTiledModeHelper().wrapPosition(rgn);
|
||||
loop->getTiledModeHelper().collapseRegionByTiledMode(rgn);
|
||||
|
||||
for (auto a : rgn) {
|
||||
a.offset(-loop->getCelOrigin());
|
||||
|
||||
if (m_tempTileset) {
|
||||
forEachTilePos(
|
||||
loop, m_grid.tilesInCanvasRegion(gfx::Region(a)),
|
||||
[loop](const doc::ImageRef existentTileImage,
|
||||
const gfx::Point tilePos) {
|
||||
loop->getDstImage()->copy(
|
||||
existentTileImage.get(),
|
||||
gfx::Clip(tilePos.x, tilePos.y, 0, 0,
|
||||
existentTileImage.get()->width(),
|
||||
existentTileImage.get()->height()));
|
||||
});
|
||||
}
|
||||
|
||||
ImageRef i(Image::create(loop->getDstImage()->pixelFormat(), a.w, a.h));
|
||||
i->copy(loop->getDstImage(), gfx::Clip(0, 0, a));
|
||||
m_savedAreas.push_back(SavedArea{ i, pt, a });
|
||||
}
|
||||
}
|
||||
|
||||
// Takes the images saved by savePointshapeStrokePtArea and copies them to
|
||||
// the destination image. It restores the destination image because the
|
||||
// images in m_savedAreas are from previous states of the destination
|
||||
// image. This method is used by IntertwineAsPixelPerfect.joinStroke()
|
||||
// method.
|
||||
void restoreLastPts(ToolLoop* loop, const int pti, const tools::Stroke::Pt& pt) {
|
||||
if (m_savedAreas.empty() || pti != m_lastPti || m_savedAreas[0].pos != pt)
|
||||
return;
|
||||
|
||||
m_restoredRegion.clear();
|
||||
|
||||
tools::Stroke::Pt pos;
|
||||
for (int i=0; i<m_savedAreas.size(); ++i) {
|
||||
loop->getDstImage()->copy(
|
||||
m_savedAreas[i].img.get(),
|
||||
gfx::Clip(m_savedAreas[i].r.origin(),
|
||||
m_savedAreas[i].img->bounds()));
|
||||
|
||||
if (m_tempTileset) {
|
||||
auto r = m_savedAreas[i].r;
|
||||
forEachTilePos(
|
||||
loop, m_grid.tilesInCanvasRegion(gfx::Region(r)),
|
||||
[this, i, r](const doc::ImageRef existentTileImage,
|
||||
const gfx::Point tilePos) {
|
||||
existentTileImage->copy(
|
||||
m_savedAreas[i].img.get(),
|
||||
gfx::Clip(r.x - tilePos.x,
|
||||
r.y - tilePos.y,
|
||||
0, 0, r.w, r.h));
|
||||
});
|
||||
}
|
||||
|
||||
m_restoredRegion |= gfx::Region(m_savedAreas[i].r);
|
||||
}
|
||||
}
|
||||
|
||||
void updateTempTileset(ToolLoop* loop, const tools::Stroke::Pt& pt) {
|
||||
if (!m_tempTileset)
|
||||
return;
|
||||
|
||||
gfx::Rect r;
|
||||
loop->getPointShape()->getModifiedArea(loop, pt.x, pt.y, r);
|
||||
|
||||
auto tilesPts = m_grid.tilesInCanvasRegion(gfx::Region(r));
|
||||
forEachTilePos(
|
||||
loop, tilesPts,
|
||||
[loop, r](const doc::ImageRef existentTileImage,
|
||||
const gfx::Point tilePos) {
|
||||
existentTileImage->copy(
|
||||
loop->getDstImage(),
|
||||
gfx::Clip(r.x - tilePos.x,
|
||||
r.y - tilePos.y, r));
|
||||
});
|
||||
|
||||
if (tilesPts.size() > 1) {
|
||||
forEachTilePos(
|
||||
loop, tilesPts,
|
||||
[loop](const doc::ImageRef existentTileImage,
|
||||
const gfx::Point tilePos) {
|
||||
loop->getDstImage()->copy(
|
||||
existentTileImage.get(),
|
||||
gfx::Clip(tilePos.x, tilePos.y, 0, 0,
|
||||
existentTileImage.get()->width(),
|
||||
existentTileImage.get()->height()));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Loops over the points in tilesPts, and for each one calls the provided
|
||||
// processTempTileImage callback passing to it the corresponding temp tile
|
||||
// image and canvas position.
|
||||
void forEachTilePos(ToolLoop* loop,
|
||||
const std::vector<gfx::Point>& tilesPts,
|
||||
const std::function<void(const doc::ImageRef existentTileImage,
|
||||
const gfx::Point tilePos)>& processTempTileImage) {
|
||||
ASSERT(loop->getCel());
|
||||
if (!loop->getCel())
|
||||
return;
|
||||
|
||||
const Image* tilemapImage = loop->getCel()->image();
|
||||
for (const gfx::Point& tilePt : tilesPts) {
|
||||
// Ignore modifications outside the tilemap
|
||||
if (!tilemapImage->bounds().contains(tilePt.x, tilePt.y))
|
||||
continue;
|
||||
|
||||
const doc::tile_t t = tilemapImage->getPixel(tilePt.x, tilePt.y);
|
||||
if (t == doc::notile)
|
||||
continue;
|
||||
|
||||
const doc::tile_index ti = doc::tile_geti(t);
|
||||
const doc::ImageRef existentTileImage = m_tempTileset->get(ti);
|
||||
if (!existentTileImage) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto tilePos = m_grid.tileToCanvas(tilePt);
|
||||
|
||||
processTempTileImage(existentTileImage, tilePos);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace tools
|
||||
|
@ -90,9 +90,13 @@ namespace app {
|
||||
// Returns the layer that will be modified if the tool paints
|
||||
virtual Layer* getLayer() = 0;
|
||||
|
||||
virtual const Cel* getCel() = 0;
|
||||
|
||||
// Returns true if the current mode is TileMap (false = Pixels)
|
||||
virtual bool isTilemapMode() = 0;
|
||||
|
||||
virtual bool isManualTilesetMode() const = 0;
|
||||
|
||||
// Returns the frame where we're paiting
|
||||
virtual frame_t getFrame() = 0;
|
||||
|
||||
@ -254,13 +258,6 @@ namespace app {
|
||||
// Called when the user release the mouse on SliceInk
|
||||
virtual void onSliceRect(const gfx::Rect& bounds) = 0;
|
||||
|
||||
// The following functions are used in pixel perfect mode
|
||||
virtual void clearPointshapeStrokePtAreas() = 0;
|
||||
virtual void setLastPtIndex(const int pti) = 0;
|
||||
virtual void savePointshapeStrokePtArea(const Stroke::Pt& pt) = 0;
|
||||
virtual void restoreLastPts(const int pti, const tools::Stroke::Pt& pt) = 0;
|
||||
virtual void updateTempTileset(const tools::Stroke::Pt& pt) = 0;
|
||||
|
||||
virtual const app::TiledModeHelper& getTiledModeHelper() = 0;
|
||||
};
|
||||
|
||||
|
@ -79,7 +79,7 @@ void ToolLoopManager::prepareLoop(const Pointer& pointer)
|
||||
// Prepare the ink
|
||||
m_toolLoop->getInk()->prepareInk(m_toolLoop);
|
||||
m_toolLoop->getController()->prepareController(m_toolLoop);
|
||||
m_toolLoop->getIntertwine()->prepareIntertwine();
|
||||
m_toolLoop->getIntertwine()->prepareIntertwine(m_toolLoop);
|
||||
m_toolLoop->getPointShape()->preparePointShape(m_toolLoop);
|
||||
}
|
||||
|
||||
|
@ -326,7 +326,7 @@ void BrushPreview::show(const gfx::Point& screenPos)
|
||||
if (loop) {
|
||||
loop->getInk()->prepareInk(loop.get());
|
||||
loop->getController()->prepareController(loop.get());
|
||||
loop->getIntertwine()->prepareIntertwine();
|
||||
loop->getIntertwine()->prepareIntertwine(loop.get());
|
||||
loop->getPointShape()->preparePointShape(loop.get());
|
||||
|
||||
tools::Stroke::Pt pt(brushBounds.x-origBrushBounds.x,
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "app/tools/controller.h"
|
||||
#include "app/tools/freehand_algorithm.h"
|
||||
#include "app/tools/ink.h"
|
||||
#include "app/tools/intertwine.h"
|
||||
#include "app/tools/point_shape.h"
|
||||
#include "app/tools/symmetry.h"
|
||||
#include "app/tools/tool.h"
|
||||
@ -96,6 +97,7 @@ protected:
|
||||
Sprite* m_sprite;
|
||||
Layer* m_layer;
|
||||
frame_t m_frame;
|
||||
TilesetMode m_tilesetMode;
|
||||
RgbMap* m_rgbMap;
|
||||
DocumentPreferences& m_docPref;
|
||||
ToolPreferences& m_toolPref;
|
||||
@ -133,24 +135,6 @@ protected:
|
||||
// given document.
|
||||
gfx::Region m_allVisibleRgn;
|
||||
|
||||
// Helper struct to store an image's area that will be affected by the stroke
|
||||
// point at the specified position of the original image.
|
||||
struct SavedArea {
|
||||
doc::ImageRef img;
|
||||
// Original stroke point position.
|
||||
tools::Stroke::Pt pos;
|
||||
// Area of the original image that was saved into img.
|
||||
gfx::Rect r;
|
||||
};
|
||||
// Holds the areas saved by savePointshapeStrokePtArea method and restored by
|
||||
// restoreLastPts method.
|
||||
std::vector<SavedArea> m_savedAreas;
|
||||
// When a SavedArea is restored we add its Rect to this Region, then we use
|
||||
// this to expand the modified region when editing a tilemap manually.
|
||||
gfx::Region m_restoredRegion;
|
||||
// Last point index.
|
||||
int m_lastPti;
|
||||
|
||||
app::TiledModeHelper m_tiledModeHelper;
|
||||
|
||||
public:
|
||||
@ -166,6 +150,7 @@ public:
|
||||
, m_sprite(site.sprite())
|
||||
, m_layer(site.layer())
|
||||
, m_frame(site.frame())
|
||||
, m_tilesetMode(site.tilesetMode())
|
||||
, m_rgbMap(nullptr)
|
||||
, m_docPref(Preferences::instance().document(m_document))
|
||||
, m_toolPref(Preferences::instance().tool(m_tool))
|
||||
@ -304,7 +289,9 @@ public:
|
||||
Doc* getDocument() override { return m_document; }
|
||||
Sprite* sprite() override { return m_sprite; }
|
||||
Layer* getLayer() override { return m_layer; }
|
||||
const Cel* getCel() override { return nullptr; }
|
||||
bool isTilemapMode() override { return m_tilesMode; };
|
||||
bool isManualTilesetMode() const override { return m_tilesetMode == TilesetMode::Manual; };
|
||||
frame_t getFrame() override { return m_frame; }
|
||||
RgbMap* getRgbMap() override {
|
||||
if (!m_rgbMap) {
|
||||
@ -438,16 +425,6 @@ public:
|
||||
|
||||
void onSliceRect(const gfx::Rect& bounds) override { }
|
||||
|
||||
void clearPointshapeStrokePtAreas() override { }
|
||||
|
||||
void setLastPtIndex(const int pti) override { }
|
||||
|
||||
void savePointshapeStrokePtArea(const tools::Stroke::Pt& pt) override { }
|
||||
|
||||
void restoreLastPts(const int pti, const tools::Stroke::Pt& pt) override { }
|
||||
|
||||
void updateTempTileset(const tools::Stroke::Pt& pt) override { }
|
||||
|
||||
const app::TiledModeHelper& getTiledModeHelper() override {
|
||||
return m_tiledModeHelper;
|
||||
}
|
||||
@ -489,9 +466,6 @@ class ToolLoopImpl : public ToolLoopBase,
|
||||
std::unique_ptr<ExpandCelCanvas> m_expandCelCanvas;
|
||||
Image* m_floodfillSrcImage;
|
||||
bool m_saveLastPoint;
|
||||
// Temporal tileset with latest changes to be used by pixel perfect only when
|
||||
// modifying a tilemap in Manual mode.
|
||||
std::unique_ptr<Tileset> m_tempTileset;
|
||||
|
||||
public:
|
||||
ToolLoopImpl(Editor* editor,
|
||||
@ -512,7 +486,6 @@ public:
|
||||
ModifyDocument))
|
||||
, m_floodfillSrcImage(nullptr)
|
||||
, m_saveLastPoint(saveLastPoint)
|
||||
, m_tempTileset(nullptr)
|
||||
{
|
||||
if (m_pointShape->isFloodFill()) {
|
||||
if (m_tilesMode) {
|
||||
@ -608,11 +581,6 @@ public:
|
||||
if (m_editor)
|
||||
m_editor->add_observer(this);
|
||||
#endif
|
||||
|
||||
if (m_layer->isTilemap() && site.tilesetMode() == TilesetMode::Manual) {
|
||||
const Tileset* srcTileset = static_cast<LayerTilemap*>(m_layer)->tileset();
|
||||
m_tempTileset.reset(Tileset::MakeCopyCopyingImages(srcTileset));
|
||||
}
|
||||
}
|
||||
|
||||
~ToolLoopImpl() {
|
||||
@ -700,6 +668,7 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
const Cel* getCel() override { return m_expandCelCanvas->getCel(); }
|
||||
const Image* getSrcImage() override { return m_expandCelCanvas->getSourceCanvas(); }
|
||||
const Image* getFloodFillSrcImage() override { return m_floodfillSrcImage; }
|
||||
Image* getDstImage() override { return m_expandCelCanvas->getDestCanvas(); }
|
||||
@ -711,7 +680,8 @@ public:
|
||||
m_expandCelCanvas->validateDestCanvas(rgn);
|
||||
}
|
||||
void validateDstTileset(const gfx::Region& rgn) override {
|
||||
m_expandCelCanvas->validateDestTileset(rgn, m_restoredRegion);
|
||||
m_expandCelCanvas->validateDestTileset(
|
||||
rgn, getIntertwine()->forceTilemapRegionToValidate());
|
||||
}
|
||||
void invalidateDstImage() override {
|
||||
m_expandCelCanvas->invalidateDestCanvas();
|
||||
@ -766,141 +736,8 @@ public:
|
||||
m_internalCancel = true;
|
||||
}
|
||||
|
||||
void clearPointshapeStrokePtAreas() override {
|
||||
m_savedAreas.clear();
|
||||
}
|
||||
|
||||
void setLastPtIndex(const int pti) override {
|
||||
m_lastPti = pti;
|
||||
}
|
||||
|
||||
// Saves the destination image's area that will be updated by the point
|
||||
// passed. The idea is to have the state of the image (only the
|
||||
// portion modified by the stroke's point shape) before drawing the last
|
||||
// point of the stroke, then if that point has to be deleted by the
|
||||
// pixel-perfect algorithm, we can use this image to restore the image to the
|
||||
// state previous to the deletion. This method is used by
|
||||
// IntertwineAsPixelPerfect.joinStroke() method.
|
||||
void savePointshapeStrokePtArea(const tools::Stroke::Pt& pt) override {
|
||||
gfx::Rect r;
|
||||
getPointShape()->getModifiedArea(this, pt.x, pt.y, r);
|
||||
|
||||
gfx::Region rgn(r);
|
||||
// By wrapping the modified area's position when tiled mode is active, the
|
||||
// user can draw outside the canvas and still get the pixel-perfect
|
||||
// effect.
|
||||
m_tiledModeHelper.wrapPosition(rgn);
|
||||
m_tiledModeHelper.collapseRegionByTiledMode(rgn);
|
||||
|
||||
for (auto a : rgn) {
|
||||
a.offset(-m_celOrigin);
|
||||
|
||||
if (m_tempTileset) {
|
||||
forEachTilePos(
|
||||
m_grid.tilesInCanvasRegion(gfx::Region(a)),
|
||||
[this](const doc::ImageRef existentTileImage,
|
||||
const gfx::Point tilePos) {
|
||||
getDstImage()->copy(existentTileImage.get(),
|
||||
gfx::Clip(tilePos.x, tilePos.y, 0, 0,
|
||||
existentTileImage.get()->width(),
|
||||
existentTileImage.get()->height()));
|
||||
});
|
||||
}
|
||||
|
||||
ImageRef i(Image::create(getDstImage()->pixelFormat(), a.w, a.h));
|
||||
i->copy(getDstImage(), gfx::Clip(0, 0, a));
|
||||
m_savedAreas.push_back(SavedArea{ i, pt, a});
|
||||
}
|
||||
}
|
||||
|
||||
// Takes the images saved by savePointshapeStrokePtArea and copies them to
|
||||
// the destination image. It restores the destination image because the
|
||||
// images in m_savedAreas are from previous states of the destination
|
||||
// image. This method is used by IntertwineAsPixelPerfect.joinStroke()
|
||||
// method.
|
||||
void restoreLastPts(const int pti, const tools::Stroke::Pt& pt) override {
|
||||
if (m_savedAreas.empty() || pti != m_lastPti || m_savedAreas[0].pos != pt)
|
||||
return;
|
||||
|
||||
m_restoredRegion.clear();
|
||||
|
||||
tools::Stroke::Pt pos;
|
||||
for (int i=0; i<m_savedAreas.size(); ++i) {
|
||||
getDstImage()->copy(m_savedAreas[i].img.get(),
|
||||
gfx::Clip(m_savedAreas[i].r.origin(),
|
||||
m_savedAreas[i].img->bounds()));
|
||||
|
||||
if (m_tempTileset) {
|
||||
auto r = m_savedAreas[i].r;
|
||||
forEachTilePos(
|
||||
m_grid.tilesInCanvasRegion(gfx::Region(r)),
|
||||
[this, i, r](const doc::ImageRef existentTileImage,
|
||||
const gfx::Point tilePos) {
|
||||
existentTileImage->copy(m_savedAreas[i].img.get(), gfx::Clip(r.x - tilePos.x, r.y - tilePos.y, 0, 0, r.w, r.h));
|
||||
});
|
||||
}
|
||||
|
||||
m_restoredRegion |= gfx::Region(m_savedAreas[i].r);
|
||||
}
|
||||
}
|
||||
|
||||
void updateTempTileset(const tools::Stroke::Pt& pt) override {
|
||||
if (!m_tempTileset)
|
||||
return;
|
||||
|
||||
gfx::Rect r;
|
||||
getPointShape()->getModifiedArea(this, pt.x, pt.y, r);
|
||||
|
||||
auto tilesPts = m_grid.tilesInCanvasRegion(gfx::Region(r));
|
||||
forEachTilePos(
|
||||
tilesPts,
|
||||
[this, r](const doc::ImageRef existentTileImage,
|
||||
const gfx::Point tilePos) {
|
||||
existentTileImage->copy(getDstImage(), gfx::Clip(r.x - tilePos.x, r.y - tilePos.y, r.x, r.y, r.w, r.h));
|
||||
});
|
||||
|
||||
if (tilesPts.size() > 1) {
|
||||
forEachTilePos(
|
||||
tilesPts,
|
||||
[this](const doc::ImageRef existentTileImage,
|
||||
const gfx::Point tilePos) {
|
||||
getDstImage()->copy(existentTileImage.get(), gfx::Clip(tilePos.x, tilePos.y, 0, 0,
|
||||
existentTileImage.get()->width(),
|
||||
existentTileImage.get()->height()));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// Loops over the points in tilesPts, and for each one calls the provided
|
||||
// processTempTileImage callback passing to it the corresponding temp tile
|
||||
// image and canvas position.
|
||||
void forEachTilePos(const std::vector<gfx::Point>& tilesPts,
|
||||
const std::function<void(const doc::ImageRef existentTileImage,
|
||||
const gfx::Point tilePos)>& processTempTileImage) {
|
||||
auto cel = m_expandCelCanvas->getCel();
|
||||
for (const gfx::Point& tilePt : tilesPts) {
|
||||
// Ignore modifications outside the tilemap
|
||||
if (!cel->image()->bounds().contains(tilePt.x, tilePt.y))
|
||||
continue;
|
||||
|
||||
const doc::tile_t t = cel->image()->getPixel(tilePt.x, tilePt.y);
|
||||
if (t == doc::notile)
|
||||
continue;
|
||||
|
||||
const doc::tile_index ti = doc::tile_geti(t);
|
||||
const doc::ImageRef existentTileImage = m_tempTileset->get(ti);
|
||||
if (!existentTileImage) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto tilePos = m_grid.tileToCanvas(tilePt);
|
||||
|
||||
processTempTileImage(existentTileImage, tilePos);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_UI
|
||||
// EditorObserver impl
|
||||
void onScrollChanged(Editor* editor) override { updateAllVisibleRegion(); }
|
||||
|
Loading…
x
Reference in New Issue
Block a user