1 // Aseprite
2 // Copyright (C) 2001-2018  David Capello
3 //
4 // This program is distributed under the terms of
5 // the End-User License Agreement for Aseprite.
6 
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif
10 
11 #include "app/app.h"
12 #include "app/cmd/clear_mask.h"
13 #include "app/cmd/deselect_mask.h"
14 #include "app/cmd/trim_cel.h"
15 #include "app/console.h"
16 #include "app/context_access.h"
17 #include "app/doc.h"
18 #include "app/doc_api.h"
19 #include "app/doc_range.h"
20 #include "app/doc_range_ops.h"
21 #include "app/modules/editors.h"
22 #include "app/modules/gfx.h"
23 #include "app/modules/gui.h"
24 #include "app/pref/preferences.h"
25 #include "app/transaction.h"
26 #include "app/ui/color_bar.h"
27 #include "app/ui/editor/editor.h"
28 #include "app/ui/skin/skin_theme.h"
29 #include "app/ui/timeline/timeline.h"
30 #include "app/ui_context.h"
31 #include "app/util/clipboard.h"
32 #include "app/util/clipboard_native.h"
33 #include "app/util/new_image_from_mask.h"
34 #include "base/shared_ptr.h"
35 #include "clip/clip.h"
36 #include "doc/doc.h"
37 #include "render/ordered_dither.h"
38 #include "render/quantization.h"
39 
40 #include <stdexcept>
41 
42 namespace app {
43 
44 namespace {
45 
46   class ClipboardRange : public DocsObserver {
47   public:
ClipboardRange()48     ClipboardRange() : m_doc(nullptr) {
49     }
50 
~ClipboardRange()51     ~ClipboardRange() {
52       ASSERT(!m_doc);
53     }
54 
observeUIContext()55     void observeUIContext() {
56       UIContext::instance()->documents().add_observer(this);
57     }
58 
unobserveUIContext()59     void unobserveUIContext() {
60       UIContext::instance()->documents().remove_observer(this);
61     }
62 
valid()63     bool valid() {
64       return (m_doc != nullptr);
65     }
66 
invalidate()67     void invalidate() {
68       m_doc = nullptr;
69     }
70 
setRange(Doc * doc,const DocRange & range)71     void setRange(Doc* doc, const DocRange& range) {
72       m_doc = doc;
73       m_range = range;
74     }
75 
document() const76     Doc* document() const { return m_doc; }
range() const77     DocRange range() const { return m_range; }
78 
79     // DocsObserver impl
onRemoveDocument(Doc * doc)80     void onRemoveDocument(Doc* doc) override {
81       if (doc == m_doc)
82         invalidate();
83     }
84 
85   private:
86     Doc* m_doc;
87     DocRange m_range;
88   };
89 
90 }
91 
92 namespace clipboard {
93 
94 using namespace doc;
95 
96 static base::SharedPtr<Palette> clipboard_palette;
97 static PalettePicks clipboard_picks;
98 static ImageRef clipboard_image;
99 static base::SharedPtr<Mask> clipboard_mask;
100 static ClipboardRange clipboard_range;
101 
102 static ClipboardManager* g_instance = nullptr;
103 
use_native_clipboard()104 static bool use_native_clipboard()
105 {
106   return Preferences::instance().experimental.useNativeClipboard();
107 }
108 
instance()109 ClipboardManager* ClipboardManager::instance()
110 {
111   return g_instance;
112 }
113 
ClipboardManager()114 ClipboardManager::ClipboardManager()
115 {
116   ASSERT(!g_instance);
117   g_instance = this;
118 
119   register_native_clipboard_formats();
120 
121   clipboard_range.observeUIContext();
122 }
123 
~ClipboardManager()124 ClipboardManager::~ClipboardManager()
125 {
126   clipboard_range.invalidate();
127   clipboard_range.unobserveUIContext();
128 
129   // Clean the whole clipboard
130   clipboard_palette.reset();
131   clipboard_image.reset();
132   clipboard_mask.reset();
133 
134   ASSERT(g_instance == this);
135   g_instance = nullptr;
136 }
137 
setClipboardText(const std::string & text)138 void ClipboardManager::setClipboardText(const std::string& text)
139 {
140   if (use_native_clipboard()) {
141     clip::set_text(text);
142   }
143   else {
144     m_text = text;
145   }
146 }
147 
getClipboardText(std::string & text)148 bool ClipboardManager::getClipboardText(std::string& text)
149 {
150   if (use_native_clipboard()) {
151     return clip::get_text(text);
152   }
153   else {
154     text = m_text;
155     return true;
156   }
157 }
158 
set_clipboard_image(Image * image,Mask * mask,Palette * palette,bool set_system_clipboard,bool image_source_is_transparent)159 static void set_clipboard_image(Image* image,
160                                 Mask* mask,
161                                 Palette* palette,
162                                 bool set_system_clipboard,
163                                 bool image_source_is_transparent)
164 {
165   clipboard_palette.reset(palette);
166   clipboard_picks.clear();
167   clipboard_image.reset(image);
168   clipboard_mask.reset(mask);
169 
170   // Copy image to the native clipboard
171   if (set_system_clipboard) {
172     color_t oldMask;
173     if (image) {
174       oldMask = image->maskColor();
175       if (!image_source_is_transparent)
176         image->setMaskColor(-1);
177     }
178 
179     if (use_native_clipboard())
180       set_native_clipboard_bitmap(image, mask, palette);
181 
182     if (image && !image_source_is_transparent)
183       image->setMaskColor(oldMask);
184   }
185 
186   clipboard_range.invalidate();
187 }
188 
copy_from_document(const Site & site,bool merged=false)189 static bool copy_from_document(const Site& site, bool merged = false)
190 {
191   const Doc* document = static_cast<const Doc*>(site.document());
192   ASSERT(document);
193 
194   const Mask* mask = document->mask();
195   Image* image = new_image_from_mask(site, mask, merged);
196   if (!image)
197     return false;
198 
199   const Palette* pal = document->sprite()->palette(site.frame());
200   set_clipboard_image(
201     image,
202     (mask ? new Mask(*mask): nullptr),
203     (pal ? new Palette(*pal): nullptr),
204     true,
205     site.layer() && !site.layer()->isBackground());
206 
207   return true;
208 }
209 
get_current_format()210 ClipboardFormat get_current_format()
211 {
212   // Check if the native clipboard has an image
213   if (use_native_clipboard() &&
214       has_native_clipboard_bitmap())
215     return ClipboardImage;
216   else if (clipboard_image)
217     return ClipboardImage;
218   else if (clipboard_range.valid())
219     return ClipboardDocRange;
220   else if (clipboard_palette && clipboard_picks.picks())
221     return ClipboardPaletteEntries;
222   else
223     return ClipboardNone;
224 }
225 
get_document_range_info(Doc ** document,DocRange * range)226 void get_document_range_info(Doc** document, DocRange* range)
227 {
228   if (clipboard_range.valid()) {
229     *document = clipboard_range.document();
230     *range = clipboard_range.range();
231   }
232   else {
233     *document = NULL;
234   }
235 }
236 
clear_content()237 void clear_content()
238 {
239   set_clipboard_image(nullptr, nullptr, nullptr, true, false);
240 }
241 
cut(ContextWriter & writer)242 void cut(ContextWriter& writer)
243 {
244   ASSERT(writer.document() != NULL);
245   ASSERT(writer.sprite() != NULL);
246   ASSERT(writer.layer() != NULL);
247 
248   if (!copy_from_document(*writer.site())) {
249     Console console;
250     console.printf("Can't copying an image portion from the current layer\n");
251   }
252   else {
253     {
254       Transaction transaction(writer.context(), "Cut");
255       transaction.execute(new cmd::ClearMask(writer.cel()));
256 
257       ASSERT(writer.cel());
258       if (writer.cel() &&
259           writer.cel()->layer()->isTransparent())
260         transaction.execute(new cmd::TrimCel(writer.cel()));
261 
262       transaction.execute(new cmd::DeselectMask(writer.document()));
263       transaction.commit();
264     }
265     writer.document()->generateMaskBoundaries();
266     update_screen_for_document(writer.document());
267   }
268 }
269 
copy(const ContextReader & reader)270 void copy(const ContextReader& reader)
271 {
272   ASSERT(reader.document() != NULL);
273 
274   if (!copy_from_document(*reader.site())) {
275     Console console;
276     console.printf("Can't copying an image portion from the current layer\n");
277     return;
278   }
279 }
280 
copy_merged(const ContextReader & reader)281 void copy_merged(const ContextReader& reader)
282 {
283   ASSERT(reader.document() != NULL);
284 
285   copy_from_document(*reader.site(), true);
286 }
287 
copy_range(const ContextReader & reader,const DocRange & range)288 void copy_range(const ContextReader& reader, const DocRange& range)
289 {
290   ASSERT(reader.document() != NULL);
291 
292   ContextWriter writer(reader);
293 
294   clear_content();
295   clipboard_range.setRange(writer.document(), range);
296 
297   // TODO Replace this with a signal, because here the timeline
298   // depends on the clipboard and the clipboard on the timeline.
299   App::instance()->timeline()->activateClipboardRange();
300 }
301 
copy_image(const Image * image,const Mask * mask,const Palette * pal)302 void copy_image(const Image* image, const Mask* mask, const Palette* pal)
303 {
304   set_clipboard_image(
305     Image::createCopy(image),
306     (mask ? new Mask(*mask): nullptr),
307     (pal ? new Palette(*pal): nullptr),
308     true, false);
309 }
310 
copy_palette(const Palette * palette,const doc::PalettePicks & picks)311 void copy_palette(const Palette* palette, const doc::PalettePicks& picks)
312 {
313   if (!picks.picks())
314     return;                     // Do nothing case
315 
316   set_clipboard_image(nullptr,
317                       nullptr,
318                       new Palette(*palette),
319                       true, false);
320   clipboard_picks = picks;
321 }
322 
paste()323 void paste()
324 {
325   Editor* editor = current_editor;
326   if (editor == NULL)
327     return;
328 
329   Doc* dstDoc = editor->document();
330   Sprite* dstSpr = dstDoc->sprite();
331 
332   switch (get_current_format()) {
333 
334     case clipboard::ClipboardImage: {
335       // Get the image from the native clipboard.
336       if (use_native_clipboard()) {
337         Image* native_image = nullptr;
338         Mask* native_mask = nullptr;
339         Palette* native_palette = nullptr;
340         get_native_clipboard_bitmap(&native_image, &native_mask, &native_palette);
341         if (native_image)
342           set_clipboard_image(native_image, native_mask, native_palette,
343                               false, false);
344       }
345 
346       if (!clipboard_image)
347         return;
348 
349       Palette* dst_palette = dstSpr->palette(editor->frame());
350 
351       // Source image (clipboard or a converted copy to the destination 'imgtype')
352       ImageRef src_image;
353       if (clipboard_image->pixelFormat() == dstSpr->pixelFormat() &&
354         // Indexed images can be copied directly only if both images
355         // have the same palette.
356         (clipboard_image->pixelFormat() != IMAGE_INDEXED ||
357           clipboard_palette->countDiff(dst_palette, NULL, NULL) == 0)) {
358         src_image = clipboard_image;
359       }
360       else {
361         RgbMap* dst_rgbmap = dstSpr->rgbMap(editor->frame());
362 
363         src_image.reset(
364           render::convert_pixel_format(
365             clipboard_image.get(), NULL, dstSpr->pixelFormat(),
366             render::DitheringAlgorithm::None,
367             render::DitheringMatrix(),
368             dst_rgbmap, clipboard_palette.get(),
369             false,
370             0));
371       }
372 
373       // Change to MovingPixelsState
374       editor->pasteImage(src_image.get(),
375                          clipboard_mask.get());
376       break;
377     }
378 
379     case clipboard::ClipboardDocRange: {
380       DocRange srcRange = clipboard_range.range();
381       Doc* srcDoc = clipboard_range.document();
382       Sprite* srcSpr = srcDoc->sprite();
383 
384       switch (srcRange.type()) {
385 
386         case DocRange::kCels: {
387           Layer* dstLayer = editor->layer();
388           frame_t dstFrameFirst = editor->frame();
389 
390           DocRange dstRange;
391           dstRange.startRange(dstLayer, dstFrameFirst, DocRange::kCels);
392           for (layer_t i=1; i<srcRange.layers(); ++i) {
393             dstLayer = dstLayer->getPreviousBrowsable();
394             if (dstLayer == nullptr)
395               break;
396           }
397           dstRange.endRange(dstLayer, dstFrameFirst+srcRange.frames()-1);
398 
399           // We can use a document range op (copy_range) to copy/paste
400           // cels in the same document.
401           if (srcDoc == dstDoc) {
402             // This is the app::copy_range (not clipboard::copy_range()).
403             if (srcRange.layers() == dstRange.layers())
404               app::copy_range(srcDoc, srcRange, dstRange, kDocRangeBefore);
405             editor->invalidate();
406             return;
407           }
408 
409           Transaction transaction(UIContext::instance(), "Paste Cels");
410           DocApi api = dstDoc->getApi(transaction);
411 
412           // Add extra frames if needed
413           while (dstFrameFirst+srcRange.frames() > dstSpr->totalFrames())
414             api.addFrame(dstSpr, dstSpr->totalFrames());
415 
416           auto srcLayers = srcRange.selectedLayers().toLayerList();
417           auto dstLayers = dstRange.selectedLayers().toLayerList();
418 
419           auto srcIt = srcLayers.begin();
420           auto dstIt = dstLayers.begin();
421           auto srcEnd = srcLayers.end();
422           auto dstEnd = dstLayers.end();
423 
424           for (; srcIt != srcEnd && dstIt != dstEnd; ++srcIt, ++dstIt) {
425             auto srcLayer = *srcIt;
426             auto dstLayer = *dstIt;
427 
428             if (!srcLayer->isImage() ||
429                 !dstLayer->isImage())
430               continue;
431 
432             // Maps a linked Cel in the original sprite with its
433             // corresponding copy in the new sprite. In this way
434             // we can.
435             std::map<Cel*, Cel*> relatedCels;
436 
437             frame_t dstFrame = dstFrameFirst;
438             for (frame_t srcFrame : srcRange.selectedFrames()) {
439               Cel* srcCel = srcLayer->cel(srcFrame);
440               Cel* srcLink = nullptr;
441 
442               if (srcCel && srcCel->image()) {
443                 bool createCopy = true;
444 
445                 if (dstLayer->isContinuous() &&
446                     srcCel->links()) {
447                   srcLink = srcCel->link();
448                   if (!srcLink)
449                     srcLink = srcCel;
450 
451                   if (srcLink) {
452                     Cel* dstRelated = relatedCels[srcLink];
453                     if (dstRelated) {
454                       createCopy = false;
455 
456                       // Create a link from dstRelated
457                       api.copyCel(
458                         static_cast<LayerImage*>(dstLayer), dstRelated->frame(),
459                         static_cast<LayerImage*>(dstLayer), dstFrame);
460                     }
461                   }
462                 }
463 
464                 if (createCopy) {
465                   api.copyCel(
466                     static_cast<LayerImage*>(srcLayer), srcFrame,
467                     static_cast<LayerImage*>(dstLayer), dstFrame);
468 
469                   if (srcLink)
470                     relatedCels[srcLink] = dstLayer->cel(dstFrame);
471                 }
472               }
473               else {
474                 Cel* dstCel = dstLayer->cel(dstFrame);
475                 if (dstCel)
476                   api.clearCel(dstCel);
477               }
478 
479               ++dstFrame;
480             }
481           }
482 
483           transaction.commit();
484           editor->invalidate();
485           break;
486         }
487 
488         case DocRange::kFrames: {
489           frame_t dstFrame = editor->frame();
490 
491           // We use a DocRange operation to copy frames inside
492           // the same sprite.
493           if (srcSpr == dstSpr) {
494             DocRange dstRange;
495             dstRange.startRange(nullptr, dstFrame, DocRange::kFrames);
496             dstRange.endRange(nullptr, dstFrame);
497             app::copy_range(srcDoc, srcRange, dstRange, kDocRangeBefore);
498             break;
499           }
500 
501           Transaction transaction(UIContext::instance(), "Paste Frames");
502           DocApi api = dstDoc->getApi(transaction);
503 
504           auto srcLayers = srcSpr->allBrowsableLayers();
505           auto dstLayers = dstSpr->allBrowsableLayers();
506 
507           for (frame_t srcFrame : srcRange.selectedFrames()) {
508             api.addEmptyFrame(dstSpr, dstFrame);
509             api.setFrameDuration(dstSpr, dstFrame, srcSpr->frameDuration(srcFrame));
510 
511             auto srcIt = srcLayers.begin();
512             auto dstIt = dstLayers.begin();
513             auto srcEnd = srcLayers.end();
514             auto dstEnd = dstLayers.end();
515 
516             for (; srcIt != srcEnd && dstIt != dstEnd; ++srcIt, ++dstIt) {
517               auto srcLayer = *srcIt;
518               auto dstLayer = *dstIt;
519 
520               if (!srcLayer->isImage() ||
521                   !dstLayer->isImage())
522                 continue;
523 
524               Cel* cel = static_cast<LayerImage*>(srcLayer)->cel(srcFrame);
525               if (cel && cel->image()) {
526                 api.copyCel(
527                   static_cast<LayerImage*>(srcLayer), srcFrame,
528                   static_cast<LayerImage*>(dstLayer), dstFrame);
529               }
530             }
531 
532             ++dstFrame;
533           }
534 
535           transaction.commit();
536           editor->invalidate();
537           break;
538         }
539 
540         case DocRange::kLayers: {
541           if (srcDoc->colorMode() != dstDoc->colorMode())
542             throw std::runtime_error("You cannot copy layers of document with different color modes");
543 
544           Transaction transaction(UIContext::instance(), "Paste Layers");
545           DocApi api = dstDoc->getApi(transaction);
546 
547           // Remove children if their parent is selected so we only
548           // copy the parent.
549           SelectedLayers srcLayersSet = srcRange.selectedLayers();
550           srcLayersSet.removeChildrenIfParentIsSelected();
551           LayerList srcLayers = srcLayersSet.toLayerList();
552 
553           // Expand frames of dstDoc if it's needed.
554           frame_t maxFrame = 0;
555           for (Layer* srcLayer : srcLayers) {
556             if (!srcLayer->isImage())
557               continue;
558 
559             Cel* lastCel = static_cast<LayerImage*>(srcLayer)->getLastCel();
560             if (lastCel && maxFrame < lastCel->frame())
561               maxFrame = lastCel->frame();
562           }
563           while (dstSpr->totalFrames() < maxFrame+1)
564             api.addEmptyFrame(dstSpr, dstSpr->totalFrames());
565 
566           for (Layer* srcLayer : srcLayers) {
567             Layer* afterThis;
568             if (srcLayer->isBackground() && !dstDoc->sprite()->backgroundLayer())
569               afterThis = nullptr;
570             else
571               afterThis = dstSpr->root()->lastLayer();
572 
573             Layer* newLayer = nullptr;
574             if (srcLayer->isImage())
575               newLayer = new LayerImage(dstSpr);
576             else if (srcLayer->isGroup())
577               newLayer = new LayerGroup(dstSpr);
578             else
579               continue;
580 
581             api.addLayer(dstSpr->root(), newLayer, afterThis);
582 
583             srcDoc->copyLayerContent(srcLayer, dstDoc, newLayer);
584           }
585 
586           transaction.commit();
587           editor->invalidate();
588           break;
589         }
590       }
591       break;
592     }
593 
594   }
595 }
596 
get_image_size(gfx::Size & size)597 bool get_image_size(gfx::Size& size)
598 {
599   if (use_native_clipboard() &&
600       get_native_clipboard_bitmap_size(&size))
601     return true;
602 
603   if (clipboard_image) {
604     size.w = clipboard_image->width();
605     size.h = clipboard_image->height();
606     return true;
607   }
608 
609   return false;
610 }
611 
get_palette()612 Palette* get_palette()
613 {
614   if (clipboard::get_current_format() == ClipboardPaletteEntries) {
615     ASSERT(clipboard_palette);
616     return clipboard_palette.get();
617   }
618   else
619     return nullptr;
620 }
621 
get_palette_picks()622 const PalettePicks& get_palette_picks()
623 {
624   return clipboard_picks;
625 }
626 
627 }  // namespace clipboard
628 
629 } // namespace app
630