]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/pixops/pixops.c
Patch from Matthias Clasen to fix some typos (#77246)
[~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 * q1[3];
497       r += ta * q1[0];
498       g += ta * q1[1];
499       b += ta * q1[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[3];
885       g += w4 * q1[4];
886       b += w4 * q1[5];
887
888       dest[0] = (r + 0x8000) >> 16;
889       dest[1] = (g + 0x8000) >> 16;
890       dest[2] = (b + 0x8000) >> 16;
891       
892       dest += 3;
893       x += x_step;
894     }
895   
896   return dest;
897 }
898
899 static void
900 process_pixel (int *weights, int n_x, int n_y,
901                guchar *dest, int dest_x, int dest_channels, int dest_has_alpha,
902                guchar **src, int src_channels, gboolean src_has_alpha,
903                int x_start, int src_width,
904                int check_size, guint32 color1, guint32 color2,
905                PixopsPixelFunc pixel_func)
906 {
907   unsigned int r = 0, g = 0, b = 0, a = 0;
908   int i, j;
909   
910   for (i=0; i<n_y; i++)
911     {
912       int *line_weights  = weights + n_x * i;
913
914       for (j=0; j<n_x; j++)
915         {
916           unsigned int ta;
917           guchar *q;
918
919           if (x_start + j < 0)
920             q = src[i];
921           else if (x_start + j < src_width)
922             q = src[i] + (x_start + j) * src_channels;
923           else
924             q = src[i] + (src_width - 1) * src_channels;
925
926           if (src_has_alpha)
927             ta = q[3] * line_weights[j];
928           else
929             ta = 0xff * line_weights[j];
930
931           r += ta * q[0];
932           g += ta * q[1];
933           b += ta * q[2];
934           a += ta;
935         }
936     }
937
938   (*pixel_func) (dest, dest_x, dest_channels, dest_has_alpha, src_has_alpha, check_size, color1, color2, r, g, b, a);
939 }
940
941 static void
942 pixops_process (guchar         *dest_buf,
943                 int             render_x0,
944                 int             render_y0,
945                 int             render_x1,
946                 int             render_y1,
947                 int             dest_rowstride,
948                 int             dest_channels,
949                 gboolean        dest_has_alpha,
950                 const guchar   *src_buf,
951                 int             src_width,
952                 int             src_height,
953                 int             src_rowstride,
954                 int             src_channels,
955                 gboolean        src_has_alpha,
956                 double          scale_x,
957                 double          scale_y,
958                 int             check_x,
959                 int             check_y,
960                 int             check_size,
961                 guint32         color1,
962                 guint32         color2,
963                 PixopsFilter   *filter,
964                 PixopsLineFunc  line_func,
965                 PixopsPixelFunc pixel_func)
966 {
967   int i, j;
968   int x, y;                     /* X and Y position in source (fixed_point) */
969   guchar **line_bufs = g_new (guchar *, filter->n_y);
970
971   int x_step = (1 << SCALE_SHIFT) / scale_x; /* X step in source (fixed point) */
972   int y_step = (1 << SCALE_SHIFT) / scale_y; /* Y step in source (fixed point) */
973
974   int check_shift = check_size ? get_check_shift (check_size) : 0;
975
976   int scaled_x_offset = floor (filter->x_offset * (1 << SCALE_SHIFT));
977
978   /* Compute the index where we run off the end of the source buffer. The furthest
979    * source pixel we access at index i is:
980    *
981    *  ((render_x0 + i) * x_step + scaled_x_offset) >> SCALE_SHIFT + filter->n_x - 1
982    *
983    * So, run_end_index is the smallest i for which this pixel is src_width, i.e, for which:
984    *
985    *  (i + render_x0) * x_step >= ((src_width - filter->n_x + 1) << SCALE_SHIFT) - scaled_x_offset
986    *
987    */
988 #define MYDIV(a,b) ((a) > 0 ? (a) / (b) : ((a) - (b) + 1) / (b))    /* Division so that -1/5 = -1 */
989   
990   int run_end_x = (((src_width - filter->n_x + 1) << SCALE_SHIFT) - scaled_x_offset);
991   int run_end_index = MYDIV (run_end_x + x_step - 1, x_step) - render_x0;
992   run_end_index = MIN (run_end_index, render_x1 - render_x0);
993
994   y = render_y0 * y_step + floor (filter->y_offset * (1 << SCALE_SHIFT));
995   for (i = 0; i < (render_y1 - render_y0); i++)
996     {
997       int dest_x;
998       int y_start = y >> SCALE_SHIFT;
999       int x_start;
1000       int *run_weights = filter->weights + ((y >> (SCALE_SHIFT - SUBSAMPLE_BITS)) & SUBSAMPLE_MASK) * filter->n_x * filter->n_y * SUBSAMPLE;
1001       guchar *new_outbuf;
1002       guint32 tcolor1, tcolor2;
1003       
1004       guchar *outbuf = dest_buf + dest_rowstride * i;
1005       guchar *outbuf_end = outbuf + dest_channels * (render_x1 - render_x0);
1006
1007       if (((i + check_y) >> check_shift) & 1)
1008         {
1009           tcolor1 = color2;
1010           tcolor2 = color1;
1011         }
1012       else
1013         {
1014           tcolor1 = color1;
1015           tcolor2 = color2;
1016         }
1017
1018       for (j=0; j<filter->n_y; j++)
1019         {
1020           if (y_start <  0)
1021             line_bufs[j] = (guchar *)src_buf;
1022           else if (y_start < src_height)
1023             line_bufs[j] = (guchar *)src_buf + src_rowstride * y_start;
1024           else
1025             line_bufs[j] = (guchar *)src_buf + src_rowstride * (src_height - 1);
1026
1027           y_start++;
1028         }
1029
1030       dest_x = check_x;
1031       x = render_x0 * x_step + scaled_x_offset;
1032       x_start = x >> SCALE_SHIFT;
1033
1034       while (x_start < 0 && outbuf < outbuf_end)
1035         {
1036           process_pixel (run_weights + ((x >> (SCALE_SHIFT - SUBSAMPLE_BITS)) & SUBSAMPLE_MASK) * (filter->n_x * filter->n_y), filter->n_x, filter->n_y,
1037                          outbuf, dest_x, dest_channels, dest_has_alpha,
1038                          line_bufs, src_channels, src_has_alpha,
1039                          x >> SCALE_SHIFT, src_width,
1040                          check_size, tcolor1, tcolor2, pixel_func);
1041           
1042           x += x_step;
1043           x_start = x >> SCALE_SHIFT;
1044           dest_x++;
1045           outbuf += dest_channels;
1046         }
1047
1048       new_outbuf = (*line_func) (run_weights, filter->n_x, filter->n_y,
1049                                  outbuf, dest_x,
1050                                  dest_buf + dest_rowstride * i + run_end_index * dest_channels,
1051                                  dest_channels, dest_has_alpha,
1052                                  line_bufs, src_channels, src_has_alpha,
1053                                  x, x_step, src_width, check_size, tcolor1, tcolor2);
1054
1055       dest_x += (new_outbuf - outbuf) / dest_channels;
1056
1057       x = (dest_x - check_x + render_x0) * x_step + scaled_x_offset;
1058       outbuf = new_outbuf;
1059
1060       while (outbuf < outbuf_end)
1061         {
1062           process_pixel (run_weights + ((x >> (SCALE_SHIFT - SUBSAMPLE_BITS)) & SUBSAMPLE_MASK) * (filter->n_x * filter->n_y), filter->n_x, filter->n_y,
1063                          outbuf, dest_x, dest_channels, dest_has_alpha,
1064                          line_bufs, src_channels, src_has_alpha,
1065                          x >> SCALE_SHIFT, src_width,
1066                          check_size, tcolor1, tcolor2, pixel_func);
1067           
1068           x += x_step;
1069           dest_x++;
1070           outbuf += dest_channels;
1071         }
1072
1073       y += y_step;
1074     }
1075
1076   g_free (line_bufs);
1077 }
1078
1079 static void 
1080 correct_total (int    *weights, 
1081                int    n_x, 
1082                int    n_y,
1083                int    total, 
1084                double overall_alpha)
1085 {
1086   int correction = (int)(0.5 + 65536 * overall_alpha) - total;
1087   int i;
1088   for (i = n_x * n_y - 1; i >= 0; i--) 
1089     {
1090       if (*(weights + i) + correction >= 0) 
1091         {
1092           *(weights + i) += correction;
1093           break;
1094         }
1095     }
1096 }  
1097
1098 static void
1099 tile_make_weights (PixopsFilter *filter, double x_scale, double y_scale, double overall_alpha)
1100 {
1101   int i_offset, j_offset;
1102
1103   int n_x = ceil(1/x_scale + 1);
1104   int n_y = ceil(1/y_scale + 1);
1105
1106   filter->x_offset = 0;
1107   filter->y_offset = 0;
1108   filter->n_x = n_x;
1109   filter->n_y = n_y;
1110   filter->weights = g_new (int, SUBSAMPLE * SUBSAMPLE * n_x * n_y);
1111
1112   for (i_offset=0; i_offset<SUBSAMPLE; i_offset++)
1113     for (j_offset=0; j_offset<SUBSAMPLE; j_offset++)
1114       {
1115         int *pixel_weights = filter->weights + ((i_offset*SUBSAMPLE) + j_offset) * n_x * n_y;
1116         double x = (double)j_offset / SUBSAMPLE;
1117         double y = (double)i_offset / SUBSAMPLE;
1118         int i,j;
1119         int total = 0;
1120           
1121         for (i = 0; i < n_y; i++)
1122           {
1123             double tw, th;
1124                 
1125             if (i < y)
1126               {
1127
1128                 if (i + 1 > y)
1129                   th = MIN(i+1, y + 1/y_scale) - y;
1130                 else
1131                   th = 0;
1132               }
1133             else
1134               {
1135                 if (y + 1/y_scale > i)
1136                   th = MIN(i+1, y + 1/y_scale) - i;
1137                 else
1138                   th = 0;
1139               }
1140                 
1141             for (j = 0; j < n_x; j++)
1142               {
1143                 int weight;
1144                 
1145                 if (j < x)
1146                   {
1147                     if (j + 1 > x)
1148                       tw = MIN(j+1, x + 1/x_scale) - x;
1149                     else
1150                       tw = 0;
1151                   }
1152                 else
1153                   {
1154                     if (x + 1/x_scale > j)
1155                       tw = MIN(j+1, x + 1/x_scale) - j;
1156                     else
1157                       tw = 0;
1158                   }
1159
1160                 weight = 65536 * tw * x_scale * th * y_scale * overall_alpha + 0.5;
1161                 total += weight;
1162                 *(pixel_weights + n_x * i + j) = weight;
1163               }
1164           }
1165         
1166         correct_total (pixel_weights, n_x, n_y, total, overall_alpha);
1167       }
1168 }
1169
1170 static void
1171 bilinear_make_fast_weights (PixopsFilter *filter, double x_scale, double y_scale, double overall_alpha)
1172 {
1173   int i_offset, j_offset;
1174   double *x_weights, *y_weights;
1175   int n_x, n_y;
1176
1177   if (x_scale > 1.0)            /* Bilinear */
1178     {
1179       n_x = 2;
1180       filter->x_offset = 0.5 * (1/x_scale - 1);
1181     }
1182   else                          /* Tile */
1183     {
1184       n_x = ceil(1.0 + 1.0/x_scale);
1185       filter->x_offset = 0.0;
1186     }
1187
1188   if (y_scale > 1.0)            /* Bilinear */
1189     {
1190       n_y = 2;
1191       filter->y_offset = 0.5 * (1/y_scale - 1);
1192     }
1193   else                          /* Tile */
1194     {
1195       n_y = ceil(1.0 + 1.0/y_scale);
1196       filter->y_offset = 0.0;
1197     }
1198
1199   filter->n_y = n_y;
1200   filter->n_x = n_x;
1201   filter->weights = g_new (int, SUBSAMPLE * SUBSAMPLE * n_x * n_y);
1202
1203   x_weights = g_new (double, n_x);
1204   y_weights = g_new (double, n_y);
1205
1206   for (i_offset=0; i_offset<SUBSAMPLE; i_offset++)
1207     for (j_offset=0; j_offset<SUBSAMPLE; j_offset++)
1208       {
1209         int *pixel_weights = filter->weights + ((i_offset*SUBSAMPLE) + j_offset) * n_x * n_y;
1210         double x = (double)j_offset / SUBSAMPLE;
1211         double y = (double)i_offset / SUBSAMPLE;
1212         int i,j;
1213         int total = 0;
1214
1215         if (x_scale > 1.0)      /* Bilinear */
1216           {
1217             for (i = 0; i < n_x; i++)
1218               {
1219                 x_weights[i] = ((i == 0) ? (1 - x) : x) / x_scale;
1220               }
1221           }
1222         else                    /* Tile */
1223           {
1224             /*           x
1225              * ---------|--.-|----|--.-|-------  SRC
1226              * ------------|---------|---------  DEST
1227              */
1228             for (i = 0; i < n_x; i++)
1229               {
1230                 if (i < x)
1231                   {
1232                     if (i + 1 > x)
1233                       x_weights[i] = MIN(i+1, x + 1/x_scale) - x;
1234                     else
1235                       x_weights[i] = 0;
1236                   }
1237                 else
1238                   {
1239                     if (x + 1/x_scale > i)
1240                       x_weights[i] = MIN(i+1, x + 1/x_scale) - i;
1241                     else
1242                       x_weights[i] = 0;
1243                   }
1244               }
1245           }
1246
1247         if (y_scale > 1.0)      /* Bilinear */
1248           {
1249             for (i = 0; i < n_y; i++)
1250               {
1251                 y_weights[i] = ((i == 0) ? (1 - y) : y) / y_scale;
1252               }
1253           }
1254         else                    /* Tile */
1255           {
1256             /*           y
1257              * ---------|--.-|----|--.-|-------  SRC
1258              * ------------|---------|---------  DEST
1259              */
1260             for (i = 0; i < n_y; i++)
1261               {
1262                 if (i < y)
1263                   {
1264                     if (i + 1 > y)
1265                       y_weights[i] = MIN(i+1, y + 1/y_scale) - y;
1266                     else
1267                       y_weights[i] = 0;
1268                   }
1269                 else
1270                   {
1271                     if (y + 1/y_scale > i)
1272                       y_weights[i] = MIN(i+1, y + 1/y_scale) - i;
1273                     else
1274                       y_weights[i] = 0;
1275                   }
1276               }
1277           }
1278
1279         for (i = 0; i < n_y; i++)
1280           for (j = 0; j < n_x; j++)
1281             {
1282               int weight = 65536 * x_weights[j] * x_scale * y_weights[i] * y_scale * overall_alpha + 0.5;
1283               *(pixel_weights + n_x * i + j) = weight;
1284               total += weight;
1285             }
1286
1287         correct_total (pixel_weights, n_x, n_y, total, overall_alpha);
1288       }
1289
1290   g_free (x_weights);
1291   g_free (y_weights);
1292 }
1293
1294 static double
1295 bilinear_quadrant (double bx0, double bx1, double by0, double by1)
1296 {
1297   double ax0, ax1, ay0, ay1;
1298   double x0, x1, y0, y1;
1299
1300   ax0 = 0.;
1301   ax1 = 1.;
1302   ay0 = 0.;
1303   ay1 = 1.;
1304
1305   if (ax0 < bx0)
1306     {
1307       if (ax1 > bx0)
1308         {
1309           x0 = bx0;
1310           x1 = MIN (ax1, bx1);
1311         }
1312       else
1313         return 0;
1314     }
1315   else
1316     {
1317       if (bx1 > ax0)
1318         {
1319           x0 = ax0;
1320           x1 = MIN (ax1, bx1);
1321         }
1322       else
1323         return 0;
1324     }
1325
1326   if (ay0 < by0)
1327     {
1328       if (ay1 > by0)
1329         {
1330           y0 = by0;
1331           y1 = MIN (ay1, by1);
1332         }
1333       else
1334         return 0;
1335     }
1336   else
1337     {
1338       if (by1 > ay0)
1339         {
1340           y0 = ay0;
1341           y1 = MIN (ay1, by1);
1342         }
1343       else
1344         return 0;
1345     }
1346
1347   return 0.25 * (x1*x1 - x0*x0) * (y1*y1 - y0*y0);
1348 }
1349
1350 static void
1351 bilinear_make_weights (PixopsFilter *filter, double x_scale, double y_scale, double overall_alpha)
1352 {
1353   int i_offset, j_offset;
1354
1355   int n_x = ceil(1/x_scale + 2.0);
1356   int n_y = ceil(1/y_scale + 2.0);
1357
1358   filter->x_offset = -1.0;
1359   filter->y_offset = -1.0;
1360   filter->n_x = n_x;
1361   filter->n_y = n_y;
1362   
1363   filter->weights = g_new (int, SUBSAMPLE * SUBSAMPLE * n_x * n_y);
1364
1365   for (i_offset=0; i_offset<SUBSAMPLE; i_offset++)
1366     for (j_offset=0; j_offset<SUBSAMPLE; j_offset++)
1367       {
1368         int *pixel_weights = filter->weights + ((i_offset*SUBSAMPLE) + j_offset) * n_x * n_y;
1369         double x = (double)j_offset / SUBSAMPLE;
1370         double y = (double)i_offset / SUBSAMPLE;
1371         int i,j;
1372         int total = 0;
1373
1374         for (i = 0; i < n_y; i++)
1375           for (j = 0; j < n_x; j++)
1376             {
1377               double w;
1378               int weight;
1379               
1380               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);
1381               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);
1382               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);
1383               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);
1384               weight = 65536 * w * x_scale * y_scale * overall_alpha + 0.5;
1385               *(pixel_weights + n_x * i + j) = weight;
1386               total += weight;
1387             }
1388         
1389         correct_total (pixel_weights, n_x, n_y, total, overall_alpha);
1390       }
1391 }
1392
1393 void
1394 pixops_composite_color (guchar         *dest_buf,
1395                         int             render_x0,
1396                         int             render_y0,
1397                         int             render_x1,
1398                         int             render_y1,
1399                         int             dest_rowstride,
1400                         int             dest_channels,
1401                         gboolean        dest_has_alpha,
1402                         const guchar   *src_buf,
1403                         int             src_width,
1404                         int             src_height,
1405                         int             src_rowstride,
1406                         int             src_channels,
1407                         gboolean        src_has_alpha,
1408                         double          scale_x,
1409                         double          scale_y,
1410                         PixopsInterpType   interp_type,
1411                         int             overall_alpha,
1412                         int             check_x,
1413                         int             check_y,
1414                         int             check_size,
1415                         guint32         color1,
1416                         guint32         color2)
1417 {
1418   PixopsFilter filter;
1419   PixopsLineFunc line_func;
1420   
1421 #ifdef USE_MMX
1422   gboolean found_mmx = pixops_have_mmx();
1423 #endif
1424
1425   g_return_if_fail (!(dest_channels == 3 && dest_has_alpha));
1426   g_return_if_fail (!(src_channels == 3 && src_has_alpha));
1427
1428   if (scale_x == 0 || scale_y == 0)
1429     return;
1430
1431   if (!src_has_alpha && overall_alpha == 255)
1432     pixops_scale (dest_buf, render_x0, render_y0, render_x1, render_y1,
1433                   dest_rowstride, dest_channels, dest_has_alpha,
1434                   src_buf, src_width, src_height, src_rowstride, src_channels,
1435                   src_has_alpha, scale_x, scale_y, interp_type);
1436
1437   switch (interp_type)
1438     {
1439     case PIXOPS_INTERP_NEAREST:
1440       pixops_composite_color_nearest (dest_buf, render_x0, render_y0, render_x1, render_y1,
1441                                       dest_rowstride, dest_channels, dest_has_alpha,
1442                                       src_buf, src_width, src_height, src_rowstride, src_channels, src_has_alpha,
1443                                       scale_x, scale_y, overall_alpha,
1444                                       check_x, check_y, check_size, color1, color2);
1445       return;
1446
1447     case PIXOPS_INTERP_TILES:
1448       tile_make_weights (&filter, scale_x, scale_y, overall_alpha / 255.);
1449       break;
1450       
1451     case PIXOPS_INTERP_BILINEAR:
1452       bilinear_make_fast_weights (&filter, scale_x, scale_y, overall_alpha / 255.);
1453       break;
1454       
1455     case PIXOPS_INTERP_HYPER:
1456       bilinear_make_weights (&filter, scale_x, scale_y, overall_alpha / 255.);
1457       break;
1458     }
1459
1460 #ifdef USE_MMX
1461   if (filter.n_x == 2 && filter.n_y == 2 &&
1462       dest_channels == 4 && src_channels == 4 && src_has_alpha && !dest_has_alpha && found_mmx)
1463     line_func = composite_line_color_22_4a4_mmx_stub;
1464   else
1465 #endif
1466     line_func = composite_line_color;
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, check_x, check_y, check_size, color1, color2,
1472                   &filter, line_func, composite_pixel_color);
1473
1474   g_free (filter.weights);
1475 }
1476
1477 /**
1478  * pixops_composite:
1479  * @dest_buf: pointer to location to store result
1480  * @render_x0: x0 of region of scaled source to store into @dest_buf
1481  * @render_y0: y0 of region of scaled source to store into @dest_buf
1482  * @render_x1: x1 of region of scaled source to store into @dest_buf
1483  * @render_y1: x1 of region of scaled source to store into @dest_buf
1484  * @dest_rowstride: rowstride of @dest_buf
1485  * @dest_channels: number of channels in @dest_buf
1486  * @dest_has_alpha: whether @dest_buf has alpha
1487  * @src_buf: pointer to source pixels
1488  * @src_width: width of source (used for clipping)
1489  * @src_height: height of source (used for clipping)
1490  * @src_rowstride: rowstride of source
1491  * @src_channels: number of channels in @src_buf
1492  * @src_has_alpha: whether @src_buf has alpha
1493  * @scale_x: amount to scale source by in X direction
1494  * @scale_y: amount to scale source by in Y direction
1495  * @interp_type: type of enumeration
1496  * @overall_alpha: overall alpha factor to multiply source by
1497  * 
1498  * Scale source buffer by scale_x / scale_y, then composite a given rectangle
1499  * of the result into the destination buffer.
1500  **/
1501 void
1502 pixops_composite (guchar        *dest_buf,
1503                   int            render_x0,
1504                   int            render_y0,
1505                   int            render_x1,
1506                   int            render_y1,
1507                   int            dest_rowstride,
1508                   int            dest_channels,
1509                   gboolean       dest_has_alpha,
1510                   const guchar  *src_buf,
1511                   int            src_width,
1512                   int            src_height,
1513                   int            src_rowstride,
1514                   int            src_channels,
1515                   gboolean       src_has_alpha,
1516                   double         scale_x,
1517                   double         scale_y,
1518                   PixopsInterpType  interp_type,
1519                   int            overall_alpha)
1520 {
1521   PixopsFilter filter;
1522   PixopsLineFunc line_func;
1523   
1524 #ifdef USE_MMX
1525   gboolean found_mmx = pixops_have_mmx();
1526 #endif
1527
1528   g_return_if_fail (!(dest_channels == 3 && dest_has_alpha));
1529   g_return_if_fail (!(src_channels == 3 && src_has_alpha));
1530
1531   if (scale_x == 0 || scale_y == 0)
1532     return;
1533
1534   if (!src_has_alpha && overall_alpha == 255)
1535     pixops_scale (dest_buf, render_x0, render_y0, render_x1, render_y1,
1536                   dest_rowstride, dest_channels, dest_has_alpha,
1537                   src_buf, src_width, src_height, src_rowstride, src_channels,
1538                   src_has_alpha, scale_x, scale_y, interp_type);
1539
1540   switch (interp_type)
1541     {
1542     case PIXOPS_INTERP_NEAREST:
1543       pixops_composite_nearest (dest_buf, render_x0, render_y0, render_x1, render_y1,
1544                                 dest_rowstride, dest_channels, dest_has_alpha,
1545                                 src_buf, src_width, src_height, src_rowstride, src_channels,
1546                                 src_has_alpha, scale_x, scale_y, overall_alpha);
1547       return;
1548
1549     case PIXOPS_INTERP_TILES:
1550       tile_make_weights (&filter, scale_x, scale_y, overall_alpha / 255.);
1551       break;
1552       
1553     case PIXOPS_INTERP_BILINEAR:
1554       bilinear_make_fast_weights (&filter, scale_x, scale_y, overall_alpha / 255.);
1555       break;
1556       
1557     case PIXOPS_INTERP_HYPER:
1558       bilinear_make_weights (&filter, scale_x, scale_y, overall_alpha / 255.);
1559       break;
1560     }
1561
1562   if (filter.n_x == 2 && filter.n_y == 2 &&
1563       dest_channels == 4 && src_channels == 4 && src_has_alpha && !dest_has_alpha)
1564     {
1565 #ifdef USE_MMX
1566       if (found_mmx)
1567         line_func = composite_line_22_4a4_mmx_stub;
1568       else
1569 #endif  
1570         line_func = composite_line_22_4a4;
1571     }
1572   else
1573     line_func = composite_line;
1574   
1575   pixops_process (dest_buf, render_x0, render_y0, render_x1, render_y1,
1576                   dest_rowstride, dest_channels, dest_has_alpha,
1577                   src_buf, src_width, src_height, src_rowstride, src_channels,
1578                   src_has_alpha, scale_x, scale_y, 0, 0, 0, 0, 0, 
1579                   &filter, line_func, composite_pixel);
1580
1581   g_free (filter.weights);
1582 }
1583
1584 void
1585 pixops_scale (guchar        *dest_buf,
1586               int            render_x0,
1587               int            render_y0,
1588               int            render_x1,
1589               int            render_y1,
1590               int            dest_rowstride,
1591               int            dest_channels,
1592               gboolean       dest_has_alpha,
1593               const guchar  *src_buf,
1594               int            src_width,
1595               int            src_height,
1596               int            src_rowstride,
1597               int            src_channels,
1598               gboolean       src_has_alpha,
1599               double         scale_x,
1600               double         scale_y,
1601               PixopsInterpType  interp_type)
1602 {
1603   PixopsFilter filter;
1604   PixopsLineFunc line_func;
1605
1606 #ifdef USE_MMX
1607   gboolean found_mmx = pixops_have_mmx();
1608 #endif
1609
1610   g_return_if_fail (!(dest_channels == 3 && dest_has_alpha));
1611   g_return_if_fail (!(src_channels == 3 && src_has_alpha));
1612   g_return_if_fail (!(src_has_alpha && !dest_has_alpha));
1613
1614   if (scale_x == 0 || scale_y == 0)
1615     return;
1616
1617   switch (interp_type)
1618     {
1619     case PIXOPS_INTERP_NEAREST:
1620       pixops_scale_nearest (dest_buf, render_x0, render_y0, render_x1, render_y1,
1621                             dest_rowstride, dest_channels, dest_has_alpha,
1622                             src_buf, src_width, src_height, src_rowstride, src_channels, src_has_alpha,
1623                             scale_x, scale_y);
1624       return;
1625
1626     case PIXOPS_INTERP_TILES:
1627       tile_make_weights (&filter, scale_x, scale_y, 1.0);
1628       break;
1629       
1630     case PIXOPS_INTERP_BILINEAR:
1631       bilinear_make_fast_weights (&filter, scale_x, scale_y, 1.0);
1632       break;
1633       
1634     case PIXOPS_INTERP_HYPER:
1635       bilinear_make_weights (&filter, scale_x, scale_y, 1.0);
1636       break;
1637     }
1638
1639   if (filter.n_x == 2 && filter.n_y == 2 && dest_channels == 3 && src_channels == 3)
1640     {
1641 #ifdef USE_MMX
1642       if (found_mmx)
1643         line_func = scale_line_22_33_mmx_stub;
1644       else
1645 #endif
1646         line_func = scale_line_22_33;
1647     }
1648   else
1649     line_func = scale_line;
1650   
1651   pixops_process (dest_buf, render_x0, render_y0, render_x1, render_y1,
1652                   dest_rowstride, dest_channels, dest_has_alpha,
1653                   src_buf, src_width, src_height, src_rowstride, src_channels,
1654                   src_has_alpha, scale_x, scale_y, 0, 0, 0, 0, 0,
1655                   &filter, line_func, scale_pixel);
1656
1657   g_free (filter.weights);
1658 }
1659