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 | |
22 | namespace app { |
23 | |
24 | using namespace doc; |
25 | |
26 | Image* 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 | |
32 | doc::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 | |
119 | doc::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 | |