Coverage Report

Created: 2026-10-28 07:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/graphicsmagick/magick/fx.c
Line
Count
Source
1
/*
2
% Copyright (C) 2003-2026 GraphicsMagick Group
3
% Copyright (C) 2002 ImageMagick Studio
4
% Copyright 1991-1999 E. I. du Pont de Nemours and Company
5
% SPDX-License-Identifier: MIT
6
%
7
% This program is covered by multiple licenses, which are described in
8
% Copyright.txt. You should have received a copy of Copyright.txt with this
9
% package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
10
%
11
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12
%                                                                             %
13
%                                                                             %
14
%                                                                             %
15
%                                 FFFFF  X   X                                %
16
%                                 F       X X                                 %
17
%                                 FFF      X                                  %
18
%                                 F       X X                                 %
19
%                                 F      X   X                                %
20
%                                                                             %
21
%                                                                             %
22
%                 GraphicsMagick Image Special Effects Methods                %
23
%                                                                             %
24
%                                                                             %
25
%                               Software Design                               %
26
%                                 John Cristy                                 %
27
%                                 October 1996                                %
28
%                                                                             %
29
%                                                                             %
30
%                                                                             %
31
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
32
%
33
%
34
%
35
*/
36

37
/*
38
  Include declarations.
39
*/
40
#include "magick/studio.h"
41
#include "magick/color.h"
42
#include "magick/effect.h"
43
#include "magick/enhance.h"
44
#include "magick/fx.h"
45
#include "magick/gem.h"
46
#include "magick/log.h"
47
#include "magick/pixel_cache.h"
48
#include "magick/pixel_iterator.h"
49
#include "magick/monitor.h"
50
#include "magick/resize.h"
51
#include "magick/utility.h"
52

53
/*
54
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
55
%                                                                             %
56
%                                                                             %
57
%     C h a r c o a l I m a g e                                               %
58
%                                                                             %
59
%                                                                             %
60
%                                                                             %
61
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
62
%
63
%  Method CharcoalImage creates a new image that is a copy of an existing
64
%  one with the edge highlighted.  It allocates the memory necessary for the
65
%  new Image structure and returns a pointer to the new image.
66
%
67
%  The format of the CharcoalImage method is:
68
%
69
%      Image *CharcoalImage(const Image *image,const double radius,
70
%        const double sigma,ExceptionInfo *exception)
71
%
72
%  A description of each parameter follows:
73
%
74
%    o charcoal_image: Method CharcoalImage returns a pointer to the image
75
%      after it is embossed.  A null image is returned if there is a memory
76
%      shortage.
77
%
78
%    o image: The image.
79
%
80
%    o radius: the radius of the pixel neighborhood.
81
%
82
%    o sigma: The standard deviation of the Gaussian, in pixels.
83
%
84
%    o exception: Return any errors or warnings in this structure.
85
%
86
%
87
*/
88
MagickExport Image *CharcoalImage(const Image *image,const double radius,
89
                                  const double sigma,ExceptionInfo *exception)
90
0
{
91
0
  Image
92
0
    *blur_image = (Image *) NULL,
93
0
    *charcoal_image,
94
0
    *edge_image = (Image *) NULL;
95
96
0
  MagickPassFail
97
0
    status = MagickFail;
98
99
0
  assert(image != (Image *) NULL);
100
0
  assert(image->signature == MagickSignature);
101
0
  assert(exception != (ExceptionInfo *) NULL);
102
0
  assert(exception->signature == MagickSignature);
103
104
0
  do
105
0
    {
106
0
      if ((charcoal_image=CloneImage(image,0,0,True,exception)) == (Image *) NULL)
107
0
        break;
108
0
      if (SetImageType(charcoal_image,GrayscaleType) == MagickFail)
109
0
        break;
110
0
      if ((edge_image=EdgeImage(charcoal_image,radius,exception)) == (Image *) NULL)
111
0
        break;
112
0
      DestroyImage(charcoal_image);
113
0
      charcoal_image=(Image *) NULL;
114
0
      if ((blur_image=BlurImage(edge_image,radius,sigma,exception)) == (Image *) NULL)
115
0
        break;
116
0
      DestroyImage(edge_image);
117
0
      edge_image=(Image *) NULL;
118
0
      if (NormalizeImage(blur_image) == MagickFail)
119
0
        {
120
0
          if (blur_image->exception.severity > exception->severity)
121
0
            CopyException(exception,&blur_image->exception);
122
0
          break;
123
0
        }
124
0
      if (NegateImage(blur_image,False) == MagickFail)
125
0
        {
126
0
          if (blur_image->exception.severity > exception->severity)
127
0
            CopyException(exception,&blur_image->exception);
128
0
          break;
129
0
        }
130
0
      if (SetImageType(blur_image,GrayscaleType) == MagickFail)
131
0
        {
132
0
          if (blur_image->exception.severity > exception->severity)
133
0
            CopyException(exception,&blur_image->exception);
134
0
          break;
135
0
        }
136
0
      status = MagickPass;
137
0
    } while(0);
138
139
0
  if (charcoal_image)
140
0
    DestroyImage(charcoal_image);
141
0
  if (edge_image)
142
0
    DestroyImage(edge_image);
143
0
  if (status == MagickFail)
144
0
    {
145
0
      DestroyImage(blur_image);
146
0
      blur_image=(Image *) NULL;
147
0
    }
148
149
0
  return(blur_image);
150
0
}
151

152
/*
153
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
154
%                                                                             %
155
%                                                                             %
156
%     C o l o r i z e I m a g e                                               %
157
%                                                                             %
158
%                                                                             %
159
%                                                                             %
160
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
161
%
162
%  ColorizeImage() blends the fill color with each pixel in the image.
163
%  A percentage blend is specified with opacity.  Control the application
164
%  of different color components by specifying a different percentage for
165
%  each component (e.g. 90/100/10 is 90% red, 100% green, and 10% blue).
166
%
167
%  The format of the ColorizeImage method is:
168
%
169
%      Image *ColorizeImage(const Image *image,const char *opacity,
170
%        const PixelPacket target,ExceptionInfo *exception)
171
%
172
%  A description of each parameter follows:
173
%
174
%    o image: The image.
175
%
176
%    o opacity:  A character string indicating the level of opacity as a
177
%      percentage.
178
%
179
%    o target: A color value.
180
%
181
%    o exception: Return any errors or warnings in this structure.
182
%
183
%
184
*/
185
typedef struct _ColorizeImagePixelsOptions
186
{
187
  DoublePixelPacket amount;
188
  DoublePixelPacket color;
189
} ColorizeImagePixelsOptions;
190
static MagickPassFail
191
ColorizeImagePixelsCB(void *mutable_data,                /* User provided mutable data */
192
                      const void *immutable_data,        /* User provided immutable data */
193
                      const Image * restrict source_image,         /* Source image */
194
                      const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
195
                      const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
196
                      Image * restrict new_image,                  /* New image */
197
                      PixelPacket * restrict new_pixels,           /* Pixel row in new image */
198
                      IndexPacket * restrict new_indexes,          /* Pixel row indexes in new image */
199
                      const long npixels,                /* Number of pixels in row */
200
                      ExceptionInfo *exception           /* Exception report */
201
                      )
202
0
{
203
0
  ColorizeImagePixelsOptions
204
0
    options = *((const ColorizeImagePixelsOptions *) immutable_data);
205
206
0
  register long
207
0
    i;
208
209
0
  ARG_NOT_USED(mutable_data);
210
0
  ARG_NOT_USED(source_image);
211
0
  ARG_NOT_USED(source_indexes);
212
0
  ARG_NOT_USED(new_image);
213
0
  ARG_NOT_USED(new_indexes);
214
0
  ARG_NOT_USED(exception);
215
216
0
  for (i=0; i < npixels; i++)
217
0
    {
218
0
      new_pixels[i].red=RoundDoubleToQuantum
219
0
        (((double) source_pixels[i].red*(100.0-options.amount.red)+
220
0
          options.color.red*options.amount.red)/100.0);
221
0
      new_pixels[i].green=RoundDoubleToQuantum
222
0
        (((double) source_pixels[i].green*(100.0-options.amount.green)+
223
0
          options.color.green*options.amount.green)/100.0);
224
0
      new_pixels[i].blue=RoundDoubleToQuantum
225
0
        (((double) source_pixels[i].blue*(100.0-options.amount.blue)+
226
0
          options.color.blue*options.amount.blue)/100.0);
227
0
      new_pixels[i].opacity=RoundDoubleToQuantum
228
0
        (((double) source_pixels[i].opacity*(100.0-options.amount.opacity)+
229
0
          options.color.opacity*options.amount.opacity)/100.0);
230
0
    }
231
232
0
  return MagickPass;
233
0
}
234
MagickExport Image *ColorizeImage(const Image *image,const char *opacity,
235
                                  const PixelPacket target,ExceptionInfo *exception)
