Add possibility to paint with tiles

This commit is contained in:
David Capello 2020-06-25 19:21:35 -03:00
parent fbe2f8645d
commit d0c95cf46f
24 changed files with 346 additions and 130 deletions

View File

@ -100,6 +100,9 @@ doc::color_t color_utils::color_for_image(const app::Color& color, PixelFormat f
case IMAGE_INDEXED: case IMAGE_INDEXED:
c = color.getIndex(); c = color.getIndex();
break; break;
case IMAGE_TILEMAP:
c = color.getIndex(); // TODO Add app::Color::getTile() ?
break;
} }
return c; return c;
@ -122,6 +125,9 @@ doc::color_t color_utils::color_for_image_without_alpha(const app::Color& color,
case IMAGE_INDEXED: case IMAGE_INDEXED:
c = color.getIndex(); c = color.getIndex();
break; break;
case IMAGE_TILEMAP:
c = color.getIndex(); // TODO Add app::Color::getTile() ?
break;
} }
return c; return c;
@ -165,6 +171,9 @@ doc::color_t color_utils::color_for_target_mask(const app::Color& color, const C
c = get_current_palette()->findBestfit(r, g, b, a, mask); c = get_current_palette()->findBestfit(r, g, b, a, mask);
} }
break; break;
case IMAGE_TILEMAP:
c = color.getIndex(); // TODO Add app::Color::getTile() ?
break;
} }
} }

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2019 Igara Studio S.A. // Copyright (c) 2019-2020 Igara Studio S.A.
// //
// This program is distributed under the terms of // This program is distributed under the terms of
// the End-User License Agreement for Aseprite. // the End-User License Agreement for Aseprite.
@ -25,12 +25,15 @@ public:
protected: protected:
bool onChecked(Context* context) override { bool onChecked(Context* context) override {
auto colorBar = ColorBar::instance(); auto colorBar = ColorBar::instance();
return colorBar->inTilesMode(); return (colorBar->tilemapMode() == TilemapMode::Tiles);
} }
void onExecute(Context* context) override { void onExecute(Context* context) override {
auto colorBar = ColorBar::instance(); auto colorBar = ColorBar::instance();
colorBar->setTilesMode(!colorBar->inTilesMode()); colorBar->setTilemapMode(
colorBar->tilemapMode() == TilemapMode::Pixels ?
TilemapMode::Tiles:
TilemapMode::Pixels);
} }
}; };

View File

