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