236
0
{
237
0
#define ColorizeImageText "[%s] Colorize..."
238
239
0
  ColorizeImagePixelsOptions
240
0
    options;
241
242
0
  Image
243
0
    *colorize_image;
244
245
0
  long
246
0
    count;
247
248
0
  unsigned int
249
0
    is_grayscale;
250
251
0
  MagickPassFail
252
0
    status = MagickPass;
253
254
  /*
255
    Allocate colorized image.
256
  */
257
0
  assert(image != (const Image *) NULL);
258
0
  assert(image->signature == MagickSignature);
259
0
  assert(exception != (ExceptionInfo *) NULL);
260
0
  assert(exception->signature == MagickSignature);
261
0
  is_grayscale=image->is_grayscale;
262
0
  colorize_image=CloneImage(image,image->columns,image->rows,True,exception);
263
0
  if (colorize_image == (Image *) NULL)
264
0
    return((Image *) NULL);
265
0
  status &= SetImageType(colorize_image,TrueColorType);
266
0
  if (status == MagickFail)
267
0
    {
268
0
      CopyException(exception,&colorize_image->exception);
269
0
      DestroyImage(colorize_image);
270
0
      return (Image *) NULL;
271
0
    }
272
0
  if (opacity == (const char *) NULL)
273
0
    return(colorize_image);
274
  /*
275
    Determine percentage RGB values of the pen color.
276
  */
277
0
  options.amount.red=100.0;
278
0
  options.amount.green=100.0;
279
0
  options.amount.blue=100.0;
280
0
  options.amount.opacity=0.0;
281
0
  count=sscanf(opacity,"%lf%*[/,]%lf%*[/,]%lf%*[/,]%lf",
282
0
               &options.amount.red,&options.amount.green,&options.amount.blue,&options.amount.opacity);
283
0
  if (count == 1)
284
0
    {
285
0
      if (options.amount.red == 0.0)
286
0
        return(colorize_image);
287
0
      options.amount.green=options.amount.red;
288
0
      options.amount.blue=options.amount.red;
289
0
      options.amount.opacity=options.amount.red;
290
0
    }
291
0
  options.color.red=target.red;
292
0
  options.color.green=target.green;
293
0
  options.color.blue=target.blue;
294
0
  options.color.opacity=target.opacity;
295
  /*
296
    Colorize DirectClass image.
297
  */
298
0
  status &= PixelIterateDualNew(ColorizeImagePixelsCB,NULL,
299
0
                                ColorizeImageText,NULL,&options,
300
0
                                image->columns,image->rows,image,0,0,
301
0
                                colorize_image,0,0,exception);
302
0
  colorize_image->is_grayscale=(is_grayscale && IsGray(target));
303
0
  if (status == MagickFail)
304
0
    {
305
0
      DestroyImage(colorize_image);
306
0
      colorize_image=(Image *) NULL;
307
0
    }
308
0
  return(colorize_image);
309
0
}
310

311
/*
312
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
313
%                                                                             %
314
%                                                                             %
315
%     C o l o r M a t r i x I m a g e                                         %
316
%                                                                             %
317
%                                                                             %
318
%                                                                             %
319
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
320
%
321
%  ColorMatrixImage() applies a color matrix to the image channels.  The
322
%  user supplied matrix may be of order 1 to 5 (1x1 through 5x5) and is
323
%  used to update the default identity matrix:
324
%
325
%    1 0 0 0 0
326
%    0 1 0 0 0
327
%    0 0 1 0 0
328
%    0 0 0 1 0
329
%    0 0 0 0 1
330
%
331
%  where the first four columns represent the ratio of the color (red,
332
%  green, blue) and opacity components incorporated in the output summation.
333
%  The first four rows represent the summations for red, green, blue, and
334
%  opacity.  The last row is a dummy row and is not used.  The last column
335
%  represents a constant value (expressed as a ratio of MaxRGB) to be
336
%  added to the row summation.  The following is a summary of how the
337
%  matrix is applied:
338
%
339
%    r' = r*m[0,0] + g*m[1,0] + b*m[2,0] + o*m[3,0] + MaxRGB*m[4,0]
340
%    g' = r*m[0,1] + g*m[1,1] + b*m[2,1] + o*m[3,1] + MaxRGB*m[4,1]
341
%    b' = r*m[0,2] + g*m[1,2] + b*m[2,2] + o*m[3,2] + MaxRGB*m[4,2]
342
%    o' = r*m[0,3] + g*m[1,3] + b*m[2,3] + o*m[3,3] + MaxRGB*m[4,3]
343
%
344
%  The format of the ColorMatrixImage method is:
345
%
346
%      MagickPassFail ColorMatrixImage(Image *image,
347
%                                      const unsigned int order,
348
%                                      const double *color_matrix)
349
%
350
%  A description of each parameter follows:
351
%
352
%    o image: The image.
353
%
354
%    o order: The number of columns and rows in the filter kernel.
355
%
356
%    o matrix: An array of double representing the matrix
357
%
358
%
359
*/
360
typedef struct _ColorMatrixImageOptions_t
361
{
362
  const double
363
    *matrix[5];
364
365
} ColorMatrixImageOptions_t;
366
367
static MagickPassFail
368
ColorMatrixImagePixels(void *mutable_data,         /* User provided mutable data */
369
                       const void *immutable_data, /* User provided immutable data */
370
                       Image * restrict image,     /* Modify image */
371
                       PixelPacket * restrict pixels, /* Pixel row */
372
                       IndexPacket * restrict indexes, /* Pixel row indexes */
373
                       const long npixels,         /* Number of pixels in row */
374
                       ExceptionInfo *exception)   /* Exception report */
375
0
{
376
  /*
377
    Color matrix image pixels.
378
  */
379
0
  const ColorMatrixImageOptions_t
380
0
    *options = (const ColorMatrixImageOptions_t *) immutable_data;
381
382
0
  long
383
0
    i;
384
385
0
  double
386
0
    column[5],
387
0
    sums[4];
388
389
0
  ARG_NOT_USED(mutable_data);
390
0
  ARG_NOT_USED(image);
391
0
  ARG_NOT_USED(indexes);
392
0
  ARG_NOT_USED(exception);
393
394
0
  for (i=0; i < (long) (sizeof(sums)/sizeof(sums[0])); i++)
395
0
    sums[i] = 0.0;
396
397
0
  column[3] = MaxRGBDouble;
398
0
  column[4] = MaxRGBDouble;
399
400
0
  for (i=0; i < npixels; i++)
401
0
    {
402
0
      unsigned int
403
0
        row;
404
405
      /*
406
        Accumulate float input pixel
407
      */
408
0
      column[0]=(double) pixels[i].red;
409
0
      column[1]=(double) pixels[i].green;
410
0
      column[2]=(double) pixels[i].blue;
411
0
      if (image->matte)
412
0
        column[3]=(MaxRGBDouble-(double) pixels[i].opacity);
413
414
      /*
415
        Compute row sums.
416
      */
417
0
      for (row=0; row < 4; row++)
418
0
        {
419
0
          const double
420
0
            *m;
421
422
0
          if ((m = options->matrix[row]) != (const double *) NULL)
423
0
            sums[row]=m[0]*column[0] + m[1]*column[1] + m[2]*column[2] +
424
0
              m[3]*column[3] + m[4]*column[4];
425
0
        }
426
427
      /*
428
        Assign results.
429
      */
430
0
      for (row=0; row < 4; row++)
431
0
        {
432
0
          if (options->matrix[row] != (const double *) NULL)
433
0
            {
434
0
              switch (row)
435
0
                {
436
0
                case 0:
437
0
                  pixels[i].red = RoundDoubleToQuantum(sums[row]);
438
0
                  break;
439
0
                case 1:
440
0
                  pixels[i].green = RoundDoubleToQuantum(sums[row]);
441
0
                  break;
442
0
                case 2:
443
0
                  pixels[i].blue = RoundDoubleToQuantum(sums[row]);
444
0
                  break;
445
0
                case 3:
446
0
                  sums[row]=(MaxRGBDouble-sums[row]);
447
0
                  pixels[i].opacity = RoundDoubleToQuantum(sums[row]);
448
0
                  break;
449
0
                }
450
0
            }
451
0
        }
452
0
    }
453
454
0
  return MagickPass;
455
0
}
456
457
0
#define ColorMatrixImageText "[%s] Color matrix..."
458
MagickExport MagickPassFail
459
ColorMatrixImage(Image *image,const unsigned int order,const double *color_matrix)
460
0
{
461
0
  double
462
0
    matrix[] =
463
0
    {
464
0
      1.0, 0.0, 0.0, 0.0, 0.0,
465
0
      0.0, 1.0, 0.0, 0.0, 0.0,
466
0
      0.0, 0.0, 1.0, 0.0, 0.0,
467
0
      0.0, 0.0, 0.0, 1.0, 0.0,
468
0
      0.0, 0.0, 0.0, 0.0, 1.0
469
0
    };
470
471
0
  unsigned int
472
0
    i;
473
474
0
  int
475
0
    width = 5;
476
477
0
  ColorMatrixImageOptions_t
478
0
    options;
479
480
0
  MagickPassFail
481
0
    status = MagickPass;
482
483
0
  if ((order < 1) || (order > 5))
484
0
    ThrowBinaryException(OptionError,MatrixOrderOutOfRange,
485
0
                         MagickMsg(OptionError,UnableToColorMatrixImage));
486
487
0
  assert(color_matrix != (const double *) NULL);
488
489
0
  for (i=0; i < sizeof(options.matrix)/sizeof(options.matrix[0]); i++)
490
0
    options.matrix[i] = (double *) NULL;
491
492
0
  {
493
0
    double
494
0
      *d;
495
496
0
    const double
497
0
      *u;
498
499
0
    unsigned int
500
0
      j;
501
502
0
    u = color_matrix;
503
0
    for (i=0; i < order; i++)
504
0
      {
505
0
        d = &matrix[i*5];
506
0
        for (j=0; j < order; j++)
507
0
          {
508
0
            if (d[j] != *u)
509
0
              {
510
0
                d[j]=*u;
511
0
                options.matrix[i]=&matrix[i*5];
512
0
              }
513
0
            u++;
514
0
          }
515
0
      }
516
517
    /*
518
      Add opacity channel if we will be updating opacity.
519
    */
520
0
    if ((!image->matte) && (options.matrix[3] != (double *) NULL))
521
0
      SetImageOpacity(image,OpaqueOpacity);
522
0
  }
523
524
0
  if (LogMagickEvent(TransformEvent,GetMagickModule(),
525
0
                     "  ColorMatrix with %dx%d matrix:",width,width))
526
0
    {
527
      /*
528
        Log matrix.
529
      */
530
0
      char
531
0
        cell_text[MaxTextExtent],
532
0
        row_text[MaxTextExtent];
533
534
0
      const double
535
0
        *k;
536
537
0
      long
538
0
        u,
539
0
        v;
540
541
0
      k=matrix;
542
0
      for (v=0; v < width; v++)
543
0
        {
544
0
          *row_text='\0';
545
0
          for (u=0; u < width; u++)
546
0
            {
547
0
              MagickFormatString(cell_text,sizeof(cell_text),"%#12.4g",*k++);
548
0
              (void) strlcat(row_text,cell_text,sizeof(row_text));
549
0
              if (u%5 == 4)
550
0
                {
551
0
                  (void) LogMagickEvent(TransformEvent,GetMagickModule(),
552
0
                                        "   %.64s", row_text);
553
0
                  *row_text='\0';
554
0
                }
555
0
            }
556
0
          if (u > 5)
557
0
            (void) strlcat(row_text,"\n",sizeof(row_text));
558
0
          if (row_text[0] != '\0')
559
0
            (void) LogMagickEvent(TransformEvent,GetMagickModule(),
560
0
                                  "   %s", row_text);
561
0
        }
562
0
    }
563
564
0
  if ((options.matrix[0] != (double *) NULL) ||
565
0
      (options.matrix[1] != (double *) NULL) ||
566
0
      (options.matrix[2] != (double *) NULL) ||
567
0
      (options.matrix[3] != (double *) NULL))
568
0
    {
569
0
      image->storage_class=DirectClass;
570
      /*
571
        We don't currently handle CMYK(A) colorspaces, although
572
        manipulation in other alternate colorspaces may be useful.
573
      */
574
0
      if (image->colorspace == CMYKColorspace)
575
0
        (void) TransformColorspace(image,RGBColorspace);
576
0
      status=PixelIterateMonoModify(ColorMatrixImagePixels,
577
0
                                    NULL,
578
0
                                    ColorMatrixImageText,
579
0
                                    NULL,&options,
580
0
                                    0,0,image->columns,image->rows,
581
0
                                    image,
582
0
                                    &image->exception);
583
0
    }
584
585
0
  return status;
586
0
}
587

