Pencil2D Animation
Download Community News Docs Contribute
  • Overview
  • Articles
  • Code
  •  
  • Class List
  • Class Index
  • Class Hierarchy
  • Class Members
  • File List
Loading...
Searching...
No Matches
  • core_lib
  • src
  • structure
layer.cpp
1/*
2
3Pencil2D - Traditional Animation Software
4Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
5Copyright (C) 2012-2020 Matthew Chiawen Chang
6
7This program is free software; you can redistribute it and/or
8modify it under the terms of the GNU General Public License
9as published by the Free Software Foundation; version 2 of the License.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16*/
17#include "layer.h"
18
19#include <QApplication>
20#include <QDebug>
21#include <QSettings>
22#include <QPainter>
23#include <QDomElement>
24#include "keyframe.h"
25
26// Used to sort the selected frames list
27bool sortAsc(int left, int right)
28{
29 return left < right;
30}
31
32Layer::Layer(int id, LAYER_TYPE eType)
33{
34 Q_ASSERT(eType != UNDEFINED);
35
36 meType = eType;
37 mName = QString(tr("Undefined Layer"));
38
39 mId = id;
40}
41
42Layer::~Layer()
43{
44 for (auto it : mKeyFrames)
45 {
46 KeyFrame* pKeyFrame = it.second;
47 delete pKeyFrame;
48 }
49 mKeyFrames.clear();
50}
51
52void Layer::foreachKeyFrame(std::function<void(KeyFrame*)> action) const
53{
54 for (auto pair : mKeyFrames)
55 {
56 action(pair.second);
57 }
58}
59
60bool Layer::keyExists(int position) const
61{
62 return (mKeyFrames.find(position) != mKeyFrames.end());
63}
64
65KeyFrame* Layer::getKeyFrameAt(int position) const
66{
67 auto it = mKeyFrames.find(position);
68 if (it == mKeyFrames.end())
69 {
70 return nullptr;
71 }
72 return it->second;
73}
74
75KeyFrame* Layer::getLastKeyFrameAtPosition(int position) const
76{
77 if (position < 1)
78 {
79 position = 1;
80 }
81 auto it = mKeyFrames.lower_bound(position);
82 if (it == mKeyFrames.end())
83 {
84 return nullptr;
85 }
86 return it->second;
87}
88
89int Layer::getPreviousKeyFramePosition(int position) const
90{
91 auto it = mKeyFrames.upper_bound(position);
92 if (it == mKeyFrames.end())
93 {
94 return firstKeyFramePosition();
95 }
96 return it->first;
97}
98
99int Layer::getNextKeyFramePosition(int position) const
100{
101 // workaround: bug with lower_bound?
102 // when position is before the first frame it == mKeyFrames.end() for some reason
103 if (position < firstKeyFramePosition())
104 {
105 return firstKeyFramePosition();
106 }
107
108 auto it = mKeyFrames.lower_bound(position);
109 if (it == mKeyFrames.end())
110 {
111 return getMaxKeyFramePosition();
112 }
113
114 if (it != mKeyFrames.begin())
115 {
116 --it;
117 }
118 return it->first;
119}
120
121int Layer::getPreviousFrameNumber(int position, bool isAbsolute) const
122{
123 int prevNumber;
124
125 if (isAbsolute)
126 prevNumber = getPreviousKeyFramePosition(position);
127 else
128 prevNumber = position - 1;
129
130 if (prevNumber >= position)
131 {
132 return -1; // There is no previous keyframe
133 }
134 return prevNumber;
135}
136
137int Layer::getNextFrameNumber(int position, bool isAbsolute) const
138{
139 int nextNumber;
140
141 if (isAbsolute)
142 nextNumber = getNextKeyFramePosition(position);
143 else
144 nextNumber = position + 1;
145
146 if (nextNumber <= position)
147 return -1; // There is no next keyframe
148
149 return nextNumber;
150}
151
152int Layer::firstKeyFramePosition() const
153{
154 if (!mKeyFrames.empty())
155 {
156 return mKeyFrames.rbegin()->first; // rbegin is the lowest key frame position
157 }
158 return 0;
159}
160
161int Layer::getMaxKeyFramePosition() const
162{
163 if (!mKeyFrames.empty())
164 {
165 return mKeyFrames.begin()->first; // begin is the highest key frame position
166 }
167 return 0;
168}
169
170bool Layer::addNewKeyFrameAt(int position)
171{
172 if (position <= 0) return false;
173
174 KeyFrame* key = createKeyFrame(position);
175 if (!addKeyFrame(position, key))
176 {
177 delete key;
178 return false;
179 }
180 return true;
181}
182
183void Layer::addOrReplaceKeyFrame(int position, KeyFrame* pKeyFrame)
184{
185 Q_ASSERT(position > 0);
186 pKeyFrame->setPos(position);
187 loadKey(pKeyFrame);
188 markFrameAsDirty(position);
189}
190
191bool Layer::addKeyFrame(int position, KeyFrame* pKeyFrame)
192{
193 if (keyExists(position))
194 {
195 return false;
196 }
197
198 pKeyFrame->setPos(position);
199 mKeyFrames.emplace(position, pKeyFrame);
200 markFrameAsDirty(position);
201 return true;
202}
203
204bool Layer::insertExposureAt(int position)
205{
206 if(position < 1 || position > getMaxKeyFramePosition() || !getKeyFrameAt(position))
207 {
208 return false;
209 }
210
211 newSelectionOfConnectedFrames(position + 1);
212 moveSelectedFrames(1);
213 deselectAll();
214
215 return true;
216}
217
218bool Layer::removeKeyFrame(int position)
219{
220 if (keyFrameCount() == 1 && this->type() != SOUND) { return false; }
221 auto frame = getKeyFrameWhichCovers(position);
222
223 if (frame)
224 {
225 if (frame->isSelected()) {
226 removeFromSelectionList(frame->pos());
227 }
228 mKeyFrames.erase(frame->pos());
229 markFrameAsDirty(frame->pos());
230 delete frame;
231 }
232 return true;
233}
234
235void Layer::removeFromSelectionList(int position)
236{
237 mSelectedFrames_byLast.removeAll(position);
238 mSelectedFrames_byPosition.removeAll(position);
239}
240
241bool Layer::moveKeyFrame(int position, int offset)
242{
243 int newPos = position + offset;
244 if (newPos < 1) { return false; }
245
246 auto listOfFramesLast = mSelectedFrames_byLast;
247 auto listOfFramesPos = mSelectedFrames_byPosition;
248 bool frameSelected = isFrameSelected(position);
249
250 if (swapKeyFrames(position, newPos)) {
251
252 auto oldPosIndex = mSelectedFrames_byPosition.indexOf(position);
253 auto newPosIndex = mSelectedFrames_byPosition.indexOf(newPos);
254
255 auto oldLastIndex = mSelectedFrames_byLast.indexOf(position);
256 auto newLastndex = mSelectedFrames_byLast.indexOf(newPos);
257
258
259 // Old position is selected
260 if (oldPosIndex != -1) {
261 mSelectedFrames_byPosition[oldPosIndex] = newPos;
262 mSelectedFrames_byLast[oldLastIndex] = newPos;
263 }
264
265 // Old position is selected
266 if (newPosIndex != -1) {
267 mSelectedFrames_byPosition[newPosIndex] = position;
268 mSelectedFrames_byLast[newLastndex] = position;
269 }
270 return true;
271 }
272
273 mSelectedFrames_byLast.clear();
274 mSelectedFrames_byPosition.clear();
275
276 setFrameSelected(position, true);
277
278 // If the move fails, assume we can't move at all and revert to old selection
279 if (!moveSelectedFrames(offset)) {
280 mSelectedFrames_byLast = listOfFramesLast;
281 mSelectedFrames_byPosition = listOfFramesPos;
282 return false;
283 }
284
285 // Remove old position from selection list
286 listOfFramesLast.removeOne(position);
287 listOfFramesPos.removeOne(position);
288
289 mSelectedFrames_byLast = listOfFramesLast;
290 mSelectedFrames_byPosition = listOfFramesPos;
291
292 // If the frame was selected prior to moving, make sure it's still selected.
293 setFrameSelected(newPos, frameSelected);
294
295 return true;
296}
297
298// Current behaviour, need to refresh the swapped cels
299bool Layer::swapKeyFrames(int position1, int position2)
300{
301 KeyFrame* pFirstFrame = nullptr;
302 KeyFrame* pSecondFrame = nullptr;
303
304 if (mKeyFrames.count(position1) != 1 || mKeyFrames.count(position2) != 1)
305 {
306 return false;
307 }
308
309 // Both keys exist
310 pFirstFrame = mKeyFrames[position1];
311 pSecondFrame = mKeyFrames[position2];
312
313 mKeyFrames[position1] = pSecondFrame;
314 mKeyFrames[position2] = pFirstFrame;
315
316 pFirstFrame->setPos(position2);
317 pSecondFrame->setPos(position1);
318
319 pFirstFrame->modification();
320 pSecondFrame->modification();
321
322 markFrameAsDirty(position1);
323 markFrameAsDirty(position2);
324
325 return true;
326}
327
328bool Layer::loadKey(KeyFrame* pKey)
329{
330 auto it = mKeyFrames.find(pKey->pos());
331 if (it != mKeyFrames.end())
332 {
333 delete it->second;
334 mKeyFrames.erase(it);
335 }
336 mKeyFrames.emplace(pKey->pos(), pKey);
337 return true;
338}
339
340Status Layer::save(const QString& sDataFolder, QStringList& attachedFiles, ProgressCallback progressStep)
341{
342 DebugDetails dd;
343 dd << "\n[Layer SAVE diagnostics]\n";
344
345 bool ok = true;
346
347 for (auto pair : mKeyFrames)
348 {
349 KeyFrame* keyFrame = pair.second;
350 Status st = saveKeyFrameFile(keyFrame, sDataFolder);
351 if (st.ok())
352 {
353 //qDebug() << "Layer [" << name() << "] FN=" << keyFrame->fileName();
354 if (!keyFrame->fileName().isEmpty())
355 attachedFiles.append(keyFrame->fileName());
356 }
357 else
358 {
359 ok = false;
360 dd.collect(st.details());
361 dd << QString("- Keyframe[%1] failed to save").arg(keyFrame->pos());
362 }
363 progressStep();
364 }
365 if (!ok)
366 {
367 dd << "\nError: Failed to save one or more files";
368 return Status(Status::FAIL, dd);
369 }
370 return Status::OK;
371}
372
373void Layer::setModified(int position, bool modified) const
374{
375 KeyFrame* key = getKeyFrameAt(position);
376 if (key)
377 {
378 key->setModified(modified);
379 }
380}
381
382bool Layer::isFrameSelected(int position) const
383{
384 KeyFrame* keyFrame = getKeyFrameWhichCovers(position);
385 if (keyFrame == nullptr) { return false; }
386
387 int frameFound = mSelectedFrames_byLast.contains(keyFrame->pos());
388 Q_ASSERT(!frameFound || keyFrame->isSelected());
389 return frameFound;
390}
391
392void Layer::setFrameSelected(int position, bool isSelected)
393{
394 KeyFrame* keyFrame = getKeyFrameWhichCovers(position);
395 if (keyFrame != nullptr)
396 {
397 int startPosition = keyFrame->pos();
398
399 if (isSelected && !mSelectedFrames_byLast.contains(startPosition))
400 {
401 // Add the selected frame to the lists
402 mSelectedFrames_byLast.insert(0, startPosition);
403 mSelectedFrames_byPosition.append(startPosition);
404
405 // We need to keep the list of selected frames sorted
406 // in order to easily handle their movement
407 std::sort(mSelectedFrames_byPosition.begin(), mSelectedFrames_byPosition.end(), sortAsc);
408 }
409 else if (!isSelected)
410 {
411 mSelectedFrames_byLast.removeOne(startPosition);
412 mSelectedFrames_byPosition.removeOne(startPosition);
413 }
414 keyFrame->setSelected(isSelected);
415 }
416}
417
418void Layer::toggleFrameSelected(int position, bool allowMultiple)
419{
420 bool wasSelected = isFrameSelected(position);
421
422 if (!allowMultiple)
423 {
424 deselectAll();
425 }
426
427 setFrameSelected(position, !wasSelected);
428}
429
430void Layer::extendSelectionTo(int position)
431{
432 if (mSelectedFrames_byLast.count() > 0)
433 {
434 int lastSelected = mSelectedFrames_byLast[0];
435 int startPos;
436 int endPos;
437
438 if (lastSelected < position)
439 {
440 startPos = lastSelected;
441 endPos = position;
442 }
443 else
444 {
445 startPos = position;
446 endPos = lastSelected;
447 }
448
449 int i = startPos;
450 while (i <= endPos)
451 {
452 setFrameSelected(i, true);
453 i++;
454 }
455 }
456}
457
458bool Layer::newSelectionOfConnectedFrames(int position)
459{
460 // Deselect all before extending to make sure we don't get already
461 // selected frames
462 deselectAll();
463
464 if (!keyExists(position)) { return false; }
465
466 setFrameSelected(position, true);
467
468 // Find keyframes that are connected and make sure we're below max.
469 while (position < getMaxKeyFramePosition() && getKeyFrameWhichCovers(position) != nullptr) {
470 position++;
471 }
472
473 extendSelectionTo(position);
474
475 return true;
476}
477
478void Layer::selectAllFramesAfter(int position)
479{
480 int startPosition = position;
481 int endPosition = getMaxKeyFramePosition();
482
483 if (!keyExists(startPosition))
484 {
485 startPosition = getNextKeyFramePosition(startPosition);
486 }
487
488 if (startPosition > 0 && startPosition <= endPosition)
489 {
490 deselectAll();
491 setFrameSelected(startPosition, true);
492 extendSelectionTo(endPosition);
493 }
494}
495
496void Layer::deselectAll()
497{
498 mSelectedFrames_byLast.clear();
499 mSelectedFrames_byPosition.clear();
500
501 for (auto pair : mKeyFrames)
502 {
503 pair.second->setSelected(false);
504 }
505}
506
507bool Layer::canMoveSelectedFramesToOffset(int offset) const
508{
509 QList<int> newByPositions = mSelectedFrames_byPosition;
510
511 for (int pos : newByPositions)
512 {
513 pos += offset;
514 if (keyExists(pos) && !newByPositions.contains(pos)) {
515 return false;
516 }
517 }
518
519 return true;
520}
521
522void Layer::setExposureForSelectedFrames(int offset)
523{
524 auto selectedFramesByLast = mSelectedFrames_byLast;
525 auto selectedFramesByPos = mSelectedFrames_byPosition;
526
527 int addSpaceBetweenFrames = offset;
528
529 if (selectedFramesByLast.isEmpty()) { return; }
530
531 const int max = selectedFramesByPos.count()-1;
532 const int posForIndex = selectedFramesByPos[max];
533 const int nextPos = getNextKeyFramePosition(selectedFramesByPos[max]);
534
535 // When exposing the frame in front of the right most element in the selection.
536 if (posForIndex != nextPos) {
537 selectedFramesByPos.append(nextPos);
538 }
539
540 auto initialLastList = selectedFramesByLast;
541 auto initialPosList = selectedFramesByPos;
542
543 auto offsetList = QList<int>();
544
545 // Create an offset list to have reference of how many frames should be moved
546 for (int offset = 0; offset < selectedFramesByPos.count(); offset++)
547 {
548 int pos = selectedFramesByPos[offset];
549 int nextKeyPos = getNextKeyFramePosition(pos);
550
551 if (pos >= getMaxKeyFramePosition()) {
552 offsetList << (pos - 1) - getPreviousKeyFramePosition(pos) + addSpaceBetweenFrames;
553 } else { // first frame doesn't move so only the space that's required
554 offsetList << nextKeyPos - (pos + 1) + addSpaceBetweenFrames;
555 }
556 }
557
558 // Either positive or negative
559 int offsetDirection = offset > 0 ? 1 : -1;
560
561 for (int i = 0; i < selectedFramesByPos.count(); i++) {
562 const int itPos = selectedFramesByPos[i];
563 const int nextIndex = i + 1;
564 const int positionInFront = itPos + 1;
565
566 // Index safety
567 if (nextIndex < 0 || nextIndex >= selectedFramesByPos.count()) {
568 continue;
569 }
570
571 // Offset above 0 will move frames forward
572 // Offset below 0 will move a frame backwards
573 while ((offset > 0 && getNextKeyFramePosition(itPos) - positionInFront < offsetList[i]) ||
574 (getNextKeyFramePosition(itPos) - positionInFront > offsetList[i] && getNextKeyFramePosition(itPos) - positionInFront > 0)) {
575
576 selectAllFramesAfter(getNextKeyFramePosition(itPos));
577
578 for (int selIndex = 0; selIndex < mSelectedFrames_byPosition.count(); selIndex++) {
579
580 if (nextIndex+selIndex >= selectedFramesByPos.count()) { break; }
581
582 int pos = selectedFramesByPos[nextIndex+selIndex];
583
584 if (!mSelectedFrames_byPosition.contains(pos)) { continue; }
585
586 selectedFramesByPos[nextIndex+selIndex] = pos + offsetDirection;
587
588 // To make the sure we get the correct index for last selection list
589 // use the initial list where values doesn't affect the index.
590 int initialPos = initialPosList[nextIndex+selIndex];
591 int indexOfLast = initialLastList.indexOf(initialPos);
592 if (indexOfLast == -1 || nextIndex+selIndex >= selectedFramesByLast.count()) {
593 continue;
594 }
595 selectedFramesByLast[indexOfLast] = selectedFramesByLast[indexOfLast] + offsetDirection;
596 }
597
598 moveSelectedFrames(offsetDirection);
599 }
600 }
601
602 deselectAll();
603
604 // Reselect frames again based on last selection list to ensure selection behaviour
605 // works correctly
606 for (int pos : selectedFramesByLast) {
607 Q_UNUSED(pos)
608 setFrameSelected(selectedFramesByLast.takeLast(), true);
609 }
610}
611
612bool Layer::reverseOrderOfSelection()
613{
614 QList<int> selectedIndexes = mSelectedFrames_byPosition;
615
616 if (selectedIndexes.isEmpty()) { return false; }
617
618 for (int swapBegin = 0, swapEnd = selectedIndexes.count()-1; swapBegin < swapEnd; swapBegin++, swapEnd--) {
619 int oldPos = selectedIndexes[swapBegin];
620 int newPos = selectedIndexes[swapEnd];
621 bool canSwap = swapKeyFrames(oldPos, newPos);
622 Q_ASSERT(canSwap);
623 }
624 return true;
625}
626
627bool Layer::moveSelectedFrames(int offset)
628{
629 if (offset == 0 || mSelectedFrames_byPosition.count() <= 0) {
630 return false;
631 }
632
633 // If we are moving to the right we start moving selected frames from the highest (right) to the lowest (left)
634 int indexInSelection = mSelectedFrames_byPosition.count() - 1;
635 int step = -1;
636
637 if (offset < 0)
638 {
639 // If we are moving to the left we start moving selected frames from the lowest (left) to the highest (right)
640 indexInSelection = 0;
641 step = 1;
642
643 // Check if we are not moving out of the timeline
644 if (mSelectedFrames_byPosition[0] + offset < 1) {
645 offset = 1 - mSelectedFrames_byPosition[0];
646 }
647 }
648
649 while (!canMoveSelectedFramesToOffset(offset)) { offset += 1; }
650 if (offset == 0) { return false; }
651
652 for (; indexInSelection > -1 && indexInSelection < mSelectedFrames_byPosition.count(); indexInSelection += step)
653 {
654 int fromPos = mSelectedFrames_byPosition[indexInSelection];
655 int toPos = fromPos + offset;
656
657 // Get the frame to move
658 KeyFrame* selectedFrame = getKeyFrameAt(fromPos);
659
660 Q_ASSERT(!keyExists(toPos));
661
662 mKeyFrames.erase(fromPos);
663 markFrameAsDirty(fromPos);
664
665 // Update the position of the selected frame
666 selectedFrame->setPos(toPos);
667 mKeyFrames.insert(std::make_pair(toPos, selectedFrame));
668 markFrameAsDirty(toPos);
669 }
670
671 // Update selection lists
672 for (int& pos : mSelectedFrames_byPosition)
673 {
674 pos += offset;
675 }
676 for (int& pos : mSelectedFrames_byLast)
677 {
678 pos += offset;
679 }
680 return true;
681}
682
683bool Layer::isPaintable() const
684{
685 return (type() == BITMAP || type() == VECTOR);
686}
687
688bool Layer::keyExistsWhichCovers(int frameNumber)
689{
690 return getKeyFrameWhichCovers(frameNumber) != nullptr;
691}
692
693KeyFrame* Layer::getKeyFrameWhichCovers(int frameNumber) const
694{
695 auto keyFrame = getLastKeyFrameAtPosition(frameNumber);
696 if (keyFrame != nullptr)
697 {
698 if (keyFrame->pos() + keyFrame->length() > frameNumber)
699 {
700 return keyFrame;
701 }
702 }
703 return nullptr;
704}
705
706QDomElement Layer::createBaseDomElement(QDomDocument& doc) const
707{
708 QDomElement layerTag = doc.createElement("layer");
709 layerTag.setAttribute("id", id());
710 layerTag.setAttribute("name", name());
711 layerTag.setAttribute("visibility", visible());
712 layerTag.setAttribute("type", type());
713 return layerTag;
714}
715
716void Layer::loadBaseDomElement(const QDomElement& elem)
717{
718 if (!elem.attribute("id").isNull())
719 {
720 int id = elem.attribute("id").toInt();
721 setId(id);
722 }
723 setName(elem.attribute("name", "untitled"));
724 setVisible(elem.attribute("visibility", "1").toInt());
725}
DebugDetails
Definition: pencilerror.h:25
KeyFrame
Definition: keyframe.h:30
Layer::markFrameAsDirty
void markFrameAsDirty(const int frameNumber)
Mark the frame position as dirty.
Definition: layer.h:170
Layer::newSelectionOfConnectedFrames
bool newSelectionOfConnectedFrames(int position)
Make a selection from specified position until a blank spot appears The search is only looking forwar...
Definition: layer.cpp:458
Layer::addNewKeyFrameAt
bool addNewKeyFrameAt(int position)
Creates a new keyframe at the given position, unless one already exists.
Definition: layer.cpp:170
Layer::reverseOrderOfSelection
bool reverseOrderOfSelection()
Reverse order of selected frames.
Definition: layer.cpp:612
Layer::addKeyFrame
virtual bool addKeyFrame(int position, KeyFrame *pKeyFrame)
Adds a keyframe at the given position, unless one already exists.
Definition: layer.cpp:191
Layer::setExposureForSelectedFrames
void setExposureForSelectedFrames(int offset)
Add or subtract exposure from selected frames.
Definition: layer.cpp:522
Layer::canMoveSelectedFramesToOffset
bool canMoveSelectedFramesToOffset(int offset) const
Predetermines whether the frames can be moved to a new position depending on the offset.
Definition: layer.cpp:507
Layer::insertExposureAt
bool insertExposureAt(int position)
Will insert an empty frame (exposure) after the given position.
Definition: layer.cpp:204
Status
Definition: pencilerror.h:40
QDomDocument
QDomDocument::createElement
QDomElement createElement(const QString &tagName)
QDomElement
QDomElement::attribute
QString attribute(const QString &name, const QString &defValue) const const
QDomElement::setAttribute
void setAttribute(const QString &name, const QString &value)
QList
QList::append
void append(const T &value)
QList::begin
QList::iterator begin()
QList::clear
void clear()
QList::contains
bool contains(const T &value) const const
QList::count
int count(const T &value) const const
QList::end
QList::iterator end()
QList::indexOf
int indexOf(const T &value, int from) const const
QList::insert
void insert(int i, const T &value)
QList::isEmpty
bool isEmpty() const const
QList::removeAll
int removeAll(const T &value)
QList::removeOne
bool removeOne(const T &value)
QString
QString::arg
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QString::isEmpty
bool isEmpty() const const
QString::isNull
bool isNull() const const
QString::toInt
int toInt(bool *ok, int base) const const
QStringList
Qt::left
QTextStream & left(QTextStream &stream)
Qt::right
QTextStream & right(QTextStream &stream)
Generated on Thu May 8 2025 04:47:53 for Pencil2D by doxygen 1.9.6 based on revision 4513250b1d5b1a3676ec0e67b06b7a885ceaae39