2015-02-12 23:16:25 +08:00
|
|
|
// Aseprite
|
2018-09-18 11:19:24 +08:00
|
|
|
// Copyright (C) 2001-2018 David Capello
|
2015-02-12 23:16:25 +08:00
|
|
|
//
|
2016-08-27 04:02:58 +08:00
|
|
|
// This program is distributed under the terms of
|
|
|
|
// the End-User License Agreement for Aseprite.
|
2015-01-19 09:05:33 +08:00
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "app/cmd/flatten_layers.h"
|
|
|
|
|
|
|
|
#include "app/cmd/add_layer.h"
|
|
|
|
#include "app/cmd/configure_background.h"
|
|
|
|
#include "app/cmd/copy_rect.h"
|
2018-09-18 11:19:24 +08:00
|
|
|
#include "app/cmd/move_layer.h"
|
2015-01-19 09:05:33 +08:00
|
|
|
#include "app/cmd/remove_layer.h"
|
2015-07-27 19:20:15 +08:00
|
|
|
#include "app/cmd/set_layer_flags.h"
|
2018-07-07 22:54:44 +08:00
|
|
|
#include "app/cmd/set_layer_name.h"
|
2015-03-20 04:37:22 +08:00
|
|
|
#include "app/cmd/unlink_cel.h"
|
2018-07-07 22:54:44 +08:00
|
|
|
#include "app/doc.h"
|
2018-09-18 11:19:24 +08:00
|
|
|
#include "app/restore_visible_layers.h"
|
|
|
|
#include "doc/algorithm/shrink_bounds.h"
|
2015-01-19 09:05:33 +08:00
|
|
|
#include "doc/cel.h"
|
|
|
|
#include "doc/layer.h"
|
|
|
|
#include "doc/primitives.h"
|
|
|
|
#include "doc/sprite.h"
|
|
|
|
#include "render/render.h"
|
|
|
|
|
|
|
|
namespace app {
|
|
|
|
namespace cmd {
|
|
|
|
|
2018-09-18 11:19:24 +08:00
|
|
|
FlattenLayers::FlattenLayers(doc::Sprite* sprite,
|
|
|
|
const doc::SelectedLayers& layers0)
|
2015-01-19 09:05:33 +08:00
|
|
|
: WithSprite(sprite)
|
|
|
|
{
|
2018-09-18 11:19:24 +08:00
|
|
|
doc::SelectedLayers layers(layers0);
|
|
|
|
layers.removeChildrenIfParentIsSelected();
|
|
|
|
|
|
|
|
m_layerIds.reserve(layers.size());
|
|
|
|
for (auto layer : layers)
|
|
|
|
m_layerIds.push_back(layer->id());
|
2015-01-19 09:05:33 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void FlattenLayers::onExecute()
|
|
|
|
{
|
|
|
|
Sprite* sprite = this->sprite();
|
2018-07-07 22:54:44 +08:00
|
|
|
auto doc = static_cast<Doc*>(sprite->document());
|
2015-01-19 09:05:33 +08:00
|
|
|
|
2018-09-18 11:19:24 +08:00
|
|
|
// Set of layers to be flattened.
|
|
|
|
bool backgroundIsSel = false;
|
|
|
|
SelectedLayers layers;
|
|
|
|
for (auto layerId : m_layerIds) {
|
|
|
|
doc::Layer* layer = doc::get<doc::Layer>(layerId);
|
|
|
|
ASSERT(layer);
|
|
|
|
layers.insert(layer);
|
|
|
|
if (layer->isBackground())
|
|
|
|
backgroundIsSel = true;
|
|
|
|
}
|
|
|
|
|
2015-01-19 09:05:33 +08:00
|
|
|
// Create a temporary image.
|
|
|
|
ImageRef image(Image::create(sprite->pixelFormat(),
|
|
|
|
sprite->width(),
|
|
|
|
sprite->height()));
|
|
|
|
|
2015-09-22 01:19:39 +08:00
|
|
|
LayerImage* flatLayer; // The layer onto which everything will be flattened.
|
|
|
|
color_t bgcolor; // The background color to use for flatLayer.
|
2015-09-22 04:47:42 +08:00
|
|
|
|
2015-09-22 01:19:39 +08:00
|
|
|
flatLayer = sprite->backgroundLayer();
|
2018-09-18 11:19:24 +08:00
|
|
|
if (backgroundIsSel && flatLayer && flatLayer->isVisible()) {
|
2015-09-22 01:19:39 +08:00
|
|
|
// There exists a visible background layer, so we will flatten onto that.
|
|
|
|
bgcolor = doc->bgColor(flatLayer);
|
2015-01-19 09:05:33 +08:00
|
|
|
}
|
2015-09-22 04:47:42 +08:00
|
|
|
else {
|
2018-09-18 11:19:24 +08:00
|
|
|
// Create a new transparent layer to flatten everything onto it.
|
2015-09-22 01:19:39 +08:00
|
|
|
flatLayer = new LayerImage(sprite);
|
|
|
|
ASSERT(flatLayer->isVisible());
|
2016-06-08 06:38:56 +08:00
|
|
|
executeAndAdd(new cmd::AddLayer(sprite->root(), flatLayer, nullptr));
|
2015-09-22 01:19:39 +08:00
|
|
|
executeAndAdd(new cmd::SetLayerName(flatLayer, "Flattened"));
|
2015-07-27 19:34:05 +08:00
|
|
|
bgcolor = sprite->transparentColor();
|
2018-09-18 11:19:24 +08:00
|
|
|
|
|
|
|
LayerList list = layers.toLayerList();
|
|
|
|
if (list.front())
|
|
|
|
executeAndAdd(new cmd::MoveLayer(flatLayer,
|
|
|
|
list.front()->parent(),
|
|
|
|
list.front()));
|
2015-09-22 01:19:39 +08:00
|
|
|
}
|
2015-07-27 19:34:05 +08:00
|
|
|
|
2015-01-19 09:05:33 +08:00
|
|
|
render::Render render;
|
|
|
|
render.setBgType(render::BgType::NONE);
|
|
|
|
|
2018-09-18 11:19:24 +08:00
|
|
|
{
|
|
|
|
// Show only the layers to be flattened so other layers are hidden
|
|
|
|
// temporarily.
|
|
|
|
RestoreVisibleLayers restore;
|
|
|
|
restore.showSelectedLayers(sprite, layers);
|
|
|
|
|
|
|
|
// Copy all frames to the background.
|
|
|
|
for (frame_t frame(0); frame<sprite->totalFrames(); ++frame) {
|
|
|
|
// Clear the image and render this frame.
|
|
|
|
clear_image(image.get(), bgcolor);
|
|
|
|
render.renderSprite(image.get(), sprite, frame);
|
|
|
|
|
|
|
|
// TODO Keep cel links when possible
|
|
|
|
|
|
|
|
ImageRef cel_image;
|
|
|
|
Cel* cel = flatLayer->cel(frame);
|
|
|
|
if (cel) {
|
|
|
|
if (cel->links())
|
|
|
|
executeAndAdd(new cmd::UnlinkCel(cel));
|
|
|
|
|
|
|
|
cel_image = cel->imageRef();
|
|
|
|
ASSERT(cel_image);
|
|
|
|
|
|
|
|
executeAndAdd(
|
|
|
|
new cmd::CopyRect(cel_image.get(), image.get(),
|
|
|
|
gfx::Clip(0, 0, image->bounds())));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
gfx::Rect bounds(image->bounds());
|
|
|
|
if (doc::algorithm::shrink_bounds(
|
|
|
|
image.get(), bounds, image->maskColor())) {
|
|
|
|
cel_image.reset(
|
|
|
|
doc::crop_image(image.get(), bounds, image->maskColor()));
|
|
|
|
cel = new Cel(frame, cel_image);
|
|
|
|
cel->setPosition(bounds.origin());
|
|
|
|
flatLayer->addCel(cel);
|
|
|
|
}
|
|
|
|
}
|
2015-01-19 09:05:33 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-18 11:19:24 +08:00
|
|
|
// Delete flattened layers.
|
|
|
|
for (Layer* layer : layers) {
|
|
|
|
// layer can be == flatLayer when we are flattening on the
|
|
|
|
// background layer.
|
|
|
|
if (layer != flatLayer) {
|
2015-01-19 09:05:33 +08:00
|
|
|
executeAndAdd(new cmd::RemoveLayer(layer));
|
2018-09-18 11:19:24 +08:00
|
|
|
}
|
|
|
|
}
|
2015-01-19 09:05:33 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace cmd
|
|
|
|
} // namespace app
|