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