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
24namespace doc {
25namespace algorithm {
26
27using namespace fixmath;
28
29static void ase_parallelogram_map_standard(
30 Image* bmp, const Image* sprite, const Image* mask,
31 fixed xs[4], fixed ys[4]);
32
33static 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
42template<typename ImageTraits, typename BlendFunc>
43static 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
86static color_t rgba_blender(color_t back, color_t front) {
87 return rgba_blender_normal(back, front);
88}
89
90static color_t grayscale_blender(color_t back, color_t front) {
91 return graya_blender_normal(back, front);
92}
93
94class if_blender {
95public:
96 if_blender(color_t mask) : m_mask(mask) {
97 }
98 color_t operator()(color_t back, color_t front) {
99 if (front != m_mask)
100 return front;
101 else
102 return back;
103 }
104private:
105 color_t m_mask;
106};
107
108void 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
153void 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 */
173void 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
193template<class Traits, class Delegate>
194static 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
227template<class Traits>
228class GenericDelegate {
229public:
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
236 void unlockBits() {
237 m_bits.unlock();
238 }
239
240 void nextPixel() {
241 ASSERT(m_it != m_end);
242 ++m_it;
243 }
244
245private:
246 ImageBits<Traits> m_bits;
247
248protected:
249 typename LockImageBits<Traits>::iterator m_it, m_end;
250};
251
252class RgbDelegate : public GenericDelegate<RgbTraits> {
253public:
254 RgbDelegate(color_t mask_color) {
255 m_mask_color = mask_color;
256 }
257
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
266private:
267 color_t m_mask_color;
268};
269
270class GrayscaleDelegate : public GenericDelegate<GrayscaleTraits> {
271public:
272 GrayscaleDelegate(color_t mask_color) {
273 m_mask_color = mask_color;
274 }
275
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
284private:
285 color_t m_mask_color;
286};
287
288class IndexedDelegate : public GenericDelegate<IndexedTraits> {
289public:
290 IndexedDelegate(color_t mask_color) :
291 m_mask_color(mask_color) {
292 }
293
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
302private:
303 color_t m_mask_color;
304};
305
306class BitmapDelegate : public GenericDelegate<BitmapTraits> {
307public:
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 */
341template<class Traits, class Delegate>
342static 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 */
747static 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 */
783static 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