1 /*
2 This file contains code from the GraphicsMagick software distributed by the
3 GraphicsMagick Group.
4 
5 image_downsize_gm is based on GraphicsMagick's ResizeImage function.
6 
7 The licenses which components of this software fall under are as follows.
8 
9 1)
10   In November 2002, the GraphicsMagick Group created GraphicsMagick
11   from ImageMagick Studio's ImageMagick and applied the "MIT" style
12   license:
13 
14   Copyright (C) 2002 - 2010 GraphicsMagick Group
15 
16   Permission is hereby granted, free of charge, to any person
17   obtaining a copy of this software and associated documentation files
18   (the "Software"), to deal in the Software without restriction,
19   including without limitation the rights to use, copy, modify, merge,
20   publish, distribute, sublicense, and/or sell copies of the Software,
21   and to permit persons to whom the Software is furnished to do so,
22   subject to the following conditions:
23 
24   The above copyright notice and this permission notice shall be
25   included in all copies or substantial portions of the Software.
26 
27   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
31   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
32   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
33   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
34   SOFTWARE.
35 
36 2)
37   In October 1999, ImageMagick Studio assumed the responsibility for
38   the development of ImageMagick (forking from the distribution by
39   E. I. du Pont de Nemours and Company) and applied a new license:
40 
41   Copyright (C) 2002 ImageMagick Studio, a non-profit organization dedicated
42   to making software imaging solutions freely available.
43 
44   Permission is hereby granted, free of charge, to any person obtaining a
buf_src_init(j_decompress_ptr cinfo)45   copy of this software and associated documentation files ("ImageMagick"),
46   to deal in ImageMagick without restriction, including without limitation
47   the rights to use, copy, modify, merge, publish, distribute, sublicense,
48   and/or sell copies of ImageMagick, and to permit persons to whom the
49   ImageMagick is furnished to do so, subject to the following conditions:
50 
51   The above copyright notice and this permission notice shall be included in
52   all copies or substantial portions of ImageMagick.
53 
54   The software is provided "as is", without warranty of any kind, express or
55   implied, including but not limited to the warranties of merchantability,
56   fitness for a particular purpose and noninfringement.  In no event shall
57   ImageMagick Studio be liable for any claim, damages or other liability,
58   whether in an action of contract, tort or otherwise, arising from, out of
59   or in connection with ImageMagick or the use or other dealings in
60   ImageMagick.
61 
62   Except as contained in this notice, the name of the ImageMagick Studio
63   shall not be used in advertising or otherwise to promote the sale, use or
64   other dealings in ImageMagick without prior written authorization from the
65   ImageMagick Studio.
66 
67 3)
68   From 1991 to October 1999 (through ImageMagick 4.2.9), ImageMagick
69   was developed and distributed by E. I. du Pont de Nemours and
70   Company:
71 
72   Copyright 1999 E. I. du Pont de Nemours and Company
73 
74   Permission is hereby granted, free of charge, to any person obtaining a
75   copy of this software and associated documentation files
76   ("ImageMagick"), to deal in ImageMagick without restriction, including
77   without limitation the rights to use, copy, modify, merge, publish,
78   distribute, sublicense, and/or sell copies of ImageMagick, and to
79   permit persons to whom the ImageMagick is furnished to do so, subject
80   to the following conditions:
81 
82   The above copyright notice and this permission notice shall be included
83   in all copies or substantial portions of ImageMagick.
84 
85   The software is provided "as is", without warranty of any kind, express
86   or implied, including but not limited to the warranties of
87   merchantability, fitness for a particular purpose and noninfringement.
88   In no event shall E. I. du Pont de Nemours and Company be liable for
89   any claim, damages or other liability, whether in an action of
90   contract, tort or otherwise, arising from, out of or in connection with
91   ImageMagick or the use or other dealings in ImageMagick.
92 
93   Except as contained in this notice, the name of the E. I. du Pont de
94   Nemours and Company shall not be used in advertising or otherwise to
95   promote the sale, use or other dealings in ImageMagick without prior
96   written authorization from the E. I. du Pont de Nemours and Company.
97 
98 ---------------------------------------------------------------------------
99 
100 | Copyright (C) 2002 - 2010 GraphicsMagick Group
101 */
102 
103 #include "magick.h"
104 
105 static double J1(double x)
106 {
107   double
108     p,
109     q;
110 
111   register long
112     i;
113 
114   static const double
115     Pone[] =
116     {
117        0.581199354001606143928050809e+21,
118       -0.6672106568924916298020941484e+20,
119        0.2316433580634002297931815435e+19,
120       -0.3588817569910106050743641413e+17,
121        0.2908795263834775409737601689e+15,
122       -0.1322983480332126453125473247e+13,
123        0.3413234182301700539091292655e+10,
124       -0.4695753530642995859767162166e+7,
125        0.270112271089232341485679099e+4
126     },
127     Qone[] =
128     {
129       0.11623987080032122878585294e+22,
130       0.1185770712190320999837113348e+20,
131       0.6092061398917521746105196863e+17,
132       0.2081661221307607351240184229e+15,
133       0.5243710262167649715406728642e+12,
134       0.1013863514358673989967045588e+10,
135       0.1501793594998585505921097578e+7,
136       0.1606931573481487801970916749e+4,
137       0.1e+1
138     };
139 
140   p=Pone[8];
141   q=Qone[8];
142   for (i=7; i >= 0; i--)
143   {
144     p=p*x*x+Pone[i];
145     q=q*x*x+Qone[i];
146   }
147   return(p/q);
148 }
149 
150 static double P1(double x)
151 {
152   double
153     p,
sv_dst_mgr_init(j_compress_ptr cinfo)154     q;
155 
156   register long
157     i;
158 
159   static const double
160     Pone[] =
161     {
162       0.352246649133679798341724373e+5,
163       0.62758845247161281269005675e+5,
164       0.313539631109159574238669888e+5,
165       0.49854832060594338434500455e+4,
sv_dst_mgr_empty(j_compress_ptr cinfo)166       0.2111529182853962382105718e+3,
167       0.12571716929145341558495e+1
168     },
169     Qone[] =
170     {
171       0.352246649133679798068390431e+5,
172       0.626943469593560511888833731e+5,
173       0.312404063819041039923015703e+5,
174       0.4930396490181088979386097e+4,
175       0.2030775189134759322293574e+3,
176       0.1e+1
177     };
178 
179   p=Pone[5];
180   q=Qone[5];
181   for (i=4; i >= 0; i--)
182   {
183     p=p*(8.0/x)*(8.0/x)+Pone[i];
sv_dst_mgr_term(j_compress_ptr cinfo)184     q=q*(8.0/x)*(8.0/x)+Qone[i];
185   }
186   return(p/q);
187 }
188 
189 static double Q1(double x)
190 {
191   double
192     p,
193     q;
194 
195   register long
196     i;
197 
198   static const double
199     Pone[] =
200     {
image_jpeg_sv_dest(j_compress_ptr cinfo,struct sv_dst_mgr * dst,SV * sv_buf)201       0.3511751914303552822533318e+3,
202       0.7210391804904475039280863e+3,
203       0.4259873011654442389886993e+3,
204       0.831898957673850827325226e+2,
205       0.45681716295512267064405e+1,
206       0.3532840052740123642735e-1
207     },
208     Qone[] =
209     {
210       0.74917374171809127714519505e+4,
211       0.154141773392650970499848051e+5,
212       0.91522317015169922705904727e+4,
213       0.18111867005523513506724158e+4,
214       0.1038187585462133728776636e+3,
215       0.1e+1
216     };
217 
218   p=Pone[5];
219   q=Qone[5];
220   for (i=4; i >= 0; i--)
221   {
222     p=p*(8.0/x)*(8.0/x)+Pone[i];
223     q=q*(8.0/x)*(8.0/x)+Qone[i];
224   }
225   return(p/q);
226 }
227 
228 static double BesselOrderOne(double x)
229 {
230   double
231     p,
232     q;
233 
234   if (x == 0.0)
235     return(0.0);
236   p=x;
237   if (x < 0.0)
238     x=(-x);
239   if (x < 8.0)
240     return(p*J1(x));
241   q=sqrt(2.0/(PI*x))*(P1(x)*(1.0/sqrt(2.0)*(sin(x)-cos(x)))-8.0/x*Q1(x)*
242     (-1.0/sqrt(2.0)*(sin(x)+cos(x))));
243   if (p < 0.0)
244     q=(-q);
245   return(q);
libjpeg_error_handler(j_common_ptr cinfo)246 }
247 
248 static float Bessel(const float x,const float ARGUNUSED(support))
249 {
250   if (x == 0.0)
251     return(PI/4.0);
252   return(BesselOrderOne(PI*x)/(2.0*x));
253 }
libjpeg_output_message(j_common_ptr cinfo)254 
255 static float Sinc(const float x,const float ARGUNUSED(support))
256 {
257   if (x == 0.0)
258     return(1.0);
259   return(sin(PI*x)/(PI*x));
260 }
261 
262 static float Blackman(const float x,const float ARGUNUSED(support))
263 {
264   return(0.42+0.5*cos(PI*x)+0.08*cos(2*PI*x));
image_jpeg_read_header(image * im)265 }
266 
267 static float BlackmanBessel(const float x,const float support)
268 {
269   return(Blackman(x/support,support)*Bessel(x,support));
270 }
271 
272 static float BlackmanSinc(const float x,const float support)
273 {
274   return(Blackman(x/support,support)*Sinc(x,support));
275 }
276 
277 static float Box(const float x,const float ARGUNUSED(support))
278 {
279   if (x < -0.5)
280     return(0.0);
281   if (x < 0.5)
282     return(1.0);
283   return(0.0);
284 }
285 
286 static float Catrom(const float x,const float ARGUNUSED(support))
287 {
288   if (x < -2.0)
289     return(0.0);
290   if (x < -1.0)
291     return(0.5*(4.0+x*(8.0+x*(5.0+x))));
292   if (x < 0.0)
293     return(0.5*(2.0+x*x*(-5.0-3.0*x)));
294   if (x < 1.0)
295     return(0.5*(2.0+x*x*(-5.0+3.0*x)));
296   if (x < 2.0)
297     return(0.5*(4.0+x*(-8.0+x*(5.0-x))));
298   return(0.0);
299 }
300 
301 static float Cubic(const float x,const float ARGUNUSED(support))
302 {
303   if (x < -2.0)
304     return(0.0);
305   if (x < -1.0)
306     return((2.0+x)*(2.0+x)*(2.0+x)/6.0);
307   if (x < 0.0)
308     return((4.0+x*x*(-6.0-3.0*x))/6.0);
309   if (x < 1.0)
310     return((4.0+x*x*(-6.0+3.0*x))/6.0);
311   if (x < 2.0)
312     return((2.0-x)*(2.0-x)*(2.0-x)/6.0);
313   return(0.0);
314 }
315 
316 static float Gaussian(const float x,const float ARGUNUSED(support))
317 {
318   return(exp(-2.0*x*x)*sqrt(2.0/PI));
319 }
320 
321 static float Hanning(const float x,const float ARGUNUSED(support))
322 {
323   return(0.5+0.5*cos(PI*x));
324 }
325 
326 static float Hamming(const float x,const float ARGUNUSED(support))
327 {
328   return(0.54+0.46*cos(PI*x));
329 }
330 
331 static float Hermite(const float x,const float ARGUNUSED(support))
332 {
333   if (x < -1.0)
334     return(0.0);
image_jpeg_load(image * im)335   if (x < 0.0)
336     return((2.0*(-x)-3.0)*(-x)*(-x)+1.0);
337   if (x < 1.0)
338     return((2.0*x-3.0)*x*x+1.0);
339   return(0.0);
340 }
341 
342 static float Lanczos(const float x,const float support)
343 {
344   if (x < -3.0)
345     return(0.0);
346   if (x < 0.0)
347     return(Sinc(-x,support)*Sinc(-x/3.0,support));
348   if (x < 3.0)
349     return(Sinc(x,support)*Sinc(x/3.0,support));
350   return(0.0);
351 }
352 
353 static float Mitchell(const float x,const float ARGUNUSED(support))
354 {
355 #define B   (1.0/3.0)
356 #define C   (1.0/3.0)
357 #define P0  ((  6.0- 2.0*B       )/6.0)
358 #define P2  ((-18.0+12.0*B+ 6.0*C)/6.0)
359 #define P3  (( 12.0- 9.0*B- 6.0*C)/6.0)
360 #define Q0  ((       8.0*B+24.0*C)/6.0)
361 #define Q1  ((     -12.0*B-48.0*C)/6.0)
362 #define Q2  ((       6.0*B+30.0*C)/6.0)
363 #define Q3  ((     - 1.0*B- 6.0*C)/6.0)
364 
365   if (x < -2.0)
366     return(0.0);
367   if (x < -1.0)
368     return(Q0-x*(Q1-x*(Q2-x*Q3)));
369   if (x < 0.0)
370     return(P0+x*x*(P2-x*P3));
371   if (x < 1.0)
372     return(P0+x*x*(P2+x*P3));
373   if (x < 2.0)
374     return(Q0+x*(Q1+x*(Q2+x*Q3)));
375   return(0.0);
376 }
377 
378 static float Quadratic(const float x,const float ARGUNUSED(support))
379 {
380   if (x < -1.5)
381     return(0.0);
382   if (x < -0.5)
383     return(0.5*(x+1.5)*(x+1.5));
384   if (x < 0.5)
385     return(0.75-x*x);
386   if (x < 1.5)
387     return(0.5*(x-1.5)*(x-1.5));
388   return(0.0);
389 }
390 
391 static float Triangle(const float x,const float ARGUNUSED(support))
392 {
393   if (x < -1.0)
394     return(0.0);
395   if (x < 0.0)
396     return(1.0+x);
397   if (x < 1.0)
398     return(1.0-x);
399   return(0.0);
400 }
401 
402 static void
403 image_downsize_gm_horizontal_filter(image *im, ImageInfo *source, ImageInfo *destination,
404   const float x_factor, const FilterInfo *filter_info, ContributionInfo *contribution, int rotate)
405 {
406   float scale, support;
407   int x;
408   int dstX = 0;
409   int dstW = destination->columns;
410 
411   if (im->width_padding) {
412     dstX = im->width_padding;
413     dstW = im->width_inner;
414   }
415 
416   scale = BLUR * MAX(1.0 / x_factor, 1.0);
417   support = scale * filter_info->support;
418   if (support <= 0.5) {
419     // Reduce to point sampling
420     support = 0.5 + EPSILON;
421     scale = 1.0;
422   }
423   scale = 1.0 / scale;
424 
425   for (x = dstX; (x < dstX + dstW); x++) {
426     float center, density;
427     int n, start, stop, y;
428 
429     center  = (float)(x - dstX + 0.5) / x_factor;
430     start   = (int)MAX(center - support + 0.5, 0);
431     stop    = (int)MIN(center + support + 0.5, source->columns);
432     density = 0.0;
433 
434     //DEBUG_TRACE("x %d: center %.2f, start %d, stop %d\n", x, center, start, stop);
435 
436     for (n = 0; n < (stop - start); n++) {
437       contribution[n].pixel = start + n;
438       contribution[n].weight = filter_info->function(scale * (start + n - center + 0.5), filter_info->support);
439       density += contribution[n].weight;
440       //DEBUG_TRACE("  contribution[%d].pixel %d, weight %.2f, density %.2f\n", n, contribution[n].pixel, contribution[n].weight, density);
441     }
442 
443     if ((density != 0.0) && (density != 1.0)) {
444       // Normalize
445       int i;
446 
447       density = 1.0 / density;
448       for (i = 0; i < n; i++) {
449         contribution[i].weight *= density;
450         //DEBUG_TRACE("  normalize contribution[%d].weight to %.2f\n", i, contribution[i].weight);
451       }
452     }
453 
454     for (y = 0; y < destination->rows; y++) {
455       float weight;
456       float red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0;
457       pix p;
458       int j;
459       register int i;
460 
461       //DEBUG_TRACE("y %d:\n", y);
462 
463       if (im->has_alpha) {
464         float normalize;
465 
image_jpeg_compress(image * im,struct jpeg_compress_struct * cinfo,int quality)466         normalize = 0.0;
467         for (i = 0; i < n; i++) {
468           j = (int)((y * source->columns) + contribution[i].pixel);
469           weight = contribution[i].weight;
470           p = source->buf[j];
471 
472           // XXX The original GM code weighted based on transparency for some reason,
473           // but this produces bad results, so we use only the weight
474           //transparency_coeff = weight * ((float)COL_ALPHA(p) / 255);
475 
476           /*
477           DEBUG_TRACE("    merging with pix (%d, %d) @ %d (%d %d %d %d) weight %.2f\n",
478             x, contribution[i].pixel, j,
479             COL_RED(p), COL_GREEN(p), COL_BLUE(p), COL_ALPHA(p),
480             weight);
481           */
482 
483           red   += weight * COL_RED(p);
484           green += weight * COL_GREEN(p);
485           blue  += weight * COL_BLUE(p);
486           alpha += weight * COL_ALPHA(p);
487           normalize += weight;
488         }
489 
490         normalize = 1.0 / (ABS(normalize) <= EPSILON ? 1.0 : normalize);
491         red   *= normalize;
492         green *= normalize;
493         blue  *= normalize;
494       }
495       else {
496         for (i = 0; i < n; i++) {
497           j = (int)((y * source->columns) + contribution[i].pixel);
498           weight = contribution[i].weight;
499           p = source->buf[j];
500 
501           /*
502           DEBUG_TRACE("    merging with pix (%d, %d) @ %d (%d %d %d) weight %.2f\n",
503             contribution[i].pixel, y, j,
504             COL_RED(p), COL_GREEN(p), COL_BLUE(p),
505             weight);
506           */
507 
508           red   += weight * COL_RED(p);
509           green += weight * COL_GREEN(p);
510           blue  += weight * COL_BLUE(p);
511         }
512 
513         alpha = 255.0;
514       }
515 
516       /*
517       DEBUG_TRACE("  -> (%d, %d) @ %d (%d %d %d %d)\n",
518         x, y, (y * destination->columns) + x,
519         ROUND_FLOAT_TO_INT(red),
520         ROUND_FLOAT_TO_INT(green),
521         ROUND_FLOAT_TO_INT(blue),
522         ROUND_FLOAT_TO_INT(alpha));
523       */
524 
525       if (rotate && im->orientation != ORIENTATION_NORMAL) {
526         int ox, oy; // new destination pixel coordinates after rotating
527 
528         image_get_rotated_coords(im, x, y, &ox, &oy);
529 
530         if (im->orientation >= 5) {
531           // 90 and 270 rotations, width/height are swapped
532           destination->buf[(oy * destination->rows) + ox] = COL_FULL(
image_jpeg_save(image * im,const char * path,int quality)533             ROUND_FLOAT_TO_INT(red),
534             ROUND_FLOAT_TO_INT(green),
535             ROUND_FLOAT_TO_INT(blue),
536             ROUND_FLOAT_TO_INT(alpha)
537           );
538         }
539         else {
540           destination->buf[(oy * destination->columns) + ox] = COL_FULL(
541             ROUND_FLOAT_TO_INT(red),
542             ROUND_FLOAT_TO_INT(green),
543             ROUND_FLOAT_TO_INT(blue),
544             ROUND_FLOAT_TO_INT(alpha)
545           );
546         }
547       }
548       else {
549         destination->buf[(y * destination->columns) + x] = COL_FULL(
550           ROUND_FLOAT_TO_INT(red),
551           ROUND_FLOAT_TO_INT(green),
552           ROUND_FLOAT_TO_INT(blue),
553           ROUND_FLOAT_TO_INT(alpha)
554         );
555       }
556     }
image_jpeg_to_sv(image * im,int quality,SV * sv_buf)557   }
558 }
559 
560 static void
561 image_downsize_gm_vertical_filter(image *im, ImageInfo *source, ImageInfo *destination,
562   const float y_factor, const FilterInfo *filter_info, ContributionInfo *contribution, int rotate)
563 {
564   float scale, support;
565   int y;
566   int dstY = 0;
567   int dstH = destination->rows;
568 
569   if (im->height_padding) {
570     dstY = im->height_padding;
571     dstH = im->height_inner;
572   }
573 
574   //DEBUG_TRACE("y_factor %.2f\n", y_factor);
575 
576   // Apply filter to resize vertically from source to destination
577   scale = BLUR * MAX(1.0 / y_factor, 1.0);
578   support = scale * filter_info->support;
579   if (support <= 0.5) {
580     // Reduce to point sampling
581     support = 0.5 + EPSILON;
582     scale = 1.0;
583   }
584   scale = 1.0 / scale;
585 
586   for (y = dstY; (y < dstY + dstH); y++) {
587     float center, density;
588     int n, start, stop, x;
589 
590     center  = (float)(y - dstY + 0.5) / y_factor;
591     start   = (int)MAX(center - support + 0.5, 0);
592     stop    = (int)MIN(center + support + 0.5, source->rows);
593     density = 0.0;
594 
595     //DEBUG_TRACE("y %d: center %.2f, start %d, stop %d\n", y, center, start, stop);
596 
597     for (n = 0; n < (stop - start); n++) {
598       contribution[n].pixel = start + n;
599       contribution[n].weight = filter_info->function(scale * (start + n - center + 0.5), filter_info->support);
600       density += contribution[n].weight;
601       //DEBUG_TRACE("  contribution[%d].pixel %d, weight %.2f, density %.2f\n", n, contribution[n].pixel, contribution[n].weight, density);
602     }
603 
604     if ((density != 0.0) && (density != 1.0)) {
605       // Normalize
606       int i;
607 
608       density = 1.0 / density;
609       for (i = 0; i < n; i++) {
610         contribution[i].weight *= density;
611         //DEBUG_TRACE("  normalize contribution[%d].weight to %.2f\n", i, contribution[i].weight);
612       }
613     }
614 
615     for (x = 0; x < destination->columns; x++) {
616       float weight;
617       float red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0;
618       pix p;
619       int j;
620       register int i;
621 
622       //DEBUG_TRACE("x %d:\n", x);
623 
624       if (im->has_alpha) {
625         float normalize;
626 
627         normalize = 0.0;
628         for (i = 0; i < n; i++) {
629           j = (int)((contribution[i].pixel * source->columns) + x);
630           weight = contribution[i].weight;
631           p = source->buf[j];
632 
633           // XXX The original GM code weighted based on transparency for some reason,
634           // but this produces bad results, so we use only the weight
635           //transparency_coeff = weight * ((float)COL_ALPHA(p) / 255);
636 
637           /*
638           DEBUG_TRACE("    merging with pix (%d, %d) @ %d (%d %d %d %d) weight %.2f\n",
639             x, contribution[i].pixel, j,
640             COL_RED(p), COL_GREEN(p), COL_BLUE(p), COL_ALPHA(p),
641             weight
642           );
643           */
644 
645           red   += weight * COL_RED(p);
646           green += weight * COL_GREEN(p);
647           blue  += weight * COL_BLUE(p);
648           alpha += weight * COL_ALPHA(p);
649           normalize += weight;
650         }
651 
652         normalize = 1.0 / (ABS(normalize) <= EPSILON ? 1.0 : normalize);
653         red   *= normalize;
654         green *= normalize;
655         blue  *= normalize;
656       }
657       else {
658         for (i = 0; i < n; i++) {
659           j = (int)((contribution[i].pixel * source->columns) + x);
660           weight = contribution[i].weight;
661           p = source->buf[j];
662 
663           /*
664           DEBUG_TRACE("    merging with pix (%d, %d) @ %d (%d %d %d) weight %.2f\n",
665             x, contribution[i].pixel, j,
666             COL_RED(p), COL_GREEN(p), COL_BLUE(p),
667             weight);
668           */
669 
670           red   += weight * COL_RED(p);
671           green += weight * COL_GREEN(p);
672           blue  += weight * COL_BLUE(p);
673         }
674 
675         alpha = 255.0;
676       }
677 
678       /*
679       DEBUG_TRACE("  -> (%d, %d) @ %d (%d %d %d %d)\n",
680         x, y, (y * destination->columns) + x,
681         ROUND_FLOAT_TO_INT(red),
682         ROUND_FLOAT_TO_INT(green),
683         ROUND_FLOAT_TO_INT(blue),
684         ROUND_FLOAT_TO_INT(alpha));
685       */
686 
687       if (rotate && im->orientation != ORIENTATION_NORMAL) {
688         int ox, oy; // new destination pixel coordinates after rotating
689 
690         image_get_rotated_coords(im, x, y, &ox, &oy);
691 
692         if (im->orientation >= 5) {
693           // 90 and 270 rotations, width/height are swapped
694           destination->buf[(oy * destination->rows) + ox] = COL_FULL(
695             ROUND_FLOAT_TO_INT(red),
696             ROUND_FLOAT_TO_INT(green),
697             ROUND_FLOAT_TO_INT(blue),
698             ROUND_FLOAT_TO_INT(alpha)
699           );
700         }
701         else {
702           destination->buf[(oy * destination->columns) + ox] = COL_FULL(
703             ROUND_FLOAT_TO_INT(red),
704             ROUND_FLOAT_TO_INT(green),
705             ROUND_FLOAT_TO_INT(blue),
706             ROUND_FLOAT_TO_INT(alpha)
707           );
708         }
709       }
710       else {
711         destination->buf[(y * destination->columns) + x] = COL_FULL(
712           ROUND_FLOAT_TO_INT(red),
713           ROUND_FLOAT_TO_INT(green),
714           ROUND_FLOAT_TO_INT(blue),
715           ROUND_FLOAT_TO_INT(alpha)
716         );
717       }
718     }
719   }
720 }
721 
722 void
723 image_downsize_gm(image *im)
724 {
725   float x_factor, y_factor;
726   float support, x_support, y_support;
727   int columns, rows;
728   int order;
729   int filter;
730   ContributionInfo *contribution;
731   ImageInfo source, destination;
732 
733   static const FilterInfo
734     filters[SincFilter+1] =
735     {
736       { Box, 0.0 },
737       { Box, 0.0 },
738       { Box, 0.5 },
739       { Triangle, 1.0 },
740       { Hermite, 1.0 },
741       { Hanning, 1.0 },
742       { Hamming, 1.0 },
743       { Blackman, 1.0 },
744       { Gaussian, 1.25 },
745       { Quadratic, 1.5 },
746       { Cubic, 2.0 },
747       { Catrom, 2.0 },
748       { Mitchell, 2.0 },
749       { Lanczos, 3.0 },
750       { BlackmanBessel, 3.2383 },
751       { BlackmanSinc, 4.0 }
752     };
753 
754   columns = im->target_width;
755   rows = im->target_height;
756   filter = im->filter;
757 
758   if (!filter) {
759     // Lanczos for downsizing, Mitchell for upsizing or if transparent
760     if (im->has_alpha || columns > im->width || rows > im->height)
761       filter = MitchellFilter;
762     else
763       filter = LanczosFilter;
764   }
765 
766   DEBUG_TRACE("Resizing with filter %d\n", filter);
767 
768   // Determine which dimension to resize first
769   order = (((float)columns * (im->height + rows)) >
770          ((float)rows * (im->width + columns)));
771 
772   if (im->width_padding)
773     x_factor = (float)im->width_inner / im->width;
774   else
775     x_factor = (float)im->target_width / im->width;
776 
777   if (im->height_padding)
778     y_factor = (float)im->height_inner / im->height;
779   else
780     y_factor = (float)im->target_height / im->height;
781 
782   x_support = BLUR * MAX(1.0 / x_factor, 1.0) * filters[filter].support;
783   y_support = BLUR * MAX(1.0 / y_factor, 1.0) * filters[filter].support;
784   support = MAX(x_support, y_support);
785   if (support < filters[filter].support)
786     support = filters[filter].support;
787 
788   DEBUG_TRACE("ContributionInfo allocated for %ld items\n", (size_t)(2.0 * MAX(support, 0.5) + 3));
789   New(0, contribution, (size_t)(2.0 * MAX(support, 0.5) + 3), ContributionInfo);
790 
791   DEBUG_TRACE("order %d, x_factor %f, y_factor %f, support %f\n", order, x_factor, y_factor, support);
792 
793   source.rows    = im->height;
794   source.columns = im->width;
795   source.buf     = im->pixbuf;
796 
797   if (order) {
798     DEBUG_TRACE("Allocating temporary buffer size %ld\n", im->target_width * im->height * sizeof(pix));
799     New(0, im->tmpbuf, im->target_width * im->height, pix);
800 
801     // Fill new space with the bgcolor or zeros
802     image_bgcolor_fill(im->tmpbuf, im->target_width * im->height, im->bgcolor);
803 
804     // Resize horizontally from source -> tmp
805     destination.rows    = im->height;
806     destination.columns = im->target_width;
807     destination.buf     = im->tmpbuf;
808     image_downsize_gm_horizontal_filter(im, &source, &destination, x_factor, &filters[filter], contribution, 0);
809 
810     // Resize vertically from tmp -> out
811     source.rows    = destination.rows;
812     source.columns = destination.columns;
813     source.buf     = destination.buf;
814 
815     destination.rows = im->target_height;
816     destination.buf  = im->outbuf;
817     image_downsize_gm_vertical_filter(im, &source, &destination, y_factor, &filters[filter], contribution, 1);
818   }
819   else {
820     DEBUG_TRACE("Allocating temporary buffer size %ld\n", im->width * im->target_height * sizeof(pix));
821     New(0, im->tmpbuf, im->width * im->target_height, pix);
822 
823     // Fill new space with the bgcolor or zeros
824     image_bgcolor_fill(im->tmpbuf, im->width * im->target_height, im->bgcolor);
825 
826     // Resize vertically from source -> tmp
827     destination.rows    = im->target_height;
828     destination.columns = im->width;
829     destination.buf     = im->tmpbuf;
830     image_downsize_gm_vertical_filter(im, &source, &destination, y_factor, &filters[filter], contribution, 0);
831 
832     // Resize horizontally from tmp -> out
833     source.rows    = destination.rows;
834     source.columns = destination.columns;
835     source.buf     = destination.buf;
836 
837     destination.columns = im->target_width;
838     destination.buf     = im->outbuf;
839     image_downsize_gm_horizontal_filter(im, &source, &destination, x_factor, &filters[filter], contribution, 1);
840   }
841 
842   Safefree(im->tmpbuf);
843   Safefree(contribution);
844 }
845