1 // Aseprite
2 // Copyright (C) 2001-2018 David Capello
3 //
4 // This program is distributed under the terms of
5 // the End-User License Agreement for Aseprite.
6
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif
10
11 #include "app/doc_api.h"
12
13 #include "app/cmd/add_cel.h"
14 #include "app/cmd/add_frame.h"
15 #include "app/cmd/add_layer.h"
16 #include "app/cmd/background_from_layer.h"
17 #include "app/cmd/clear_cel.h"
18 #include "app/cmd/clear_image.h"
19 #include "app/cmd/copy_cel.h"
20 #include "app/cmd/copy_frame.h"
21 #include "app/cmd/flip_image.h"
22 #include "app/cmd/layer_from_background.h"
23 #include "app/cmd/move_cel.h"
24 #include "app/cmd/move_layer.h"
25 #include "app/cmd/remove_cel.h"
26 #include "app/cmd/remove_frame.h"
27 #include "app/cmd/remove_frame_tag.h"
28 #include "app/cmd/remove_layer.h"
29 #include "app/cmd/replace_image.h"
30 #include "app/cmd/set_cel_bounds.h"
31 #include "app/cmd/set_cel_frame.h"
32 #include "app/cmd/set_cel_opacity.h"
33 #include "app/cmd/set_cel_position.h"
34 #include "app/cmd/set_frame_duration.h"
35 #include "app/cmd/set_frame_tag_range.h"
36 #include "app/cmd/set_mask.h"
37 #include "app/cmd/set_mask_position.h"
38 #include "app/cmd/set_palette.h"
39 #include "app/cmd/set_slice_key.h"
40 #include "app/cmd/set_sprite_size.h"
41 #include "app/cmd/set_total_frames.h"
42 #include "app/cmd/set_transparent_color.h"
43 #include "app/color_target.h"
44 #include "app/color_utils.h"
45 #include "app/context.h"
46 #include "app/doc.h"
47 #include "app/doc_undo.h"
48 #include "app/transaction.h"
49 #include "base/unique_ptr.h"
50 #include "doc/algorithm/flip_image.h"
51 #include "doc/algorithm/shrink_bounds.h"
52 #include "doc/cel.h"
53 #include "doc/frame_tag.h"
54 #include "doc/frame_tags.h"
55 #include "doc/mask.h"
56 #include "doc/palette.h"
57 #include "doc/slice.h"
58 #include "render/render.h"
59
60 #include <set>
61
62 #define TRACE_DOCAPI(...)
63
64 namespace app {
65
DocApi(Doc * document,Transaction & transaction)66 DocApi::DocApi(Doc* document, Transaction& transaction)
67 : m_document(document)
68 , m_transaction(transaction)
69 {
70 }
71
setSpriteSize(Sprite * sprite,int w,int h)72 void DocApi::setSpriteSize(Sprite* sprite, int w, int h)
73 {
74 m_transaction.execute(new cmd::SetSpriteSize(sprite, w, h));
75 }
76
setSpriteTransparentColor(Sprite * sprite,color_t maskColor)77 void DocApi::setSpriteTransparentColor(Sprite* sprite, color_t maskColor)
78 {
79 m_transaction.execute(new cmd::SetTransparentColor(sprite, maskColor));
80 }
81
cropSprite(Sprite * sprite,const gfx::Rect & bounds)82 void DocApi::cropSprite(Sprite* sprite, const gfx::Rect& bounds)
83 {
84 setSpriteSize(sprite, bounds.w, bounds.h);
85
86 Doc* doc = static_cast<Doc*>(sprite->document());
87 LayerList layers = sprite->allLayers();
88 for (Layer* layer : layers) {
89 if (!layer->isImage())
90 continue;
91
92 std::set<ObjectId> visited;
93 CelIterator it = ((LayerImage*)layer)->getCelBegin();
94 CelIterator end = ((LayerImage*)layer)->getCelEnd();
95 for (; it != end; ++it) {
96 Cel* cel = *it;
97 if (visited.find(cel->data()->id()) != visited.end())
98 continue;
99 visited.insert(cel->data()->id());
100
101 if (layer->isBackground()) {
102 Image* image = cel->image();
103 if (image && !cel->link()) {
104 ASSERT(cel->x() == 0);
105 ASSERT(cel->y() == 0);
106
107 // Create the new image through a crop
108 ImageRef new_image(
109 crop_image(image,
110 bounds.x, bounds.y,
111 bounds.w, bounds.h,
112 doc->bgColor(layer)));
113
114 // Replace the image in the stock that is pointed by the cel
115 replaceImage(sprite, cel->imageRef(), new_image);
116 }
117 }
118 else if (layer->isReference()) {
119 // Update the ref cel's bounds
120 gfx::RectF newBounds = cel->boundsF();
121 newBounds.x -= bounds.x;
122 newBounds.y -= bounds.y;
123 m_transaction.execute(new cmd::SetCelBoundsF(cel, newBounds));
124 }
125 else {
126 // Update the cel's position
127 setCelPosition(sprite, cel,
128 cel->x()-bounds.x, cel->y()-bounds.y);
129 }
130 }
131 }
132
133 // Update mask position
134 if (!m_document->mask()->isEmpty())
135 setMaskPosition(m_document->mask()->bounds().x-bounds.x,
136 m_document->mask()->bounds().y-bounds.y);
137
138 // Update slice positions
139 if (bounds.origin() != gfx::Point(0, 0)) {
140 for (auto& slice : m_document->sprite()->slices()) {
141 for (auto& k : *slice) {
142 const SliceKey& key = *k.value();
143 if (key.isEmpty())
144 continue;
145
146 SliceKey newKey = key;
147 newKey.setBounds(
148 gfx::Rect(newKey.bounds()).offset(-bounds.origin()));
149
150 // As SliceKey::center() and pivot() properties are relative
151 // to the bounds(), we don't need to adjust them.
152
153 m_transaction.execute(
154 new cmd::SetSliceKey(slice, k.frame(), newKey));
155 }
156 }
157 }
158 }
159
trimSprite(Sprite * sprite)160 void DocApi::trimSprite(Sprite* sprite)
161 {
162 gfx::Rect bounds;
163
164 base::UniquePtr<Image> image_wrap(Image::create(sprite->pixelFormat(),
165 sprite->width(),
166 sprite->height()));
167 Image* image = image_wrap.get();
168 render::Render render;
169
170 for (frame_t frame(0); frame<sprite->totalFrames(); ++frame) {
171 render.renderSprite(image, sprite, frame);
172
173 // TODO configurable (what color pixel to use as "refpixel",
174 // here we are using the top-left pixel by default)
175 gfx::Rect frameBounds;
176 if (doc::algorithm::shrink_bounds(image, frameBounds, get_pixel(image, 0, 0)))
177 bounds = bounds.createUnion(frameBounds);
178 }
179
180 if (!bounds.isEmpty())
181 cropSprite(sprite, bounds);
182 }
183
addFrame(Sprite * sprite,frame_t newFrame)184 void DocApi::addFrame(Sprite* sprite, frame_t newFrame)
185 {
186 copyFrame(sprite, newFrame-1, newFrame,
187 kDropBeforeFrame,
188 kDefaultTagsAdjustment);
189 }
190
addEmptyFrame(Sprite * sprite,frame_t newFrame)191 void DocApi::addEmptyFrame(Sprite* sprite, frame_t newFrame)
192 {
193 m_transaction.execute(new cmd::AddFrame(sprite, newFrame));
194 adjustFrameTags(sprite, newFrame, +1,
195 kDropBeforeFrame,
196 kDefaultTagsAdjustment);
197 }
198
addEmptyFramesTo(Sprite * sprite,frame_t newFrame)199 void DocApi::addEmptyFramesTo(Sprite* sprite, frame_t newFrame)
200 {
201 while (sprite->totalFrames() <= newFrame)
202 addEmptyFrame(sprite, sprite->totalFrames());
203 }
204
copyFrame(Sprite * sprite,const frame_t fromFrame,const frame_t newFrame,const DropFramePlace dropFramePlace,const TagsHandling tagsHandling)205 void DocApi::copyFrame(Sprite* sprite,
206 const frame_t fromFrame,
207 const frame_t newFrame,
208 const DropFramePlace dropFramePlace,
209 const TagsHandling tagsHandling)
210 {
211 ASSERT(sprite);
212 m_transaction.execute(
213 new cmd::CopyFrame(
214 sprite, fromFrame,
215 (dropFramePlace == kDropBeforeFrame ? newFrame:
216 newFrame+1)));
217
218 adjustFrameTags(sprite, newFrame, +1,
219 dropFramePlace,
220 tagsHandling);
221 }
222
removeFrame(Sprite * sprite,frame_t frame)223 void DocApi::removeFrame(Sprite* sprite, frame_t frame)
224 {
225 ASSERT(frame >= 0);
226 m_transaction.execute(new cmd::RemoveFrame(sprite, frame));
227 adjustFrameTags(sprite, frame, -1,
228 kDropBeforeFrame,
229 kDefaultTagsAdjustment);
230 }
231
setTotalFrames(Sprite * sprite,frame_t frames)232 void DocApi::setTotalFrames(Sprite* sprite, frame_t frames)
233 {
234 ASSERT(frames >= 1);
235 m_transaction.execute(new cmd::SetTotalFrames(sprite, frames));
236 }
237
setFrameDuration(Sprite * sprite,frame_t frame,int msecs)238 void DocApi::setFrameDuration(Sprite* sprite, frame_t frame, int msecs)
239 {
240 m_transaction.execute(new cmd::SetFrameDuration(sprite, frame, msecs));
241 }
242
setFrameRangeDuration(Sprite * sprite,frame_t from,frame_t to,int msecs)243 void DocApi::setFrameRangeDuration(Sprite* sprite, frame_t from, frame_t to, int msecs)
244 {
245 ASSERT(from >= frame_t(0));
246 ASSERT(from < to);
247 ASSERT(to <= sprite->lastFrame());
248
249 for (frame_t fr=from; fr<=to; ++fr)
250 m_transaction.execute(new cmd::SetFrameDuration(sprite, fr, msecs));
251 }
252
moveFrame(Sprite * sprite,const frame_t frame,frame_t targetFrame,const DropFramePlace dropFramePlace,const TagsHandling tagsHandling)253 void DocApi::moveFrame(Sprite* sprite,
254 const frame_t frame,
255 frame_t targetFrame,
256 const DropFramePlace dropFramePlace,
257 const TagsHandling tagsHandling)
258 {
259 const frame_t beforeFrame =
260 (dropFramePlace == kDropBeforeFrame ? targetFrame: targetFrame+1);
261
262 if (frame >= 0 && frame <= sprite->lastFrame() &&
263 beforeFrame >= 0 && beforeFrame <= sprite->lastFrame()+1 &&
264 ((frame != beforeFrame) ||
265 (!sprite->frameTags().empty() &&
266 tagsHandling != kDontAdjustTags))) {
267 // Change the frame-lengths.
268 int frlen_aux = sprite->frameDuration(frame);
269
270 // Moving the frame to the future.
271 if (frame < beforeFrame) {
272 for (frame_t c=frame; c<beforeFrame-1; ++c)
273 setFrameDuration(sprite, c, sprite->frameDuration(c+1));
274 setFrameDuration(sprite, beforeFrame-1, frlen_aux);
275 }
276 // Moving the frame to the past.
277 else if (beforeFrame < frame) {
278 for (frame_t c=frame; c>beforeFrame; --c)
279 setFrameDuration(sprite, c, sprite->frameDuration(c-1));
280 setFrameDuration(sprite, beforeFrame, frlen_aux);
281 }
282
283 if (tagsHandling != kDontAdjustTags) {
284 adjustFrameTags(sprite, frame, -1, dropFramePlace, tagsHandling);
285 if (targetFrame >= frame)
286 --targetFrame;
287 adjustFrameTags(sprite, targetFrame, +1, dropFramePlace, tagsHandling);
288 }
289
290 // Change cel positions.
291 if (frame != beforeFrame)
292 moveFrameLayer(sprite->root(), frame, beforeFrame);
293 }
294 }
295
moveFrameLayer(Layer * layer,frame_t frame,frame_t beforeFrame)296 void DocApi::moveFrameLayer(Layer* layer, frame_t frame, frame_t beforeFrame)
297 {
298 ASSERT(layer);
299
300 switch (layer->type()) {
301
302 case ObjectType::LayerImage: {
303 LayerImage* imglayer = static_cast<LayerImage*>(layer);
304
305 CelList cels;
306 imglayer->getCels(cels);
307
308 CelIterator it = cels.begin();
309 CelIterator end = cels.end();
310
311 for (; it != end; ++it) {
312 Cel* cel = *it;
313 frame_t celFrame = cel->frame();
314 frame_t newFrame = celFrame;
315
316 // moving the frame to the future
317 if (frame < beforeFrame) {
318 if (celFrame == frame) {
319 newFrame = beforeFrame-1;
320 }
321 else if (celFrame > frame &&
322 celFrame < beforeFrame) {
323 --newFrame;
324 }
325 }
326 // moving the frame to the past
327 else if (beforeFrame < frame) {
328 if (celFrame == frame) {
329 newFrame = beforeFrame;
330 }
331 else if (celFrame >= beforeFrame &&
332 celFrame < frame) {
333 ++newFrame;
334 }
335 }
336
337 if (celFrame != newFrame)
338 setCelFramePosition(cel, newFrame);
339 }
340 break;
341 }
342
343 case ObjectType::LayerGroup: {
344 for (Layer* child : static_cast<LayerGroup*>(layer)->layers())
345 moveFrameLayer(child, frame, beforeFrame);
346 break;
347 }
348
349 }
350 }
351
addCel(LayerImage * layer,Cel * cel)352 void DocApi::addCel(LayerImage* layer, Cel* cel)
353 {
354 ASSERT(layer);
355 ASSERT(cel);
356
357 m_transaction.execute(new cmd::AddCel(layer, cel));
358 }
359
setCelFramePosition(Cel * cel,frame_t frame)360 void DocApi::setCelFramePosition(Cel* cel, frame_t frame)
361 {
362 ASSERT(cel);
363 ASSERT(frame >= 0);
364
365 m_transaction.execute(new cmd::SetCelFrame(cel, frame));
366 }
367
setCelPosition(Sprite * sprite,Cel * cel,int x,int y)368 void DocApi::setCelPosition(Sprite* sprite, Cel* cel, int x, int y)
369 {
370 ASSERT(cel);
371
372 m_transaction.execute(new cmd::SetCelPosition(cel, x, y));
373 }
374
setCelOpacity(Sprite * sprite,Cel * cel,int newOpacity)375 void DocApi::setCelOpacity(Sprite* sprite, Cel* cel, int newOpacity)
376 {
377 ASSERT(cel);
378 ASSERT(sprite->supportAlpha());
379
380 m_transaction.execute(new cmd::SetCelOpacity(cel, newOpacity));
381 }
382
clearCel(LayerImage * layer,frame_t frame)383 void DocApi::clearCel(LayerImage* layer, frame_t frame)
384 {
385 if (Cel* cel = layer->cel(frame))
386 clearCel(cel);
387 }
388
clearCel(Cel * cel)389 void DocApi::clearCel(Cel* cel)
390 {
391 ASSERT(cel);
392 m_transaction.execute(new cmd::ClearCel(cel));
393 }
394
moveCel(LayerImage * srcLayer,frame_t srcFrame,LayerImage * dstLayer,frame_t dstFrame)395 void DocApi::moveCel(
396 LayerImage* srcLayer, frame_t srcFrame,
397 LayerImage* dstLayer, frame_t dstFrame)
398 {
399 ASSERT(srcLayer != dstLayer || srcFrame != dstFrame);
400 m_transaction.execute(new cmd::MoveCel(
401 srcLayer, srcFrame,
402 dstLayer, dstFrame, dstLayer->isContinuous()));
403 }
404
copyCel(LayerImage * srcLayer,frame_t srcFrame,LayerImage * dstLayer,frame_t dstFrame)405 void DocApi::copyCel(
406 LayerImage* srcLayer, frame_t srcFrame,
407 LayerImage* dstLayer, frame_t dstFrame)
408 {
409 copyCel(
410 srcLayer, srcFrame,
411 dstLayer, dstFrame, dstLayer->isContinuous());
412 }
413
copyCel(LayerImage * srcLayer,frame_t srcFrame,LayerImage * dstLayer,frame_t dstFrame,bool continuous)414 void DocApi::copyCel(
415 LayerImage* srcLayer, frame_t srcFrame,
416 LayerImage* dstLayer, frame_t dstFrame, bool continuous)
417 {
418 ASSERT(srcLayer != dstLayer || srcFrame != dstFrame);
419
420 if (srcLayer == dstLayer && srcFrame == dstFrame)
421 return; // Nothing to be done
422
423 m_transaction.execute(
424 new cmd::CopyCel(
425 srcLayer, srcFrame,
426 dstLayer, dstFrame, continuous));
427 }
428
swapCel(LayerImage * layer,frame_t frame1,frame_t frame2)429 void DocApi::swapCel(
430 LayerImage* layer, frame_t frame1, frame_t frame2)
431 {
432 ASSERT(frame1 != frame2);
433
434 Sprite* sprite = layer->sprite();
435 ASSERT(sprite != NULL);
436 ASSERT(frame1 >= 0 && frame1 < sprite->totalFrames());
437 ASSERT(frame2 >= 0 && frame2 < sprite->totalFrames());
438 (void)sprite; // To avoid unused variable warning on Release mode
439
440 Cel* cel1 = layer->cel(frame1);
441 Cel* cel2 = layer->cel(frame2);
442
443 if (cel1) setCelFramePosition(cel1, frame2);
444 if (cel2) setCelFramePosition(cel2, frame1);
445 }
446
newLayer(LayerGroup * parent,const std::string & name)447 LayerImage* DocApi::newLayer(LayerGroup* parent, const std::string& name)
448 {
449 LayerImage* layer = new LayerImage(parent->sprite());
450 layer->setName(name);
451
452 addLayer(parent, layer, parent->lastLayer());
453 return layer;
454 }
455
newGroup(LayerGroup * parent,const std::string & name)456 LayerGroup* DocApi::newGroup(LayerGroup* parent, const std::string& name)
457 {
458 LayerGroup* layer = new LayerGroup(parent->sprite());
459 layer->setName(name);
460
461 addLayer(parent, layer, parent->lastLayer());
462 return layer;
463 }
464
addLayer(LayerGroup * parent,Layer * newLayer,Layer * afterThis)465 void DocApi::addLayer(LayerGroup* parent, Layer* newLayer, Layer* afterThis)
466 {
467 m_transaction.execute(new cmd::AddLayer(parent, newLayer, afterThis));
468 }
469
removeLayer(Layer * layer)470 void DocApi::removeLayer(Layer* layer)
471 {
472 ASSERT(layer);
473
474 m_transaction.execute(new cmd::RemoveLayer(layer));
475 }
476
restackLayerAfter(Layer * layer,LayerGroup * parent,Layer * afterThis)477 void DocApi::restackLayerAfter(Layer* layer, LayerGroup* parent, Layer* afterThis)
478 {
479 ASSERT(parent);
480
481 if (layer == afterThis)
482 return;
483
484 m_transaction.execute(new cmd::MoveLayer(layer, parent, afterThis));
485 }
486
restackLayerBefore(Layer * layer,LayerGroup * parent,Layer * beforeThis)487 void DocApi::restackLayerBefore(Layer* layer, LayerGroup* parent, Layer* beforeThis)
488 {
489 ASSERT(parent);
490
491 if (layer == beforeThis)
492 return;
493
494 Layer* afterThis;
495 if (beforeThis)
496 afterThis = beforeThis->getPrevious();
497 else
498 afterThis = parent->lastLayer();
499
500 restackLayerAfter(layer, parent, afterThis);
501 }
502
backgroundFromLayer(Layer * layer)503 void DocApi::backgroundFromLayer(Layer* layer)
504 {
505 m_transaction.execute(new cmd::BackgroundFromLayer(layer));
506 }
507
layerFromBackground(Layer * layer)508 void DocApi::layerFromBackground(Layer* layer)
509 {
510 m_transaction.execute(new cmd::LayerFromBackground(layer));
511 }
512
duplicateLayerAfter(Layer * sourceLayer,LayerGroup * parent,Layer * afterLayer)513 Layer* DocApi::duplicateLayerAfter(Layer* sourceLayer, LayerGroup* parent, Layer* afterLayer)
514 {
515 ASSERT(parent);
516 base::UniquePtr<Layer> newLayerPtr;
517
518 if (sourceLayer->isImage())
519 newLayerPtr.reset(new LayerImage(sourceLayer->sprite()));
520 else if (sourceLayer->isGroup())
521 newLayerPtr.reset(new LayerGroup(sourceLayer->sprite()));
522 else
523 throw std::runtime_error("Invalid layer type");
524
525 m_document->copyLayerContent(sourceLayer, m_document, newLayerPtr);
526
527 newLayerPtr->setName(newLayerPtr->name() + " Copy");
528
529 addLayer(parent, newLayerPtr, afterLayer);
530
531 // Release the pointer as it is owned by the sprite now.
532 return newLayerPtr.release();
533 }
534
duplicateLayerBefore(Layer * sourceLayer,LayerGroup * parent,Layer * beforeLayer)535 Layer* DocApi::duplicateLayerBefore(Layer* sourceLayer, LayerGroup* parent, Layer* beforeLayer)
536 {
537 ASSERT(parent);
538 Layer* afterThis = (beforeLayer ? beforeLayer->getPreviousBrowsable(): nullptr);
539 Layer* newLayer = duplicateLayerAfter(sourceLayer, parent, afterThis);
540 if (newLayer)
541 restackLayerBefore(newLayer, parent, beforeLayer);
542 return newLayer;
543 }
544
addCel(LayerImage * layer,frame_t frameNumber,const ImageRef & image)545 Cel* DocApi::addCel(LayerImage* layer, frame_t frameNumber, const ImageRef& image)
546 {
547 ASSERT(layer->cel(frameNumber) == NULL);
548
549 base::UniquePtr<Cel> cel(new Cel(frameNumber, image));
550
551 addCel(layer, cel);
552 return cel.release();
553 }
554
replaceImage(Sprite * sprite,const ImageRef & oldImage,const ImageRef & newImage)555 void DocApi::replaceImage(Sprite* sprite, const ImageRef& oldImage, const ImageRef& newImage)
556 {
557 ASSERT(oldImage);
558 ASSERT(newImage);
559 ASSERT(oldImage->maskColor() == newImage->maskColor());
560
561 m_transaction.execute(new cmd::ReplaceImage(
562 sprite, oldImage, newImage));
563 }
564
flipImage(Image * image,const gfx::Rect & bounds,doc::algorithm::FlipType flipType)565 void DocApi::flipImage(Image* image, const gfx::Rect& bounds,
566 doc::algorithm::FlipType flipType)
567 {
568 m_transaction.execute(new cmd::FlipImage(image, bounds, flipType));
569 }
570
copyToCurrentMask(Mask * mask)571 void DocApi::copyToCurrentMask(Mask* mask)
572 {
573 ASSERT(m_document->mask());
574 ASSERT(mask);
575
576 m_transaction.execute(new cmd::SetMask(m_document, mask));
577 }
578
setMaskPosition(int x,int y)579 void DocApi::setMaskPosition(int x, int y)
580 {
581 ASSERT(m_document->mask());
582
583 m_transaction.execute(new cmd::SetMaskPosition(m_document, gfx::Point(x, y)));
584 }
585
setPalette(Sprite * sprite,frame_t frame,const Palette * newPalette)586 void DocApi::setPalette(Sprite* sprite, frame_t frame, const Palette* newPalette)
587 {
588 Palette* currentSpritePalette = sprite->palette(frame); // Sprite current pal
589 int from, to;
590
591 // Check differences between current sprite palette and current system palette
592 from = to = -1;
593 currentSpritePalette->countDiff(newPalette, &from, &to);
594
595 if (from >= 0 && to >= from) {
596 m_transaction.execute(new cmd::SetPalette(
597 sprite, frame, newPalette));
598 }
599 }
600
adjustFrameTags(Sprite * sprite,const frame_t frame,const frame_t delta,const DropFramePlace dropFramePlace,const TagsHandling tagsHandling)601 void DocApi::adjustFrameTags(Sprite* sprite,
602 const frame_t frame,
603 const frame_t delta,
604 const DropFramePlace dropFramePlace,
605 const TagsHandling tagsHandling)
606 {
607 TRACE_DOCAPI(
608 "\n adjustFrameTags %s frame %d delta=%d tags=%s:\n",
609 (dropFramePlace == kDropBeforeFrame ? "before": "after"),
610 frame, delta,
611 (tagsHandling == kDefaultTagsAdjustment ? "default":
612 tagsHandling == kFitInsideTags ? "fit-inside":
613 "fit-outside"));
614 ASSERT(tagsHandling != kDontAdjustTags);
615
616 // As FrameTag::setFrameRange() changes m_frameTags, we need to use
617 // a copy of this collection
618 std::vector<FrameTag*> tags(sprite->frameTags().begin(), sprite->frameTags().end());
619
620 for (FrameTag* tag : tags) {
621 frame_t from = tag->fromFrame();
622 frame_t to = tag->toFrame();
623
624 TRACE_DOCAPI(" - [from to]=[%d %d] ->", from, to);
625
626 // When delta = +1, frame = beforeFrame
627 if (delta == +1) {
628 switch (tagsHandling) {
629 case kDefaultTagsAdjustment:
630 if (frame <= from) { ++from; }
631 if (frame <= to+1) { ++to; }
632 break;
633 case kFitInsideTags:
634 if (frame < from) { ++from; }
635 if (frame <= to) { ++to; }
636 break;
637 case kFitOutsideTags:
638 if ((frame < from) ||
639 (frame == from &&
640 dropFramePlace == kDropBeforeFrame)) {
641 ++from;
642 }
643 if ((frame < to) ||
644 (frame == to &&
645 dropFramePlace == kDropBeforeFrame)) {
646 ++to;
647 }
648 break;
649 }
650 }
651 else if (delta == -1) {
652 if (frame < from) { --from; }
653 if (frame <= to) { --to; }
654 }
655
656 TRACE_DOCAPI(" [%d %d]\n", from, to);
657
658 if (from != tag->fromFrame() ||
659 to != tag->toFrame()) {
660 if (from > to)
661 m_transaction.execute(new cmd::RemoveFrameTag(sprite, tag));
662 else
663 m_transaction.execute(new cmd::SetFrameTagRange(tag, from, to));
664 }
665 }
666 }
667
668 } // namespace app
669