1 // Aseprite Document Library
2 // Copyright (c) 2001-2016 David Capello
3 //
4 // This file is released under the terms of the MIT license.
5 // Read LICENSE.txt for more information.
6
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif
10
11 #include "doc/brush.h"
12
13 #include "base/pi.h"
14 #include "doc/algo.h"
15 #include "doc/algorithm/polygon.h"
16 #include "doc/blend_internals.h"
17 #include "doc/image.h"
18 #include "doc/image_impl.h"
19 #include "doc/primitives.h"
20
21 #include <cmath>
22
23 namespace doc {
24
25 static int generation = 0;
26
Brush()27 Brush::Brush()
28 {
29 m_type = kCircleBrushType;
30 m_size = 1;
31 m_angle = 0;
32 m_pattern = BrushPattern::DEFAULT;
33 m_gen = 0;
34
35 regenerate();
36 }
37
Brush(BrushType type,int size,int angle)38 Brush::Brush(BrushType type, int size, int angle)
39 {
40 m_type = type;
41 m_size = size;
42 m_angle = angle;
43 m_pattern = BrushPattern::DEFAULT;
44 m_gen = 0;
45
46 regenerate();
47 }
48
Brush(const Brush & brush)49 Brush::Brush(const Brush& brush)
50 {
51 m_type = brush.m_type;
52 m_size = brush.m_size;
53 m_angle = brush.m_angle;
54 m_image = brush.m_image;
55 m_maskBitmap = brush.m_maskBitmap;
56 m_pattern = brush.m_pattern;
57 m_patternOrigin = brush.m_patternOrigin;
58 m_gen = 0;
59
60 regenerate();
61 }
62
~Brush()63 Brush::~Brush()
64 {
65 clean();
66 }
67
setType(BrushType type)68 void Brush::setType(BrushType type)
69 {
70 m_type = type;
71 if (m_type != kImageBrushType)
72 regenerate();
73 else
74 clean();
75 }
76
setSize(int size)77 void Brush::setSize(int size)
78 {
79 m_size = size;
80 regenerate();
81 }
82
setAngle(int angle)83 void Brush::setAngle(int angle)
84 {
85 m_angle = angle;
86 regenerate();
87 }
88
setImage(const Image * image,const Image * maskBitmap)89 void Brush::setImage(const Image* image,
90 const Image* maskBitmap)
91 {
92 m_type = kImageBrushType;
93 m_image.reset(Image::createCopy(image));
94 if (maskBitmap)
95 m_maskBitmap.reset(Image::createCopy(maskBitmap));
96 else {
97 int w = image->width();
98 int h = image->height();
99 m_maskBitmap.reset(Image::create(IMAGE_BITMAP, w, h));
100 LockImageBits<BitmapTraits> bits(m_maskBitmap.get());
101 auto pos = bits.begin();
102 for (int v=0; v<h; ++v)
103 for (int u=0; u<w; ++u, ++pos)
104 *pos = (get_pixel(image, u, v) != image->maskColor());
105 }
106
107 m_backupImage.reset();
108 m_mainColor.reset();
109 m_bgColor.reset();
110
111 resetBounds();
112 }
113
114 template<class ImageTraits,
115 color_t color_mask,
116 color_t alpha_mask,
117 color_t alpha_shift>
replace_image_colors(Image * image,Image * maskBitmap,const bool useMain,color_t mainColor,const bool useBg,color_t bgColor)118 static void replace_image_colors(
119 Image* image,
120 Image* maskBitmap,
121 const bool useMain, color_t mainColor,
122 const bool useBg, color_t bgColor)
123 {
124 LockImageBits<ImageTraits> bits(image, Image::ReadWriteLock);
125 const LockImageBits<BitmapTraits> maskBits(maskBitmap);
126 bool hasAlpha = false; // True if "image" has a pixel with alpha < 255
127 color_t srcMainColor, srcBgColor;
128 srcMainColor = srcBgColor = 0;
129
130 auto mask_it = maskBits.begin();
131 for (const auto& pixel : bits) {
132 if (!*mask_it)
133 continue;
134
135 if ((pixel & alpha_mask) != alpha_mask) { // If alpha != 255
136 hasAlpha = true;
137 }
138 else if (srcBgColor == 0) {
139 srcMainColor = srcBgColor = pixel;
140 }
141 else if (pixel != srcBgColor && srcMainColor == srcBgColor) {
142 srcMainColor = pixel;
143 }
144
145 ++mask_it;
146 }
147
148 int t;
149 if (hasAlpha) {
150 if (useMain || useBg) {
151 const color_t color = (useMain ? mainColor: useBg);
152 for (auto& pixel : bits) {
153 color_t a1 = (pixel & alpha_mask) >> alpha_shift;
154 const color_t a2 = (color & alpha_mask) >> alpha_shift;
155 a1 = MUL_UN8(a1, a2, t);
156 pixel =
157 (a1 << alpha_shift) |
158 (color & color_mask);
159 }
160 }
161 }
162 else {
163 for (auto& pixel : bits) {
164 color_t color;
165 if (useMain && ((pixel != srcBgColor) || (srcMainColor == srcBgColor)))
166 color = mainColor;
167 else if (useBg && (pixel == srcBgColor))
168 color = bgColor;
169 else
170 continue;
171
172 color_t a1 = (pixel & alpha_mask) >> alpha_shift;
173 color_t a2 = (color & alpha_mask) >> alpha_shift;
174 a1 = MUL_UN8(a1, a2, t);
175 pixel =
176 (a1 << alpha_shift) |
177 (color & color_mask);
178 }
179 }
180 }
181
replace_image_colors_indexed(Image * image,Image * maskBitmap,const bool useMain,const color_t mainColor,const bool useBg,const color_t bgColor)182 static void replace_image_colors_indexed(
183 Image* image,
184 Image* maskBitmap,
185 const bool useMain, const color_t mainColor,
186 const bool useBg, const color_t bgColor)
187 {
188 LockImageBits<IndexedTraits> bits(image, Image::ReadWriteLock);
189 const LockImageBits<BitmapTraits> maskBits(maskBitmap);
190 bool hasAlpha = false; // True if "image" has a pixel with the mask color
191 color_t maskColor = image->maskColor();
192 color_t srcMainColor, srcBgColor;
193 srcMainColor = srcBgColor = maskColor;
194
195 auto mask_it = maskBits.begin();
196 for (const auto& pixel : bits) {
197 if (!*mask_it)
198 continue;
199
200 if (pixel == maskColor) {
201 hasAlpha = true;
202 }
203 else if (srcBgColor == maskColor) {
204 srcMainColor = srcBgColor = pixel;
205 }
206 else if (pixel != srcBgColor && srcMainColor == srcBgColor) {
207 srcMainColor = pixel;
208 }
209
210 ++mask_it;
211 }
212
213 if (hasAlpha) {
214 for (auto& pixel : bits) {
215 if (pixel != maskColor) {
216 if (useMain)
217 pixel = mainColor;
218 else if (useBg)
219 pixel = bgColor;
220 }
221 }
222 }
223 else {
224 for (auto& pixel : bits) {
225 if (useMain && ((pixel != srcBgColor) || (srcMainColor == srcBgColor))) {
226 pixel = mainColor;
227 }
228 else if (useBg && (pixel == srcBgColor)) {
229 pixel = bgColor;
230 }
231 }
232 }
233 }
234
setImageColor(ImageColor imageColor,color_t color)235 void Brush::setImageColor(ImageColor imageColor, color_t color)
236 {
237 ASSERT(m_image);
238 if (!m_image)
239 return;
240
241 if (!m_backupImage)
242 m_backupImage.reset(Image::createCopy(m_image.get()));
243 else
244 m_image.reset(Image::createCopy(m_backupImage.get()));
245
246 ASSERT(m_maskBitmap);
247
248 switch (imageColor) {
249 case ImageColor::MainColor:
250 m_mainColor.reset(new color_t(color));
251 break;
252 case ImageColor::BackgroundColor:
253 m_bgColor.reset(new color_t(color));
254 break;
255 }
256
257 switch (m_image->pixelFormat()) {
258
259 case IMAGE_RGB:
260 replace_image_colors<RgbTraits, rgba_rgb_mask, rgba_a_mask, rgba_a_shift>(
261 m_image.get(), m_maskBitmap.get(),
262 (m_mainColor ? true: false), (m_mainColor ? *m_mainColor: 0),
263 (m_bgColor ? true: false), (m_bgColor ? *m_bgColor: 0));
264 break;
265
266 case IMAGE_GRAYSCALE:
267 replace_image_colors<GrayscaleTraits, graya_v_mask, graya_a_mask, graya_a_shift>(
268 m_image.get(), m_maskBitmap.get(),
269 (m_mainColor ? true: false), (m_mainColor ? *m_mainColor: 0),
270 (m_bgColor ? true: false), (m_bgColor ? *m_bgColor: 0));
271 break;
272
273 case IMAGE_INDEXED:
274 replace_image_colors_indexed(
275 m_image.get(), m_maskBitmap.get(),
276 (m_mainColor ? true: false), (m_mainColor ? *m_mainColor: 0),
277 (m_bgColor ? true: false), (m_bgColor ? *m_bgColor: 0));
278 break;
279 }
280 }
281
282 // Cleans the brush's data (image and region).
clean()283 void Brush::clean()
284 {
285 m_gen = ++generation;
286 m_image.reset();
287 m_maskBitmap.reset();
288 m_backupImage.reset();
289 }
290
algo_hline(int x1,int y,int x2,void * data)291 static void algo_hline(int x1, int y, int x2, void *data)
292 {
293 draw_hline(reinterpret_cast<Image*>(data), x1, y, x2, BitmapTraits::max_value);
294 }
295
296 // Regenerates the brush bitmap and its rectangle's region.
regenerate()297 void Brush::regenerate()
298 {
299 clean();
300
301 ASSERT(m_size > 0);
302
303 int size = m_size;
304 if (m_type == kSquareBrushType && m_angle != 0 && m_size > 2)
305 size = (int)std::sqrt((double)2*m_size*m_size)+2;
306
307 m_image.reset(Image::create(IMAGE_BITMAP, size, size));
308 m_maskBitmap.reset();
309
310 if (size == 1) {
311 clear_image(m_image.get(), BitmapTraits::max_value);
312 }
313 else {
314 clear_image(m_image.get(), BitmapTraits::min_value);
315
316 switch (m_type) {
317
318 case kCircleBrushType:
319 fill_ellipse(m_image.get(), 0, 0, size-1, size-1, BitmapTraits::max_value);
320 break;
321
322 case kSquareBrushType:
323 if (m_angle == 0 || size <= 2) {
324 clear_image(m_image.get(), BitmapTraits::max_value);
325 }
326 else {
327 double a = PI * m_angle / 180;
328 int c = size/2;
329 int r = m_size/2;
330 int d = m_size;
331 int x1 = int(c + r*cos(a-PI/2) + r*cos(a-PI));
332 int y1 = int(c - r*sin(a-PI/2) - r*sin(a-PI));
333 int x2 = int(x1 + d*cos(a));
334 int y2 = int(y1 - d*sin(a));
335 int x3 = int(x2 + d*cos(a+PI/2));
336 int y3 = int(y2 - d*sin(a+PI/2));
337 int x4 = int(x3 + d*cos(a+PI));
338 int y4 = int(y3 - d*sin(a+PI));
339 int points[8] = { x1, y1, x2, y2, x3, y3, x4, y4 };
340
341 doc::algorithm::polygon(4, points, m_image.get(), algo_hline);
342 }
343 break;
344
345 case kLineBrushType: {
346 double a = PI * m_angle / 180;
347 double r = m_size/2;
348 double d = m_size;
349 int x1 = int(r + r*cos(a+PI));
350 int y1 = int(r - r*sin(a+PI));
351 int x2 = int(x1 + d*cos(a));
352 int y2 = int(y1 - d*sin(a));
353
354 draw_line(m_image.get(), x1, y1, x2, y2, BitmapTraits::max_value);
355 break;
356 }
357 }
358 }
359
360 resetBounds();
361 }
362
resetBounds()363 void Brush::resetBounds()
364 {
365 m_center = gfx::Point(m_image->width()/2,
366 m_image->height()/2);
367 m_bounds = gfx::Rect(-m_center,
368 gfx::Size(m_image->width(),
369 m_image->height()));
370 }
371
372 } // namespace doc
373