1// Aseprite
2// Copyright (C) 2019-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/util/new_image_from_mask.h"
13
14#include "app/doc.h"
15#include "app/site.h"
16#include "doc/image_impl.h"
17#include "doc/layer.h"
18#include "doc/mask.h"
19#include "doc/primitives.h"
20#include "render/render.h"
21
22namespace app {
23
24using namespace doc;
25
26Image* new_image_from_mask(const Site& site, const bool newBlend)
27{
28 const Mask* srcMask = site.document()->mask();
29 return new_image_from_mask(site, srcMask, newBlend);
30}
31
32doc::Image* new_image_from_mask(const Site& site,
33 const doc::Mask* srcMask,
34 const bool newBlend,
35 bool merged)
36{
37 const Sprite* srcSprite = site.sprite();
38 ASSERT(srcSprite);
39 ASSERT(srcMask);
40
41 const Image* srcMaskBitmap = srcMask->bitmap();
42 const gfx::Rect& srcBounds = srcMask->bounds();
43
44 ASSERT(srcMaskBitmap);
45 ASSERT(!srcBounds.isEmpty());
46
47 std::unique_ptr<Image> dst(Image::create(srcSprite->pixelFormat(), srcBounds.w, srcBounds.h));
48 if (!dst)
49 return nullptr;
50
51 // Clear the new image
52 dst->setMaskColor(srcSprite->transparentColor());
53 clear_image(dst.get(), dst->maskColor());
54
55 const Image* src = nullptr;
56 int x = 0, y = 0;
57 if (merged || site.layer()->isTilemap()) {
58 render::Render render;
59 render.setNewBlend(newBlend);
60 if (merged)
61 render.renderSprite(dst.get(), srcSprite, site.frame(),
62 gfx::Clip(0, 0, srcBounds));
63 else {
64 ASSERT(site.layer()->isTilemap());
65 if (auto cel = site.cel()) {
66 render.renderCel(
67 dst.get(), cel, srcSprite,
68 cel->image(), cel->layer(),
69 srcSprite->palette(cel->frame()),
70 cel->bounds(),
71 gfx::Clip(0, 0, srcBounds),
72 255, BlendMode::NORMAL);
73 }
74 }
75
76 src = dst.get();
77 }
78 else {
79 src = site.image(&x, &y);
80 }
81
82 // Copy the masked zones
83 if (src) {
84 if (srcMaskBitmap) {
85 // Copy active layer with mask
86 const LockImageBits<BitmapTraits> maskBits(srcMaskBitmap, gfx::Rect(0, 0, srcBounds.w, srcBounds.h));
87 LockImageBits<BitmapTraits>::const_iterator mask_it = maskBits.begin();
88
89 for (int v=0; v<srcBounds.h; ++v) {
90 for (int u=0; u<srcBounds.w; ++u, ++mask_it) {
91 ASSERT(mask_it != maskBits.end());
92
93 if (src != dst.get()) {
94 if (*mask_it) {
95 int getx = u+srcBounds.x-x;
96 int gety = v+srcBounds.y-y;
97
98 if ((getx >= 0) && (getx < src->width()) &&
99 (gety >= 0) && (gety < src->height()))
100 dst->putPixel(u, v, src->getPixel(getx, gety));
101 }
102 }
103 else {
104 if (!*mask_it) {
105 dst->putPixel(u, v, dst->maskColor());
106 }
107 }
108 }
109 }
110 }
111 else if (src != dst.get()) {
112 copy_image(dst.get(), src, -srcBounds.x, -srcBounds.y);
113 }
114 }
115
116 return dst.release();
117}
118
119doc::Image* new_tilemap_from_mask(const Site& site,
120 const doc::Mask* srcMask)
121{
122 const Sprite* srcSprite = site.sprite();
123 ASSERT(srcSprite);
124 ASSERT(srcMask);
125
126 const Cel* srcCel = site.cel();
127 if (!srcCel)
128 return nullptr;
129
130 const doc::Grid grid = site.grid();
131 const Image* srcMaskBitmap = srcMask->bitmap();
132 const gfx::Rect& srcBounds = srcMask->bounds();
133 const gfx::Rect srcTilesBounds = grid.canvasToTile(srcBounds);
134
135 ASSERT(srcMaskBitmap);
136 ASSERT(!srcTilesBounds.isEmpty());
137 ASSERT(site.layer()->isTilemap());
138
139 std::unique_ptr<Image> dst(Image::create(IMAGE_TILEMAP,
140 srcTilesBounds.w,
141 srcTilesBounds.h));
142 if (!dst)
143 return nullptr;
144
145 // Clear the new tilemap
146 clear_image(dst.get(), dst->maskColor());
147
148 // Copy the masked zones
149 if (srcMaskBitmap) {
150 // Copy active layer with mask
151 const LockImageBits<BitmapTraits> maskBits(srcMaskBitmap, gfx::Rect(0, 0, srcBounds.w, srcBounds.h));
152 auto mask_it = maskBits.begin();
153 const gfx::Point originPt = grid.canvasToTile(srcBounds.origin());
154
155 for (int v=0; v<srcBounds.h; ++v) {
156 for (int u=0; u<srcBounds.w; ++u, ++mask_it) {
157 ASSERT(mask_it != maskBits.end());
158 if (*mask_it) {
159 gfx::Point srcPt = grid.canvasToTile(gfx::Point(srcBounds.x+u, srcBounds.y+v));
160 gfx::Point dstPt = srcPt - originPt;
161
162 if (dst->bounds().contains(dstPt) &&
163 srcCel->image()->bounds().contains(srcPt)) {
164 dst->putPixel(dstPt.x, dstPt.y, srcCel->image()->getPixel(srcPt.x, srcPt.y));
165 }
166 }
167 }
168 }
169 }
170 else {
171 // Just copy tilemap data
172 dst->copy(srcCel->image(), gfx::Clip(0, 0, srcTilesBounds));
173 }
174
175 return dst.release();
176}
177
178} // namespace app
179