588
/*
589
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
590
%                                                                             %
591
%                                                                             %
592
%     I m p l o d e I m a g e                                                 %
593
%                                                                             %
594
%                                                                             %
595
%                                                                             %
596
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
597
%
598
%  Method ImplodeImage creates a new image that is a copy of an existing
599
%  one with the image pixels "implode" by the specified percentage.  It
600
%  allocates the memory necessary for the new Image structure and returns a
601
%  pointer to the new image.
602
%
603
%  The format of the ImplodeImage method is:
604
%
605
%      Image *ImplodeImage(const Image *image,const double amount,
606
%        ExceptionInfo *exception)
607
%
608
%  A description of each parameter follows:
609
%
610
%    o implode_image: Method ImplodeImage returns a pointer to the image
611
%      after it is implode.  A null image is returned if there is a memory
612
%      shortage.
613
%
614
%    o image: The image.
615
%
616
%    o amount:  Define the extent of the implosion.
617
%
618
%    o exception: Return any errors or warnings in this structure.
619
%
620
%
621
*/
622
MagickExport Image *ImplodeImage(const Image * restrict image,const double amount,
623
                                 ExceptionInfo *exception)
624
0
{
625
0
#define ImplodeImageText "[%s] Implode..."
626
627
0
  double
628
0
    radius,
629
0
    x_center,
630
0
    x_scale,
631
0
    y_center,
632
0
    y_scale;
633
634
0
  Image
635
0
    * restrict implode_image;
636
637
0
  long
638
0
    y;
639
640
0
  MagickPassFail
641
0
    status = MagickPass;
642
643
  /*
644
    Initialize implode image attributes.
645
  */
646
0
  assert(image != (Image *) NULL);
647
0
  assert(image->signature == MagickSignature);
648
0
  assert(exception != (ExceptionInfo *) NULL);
649
0
  assert(exception->signature == MagickSignature);
650
0
  implode_image=CloneImage(image,image->columns,image->rows,True,exception);
651
0
  if (implode_image == (Image *) NULL)
652
0
    return((Image *) NULL);
653
0
  status &= SetImageType(implode_image,implode_image->background_color.opacity !=
654
0
                      OpaqueOpacity ? TrueColorMatteType : TrueColorType);
655
0
  if (status == MagickFail)
656
0
    {
657
0
      CopyException(exception,&implode_image->exception);
658
0
      DestroyImage(implode_image);
659
0
      return (Image *) NULL;
660
0
    }
661
  /*
662
    Compute scaling factor.
663
  */
664
0
  x_scale=1.0;
665
0
  y_scale=1.0;
666
0
  x_center=0.5*image->columns;
667
0
  y_center=0.5*image->rows;
668
0
  radius=x_center;
669
0
  if (image->columns > image->rows)
670
0
    y_scale=(double) image->columns/image->rows;
671
0
  else
672
0
    if (image->columns < image->rows)
673
0
      {
674
0
        x_scale=(double) image->rows/image->columns;
675
0
        radius=y_center;
676
0
      }
677
  /*
678
    Implode each row.
679
  */
680
0
  {
681
0
    unsigned long
682
0
      row_count=0;
683
684
0
    MagickBool
685
0
      monitor_active;
686
687
0
    monitor_active=MagickMonitorActive();
688
689
#if defined(HAVE_OPENMP)
690
#  if defined(TUNE_OPENMP)
691
#    pragma omp parallel for schedule(runtime) shared(row_count, status)
692
#  else
693
#    pragma omp parallel for schedule(guided) shared(row_count, status)
694
#  endif
695
#endif
696
0
    for (y=0; y < (long) image->rows; y++)
697
0
      {
698
0
        register long
699
0
          x;
700
701
0
        register PixelPacket
702
0
          * restrict q;
703
704
0
        double
705
0
          distance,
706
0
          x_distance,
707
0
          y_distance;
708
709
0
        ViewInfo
710
0
          *image_view;
711
712
0
        MagickPassFail
713
0
          thread_status;
714
715
0
        thread_status=status;
716
0
        if (thread_status == MagickFail)
717
0
          continue;
718
719
0
        image_view=AccessDefaultCacheView(image);
720
0
        q=SetImagePixelsEx(implode_image,0,y,implode_image->columns,1,
721
0
                           exception);
722
0
        if (q == (PixelPacket *) NULL)
723
0
          thread_status=MagickFail;
724
0
        if (thread_status != MagickFail)
725
0
          {
726
0
            y_distance=y_scale*(y-y_center);
727
0
            for (x=0; x < (long) image->columns; x++)
728
0
              {
729
                /*
730
                  Determine if the pixel is within an ellipse.
731
                */
732
0
                x_distance=x_scale*(x-x_center);
733
0
                distance=x_distance*x_distance+y_distance*y_distance;
734
0
                if (distance >= (radius*radius))
735
0
                  (void) AcquireOneCacheViewPixel(image_view,q,x,y,exception);
736
0
                else
737
0
                  {
738
0
                    double
739
0
                      factor;
740
741
                    /*
742
                      Implode the pixel.
743
                    */
744
0
                    factor=1.0;
745
0
                    if (distance > 0.0)
746
0
                      factor=pow(sin(MagickPI*sqrt(distance)/radius/2),-amount);
747
0
                    if (InterpolateViewColor(image_view,q,
748
0
                                             factor*x_distance/x_scale+x_center,
749
0
                                             factor*y_distance/y_scale+y_center,
750
0
                                             exception) == MagickFail)
751
0
                      {
752
0
                        thread_status=MagickFail;
753
0
                        break;
754
0
                      }
755
0
                  }
756
0
                q++;
757
0
              }
758
0
            if (thread_status != MagickFail)
759
0
              if (!SyncImagePixelsEx(implode_image,exception))
760
0
                thread_status=MagickFail;
761
0
          }
762
763
0
        if (monitor_active)
764
0
          {
765
0
            unsigned long
766
0
              thread_row_count;
767
768
#if defined(HAVE_OPENMP)
769
#  pragma omp atomic
770
#endif
771
0
            row_count++;
772
#if defined(HAVE_OPENMP)
773
#  pragma omp flush (row_count)
774
#endif
775
0
            thread_row_count=row_count;
776
0
            if (QuantumTick(thread_row_count,image->rows))
777
0
              if (!MagickMonitorFormatted(thread_row_count,image->rows,exception,
778
0
                                          ImplodeImageText,implode_image->filename))
779
0
                thread_status=MagickFail;
780
0
          }
781
782
0
        if (thread_status == MagickFail)
783
0
          {
784
0
            status=MagickFail;
785
#if defined(HAVE_OPENMP)
786
#  pragma omp flush (status)
787
#endif
788
0
          }
789
0
      }
790
0
  }
791
0
  implode_image->is_grayscale=image->is_grayscale;
792
0
  if (status == MagickFail)
793
0
    {
794
0
      DestroyImage(implode_image);
795
0
      implode_image=(Image *) NULL;
796
0
    }
797
0
  return(implode_image);
798
0
}
799

