]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/pixops/pixops.c
fix some shell typos
[~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 static guchar *
822 scale_line_22_33 (int *weights, int n_x, int n_y,
823                   guchar *dest, guchar *dest_end, int dest_channels, int dest_has_alpha,
824                   guchar **src, int src_channels, gboolean src_has_alpha,
825                   int x_init, int x_step, int src_width,
826                   int check_size, guint32 color1, guint32 color2)
827 {
828   int x = x_init;
829   guchar *src0 = src[0];
830   guchar *src1 = src[1];
831   
832   while (dest < dest_end)
833     {
834       unsigned int r, g, b;
835       int x_scaled = x >> SCALE_SHIFT;
836       int *pixel_weights;
837       guchar *q0, *q1;
838       int w1, w2, w3, w4;
839
840       q0 = src0 + x_scaled * 3;
841       q1 = src1 + x_scaled * 3;
842       
843       pixel_weights = (int *)((char *)weights + ((x >> (SCALE_SHIFT - SUBSAMPLE_BITS - 4)) & (SUBSAMPLE_MASK << 4)));
844       
845       w1 = pixel_weights[0];
846       w2 = pixel_weights[1];
847       w3 = pixel_weights[2];
848       w4 = pixel_weights[3];
849
850       r = w1 * q0[0];
851       g = w1 * q0[1];
852       b = w1 * q0[2];
853
854       r += w2 * q0[3];
855       g += w2 * q0[4];
856       b += w2 * q0[5];
857
858       r += w3 * q1[0];
859       g += w3 * q1[1];
860       b += w3 * q1[2];
861
862       r += w4 * q1[4];
863       g += w4 * q1[5];
864       b += w4 * q1[6];
865
866       dest[0] = r >> 16;
867       dest[1] = g >> 16;
868       dest[2] = b >> 16;
869       
870       dest += 3;
871       
872       x += x_step;
873     }
874   
875   return dest;
876 }
877
878 static void
879 process_pixel (int *weights, int n_x, int n_y,
880                guchar *dest, int dest_x, int dest_channels, int dest_has_alpha,
881                guchar **src, int src_channels, gboolean src_has_alpha,
882                int x_start, int src_width,
883                int check_size, guint32 color1, guint32 color2,
884                PixopsPixelFunc pixel_func)
885 {
886   unsigned int r = 0, g = 0, b = 0, a = 0;
887   int i, j;
888   
889   for (i=0; i<n_y; i++)
890     {
891       int *line_weights  = weights + n_x * i;
892
893       for (j=0; j<n_x; j++)
894         {
895           unsigned int ta;
896           guchar *q;
897
898           if (x_start + j < 0)
899             q = src[i];
900           else if (x_start + j < src_width)
901             q = src[i] + (x_start + j) * src_channels;
902           else
903             q = src[i] + (src_width - 1) * src_channels;
904
905           if (src_has_alpha)
906             ta = q[3] * line_weights[j];
907           else
908             ta = 0xff * line_weights[j];
909
910           r += ta * q[0];
911           g += ta * q[1];
912           b += ta * q[2];
913           a += ta;
914         }
915     }
916
917   (*pixel_func) (dest, dest_x, dest_channels, dest_has_alpha, src_has_alpha, check_size, color1, color2, r, g, b, a);
918 }
919
920 static void
921 pixops_process (guchar         *dest_buf,
922                 int             render_x0,
923                 int             render_y0,
924                 int             render_x1,
925                 int             render_y1,
926                 int             dest_rowstride,
927                 int             dest_channels,
928                 gboolean        dest_has_alpha,
929                 const guchar   *src_buf,
930                 int             src_width,
931                 int             src_height,
932                 int             src_rowstride,
933                 int             src_channels,
934                 gboolean        src_has_alpha,
935                 double          scale_x,
936                 double          scale_y,
937                 int             check_x,
938                 int             check_y,
939                 int             check_size,
940                 guint32         color1,
941                 guint32         color2,
942                 PixopsFilter   *filter,
943                 PixopsLineFunc  line_func,
944                 PixopsPixelFunc pixel_func)
945 {
946   int i, j;
947   int x, y;
948   guchar **line_bufs = g_new (guchar *, filter->n_y);
949
950   int x_step = (1 << SCALE_SHIFT) / scale_x;
951   int y_step = (1 << SCALE_SHIFT) / scale_y;
952
953   int dest_x;
954   int scaled_x_offset = floor (filter->x_offset * (1 << SCALE_SHIFT));
955
956   int run_end_index = (((src_width - filter->n_x + 1) << SCALE_SHIFT) - scaled_x_offset - 1) / x_step + 1 - render_x0;
957   int check_shift = check_size ? get_check_shift (check_size) : 0;
958
959   y = render_y0 * y_step + floor (filter->y_offset * (1 << SCALE_SHIFT));
960   for (i = 0; i < (render_y1 - render_y0); i++)
961     {
962       int y_start = y >> SCALE_SHIFT;
963       int x_start;
964       int *run_weights = filter->weights + ((y >> (SCALE_SHIFT - SUBSAMPLE_BITS)) & SUBSAMPLE_MASK) * filter->n_x * filter->n_y * SUBSAMPLE;
965       guchar *new_outbuf;
966       guint32 tcolor1, tcolor2;
967       
968       guchar *outbuf = dest_buf + dest_rowstride * i;
969       guchar *outbuf_end = outbuf + dest_channels * (render_x1 - render_x0);
970
971       if (((i + check_y) >> check_shift) & 1)
972         {
973           tcolor1 = color2;
974           tcolor2 = color1;
975         }
976       else
977         {
978           tcolor1 = color1;
979           tcolor2 = color2;
980         }
981
982       for (j=0; j<filter->n_y; j++)
983         {
984           if (y_start <  0)
985             line_bufs[j] = (guchar *)src_buf;
986           else if (y_start < src_height)
987             line_bufs[j] = (guchar *)src_buf + src_rowstride * y_start;
988           else
989             line_bufs[j] = (guchar *)src_buf + src_rowstride * (src_height - 1);
990
991           y_start++;
992         }
993
994       dest_x = check_x;
995       x = render_x0 * x_step + scaled_x_offset;
996       x_start = x >> SCALE_SHIFT;
997
998       while (x_start < 0 && outbuf < outbuf_end)
999         {
1000           process_pixel (run_weights + ((x >> (SCALE_SHIFT - SUBSAMPLE_BITS)) & SUBSAMPLE_MASK) * (filter->n_x * filter->n_y), filter->n_x, filter->n_y,
1001                          outbuf, dest_x, dest_channels, dest_has_alpha,
1002                          line_bufs, src_channels, src_has_alpha,
1003                          x >> SCALE_SHIFT, src_width,
1004                          check_size, tcolor1, tcolor2, pixel_func);
1005           
1006           x += x_step;
1007           x_start = x >> SCALE_SHIFT;
1008           dest_x++;
1009           outbuf += dest_channels;
1010         }
1011
1012       new_outbuf = (*line_func)(run_weights, filter->n_x, filter->n_y,
1013                                 outbuf, dest_x,
1014                                 MIN (outbuf_end, dest_buf + dest_rowstride * i + run_end_index * dest_channels),
1015                                 dest_channels, dest_has_alpha,
1016                                 line_bufs, src_channels, src_has_alpha,
1017                                 x, x_step, src_width, check_size, tcolor1, tcolor2);
1018
1019       dest_x += (new_outbuf - outbuf) / dest_channels;
1020
1021       x = (dest_x - check_x + render_x0) * x_step + scaled_x_offset;
1022       outbuf = new_outbuf;
1023
1024       while (outbuf < outbuf_end)
1025         {
1026           process_pixel (run_weights + ((x >> (SCALE_SHIFT - SUBSAMPLE_BITS)) & SUBSAMPLE_MASK) * (filter->n_x * filter->n_y), filter->n_x, filter->n_y,
1027                          outbuf, dest_x, dest_channels, dest_has_alpha,
1028                          line_bufs, src_channels, src_has_alpha,
1029                          x >> SCALE_SHIFT, src_width,
1030                          check_size, tcolor1, tcolor2, pixel_func);
1031           
1032           x += x_step;
1033           dest_x++;
1034           outbuf += dest_channels;
1035         }
1036
1037       y += y_step;
1038     }
1039
1040   g_free (line_bufs);
1041 }
1042
1043 static void
1044 tile_make_weights (PixopsFilter *filter, double x_scale, double y_scale, double overall_alpha)
1045 {
1046   int i_offset, j_offset;
1047
1048   int n_x = ceil(1/x_scale + 1);
1049   int n_y = ceil(1/y_scale + 1);
1050
1051   filter->x_offset = 0;
1052   filter->y_offset = 0;
1053   filter->n_x = n_x;
1054   filter->n_y = n_y;
1055   filter->weights = g_new (int, SUBSAMPLE * SUBSAMPLE * n_x * n_y);
1056
1057   for (i_offset=0; i_offset<SUBSAMPLE; i_offset++)
1058     for (j_offset=0; j_offset<SUBSAMPLE; j_offset++)
1059       {
1060         int *pixel_weights = filter->weights + ((i_offset*SUBSAMPLE) + j_offset) * n_x * n_y;
1061         double x = (double)j_offset / 16;
1062         double y = (double)i_offset / 16;
1063         int i,j;
1064           
1065         for (i = 0; i < n_y; i++)
1066           {
1067             double tw, th;
1068                 
1069             if (i < y)
1070               {
1071                 if (i + 1 > y)
1072                   th = MIN(i+1, y + 1/y_scale) - y;
1073                 else
1074                   th = 0;
1075               }
1076             else
1077               {
1078                 if (y + 1/y_scale > i)
1079                   th = MIN(i+1, y + 1/y_scale) - i;
1080                 else
1081                   th = 0;
1082               }
1083                 
1084             for (j = 0; j < n_x; j++)
1085               {
1086                 if (j < x)
1087                   {
1088                     if (j + 1 > x)
1089                       tw = MIN(j+1, x + 1/x_scale) - x;
1090                     else
1091                       tw = 0;
1092                   }
1093                 else
1094                   {
1095                     if (x + 1/x_scale > j)
1096                       tw = MIN(j+1, x + 1/x_scale) - j;
1097                     else
1098                       tw = 0;
1099                   }
1100
1101                 *(pixel_weights + n_x * i + j) = 65536 * tw * x_scale * th * y_scale * overall_alpha;
1102               }
1103           }
1104       }
1105 }
1106
1107 static void
1108 bilinear_make_fast_weights (PixopsFilter *filter, double x_scale, double y_scale, double overall_alpha)
1109 {
1110   int i_offset, j_offset;
1111   double *x_weights, *y_weights;
1112   int n_x, n_y;
1113
1114   if (x_scale > 1.0)            /* Bilinear */
1115     {
1116       n_x = 2;
1117       filter->x_offset = 0.5 * (1/x_scale - 1);
1118     }
1119   else                          /* Tile */
1120     {
1121       n_x = ceil(1.0 + 1.0/x_scale);
1122       filter->x_offset = 0.0;
1123     }
1124
1125   if (y_scale > 1.0)            /* Bilinear */
1126     {
1127       n_y = 2;
1128       filter->y_offset = 0.5 * (1/y_scale - 1);
1129     }
1130   else                          /* Tile */
1131     {
1132       n_y = ceil(1.0 + 1.0/y_scale);
1133       filter->y_offset = 0.0;
1134     }
1135
1136   filter->n_y = n_y;
1137   filter->n_x = n_x;
1138   filter->weights = g_new (int, SUBSAMPLE * SUBSAMPLE * n_x * n_y);
1139
1140   x_weights = g_new (double, n_x);
1141   y_weights = g_new (double, n_y);
1142
1143   for (i_offset=0; i_offset<SUBSAMPLE; i_offset++)
1144     for (j_offset=0; j_offset<SUBSAMPLE; j_offset++)
1145       {
1146         int *pixel_weights = filter->weights + ((i_offset*SUBSAMPLE) + j_offset) * n_x * n_y;
1147         double x = (double)j_offset / 16;
1148         double y = (double)i_offset / 16;
1149         int i,j;
1150
1151         if (x_scale > 1.0)      /* Bilinear */
1152           {
1153             for (i = 0; i < n_x; i++)
1154               {
1155                 x_weights[i] = ((i == 0) ? (1 - x) : x) / x_scale;
1156               }
1157           }
1158         else                    /* Tile */
1159           {
1160             for (i = 0; i < n_x; i++)
1161               {
1162                 if (i < x)
1163                   {
1164                     if (i + 1 > x)
1165                       x_weights[i] = MIN(i+1, x + 1/x_scale) - x;
1166                     else
1167                       x_weights[i] = 0;
1168                   }
1169                 else
1170                   {
1171                     if (x + 1/x_scale > i)
1172                       x_weights[i] = MIN(i+1, x + 1/x_scale) - i;
1173                     else
1174                       x_weights[i] = 0;
1175                   }
1176               }
1177           }
1178
1179         if (y_scale > 1.0)      /* Bilinear */
1180           {
1181             for (i = 0; i < n_y; i++)
1182               {
1183                 y_weights[i] = ((i == 0) ? (1 - y) : y) / y_scale;
1184               }
1185           }
1186         else                    /* Tile */
1187           {
1188             for (i = 0; i < n_y; i++)
1189               {
1190                 if (i < y)
1191                   {
1192                     if (i + 1 > y)
1193                       y_weights[i] = MIN(i+1, y + 1/y_scale) - y;
1194                     else
1195                       y_weights[i] = 0;
1196                   }
1197                 else
1198                   {
1199                     if (y + 1/y_scale > i)
1200                       y_weights[i] = MIN(i+1, y + 1/y_scale) - i;
1201                     else
1202                       y_weights[i] = 0;
1203                   }
1204               }
1205           }
1206
1207         for (i = 0; i < n_y; i++)
1208           for (j = 0; j < n_x; j++)
1209             *(pixel_weights + n_x * i + j) = 65536 * x_weights[j] * x_scale * y_weights[i] * y_scale * overall_alpha;
1210       }
1211
1212   g_free (x_weights);
1213   g_free (y_weights);
1214 }
1215
1216 static double
1217 bilinear_quadrant (double bx0, double bx1, double by0, double by1)
1218 {
1219   double ax0, ax1, ay0, ay1;
1220   double x0, x1, y0, y1;
1221
1222   ax0 = 0.;
1223   ax1 = 1.;
1224   ay0 = 0.;
1225   ay1 = 1.;
1226
1227   if (ax0 < bx0)
1228     {
1229       if (ax1 > bx0)
1230         {
1231           x0 = bx0;
1232           x1 = MIN (ax1, bx1);
1233         }
1234       else
1235         return 0;
1236     }
1237   else
1238     {
1239       if (bx1 > ax0)
1240         {
1241           x0 = ax0;
1242           x1 = MIN (ax1, bx1);
1243         }
1244       else
1245         return 0;
1246     }
1247
1248   if (ay0 < by0)
1249     {
1250       if (ay1 > by0)
1251         {
1252           y0 = by0;
1253           y1 = MIN (ay1, by1);
1254         }
1255       else
1256         return 0;
1257     }
1258   else
1259     {
1260       if (by1 > ay0)
1261         {
1262           y0 = ay0;
1263           y1 = MIN (ay1, by1);
1264         }
1265       else
1266         return 0;
1267     }
1268
1269   return 0.25 * (x1*x1 - x0*x0) * (y1*y1 - y0*y0);
1270 }
1271
1272 static void
1273 bilinear_make_weights (PixopsFilter *filter, double x_scale, double y_scale, double overall_alpha)
1274 {
1275   int i_offset, j_offset;
1276
1277   int n_x = ceil(1/x_scale + 2.0);
1278   int n_y = ceil(1/y_scale + 2.0);
1279
1280   filter->x_offset = -1.0;
1281   filter->y_offset = -1.0;
1282   filter->n_x = n_x;
1283   filter->n_y = n_y;
1284   
1285   filter->weights = g_new (int, SUBSAMPLE * SUBSAMPLE * n_x * n_y);
1286
1287   for (i_offset=0; i_offset<SUBSAMPLE; i_offset++)
1288     for (j_offset=0; j_offset<SUBSAMPLE; j_offset++)
1289       {
1290         int *pixel_weights = filter->weights + ((i_offset*SUBSAMPLE) + j_offset) * n_x * n_y;
1291         double x = (double)j_offset / 16;
1292         double y = (double)i_offset / 16;
1293         int i,j;
1294           
1295         for (i = 0; i < n_y; i++)
1296           for (j = 0; j < n_x; j++)
1297             {
1298               double w;
1299
1300               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);
1301               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);
1302               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);
1303               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);
1304               
1305               *(pixel_weights + n_x * i + j) = 65536 * w * x_scale * y_scale * overall_alpha;
1306             }
1307       }
1308 }
1309
1310 void
1311 pixops_composite_color (guchar         *dest_buf,
1312                         int             render_x0,
1313                         int             render_y0,
1314                         int             render_x1,
1315                         int             render_y1,
1316                         int             dest_rowstride,
1317                         int             dest_channels,
1318                         gboolean        dest_has_alpha,
1319                         const guchar   *src_buf,
1320                         int             src_width,
1321                         int             src_height,
1322                         int             src_rowstride,
1323                         int             src_channels,
1324                         gboolean        src_has_alpha,
1325                         double          scale_x,
1326                         double          scale_y,
1327                         GdkInterpType   interp_type,
1328                         int             overall_alpha,
1329                         int             check_x,
1330                         int             check_y,
1331                         int             check_size,
1332                         guint32         color1,
1333                         guint32         color2)
1334 {
1335   PixopsFilter filter;
1336   PixopsLineFunc line_func;
1337   
1338 #ifdef USE_MMX
1339   gboolean found_mmx = pixops_have_mmx();
1340 #endif
1341
1342   g_return_if_fail (!(dest_channels == 3 && dest_has_alpha));
1343   g_return_if_fail (!(src_channels == 3 && src_has_alpha));
1344
1345   if (scale_x == 0 || scale_y == 0)
1346     return;
1347
1348   if (!src_has_alpha && overall_alpha == 255)
1349     pixops_scale (dest_buf, render_x0, render_y0, render_x1, render_y1,
1350                   dest_rowstride, dest_channels, dest_has_alpha,
1351                   src_buf, src_width, src_height, src_rowstride, src_channels,
1352                   src_has_alpha, scale_x, scale_y, interp_type);
1353
1354   switch (interp_type)
1355     {
1356     case GDK_INTERP_NEAREST:
1357       pixops_composite_color_nearest (dest_buf, render_x0, render_y0, render_x1, render_y1,
1358                                       dest_rowstride, dest_channels, dest_has_alpha,
1359                                       src_buf, src_width, src_height, src_rowstride, src_channels, src_has_alpha,
1360                                       scale_x, scale_y, overall_alpha,
1361                                       check_x, check_y, check_size, color1, color2);
1362       return;
1363
1364     case GDK_INTERP_TILES:
1365       tile_make_weights (&filter, scale_x, scale_y, overall_alpha / 255.);
1366       break;
1367       
1368     case GDK_INTERP_BILINEAR:
1369       bilinear_make_fast_weights (&filter, scale_x, scale_y, overall_alpha / 255.);
1370       break;
1371       
1372     case GDK_INTERP_HYPER:
1373       bilinear_make_weights (&filter, scale_x, scale_y, overall_alpha / 255.);
1374       break;
1375     }
1376
1377 #ifdef USE_MMX
1378   if (filter.n_x == 2 && filter.n_y == 2 &&
1379       dest_channels == 4 && src_channels == 4 && src_has_alpha && !dest_has_alpha && found_mmx)
1380     line_func = composite_line_color_22_4a4_mmx_stub;
1381   else
1382 #endif    
1383     line_func = composite_line_color;
1384   
1385   pixops_process (dest_buf, render_x0, render_y0, render_x1, render_y1,
1386                   dest_rowstride, dest_channels, dest_has_alpha,
1387                   src_buf, src_width, src_height, src_rowstride, src_channels,
1388                   src_has_alpha, scale_x, scale_y, check_x, check_y, check_size, color1, color2,
1389                   &filter, line_func, composite_pixel_color);
1390
1391   g_free (filter.weights);
1392 }
1393
1394 void
1395 pixops_composite (guchar        *dest_buf,
1396                   int            render_x0,
1397                   int            render_y0,
1398                   int            render_x1,
1399                   int            render_y1,
1400                   int            dest_rowstride,
1401                   int            dest_channels,
1402                   gboolean       dest_has_alpha,
1403                   const guchar  *src_buf,
1404                   int            src_width,
1405                   int            src_height,
1406                   int            src_rowstride,
1407                   int            src_channels,
1408                   gboolean       src_has_alpha,
1409                   double         scale_x,
1410                   double         scale_y,
1411                   GdkInterpType  interp_type,
1412                   int            overall_alpha)
1413 {
1414   PixopsFilter filter;
1415   PixopsLineFunc line_func;
1416   
1417 #ifdef USE_MMX
1418   gboolean found_mmx = pixops_have_mmx();
1419 #endif
1420
1421   g_return_if_fail (!(dest_channels == 3 && dest_has_alpha));
1422   g_return_if_fail (!(src_channels == 3 && src_has_alpha));
1423
1424   if (scale_x == 0 || scale_y == 0)
1425     return;
1426
1427   if (!src_has_alpha && overall_alpha == 255)
1428     pixops_scale (dest_buf, render_x0, render_y0, render_x1, render_y1,
1429                   dest_rowstride, dest_channels, dest_has_alpha,
1430                   src_buf, src_width, src_height, src_rowstride, src_channels,
1431                   src_has_alpha, scale_x, scale_y, interp_type);
1432
1433   switch (interp_type)
1434     {
1435     case GDK_INTERP_NEAREST:
1436       pixops_composite_nearest (dest_buf, render_x0, render_y0, render_x1, render_y1,
1437                                 dest_rowstride, dest_channels, dest_has_alpha,
1438                                 src_buf, src_width, src_height, src_rowstride, src_channels,
1439                                 src_has_alpha, scale_x, scale_y, overall_alpha);
1440       return;
1441
1442     case GDK_INTERP_TILES:
1443       tile_make_weights (&filter, scale_x, scale_y, overall_alpha / 255.);
1444       break;
1445       
1446     case GDK_INTERP_BILINEAR:
1447       bilinear_make_fast_weights (&filter, scale_x, scale_y, overall_alpha / 255.);
1448       break;
1449       
1450     case GDK_INTERP_HYPER:
1451       bilinear_make_weights (&filter, scale_x, scale_y, overall_alpha / 255.);
1452       break;
1453     }
1454
1455   if (filter.n_x == 2 && filter.n_y == 2 &&
1456       dest_channels == 4 && src_channels == 4 && src_has_alpha && !dest_has_alpha)
1457     {
1458 #ifdef USE_MMX
1459       if (found_mmx)
1460         line_func = composite_line_22_4a4_mmx_stub;
1461       else
1462 #endif  
1463         line_func = composite_line_22_4a4;
1464     }
1465   else
1466     line_func = composite_line;
1467   
1468   pixops_process (dest_buf, render_x0, render_y0, render_x1, render_y1,
1469                   dest_rowstride, dest_channels, dest_has_alpha,
1470                   src_buf, src_width, src_height, src_rowstride, src_channels,
1471                   src_has_alpha, scale_x, scale_y, 0, 0, 0, 0, 0, 
1472                   &filter, line_func, composite_pixel);
1473
1474   g_free (filter.weights);
1475 }
1476
1477 void
1478 pixops_scale (guchar        *dest_buf,
1479               int            render_x0,
1480               int            render_y0,
1481               int            render_x1,
1482               int            render_y1,
1483               int            dest_rowstride,
1484               int            dest_channels,
1485               gboolean       dest_has_alpha,
1486               const guchar  *src_buf,
1487               int            src_width,
1488               int            src_height,
1489               int            src_rowstride,
1490               int            src_channels,
1491               gboolean       src_has_alpha,
1492               double         scale_x,
1493               double         scale_y,
1494               GdkInterpType  interp_type)
1495 {
1496   PixopsFilter filter;
1497   PixopsLineFunc line_func;
1498
1499 #ifdef USE_MMX
1500   gboolean found_mmx = pixops_have_mmx();
1501 #endif
1502
1503   g_return_if_fail (!(dest_channels == 3 && dest_has_alpha));
1504   g_return_if_fail (!(src_channels == 3 && src_has_alpha));
1505   g_return_if_fail (!(src_has_alpha && !dest_has_alpha));
1506
1507   if (scale_x == 0 || scale_y == 0)
1508     return;
1509
1510   switch (interp_type)
1511     {
1512     case GDK_INTERP_NEAREST:
1513       pixops_scale_nearest (dest_buf, render_x0, render_y0, render_x1, render_y1,
1514                             dest_rowstride, dest_channels, dest_has_alpha,
1515                             src_buf, src_width, src_height, src_rowstride, src_channels, src_has_alpha,
1516                             scale_x, scale_y);
1517       return;
1518
1519     case GDK_INTERP_TILES:
1520       tile_make_weights (&filter, scale_x, scale_y, 1.0);
1521       break;
1522       
1523     case GDK_INTERP_BILINEAR:
1524       bilinear_make_fast_weights (&filter, scale_x, scale_y, 1.0);
1525       break;
1526       
1527     case GDK_INTERP_HYPER:
1528       bilinear_make_weights (&filter, scale_x, scale_y, 1.0);
1529       break;
1530     }
1531
1532 #ifdef USE_MMX
1533   if (filter.n_x == 2 && filter.n_y == 2 &&
1534       found_mmx && dest_channels == 3 && src_channels == 3)
1535     line_func = scale_line_22_33_mmx_stub;
1536   else
1537 #endif    
1538     line_func = scale_line;
1539   
1540   pixops_process (dest_buf, render_x0, render_y0, render_x1, render_y1,
1541                   dest_rowstride, dest_channels, dest_has_alpha,
1542                   src_buf, src_width, src_height, src_rowstride, src_channels,
1543                   src_has_alpha, scale_x, scale_y, 0, 0, 0, 0, 0,
1544                   &filter, line_func, scale_pixel);
1545
1546   g_free (filter.weights);
1547 }
1548