]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/pixops/pixops.c
Bumped version number to 0.5.0.
[~andy/gtk] / gdk-pixbuf / pixops / pixops.c
1 #include <math.h>
2 #include <glib.h>
3 #include "config.h"
4
5 #include "pixops.h"
6 #include "pixops-internal.h"
7
8 #define SUBSAMPLE_BITS 4
9 #define SUBSAMPLE (1 << SUBSAMPLE_BITS)
10 #define SUBSAMPLE_MASK ((1 << SUBSAMPLE_BITS)-1)
11 #define SCALE_SHIFT 16
12
13 typedef struct _PixopsFilter PixopsFilter;
14
15 struct _PixopsFilter
16 {
17   int *weights;
18   int n_x;
19   int n_y;
20   double x_offset;
21   double y_offset;
22 }; 
23
24 typedef art_u8 *(*PixopsLineFunc) (int *weights, int n_x, int n_y,
25                                    art_u8 *dest, int dest_x, art_u8 *dest_end, int dest_channels, int dest_has_alpha,
26                                    art_u8 **src, int src_channels, art_boolean src_has_alpha,
27                                    int x_init, int x_step, int src_width,
28                                    int check_size, art_u32 color1, art_u32 color2);
29
30 typedef void (*PixopsPixelFunc) (art_u8 *dest, int dest_x, int dest_channels, int dest_has_alpha,
31                                  int src_has_alpha, int check_size, art_u32 color1,
32                                  art_u32 color2,
33                                  int r, int g, int b, int a);
34
35 static int
36 get_check_shift (int check_size)
37 {
38   int check_shift = 0;
39   g_return_val_if_fail (check_size >= 0, 4);
40
41   while (!(check_size & 1))
42     {
43       check_shift++;
44       check_size >>= 1;
45     }
46
47   return check_shift;
48 }
49
50 static void
51 pixops_scale_nearest (art_u8        *dest_buf,
52                       int            render_x0,
53                       int            render_y0,
54                       int            render_x1,
55                       int            render_y1,
56                       int            dest_rowstride,
57                       int            dest_channels,
58                       art_boolean    dest_has_alpha,
59                       art_u8        *src_buf,
60                       int            src_width,
61                       int            src_height,
62                       int            src_rowstride,
63                       int            src_channels,
64                       art_boolean    src_has_alpha,
65                       double         scale_x,
66                       double         scale_y)
67 {
68   int i, j;
69   int x;
70   int x_step = (1 << SCALE_SHIFT) / scale_x;
71   int y_step = (1 << SCALE_SHIFT) / scale_y;
72
73 #define INNER_LOOP(SRC_CHANNELS,DEST_CHANNELS)                  \
74       for (j=0; j < (render_x1 - render_x0); j++)               \
75         {                                                       \
76           art_u8 *p = src + (x >> SCALE_SHIFT) * SRC_CHANNELS;  \
77                                                                 \
78           dest[0] = p[0];                                       \
79           dest[1] = p[1];                                       \
80           dest[2] = p[2];                                       \
81                                                                 \
82           if (DEST_CHANNELS == 4)                               \
83             {                                                   \
84               if (SRC_CHANNELS == 4)                            \
85                 dest[3] = p[3];                                 \
86               else                                              \
87                 dest[3] = 0xff;                                 \
88             }                                                   \
89                                                                 \
90           dest += DEST_CHANNELS;                                \
91           x += x_step;                                          \
92         }
93
94   for (i = 0; i < (render_y1 - render_y0); i++)
95     {
96       art_u8 *src = src_buf + ((i * y_step + y_step / 2) >> SCALE_SHIFT) * src_rowstride;
97       art_u8 *dest = dest_buf + i * dest_rowstride;
98
99       x = render_x0 * x_step + x_step / 2;
100
101       if (src_channels == 3)
102         {
103           if (dest_channels == 3)
104             {
105               INNER_LOOP (3, 3);
106             }
107           else
108             {
109               INNER_LOOP (3, 4);
110             }
111         }
112       else if (src_channels == 4)
113         {
114           if (dest_channels == 3)
115             {
116               INNER_LOOP (4, 3);
117             }
118           else
119             {
120               for (j=0; j < (render_x1 - render_x0); j++)
121                 {
122                   art_u8 *p = src + (x >> SCALE_SHIFT) * 4;
123
124                   *(art_u32 *)dest = *(art_u32 *)p;
125                   
126                   dest += 4;
127                   x += x_step;
128                 }
129             }
130         }
131     }
132 #undef INNER_LOOP  
133 }
134
135 static void
136 pixops_composite_nearest (art_u8        *dest_buf,
137                           int            render_x0,
138                           int            render_y0,
139                           int            render_x1,
140                           int            render_y1,
141                           int            dest_rowstride,
142                           int            dest_channels,
143                           art_boolean    dest_has_alpha,
144                           art_u8        *src_buf,
145                           int            src_width,
146                           int            src_height,
147                           int            src_rowstride,
148                           int            src_channels,
149                           art_boolean    src_has_alpha,
150                           double         scale_x,
151                           double         scale_y,
152                           int            overall_alpha)
153 {
154   int i, j;
155   int x;
156   int x_step = (1 << SCALE_SHIFT) / scale_x;
157   int y_step = (1 << SCALE_SHIFT) / scale_y;
158
159   for (i = 0; i < (render_y1 - render_y0); i++)
160     {
161       art_u8 *src = src_buf + (((i + render_y0) * y_step + y_step / 2) >> SCALE_SHIFT) * src_rowstride;
162       art_u8 *dest = dest_buf + i * dest_rowstride + render_x0 * dest_channels;
163
164       x = render_x0 * x_step + x_step / 2;
165       
166       for (j=0; j < (render_x1 - render_x0); j++)
167         {
168           art_u8 *p = src + (x >> SCALE_SHIFT) * src_channels;
169           unsigned int a0;
170
171           if (src_has_alpha)
172             a0 = (p[3] * overall_alpha + 0xff) >> 8;
173           else
174             a0 = overall_alpha;
175
176           if (dest_has_alpha)
177             {
178               unsigned int a1 = dest[3];
179               unsigned int total = a0 + a1;
180
181               if (total)
182                 {
183                   dest[0] = (a0 * src[0] + a1 * dest[0]) / (total);
184                   dest[1] = (a0 * src[1] + a1 * dest[1]) / (total);
185                   dest[2] = (a0 * src[2] + a1 * dest[2]) / (total);
186                   dest[3] = total - ((a0 * a1 + 0xff) >> 8);
187                 }
188               else
189                 {
190                   dest[0] = 0;
191                   dest[1] = 0;
192                   dest[2] = 0;
193                   dest[3] = 0;
194                 }
195             }
196           else
197             {
198               dest[0] = dest[0] + ((a0 * (p[0] - dest[0]) + 0xff) >> 8);
199               dest[1] = dest[1] + ((a0 * (p[1] - dest[1]) + 0xff) >> 8);
200               dest[2] = dest[2] + ((a0 * (p[2] - dest[2]) + 0xff) >> 8);
201
202               if (dest_channels == 4)
203                 dest[3] = 0xff;
204             }
205
206           dest += dest_channels;
207           x += x_step;
208         }
209     }
210 }
211
212 static void
213 pixops_composite_color_nearest (art_u8        *dest_buf,
214                                 int            render_x0,
215                                 int            render_y0,
216                                 int            render_x1,
217                                 int            render_y1,
218                                 int            dest_rowstride,
219                                 int            dest_channels,
220                                 art_boolean    dest_has_alpha,
221                                 art_u8        *src_buf,
222                                 int            src_width,
223                                 int            src_height,
224                                 int            src_rowstride,
225                                 int            src_channels,
226                                 art_boolean    src_has_alpha,
227                                 double         scale_x,
228                                 double         scale_y,
229                                 int            overall_alpha,
230                                 int            check_x,
231                                 int            check_y,
232                                 int            check_size,
233                                 art_u32        color1,
234                                 art_u32        color2)
235 {
236   int i, j;
237   int x;
238   int x_step = (1 << SCALE_SHIFT) / scale_x;
239   int y_step = (1 << SCALE_SHIFT) / scale_y;
240   int r1, g1, b1, r2, g2, b2;
241   int check_shift = get_check_shift (check_size);
242
243   for (i = 0; i < (render_y1 - render_y0); i++)
244     {
245       art_u8 *src = src_buf + (((i + render_y0) * y_step + y_step/2) >> SCALE_SHIFT) * src_rowstride;
246       art_u8 *dest = dest_buf + i * dest_rowstride;
247
248       x = render_x0 * x_step + x_step / 2;
249       
250       if (((i + check_y) >> check_shift) & 1)
251         {
252           r1 = color2 & 0xff0000 >> 16;
253           g1 = color2 & 0xff00 >> 8;
254           b1 = color2 & 0xff;
255
256           r2 = color1 & 0xff0000 >> 16;
257           g2 = color1 & 0xff00 >> 8;
258           b2 = color1 & 0xff;
259         }
260       else
261         {
262           r1 = color1 & 0xff0000 >> 16;
263           g1 = color1 & 0xff00 >> 8;
264           b1 = color1 & 0xff;
265
266           r2 = color2 & 0xff0000 >> 16;
267           g2 = color2 & 0xff00 >> 8;
268           b2 = color2 & 0xff;
269         }
270
271       for (j=0 ; j < (render_x1 - render_x0); j++)
272         {
273           art_u8 *p = src + (x >> SCALE_SHIFT) * src_channels;
274           unsigned int a0;
275
276           if (src_has_alpha)
277             a0 = (p[3] * overall_alpha + 0xff) >> 8;
278           else
279             a0 = overall_alpha;
280
281           if (((j + check_x) >> check_shift) & 1)
282             {
283               dest[0] = r2 + ((a0 * ((int)p[0] - r2) + 0xff) >> 8);
284               dest[1] = g2 + ((a0 * ((int)p[1] - g2) + 0xff) >> 8);
285               dest[2] = b2 + ((a0 * ((int)p[2] - b2) + 0xff) >> 8);
286             }
287           else
288             {
289               dest[0] = r1 + ((a0 * ((int)p[0] - r1) + 0xff) >> 8);
290               dest[1] = g1 + ((a0 * ((int)p[1] - g1) + 0xff) >> 8);
291               dest[2] = b1 + ((a0 * ((int)p[2] - b1) + 0xff) >> 8);
292             }
293           
294           if (dest_channels == 4)
295             dest[3] = 0xff;
296
297           dest += dest_channels;
298           x += x_step;
299         }
300     }
301 }
302
303 static void
304 composite_pixel (art_u8 *dest, int dest_x, int dest_channels, int dest_has_alpha,
305                  int src_has_alpha, int check_size, art_u32 color1, art_u32 color2,
306                  int r, int g, int b, int a)
307 {
308   if (dest_has_alpha)
309     {
310       unsigned int w = (((1 << 16) - a) * dest[3]) >> 8;
311       unsigned int total = a + w;
312
313       if (total)
314         {
315           dest[0] = (r + w * dest[0]) / total;
316           dest[1] = (g + w * dest[1]) / total;
317           dest[2] = (b + w * dest[2]) / total;
318           dest[3] = (r * w) >> 16;
319         }
320       else
321         {
322           dest[0] = 0;
323           dest[1] = 0;
324           dest[2] = 0;
325           dest[3] = 0;
326         }
327     }
328   else
329     {
330       dest[0] = ((0xff0000 - a) * dest[0] + r) >> 24;
331       dest[1] = ((0xff0000 - a) * dest[1] + g) >> 24;
332       dest[2] = ((0xff0000 - a) * dest[2] + b) >> 24;
333     }
334 }
335
336 static art_u8 *
337 composite_line (int *weights, int n_x, int n_y,
338                 art_u8 *dest, int dest_x, art_u8 *dest_end, int dest_channels, int dest_has_alpha,
339                 art_u8 **src, int src_channels, art_boolean src_has_alpha,
340                 int x_init, int x_step, int src_width,
341                 int check_size, art_u32 color1, art_u32 color2)
342 {
343   int x = x_init;
344   int i, j;
345
346   while (dest < dest_end)
347     {
348       int x_scaled = x >> SCALE_SHIFT;
349       unsigned int r = 0, g = 0, b = 0, a = 0;
350       int *pixel_weights;
351       
352       pixel_weights = weights + ((x >> (SCALE_SHIFT - SUBSAMPLE_BITS)) & SUBSAMPLE_MASK) * n_x * n_y;
353
354       for (i=0; i<n_y; i++)
355         {
356           art_u8 *q = src[i] + x_scaled * src_channels;
357           int *line_weights = pixel_weights + n_x * i;
358           
359           for (j=0; j<n_x; j++)
360             {
361               unsigned int ta;
362
363               if (src_has_alpha)
364                 ta = q[3] * line_weights[j];
365               else
366                 ta = 0xff * line_weights[j];
367                   
368               r += ta * q[0];
369               g += ta * q[1];
370               b += ta * q[2];
371               a += ta;
372
373               q += src_channels;
374             }
375         }
376
377       if (dest_has_alpha)
378         {
379           unsigned int w = (((1 << 16) - a) * dest[3]) >> 8;
380           unsigned int total = a + w;
381
382           if (total)
383             {
384               dest[0] = (r + w * dest[0]) / total;
385               dest[1] = (r + w * dest[1]) / total;
386               dest[2] = (r + w * dest[2]) / total;
387               dest[3] = (r * w) >> 16;
388             }
389           else
390             {
391               dest[0] = 0;
392               dest[1] = 0;
393               dest[2] = 0;
394               dest[3] = 0;
395             }
396         }
397       else
398         {
399           dest[0] = ((0xff0000 - a) * dest[0] + r) >> 24;
400           dest[1] = ((0xff0000 - a) * dest[1] + g) >> 24;
401           dest[2] = ((0xff0000 - a) * dest[2] + b) >> 24;
402         }
403       
404       dest += dest_channels;
405       x += x_step;
406     }
407
408   return dest;
409 }
410
411 static art_u8 *
412 composite_line_22_4a4 (int *weights, int n_x, int n_y,
413                        art_u8 *dest, int dest_x, art_u8 *dest_end, int dest_channels, int dest_has_alpha,
414                        art_u8 **src, int src_channels, art_boolean src_has_alpha,
415                        int x_init, int x_step, int src_width,
416                        int check_size, art_u32 color1, art_u32 color2)
417 {
418   int x = x_init;
419   art_u8 *src0 = src[0];
420   art_u8 *src1 = src[1];
421
422   g_return_val_if_fail (src_channels != 3, dest);
423   g_return_val_if_fail (src_has_alpha, dest);
424   
425   while (dest < dest_end)
426     {
427       int x_scaled = x >> SCALE_SHIFT;
428       unsigned int r, g, b, a, ta;
429       int *pixel_weights;
430       art_u8 *q0, *q1;
431       int w1, w2, w3, w4;
432       
433       q0 = src0 + x_scaled * 4;
434       q1 = src1 + x_scaled * 4;
435       
436       pixel_weights = (int *)((char *)weights + ((x >> (SCALE_SHIFT - SUBSAMPLE_BITS - 4)) & (SUBSAMPLE_MASK << 4)));
437       
438       w1 = pixel_weights[0];
439       w2 = pixel_weights[1];
440       w3 = pixel_weights[2];
441       w4 = pixel_weights[3];
442
443       a = w1 * q0[3];
444       r = a * q0[0];
445       g = a * q0[1];
446       b = a * q0[2];
447
448       ta = w2 * q0[7];
449       r += ta * q0[4];
450       g += ta * q0[5];
451       b += ta * q0[6];
452       a += ta;
453
454       ta = w3 * q0[3];
455       r += ta * q0[0];
456       g += ta * q0[1];
457       b += ta * q0[2];
458       a += ta;
459
460       ta += w4 * q1[7];
461       r += ta * q1[4];
462       g += ta * q1[5];
463       b += ta * q1[6];
464       a += ta;
465
466       dest[0] = ((0xff0000 - a) * dest[0] + r) >> 24;
467       dest[1] = ((0xff0000 - a) * dest[1] + g) >> 24;
468       dest[2] = ((0xff0000 - a) * dest[2] + b) >> 24;
469       dest[3] = a >> 16;
470       
471       dest += 4;
472       x += x_step;
473     }
474
475   return dest;
476 }
477
478 #ifdef USE_MMX
479 static art_u8 *
480 composite_line_22_4a4_mmx_stub (int *weights, int n_x, int n_y,
481                                 art_u8 *dest, int dest_x, art_u8 *dest_end, int dest_channels, int dest_has_alpha,
482                                 art_u8 **src, int src_channels, art_boolean src_has_alpha,
483                                 int x_init, int x_step, int src_width,
484                                 int check_size, art_u32 color1, art_u32 color2)
485 {
486   art_u32 mmx_weights[16][8];
487   int j;
488
489   for (j=0; j<16; j++)
490     {
491       mmx_weights[j][0] = 0x00010001 * (weights[4*j] >> 8);
492       mmx_weights[j][1] = 0x00010001 * (weights[4*j] >> 8);
493       mmx_weights[j][2] = 0x00010001 * (weights[4*j + 1] >> 8);
494       mmx_weights[j][3] = 0x00010001 * (weights[4*j + 1] >> 8);
495       mmx_weights[j][4] = 0x00010001 * (weights[4*j + 2] >> 8);
496       mmx_weights[j][5] = 0x00010001 * (weights[4*j + 2] >> 8);
497       mmx_weights[j][6] = 0x00010001 * (weights[4*j + 3] >> 8);
498       mmx_weights[j][7] = 0x00010001 * (weights[4*j + 3] >> 8);
499     }
500
501   return pixops_composite_line_22_4a4_mmx (mmx_weights, dest, src[0], src[1], x_step, dest_end, x_init);
502 }
503 #endif /* USE_MMX */
504
505 static void
506 composite_pixel_color (art_u8 *dest, int dest_x, int dest_channels, int dest_has_alpha,
507                        int src_has_alpha, int check_size, art_u32 color1, art_u32 color2,
508                        int r, int g, int b, int a)
509 {
510   int dest_r, dest_g, dest_b;
511   int check_shift = get_check_shift (check_size);
512
513   if ((dest_x >> check_shift) & 1)
514     {
515       dest_r = color2 & 0xff0000 >> 16;
516       dest_g = color2 & 0xff00 >> 8;
517       dest_b = color2 & 0xff;
518     }
519   else
520     {
521       dest_r = color1 & 0xff0000 >> 16;
522       dest_g = color1 & 0xff00 >> 8;
523       dest_b = color1 & 0xff;
524     }
525
526   dest[0] = ((0xff0000 - a) * dest_r + r) >> 24;
527   dest[1] = ((0xff0000 - a) * dest_g + g) >> 24;
528   dest[2] = ((0xff0000 - a) * dest_b + b) >> 24;
529
530   if (dest_has_alpha)
531     dest[3] = 0xff;
532   else if (dest_channels == 4)
533     dest[3] = a >> 16;
534 }
535
536 static art_u8 *
537 composite_line_color (int *weights, int n_x, int n_y,
538                       art_u8 *dest, int dest_x, art_u8 *dest_end, int dest_channels, int dest_has_alpha,
539                       art_u8 **src, int src_channels, art_boolean src_has_alpha,
540                       int x_init, int x_step, int src_width,
541                       int check_size, art_u32 color1, art_u32 color2)
542 {
543   int x = x_init;
544   int i, j;
545   int check_shift = get_check_shift (check_size);
546   int dest_r1, dest_g1, dest_b1;
547   int dest_r2, dest_g2, dest_b2;
548
549   g_return_val_if_fail (check_size != 0, dest);
550
551   dest_r1 = color1 & 0xff0000 >> 16;
552   dest_g1 = color1 & 0xff00 >> 8;
553   dest_b1 = color1 & 0xff;
554
555   dest_r2 = color2 & 0xff0000 >> 16;
556   dest_g2 = color2 & 0xff00 >> 8;
557   dest_b2 = color2 & 0xff;
558
559   while (dest < dest_end)
560     {
561       int x_scaled = x >> SCALE_SHIFT;
562       unsigned int r = 0, g = 0, b = 0, a = 0;
563       int *pixel_weights;
564       
565       pixel_weights = weights + ((x >> (SCALE_SHIFT - SUBSAMPLE_BITS)) & SUBSAMPLE_MASK) * n_x * n_y;
566
567       for (i=0; i<n_y; i++)
568         {
569           art_u8 *q = src[i] + x_scaled * src_channels;
570           int *line_weights = pixel_weights + n_x * i;
571           
572           for (j=0; j<n_x; j++)
573             {
574               unsigned int ta;
575               
576               if (src_has_alpha)
577                 ta = q[3] * line_weights[j];
578               else
579                 ta = 0xff * line_weights[j];
580                   
581               r += ta * q[0];
582               g += ta * q[1];
583               b += ta * q[2];
584               a += ta;
585
586               q += src_channels;
587             }
588         }
589
590       if ((dest_x >> check_shift) & 1)
591         {
592           dest[0] = ((0xff0000 - a) * dest_r2 + r) >> 24;
593           dest[1] = ((0xff0000 - a) * dest_g2 + g) >> 24;
594           dest[2] = ((0xff0000 - a) * dest_b2 + b) >> 24;
595         }
596       else
597         {
598           dest[0] = ((0xff0000 - a) * dest_r1 + r) >> 24;
599           dest[1] = ((0xff0000 - a) * dest_g1 + g) >> 24;
600           dest[2] = ((0xff0000 - a) * dest_b1 + b) >> 24;
601         }
602
603       if (dest_has_alpha)
604         dest[3] = 0xff;
605       else if (dest_channels == 4)
606         dest[3] = a >> 16;
607         
608       dest += dest_channels;
609       x += x_step;
610       dest_x++;
611     }
612
613   return dest;
614 }
615
616 #ifdef USE_MMX
617 static art_u8 *
618 composite_line_color_22_4a4_mmx_stub (int *weights, int n_x, int n_y,
619                                       art_u8 *dest, int dest_x, art_u8 *dest_end, int dest_channels, int dest_has_alpha,
620                                       art_u8 **src, int src_channels, art_boolean src_has_alpha,
621                                       int x_init, int x_step, int src_width,
622                                       int check_size, art_u32 color1, art_u32 color2)
623 {
624   art_u32 mmx_weights[16][8];
625   int check_shift = get_check_shift (check_size);
626   int colors[4];
627   int j;
628
629   for (j=0; j<16; j++)
630     {
631       mmx_weights[j][0] = 0x00010001 * (weights[4*j] >> 8);
632       mmx_weights[j][1] = 0x00010001 * (weights[4*j] >> 8);
633       mmx_weights[j][2] = 0x00010001 * (weights[4*j + 1] >> 8);
634       mmx_weights[j][3] = 0x00010001 * (weights[4*j + 1] >> 8);
635       mmx_weights[j][4] = 0x00010001 * (weights[4*j + 2] >> 8);
636       mmx_weights[j][5] = 0x00010001 * (weights[4*j + 2] >> 8);
637       mmx_weights[j][6] = 0x00010001 * (weights[4*j + 3] >> 8);
638       mmx_weights[j][7] = 0x00010001 * (weights[4*j + 3] >> 8);
639     }
640
641   colors[0] = (color1 & 0xff00) << 8 | (color1 & 0xff);
642   colors[1] = (color1 & 0xff0000) >> 16;
643   colors[2] = (color2 & 0xff00) << 8 | (color2 & 0xff);
644   colors[3] = (color2 & 0xff0000) >> 16;
645
646   return pixops_composite_line_color_22_4a4_mmx (mmx_weights, dest, src[0], src[1], x_step, dest_end, x_init,
647                                                  dest_x, check_shift, colors);
648 }
649 #endif /* USE_MMX */
650
651 static void
652 scale_pixel (art_u8 *dest, int dest_x, int dest_channels, int dest_has_alpha,
653              int src_has_alpha, int check_size, art_u32 color1, art_u32 color2,
654              int r, int g, int b, int a)
655 {
656   if (src_has_alpha)
657     {
658       if (a)
659         {
660           dest[0] = r / a;
661           dest[1] = g / a;
662           dest[2] = b / a;
663           dest[3] = a >> 16;
664         }
665       else
666         {
667           dest[0] = 0;
668           dest[1] = 0;
669           dest[2] = 0;
670           dest[3] = 0;
671         }
672     }
673   else
674     {
675       dest[0] = r >> 16;
676       dest[1] = g >> 16;
677       dest[2] = b >> 16;
678       
679       if (dest_has_alpha)
680         dest[3] = 0xff;
681     }
682 }
683
684 static art_u8 *
685 scale_line (int *weights, int n_x, int n_y,
686             art_u8 *dest, int dest_x, art_u8 *dest_end, int dest_channels, int dest_has_alpha,
687             art_u8 **src, int src_channels, art_boolean src_has_alpha,
688             int x_init, int x_step, int src_width,
689             int check_size, art_u32 color1, art_u32 color2)
690 {
691   int x = x_init;
692   int i, j;
693
694   while (dest < dest_end)
695     {
696       int x_scaled = x >> SCALE_SHIFT;
697       int *pixel_weights;
698
699       pixel_weights = weights + ((x >> (SCALE_SHIFT - SUBSAMPLE_BITS)) & SUBSAMPLE_MASK) * n_x * n_y;
700
701       if (src_has_alpha)
702         {
703           unsigned int r = 0, g = 0, b = 0, a = 0;
704           for (i=0; i<n_y; i++)
705             {
706               art_u8 *q = src[i] + x_scaled * src_channels;
707               int *line_weights  = pixel_weights + n_x * i;
708               
709               for (j=0; j<n_x; j++)
710                 {
711                   unsigned int ta;
712                   
713                   ta = q[3] * line_weights[j];
714                   r = ta * q[0];
715                   g = ta * q[1];
716                   b = ta * q[2];
717                   a += ta;
718                   
719                   q += src_channels;
720                 }
721             }
722
723           if (a)
724             {
725               dest[0] = r / a;
726               dest[1] = g / a;
727               dest[2] = b / a;
728               dest[3] = a >> 16;
729             }
730           else
731             {
732               dest[0] = 0;
733               dest[1] = 0;
734               dest[2] = 0;
735               dest[3] = 0;
736             }
737         }
738       else
739         {
740           unsigned int r = 0, g = 0, b = 0;
741           for (i=0; i<n_y; i++)
742             {
743               art_u8 *q = src[i] + x_scaled * src_channels;
744               int *line_weights  = pixel_weights + n_x * i;
745               
746               for (j=0; j<n_x; j++)
747                 {
748                   unsigned int ta = line_weights[j];
749                   
750                   r += ta * q[0];
751                   g += ta * q[1];
752                   b += ta * q[2];
753
754                   q += src_channels;
755                 }
756             }
757
758           dest[0] = r >> 16;
759           dest[1] = g >> 16;
760           dest[2] = b >> 16;
761           
762           if (dest_has_alpha)
763             dest[3] = 0xff;
764         }
765
766       dest += dest_channels;
767       
768       x += x_step;
769     }
770
771   return dest;
772 }
773
774 #ifdef USE_MMX 
775 static art_u8 *
776 scale_line_22_33_mmx_stub (int *weights, int n_x, int n_y,
777                            art_u8 *dest, int dest_x, art_u8 *dest_end, int dest_channels, int dest_has_alpha,
778                            art_u8 **src, int src_channels, art_boolean src_has_alpha,
779                            int x_init, int x_step, int src_width,
780                            int check_size, art_u32 color1, art_u32 color2)
781 {
782   art_u32 mmx_weights[16][8];
783   int j;
784
785   for (j=0; j<16; j++)
786     {
787       mmx_weights[j][0] = 0x00010001 * (weights[4*j] >> 8);
788       mmx_weights[j][1] = 0x00010001 * (weights[4*j] >> 8);
789       mmx_weights[j][2] = 0x00010001 * (weights[4*j + 1] >> 8);
790       mmx_weights[j][3] = 0x00010001 * (weights[4*j + 1] >> 8);
791       mmx_weights[j][4] = 0x00010001 * (weights[4*j + 2] >> 8);
792       mmx_weights[j][5] = 0x00010001 * (weights[4*j + 2] >> 8);
793       mmx_weights[j][6] = 0x00010001 * (weights[4*j + 3] >> 8);
794       mmx_weights[j][7] = 0x00010001 * (weights[4*j + 3] >> 8);
795     }
796
797   return pixops_scale_line_22_33_mmx (mmx_weights, dest, src[0], src[1], x_step, dest_end, x_init);
798 }
799 #endif /* USE_MMX */
800
801 static art_u8 *
802 scale_line_22_33 (int *weights, int n_x, int n_y,
803                   art_u8 *dest, art_u8 *dest_end, int dest_channels, int dest_has_alpha,
804                   art_u8 **src, int src_channels, art_boolean src_has_alpha,
805                   int x_init, int x_step, int src_width,
806                   int check_size, art_u32 color1, art_u32 color2)
807 {
808   int x = x_init;
809   art_u8 *src0 = src[0];
810   art_u8 *src1 = src[1];
811   
812   while (dest < dest_end)
813     {
814       unsigned int r, g, b;
815       int x_scaled = x >> SCALE_SHIFT;
816       int *pixel_weights;
817       art_u8 *q0, *q1;
818       int w1, w2, w3, w4;
819
820       q0 = src0 + x_scaled * 3;
821       q1 = src1 + x_scaled * 3;
822       
823       pixel_weights = (int *)((char *)weights + ((x >> (SCALE_SHIFT - SUBSAMPLE_BITS - 4)) & (SUBSAMPLE_MASK << 4)));
824       
825       w1 = pixel_weights[0];
826       w2 = pixel_weights[1];
827       w3 = pixel_weights[2];
828       w4 = pixel_weights[3];
829
830       r = w1 * q0[0];
831       g = w1 * q0[1];
832       b = w1 * q0[2];
833
834       r += w2 * q0[3];
835       g += w2 * q0[4];
836       b += w2 * q0[5];
837
838       r += w3 * q1[0];
839       g += w3 * q1[1];
840       b += w3 * q1[2];
841
842       r += w4 * q1[4];
843       g += w4 * q1[5];
844       b += w4 * q1[6];
845
846       dest[0] = r >> 16;
847       dest[1] = g >> 16;
848       dest[2] = b >> 16;
849       
850       dest += 3;
851       
852       x += x_step;
853     }
854   
855   return dest;
856 }
857
858 static void
859 process_pixel (int *weights, int n_x, int n_y,
860                art_u8 *dest, int dest_x, int dest_channels, int dest_has_alpha,
861                art_u8 **src, int src_channels, art_boolean src_has_alpha,
862                int x_start, int src_width,
863                int check_size, art_u32 color1, art_u32 color2,
864                PixopsPixelFunc pixel_func)
865 {
866   unsigned int r = 0, g = 0, b = 0, a = 0;
867   int i, j;
868   
869   for (i=0; i<n_y; i++)
870     {
871       int *line_weights  = weights + n_x * i;
872
873       for (j=0; j<n_x; j++)
874         {
875           unsigned int ta;
876           art_u8 *q;
877
878           if (x_start + j < 0)
879             q = src[i];
880           else if (x_start + j < src_width)
881             q = src[i] + (x_start + j) * src_channels;
882           else
883             q = src[i] + (src_width - 1) * src_channels;
884
885           if (src_has_alpha)
886             ta = q[3] * line_weights[j];
887           else
888             ta = 0xff * line_weights[j];
889
890           r += ta * q[0];
891           g += ta * q[1];
892           b += ta * q[2];
893           a += ta;
894         }
895     }
896
897   (*pixel_func) (dest, dest_x, dest_channels, dest_has_alpha, src_has_alpha, check_size, color1, color2, r, g, b, a);
898 }
899
900 static void
901 pixops_process (art_u8         *dest_buf,
902                 int             render_x0,
903                 int             render_y0,
904                 int             render_x1,
905                 int             render_y1,
906                 int             dest_rowstride,
907                 int             dest_channels,
908                 art_boolean     dest_has_alpha,
909                 art_u8         *src_buf,
910                 int             src_width,
911                 int             src_height,
912                 int             src_rowstride,
913                 int             src_channels,
914                 art_boolean     src_has_alpha,
915                 double          scale_x,
916                 double          scale_y,
917                 int             check_x,
918                 int             check_y,
919                 int             check_size,
920                 art_u32         color1,
921                 art_u32         color2,
922                 PixopsFilter   *filter,
923                 PixopsLineFunc  line_func,
924                 PixopsPixelFunc pixel_func)
925 {
926   int i, j;
927   int x, y;
928   art_u8 **line_bufs = g_new (art_u8 *, filter->n_y);
929
930   int x_step = (1 << SCALE_SHIFT) / scale_x;
931   int y_step = (1 << SCALE_SHIFT) / scale_y;
932
933   int dest_x;
934   int scaled_x_offset = floor (filter->x_offset * (1 << SCALE_SHIFT));
935
936   int run_end_index = (((src_width - filter->n_x + 1) << SCALE_SHIFT) - scaled_x_offset - 1) / x_step + 1 - render_x0;
937   int check_shift = check_size ? get_check_shift (check_size) : 0;
938
939   y = render_y0 * y_step + floor (filter->y_offset * (1 << SCALE_SHIFT));
940   for (i = 0; i < (render_y1 - render_y0); i++)
941     {
942       int y_start = y >> SCALE_SHIFT;
943       int x_start;
944       int *run_weights = filter->weights + ((y >> (SCALE_SHIFT - SUBSAMPLE_BITS)) & SUBSAMPLE_MASK) * filter->n_x * filter->n_y * SUBSAMPLE;
945       art_u8 *new_outbuf;
946       art_u32 tcolor1, tcolor2;
947       
948       art_u8 *outbuf = dest_buf + dest_rowstride * i;
949       art_u8 *outbuf_end = outbuf + dest_channels * (render_x1 - render_x0);
950
951       if (((i + check_y) >> check_shift) & 1)
952         {
953           tcolor1 = color2;
954           tcolor2 = color1;
955         }
956       else
957         {
958           tcolor1 = color1;
959           tcolor2 = color2;
960         }
961
962       for (j=0; j<filter->n_y; j++)
963         {
964           if (y_start <  0)
965             line_bufs[j] = src_buf;
966           else if (y_start < src_height)
967             line_bufs[j] = src_buf + src_rowstride * y_start;
968           else
969             line_bufs[j] = src_buf + src_rowstride * (src_height - 1);
970
971           y_start++;
972         }
973
974       dest_x = check_x;
975       x = render_x0 * x_step + scaled_x_offset;
976       x_start = x >> SCALE_SHIFT;
977
978       while (x_start < 0 && outbuf < outbuf_end)
979         {
980           process_pixel (run_weights + ((x >> (SCALE_SHIFT - SUBSAMPLE_BITS)) & SUBSAMPLE_MASK) * (filter->n_x * filter->n_y), filter->n_x, filter->n_y,
981                          outbuf, dest_x, dest_channels, dest_has_alpha,
982                          line_bufs, src_channels, src_has_alpha,
983                          x >> SCALE_SHIFT, src_width,
984                          check_size, tcolor1, tcolor2, pixel_func);
985           
986           x += x_step;
987           x_start = x >> SCALE_SHIFT;
988           dest_x++;
989           outbuf += dest_channels;
990         }
991
992       new_outbuf = (*line_func)(run_weights, filter->n_x, filter->n_y,
993                                 outbuf, dest_x,
994                                 MIN (outbuf_end, dest_buf + dest_rowstride * i + run_end_index * dest_channels),
995                                 dest_channels, dest_has_alpha,
996                                 line_bufs, src_channels, src_has_alpha,
997                                 x, x_step, src_width, check_size, tcolor1, tcolor2);
998
999       dest_x += (new_outbuf - outbuf) / dest_channels;
1000
1001       x = dest_x * x_step + scaled_x_offset;
1002       outbuf = new_outbuf;
1003
1004       while (outbuf < outbuf_end)
1005         {
1006           process_pixel (run_weights + ((x >> (SCALE_SHIFT - SUBSAMPLE_BITS)) & SUBSAMPLE_MASK) * (filter->n_x * filter->n_y), filter->n_x, filter->n_y,
1007                          outbuf, dest_x, dest_channels, dest_has_alpha,
1008                          line_bufs, src_channels, src_has_alpha,
1009                          x >> SCALE_SHIFT, src_width,
1010                          check_size, tcolor1, tcolor2, pixel_func);
1011           
1012           x += x_step;
1013           dest_x++;
1014           outbuf += dest_channels;
1015         }
1016
1017       y += y_step;
1018     }
1019
1020   g_free (line_bufs);
1021 }
1022
1023 static void
1024 tile_make_weights (PixopsFilter *filter, double x_scale, double y_scale, double overall_alpha)
1025 {
1026   int i_offset, j_offset;
1027
1028   int n_x = ceil(1/x_scale + 1);
1029   int n_y = ceil(1/y_scale + 1);
1030
1031   filter->x_offset = 0;
1032   filter->y_offset = 0;
1033   filter->n_x = n_x;
1034   filter->n_y = n_y;
1035   filter->weights = g_new (int, SUBSAMPLE * SUBSAMPLE * n_x * n_y);
1036
1037   for (i_offset=0; i_offset<SUBSAMPLE; i_offset++)
1038     for (j_offset=0; j_offset<SUBSAMPLE; j_offset++)
1039       {
1040         int *pixel_weights = filter->weights + ((i_offset*SUBSAMPLE) + j_offset) * n_x * n_y;
1041         double x = (double)j_offset / 16;
1042         double y = (double)i_offset / 16;
1043         int i,j;
1044           
1045         for (i = 0; i < n_y; i++)
1046           {
1047             double tw, th;
1048                 
1049             if (i < y)
1050               {
1051                 if (i + 1 > y)
1052                   th = MIN(i+1, y + 1/y_scale) - y;
1053                 else
1054                   th = 0;
1055               }
1056             else
1057               {
1058                 if (y + 1/y_scale > i)
1059                   th = MIN(i+1, y + 1/y_scale) - i;
1060                 else
1061                   th = 0;
1062               }
1063                 
1064             for (j = 0; j < n_x; j++)
1065               {
1066                 if (j < x)
1067                   {
1068                     if (j + 1 > x)
1069                       tw = MIN(j+1, x + 1/x_scale) - x;
1070                     else
1071                       tw = 0;
1072                   }
1073                 else
1074                   {
1075                     if (x + 1/x_scale > j)
1076                       tw = MIN(j+1, x + 1/x_scale) - j;
1077                     else
1078                       tw = 0;
1079                   }
1080
1081                 *(pixel_weights + n_x * i + j) = 65536 * tw * x_scale * th * y_scale * overall_alpha;
1082               }
1083           }
1084       }
1085 }
1086
1087 static void
1088 bilinear_make_fast_weights (PixopsFilter *filter, double x_scale, double y_scale, double overall_alpha)
1089 {
1090   int i_offset, j_offset;
1091   double *x_weights, *y_weights;
1092   int n_x, n_y;
1093
1094   if (x_scale > 1.0)            /* Bilinear */
1095     {
1096       n_x = 2;
1097       filter->x_offset = 0.5 * (1/x_scale - 1);
1098     }
1099   else                          /* Tile */
1100     {
1101       n_x = ceil(1.0 + 1.0/x_scale);
1102       filter->x_offset = 0.0;
1103     }
1104
1105   if (y_scale > 1.0)            /* Bilinear */
1106     {
1107       n_y = 2;
1108       filter->y_offset = 0.5 * (1/y_scale - 1);
1109     }
1110   else                          /* Tile */
1111     {
1112       n_y = ceil(1.0 + 1.0/y_scale);
1113       filter->y_offset = 0.0;
1114     }
1115
1116   filter->n_y = n_y;
1117   filter->n_x = n_x;
1118   filter->weights = g_new (int, SUBSAMPLE * SUBSAMPLE * n_x * n_y);
1119
1120   x_weights = g_new (double, n_x);
1121   y_weights = g_new (double, n_y);
1122
1123   for (i_offset=0; i_offset<SUBSAMPLE; i_offset++)
1124     for (j_offset=0; j_offset<SUBSAMPLE; j_offset++)
1125       {
1126         int *pixel_weights = filter->weights + ((i_offset*SUBSAMPLE) + j_offset) * n_x * n_y;
1127         double x = (double)j_offset / 16;
1128         double y = (double)i_offset / 16;
1129         int i,j;
1130
1131         if (x_scale > 1.0)      /* Bilinear */
1132           {
1133             for (i = 0; i < n_x; i++)
1134               {
1135                 x_weights[i] = ((i == 0) ? (1 - x) : x) / x_scale;
1136               }
1137           }
1138         else                    /* Tile */
1139           {
1140             for (i = 0; i < n_x; i++)
1141               {
1142                 if (i < x)
1143                   {
1144                     if (i + 1 > x)
1145                       x_weights[i] = MIN(i+1, x + 1/x_scale) - x;
1146                     else
1147                       x_weights[i] = 0;
1148                   }
1149                 else
1150                   {
1151                     if (x + 1/x_scale > i)
1152                       x_weights[i] = MIN(i+1, x + 1/x_scale) - i;
1153                     else
1154                       x_weights[i] = 0;
1155                   }
1156               }
1157           }
1158
1159         if (y_scale > 1.0)      /* Bilinear */
1160           {
1161             for (i = 0; i < n_y; i++)
1162               {
1163                 y_weights[i] = ((i == 0) ? (1 - y) : y) / y_scale;
1164               }
1165           }
1166         else                    /* Tile */
1167           {
1168             for (i = 0; i < n_y; i++)
1169               {
1170                 if (i < y)
1171                   {
1172                     if (i + 1 > y)
1173                       y_weights[i] = MIN(i+1, y + 1/y_scale) - y;
1174                     else
1175                       y_weights[i] = 0;
1176                   }
1177                 else
1178                   {
1179                     if (y + 1/y_scale > i)
1180                       y_weights[i] = MIN(i+1, y + 1/y_scale) - i;
1181                     else
1182                       y_weights[i] = 0;
1183                   }
1184               }
1185           }
1186
1187         for (i = 0; i < n_y; i++)
1188           for (j = 0; j < n_x; j++)
1189             *(pixel_weights + n_x * i + j) = 65536 * x_weights[j] * x_scale * y_weights[i] * y_scale * overall_alpha;
1190       }
1191
1192   g_free (x_weights);
1193   g_free (y_weights);
1194 }
1195
1196 static double
1197 bilinear_quadrant (double bx0, double bx1, double by0, double by1)
1198 {
1199   double ax0, ax1, ay0, ay1;
1200   double x0, x1, y0, y1;
1201
1202   ax0 = 0.;
1203   ax1 = 1.;
1204   ay0 = 0.;
1205   ay1 = 1.;
1206
1207   if (ax0 < bx0)
1208     {
1209       if (ax1 > bx0)
1210         {
1211           x0 = bx0;
1212           x1 = MIN (ax1, bx1);
1213         }
1214       else
1215         return 0;
1216     }
1217   else
1218     {
1219       if (bx1 > ax0)
1220         {
1221           x0 = ax0;
1222           x1 = MIN (ax1, bx1);
1223         }
1224       else
1225         return 0;
1226     }
1227
1228   if (ay0 < by0)
1229     {
1230       if (ay1 > by0)
1231         {
1232           y0 = by0;
1233           y1 = MIN (ay1, by1);
1234         }
1235       else
1236         return 0;
1237     }
1238   else
1239     {
1240       if (by1 > ay0)
1241         {
1242           y0 = ay0;
1243           y1 = MIN (ay1, by1);
1244         }
1245       else
1246         return 0;
1247     }
1248
1249   return 0.25 * (x1*x1 - x0*x0) * (y1*y1 - y0*y0);
1250 }
1251
1252 static void
1253 bilinear_make_weights (PixopsFilter *filter, double x_scale, double y_scale, double overall_alpha)
1254 {
1255   int i_offset, j_offset;
1256
1257   int n_x = ceil(1/x_scale + 2.0);
1258   int n_y = ceil(1/y_scale + 2.0);
1259
1260   filter->x_offset = -1.0;
1261   filter->y_offset = -1.0;
1262   filter->n_x = n_x;
1263   filter->n_y = n_y;
1264   
1265   filter->weights = g_new (int, SUBSAMPLE * SUBSAMPLE * n_x * n_y);
1266
1267   for (i_offset=0; i_offset<SUBSAMPLE; i_offset++)
1268     for (j_offset=0; j_offset<SUBSAMPLE; j_offset++)
1269       {
1270         int *pixel_weights = filter->weights + ((i_offset*SUBSAMPLE) + j_offset) * n_x * n_y;
1271         double x = (double)j_offset / 16;
1272         double y = (double)i_offset / 16;
1273         int i,j;
1274           
1275         for (i = 0; i < n_y; i++)
1276           for (j = 0; j < n_x; j++)
1277             {
1278               double w;
1279
1280               w = bilinear_quadrant  (0.5 + j - (x + 1 / x_scale), 0.5 + j - x, 0.5 + i - (y + 1 / y_scale), 0.5 + i - y);
1281               w += bilinear_quadrant (1.5 + x - j, 1.5 + (x + 1 / x_scale) - j, 0.5 + i - (y + 1 / y_scale), 0.5 + i - y);
1282               w += bilinear_quadrant (0.5 + j - (x + 1 / x_scale), 0.5 + j - x, 1.5 + y - i, 1.5 + (y + 1 / y_scale) - i);
1283               w += bilinear_quadrant (1.5 + x - j, 1.5 + (x + 1 / x_scale) - j, 1.5 + y - i, 1.5 + (y + 1 / y_scale) - i);
1284               
1285               *(pixel_weights + n_x * i + j) = 65536 * w * x_scale * y_scale * overall_alpha;
1286             }
1287       }
1288 }
1289
1290 void
1291 pixops_composite_color (art_u8         *dest_buf,
1292                         int             render_x0,
1293                         int             render_y0,
1294                         int             render_x1,
1295                         int             render_y1,
1296                         int             dest_rowstride,
1297                         int             dest_channels,
1298                         art_boolean     dest_has_alpha,
1299                         art_u8         *src_buf,
1300                         int             src_width,
1301                         int             src_height,
1302                         int             src_rowstride,
1303                         int             src_channels,
1304                         art_boolean     src_has_alpha,
1305                         double          scale_x,
1306                         double          scale_y,
1307                         ArtFilterLevel  filter_level,
1308                         int             overall_alpha,
1309                         int             check_x,
1310                         int             check_y,
1311                         int             check_size,
1312                         art_u32         color1,
1313                         art_u32         color2)
1314 {
1315   PixopsFilter filter;
1316   PixopsLineFunc line_func;
1317   
1318 #ifdef USE_MMX
1319   art_boolean found_mmx = pixops_have_mmx();
1320 #endif
1321
1322   g_return_if_fail (!(dest_channels == 3 && dest_has_alpha));
1323   g_return_if_fail (!(src_channels == 3 && src_has_alpha));
1324
1325   if (scale_x == 0 || scale_y == 0)
1326     return;
1327
1328   if (!src_has_alpha && overall_alpha == 255)
1329     pixops_scale (dest_buf, render_x0, render_y0, render_x1, render_y1,
1330                   dest_rowstride, dest_channels, dest_has_alpha,
1331                   src_buf, src_width, src_height, src_rowstride, src_channels,
1332                   src_has_alpha, scale_x, scale_y, filter_level);
1333
1334   switch (filter_level)
1335     {
1336     case ART_FILTER_NEAREST:
1337       pixops_composite_color_nearest (dest_buf, render_x0, render_y0, render_x1, render_y1,
1338                                       dest_rowstride, dest_channels, dest_has_alpha,
1339                                       src_buf, src_width, src_height, src_rowstride, src_channels, src_has_alpha,
1340                                       scale_x, scale_y, overall_alpha,
1341                                       check_x, check_y, check_size, color1, color2);
1342       return;
1343
1344     case ART_FILTER_TILES:
1345       tile_make_weights (&filter, scale_x, scale_y, overall_alpha / 255.);
1346       break;
1347       
1348     case ART_FILTER_BILINEAR:
1349       bilinear_make_fast_weights (&filter, scale_x, scale_y, overall_alpha / 255.);
1350       break;
1351       
1352     case ART_FILTER_HYPER:
1353       bilinear_make_weights (&filter, scale_x, scale_y, overall_alpha / 255.);
1354       break;
1355     }
1356
1357 #ifdef USE_MMX
1358   if (filter.n_x == 2 && filter.n_y == 2 &&
1359       dest_channels == 4 && src_channels == 4 && src_has_alpha && !dest_has_alpha && found_mmx)
1360     line_func = composite_line_color_22_4a4_mmx_stub;
1361   else
1362 #endif    
1363     line_func = composite_line_color;
1364   
1365   pixops_process (dest_buf, render_x0, render_y0, render_x1, render_y1,
1366                   dest_rowstride, dest_channels, dest_has_alpha,
1367                   src_buf, src_width, src_height, src_rowstride, src_channels,
1368                   src_has_alpha, scale_x, scale_y, check_x, check_y, check_size, color1, color2,
1369                   &filter, line_func, composite_pixel_color);
1370
1371   g_free (filter.weights);
1372 }
1373
1374 void
1375 pixops_composite (art_u8        *dest_buf,
1376                   int            render_x0,
1377                   int            render_y0,
1378                   int            render_x1,
1379                   int            render_y1,
1380                   int            dest_rowstride,
1381                   int            dest_channels,
1382                   art_boolean    dest_has_alpha,
1383                   art_u8        *src_buf,
1384                   int            src_width,
1385                   int            src_height,
1386                   int            src_rowstride,
1387                   int            src_channels,
1388                   art_boolean    src_has_alpha,
1389                   double         scale_x,
1390                   double         scale_y,
1391                   ArtFilterLevel filter_level,
1392                   int            overall_alpha)
1393 {
1394   PixopsFilter filter;
1395   PixopsLineFunc line_func;
1396   
1397 #ifdef USE_MMX
1398   art_boolean found_mmx = pixops_have_mmx();
1399 #endif
1400
1401   g_return_if_fail (!(dest_channels == 3 && dest_has_alpha));
1402   g_return_if_fail (!(src_channels == 3 && src_has_alpha));
1403
1404   if (scale_x == 0 || scale_y == 0)
1405     return;
1406
1407   if (!src_has_alpha && overall_alpha == 255)
1408     pixops_scale (dest_buf, render_x0, render_y0, render_x1, render_y1,
1409                   dest_rowstride, dest_channels, dest_has_alpha,
1410                   src_buf, src_width, src_height, src_rowstride, src_channels,
1411                   src_has_alpha, scale_x, scale_y, filter_level);
1412
1413   switch (filter_level)
1414     {
1415     case ART_FILTER_NEAREST:
1416       pixops_composite_nearest (dest_buf, render_x0, render_y0, render_x1, render_y1,
1417                                 dest_rowstride, dest_channels, dest_has_alpha,
1418                                 src_buf, src_width, src_height, src_rowstride, src_channels,
1419                                 src_has_alpha, scale_x, scale_y, overall_alpha);
1420       return;
1421
1422     case ART_FILTER_TILES:
1423       tile_make_weights (&filter, scale_x, scale_y, overall_alpha / 255.);
1424       break;
1425       
1426     case ART_FILTER_BILINEAR:
1427       bilinear_make_fast_weights (&filter, scale_x, scale_y, overall_alpha / 255.);
1428       break;
1429       
1430     case ART_FILTER_HYPER:
1431       bilinear_make_weights (&filter, scale_x, scale_y, overall_alpha / 255.);
1432       break;
1433     }
1434
1435   if (filter.n_x == 2 && filter.n_y == 2 &&
1436       dest_channels == 4 && src_channels == 4 && src_has_alpha && !dest_has_alpha)
1437     {
1438 #ifdef USE_MMX
1439       if (found_mmx)
1440         line_func = composite_line_22_4a4_mmx_stub;
1441       else
1442 #endif  
1443         line_func = composite_line_22_4a4;
1444     }
1445   else
1446     line_func = composite_line;
1447   
1448   pixops_process (dest_buf, render_x0, render_y0, render_x1, render_y1,
1449                   dest_rowstride, dest_channels, dest_has_alpha,
1450                   src_buf, src_width, src_height, src_rowstride, src_channels,
1451                   src_has_alpha, scale_x, scale_y, 0, 0, 0, 0, 0, 
1452                   &filter, line_func, composite_pixel);
1453
1454   g_free (filter.weights);
1455 }
1456
1457 void
1458 pixops_scale (art_u8        *dest_buf,
1459               int            render_x0,
1460               int            render_y0,
1461               int            render_x1,
1462               int            render_y1,
1463               int            dest_rowstride,
1464               int            dest_channels,
1465               art_boolean    dest_has_alpha,
1466               art_u8        *src_buf,
1467               int            src_width,
1468               int            src_height,
1469               int            src_rowstride,
1470               int            src_channels,
1471               art_boolean    src_has_alpha,
1472               double         scale_x,
1473               double         scale_y,
1474               ArtFilterLevel filter_level)
1475 {
1476   PixopsFilter filter;
1477   PixopsLineFunc line_func;
1478
1479 #ifdef USE_MMX
1480   art_boolean found_mmx = pixops_have_mmx();
1481 #endif
1482
1483   g_return_if_fail (!(dest_channels == 3 && dest_has_alpha));
1484   g_return_if_fail (!(src_channels == 3 && src_has_alpha));
1485   g_return_if_fail (!(src_has_alpha && !dest_has_alpha));
1486
1487   if (scale_x == 0 || scale_y == 0)
1488     return;
1489
1490   switch (filter_level)
1491     {
1492     case ART_FILTER_NEAREST:
1493       pixops_scale_nearest (dest_buf, render_x0, render_y0, render_x1, render_y1,
1494                             dest_rowstride, dest_channels, dest_has_alpha,
1495                             src_buf, src_width, src_height, src_rowstride, src_channels, src_has_alpha,
1496                             scale_x, scale_y);
1497       return;
1498
1499     case ART_FILTER_TILES:
1500       tile_make_weights (&filter, scale_x, scale_y, 1.0);
1501       break;
1502       
1503     case ART_FILTER_BILINEAR:
1504       bilinear_make_fast_weights (&filter, scale_x, scale_y, 1.0);
1505       break;
1506       
1507     case ART_FILTER_HYPER:
1508       bilinear_make_weights (&filter, scale_x, scale_y, 1.0);
1509       break;
1510     }
1511
1512 #ifdef USE_MMX
1513   if (filter.n_x == 2 && filter.n_y == 2 &&
1514       found_mmx && dest_channels == 3 && src_channels == 3)
1515     line_func = scale_line_22_33_mmx_stub;
1516   else
1517 #endif    
1518     line_func = scale_line;
1519   
1520   pixops_process (dest_buf, render_x0, render_y0, render_x1, render_y1,
1521                   dest_rowstride, dest_channels, dest_has_alpha,
1522                   src_buf, src_width, src_height, src_rowstride, src_channels,
1523                   src_has_alpha, scale_x, scale_y, 0, 0, 0, 0, 0,
1524                   &filter, line_func, scale_pixel);
1525
1526   g_free (filter.weights);
1527 }
1528