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