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