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/primitives.h"
12
13 #include "doc/algo.h"
14 #include "doc/brush.h"
15 #include "doc/image_impl.h"
16 #include "doc/palette.h"
17 #include "doc/remap.h"
18 #include "doc/rgbmap.h"
19
20 #include <stdexcept>
21
22 namespace doc {
23
get_pixel(const Image * image,int x,int y)24 color_t get_pixel(const Image* image, int x, int y)
25 {
26 ASSERT(image);
27
28 if ((x >= 0) && (y >= 0) && (x < image->width()) && (y < image->height()))
29 return image->getPixel(x, y);
30 else
31 return -1;
32 }
33
put_pixel(Image * image,int x,int y,color_t color)34 void put_pixel(Image* image, int x, int y, color_t color)
35 {
36 ASSERT(image);
37
38 if ((x >= 0) && (y >= 0) && (x < image->width()) && (y < image->height()))
39 image->putPixel(x, y, color);
40 }
41
clear_image(Image * image,color_t color)42 void clear_image(Image* image, color_t color)
43 {
44 ASSERT(image);
45
46 image->clear(color);
47 }
48
copy_image(Image * dst,const Image * src)49 void copy_image(Image* dst, const Image* src)
50 {
51 ASSERT(dst);
52 ASSERT(src);
53
54 dst->copy(src, gfx::Clip(0, 0, 0, 0, src->width(), src->height()));
55 }
56
copy_image(Image * dst,const Image * src,int x,int y)57 void copy_image(Image* dst, const Image* src, int x, int y)
58 {
59 ASSERT(dst);
60 ASSERT(src);
61
62 dst->copy(src, gfx::Clip(x, y, 0, 0, src->width(), src->height()));
63 }
64
crop_image(const Image * image,int x,int y,int w,int h,color_t bg,const ImageBufferPtr & buffer)65 Image* crop_image(const Image* image, int x, int y, int w, int h, color_t bg, const ImageBufferPtr& buffer)
66 {
67 ASSERT(image);
68
69 if (w < 1) throw std::invalid_argument("crop_image: Width is less than 1");
70 if (h < 1) throw std::invalid_argument("crop_image: Height is less than 1");
71
72 Image* trim = Image::create(image->pixelFormat(), w, h, buffer);
73 trim->setMaskColor(image->maskColor());
74
75 clear_image(trim, bg);
76 trim->copy(image, gfx::Clip(0, 0, x, y, w, h));
77
78 return trim;
79 }
80
crop_image(const Image * image,const gfx::Rect & bounds,color_t bg,const ImageBufferPtr & buffer)81 Image* crop_image(const Image* image, const gfx::Rect& bounds, color_t bg, const ImageBufferPtr& buffer)
82 {
83 return crop_image(image, bounds.x, bounds.y, bounds.w, bounds.h, bg, buffer);
84 }
85
rotate_image(const Image * src,Image * dst,int angle)86 void rotate_image(const Image* src, Image* dst, int angle)
87 {
88 ASSERT(src);
89 ASSERT(dst);
90 int x, y;
91
92 switch (angle) {
93
94 case 180:
95 ASSERT(dst->width() == src->width());
96 ASSERT(dst->height() == src->height());
97
98 for (y=0; y<src->height(); ++y)
99 for (x=0; x<src->width(); ++x)
100 dst->putPixel(src->width() - x - 1,
101 src->height() - y - 1, src->getPixel(x, y));
102 break;
103
104 case 90:
105 ASSERT(dst->width() == src->height());
106 ASSERT(dst->height() == src->width());
107
108 for (y=0; y<src->height(); ++y)
109 for (x=0; x<src->width(); ++x)
110 dst->putPixel(src->height() - y - 1, x, src->getPixel(x, y));
111 break;
112
113 case -90:
114 ASSERT(dst->width() == src->height());
115 ASSERT(dst->height() == src->width());
116
117 for (y=0; y<src->height(); ++y)
118 for (x=0; x<src->width(); ++x)
119 dst->putPixel(y, src->width() - x - 1, src->getPixel(x, y));
120 break;
121
122 // bad angle
123 default:
124 throw std::invalid_argument("Invalid angle specified to rotate the image");
125 }
126 }
127
draw_hline(Image * image,int x1,int y,int x2,color_t color)128 void draw_hline(Image* image, int x1, int y, int x2, color_t color)
129 {
130 ASSERT(image);
131 int t;
132
133 if (x1 > x2) {
134 t = x1;
135 x1 = x2;
136 x2 = t;
137 }
138
139 if ((x2 < 0) || (x1 >= image->width()) || (y < 0) || (y >= image->height()))
140 return;
141
142 if (x1 < 0) x1 = 0;
143 if (x2 >= image->width()) x2 = image->width()-1;
144
145 image->drawHLine(x1, y, x2, color);
146 }
147
draw_vline(Image * image,int x,int y1,int y2,color_t color)148 void draw_vline(Image* image, int x, int y1, int y2, color_t color)
149 {
150 ASSERT(image);
151 int t;
152
153 if (y1 > y2) {
154 t = y1;
155 y1 = y2;
156 y2 = t;
157 }
158
159 if ((y2 < 0) || (y1 >= image->height()) || (x < 0) || (x >= image->width()))
160 return;
161
162 if (y1 < 0) y1 = 0;
163 if (y2 >= image->height()) y2 = image->height()-1;
164
165 for (t=y1; t<=y2; t++)
166 image->putPixel(x, t, color);
167 }
168
draw_rect(Image * image,int x1,int y1,int x2,int y2,color_t color)169 void draw_rect(Image* image, int x1, int y1, int x2, int y2, color_t color)
170 {
171 ASSERT(image);
172 int t;
173
174 if (x1 > x2) {
175 t = x1;
176 x1 = x2;
177 x2 = t;
178 }
179
180 if (y1 > y2) {
181 t = y1;
182 y1 = y2;
183 y2 = t;
184 }
185
186 if ((x2 < 0) || (x1 >= image->width()) || (y2 < 0) || (y1 >= image->height()))
187 return;
188
189 draw_hline(image, x1, y1, x2, color);
190 draw_hline(image, x1, y2, x2, color);
191 if (y2-y1 > 1) {
192 draw_vline(image, x1, y1+1, y2-1, color);
193 draw_vline(image, x2, y1+1, y2-1, color);
194 }
195 }
196
fill_rect(Image * image,int x1,int y1,int x2,int y2,color_t color)197 void fill_rect(Image* image, int x1, int y1, int x2, int y2, color_t color)
198 {
199 ASSERT(image);
200 int t;
201
202 if (x1 > x2) {
203 t = x1;
204 x1 = x2;
205 x2 = t;
206 }
207
208 if (y1 > y2) {
209 t = y1;
210 y1 = y2;
211 y2 = t;
212 }
213
214 if ((x2 < 0) || (x1 >= image->width()) || (y2 < 0) || (y1 >= image->height()))
215 return;
216
217 if (x1 < 0) x1 = 0;
218 if (y1 < 0) y1 = 0;
219 if (x2 >= image->width()) x2 = image->width()-1;
220 if (y2 >= image->height()) y2 = image->height()-1;
221
222 image->fillRect(x1, y1, x2, y2, color);
223 }
224
fill_rect(Image * image,const gfx::Rect & rc,color_t c)225 void fill_rect(Image* image, const gfx::Rect& rc, color_t c)
226 {
227 ASSERT(image);
228
229 gfx::Rect clip = rc.createIntersection(image->bounds());
230 if (!clip.isEmpty())
231 image->fillRect(clip.x, clip.y,
232 clip.x+clip.w-1, clip.y+clip.h-1, c);
233 }
234
blend_rect(Image * image,int x1,int y1,int x2,int y2,color_t color,int opacity)235 void blend_rect(Image* image, int x1, int y1, int x2, int y2, color_t color, int opacity)
236 {
237 ASSERT(image);
238 int t;
239
240 if (x1 > x2) {
241 t = x1;
242 x1 = x2;
243 x2 = t;
244 }
245
246 if (y1 > y2) {
247 t = y1;
248 y1 = y2;
249 y2 = t;
250 }
251
252 if ((x2 < 0) || (x1 >= image->width()) || (y2 < 0) || (y1 >= image->height()))
253 return;
254
255 if (x1 < 0) x1 = 0;
256 if (y1 < 0) y1 = 0;
257 if (x2 >= image->width()) x2 = image->width()-1;
258 if (y2 >= image->height()) y2 = image->height()-1;
259
260 image->blendRect(x1, y1, x2, y2, color, opacity);
261 }
262
263 struct Data {
264 Image* image;
265 color_t color;
266 };
267
pixel_for_image(int x,int y,Data * data)268 static void pixel_for_image(int x, int y, Data* data)
269 {
270 put_pixel(data->image, x, y, data->color);
271 }
272
hline_for_image(int x1,int y,int x2,Data * data)273 static void hline_for_image(int x1, int y, int x2, Data* data)
274 {
275 draw_hline(data->image, x1, y, x2, data->color);
276 }
277
draw_line(Image * image,int x1,int y1,int x2,int y2,color_t color)278 void draw_line(Image* image, int x1, int y1, int x2, int y2, color_t color)
279 {
280 Data data = { image, color };
281 algo_line(x1, y1, x2, y2, &data, (AlgoPixel)pixel_for_image);
282 }
283
draw_ellipse(Image * image,int x1,int y1,int x2,int y2,color_t color)284 void draw_ellipse(Image* image, int x1, int y1, int x2, int y2, color_t color)
285 {
286 Data data = { image, color };
287 algo_ellipse(x1, y1, x2, y2, &data, (AlgoPixel)pixel_for_image);
288 }
289
fill_ellipse(Image * image,int x1,int y1,int x2,int y2,color_t color)290 void fill_ellipse(Image* image, int x1, int y1, int x2, int y2, color_t color)
291 {
292 Data data = { image, color };
293 algo_ellipsefill(x1, y1, x2, y2, &data, (AlgoHLine)hline_for_image);
294 }
295
296 namespace {
297
298 template<typename ImageTraits>
count_diff_between_images_templ(const Image * i1,const Image * i2)299 int count_diff_between_images_templ(const Image* i1, const Image* i2)
300 {
301 int diff = 0;
302 const LockImageBits<ImageTraits> bits1(i1);
303 const LockImageBits<ImageTraits> bits2(i2);
304 typename LockImageBits<ImageTraits>::const_iterator it1, it2, end1, end2;
305 for (it1 = bits1.begin(), end1 = bits1.end(),
306 it2 = bits2.begin(), end2 = bits2.end();
307 it1 != end1 && it2 != end2; ++it1, ++it2) {
308 if (*it1 != *it2)
309 diff++;
310 }
311
312 ASSERT(it1 == end1);
313 ASSERT(it2 == end2);
314 return diff;
315 }
316
317 } // anonymous namespace
318
count_diff_between_images(const Image * i1,const Image * i2)319 int count_diff_between_images(const Image* i1, const Image* i2)
320 {
321 if ((i1->pixelFormat() != i2->pixelFormat()) ||
322 (i1->width() != i2->width()) ||
323 (i1->height() != i2->height()))
324 return -1;
325
326 switch (i1->pixelFormat()) {
327 case IMAGE_RGB: return count_diff_between_images_templ<RgbTraits>(i1, i2);
328 case IMAGE_GRAYSCALE: return count_diff_between_images_templ<GrayscaleTraits>(i1, i2);
329 case IMAGE_INDEXED: return count_diff_between_images_templ<IndexedTraits>(i1, i2);
330 case IMAGE_BITMAP: return count_diff_between_images_templ<BitmapTraits>(i1, i2);
331 }
332
333 ASSERT(false);
334 return -1;
335 }
336
remap_image(Image * image,const Remap & remap)337 void remap_image(Image* image, const Remap& remap)
338 {
339 ASSERT(image->pixelFormat() == IMAGE_INDEXED);
340 if (image->pixelFormat() != IMAGE_INDEXED)
341 return;
342
343 LockImageBits<IndexedTraits> bits(image);
344 LockImageBits<IndexedTraits>::iterator
345 it = bits.begin(),
346 end = bits.end();
347
348 for (; it != end; ++it)
349 *it = remap[*it];
350 }
351
352 } // namespace doc
353