800
/*
801
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
802
%                                                                             %
803
%                                                                             %
804
%     M o r p h I m a g e s                                                   %
805
%                                                                             %
806
%                                                                             %
807
%                                                                             %
808
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
809
%
810
%  The MorphImages() method requires a minimum of two images.  The first
811
%  image is transformed into the second by a number of intervening images
812
%  as specified by frames.
813
%
814
%  The format of the MorphImage method is:
815
%
816
%      Image *MorphImages(const Image *image,const unsigned long number_frames,
817
%        ExceptionInfo *exception)
818
%
819
%  A description of each parameter follows:
820
%
821
%    o image: The image.
822
%
823
%    o number_frames:  Define the number of in-between image to generate.
824
%      The more in-between frames, the smoother the morph.
825
%
826
%    o exception: Return any errors or warnings in this structure.
827
%
828
%
829
*/
830
typedef struct _MorphImagePixelsOptions
831
{
832
  double alpha;
833
  double beta;
834
} MorphImagePixelsOptions;
835
static MagickPassFail
836
MorphImagePixelsCB(void *mutable_data,                /* User provided mutable data */
837
                   const void *immutable_data,        /* User provided immutable data */
838
                   const Image * restrict source_image,         /* Source image */
839
                   const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
840
                   const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
841
                   Image * restrict new_image,                  /* New image */
842
                   PixelPacket * restrict new_pixels,           /* Pixel row in new image */
843
                   IndexPacket * restrict new_indexes,          /* Pixel row indexes in new image */
844
                   const long npixels,                /* Number of pixels in row */
845
                   ExceptionInfo *exception           /* Exception report */
846
                   )
847
0
{
848
0
  MorphImagePixelsOptions
849
0
    options = *((const MorphImagePixelsOptions *) immutable_data);
850
851
0
  register long
852
0
    i;
853
854
0
  ARG_NOT_USED(mutable_data);
855
0
  ARG_NOT_USED(source_image);
856
0
  ARG_NOT_USED(source_indexes);
857
0
  ARG_NOT_USED(new_image);
858
0
  ARG_NOT_USED(new_indexes);
859
0
  ARG_NOT_USED(exception);
860
861
0
  for (i=0; i < npixels; i++)
862
0
    {
863
0
      new_pixels[i].red=(Quantum) (options.alpha*new_pixels[i].red+
864
0
                                   options.beta*source_pixels[i].red+0.5);
865
0
      new_pixels[i].green=(Quantum) (options.alpha*new_pixels[i].green+
866
0
                                     options.beta*source_pixels[i].green+0.5);
867
0
      new_pixels[i].blue=(Quantum) (options.alpha*new_pixels[i].blue+
868
0
                                    options.beta*source_pixels[i].blue+0.5);
869
0
      new_pixels[i].opacity=(Quantum) (options.alpha*new_pixels[i].opacity+
870
0
                                       options.beta*source_pixels[i].opacity+0.5);
871
0
    }
872
873
0
  return MagickPass;
874
0
}
875
MagickExport Image *MorphImages(const Image *image,
876
  const unsigned long number_frames,ExceptionInfo *exception)
877
0
{
878
0
#define MorphImageText "[%s] Morph sequence..."
879
880
0
  MorphImagePixelsOptions
881
0
    options;
882
883
0
  Image
884
0
    *clone_image,
885
0
    *morph_image,
886
0
    *morph_images;
887
888
0
  MonitorHandler
889
0
    handler;
890
891
0
  register const Image
892
0
    *next;
893
894
0
  register long
895
0
    i;
896
897
0
  unsigned long
898
0
    scene;
899
900
0
  MagickPassFail
901
0
    status = MagickPass;
902
903
  /*
904
    Clone first frame in sequence.
905
  */
906
0
  assert(image != (Image *) NULL);
907
0
  assert(image->signature == MagickSignature);
908
0
  assert(exception != (ExceptionInfo *) NULL);
909
0
  assert(exception->signature == MagickSignature);
910
0
  morph_images=CloneImage(image,0,0,True,exception);
911
0
  if (morph_images == (Image *) NULL)
912
0
    return((Image *) NULL);
913
0
  if (image->next == (Image *) NULL)
914
0
    {
915
      /*
916
        Morph single image.
917
      */
918
0
      for (i=1; i < (long) number_frames; i++)
919
0
      {
920
0
        morph_images->next=CloneImage(image,0,0,True,exception);
921
0
        if (morph_images->next == (Image *) NULL)
922
0
          {
923
0
            DestroyImageList(morph_images);
924
0
            return((Image *) NULL);
925
0
          }
926
0
        morph_images->next->previous=morph_images;
927
0
        morph_images=morph_images->next;
928
0
        if (!MagickMonitorFormatted(i,number_frames,exception,
929
0
                                    MorphImageText,image->filename))
930
0
          break;
931
0
      }
932
0
      while (morph_images->previous != (Image *) NULL)
933
0
        morph_images=morph_images->previous;
934
0
      return(morph_images);
935
0
    }
936
  /*
937
    Morph image sequence.
938
  */
939
0
  scene=0;
940
0
  for (next=image; next->next != (Image *) NULL; next=next->next)
941
0
  {
942
0
    handler=SetMonitorHandler((MonitorHandler) NULL);
943
0
    for (i=0; i < (long) number_frames; i++)
944
0
    {
945
0
      options.beta=((double) i+1.0)/(number_frames+1.0);
946
0
      options.alpha=1.0-options.beta;
947
0
      clone_image=CloneImage(next,0,0,True,exception);
948
0
      if (clone_image == (Image *) NULL)
949
0
        break;
950
0
      morph_images->next=ZoomImage(clone_image,
951
0
        (unsigned long) (options.alpha*next->columns+options.beta*next->next->columns+0.5),
952
0
        (unsigned long) (options.alpha*next->rows+options.beta*next->next->rows+0.5),exception);
953
0
      DestroyImage(clone_image);
954
0
      if (morph_images->next == (Image *) NULL)
955
0
        break;
956
0
      morph_images->next->previous=morph_images;
957
0
      morph_images=morph_images->next;
958
0
      clone_image=CloneImage(next->next,0,0,True,exception);
959
0
      if (clone_image == (Image *) NULL)
960
0
        break;
961
0
      morph_image=ZoomImage(clone_image,morph_images->columns,
962
0
        morph_images->rows,exception);
963
0
      DestroyImage(clone_image);
964
0
      if (morph_image == (Image *) NULL)
965
0
        break;
966
0
      status &= SetImageType(morph_images,TrueColorType);
967
0
      status &= PixelIterateDualNew(MorphImagePixelsCB,NULL,
968
0
                                 MorphImageText,NULL,&options,
969
0
                                 morph_images->columns,morph_images->rows,morph_image,0,0,
970
0
                                 morph_images,0,0,exception);
971
0
      DestroyImage(morph_image);
972
0
    }
973
0
    if ((i < (long) number_frames) || (MagickFail == status))
974
0
      break;
975
    /*
976
      Clone last frame in sequence.
977
    */
978
0
    morph_images->next=CloneImage(next->next,0,0,True,exception);
979
0
    if (morph_images->next == (Image *) NULL)
980
0
      break;
981
0
    morph_images->next->previous=morph_images;
982
0
    morph_images=morph_images->next;
983
0
    (void) SetMonitorHandler(handler);
984
0
    if (!MagickMonitorFormatted(scene,GetImageListLength(image),exception,
985
0
                                MorphImageText,image->filename))
986
0
      break;
987
0
    scene++;
988
0
  }
989
0
  while (morph_images->previous != (Image *) NULL)
990
0
    morph_images=morph_images->previous;
991
0
  if ((MagickFail == status) || (next->next != (Image *) NULL))
992
0
    {
993
0
      DestroyImageList(morph_images);
994
0
      return((Image *) NULL);
995
0
    }
996
0
  return(morph_images);
997
0
}
998

