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:
David Capello 2020-08-14 20:26:10 -03:00
parent 74c1f6af6b
commit 0901ce15b5
10 changed files with 110 additions and 80 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 = &regionInCanvas;
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 {

View File

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

View File

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