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