mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-14 04:19:12 +00:00
Add support to clear/fill/stroke selections in tiles mode
This commit is contained in:
parent
4eeaad5a69
commit
b1c0d80356
@ -1,4 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -16,6 +17,7 @@
|
||||
#include "doc/cel.h"
|
||||
#include "doc/image_impl.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/layer_tilemap.h"
|
||||
#include "doc/mask.h"
|
||||
#include "doc/primitives.h"
|
||||
|
||||
@ -33,6 +35,9 @@ ClearMask::ClearMask(Cel* cel)
|
||||
// entire image in the cel.
|
||||
if (!doc->isMaskVisible()) {
|
||||
m_seq.add(new cmd::ClearCel(cel));
|
||||
|
||||
// In this case m_copy will be nullptr, so the clear()/restore()
|
||||
// member functions will have no effect.
|
||||
return;
|
||||
}
|
||||
|
||||
@ -41,60 +46,78 @@ ClearMask::ClearMask(Cel* cel)
|
||||
if (!image)
|
||||
return;
|
||||
|
||||
Mask* mask = doc->mask();
|
||||
m_offset = mask->bounds().origin() - cel->position();
|
||||
const Mask* mask = doc->mask();
|
||||
gfx::Rect imageBounds;
|
||||
gfx::Rect maskBounds;
|
||||
if (image->pixelFormat() == IMAGE_TILEMAP) {
|
||||
auto grid = cel->grid();
|
||||
imageBounds = gfx::Rect(grid.canvasToTile(cel->position()),
|
||||
cel->image()->size());
|
||||
maskBounds = grid.canvasToTile(mask->bounds());
|
||||
m_bgcolor = doc::tile_i_notile; // TODO configurable empty tile
|
||||
}
|
||||
else {
|
||||
imageBounds = cel->bounds();
|
||||
maskBounds = mask->bounds();
|
||||
m_bgcolor = doc->bgColor(cel->layer());
|
||||
}
|
||||
|
||||
gfx::Rect bounds =
|
||||
image->bounds().createIntersection(
|
||||
gfx::Rect(
|
||||
m_offset.x, m_offset.y,
|
||||
mask->bounds().w, mask->bounds().h));
|
||||
if (bounds.isEmpty())
|
||||
gfx::Rect cropBounds = (imageBounds & maskBounds);
|
||||
if (cropBounds.isEmpty())
|
||||
return;
|
||||
|
||||
m_dstImage.reset(new WithImage(image));
|
||||
m_bgcolor = doc->bgColor(cel->layer());
|
||||
m_boundsX = bounds.x;
|
||||
m_boundsY = bounds.y;
|
||||
cropBounds.offset(-imageBounds.origin());
|
||||
m_cropPos = cropBounds.origin();
|
||||
|
||||
m_copy.reset(crop_image(image,
|
||||
bounds.x, bounds.y, bounds.w, bounds.h, m_bgcolor));
|
||||
m_copy.reset(crop_image(image, cropBounds, m_bgcolor));
|
||||
}
|
||||
|
||||
void ClearMask::onExecute()
|
||||
{
|
||||
m_seq.execute(context());
|
||||
if (m_dstImage)
|
||||
clear();
|
||||
clear();
|
||||
}
|
||||
|
||||
void ClearMask::onUndo()
|
||||
{
|
||||
if (m_dstImage)
|
||||
restore();
|
||||
restore();
|
||||
m_seq.undo();
|
||||
}
|
||||
|
||||
void ClearMask::onRedo()
|
||||
{
|
||||
m_seq.redo();
|
||||
if (m_dstImage)
|
||||
clear();
|
||||
clear();
|
||||
}
|
||||
|
||||
void ClearMask::clear()
|
||||
{
|
||||
if (!m_copy)
|
||||
return;
|
||||
|
||||
Cel* cel = this->cel();
|
||||
Image* image = m_dstImage->image();
|
||||
Doc* doc = static_cast<Doc*>(cel->document());
|
||||
Mask* mask = doc->mask();
|
||||
|
||||
doc::algorithm::fill_selection(image, m_offset, mask, m_bgcolor);
|
||||
Grid grid = cel->grid();
|
||||
doc::algorithm::fill_selection(
|
||||
cel->image(),
|
||||
cel->bounds(),
|
||||
mask,
|
||||
m_bgcolor,
|
||||
(cel->image()->isTilemap() ? &grid: nullptr));
|
||||
}
|
||||
|
||||
void ClearMask::restore()
|
||||
{
|
||||
copy_image(m_dstImage->image(), m_copy.get(), m_boundsX, m_boundsY);
|
||||
if (!m_copy)
|
||||
return;
|
||||
|
||||
Cel* cel = this->cel();
|
||||
copy_image(cel->image(),
|
||||
m_copy.get(),
|
||||
m_cropPos.x,
|
||||
m_cropPos.y);
|
||||
}
|
||||
|
||||
} // namespace cmd
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -13,6 +14,7 @@
|
||||
#include "app/cmd/with_image.h"
|
||||
#include "app/cmd_sequence.h"
|
||||
#include "doc/image_ref.h"
|
||||
#include "gfx/rect.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
@ -39,10 +41,8 @@ namespace cmd {
|
||||
void restore();
|
||||
|
||||
CmdSequence m_seq;
|
||||
std::unique_ptr<WithImage> m_dstImage;
|
||||
ImageRef m_copy;
|
||||
gfx::Point m_offset;
|
||||
int m_boundsX, m_boundsY;
|
||||
gfx::Point m_cropPos;
|
||||
color_t m_bgcolor;
|
||||
};
|
||||
|
||||
|
@ -74,7 +74,11 @@ void FillCommand::onExecute(Context* ctx)
|
||||
return;
|
||||
|
||||
Preferences& pref = Preferences::instance();
|
||||
app::Color color = pref.colorBar.fgColor();
|
||||
doc::color_t color;
|
||||
if (site.tilemapMode() == TilemapMode::Tiles)
|
||||
color = pref.colorBar.fgTile();
|
||||
else
|
||||
color = color_utils::color_for_layer(pref.colorBar.fgColor(), layer);
|
||||
|
||||
{
|
||||
Tx tx(writer.context(), "Fill Selection with Foreground Color");
|
||||
@ -88,25 +92,28 @@ void FillCommand::onExecute(Context* ctx)
|
||||
mask->bounds());
|
||||
expand.validateDestCanvas(rgn);
|
||||
|
||||
const gfx::Point offset = (mask->bounds().origin()
|
||||
- expand.getCel()->position());
|
||||
const doc::color_t docColor =
|
||||
color_utils::color_for_layer(
|
||||
color, layer);
|
||||
gfx::Rect imageBounds(expand.getCel()->position(),
|
||||
expand.getDestCanvas()->size());
|
||||
doc::Grid grid = site.grid();
|
||||
|
||||
if (site.tilemapMode() == TilemapMode::Tiles)
|
||||
imageBounds = grid.tileToCanvas(imageBounds);
|
||||
|
||||
if (m_type == Stroke) {
|
||||
doc::algorithm::stroke_selection(
|
||||
expand.getDestCanvas(),
|
||||
offset,
|
||||
imageBounds,
|
||||
mask,
|
||||
docColor);
|
||||
color,
|
||||
(site.tilemapMode() == TilemapMode::Tiles ? &grid: nullptr));
|
||||
}
|
||||
else {
|
||||
doc::algorithm::fill_selection(
|
||||
expand.getDestCanvas(),
|
||||
offset,
|
||||
imageBounds,
|
||||
mask,
|
||||
docColor);
|
||||
color,
|
||||
(site.tilemapMode() == TilemapMode::Tiles ? &grid: nullptr));
|
||||
}
|
||||
|
||||
expand.commit();
|
||||
|
@ -1062,25 +1062,18 @@ void GradientInkProcessing<IndexedTraits>::processPixel(int x, int y)
|
||||
template<typename ImageTraits>
|
||||
class XorInkProcessing : public DoubleInkProcessing<XorInkProcessing<ImageTraits>, ImageTraits> {
|
||||
public:
|
||||
XorInkProcessing(ToolLoop* loop) {
|
||||
m_color = loop->getPrimaryColor();
|
||||
}
|
||||
void processPixel(int x, int y) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
private:
|
||||
color_t m_color;
|
||||
XorInkProcessing(ToolLoop* loop) { }
|
||||
void processPixel(int x, int y) { }
|
||||
};
|
||||
|
||||
template<>
|
||||
void XorInkProcessing<RgbTraits>::processPixel(int x, int y) {
|
||||
*m_dstAddress = rgba_blender_neg_bw(*m_srcAddress, m_color, 255);
|
||||
*m_dstAddress = rgba_blender_neg_bw(*m_srcAddress, 0, 255);
|
||||
}
|
||||
|
||||
template<>
|
||||
void XorInkProcessing<GrayscaleTraits>::processPixel(int x, int y) {
|
||||
*m_dstAddress = graya_blender_neg_bw(*m_srcAddress, m_color, 255);
|
||||
*m_dstAddress = graya_blender_neg_bw(*m_srcAddress, 0, 255);
|
||||
}
|
||||
|
||||
template<>
|
||||
@ -1088,19 +1081,17 @@ class XorInkProcessing<IndexedTraits> : public DoubleInkProcessing<XorInkProcess
|
||||
public:
|
||||
XorInkProcessing(ToolLoop* loop) :
|
||||
m_palette(get_current_palette()),
|
||||
m_rgbmap(loop->getRgbMap()),
|
||||
m_color(m_palette->getEntry(loop->getPrimaryColor())) {
|
||||
m_rgbmap(loop->getRgbMap()) {
|
||||
}
|
||||
|
||||
void processPixel(int x, int y) {
|
||||
color_t c = rgba_blender_neg_bw(m_palette->getEntry(*m_srcAddress), m_color, 255);
|
||||
color_t c = rgba_blender_neg_bw(m_palette->getEntry(*m_srcAddress), 0, 255);
|
||||
*m_dstAddress = m_rgbmap->mapColor(c);
|
||||
}
|
||||
|
||||
private:
|
||||
const Palette* m_palette;
|
||||
const RgbMap* m_rgbmap;
|
||||
color_t m_color;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
@ -280,6 +280,7 @@ void PixelsMovement::cutMask()
|
||||
if (writer.cel()) {
|
||||
clear_mask_from_cel(m_tx,
|
||||
writer.cel(),
|
||||
m_site.tilemapMode(),
|
||||
m_site.tilesetMode());
|
||||
|
||||
// Do not trim here so we don't lost the information about all
|
||||
@ -1100,6 +1101,7 @@ void PixelsMovement::reproduceAllTransformationsWithInnerCmds()
|
||||
case InnerCmd::Clear:
|
||||
clear_mask_from_cel(m_tx,
|
||||
m_site.cel(),
|
||||
m_site.tilemapMode(),
|
||||
m_site.tilesetMode());
|
||||
break;
|
||||
case InnerCmd::Flip:
|
||||
|
@ -612,13 +612,14 @@ void modify_tilemap_cel_region(
|
||||
|
||||
void clear_mask_from_cel(CmdSequence* cmds,
|
||||
doc::Cel* cel,
|
||||
const TilemapMode tilemapMode,
|
||||
const TilesetMode tilesetMode)
|
||||
{
|
||||
ASSERT(cmds);
|
||||
ASSERT(cel);
|
||||
ASSERT(cel->layer());
|
||||
|
||||
if (cel->layer()->isTilemap()) {
|
||||
if (cel->layer()->isTilemap() && tilemapMode == TilemapMode::Pixels) {
|
||||
Doc* doc = static_cast<Doc*>(cel->document());
|
||||
|
||||
// Simple case (there is no visible selection, so we remove the
|
||||
@ -640,9 +641,10 @@ void clear_mask_from_cel(CmdSequence* cmds,
|
||||
doc::ImageRef modified(doc::Image::createCopy(origTile.get()));
|
||||
doc::algorithm::fill_selection(
|
||||
modified.get(),
|
||||
mask->bounds().origin() - tileBoundsInCanvas.origin(),
|
||||
tileBoundsInCanvas,
|
||||
mask,
|
||||
bgcolor);
|
||||
bgcolor,
|
||||
nullptr);
|
||||
return modified;
|
||||
});
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -9,6 +9,7 @@
|
||||
#define APP_UTIL_CEL_OPS_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/tilemap_mode.h"
|
||||
#include "app/tileset_mode.h"
|
||||
#include "doc/color.h"
|
||||
#include "doc/frame.h"
|
||||
@ -69,6 +70,7 @@ namespace app {
|
||||
void clear_mask_from_cel(
|
||||
CmdSequence* cmds,
|
||||
doc::Cel* cel,
|
||||
const TilemapMode tilemapMode,
|
||||
const TilesetMode tilesetMode);
|
||||
|
||||
// unusedTiles is a set of possibles tiles to check if they are
|
||||
|
@ -253,7 +253,10 @@ void clear_mask_from_cels(Tx& tx,
|
||||
ObjectId celId = cel->id();
|
||||
|
||||
clear_mask_from_cel(
|
||||
tx, cel, ColorBar::instance()->tilesetMode());
|
||||
tx, cel,
|
||||
// TODO use Site information instead of color bar
|
||||
ColorBar::instance()->tilemapMode(),
|
||||
ColorBar::instance()->tilesetMode());
|
||||
|
||||
// Get cel again just in case the cmd::ClearMask() called cmd::ClearCel()
|
||||
cel = doc::get<Cel>(celId);
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2020 Igara Studio S.A.
|
||||
// Copyright (c) 2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -10,6 +11,7 @@
|
||||
|
||||
#include "doc/algorithm/fill_selection.h"
|
||||
|
||||
#include "doc/grid.h"
|
||||
#include "doc/image_impl.h"
|
||||
#include "doc/mask.h"
|
||||
#include "doc/primitives.h"
|
||||
@ -18,27 +20,40 @@ namespace doc {
|
||||
namespace algorithm {
|
||||
|
||||
void fill_selection(Image* image,
|
||||
const gfx::Point& offset,
|
||||
const gfx::Rect& imageBounds,
|
||||
const Mask* mask,
|
||||
const color_t color)
|
||||
const color_t color,
|
||||
const Grid* grid)
|
||||
{
|
||||
ASSERT(mask);
|
||||
ASSERT(mask->bitmap());
|
||||
if (!mask || !mask->bitmap())
|
||||
return;
|
||||
|
||||
const LockImageBits<BitmapTraits> maskBits(mask->bitmap());
|
||||
LockImageBits<BitmapTraits>::const_iterator it = maskBits.begin();
|
||||
const auto rc = (imageBounds & mask->bounds());
|
||||
if (rc.isEmpty())
|
||||
return; // <- There is no intersection between image bounds and mask bounds
|
||||
|
||||
const gfx::Rect maskBounds = mask->bounds();
|
||||
for (int v=0; v<maskBounds.h; ++v) {
|
||||
for (int u=0; u<maskBounds.w; ++u, ++it) {
|
||||
const LockImageBits<BitmapTraits> maskBits(mask->bitmap(),
|
||||
gfx::Rect(rc).offset(-mask->origin()));
|
||||
auto it = maskBits.begin();
|
||||
|
||||
for (int v=0; v<rc.h; ++v) {
|
||||
for (int u=0; u<rc.w; ++u, ++it) {
|
||||
ASSERT(it != maskBits.end());
|
||||
if (*it) {
|
||||
// TODO use iterators
|
||||
put_pixel(image,
|
||||
u + offset.x,
|
||||
v + offset.y, color);
|
||||
gfx::Point pt(u + rc.x,
|
||||
v + rc.y);
|
||||
|
||||
if (grid) {
|
||||
pt = grid->canvasToTile(pt);
|
||||
}
|
||||
else {
|
||||
pt -= imageBounds.origin();
|
||||
}
|
||||
|
||||
// TODO use iterator
|
||||
put_pixel(image, pt.x, pt.y, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2020 Igara Studio S.A.
|
||||
// Copyright (c) 2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -9,17 +10,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "doc/color.h"
|
||||
#include "gfx/point.h"
|
||||
#include "gfx/rect.h"
|
||||
|
||||
namespace doc {
|
||||
class Grid;
|
||||
class Image;
|
||||
class Mask;
|
||||
|
||||
namespace algorithm {
|
||||
|
||||
void fill_selection(Image* image,
|
||||
const gfx::Point& offset,
|
||||
const Mask* mask,
|
||||
const color_t color);
|
||||
void fill_selection(
|
||||
Image* image,
|
||||
const gfx::Rect& imageBounds,
|
||||
const Mask* mask,
|
||||
// This can be a color_t or a tile_t if the image is a tilemap
|
||||
const color_t color,
|
||||
// Optional grid for tilemaps
|
||||
const Grid* grid = nullptr);
|
||||
|
||||
} // namespace algorithm
|
||||
} // namespace doc
|
||||
|
101
src/doc/algorithm/fill_selection_tests.cpp
Normal file
101
src/doc/algorithm/fill_selection_tests.cpp
Normal file
@ -0,0 +1,101 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2020 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "doc/algorithm/fill_selection.h"
|
||||
#include "doc/grid.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/mask.h"
|
||||
|
||||
using namespace doc;
|
||||
using namespace gfx;
|
||||
|
||||
::testing::AssertionResult cmp_img(const std::vector<color_t>& pixels,
|
||||
const Image* image)
|
||||
{
|
||||
int c = 0;
|
||||
for (int y=0; y<image->height(); ++y) {
|
||||
for (int x=0; x<image->width(); ++x) {
|
||||
if (pixels[c] != image->getPixel(x, y)) {
|
||||
return ::testing::AssertionFailure()
|
||||
<< "ExpectedPixel=" << (int)pixels[c]
|
||||
<< " ActualPixel=" << (int)image->getPixel(x, y)
|
||||
<< " x=" << x
|
||||
<< " y=" << y;
|
||||
}
|
||||
++c;
|
||||
}
|
||||
}
|
||||
return ::testing::AssertionSuccess();
|
||||
}
|
||||
|
||||
TEST(FillSelection, Image)
|
||||
{
|
||||
ImageRef image(Image::create(IMAGE_INDEXED, 4, 4));
|
||||
image->clear(1);
|
||||
|
||||
// No-op (no intersection between image & mask)
|
||||
Mask mask;
|
||||
mask.replace(Rect(0, 0, 1, 5));
|
||||
algorithm::fill_selection(image.get(), Rect(1, 1, 4, 4), &mask, 2, nullptr);
|
||||
EXPECT_TRUE(cmp_img({ 1, 1, 1, 1,
|
||||
1, 1, 1, 1,
|
||||
1, 1, 1, 1,
|
||||
1, 1, 1, 1 }, image.get()));
|
||||
|
||||
mask.replace(Rect(1, 0, 2, 3));
|
||||
algorithm::fill_selection(image.get(), Rect(1, 1, 4, 4), &mask, 2, nullptr);
|
||||
EXPECT_TRUE(cmp_img({ 2, 2, 1, 1,
|
||||
2, 2, 1, 1,
|
||||
1, 1, 1, 1,
|
||||
1, 1, 1, 1 }, image.get()));
|
||||
|
||||
mask.replace(Rect(2, 2, 2, 3));
|
||||
algorithm::fill_selection(image.get(), Rect(1, 3, 4, 4), &mask, 3, nullptr);
|
||||
EXPECT_TRUE(cmp_img({ 2, 3, 3, 1,
|
||||
2, 3, 3, 1,
|
||||
1, 1, 1, 1,
|
||||
1, 1, 1, 1 }, image.get()));
|
||||
}
|
||||
|
||||
TEST(FillSelection, Tilemap)
|
||||
{
|
||||
ImageRef image(Image::create(IMAGE_TILEMAP, 4, 4));
|
||||
image->clear(1);
|
||||
|
||||
Grid grid(Size(8, 8));
|
||||
grid.origin(Point(4, 4));
|
||||
|
||||
// No-op (no intersection between image & mask)
|
||||
Mask mask;
|
||||
mask.replace(Rect(0, 0, 4, 4));
|
||||
algorithm::fill_selection(image.get(), Rect(4, 4, 32, 32), &mask, 2, &grid);
|
||||
EXPECT_TRUE(cmp_img({ 1, 1, 1, 1,
|
||||
1, 1, 1, 1,
|
||||
1, 1, 1, 1,
|
||||
1, 1, 1, 1 }, image.get()));
|
||||
|
||||
mask.replace(Rect(0, 0, 5, 5));
|
||||
algorithm::fill_selection(image.get(), Rect(4, 4, 32, 32), &mask, 2, &grid);
|
||||
EXPECT_TRUE(cmp_img({ 2, 1, 1, 1,
|
||||
1, 1, 1, 1,
|
||||
1, 1, 1, 1,
|
||||
1, 1, 1, 1 }, image.get()));
|
||||
|
||||
mask.replace(Rect(12, 12, 9, 8));
|
||||
algorithm::fill_selection(image.get(), Rect(4, 4, 32, 32), &mask, 3, &grid);
|
||||
EXPECT_TRUE(cmp_img({ 2, 1, 1, 1,
|
||||
1, 3, 3, 1,
|
||||
1, 1, 1, 1,
|
||||
1, 1, 1, 1 }, image.get()));
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
@ -18,9 +18,10 @@ namespace doc {
|
||||
namespace algorithm {
|
||||
|
||||
void stroke_selection(Image* image,
|
||||
const gfx::Point& offset,
|
||||
const gfx::Rect& imageBounds,
|
||||
const Mask* origMask,
|
||||
const color_t color)
|
||||
const color_t color,
|
||||
const Grid* grid)
|
||||
{
|
||||
ASSERT(origMask);
|
||||
ASSERT(origMask->bitmap());
|
||||
@ -44,7 +45,7 @@ void stroke_selection(Image* image,
|
||||
ASSERT(mask.bounds() == origMask->bounds());
|
||||
|
||||
if (mask.bitmap())
|
||||
fill_selection(image, offset, &mask, color);
|
||||
fill_selection(image, imageBounds, &mask, color, grid);
|
||||
}
|
||||
|
||||
} // namespace algorithm
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2020 Igara Studio S.A.
|
||||
// Copyright (c) 2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -9,17 +10,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "doc/color.h"
|
||||
#include "gfx/point.h"
|
||||
#include "gfx/rect.h"
|
||||
|
||||
namespace doc {
|
||||
class Grid;
|
||||
class Image;
|
||||
class Mask;
|
||||
|
||||
namespace algorithm {
|
||||
|
||||
void stroke_selection(Image* image,
|
||||
const gfx::Point& offset,
|
||||
const Mask* mask,
|
||||
const color_t color);
|
||||
void stroke_selection(
|
||||
Image* image,
|
||||
const gfx::Rect& imageBounds,
|
||||
const Mask* mask,
|
||||
// This can be a color_t or a tile_t if the image is a tilemap
|
||||
const color_t color,
|
||||
// Optional grid for tilemaps
|
||||
const Grid* grid = nullptr);
|
||||
|
||||
} // namespace algorithm
|
||||
} // namespace doc
|
||||
|
@ -11,11 +11,13 @@
|
||||
|
||||
#include "doc/cel.h"
|
||||
|
||||
#include "gfx/rect.h"
|
||||
#include "doc/grid.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/layer.h"
|
||||
#include "doc/layer_tilemap.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "doc/tile.h"
|
||||
#include "gfx/rect.h"
|
||||
|
||||
namespace doc {
|
||||
|
||||
@ -146,6 +148,20 @@ void Cel::setParentLayer(LayerImage* layer)
|
||||
fixupImage();
|
||||
}
|
||||
|
||||
Grid Cel::grid() const
|
||||
{
|
||||
if (m_layer) {
|
||||
if (m_layer->isTilemap()) {
|
||||
doc::Grid grid = static_cast<LayerTilemap*>(m_layer)->tileset()->grid();
|
||||
grid.origin(grid.origin() + position());
|
||||
return grid;
|
||||
}
|
||||
else
|
||||
return m_layer->grid();
|
||||
}
|
||||
return Grid();
|
||||
}
|
||||
|
||||
void Cel::fixupImage()
|
||||
{
|
||||
// Change the mask color to the sprite mask color
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2019 Igara Studio S.A.
|
||||
// Copyright (c) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (c) 2001-2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -20,6 +20,7 @@
|
||||
namespace doc {
|
||||
|
||||
class Document;
|
||||
class Grid;
|
||||
class LayerImage;
|
||||
class Sprite;
|
||||
|
||||
@ -67,6 +68,7 @@ namespace doc {
|
||||
}
|
||||
|
||||
void setParentLayer(LayerImage* layer);
|
||||
Grid grid() const;
|
||||
|
||||
private:
|
||||
void fixupImage();
|
||||
|
@ -46,6 +46,7 @@ namespace doc {
|
||||
const ImageSpec& spec() const { return m_spec; }
|
||||
ColorMode colorMode() const { return m_spec.colorMode(); }
|
||||
PixelFormat pixelFormat() const { return (PixelFormat)colorMode(); }
|
||||
bool isTilemap() const { return m_spec.colorMode() == ColorMode::TILEMAP; }
|
||||
int width() const { return m_spec.width(); }
|
||||
int height() const { return m_spec.height(); }
|
||||
gfx::Size size() const { return m_spec.size(); }
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "doc/layer.h"
|
||||
|
||||
#include "doc/cel.h"
|
||||
#include "doc/grid.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/primitives.h"
|
||||
#include "doc/sprite.h"
|
||||
@ -190,6 +191,15 @@ bool Layer::hasAncestor(const Layer* ancestor) const
|
||||
return false;
|
||||
}
|
||||
|
||||
Grid Layer::grid() const
|
||||
{
|
||||
gfx::Rect rc = (m_sprite ? m_sprite->gridBounds():
|
||||
doc::Sprite::DefaultGridBounds());
|
||||
doc::Grid grid = Grid(rc.size());
|
||||
grid.origin(gfx::Point(rc.x % rc.w, rc.y % rc.h));
|
||||
return grid;
|
||||
}
|
||||
|
||||
Cel* Layer::cel(frame_t frame) const
|
||||
{
|
||||
return nullptr;
|
||||
|
@ -21,11 +21,12 @@
|
||||
namespace doc {
|
||||
|
||||
class Cel;
|
||||
class Grid;
|
||||
class Image;
|
||||
class Sprite;
|
||||
class Layer;
|
||||
class LayerGroup;
|
||||
class LayerImage;
|
||||
class Sprite;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Layer class
|
||||
@ -120,6 +121,7 @@ namespace doc {
|
||||
m_flags = LayerFlags(int(m_flags) & ~int(flags));
|
||||
}
|
||||
|
||||
virtual Grid grid() const;
|
||||
virtual Cel* cel(frame_t frame) const;
|
||||
virtual void getCels(CelList& cels) const = 0;
|
||||
virtual void displaceFrames(frame_t fromThis, frame_t delta) = 0;
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2019 Igara Studio S.A.
|
||||
// Copyright (c) 2019-2020 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -28,6 +28,14 @@ LayerTilemap::~LayerTilemap()
|
||||
{
|
||||
}
|
||||
|
||||
Grid LayerTilemap::grid() const
|
||||
{
|
||||
if (m_tileset)
|
||||
return m_tileset->grid();
|
||||
else
|
||||
return Layer::grid();
|
||||
}
|
||||
|
||||
void LayerTilemap::setTilesetIndex(tileset_index tsi)
|
||||
{
|
||||
m_tilesetIndex = tsi;
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2019 Igara Studio S.A.
|
||||
// Copyright (c) 2019-2020 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -19,6 +19,8 @@ namespace doc {
|
||||
explicit LayerTilemap(Sprite* sprite, const tileset_index tsi);
|
||||
~LayerTilemap();
|
||||
|
||||
Grid grid() const override;
|
||||
|
||||
// Returns the tileset of this layer. New automatically-created
|
||||
// tiles should be stored into this tileset, and all tiles in the
|
||||
// layer should share the same Grid spec.
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2020 Igara Studio S.A.
|
||||
// Copyright (c) 2001-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -50,6 +51,7 @@ namespace doc {
|
||||
get_pixel(m_bitmap.get(), u-m_bounds.x, v-m_bounds.y));
|
||||
}
|
||||
|
||||
gfx::Point origin() const { return m_bounds.origin(); }
|
||||
const gfx::Rect& bounds() const { return m_bounds; }
|
||||
|
||||
void setOrigin(int x, int y) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user