999
/*
1000
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1001
%                                                                             %
1002
%                                                                             %
1003
%     O i l P a i n t I m a g e                                               %
1004
%                                                                             %
1005
%                                                                             %
1006
%                                                                             %
1007
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1008
%
1009
%  OilPaintImage() applies a special effect filter that simulates an oil
1010
%  painting.  Each pixel is replaced by the most frequent color occurring
1011
%  in a circular region defined by radius.
1012
%
1013
%  The format of the OilPaintImage method is:
1014
%
1015
%      Image *OilPaintImage(const Image *image,const double radius,
1016
%        ExceptionInfo *exception)
1017
%
1018
%  A description of each parameter follows:
1019
%
1020
%    o image: The image.
1021
%
1022
%    o radius: The radius of the circular neighborhood.
1023
%
1024
%    o exception: Return any errors or warnings in this structure.
1025
%
1026
%
1027
*/
1028
#define PaintHistSize 256
1029
MagickExport Image *OilPaintImage(const Image *image,const double radius,
1030
                                  ExceptionInfo *exception)
1031
0
{
1032
0
#define OilPaintImageText "[%s] OilPaint..."
1033
1034
0
  Image
1035
0
    *paint_image;
1036
1037
0
  long
1038
0
    width,
1039
0
    y;
1040
1041
0
  unsigned long
1042
0
    row_count=0;
1043
1044
0
  MagickBool
1045
0
    monitor_active;
1046
1047
0
  MagickPassFail
1048
0
    status=MagickPass;
1049
1050
  /*
1051
    Initialize painted image attributes.
1052
  */
1053
0
  assert(image != (const Image *) NULL);
1054
0
  assert(image->signature == MagickSignature);
1055
0
  assert(exception != (ExceptionInfo *) NULL);
1056
0
  assert(exception->signature == MagickSignature);
1057
0
  width=GetOptimalKernelWidth(radius,0.5);
1058
0
  if (((long) image->columns < width) || ((long) image->rows < width))
1059
0
    ThrowImageException3(OptionError,UnableToPaintImage,
1060
0
                         ImageSmallerThanRadius);
1061
1062
0
  paint_image=CloneImage(image,image->columns,image->rows,True,exception);
1063
0
  if (paint_image == (Image *) NULL)
1064
0
    return((Image *) NULL);
1065
1066
0
  status &= SetImageType(paint_image,TrueColorType);
1067
0
  if (status == MagickFail)
1068
0
    {
1069
0
      CopyException(exception,&paint_image->exception);
1070
0
      DestroyImage(paint_image);
1071
0
      return (Image *) NULL;
1072
0
    }
1073
1074
0
  monitor_active=MagickMonitorActive();
1075
1076
  /*
1077
    Paint each row of the image.
1078
  */
1079
#if defined(HAVE_OPENMP)
1080
#  if defined(TUNE_OPENMP)
1081
#    pragma omp parallel for schedule(runtime) shared(row_count, status)
1082
#  else
1083
#    pragma omp parallel for schedule(guided) shared(row_count, status)
1084
#  endif
1085
#endif
1086
0
  for (y=0; y < (long) image->rows; y++)
1087
0
    {
1088
0
      const PixelPacket
1089
0
        * restrict p,
1090
0
        *r;
1091
1092
0
      PixelPacket
1093
0
        * restrict q;
1094
1095
0
      long
1096
0
        x;
1097
1098
0
      const PixelPacket
1099
0
        *s;
1100
1101
0
      MagickBool
1102
0
        thread_status;
1103
1104
0
      thread_status=status;
1105
0
      if (thread_status == MagickFail)
1106
0
        continue;
1107
1108
0
      p=AcquireImagePixels(image,-width/2,y-width/2,image->columns+width,width,
1109
0
                           exception);
1110
0
      q=SetImagePixelsEx(paint_image,0,y,paint_image->columns,1,exception);
1111
0
      if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1112
0
        thread_status=MagickFail;
1113
0
      if (thread_status != MagickFail)
1114
0
        {
1115
0
          for (x=(long) image->columns; x > 0; x--)
1116
0
            {
1117
0
              long
1118
0
                v;
1119
1120
0
              unsigned long
1121
0
                count;
1122
1123
0
              unsigned int
1124
0
                histogram[PaintHistSize];
1125
1126
              /*
1127
                Determine most frequent color.
1128
              */
1129
0
              count=0;
1130
0
              (void) memset(histogram,0,sizeof(histogram));
1131
0
              r=p++;
1132
0
              s=r;
1133
0
              for (v=width; v > 0; v--)
1134
0
                {
1135
0
                  register long
1136
0
                    u;
1137
1138
0
                  register const PixelPacket
1139
0
                    *ru;
1140
1141
0
                  ru=r;
1142
0
                  for (u=width; u > 0; u--)
1143
0
                    {
1144
0
                      register unsigned int
1145
0
                        *hp;
1146
1147
0
                      Quantum
1148
0
                        intensity;
1149
1150
0
                      if (image->is_grayscale)
1151
0
                        intensity=ru->red;
1152
0
                      else
1153
0
                        intensity=PixelIntensityToQuantum(ru);
1154
1155
0
                      hp=histogram+ScaleQuantumToChar(intensity);
1156
0
                      (*hp)++;
1157
0
                      if (*hp > count)
1158
0
                        {
1159
0
                          s=ru;
1160
0
                          count=*hp;
1161
0
                        }
1162
0
                      ru++;
1163
0
                    }
1164
0
                  r+=(size_t) image->columns+width;
1165
0
                }
1166
0
              *q++=(*s);
1167
0
            }
1168
0
          if (!SyncImagePixelsEx(paint_image,exception))
1169
0
            thread_status=MagickFail;
1170
0
        }
1171
1172
0
      if (monitor_active)
1173
0
        {
1174
0
          unsigned long
1175
0
            thread_row_count;
1176
1177
#if defined(HAVE_OPENMP)
1178
#  pragma omp atomic
1179
#endif
1180
0
          row_count++;
1181
#if defined(HAVE_OPENMP)
1182
#  pragma omp flush (row_count)
1183
#endif
1184
0
          thread_row_count=row_count;
1185
0
          if (QuantumTick(thread_row_count,image->rows))
1186
0
            if (!MagickMonitorFormatted(thread_row_count,image->rows,exception,
1187
0
                                        OilPaintImageText,image->filename))
1188
0
              thread_status=MagickFail;
1189
0
        }
1190
1191
0
      if (thread_status == MagickFail)
1192
0
        {
1193
0
          status=MagickFail;
1194
#if defined(HAVE_OPENMP)
1195
#  pragma omp flush (status)
1196
#endif
1197
0
        }
1198
0
    }
1199
1200
0
  paint_image->is_grayscale=image->is_grayscale;
1201
0
  if (status == MagickFail)
1202
0
    {
1203
0
      DestroyImage(paint_image);
1204
0
      paint_image=(Image *) NULL;
1205
0
    }
1206
0
  return(paint_image);
1207
0
}
1208

1209
/*
1210
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1211
%                                                                             %
1212
%                                                                             %
1213
%     S o l a r i z e I m a g e                                               %
1214
%                                                                             %
1215
%                                                                             %
1216
%                                                                             %
1217
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1218
%
1219
%  SolarizeImage() applies a special effect to the image, similar to the effect
1220
%  achieved in a photo darkroom by selectively exposing areas of photo
1221
%  sensitive paper to light.  Threshold ranges from 0 to MaxRGB and is a
1222
%  measure of the extent of the solarization. False is returned if an error
1223
%  is encountered.
1224
%
1225
%  The format of the SolarizeImage method is:
1226
%
1227
%      unsigned int SolarizeImage(Image *image,const double threshold)
1228
%
1229
%  A description of each parameter follows:
1230
%
1231
%    o image: The image.
1232
%
1233
%    o threshold:  Define the extent of the solarization.
1234
%
1235
%
1236
*/
1237
static MagickPassFail
1238
SolarizeImagePixelsCB(void *mutable_data,         /* User provided mutable data */
1239
                      const void *immutable_data, /* User provided immutable data */
1240
                      Image * restrict image,               /* Modify image */
1241
                      PixelPacket * restrict pixels,        /* Pixel row */
1242
                      IndexPacket * restrict indexes,       /* Pixel row indexes */
1243
                      const long npixels,         /* Number of pixels in row */
1244
                      ExceptionInfo *exception)   /* Exception report */
