1// Aseprite
2// Copyright (C) 2019-2020 Igara Studio S.A.
3// Copyright (C) 2001-2016 David Capello
4//
5// This program is distributed under the terms of
6// the End-User License Agreement for Aseprite.
7
8#ifdef HAVE_CONFIG_H
9#include "config.h"
10#endif
11
12#include "app/cmd/copy_cel.h"
13
14#include "app/cmd/add_cel.h"
15#include "app/cmd/add_frame.h"
16#include "app/cmd/clear_cel.h"
17#include "app/cmd/copy_rect.h"
18#include "app/cmd/remove_cel.h"
19#include "app/cmd/set_cel_data.h"
20#include "app/cmd/unlink_cel.h"
21#include "app/doc.h"
22#include "app/util/cel_ops.h"
23#include "doc/cel.h"
24#include "doc/layer.h"
25#include "doc/primitives.h"
26#include "doc/sprite.h"
27#include "render/rasterize.h"
28#include "render/render.h"
29
30namespace app {
31namespace cmd {
32
33using namespace doc;
34
35CopyCel::CopyCel(
36 Layer* srcLayer, frame_t srcFrame,
37 Layer* dstLayer, frame_t dstFrame, bool continuous)
38 : m_srcLayer(srcLayer)
39 , m_dstLayer(dstLayer)
40 , m_srcFrame(srcFrame)
41 , m_dstFrame(dstFrame)
42 , m_continuous(continuous)
43{
44}
45
46void CopyCel::onExecute()
47{
48 Layer* srcLayer = m_srcLayer.layer();
49 Layer* dstLayer = m_dstLayer.layer();
50
51 ASSERT(srcLayer);
52 ASSERT(dstLayer);
53
54 Sprite* srcSprite = srcLayer->sprite();
55 Sprite* dstSprite = dstLayer->sprite();
56 ASSERT(srcSprite);
57 ASSERT(dstSprite);
58 ASSERT(m_srcFrame >= 0 && m_srcFrame < srcSprite->totalFrames());
59 ASSERT(m_dstFrame >= 0);
60
61 Cel* srcCel = srcLayer->cel(m_srcFrame);
62 Cel* dstCel = dstLayer->cel(m_dstFrame);
63
64 // Clear destination cel if it does exist. It'll be overriden by the
65 // copy of srcCel.
66 if (dstCel) {
67 if (dstCel->links())
68 executeAndAdd(new cmd::UnlinkCel(dstCel));
69 executeAndAdd(new cmd::ClearCel(dstCel));
70 }
71
72 // Add empty frames until newFrame
73 while (dstSprite->totalFrames() <= m_dstFrame)
74 executeAndAdd(new cmd::AddFrame(dstSprite, dstSprite->totalFrames()));
75
76 Image* srcImage = (srcCel ? srcCel->image(): NULL);
77 ImageRef dstImage;
78 dstCel = dstLayer->cel(m_dstFrame);
79 if (dstCel)
80 dstImage = dstCel->imageRef();
81
82 bool createLink =
83 (srcLayer == dstLayer && m_continuous);
84
85 // For background layer
86 if (dstLayer->isBackground()) {
87 ASSERT(dstCel);
88 ASSERT(dstImage);
89 if (!dstCel || !dstImage ||
90 !srcCel || !srcImage)
91 return;
92
93 ASSERT(!dstLayer->isTilemap()); // TODO support background tilemaps
94
95 if (createLink) {
96 executeAndAdd(new cmd::SetCelData(dstCel, srcCel->dataRef()));
97 }
98 // Rasterize tilemap into the regular image background layer
99 else if (srcLayer->isTilemap()) {
100 ImageRef tmp(Image::createCopy(dstImage.get()));
101 render::rasterize(tmp.get(), srcCel, 0, 0, false);
102 executeAndAdd(new cmd::CopyRect(dstImage.get(), tmp.get(), gfx::Clip(tmp->bounds())));
103 }
104 else {
105 BlendMode blend = (srcLayer->isBackground() ?
106 BlendMode::SRC:
107 BlendMode::NORMAL);
108
109 ImageRef tmp(Image::createCopy(dstImage.get()));
110 render::composite_image(
111 tmp.get(), srcImage,
112 srcSprite->palette(m_srcFrame),
113 srcCel->x(), srcCel->y(), 255, blend);
114 executeAndAdd(new cmd::CopyRect(dstImage.get(), tmp.get(), gfx::Clip(tmp->bounds())));
115 }
116 }
117 // For transparent layers
118 else {
119 if (dstCel)
120 executeAndAdd(new cmd::RemoveCel(dstCel));
121
122 if (srcCel) {
123 if (createLink)
124 dstCel = Cel::MakeLink(m_dstFrame, srcCel);
125 else
126 dstCel = create_cel_copy(this, srcCel, dstSprite, dstLayer, m_dstFrame);
127
128 executeAndAdd(new cmd::AddCel(dstLayer, dstCel));
129 }
130 }
131}
132
133void CopyCel::onFireNotifications()
134{
135 CmdSequence::onFireNotifications();
136
137 // The m_srcLayer can be nullptr now because the layer from where we
138 // copied this cel might not exist anymore (e.g. if we copied the
139 // cel from another document that is already closed)
140 //ASSERT(m_srcLayer.layer());
141
142 ASSERT(m_dstLayer.layer());
143
144 static_cast<Doc*>(m_dstLayer.layer()->sprite()->document())
145 ->notifyCelCopied(
146 m_srcLayer.layer(), m_srcFrame,
147 m_dstLayer.layer(), m_dstFrame);
148}
149
150} // namespace cmd
151} // namespace app
152