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