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