@ -1,4 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2020 Igara Studio S.A.
// Copyright (C) 2001-2016 David Capello // Copyright (C) 2001-2016 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -20,21 +21,29 @@ ExtraCel::ExtraCel()
{ {
} }
void ExtraCel::create(doc::Sprite* sprite, void ExtraCel::create(const TilemapMode tilemapMode,
doc::Sprite* sprite,
const gfx::Rect& bounds, const gfx::Rect& bounds,
doc::frame_t frame, const gfx::Size& imageSize,
int opacity) const doc::frame_t frame,
const int opacity)
{ {
ASSERT(sprite); ASSERT(sprite);
doc::PixelFormat pixelFormat;
if (tilemapMode == TilemapMode::Tiles)
pixelFormat = doc::IMAGE_TILEMAP;
else
pixelFormat = sprite->pixelFormat();
if (!m_image || if (!m_image ||
m_image->pixelFormat() != sprite->pixelFormat() || m_image->pixelFormat() != pixelFormat ||
m_image->width() != bounds.w || m_image->width() != imageSize.w ||
m_image->height() != bounds.h) { m_image->height() != imageSize.h) {
if (!m_imageBuffer) if (!m_imageBuffer)
m_imageBuffer.reset(new doc::ImageBuffer(1)); m_imageBuffer.reset(new doc::ImageBuffer(1));
doc::Image* newImage = doc::Image::create(sprite->pixelFormat(), doc::Image* newImage = doc::Image::create(pixelFormat,
bounds.w, bounds.h, imageSize.w, imageSize.h,
m_imageBuffer); m_imageBuffer);
m_image.reset(newImage); m_image.reset(newImage);
} }

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2019 Igara Studio S.A. // Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -9,6 +9,7 @@
#define APP_EXTRA_CEL_H_INCLUDED #define APP_EXTRA_CEL_H_INCLUDED
#pragma once #pragma once
#include "app/tilemap_mode.h"
#include "base/disable_copying.h" #include "base/disable_copying.h"
#include "doc/blend_mode.h" #include "doc/blend_mode.h"
#include "doc/cel.h" #include "doc/cel.h"
@ -30,7 +31,12 @@ namespace app {
public: public:
ExtraCel(); ExtraCel();
void create(doc::Sprite* sprite, const gfx::Rect& bounds, doc::frame_t frame, int opacity); void create(const TilemapMode tilemapMode,
doc::Sprite* sprite,
const gfx::Rect& bounds,
const gfx::Size& imageSize,
const doc::frame_t frame,
const int opacity);
render::ExtraType type() const { return m_type; } render::ExtraType type() const { return m_type; }
void setType(render::ExtraType type) { m_type = type; } void setType(render::ExtraType type) { m_type = type; }

View File

@ -35,15 +35,7 @@ RgbMap* Site::rgbMap() const
return (m_sprite ? m_sprite->rgbMap(m_frame): nullptr); return (m_sprite ? m_sprite->rgbMap(m_frame): nullptr);
} }
const Cel* Site::cel() const Cel* Site::cel() const
{
if (m_layer)
return m_layer->cel(m_frame);
else
return nullptr;
}
Cel* Site::cel()
{ {
if (m_layer) if (m_layer)
return m_layer->cel(m_frame); return m_layer->cel(m_frame);

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2019 Igara Studio S.A. // Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -10,6 +10,7 @@
#pragma once #pragma once
#include "app/doc_range.h" #include "app/doc_range.h"
#include "app/tilemap_mode.h"
#include "app/tileset_mode.h" #include "app/tileset_mode.h"
#include "doc/frame.h" #include "doc/frame.h"
#include "doc/palette_picks.h" #include "doc/palette_picks.h"
@ -52,6 +53,7 @@ namespace app {
, m_sprite(nullptr) , m_sprite(nullptr)
, m_layer(nullptr) , m_layer(nullptr)
, m_frame(0) , m_frame(0)
, m_tilemapMode(TilemapMode::Pixels)
, m_tilesetMode(TilesetMode::Manual) { } , m_tilesetMode(TilesetMode::Manual) { }
const Focus focus() const { return m_focus; } const Focus focus() const { return m_focus; }
@ -62,16 +64,11 @@ namespace app {
bool inColorBar() const { return m_focus == InColorBar; } bool inColorBar() const { return m_focus == InColorBar; }
bool inTimeline() const { return (inLayers() || inFrames() || inCels()); } bool inTimeline() const { return (inLayers() || inFrames() || inCels()); }
const Doc* document() const { return m_document; } Doc* document() const { return m_document; }
const doc::Sprite* sprite() const { return m_sprite; } doc::Sprite* sprite() const { return m_sprite; }
const doc::Layer* layer() const { return m_layer; } doc::Layer* layer() const { return m_layer; }
doc::frame_t frame() const { return m_frame; } doc::frame_t frame() const { return m_frame; }
const doc::Cel* cel() const; doc::Cel* cel() const;
Doc* document() { return m_document; }
doc::Sprite* sprite() { return m_sprite; }
doc::Layer* layer() { return m_layer; }
doc::Cel* cel();
const DocRange& range() const { return m_range; } const DocRange& range() const { return m_range; }
void focus(Focus focus) { m_focus = focus; } void focus(Focus focus) { m_focus = focus; }
@ -112,8 +109,10 @@ namespace app {
doc::Grid grid() const; doc::Grid grid() const;
gfx::Rect gridBounds() const; gfx::Rect gridBounds() const;
void tilesetMode(const TilesetMode& mode) { m_tilesetMode = mode; } void tilemapMode(const TilemapMode mode) { m_tilemapMode = mode; }
const TilesetMode& tilesetMode() const { return m_tilesetMode; } void tilesetMode(const TilesetMode mode) { m_tilesetMode = mode; }
TilemapMode tilemapMode() const { return m_tilemapMode; }
TilesetMode tilesetMode() const { return m_tilesetMode; }
private: private:
Focus m_focus; Focus m_focus;
@ -125,6 +124,7 @@ namespace app {
doc::PalettePicks m_selectedColors; doc::PalettePicks m_selectedColors;
doc::PalettePicks m_selectedTiles; doc::PalettePicks m_selectedTiles;
doc::SelectedObjects m_selectedSlices; doc::SelectedObjects m_selectedSlices;
TilemapMode m_tilemapMode;
TilesetMode m_tilesetMode; TilesetMode m_tilesetMode;
}; };

21
src/app/tilemap_mode.h Normal file
View File

@ -0,0 +1,21 @@
// Aseprite
// Copyright (c) 2020 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_TILEMAP_MODE_H_INCLUDED
#define APP_TILEMAP_MODE_H_INCLUDED
#pragma once
namespace app {
// Should we edit the pixels or the tiles of the tilemap?
enum class TilemapMode {
Pixels, // Edit tile pixels
Tiles, // Edit tiles
};
} // namespace app
#endif

View File

@ -1,11 +1,11 @@
// Aseprite // Aseprite
// Copyright (C) 2019 Igara Studio S.A. // Copyright (C) 2019-2020 Igara Studio S.A.
// //
// This program is distributed under the terms of // This program is distributed under the terms of
// the End-User License Agreement for Aseprite. // the End-User License Agreement for Aseprite.
#ifndef APP_TILES_MODE_H_INCLUDED #ifndef APP_TILESET_MODE_H_INCLUDED
#define APP_TILES_MODE_H_INCLUDED #define APP_TILESET_MODE_H_INCLUDED
#pragma once #pragma once
namespace app { namespace app {

View File

@ -158,7 +158,12 @@ public:
break; break;
} }
case Copy: case Copy:
setProc(get_ink_proc<CopyInkProcessing>(loop)); if (loop->getDstImage()->pixelFormat() == IMAGE_TILEMAP) {
setProc(new CopyInkProcessing<TilemapTraits>(loop));
}
else {
setProc(get_ink_proc<CopyInkProcessing>(loop));
}
break; break;
case LockAlpha: case LockAlpha:
setProc(get_ink_proc<LockAlphaInkProcessing>(loop)); setProc(get_ink_proc<LockAlphaInkProcessing>(loop));

View File

@ -38,6 +38,24 @@ public:
} }
}; };
class TilePointShape : public PointShape {
public:
bool isPixel() override { return true; }
void transformPoint(ToolLoop* loop, const Stroke::Pt& pt) override {
const doc::Grid& grid = loop->getGrid();
gfx::Point newPos = grid.canvasToTile(pt.toPoint());
loop->getInk()->prepareForPointShape(loop, true, newPos.x, newPos.y);
doInkHline(newPos.x, newPos.y, newPos.x, loop);
}
void getModifiedArea(ToolLoop* loop, int x, int y, Rect& area) override {
const doc::Grid& grid = loop->getGrid();
area = grid.alignBounds(Rect(x, y, 1, 1));
}
};
class BrushPointShape : public PointShape { class BrushPointShape : public PointShape {
bool m_firstPoint; bool m_firstPoint;
Brush* m_lastBrush; Brush* m_lastBrush;

View File

@ -92,6 +92,7 @@ const char* WellKnownIntertwiners::AsPixelPerfect = "as_pixel_perfect";
const char* WellKnownPointShapes::None = "none"; const char* WellKnownPointShapes::None = "none";
const char* WellKnownPointShapes::Pixel = "pixel"; const char* WellKnownPointShapes::Pixel = "pixel";
const char* WellKnownPointShapes::Tile = "tile";
const char* WellKnownPointShapes::Brush = "brush"; const char* WellKnownPointShapes::Brush = "brush";
const char* WellKnownPointShapes::FloodFill = "floodfill"; const char* WellKnownPointShapes::FloodFill = "floodfill";
const char* WellKnownPointShapes::Spray = "spray"; const char* WellKnownPointShapes::Spray = "spray";
@ -143,6 +144,7 @@ ToolBox::ToolBox()
m_pointshapers[WellKnownPointShapes::None] = new NonePointShape(); m_pointshapers[WellKnownPointShapes::None] = new NonePointShape();
m_pointshapers[WellKnownPointShapes::Pixel] = new PixelPointShape(); m_pointshapers[WellKnownPointShapes::Pixel] = new PixelPointShape();
m_pointshapers[WellKnownPointShapes::Tile] = new TilePointShape();
m_pointshapers[WellKnownPointShapes::Brush] = new BrushPointShape(); m_pointshapers[WellKnownPointShapes::Brush] = new BrushPointShape();
m_pointshapers[WellKnownPointShapes::FloodFill] = new FloodFillPointShape(); m_pointshapers[WellKnownPointShapes::FloodFill] = new FloodFillPointShape();
m_pointshapers[WellKnownPointShapes::Spray] = new SprayPointShape(); m_pointshapers[WellKnownPointShapes::Spray] = new SprayPointShape();

View File

@ -78,6 +78,7 @@ namespace app {
namespace WellKnownPointShapes { namespace WellKnownPointShapes {
extern const char* None; extern const char* None;
extern const char* Pixel; extern const char* Pixel;
extern const char* Tile;
extern const char* Brush; extern const char* Brush;
extern const char* FloodFill; extern const char* FloodFill;
extern const char* Spray; extern const char* Spray;

View File

@ -16,6 +16,7 @@
#include "doc/brush.h" #include "doc/brush.h"
#include "doc/color.h" #include "doc/color.h"
#include "doc/frame.h" #include "doc/frame.h"
#include "doc/grid.h"
#include "filters/tiled_mode.h" #include "filters/tiled_mode.h"
#include "gfx/point.h" #include "gfx/point.h"
#include "gfx/rect.h" #include "gfx/rect.h"
@ -182,6 +183,7 @@ namespace app {
virtual bool getSnapToGrid() = 0; virtual bool getSnapToGrid() = 0;
virtual bool isSelectingTiles() = 0; virtual bool isSelectingTiles() = 0;
virtual bool getStopAtGrid() = 0; // For floodfill-like tools virtual bool getStopAtGrid() = 0; // For floodfill-like tools
virtual const doc::Grid& getGrid() const = 0;
virtual gfx::Rect getGridBounds() = 0; virtual gfx::Rect getGridBounds() = 0;
virtual bool isPixelConnectivityEightConnected() = 0; virtual bool isPixelConnectivityEightConnected() = 0;

View File

@ -170,7 +170,7 @@ ColorBar::ColorBar(int align, TooltipManager* tooltipManager)
, m_ascending(true) , m_ascending(true)
, m_lastButton(kButtonLeft) , m_lastButton(kButtonLeft)
, m_editMode(false) , m_editMode(false)
, m_tilesMode(false) , m_tilemapMode(TilemapMode::Pixels)
, m_tilesetMode(TilesetMode::Auto) , m_tilesetMode(TilesetMode::Auto)
, m_redrawTimer(250, this) , m_redrawTimer(250, this)
, m_redrawAll(false) , m_redrawAll(false)
@ -388,6 +388,16 @@ void ColorBar::setBgColor(const app::Color& color)
onColorButtonChange(color); onColorButtonChange(color);
} }
doc::tile_index ColorBar::getFgTile() const
{
return m_fgColor.getColor().getIndex(); // TODO
}
doc::tile_index ColorBar::getBgTile() const
{
return m_bgColor.getColor().getIndex(); // TODO
}
PaletteView* ColorBar::getPaletteView() PaletteView* ColorBar::getPaletteView()
{ {
return &m_paletteView; return &m_paletteView;
@ -485,28 +495,31 @@ void ColorBar::setEditMode(bool state)
m_paletteView.deselect(); m_paletteView.deselect();
} }
bool ColorBar::inTilesMode() const TilemapMode ColorBar::tilemapMode() const
{ {
return return
(m_tilesMode && (m_tilesHBox.isVisible() &&
m_tilesHBox.isVisible() &&
m_lastDocument && m_lastDocument &&
m_lastDocument->sprite()); m_lastDocument->sprite()) ? m_tilemapMode:
TilemapMode::Pixels;
} }
void ColorBar::setTilesMode(bool state) void ColorBar::setTilemapMode(const TilemapMode mode)
{ {
const Site site = UIContext::instance()->activeSite(); const Site site = UIContext::instance()->activeSite();
const bool isTilemap = (site.layer() && site.layer()->isTilemap()); const bool isTilemap = (site.layer() && site.layer()->isTilemap());
const bool editTiles = (mode == TilemapMode::Tiles);
SkinTheme* theme = static_cast<SkinTheme*>(this->theme()); SkinTheme* theme = static_cast<SkinTheme*>(this->theme());
ButtonSet::Item* item = m_tilesButton.getItem(0); ButtonSet::Item* item = m_tilesButton.getItem(0);
m_tilesMode = state; m_tilemapMode = mode;
item->setHotColor(state ? theme->colors.editPalFace(): gfx::ColorNone); item->setHotColor(editTiles ?
theme->colors.editPalFace():
gfx::ColorNone);
item->setMono(true); item->setMono(true);
if (state && isTilemap) { if (editTiles && isTilemap) {
manager()->freeWidget(&m_paletteView); manager()->freeWidget(&m_paletteView);
m_scrollablePalView.setVisible(false); m_scrollablePalView.setVisible(false);
m_scrollableTilesView.setVisible(true); m_scrollableTilesView.setVisible(true);
@ -575,8 +588,8 @@ void ColorBar::onActiveSiteChange(const Site& site)
} }
if (!isTilemap) { if (!isTilemap) {
m_lastTilesetId = doc::NullId; m_lastTilesetId = doc::NullId;
if (m_tilesMode) if (m_tilemapMode == TilemapMode::Tiles)
setTilesMode(false); setTilemapMode(TilemapMode::Pixels);
} }
} }
@ -679,7 +692,9 @@ void ColorBar::onPaletteButtonClick()
void ColorBar::onTilesButtonClick() void ColorBar::onTilesButtonClick()
{ {
m_tilesButton.deselectItems(); m_tilesButton.deselectItems();
setTilesMode(!inTilesMode()); setTilemapMode(
(tilemapMode() == TilemapMode::Pixels ? TilemapMode::Tiles:
TilemapMode::Pixels));
} }
void ColorBar::onTilesetModeButtonClick() void ColorBar::onTilesetModeButtonClick()
@ -885,7 +900,7 @@ void ColorBar::setTransparentIndex(int index)
void ColorBar::onPaletteViewChangeSize(int boxsize) void ColorBar::onPaletteViewChangeSize(int boxsize)
{ {
if (inTilesMode()) if (tilemapMode() == TilemapMode::Tiles)
Preferences::instance().colorBar.tilesBoxSize(boxsize); Preferences::instance().colorBar.tilesBoxSize(boxsize);
else else
Preferences::instance().colorBar.boxSize(boxsize); Preferences::instance().colorBar.boxSize(boxsize);
@ -1037,6 +1052,14 @@ void ColorBar::onTilesViewDragAndDrop(doc::Tileset* tileset,
void ColorBar::onTilesViewIndexChange(int index, ui::MouseButton button) void ColorBar::onTilesViewIndexChange(int index, ui::MouseButton button)
{ {
// TODO show tools to stamp/draw/pick tiles // TODO show tools to stamp/draw/pick tiles
if (button == kButtonRight)
setBgColor(app::Color::fromIndex(index));
else if (button == kButtonLeft)
setFgColor(app::Color::fromIndex(index));
else if (button == kButtonMiddle) {
// TODO ?
}
} }
void ColorBar::onFgColorChangeFromPreferences() void ColorBar::onFgColorChangeFromPreferences()
@ -1323,7 +1346,7 @@ void ColorBar::onNewInputPriority(InputChainElement* element,
return; return;
if (element != this) { if (element != this) {
if (m_tilesMode) if (m_tilemapMode == TilemapMode::Tiles)
m_tilesView.deselect(); m_tilesView.deselect();
else else
m_paletteView.deselect(); m_paletteView.deselect();
@ -1332,7 +1355,7 @@ void ColorBar::onNewInputPriority(InputChainElement* element,
bool ColorBar::onCanCut(Context* ctx) bool ColorBar::onCanCut(Context* ctx)
{ {
if (m_tilesMode) if (m_tilemapMode == TilemapMode::Tiles)
return (m_tilesView.getSelectedEntriesCount() > 0); return (m_tilesView.getSelectedEntriesCount() > 0);
else else
return (m_paletteView.getSelectedEntriesCount() > 0); return (m_paletteView.getSelectedEntriesCount() > 0);
@ -1345,7 +1368,7 @@ bool ColorBar::onCanCopy(Context* ctx)
bool ColorBar::onCanPaste(Context* ctx) bool ColorBar::onCanPaste(Context* ctx)
{ {
if (m_tilesMode) if (m_tilemapMode == TilemapMode::Tiles)
return (clipboard::get_current_format() == clipboard::ClipboardTiles); return (clipboard::get_current_format() == clipboard::ClipboardTiles);
else else
return (clipboard::get_current_format() == clipboard::ClipboardPaletteEntries); return (clipboard::get_current_format() == clipboard::ClipboardPaletteEntries);
@ -1358,7 +1381,7 @@ bool ColorBar::onCanClear(Context* ctx)
bool ColorBar::onCut(Context* ctx) bool ColorBar::onCut(Context* ctx)
{ {
if (m_tilesMode) { if (m_tilemapMode == TilemapMode::Tiles) {
m_tilesView.cutToClipboard(); m_tilesView.cutToClipboard();
showRemapTiles(); showRemapTiles();
} }
@ -1369,7 +1392,7 @@ bool ColorBar::onCut(Context* ctx)
bool ColorBar::onCopy(Context* ctx) bool ColorBar::onCopy(Context* ctx)
{ {
if (m_tilesMode) if (m_tilemapMode == TilemapMode::Tiles)
m_tilesView.copyToClipboard(); m_tilesView.copyToClipboard();
else else
m_paletteView.copyToClipboard(); m_paletteView.copyToClipboard();
@ -1378,7 +1401,7 @@ bool ColorBar::onCopy(Context* ctx)
bool ColorBar::onPaste(Context* ctx) bool ColorBar::onPaste(Context* ctx)
{ {
if (m_tilesMode) { if (m_tilemapMode == TilemapMode::Tiles) {
m_tilesView.pasteFromClipboard(); m_tilesView.pasteFromClipboard();
showRemapTiles(); showRemapTiles();
} }
@ -1389,7 +1412,7 @@ bool ColorBar::onPaste(Context* ctx)
bool ColorBar::onClear(Context* ctx) bool ColorBar::onClear(Context* ctx)
{ {
if (m_tilesMode) { if (m_tilemapMode == TilemapMode::Tiles) {
m_tilesView.clearSelection(); m_tilesView.clearSelection();
showRemapTiles(); showRemapTiles();
} }

View File

@ -13,6 +13,7 @@
#include "app/context_observer.h" #include "app/context_observer.h"
#include "app/doc_observer.h" #include "app/doc_observer.h"
#include "app/docs_observer.h" #include "app/docs_observer.h"
#include "app/tilemap_mode.h"
#include "app/tileset_mode.h" #include "app/tileset_mode.h"
#include "app/ui/button_set.h" #include "app/ui/button_set.h"
#include "app/ui/color_button.h" #include "app/ui/color_button.h"
@ -71,6 +72,9 @@ namespace app {
void setFgColor(const app::Color& color); void setFgColor(const app::Color& color);
void setBgColor(const app::Color& color); void setBgColor(const app::Color& color);
doc::tile_index getFgTile() const;
doc::tile_index getBgTile() const;
PaletteView* getPaletteView(); PaletteView* getPaletteView();
ColorSelector getColorSelector() const; ColorSelector getColorSelector() const;
@ -81,8 +85,8 @@ namespace app {
bool inEditMode() const; bool inEditMode() const;
void setEditMode(bool state); void setEditMode(bool state);
bool inTilesMode() const; TilemapMode tilemapMode() const;
void setTilesMode(bool state); void setTilemapMode(const TilemapMode mode);
TilesetMode tilesetMode() const; TilesetMode tilesetMode() const;
void setTilesetMode(const TilesetMode mode); void setTilesetMode(const TilesetMode mode);
@ -234,7 +238,7 @@ namespace app {
bool m_editMode; bool m_editMode;
// True if we should be putting/setting tiles. // True if we should be putting/setting tiles.
bool m_tilesMode; TilemapMode m_tilemapMode;
TilesetMode m_tilesetMode; TilesetMode m_tilesetMode;
// Timer to redraw editors after a palette change. // Timer to redraw editors after a palette change.

View File

@ -22,6 +22,7 @@
#include "app/tools/point_shape.h" #include "app/tools/point_shape.h"
#include "app/tools/tool.h" #include "app/tools/tool.h"
#include "app/tools/tool_loop.h" #include "app/tools/tool_loop.h"
#include "app/ui/color_bar.h"
#include "app/ui/context_bar.h" #include "app/ui/context_bar.h"
#include "app/ui/editor/editor.h" #include "app/ui/editor/editor.h"
#include "app/ui/editor/tool_loop_impl.h" #include "app/ui/editor/tool_loop_impl.h"
@ -34,8 +35,8 @@
#include "doc/image_impl.h" #include "doc/image_impl.h"
#include "doc/layer.h" #include "doc/layer.h"
#include "doc/primitives.h" #include "doc/primitives.h"
#include "render/render.h"
#include "os/display.h" #include "os/display.h"
#include "render/render.h"
#include "ui/manager.h" #include "ui/manager.h"
#include "ui/system.h" #include "ui/system.h"
@ -201,7 +202,12 @@ void BrushPreview::show(const gfx::Point& screenPos)
// Draw pixel/brush preview // Draw pixel/brush preview
if (showPreview) { if (showPreview) {
gfx::Rect origBrushBounds = (isFloodfill ? gfx::Rect(0, 0, 1, 1): brush->bounds()); Site site = m_editor->getSite();
// TODO add support for "tile-brushes"
gfx::Rect origBrushBounds =
(isFloodfill || site.tilemapMode() == TilemapMode::Tiles ? gfx::Rect(0, 0, 1, 1):
brush->bounds());
gfx::Rect brushBounds = origBrushBounds; gfx::Rect brushBounds = origBrushBounds;
brushBounds.offset(spritePos); brushBounds.offset(spritePos);
gfx::Rect extraCelBounds = brushBounds; gfx::Rect extraCelBounds = brushBounds;
@ -226,8 +232,17 @@ void BrushPreview::show(const gfx::Point& screenPos)
} }
} }
gfx::Rect extraCelBoundsInCanvas;
if (site.tilemapMode() == TilemapMode::Tiles) {
ASSERT(layer->isTilemap());
extraCelBounds.setOrigin(site.grid().canvasToTile(extraCelBounds.origin()));
extraCelBoundsInCanvas = site.grid().tileToCanvas(extraCelBounds);
}
else {
extraCelBoundsInCanvas = extraCelBounds;
}
// Create the extra cel to show the brush preview // Create the extra cel to show the brush preview
Site site = m_editor->getSite();
Cel* cel = site.cel(); Cel* cel = site.cel();
int t, opacity = 255; int t, opacity = 255;
@ -236,7 +251,14 @@ void BrushPreview::show(const gfx::Point& screenPos)
if (!m_extraCel) if (!m_extraCel)
m_extraCel.reset(new ExtraCel); m_extraCel.reset(new ExtraCel);
m_extraCel->create(document->sprite(), extraCelBounds, site.frame(), opacity);
m_extraCel->create(
site.tilemapMode(),
document->sprite(),
extraCelBoundsInCanvas,
extraCelBounds.size(),
site.frame(),
opacity);
m_extraCel->setType(render::ExtraType::NONE); m_extraCel->setType(render::ExtraType::NONE);
m_extraCel->setBlendMode( m_extraCel->setBlendMode(
(layer ? static_cast<LayerImage*>(layer)->blendMode(): (layer ? static_cast<LayerImage*>(layer)->blendMode():
@ -252,7 +274,7 @@ void BrushPreview::show(const gfx::Point& screenPos)
if (layer) { if (layer) {
render::Render().renderLayer( render::Render().renderLayer(
extraImage, layer, site.frame(), extraImage, layer, site.frame(),
gfx::Clip(0, 0, extraCelBounds), gfx::Clip(0, 0, extraCelBoundsInCanvas),
BlendMode::SRC); BlendMode::SRC);
// This extra cel is a patch for the current layer/frame // This extra cel is a patch for the current layer/frame
@ -279,7 +301,7 @@ void BrushPreview::show(const gfx::Point& screenPos)
} }
document->notifySpritePixelsModified( document->notifySpritePixelsModified(
sprite, gfx::Region(m_lastBounds = extraCelBounds), sprite, gfx::Region(m_lastBounds = extraCelBoundsInCanvas),
m_lastFrame = site.frame()); m_lastFrame = site.frame());
m_withRealPreview = true; m_withRealPreview = true;

View File

@ -410,12 +410,16 @@ void Editor::getSite(Site* site) const
} }
if (m_layer && m_layer->isTilemap()) { if (m_layer && m_layer->isTilemap()) {
TilesetMode mode = site->tilesetMode(); TilemapMode tilemapMode = site->tilemapMode();
TilesetMode tilesetMode = site->tilesetMode();
const ColorBar* colorbar = ColorBar::instance(); const ColorBar* colorbar = ColorBar::instance();
ASSERT(colorbar); ASSERT(colorbar);
if (colorbar) if (colorbar) {
mode = colorbar->tilesetMode(); tilemapMode = colorbar->tilemapMode();
site->tilesetMode(mode); tilesetMode = colorbar->tilesetMode();
}
site->tilemapMode(tilemapMode);
site->tilesetMode(tilesetMode);
} }
} }

View File

@ -785,7 +785,14 @@ void PixelsMovement::redrawExtraImage(Transformation* transformation)
if (!m_extraCel) if (!m_extraCel)
m_extraCel.reset(new ExtraCel); m_extraCel.reset(new ExtraCel);
m_extraCel->create(m_document->sprite(), bounds, m_site.frame(), opacity); m_extraCel->create(
m_site.tilemapMode(),
m_document->sprite(),
bounds,
(m_site.tilemapMode() == TilemapMode::Tiles ? m_site.grid().tileToCanvas(bounds).size():
bounds.size()),
m_site.frame(),
opacity);
m_extraCel->setType(render::ExtraType::PATCH); m_extraCel->setType(render::ExtraType::PATCH);
m_extraCel->setBlendMode(m_site.layer()->isImage() ? m_extraCel->setBlendMode(m_site.layer()->isImage() ?
static_cast<LayerImage*>(m_site.layer())->blendMode(): static_cast<LayerImage*>(m_site.layer())->blendMode():

View File

@ -103,6 +103,7 @@ protected:
bool m_contiguous; bool m_contiguous;
bool m_snapToGrid; bool m_snapToGrid;
bool m_isSelectingTiles; bool m_isSelectingTiles;
doc::Grid m_grid;
gfx::Rect m_gridBounds; gfx::Rect m_gridBounds;
gfx::Point m_celOrigin; gfx::Point m_celOrigin;
gfx::Point m_speed; gfx::Point m_speed;
@ -123,7 +124,8 @@ protected:
tools::DynamicsOptions m_dynamics; tools::DynamicsOptions m_dynamics;
public: public:
ToolLoopBase(Editor* editor, Site site, ToolLoopBase(Editor* editor,
const Site& site,
ToolLoopParams& params) ToolLoopParams& params)
: m_editor(editor) : m_editor(editor)
, m_tool(params.tool) , m_tool(params.tool)
@ -142,7 +144,8 @@ public:
, m_contiguous(params.contiguous) , m_contiguous(params.contiguous)
, m_snapToGrid(m_docPref.grid.snap()) , m_snapToGrid(m_docPref.grid.snap())
, m_isSelectingTiles(false) , m_isSelectingTiles(false)
, m_gridBounds(params.gridBounds) , m_grid(site.grid())
, m_gridBounds(site.gridBounds())
, m_button(params.button) , m_button(params.button)
, m_ink(params.ink->clone()) , m_ink(params.ink->clone())
, m_controller(params.controller) , m_controller(params.controller)
@ -150,7 +153,9 @@ public:
, m_intertwine(m_tool->getIntertwine(m_button)) , m_intertwine(m_tool->getIntertwine(m_button))
, m_tracePolicy(m_tool->getTracePolicy(m_button)) , m_tracePolicy(m_tool->getTracePolicy(m_button))
, m_symmetry(nullptr) , m_symmetry(nullptr)
, m_colorTarget(m_layer ? ColorTarget(m_layer): , m_colorTarget(site.tilemapMode() == TilemapMode::Tiles ? ColorTarget(ColorTarget::BackgroundLayer,
IMAGE_TILEMAP, 0):
m_layer ? ColorTarget(m_layer):
ColorTarget(ColorTarget::BackgroundLayer, ColorTarget(ColorTarget::BackgroundLayer,
m_sprite->pixelFormat(), m_sprite->pixelFormat(),
m_sprite->transparentColor())) m_sprite->transparentColor()))
@ -163,6 +168,11 @@ public:
ASSERT(m_ink); ASSERT(m_ink);
ASSERT(m_controller); ASSERT(m_controller);
if (site.tilemapMode() == TilemapMode::Tiles) {
m_pointShape = App::instance()->toolBox()->getPointShapeById(
tools::WellKnownPointShapes::Tile);
}
#ifdef ENABLE_UI // TODO add dynamics support when UI is not enabled #ifdef ENABLE_UI // TODO add dynamics support when UI is not enabled
if (m_controller->isFreehand() && if (m_controller->isFreehand() &&
!m_pointShape->isFloodFill() && !m_pointShape->isFloodFill() &&
@ -309,6 +319,7 @@ public:
== app::gen::PixelConnectivity::EIGHT_CONNECTED); == app::gen::PixelConnectivity::EIGHT_CONNECTED);
} }
const doc::Grid& getGrid() const override { return m_grid; }
gfx::Rect getGridBounds() override { return m_gridBounds; } gfx::Rect getGridBounds() override { return m_gridBounds; }
gfx::Point getCelOrigin() override { return m_celOrigin; } gfx::Point getCelOrigin() override { return m_celOrigin; }
void setSpeed(const gfx::Point& speed) override { m_speed = speed; } void setSpeed(const gfx::Point& speed) override { m_speed = speed; }
@ -432,7 +443,7 @@ class ToolLoopImpl : public ToolLoopBase {
public: public:
ToolLoopImpl(Editor* editor, ToolLoopImpl(Editor* editor,
Site site, const Site& site,
Context* context, Context* context,
ToolLoopParams& params, ToolLoopParams& params,
const bool saveLastPoint) const bool saveLastPoint)
@ -691,9 +702,18 @@ tools::ToolLoop* create_tool_loop(
const bool convertLineToFreehand, const bool convertLineToFreehand,
const bool selectTiles) const bool selectTiles)
{ {
Site site = editor->getSite();
ToolLoopParams params; ToolLoopParams params;
params.tool = editor->getCurrentEditorTool(); params.tool = editor->getCurrentEditorTool();
params.ink = editor->getCurrentEditorInk(); params.ink = editor->getCurrentEditorInk();
// TODO add inks for tilemaps
if (site.tilemapMode() == TilemapMode::Tiles) {
if (!params.ink->isSelection())
params.ink = App::instance()->toolBox()->getInkById(tools::WellKnownInks::PaintCopy);
}
if (!params.tool || !params.ink) if (!params.tool || !params.ink)
return nullptr; return nullptr;
@ -702,14 +722,6 @@ tools::ToolLoop* create_tool_loop(
params.ink = params.tool->getInk(button == tools::Pointer::Left ? 0: 1); params.ink = params.tool->getInk(button == tools::Pointer::Left ? 0: 1);
} }
Site site = editor->getSite();
// Get grid bounds from the original site (as we call
// site.layer(nullptr) in certain cases, we need to know if the
// active layer is a tilemap, and in that case the grid bounds can
// be different than the sprite grid bounds).
params.gridBounds = site.gridBounds();
// For selection tools, we can use any layer (even without layers at // For selection tools, we can use any layer (even without layers at
// all), so we specify a nullptr here as the active layer. This is // all), so we specify a nullptr here as the active layer. This is
// used as a special case by the render::Render class to show the // used as a special case by the render::Render class to show the
@ -750,11 +762,18 @@ tools::ToolLoop* create_tool_loop(
// Get fg/bg colors // Get fg/bg colors
ColorBar* colorbar = ColorBar::instance(); ColorBar* colorbar = ColorBar::instance();
params.fg = colorbar->getFgColor(); if (site.tilemapMode() == TilemapMode::Tiles) {
params.bg = colorbar->getBgColor(); params.fg = app::Color::fromIndex(colorbar->getFgTile()); // TODO Color::fromTileIndex?
params.bg = app::Color::fromIndex(colorbar->getBgTile());
}
else {
params.fg = colorbar->getFgColor();
params.bg = colorbar->getBgColor();
}
if (!params.fg.isValid() || if (site.tilemapMode() == TilemapMode::Pixels &&
!params.bg.isValid()) { (!params.fg.isValid() ||
!params.bg.isValid())) {
if (Preferences::instance().colorBar.showInvalidFgBgColorAlert()) { if (Preferences::instance().colorBar.showInvalidFgBgColorAlert()) {
OptionalAlert::show( OptionalAlert::show(
Preferences::instance().colorBar.showInvalidFgBgColorAlert, Preferences::instance().colorBar.showInvalidFgBgColorAlert,
@ -817,9 +836,6 @@ tools::ToolLoop* create_tool_loop_for_script(
if (!site.layer()) if (!site.layer())
return nullptr; return nullptr;
// TODO should gridBounds be specified by the caller?
params.gridBounds = site.gridBounds();
try { try {
// If we don't have the UI available, we reset the tools // If we don't have the UI available, we reset the tools
// preferences, so scripts that are executed in batch mode have a // preferences, so scripts that are executed in batch mode have a
@ -849,10 +865,11 @@ class PreviewToolLoopImpl : public ToolLoopBase {
public: public:
PreviewToolLoopImpl( PreviewToolLoopImpl(
Editor* editor, Editor* editor,
const Site& site,
ToolLoopParams& params, ToolLoopParams& params,
Image* image, Image* image,
const gfx::Point& celOrigin) const gfx::Point& celOrigin)
: ToolLoopBase(editor, editor->getSite(), params) : ToolLoopBase(editor, site, params)
, m_image(image) , m_image(image)
{ {
m_celOrigin = celOrigin; m_celOrigin = celOrigin;
@ -906,9 +923,19 @@ tools::ToolLoop* create_tool_loop_preview(
Image* image, Image* image,
const gfx::Point& celOrigin) const gfx::Point& celOrigin)
{ {
Site site = editor->getSite();
ToolLoopParams params; ToolLoopParams params;
params.tool = editor->getCurrentEditorTool(); params.tool = editor->getCurrentEditorTool();
params.ink = editor->getCurrentEditorInk(); params.ink = editor->getCurrentEditorInk();
// TODO add inks for tilemaps
if (site.tilemapMode() == TilemapMode::Tiles &&
image->pixelFormat() == IMAGE_TILEMAP) {
if (!params.ink->isSelection())
params.ink = App::instance()->toolBox()->getInkById(tools::WellKnownInks::PaintCopy);
}
if (!params.tool || !params.ink) if (!params.tool || !params.ink)
return nullptr; return nullptr;
@ -922,8 +949,16 @@ tools::ToolLoop* create_tool_loop_preview(
// Get fg/bg colors // Get fg/bg colors
ColorBar* colorbar = ColorBar::instance(); ColorBar* colorbar = ColorBar::instance();
params.fg = colorbar->getFgColor(); if (site.tilemapMode() == TilemapMode::Tiles) {
params.bg = colorbar->getBgColor(); params.fg = app::Color::fromIndex(colorbar->getFgTile()); // TODO Color::fromTileIndex?
params.bg = app::Color::fromIndex(colorbar->getBgTile());
if (!params.fg.isValid() || !params.bg.isValid())
return nullptr;
}
else {
params.fg = colorbar->getFgColor();
params.bg = colorbar->getBgColor();
}
if (!params.fg.isValid() || if (!params.fg.isValid() ||
!params.bg.isValid()) !params.bg.isValid())
return nullptr; return nullptr;
@ -931,14 +966,13 @@ tools::ToolLoop* create_tool_loop_preview(
params.brush = brush; params.brush = brush;
params.button = tools::ToolLoop::Left; params.button = tools::ToolLoop::Left;
params.controller = params.tool->getController(params.button); params.controller = params.tool->getController(params.button);
params.gridBounds = editor->getSite().gridBounds();
// Create the new tool loop // Create the new tool loop
try { try {
fill_toolloop_params_from_tool_preferences(params); fill_toolloop_params_from_tool_preferences(params);
return new PreviewToolLoopImpl( return new PreviewToolLoopImpl(
editor, params, image, celOrigin); editor, site, params, image, celOrigin);
} }
catch (const std::exception& e) { catch (const std::exception& e) {
LOG(ERROR, e.what()); LOG(ERROR, e.what());

View File

@ -46,8 +46,6 @@ namespace app {
int tolerance = 0; int tolerance = 0;
bool contiguous = true; bool contiguous = true;
tools::FreehandAlgorithm freehandAlgorithm = tools::FreehandAlgorithm::DEFAULT; tools::FreehandAlgorithm freehandAlgorithm = tools::FreehandAlgorithm::DEFAULT;
gfx::Rect gridBounds;
}; };
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2019 Igara Studio S.A. // Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -86,6 +86,8 @@ ExpandCelCanvas::ExpandCelCanvas(
, m_closed(false) , m_closed(false)
, m_committed(false) , m_committed(false)
, m_cmds(cmds) , m_cmds(cmds)
, m_grid(site.grid())
, m_tilemapMode(site.tilemapMode())
, m_tilesetMode(site.tilesetMode()) , m_tilesetMode(site.tilesetMode())
{ {
if (m_layer && m_layer->isTilemap()) { if (m_layer && m_layer->isTilemap()) {
@ -126,6 +128,10 @@ ExpandCelCanvas::ExpandCelCanvas(
m_bounds = spriteBounds; m_bounds = spriteBounds;
} }
if (m_tilemapMode == TilemapMode::Tiles) {
m_bounds = site.grid().canvasToTile(m_bounds);
}
// We have to adjust the cel position to match the m_dstImage // We have to adjust the cel position to match the m_dstImage
// position (the new m_dstImage will be used in RenderEngine to // position (the new m_dstImage will be used in RenderEngine to
// draw this cel). // draw this cel).
@ -140,7 +146,8 @@ ExpandCelCanvas::ExpandCelCanvas(
} }
// If we are in a tilemap, we use m_dstImage to draw pixels (instead // If we are in a tilemap, we use m_dstImage to draw pixels (instead
// of the tilemap image). // of the tilemap image).
else if (m_layer->isTilemap()) { else if (m_layer->isTilemap() &&
m_tilemapMode == TilemapMode::Pixels) {
// Calling "getDestCanvas()" we create the m_dstImage // Calling "getDestCanvas()" we create the m_dstImage
getDestCanvas(); getDestCanvas();
m_cel->data()->setImage(m_dstImage, m_layer); m_cel->data()->setImage(m_dstImage, m_layer);
@ -191,7 +198,8 @@ void ExpandCelCanvas::commit()
gfx::Rect trimBounds = getTrimDstImageBounds(); gfx::Rect trimBounds = getTrimDstImageBounds();
if (!trimBounds.isEmpty()) { if (!trimBounds.isEmpty()) {
// Convert the image to tiles // Convert the image to tiles
if (m_layer->isTilemap()) { if (m_layer->isTilemap() &&
m_tilemapMode == TilemapMode::Pixels) {
doc::ImageRef newTilemap; doc::ImageRef newTilemap;
draw_image_into_new_tilemap_cel( draw_image_into_new_tilemap_cel(
m_cmds, static_cast<doc::LayerTilemap*>(m_layer), m_cel, m_cmds, static_cast<doc::LayerTilemap*>(m_layer), m_cel,
@ -226,7 +234,8 @@ void ExpandCelCanvas::commit()
m_cel->setPosition(m_origCelPos); m_cel->setPosition(m_origCelPos);
#ifdef _DEBUG #ifdef _DEBUG
if (m_layer->isTilemap()) { if (m_layer->isTilemap() &&
m_tilemapMode == TilemapMode::Pixels) {
ASSERT(m_cel->image() != m_celImage.get()); ASSERT(m_cel->image() != m_celImage.get());
} }
else { else {
@ -253,7 +262,8 @@ void ExpandCelCanvas::commit()
EXP_TRACE(" - regionToPatch", regionToPatch->bounds()); EXP_TRACE(" - regionToPatch", regionToPatch->bounds());
// Convert the image to tiles again // Convert the image to tiles again
if (m_layer->isTilemap()) { if (m_layer->isTilemap() &&
m_tilemapMode == TilemapMode::Pixels) {
ASSERT(m_celImage.get() != m_cel->image()); ASSERT(m_celImage.get() != m_cel->image());
ASSERT(m_celImage->pixelFormat() == IMAGE_TILEMAP); ASSERT(m_celImage->pixelFormat() == IMAGE_TILEMAP);
@ -326,7 +336,6 @@ void ExpandCelCanvas::rollback()
} }
// Restore the original tilemap // Restore the original tilemap
else if (m_layer->isTilemap()) { else if (m_layer->isTilemap()) {
ASSERT(m_cel->image()->pixelFormat() == m_sprite->pixelFormat());
ASSERT(m_celImage->pixelFormat() == IMAGE_TILEMAP); ASSERT(m_celImage->pixelFormat() == IMAGE_TILEMAP);
m_cel->data()->setImage(m_celImage, m_cel->data()->setImage(m_celImage,
m_cel->layer()); m_cel->layer());
@ -340,8 +349,10 @@ Image* ExpandCelCanvas::getSourceCanvas()
ASSERT((m_flags & NeedsSource) == NeedsSource); ASSERT((m_flags & NeedsSource) == NeedsSource);
if (!m_srcImage) { if (!m_srcImage) {
m_srcImage.reset(Image::create(m_sprite->pixelFormat(), m_srcImage.reset(
m_bounds.w, m_bounds.h, src_buffer)); Image::create(m_tilemapMode == TilemapMode::Tiles ? IMAGE_TILEMAP:
m_sprite->pixelFormat(),
m_bounds.w, m_bounds.h, src_buffer));
m_srcImage->setMaskColor(m_sprite->transparentColor()); m_srcImage->setMaskColor(m_sprite->transparentColor());
} }
@ -351,8 +362,10 @@ Image* ExpandCelCanvas::getSourceCanvas()
Image* ExpandCelCanvas::getDestCanvas() Image* ExpandCelCanvas::getDestCanvas()
{ {
if (!m_dstImage) { if (!m_dstImage) {
m_dstImage.reset(Image::create(m_sprite->pixelFormat(), m_dstImage.reset(
m_bounds.w, m_bounds.h, dst_buffer)); Image::create(m_tilemapMode == TilemapMode::Tiles ? IMAGE_TILEMAP:
m_sprite->pixelFormat(),
m_bounds.w, m_bounds.h, dst_buffer));
m_dstImage->setMaskColor(m_sprite->transparentColor()); m_dstImage->setMaskColor(m_sprite->transparentColor());
} }
@ -365,7 +378,16 @@ void ExpandCelCanvas::validateSourceCanvas(const gfx::Region& rgn)
getSourceCanvas(); getSourceCanvas();
gfx::Region rgnToValidate(rgn); gfx::Region rgnToValidate;
if (m_tilemapMode == TilemapMode::Tiles) {
for (const auto& rc : rgn)
rgnToValidate |= gfx::Region(m_grid.canvasToTile(rc));
}
else {
rgnToValidate = rgn;
}
EXP_TRACE(" ->", rgnToValidate.bounds());
rgnToValidate.offset(-m_bounds.origin()); rgnToValidate.offset(-m_bounds.origin());
rgnToValidate.createSubtraction(rgnToValidate, m_validSrcRegion); rgnToValidate.createSubtraction(rgnToValidate, m_validSrcRegion);
rgnToValidate.createIntersection(rgnToValidate, gfx::Region(m_srcImage->bounds())); rgnToValidate.createIntersection(rgnToValidate, gfx::Region(m_srcImage->bounds()));
@ -380,7 +402,10 @@ void ExpandCelCanvas::validateSourceCanvas(const gfx::Region& rgn)
for (const auto& rc : rgnToClear) for (const auto& rc : rgnToClear)
fill_rect(m_srcImage.get(), rc, m_srcImage->maskColor()); fill_rect(m_srcImage.get(), rc, m_srcImage->maskColor());
if (m_celImage->pixelFormat() == IMAGE_TILEMAP) { if (m_celImage->pixelFormat() == IMAGE_TILEMAP &&
m_srcImage->pixelFormat() != IMAGE_TILEMAP) {
ASSERT(m_tilemapMode == TilemapMode::Pixels);
// For tilemaps, we can use the Render class to render visible // For tilemaps, we can use the Render class to render visible
// tiles in the rgnToValidate of this cel. // tiles in the rgnToValidate of this cel.
render::Render subRender; render::Render subRender;
@ -399,6 +424,9 @@ void ExpandCelCanvas::validateSourceCanvas(const gfx::Region& rgn)
} }
} }
else { else {
ASSERT(m_celImage->pixelFormat() != IMAGE_TILEMAP ||
m_tilemapMode == TilemapMode::Tiles);
// We can copy the cel image directly // We can copy the cel image directly
for (const auto& rc : rgnToValidate) for (const auto& rc : rgnToValidate)
m_srcImage->copy( m_srcImage->copy(
@ -436,7 +464,16 @@ void ExpandCelCanvas::validateDestCanvas(const gfx::Region& rgn)
getDestCanvas(); // Create m_dstImage getDestCanvas(); // Create m_dstImage
gfx::Region rgnToValidate(rgn); gfx::Region rgnToValidate;
if (m_tilemapMode == TilemapMode::Tiles) {
for (const auto& rc : rgn)
rgnToValidate |= gfx::Region(m_grid.canvasToTile(rc));
}
else {
rgnToValidate = rgn;
}
EXP_TRACE(" ->", rgnToValidate.bounds());
rgnToValidate.offset(-m_bounds.origin()); rgnToValidate.offset(-m_bounds.origin());
rgnToValidate.createSubtraction(rgnToValidate, m_validDstRegion); rgnToValidate.createSubtraction(rgnToValidate, m_validDstRegion);
rgnToValidate.createIntersection(rgnToValidate, gfx::Region(m_dstImage->bounds())); rgnToValidate.createIntersection(rgnToValidate, gfx::Region(m_dstImage->bounds()));

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2019 Igara Studio S.A. // Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -9,8 +9,10 @@
#define APP_UTIL_EXPAND_CEL_CANVAS_H_INCLUDED #define APP_UTIL_EXPAND_CEL_CANVAS_H_INCLUDED
#pragma once #pragma once
#include "app/tilemap_mode.h"
#include "app/tileset_mode.h" #include "app/tileset_mode.h"
#include "doc/frame.h" #include "doc/frame.h"
#include "doc/grid.h"
#include "doc/image_ref.h" #include "doc/image_ref.h"
#include "filters/tiled_mode.h" #include "filters/tiled_mode.h"
#include "gfx/point.h" #include "gfx/point.h"
@ -100,6 +102,8 @@ namespace app {
// reduce the patched region because both images will be the same. // reduce the patched region because both images will be the same.
bool m_canCompareSrcVsDst; bool m_canCompareSrcVsDst;
doc::Grid m_grid;
TilemapMode m_tilemapMode;
TilesetMode m_tilesetMode; TilesetMode m_tilesetMode;
}; };

View File

@ -1,5 +1,5 @@
// Aseprite Document Library // Aseprite Document Library
// Copyright (c) 2019 Igara Studio S.A. // Copyright (c) 2019-2020 Igara Studio S.A.
// Copyright (c) 2001-2016 David Capello // Copyright (c) 2001-2016 David Capello
// //
// This file is released under the terms of the MIT license. // This file is released under the terms of the MIT license.
@ -399,7 +399,7 @@ bool shrink_bounds2(const Image* a,
case IMAGE_GRAYSCALE: return shrink_bounds_templ2<GrayscaleTraits>(a, b, bounds); case IMAGE_GRAYSCALE: return shrink_bounds_templ2<GrayscaleTraits>(a, b, bounds);
case IMAGE_INDEXED: return shrink_bounds_templ2<IndexedTraits>(a, b, bounds); case IMAGE_INDEXED: return shrink_bounds_templ2<IndexedTraits>(a, b, bounds);
case IMAGE_BITMAP: return shrink_bounds_templ2<BitmapTraits>(a, b, bounds); case IMAGE_BITMAP: return shrink_bounds_templ2<BitmapTraits>(a, b, bounds);
// case IMAGE_TILEMAP: return shrink_bounds_templ2<TilemapTraits>(a, b, bounds); case IMAGE_TILEMAP: return shrink_bounds_templ2<TilemapTraits>(a, b, bounds);
} }
ASSERT(false); ASSERT(false);
return false; return false;

View File

@ -677,7 +677,8 @@ void Render::renderLayer(
CompositeImageFunc compositeImage = CompositeImageFunc compositeImage =
getImageComposition( getImageComposition(
dstImage->pixelFormat(), (dstImage->pixelFormat() != IMAGE_TILEMAP ? dstImage->pixelFormat():
m_sprite->pixelFormat()),
m_sprite->pixelFormat(), layer); m_sprite->pixelFormat(), layer);
if (!compositeImage) if (!compositeImage)
return; return;
@ -757,7 +758,10 @@ void Render::renderSprite(
// Overlay preview image // Overlay preview image
if (m_previewImage && if (m_previewImage &&
m_selectedLayer == nullptr && m_selectedLayer == nullptr &&
m_selectedFrame == frame) { m_selectedFrame == frame
// TODO allow previewImage for tilemaps?
&& m_previewImage->pixelFormat() == m_sprite->pixelFormat()
) {
renderImage( renderImage(
dstImage, dstImage,
m_previewImage, m_previewImage,
@ -768,7 +772,8 @@ void Render::renderSprite(
area, area,
getImageComposition( getImageComposition(
dstImage->pixelFormat(), dstImage->pixelFormat(),
m_previewImage->pixelFormat(), sprite->root()), m_previewImage->pixelFormat(),
sprite->root()),
255, 255,
m_previewBlendMode); m_previewBlendMode);
} }
@ -1033,11 +1038,7 @@ void Render::renderLayer(
} }
if (drawExtra) { if (drawExtra) {
extraArea = gfx::Rect( extraArea = m_extraCel->bounds();
m_extraCel->x(),
m_extraCel->y(),
m_extraImage->width(),
m_extraImage->height());
extraArea = m_proj.apply(extraArea); extraArea = m_proj.apply(extraArea);
if (m_proj.scaleX() < 1.0) extraArea.w--; if (m_proj.scaleX() < 1.0) extraArea.w--;
if (m_proj.scaleY() < 1.0) extraArea.h--; if (m_proj.scaleY() < 1.0) extraArea.h--;
@ -1178,14 +1179,15 @@ void Render::renderLayer(
if (drawExtra && m_extraType != ExtraType::NONE) { if (drawExtra && m_extraType != ExtraType::NONE) {
if (m_extraCel->opacity() > 0) { if (m_extraCel->opacity() > 0) {
renderCel( renderCel(
image, m_extraImage, image,
nullptr, // Without layer m_sprite,
m_extraImage,
m_currentLayer, // Current layer (useful to use get the tileset if extra cel is a tilemap)
m_sprite->palette(frame), m_sprite->palette(frame),
m_extraCel->bounds(), m_extraCel->bounds(),
gfx::Clip(area.dst.x+extraArea.x-area.src.x, gfx::Clip(area.dst.x+extraArea.x-area.src.x,
area.dst.y+extraArea.y-area.src.y, area.dst.y+extraArea.y-area.src.y,
extraArea), extraArea),
compositeImage,
m_extraCel->opacity(), m_extraCel->opacity(),
m_extraBlendMode); m_extraBlendMode);
} }
@ -1285,12 +1287,17 @@ void Render::renderCel(
const tile_t t = cel_image->getPixel(u, v); const tile_t t = cel_image->getPixel(u, v);
const tile_index i = tile_geti(t); const tile_index i = tile_geti(t);
const ImageRef tile_image = tileset->get(i); if (dst_image->pixelFormat() == IMAGE_TILEMAP) {
if (!tile_image) put_pixel(dst_image, u-area.dst.x, v-area.dst.y, t);
continue; }
else {
const ImageRef tile_image = tileset->get(i);
if (!tile_image)
continue;
renderImage(dst_image, tile_image.get(), pal, tileBoundsOnCanvas, renderImage(dst_image, tile_image.get(), pal, tileBoundsOnCanvas,
area, compositeImage, opacity, blendMode); area, compositeImage, opacity, blendMode);
}
} }
} }
} }
@ -1374,8 +1381,16 @@ CompositeImageFunc Render::getImageComposition(
case IMAGE_INDEXED: return get_fastest_composition_path<IndexedTraits, IndexedTraits>(m_proj, finegrain); case IMAGE_INDEXED: return get_fastest_composition_path<IndexedTraits, IndexedTraits>(m_proj, finegrain);
} }
break; break;
case IMAGE_TILEMAP:
switch (dstFormat) {
case IMAGE_TILEMAP:
return get_fastest_composition_path<TilemapTraits, TilemapTraits>(m_proj, finegrain);
}
break;
} }
TRACE_RENDER_CEL("Render::getImageComposition srcFormat", srcFormat, "dstFormat", dstFormat);
ASSERT(false && "Invalid pixel formats"); ASSERT(false && "Invalid pixel formats");
return nullptr; return nullptr;
} }