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