1245
0
{
1246
1247
0
  const unsigned int
1248
0
    threshold = *((const unsigned int *) immutable_data);
1249
1250
0
  register long
1251
0
    i;
1252
1253
0
  ARG_NOT_USED(mutable_data);
1254
0
  ARG_NOT_USED(image);
1255
0
  ARG_NOT_USED(indexes);
1256
0
  ARG_NOT_USED(exception);
1257
1258
0
  for (i=0; i < npixels; i++)
1259
0
    {
1260
0
      pixels[i].red=(pixels[i].red > threshold ?
1261
0
                     MaxRGB-pixels[i].red : pixels[i].red);
1262
0
      pixels[i].green=(pixels[i].green > threshold ?
1263
0
                       MaxRGB-pixels[i].green : pixels[i].green);
1264
0
      pixels[i].blue=(pixels[i].blue > threshold ?
1265
0
                      MaxRGB-pixels[i].blue : pixels[i].blue);
1266
0
    }
1267
1268
0
  return MagickPass;
1269
0
}
1270
MagickExport MagickPassFail SolarizeImage(Image *image,const double threshold)
1271
0
{
1272
0
#define SolarizeImageText "[%s] Solarize..."
1273
1274
0
  unsigned int
1275
0
    is_grayscale,
1276
0
    threshold_int;
1277
1278
0
  MagickPassFail
1279
0
    status=MagickPass;
1280
1281
0
  assert(image != (Image *) NULL);
1282
0
  assert(image->signature == MagickSignature);
1283
0
  is_grayscale=image->is_grayscale;
1284
0
  threshold_int=(threshold < 0.0 ? 0U :
1285
0
                 (threshold > MaxRGBDouble) ? MaxRGB :
1286
0
                 (unsigned int) (threshold + 0.5));
1287
0
  switch (image->storage_class)
1288
0
  {
1289
0
    case DirectClass:
1290
0
    default:
1291
0
    {
1292
      /*
1293
        Solarize DirectClass packets.
1294
      */
1295
0
      status=PixelIterateMonoModify(SolarizeImagePixelsCB,
1296
0
                                    NULL,
1297
0
                                    SolarizeImageText,
1298
0
                                    NULL,&threshold_int,0,0,image->columns,image->rows,
1299
0
                                    image,&image->exception);
1300
0
      break;
1301
0
    }
1302
0
    case PseudoClass:
1303
0
    {
1304
      /*
1305
        Solarize PseudoClass packets.
1306
      */
1307
0
      SolarizeImagePixelsCB(0,
1308
0
                            &threshold_int,
1309
0
                            image,
1310
0
                            image->colormap,
1311
0
                            (IndexPacket *) NULL,
1312
0
                            image->colors,
1313
0
                            &image->exception);
1314
0
      status &= SyncImage(image);
1315
0
      break;
1316
0
    }
1317
0
  }
1318
0
  image->is_grayscale=is_grayscale;
1319
0
  return (status);
1320
0
}
1321

1322
/*
1323
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1324
%                                                                             %
1325
%                                                                             %
1326
%                                                                             %
1327
%   S t e g a n o I m a g e                                                   %
1328
%                                                                             %
1329
%                                                                             %
1330
%                                                                             %
1331
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1332
%
1333
%  Use SteganoImage() to hide a digital watermark within the image.
1334
%  Recover the hidden watermark later to prove that the authenticity of
1335
%  an image.  Offset defines the start position within the image to hide
1336
%  the watermark.
1337
%
1338
%  The format of the SteganoImage method is:
1339
%
1340
%      Image *SteganoImage(const Image *image,Image *watermark,
1341
%        ExceptionInfo *exception)
1342
%
1343
%  A description of each parameter follows:
1344
%
1345
%    o image: The image.
1346
%
1347
%    o watermark: The watermark image.
1348
%
1349
%    o exception: Return any errors or warnings in this structure.
1350
%
1351
%
1352
*/
1353
#define GetBit(a,i) (((a) >> (i)) & 0x01)
1354
#define SetBit(a,i,set) \
1355
0
  a=(Quantum) ((set) ? (a) | (1UL << (i)) : (a) & ~(1UL << (i)))
1356
0
#define SteganoImageText "[%s] Stegano..."
1357
MagickExport Image *SteganoImage(const Image * restrict image,const Image * restrict watermark,
1358
  ExceptionInfo *exception)
1359
0
{
1360
1361
0
  Image
1362
0
    * restrict stegano_image;
1363
1364
0
  long
1365
0
    c,
1366
0
    i,
1367
0
    j,
1368
0
    k,
1369
0
    y;
1370
1371
0
  PixelPacket
1372
0
    pixel;
1373
1374
0
  register long
1375
0
    x;
1376
1377
0
  unsigned int
1378
0
    is_grayscale;
1379
1380
0
  register PixelPacket
1381
0
    * restrict q;
1382
1383
0
  MagickPassFail
1384
0
    status = MagickPass;
1385
1386
  /*
1387
    Initialize steganographic image attributes.
1388
  */
1389
0
  assert(image != (const Image *) NULL);
1390
0
  assert(image->signature == MagickSignature);
1391
0
  assert(watermark != (const Image *) NULL);
1392
0
  assert(watermark->signature == MagickSignature);
1393
0
  assert(exception != (ExceptionInfo *) NULL);
1394
0
  assert(exception->signature == MagickSignature);
1395
0
  is_grayscale=(image->is_grayscale && watermark->is_grayscale);
1396
0
  stegano_image=CloneImage(image,0,0,True,exception);
1397
0
  if (stegano_image == (Image *) NULL)
1398
0
    return((Image *) NULL);
1399
0
  status &= SetImageType(stegano_image,TrueColorType);
1400
0
  if (status == MagickFail)
1401
0
    {
1402
0
      CopyException(exception,&stegano_image->exception);
1403
0
      DestroyImage(stegano_image);
1404
0
      return (Image *) NULL;
1405
0
    }
1406
1407
0
  stegano_image->depth=QuantumDepth;
1408
  /*
1409
    Hide watermark in low-order bits of image.
1410
  */
1411
0
  c=0;
1412
0
  i=0;
1413
0
  j=0;
1414
0
  k=image->offset;
1415
0
  for (i=QuantumDepth-1; (i >= 0) && (j < QuantumDepth); i--)
1416
0
  {
1417
0
    for (y=0; (y < (long) watermark->rows) && (j < QuantumDepth); y++)
1418
0
    {
1419
0
      for (x=0; (x < (long) watermark->columns) && (j < QuantumDepth); x++)
1420
0
      {
1421
0
        (void) AcquireOnePixelByReference(watermark,&pixel,x,y,exception);
1422
0
        q=GetImagePixels(stegano_image,k % (long) stegano_image->columns,
1423
0
          k/(long) stegano_image->columns,1,1);
1424
0
        if (q == (PixelPacket *) NULL)
1425
0
          break;
1426
0
        switch ((int) c)
1427
0
        {
1428
0
          case 0:
1429
0
          {
1430
0
            SetBit(q->red,j,GetBit(PixelIntensityToQuantum(&pixel),i));
1431
0
            break;
1432
0
          }
1433
0
          case 1:
1434
0
          {
1435
0
            SetBit(q->green,j,GetBit(PixelIntensityToQuantum(&pixel),i));
1436
0
            break;
1437
0
          }
1438
0
          case 2:
1439
0
          {
1440
0
            SetBit(q->blue,j,GetBit(PixelIntensityToQuantum(&pixel),i));
1441
0
            break;
1442
0
          }
1443
0
        }
1444
0
        (void) SyncImage(stegano_image);
1445
0
        c++;
1446
0
        if (c == 3)
1447
0
          c=0;
1448
0
        k++;
1449
0
        if (k == (long) (stegano_image->columns*stegano_image->columns))
1450
0
          k=0;
1451
0
        if (k == image->offset)
1452
0
          j++;
1453
0
      }
1454
0
    }
1455
0
    if (QuantumTick(i,QuantumDepth))
1456
0
      if (!MagickMonitorFormatted(i,QuantumDepth,exception,
1457
0
                                  SteganoImageText,image->filename))
1458
0
        break;
1459
0
  }
1460
0
  if (stegano_image->storage_class == PseudoClass)
1461
0
    (void) SyncImage(stegano_image);
1462
0
  stegano_image->is_grayscale=is_grayscale;
1463
0
  return(stegano_image);
1464
0
}
1465

1466
/*
1467
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1468
%                                                                             %
1469
%                                                                             %
1470
%                                                                             %
1471
%   S t e r e o I m a g e                                                     %
1472
%                                                                             %
1473
%                                                                             %
1474
%                                                                             %
1475
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1476
%
1477
%  StereoImage() combines two images and produces a single image that is the
1478
%  composite of a left and right image of a stereo pair.  Special red-green
1479
%  stereo glasses are required to view this effect.
1480
%
1481
%  The format of the StereoImage method is:
1482
%
1483
%      Image *StereoImage(const Image *image,const Image *offset_image,
1484
%        ExceptionInfo *exception)
1485
%
1486
%  A description of each parameter follows:
1487
%
1488
%    o stereo_image: Method StereoImage returns a pointer to the stereo
1489
%      image.  A null image is returned if there is a memory shortage.
1490
%
1491
%    o image: The image.
1492
%
1493
%    o offset_image: Another image.
1494
%
1495
%    o exception: Return any errors or warnings in this structure.
1496
%
1497
%
1498
*/
1499
MagickExport Image *StereoImage(const Image * restrict image,const Image * restrict offset_image,
1500
  ExceptionInfo *exception)
