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 | |
45 | namespace app { |
46 | |
47 | using namespace base; |
48 | using namespace doc; |
49 | |
50 | Doc::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 | |
70 | Doc::~Doc() |
71 | { |
72 | DOC_TRACE("DOC: Deleting" , this); |
73 | removeFromContext(); |
74 | } |
75 | |
76 | void 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 | |
99 | bool Doc::canWriteLockFromRead() const |
100 | { |
101 | return m_rwLock.canWriteLockFromRead(); |
102 | } |
103 | |
104 | bool Doc::readLock(int timeout) |
105 | { |
106 | return m_rwLock.lock(base::RWLock::ReadLock, timeout); |
107 | } |
108 | |
109 | bool Doc::writeLock(int timeout) |
110 | { |
111 | return m_rwLock.lock(base::RWLock::WriteLock, timeout); |
112 | } |
113 | |
114 | bool Doc::upgradeToWrite(int timeout) |
115 | { |
116 | return m_rwLock.upgradeToWrite(timeout); |
117 | } |
118 | |
119 | void Doc::downgradeToRead() |
120 | { |
121 | m_rwLock.downgradeToRead(); |
122 | } |
123 | |
124 | void Doc::unlock() |
125 | { |
126 | m_rwLock.unlock(); |
127 | } |
128 | |
129 | bool Doc::weakLock(std::atomic<base::RWLock::WeakLock>* weak_lock_flag) |
130 | { |
131 | return m_rwLock.weakLock(weak_lock_flag); |
132 | } |
133 | |
134 | void Doc::weakUnlock() |
135 | { |
136 | m_rwLock.weakUnlock(); |
137 | } |
138 | |
139 | void 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 | |
151 | DocApi Doc::getApi(Transaction& transaction) |
152 | { |
153 | return DocApi(this, transaction); |
154 | } |
155 | |
156 | ////////////////////////////////////////////////////////////////////// |
157 | // Main properties |
158 | |
159 | color_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 | |
168 | color_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 | |
181 | void Doc::notifyGeneralUpdate() |
182 | { |
183 | DocEvent ev(this); |
184 | notify_observers<DocEvent&>(&DocObserver::onGeneralUpdate, ev); |
185 | } |
186 | |
187 | void Doc::notifyColorSpaceChanged() |
188 | { |
189 | updateOSColorSpace(true); |
190 | |
191 | DocEvent ev(this); |
192 | ev.sprite(sprite()); |
193 | notify_observers<DocEvent&>(&DocObserver::onColorSpaceChanged, ev); |
194 | } |
195 | |
196 | void Doc::notifyPaletteChanged() |
197 | { |
198 | DocEvent ev(this); |
199 | ev.sprite(sprite()); |
200 | notify_observers<DocEvent&>(&DocObserver::onPaletteChanged, ev); |
201 | } |
202 | |
203 | void 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 | |
212 | void 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 | |
220 | void 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 | |
229 | void 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 | |
240 | void 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 | |
251 | void Doc::notifySelectionChanged() |
252 | { |
253 | DocEvent ev(this); |
254 | notify_observers<DocEvent&>(&DocObserver::onSelectionChanged, ev); |
255 | } |
256 | |
257 | void Doc::notifySelectionBoundariesChanged() |
258 | { |
259 | DocEvent ev(this); |
260 | notify_observers<DocEvent&>(&DocObserver::onSelectionBoundariesChanged, ev); |
261 | } |
262 | |
263 | void Doc::notifyTilesetChanged(Tileset* tileset) |
264 | { |
265 | DocEvent ev(this); |
266 | ev.tileset(tileset); |
267 | notify_observers<DocEvent&>(&DocObserver::onTilesetChanged, ev); |
268 | } |
269 | |
270 | bool Doc::isModified() const |
271 | { |
272 | return !m_undo->isSavedState(); |
273 | } |
274 | |
275 | bool Doc::isAssociatedToFile() const |
276 | { |
277 | return (m_flags & kAssociatedToFile) == kAssociatedToFile; |
278 | } |
279 | |
280 | void Doc::markAsSaved() |
281 | { |
282 | m_undo->markSavedState(); |
283 | m_flags |= kAssociatedToFile; |
284 | } |
285 | |
286 | void Doc::impossibleToBackToSavedState() |
287 | { |
288 | m_undo->impossibleToBackToSavedState(); |
289 | } |
290 | |
291 | bool 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 | |
298 | bool Doc::inhibitBackup() const |
299 | { |
300 | return (m_flags & kInhibitBackup) == kInhibitBackup; |
301 | } |
302 | |
303 | void Doc::setInhibitBackup(const bool inhibitBackup) |
304 | { |
305 | if (inhibitBackup) |
306 | m_flags |= kInhibitBackup; |
307 | else |
308 | m_flags &= ~kInhibitBackup; |
309 | } |
310 | |
311 | void Doc::markAsBackedUp() |
312 | { |
313 | DOC_TRACE("DOC: Mark as fully backed up" , this); |
314 | |
315 | m_flags |= kFullyBackedUp; |
316 | } |
317 | |
318 | bool Doc::isFullyBackedUp() const |
319 | { |
320 | return (m_flags & kFullyBackedUp ? true: false); |
321 | } |
322 | |
323 | ////////////////////////////////////////////////////////////////////// |
324 | // Loaded options from file |
325 | |
326 | void Doc::setFormatOptions(const FormatOptionsPtr& format_options) |
327 | { |
328 | m_format_options = format_options; |
329 | } |
330 | |
331 | ////////////////////////////////////////////////////////////////////// |
332 | // Boundaries |
333 | |
334 | void Doc::destroyMaskBoundaries() |
335 | { |
336 | m_maskBoundaries.reset(); |
337 | notifySelectionBoundariesChanged(); |
338 | } |
339 | |
340 | void 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 | |
366 | void Doc::setMask(const Mask* mask) |
367 | { |
368 | ASSERT(mask); |
369 | |
370 | m_mask->copyFrom(mask); |
371 | m_flags |= kMaskVisible; |
372 | |
373 | resetTransformation(); |
374 | } |
375 | |
376 | bool 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 | |
383 | void Doc::setMaskVisible(bool visible) |
384 | { |
385 | if (visible) |
386 | m_flags |= kMaskVisible; |
387 | else |
388 | m_flags &= ~kMaskVisible; |
389 | } |
390 | |
391 | ////////////////////////////////////////////////////////////////////// |
392 | // Transformation |
393 | |
394 | Transformation Doc::getTransformation() const |
395 | { |
396 | return m_transformation; |
397 | } |
398 | |
399 | void Doc::setTransformation(const Transformation& transform) |
400 | { |
401 | m_transformation = transform; |
402 | } |
403 | |
404 | void Doc::resetTransformation() |
405 | { |
406 | m_transformation = Transformation(gfx::RectF(m_mask->bounds()), m_transformation.cornerThick()); |
407 | } |
408 | |
409 | ////////////////////////////////////////////////////////////////////// |
410 | // Copying |
411 | |
412 | void 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 | |
503 | Doc* 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 | |
585 | void 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 | |
597 | void Doc::onFileNameChange() |
598 | { |
599 | notify_observers(&DocObserver::onFileNameChanged, this); |
600 | } |
601 | |
602 | void Doc::onContextChanged() |
603 | { |
604 | m_undo->setContext(context()); |
605 | } |
606 | |
607 | void Doc::removeFromContext() |
608 | { |
609 | if (m_ctx) { |
610 | m_ctx->documents().remove(this); |
611 | m_ctx = nullptr; |
612 | |
613 | onContextChanged(); |
614 | } |
615 | } |
616 | |
617 | void 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 |
634 | gfx::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 | |