]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/pixops/pixops.c
Include "config.h" instead of <config.h> Command used: find -name
[~andy/gtk] / gdk-pixbuf / pixops / pixops.c
1 /*
2  * Copyright (C) 2000 Red Hat, Inc
3  * mediaLib integration Copyright (c) 2001-2007 Sun Microsystems, Inc.
4  * All rights reserved.  (Brian Cameron, Dmitriy Demin, James Cheng,
5  * Padraig O'Briain)
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22 #include "config.h"
23 #include <math.h>
24 #include <glib.h>
25
26 #include "pixops.h"
27 #include "pixops-internal.h"
28
29 #define SUBSAMPLE_BITS 4
30 #define SUBSAMPLE (1 << SUBSAMPLE_BITS)
31 #define SUBSAMPLE_MASK ((1 << SUBSAMPLE_BITS)-1)
32 #define SCALE_SHIFT 16
33
34 static void
35 _pixops_scale_real (guchar        *dest_buf,
36                     int            render_x0,
37                     int            render_y0,
38                     int            render_x1,
39                     int            render_y1,
40                     int            dest_rowstride,
41                     int            dest_channels,
42                     gboolean       dest_has_alpha,
43                     const guchar  *src_buf,
44                     int            src_width,
45                     int            src_height,
46                     int            src_rowstride,
47                     int            src_channels,
48                     gboolean       src_has_alpha,
49                     double         scale_x,
50                     double         scale_y,
51                     PixopsInterpType  interp_type);
52
53 typedef struct _PixopsFilter PixopsFilter;
54 typedef struct _PixopsFilterDimension PixopsFilterDimension;
55
56 struct _PixopsFilterDimension
57 {
58   int n;
59   double offset;
60   double *weights;
61 };
62
63 struct _PixopsFilter
64 {
65   PixopsFilterDimension x;
66   PixopsFilterDimension y;
67   double overall_alpha;
68 }; 
69
70 typedef guchar *(*PixopsLineFunc) (int *weights, int n_x, int n_y,
71                                    guchar *dest, int dest_x, guchar *dest_end,
72                                    int dest_channels, int dest_has_alpha,
73                                    guchar **src, int src_channels,
74                                    gboolean src_has_alpha, int x_init,
75                                    int x_step, int src_width, int check_size,
76                                    guint32 color1, guint32 color2);
77 typedef void (*PixopsPixelFunc)   (guchar *dest, int dest_x, int dest_channels,
78                                    int dest_has_alpha, int src_has_alpha,
79                                    int check_size, guint32 color1,
80                                    guint32 color2,
81                                    guint r, guint g, guint b, guint a);
82
83 #ifdef USE_MEDIALIB
84 #include <stdlib.h>
85 #include <dlfcn.h>
86 #include <mlib_image.h>
87
88 #ifdef HAVE_STRINGS_H
89 #include <strings.h>
90 #endif
91
92 #ifdef HAVE_STRING_H
93 #include <string.h>
94 #endif
95
96 #if defined(HAVE_SYS_SYSTEMINFO_H)
97 #include <sys/systeminfo.h>
98 #elif defined(HAVE_SYS_SYSINFO_H)
99 #include <sys/sysinfo.h>
100 #endif
101
102 static void pixops_medialib_composite    (guchar          *dest_buf,
103                                           int              dest_width,
104                                           int              dest_height,
105                                           int              dest_rowstride,
106                                           int              dest_channels,
107                                           int              dest_has_alpha,
108                                           const guchar    *src_buf,
109                                           int              src_width,
110                                           int              src_height,
111                                           int              src_rowstride,
112                                           int              src_channels,
113                                           int              src_has_alpha,
114                                           int              dest_x,
115                                           int              dest_y,
116                                           int              dest_region_width,
117                                           int              dest_region_height,
118                                           double           offset_x,
119                                           double           offset_y,
120                                           double           scale_x,
121                                           double           scale_y,
122                                           PixopsInterpType interp_type,
123                                           int              overall_alpha);
124
125 static void pixops_medialib_scale        (guchar          *dest_buf,
126                                           int              dest_width,
127                                           int              dest_height,
128                                           int              dest_rowstride,
129                                           int              dest_channels,
130                                           int              dest_has_alpha,
131                                           const guchar    *src_buf,
132                                           int              src_width,
133                                           int              src_height,
134                                           int              src_rowstride,
135                                           int              src_channels,
136                                           int              src_has_alpha,
137                                           int              dest_x,
138                                           int              dest_y,
139                                           int              dest_region_width,
140                                           int              dest_region_height,
141                                           double           offset_x,
142                                           double           offset_y,
143                                           double           scale_x,
144                                           double           scale_y,
145                                           PixopsInterpType interp_type);
146
147 typedef struct _mlInterp mlInterp;
148
149 struct _mlInterp
150 {
151   double       tx;
152   double       ty;
153   PixopsFilter po_filter;
154   void         *interp_table;
155 };
156
157 static gboolean medialib_initialized = FALSE;
158 static gboolean use_medialib         = TRUE;
159
160 /*
161  * Sun mediaLib(tm) support.
162  *
163  *   http://www.sun.com/processors/vis/mlib.html
164  *
165  */
166 static void
167 _pixops_use_medialib ()
168 {
169   char *mlib_version_string;
170   char  sys_info[257];
171   long  count;
172
173   medialib_initialized = TRUE; 
174
175   if (getenv ("GDK_DISABLE_MEDIALIB"))
176     {
177       use_medialib = FALSE;
178       return;
179     }
180
181   /*
182    * The imaging functions we want to use were added in mediaLib version 2.
183    * So turn off mediaLib support if the user has an older version.
184    * mlib_version returns a string in this format:
185    *
186    * mediaLib:0210:20011101:v8plusa
187    * ^^^^^^^^ ^^^^ ^^^^^^^^ ^^^^^^^
188    * libname  vers  build   ISALIST identifier
189    *                date    (in this case sparcv8plus+vis)
190    * 
191    * The first 2 digits of the version are the major version.  The 3rd digit
192    * is the minor version, and the 4th digit is the micro version.  So the
193    * above string corresponds to version 2.1.0.  In the following test we only
194    * care about the major version.
195    */
196   mlib_version_string = mlib_version ();
197
198   count = sysinfo (SI_ARCHITECTURE, &sys_info[0], 257);
199
200   if (count != -1)
201     {
202       if (strcmp (sys_info, "i386") == 0)
203         {
204           char *mlib_target_isa = &mlib_version_string[23];
205
206           /*
207            * For x86 processors mediaLib generic C implementation
208            * does not give any performance advantage so disable it
209            */
210           if (strncmp (mlib_target_isa, "sse", 3) != 0)
211             {
212               use_medialib = FALSE;
213               return;
214             }
215
216           /*
217            * For x86 processors use of libumem conflicts with
218            * mediaLib, so avoid using it.
219            */
220           if ((dlsym (RTLD_DEFAULT, "umem_alloc") != NULL) ||
221               (dlsym (RTLD_PROBE,   "umem_alloc") != NULL) ||
222               (dlsym (RTLD_NEXT,    "umem_alloc") != NULL) ||
223               (dlsym (RTLD_SELF,    "umem_alloc") != NULL))
224             {
225               use_medialib = FALSE;
226               return;
227             }
228         }
229     }
230   else
231     {
232       /* Failed to get system architecture, disable mediaLib anyway */
233       use_medialib = FALSE;
234       return;
235     }
236 }
237 #endif
238
239 static int
240 get_check_shift (int check_size)
241 {
242   int check_shift = 0;
243   g_return_val_if_fail (check_size >= 0, 4);
244
245   while (!(check_size & 1))
246     {
247       check_shift++;
248       check_size >>= 1;
249     }
250
251   return check_shift;
252 }
253
254 static void
255 pixops_scale_nearest (guchar        *dest_buf,
256                       int            render_x0,
257                       int            render_y0,
258                       int            render_x1,
259                       int            render_y1,
260                       int            dest_rowstride,
261                       int            dest_channels,
262                       gboolean       dest_has_alpha,
263                       const guchar  *src_buf,
264                       int            src_width,
265                       int            src_height,
266                       int            src_rowstride,
267                       int            src_channels,
268                       gboolean       src_has_alpha,
269                       double         scale_x,
270                       double         scale_y)
271 {
272   int i;
273   int x;
274   int x_step = (1 << SCALE_SHIFT) / scale_x;
275   int y_step = (1 << SCALE_SHIFT) / scale_y;
276   int xmax, xstart, xstop, x_pos, y_pos;
277   const guchar *p;
278
279 #define INNER_LOOP(SRC_CHANNELS,DEST_CHANNELS,ASSIGN_PIXEL)     \
280       xmax = x + (render_x1 - render_x0) * x_step;              \
281       xstart = MIN (0, xmax);                                   \
282       xstop = MIN (src_width << SCALE_SHIFT, xmax);             \
283       p = src + (CLAMP (x, xstart, xstop) >> SCALE_SHIFT) * SRC_CHANNELS; \
284       while (x < xstart)                                        \
285         {                                                       \
286           ASSIGN_PIXEL;                                         \
287           dest += DEST_CHANNELS;                                \
288           x += x_step;                                          \
289         }                                                       \
290       while (x < xstop)                                         \
291         {                                                       \
292           p = src + (x >> SCALE_SHIFT) * SRC_CHANNELS;          \
293           ASSIGN_PIXEL;                                         \
294           dest += DEST_CHANNELS;                                \
295           x += x_step;                                          \
296         }                                                       \
297       x_pos = x >> SCALE_SHIFT;                                 \
298       p = src + CLAMP (x_pos, 0, src_width - 1) * SRC_CHANNELS; \
299       while (x < xmax)                                          \
300         {                                                       \
301           ASSIGN_PIXEL;                                         \
302           dest += DEST_CHANNELS;                                \
303           x += x_step;                                          \
304         }
305
306   for (i = 0; i < (render_y1 - render_y0); i++)
307     {
308       const guchar *src;
309       guchar       *dest;
310       y_pos = ((i + render_y0) * y_step + y_step / 2) >> SCALE_SHIFT;
311       y_pos = CLAMP (y_pos, 0, src_height - 1);
312       src  = src_buf + y_pos * src_rowstride;
313       dest = dest_buf + i * dest_rowstride;
314
315       x = render_x0 * x_step + x_step / 2;
316
317       if (src_channels == 3)
318         {
319           if (dest_channels == 3)
320             {
321               INNER_LOOP (3, 3, dest[0]=p[0];dest[1]=p[1];dest[2]=p[2]);
322             }
323           else
324             {
325               INNER_LOOP (3, 4, dest[0]=p[0];dest[1]=p[1];dest[2]=p[2];dest[3]=0xff);
326             }
327         }
328       else if (src_channels == 4)
329         {
330           if (dest_channels == 3)
331             {
332               INNER_LOOP (4, 3, dest[0]=p[0];dest[1]=p[1];dest[2]=p[2]);
333             }
334           else
335             {
336               guint32 *p32;
337               INNER_LOOP(4, 4, p32=(guint32*)dest;*p32=*((guint32*)p));
338             }
339         }
340     }
341 }
342
343 static void
344 pixops_composite_nearest (guchar        *dest_buf,
345                           int            render_x0,
346                           int            render_y0,
347                           int            render_x1,
348                           int            render_y1,
349                           int            dest_rowstride,
350                           int            dest_channels,
351                           gboolean       dest_has_alpha,
352                           const guchar  *src_buf,
353                           int            src_width,
354                           int            src_height,
355                           int            src_rowstride,
356                           int            src_channels,
357                           gboolean       src_has_alpha,
358                           double         scale_x,
359                           double         scale_y,
360                           int            overall_alpha)
361 {
362   int i;
363   int x;
364   int x_step = (1 << SCALE_SHIFT) / scale_x;
365   int y_step = (1 << SCALE_SHIFT) / scale_y;
366   int xmax, xstart, xstop, x_pos, y_pos;
367   const guchar *p;
368   unsigned int  a0;
369
370   for (i = 0; i < (render_y1 - render_y0); i++)
371     {
372       const guchar *src;
373       guchar       *dest;
374       y_pos = ((i + render_y0) * y_step + y_step / 2) >> SCALE_SHIFT;
375       y_pos = CLAMP (y_pos, 0, src_height - 1);
376       src  = src_buf + y_pos * src_rowstride;
377       dest = dest_buf + i * dest_rowstride;
378
379       x = render_x0 * x_step + x_step / 2;
380       
381       INNER_LOOP(src_channels, dest_channels,
382           if (src_has_alpha)
383             a0 = (p[3] * overall_alpha) / 0xff;
384           else
385             a0 = overall_alpha;
386
387           switch (a0)
388             {
389             case 0:
390               break;
391             case 255:
392               dest[0] = p[0];
393               dest[1] = p[1];
394               dest[2] = p[2];
395               if (dest_has_alpha)
396                 dest[3] = 0xff;
397               break;
398             default:
399               if (dest_has_alpha)
400                 {
401                   unsigned int w0 = 0xff * a0;
402                   unsigned int w1 = (0xff - a0) * dest[3];
403                   unsigned int w = w0 + w1;
404
405                   dest[0] = (w0 * p[0] + w1 * dest[0]) / w;
406                   dest[1] = (w0 * p[1] + w1 * dest[1]) / w;
407                   dest[2] = (w0 * p[2] + w1 * dest[2]) / w;
408                   dest[3] = w / 0xff;
409                 }
410               else
411                 {
412                   unsigned int a1 = 0xff - a0;
413                   unsigned int tmp;
414
415                   tmp = a0 * p[0] + a1 * dest[0] + 0x80;
416                   dest[0] = (tmp + (tmp >> 8)) >> 8;
417                   tmp = a0 * p[1] + a1 * dest[1] + 0x80;
418                   dest[1] = (tmp + (tmp >> 8)) >> 8;
419                   tmp = a0 * p[2] + a1 * dest[2] + 0x80;
420                   dest[2] = (tmp + (tmp >> 8)) >> 8;
421                 }
422               break;
423             }
424         );
425     }
426 }
427
428 static void
429 pixops_composite_color_nearest (guchar        *dest_buf,
430                                 int            render_x0,
431                                 int            render_y0,
432                                 int            render_x1,
433                                 int            render_y1,
434                                 int            dest_rowstride,
435                                 int            dest_channels,
436                                 gboolean       dest_has_alpha,
437                                 const guchar  *src_buf,
438                                 int            src_width,
439                                 int            src_height,
440                                 int            src_rowstride,
441                                 int            src_channels,
442                                 gboolean       src_has_alpha,
443                                 double         scale_x,
444                                 double         scale_y,
445                                 int            overall_alpha,
446                                 int            check_x,
447                                 int            check_y,
448                                 int            check_size,
449                                 guint32        color1,
450                                 guint32        color2)
451 {
452   int i, j;
453   int x;
454   int x_step = (1 << SCALE_SHIFT) / scale_x;
455   int y_step = (1 << SCALE_SHIFT) / scale_y;
456   int r1, g1, b1, r2, g2, b2;
457   int check_shift = get_check_shift (check_size);
458   int xmax, xstart, xstop, x_pos, y_pos;
459   const guchar *p;
460   unsigned int  a0;
461
462   for (i = 0; i < (render_y1 - render_y0); i++)
463     {
464       const guchar *src;
465       guchar       *dest;
466       y_pos = ((i + render_y0) * y_step + y_step / 2) >> SCALE_SHIFT;
467       y_pos = CLAMP (y_pos, 0, src_height - 1);
468       src  = src_buf + y_pos * src_rowstride;
469       dest = dest_buf + i * dest_rowstride;
470
471       x = render_x0 * x_step + x_step / 2;
472       
473       
474       if (((i + check_y) >> check_shift) & 1)
475         {
476           r1 = (color2 & 0xff0000) >> 16;
477           g1 = (color2 & 0xff00) >> 8;
478           b1 = color2 & 0xff;
479
480           r2 = (color1 & 0xff0000) >> 16;
481           g2 = (color1 & 0xff00) >> 8;
482           b2 = color1 & 0xff;
483         }
484       else
485         {
486           r1 = (color1 & 0xff0000) >> 16;
487           g1 = (color1 & 0xff00) >> 8;
488           b1 = color1 & 0xff;
489
490           r2 = (color2 & 0xff0000) >> 16;
491           g2 = (color2 & 0xff00) >> 8;
492           b2 = color2 & 0xff;
493         }
494
495       j = 0;
496       INNER_LOOP(src_channels, dest_channels,
497           if (src_has_alpha)
498             a0 = (p[3] * overall_alpha + 0xff) >> 8;
499           else
500             a0 = overall_alpha;
501
502           switch (a0)
503             {
504             case 0:
505               if (((j + check_x) >> check_shift) & 1)
506                 {
507                   dest[0] = r2; 
508                   dest[1] = g2; 
509                   dest[2] = b2;
510                 }
511               else
512                 {
513                   dest[0] = r1; 
514                   dest[1] = g1; 
515                   dest[2] = b1;
516                 }
517             break;
518             case 255:
519               dest[0] = p[0];
520               dest[1] = p[1];
521               dest[2] = p[2];
522               break;
523             default:
524                      {
525                        unsigned int tmp;
526               if (((j + check_x) >> check_shift) & 1)
527                 {
528                   tmp = ((int) p[0] - r2) * a0;
529                   dest[0] = r2 + ((tmp + (tmp >> 8) + 0x80) >> 8);
530                   tmp = ((int) p[1] - g2) * a0;
531                   dest[1] = g2 + ((tmp + (tmp >> 8) + 0x80) >> 8);
532                   tmp = ((int) p[2] - b2) * a0;
533                   dest[2] = b2 + ((tmp + (tmp >> 8) + 0x80) >> 8);
534                 }
535               else
536                 {
537                   tmp = ((int) p[0] - r1) * a0;
538                   dest[0] = r1 + ((tmp + (tmp >> 8) + 0x80) >> 8);
539                   tmp = ((int) p[1] - g1) * a0;
540                   dest[1] = g1 + ((tmp + (tmp >> 8) + 0x80) >> 8);
541                   tmp = ((int) p[2] - b1) * a0;
542                   dest[2] = b1 + ((tmp + (tmp >> 8) + 0x80) >> 8);
543                 }
544                      }
545               break;
546             }
547           
548           if (dest_channels == 4)
549             dest[3] = 0xff;
550
551                  j++;
552         );
553     }
554 }
555 #undef INNER_LOOP
556
557 static void
558 composite_pixel (guchar *dest, int dest_x, int dest_channels, int dest_has_alpha,
559                  int src_has_alpha, int check_size, guint32 color1, guint32 color2,
560                  guint r, guint g, guint b, guint a)
561 {
562   if (dest_has_alpha)
563     {
564       unsigned int w0 = a - (a >> 8);
565       unsigned int w1 = ((0xff0000 - a) >> 8) * dest[3];
566       unsigned int w = w0 + w1;
567       
568       if (w != 0)
569         {
570           dest[0] = (r - (r >> 8) + w1 * dest[0]) / w;
571           dest[1] = (g - (g >> 8) + w1 * dest[1]) / w;
572           dest[2] = (b - (b >> 8) + w1 * dest[2]) / w;
573           dest[3] = w / 0xff00;
574         }
575       else
576         {
577           dest[0] = 0;
578           dest[1] = 0;
579           dest[2] = 0;
580           dest[3] = 0;
581         }
582     }
583   else
584     {
585       dest[0] = (r + (0xff0000 - a) * dest[0]) / 0xff0000;
586       dest[1] = (g + (0xff0000 - a) * dest[1]) / 0xff0000;
587       dest[2] = (b + (0xff0000 - a) * dest[2]) / 0xff0000;
588     }
589 }
590
591 static guchar *
592 composite_line (int *weights, int n_x, int n_y,
593                 guchar *dest, int dest_x, guchar *dest_end, int dest_channels, int dest_has_alpha,
594                 guchar **src, int src_channels, gboolean src_has_alpha,
595                 int x_init, int x_step, int src_width,
596                 int check_size, guint32 color1, guint32 color2)
597 {
598   int x = x_init;
599   int i, j;
600
601   while (dest < dest_end)
602     {
603       int x_scaled = x >> SCALE_SHIFT;
604       unsigned int r = 0, g = 0, b = 0, a = 0;
605       int *pixel_weights;
606       
607       pixel_weights = weights + ((x >> (SCALE_SHIFT - SUBSAMPLE_BITS)) & SUBSAMPLE_MASK) * n_x * n_y;
608       
609       for (i=0; i<n_y; i++)
610         {
611           guchar *q = src[i] + x_scaled * src_channels;
612           int *line_weights = pixel_weights + n_x * i;
613           
614           for (j=0; j<n_x; j++)
615             {
616               unsigned int ta;
617
618               if (src_has_alpha)
619                 ta = q[3] * line_weights[j];
620               else
621                 ta = 0xff * line_weights[j];
622               
623               r += ta * q[0];
624               g += ta * q[1];
625               b += ta * q[2];
626               a += ta;
627
628               q += src_channels;
629             }
630         }
631
632       if (dest_has_alpha)
633         {
634           unsigned int w0 = a - (a >> 8);
635           unsigned int w1 = ((0xff0000 - a) >> 8) * dest[3];
636           unsigned int w = w0 + w1;
637
638           if (w != 0)
639             {
640               dest[0] = (r - (r >> 8) + w1 * dest[0]) / w;
641               dest[1] = (g - (g >> 8) + w1 * dest[1]) / w;
642               dest[2] = (b - (b >> 8) + w1 * dest[2]) / w;
643               dest[3] = w / 0xff00;
644             }
645           else
646             {
647               dest[0] = 0;
648               dest[1] = 0;
649               dest[2] = 0;
650               dest[3] = 0;
651             }
652         }
653       else
654         {
655           dest[0] = (r + (0xff0000 - a) * dest[0]) / 0xff0000;
656           dest[1] = (g + (0xff0000 - a) * dest[1]) / 0xff0000;
657           dest[2] = (b + (0xff0000 - a) * dest[2]) / 0xff0000;
658         }
659       
660       dest += dest_channels;
661       x += x_step;
662     }
663
664   return dest;
665 }
666
667 static guchar *
668 composite_line_22_4a4 (int *weights, int n_x, int n_y,
669                        guchar *dest, int dest_x, guchar *dest_end, int dest_channels, int dest_has_alpha,
670                        guchar **src, int src_channels, gboolean src_has_alpha,
671                        int x_init, int x_step, int src_width,
672                        int check_size, guint32 color1, guint32 color2)
673 {
674   int x = x_init;
675   guchar *src0 = src[0];
676   guchar *src1 = src[1];
677
678   g_return_val_if_fail (src_channels != 3, dest);
679   g_return_val_if_fail (src_has_alpha, dest);
680   
681   while (dest < dest_end)
682     {
683       int x_scaled = x >> SCALE_SHIFT;
684       unsigned int r, g, b, a, ta;
685       int *pixel_weights;
686       guchar *q0, *q1;
687       int w1, w2, w3, w4;
688       
689       q0 = src0 + x_scaled * 4;
690       q1 = src1 + x_scaled * 4;
691       
692       pixel_weights = (int *)((char *)weights +
693         ((x >> (SCALE_SHIFT - SUBSAMPLE_BITS - 4)) & (SUBSAMPLE_MASK << 4)));
694       
695       w1 = pixel_weights[0];
696       w2 = pixel_weights[1];
697       w3 = pixel_weights[2];
698       w4 = pixel_weights[3];
699
700       a = w1 * q0[3];
701       r = a * q0[0];
702       g = a * q0[1];
703       b = a * q0[2];
704
705       ta = w2 * q0[7];
706       r += ta * q0[4];
707       g += ta * q0[5];
708       b += ta * q0[6];
709       a += ta;
710
711       ta = w3 * q1[3];
712       r += ta * q1[0];
713       g += ta * q1[1];
714       b += ta * q1[2];
715       a += ta;
716
717       ta = w4 * q1[7];
718       r += ta * q1[4];
719       g += ta * q1[5];
720       b += ta * q1[6];
721       a += ta;
722
723       dest[0] = ((0xff0000 - a) * dest[0] + r) >> 24;
724       dest[1] = ((0xff0000 - a) * dest[1] + g) >> 24;
725       dest[2] = ((0xff0000 - a) * dest[2] + b) >> 24;
726       dest[3] = a >> 16;
727       
728       dest += 4;
729       x += x_step;
730     }
731
732   return dest;
733 }
734
735 #ifdef USE_MMX
736 static guchar *
737 composite_line_22_4a4_mmx_stub (int *weights, int n_x, int n_y, guchar *dest,
738                                 int dest_x, guchar *dest_end,
739                                 int dest_channels, int dest_has_alpha,
740                                 guchar **src, int src_channels,
741                                 gboolean src_has_alpha, int x_init,
742                                 int x_step, int src_width, int check_size,
743                                 guint32 color1, guint32 color2)
744 {
745   guint32 mmx_weights[16][8];
746   int j;
747
748   for (j=0; j<16; j++)
749     {
750       mmx_weights[j][0] = 0x00010001 * (weights[4*j] >> 8);
751       mmx_weights[j][1] = 0x00010001 * (weights[4*j] >> 8);
752       mmx_weights[j][2] = 0x00010001 * (weights[4*j + 1] >> 8);
753       mmx_weights[j][3] = 0x00010001 * (weights[4*j + 1] >> 8);
754       mmx_weights[j][4] = 0x00010001 * (weights[4*j + 2] >> 8);
755       mmx_weights[j][5] = 0x00010001 * (weights[4*j + 2] >> 8);
756       mmx_weights[j][6] = 0x00010001 * (weights[4*j + 3] >> 8);
757       mmx_weights[j][7] = 0x00010001 * (weights[4*j + 3] >> 8);
758     }
759
760   return _pixops_composite_line_22_4a4_mmx (mmx_weights, dest, src[0], src[1],
761                                             x_step, dest_end, x_init);
762 }
763 #endif /* USE_MMX */
764
765 static void
766 composite_pixel_color (guchar *dest, int dest_x, int dest_channels,
767                        int dest_has_alpha, int src_has_alpha, int check_size,
768                        guint32 color1, guint32 color2, guint r, guint g,
769                        guint b, guint a)
770 {
771   int dest_r, dest_g, dest_b;
772   int check_shift = get_check_shift (check_size);
773
774   if ((dest_x >> check_shift) & 1)
775     {
776       dest_r = (color2 & 0xff0000) >> 16;
777       dest_g = (color2 & 0xff00) >> 8;
778       dest_b = color2 & 0xff;
779     }
780   else
781     {
782       dest_r = (color1 & 0xff0000) >> 16;
783       dest_g = (color1 & 0xff00) >> 8;
784       dest_b = color1 & 0xff;
785     }
786
787   dest[0] = ((0xff0000 - a) * dest_r + r) >> 24;
788   dest[1] = ((0xff0000 - a) * dest_g + g) >> 24;
789   dest[2] = ((0xff0000 - a) * dest_b + b) >> 24;
790
791   if (dest_has_alpha)
792     dest[3] = 0xff;
793   else if (dest_channels == 4)
794     dest[3] = a >> 16;
795 }
796
797 static guchar *
798 composite_line_color (int *weights, int n_x, int n_y, guchar *dest,
799                       int dest_x, guchar *dest_end, int dest_channels,
800                       int dest_has_alpha, guchar **src, int src_channels,
801                       gboolean src_has_alpha, int x_init, int x_step,
802                       int src_width, int check_size, guint32 color1,
803                       guint32 color2)
804 {
805   int x = x_init;
806   int i, j;
807   int check_shift = get_check_shift (check_size);
808   int dest_r1, dest_g1, dest_b1;
809   int dest_r2, dest_g2, dest_b2;
810
811   g_return_val_if_fail (check_size != 0, dest);
812
813   dest_r1 = (color1 & 0xff0000) >> 16;
814   dest_g1 = (color1 & 0xff00) >> 8;
815   dest_b1 = color1 & 0xff;
816
817   dest_r2 = (color2 & 0xff0000) >> 16;
818   dest_g2 = (color2 & 0xff00) >> 8;
819   dest_b2 = color2 & 0xff;
820
821   while (dest < dest_end)
822     {
823       int x_scaled = x >> SCALE_SHIFT;
824       unsigned int r = 0, g = 0, b = 0, a = 0;
825       int *pixel_weights;
826       
827       pixel_weights = weights + ((x >> (SCALE_SHIFT - SUBSAMPLE_BITS)) & SUBSAMPLE_MASK) * n_x * n_y;
828
829       for (i=0; i<n_y; i++)
830         {
831           guchar *q = src[i] + x_scaled * src_channels;
832           int *line_weights = pixel_weights + n_x * i;
833           
834           for (j=0; j<n_x; j++)
835             {
836               unsigned int ta;
837               
838               if (src_has_alpha)
839                 ta = q[3] * line_weights[j];
840               else
841                 ta = 0xff * line_weights[j];
842                   
843               r += ta * q[0];
844               g += ta * q[1];
845               b += ta * q[2];
846               a += ta;
847
848               q += src_channels;
849             }
850         }
851
852       if ((dest_x >> check_shift) & 1)
853         {
854           dest[0] = ((0xff0000 - a) * dest_r2 + r) >> 24;
855           dest[1] = ((0xff0000 - a) * dest_g2 + g) >> 24;
856           dest[2] = ((0xff0000 - a) * dest_b2 + b) >> 24;
857         }
858       else
859         {
860           dest[0] = ((0xff0000 - a) * dest_r1 + r) >> 24;
861           dest[1] = ((0xff0000 - a) * dest_g1 + g) >> 24;
862           dest[2] = ((0xff0000 - a) * dest_b1 + b) >> 24;
863         }
864
865       if (dest_has_alpha)
866         dest[3] = 0xff;
867       else if (dest_channels == 4)
868         dest[3] = a >> 16;
869         
870       dest += dest_channels;
871       x += x_step;
872       dest_x++;
873     }
874
875   return dest;
876 }
877
878 #ifdef USE_MMX
879 static guchar *
880 composite_line_color_22_4a4_mmx_stub (int *weights, int n_x, int n_y,
881                                       guchar *dest, int dest_x,
882                                       guchar *dest_end, int dest_channels,
883                                       int dest_has_alpha, guchar **src,
884                                       int src_channels, gboolean src_has_alpha,
885                                       int x_init, int x_step, int src_width,
886                                       int check_size, guint32 color1,
887                                       guint32 color2)
888 {
889   guint32 mmx_weights[16][8];
890   int check_shift = get_check_shift (check_size);
891   int colors[4];
892   int j;
893
894   for (j=0; j<16; j++)
895     {
896       mmx_weights[j][0] = 0x00010001 * (weights[4*j] >> 8);
897       mmx_weights[j][1] = 0x00010001 * (weights[4*j] >> 8);
898       mmx_weights[j][2] = 0x00010001 * (weights[4*j + 1] >> 8);
899       mmx_weights[j][3] = 0x00010001 * (weights[4*j + 1] >> 8);
900       mmx_weights[j][4] = 0x00010001 * (weights[4*j + 2] >> 8);
901       mmx_weights[j][5] = 0x00010001 * (weights[4*j + 2] >> 8);
902       mmx_weights[j][6] = 0x00010001 * (weights[4*j + 3] >> 8);
903       mmx_weights[j][7] = 0x00010001 * (weights[4*j + 3] >> 8);
904     }
905
906   colors[0] = (color1 & 0xff00) << 8 | (color1 & 0xff);
907   colors[1] = (color1 & 0xff0000) >> 16;
908   colors[2] = (color2 & 0xff00) << 8 | (color2 & 0xff);
909   colors[3] = (color2 & 0xff0000) >> 16;
910
911   return _pixops_composite_line_color_22_4a4_mmx (mmx_weights, dest, src[0],
912     src[1], x_step, dest_end, x_init, dest_x, check_shift, colors);
913 }
914 #endif /* USE_MMX */
915
916 static void
917 scale_pixel (guchar *dest, int dest_x, int dest_channels, int dest_has_alpha,
918              int src_has_alpha, int check_size, guint32 color1, guint32 color2,
919              guint r, guint g, guint b, guint a)
920 {
921   if (src_has_alpha)
922     {
923       if (a)
924         {
925           dest[0] = r / a;
926           dest[1] = g / a;
927           dest[2] = b / a;
928           dest[3] = a >> 16;
929         }
930       else
931         {
932           dest[0] = 0;
933           dest[1] = 0;
934           dest[2] = 0;
935           dest[3] = 0;
936         }
937     }
938   else
939     {
940       dest[0] = (r + 0xffffff) >> 24;
941       dest[1] = (g + 0xffffff) >> 24;
942       dest[2] = (b + 0xffffff) >> 24;
943       
944       if (dest_has_alpha)
945         dest[3] = 0xff;
946     }
947 }
948
949 static guchar *
950 scale_line (int *weights, int n_x, int n_y, guchar *dest, int dest_x,
951             guchar *dest_end, int dest_channels, int dest_has_alpha,
952             guchar **src, int src_channels, gboolean src_has_alpha, int x_init,
953             int x_step, int src_width, int check_size, guint32 color1,
954             guint32 color2)
955 {
956   int x = x_init;
957   int i, j;
958
959   while (dest < dest_end)
960     {
961       int x_scaled = x >> SCALE_SHIFT;
962       int *pixel_weights;
963
964       pixel_weights = weights +
965         ((x >> (SCALE_SHIFT - SUBSAMPLE_BITS)) & SUBSAMPLE_MASK) * n_x * n_y;
966
967       if (src_has_alpha)
968         {
969           unsigned int r = 0, g = 0, b = 0, a = 0;
970           for (i=0; i<n_y; i++)
971             {
972               guchar *q = src[i] + x_scaled * src_channels;
973               int *line_weights  = pixel_weights + n_x * i;
974               
975               for (j=0; j<n_x; j++)
976                 {
977                   unsigned int ta;
978                   
979                   ta = q[3] * line_weights[j];
980                   r += ta * q[0];
981                   g += ta * q[1];
982                   b += ta * q[2];
983                   a += ta;
984                   
985                   q += src_channels;
986                 }
987             }
988
989           if (a)
990             {
991               dest[0] = r / a;
992               dest[1] = g / a;
993               dest[2] = b / a;
994               dest[3] = a >> 16;
995             }
996           else
997             {
998               dest[0] = 0;
999               dest[1] = 0;
1000               dest[2] = 0;
1001               dest[3] = 0;
1002             }
1003         }
1004       else
1005         {
1006           unsigned int r = 0, g = 0, b = 0;
1007           for (i=0; i<n_y; i++)
1008             {
1009               guchar *q = src[i] + x_scaled * src_channels;
1010               int *line_weights  = pixel_weights + n_x * i;
1011               
1012               for (j=0; j<n_x; j++)
1013                 {
1014                   unsigned int ta = line_weights[j];
1015                   
1016                   r += ta * q[0];
1017                   g += ta * q[1];
1018                   b += ta * q[2];
1019
1020                   q += src_channels;
1021                 }
1022             }
1023
1024           dest[0] = (r + 0xffff) >> 16;
1025           dest[1] = (g + 0xffff) >> 16;
1026           dest[2] = (b + 0xffff) >> 16;
1027           
1028           if (dest_has_alpha)
1029             dest[3] = 0xff;
1030         }
1031
1032       dest += dest_channels;
1033       
1034       x += x_step;
1035     }
1036
1037   return dest;
1038 }
1039
1040 #ifdef USE_MMX 
1041 static guchar *
1042 scale_line_22_33_mmx_stub (int *weights, int n_x, int n_y, guchar *dest,
1043                            int dest_x, guchar *dest_end, int dest_channels,
1044                            int dest_has_alpha, guchar **src, int src_channels,
1045                            gboolean src_has_alpha, int x_init, int x_step,
1046                            int src_width, int check_size, guint32 color1,
1047                            guint32 color2)
1048 {
1049   guint32 mmx_weights[16][8];
1050   int j;
1051
1052   for (j=0; j<16; j++)
1053     {
1054       mmx_weights[j][0] = 0x00010001 * (weights[4*j] >> 8);
1055       mmx_weights[j][1] = 0x00010001 * (weights[4*j] >> 8);
1056       mmx_weights[j][2] = 0x00010001 * (weights[4*j + 1] >> 8);
1057       mmx_weights[j][3] = 0x00010001 * (weights[4*j + 1] >> 8);
1058       mmx_weights[j][4] = 0x00010001 * (weights[4*j + 2] >> 8);
1059       mmx_weights[j][5] = 0x00010001 * (weights[4*j + 2] >> 8);
1060       mmx_weights[j][6] = 0x00010001 * (weights[4*j + 3] >> 8);
1061       mmx_weights[j][7] = 0x00010001 * (weights[4*j + 3] >> 8);
1062     }
1063
1064   return _pixops_scale_line_22_33_mmx (mmx_weights, dest, src[0], src[1],
1065                                        x_step, dest_end, x_init);
1066 }
1067 #endif /* USE_MMX */
1068
1069 static guchar *
1070 scale_line_22_33 (int *weights, int n_x, int n_y, guchar *dest, int dest_x,
1071                   guchar *dest_end, int dest_channels, int dest_has_alpha,
1072                   guchar **src, int src_channels, gboolean src_has_alpha,
1073                   int x_init, int x_step, int src_width,
1074                   int check_size, guint32 color1, guint32 color2)
1075 {
1076   int x = x_init;
1077   guchar *src0 = src[0];
1078   guchar *src1 = src[1];
1079   
1080   while (dest < dest_end)
1081     {
1082       unsigned int r, g, b;
1083       int x_scaled = x >> SCALE_SHIFT;
1084       int *pixel_weights;
1085       guchar *q0, *q1;
1086       int w1, w2, w3, w4;
1087
1088       q0 = src0 + x_scaled * 3;
1089       q1 = src1 + x_scaled * 3;
1090       
1091       pixel_weights = weights +
1092         ((x >> (SCALE_SHIFT - SUBSAMPLE_BITS)) & SUBSAMPLE_MASK) * 4;
1093
1094       w1 = pixel_weights[0];
1095       w2 = pixel_weights[1];
1096       w3 = pixel_weights[2];
1097       w4 = pixel_weights[3];
1098
1099       r = w1 * q0[0];
1100       g = w1 * q0[1];
1101       b = w1 * q0[2];
1102
1103       r += w2 * q0[3];
1104       g += w2 * q0[4];
1105       b += w2 * q0[5];
1106
1107       r += w3 * q1[0];
1108       g += w3 * q1[1];
1109       b += w3 * q1[2];
1110
1111       r += w4 * q1[3];
1112       g += w4 * q1[4];
1113       b += w4 * q1[5];
1114
1115       dest[0] = (r + 0x8000) >> 16;
1116       dest[1] = (g + 0x8000) >> 16;
1117       dest[2] = (b + 0x8000) >> 16;
1118       
1119       dest += 3;
1120       x += x_step;
1121     }
1122   
1123   return dest;
1124 }
1125
1126 static void
1127 process_pixel (int *weights, int n_x, int n_y, guchar *dest, int dest_x,
1128                int dest_channels, int dest_has_alpha, guchar **src,
1129                int src_channels, gboolean src_has_alpha, int x_start,
1130                int src_width, int check_size, guint32 color1, guint32 color2,
1131                PixopsPixelFunc pixel_func)
1132 {
1133   unsigned int r = 0, g = 0, b = 0, a = 0;
1134   int i, j;
1135   
1136   for (i=0; i<n_y; i++)
1137     {
1138       int *line_weights  = weights + n_x * i;
1139
1140       for (j=0; j<n_x; j++)
1141         {
1142           unsigned int ta;
1143           guchar *q;
1144
1145           if (x_start + j < 0)
1146             q = src[i];
1147           else if (x_start + j < src_width)
1148             q = src[i] + (x_start + j) * src_channels;
1149           else
1150             q = src[i] + (src_width - 1) * src_channels;
1151
1152           if (src_has_alpha)
1153             ta = q[3] * line_weights[j];
1154           else
1155             ta = 0xff * line_weights[j];
1156
1157           r += ta * q[0];
1158           g += ta * q[1];
1159           b += ta * q[2];
1160           a += ta;
1161         }
1162     }
1163
1164   (*pixel_func) (dest, dest_x, dest_channels, dest_has_alpha, src_has_alpha,
1165     check_size, color1, color2, r, g, b, a);
1166 }
1167
1168 static void 
1169 correct_total (int    *weights, 
1170                int    n_x, 
1171                int    n_y,
1172                int    total, 
1173                double overall_alpha)
1174 {
1175   int correction = (int)(0.5 + 65536 * overall_alpha) - total;
1176   int remaining, c, d, i;
1177   
1178   if (correction != 0)
1179     {
1180       remaining = correction;
1181       for (d = 1, c = correction; c != 0 && remaining != 0; d++, c = correction / d) 
1182         for (i = n_x * n_y - 1; i >= 0 && c != 0 && remaining != 0; i--) 
1183           if (*(weights + i) + c >= 0) 
1184             {
1185               *(weights + i) += c;
1186               remaining -= c;
1187               if ((0 < remaining && remaining < c) ||
1188                   (0 > remaining && remaining > c))
1189                 c = remaining;
1190             }
1191     }
1192 }
1193
1194 static int *
1195 make_filter_table (PixopsFilter *filter)
1196 {
1197   int i_offset, j_offset;
1198   int n_x = filter->x.n;
1199   int n_y = filter->y.n;
1200   int *weights = g_new (int, SUBSAMPLE * SUBSAMPLE * n_x * n_y);
1201
1202   for (i_offset=0; i_offset < SUBSAMPLE; i_offset++)
1203     for (j_offset=0; j_offset < SUBSAMPLE; j_offset++)
1204       {
1205         double weight;
1206         int *pixel_weights = weights + ((i_offset*SUBSAMPLE) + j_offset) * n_x * n_y;
1207         int total = 0;
1208         int i, j;
1209
1210         for (i=0; i < n_y; i++)
1211           for (j=0; j < n_x; j++)
1212             {
1213               weight = filter->x.weights[(j_offset * n_x) + j] *
1214                        filter->y.weights[(i_offset * n_y) + i] *
1215                        filter->overall_alpha * 65536 + 0.5;
1216
1217               total += (int)weight;
1218
1219               *(pixel_weights + n_x * i + j) = weight;
1220             }
1221
1222         correct_total (pixel_weights, n_x, n_y, total, filter->overall_alpha);
1223       }
1224
1225   return weights;
1226 }
1227
1228 static void
1229 pixops_process (guchar         *dest_buf,
1230                 int             render_x0,
1231                 int             render_y0,
1232                 int             render_x1,
1233                 int             render_y1,
1234                 int             dest_rowstride,
1235                 int             dest_channels,
1236                 gboolean        dest_has_alpha,
1237                 const guchar   *src_buf,
1238                 int             src_width,
1239                 int             src_height,
1240                 int             src_rowstride,
1241                 int             src_channels,
1242                 gboolean        src_has_alpha,
1243                 double          scale_x,
1244                 double          scale_y,
1245                 int             check_x,
1246                 int             check_y,
1247                 int             check_size,
1248                 guint32         color1,
1249                 guint32         color2,
1250                 PixopsFilter   *filter,
1251                 PixopsLineFunc  line_func,
1252                 PixopsPixelFunc pixel_func)
1253 {
1254   int i, j;
1255   int x, y;                     /* X and Y position in source (fixed_point) */
1256   
1257   guchar **line_bufs = g_new (guchar *, filter->y.n);
1258   int *filter_weights = make_filter_table (filter);
1259
1260   int x_step = (1 << SCALE_SHIFT) / scale_x; /* X step in source (fixed point) */
1261   int y_step = (1 << SCALE_SHIFT) / scale_y; /* Y step in source (fixed point) */
1262
1263   int check_shift = check_size ? get_check_shift (check_size) : 0;
1264
1265   int scaled_x_offset = floor (filter->x.offset * (1 << SCALE_SHIFT));
1266
1267   /* Compute the index where we run off the end of the source buffer. The
1268    * furthest source pixel we access at index i is:
1269    *
1270    *  ((render_x0 + i) * x_step + scaled_x_offset) >> SCALE_SHIFT + filter->x.n - 1
1271    *
1272    * So, run_end_index is the smallest i for which this pixel is src_width,
1273    * i.e, for which:
1274    *
1275    *  (i + render_x0) * x_step >= ((src_width - filter->x.n + 1) << SCALE_SHIFT) - scaled_x_offset
1276    *
1277    */
1278 #define MYDIV(a,b) ((a) > 0 ? (a) / (b) : ((a) - (b) + 1) / (b))    /* Division so that -1/5 = -1 */
1279   
1280   int run_end_x = (((src_width - filter->x.n + 1) << SCALE_SHIFT) - scaled_x_offset);
1281   int run_end_index = MYDIV (run_end_x + x_step - 1, x_step) - render_x0;
1282   run_end_index = MIN (run_end_index, render_x1 - render_x0);
1283
1284   y = render_y0 * y_step + floor (filter->y.offset * (1 << SCALE_SHIFT));
1285   for (i = 0; i < (render_y1 - render_y0); i++)
1286     {
1287       int dest_x;
1288       int y_start = y >> SCALE_SHIFT;
1289       int x_start;
1290       int *run_weights = filter_weights +
1291                          ((y >> (SCALE_SHIFT - SUBSAMPLE_BITS)) & SUBSAMPLE_MASK) *
1292                          filter->x.n * filter->y.n * SUBSAMPLE;
1293       guchar *new_outbuf;
1294       guint32 tcolor1, tcolor2;
1295       
1296       guchar *outbuf = dest_buf + dest_rowstride * i;
1297       guchar *outbuf_end = outbuf + dest_channels * (render_x1 - render_x0);
1298
1299       if (((i + check_y) >> check_shift) & 1)
1300         {
1301           tcolor1 = color2;
1302           tcolor2 = color1;
1303         }
1304       else
1305         {
1306           tcolor1 = color1;
1307           tcolor2 = color2;
1308         }
1309
1310       for (j=0; j<filter->y.n; j++)
1311         {
1312           if (y_start <  0)
1313             line_bufs[j] = (guchar *)src_buf;
1314           else if (y_start < src_height)
1315             line_bufs[j] = (guchar *)src_buf + src_rowstride * y_start;
1316           else
1317             line_bufs[j] = (guchar *)src_buf + src_rowstride * (src_height - 1);
1318
1319           y_start++;
1320         }
1321
1322       dest_x = check_x;
1323       x = render_x0 * x_step + scaled_x_offset;
1324       x_start = x >> SCALE_SHIFT;
1325
1326       while (x_start < 0 && outbuf < outbuf_end)
1327         {
1328           process_pixel (run_weights + ((x >> (SCALE_SHIFT - SUBSAMPLE_BITS)) & SUBSAMPLE_MASK) * (filter->x.n * filter->y.n), filter->x.n, filter->y.n,
1329                          outbuf, dest_x, dest_channels, dest_has_alpha,
1330                          line_bufs, src_channels, src_has_alpha,
1331                          x >> SCALE_SHIFT, src_width,
1332                          check_size, tcolor1, tcolor2, pixel_func);
1333           
1334           x += x_step;
1335           x_start = x >> SCALE_SHIFT;
1336           dest_x++;
1337           outbuf += dest_channels;
1338         }
1339
1340       new_outbuf = (*line_func) (run_weights, filter->x.n, filter->y.n,
1341                                  outbuf, dest_x, dest_buf + dest_rowstride *
1342                                  i + run_end_index * dest_channels,
1343                                  dest_channels, dest_has_alpha,
1344                                  line_bufs, src_channels, src_has_alpha,
1345                                  x, x_step, src_width, check_size, tcolor1,
1346                                  tcolor2);
1347
1348       dest_x += (new_outbuf - outbuf) / dest_channels;
1349
1350       x = (dest_x - check_x + render_x0) * x_step + scaled_x_offset;
1351       outbuf = new_outbuf;
1352
1353       while (outbuf < outbuf_end)
1354         {
1355           process_pixel (run_weights + ((x >> (SCALE_SHIFT - SUBSAMPLE_BITS)) & SUBSAMPLE_MASK) * (filter->x.n * filter->y.n), filter->x.n, filter->y.n,
1356                          outbuf, dest_x, dest_channels, dest_has_alpha,
1357                          line_bufs, src_channels, src_has_alpha,
1358                          x >> SCALE_SHIFT, src_width,
1359                          check_size, tcolor1, tcolor2, pixel_func);
1360           
1361           x += x_step;
1362           dest_x++;
1363           outbuf += dest_channels;
1364         }
1365
1366       y += y_step;
1367     }
1368
1369   g_free (line_bufs);
1370   g_free (filter_weights);
1371 }
1372
1373 /* Compute weights for reconstruction by replication followed by
1374  * sampling with a box filter
1375  */
1376 static void
1377 tile_make_weights (PixopsFilterDimension *dim,
1378                    double                 scale)
1379 {
1380   int n = ceil (1 / scale + 1);
1381   double *pixel_weights = g_new (double, SUBSAMPLE * n);
1382   int offset;
1383   int i;
1384
1385   dim->n = n;
1386   dim->offset = 0;
1387   dim->weights = pixel_weights;
1388
1389   for (offset = 0; offset < SUBSAMPLE; offset++)
1390     {
1391       double x = (double)offset / SUBSAMPLE;
1392       double a = x + 1 / scale;
1393
1394       for (i = 0; i < n; i++)
1395         {
1396           if (i < x)
1397             {
1398               if (i + 1 > x)
1399                 *(pixel_weights++)  = (MIN (i + 1, a) - x) * scale;
1400               else
1401                 *(pixel_weights++) = 0;
1402             }
1403           else
1404             {
1405               if (a > i)
1406                 *(pixel_weights++)  = (MIN (i + 1, a) - i) * scale;
1407               else
1408                 *(pixel_weights++) = 0;
1409             }
1410        }
1411     }
1412 }
1413
1414 /* Compute weights for a filter that, for minification
1415  * is the same as 'tiles', and for magnification, is bilinear
1416  * reconstruction followed by a sampling with a delta function.
1417  */
1418 static void
1419 bilinear_magnify_make_weights (PixopsFilterDimension *dim,
1420                                double                 scale)
1421 {
1422   double *pixel_weights;
1423   int n;
1424   int offset;
1425   int i;
1426
1427   if (scale > 1.0)            /* Linear */
1428     {
1429       n = 2;
1430       dim->offset = 0.5 * (1 / scale - 1);
1431     }
1432   else                          /* Tile */
1433     {
1434       n = ceil (1.0 + 1.0 / scale);
1435       dim->offset = 0.0;
1436     }
1437
1438   dim->n = n;
1439   dim->weights = g_new (double, SUBSAMPLE * n);
1440
1441   pixel_weights = dim->weights;
1442
1443   for (offset=0; offset < SUBSAMPLE; offset++)
1444     {
1445       double x = (double)offset / SUBSAMPLE;
1446
1447       if (scale > 1.0)      /* Linear */
1448         {
1449           for (i = 0; i < n; i++)
1450             *(pixel_weights++) = (((i == 0) ? (1 - x) : x) / scale) * scale;
1451         }
1452       else                  /* Tile */
1453         {
1454           double a = x + 1 / scale;
1455
1456           /*           x
1457            * ---------|--.-|----|--.-|-------  SRC
1458            * ------------|---------|---------  DEST
1459            */
1460           for (i = 0; i < n; i++)
1461             {
1462               if (i < x)
1463                 {
1464                   if (i + 1 > x)
1465                     *(pixel_weights++) = (MIN (i + 1, a) - x) * scale;
1466                   else
1467                     *(pixel_weights++) = 0;
1468                 }
1469               else
1470                 {
1471                   if (a > i)
1472                     *(pixel_weights++) = (MIN (i + 1, a) - i) * scale;
1473                   else
1474                     *(pixel_weights++) = 0;
1475                 }
1476             }
1477         }
1478     }
1479 }
1480
1481 /* Computes the integral from b0 to b1 of
1482  *
1483  * f(x) = x; 0 <= x < 1
1484  * f(x) = 0; otherwise
1485  *
1486  * We combine two of these to compute the convolution of
1487  * a box filter with a triangular spike.
1488  */
1489 static double
1490 linear_box_half (double b0, double b1)
1491 {
1492   double a0, a1;
1493   double x0, x1;
1494
1495   a0 = 0.;
1496   a1 = 1.;
1497
1498   if (a0 < b0)
1499     {
1500       if (a1 > b0)
1501         {
1502           x0 = b0;
1503           x1 = MIN (a1, b1);
1504         }
1505       else
1506         return 0;
1507     }
1508   else
1509     {
1510       if (b1 > a0)
1511         {
1512           x0 = a0;
1513           x1 = MIN (a1, b1);
1514         }
1515       else
1516         return 0;
1517     }
1518
1519   return 0.5 * (x1*x1 - x0*x0);
1520 }
1521
1522 /* Compute weights for reconstructing with bilinear
1523  * interpolation, then sampling with a box filter
1524  */
1525 static void
1526 bilinear_box_make_weights (PixopsFilterDimension *dim,
1527                            double                 scale)
1528 {
1529   int n = ceil (1/scale + 3.0);
1530   double *pixel_weights = g_new (double, SUBSAMPLE * n);
1531   double w;
1532   int offset, i;
1533
1534   dim->offset = -1.0;
1535   dim->n = n;
1536   dim->weights = pixel_weights;
1537
1538   for (offset = 0; offset < SUBSAMPLE; offset++)
1539     {
1540       double x = (double)offset / SUBSAMPLE;
1541       double a = x + 1 / scale;
1542
1543       for (i = 0; i < n; i++)
1544         {
1545           w  = linear_box_half (0.5 + i - a, 0.5 + i - x);
1546           w += linear_box_half (1.5 + x - i, 1.5 + a - i);
1547       
1548           *(pixel_weights++) = w * scale;
1549         }
1550     }
1551 }
1552
1553 static void
1554 make_weights (PixopsFilter     *filter,
1555               PixopsInterpType  interp_type,          
1556               double            scale_x,
1557               double            scale_y)
1558 {
1559   switch (interp_type)
1560     {
1561     case PIXOPS_INTERP_NEAREST:
1562       g_assert_not_reached ();
1563       break;
1564
1565     case PIXOPS_INTERP_TILES:
1566       tile_make_weights (&filter->x, scale_x);
1567       tile_make_weights (&filter->y, scale_y);
1568       break;
1569       
1570     case PIXOPS_INTERP_BILINEAR:
1571       bilinear_magnify_make_weights (&filter->x, scale_x);
1572       bilinear_magnify_make_weights (&filter->y, scale_y);
1573       break;
1574       
1575     case PIXOPS_INTERP_HYPER:
1576       bilinear_box_make_weights (&filter->x, scale_x);
1577       bilinear_box_make_weights (&filter->y, scale_y);
1578       break;
1579     }
1580 }
1581
1582 static void
1583 _pixops_composite_color_real (guchar          *dest_buf,
1584                               int              render_x0,
1585                               int              render_y0,
1586                               int              render_x1,
1587                               int              render_y1,
1588                               int              dest_rowstride,
1589                               int              dest_channels,
1590                               gboolean         dest_has_alpha,
1591                               const guchar    *src_buf,
1592                               int              src_width,
1593                               int              src_height,
1594                               int              src_rowstride,
1595                               int              src_channels,
1596                               gboolean         src_has_alpha,
1597                               double           scale_x,
1598                               double           scale_y,
1599                               PixopsInterpType interp_type,
1600                               int              overall_alpha,
1601                               int              check_x,
1602                               int              check_y,
1603                               int              check_size,
1604                               guint32          color1,
1605                               guint32          color2)
1606 {
1607   PixopsFilter filter;
1608   PixopsLineFunc line_func;
1609   
1610 #ifdef USE_MMX
1611   gboolean found_mmx = _pixops_have_mmx ();
1612 #endif
1613
1614   g_return_if_fail (!(dest_channels == 3 && dest_has_alpha));
1615   g_return_if_fail (!(src_channels == 3 && src_has_alpha));
1616
1617   if (scale_x == 0 || scale_y == 0)
1618     return;
1619
1620   if (interp_type == PIXOPS_INTERP_NEAREST)
1621     {
1622       pixops_composite_color_nearest (dest_buf, render_x0, render_y0,
1623                                       render_x1, render_y1, dest_rowstride,
1624                                       dest_channels, dest_has_alpha, src_buf,
1625                                       src_width, src_height, src_rowstride,
1626                                       src_channels, src_has_alpha, scale_x,
1627                                       scale_y, overall_alpha, check_x, check_y,
1628                                       check_size, color1, color2);
1629       return;
1630     }
1631   
1632   filter.overall_alpha = overall_alpha / 255.;
1633   make_weights (&filter, interp_type, scale_x, scale_y);
1634
1635 #ifdef USE_MMX
1636   if (filter.x.n == 2 && filter.y.n == 2 &&
1637       dest_channels == 4 && src_channels == 4 &&
1638       src_has_alpha && !dest_has_alpha && found_mmx)
1639     line_func = composite_line_color_22_4a4_mmx_stub;
1640   else
1641 #endif
1642     line_func = composite_line_color;
1643   
1644   pixops_process (dest_buf, render_x0, render_y0, render_x1, render_y1,
1645                   dest_rowstride, dest_channels, dest_has_alpha,
1646                   src_buf, src_width, src_height, src_rowstride, src_channels,
1647                   src_has_alpha, scale_x, scale_y, check_x, check_y, check_size, color1, color2,
1648                   &filter, line_func, composite_pixel_color);
1649
1650   g_free (filter.x.weights);
1651   g_free (filter.y.weights);
1652 }
1653
1654 void
1655 _pixops_composite_color (guchar          *dest_buf,
1656                          int              dest_width,
1657                          int              dest_height,
1658                          int              dest_rowstride,
1659                          int              dest_channels,
1660                          gboolean         dest_has_alpha,
1661                          const guchar    *src_buf,
1662                          int              src_width,
1663                          int              src_height,
1664                          int              src_rowstride,
1665                          int              src_channels,
1666                          gboolean         src_has_alpha,
1667                          int              dest_x,
1668                          int              dest_y,
1669                          int              dest_region_width,
1670                          int              dest_region_height,
1671                          double           offset_x,
1672                          double           offset_y,
1673                          double           scale_x,
1674                          double           scale_y,
1675                          PixopsInterpType interp_type,
1676                          int              overall_alpha,
1677                          int              check_x,
1678                          int              check_y,
1679                          int              check_size,
1680                          guint32          color1,
1681                          guint32          color2)
1682 {
1683   guchar *new_dest_buf;
1684   int render_x0;
1685   int render_y0;
1686   int render_x1;
1687   int render_y1;
1688
1689   if (!src_has_alpha && overall_alpha == 255)
1690     {
1691       _pixops_scale (dest_buf, dest_width, dest_height, dest_rowstride,
1692                      dest_channels, dest_has_alpha, src_buf, src_width,
1693                      src_height, src_rowstride, src_channels, src_has_alpha,
1694                      dest_x, dest_y, dest_region_width, dest_region_height,
1695                      offset_x, offset_y, scale_x, scale_y, interp_type);
1696       return;
1697     }
1698
1699   new_dest_buf = dest_buf + dest_y * dest_rowstride + dest_x *
1700                  dest_channels;
1701   render_x0 = dest_x - offset_x;
1702   render_y0 = dest_y - offset_y;
1703   render_x1 = dest_x + dest_region_width  - offset_x;
1704   render_y1 = dest_y + dest_region_height - offset_y;
1705
1706   _pixops_composite_color_real (new_dest_buf, render_x0, render_y0, render_x1,
1707                                 render_y1, dest_rowstride, dest_channels,
1708                                 dest_has_alpha, src_buf, src_width,
1709                                 src_height, src_rowstride, src_channels,
1710                                 src_has_alpha, scale_x, scale_y,
1711                                 (PixopsInterpType)interp_type, overall_alpha,
1712                                 check_x, check_y, check_size, color1, color2);
1713 }
1714
1715 /**
1716  * _pixops_composite_real:
1717  * @dest_buf: pointer to location to store result
1718  * @render_x0: x0 of region of scaled source to store into @dest_buf
1719  * @render_y0: y0 of region of scaled source to store into @dest_buf
1720  * @render_x1: x1 of region of scaled source to store into @dest_buf
1721  * @render_y1: y1 of region of scaled source to store into @dest_buf
1722  * @dest_rowstride: rowstride of @dest_buf
1723  * @dest_channels: number of channels in @dest_buf
1724  * @dest_has_alpha: whether @dest_buf has alpha
1725  * @src_buf: pointer to source pixels
1726  * @src_width: width of source (used for clipping)
1727  * @src_height: height of source (used for clipping)
1728  * @src_rowstride: rowstride of source
1729  * @src_channels: number of channels in @src_buf
1730  * @src_has_alpha: whether @src_buf has alpha
1731  * @scale_x: amount to scale source by in X direction
1732  * @scale_y: amount to scale source by in Y direction
1733  * @interp_type: type of enumeration
1734  * @overall_alpha: overall alpha factor to multiply source by
1735  * 
1736  * Scale source buffer by scale_x / scale_y, then composite a given rectangle
1737  * of the result into the destination buffer.
1738  **/
1739 static void
1740 _pixops_composite_real (guchar          *dest_buf,
1741                         int              render_x0,
1742                         int              render_y0,
1743                         int              render_x1,
1744                         int              render_y1,
1745                         int              dest_rowstride,
1746                         int              dest_channels,
1747                         gboolean         dest_has_alpha,
1748                         const guchar    *src_buf,
1749                         int              src_width,
1750                         int              src_height,
1751                         int              src_rowstride,
1752                         int              src_channels,
1753                         gboolean         src_has_alpha,
1754                         double           scale_x,
1755                         double           scale_y,
1756                         PixopsInterpType interp_type,
1757                         int              overall_alpha)
1758 {
1759   PixopsFilter filter;
1760   PixopsLineFunc line_func;
1761   
1762 #ifdef USE_MMX
1763   gboolean found_mmx = _pixops_have_mmx ();
1764 #endif
1765
1766   g_return_if_fail (!(dest_channels == 3 && dest_has_alpha));
1767   g_return_if_fail (!(src_channels == 3 && src_has_alpha));
1768
1769   if (scale_x == 0 || scale_y == 0)
1770     return;
1771
1772   if (interp_type == PIXOPS_INTERP_NEAREST)
1773     {
1774       pixops_composite_nearest (dest_buf, render_x0, render_y0, render_x1,
1775                                 render_y1, dest_rowstride, dest_channels,
1776                                 dest_has_alpha, src_buf, src_width, src_height,
1777                                 src_rowstride, src_channels, src_has_alpha,
1778                                 scale_x, scale_y, overall_alpha);
1779       return;
1780     }
1781   
1782   filter.overall_alpha = overall_alpha / 255.;
1783   make_weights (&filter, interp_type, scale_x, scale_y);
1784
1785   if (filter.x.n == 2 && filter.y.n == 2 && dest_channels == 4 &&
1786       src_channels == 4 && src_has_alpha && !dest_has_alpha)
1787     {
1788 #ifdef USE_MMX
1789       if (found_mmx)
1790         line_func = composite_line_22_4a4_mmx_stub;
1791       else
1792 #endif  
1793         line_func = composite_line_22_4a4;
1794     }
1795   else
1796     line_func = composite_line;
1797   
1798   pixops_process (dest_buf, render_x0, render_y0, render_x1, render_y1,
1799                   dest_rowstride, dest_channels, dest_has_alpha,
1800                   src_buf, src_width, src_height, src_rowstride, src_channels,
1801                   src_has_alpha, scale_x, scale_y, 0, 0, 0, 0, 0, 
1802                   &filter, line_func, composite_pixel);
1803
1804   g_free (filter.x.weights);
1805   g_free (filter.y.weights);
1806 }
1807
1808 void
1809 _pixops_composite (guchar          *dest_buf,
1810                    int              dest_width,
1811                    int              dest_height,
1812                    int              dest_rowstride,
1813                    int              dest_channels,
1814                    int              dest_has_alpha,
1815                    const guchar    *src_buf,
1816                    int              src_width,
1817                    int              src_height,
1818                    int              src_rowstride,
1819                    int              src_channels,
1820                    int              src_has_alpha,
1821                    int              dest_x,
1822                    int              dest_y,
1823                    int              dest_region_width,
1824                    int              dest_region_height,
1825                    double           offset_x,
1826                    double           offset_y,
1827                    double           scale_x,
1828                    double           scale_y,
1829                    PixopsInterpType interp_type,
1830                    int              overall_alpha)
1831 {
1832   guchar *new_dest_buf;
1833   int render_x0;
1834   int render_y0;
1835   int render_x1;
1836   int render_y1;
1837
1838   if (!src_has_alpha && overall_alpha == 255)
1839     {
1840       _pixops_scale (dest_buf, dest_width, dest_height, dest_rowstride,
1841                      dest_channels, dest_has_alpha, src_buf, src_width,
1842                      src_height, src_rowstride, src_channels, src_has_alpha,
1843                      dest_x, dest_y, dest_region_width, dest_region_height,
1844                      offset_x, offset_y, scale_x, scale_y, interp_type);
1845       return;
1846     }
1847
1848 #ifdef USE_MEDIALIB
1849   pixops_medialib_composite (dest_buf, dest_width, dest_height, dest_rowstride,
1850                              dest_channels, dest_has_alpha, src_buf,
1851                              src_width, src_height, src_rowstride,
1852                              src_channels, src_has_alpha, dest_x, dest_y,
1853                              dest_region_width, dest_region_height, offset_x,
1854                              offset_y, scale_x, scale_y,
1855                              (PixopsInterpType)interp_type, overall_alpha);
1856   return;
1857 #endif
1858
1859   new_dest_buf = dest_buf + dest_y * dest_rowstride + dest_x * dest_channels;
1860   render_x0 = dest_x - offset_x;
1861   render_y0 = dest_y - offset_y;
1862   render_x1 = dest_x + dest_region_width  - offset_x;
1863   render_y1 = dest_y + dest_region_height - offset_y;
1864
1865   _pixops_composite_real (new_dest_buf, render_x0, render_y0, render_x1,
1866                           render_y1, dest_rowstride, dest_channels,
1867                           dest_has_alpha, src_buf, src_width, src_height,
1868                           src_rowstride, src_channels, src_has_alpha, scale_x,
1869                           scale_y, (PixopsInterpType)interp_type,
1870                           overall_alpha);
1871 }
1872
1873 #ifdef USE_MEDIALIB
1874 static void
1875 medialib_get_interpolation (mlInterp * ml_interp,
1876                             PixopsInterpType interp_type,
1877                             double scale_x,
1878                             double scale_y,
1879                             double overall_alpha)
1880 {
1881   mlib_s32 leftPadding, topPadding;
1882   ml_interp->interp_table = NULL;
1883
1884  /*
1885   * medialib 2.1 and later supports scaling with user-defined interpolation
1886   * tables, so this logic is used.  
1887   *
1888   * bilinear_magnify_make_weights builds an interpolation table of size 2x2 if
1889   * the scale factor >= 1.0 and "ceil (1.0 + 1.0/scale)" otherwise.  These map
1890   * most closely to MLIB_BILINEAR, which uses an interpolation table of size
1891   * 2x2.
1892   *
1893   * tile_make_weights builds an interpolation table of size 2x2 if the scale
1894   * factor >= 1.0 and "ceil (1.0 + 1.0/scale)" otherwise.  These map most
1895   * closely to MLIB_BILINEAR, which uses an interpolation table of size 2x2.
1896   *
1897   * bilinear_box_make_weights builds an interpolation table of size 4x4 if the
1898   * scale factor >= 1.0 and "ceil (1.0 + 1.0/scale)" otherwise.  These map most
1899   * closely to MLIB_BICUBIC, which uses an interpolation table of size 4x4.
1900   *
1901   * PIXOPS_INTERP_NEAREST calls pixops_scale_nearest which does not use an
1902   * interpolation table.  This maps to MLIB_NEAREST.
1903   */
1904   switch (interp_type)
1905     {
1906     case PIXOPS_INTERP_BILINEAR:
1907       bilinear_magnify_make_weights (&(ml_interp->po_filter.x), scale_x);
1908       bilinear_magnify_make_weights (&(ml_interp->po_filter.y), scale_y);
1909       leftPadding = 0;
1910       topPadding  = 0;
1911
1912       if (scale_x <= 1.0)
1913           ml_interp->tx = 0.5 * (1 - scale_x);
1914       else
1915           ml_interp->tx = 0.0;
1916
1917       if (scale_y <= 1.0)
1918           ml_interp->ty = 0.5 * (1 - scale_y);
1919       else
1920           ml_interp->ty = 0.0;
1921
1922       break;
1923
1924     case PIXOPS_INTERP_TILES:
1925       tile_make_weights (&(ml_interp->po_filter.x), scale_x);
1926       tile_make_weights (&(ml_interp->po_filter.y), scale_y);
1927       leftPadding   = 0;
1928       topPadding    = 0;
1929       ml_interp->tx = 0.5 * (1 - scale_x);
1930       ml_interp->ty = 0.5 * (1 - scale_y);
1931       break;
1932
1933     case PIXOPS_INTERP_HYPER:
1934       bilinear_box_make_weights (&(ml_interp->po_filter.x), scale_x);
1935       bilinear_box_make_weights (&(ml_interp->po_filter.y), scale_y);
1936       leftPadding   = 1;
1937       topPadding    = 1;
1938       ml_interp->tx = 0.5 * (1 - scale_x);
1939       ml_interp->ty = 0.5 * (1 - scale_y);
1940       break;
1941
1942     case PIXOPS_INTERP_NEAREST:
1943     default:
1944       /*
1945        * Note that this function should not be called in the
1946        * PIXOPS_INTERP_NEAREST case since it does not use an interpolation
1947        * table.
1948        */
1949       g_assert_not_reached ();
1950       break;
1951     }
1952
1953  /* 
1954   * If overall_alpha is not 1.0, then multiply the vectors built by the
1955   * sqrt (overall_alpha).  This will cause overall_alpha to get evenly
1956   * blended across both axis.
1957   *
1958   * Note there is no need to multiply the vectors built by the various
1959   * make-weight functions by sqrt (overall_alpha) since the make-weight
1960   * functions are called with overall_alpha hardcoded to 1.0.
1961   */
1962   if (overall_alpha != 1.0)
1963     {
1964       double sqrt_alpha = sqrt (overall_alpha);
1965       int i;
1966
1967       for (i=0; i < SUBSAMPLE * ml_interp->po_filter.x.n; i++)
1968          ml_interp->po_filter.x.weights[i] *= sqrt_alpha;
1969       for (i=0; i < SUBSAMPLE * ml_interp->po_filter.y.n; i++)
1970          ml_interp->po_filter.y.weights[i] *= sqrt_alpha;
1971     }
1972     
1973   ml_interp->interp_table = (void *) mlib_ImageInterpTableCreate (MLIB_DOUBLE,
1974     ml_interp->po_filter.x.n, ml_interp->po_filter.y.n, leftPadding,
1975     topPadding, SUBSAMPLE_BITS, SUBSAMPLE_BITS, 8,
1976     ml_interp->po_filter.x.weights, ml_interp->po_filter.y.weights);
1977
1978   g_free (ml_interp->po_filter.x.weights);
1979   g_free (ml_interp->po_filter.y.weights);  
1980 }
1981
1982 static void
1983 pixops_medialib_composite (guchar          *dest_buf,
1984                            int              dest_width,
1985                            int              dest_height,
1986                            int              dest_rowstride,
1987                            int              dest_channels,
1988                            int              dest_has_alpha,
1989                            const guchar    *src_buf,
1990                            int              src_width,
1991                            int              src_height,
1992                            int              src_rowstride,
1993                            int              src_channels,
1994                            int              src_has_alpha,
1995                            int              dest_x,
1996                            int              dest_y,
1997                            int              dest_region_width,
1998                            int              dest_region_height,
1999                            double           offset_x,
2000                            double           offset_y,
2001                            double           scale_x,
2002                            double           scale_y,
2003                            PixopsInterpType interp_type,
2004                            int              overall_alpha)
2005 {
2006   mlib_blend blend;
2007   g_return_if_fail (!(dest_channels == 3 && dest_has_alpha));
2008   g_return_if_fail (!(src_channels == 3 && src_has_alpha));
2009
2010   if (scale_x == 0 || scale_y == 0)
2011     return;
2012
2013   if (!medialib_initialized)
2014     _pixops_use_medialib ();
2015
2016   if (!use_medialib)
2017     {
2018       /* Use non-mediaLib version */
2019       _pixops_composite_real (dest_buf + dest_y * dest_rowstride + dest_x *
2020                               dest_channels, dest_x - offset_x, dest_y -
2021                               offset_y, dest_x + dest_region_width - offset_x,
2022                               dest_y + dest_region_height - offset_y,
2023                               dest_rowstride, dest_channels, dest_has_alpha,
2024                               src_buf, src_width, src_height, src_rowstride,
2025                               src_channels, src_has_alpha, scale_x, scale_y,
2026                               interp_type, overall_alpha);
2027     }
2028   else
2029     {
2030       mlInterp ml_interp;
2031       mlib_image img_src, img_dest;
2032       double ml_offset_x, ml_offset_y;
2033
2034       if (!src_has_alpha && overall_alpha == 255 &&
2035           dest_channels <= src_channels) 
2036         {
2037           pixops_medialib_scale (dest_buf, dest_region_width,
2038                                  dest_region_height, dest_rowstride,
2039                                  dest_channels, dest_has_alpha, src_buf,
2040                                  src_width, src_height, src_rowstride,
2041                                  src_channels, src_has_alpha, dest_x, dest_y,
2042                                  dest_region_width, dest_region_height,
2043                                  offset_x, offset_y, scale_x, scale_y,
2044                                  interp_type);
2045           return;
2046         }
2047
2048       mlib_ImageSetStruct (&img_src, MLIB_BYTE, src_channels,
2049                            src_width, src_height, src_rowstride, src_buf);
2050
2051       if (dest_x == 0 && dest_y == 0 &&
2052           dest_width  == dest_region_width &&
2053           dest_height == dest_region_height)
2054         {
2055           mlib_ImageSetStruct (&img_dest, MLIB_BYTE, dest_channels,
2056                                dest_width, dest_height, dest_rowstride,
2057                                dest_buf);
2058         }
2059       else
2060         {
2061           mlib_u8 *data = dest_buf + (dest_y * dest_rowstride) + 
2062                                      (dest_x * dest_channels);
2063
2064           mlib_ImageSetStruct (&img_dest, MLIB_BYTE, dest_channels,
2065                                dest_region_width, dest_region_height,
2066                                dest_rowstride, data);
2067         }
2068
2069       ml_offset_x = floor (offset_x) - dest_x;
2070       ml_offset_y = floor (offset_y) - dest_y;
2071
2072       if (interp_type == PIXOPS_INTERP_NEAREST)
2073         {
2074           blend = src_has_alpha ? MLIB_BLEND_GTK_SRC_OVER2 : MLIB_BLEND_GTK_SRC;
2075
2076           mlib_ImageZoomTranslateBlend (&img_dest,
2077                                         &img_src,
2078                                         scale_x,
2079                                         scale_y,
2080                                         ml_offset_x,
2081                                         ml_offset_y,
2082                                         MLIB_NEAREST,
2083                                         MLIB_EDGE_SRC_EXTEND_INDEF,
2084                                         blend,
2085                                         overall_alpha,
2086                                         1);
2087         }
2088       else
2089         {
2090           blend = src_has_alpha ? MLIB_BLEND_GTK_SRC_OVER : MLIB_BLEND_GTK_SRC;
2091
2092           if (interp_type == PIXOPS_INTERP_BILINEAR &&
2093               scale_x > 1.0 && scale_y > 1.0)
2094             {
2095               mlib_ImageZoomTranslateBlend (&img_dest,
2096                                             &img_src,
2097                                             scale_x,
2098                                             scale_y,
2099                                             ml_offset_x,
2100                                             ml_offset_y,
2101                                             MLIB_BILINEAR,
2102                                             MLIB_EDGE_SRC_EXTEND_INDEF,
2103                                             blend,
2104                                             overall_alpha,
2105                                             1);
2106             }
2107           else
2108             {
2109               medialib_get_interpolation (&ml_interp, interp_type, scale_x,
2110                                           scale_y, overall_alpha/255.0);
2111
2112               if (ml_interp.interp_table != NULL)
2113                 {
2114                   mlib_ImageZoomTranslateTableBlend (&img_dest,
2115                                                      &img_src,
2116                                                      scale_x,
2117                                                      scale_y,
2118                                                      ml_offset_x + ml_interp.tx,
2119                                                      ml_offset_y + ml_interp.ty,
2120                                                      ml_interp.interp_table,
2121                                                      MLIB_EDGE_SRC_EXTEND_INDEF,
2122                                                      blend,
2123                                                      1);
2124                   mlib_ImageInterpTableDelete (ml_interp.interp_table);
2125                 }
2126               else
2127                 {
2128                   /* Should not happen - Use non-mediaLib version */
2129                   _pixops_composite_real (dest_buf + dest_y * dest_rowstride +
2130                                           dest_x * dest_channels,
2131                                           dest_x - offset_x, dest_y - offset_y,
2132                                           dest_x + dest_region_width - offset_x,
2133                                           dest_y + dest_region_height - offset_y,
2134                                           dest_rowstride, dest_channels,
2135                                           dest_has_alpha, src_buf, src_width,
2136                                           src_height, src_rowstride,
2137                                           src_channels, src_has_alpha, scale_x,
2138                                           scale_y, interp_type, overall_alpha);
2139                 }
2140             }
2141         }
2142     }
2143 }
2144 #endif
2145
2146 static void
2147 _pixops_scale_real (guchar        *dest_buf,
2148                     int            render_x0,
2149                     int            render_y0,
2150                     int            render_x1,
2151                     int            render_y1,
2152                     int            dest_rowstride,
2153                     int            dest_channels,
2154                     gboolean       dest_has_alpha,
2155                     const guchar  *src_buf,
2156                     int            src_width,
2157                     int            src_height,
2158                     int            src_rowstride,
2159                     int            src_channels,
2160                     gboolean       src_has_alpha,
2161                     double         scale_x,
2162                     double         scale_y,
2163                     PixopsInterpType  interp_type)
2164 {
2165   PixopsFilter filter;
2166   PixopsLineFunc line_func;
2167
2168 #ifdef USE_MMX
2169   gboolean found_mmx = _pixops_have_mmx ();
2170 #endif
2171
2172   g_return_if_fail (!(dest_channels == 3 && dest_has_alpha));
2173   g_return_if_fail (!(src_channels == 3 && src_has_alpha));
2174   g_return_if_fail (!(src_has_alpha && !dest_has_alpha));
2175
2176   if (scale_x == 0 || scale_y == 0)
2177     return;
2178
2179   if (interp_type == PIXOPS_INTERP_NEAREST)
2180     {
2181       pixops_scale_nearest (dest_buf, render_x0, render_y0, render_x1,
2182                             render_y1, dest_rowstride, dest_channels,
2183                             dest_has_alpha, src_buf, src_width, src_height,
2184                             src_rowstride, src_channels, src_has_alpha,
2185                             scale_x, scale_y);
2186       return;
2187     }
2188   
2189   filter.overall_alpha = 1.0;
2190   make_weights (&filter, interp_type, scale_x, scale_y);
2191
2192   if (filter.x.n == 2 && filter.y.n == 2 && dest_channels == 3 && src_channels == 3)
2193     {
2194 #ifdef USE_MMX
2195       if (found_mmx)
2196         line_func = scale_line_22_33_mmx_stub;
2197       else
2198 #endif
2199         line_func = scale_line_22_33;
2200     }
2201   else
2202     line_func = scale_line;
2203   
2204   pixops_process (dest_buf, render_x0, render_y0, render_x1, render_y1,
2205                   dest_rowstride, dest_channels, dest_has_alpha,
2206                   src_buf, src_width, src_height, src_rowstride, src_channels,
2207                   src_has_alpha, scale_x, scale_y, 0, 0, 0, 0, 0,
2208                   &filter, line_func, scale_pixel);
2209
2210   g_free (filter.x.weights);
2211   g_free (filter.y.weights);
2212 }
2213
2214 void
2215 _pixops_scale (guchar          *dest_buf,
2216                int              dest_width,
2217                int              dest_height,
2218                int              dest_rowstride,
2219                int              dest_channels,
2220                int              dest_has_alpha,
2221                const guchar    *src_buf,
2222                int              src_width,
2223                int              src_height,
2224                int              src_rowstride,
2225                int              src_channels,
2226                int              src_has_alpha,
2227                int              dest_x,
2228                int              dest_y,
2229                int              dest_region_width,
2230                int              dest_region_height,
2231                double           offset_x,
2232                double           offset_y,
2233                double           scale_x,
2234                double           scale_y,
2235                PixopsInterpType interp_type)
2236 {
2237   guchar *new_dest_buf;
2238   int render_x0;
2239   int render_y0;
2240   int render_x1;
2241   int render_y1;
2242
2243 #ifdef USE_MEDIALIB
2244   pixops_medialib_scale (dest_buf, dest_width, dest_height, dest_rowstride,
2245                          dest_channels, dest_has_alpha, src_buf, src_width,
2246                          src_height, src_rowstride, src_channels,
2247                          src_has_alpha, dest_x, dest_y, dest_region_width,
2248                          dest_region_height, offset_x, offset_y, scale_x,
2249                          scale_y, (PixopsInterpType)interp_type);
2250   return;
2251 #endif
2252
2253   new_dest_buf = dest_buf + dest_y * dest_rowstride + dest_x * dest_channels;
2254   render_x0    = dest_x - offset_x;
2255   render_y0    = dest_y - offset_y;
2256   render_x1    = dest_x + dest_region_width  - offset_x;
2257   render_y1    = dest_y + dest_region_height - offset_y;
2258
2259   _pixops_scale_real (new_dest_buf, render_x0, render_y0, render_x1,
2260                       render_y1, dest_rowstride, dest_channels,
2261                       dest_has_alpha, src_buf, src_width, src_height,
2262                       src_rowstride, src_channels, src_has_alpha,
2263                       scale_x, scale_y, (PixopsInterpType)interp_type);
2264 }
2265
2266 #ifdef USE_MEDIALIB
2267 static void
2268 pixops_medialib_scale     (guchar          *dest_buf,
2269                            int              dest_width,
2270                            int              dest_height,
2271                            int              dest_rowstride,
2272                            int              dest_channels,
2273                            int              dest_has_alpha,
2274                            const guchar    *src_buf,
2275                            int              src_width,
2276                            int              src_height,
2277                            int              src_rowstride,
2278                            int              src_channels,
2279                            int              src_has_alpha,
2280                            int              dest_x,
2281                            int              dest_y,
2282                            int              dest_region_width,
2283                            int              dest_region_height,
2284                            double           offset_x,
2285                            double           offset_y,
2286                            double           scale_x,
2287                            double           scale_y,
2288                            PixopsInterpType interp_type)
2289 {
2290   if (scale_x == 0 || scale_y == 0)
2291     return;
2292
2293   if (!medialib_initialized)
2294     _pixops_use_medialib ();
2295  
2296   /*
2297    * We no longer support mediaLib 2.1 because it has a core dumping problem
2298    * in the mlib_ImageZoomTranslateTable function that has been corrected in
2299    * 2.2.  Although the mediaLib_zoom function could be used, it does not
2300    * work properly if the source and destination images have different 
2301    * values for "has_alpha" or "num_channels".  The complicated if-logic
2302    * required to support both versions is not worth supporting
2303    * mediaLib 2.1 moving forward.
2304    */
2305   if (!use_medialib)
2306     {
2307       _pixops_scale_real (dest_buf + dest_y * dest_rowstride + dest_x *
2308                           dest_channels, dest_x - offset_x, dest_y - offset_y, 
2309                           dest_x + dest_region_width - offset_x,
2310                           dest_y + dest_region_height - offset_y,
2311                           dest_rowstride, dest_channels, dest_has_alpha,
2312                           src_buf, src_width, src_height, src_rowstride,
2313                           src_channels, src_has_alpha, scale_x, scale_y,
2314                           interp_type);
2315     }
2316   else 
2317     {
2318       mlInterp ml_interp;
2319       mlib_image img_orig_src, img_src, img_dest;
2320       double ml_offset_x, ml_offset_y;
2321       guchar *tmp_buf = NULL;
2322
2323       mlib_ImageSetStruct (&img_orig_src, MLIB_BYTE, src_channels, src_width, 
2324                            src_height, src_rowstride, src_buf);
2325
2326       if (dest_x == 0 && dest_y == 0 &&
2327           dest_width == dest_region_width &&
2328           dest_height == dest_region_height)
2329         {
2330           mlib_ImageSetStruct (&img_dest, MLIB_BYTE, dest_channels,
2331                                dest_width, dest_height, dest_rowstride,
2332                                dest_buf);
2333         }
2334       else
2335         {
2336           mlib_u8 *data = dest_buf + (dest_y * dest_rowstride) + 
2337                                      (dest_x * dest_channels);
2338
2339           mlib_ImageSetStruct (&img_dest, MLIB_BYTE, dest_channels,
2340                                dest_region_width, dest_region_height,
2341                                dest_rowstride, data);
2342         }
2343
2344       ml_offset_x = floor (offset_x) - dest_x;
2345       ml_offset_y = floor (offset_y) - dest_y;
2346
2347      /*
2348       * Note that zoomTranslate and zoomTranslateTable are faster
2349       * than zoomTranslateBlend and zoomTranslateTableBlend.  However
2350       * the faster functions only work in the following case:
2351       *
2352       *   if (src_channels == dest_channels &&
2353       *       (!src_alpha && interp_table != PIXOPS_INTERP_NEAREST))
2354       *
2355       * We use the faster versions if we can.
2356       *
2357       * Note when the interp_type is BILINEAR and the interpolation
2358       * table will be size 2x2 (when both x/y scale factors > 1.0),
2359       * then we do not bother building the interpolation table.   In
2360       * this case we can just use MLIB_BILINEAR, which is faster than
2361       * using a specified interpolation table.
2362       */
2363       img_src = img_orig_src;
2364
2365       if (!src_has_alpha)
2366         {
2367           if (src_channels > dest_channels)
2368             {
2369               int channels  = 3;
2370               int rowstride = (channels * src_width + 3) & ~3;
2371         
2372               tmp_buf = g_malloc (src_rowstride * src_height);
2373
2374               if (src_buf != NULL)
2375                 {
2376                   src_channels  = channels;
2377                   src_rowstride = rowstride;
2378           
2379                   mlib_ImageSetStruct (&img_src, MLIB_BYTE, src_channels,
2380                                        src_width, src_height, src_rowstride,
2381                                        tmp_buf);
2382                   mlib_ImageChannelExtract (&img_src, &img_orig_src, 0xE);  
2383                 }
2384             }
2385         }
2386     
2387       if (interp_type == PIXOPS_INTERP_NEAREST)
2388         {
2389           if (src_channels == dest_channels)
2390             {
2391               mlib_ImageZoomTranslate (&img_dest,
2392                                        &img_src,
2393                                        scale_x,
2394                                        scale_y,
2395                                        ml_offset_x,
2396                                        ml_offset_y,
2397                                        MLIB_NEAREST,
2398                                        MLIB_EDGE_SRC_EXTEND_INDEF);
2399             }
2400           else
2401             {
2402               mlib_ImageZoomTranslateBlend (&img_dest,
2403                                             &img_src,
2404                                             scale_x,
2405                                             scale_y,
2406                                             ml_offset_x,
2407                                             ml_offset_y,
2408                                             MLIB_NEAREST,
2409                                             MLIB_EDGE_SRC_EXTEND_INDEF,
2410                                             MLIB_BLEND_GTK_SRC,
2411                                             1.0,
2412                                             1);
2413             }
2414         }
2415       else if (src_channels == dest_channels && !src_has_alpha)
2416         {
2417           if (interp_type == PIXOPS_INTERP_BILINEAR &&
2418               scale_x > 1.0 && scale_y > 1.0)
2419             {
2420                mlib_ImageZoomTranslate (&img_dest,
2421                                         &img_src,
2422                                         scale_x,
2423                                         scale_y,
2424                                         ml_offset_x,
2425                                         ml_offset_y,
2426                                         MLIB_BILINEAR,
2427                                         MLIB_EDGE_SRC_EXTEND_INDEF);
2428             }
2429           else
2430             {
2431               medialib_get_interpolation (&ml_interp, interp_type,
2432                                           scale_x, scale_y, 1.0);
2433
2434               if (ml_interp.interp_table != NULL)
2435                 {
2436                   mlib_ImageZoomTranslateTable (&img_dest, 
2437                                                 &img_src,
2438                                                 scale_x,
2439                                                 scale_y,
2440                                                 ml_offset_x + ml_interp.tx,
2441                                                 ml_offset_y + ml_interp.ty,
2442                                                 ml_interp.interp_table,
2443                                                 MLIB_EDGE_SRC_EXTEND_INDEF);
2444
2445                   mlib_ImageInterpTableDelete (ml_interp.interp_table);
2446                 }
2447               else
2448                 {
2449                   /* Should not happen. */
2450                   mlib_filter  ml_filter;
2451
2452                   switch (interp_type)
2453                     {
2454                     case PIXOPS_INTERP_BILINEAR:
2455                       ml_filter = MLIB_BILINEAR;
2456                       break;
2457
2458                     case PIXOPS_INTERP_TILES:
2459                       ml_filter = MLIB_BILINEAR;
2460                       break;
2461
2462                     case PIXOPS_INTERP_HYPER:
2463                       ml_filter = MLIB_BICUBIC;
2464                       break;
2465                     }
2466
2467                   mlib_ImageZoomTranslate (&img_dest,
2468                                            &img_src,
2469                                            scale_x,
2470                                            scale_y,
2471                                            ml_offset_x,
2472                                            ml_offset_y,
2473                                            ml_filter,
2474                                            MLIB_EDGE_SRC_EXTEND_INDEF);
2475                 }
2476             }
2477         }
2478
2479       /* Deal with case where src_channels != dest_channels || src_has_alpha */
2480       else if (interp_type == PIXOPS_INTERP_BILINEAR &&
2481                scale_x > 1.0 && scale_y > 1.0)
2482         {
2483           mlib_ImageZoomTranslateBlend (&img_dest,
2484                                         &img_src,
2485                                         scale_x,
2486                                         scale_y,
2487                                         ml_offset_x,
2488                                         ml_offset_y,
2489                                         MLIB_BILINEAR,
2490                                         MLIB_EDGE_SRC_EXTEND_INDEF,
2491                                         MLIB_BLEND_GTK_SRC,
2492                                         1.0,
2493                                         1);
2494         }
2495       else
2496         {
2497           medialib_get_interpolation (&ml_interp, interp_type,
2498                                       scale_x, scale_y, 1.0);
2499
2500           if (ml_interp.interp_table != NULL)
2501             {
2502               mlib_ImageZoomTranslateTableBlend (&img_dest,
2503                                                  &img_src,
2504                                                  scale_x,
2505                                                  scale_y,
2506                                                  ml_offset_x + ml_interp.tx,
2507                                                  ml_offset_y + ml_interp.ty,
2508                                                  ml_interp.interp_table,
2509                                                  MLIB_EDGE_SRC_EXTEND_INDEF,
2510                                                  MLIB_BLEND_GTK_SRC,
2511                                                  1);
2512               mlib_ImageInterpTableDelete (ml_interp.interp_table);
2513             }
2514           else
2515             {
2516               mlib_filter  ml_filter;
2517
2518               switch (interp_type)
2519                 {
2520                 case PIXOPS_INTERP_BILINEAR:
2521                   ml_filter = MLIB_BILINEAR;
2522                   break;
2523             
2524                 case PIXOPS_INTERP_TILES:
2525                   ml_filter = MLIB_BILINEAR;
2526                   break;
2527
2528                 case PIXOPS_INTERP_HYPER:
2529                   ml_filter = MLIB_BICUBIC;
2530                   break;
2531                 }
2532
2533               mlib_ImageZoomTranslate (&img_dest,
2534                                        &img_src,
2535                                        scale_x,
2536                                        scale_y,
2537                                        ml_offset_x,
2538                                        ml_offset_y,
2539                                        ml_filter,
2540                                        MLIB_EDGE_SRC_EXTEND_INDEF);
2541             }
2542         }
2543
2544       if (tmp_buf != NULL)
2545         g_free (tmp_buf);
2546     }
2547 }
2548 #endif