From 22a4344b14518ea88ca060c3ddecef1ed5bc46ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Mon, 24 Jun 2024 16:14:13 -0300 Subject: [PATCH] Try specific logic to transform slices contents Instead of trying to reuse the PixelsMovement class we create new commands and logic to handle the slices content transformation --- src/app/CMakeLists.txt | 2 + src/app/cmd/clear_slices.cpp | 122 +++++++++ src/app/cmd/clear_slices.h | 65 +++++ src/app/cmd/stamp_in_cel.cpp | 170 ++++++++++++ src/app/cmd/stamp_in_cel.h | 46 ++++ src/app/ui/editor/moving_slice_state.cpp | 317 +++++++++++++++++++---- src/app/ui/editor/moving_slice_state.h | 32 ++- src/doc/mask.h | 2 + 8 files changed, 699 insertions(+), 57 deletions(-) create mode 100644 src/app/cmd/clear_slices.cpp create mode 100644 src/app/cmd/clear_slices.h create mode 100644 src/app/cmd/stamp_in_cel.cpp create mode 100644 src/app/cmd/stamp_in_cel.h diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 7182f1f8a..6fbaaf33e 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -506,6 +506,7 @@ target_sources(app-lib PRIVATE cmd/clear_image.cpp cmd/clear_mask.cpp cmd/clear_rect.cpp + cmd/clear_slices.cpp cmd/configure_background.cpp cmd/convert_color_profile.cpp cmd/copy_cel.cpp @@ -576,6 +577,7 @@ target_sources(app-lib PRIVATE cmd/set_user_data_properties.cpp cmd/set_user_data_property.cpp cmd/shift_masked_cel.cpp + cmd/stamp_in_cel.cpp cmd/trim_cel.cpp cmd/unlink_cel.cpp cmd/with_cel.cpp diff --git a/src/app/cmd/clear_slices.cpp b/src/app/cmd/clear_slices.cpp new file mode 100644 index 000000000..215f95136 --- /dev/null +++ b/src/app/cmd/clear_slices.cpp @@ -0,0 +1,122 @@ +// Aseprite +// Copyright (C) 2024 Igara Studio S.A. +// +// This program is distributed under the terms of +// the End-User License Agreement for Aseprite. + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "app/cmd/clear_slices.h" + +#include "app/doc.h" +#include "doc/algorithm/fill_selection.h" +#include "doc/cel.h" +#include "doc/grid.h" +#include "doc/layer.h" +#include "doc/layer_list.h" +#include "doc/primitives.h" + +namespace app { +namespace cmd { + +using namespace doc; + +ClearSlices::ClearSlices(const LayerList& layers, frame_t frame, const std::vector& slicesKeys) +{ + //Doc* doc = static_cast(cel->document()); + + if (layers.empty()) + return; + + Doc* doc = static_cast((*layers.begin())->sprite()->document()); + + for (const auto& sk : slicesKeys) { + m_mask.add(sk.bounds()); + } + const gfx::Rect maskBounds = m_mask.bounds(); + +// 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::notile; // TODO configurable empty tile +// } +// else { + for (auto* layer : layers) { + Cel* cel = layer->cel(frame); + const gfx::Rect imageBounds = cel->bounds(); + gfx::Rect cropBounds = (imageBounds & maskBounds); + if (cropBounds.isEmpty()) + continue; + + cropBounds.offset(-imageBounds.origin()); + + Image* image = cel->image(); + assert(image); + if (!image) + continue; + + SlicesContent sc; + sc.cel = cel; + sc.cropPos = cropBounds.origin(); + sc.bgcolor = doc->bgColor(layer); + sc.copy.reset(crop_image(image, cropBounds, sc.bgcolor)); + + m_slicesContents.push_back(sc); + } +} + +void ClearSlices::onExecute() +{ + m_seq.execute(context()); + clear(); +} + +void ClearSlices::onUndo() +{ + restore(); + m_seq.undo(); +} + +void ClearSlices::onRedo() +{ + m_seq.redo(); + clear(); +} + +void ClearSlices::clear() +{ + for (auto& sc : m_slicesContents) { + if (!sc.copy) + continue; + + Grid grid = sc.cel->grid(); + doc::algorithm::fill_selection( + sc.cel->image(), + sc.cel->bounds(), + &m_mask, + sc.bgcolor, + (sc.cel->image()->isTilemap() ? &grid: nullptr)); + + } +} + +void ClearSlices::restore() +{ + for (auto& sc : m_slicesContents) { + if (!sc.copy) + continue; + + copy_image(sc.cel->image(), + sc.copy.get(), + sc.cropPos.x, + sc.cropPos.y); + } +} + +} // namespace cmd +} // namespace app diff --git a/src/app/cmd/clear_slices.h b/src/app/cmd/clear_slices.h new file mode 100644 index 000000000..32e7a3db9 --- /dev/null +++ b/src/app/cmd/clear_slices.h @@ -0,0 +1,65 @@ +// Aseprite +// Copyright (C) 2024 Igara Studio S.A. +// +// This program is distributed under the terms of +// the End-User License Agreement for Aseprite. + +#ifndef APP_CMD_CLEAR_SLICES_H_INCLUDED +#define APP_CMD_CLEAR_SLICES_H_INCLUDED +#pragma once + +#include "app/cmd.h" +#include "app/cmd_sequence.h" +#include "doc/cel.h" +#include "doc/image_ref.h" +#include "doc/layer_list.h" +#include "doc/mask.h" +#include "doc/slice.h" + +#include + +namespace app { +namespace cmd { + using namespace doc; + + class ClearSlices : public Cmd { + public: + ClearSlices(const LayerList& layers, frame_t frame, const std::vector& slicesKeys); + + protected: + void onExecute() override; + void onUndo() override; + void onRedo() override; + size_t onMemSize() const override { + size_t sliceContentsSize = 0; + for (const auto& sc : m_slicesContents) { + sliceContentsSize += sc.memSize(); + } + return sizeof(*this) + m_seq.memSize() + sliceContentsSize; + } + + private: + struct SlicesContent { + Cel* cel = nullptr; + // Image having a copy of the content of each selected slice. + ImageRef copy = nullptr; + gfx::Point cropPos; + color_t bgcolor; + size_t memSize() const { + return sizeof(*this) + (copy ? copy->getMemSize(): 0); + } + }; + + void clear(); + void restore(); + + Mask m_mask; + CmdSequence m_seq; + // Slices content for each selected layer's cel + std::vector m_slicesContents; + }; + +} // namespace cmd +} // namespace app + +#endif diff --git a/src/app/cmd/stamp_in_cel.cpp b/src/app/cmd/stamp_in_cel.cpp new file mode 100644 index 000000000..302f8b727 --- /dev/null +++ b/src/app/cmd/stamp_in_cel.cpp @@ -0,0 +1,170 @@ +// Aseprite +// Copyright (C) 2020 Igara Studio S.A. +// Copyright (C) 2016 David Capello +// +// This program is distributed under the terms of +// the End-User License Agreement for Aseprite. + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "app/cmd/stamp_in_cel.h" + +#include "app/cmd/copy_region.h" +#include "app/cmd/crop_cel.h" +#include "app/cmd/trim_cel.h" +#include "app/cmd/with_image.h" +#include "app/util/buffer_region.h" +#include "base/debug.h" +#include "doc/algorithm/rotate.h" +#include "doc/cel.h" +#include "doc/layer_tilemap.h" +#include "doc/mask.h" +#include "gfx/point.h" +#include "gfx/region.h" + + +namespace app { +namespace cmd { + +using namespace doc; + +class StampImage : public Cmd + , public WithImage { + +public: + StampImage(doc::Image* dst, + const doc::ImageRef& src, + const doc::MaskRef& mask, + const gfx::Rect& stampBounds) : WithImage(dst) + , m_src(src) + , m_mask(mask) + , m_stampBounds(stampBounds) { + ASSERT(!stampBounds.isEmpty()); + ASSERT(src); + ASSERT(mask); + + + gfx::Rect rc = stampBounds; + gfx::Clip clip( + rc.x, rc.y, + 0, 0, rc.w, rc.h); + if (clip.clip( + dst->width(), dst->height(), + rc.w, rc.h)) { + // Create region to save/swap later + m_region = gfx::Region(stampBounds); + m_region &= gfx::Region(clip.dstBounds()); + } + + save_image_region_in_buffer(m_region, dst, gfx::Point(0,0), m_buffer); + } + +protected: + void onExecute() override { + ASSERT(image()); + + gfx::Rect rc = m_stampBounds; + doc::algorithm::parallelogram( + image(), m_src.get(), m_mask->bitmap(), + rc.x , rc.y, + rc.x+rc.w , rc.y, + rc.x+rc.w , rc.y+rc.h, + rc.x , rc.y+rc.h + ); + + image()->incrementVersion(); + } + + void onUndo() override { + swap(); + } + + void onRedo() override { + swap(); + } + + void swap() + { + Image* image = this->image(); + ASSERT(image); + + swap_image_region_with_buffer(m_region, image, m_buffer); + image->incrementVersion(); + + //rehash(); + } + + size_t onMemSize() const override { + return sizeof(*this) + m_buffer.size(); + } + +private: + const doc::ImageRef& m_src = nullptr; + const doc::MaskRef& m_mask = nullptr; + const gfx::Rect m_stampBounds; + gfx::Region m_region; + base::buffer m_buffer; +}; + + +StampInCel::StampInCel(doc::Cel* dstCel, + const doc::ImageRef& image, + const doc::MaskRef& mask, + const gfx::Rect& stampBounds) + : WithCel(dstCel) + , m_image(image) + , m_mask(mask) + , m_stampBounds(stampBounds) +{ + ASSERT(image.get()); + ASSERT(mask.get()); + ASSERT(!stampBounds.isEmpty()); +} + +void StampInCel::onExecute() +{ + Cel* cel = this->cel(); + + gfx::Rect newBounds; + gfx::Region regionInTiles; + doc::Grid grid; + if (cel->image()->pixelFormat() == IMAGE_TILEMAP) { + //newBounds = cel->bounds() | m_region.bounds(); + //auto tileset = static_cast(cel->layer())->tileset(); + //grid = tileset->grid(); + //grid.origin(m_pos); + //regionInTiles = grid.canvasToTile(m_region); + } + else { + newBounds = cel->bounds() | m_stampBounds; + } + + if (cel->bounds() != newBounds) + executeAndAdd(new CropCel(cel, newBounds)); + + if (cel->image()->pixelFormat() == IMAGE_TILEMAP) { +/* + executeAndAdd( + new CopyRegion(cel->image(), + m_patch, + regionInTiles, + -grid.canvasToTile(cel->position()))); +*/ + } + else { + executeAndAdd( + new StampImage(cel->image(), + m_image, + m_mask, + m_stampBounds.offset(-cel->position()))); + + } + + executeAndAdd(new TrimCel(cel)); +} + +} // namespace cmd +} // namespace app diff --git a/src/app/cmd/stamp_in_cel.h b/src/app/cmd/stamp_in_cel.h new file mode 100644 index 000000000..520d67849 --- /dev/null +++ b/src/app/cmd/stamp_in_cel.h @@ -0,0 +1,46 @@ +// Aseprite +// Copyright (C) 2024 Igara Studio S.A. +// +// This program is distributed under the terms of +// the End-User License Agreement for Aseprite. + +#ifndef APP_CMD_STAMP_IN_CEL_H_INCLUDED +#define APP_CMD_STAMP_IN_CEL_H_INCLUDED +#pragma once + +#include "app/cmd/with_cel.h" +#include "app/cmd_sequence.h" +#include "doc/mask.h" +#include "gfx/rect.h" + +namespace doc { + class Cel; + class Image; +} + +namespace app { +namespace cmd { + + class StampInCel : public CmdSequence + , public WithCel { + public: + // Stamps image into dstCel using the specified mask. The image is + // positioned and scaled according to the stampBounds rectangle. + StampInCel(doc::Cel* dstCel, + const doc::ImageRef& image, + const doc::MaskRef& mask, + const gfx::Rect& stampBounds); + + protected: + void onExecute() override; + + private: + const doc::ImageRef m_image; + const doc::MaskRef m_mask; + gfx::Rect m_stampBounds; + }; + +} // namespace cmd +} // namespace app + +#endif diff --git a/src/app/ui/editor/moving_slice_state.cpp b/src/app/ui/editor/moving_slice_state.cpp index da3fcb300..dd5865d32 100644 --- a/src/app/ui/editor/moving_slice_state.cpp +++ b/src/app/ui/editor/moving_slice_state.cpp @@ -9,23 +9,37 @@ #include "config.h" #endif -#include "app/ui/editor/moving_slice_state.h" - +#include "app/cmd/stamp_in_cel.h" +#include "app/cmd_sequence.h" +#include "app/cmd_transaction.h" #include "app/cmd/set_slice_key.h" +#include "app/cmd/clear_slices.h" #include "app/context_access.h" #include "app/tx.h" #include "app/ui/editor/editor.h" +#include "app/ui/editor/moving_slice_state.h" #include "app/ui/status_bar.h" #include "app/ui_context.h" #include "app/util/new_image_from_mask.h" -#include "doc/slice.h" +#include "doc/algorithm/rotate.h" +#include "doc/blend_internals.h" +#include "doc/color.h" +#include "doc/image_ref.h" #include "doc/mask.h" +#include "doc/primitives.h" +#include "doc/slice.h" +#include "doc/algorithm/fill_selection.h" #include "gfx/point.h" +#include "gfx/rect.h" +#include "gfx/region_skia.h" #include "ui/message.h" +#include "render/render.h" #include #include +#include #include +#include namespace app { @@ -38,6 +52,10 @@ MovingSliceState::MovingSliceState(Editor* editor, : m_frame(editor->frame()) , m_hit(hit) , m_items(std::max(1, selectedSlices.size())) + //, m_reader(UIContext::instance()) + , m_tx(Tx::DontLockDoc, UIContext::instance(), + UIContext::instance()->activeDocument(), + (editor->slicesTransforms() ? "Slices Transformation" : "Slice Movement")) { m_mouseStart = editor->screenToEditor(msg->position()); @@ -52,84 +70,118 @@ MovingSliceState::MovingSliceState(Editor* editor, } } + editor->getSite(&m_site); + + m_selectedLayers = m_site.range().selectedLayers().toAllLayersList(); + if (m_selectedLayers.empty()) { + m_selectedLayers.push_back(m_site.layer()); + } + editor->captureMouse(); } void MovingSliceState::onEnterState(Editor* editor) { if (editor->slicesTransforms() && !m_items.empty()) { - auto site = Site(); - editor->getSite(&site); - ImageRef tmpImage; + for (auto& item : m_items) { + item.imgs.reserve(m_selectedLayers.size()); + item.masks.reserve(m_selectedLayers.size()); + int i = 0; + for (const auto* layer : m_selectedLayers) { + item.masks.push_back(std::make_shared()); + item.imgs.push_back(ImageRef()); + item.masks[i]->add(item.newKey.bounds()); + item.masks[i]->freeze(); + if (layer && + layer->isTilemap() && + m_site.tilemapMode() == TilemapMode::Tiles) { + //item.img.reset(new_tilemap_from_mask(m_site, item.mask.get())); + } + else { + item.imgs[i].reset(new_image_from_mask( + *layer, + m_frame, + item.masks[i].get(), + Preferences::instance().experimental.newBlend())); + item.masks[i]->fromImage(item.imgs[i].get(), item.masks[i]->origin()); + } - Mask newMask; - for (const auto& item : m_items) { - newMask.add(item.newKey.bounds()); - } - - if (site.layer() && - site.layer()->isTilemap() && - site.tilemapMode() == TilemapMode::Tiles) { - tmpImage.reset(new_tilemap_from_mask(site, &newMask)); - } - else { - tmpImage.reset(new_image_from_mask(site, &newMask, - Preferences::instance().experimental.newBlend())); - } - - ASSERT(tmpImage); - if (!tmpImage) { - editor->releaseMouse(); - // We've received a bug report with this case, we're not sure - // yet how to reproduce it. Probably new_tilemap_from_mask() can - // return nullptr (e.g. when site.cel() is nullptr?) - editor->backToPreviousState(); - return; + // If there is just one layer selected, we can use the same image as the + // mergedImg. + if (m_selectedLayers.size() == 1) { + item.mergedImg = item.imgs[0]; + item.mergedMask = item.masks[0]; + } + else { + if (i == 0) { + const gfx::Rect& srcBounds = item.imgs[i]->bounds(); + item.mergedImg.reset(Image::create(layer->sprite()->pixelFormat(), srcBounds.w, srcBounds.h)); + item.mergedImg->clear(layer->sprite()->transparentColor()); + item.mergedMask = std::make_shared(*item.masks[i].get()); + item.mergedMask->freeze(); + } + else { + item.mergedMask->add(*item.masks[i].get()); + } + copy_masked_zones(item.mergedImg.get(), + item.imgs[i].get(), + item.masks[i].get(), + item.masks[i]->bounds().x, + item.masks[i]->bounds().y); + } + i++; + } } // Clear brush preview, as the extra cel will be replaced with the // transformed image. editor->brushPreview().hide(); - m_pixelsMovement = PixelsMovementPtr( - new PixelsMovement(UIContext::instance(), - site, - tmpImage.get(), - &newMask, - "Transformation")); + clearSlices(); - const gfx::Rect totalBounds = selectedSlicesBounds(); - if (m_hit.border() == (CENTER | MIDDLE)) { - m_pixelsMovement->catchImage(gfx::PointF(totalBounds.origin()), - HandleType::MovePixelsHandle); + if (editor->slicesTransforms()) { + drawExtraCel(editor); + + // Redraw the editor. + editor->invalidate(); } - m_pixelsMovement->cutMask(); } } EditorState::LeaveAction MovingSliceState::onLeaveState(Editor *editor, EditorState *newState) { - editor->document()->resetTransformation(); + //editor->document()->resetTransformation(); + return StandbyState::onLeaveState(editor, newState); } bool MovingSliceState::onMouseUp(Editor* editor, MouseMessage* msg) { - if (m_pixelsMovement) { - m_pixelsMovement->dropImage(); - m_pixelsMovement.reset(); - } { ContextWriter writer(UIContext::instance(), 1000); - Tx tx(writer, "Slice Movement", ModifyDocument); - + CmdTransaction* cmds = m_tx; for (const auto& item : m_items) { item.slice->insert(m_frame, item.oldKey); - tx(new cmd::SetSliceKey(item.slice, m_frame, item.newKey)); + cmds->addAndExecute(writer.context(), + new cmd::SetSliceKey(item.slice, m_frame, item.newKey)); + + if (editor->slicesTransforms()) { + for (int i=0; icel(m_frame); + cmds->addAndExecute(writer.context(), + new cmd::StampInCel( + cel, + item.imgs[i], + item.masks[i], + item.newKey.bounds() + )); + } + } } - tx.commit(); + m_tx.commit(); } editor->backToPreviousState(); @@ -138,6 +190,112 @@ bool MovingSliceState::onMouseUp(Editor* editor, MouseMessage* msg) return true; } +void MovingSliceState::drawExtraCel(Editor* editor) +{ + int t, opacity = (m_site.layer()->isImage() ? + static_cast(m_site.layer())->opacity(): 255); + Cel* cel = m_site.cel(); + if (cel) opacity = MUL_UN8(opacity, cel->opacity(), t); + + if (!m_extraCel) + m_extraCel.reset(new ExtraCel); + + gfx::Rect bounds; + for (auto& item : m_items) + bounds |= item.newKey.bounds(); + + + if (!bounds.isEmpty()) { + gfx::Size extraCelSize; + if (m_site.tilemapMode() == TilemapMode::Tiles) { + // Transforming tiles + extraCelSize = m_site.grid().canvasToTile(bounds).size(); + } + else { + // Transforming pixels + extraCelSize = bounds.size(); + } + + m_extraCel->create( + m_site.tilemapMode(), + m_site.document()->sprite(), + bounds, + extraCelSize, + m_site.frame(), + opacity); + m_extraCel->setType(render::ExtraType::PATCH); + m_extraCel->setBlendMode(m_site.layer()->isImage() ? + static_cast(m_site.layer())->blendMode(): + doc::BlendMode::NORMAL); + } + + m_site.document()->setExtraCel(m_extraCel); + + if (m_extraCel->image()) { + Image* dst = m_extraCel->image(); + if (m_site.tilemapMode() == TilemapMode::Tiles) { + dst->setMaskColor(doc::notile); + dst->clear(dst->maskColor()); + /* + TODO: Fix this when the TilemapMode::Pixels mode works + if (m_site.cel()) { + doc::Grid grid = m_site.grid(); + dst->copy(m_site.cel()->image(), + gfx::Clip(0, 0, grid.canvasToTile(bounds))); + //dst->copy(item.img.get(), + // gfx::Clip(0, 0, grid.canvasToTile(bounds))); + } + */ + } + else { + dst->setMaskColor(m_site.sprite()->transparentColor()); + dst->clear(dst->maskColor()); + + render::Render render; + render.renderLayer( + dst, m_site.layer(), m_site.frame(), + gfx::Clip(0, 0, bounds), + doc::BlendMode::SRC); + + } + + for (auto& item : m_items) { + // Draw the transformed pixels in the extra-cel which is the chunk + // of pixels that the user is moving. + drawImage(item, dst, gfx::PointF(bounds.origin())); + } + } +} + + +void MovingSliceState::drawImage(const Item& item, doc::Image* dst, const gfx::PointF& pt) +{ + ASSERT(dst); + + if (!item.mergedImg.get()) return; + + gfx::Rect bounds = item.newKey.bounds(); + + if (m_site.tilemapMode() == TilemapMode::Tiles) { + + /* TODO: Finish this when TilemapMode::Pixels works + drawTransformedTilemap( + transformation, + dst, m_originalImage.get(), + m_initialMask.get()); + */ + } + else { + doc::algorithm::parallelogram( + dst, item.mergedImg.get(), item.mergedMask->bitmap(), + bounds.x-pt.x , bounds.y-pt.y, + bounds.x+bounds.w-pt.x, bounds.y-pt.y, + bounds.x+bounds.w-pt.x, bounds.y+bounds.h-pt.y, + bounds.x-pt.x , bounds.y+bounds.h-pt.y + ); + } +} + bool MovingSliceState::onMouseMove(Editor* editor, MouseMessage* msg) { gfx::Point newCursorPos = editor->screenToEditor(msg->position()); @@ -159,12 +317,6 @@ bool MovingSliceState::onMouseMove(Editor* editor, MouseMessage* msg) if (m_hit.border() == (CENTER | MIDDLE)) { rc.x += delta.x; rc.y += delta.y; - - if (m_pixelsMovement) { - m_pixelsMovement->moveImage(gfx::PointF(totalBounds.origin()+delta), - PixelsMovement::MoveModifier::NormalMovement); - m_pixelsMovement->dropImageTemporarily(); - } } // Move/resize 9-slices center else if (m_hit.type() == EditorHit::SliceCenter) { @@ -236,6 +388,9 @@ bool MovingSliceState::onMouseMove(Editor* editor, MouseMessage* msg) item.slice->insert(m_frame, key); } + if (editor->slicesTransforms()) + drawExtraCel(editor); + // Redraw the editor. editor->invalidate(); @@ -295,4 +450,56 @@ gfx::Rect MovingSliceState::selectedSlicesBounds() const return bounds; } +void MovingSliceState::clearSlices() +{ + ContextWriter writer(UIContext::instance(), 1000); + if (writer.cel()) { + std::vector slicesKeys; + slicesKeys.reserve(m_items.size()); + for (auto& item : m_items) { + slicesKeys.push_back(item.newKey); + } + + // TODO: Add tilemap and tileset case. + auto tilemapMode = m_site.tilemapMode(); + auto tilesetMode = m_site.tilesetMode(); + + CmdTransaction* cmds = m_tx; + //if (cel->layer()->isTilemap() && tilemapMode == TilemapMode::Pixels) { + /* + Doc* doc = static_cast(cel->document()); + + // Simple case (there is no visible selection, so we remove the + // whole cel) + if (!doc->isMaskVisible()) { + cmds->executeAndAdd(new cmd::ClearCel(cel)); + return; + } + + color_t bgcolor = doc->bgColor(cel->layer()); + doc::Mask* mask = doc->mask(); + + modify_tilemap_cel_region( + cmds, cel, nullptr, + gfx::Region(doc->mask()->bounds()), + tilesetMode, + [bgcolor, mask](const doc::ImageRef& origTile, + const gfx::Rect& tileBoundsInCanvas) -> doc::ImageRef { + doc::ImageRef modified(doc::Image::createCopy(origTile.get())); + doc::algorithm::fill_selection( + modified.get(), + tileBoundsInCanvas, + mask, + bgcolor, + nullptr); + return modified; + }); + */ + //} + //else { + cmds->executeAndAdd(new cmd::ClearSlices(m_selectedLayers, m_frame, slicesKeys)); + //} + } +} + } // namespace app diff --git a/src/app/ui/editor/moving_slice_state.h b/src/app/ui/editor/moving_slice_state.h index 689ee561f..034ec4ed1 100644 --- a/src/app/ui/editor/moving_slice_state.h +++ b/src/app/ui/editor/moving_slice_state.h @@ -13,6 +13,9 @@ #include "app/ui/editor/standby_state.h" #include "app/ui/editor/pixels_movement.h" #include "doc/frame.h" +#include "doc/image_ref.h" +#include "doc/mask.h" +#include "doc/selected_layers.h" #include "doc/selected_objects.h" #include "doc/slice.h" @@ -39,17 +42,42 @@ namespace app { doc::Slice* slice; doc::SliceKey oldKey; doc::SliceKey newKey; + // Images containing the parts of each selected layer of the sprite under + // the slice bounds that will be transformed when Slice Transform is + // enabled + std::vector imgs; + // Masks for each of the images in imgs vector + std::vector masks; + + // Image containing the result of merging all the images in the imgs + // vector + ImageRef mergedImg = nullptr; + MaskRef mergedMask = nullptr; + + ~Item() { + if (!masks.empty() && mergedMask != masks[0]) + mergedMask->unfreeze(); + for (auto& m : masks) + m->unfreeze(); + } }; Item getItemForSlice(doc::Slice* slice); gfx::Rect selectedSlicesBounds() const; + void drawExtraCel(Editor* editor); + void drawImage(const Item& item, doc::Image* dst, const gfx::PointF& pt); + + void clearSlices(); + doc::frame_t m_frame; EditorHit m_hit; gfx::Point m_mouseStart; std::vector m_items; - // Helper member to move/translate the pixels under the slices. - PixelsMovementPtr m_pixelsMovement = nullptr; + LayerList m_selectedLayers; + Site m_site; + ExtraCelRef m_extraCel = nullptr; + Tx m_tx; }; } // namespace app diff --git a/src/doc/mask.h b/src/doc/mask.h index e02465904..54a5c85bf 100644 --- a/src/doc/mask.h +++ b/src/doc/mask.h @@ -119,6 +119,8 @@ namespace doc { Mask& operator=(const Mask& mask); }; + typedef std::shared_ptr MaskRef; + } // namespace doc #endif