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