1// Aseprite
2// Copyright (C) 2019-2020 Igara Studio S.A.
3// Copyright (C) 2001-2018 David Capello
4//
5// This program is distributed under the terms of
6// the End-User License Agreement for Aseprite.
7
8// Uncomment this in case you want to debug range ops
9//#define TRACE_RANGE_OPS
10
11#ifdef HAVE_CONFIG_H
12#include "config.h"
13#endif
14
15#include "app/doc_range_ops.h"
16
17#include "app/app.h"
18#include "app/context_access.h"
19#include "app/doc_api.h"
20#include "app/doc_range.h"
21#include "app/transaction.h"
22#include "app/tx.h"
23#include "doc/layer.h"
24#include "doc/sprite.h"
25
26#include <stdexcept>
27
28#ifdef TRACE_RANGE_OPS
29#include <iostream>
30#endif
31
32namespace app {
33
34enum Op { Move, Copy };
35
36static void move_or_copy_cels(
37 DocApi& api, Op op,
38 const LayerList& srcLayers,
39 const LayerList& dstLayers,
40 const SelectedFrames& srcFrames,
41 const SelectedFrames& dstFrames)
42{
43 ASSERT(srcLayers.size() == dstLayers.size());
44
45 for (layer_t i=0; i<srcLayers.size(); ++i) {
46 auto srcFrame = srcFrames.begin();
47 auto dstFrame = dstFrames.begin();
48 auto srcFrameEnd = srcFrames.end();
49 auto dstFrameEnd = dstFrames.end();
50
51 for (; srcFrame != srcFrameEnd &&
52 dstFrame != dstFrameEnd; ++srcFrame, ++dstFrame) {
53 if (i >= 0 && i < srcLayers.size() && srcLayers[i]->isImage()) {
54 LayerImage* srcLayer = static_cast<LayerImage*>(srcLayers[i]);
55
56 if (i < dstLayers.size() && dstLayers[i]->isImage()) {
57 LayerImage* dstLayer = static_cast<LayerImage*>(dstLayers[i]);
58
59#ifdef TRACE_RANGE_OPS
60 std::clog << (op == Move ? "Moving": "Copying")
61 << " cel " << srcLayer->name() << "[" << *srcFrame << "]"
62 << " into " << dstLayer->name() << "[" << *dstFrame << "]\n";
63#endif
64
65 switch (op) {
66 case Move: api.moveCel(srcLayer, *srcFrame, dstLayer, *dstFrame); break;
67 case Copy: api.copyCel(srcLayer, *srcFrame, dstLayer, *dstFrame); break;
68 }
69 }
70 // All cels moved from a image layer and dropped in other kind
71 // of layer (e.g. a group) will be discarded/deleted.
72 else if (op == Move) {
73 api.clearCel(srcLayer, *srcFrame);
74 }
75 }
76 }
77 }
78}
79
80static DocRange move_or_copy_frames(
81 DocApi& api, Op op,
82 Sprite* sprite,
83 const DocRange& srcRange,
84 frame_t dstFrame,
85 const DocRangePlace place,
86 const TagsHandling tagsHandling)
87{
88 const SelectedFrames& srcFrames = srcRange.selectedFrames();
89
90#ifdef TRACE_RANGE_OPS
91 std::clog << "move_or_copy_frames frames[";
92 for (auto srcFrame : srcFrames) {
93 std::clog << srcFrame << ", ";
94 }
95 std::clog << "] "
96 << (place == kDocRangeBefore ? "before":
97 place == kDocRangeAfter ? "after":
98 "as first child")
99 << " " << dstFrame << "\n";
100#endif
101
102 auto srcFrame = srcFrames.begin();
103 auto srcFrameEnd = srcFrames.end();
104 frame_t srcDelta = 0;
105 frame_t firstCopiedBlock = 0;
106 frame_t dstBeforeFrame =
107 (place == kDocRangeBefore ? dstFrame:
108 dstFrame+1);
109
110 for (; srcFrame != srcFrameEnd; ++srcFrame) {
111 frame_t fromFrame = (*srcFrame)+srcDelta;
112
113 switch (op) {
114
115 case Move:
116 if ((*srcFrame) >= dstBeforeFrame) {
117 srcDelta = 0;
118 fromFrame = *srcFrame;
119 }
120 break;
121
122 case Copy:
123 if (fromFrame >= dstBeforeFrame-1 && firstCopiedBlock) {
124 srcDelta += firstCopiedBlock;
125 fromFrame += firstCopiedBlock;
126 firstCopiedBlock = 0;
127 }
128 break;
129 }
130
131#ifdef TRACE_RANGE_OPS
132 std::clog << " [";
133 for (frame_t i=0; i<=sprite->lastFrame(); ++i) {
134 std::clog << (sprite->frameDuration(i)-1);
135 }
136 std::clog << "] => "
137 << (op == Move ? "Move": "Copy")
138 << " " << (*srcFrame) << "+" << (srcDelta)
139 << (place == kDocRangeBefore ? " before ": " after ")
140 << dstFrame << " => ";
141#endif
142
143 switch (op) {
144
145 case Move:
146 api.moveFrame(sprite, fromFrame, dstFrame,
147 (place == kDocRangeBefore ? kDropBeforeFrame:
148 kDropAfterFrame),
149 tagsHandling);
150
151 if (fromFrame < dstBeforeFrame-1) {
152 --srcDelta;
153 }
154 else if (fromFrame > dstBeforeFrame-1) {
155 ++dstBeforeFrame;
156 ++dstFrame;
157 }
158 break;
159
160 case Copy:
161 api.copyFrame(sprite, fromFrame, dstFrame,
162 (place == kDocRangeBefore ? kDropBeforeFrame:
163 kDropAfterFrame),
164 tagsHandling);
165
166 if (fromFrame < dstBeforeFrame-1) {
167 ++firstCopiedBlock;
168 }
169 else if (fromFrame >= dstBeforeFrame-1) {
170 ++srcDelta;
171 }
172 ++dstBeforeFrame;
173 ++dstFrame;
174 break;
175 }
176
177#ifdef TRACE_RANGE_OPS
178 std::clog << " [";
179 for (frame_t i=0; i<=sprite->lastFrame(); ++i) {
180 std::clog << (sprite->frameDuration(i)-1);
181 }
182 std::clog << "]\n";
183#endif
184 }
185
186 DocRange result;
187 if (!srcRange.selectedLayers().empty())
188 result.selectLayers(srcRange.selectedLayers());
189 result.startRange(nullptr, dstBeforeFrame-srcFrames.size(), DocRange::kFrames);
190 result.endRange(nullptr, dstBeforeFrame-1);
191 return result;
192}
193
194static bool has_child(LayerGroup* parent, Layer* child)
195{
196 for (auto c : parent->layers()) {
197 if (c == child)
198 return true;
199 else if (c->isGroup() &&
200 has_child(static_cast<LayerGroup*>(c), child))
201 return true;
202 }
203 return false;
204}
205
206static DocRange drop_range_op(
207 Doc* doc,
208 const Op op,
209 const DocRange& from,
210 DocRangePlace place,
211 const TagsHandling tagsHandling,
212 DocRange to)
213{
214 // Convert "first child" operation into a insert after last child.
215 LayerGroup* parent = nullptr;
216 if (to.type() == DocRange::kLayers &&
217 !to.selectedLayers().empty()) {
218 if (place == kDocRangeFirstChild &&
219 (*to.selectedLayers().begin())->isGroup()) {
220 place = kDocRangeAfter;
221 parent = static_cast<LayerGroup*>((*to.selectedLayers().begin()));
222
223 to.clearRange();
224 to.startRange(parent->lastLayer(), -1, DocRange::kLayers);
225 to.endRange(parent->lastLayer(), -1);
226 }
227 else {
228 parent = (*to.selectedLayers().begin())->parent();
229 }
230
231 // Check that we're not moving a group inside itself
232 for (auto moveThis : from.selectedLayers()) {
233 if (moveThis == parent ||
234 (moveThis->isGroup() &&
235 has_child(static_cast<LayerGroup*>(moveThis), parent)))
236 return from;
237 }
238 }
239
240 if (place != kDocRangeBefore &&
241 place != kDocRangeAfter) {
242 ASSERT(false);
243 throw std::invalid_argument("Invalid 'place' argument");
244 }
245
246 Sprite* sprite = doc->sprite();
247
248 // Check noop/trivial/do nothing cases, i.e., move a range to the same place.
249 // Also check invalid cases, like moving a Background layer.
250 switch (from.type()) {
251
252 case DocRange::kCels:
253 if (from == to)
254 return from;
255 break;
256
257 case DocRange::kFrames:
258 if (op == Move) {
259 // Simple cases with one continuos range of frames that are a
260 // no-op.
261 if ((from.selectedFrames().ranges() == 1) &&
262 ((to.firstFrame() >= from.firstFrame() &&
263 to.lastFrame() <= from.lastFrame()) ||
264 (place == kDocRangeBefore && to.firstFrame() == from.lastFrame()+1) ||
265 (place == kDocRangeAfter && to.lastFrame() == from.firstFrame()-1)) &&
266 // If there are tags, this might not be a no-op
267 (sprite->tags().empty() ||
268 tagsHandling == kDontAdjustTags)) {
269 return from;
270 }
271 }
272 break;
273
274 case DocRange::kLayers:
275 if (op == Move) {
276 SelectedLayers srcSelLayers = from.selectedLayers();
277 SelectedLayers dstSelLayers = to.selectedLayers();
278 LayerList srcLayers = srcSelLayers.toBrowsableLayerList();
279 LayerList dstLayers = dstSelLayers.toBrowsableLayerList();
280 ASSERT(!srcLayers.empty());
281 if (srcLayers.empty())
282 return from;
283
284 // dstLayers can be nullptr when we insert the first child in
285 // a group.
286
287 // Check no-ops when we move layers at the same level (all
288 // layers with the same parent), all adjacents, and which are
289 // moved to the same place.
290 if (!dstSelLayers.empty() &&
291 srcSelLayers.hasSameParent() &&
292 dstSelLayers.hasSameParent() &&
293 are_layers_adjacent(srcLayers) &&
294 are_layers_adjacent(dstLayers)) {
295 for (Layer* srcLayer : srcLayers)
296 if (dstSelLayers.contains(srcLayer))
297 return from;
298
299 if ((place == kDocRangeBefore
300 && dstLayers.front() == srcLayers.back()->getNext()) ||
301 (place == kDocRangeAfter
302 && dstLayers.back() == srcLayers.front()->getPrevious()))
303 return from;
304 }
305
306 // We cannot move the background
307 for (Layer* layer : srcSelLayers)
308 if (layer->isBackground())
309 throw std::runtime_error("The background layer cannot be moved");
310 }
311
312 // Before background
313 if (place == kDocRangeBefore) {
314 for (Layer* background : to.selectedLayers()) {
315 if (background && background->isBackground())
316 throw std::runtime_error("You cannot move or copy something below the background layer");
317 }
318 }
319 break;
320 }
321
322 const char* undoLabel = NULL;
323 switch (op) {
324 case Move: undoLabel = "Move Range"; break;
325 case Copy: undoLabel = "Copy Range"; break;
326 default:
327 ASSERT(false);
328 throw std::invalid_argument("Invalid 'op' argument");
329 }
330 DocRange resultRange;
331
332 {
333 const app::Context* context = static_cast<app::Context*>(doc->context());
334 const ContextReader reader(context);
335 ContextWriter writer(reader);
336 Tx tx(writer.context(), undoLabel, ModifyDocument);
337 DocApi api = doc->getApi(tx);
338
339 // TODO Try to add the range with just one call to DocApi
340 // methods, to avoid generating a lot of cmd::SetCelFrame (see
341 // DocApi::setCelFramePosition() function).
342
343 switch (from.type()) {
344
345 case DocRange::kCels: {
346 LayerList allLayers = sprite->allBrowsableLayers();
347 if (allLayers.empty())
348 break;
349
350 LayerList srcLayers = from.selectedLayers().toBrowsableLayerList();
351 LayerList dstLayers = to.selectedLayers().toBrowsableLayerList();
352 if (srcLayers.empty() ||
353 dstLayers.empty())
354 throw std::invalid_argument("You need to specify a non-empty cels range");
355
356 if (find_layer_index(allLayers, srcLayers.front()) <
357 find_layer_index(allLayers, dstLayers.front())) {
358 std::reverse(srcLayers.begin(), srcLayers.end());
359 std::reverse(dstLayers.begin(), dstLayers.end());
360 }
361
362 if (from.firstFrame() < to.firstFrame()) {
363 auto srcFrames = from.selectedFrames().makeReverse();
364 auto dstFrames = to.selectedFrames().makeReverse();
365
366 move_or_copy_cels(api, op, srcLayers, dstLayers, srcFrames, dstFrames);
367 }
368 else {
369 const auto& srcFrames = from.selectedFrames();
370 const auto& dstFrames = to.selectedFrames();
371
372 move_or_copy_cels(api, op, srcLayers, dstLayers, srcFrames, dstFrames);
373 }
374
375 resultRange = to;
376 break;
377 }
378
379 case DocRange::kFrames: {
380 frame_t dstFrame;
381 if (place == kDocRangeBefore)
382 dstFrame = to.firstFrame();
383 else
384 dstFrame = to.lastFrame();
385
386 resultRange = move_or_copy_frames(api, op, sprite,
387 from, dstFrame,
388 place, tagsHandling);
389 break;
390 }
391
392 case DocRange::kLayers: {
393 LayerList allLayers = sprite->allBrowsableLayers();
394 if (allLayers.empty())
395 break;
396
397 LayerList srcLayers = from.selectedLayers().toBrowsableLayerList();
398 LayerList dstLayers = to.selectedLayers().toBrowsableLayerList();
399 ASSERT(!srcLayers.empty());
400
401 switch (op) {
402
403 case Move:
404 if (place == kDocRangeBefore) {
405 Layer* beforeThis = (!dstLayers.empty() ? dstLayers.front(): nullptr);
406 Layer* afterThis = nullptr;
407
408 for (Layer* srcLayer : srcLayers) {
409 if (afterThis)
410 api.restackLayerAfter(srcLayer, parent, afterThis);
411 else
412 api.restackLayerBefore(srcLayer, parent, beforeThis);
413
414 afterThis = srcLayer;
415 }
416 }
417 else if (place == kDocRangeAfter) {
418 Layer* afterThis = (!dstLayers.empty() ? dstLayers.back(): nullptr);
419 for (Layer* srcLayer : srcLayers) {
420 api.restackLayerAfter(srcLayer, parent, afterThis);
421 afterThis = srcLayer;
422 }
423 }
424
425 // Same set of layers than the "from" range
426 resultRange = from;
427 break;
428
429 case Copy: {
430 if (place == kDocRangeBefore) {
431 Layer* beforeThis = (!dstLayers.empty() ? dstLayers.front(): nullptr);
432 for (Layer* srcLayer : srcLayers) {
433 Layer* copiedLayer = api.duplicateLayerBefore(
434 srcLayer, parent, beforeThis);
435
436 resultRange.startRange(copiedLayer, -1, DocRange::kLayers);
437 resultRange.endRange(copiedLayer, -1);
438 }
439 }
440 else if (place == kDocRangeAfter) {
441 std::reverse(srcLayers.begin(), srcLayers.end());
442
443 Layer* afterThis = (!dstLayers.empty() ? dstLayers.back(): nullptr);
444 for (Layer* srcLayer : srcLayers) {
445 Layer* copiedLayer = api.duplicateLayerAfter(
446 srcLayer, parent, afterThis);
447
448 resultRange.startRange(copiedLayer, -1, DocRange::kLayers);
449 resultRange.endRange(copiedLayer, -1);
450 }
451 }
452 break;
453 }
454 }
455 break;
456 }
457 }
458
459 if (resultRange.type() != DocRange::kNone)
460 tx.setNewDocRange(resultRange);
461
462 tx.commit();
463 }
464
465 return resultRange;
466}
467
468DocRange move_range(Doc* doc,
469 const DocRange& from,
470 const DocRange& to,
471 const DocRangePlace place,
472 const TagsHandling tagsHandling)
473{
474 return drop_range_op(doc, Move, from, place,
475 tagsHandling, DocRange(to));
476}
477
478DocRange copy_range(Doc* doc,
479 const DocRange& from,
480 const DocRange& to,
481 const DocRangePlace place,
482 const TagsHandling tagsHandling)
483{
484 return drop_range_op(doc, Copy, from, place,
485 tagsHandling, DocRange(to));
486}
487
488void reverse_frames(Doc* doc, const DocRange& range)
489{
490 const app::Context* context = static_cast<app::Context*>(doc->context());
491 const ContextReader reader(context);
492 ContextWriter writer(reader);
493 Tx tx(writer.context(), "Reverse Frames");
494 DocApi api = doc->getApi(tx);
495 Sprite* sprite = doc->sprite();
496 LayerList layers;
497 frame_t frameBegin, frameEnd;
498 bool moveFrames = false;
499 bool swapCels = false;
500
501 switch (range.type()) {
502 case DocRange::kCels:
503 frameBegin = range.firstFrame();
504 frameEnd = range.lastFrame();
505 layers = range.selectedLayers().toBrowsableLayerList();
506 swapCels = true;
507 break;
508 case DocRange::kFrames:
509 frameBegin = range.firstFrame();
510 frameEnd = range.lastFrame();
511 layers = sprite->allLayers();
512 moveFrames = true;
513 break;
514 case DocRange::kLayers:
515 frameBegin = frame_t(0);
516 frameEnd = sprite->totalFrames()-1;
517 layers = range.selectedLayers().toBrowsableLayerList();
518 swapCels = true;
519 break;
520 }
521
522 if (moveFrames) {
523 for (frame_t frameRev = frameEnd+1;
524 frameRev > frameBegin;
525 --frameRev) {
526 api.moveFrame(sprite, frameBegin, frameRev,
527 kDropBeforeFrame,
528 kDontAdjustTags);
529 }
530 }
531 else if (swapCels) {
532 for (Layer* layer : layers) {
533 if (!layer->isImage())
534 continue;
535
536 for (frame_t frame = frameBegin,
537 frameRev = frameEnd;
538 frame != (frameBegin+frameEnd)/2+1;
539 ++frame, --frameRev) {
540 if (frame == frameRev)
541 continue;
542
543 LayerImage* imageLayer = static_cast<LayerImage*>(layer);
544 api.swapCel(imageLayer, frame, frameRev);
545 }
546 }
547 }
548
549 tx.setNewDocRange(range);
550 tx.commit();
551}
552
553} // namespace app
554