1501
0
{
1502
0
#define StereoImageText "[%s] Stereo..."
1503
1504
0
  Image
1505
0
    * restrict stereo_image;
1506
1507
0
  long
1508
0
    y;
1509
1510
0
  register const PixelPacket
1511
0
    * restrict p,
1512
0
    * restrict q;
1513
1514
0
  register long
1515
0
    x;
1516
1517
0
  register PixelPacket
1518
0
    * restrict r;
1519
1520
0
  MagickPassFail
1521
0
    status = MagickPass;
1522
1523
0
  assert(image != (const Image *) NULL);
1524
0
  assert(image->signature == MagickSignature);
1525
0
  assert(exception != (ExceptionInfo *) NULL);
1526
0
  assert(exception->signature == MagickSignature);
1527
0
  assert(offset_image != (const Image *) NULL);
1528
0
  if ((image->columns != offset_image->columns) ||
1529
0
      (image->rows != offset_image->rows))
1530
0
    ThrowImageException3(ImageError,UnableToCreateStereoImage,
1531
0
      LeftAndRightImageSizesDiffer);
1532
  /*
1533
    Initialize stereo image attributes.
1534
  */
1535
0
  stereo_image=CloneImage(image,image->columns,image->rows,True,exception);
1536
0
  if (stereo_image == (Image *) NULL)
1537
0
    return((Image *) NULL);
1538
0
  status &= SetImageType(stereo_image,TrueColorType);
1539
0
  if (status == MagickFail)
1540
0
    {
1541
0
      CopyException(exception,&stereo_image->exception);
1542
0
      DestroyImage(stereo_image);
1543
0
      return (Image *) NULL;
1544
0
    }
1545
  /*
1546
    Copy left image to red channel and right image to blue channel.
1547
  */
1548
0
  for (y=0; y < (long) stereo_image->rows; y++)
1549
0
  {
1550
0
    p=AcquireImagePixels(image,0,y,image->columns,1,exception);
1551
0
    q=AcquireImagePixels(offset_image,0,y,offset_image->columns,1,exception);
1552
0
    r=SetImagePixels(stereo_image,0,y,stereo_image->columns,1);
1553
0
    if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL) ||
1554
0
        (r == (PixelPacket *) NULL))
1555
0
      break;
1556
0
    for (x=0; x < (long) stereo_image->columns; x++)
1557
0
    {
1558
0
      r->red=p->red;
1559
0
      r->green=q->green;
1560
0
      r->blue=q->blue;
1561
0
      r->opacity=(Quantum) (((double) p->opacity+q->opacity)/2.0);
1562
0
      p++;
1563
0
      q++;
1564
0
      r++;
1565
0
    }
1566
0
    if (!SyncImagePixels(stereo_image))
1567
0
      break;
1568
0
    if (QuantumTick(y,stereo_image->rows))
1569
0
      if (!MagickMonitorFormatted(y,stereo_image->rows,exception,
1570
0
                                  StereoImageText,image->filename))
1571
0
        break;
1572
0
  }
1573
0
  if (y != (long) stereo_image->rows)
1574
0
    {
1575
0
      if (stereo_image->exception.severity > exception->severity)
1576
0
        CopyException(exception,&stereo_image->exception);
1577
0
      DestroyImage(stereo_image);
1578
0
      stereo_image=(Image *) NULL;
1579
0
    }
1580
0
  return(stereo_image);
1581
0
}
1582

1583
/*
1584
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1585
%                                                                             %
1586
%                                                                             %
1587
%     S w i r l I m a g e                                                     %
1588
%                                                                             %
1589
%                                                                             %
1590
%                                                                             %
1591
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1592
%
1593
%  SwirlImage() swirls the pixels about the center of the image, where
1594
%  degrees indicates the sweep of the arc through which each pixel is moved.
1595
%  You get a more dramatic effect as the degrees move from 1 to 360.
1596
%
1597
%  The format of the SwirlImage method is:
1598
%
1599
%      Image *SwirlImage(const Image *image,double degrees,
1600
%        ExceptionInfo *exception)
1601
%
1602
%  A description of each parameter follows:
1603
%
1604
%    o image: The image.
1605
%
1606
%    o degrees: Define the tightness of the swirling effect.
1607
%
1608
%    o exception: Return any errors or warnings in this structure.
1609
%
1610
%
1611
*/
1612
MagickExport Image *SwirlImage(const Image * restrict image,double degrees,
1613
                               ExceptionInfo *exception)
1614
0
{
1615
0
#define SwirlImageText "[%s] Swirl..."
1616
1617
0
  double
1618
0
    radius,
1619
0
    x_center,
1620
0
    x_scale,
1621
0
    y_center,
1622
0
    y_scale;
1623
1624
0
  long
1625
0
    y;
1626
1627
0
  Image
1628
0
    * restrict swirl_image;
1629
1630
0
  MagickPassFail
1631
0
    status = MagickPass;
1632
1633
  /*
1634
    Initialize swirl image attributes.
1635
  */
1636
0
  assert(image != (const Image *) NULL);
1637
0
  assert(image->signature == MagickSignature);
1638
0
  assert(exception != (ExceptionInfo *) NULL);
1639
0
  assert(exception->signature == MagickSignature);
1640
0
  swirl_image=CloneImage(image,image->columns,image->rows,True,exception);
1641
0
  if (swirl_image == (Image *) NULL)
1642
0
    return((Image *) NULL);
1643
0
  status &= SetImageType(swirl_image,swirl_image->background_color.opacity !=
1644
0
                      OpaqueOpacity ? TrueColorMatteType : TrueColorType);
1645
0
  if (status == MagickFail)
1646
0
    {
1647
0
      CopyException(exception,&swirl_image->exception);
1648
0
      DestroyImage(swirl_image);
1649
0
      return (Image *) NULL;
1650
0
    }
1651
  /*
1652
    Compute scaling factor.
1653
  */
1654
0
  x_center=image->columns/2.0;
1655
0
  y_center=image->rows/2.0;
1656
0
  radius=Max(x_center,y_center);
1657
0
  x_scale=1.0;
1658
0
  y_scale=1.0;
1659
0
  if (image->columns > image->rows)
1660
0
    y_scale=(double) image->columns/image->rows;
1661
0
  else
1662
0
    if (image->columns < image->rows)
1663
0
      x_scale=(double) image->rows/image->columns;
1664
0
  degrees=DegreesToRadians(degrees);
1665
  /*
1666
    Swirl each row.
1667
  */
1668
0
  {
1669
0
    unsigned long
1670
0
      row_count=0;
1671
1672
0
    MagickBool
1673
0
      monitor_active;
1674
1675
0
    monitor_active=MagickMonitorActive();
1676
1677
#if defined(HAVE_OPENMP)
1678
#  if defined(TUNE_OPENMP)
1679
#    pragma omp parallel for schedule(runtime) shared(row_count, status)
1680
#  else
1681
#    pragma omp parallel for schedule(guided) shared(row_count, status)
1682
#  endif
1683
#endif
1684
0
    for (y=0; y < (long) image->rows; y++)
1685
0
      {
1686
0
        register PixelPacket
1687
0
          * restrict q;
1688
1689
0
        register long
1690
0
          x;
1691
1692
0
        double
1693
0
          x_distance,
1694
0
          y_distance,
1695
0
          distance;
1696
1697
0
        ViewInfo
1698
0
          *image_view;
1699
1700
0
        MagickPassFail
1701
0
          thread_status;
1702
1703
0
        thread_status=status;
1704
0
        if (thread_status == MagickFail)
1705
0
          continue;
1706
1707
0
        image_view=AccessDefaultCacheView(image);
1708
0
        q=SetImagePixelsEx(swirl_image,0,y,swirl_image->columns,1,exception);
1709
0
        if (q == (PixelPacket *) NULL)
1710
0
          thread_status=MagickFail;
1711
0
        if (thread_status != MagickFail)
1712
0
          {
1713
0
            y_distance=y_scale*(y-y_center);
1714
0
            for (x=0; x < (long) image->columns; x++)
1715
0
              {
1716
                /*
1717
                  Determine if the pixel is within an ellipse.
1718
                */
1719
0
                x_distance=x_scale*(x-x_center);
1720
0
                distance=x_distance*x_distance+y_distance*y_distance;
1721
0
                if (distance >= (radius*radius))
1722
0
                  (void) AcquireOneCacheViewPixel(image_view,q,x,y,exception);
1723
0
                else
1724
0
                  {
1725
0
                    double
1726
0
                      cosine,
1727
0
                      factor,
1728
0
                      sine;
1729
1730
                    /*
1731
                      Swirl the pixel.
1732
                    */
1733
0
                    factor=1.0-sqrt(distance)/radius;
1734
0
                    sine=sin(degrees*factor*factor);
1735
0
                    cosine=cos(degrees*factor*factor);
1736
0
                    if (InterpolateViewColor(image_view,q,
1737
0
                                             (cosine*x_distance-sine*y_distance)/x_scale+x_center,
1738
0
                                             (sine*x_distance+cosine*y_distance)/y_scale+y_center,
1739
0
                                             exception) == MagickFail)
1740
0
                      {
1741
0
                        thread_status=MagickFail;
1742
0
                        break;
1743
0
                      }
1744
0
                  }
1745
0
                q++;
1746
0
              }
1747
0
            if (thread_status != MagickFail)
1748
0
              if (!SyncImagePixelsEx(swirl_image,exception))
1749
0
                thread_status=MagickFail;
1750
0
          }
1751
1752
0
        if (monitor_active)
1753
0
          {
1754
0
            unsigned long
1755
0
              thread_row_count;
1756
1757
#if defined(HAVE_OPENMP)
1758
#  pragma omp atomic
1759
#endif
1760
0
            row_count++;
1761
#if defined(HAVE_OPENMP)
1762
#  pragma omp flush (row_count)
1763
#endif
1764
0
            thread_row_count=row_count;
1765
0
            if (QuantumTick(thread_row_count,image->rows))
1766
0
              if (!MagickMonitorFormatted(thread_row_count,image->rows,exception,
1767
0
                                          SwirlImageText,image->filename))
1768
0
                thread_status=MagickFail;
1769
0
          }
1770
1771
0
        if (thread_status == MagickFail)
1772
0
          {
1773
0
            status=MagickFail;
1774
#if defined(HAVE_OPENMP)
1775
#  pragma omp flush (status)
1776
#endif
1777
0
          }
1778
0
      }
1779
0
  }
1780
0
  swirl_image->is_grayscale=image->is_grayscale;
1781
0
  if (status == MagickFail)
1782
0
    {
1783
0
      DestroyImage(swirl_image);
1784
0
      swirl_image=(Image *) NULL;
1785
0
    }
1786
0
  return(swirl_image);
1787
0
}
1788

