~ubuntu-branches/ubuntu/intrepid/graphicsmagick/intrepid

« back to all changes in this revision

Viewing changes to magick/resize.c

  • Committer: Bazaar Package Importer
  • Author(s): Daniel Kobras
  • Date: 2006-05-06 16:28:08 UTC
  • Revision ID: james.westby@ubuntu.com-20060506162808-vt2ni3r5nytcszms
Tags: upstream-1.1.7
ImportĀ upstreamĀ versionĀ 1.1.7

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
% Copyright (C) 2003 GraphicsMagick Group
 
3
% Copyright (C) 2002 ImageMagick Studio
 
4
%
 
5
% This program is covered by multiple licenses, which are described in
 
6
% Copyright.txt. You should have received a copy of Copyright.txt with this
 
7
% package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
 
8
%
 
9
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
10
%                                                                             %
 
11
%                                                                             %
 
12
%                 RRRR   EEEEE  SSSSS  IIIII  ZZZZZ  EEEEE                    %
 
13
%                 R   R  E      SS       I       ZZ  E                        %
 
14
%                 RRRR   EEE     SSS     I     ZZZ   EEE                      %
 
15
%                 R R    E         SS    I    ZZ     E                        %
 
16
%                 R  R   EEEEE  SSSSS  IIIII  ZZZZZ  EEEEE                    %
 
17
%                                                                             %
 
18
%                    GraphicsMagick Image Resize Methods                      %
 
19
%                                                                             %
 
20
%                                                                             %
 
21
%                              Software Design                                %
 
22
%                                John Cristy                                  %
 
23
%                                 July 1992                                   %
 
24
%                                                                             %
 
25
%                                                                             %
 
26
%                                                                             %
 
27
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
28
%
 
29
%
 
30
*/
 
31
 
 
32
/*
 
33
  Include declarations.
 
34
*/
 
35
#include "magick/studio.h"
 
36
#include "magick/cache.h"
 
37
#include "magick/log.h"
 
38
#include "magick/monitor.h"
 
39
#include "magick/resize.h"
 
40
#include "magick/utility.h"
 
41
 
 
42
/*
 
43
  Typedef declarations.
 
44
*/
 
45
typedef struct _ContributionInfo
 
46
{
 
47
  double
 
48
    weight;
 
49
 
 
50
  long
 
51
    pixel;
 
52
} ContributionInfo;
 
53
 
 
54
typedef struct _FilterInfo
 
55
{
 
56
  double
 
57
    (*function)(const double,const double),
 
58
    support;
 
59
} FilterInfo;
 
60
/*
 
61
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
62
%                                                                             %
 
63
%                                                                             %
 
64
%                                                                             %
 
65
+   B e s s e l O r d e r O n e                                               %
 
66
%                                                                             %
 
67
%                                                                             %
 
68
%                                                                             %
 
69
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
70
%
 
71
%  BesselOrderOne() computes the Bessel function of x of the first kind of
 
72
%  order 0:
 
73
%
 
74
%    Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
 
75
%
 
76
%       j1(x) = x*j1(x);
 
77
%
 
78
%    For x in (8,inf)
 
79
%
 
80
%       j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
 
81
%
 
82
%    where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
 
83
%
 
84
%       cos(x1) =  cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
 
85
%               =  1/sqrt(2) * (sin(x) - cos(x))
 
86
%       sin(x1) =  sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
 
87
%               = -1/sqrt(2) * (sin(x) + cos(x))
 
88
%
 
89
%  The format of the BesselOrderOne method is:
 
90
%
 
91
%      double BesselOrderOne(double x)
 
92
%
 
93
%  A description of each parameter follows:
 
94
%
 
95
%    o value: Method BesselOrderOne returns the Bessel function of x of the
 
96
%      first kind of orders 1.
 
97
%
 
98
%    o x: double value.
 
99
%
 
100
%
 
101
*/
 
102
 
 
103
static double J1(double x)
 
104
{
 
105
  double
 
106
    p,
 
107
    q;
 
108
 
 
109
  register long
 
110
    i;
 
111
 
 
112
  static const double
 
113
    Pone[] =
 
114
    {
 
115
       0.581199354001606143928050809e+21,
 
116
      -0.6672106568924916298020941484e+20,
 
117
       0.2316433580634002297931815435e+19,
 
118
      -0.3588817569910106050743641413e+17,
 
119
       0.2908795263834775409737601689e+15,
 
120
      -0.1322983480332126453125473247e+13,
 
121
       0.3413234182301700539091292655e+10,
 
122
      -0.4695753530642995859767162166e+7,
 
123
       0.270112271089232341485679099e+4
 
124
    },
 
125
    Qone[] =
 
126
    {
 
127
      0.11623987080032122878585294e+22,
 
128
      0.1185770712190320999837113348e+20,
 
129
      0.6092061398917521746105196863e+17,
 
130
      0.2081661221307607351240184229e+15,
 
131
      0.5243710262167649715406728642e+12,
 
132
      0.1013863514358673989967045588e+10,
 
133
      0.1501793594998585505921097578e+7,
 
134
      0.1606931573481487801970916749e+4,
 
135
      0.1e+1
 
136
    };
 
137
 
 
138
  p=Pone[8];
 
139
  q=Qone[8];
 
140
  for (i=7; i >= 0; i--)
 
141
  {
 
142
    p=p*x*x+Pone[i];
 
143
    q=q*x*x+Qone[i];
 
144
  }
 
145
  return(p/q);
 
146
}
 
147
 
 
148
static double P1(double x)
 
149
{
 
150
  double
 
151
    p,
 
152
    q;
 
153
 
 
154
  register long
 
155
    i;
 
156
 
 
157
  static const double
 
158
    Pone[] =
 
159
    {
 
160
      0.352246649133679798341724373e+5,
 
161
      0.62758845247161281269005675e+5,
 
162
      0.313539631109159574238669888e+5,
 
163
      0.49854832060594338434500455e+4,
 
164
      0.2111529182853962382105718e+3,
 
165
      0.12571716929145341558495e+1
 
166
    },
 
167
    Qone[] =
 
168
    {
 
169
      0.352246649133679798068390431e+5,
 
170
      0.626943469593560511888833731e+5,
 
171
      0.312404063819041039923015703e+5,
 
172
      0.4930396490181088979386097e+4,
 
173
      0.2030775189134759322293574e+3,
 
174
      0.1e+1
 
175
    };
 
176
 
 
177
  p=Pone[5];
 
178
  q=Qone[5];
 
179
  for (i=4; i >= 0; i--)
 
180
  {
 
181
    p=p*(8.0/x)*(8.0/x)+Pone[i];
 
182
    q=q*(8.0/x)*(8.0/x)+Qone[i];
 
183
  }
 
184
  return(p/q);
 
185
}
 
186
 
 
187
static double Q1(double x)
 
188
{
 
189
  double
 
190
    p,
 
191
    q;
 
192
 
 
193
  register long
 
194
    i;
 
195
 
 
196
  static const double
 
197
    Pone[] =
 
198
    {
 
199
      0.3511751914303552822533318e+3,
 
200
      0.7210391804904475039280863e+3,
 
201
      0.4259873011654442389886993e+3,
 
202
      0.831898957673850827325226e+2,
 
203
      0.45681716295512267064405e+1,
 
204
      0.3532840052740123642735e-1
 
205
    },
 
206
    Qone[] =
 
207
    {
 
208
      0.74917374171809127714519505e+4,
 
209
      0.154141773392650970499848051e+5,
 
210
      0.91522317015169922705904727e+4,
 
211
      0.18111867005523513506724158e+4,
 
212
      0.1038187585462133728776636e+3,
 
213
      0.1e+1
 
214
    };
 
215
 
 
216
  p=Pone[5];
 
217
  q=Qone[5];
 
218
  for (i=4; i >= 0; i--)
 
219
  {
 
220
    p=p*(8.0/x)*(8.0/x)+Pone[i];
 
221
    q=q*(8.0/x)*(8.0/x)+Qone[i];
 
222
  }
 
223
  return(p/q);
 
224
}
 
225
 
 
226
static double BesselOrderOne(double x)
 
227
{
 
228
  double
 
229
    p,
 
230
    q;
 
231
 
 
232
  if (x == 0.0)
 
233
    return(0.0);
 
234
  p=x;
 
235
  if (x < 0.0)
 
236
    x=(-x);
 
237
  if (x < 8.0)
 
238
    return(p*J1(x));
 
239
  q=sqrt(2.0/(MagickPI*x))*(P1(x)*(1.0/sqrt(2.0)*(sin(x)-cos(x)))-8.0/x*Q1(x)*
 
240
    (-1.0/sqrt(2.0)*(sin(x)+cos(x))));
 
241
  if (p < 0.0)
 
242
    q=(-q);
 
243
  return(q);
 
244
}
 
245
 
 
246
/*
 
247
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
248
%                                                                             %
 
249
%                                                                             %
 
250
%                                                                             %
 
251
%   M a g n i f y I m a g e                                                   %
 
252
%                                                                             %
 
253
%                                                                             %
 
254
%                                                                             %
 
255
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
256
%
 
257
%  MagnifyImage() is a convenience method that scales an image proportionally
 
258
%  to twice its size.
 
259
%
 
260
%  The format of the MagnifyImage method is:
 
261
%
 
262
%      Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
 
263
%
 
264
%  A description of each parameter follows:
 
265
%
 
266
%    o image: The image.
 
267
%
 
268
%    o exception: Return any errors or warnings in this structure.
 
269
%
 
270
%
 
271
*/
 
