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