1 // Most code come from original Allegro rotation code:
2 // By Shawn Hargreaves.
3 // Flipping routines by Andrew Geers.
4 // Optimized by Sven Sandberg.
5 // To C++ templates by David Capello
6 //
7 // This file is released under the terms of the MIT license.
8 // Read LICENSE.txt for more information.
9 
10 #ifdef HAVE_CONFIG_H
11 #include "config.h"
12 #endif
13 
14 #include "base/pi.h"
15 #include "doc/blend_funcs.h"
16 #include "doc/image_impl.h"
17 #include "doc/mask.h"
18 #include "doc/primitives.h"
19 #include "doc/primitives_fast.h"
20 #include "fixmath/fixmath.h"
21 
22 #include <cmath>
23 
24 namespace doc {
25 namespace algorithm {
26 
27 using namespace fixmath;
28 
29 static void ase_parallelogram_map_standard(
30   Image* bmp, const Image* sprite, const Image* mask,
31   fixed xs[4], fixed ys[4]);
32 
33 static void ase_rotate_scale_flip_coordinates(
34   fixed w, fixed h,
35   fixed x, fixed y,
36   fixed cx, fixed cy,
37   fixed angle,
38   fixed scale_x, fixed scale_y,
39   int h_flip, int v_flip,
40   fixed xs[4], fixed ys[4]);
41 
42 template<typename ImageTraits, typename BlendFunc>
image_scale_tpl(Image * dst,const Image * src,int dst_x,int dst_y,int dst_w,int dst_h,int src_x,int src_y,int src_w,int src_h,BlendFunc blend)43 static void image_scale_tpl(
44   Image* dst, const Image* src,
45   int dst_x, int dst_y, int dst_w, int dst_h,
46   int src_x, int src_y, int src_w, int src_h, BlendFunc blend)
47 {
48   LockImageBits<ImageTraits> dst_bits(dst, gfx::Rect(dst_x, dst_y, dst_w, dst_h));
49   typename LockImageBits<ImageTraits>::iterator dst_it = dst_bits.begin();
50   fixed x, first_x = itofix(src_x);
51   fixed y = itofix(src_y);
52   fixed dx = fixdiv(itofix(src_w-1), itofix(dst_w-1));
53   fixed dy = fixdiv(itofix(src_h-1), itofix(dst_h-1));
54   int old_x, new_x;
55 
56   for (int v=0; v<dst_h; ++v) {
57     old_x = fixtoi(x = first_x);
58 
59     const LockImageBits<ImageTraits> src_bits(src, gfx::Rect(src_x, fixtoi(y), src_w, 1));
60     auto src_it = src_bits.begin();
61 
62     for (int u=0; u<dst_w; ++u) {
63       ASSERT(dst_it != dst_bits.end());
64 
65       *dst_it = blend(*dst_it, *src_it);
66       ++dst_it;
67 
68       x = fixadd(x, dx);
69       new_x = fixtoi(x);
70       if (old_x != new_x) {
71         // We don't want to move the "src_it" iterator outside the src
72         // image bounds.
73         if (new_x < src_w) {
74           src_it += (new_x - old_x);
75           old_x = new_x;
76         }
77         else
78           break;
79       }
80     }
81 
82     y = fixadd(y, dy);
83   }
84 }
85 
rgba_blender(color_t back,color_t front)86 static color_t rgba_blender(color_t back, color_t front) {
87   return rgba_blender_normal(back, front);
88 }
89 
grayscale_blender(color_t back,color_t front)90 static color_t grayscale_blender(color_t back, color_t front) {
91   return graya_blender_normal(back, front);
92 }
93 
94 class if_blender {
95 public:
if_blender(color_t mask)96   if_blender(color_t mask) : m_mask(mask) {
97   }
operator ()(color_t back,color_t front)98   color_t operator()(color_t back, color_t front) {
99     if (front != m_mask)
100       return front;
101     else
102       return back;
103   }
104 private:
105   color_t m_mask;
106 };
107 
scale_image(Image * dst,const Image * src,int dst_x,int dst_y,int dst_w,int dst_h,int src_x,int src_y,int src_w,int src_h)108 void scale_image(Image* dst, const Image* src,
109                  int dst_x, int dst_y, int dst_w, int dst_h,
110                  int src_x, int src_y, int src_w, int src_h)
111 {
112   gfx::Clip clip(dst_x, dst_y, src_x, src_y, dst_w, dst_h);
113   if (src_w == dst_w && src_h == dst_h) {
114     dst->copy(src, clip);
115     return;
116   }
117 
118   if (!clip.clip(dst->width(), dst->height(), src->width(), src->height()))
119     return;
120 
121   switch (dst->pixelFormat()) {
122 
123     case IMAGE_RGB:
124       image_scale_tpl<RgbTraits>(
125         dst, src,
126         dst_x, dst_y, dst_w, dst_h,
127         src_x, src_y, src_w, src_h, rgba_blender);
128       break;
129 
130     case IMAGE_GRAYSCALE:
131       image_scale_tpl<GrayscaleTraits>(
132         dst, src,
133         dst_x, dst_y, dst_w, dst_h,
134         src_x, src_y, src_w, src_h, grayscale_blender);
135       break;
136 
137     case IMAGE_INDEXED:
138       image_scale_tpl<IndexedTraits>(
139         dst, src,
140         dst_x, dst_y, dst_w, dst_h,
141         src_x, src_y, src_w, src_h, if_blender(src->maskColor()));
142       break;
143 
144     case IMAGE_BITMAP:
145       image_scale_tpl<BitmapTraits>(
146         dst, src,
147         dst_x, dst_y, dst_w, dst_h,
148         src_x, src_y, src_w, src_h, if_blender(0));
149       break;
150   }
151 }
152 
rotate_image(Image * dst,const Image * src,int x,int y,int w,int h,int cx,int cy,double angle)153 void rotate_image(Image* dst, const Image* src, int x, int y, int w, int h,
154   int cx, int cy, double angle)
155 {
156   fixed xs[4], ys[4];
157 
158   ase_rotate_scale_flip_coordinates(itofix(src->width()), itofix (src->height()),
159                                     itofix(x), itofix(y),
160                                     itofix(cx), itofix(cy),
161                                     ftofix(256 * angle / PI),
162                                     fixdiv(itofix(w), itofix(src->width())),
163                                     fixdiv(itofix(h), itofix(src->height())),
164                                     false, false, xs, ys);
165 
166   ase_parallelogram_map_standard(dst, src, nullptr, xs, ys);
167 }
168 
169 /*    1-----2
170       |     |
171       4-----3
172  */
parallelogram(Image * bmp,const Image * sprite,const Image * mask,int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4)173 void parallelogram(Image* bmp, const Image* sprite, const Image* mask,
174   int x1, int y1, int x2, int y2,
175   int x3, int y3, int x4, int y4)
176 {
177   fixed xs[4], ys[4];
178 
179   xs[0] = itofix(x1);
180   ys[0] = itofix(y1);
181   xs[1] = itofix(x2);
182   ys[1] = itofix(y2);
183   xs[2] = itofix(x3);
184   ys[2] = itofix(y3);
185   xs[3] = itofix(x4);
186   ys[3] = itofix(y4);
187 
188   ase_parallelogram_map_standard(bmp, sprite, mask, xs, ys);
189 }
190 
191 // Scanline drawers.
192 
193 template<class Traits, class Delegate>
draw_scanline(Image * bmp,const Image * spr,const Image * mask,fixed l_bmp_x,int bmp_y_i,fixed r_bmp_x,fixed l_spr_x,fixed l_spr_y,fixed spr_dx,fixed spr_dy,Delegate & delegate)194 static void draw_scanline(
195   Image* bmp,
196   const Image* spr,
197   const Image* mask,
198   fixed l_bmp_x, int bmp_y_i,
199   fixed r_bmp_x,
200   fixed l_spr_x, fixed l_spr_y,
201   fixed spr_dx, fixed spr_dy,
202   Delegate& delegate)
203 {
204   r_bmp_x >>= 16;
205   l_bmp_x >>= 16;
206 
207   delegate.lockBits(bmp, gfx::Rect(l_bmp_x, bmp_y_i, r_bmp_x - l_bmp_x + 1, 1));
208 
209   gfx::Rect maskBounds = (mask ? mask->bounds(): spr->bounds());
210 
211   for (int x=(int)l_bmp_x; x<=(int)r_bmp_x; ++x) {
212     int u = l_spr_x>>16;
213     int v = l_spr_y>>16;
214 
215     if (!mask ||
216         (maskBounds.contains(u, v) && get_pixel_fast<BitmapTraits>(mask, u, v)))
217       delegate.putPixel(spr, u, v);
218     delegate.nextPixel();
219 
220     l_spr_x += spr_dx;
221     l_spr_y += spr_dy;
222   }
223 
224   delegate.unlockBits();
225 }
226 
227 template<class Traits>
228 class GenericDelegate {
229 public:
lockBits(Image * bmp,const gfx::Rect & bounds)230   void lockBits(Image* bmp, const gfx::Rect& bounds) {
231     m_bits = bmp->lockBits<Traits>(Image::ReadWriteLock, bounds);
232     m_it = m_bits.begin();
233     m_end = m_bits.end();
234   }
235 
unlockBits()236   void unlockBits() {
237     m_bits.unlock();
238   }
239 
nextPixel()240   void nextPixel() {
241     ASSERT(m_it != m_end);
242     ++m_it;
243   }
244 
245 private:
246   ImageBits<Traits> m_bits;
247 
248 protected:
249   typename LockImageBits<Traits>::iterator m_it, m_end;
250 };
251 
252 class RgbDelegate : public GenericDelegate<RgbTraits> {
253 public:
RgbDelegate(color_t mask_color)254   RgbDelegate(color_t mask_color) {
255     m_mask_color = mask_color;
256   }
257 
putPixel(const Image * spr,int spr_x,int spr_y)258   void putPixel(const Image* spr, int spr_x, int spr_y) {
259     ASSERT(m_it != m_end);
260 
261     int c = get_pixel_fast<RgbTraits>(spr, spr_x, spr_y);
262     if ((rgba_geta(m_mask_color) == 0) || ((c & rgba_rgb_mask) != (m_mask_color & rgba_rgb_mask)))
263       *m_it = rgba_blender_normal(*m_it, c);
264   }
265 
266 private:
267   color_t m_mask_color;
268 };
269 
270 class GrayscaleDelegate : public GenericDelegate<GrayscaleTraits> {
271 public:
GrayscaleDelegate(color_t mask_color)272   GrayscaleDelegate(color_t mask_color) {
273     m_mask_color = mask_color;
274   }
275 
putPixel(const Image * spr,int spr_x,int spr_y)276   void putPixel(const Image* spr, int spr_x, int spr_y) {
277     ASSERT(m_it != m_end);
278 
279     int c = get_pixel_fast<GrayscaleTraits>(spr, spr_x, spr_y);
280     if ((graya_geta(m_mask_color) == 0) || ((c & graya_v_mask) != (m_mask_color & graya_v_mask)))
281       *m_it = graya_blender_normal(*m_it, c, 255);
282   }
283 
284 private:
285   color_t m_mask_color;
286 };
287 
288 class IndexedDelegate : public GenericDelegate<IndexedTraits> {
289 public:
IndexedDelegate(color_t mask_color)290   IndexedDelegate(color_t mask_color) :
291     m_mask_color(mask_color) {
292   }
293 
putPixel(const Image * spr,int spr_x,int spr_y)294   void putPixel(const Image* spr, int spr_x, int spr_y) {
295     ASSERT(m_it != m_end);
296 
297     color_t c = get_pixel_fast<IndexedTraits>(spr, spr_x, spr_y);
298     if (c != m_mask_color)
299       *m_it = c;
300   }
301 
302 private:
303   color_t m_mask_color;
304 };
305 
306 class BitmapDelegate : public GenericDelegate<BitmapTraits> {
307 public:
putPixel(const Image * spr,int spr_x,int spr_y)308   void putPixel(const Image* spr, int spr_x, int spr_y) {
309     ASSERT(m_it != m_end);
310 
311     int c = get_pixel_fast<BitmapTraits>(spr, spr_x, spr_y);
312     if (c != 0)                 // TODO
313       *m_it = c;
314   }
315 };
316 
317 /* _parallelogram_map:
318  *  Worker routine for drawing rotated and/or scaled and/or flipped sprites:
319  *  It actually maps the sprite to any parallelogram-shaped area of the
320  *  bitmap. The top left corner is mapped to (xs[0], ys[0]), the top right to
321  *  (xs[1], ys[1]), the bottom right to x (xs[2], ys[2]), and the bottom left
322  *  to (xs[3], ys[3]). The corners are assumed to form a perfect
323  *  parallelogram, i.e. xs[0]+xs[2] = xs[1]+xs[3]. The corners are given in
324  *  fixed point format, so xs[] and ys[] are coordinates of the outer corners
325  *  of corner pixels in clockwise order beginning with top left.
326  *  All coordinates begin with 0 in top left corner of pixel (0, 0). So a
327  *  rotation by 0 degrees of a sprite to the top left of a bitmap can be
328  *  specified with coordinates (0, 0) for the top left pixel in source
329  *  bitmap. With the default scanline drawer, a pixel in the destination
330  *  bitmap is drawn if and only if its center is covered by any pixel in the
331  *  sprite. The color of this covering sprite pixel is used to draw.
332  *  If sub_pixel_accuracy=false, then the scanline drawer will be called with
333  *  *_bmp_x being a fixed point representation of the integers representing
334  *  the x coordinate of the first and last point in bmp whose centre is
335  *  covered by the sprite. If sub_pixel_accuracy=true, then the scanline
336  *  drawer will be called with the exact fixed point position of the first
337  *  and last point in which the horizontal line passing through the centre is
338  *  at least partly covered by the sprite. This is useful for doing
339  *  anti-aliased blending.
340  */
341 template<class Traits, class Delegate>
ase_parallelogram_map(Image * bmp,const Image * spr,const Image * mask,fixed xs[4],fixed ys[4],int sub_pixel_accuracy,Delegate delegate)342 static void ase_parallelogram_map(
343   Image* bmp, const Image* spr, const Image* mask,
344   fixed xs[4], fixed ys[4],
345   int sub_pixel_accuracy, Delegate delegate)
346 {
347   /* Index in xs[] and ys[] to topmost point. */
348   int top_index;
349   /* Rightmost point has index (top_index+right_index) int xs[] and ys[]. */
350   int right_index;
351   /* Loop variables. */
352   int index, i;
353   /* Coordinates in bmp ordered as top-right-bottom-left. */
354   fixed corner_bmp_x[4], corner_bmp_y[4];
355   /* Coordinates in spr ordered as top-right-bottom-left. */
356   fixed corner_spr_x[4], corner_spr_y[4];
357   /* y coordinate of bottom point, left point and right point. */
358   int clip_bottom_i, l_bmp_y_bottom_i, r_bmp_y_bottom_i;
359   /* Left and right clipping. */
360   fixed clip_left, clip_right;
361   /* Temporary variable. */
362   fixed extra_scanline_fraction;
363 
364   /*
365    * Variables used in the loop
366    */
367   /* Coordinates of sprite and bmp points in beginning of scanline. */
368   fixed l_spr_x, l_spr_y, l_bmp_x, l_bmp_dx;
369   /* Increment of left sprite point as we move a scanline down. */
370   fixed l_spr_dx, l_spr_dy;
371   /* Coordinates of sprite and bmp points in end of scanline. */
372   fixed r_bmp_x, r_bmp_dx;
373 #ifdef KEEP_TRACK_OF_RIGHT_SPRITE_SCANLINE
374   fixed r_spr_x, r_spr_y;
375   /* Increment of right sprite point as we move a scanline down. */
376   fixed r_spr_dx, r_spr_dy;
377 #endif
378   /* Increment of sprite point as we move right inside a scanline. */
379   fixed spr_dx, spr_dy;
380   /* Positions of beginning of scanline after rounding to integer coordinate
381      in bmp. */
382   fixed l_spr_x_rounded, l_spr_y_rounded, l_bmp_x_rounded;
383   fixed r_bmp_x_rounded;
384   /* Current scanline. */
385   int bmp_y_i;
386   /* Right edge of scanline. */
387   int right_edge_test;
388 
389   /* Get index of topmost point. */
390   top_index = 0;
391   if (ys[1] < ys[0])
392     top_index = 1;
393   if (ys[2] < ys[top_index])
394     top_index = 2;
395   if (ys[3] < ys[top_index])
396     top_index = 3;
397 
398   /* Get direction of points: clockwise or anti-clockwise. */
399   if (fixmul(xs[(top_index+1) & 3] - xs[top_index],
400              ys[(top_index-1) & 3] - ys[top_index]) >
401       fixmul(xs[(top_index-1) & 3] - xs[top_index],
402              ys[(top_index+1) & 3] - ys[top_index]))
403     right_index = 1;
404   else
405     right_index = -1;
406 
407   /*
408    * Get coordinates of the corners.
409    */
410 
411   /* corner_*[0] is top, [1] is right, [2] is bottom, [3] is left. */
412   index = top_index;
413   for (i = 0; i < 4; i++) {
414     corner_bmp_x[i] = xs[index];
415     corner_bmp_y[i] = ys[index];
416     if (index < 2)
417       corner_spr_y[i] = 0;
418     else
419       /* Need `- 1' since otherwise it would be outside sprite. */
420       corner_spr_y[i] = (spr->height() << 16) - 1;
421     if ((index == 0) || (index == 3))
422       corner_spr_x[i] = 0;
423     else
424       corner_spr_x[i] = (spr->width() << 16) - 1;
425     index = (index + right_index) & 3;
426   }
427 
428   /*
429    * Get scanline starts, ends and deltas, and clipping coordinates.
430    */
431 #define top_bmp_y    corner_bmp_y[0]
432 #define right_bmp_y  corner_bmp_y[1]
433 #define bottom_bmp_y corner_bmp_y[2]
434 #define left_bmp_y   corner_bmp_y[3]
435 #define top_bmp_x    corner_bmp_x[0]
436 #define right_bmp_x  corner_bmp_x[1]
437 #define bottom_bmp_x corner_bmp_x[2]
438 #define left_bmp_x   corner_bmp_x[3]
439 #define top_spr_y    corner_spr_y[0]
440 #define right_spr_y  corner_spr_y[1]
441 #define bottom_spr_y corner_spr_y[2]
442 #define left_spr_y   corner_spr_y[3]
443 #define top_spr_x    corner_spr_x[0]
444 #define right_spr_x  corner_spr_x[1]
445 #define bottom_spr_x corner_spr_x[2]
446 #define left_spr_x   corner_spr_x[3]
447 
448   /* Calculate left and right clipping. */
449   clip_left = 0;
450   clip_right = (bmp->width() << 16) - 1;
451 
452   /* Quit if we're totally outside. */
453   if ((left_bmp_x > clip_right) &&
454       (top_bmp_x > clip_right) &&
455       (bottom_bmp_x > clip_right))
456     return;
457   if ((right_bmp_x < clip_left) &&
458       (top_bmp_x < clip_left) &&
459       (bottom_bmp_x < clip_left))
460     return;
461 
462   /* Bottom clipping. */
463   if (sub_pixel_accuracy)
464     clip_bottom_i = (bottom_bmp_y + 0xffff) >> 16;
465   else
466     clip_bottom_i = (bottom_bmp_y + 0x8000) >> 16;
467 
468   if (clip_bottom_i > bmp->height())
469     clip_bottom_i = bmp->height();
470 
471   /* Calculate y coordinate of first scanline. */
472   if (sub_pixel_accuracy)
473     bmp_y_i = top_bmp_y >> 16;
474   else
475     bmp_y_i = (top_bmp_y + 0x8000) >> 16;
476 
477   if (bmp_y_i < 0)
478     bmp_y_i = 0;
479 
480   /* Sprite is above or below bottom clipping area. */
481   if (bmp_y_i >= clip_bottom_i)
482     return;
483 
484   /* Vertical gap between top corner and centre of topmost scanline. */
485   extra_scanline_fraction = (bmp_y_i << 16) + 0x8000 - top_bmp_y;
486   /* Calculate x coordinate of beginning of scanline in bmp. */
487   l_bmp_dx = fixdiv(left_bmp_x - top_bmp_x,
488                     left_bmp_y - top_bmp_y);
489   l_bmp_x = top_bmp_x + fixmul(extra_scanline_fraction, l_bmp_dx);
490   /* Calculate x coordinate of beginning of scanline in spr. */
491   /* note: all these are rounded down which is probably a Good Thing (tm) */
492   l_spr_dx = fixdiv(left_spr_x - top_spr_x,
493                     left_bmp_y - top_bmp_y);
494   l_spr_x = top_spr_x + fixmul(extra_scanline_fraction, l_spr_dx);
495   /* Calculate y coordinate of beginning of scanline in spr. */
496   l_spr_dy = fixdiv(left_spr_y - top_spr_y,
497                     left_bmp_y - top_bmp_y);
498   l_spr_y = top_spr_y + fixmul(extra_scanline_fraction, l_spr_dy);
499 
500   /* Calculate left loop bound. */
501   l_bmp_y_bottom_i = (left_bmp_y + 0x8000) >> 16;
502   if (l_bmp_y_bottom_i > clip_bottom_i)
503     l_bmp_y_bottom_i = clip_bottom_i;
504 
505   /* Calculate x coordinate of end of scanline in bmp. */
506   r_bmp_dx = fixdiv(right_bmp_x - top_bmp_x,
507                     right_bmp_y - top_bmp_y);
508   r_bmp_x = top_bmp_x + fixmul(extra_scanline_fraction, r_bmp_dx);
509 #ifdef KEEP_TRACK_OF_RIGHT_SPRITE_SCANLINE
510   /* Calculate x coordinate of end of scanline in spr. */
511   r_spr_dx = fixdiv(right_spr_x - top_spr_x,
512                     right_bmp_y - top_bmp_y);
513   r_spr_x = top_spr_x + fixmul(extra_scanline_fraction, r_spr_dx);
514   /* Calculate y coordinate of end of scanline in spr. */
515   r_spr_dy = fixdiv(right_spr_y - top_spr_y,
516                     right_bmp_y - top_bmp_y);
517   r_spr_y = top_spr_y + fixmul(extra_scanline_fraction, r_spr_dy);
518 #endif
519 
520   /* Calculate right loop bound. */
521   r_bmp_y_bottom_i = (right_bmp_y + 0x8000) >> 16;
522 
523   /* Get dx and dy, the offsets to add to the source coordinates as we move
524      one pixel rightwards along a scanline. This formula can be derived by
525      considering the 2x2 matrix that transforms the sprite to the
526      parallelogram.
527      We'd better use double to get this as exact as possible, since any
528      errors will be accumulated along the scanline.
529   */
530   spr_dx = (fixed)((ys[3] - ys[0]) * 65536.0 * (65536.0 * spr->width()) /
531                    ((xs[1] - xs[0]) * (double)(ys[3] - ys[0]) -
532                     (xs[3] - xs[0]) * (double)(ys[1] - ys[0])));
533   spr_dy = (fixed)((ys[1] - ys[0]) * 65536.0 * (65536.0 * spr->height()) /
534                    ((xs[3] - xs[0]) * (double)(ys[1] - ys[0]) -
535                     (xs[1] - xs[0]) * (double)(ys[3] - ys[0])));
536 
537   /*
538    * Loop through scanlines.
539    */
540 
541   while (1) {
542     /* Has beginning of scanline passed a corner? */
543     if (bmp_y_i >= l_bmp_y_bottom_i) {
544       /* Are we done? */
545       if (bmp_y_i >= clip_bottom_i)
546         break;
547 
548       /* Vertical gap between left corner and centre of scanline. */
549       extra_scanline_fraction = (bmp_y_i << 16) + 0x8000 - left_bmp_y;
550       /* Update x coordinate of beginning of scanline in bmp. */
551       l_bmp_dx = fixdiv(bottom_bmp_x - left_bmp_x,
552                         bottom_bmp_y - left_bmp_y);
553       l_bmp_x = left_bmp_x + fixmul(extra_scanline_fraction, l_bmp_dx);
554       /* Update x coordinate of beginning of scanline in spr. */
555       l_spr_dx = fixdiv(bottom_spr_x - left_spr_x,
556                         bottom_bmp_y - left_bmp_y);
557       l_spr_x = left_spr_x + fixmul(extra_scanline_fraction, l_spr_dx);
558       /* Update y coordinate of beginning of scanline in spr. */
559       l_spr_dy = fixdiv(bottom_spr_y - left_spr_y,
560                         bottom_bmp_y - left_bmp_y);
561       l_spr_y = left_spr_y + fixmul(extra_scanline_fraction, l_spr_dy);
562 
563       /* Update loop bound. */
564       if (sub_pixel_accuracy)
565         l_bmp_y_bottom_i = (bottom_bmp_y + 0xffff) >> 16;
566       else
567         l_bmp_y_bottom_i = (bottom_bmp_y + 0x8000) >> 16;
568       if (l_bmp_y_bottom_i > clip_bottom_i)
569         l_bmp_y_bottom_i = clip_bottom_i;
570     }
571 
572     /* Has end of scanline passed a corner? */
573     if (bmp_y_i >= r_bmp_y_bottom_i) {
574       /* Vertical gap between right corner and centre of scanline. */
575       extra_scanline_fraction = (bmp_y_i << 16) + 0x8000 - right_bmp_y;
576       /* Update x coordinate of end of scanline in bmp. */
577       r_bmp_dx = fixdiv(bottom_bmp_x - right_bmp_x,
578                         bottom_bmp_y - right_bmp_y);
579       r_bmp_x = right_bmp_x + fixmul(extra_scanline_fraction, r_bmp_dx);
580 #ifdef KEEP_TRACK_OF_RIGHT_SPRITE_SCANLINE
581       /* Update x coordinate of beginning of scanline in spr. */
582       r_spr_dx = fixdiv(bottom_spr_x - right_spr_x,
583                         bottom_bmp_y - right_bmp_y);
584       r_spr_x = right_spr_x + fixmul(extra_scanline_fraction, r_spr_dx);
585       /* Update y coordinate of beginning of scanline in spr. */
586       r_spr_dy = fixdiv(bottom_spr_y - right_spr_y,
587                         bottom_bmp_y - right_bmp_y);
588       r_spr_y = right_spr_y + fixmul(extra_scanline_fraction, r_spr_dy);
589 #endif
590 
591       /* Update loop bound: We aren't supposed to use this any more, so
592          just set it to some big enough value. */
593       r_bmp_y_bottom_i = clip_bottom_i;
594     }
595 
596     /* Make left bmp coordinate be an integer and clip it. */
597     if (sub_pixel_accuracy)
598       l_bmp_x_rounded = l_bmp_x;
599     else
600       l_bmp_x_rounded = (l_bmp_x + 0x8000) & ~0xffff;
601     if (l_bmp_x_rounded < clip_left)
602       l_bmp_x_rounded = clip_left;
603 
604     /* ... and move starting point in sprite accordingly. */
605     if (sub_pixel_accuracy) {
606       l_spr_x_rounded = l_spr_x +
607         fixmul((l_bmp_x_rounded - l_bmp_x), spr_dx);
608       l_spr_y_rounded = l_spr_y +
609         fixmul((l_bmp_x_rounded - l_bmp_x), spr_dy);
610     }
611     else {
612       l_spr_x_rounded = l_spr_x +
613         fixmul(l_bmp_x_rounded + 0x7fff - l_bmp_x, spr_dx);
614       l_spr_y_rounded = l_spr_y +
615         fixmul(l_bmp_x_rounded + 0x7fff - l_bmp_x, spr_dy);
616     }
617 
618     /* Make right bmp coordinate be an integer and clip it. */
619     if (sub_pixel_accuracy)
620       r_bmp_x_rounded = r_bmp_x;
621     else
622       r_bmp_x_rounded = (r_bmp_x - 0x8000) & ~0xffff;
623     if (r_bmp_x_rounded > clip_right)
624       r_bmp_x_rounded = clip_right;
625 
626     /* Draw! */
627     if (l_bmp_x_rounded <= r_bmp_x_rounded) {
628       if (!sub_pixel_accuracy) {
629         /* The bodies of these ifs are only reached extremely seldom,
630            it's an ugly hack to avoid reading outside the sprite when
631            the rounding errors are accumulated the wrong way. It would
632            be nicer if we could ensure that this never happens by making
633            all multiplications and divisions be rounded up or down at
634            the correct places.
635            I did try another approach: recalculate the edges of the
636            scanline from scratch each scanline rather than incrementally.
637            Drawing a sprite with that routine took about 25% longer time
638            though.
639         */
640         if ((unsigned)(l_spr_x_rounded >> 16) >= (unsigned)spr->width()) {
641           if (((l_spr_x_rounded < 0) && (spr_dx <= 0)) ||
642               ((l_spr_x_rounded > 0) && (spr_dx >= 0))) {
643             /* This can happen. */
644             goto skip_draw;
645           }
646           else {
647             /* I don't think this can happen, but I can't prove it. */
648             do {
649               l_spr_x_rounded += spr_dx;
650               l_bmp_x_rounded += 65536;
651               if (l_bmp_x_rounded > r_bmp_x_rounded)
652                 goto skip_draw;
653             } while ((unsigned)(l_spr_x_rounded >> 16) >=
654                      (unsigned)spr->width());
655 
656           }
657         }
658         right_edge_test = l_spr_x_rounded +
659           ((r_bmp_x_rounded - l_bmp_x_rounded) >> 16) *
660           spr_dx;
661         if ((unsigned)(right_edge_test >> 16) >= (unsigned)spr->width()) {
662           if (((right_edge_test < 0) && (spr_dx <= 0)) ||
663               ((right_edge_test > 0) && (spr_dx >= 0))) {
664             /* This can happen. */
665             do {
666               r_bmp_x_rounded -= 65536;
667               right_edge_test -= spr_dx;
668               if (l_bmp_x_rounded > r_bmp_x_rounded)
669                 goto skip_draw;
670             } while ((unsigned)(right_edge_test >> 16) >=
671                      (unsigned)spr->width());
672           }
673           else {
674             /* I don't think this can happen, but I can't prove it. */
675             goto skip_draw;
676           }
677         }
678         if ((unsigned)(l_spr_y_rounded >> 16) >= (unsigned)spr->height()) {
679           if (((l_spr_y_rounded < 0) && (spr_dy <= 0)) ||
680               ((l_spr_y_rounded > 0) && (spr_dy >= 0))) {
681             /* This can happen. */
682             goto skip_draw;
683           }
684           else {
685             /* I don't think this can happen, but I can't prove it. */
686             do {
687               l_spr_y_rounded += spr_dy;
688               l_bmp_x_rounded += 65536;
689               if (l_bmp_x_rounded > r_bmp_x_rounded)
690                 goto skip_draw;
691             } while (((unsigned)l_spr_y_rounded >> 16) >=
692                      (unsigned)spr->height());
693           }
694         }
695         right_edge_test = l_spr_y_rounded +
696           ((r_bmp_x_rounded - l_bmp_x_rounded) >> 16) *
697           spr_dy;
698         if ((unsigned)(right_edge_test >> 16) >= (unsigned)spr->height()) {
699           if (((right_edge_test < 0) && (spr_dy <= 0)) ||
700               ((right_edge_test > 0) && (spr_dy >= 0))) {
701             /* This can happen. */
702             do {
703               r_bmp_x_rounded -= 65536;
704               right_edge_test -= spr_dy;
705               if (l_bmp_x_rounded > r_bmp_x_rounded)
706                 goto skip_draw;
707             } while ((unsigned)(right_edge_test >> 16) >=
708                      (unsigned)spr->height());
709           }
710           else {
711             /* I don't think this can happen, but I can't prove it. */
712             goto skip_draw;
713           }
714         }
715       }
716       draw_scanline<Traits, Delegate>(bmp, spr, mask,
717         l_bmp_x_rounded, bmp_y_i, r_bmp_x_rounded,
718         l_spr_x_rounded, l_spr_y_rounded,
719         spr_dx, spr_dy, delegate);
720 
721     }
722     /* I'm not going to apoligize for this label and its gotos: to get
723        rid of it would just make the code look worse. */
724   skip_draw:
725 
726     /* Jump to next scanline. */
727     bmp_y_i++;
728     /* Update beginning of scanline. */
729     l_bmp_x += l_bmp_dx;
730     l_spr_x += l_spr_dx;
731     l_spr_y += l_spr_dy;
732     /* Update end of scanline. */
733     r_bmp_x += r_bmp_dx;
734 #ifdef KEEP_TRACK_OF_RIGHT_SPRITE_SCANLINE
735     r_spr_x += r_spr_dx;
736     r_spr_y += r_spr_dy;
737 #endif
738   }
739 }
740 
741 /* _parallelogram_map_standard:
742  *  Helper function for calling _parallelogram_map() with the appropriate
743  *  scanline drawer. I didn't want to include this in the
744  *  _parallelogram_map() function since then you can bypass it and define
745  *  your own scanline drawer, eg. for anti-aliased rotations.
746  */
ase_parallelogram_map_standard(Image * bmp,const Image * sprite,const Image * mask,fixed xs[4],fixed ys[4])747 static void ase_parallelogram_map_standard(
748   Image* bmp, const Image* sprite, const Image* mask,
749   fixed xs[4], fixed ys[4])
750 {
751   switch (bmp->pixelFormat()) {
752 
753     case IMAGE_RGB: {
754       RgbDelegate delegate(sprite->maskColor());
755       ase_parallelogram_map<RgbTraits, RgbDelegate>(bmp, sprite, mask, xs, ys, false, delegate);
756       break;
757     }
758 
759     case IMAGE_GRAYSCALE: {
760       GrayscaleDelegate delegate(sprite->maskColor());
761       ase_parallelogram_map<GrayscaleTraits, GrayscaleDelegate>(bmp, sprite, mask, xs, ys, false, delegate);
762       break;
763     }
764 
765     case IMAGE_INDEXED: {
766       IndexedDelegate delegate(sprite->maskColor());
767       ase_parallelogram_map<IndexedTraits, IndexedDelegate>(bmp, sprite, mask, xs, ys, false, delegate);
768       break;
769     }
770 
771     case IMAGE_BITMAP: {
772       BitmapDelegate delegate;
773       ase_parallelogram_map<BitmapTraits, BitmapDelegate>(bmp, sprite, mask, xs, ys, false, delegate);
774       break;
775     }
776   }
777 }
778 
779 /* _rotate_scale_flip_coordinates:
780  *  Calculates the coordinates for the rotated, scaled and flipped sprite,
781  *  and passes them on to the given function.
782  */
ase_rotate_scale_flip_coordinates(fixed w,fixed h,fixed x,fixed y,fixed cx,fixed cy,fixed angle,fixed scale_x,fixed scale_y,int h_flip,int v_flip,fixed xs[4],fixed ys[4])783 static void ase_rotate_scale_flip_coordinates(fixed w, fixed h,
784                                               fixed x, fixed y,
785                                               fixed cx, fixed cy,
786                                               fixed angle,
787                                               fixed scale_x, fixed scale_y,
788                                               int h_flip, int v_flip,
789                                               fixed xs[4], fixed ys[4])
790 {
791    fixed fix_cos, fix_sin;
792    int tl = 0, tr = 1, bl = 3, br = 2;
793    int tmp;
794    double cos_angle, sin_angle;
795    fixed xofs, yofs;
796 
797    /* Setting angle to the range -180...180 degrees makes sin & cos
798       more numerically stable. (Yes, this does have an effect for big
799       angles!) Note that using "real" sin() and cos() gives much better
800       precision than fixsin() and fixcos(). */
801    angle = angle & 0xffffff;
802    if (angle >= 0x800000)
803       angle -= 0x1000000;
804 
805    cos_angle = cos(angle * (PI / (double)0x800000));
806    sin_angle = sin(angle * (PI / (double)0x800000));
807 
808    if (cos_angle >= 0)
809       fix_cos = (int)(cos_angle * 0x10000 + 0.5);
810    else
811       fix_cos = (int)(cos_angle * 0x10000 - 0.5);
812    if (sin_angle >= 0)
813       fix_sin = (int)(sin_angle * 0x10000 + 0.5);
814    else
815       fix_sin = (int)(sin_angle * 0x10000 - 0.5);
816 
817    /* Decide what order to take corners in. */
818    if (v_flip) {
819       tl = 3;
820       tr = 2;
821       bl = 0;
822       br = 1;
823    }
824    else {
825       tl = 0;
826       tr = 1;
827       bl = 3;
828       br = 2;
829    }
830    if (h_flip) {
831       tmp = tl;
832       tl = tr;
833       tr = tmp;
834       tmp = bl;
835       bl = br;
836       br = tmp;
837    }
838 
839    /* Calculate new coordinates of all corners. */
840    w = fixmul(w, scale_x);
841    h = fixmul(h, scale_y);
842    cx = fixmul(cx, scale_x);
843    cy = fixmul(cy, scale_y);
844 
845    xofs = x - fixmul(cx, fix_cos) + fixmul(cy, fix_sin);
846 
847    yofs = y - fixmul(cx, fix_sin) - fixmul(cy, fix_cos);
848 
849    xs[tl] = xofs;
850    ys[tl] = yofs;
851    xs[tr] = xofs + fixmul(w, fix_cos);
852    ys[tr] = yofs + fixmul(w, fix_sin);
853    xs[bl] = xofs - fixmul(h, fix_sin);
854    ys[bl] = yofs + fixmul(h, fix_cos);
855 
856    xs[br] = xs[tr] + xs[bl] - xs[tl];
857    ys[br] = ys[tr] + ys[bl] - ys[tl];
858 }
859 
860 } // namespace algorithm
861 } // namespace doc
862