1// Aseprite
2// Copyright (C) 2018-2021 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#ifdef HAVE_CONFIG_H
9#include "config.h"
10#endif
11
12#include "app/doc.h"
13
14#include "app/app.h"
15#include "app/color_target.h"
16#include "app/color_utils.h"
17#include "app/context.h"
18#include "app/context.h"
19#include "app/doc_api.h"
20#include "app/doc_event.h"
21#include "app/doc_observer.h"
22#include "app/doc_undo.h"
23#include "app/file/format_options.h"
24#include "app/flatten.h"
25#include "app/pref/preferences.h"
26#include "app/util/cel_ops.h"
27#include "base/memory.h"
28#include "doc/cel.h"
29#include "doc/layer.h"
30#include "doc/mask.h"
31#include "doc/mask_boundaries.h"
32#include "doc/palette.h"
33#include "doc/slice.h"
34#include "doc/sprite.h"
35#include "doc/tag.h"
36#include "os/system.h"
37#include "os/window.h"
38#include "ui/system.h"
39
40#include <limits>
41#include <map>
42
43#define DOC_TRACE(...) // TRACEARGS
44
45namespace app {
46
47using namespace base;
48using namespace doc;
49
50Doc::Doc(Sprite* sprite)
51 : m_ctx(nullptr)
52 , m_flags(kMaskVisible)
53 , m_undo(new DocUndo)
54 , m_transaction(nullptr)
55 // Information about the file format used to load/save this document
56 , m_format_options(nullptr)
57 // Mask
58 , m_mask(new Mask())
59 , m_lastDrawingPoint(Doc::NoLastDrawingPoint())
60{
61 setFilename("Sprite");
62
63 if (sprite)
64 sprites().add(sprite);
65
66 updateOSColorSpace(false);
67 DOC_TRACE("DOC: New", this);
68}
69
70Doc::~Doc()
71{
72 DOC_TRACE("DOC: Deleting", this);
73 removeFromContext();
74}
75
76void Doc::setContext(Context* ctx)
77{
78 if (ctx == m_ctx)
79 return;
80
81 removeFromContext();
82
83 m_ctx = ctx;
84 if (ctx) {
85 DOC_TRACE("DOC: Removing as fully backed up", this);
86
87 // Remove the flag that indicates that this doc is fully backed
88 // up, because now we are inside a context, so the user can change
89 // it again and the backup will be outdated.
90 if (m_flags & kFullyBackedUp)
91 m_flags ^= kFullyBackedUp;
92
93 ctx->documents().add(this);
94 }
95
96 onContextChanged();
97}
98
99bool Doc::canWriteLockFromRead() const
100{
101 return m_rwLock.canWriteLockFromRead();
102}
103
104bool Doc::readLock(int timeout)
105{
106 return m_rwLock.lock(base::RWLock::ReadLock, timeout);
107}
108
109bool Doc::writeLock(int timeout)
110{
111 return m_rwLock.lock(base::RWLock::WriteLock, timeout);
112}
113
114bool Doc::upgradeToWrite(int timeout)
115{
116 return m_rwLock.upgradeToWrite(timeout);
117}
118
119void Doc::downgradeToRead()
120{
121 m_rwLock.downgradeToRead();
122}
123
124void Doc::unlock()
125{
126 m_rwLock.unlock();
127}
128
129bool Doc::weakLock(std::atomic<base::RWLock::WeakLock>* weak_lock_flag)
130{
131 return m_rwLock.weakLock(weak_lock_flag);
132}
133
134void Doc::weakUnlock()
135{
136 m_rwLock.weakUnlock();
137}
138
139void Doc::setTransaction(Transaction* transaction)
140{
141 if (transaction) {
142 ASSERT(!m_transaction);
143 m_transaction = transaction;
144 }
145 else {
146 ASSERT(m_transaction);
147 m_transaction = nullptr;
148 }
149}
150
151DocApi Doc::getApi(Transaction& transaction)
152{
153 return DocApi(this, transaction);
154}
155
156//////////////////////////////////////////////////////////////////////
157// Main properties
158
159color_t Doc::bgColor() const
160{
161 return color_utils::color_for_target(
162 Preferences::instance().colorBar.bgColor(),
163 ColorTarget(ColorTarget::BackgroundLayer,
164 sprite()->pixelFormat(),
165 sprite()->transparentColor()));
166}
167
168color_t Doc::bgColor(Layer* layer) const
169{
170 if (layer->isBackground())
171 return color_utils::color_for_layer(
172 Preferences::instance().colorBar.bgColor(),
173 layer);
174 else
175 return layer->sprite()->transparentColor();
176}
177
178//////////////////////////////////////////////////////////////////////
179// Notifications
180
181void Doc::notifyGeneralUpdate()
182{
183 DocEvent ev(this);
184 notify_observers<DocEvent&>(&DocObserver::onGeneralUpdate, ev);
185}
186
187void Doc::notifyColorSpaceChanged()
188{
189 updateOSColorSpace(true);
190
191 DocEvent ev(this);
192 ev.sprite(sprite());
193 notify_observers<DocEvent&>(&DocObserver::onColorSpaceChanged, ev);
194}
195
196void Doc::notifyPaletteChanged()
197{
198 DocEvent ev(this);
199 ev.sprite(sprite());
200 notify_observers<DocEvent&>(&DocObserver::onPaletteChanged, ev);
201}
202
203void Doc::notifySpritePixelsModified(Sprite* sprite, const gfx::Region& region, frame_t frame)
204{
205 DocEvent ev(this);
206 ev.sprite(sprite);
207 ev.region(region);
208 ev.frame(frame);
209 notify_observers<DocEvent&>(&DocObserver::onSpritePixelsModified, ev);
210}
211
212void Doc::notifyExposeSpritePixels(Sprite* sprite, const gfx::Region& region)
213{
214 DocEvent ev(this);
215 ev.sprite(sprite);
216 ev.region(region);
217 notify_observers<DocEvent&>(&DocObserver::onExposeSpritePixels, ev);
218}
219
220void Doc::notifyLayerMergedDown(Layer* srcLayer, Layer* targetLayer)
221{
222 DocEvent ev(this);
223 ev.sprite(srcLayer->sprite());
224 ev.layer(srcLayer);
225 ev.targetLayer(targetLayer);
226 notify_observers<DocEvent&>(&DocObserver::onLayerMergedDown, ev);
227}
228
229void Doc::notifyCelMoved(Layer* fromLayer, frame_t fromFrame, Layer* toLayer, frame_t toFrame)
230{
231 DocEvent ev(this);
232 ev.sprite(toLayer->sprite());
233 ev.layer(fromLayer);
234 ev.frame(fromFrame);
235 ev.targetLayer(toLayer);
236 ev.targetFrame(toFrame);
237 notify_observers<DocEvent&>(&DocObserver::onCelMoved, ev);
238}
239
240void Doc::notifyCelCopied(Layer* fromLayer, frame_t fromFrame, Layer* toLayer, frame_t toFrame)
241{
242 DocEvent ev(this);
243 ev.sprite(toLayer->sprite());
244 ev.layer(fromLayer); // From layer can be nullptr
245 ev.frame(fromFrame);
246 ev.targetLayer(toLayer);
247 ev.targetFrame(toFrame);
248 notify_observers<DocEvent&>(&DocObserver::onCelCopied, ev);
249}
250
251void Doc::notifySelectionChanged()
252{
253 DocEvent ev(this);
254 notify_observers<DocEvent&>(&DocObserver::onSelectionChanged, ev);
255}
256
257void Doc::notifySelectionBoundariesChanged()
258{
259 DocEvent ev(this);
260 notify_observers<DocEvent&>(&DocObserver::onSelectionBoundariesChanged, ev);
261}
262
263void Doc::notifyTilesetChanged(Tileset* tileset)
264{
265 DocEvent ev(this);
266 ev.tileset(tileset);
267 notify_observers<DocEvent&>(&DocObserver::onTilesetChanged, ev);
268}
269
270bool Doc::isModified() const
271{
272 return !m_undo->isSavedState();
273}
274
275bool Doc::isAssociatedToFile() const
276{
277 return (m_flags & kAssociatedToFile) == kAssociatedToFile;
278}
279
280void Doc::markAsSaved()
281{
282 m_undo->markSavedState();
283 m_flags |= kAssociatedToFile;
284}
285
286void Doc::impossibleToBackToSavedState()
287{
288 m_undo->impossibleToBackToSavedState();
289}
290
291bool Doc::needsBackup() const
292{
293 // If the undo history isn't empty, the user has modified the
294 // document, so we need to backup those changes.
295 return m_undo->canUndo() || m_undo->canRedo();
296}
297
298bool Doc::inhibitBackup() const
299{
300 return (m_flags & kInhibitBackup) == kInhibitBackup;
301}
302
303void Doc::setInhibitBackup(const bool inhibitBackup)
304{
305 if (inhibitBackup)
306 m_flags |= kInhibitBackup;
307 else
308 m_flags &= ~kInhibitBackup;
309}
310
311void Doc::markAsBackedUp()
312{
313 DOC_TRACE("DOC: Mark as fully backed up", this);
314
315 m_flags |= kFullyBackedUp;
316}
317
318bool Doc::isFullyBackedUp() const
319{
320 return (m_flags & kFullyBackedUp ? true: false);
321}
322
323//////////////////////////////////////////////////////////////////////
324// Loaded options from file
325
326void Doc::setFormatOptions(const FormatOptionsPtr& format_options)
327{
328 m_format_options = format_options;
329}
330
331//////////////////////////////////////////////////////////////////////
332// Boundaries
333
334void Doc::destroyMaskBoundaries()
335{
336 m_maskBoundaries.reset();
337 notifySelectionBoundariesChanged();
338}
339
340void Doc::generateMaskBoundaries(const Mask* mask)
341{
342 m_maskBoundaries.reset();
343
344 // No mask specified? Use the current one in the document
345 if (!mask) {
346 if (!isMaskVisible()) // The mask is hidden
347 return; // Done, without boundaries
348 else
349 mask = this->mask(); // Use the document mask
350 }
351
352 ASSERT(mask);
353
354 if (!mask->isEmpty()) {
355 m_maskBoundaries.regen(mask->bitmap());
356 m_maskBoundaries.offset(mask->bounds().x,
357 mask->bounds().y);
358 }
359
360 notifySelectionBoundariesChanged();
361}
362
363//////////////////////////////////////////////////////////////////////
364// Mask
365
366void Doc::setMask(const Mask* mask)
367{
368 ASSERT(mask);
369
370 m_mask->copyFrom(mask);
371 m_flags |= kMaskVisible;
372
373 resetTransformation();
374}
375
376bool Doc::isMaskVisible() const
377{
378 return
379 (m_flags & kMaskVisible) && // The mask was not hidden by the user explicitly
380 !m_mask->isEmpty(); // The mask is not empty
381}
382
383void Doc::setMaskVisible(bool visible)
384{
385 if (visible)
386 m_flags |= kMaskVisible;
387 else
388 m_flags &= ~kMaskVisible;
389}
390
391//////////////////////////////////////////////////////////////////////
392// Transformation
393
394Transformation Doc::getTransformation() const
395{
396 return m_transformation;
397}
398
399void Doc::setTransformation(const Transformation& transform)
400{
401 m_transformation = transform;
402}
403
404void Doc::resetTransformation()
405{
406 m_transformation = Transformation(gfx::RectF(m_mask->bounds()), m_transformation.cornerThick());
407}
408
409//////////////////////////////////////////////////////////////////////
410// Copying
411
412void Doc::copyLayerContent(const Layer* sourceLayer0, Doc* destDoc, Layer* destLayer0) const
413{
414 LayerFlags dstFlags = sourceLayer0->flags();
415
416 // Remove the "background" flag if the destDoc already has a background layer.
417 if (((int)dstFlags & (int)LayerFlags::Background) == (int)LayerFlags::Background &&
418 (destDoc->sprite()->backgroundLayer())) {
419 dstFlags = (LayerFlags)((int)dstFlags & ~(int)(LayerFlags::BackgroundLayerFlags));
420 }
421
422 // Copy the layer name/flags/user data
423 destLayer0->setName(sourceLayer0->name());
424 destLayer0->setFlags(dstFlags);
425 destLayer0->setUserData(sourceLayer0->userData());
426
427 if (sourceLayer0->isImage() && destLayer0->isImage()) {
428 const LayerImage* sourceLayer = static_cast<const LayerImage*>(sourceLayer0);
429 LayerImage* destLayer = static_cast<LayerImage*>(destLayer0);
430
431 // Copy blend mode and opacity
432 destLayer->setBlendMode(sourceLayer->blendMode());
433 destLayer->setOpacity(sourceLayer->opacity());
434
435 // Copy cels
436 CelConstIterator it = sourceLayer->getCelBegin();
437 CelConstIterator end = sourceLayer->getCelEnd();
438
439 std::map<ObjectId, Cel*> linked;
440
441 for (; it != end; ++it) {
442 const Cel* sourceCel = *it;
443 if (sourceCel->frame() > destLayer->sprite()->lastFrame())
444 break;
445
446 std::unique_ptr<Cel> newCel(nullptr);
447
448 auto it = linked.find(sourceCel->data()->id());
449 if (it != linked.end()) {
450 newCel.reset(Cel::MakeLink(sourceCel->frame(),
451 it->second));
452 }
453 else {
454 newCel.reset(create_cel_copy(nullptr, // TODO add undo information?
455 sourceCel,
456 destLayer->sprite(),
457 destLayer,
458 sourceCel->frame()));
459 linked.insert(std::make_pair(sourceCel->data()->id(), newCel.get()));
460 }
461
462 destLayer->addCel(newCel.get());
463 newCel.release();
464 }
465 }
466 else if (sourceLayer0->isGroup() && destLayer0->isGroup()) {
467 const LayerGroup* sourceLayer = static_cast<const LayerGroup*>(sourceLayer0);
468 LayerGroup* destLayer = static_cast<LayerGroup*>(destLayer0);
469
470 for (Layer* sourceChild : sourceLayer->layers()) {
471 std::unique_ptr<Layer> destChild(nullptr);
472
473 if (sourceChild->isImage()) {
474 destChild.reset(new LayerImage(destLayer->sprite()));
475 copyLayerContent(sourceChild, destDoc, destChild.get());
476 }
477 else if (sourceChild->isGroup()) {
478 destChild.reset(new LayerGroup(destLayer->sprite()));
479 copyLayerContent(sourceChild, destDoc, destChild.get());
480 }
481 else {
482 ASSERT(false);
483 }
484
485 ASSERT(destChild != NULL);
486
487 // Add the new layer in the sprite.
488
489 Layer* newLayer = destChild.release();
490 Layer* afterThis = destLayer->lastLayer();
491
492 destLayer->addLayer(newLayer);
493 destChild.release();
494
495 destLayer->stackLayer(newLayer, afterThis);
496 }
497 }
498 else {
499 ASSERT(false && "Trying to copy two incompatible layers");
500 }
501}
502
503Doc* Doc::duplicate(DuplicateType type) const
504{
505 const Sprite* sourceSprite = sprite();
506 std::unique_ptr<Sprite> spriteCopyPtr(new Sprite(
507 sourceSprite->spec(),
508 sourceSprite->palette(frame_t(0))->size()));
509
510 std::unique_ptr<Doc> documentCopy(new Doc(spriteCopyPtr.get()));
511 Sprite* spriteCopy = spriteCopyPtr.release();
512
513 spriteCopy->setTotalFrames(sourceSprite->totalFrames());
514
515 // Copy frames duration
516 for (frame_t i(0); i < sourceSprite->totalFrames(); ++i)
517 spriteCopy->setFrameDuration(i, sourceSprite->frameDuration(i));
518
519 // Copy frame tags
520 for (const Tag* tag : sourceSprite->tags())
521 spriteCopy->tags().add(new Tag(*tag));
522
523 // Copy slices
524 for (const Slice *slice : sourceSprite->slices()) {
525 auto sliceCopy = new Slice(*slice);
526 spriteCopy->slices().add(sliceCopy);
527
528 ASSERT(sliceCopy->owner() == &spriteCopy->slices());
529 }
530
531 // Copy color palettes
532 {
533 PalettesList::const_iterator it = sourceSprite->getPalettes().begin();
534 PalettesList::const_iterator end = sourceSprite->getPalettes().end();
535 for (; it != end; ++it) {
536 const Palette* pal = *it;
537 spriteCopy->setPalette(pal, true);
538 }
539 }
540
541 switch (type) {
542
543 case DuplicateExactCopy:
544 // Copy the layer group
545 copyLayerContent(sourceSprite->root(),
546 documentCopy.get(),
547 spriteCopy->root());
548
549 ASSERT((spriteCopy->backgroundLayer() && sourceSprite->backgroundLayer()) ||
550 (!spriteCopy->backgroundLayer() && !sourceSprite->backgroundLayer()));
551 break;
552
553 case DuplicateWithFlattenLayers:
554 {
555 // Flatten layers
556 ASSERT(sourceSprite->root() != NULL);
557
558 LayerImage* flatLayer = create_flatten_layer_copy
559 (spriteCopy,
560 sourceSprite->root(),
561 gfx::Rect(0, 0, sourceSprite->width(), sourceSprite->height()),
562 frame_t(0), sourceSprite->lastFrame(),
563 Preferences::instance().experimental.newBlend());
564
565 // Add and select the new flat layer
566 spriteCopy->root()->addLayer(flatLayer);
567
568 // Configure the layer as background only if the original
569 // sprite has a background layer.
570 if (sourceSprite->backgroundLayer() != NULL)
571 flatLayer->configureAsBackground();
572 }
573 break;
574 }
575
576 // Copy only some flags
577 documentCopy->m_flags = (m_flags & kMaskVisible);
578 documentCopy->setFilename(filename());
579 documentCopy->setMask(mask());
580 documentCopy->generateMaskBoundaries();
581
582 return documentCopy.release();
583}
584
585void Doc::close()
586{
587 try {
588 notify_observers<Doc*>(&DocObserver::onCloseDocument, this);
589 }
590 catch (...) {
591 LOG(ERROR, "DOC: Exception on DocObserver::onCloseDocument()\n");
592 }
593
594 removeFromContext();
595}
596
597void Doc::onFileNameChange()
598{
599 notify_observers(&DocObserver::onFileNameChanged, this);
600}
601
602void Doc::onContextChanged()
603{
604 m_undo->setContext(context());
605}
606
607void Doc::removeFromContext()
608{
609 if (m_ctx) {
610 m_ctx->documents().remove(this);
611 m_ctx = nullptr;
612
613 onContextChanged();
614 }
615}
616
617void Doc::updateOSColorSpace(bool appWideSignal)
618{
619 auto system = os::instance();
620 if (system) {
621 m_osColorSpace = system->makeColorSpace(sprite()->colorSpace());
622 if (!m_osColorSpace && system->defaultWindow())
623 m_osColorSpace = system->defaultWindow()->colorSpace();
624 }
625
626 if (appWideSignal &&
627 context() &&
628 context()->activeDocument() == this) {
629 App::instance()->ColorSpaceChange();
630 }
631}
632
633// static
634gfx::Point Doc::NoLastDrawingPoint()
635{
636 return gfx::Point(std::numeric_limits<int>::min(),
637 std::numeric_limits<int>::min());
638}
639
640} // namespace app
641