mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-14 13:21:34 +00:00
Make the tiles mode work when cel origin offset != 0,0
Several changes/refactors to make the tilemap tests work, there are still a couple of things that are not working yet (brush preview when tile row != 0, and removing tiles with -1 after the tilemap is expanded).
This commit is contained in:
parent
74c1f6af6b
commit
0901ce15b5
@ -39,34 +39,36 @@ void PatchCel::onExecute()
|
||||
Cel* cel = this->cel();
|
||||
|
||||
gfx::Rect newBounds;
|
||||
gfx::Region copyRegion = m_region;
|
||||
gfx::Region regionInTiles;
|
||||
doc::Grid grid;
|
||||
if (cel->image()->pixelFormat() == IMAGE_TILEMAP) {
|
||||
newBounds = cel->bounds() | m_region.bounds();
|
||||
auto tileset = static_cast<LayerTilemap*>(cel->layer())->tileset();
|
||||
grid = tileset->grid();
|
||||
grid.origin(m_pos);
|
||||
copyRegion = grid.canvasToTile(m_region);
|
||||
regionInTiles = grid.canvasToTile(m_region);
|
||||
}
|
||||
else
|
||||
else {
|
||||
newBounds = cel->bounds() | gfx::Rect(m_region.bounds()).offset(m_pos);
|
||||
|
||||
if (cel->bounds() != newBounds) {
|
||||
executeAndAdd(new CropCel(cel, newBounds));
|
||||
}
|
||||
|
||||
if (cel->image()->pixelFormat() == IMAGE_TILEMAP)
|
||||
if (cel->bounds() != newBounds)
|
||||
executeAndAdd(new CropCel(cel, newBounds));
|
||||
|
||||
if (cel->image()->pixelFormat() == IMAGE_TILEMAP) {
|
||||
executeAndAdd(
|
||||
new CopyRegion(cel->image(),
|
||||
m_patch,
|
||||
copyRegion,
|
||||
regionInTiles,
|
||||
-grid.canvasToTile(cel->position())));
|
||||
else
|
||||
}
|
||||
else {
|
||||
executeAndAdd(
|
||||
new CopyRegion(cel->image(),
|
||||
m_patch,
|
||||
copyRegion,
|
||||
m_region,
|
||||
m_pos - cel->position()));
|
||||
}
|
||||
|
||||
executeAndAdd(new TrimCel(cel));
|
||||
|
||||
|
@ -135,11 +135,22 @@ app::Color Color_new(lua_State* L, int index)
|
||||
}
|
||||
else
|
||||
lua_pop(L, 1);
|
||||
|
||||
// Convert { index } into a Color
|
||||
if (lua_getfield(L, index, "index") != LUA_TNIL) {
|
||||
color = app::Color::fromIndex(lua_tonumber(L, -1));
|
||||
lua_pop(L, 1);
|
||||
return color;
|
||||
}
|
||||
else
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
// raw color into app color
|
||||
else if (!lua_isnone(L, index)) {
|
||||
if (lua_isinteger(L, index) && (index < 0 || lua_isnone(L, index+1))) {
|
||||
doc::color_t docColor = lua_tointeger(L, index);
|
||||
|
||||
// TODO depending on current pixel format?
|
||||
switch (app_get_current_pixel_format()) {
|
||||
case IMAGE_RGB:
|
||||
color = app::Color::fromRgb(doc::rgba_getr(docColor),
|
||||
|
@ -145,7 +145,7 @@ public:
|
||||
}
|
||||
|
||||
void processPixel(int x, int y) {
|
||||
*SimpleInkProcessing<CopyInkProcessing<ImageTraits>, ImageTraits>::m_dstAddress = m_color;
|
||||
*this->m_dstAddress = m_color;
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -96,7 +96,13 @@ public:
|
||||
break;
|
||||
}
|
||||
|
||||
if (loop->getBrush()->type() == doc::kImageBrushType) {
|
||||
// TODO support different ink types for tilemaps (even custom brushes,
|
||||
// and custom inks script-driven)
|
||||
if (loop->getDstImage()->pixelFormat() == IMAGE_TILEMAP) {
|
||||
setProc(new CopyInkProcessing<TilemapTraits>(loop));
|
||||
}
|
||||
// Custom brushes
|
||||
else if (loop->getBrush()->type() == doc::kImageBrushType) {
|
||||
switch (m_type) {
|
||||
case Simple:
|
||||
setProc(get_ink_proc<BrushSimpleInkProcessing>(loop));
|
||||
@ -158,12 +164,7 @@ public:
|
||||
break;
|
||||
}
|
||||
case Copy:
|
||||
if (loop->getDstImage()->pixelFormat() == IMAGE_TILEMAP) {
|
||||
setProc(new CopyInkProcessing<TilemapTraits>(loop));
|
||||
}
|
||||
else {
|
||||
setProc(get_ink_proc<CopyInkProcessing>(loop));
|
||||
}
|
||||
setProc(get_ink_proc<CopyInkProcessing>(loop));
|
||||
break;
|
||||
case LockAlpha:
|
||||
setProc(get_ink_proc<LockAlphaInkProcessing>(loop));
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "app/tools/ink.h"
|
||||
#include "app/tools/tool_loop.h"
|
||||
#include "app/util/wrap_value.h"
|
||||
#include "base/clamp.h"
|
||||
#include "doc/brush.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/sprite.h"
|
||||
@ -24,14 +25,19 @@ namespace tools {
|
||||
using namespace doc;
|
||||
using namespace filters;
|
||||
|
||||
void PointShape::doInkHline(int x1, int y, int x2, ToolLoop* loop)
|
||||
void PointShape::doInkHline(int x1, int y, int x2, ToolLoop* loop,
|
||||
bool adjustCoordinates)
|
||||
{
|
||||
Ink* ink = loop->getInk();
|
||||
TiledMode tiledMode = loop->getTiledMode();
|
||||
const int dstw = loop->getDstImage()->width();
|
||||
const int dsth = loop->getDstImage()->height();
|
||||
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()) {
|
||||
if (adjustCoordinates &&
|
||||
ink->needsCelCoordinates()) {
|
||||
gfx::Point origin = loop->getCelOrigin();
|
||||
x1 -= origin.x;
|
||||
x2 -= origin.x;
|
||||
@ -40,52 +46,49 @@ void PointShape::doInkHline(int x1, int y, int x2, ToolLoop* loop)
|
||||
|
||||
// Tiled in Y axis
|
||||
if (int(tiledMode) & int(TiledMode::Y_AXIS)) {
|
||||
size = loop->getDstImage()->height(); // size = image height
|
||||
size = dsth; // size = image height
|
||||
y = wrap_value(y, size);
|
||||
}
|
||||
else if (y < 0 || y >= loop->getDstImage()->height())
|
||||
else if (y < 0 || y >= dsth) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Tiled in X axis
|
||||
if (int(tiledMode) & int(TiledMode::X_AXIS)) {
|
||||
if (x1 > x2)
|
||||
return;
|
||||
|
||||
size = loop->getDstImage()->width(); // size = image width
|
||||
size = dstw; // size = image width
|
||||
w = x2-x1+1;
|
||||
if (w >= size)
|
||||
loop->getInk()->inkHline(0, y, size-1, loop);
|
||||
ink->inkHline(0, y, size-1, loop);
|
||||
else {
|
||||
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);
|
||||
ink->prepareUForPointShapeWholeScanline(loop, x1);
|
||||
ink->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);
|
||||
ink->prepareUForPointShapeSlicedScanline(loop, true, x1);// true = left slice
|
||||
ink->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);
|
||||
ink->prepareUForPointShapeSlicedScanline(loop, false, x1);// false = right slice
|
||||
ink->inkHline(0, y, w-(size-x)-1, loop);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Clipped in X axis
|
||||
else {
|
||||
if (x1 < 0)
|
||||
x1 = 0;
|
||||
|
||||
if (x2 >= loop->getDstImage()->width())
|
||||
x2 = loop->getDstImage()->width()-1;
|
||||
|
||||
x1 = base::clamp(x1, 0, dstw-1);
|
||||
x2 = base::clamp(x2, 0, dstw-1);
|
||||
if (x2-x1+1 < 1)
|
||||
return;
|
||||
|
||||
loop->getInk()->inkHline(x1, y, x2, loop);
|
||||
ink->inkHline(x1, y, x2, loop);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,8 @@ namespace app {
|
||||
protected:
|
||||
// Calls loop->getInk()->inkHline() function for each horizontal-scanline
|
||||
// that should be drawn (applying the "tiled" mode loop->getTiledMode())
|
||||
static void doInkHline(int x1, int y, int x2, ToolLoop* loop);
|
||||
static void doInkHline(int x1, int y, int x2, ToolLoop* loop,
|
||||
const bool adjustCoordinates = true);
|
||||
};
|
||||
|
||||
} // namespace tools
|
||||
|
@ -48,7 +48,11 @@ public:
|
||||
gfx::Point newPos = grid.canvasToTile(pt.toPoint());
|
||||
|
||||
loop->getInk()->prepareForPointShape(loop, true, newPos.x, newPos.y);
|
||||
doInkHline(newPos.x, newPos.y, newPos.x, loop);
|
||||
doInkHline(
|
||||
newPos.x, newPos.y, newPos.x, loop,
|
||||
// Don't adjust by loop->getCelOrigin() because loop->getGrid()
|
||||
// is already adjusted to the new cel position.
|
||||
false);
|
||||
}
|
||||
|
||||
void getModifiedArea(ToolLoop* loop, int x, int y, Rect& area) override {
|
||||
|
@ -317,6 +317,7 @@ void ExpandCelCanvas::commit()
|
||||
// create the new cmd
|
||||
else if (!regionToPatch->isEmpty()) {
|
||||
if (m_layer->isBackground()) {
|
||||
// TODO support for tilemap backgrounds?
|
||||
ASSERT(m_celImage.get() == m_cel->image());
|
||||
|
||||
m_cmds->executeAndAdd(
|
||||
@ -326,31 +327,36 @@ void ExpandCelCanvas::commit()
|
||||
*regionToPatch,
|
||||
m_bounds.origin()));
|
||||
}
|
||||
else {
|
||||
if (m_tilemapMode == TilemapMode::Tiles) {
|
||||
ASSERT(m_celImage.get() != m_cel->image());
|
||||
else if (m_tilemapMode == TilemapMode::Tiles) {
|
||||
ASSERT(m_celImage.get() != m_cel->image());
|
||||
|
||||
m_cel->data()->setImage(m_celImage, m_layer);
|
||||
gfx::Region regionInCanvas = m_grid.tileToCanvas(regionToPatch);
|
||||
regionToPatch = ®ionInCanvas;
|
||||
m_cmds->executeAndAdd(
|
||||
new cmd::PatchCel(
|
||||
m_cel,
|
||||
m_dstImage.get(),
|
||||
*regionToPatch,
|
||||
m_grid.origin()));
|
||||
}
|
||||
else {
|
||||
ASSERT(m_celImage.get() == m_cel->image());
|
||||
m_cel->data()->setImage(m_celImage, m_layer);
|
||||
gfx::Region regionInCanvas = m_grid.tileToCanvas(*regionToPatch);
|
||||
|
||||
m_cmds->executeAndAdd(
|
||||
new cmd::PatchCel(
|
||||
m_cel,
|
||||
m_dstImage.get(),
|
||||
*regionToPatch,
|
||||
m_bounds.origin()));
|
||||
}
|
||||
EXP_TRACE(" - Tilemap bounds to patch", regionInCanvas.bounds());
|
||||
|
||||
m_cmds->executeAndAdd(
|
||||
new cmd::PatchCel(
|
||||
m_cel,
|
||||
m_dstImage.get(),
|
||||
regionInCanvas,
|
||||
m_grid.origin()));
|
||||
}
|
||||
else {
|
||||
ASSERT(m_celImage.get() == m_cel->image());
|
||||
|
||||
m_cmds->executeAndAdd(
|
||||
new cmd::PatchCel(
|
||||
m_cel,
|
||||
m_dstImage.get(),
|
||||
*regionToPatch,
|
||||
m_bounds.origin()));
|
||||
}
|
||||
}
|
||||
// Restore the original cel image if needed (e.g. no region to
|
||||
// patch on a tilemap)
|
||||
else if (m_celImage.get() != m_cel->image()) {
|
||||
m_cel->data()->setImage(m_celImage, m_layer);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -431,19 +437,21 @@ void ExpandCelCanvas::validateSourceCanvas(const gfx::Region& rgn)
|
||||
getSourceCanvas();
|
||||
|
||||
gfx::Region rgnToValidate;
|
||||
gfx::Point celImagePos;
|
||||
gfx::Point origCelPos;
|
||||
gfx::Point zeroPos;
|
||||
if (m_tilemapMode == TilemapMode::Tiles) {
|
||||
celImagePos = m_grid.canvasToTile(m_cel->position());
|
||||
for (const auto& rc : rgn)
|
||||
rgnToValidate |= gfx::Region(m_grid.canvasToTile(rc));
|
||||
// Position of the tilemap cel inside the m_dstImage tilemap
|
||||
origCelPos = m_grid.canvasToTile(m_origCelPos);
|
||||
rgnToValidate = m_grid.canvasToTile(rgn);
|
||||
}
|
||||
else {
|
||||
origCelPos = m_origCelPos;
|
||||
rgnToValidate = rgn;
|
||||
zeroPos = -m_bounds.origin();
|
||||
}
|
||||
EXP_TRACE(" ->", rgnToValidate.bounds());
|
||||
|
||||
if (m_tilemapMode != TilemapMode::Tiles)
|
||||
rgnToValidate.offset(-m_bounds.origin());
|
||||
rgnToValidate.offset(zeroPos);
|
||||
rgnToValidate.createSubtraction(rgnToValidate, m_validSrcRegion);
|
||||
rgnToValidate.createIntersection(rgnToValidate, gfx::Region(m_srcImage->bounds()));
|
||||
|
||||
@ -452,9 +460,8 @@ void ExpandCelCanvas::validateSourceCanvas(const gfx::Region& rgn)
|
||||
rgnToClear.createSubtraction(
|
||||
rgnToValidate,
|
||||
gfx::Region(m_celImage->bounds()
|
||||
.offset(m_tilemapMode == TilemapMode::Tiles ?
|
||||
celImagePos + m_bounds.origin() : m_origCelPos)
|
||||
.offset(-m_bounds.origin())));
|
||||
.offset(origCelPos)
|
||||
.offset(zeroPos)));
|
||||
for (const auto& rc : rgnToClear)
|
||||
fill_rect(m_srcImage.get(), rc, m_srcImage->maskColor());
|
||||
|
||||
@ -474,8 +481,8 @@ void ExpandCelCanvas::validateSourceCanvas(const gfx::Region& rgn)
|
||||
m_sprite->palette(m_frame),
|
||||
gfx::RectF(0, 0, m_bounds.w, m_bounds.h),
|
||||
gfx::Clip(rc.x, rc.y,
|
||||
rc.x+m_bounds.x-m_origCelPos.x,
|
||||
rc.y+m_bounds.y-m_origCelPos.y, rc.w, rc.h),
|
||||
rc.x+m_bounds.x-origCelPos.x,
|
||||
rc.y+m_bounds.y-origCelPos.y, rc.w, rc.h),
|
||||
255, BlendMode::NORMAL);
|
||||
}
|
||||
}
|
||||
@ -484,12 +491,13 @@ void ExpandCelCanvas::validateSourceCanvas(const gfx::Region& rgn)
|
||||
ASSERT(m_tilemapMode == TilemapMode::Tiles);
|
||||
|
||||
// We can copy the cel image directly
|
||||
for (const auto& rc : rgnToValidate)
|
||||
for (const auto& rc : rgnToValidate) {
|
||||
m_srcImage->copy(
|
||||
m_celImage.get(),
|
||||
gfx::Clip(rc.x, rc.y,
|
||||
rc.x - celImagePos.x,
|
||||
rc.y - celImagePos.y, rc.w, rc.h));
|
||||
rc.x-origCelPos.x,
|
||||
rc.y-origCelPos.y, rc.w, rc.h));
|
||||
}
|
||||
}
|
||||
else {
|
||||
ASSERT(m_celImage->pixelFormat() != IMAGE_TILEMAP ||
|
||||
@ -500,8 +508,8 @@ void ExpandCelCanvas::validateSourceCanvas(const gfx::Region& rgn)
|
||||
m_srcImage->copy(
|
||||
m_celImage.get(),
|
||||
gfx::Clip(rc.x, rc.y,
|
||||
rc.x+m_bounds.x-m_origCelPos.x,
|
||||
rc.y+m_bounds.y-m_origCelPos.y, rc.w, rc.h));
|
||||
rc.x+m_bounds.x-origCelPos.x,
|
||||
rc.y+m_bounds.y-origCelPos.y, rc.w, rc.h));
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -52,10 +52,10 @@ gfx::Rect Grid::tileToCanvas(const gfx::Rect& tileBounds) const
|
||||
return gfx::Rect(pt1, pt2);
|
||||
}
|
||||
|
||||
gfx::Region Grid::tileToCanvas(gfx::Region* tileRgn)
|
||||
gfx::Region Grid::tileToCanvas(const gfx::Region& tileRgn)
|
||||
{
|
||||
gfx::Region canvasRgn;
|
||||
for (const gfx::Rect& rc : *tileRgn) {
|
||||
for (const gfx::Rect& rc : tileRgn) {
|
||||
canvasRgn |= gfx::Region(tileToCanvas(rc));
|
||||
}
|
||||
return canvasRgn;
|
||||
|
@ -47,7 +47,7 @@ namespace doc {
|
||||
// Converts a tile position into a canvas position
|
||||
gfx::Point tileToCanvas(const gfx::Point& tile) const;
|
||||
gfx::Rect tileToCanvas(const gfx::Rect& tileBounds) const;
|
||||
gfx::Region tileToCanvas(gfx::Region* tileRgn);
|
||||
gfx::Region tileToCanvas(const gfx::Region& tileRgn);
|
||||
|
||||
// Converts a canvas position/rectangle into a tile position
|
||||
gfx::Point canvasToTile(const gfx::Point& canvasPoint) const;
|
||||
|
Loading…
x
Reference in New Issue
Block a user