1 #include "common.h"
2 
3 #include "blend.h"
4 #include "rotate.h"
5 
6 /*\ Linear interpolation functions \*/
7 /*\ Between two values \*/
8 #define INTERP(v1, v2, f) \
9 	(((v1) << _ROTATE_PREC) + (((v2) - (v1)) * (f)))
10 
11 /*\ Between two colour bytes \*/
12 #define INTERP_VAL1(x_VAL, dest, l, r, x) \
13 	x_VAL(dest) = (INTERP(x_VAL(l), x_VAL(r), (x)) >> _ROTATE_PREC)
14 
15 /*\ Alpha channel: between two values and two zeroes \*/
16 #define INTERP_VAL1_A0(dest, v1, v2, f1, f2) \
17 	A_VAL(dest) = ((INTERP(A_VAL(v1), A_VAL(v2), (f1)) *	\
18 			(f2)) >> (2 * _ROTATE_PREC))
19 
20 /*\ Between four values \*/
21 #define INTERP_VAL2(x_VAL, dest, ul, ur, ll, lr, x, y) \
22 	x_VAL(dest) = (INTERP(INTERP(x_VAL(ul), x_VAL(ur), (x)),	\
23 			      INTERP(x_VAL(ll), x_VAL(lr), (x)),	\
24 			     (y)) >> (2 * _ROTATE_PREC))
25 
26 /*\ Functions used in rotation routines.
27 |*| The do { } while(0) construction is to make it one statement.
28 \*/
29 /*\ Between four colours \*/
30 #define INTERP_ARGB(dest, src, sow, x, y) do { \
31 	INTERP_VAL2(R_VAL, (dest), (src), (src) + 1, (src) + (sow), (src) + (sow) + 1, (x) & _ROTATE_PREC_BITS, (y) & _ROTATE_PREC_BITS);	\
32 	INTERP_VAL2(G_VAL, (dest), (src), (src) + 1, (src) + (sow), (src) + (sow) + 1, (x) & _ROTATE_PREC_BITS, (y) & _ROTATE_PREC_BITS);	\
33 	INTERP_VAL2(B_VAL, (dest), (src), (src) + 1, (src) + (sow), (src) + (sow) + 1, (x) & _ROTATE_PREC_BITS, (y) & _ROTATE_PREC_BITS);	\
34 	INTERP_VAL2(A_VAL, (dest), (src), (src) + 1, (src) + (sow), (src) + (sow) + 1, (x) & _ROTATE_PREC_BITS, (y) & _ROTATE_PREC_BITS);	\
35 	} while (0)
36 
37 /*\ Between two colours, alpha between two values and zeroes \*/
38 #define INTERP_RGB_A0(dest, v1, v2, f, f2) do { \
39 	INTERP_VAL1(R_VAL, (dest), (v1), (v2), (f) & _ROTATE_PREC_BITS); \
40 	INTERP_VAL1(G_VAL, (dest), (v1), (v2), (f) & _ROTATE_PREC_BITS); \
41 	INTERP_VAL1(B_VAL, (dest), (v1), (v2), (f) & _ROTATE_PREC_BITS); \
42 	INTERP_VAL1_A0(dest, (v1), (v2), (f) & _ROTATE_PREC_BITS, (f2) & _ROTATE_PREC_BITS);	\
43 	} while (0)
44 
45 /*\ One colour, alpha between one value and three zeroes \*/
46 #define INTERP_A000(dest, v, f1, f2) do {	\
47 	*(dest) = *(v);				\
48 	A_VAL(dest) = (A_VAL(dest) *		\
49 		((f1) & _ROTATE_PREC_BITS) * ((f2) & _ROTATE_PREC_BITS)) >> (2 * _ROTATE_PREC);	\
50 	} while (0)
51 
52 /*\ Rotate by pixel sampling only, target inside source \*/
53 static void
__imlib_RotateSampleInside(DATA32 * src,DATA32 * dest,int sow,int dow,int dw,int dh,int x,int y,int dxh,int dyh,int dxv,int dyv)54 __imlib_RotateSampleInside(DATA32 * src, DATA32 * dest, int sow, int dow,
55                            int dw, int dh, int x, int y,
56                            int dxh, int dyh, int dxv, int dyv)
57 {
58    int                 i;
59 
60    if ((dw < 1) || (dh < 1))
61       return;
62 
63    while (1)
64      {
65         i = dw - 1;
66         do
67           {
68              *dest = src[(x >> _ROTATE_PREC) + ((y >> _ROTATE_PREC) * sow)];
69              /*\ RIGHT; \ */
70              x += dxh;
71              y += dyh;
72              dest++;
73           }
74         while (--i >= 0);
75         if (--dh <= 0)
76            break;
77         /*\ DOWN/LEFT; \ */
78         x += dxv - dw * dxh;
79         y += dyv - dw * dyh;
80         dest += (dow - dw);
81      }
82 }
83 
84 /*\ Same as last function, but with antialiasing \*/
85 static void
__imlib_RotateAAInside(DATA32 * src,DATA32 * dest,int sow,int dow,int dw,int dh,int x,int y,int dxh,int dyh,int dxv,int dyv)86 __imlib_RotateAAInside(DATA32 * src, DATA32 * dest, int sow, int dow,
87                        int dw, int dh, int x, int y,
88                        int dxh, int dyh, int dxv, int dyv)
89 {
90    int                 i;
91 
92    if ((dw < 1) || (dh < 1))
93       return;
94 
95    while (1)
96      {
97         i = dw - 1;
98         do
99           {
100              DATA32             *src_x_y = (src + (x >> _ROTATE_PREC) +
101                                             ((y >> _ROTATE_PREC) * sow));
102              INTERP_ARGB(dest, src_x_y, sow, x, y);
103              /*\ RIGHT; \ */
104              x += dxh;
105              y += dyh;
106              dest++;
107           }
108         while (--i >= 0);
109         if (--dh <= 0)
110            break;
111         /*\ DOWN/LEFT; \ */
112         x += dxv - dw * dxh;
113         y += dyv - dw * dyh;
114         dest += (dow - dw);
115      }
116 }
117 
118 /*\ NOTE: To check if v is in [b .. t) ((v >= b) && (v < t))
119 |*|  it's quicker to do ((unsigned)(v - b) < (t - b))
120 |*|  as negative values, cast to unsigned, become large positive
121 |*|  values, and fall through the compare.
122 |*|  v in [0 .. t) is a special case: ((unsigned)v < t)
123 |*|  v in [-t .. 0) is also special, as its the same as ~v in [0 .. t)
124 \*/
125 static int
__check_inside_coords(int x,int y,int dxh,int dyh,int dxv,int dyv,int dw,int dh,int sw,int sh)126 __check_inside_coords(int x, int y, int dxh, int dyh, int dxv, int dyv,
127                       int dw, int dh, int sw, int sh)
128 {
129    sw <<= _ROTATE_PREC;
130    sh <<= _ROTATE_PREC;
131 
132    if (((unsigned)x >= (unsigned)sw) || ((unsigned)y >= (unsigned)sh))
133       return 0;
134    x += dxh * dw;
135    y += dyh * dw;
136    if (((unsigned)x >= (unsigned)sw) || ((unsigned)y >= (unsigned)sh))
137       return 0;
138    x += dxv * dh;
139    y += dyv * dh;
140    if (((unsigned)x >= (unsigned)sw) || ((unsigned)y >= (unsigned)sh))
141       return 0;
142    x -= dxh * dw;
143    y -= dyh * dw;
144    if (((unsigned)x >= (unsigned)sw) || ((unsigned)y >= (unsigned)sh))
145       return 0;
146 
147    return 1;
148 }
149 
150 /*\ These ones don't need the target to be inside the source \*/
151 void
__imlib_RotateSample(DATA32 * src,DATA32 * dest,int sow,int sw,int sh,int dow,int dw,int dh,int x,int y,int dxh,int dyh,int dxv,int dyv)152 __imlib_RotateSample(DATA32 * src, DATA32 * dest, int sow, int sw, int sh,
153                      int dow, int dw, int dh, int x, int y,
154                      int dxh, int dyh, int dxv, int dyv)
155 {
156    int                 i;
157 
158    if ((dw < 1) || (dh < 1))
159       return;
160 
161    if (__check_inside_coords(x, y, dxh, dyh, dxv, dyv, dw, dh, sw, sh))
162      {
163         __imlib_RotateSampleInside(src, dest, sow, dow, dw, dh, x, y,
164                                    dxh, dyh, dxv, dyv);
165         return;
166 
167      }
168 
169    sw <<= _ROTATE_PREC;
170    sh <<= _ROTATE_PREC;
171    while (1)
172      {
173         i = dw - 1;
174         do
175           {
176              if (((unsigned)x < (unsigned)sw) && ((unsigned)y < (unsigned)sh))
177                 *dest = src[(x >> _ROTATE_PREC) + ((y >> _ROTATE_PREC) * sow)];
178              else
179                 *dest = 0;
180              /*\ RIGHT; \ */
181              x += dxh;
182              y += dyh;
183              dest++;
184 
185           }
186         while (--i >= 0);
187         if (--dh <= 0)
188            break;
189         /*\ DOWN/LEFT; \ */
190         x += dxv - dw * dxh;
191         y += dyv - dw * dyh;
192         dest += (dow - dw);
193 
194      }
195 }
196 
197 /*\ With antialiasing.
198 |*| NB: The function 'sees' a transparent border around the source,
199 |*|     with colour channels matching the edge, so there is no need to do
200 |*|     anything special, but remember to account for this when calculating
201 |*|     the bounding box.
202 \*/
203 void
__imlib_RotateAA(DATA32 * src,DATA32 * dest,int sow,int sw,int sh,int dow,int dw,int dh,int x,int y,int dxh,int dyh,int dxv,int dyv)204 __imlib_RotateAA(DATA32 * src, DATA32 * dest, int sow, int sw, int sh,
205                  int dow, int dw, int dh, int x, int y,
206                  int dxh, int dyh, int dxv, int dyv)
207 {
208    int                 i;
209 
210    if ((dw < 1) || (dh < 1))
211       return;
212 
213    if (__check_inside_coords(x, y, dxh, dyh, dxv, dyv, dw, dh, sw - 1, sh - 1))
214      {
215         __imlib_RotateAAInside(src, dest, sow, dow, dw, dh, x, y,
216                                dxh, dyh, dxv, dyv);
217         return;
218 
219      }
220 
221    sw--;
222    sh--;
223    sw <<= _ROTATE_PREC;
224    sh <<= _ROTATE_PREC;
225    while (1)
226      {
227         i = dw - 1;
228         do
229           {
230              DATA32             *src_x_y = (src + (x >> _ROTATE_PREC) +
231                                             ((y >> _ROTATE_PREC) * sow));
232              if ((unsigned)x < (unsigned)sw)
233                {
234                   if ((unsigned)y < (unsigned)sh)
235                     {
236                        /*\  12
237                         * |*|  34
238                         * \ */
239                        INTERP_ARGB(dest, src_x_y, sow, x, y);
240                     }
241                   else if ((unsigned)(y - sh) < _ROTATE_PREC_MAX)
242                     {
243                        /*\  12
244                         * |*|  ..
245                         * \ */
246                        INTERP_RGB_A0(dest, src_x_y, src_x_y + 1, x, ~y);
247                     }
248                   else if ((unsigned)(~y) < _ROTATE_PREC_MAX)
249                     {
250                        /*\  ..
251                         * |*|  34
252                         * \ */
253                        INTERP_RGB_A0(dest, src_x_y + sow, src_x_y + sow + 1, x,
254                                      y);
255                     }
256                   else
257                      *dest = 0;
258                }
259              else if ((unsigned)(x - sw) < (_ROTATE_PREC_MAX))
260                {
261                   if ((unsigned)y < (unsigned)sh)
262                     {
263                        /*\  1.
264                         * |*|  3.
265                         * \ */
266                        INTERP_RGB_A0(dest, src_x_y, src_x_y + sow, y, ~x);
267                     }
268                   else if ((unsigned)(y - sh) < _ROTATE_PREC_MAX)
269                     {
270                        /*\  1.
271                         * |*|  ..
272                         * \ */
273                        INTERP_A000(dest, src_x_y, ~x, ~y);
274                     }
275                   else if ((unsigned)(~y) < _ROTATE_PREC_MAX)
276                     {
277                        /*\  ..
278                         * |*|  3.
279                         * \ */
280                        INTERP_A000(dest, src_x_y + sow, ~x, y);
281                     }
282                   else
283                      *dest = 0;
284                }
285              else if ((unsigned)(~x) < _ROTATE_PREC_MAX)
286                {
287                   if ((unsigned)y < (unsigned)sh)
288                     {
289                        /*\  .2
290                         * |*|  .4
291                         * \ */
292                        INTERP_RGB_A0(dest, src_x_y + 1, src_x_y + sow + 1, y,
293                                      x);
294                     }
295                   else if ((unsigned)(y - sh) < _ROTATE_PREC_MAX)
296                     {
297                        /*\  .2
298                         * |*|  ..
299                         * \ */
300                        INTERP_A000(dest, src_x_y + 1, x, ~y);
301                     }
302                   else if ((unsigned)(~y) < _ROTATE_PREC_MAX)
303                     {
304                        /*\  ..
305                         * |*|  .4
306                         * \ */
307                        INTERP_A000(dest, src_x_y + sow + 1, x, y);
308                     }
309                   else
310                      *dest = 0;
311                }
312              else
313                 *dest = 0;
314              /*\ RIGHT; \ */
315              x += dxh;
316              y += dyh;
317              dest++;
318 
319           }
320         while (--i >= 0);
321         if (--dh <= 0)
322            break;
323         /*\ DOWN/LEFT; \ */
324         x += dxv - dw * dxh;
325         y += dyv - dw * dyh;
326         dest += (dow - dw);
327 
328      }
329 }
330 
331 /*\ Should this be in blend.c ?? \*/
332 #define LINESIZE 16
333 
334 void
__imlib_BlendImageToImageSkewed(ImlibImage * im_src,ImlibImage * im_dst,char aa,char blend,char merge_alpha,int ssx,int ssy,int ssw,int ssh,int ddx,int ddy,int hsx,int hsy,int vsx,int vsy,ImlibColorModifier * cm,ImlibOp op,int clx,int cly,int clw,int clh)335 __imlib_BlendImageToImageSkewed(ImlibImage * im_src, ImlibImage * im_dst,
336                                 char aa, char blend, char merge_alpha,
337                                 int ssx, int ssy, int ssw, int ssh,
338                                 int ddx, int ddy,
339                                 int hsx, int hsy, int vsx, int vsy,
340                                 ImlibColorModifier * cm, ImlibOp op,
341                                 int clx, int cly, int clw, int clh)
342 {
343    int                 x, y, dxh, dyh, dxv, dyv, i;
344    double              xy2;
345    DATA32             *data, *src;
346 
347 #ifdef DO_MMX_ASM
348    int                 do_mmx;
349 #endif
350 
351    if ((ssw < 0) || (ssh < 0))
352       return;
353 
354    if (__imlib_LoadImageData(im_src))
355       return;
356    if (__imlib_LoadImageData(im_dst))
357       return;
358 
359    /*\ Complicated gonio.  Works on paper..
360     * |*| Too bad it doesn't all fit into integer math..
361     * \ */
362    if (vsx | vsy)
363      {
364         xy2 = (double)(hsx * vsy - vsx * hsy) / _ROTATE_PREC_MAX;
365         if (xy2 == 0.0)
366            return;
367         dxh = (double)(ssw * vsy) / xy2;
368         dxv = (double)-(ssw * vsx) / xy2;
369         dyh = (double)-(ssh * hsy) / xy2;
370         dyv = (double)(ssh * hsx) / xy2;
371      }
372    else
373      {
374         xy2 = (double)(hsx * hsx + hsy * hsy) / _ROTATE_PREC_MAX;
375         if (xy2 == 0.0)
376            return;
377         dxh = (double)(ssw * hsx) / xy2;
378         dyh = (double)-(ssw * hsy) / xy2;
379         dxv = -dyh;
380         dyv = dxh;
381      }
382    x = -(ddx * dxh + ddy * dxv);
383    y = -(ddx * dyh + ddy * dyv);
384 
385    if (ssx < 0)
386      {
387         x += ssx * _ROTATE_PREC_MAX;
388         ssw += ssx;
389         ssx = 0;
390 
391      }
392    if (ssy < 0)
393      {
394         y += ssy * _ROTATE_PREC_MAX;
395         ssh += ssy;
396         ssy = 0;
397 
398      }
399    if ((ssw + ssx) > im_src->w)
400       ssw = im_src->w - ssx;
401    if ((ssh + ssy) > im_src->h)
402       ssh = im_src->h - ssy;
403 
404    src = im_src->data + ssx + ssy * im_src->w;
405    data = malloc(im_dst->w * LINESIZE * sizeof(DATA32));
406    if (!data)
407       return;
408    if (aa)
409      {
410         /*\ Account for virtual transparent border \ */
411         x += _ROTATE_PREC_MAX;
412         y += _ROTATE_PREC_MAX;
413      }
414 #ifdef DO_MMX_ASM
415    do_mmx = __imlib_get_cpuid() & CPUID_MMX;
416 #endif
417    for (i = 0; i < im_dst->h; i += LINESIZE)
418      {
419         int                 x2, y2, w, h, l, r;
420 
421         h = MIN(LINESIZE, im_dst->h - i);
422 
423         x2 = x + h * dxv;
424         y2 = y + h * dyv;
425 
426         w = ssw << _ROTATE_PREC;
427         h = ssh << _ROTATE_PREC;
428         if (aa)
429           {
430              /*\ Account for virtual transparent border \ */
431              w += 2 << _ROTATE_PREC;
432              h += 2 << _ROTATE_PREC;
433           }
434         /*\ Pretty similar code \ */
435         if (dxh > 0)
436           {
437              if (dyh > 0)
438                {
439                   l = MAX(-MAX(y, y2) / dyh, -MAX(x, x2) / dxh);
440                   r = MIN((h - MIN(y, y2)) / dyh, (w - MIN(x, x2)) / dxh);
441 
442                }
443              else if (dyh < 0)
444                {
445                   l = MAX(-MAX(x, x2) / dxh, (h - MIN(y, y2)) / dyh);
446                   r = MIN(-MAX(y, y2) / dyh, (w - MIN(x, x2)) / dxh);
447 
448                }
449              else
450                {
451                   l = -MAX(x, x2) / dxh;
452                   r = (w - MIN(x, x2)) / dxh;
453 
454                }
455           }
456         else if (dxh < 0)
457           {
458              if (dyh > 0)
459                {
460                   l = MAX(-MAX(y, y2) / dyh, (w - MIN(x, x2)) / dxh);
461                   r = MIN(-MAX(x, x2) / dxh, (h - MIN(y, y2)) / dyh);
462 
463                }
464              else if (dyh < 0)
465                {
466                   l = MAX((h - MIN(y, y2)) / dyh, (w - MIN(x, x2)) / dxh);
467                   r = MIN(-MAX(y, y2) / dyh, -MAX(x, x2) / dxh);
468 
469                }
470              else
471                {
472                   l = (w - MIN(x, x2)) / dxh;
473                   r = -MAX(x, x2) / dxh;
474 
475                }
476 
477           }
478         else
479           {
480              if (dyh > 0)
481                {
482                   l = -MAX(y, y2) / dyh;
483                   r = (h - MIN(y, y2)) / dyh;
484 
485                }
486              else if (dyh < 0)
487                {
488                   l = (h - MIN(y, y2)) / dyh;
489                   r = -MAX(y, y2) / dyh;
490 
491                }
492              else
493                {
494                   l = 0;
495                   r = 0;
496 
497                }
498 
499           }
500         l--;
501         r += 2;                 /*\ Be paranoid about roundoff errors \ */
502         if (l < 0)
503            l = 0;
504         if (r > im_dst->w)
505            r = im_dst->w;
506         if (r <= l)
507           {
508              x = x2;
509              y = y2;
510              continue;
511 
512           }
513 
514         w = r - l;
515         h = MIN(LINESIZE, im_dst->h - i);
516         x += l * dxh;
517         y += l * dyh;
518         if (aa)
519           {
520              x -= _ROTATE_PREC_MAX;
521              y -= _ROTATE_PREC_MAX;
522 #ifdef DO_MMX_ASM
523              if (do_mmx)
524                 __imlib_mmx_RotateAA(src, data, im_src->w, ssw, ssh, w, w, h,
525                                      x, y, dxh, dyh, dxv, dyv);
526              else
527 #endif
528                 __imlib_RotateAA(src, data, im_src->w, ssw, ssh, w, w, h,
529                                  x, y, dxh, dyh, dxv, dyv);
530 
531           }
532         else
533           {
534              __imlib_RotateSample(src, data, im_src->w, ssw, ssh, w, w, h,
535                                   x, y, dxh, dyh, dxv, dyv);
536 
537           }
538         __imlib_BlendRGBAToData(data, w, h, im_dst->data,
539                                 im_dst->w, im_dst->h, 0, 0, l, i, w, h,
540                                 blend, merge_alpha, cm, op, 0);
541         x = x2;
542         y = y2;
543 
544      }
545    free(data);
546 }
547