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