272
MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
 
273
{
 
274
#define MagnifyImageText  "  Magnify image...  "
 
275
 
 
276
  const PixelPacket
 
277
    *pixels;
 
278
 
 
279
  Image
 
280
    *magnify_image;
 
281
 
 
282
  long
 
283
    rows,
 
284
    y;
 
285
 
 
286
  PixelPacket
 
287
    *scanline;
 
288
 
 
289
  register long
 
290
    x;
 
291
 
 
292
  register PixelPacket
 
293
    *p,
 
294
    *q,
 
295
    *r;
 
296
 
 
297
  /*
 
298
    Initialize magnify image attributes.
 
299
  */
 
300
  assert(image != (Image *) NULL);
 
301
  assert(image->signature == MagickSignature);
 
302
  assert(exception != (ExceptionInfo *) NULL);
 
303
  assert(exception->signature == MagickSignature);
 
304
  magnify_image=CloneImage(image,2*image->columns,2*image->rows,True,exception);
 
305
  if (magnify_image == (Image *) NULL)
 
306
    return((Image *) NULL);
 
307
 
 
308
  LogMagickEvent(TransformEvent,GetMagickModule(),
 
309
    "Magnifying image of size %lux%lu to %lux%lu",
 
310
    image->columns,image->rows,magnify_image->columns,magnify_image->rows);
 
311
 
 
312
  magnify_image->storage_class=DirectClass;
 
313
  /*
 
314
    Allocate image buffer and scanline buffer for 4 rows of the image.
 
315
  */
 
316
  scanline=MagickAllocateMemory(PixelPacket *,
 
317
    magnify_image->columns*sizeof(PixelPacket));
 
318
  if (scanline == (PixelPacket *) NULL)
 
319
    {
 
320
      DestroyImage(magnify_image);
 
321
      ThrowImageException3(ResourceLimitError,MemoryAllocationFailed,
 
322
        UnableToMagnifyImage)
 
323
    }
 
324
  /*
 
325
    Initialize magnify image pixels.
 
326
  */
 
327
  for (y=0; y < (long) image->rows; y++)
 
328
  {
 
329
    pixels=AcquireImagePixels(image,0,y,image->columns,1,exception);
 
330
    q=SetImagePixels(magnify_image,0,y,magnify_image->columns,1);
 
331
    if ((pixels == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
 
332
      break;
 
333
    (void) memcpy(q,pixels,image->columns*sizeof(PixelPacket));
 
334
    if (!SyncImagePixels(magnify_image))
 
335
      break;
 
336
  }
 
337
  /*
 
338
    Magnify each row.
 
339
  */
 
340
  for (y=0; y < (long) image->rows; y++)
 
341
  {
 
342
    p=GetImagePixels(magnify_image,0,(long) (image->rows-y-1),
 
343
      magnify_image->columns,1);
 
344
    if (p == (PixelPacket *) NULL)
 
345
      break;
 
346
    (void) memcpy(scanline,p,magnify_image->columns*sizeof(PixelPacket));
 
347
    q=GetImagePixels(magnify_image,0,(long) (2*(image->rows-y-1)),
 
348
      magnify_image->columns,1);
 
349
    if (q == (PixelPacket *) NULL)
 
350
      break;
 
351
    p=scanline+image->columns-1;
 
352
    q+=2*(image->columns-1);
 
353
    *q=(*p);
 
354
    *(q+1)=(*(p));
 
355
    for (x=1; x < (long) image->columns; x++)
 
356
    {
 
357
      p--;
 
358
      q-=2;
 
359
      *q=(*p);
 
360
      (q+1)->red=(Quantum) (((double) p->red+(double) (p+1)->red)/2+0.5);
 
361
      (q+1)->green=(Quantum) (((double) p->green+(double) (p+1)->green)/2+0.5);
 
362
      (q+1)->blue=(Quantum) (((double) p->blue+(double) (p+1)->blue)/2+0.5);
 
363
      (q+1)->opacity=(Quantum)
 
364
        (((double) p->opacity+(double) (p+1)->opacity)/2+0.5);
 
365
    }
 
366
    if (!SyncImagePixels(magnify_image))
 
367
      break;
 
368
  }
 
369
  for (y=0; y < (long) image->rows; y++)
 
370
  {
 
371
    rows=(long) Min(image->rows-y,3);
 
372
    p=GetImagePixels(magnify_image,0,2*y,magnify_image->columns,rows);
 
373
    if (p == (PixelPacket *) NULL)
 
374
      break;
 
375
    q=p;
 
376
    if (rows > 1)
 
377
      q=p+magnify_image->columns;
 
378
    r=p;
 
379
    if (rows > 2)
 
380
      r=q+magnify_image->columns;
 
381
    for (x=0; x < (long) (image->columns-1); x++)
 
382
    {
 
383
      q->red=(Quantum) (((double) p->red+(double) r->red)/2+0.5);
 
384
      q->green=(Quantum) (((double) p->green+(double) r->green)/2+0.5);
 
385
      q->blue=(Quantum) (((double) p->blue+(double) r->blue)/2+0.5);
 
386
      q->opacity=(Quantum) (((double) p->opacity+(double) r->opacity)/2+0.5);
 
387
      (q+1)->red=(Quantum) (((double) p->red+(double) (p+2)->red+
 
388
        (double) r->red+(double) (r+2)->red)/4+0.5);
 
389
      (q+1)->green=(Quantum) (((double) p->green+(double) (p+2)->green+
 
390
        (double) r->green+(double) (r+2)->green)/4+0.5);
 
391
      (q+1)->blue=(Quantum) (((double) p->blue+(double) (p+2)->blue+
 
392
        (double) r->blue+(double) (r+2)->blue)/4+0.5);
 
393
      (q+1)->opacity=(Quantum) (((double) p->opacity+(double) (p+2)->opacity+
 
394
        (double) r->opacity+(double) (r+2)->opacity)/4+0.5);
 
395
      q+=2;
 
396
      p+=2;
 
397
      r+=2;
 
398
    }
 
399
    q->red=(Quantum) (((double) p->red+(double) r->red)/2+0.5);
 
400
    q->green=(Quantum) (((double) p->green+(double) r->green)/2+0.5);
 
401
    q->blue=(Quantum) (((double) p->blue+(double) r->blue)/2+0.5);
 
402
    q->opacity=(Quantum) (((double) p->opacity+(double) r->opacity)/2+0.5);
 
403
    p++;
 
404
    q++;
 
405
    r++;
 
406
    q->red=(Quantum) (((double) p->red+(double) r->red)/2+0.5);
 
407
    q->green=(Quantum) (((double) p->green+(double) r->green)/2+0.5);
 
408
    q->blue=(Quantum) (((double) p->blue+(double) r->blue)/2+0.5);
 
409
    q->opacity=(Quantum) (((double) p->opacity+(double) r->opacity)/2+0.5);
 
410
    if (!SyncImagePixels(magnify_image))
 
411
      break;
 
412
    if (QuantumTick(y,image->rows))
 
413
      if (!MagickMonitor(MagnifyImageText,y,image->rows,exception))
 
414
        break;
 
415
  }
 
416
  p=GetImagePixels(magnify_image,0,(long) (2*image->rows-2),
 
417
    magnify_image->columns,1);
 
418
  if (p != (PixelPacket *) NULL)
 
419
    (void) memcpy(scanline,p,magnify_image->columns*sizeof(PixelPacket));
 
420
  q=GetImagePixels(magnify_image,0,(long) (2*image->rows-1),
 
421
    magnify_image->columns,1);
 
422
  if (q != (PixelPacket *) NULL)
 
423
    (void) memcpy(q,scanline,magnify_image->columns*sizeof(PixelPacket));
 
424
  (void) SyncImagePixels(magnify_image);
 
425
  MagickFreeMemory(scanline);
 
426
  magnify_image->is_grayscale=image->is_grayscale;
 
427
  return(magnify_image);
 
428
}
 
429
 
 
430
/*
 
431
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
432
%                                                                             %
 
433
%                                                                             %
 
434
%                                                                             %
 
435
%   M i n i f y I m a g e                                                     %
 
436
%                                                                             %
 
437
%                                                                             %
 
438
%                                                                             %
 
439
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
440
%
 
441
%  MinifyImage() is a convenience method that scales an image proportionally
 
442
%  to half its size.
 
443
%
 
444
%  The format of the MinifyImage method is:
 
445
%
 
446
%      Image *MinifyImage(const Image *image,ExceptionInfo *exception)
 
447
%
 
448
%  A description of each parameter follows:
 
449
%
 
450
%    o image: The image.
 
451
%
 
452
%    o exception: Return any errors or warnings in this structure.
 
453
%
 
454
%
 
455
*/
 
456
MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
 
457
{
 
458
#define Minify(weight) \
 
459
  total.red+=(weight)*(r->red); \
 
460
  total.green+=(weight)*(r->green); \
 
461
  total.blue+=(weight)*(r->blue); \
 
462
  total.opacity+=(weight)*(r->opacity); \
 
463
  r++;
 
464
#define MinifyImageText  "  Minify image...  "
 
465
 
 
466
  DoublePixelPacket
 
467
    total,
 
468
    zero;
 
469
 
 
470
  Image
 
471
    *minify_image;
 
472
 
 
473
  long
 
474
    y;
 
475
 
 
476
  register const PixelPacket
 
477
    *p,
 
478
    *r;
 
479
 
 
480
  register long
 
481
    x;
 
482
 
 
483
  register PixelPacket
 
484
    *q;
 
485
 
 
486
  /*
 
487
    Initialize minified image.
 
488
  */
 
489
  assert(image != (Image *) NULL);
 
490
  assert(image->signature == MagickSignature);
 
491
  assert(exception != (ExceptionInfo *) NULL);
 
492
  assert(exception->signature == MagickSignature);
 
493
 
 
494
  minify_image=CloneImage(image,Max(image->columns/2,1),Max(image->rows/2,1),
 
495
    True,exception);
 
496
  if (minify_image == (Image *) NULL)
 
497
    return((Image *) NULL);
 
498
 
 
499
  LogMagickEvent(TransformEvent,GetMagickModule(),
 
500
    "Minifying image of size %lux%lu to %lux%lu",
 
501
    image->columns,image->rows,minify_image->columns,minify_image->rows);
 
502
 
 
503
  minify_image->storage_class=DirectClass;
 
504
  /*
 
505
    Reduce each row.
 
506
  */
 
507
  memset(&zero,0,sizeof(DoublePixelPacket));
 
508
  for (y=0; y < (long) minify_image->rows; y++)
 
509
  {
 
510
    p=AcquireImagePixels(image,-2,2*(y-1),image->columns+4,4,exception);
 
511
    q=SetImagePixels(minify_image,0,y,minify_image->columns,1);
 
512
    if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
 
513
      break;
 
514
    for (x=0; x < (long) minify_image->columns; x++)
 
515
    {
 
516
      /*
 
517
        Compute weighted average of target pixel color components.
 
518
      */
 
519
      total=zero;
 
520
      r=p;
 
521
      Minify(3.0); Minify(7.0);  Minify(7.0);  Minify(3.0);
 
522
      r=p+(image->columns+4);
 
523
      Minify(7.0); Minify(15.0); Minify(15.0); Minify(7.0);
 
524
      r=p+2*(image->columns+4);
 
525
      Minify(7.0); Minify(15.0); Minify(15.0); Minify(7.0);
 
526
      r=p+3*(image->columns+4);
 
527
      Minify(3.0); Minify(7.0);  Minify(7.0);  Minify(3.0);
 
528
      q->red=(Quantum) (total.red/128.0+0.5);
 
529
      q->green=(Quantum) (total.green/128.0+0.5);
 
530
      q->blue=(Quantum) (total.blue/128.0+0.5);
 
531
      q->opacity=(Quantum) (total.opacity/128.0+0.5);
 
532
      p+=2;
 
533
      q++;
 
534
    }
 
535
    if (!SyncImagePixels(minify_image))
 
536
      break;
 
537
    if (QuantumTick(y,image->rows))
 
538
      if (!MagickMonitor(MinifyImageText,y,minify_image->rows,exception))
 
539
        break;
 
540
  }
 
541
  minify_image->is_grayscale=image->is_grayscale;
 
542
  return(minify_image);
 
543
}
 
544
 
 
545
/*
 
546
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
547
%                                                                             %
 
548
%                                                                             %
 
549
%                                                                             %
 
550
%   R e s i z e I m a g e                                                     %
 
551
%                                                                             %
 
552
%                                                                             %
 
553
%                                                                             %
 
554
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
555
%
 
556
%  ResizeImage() scales an image to the desired dimensions with one of these
 
557
%  filters:
 
558
%
 
559
%    Bessel   Blackman   Box
 
560
%    Catrom   Cubic      Gaussian
 
561
%    Hanning  Hermite    Lanczos
 
562
%    Mitchell Point      Quandratic
 
563
%    Sinc     Triangle
 
564
%
 
565
%  Most of the filters are FIR (finite impulse response), however, Bessel,
 
566
%  Gaussian, and Sinc are IIR (infinite impulse response).  Bessel and Sinc
 
567
%  are windowed (brought down to zero) with the Blackman filter.
 
568
%
 
569
%  ResizeImage() was inspired by Paul Heckbert's zoom program.
 
570
%
 
571
%  The format of the ResizeImage method is:
 
572
%
 
573
%      Image *ResizeImage(Image *image,const unsigned long columns,
 
574
%        const unsigned long rows,const FilterTypes filter,const double blur,
 
575
%        ExceptionInfo *exception)
 
576
%
 
577
%  A description of each parameter follows:
 
578
%
 
579
%    o image: The image.
 
580
%
 
581
%    o columns: The number of columns in the scaled image.
 
582
%
 
583
%    o rows: The number of rows in the scaled image.
 
584
%
 
585
%    o filter: Image filter to use.
 
586
%
 
587
%    o blur: The blur factor where > 1 is blurry, < 1 is sharp.
 
588
%
 
589
%    o exception: Return any errors or warnings in this structure.
 
590
%
 
591
%
 
592
*/
 
593
 
 
594
static double Bessel(const double x,const double support)
 
595
{
 
596
  if (x == 0.0)
 
597
    return(MagickPI/4.0);
 
598
  return(BesselOrderOne(MagickPI*x)/(2.0*x));
 
599
}
 
600
 
 
601
static double Sinc(const double x,const double support)
 
602
{
 
603
  if (x == 0.0)
 
604
    return(1.0);
 
605
  return(sin(MagickPI*x)/(MagickPI*x));
 
606
}
 
607
 
 
608
static double Blackman(const double x,const double support)
 
609
{
 
610
  return(0.42+0.5*cos(MagickPI*x)+0.08*cos(2*MagickPI*x));
 
611
}
 
612
 
 
613
static double BlackmanBessel(const double x,const double support)
 
614
{
 
615
  return(Blackman(x/support,support)*Bessel(x,support));
 
616
}
 
617
 
 
618
static double BlackmanSinc(const double x,const double support)
 
619
{
 
620
  return(Blackman(x/support,support)*Sinc(x,support));
 
621
}
 
622
 
 
623
static double Box(const double x,const double support)
 
624
{
 
625
  if (x < -0.5)
 
626
    return(0.0);
 
627
  if (x < 0.5)
 
628
    return(1.0);
 
629
  return(0.0);
 
630
}
 
631
 
 
632
static double Catrom(const double x,const double support)
 
633
{
 
634
  if (x < -2.0)
 
635
    return(0.0);
 
636
  if (x < -1.0)
 
637
    return(0.5*(4.0+x*(8.0+x*(5.0+x))));
 
638
  if (x < 0.0)
 
639
    return(0.5*(2.0+x*x*(-5.0-3.0*x)));
 
640
  if (x < 1.0)
 
641
    return(0.5*(2.0+x*x*(-5.0+3.0*x)));
 
642
  if (x < 2.0)
 
643
    return(0.5*(4.0+x*(-8.0+x*(5.0-x))));
 
644
  return(0.0);
 
645
}
 
646
 
 
647
static double Cubic(const double x,const double support)
 
648
{
 
649
  if (x < -2.0)
 
650
    return(0.0);
 
651
  if (x < -1.0)
 
652
    return((2.0+x)*(2.0+x)*(2.0+x)/6.0);
 
653
  if (x < 0.0)
 
654
    return((4.0+x*x*(-6.0-3.0*x))/6.0);
 
655
  if (x < 1.0)
 
656
    return((4.0+x*x*(-6.0+3.0*x))/6.0);
 
657
  if (x < 2.0)
 
658
    return((2.0-x)*(2.0-x)*(2.0-x)/6.0);
 
659
  return(0.0);
 
660
}
 
661
 
 
662
static double Gaussian(const double x,const double support)
 
663
{
 
664
  return(exp(-2.0*x*x)*sqrt(2.0/MagickPI));
 
665
}
 
666
 
 
667
static double Hanning(const double x,const double support)
 
668
{
 
669
  return(0.5+0.5*cos(MagickPI*x));
 
670
}
 
671
 
 
672
static double Hamming(const double x,const double support)
 
673
{
 
674
  return(0.54+0.46*cos(MagickPI*x));
 
675
}
 
676
 
 
677
static double Hermite(const double x,const double support)
 
678
{
 
679
  if (x < -1.0)
 
680
    return(0.0);
 
681
  if (x < 0.0)
 
682
    return((2.0*(-x)-3.0)*(-x)*(-x)+1.0);
 
683
  if (x < 1.0)
 
684
    return((2.0*x-3.0)*x*x+1.0);
 
685
  return(0.0);
 
686
}
 
687
 
 
688
static double Lanczos(const double x,const double support)
 
689
{
 
690
  if (x < -3.0)
 
691
    return(0.0);
 
692
  if (x < 0.0)
 
693
    return(Sinc(-x,support)*Sinc(-x/3.0,support));
 
694
  if (x < 3.0)
 
695
    return(Sinc(x,support)*Sinc(x/3.0,support));
 
696
  return(0.0);
 
697
}
 
698
 
 
699
static double Mitchell(const double x,const double support)
 
700
{
 
701
#define B   (1.0/3.0)
 
702
#define C   (1.0/3.0)
 
703
#define P0  ((  6.0- 2.0*B       )/6.0)
 
704
#define P2  ((-18.0+12.0*B+ 6.0*C)/6.0)
 
705
#define P3  (( 12.0- 9.0*B- 6.0*C)/6.0)
 
706
#define Q0  ((       8.0*B+24.0*C)/6.0)
 
707
#define Q1  ((     -12.0*B-48.0*C)/6.0)
 
708
#define Q2  ((       6.0*B+30.0*C)/6.0)
 
709
#define Q3  ((     - 1.0*B- 6.0*C)/6.0)
 
710
 
 
711
  if (x < -2.0)
 
712
    return(0.0);
 
713
  if (x < -1.0)
 
714
    return(Q0-x*(Q1-x*(Q2-x*Q3)));
 
715
  if (x < 0.0)
 
716
    return(P0+x*x*(P2-x*P3));
 
717
  if (x < 1.0)
 
718
    return(P0+x*x*(P2+x*P3));
 
719
  if (x < 2.0)
 
720
    return(Q0+x*(Q1+x*(Q2+x*Q3)));
 
721
  return(0.0);
 
722
}
 
723
 
 
724
static double Quadratic(const double x,const double support)
 
725
{
 
726
  if (x < -1.5)
 
727
    return(0.0);
 
728
  if (x < -0.5)
 
729
    return(0.5*(x+1.5)*(x+1.5));
 
730
  if (x < 0.5)
 
731
    return(0.75-x*x);
 
732
  if (x < 1.5)
 
733
    return(0.5*(x-1.5)*(x-1.5));
 
734
  return(0.0);
 
735
}
 
736
 
 
737
static double Triangle(const double x,const double support)
 
738
{
 
739
  if (x < -1.0)
 
740
    return(0.0);
 
741
  if (x < 0.0)
 
742
    return(1.0+x);
 
743
  if (x < 1.0)
 
744
    return(1.0-x);
 
745
  return(0.0);
 
746
}
 
747
 
 
748
static unsigned int HorizontalFilter(const Image *source,Image *destination,
 
749
  const double x_factor,const FilterInfo *filter_info,const double blur,
 
750
  ContributionInfo *contribution,const size_t span,unsigned long *quantum,
 
751
  ExceptionInfo *exception)
 
752
{
 
753
#define ResizeImageText  "  Resize image...  "
 
754
 
 
755
  double
 
756
    center,
 
757
    density,
 
758
    scale,
 
759
    support;
 
760
 
 
761
  DoublePixelPacket
 
762
    pixel,
 
763
    zero;
 
764
 
 
765
  long
 
766
    j,
 
767
    n,
 
768
    start,
 
769
    stop,
 
770
    y;
 
771
 
 
772
  register const PixelPacket
 
773
    *p;
 
774
 
 
775
  register IndexPacket
 
776
    *indexes,
 
777
    *source_indexes;
 
778
 
 
779
  register long
 
780
    i,
 
781
    x;
 
782
 
 
783
  register PixelPacket
 
784
    *q;
 
785
 
 
786
  /*
 
787
    Apply filter to resize horizontally from source to destination.
 
788
  */
 
789
  scale=blur*Max(1.0/x_factor,1.0);
 
790
  support=scale*filter_info->support;
 
791
  destination->storage_class=source->storage_class;
 
792
  if (support > 0.5)
 
793
    destination->storage_class=DirectClass;
 
794
  else
 
795
    {
 
796
      /*
 
797
        Reduce to point sampling.
 
798
      */
 
799
      support=0.5+MagickEpsilon;
 
800
      scale=1.0;
 
801
    }
 
802
  scale=1.0/scale;
 
803
  memset(&zero,0,sizeof(DoublePixelPacket));
 
804
  for (x=0; x < (long) destination->columns; x++)
 
805
  {
 
806
    center=(double) (x+0.5)/x_factor;
 
807
    start=(long) Max(center-support+0.5,0);
 
808
    stop=(long) Min(center+support+0.5,source->columns);
 
809
    density=0.0;
 
810
    for (n=0; n < (stop-start); n++)
 
811
    {
 
812
      contribution[n].pixel=start+n;
 
813
      contribution[n].weight=
 
814
        filter_info->function(scale*(start+n-center+0.5),filter_info->support);
 
815
      density+=contribution[n].weight;
 
816
    }
 
817
    if ((density != 0.0) && (density != 1.0))
 
818
      {
 
819
        /*
 
820
          Normalize.
 
821
        */
 
822
        density=1.0/density;
 
823
        for (i=0; i < n; i++)
 
824
          contribution[i].weight*=density;
 
825
      }
 
826
    p=AcquireImagePixels(source,contribution[0].pixel,0,
 
827
      contribution[n-1].pixel-contribution[0].pixel+1,source->rows,exception);
 
828
    q=SetImagePixels(destination,x,0,1,destination->rows);
 
829
    if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
 
830
      break;
 
831
    source_indexes=GetIndexes(source);
 
832
    indexes=GetIndexes(destination);
 
833
    for (y=0; y < (long) destination->rows; y++)
 
834
    {
 
835
      pixel=zero;
 
836
      for (i=0; i < n; i++)
 
837
      {
 
838
        j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
 
839
          (contribution[i].pixel-contribution[0].pixel);
 
840
        pixel.red+=contribution[i].weight*(p+j)->red;
 
841
        pixel.green+=contribution[i].weight*(p+j)->green;
 
842
        pixel.blue+=contribution[i].weight*(p+j)->blue;
 
843
        if ((source->matte) || (source->colorspace == CMYKColorspace))
 
844
          pixel.opacity+=contribution[i].weight*(p+j)->opacity;
 
845
      }
 
846
      if ((indexes != (IndexPacket *) NULL) &&
 
847
          (source_indexes != (IndexPacket *) NULL))
 
848
        {
 
849
          i=Min(Max((long) (center+0.5),start),stop-1);
 
850
          j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
 
851
            (contribution[i-start].pixel-contribution[0].pixel);
 
852
          indexes[y]=source_indexes[j];
 
853
        }
 
854
      q->red=(Quantum) ((pixel.red < 0) ? 0 :
 
855
        (pixel.red > MaxRGB) ? MaxRGB : pixel.red+0.5);
 
856
      q->green=(Quantum) ((pixel.green < 0) ? 0 :
 
857
        (pixel.green > MaxRGB) ? MaxRGB : pixel.green+0.5);
 
858
      q->blue=(Quantum) ((pixel.blue < 0) ? 0 :
 
859
        (pixel.blue > MaxRGB) ? MaxRGB : pixel.blue+0.5);
 
860
      if ((destination->matte) || (destination->colorspace == CMYKColorspace))
 
861
        q->opacity=(Quantum) ((pixel.opacity < 0) ? 0 :
 
862
          (pixel.opacity > MaxRGB) ? MaxRGB : pixel.opacity+0.5);
 
863
      q++;
 
864
    }
 
865
    if (!SyncImagePixels(destination))
 
866
      break;
 
867
    if (QuantumTick(*quantum,span))
 
868
      if (!MagickMonitor(ResizeImageText,*quantum,span,exception))
 
869
        break;
 
870
    (*quantum)++;
 
871
  }
 
872
  return(x == (long) destination->columns);
 
873
}
 
874
 
 
875
static unsigned int VerticalFilter(const Image *source,Image *destination,
 
876
  const double y_factor,const FilterInfo *filter_info,const double blur,
 
877
  ContributionInfo *contribution,const size_t span,unsigned long *quantum,
 
878
  ExceptionInfo *exception)
 
879
{
 
880
  double
 
881
    center,
 
882
    density,
 
883
    scale,
 
884
    support;
 
885
 
 
886
  DoublePixelPacket
 
887
    pixel,
 
888
    zero;
 
889
 
 
890
  long
 
891
    j,
 
892
    n,
 
893
    start,
 
894
    stop,
 
895
    x;
 
896
 
 
897
  register const PixelPacket
 
898
    *p;
 
899
 
 
900
  register IndexPacket
 
901
    *indexes,
 
902
    *source_indexes;
 
903
 
 
904
  register long
 
905
    i,
 
906
    y;
 
907
 
 
908
  register PixelPacket
 
909
    *q;
 
910
 
 
911
  /*
 
912
    Apply filter to resize vertically from source to destination.
 
913
  */
 
914
  scale=blur*Max(1.0/y_factor,1.0);
 
915
  support=scale*filter_info->support;
 
916
  destination->storage_class=source->storage_class;
 
917
  if (support > 0.5)
 
918
    destination->storage_class=DirectClass;
 
919
  else
 
920
    {
 
921
      /*
 
922
        Reduce to point sampling.
 
923
      */
 
924
      support=0.5+MagickEpsilon;
 
925
      scale=1.0;
 
926
    }
 
927
  scale=1.0/scale;
 
928
  memset(&zero,0,sizeof(DoublePixelPacket));
 
929
  for (y=0; y < (long) destination->rows; y++)
 
930
  {
 
931
    center=(double) (y+0.5)/y_factor;
 
932
    start=(long) Max(center-support+0.5,0);
 
933
    stop=(long) Min(center+support+0.5,source->rows);
 
934
    density=0.0;
 
935
    for (n=0; n < (stop-start); n++)
 
936
    {
 
937
      contribution[n].pixel=start+n;
 
938
      contribution[n].weight=
 
939
        filter_info->function(scale*(start+n-center+0.5),filter_info->support);
 
940
      density+=contribution[n].weight;
 
941
    }
 
942
    if ((density != 0.0) && (density != 1.0))
 
943
      {
 
944
        /*
 
945
          Normalize.
 
946
        */
 
947
        density=1.0/density;
 
948
        for (i=0; i < n; i++)
 
949
          contribution[i].weight*=density;
 
950
      }
 
951
    p=AcquireImagePixels(source,0,contribution[0].pixel,source->columns,
 
952
      contribution[n-1].pixel-contribution[0].pixel+1,exception);
 
953
    q=SetImagePixels(destination,0,y,destination->columns,1);
 
954
    if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
 
955
      break;
 
956
    source_indexes=GetIndexes(source);
 
957
    indexes=GetIndexes(destination);
 
958
    for (x=0; x < (long) destination->columns; x++)
 
959
    {
 
960
      pixel=zero;
 
961
      for (i=0; i < n; i++)
 
962
      {
 
963
        j=(long) ((contribution[i].pixel-contribution[0].pixel)*
 
964
          source->columns+x);
 
965
        pixel.red+=contribution[i].weight*(p+j)->red;
 
966
        pixel.green+=contribution[i].weight*(p+j)->green;
 
967
        pixel.blue+=contribution[i].weight*(p+j)->blue;
 
968
        if ((source->matte) || (source->colorspace == CMYKColorspace))
 
969
          pixel.opacity+=contribution[i].weight*(p+j)->opacity;
 
970
      }
 
971
      if ((indexes != (IndexPacket *) NULL) &&
 
972
          (source_indexes != (IndexPacket *) NULL))
 
973
        {
 
974
          i=Min(Max((long) (center+0.5),start),stop-1);
 
975
          j=(long) ((contribution[i-start].pixel-contribution[0].pixel)*
 
976
            source->columns+x);
 
977
          indexes[x]=source_indexes[j];
 
978
        }
 
979
      q->red=(Quantum) ((pixel.red < 0) ? 0 :
 
980
        (pixel.red > MaxRGB) ? MaxRGB : pixel.red+0.5);
 
981
      q->green=(Quantum) ((pixel.green < 0) ? 0 :
 
982
        (pixel.green > MaxRGB) ? MaxRGB : pixel.green+0.5);
 
983
      q->blue=(Quantum) ((pixel.blue < 0) ? 0 :
 
984
        (pixel.blue > MaxRGB) ? MaxRGB : pixel.blue+0.5);
 
985
      if ((destination->matte) || (destination->colorspace == CMYKColorspace))
 
986
        q->opacity=(Quantum) ((pixel.opacity < 0) ? 0 :
 
987
          (pixel.opacity > MaxRGB) ? MaxRGB : pixel.opacity+0.5);
 
988
      q++;
 
989
    }
 
990
    if (!SyncImagePixels(destination))
 
991
      break;
 
992
    if (QuantumTick(*quantum,span))
 
993
      if (!MagickMonitor(ResizeImageText,*quantum,span,exception))
 
994
        break;
 
995
    (*quantum)++;
 
996
  }
 
997
  return(y == (long) destination->rows);
 
998
}
 
999
 
 
1000
static const char *ResizeFilterToString(const FilterTypes filter)
 
1001
{
 
1002
  const char *
 
1003
    filter_string = "Unknown";
 
1004
 
 
1005
  switch (filter)
 
1006
    {
 
1007
    case UndefinedFilter:
 
1008
      filter_string="Undefined";
 
1009
      break;
 
1010
    case PointFilter:
 
1011
      filter_string="Point";
 
1012
      break;
 
1013
    case BoxFilter:
 
1014
      filter_string="Box";
 
1015
      break;
 
1016
    case TriangleFilter:
 
1017
      filter_string="Triangle";
 
1018
      break;
 
1019
    case HermiteFilter:
 
1020
      filter_string="Hermite";
 
1021
      break;
 
1022
    case HanningFilter:
 
1023
      filter_string="Hanning";
 
1024
      break;
 
1025
    case HammingFilter:
 
1026
      filter_string="Hamming";
 
1027
      break;
 
1028
    case BlackmanFilter:
 
1029
      filter_string="Blackman";
 
1030
      break;
 
1031
    case GaussianFilter:
 
1032
      filter_string="Gaussian";
 
1033
      break;
 
1034
    case QuadraticFilter:
 
1035
      filter_string="Quadratic";
 
1036
      break;
 
1037
    case CubicFilter:
 
1038
      filter_string="Cubi";
 
1039
      break;
 
1040
    case CatromFilter:
 
1041
      filter_string="Catrom";
 
1042
      break;
 
1043
    case MitchellFilter:
 
1044
      filter_string="Mitchell";
 
1045
      break;
 
1046
    case LanczosFilter:
 
1047
      filter_string="Lanczos";
 
1048
      break;
 
1049
    case BesselFilter:
 
1050
      filter_string="Bessel";
 
1051
      break;
 
1052
    case SincFilter:
 
1053
      filter_string="Sinc";
 
1054
      break;
 
1055
    }
 
1056
 
 
1057
  return filter_string;
 
1058
}
 
1059
 
 
1060
MagickExport Image *ResizeImage(const Image *image,const unsigned long columns,
 
1061
  const unsigned long rows,const FilterTypes filter,const double blur,
 
1062
  ExceptionInfo *exception)
 
1063
{
 
1064
  ContributionInfo
 
1065
    *contribution;
 
1066
 
 
1067
  double
 
1068
    support,
 
1069
    x_factor,
 
1070
    x_support,
 
1071
    y_factor,
 
1072
    y_support;
 
1073
 
 
1074
  Image
 
1075
    *source_image,
 
1076
    *resize_image;
 
1077
 
 
1078
  register long
 
1079
    i;
 
1080
 
 
1081
  static const FilterInfo
 
1082
    filters[SincFilter+1] =
 
1083
    {
 
1084
      { Box, 0.0 },
 
1085
      { Box, 0.0 },
 
1086
      { Box, 0.5 },
 
1087
      { Triangle, 1.0 },
 
1088
      { Hermite, 1.0 },
 
1089
      { Hanning, 1.0 },
 
1090
      { Hamming, 1.0 },
 
1091
      { Blackman, 1.0 },
 
1092
      { Gaussian, 1.25 },
 
1093
      { Quadratic, 1.5 },
 
1094
      { Cubic, 2.0 },
 
1095
      { Catrom, 2.0 },
 
1096
      { Mitchell, 2.0 },
 
1097
      { Lanczos, 3.0 },
 
1098
      { BlackmanBessel, 3.2383 },
 
1099
      { BlackmanSinc, 4.0 }
 
1100
    };
 
1101
 
 
1102
  size_t
 
1103
    span;
 
1104
 
 
1105
  unsigned int
 
1106
    status;
 
1107
 
 
1108
  unsigned long
 
1109
    quantum;
 
1110
 
 
1111
  /*
 
1112
    Initialize resize image attributes.
 
1113
  */
 
1114
  assert(image != (Image *) NULL);
 
1115
  assert(image->signature == MagickSignature);
 
1116
  assert(exception != (ExceptionInfo *) NULL);
 
1117
  assert(exception->signature == MagickSignature);
 
1118
  assert((filter >= 0) && (filter <= SincFilter));
 
1119
  if ((columns == 0) || (rows == 0))
 
1120
    ThrowImageException(ImageError,UnableToResizeImage,
 
1121
      MagickMsg(CorruptImageError,NegativeOrZeroImageSize));
 
1122
  if ((columns == image->columns) && (rows == image->rows) && (blur == 1.0))
 
1123
    return(CloneImage(image,0,0,True,exception));
 
1124
  resize_image=CloneImage(image,columns,rows,True,exception);
 
1125
  if (resize_image == (Image *) NULL)
 
1126
    return((Image *) NULL);
 
1127
  /*
 
1128
    Allocate filter contribution info.
 
1129
  */
 
1130
  x_factor=(double) resize_image->columns/image->columns;
 
1131
  y_factor=(double) resize_image->rows/image->rows;
 
1132
  i=(long) DefaultResizeFilter;
 
1133
  if (image->filter != UndefinedFilter)
 
1134
    i=(long) image->filter;
 
1135
  else
 
1136
    if ((image->storage_class == PseudoClass) || image->matte ||
 
1137
        ((x_factor*y_factor) > 1.0))
 
1138
      i=(long) MitchellFilter;
 
1139
 
 
1140
  LogMagickEvent(TransformEvent,GetMagickModule(),
 
1141
    "Resizing image of size %lux%lu to %lux%lu using %s filter",
 
1142
    image->columns,image->rows,columns,rows,
 
1143
    ResizeFilterToString((FilterTypes)i));
 
1144
 
 
1145
  x_support=blur*Max(1.0/x_factor,1.0)*filters[i].support;
 
1146
  y_support=blur*Max(1.0/y_factor,1.0)*filters[i].support;
 
1147
  support=Max(x_support,y_support);
 
1148
  if (support < filters[i].support)
 
1149
    support=filters[i].support;
 
1150
  contribution=MagickAllocateMemory(ContributionInfo *,
 
1151
    (size_t) (2.0*Max(support,0.5)+3)*sizeof(ContributionInfo));
 
1152
  if (contribution == (ContributionInfo *) NULL)
 
1153
    {
 
1154
      DestroyImage(resize_image);
 
1155
      ThrowImageException3(ResourceLimitError,MemoryAllocationFailed,
 
1156
        UnableToResizeImage)
 
1157
    }
 
1158
 
 
1159
  /*
 
1160
    Resize image.
 
1161
  */
 
1162
  quantum=0;
 
1163
  if (((double) columns*(image->rows+rows)) >
 
1164
      ((double) rows*(image->columns+columns)))
 
1165
    {
 
1166
      source_image=CloneImage(resize_image,columns,image->rows,True,exception);
 
1167
      if (source_image == (Image *) NULL)
 
1168
        {
 
1169
          MagickFreeMemory(contribution);
 
1170
          DestroyImage(resize_image);
 
1171
          return((Image *) NULL);
 
1172
        }
 
1173
      span=source_image->columns+resize_image->rows;
 
1174
      status=HorizontalFilter(image,source_image,x_factor,&filters[i],blur,
 
1175
        contribution,span,&quantum,exception);
 
1176
      status|=VerticalFilter(source_image,resize_image,y_factor,&filters[i],
 
1177
        blur,contribution,span,&quantum,exception);
 
1178
    }
 
1179
  else
 
1180
    {
 
1181
      source_image=CloneImage(resize_image,image->columns,rows,True,exception);
 
1182
      if (source_image == (Image *) NULL)
 
1183
        {
 
1184
          MagickFreeMemory(contribution);
 
1185
          DestroyImage(resize_image);
 
1186
          return((Image *) NULL);
 
1187
        }
 
1188
      span=resize_image->columns+source_image->rows;
 
1189
      status=VerticalFilter(image,source_image,y_factor,&filters[i],blur,
 
1190
        contribution,span,&quantum,exception);
 
1191
      status|=HorizontalFilter(source_image,resize_image,x_factor,&filters[i],
 
1192
        blur,contribution,span,&quantum,exception);
 
1193
    }
 
1194
  /*
 
1195
    Free allocated memory.
 
1196
  */
 
1197
  MagickFreeMemory(contribution);
 
1198
  DestroyImage(source_image);
 
1199
  if (status == False)
 
1200
    {
 
1201
      DestroyImage(resize_image);
 
1202
      ThrowImageException3(ResourceLimitError,MemoryAllocationFailed,
 
1203
        UnableToResizeImage)
 
1204
    }
 
1205
  resize_image->is_grayscale=image->is_grayscale;
 
1206
  return(resize_image);
 
1207
}
 
1208
 
 
1209
/*
 
1210
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1211
%                                                                             %
 
1212
%                                                                             %
 
1213
%                                                                             %
 
1214
%   S a m p l e I m a g e                                                     %
 
1215
%                                                                             %
 
1216
%                                                                             %
 
1217
%                                                                             %
 
1218
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1219
%
 
1220
%  SampleImage() scales an image to the desired dimensions with pixel
 
1221
%  sampling.  Unlike other scaling methods, this method does not introduce
 
1222
%  any additional color into the scaled image.
 
1223
%
 
1224
%  The format of the SampleImage method is:
 
1225
%
 
1226
%      Image *SampleImage(const Image *image,const unsigned long columns,
 
1227
%        const unsigned long rows,ExceptionInfo *exception)
 
1228
%
 
1229
%  A description of each parameter follows:
 
1230
%
 
1231
%    o image: The image.
 
1232
%
 
1233
%    o columns: The number of columns in the sampled image.
 
1234
%
 
1235
%    o rows: The number of rows in the sampled image.
 
1236
%
 
1237
%    o exception: Return any errors or warnings in this structure.
 
1238
%
 
1239
%
 
1240
*/
 
1241
MagickExport Image *SampleImage(const Image *image,const unsigned long columns,
 
1242
  const unsigned long rows,ExceptionInfo *exception)
 
1243
{
 
1244
#define SampleImageText  "  Sample image...  "
 
1245
 
 
1246
  double
 
1247
    *x_offset,
 
1248
    *y_offset;
 
1249
 
 
1250
  Image
 
1251
    *sample_image;
 
1252
 
 
1253
  long
 
1254
    j,
 
1255
    y;
 
1256
 
 
1257
  PixelPacket
 
1258
    *pixels;
 
1259
 
 
1260
  register const PixelPacket
 
1261
    *p;
 
1262
 
 
1263
  register IndexPacket
 
1264
    *indexes,
 
1265
    *sample_indexes;
 
1266
 
 
1267
  register long
 
1268
    x;
 
1269
 
 
1270
  register PixelPacket
 
1271
    *q;
 
1272
 
 
1273
  /*
 
1274
    Initialize sampled image attributes.
 
1275
  */
 
1276
  assert(image != (const Image *) NULL);
 
1277
  assert(image->signature == MagickSignature);
 
1278
  assert(exception != (ExceptionInfo *) NULL);
 
1279
  assert(exception->signature == MagickSignature);
 
1280
  if ((columns == 0) || (rows == 0))
 
1281
    ThrowImageException(ImageError,UnableToResizeImage,
 
1282
      MagickMsg(CorruptImageError,NegativeOrZeroImageSize));
 
1283
  if ((columns == image->columns) && (rows == image->rows))
 
1284
    return(CloneImage(image,0,0,True,exception));
 
1285
  sample_image=CloneImage(image,columns,rows,True,exception);
 
1286
  if (sample_image == (Image *) NULL)
 
1287
    return((Image *) NULL);
 
1288
 
 
1289
  LogMagickEvent(TransformEvent,GetMagickModule(),
 
1290
    "Sampling image of size %lux%lu to %lux%lu",
 
1291
    image->columns,image->rows,sample_image->columns,sample_image->rows);
 
1292
 
 
1293
  /*
 
1294
    Allocate scan line buffer and column offset buffers.
 
1295
  */
 
1296
  pixels=MagickAllocateMemory(PixelPacket *,image->columns*sizeof(PixelPacket));
 
1297
  x_offset=MagickAllocateMemory(double *,sample_image->columns*sizeof(double));
 
1298
  y_offset=MagickAllocateMemory(double *,sample_image->rows*sizeof(double));
 
1299
  if ((pixels == (PixelPacket *) NULL) || (x_offset == (double *) NULL) ||
 
1300
      (y_offset == (double *) NULL))
 
1301
    {
 
1302
      DestroyImage(sample_image);
 
1303
      ThrowImageException3(ResourceLimitError,MemoryAllocationFailed,
 
1304
        UnableToSampleImage)
 
1305
    }
 
1306
  /*
 
1307
    Initialize pixel offsets.
 
1308
  */
 
1309
  for (x=0; x < (long) sample_image->columns; x++)
 
1310
    x_offset[x]=(double) x*image->columns/(double) sample_image->columns;
 
1311
  for (y=0; y < (long) sample_image->rows; y++)
 
1312
    y_offset[y]=(double) y*image->rows/(double) sample_image->rows;
 
1313
  /*
 
1314
    Sample each row.
 
1315
  */
 
1316
  j=(-1);
 
1317
  for (y=0; y < (long) sample_image->rows; y++)
 
1318
  {
 
1319
    q=SetImagePixels(sample_image,0,y,sample_image->columns,1);
 
1320
    if (q == (PixelPacket *) NULL)
 
1321
      break;
 
1322
    if (j != (long) y_offset[y])
 
1323
      {
 
1324
        /*
 
1325
          Read a scan line.
 
1326
        */
 
1327
        j=(long) y_offset[y];
 
1328
        p=AcquireImagePixels(image,0,j,image->columns,1,exception);
 
1329
        if (p == (const PixelPacket *) NULL)
 
1330
          break;
 
1331
        (void) memcpy(pixels,p,image->columns*sizeof(PixelPacket));
 
1332
      }
 
1333
    /*
 
1334
      Sample each column.
 
1335
    */
 
1336
    for (x=0; x < (long) sample_image->columns; x++)
 
1337
      *q++=pixels[(long) x_offset[x]];
 
1338
    indexes=GetIndexes(image);
 
1339
    sample_indexes=GetIndexes(sample_image);
 
1340
    if ((indexes != (IndexPacket *) NULL) &&
 
1341
        (sample_indexes != (IndexPacket *) NULL))
 
1342
      for (x=0; x < (long) sample_image->columns; x++)
 
1343
        sample_indexes[x]=indexes[(long) x_offset[x]];
 
1344
    if (!SyncImagePixels(sample_image))
 
1345
      break;
 
1346
    if (QuantumTick(y,sample_image->rows))
 
1347
      if (!MagickMonitor(SampleImageText,y,sample_image->rows,exception))
 
1348
        break;
 
1349
  }
 
1350
  MagickFreeMemory(y_offset);
 
1351
  MagickFreeMemory(x_offset);
 
1352
  MagickFreeMemory(pixels);
 
1353
  /*
 
1354
    Sampling does not change the image properties.
 
1355
  */
 
1356
  sample_image->is_monochrome=image->is_monochrome;
 
1357
  sample_image->is_grayscale=image->is_grayscale;
 
1358
  return(sample_image);
 
1359
}
 
1360
 
 
1361
/*
 
1362
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1363
%                                                                             %
 
1364
%                                                                             %
 
1365
%                                                                             %
 
1366
%   S c a l e I m a g e                                                       %
 
1367
%                                                                             %
 
1368
%                                                                             %
 
1369
%                                                                             %
 
1370
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1371
%
 
1372
%  ScaleImage() changes the size of an image to the given dimensions.
 
1373
%
 
1374
%  The format of the ScaleImage method is:
 
1375
%
 
1376
%      Image *ScaleImage(const Image *image,const unsigned long columns,
 
1377
%        const unsigned long rows,ExceptionInfo *exception)
 
1378
%
 
1379
%  A description of each parameter follows:
 
1380
%
 
1381
%    o image: The image.
 
1382
%
 
1383
%    o columns: The number of columns in the scaled image.
 
1384
%
 
1385
%    o rows: The number of rows in the scaled image.
 
1386
%
 
1387
%    o exception: Return any errors or warnings in this structure.
 
1388
%
 
1389
%
 
1390
*/
 
1391
MagickExport Image *ScaleImage(const Image *image,const unsigned long columns,
 
1392
  const unsigned long rows,ExceptionInfo *exception)
 
1393
{
 
1394
#define ScaleImageText  "  Scale image...  "
 
1395
 
 
1396
  double
 
1397
    x_scale,
 
1398
    x_span,
 
1399
    y_scale,
 
1400
    y_span;
 
1401
 
 
1402
  DoublePixelPacket
 
1403
    pixel,
 
1404
    *scale_scanline,
 
1405
    *scanline,
 
1406
    *x_vector,
 
1407
    *y_vector,
 
1408
    zero;
 
1409
 
 
1410
  Image
 
1411
    *scale_image;
 
1412
 
 
1413
  long
 
1414
    number_rows,
 
1415
    y;
 
1416
 
 
1417
  register const PixelPacket
 
1418
    *p;
 
1419
 
 
1420
  register long
 
1421
    i,
 
1422
    x;
 
1423
 
 
1424
  register PixelPacket
 
1425
    *q;
 
1426
 
 
1427
  register DoublePixelPacket
 
1428
    *s,
 
1429
    *t;
 
1430
 
 
1431
  unsigned int
 
1432
    next_column,
 
1433
    next_row;
 
1434
 
 
1435
  /*
 
1436
    Initialize scaled image attributes.
 
1437
  */
 
1438
  assert(image != (const Image *) NULL);
 
1439
  assert(image->signature == MagickSignature);
 
1440
  assert(exception != (ExceptionInfo *) NULL);
 
1441
  assert(exception->signature == MagickSignature);
 
1442
  if ((columns == 0) || (rows == 0))
 
1443
    return((Image *) NULL);
 
1444
  scale_image=CloneImage(image,columns,rows,True,exception);
 
1445
  if (scale_image == (Image *) NULL)
 
1446
    return((Image *) NULL);
 
1447
 
 
1448
  LogMagickEvent(TransformEvent,GetMagickModule(),
 
1449
    "Scaling image of size %lux%lu to %lux%lu",
 
1450
    image->columns,image->rows,scale_image->columns,scale_image->rows);
 
1451
 
 
1452
  scale_image->storage_class=DirectClass;
 
1453
  /*
 
1454
    Allocate memory.
 
1455
  */
 
1456
  x_vector=MagickAllocateMemory(DoublePixelPacket *,
 
1457
    image->columns*sizeof(DoublePixelPacket));
 
1458
  scanline=x_vector;
 
1459
  if (image->rows != scale_image->rows)
 
1460
    scanline=MagickAllocateMemory(DoublePixelPacket *,
 
1461
      image->columns*sizeof(DoublePixelPacket));
 
1462
  scale_scanline=MagickAllocateMemory(DoublePixelPacket *,
 
1463
    scale_image->columns*sizeof(DoublePixelPacket));
 
1464
  y_vector=MagickAllocateMemory(DoublePixelPacket *,
 
1465
    image->columns*sizeof(DoublePixelPacket));
 
1466
  if ((scanline == (DoublePixelPacket *) NULL) ||
 
1467
      (scale_scanline == (DoublePixelPacket *) NULL) ||
 
1468
      (x_vector == (DoublePixelPacket *) NULL) ||
 
1469
      (y_vector == (DoublePixelPacket *) NULL))
 
1470
    {
 
1471
      DestroyImage(scale_image);
 
1472
      ThrowImageException3(ResourceLimitError,MemoryAllocationFailed,
 
1473
        UnableToScaleImage)
 
1474
    }
 
1475
  /*
 
1476
    Scale image.
 
1477
  */
 
1478
  number_rows=0;
 
1479
  next_row=True;
 
1480
  y_span=1.0;
 
1481
  y_scale=(double) scale_image->rows/image->rows;
 
1482
  memset(y_vector,0,image->columns*sizeof(DoublePixelPacket));
 
1483
  memset(&zero,0,sizeof(DoublePixelPacket));
 
1484
  i=0;
 
1485
  for (y=0; y < (long) scale_image->rows; y++)
 
1486
  {
 
1487
    q=SetImagePixels(scale_image,0,y,scale_image->columns,1);
 
1488
    if (q == (PixelPacket *) NULL)
 
1489
      break;
 
1490
    if (scale_image->rows == image->rows)
 
1491
      {
 
1492
        /*
 
1493
          Read a new scanline.
 
1494
        */
 
1495
        p=AcquireImagePixels(image,0,i++,image->columns,1,exception);
 
1496
        if (p == (const PixelPacket *) NULL)
 
1497
          break;
 
1498
        for (x=0; x < (long) image->columns; x++)
 
1499
        {
 
1500
          x_vector[x].red=p->red;
 
1501
          x_vector[x].green=p->green;
 
1502
          x_vector[x].blue=p->blue;
 
1503
          x_vector[x].opacity=p->opacity;
 
1504
          p++;
 
1505
        }
 
1506
      }
 
1507
    else
 
1508
      {
 
1509
        /*
 
1510
          Scale Y direction.
 
1511
        */
 
1512
        while (y_scale < y_span)
 
1513
        {
 
1514
          if (next_row && (number_rows < (long) image->rows))
 
1515
            {
 
1516
              /*
 
1517
                Read a new scanline.
 
1518
              */
 
1519
              p=AcquireImagePixels(image,0,i++,image->columns,1,exception);
 
1520
              if (p == (const PixelPacket *) NULL)
 
1521
                break;
 
1522
              for (x=0; x < (long) image->columns; x++)
 
1523
              {
 
1524
                x_vector[x].red=p->red;
 
1525
                x_vector[x].green=p->green;
 
1526
                x_vector[x].blue=p->blue;
 
1527
                x_vector[x].opacity=p->opacity;
 
1528
                p++;
 
1529
              }
 
1530
              number_rows++;
 
1531
            }
 
1532
          for (x=0; x < (long) image->columns; x++)
 
1533
          {
 
1534
            y_vector[x].red+=y_scale*x_vector[x].red;
 
1535
            y_vector[x].green+=y_scale*x_vector[x].green;
 
1536
            y_vector[x].blue+=y_scale*x_vector[x].blue;
 
1537
            y_vector[x].opacity+=y_scale*x_vector[x].opacity;
 
1538
          }
 
1539
          y_span-=y_scale;
 
1540
          y_scale=(double) scale_image->rows/image->rows;
 
1541
          next_row=True;
 
1542
        }
 
1543
        if (next_row && (number_rows < (long) image->rows))
 
1544
          {
 
1545
            /*
 
1546
              Read a new scanline.
 
1547
            */
 
1548
            p=AcquireImagePixels(image,0,i++,image->columns,1,exception);
 
1549
            if (p == (const PixelPacket *) NULL)
 
1550
              break;
 
1551
            for (x=0; x < (long) image->columns; x++)
 
1552
            {
 
1553
              x_vector[x].red=p->red;
 
1554
              x_vector[x].green=p->green;
 
1555
              x_vector[x].blue=p->blue;
 
1556
              x_vector[x].opacity=p->opacity;
 
1557
              p++;
 
1558
            }
 
1559
            number_rows++;
 
1560
            next_row=False;
 
1561
          }
 
1562
        s=scanline;
 
1563
        for (x=0; x < (long) image->columns; x++)
 
1564
        {
 
1565
          pixel.red=y_vector[x].red+y_span*x_vector[x].red;
 
1566
          pixel.green=y_vector[x].green+y_span*x_vector[x].green;
 
1567
          pixel.blue=y_vector[x].blue+y_span*x_vector[x].blue;
 
1568
          pixel.opacity=y_vector[x].opacity+y_span*x_vector[x].opacity;
 
1569
          s->red=pixel.red > MaxRGB ? MaxRGB : pixel.red;
 
1570
          s->green=pixel.green > MaxRGB ? MaxRGB : pixel.green;
 
1571
          s->blue=pixel.blue > MaxRGB ? MaxRGB : pixel.blue;
 
1572
          s->opacity=pixel.opacity > MaxRGB ? MaxRGB : pixel.opacity;
 
1573
          s++;
 
1574
          y_vector[x].red=0;
 
1575
          y_vector[x].green=0;
 
1576
          y_vector[x].blue=0;
 
1577
          y_vector[x].opacity=0;
 
1578
        }
 
1579
        y_scale-=y_span;
 
1580
        if (y_scale <= 0)
 
1581
          {
 
1582
            y_scale=(double) scale_image->rows/image->rows;
 
1583
            next_row=True;
 
1584
          }
 
1585
        y_span=1.0;
 
1586
      }
 
1587
    if (scale_image->columns == image->columns)
 
1588
      {
 
1589
        /*
 
1590
          Transfer scanline to scaled image.
 
1591
        */
 
1592
        s=scanline;
 
1593
        for (x=0; x < (long) scale_image->columns; x++)
 
1594
        {
 
1595
          q->red=(Quantum) (s->red+0.5);
 
1596
          q->green=(Quantum) (s->green+0.5);
 
1597
          q->blue=(Quantum) (s->blue+0.5);
 
1598
          q->opacity=(Quantum) (s->opacity+0.5);
 
1599
          q++;
 
1600
          s++;
 
1601
        }
 
1602
      }
 
1603
    else
 
1604
      {
 
1605
        /*
 
1606
          Scale X direction.
 
1607
        */
 
1608
        pixel=zero;
 
1609
        next_column=False;
 
1610
        x_span=1.0;
 
1611
        s=scanline;
 
1612
        t=scale_scanline;
 
1613
        for (x=0; x < (long) image->columns; x++)
 
1614
        {
 
1615
          x_scale=(double) scale_image->columns/image->columns;
 
1616
          while (x_scale >= x_span)
 
1617
          {
 
1618
            if (next_column)
 
1619
              {
 
1620
                pixel=zero;
 
1621
                t++;
 
1622
              }
 
1623
            pixel.red+=x_span*s->red;
 
1624
            pixel.green+=x_span*s->green;
 
1625
            pixel.blue+=x_span*s->blue;
 
1626
            pixel.opacity+=x_span*s->opacity;
 
1627
            t->red=pixel.red > MaxRGB ? MaxRGB : pixel.red;
 
1628
            t->green=pixel.green > MaxRGB ? MaxRGB : pixel.green;
 
1629
            t->blue=pixel.blue > MaxRGB ? MaxRGB : pixel.blue;
 
1630
            t->opacity=pixel.opacity > MaxRGB ? MaxRGB : pixel.opacity;
 
1631
            x_scale-=x_span;
 
1632
            x_span=1.0;
 
1633
            next_column=True;
 
1634
          }
 
1635
        if (x_scale > 0)
 
1636
          {
 
1637
            if (next_column)
 
1638
              {
 
1639
                pixel=zero;
 
1640
                next_column=False;
 
1641
                t++;
 
1642
              }
 
1643
            pixel.red+=x_scale*s->red;
 
1644
            pixel.green+=x_scale*s->green;
 
1645
            pixel.blue+=x_scale*s->blue;
 
1646
            pixel.opacity+=x_scale*s->opacity;
 
1647
            x_span-=x_scale;
 
1648
          }
 
1649
        s++;
 
1650
      }
 
1651
      if (x_span > 0)
 
1652
        {
 
1653
          s--;
 
1654
          pixel.red+=x_span*s->red;
 
1655
          pixel.green+=x_span*s->green;
 
1656
          pixel.blue+=x_span*s->blue;
 
1657
          pixel.opacity+=x_span*s->opacity;
 
1658
        }
 
1659
      if (!next_column && ((t-scale_scanline) < (long) scale_image->columns))
 
1660
        {
 
1661
          t->red=pixel.red > MaxRGB ? MaxRGB : pixel.red;
 
1662
          t->green=pixel.green > MaxRGB ? MaxRGB : pixel.green;
 
1663
          t->blue=pixel.blue > MaxRGB ? MaxRGB : pixel.blue;
 
1664
          t->opacity=pixel.opacity > MaxRGB ? MaxRGB : pixel.opacity;
 
1665
        }
 
1666
      /*
 
1667
        Transfer scanline to scaled image.
 
1668
      */
 
1669
      t=scale_scanline;
 
1670
      for (x=0; x < (long) scale_image->columns; x++)
 
1671
      {
 
1672
        q->red=(Quantum) (t->red+0.5);
 
1673
        q->green=(Quantum) (t->green+0.5);
 
1674
        q->blue=(Quantum) (t->blue+0.5);
 
1675
        q->opacity=(Quantum) (t->opacity+0.5);
 
1676
        q++;
 
1677
        t++;
 
1678
      }
 
1679
    }
 
1680
    if (!SyncImagePixels(scale_image))
 
1681
      break;
 
1682
    if (QuantumTick(y,scale_image->rows))
 
1683
      if (!MagickMonitor(ScaleImageText,y,scale_image->rows,exception))
 
1684
        break;
 
1685
  }
 
1686
  /*
 
1687
    Free allocated memory.
 
1688
  */
 
1689
  MagickFreeMemory(y_vector);
 
1690
  MagickFreeMemory(scale_scanline);
 
1691
  if (scale_image->rows != image->rows)
 
1692
    MagickFreeMemory(scanline);
 
1693
  MagickFreeMemory(x_vector);
 
1694
  scale_image->is_grayscale=image->is_grayscale;
 
1695
  return(scale_image);
 
1696
}
 
1697
 
 
1698
/*
 
1699
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1700
%                                                                             %
 
1701
%                                                                             %
 
1702
%                                                                             %
 
1703
%   T h u m b n a i l I m a g e                                               %
 
1704
%                                                                             %
 
1705
%                                                                             %
 
1706
%                                                                             %
 
1707
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1708
%
 
1709
%  ThumbnailImage() changes the size of an image to the given dimensions.
 
1710
%  This method was designed by Bob Friesenhahn as a low cost thumbnail
 
1711
%  generator.
 
1712
%
 
1713
%  The format of the ThumbnailImage method is:
 
1714
%
 
1715
%      Image *ThumbnailImage(const Image *image,const unsigned long columns,
 
1716
%        const unsigned long rows,ExceptionInfo *exception)
 
1717
%
 
1718
%  A description of each parameter follows:
 
1719
%
 
1720
%    o image: The image.
 
1721
%
 
1722
%    o columns: The number of columns in the scaled image.
 
1723
%
 
1724
%    o rows: The number of rows in the scaled image.
 
1725
%
 
1726
%    o exception: Return any errors or warnings in this structure.
 
1727
%
 
1728
%
 
1729
*/
 
1730
MagickExport Image *ThumbnailImage(const Image *image,
 
1731
  const unsigned long columns,const unsigned long rows,ExceptionInfo *exception)
 
1732
{
 
1733
  double
 
1734
    x_factor,
 
1735
    y_factor;
 
1736
 
 
1737
  Image
 
1738
    *sample_image,
 
1739
    *thumbnail_image;
 
1740
 
 
1741
  x_factor=(double) columns/image->columns;
 
1742
  y_factor=(double) rows/image->rows;
 
1743
  if ((x_factor*y_factor) > 0.1)
 
1744
    return(ResizeImage(image,columns,rows,BoxFilter,image->blur,exception));
 
1745
  sample_image=SampleImage(image,5*columns,5*rows,exception);
 
1746
  if (sample_image == (Image *) NULL)
 
1747
    return((Image *) NULL);
 
1748
  thumbnail_image=ResizeImage(sample_image,columns,rows,BoxFilter,
 
1749
    sample_image->blur,exception);
 
1750
  DestroyImage(sample_image);
 
1751
  return(thumbnail_image);
 
1752
}
 
1753
 
 
1754
/*
 
1755
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1756
%                                                                             %
 
1757
%                                                                             %
 
1758
%                                                                             %
 
1759
+   Z o o m I m a g e                                                         %
 
1760
%                                                                             %
 
1761
%                                                                             %
 
1762
%                                                                             %
 
1763
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1764
%
 
1765
%  ZoomImage() creates a new image that is a scaled size of an existing one.
 
1766
%  It allocates the memory necessary for the new Image structure and returns a
 
1767
%  pointer to the new image.  The Point filter gives fast pixel replication,
 
1768
%  Triangle is equivalent to bi-linear interpolation, and Mitchel giver slower,
 
1769
%  very high-quality results.  See Graphic Gems III for details on this
 
1770
%  algorithm.
 
1771
%
 
1772
%  The filter member of the Image structure specifies which image filter to
 
1773
%  use. Blur specifies the blur factor where > 1 is blurry, < 1 is sharp.
 
1774
%
 
1775
%  The format of the ZoomImage method is:
 
1776
%
 
1777
%      Image *ZoomImage(const Image *image,const unsigned long columns,
 
1778
%        const unsigned long rows,ExceptionInfo *exception)
 
1779
%
 
1780
%  A description of each parameter follows:
 
1781
%
 
1782
%    o zoom_image: Method ZoomImage returns a pointer to the image after
 
1783
%      scaling.  A null image is returned if there is a memory shortage.
 
1784
%
 
1785
%    o image: The image.
 
1786
%
 
1787
%    o columns: An integer that specifies the number of columns in the zoom
 
1788
%      image.
 
1789
%
 
1790
%    o rows: An integer that specifies the number of rows in the scaled
 
1791
%      image.
 
1792
%
 
1793
%    o exception: Return any errors or warnings in this structure.
 
1794
%
 
1795
%
 
1796
*/
 
1797
MagickExport Image *ZoomImage(const Image *image,const unsigned long columns,
 
1798
  const unsigned long rows,ExceptionInfo *exception)
 
1799
{
 
1800
  Image
 
1801
    *zoom_image;
 
1802
 
 
1803
  assert(image != (const Image *) NULL);
 
1804
  assert(image->signature == MagickSignature);
 
1805
  assert(exception != (ExceptionInfo *) NULL);
 
1806
  assert(exception->signature == MagickSignature);
 
1807
  zoom_image=ResizeImage(image,columns,rows,image->filter,image->blur,
 
1808
    exception);
 
1809
  return(zoom_image);
 
1810
}