1789
/*
1790
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1791
%                                                                             %
1792
%                                                                             %
1793
%     W a v e I m a g e                                                       %
1794
%                                                                             %
1795
%                                                                             %
1796
%                                                                             %
1797
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1798
%
1799
%  The WaveImage() filter creates a "ripple" effect in the image by shifting
1800
%  the pixels vertically along a sine wave whose amplitude and wavelength
1801
%  is specified by the given parameters.
1802
%
1803
%  The format of the WaveImage method is:
1804
%
1805
%      Image *WaveImage(const Image *image,const double amplitude,
1806
%        const double wave_length,ExceptionInfo *exception)
1807
%
1808
%  A description of each parameter follows:
1809
%
1810
%    o image: The image.
1811
%
1812
%    o amplitude, frequency:  Define the amplitude and wave_length of the
1813
%      sine wave.
1814
%
1815
%    o exception: Return any errors or warnings in this structure.
1816
%
1817
%
1818
*/
1819
MagickExport Image *WaveImage(const Image * restrict image,const double amplitude,
1820
                              const double wave_length,ExceptionInfo *exception)
1821
0
{
1822
0
#define WaveImageText "[%s] Wave..."
1823
1824
0
  VirtualPixelMethod
1825
0
    virtual_pixel_method;
1826
1827
0
  float
1828
0
    * restrict sine_map;
1829
1830
0
  Image
1831
0
    * restrict wave_image;
1832
1833
0
  long
1834
0
    y;
1835
1836
0
  MagickPassFail
1837
0
    status = MagickPass;
1838
1839
  /*
1840
    Initialize wave image attributes.
1841
  */
1842
0
  assert(image != (Image *) NULL);
1843
0
  assert(image->signature == MagickSignature);
1844
0
  assert(exception != (ExceptionInfo *) NULL);
1845
0
  assert(exception->signature == MagickSignature);
1846
0
  wave_image=CloneImage(image,image->columns,(long)
1847
0
                        (image->rows+2.0*FABSF(amplitude)),MagickTrue,exception);
1848
0
  if (wave_image == (Image *) NULL)
1849
0
    return((Image *) NULL);
1850
0
  wave_image->storage_class=DirectClass;
1851
1852
  /*
1853
    If background color is non-opaque, then initialize matte channel.
1854
  */
1855
0
  if ((wave_image->background_color.opacity != OpaqueOpacity) &&
1856
0
      (!wave_image->matte))
1857
0
    SetImageOpacity(wave_image,OpaqueOpacity);
1858
1859
  /*
1860
    Allocate and initialize sine map.
1861
  */
1862
0
  {
1863
0
    register long
1864
0
      x;
1865
1866
0
    sine_map=MagickAllocateArray(float *,wave_image->columns,sizeof(float));
1867
0
    if (sine_map == (float *) NULL)
1868
0
      {
1869
0
        DestroyImage(wave_image);
1870
0
        ThrowImageException(ResourceLimitError,MemoryAllocationFailed,
1871
0
                            MagickMsg(OptionError,UnableToWaveImage));
1872
0
      }
1873
1874
#if defined(HAVE_OPENMP)
1875
#  pragma omp parallel for schedule(static,256)
1876
#endif
1877
0
    for (x=0; x < (long) wave_image->columns; x++)
1878
0
      sine_map[x]=(float) (FABSF(amplitude)+amplitude*SINF((2.0*MagickPI*x)/wave_length));
1879
0
  }
1880
  /*
1881
    Set virtual pixel method.
1882
  */
1883
1884
0
  virtual_pixel_method=GetImageVirtualPixelMethod(image);
1885
0
  if (virtual_pixel_method == UndefinedVirtualPixelMethod)
1886
0
    (void) SetImageVirtualPixelMethod(image,ConstantVirtualPixelMethod);
1887
  /*
1888
    Wave image.
1889
  */
1890
0
  {
1891
0
    unsigned long
1892
0
      row_count=0;
1893
1894
0
    MagickBool
1895
0
      monitor_active;
1896
1897
0
    monitor_active=MagickMonitorActive();
1898
1899
#if defined(HAVE_OPENMP)
1900
#  if defined(TUNE_OPENMP)
1901
#    pragma omp parallel for schedule(runtime) shared(row_count, status)
1902
#  else
1903
#    pragma omp parallel for schedule(guided) shared(row_count, status)
1904
#  endif
1905
#endif
1906
0
    for (y=0; y < (long) wave_image->rows; y++)
1907
0
      {
1908
0
        register PixelPacket
1909
0
          * restrict q;
1910
1911
0
        register unsigned long
1912
0
          x;
1913
1914
0
        ViewInfo
1915
0
          *image_view;
1916
1917
0
        MagickPassFail
1918
0
          thread_status;
1919
1920
0
        thread_status=status;
1921
0
        if (thread_status == MagickFail)
1922
0
          continue;
1923
1924
0
        image_view=AccessDefaultCacheView(image);
1925
0
        q=SetImagePixelsEx(wave_image,0,y,wave_image->columns,1,exception);
1926
0
        if (q == (PixelPacket *) NULL)
1927
0
          thread_status=MagickFail;
1928
0
        if (thread_status != MagickFail)
1929
0
          {
1930
0
            for (x=0; x < wave_image->columns; x++)
1931
0
              {
1932
0
                if (InterpolateViewColor(image_view,&q[x],(double) x,
1933
0
                                         (double) y-sine_map[x],
1934
0
                                         exception) == MagickFail)
1935
0
                  {
1936
0
                    thread_status=MagickFail;
1937
0
                    break;
1938
0
                  }
1939
0
              }
1940
0
            if (thread_status != MagickFail)
1941
0
              if (!SyncImagePixelsEx(wave_image,exception))
1942
0
                thread_status=MagickFail;
1943
0
          }
1944
1945
0
        if (monitor_active)
1946
0
          {
1947
0
            unsigned long
1948
0
              thread_row_count;
1949
1950
#if defined(HAVE_OPENMP)
1951
#  pragma omp atomic
1952
#endif
1953
0
            row_count++;
1954
#if defined(HAVE_OPENMP)
1955
#  pragma omp flush (row_count)
1956
#endif
1957
0
            thread_row_count=row_count;
1958
0
            if (QuantumTick(thread_row_count,wave_image->rows))
1959
0
              if (!MagickMonitorFormatted(thread_row_count,wave_image->rows,exception,
1960
0
                                          WaveImageText,image->filename))
1961
0
                thread_status=MagickFail;
1962
0
          }
1963
1964
0
        if (thread_status == MagickFail)
1965
0
          {
1966
0
            status=MagickFail;
1967
#if defined(HAVE_OPENMP)
1968
#  pragma omp flush (status)
1969
#endif
1970
0
          }
1971
0
      }
1972
0
  }
1973
  /*
1974
    Restore virtual pixel method.
1975
  */
1976
0
  (void) SetImageVirtualPixelMethod(image,virtual_pixel_method);
1977
0
  MagickFreeMemory(sine_map);
1978
0
  wave_image->is_grayscale=(image->is_grayscale && IsGray(wave_image->background_color));
1979
0
  if (status == MagickFail)
1980
0
    {
1981
0
      DestroyImage(wave_image);
1982
      wave_image=(Image *) NULL;
1983
0
    }
1984
0
  return(wave_image);
1985
0
}