]> granicus.if.org Git - imagemagick/blob - MagickCore/enhance.c
(no commit message)
[imagemagick] / MagickCore / enhance.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %              EEEEE  N   N  H   H   AAA   N   N   CCCC  EEEEE                %
7 %              E      NN  N  H   H  A   A  NN  N  C      E                    %
8 %              EEE    N N N  HHHHH  AAAAA  N N N  C      EEE                  %
9 %              E      N  NN  H   H  A   A  N  NN  C      E                    %
10 %              EEEEE  N   N  H   H  A   A  N   N   CCCC  EEEEE                %
11 %                                                                             %
12 %                                                                             %
13 %                    MagickCore Image Enhancement Methods                     %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                John Cristy                                  %
17 %                                 July 1992                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 %
38 */
39 \f
40 /*
41   Include declarations.
42 */
43 #include "MagickCore/studio.h"
44 #include "MagickCore/artifact.h"
45 #include "MagickCore/cache.h"
46 #include "MagickCore/cache-view.h"
47 #include "MagickCore/color.h"
48 #include "MagickCore/color-private.h"
49 #include "MagickCore/colorspace.h"
50 #include "MagickCore/composite-private.h"
51 #include "MagickCore/enhance.h"
52 #include "MagickCore/exception.h"
53 #include "MagickCore/exception-private.h"
54 #include "MagickCore/fx.h"
55 #include "MagickCore/gem.h"
56 #include "MagickCore/geometry.h"
57 #include "MagickCore/histogram.h"
58 #include "MagickCore/image.h"
59 #include "MagickCore/image-private.h"
60 #include "MagickCore/memory_.h"
61 #include "MagickCore/monitor.h"
62 #include "MagickCore/monitor-private.h"
63 #include "MagickCore/option.h"
64 #include "MagickCore/pixel-accessor.h"
65 #include "MagickCore/quantum.h"
66 #include "MagickCore/quantum-private.h"
67 #include "MagickCore/resample.h"
68 #include "MagickCore/resample-private.h"
69 #include "MagickCore/statistic.h"
70 #include "MagickCore/string_.h"
71 #include "MagickCore/string-private.h"
72 #include "MagickCore/thread-private.h"
73 #include "MagickCore/token.h"
74 #include "MagickCore/xml-tree.h"
75 \f
76 /*
77 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
78 %                                                                             %
79 %                                                                             %
80 %                                                                             %
81 %     A u t o G a m m a I m a g e                                             %
82 %                                                                             %
83 %                                                                             %
84 %                                                                             %
85 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
86 %
87 %  AutoGammaImage() extract the 'mean' from the image and adjust the image
88 %  to try make set its gamma appropriatally.
89 %
90 %  The format of the AutoGammaImage method is:
91 %
92 %      MagickBooleanType AutoGammaImage(Image *image)
93 %
94 %  A description of each parameter follows:
95 %
96 %    o image: The image to auto-level
97 %
98 */
99 MagickExport MagickBooleanType AutoGammaImage(Image *image)
100 {
101   MagickStatusType
102     status;
103
104   double
105     gamma,
106     log_mean,
107     mean,
108     sans;
109
110   log_mean=log(0.5);
111   if (image->sync != MagickFalse)
112     {
113       /*
114         Apply gamma correction equally accross all given channels
115       */
116       (void) GetImageMean(image,&mean,&sans,&image->exception);
117       gamma=log(mean*QuantumScale)/log_mean;
118       return(LevelImage(image,0.0,(double) QuantumRange,gamma));
119     }
120   /*
121     Auto-gamma each channel separately.
122   */
123   status=MagickTrue;
124   if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
125     {
126       PushPixelComponentMap(image,RedChannel);
127       (void) GetImageMean(image,&mean,&sans,&image->exception);
128       gamma=log(mean*QuantumScale)/log_mean;
129       status=status && LevelImage(image,0.0,(double) QuantumRange,gamma);
130       PopPixelComponentMap(image);
131     }
132   if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
133     {
134       PushPixelComponentMap(image,GreenChannel);
135       (void) GetImageMean(image,&mean,&sans,&image->exception);
136       gamma=log(mean*QuantumScale)/log_mean;
137       status=status && LevelImage(image,0.0,(double) QuantumRange,gamma);
138       PopPixelComponentMap(image);
139     }
140   if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
141     {
142       PushPixelComponentMap(image,BlueChannel);
143       (void) GetImageMean(image,&mean,&sans,&image->exception);
144       gamma=log(mean*QuantumScale)/log_mean;
145       status=status && LevelImage(image,0.0,(double) QuantumRange,gamma);
146       PopPixelComponentMap(image);
147     }
148   if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
149       (image->colorspace == CMYKColorspace))
150     {
151       PushPixelComponentMap(image,BlackChannel);
152       (void) GetImageMean(image,&mean,&sans,&image->exception);
153       gamma=log(mean*QuantumScale)/log_mean;
154       status=status && LevelImage(image,0.0,(double) QuantumRange,gamma);
155       PopPixelComponentMap(image);
156     }
157   if (((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0) &&
158       (image->matte == MagickTrue))
159     {
160       PushPixelComponentMap(image,AlphaChannel);
161       (void) GetImageMean(image,&mean,&sans,&image->exception);
162       gamma=log(mean*QuantumScale)/log_mean;
163       status=status && LevelImage(image,0.0,(double) QuantumRange,gamma);
164       PopPixelComponentMap(image);
165     }
166   return(status != 0 ? MagickTrue : MagickFalse);
167 }
168 \f
169 /*
170 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
171 %                                                                             %
172 %                                                                             %
173 %                                                                             %
174 %     A u t o L e v e l I m a g e                                             %
175 %                                                                             %
176 %                                                                             %
177 %                                                                             %
178 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
179 %
180 %  AutoLevelImage() adjusts the levels of a particular image channel by
181 %  scaling the minimum and maximum values to the full quantum range.
182 %
183 %  The format of the LevelImage method is:
184 %
185 %      MagickBooleanType AutoLevelImage(Image *image)
186 %
187 %  A description of each parameter follows:
188 %
189 %    o image: The image to auto-level
190 %
191 */
192 MagickExport MagickBooleanType AutoLevelImage(Image *image)
193 {
194   return(MinMaxStretchImage(image,0.0,0.0));
195 }
196 \f
197 /*
198 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
199 %                                                                             %
200 %                                                                             %
201 %                                                                             %
202 %     B r i g h t n e s s C o n t r a s t I m a g e                           %
203 %                                                                             %
204 %                                                                             %
205 %                                                                             %
206 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
207 %
208 %  Use BrightnessContrastImage() to change the brightness and/or contrast of
209 %  an image.  It converts the brightness and contrast parameters into slope
210 %  and intercept and calls a polynomical function to apply to the image.
211 %
212 %  The format of the BrightnessContrastImage method is:
213 %
214 %      MagickBooleanType BrightnessContrastImage(Image *image,
215 %        const double brightness,const double contrast)
216 %
217 %  A description of each parameter follows:
218 %
219 %    o image: the image.
220 %
221 %    o brightness: the brightness percent (-100 .. 100).
222 %
223 %    o contrast: the contrast percent (-100 .. 100).
224 %
225 */
226 MagickExport MagickBooleanType BrightnessContrastImage(Image *image,
227   const double brightness,const double contrast)
228 {
229 #define BrightnessContastImageTag  "BrightnessContast/Image"
230
231   double
232     alpha,
233     intercept,
234     coefficients[2],
235     slope;
236
237   MagickBooleanType
238     status;
239
240   /*
241     Compute slope and intercept.
242   */
243   assert(image != (Image *) NULL);
244   assert(image->signature == MagickSignature);
245   if (image->debug != MagickFalse)
246     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
247   alpha=contrast;
248   slope=tan((double) (MagickPI*(alpha/100.0+1.0)/4.0));
249   if (slope < 0.0)
250     slope=0.0;
251   intercept=brightness/100.0+((100-brightness)/200.0)*(1.0-slope);
252   coefficients[0]=slope;
253   coefficients[1]=intercept;
254   status=FunctionImage(image,PolynomialFunction,2,coefficients,
255     &image->exception);
256   return(status);
257 }
258 \f
259 /*
260 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
261 %                                                                             %
262 %                                                                             %
263 %                                                                             %
264 %     C o l o r D e c i s i o n L i s t I m a g e                             %
265 %                                                                             %
266 %                                                                             %
267 %                                                                             %
268 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
269 %
270 %  ColorDecisionListImage() accepts a lightweight Color Correction Collection
271 %  (CCC) file which solely contains one or more color corrections and applies
272 %  the correction to the image.  Here is a sample CCC file:
273 %
274 %    <ColorCorrectionCollection xmlns="urn:ASC:CDL:v1.2">
275 %          <ColorCorrection id="cc03345">
276 %                <SOPNode>
277 %                     <Slope> 0.9 1.2 0.5 </Slope>
278 %                     <Offset> 0.4 -0.5 0.6 </Offset>
279 %                     <Power> 1.0 0.8 1.5 </Power>
280 %                </SOPNode>
281 %                <SATNode>
282 %                     <Saturation> 0.85 </Saturation>
283 %                </SATNode>
284 %          </ColorCorrection>
285 %    </ColorCorrectionCollection>
286 %
287 %  which includes the slop, offset, and power for each of the RGB channels
288 %  as well as the saturation.
289 %
290 %  The format of the ColorDecisionListImage method is:
291 %
292 %      MagickBooleanType ColorDecisionListImage(Image *image,
293 %        const char *color_correction_collection)
294 %
295 %  A description of each parameter follows:
296 %
297 %    o image: the image.
298 %
299 %    o color_correction_collection: the color correction collection in XML.
300 %
301 */
302 MagickExport MagickBooleanType ColorDecisionListImage(Image *image,
303   const char *color_correction_collection)
304 {
305 #define ColorDecisionListCorrectImageTag  "ColorDecisionList/Image"
306
307   typedef struct _Correction
308   {
309     double
310       slope,
311       offset,
312       power;
313   } Correction;
314
315   typedef struct _ColorCorrection
316   {
317     Correction
318       red,
319       green,
320       blue;
321
322     double
323       saturation;
324   } ColorCorrection;
325
326   CacheView
327     *image_view;
328
329   char
330     token[MaxTextExtent];
331
332   ColorCorrection
333     color_correction;
334
335   const char
336     *content,
337     *p;
338
339   ExceptionInfo
340     *exception;
341
342   MagickBooleanType
343     status;
344
345   MagickOffsetType
346     progress;
347
348   PixelPacket
349     *cdl_map;
350
351   register ssize_t
352     i;
353
354   ssize_t
355     y;
356
357   XMLTreeInfo
358     *cc,
359     *ccc,
360     *sat,
361     *sop;
362
363   /*
364     Allocate and initialize cdl maps.
365   */
366   assert(image != (Image *) NULL);
367   assert(image->signature == MagickSignature);
368   if (image->debug != MagickFalse)
369     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
370   if (color_correction_collection == (const char *) NULL)
371     return(MagickFalse);
372   ccc=NewXMLTree((const char *) color_correction_collection,&image->exception);
373   if (ccc == (XMLTreeInfo *) NULL)
374     return(MagickFalse);
375   cc=GetXMLTreeChild(ccc,"ColorCorrection");
376   if (cc == (XMLTreeInfo *) NULL)
377     {
378       ccc=DestroyXMLTree(ccc);
379       return(MagickFalse);
380     }
381   color_correction.red.slope=1.0;
382   color_correction.red.offset=0.0;
383   color_correction.red.power=1.0;
384   color_correction.green.slope=1.0;
385   color_correction.green.offset=0.0;
386   color_correction.green.power=1.0;
387   color_correction.blue.slope=1.0;
388   color_correction.blue.offset=0.0;
389   color_correction.blue.power=1.0;
390   color_correction.saturation=0.0;
391   sop=GetXMLTreeChild(cc,"SOPNode");
392   if (sop != (XMLTreeInfo *) NULL)
393     {
394       XMLTreeInfo
395         *offset,
396         *power,
397         *slope;
398
399       slope=GetXMLTreeChild(sop,"Slope");
400       if (slope != (XMLTreeInfo *) NULL)
401         {
402           content=GetXMLTreeContent(slope);
403           p=(const char *) content;
404           for (i=0; (*p != '\0') && (i < 3); i++)
405           {
406             GetMagickToken(p,&p,token);
407             if (*token == ',')
408               GetMagickToken(p,&p,token);
409             switch (i)
410             {
411               case 0:
412               {
413                 color_correction.red.slope=InterpretLocaleValue(token,
414                   (char **) NULL);
415                 break;
416               }
417               case 1:
418               {
419                 color_correction.green.slope=InterpretLocaleValue(token,
420                   (char **) NULL);
421                 break;
422               }
423               case 2:
424               {
425                 color_correction.blue.slope=InterpretLocaleValue(token,
426                   (char **) NULL);
427                 break;
428               }
429             }
430           }
431         }
432       offset=GetXMLTreeChild(sop,"Offset");
433       if (offset != (XMLTreeInfo *) NULL)
434         {
435           content=GetXMLTreeContent(offset);
436           p=(const char *) content;
437           for (i=0; (*p != '\0') && (i < 3); i++)
438           {
439             GetMagickToken(p,&p,token);
440             if (*token == ',')
441               GetMagickToken(p,&p,token);
442             switch (i)
443             {
444               case 0:
445               {
446                 color_correction.red.offset=InterpretLocaleValue(token,
447                   (char **) NULL);
448                 break;
449               }
450               case 1:
451               {
452                 color_correction.green.offset=InterpretLocaleValue(token,
453                   (char **) NULL);
454                 break;
455               }
456               case 2:
457               {
458                 color_correction.blue.offset=InterpretLocaleValue(token,
459                   (char **) NULL);
460                 break;
461               }
462             }
463           }
464         }
465       power=GetXMLTreeChild(sop,"Power");
466       if (power != (XMLTreeInfo *) NULL)
467         {
468           content=GetXMLTreeContent(power);
469           p=(const char *) content;
470           for (i=0; (*p != '\0') && (i < 3); i++)
471           {
472             GetMagickToken(p,&p,token);
473             if (*token == ',')
474               GetMagickToken(p,&p,token);
475             switch (i)
476             {
477               case 0:
478               {
479                 color_correction.red.power=InterpretLocaleValue(token,
480                   (char **) NULL);
481                 break;
482               }
483               case 1:
484               {
485                 color_correction.green.power=InterpretLocaleValue(token,
486                   (char **) NULL);
487                 break;
488               }
489               case 2:
490               {
491                 color_correction.blue.power=InterpretLocaleValue(token,
492                   (char **) NULL);
493                 break;
494               }
495             }
496           }
497         }
498     }
499   sat=GetXMLTreeChild(cc,"SATNode");
500   if (sat != (XMLTreeInfo *) NULL)
501     {
502       XMLTreeInfo
503         *saturation;
504
505       saturation=GetXMLTreeChild(sat,"Saturation");
506       if (saturation != (XMLTreeInfo *) NULL)
507         {
508           content=GetXMLTreeContent(saturation);
509           p=(const char *) content;
510           GetMagickToken(p,&p,token);
511           color_correction.saturation=InterpretLocaleValue(token,
512             (char **) NULL);
513         }
514     }
515   ccc=DestroyXMLTree(ccc);
516   if (image->debug != MagickFalse)
517     {
518       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
519         "  Color Correction Collection:");
520       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
521         "  color_correction.red.slope: %g",color_correction.red.slope);
522       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
523         "  color_correction.red.offset: %g",color_correction.red.offset);
524       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
525         "  color_correction.red.power: %g",color_correction.red.power);
526       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
527         "  color_correction.green.slope: %g",color_correction.green.slope);
528       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
529         "  color_correction.green.offset: %g",color_correction.green.offset);
530       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
531         "  color_correction.green.power: %g",color_correction.green.power);
532       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
533         "  color_correction.blue.slope: %g",color_correction.blue.slope);
534       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
535         "  color_correction.blue.offset: %g",color_correction.blue.offset);
536       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
537         "  color_correction.blue.power: %g",color_correction.blue.power);
538       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
539         "  color_correction.saturation: %g",color_correction.saturation);
540     }
541   cdl_map=(PixelPacket *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*cdl_map));
542   if (cdl_map == (PixelPacket *) NULL)
543     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
544       image->filename);
545 #if defined(MAGICKCORE_OPENMP_SUPPORT)
546   #pragma omp parallel for schedule(dynamic,4)
547 #endif
548   for (i=0; i <= (ssize_t) MaxMap; i++)
549   {
550     cdl_map[i].red=ClampToQuantum((MagickRealType) ScaleMapToQuantum((
551       MagickRealType) (MaxMap*(pow(color_correction.red.slope*i/MaxMap+
552       color_correction.red.offset,color_correction.red.power)))));
553     cdl_map[i].green=ClampToQuantum((MagickRealType) ScaleMapToQuantum((
554       MagickRealType) (MaxMap*(pow(color_correction.green.slope*i/MaxMap+
555       color_correction.green.offset,color_correction.green.power)))));
556     cdl_map[i].blue=ClampToQuantum((MagickRealType) ScaleMapToQuantum((
557       MagickRealType) (MaxMap*(pow(color_correction.blue.slope*i/MaxMap+
558       color_correction.blue.offset,color_correction.blue.power)))));
559   }
560   if (image->storage_class == PseudoClass)
561     {
562       /*
563         Apply transfer function to colormap.
564       */
565 #if defined(MAGICKCORE_OPENMP_SUPPORT)
566   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
567 #endif
568       for (i=0; i < (ssize_t) image->colors; i++)
569       {
570         double
571           luma;
572
573         luma=0.2126*image->colormap[i].red+0.7152*image->colormap[i].green+
574           0.0722*image->colormap[i].blue;
575         image->colormap[i].red=ClampToQuantum(luma+color_correction.saturation*
576           cdl_map[ScaleQuantumToMap(image->colormap[i].red)].red-luma);
577         image->colormap[i].green=ClampToQuantum(luma+
578           color_correction.saturation*cdl_map[ScaleQuantumToMap(
579           image->colormap[i].green)].green-luma);
580         image->colormap[i].blue=ClampToQuantum(luma+color_correction.saturation*
581           cdl_map[ScaleQuantumToMap(image->colormap[i].blue)].blue-luma);
582       }
583     }
584   /*
585     Apply transfer function to image.
586   */
587   status=MagickTrue;
588   progress=0;
589   exception=(&image->exception);
590   image_view=AcquireCacheView(image);
591 #if defined(MAGICKCORE_OPENMP_SUPPORT)
592   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
593 #endif
594   for (y=0; y < (ssize_t) image->rows; y++)
595   {
596     double
597       luma;
598
599     register Quantum
600       *restrict q;
601
602     register ssize_t
603       x;
604
605     if (status == MagickFalse)
606       continue;
607     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
608     if (q == (const Quantum *) NULL)
609       {
610         status=MagickFalse;
611         continue;
612       }
613     for (x=0; x < (ssize_t) image->columns; x++)
614     {
615       luma=0.2126*GetPixelRed(image,q)+0.7152*GetPixelGreen(image,q)+0.0722*
616         GetPixelBlue(image,q);
617       SetPixelRed(image,ClampToQuantum(luma+color_correction.saturation*
618         (cdl_map[ScaleQuantumToMap(GetPixelRed(image,q))].red-luma)),q);
619       SetPixelGreen(image,ClampToQuantum(luma+color_correction.saturation*
620         (cdl_map[ScaleQuantumToMap(GetPixelGreen(image,q))].green-luma)),q);
621       SetPixelBlue(image,ClampToQuantum(luma+color_correction.saturation*
622         (cdl_map[ScaleQuantumToMap(GetPixelBlue(image,q))].blue-luma)),q);
623       q+=GetPixelComponents(image);
624     }
625     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
626       status=MagickFalse;
627     if (image->progress_monitor != (MagickProgressMonitor) NULL)
628       {
629         MagickBooleanType
630           proceed;
631
632 #if defined(MAGICKCORE_OPENMP_SUPPORT)
633   #pragma omp critical (MagickCore_ColorDecisionListImageChannel)
634 #endif
635         proceed=SetImageProgress(image,ColorDecisionListCorrectImageTag,
636           progress++,image->rows);
637         if (proceed == MagickFalse)
638           status=MagickFalse;
639       }
640   }
641   image_view=DestroyCacheView(image_view);
642   cdl_map=(PixelPacket *) RelinquishMagickMemory(cdl_map);
643   return(status);
644 }
645 \f
646 /*
647 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
648 %                                                                             %
649 %                                                                             %
650 %                                                                             %
651 %     C l u t I m a g e                                                       %
652 %                                                                             %
653 %                                                                             %
654 %                                                                             %
655 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
656 %
657 %  ClutImage() replaces each color value in the given image, by using it as an
658 %  index to lookup a replacement color value in a Color Look UP Table in the
659 %  form of an image.  The values are extracted along a diagonal of the CLUT
660 %  image so either a horizontal or vertial gradient image can be used.
661 %
662 %  Typically this is used to either re-color a gray-scale image according to a
663 %  color gradient in the CLUT image, or to perform a freeform histogram
664 %  (level) adjustment according to the (typically gray-scale) gradient in the
665 %  CLUT image.
666 %
667 %  When the 'channel' mask includes the matte/alpha transparency channel but
668 %  one image has no such channel it is assumed that that image is a simple
669 %  gray-scale image that will effect the alpha channel values, either for
670 %  gray-scale coloring (with transparent or semi-transparent colors), or
671 %  a histogram adjustment of existing alpha channel values.   If both images
672 %  have matte channels, direct and normal indexing is applied, which is rarely
673 %  used.
674 %
675 %  The format of the ClutImage method is:
676 %
677 %      MagickBooleanType ClutImage(Image *image,Image *clut_image)
678 %
679 %  A description of each parameter follows:
680 %
681 %    o image: the image, which is replaced by indexed CLUT values
682 %
683 %    o clut_image: the color lookup table image for replacement color values.
684 %
685 %    o channel: the channel.
686 %
687 */
688 MagickExport MagickBooleanType ClutImage(Image *image,const Image *clut_image)
689 {
690 #define ClampAlphaPixelComponent(pixel) ClampToQuantum((pixel)->alpha)
691 #define ClampBlackPixelComponent(pixel) ClampToQuantum((pixel)->black)
692 #define ClampBluePixelComponent(pixel) ClampToQuantum((pixel)->blue)
693 #define ClampGreenPixelComponent(pixel) ClampToQuantum((pixel)->green)
694 #define ClampRedPixelComponent(pixel) ClampToQuantum((pixel)->red)
695 #define ClutImageTag  "Clut/Image"
696
697   CacheView
698     *clut_view,
699     *image_view;
700
701   ExceptionInfo
702     *exception;
703
704   MagickBooleanType
705     status;
706
707   MagickOffsetType
708     progress;
709
710   PixelInfo
711     *clut_map;
712
713   register ssize_t
714     i;
715
716   ssize_t
717     adjust,
718     y;
719
720   assert(image != (Image *) NULL);
721   assert(image->signature == MagickSignature);
722   if (image->debug != MagickFalse)
723     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
724   assert(clut_image != (Image *) NULL);
725   assert(clut_image->signature == MagickSignature);
726   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
727     return(MagickFalse);
728   clut_map=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,
729     sizeof(*clut_map));
730   if (clut_map == (PixelInfo *) NULL)
731     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
732       image->filename);
733   /*
734     Clut image.
735   */
736   status=MagickTrue;
737   progress=0;
738   adjust=(ssize_t) (clut_image->interpolate == IntegerInterpolatePixel ? 0 : 1);
739   exception=(&image->exception);
740   clut_view=AcquireCacheView(clut_image);
741 #if defined(MAGICKCORE_OPENMP_SUPPORT)
742   #pragma omp parallel for schedule(dynamic,4)
743 #endif
744   for (i=0; i <= (ssize_t) MaxMap; i++)
745   {
746     GetPixelInfo(clut_image,clut_map+i);
747     (void) InterpolatePixelInfo(clut_image,clut_view,
748       UndefinedInterpolatePixel,QuantumScale*i*(clut_image->columns-adjust),
749       QuantumScale*i*(clut_image->rows-adjust),clut_map+i,exception);
750   }
751   clut_view=DestroyCacheView(clut_view);
752   image_view=AcquireCacheView(image);
753 #if defined(MAGICKCORE_OPENMP_SUPPORT)
754   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
755 #endif
756   for (y=0; y < (ssize_t) image->rows; y++)
757   {
758     PixelInfo
759       pixel;
760
761     register Quantum
762       *restrict q;
763
764     register ssize_t
765       x;
766
767     if (status == MagickFalse)
768       continue;
769     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
770     if (q == (const Quantum *) NULL)
771       {
772         status=MagickFalse;
773         continue;
774       }
775     GetPixelInfo(image,&pixel);
776     for (x=0; x < (ssize_t) image->columns; x++)
777     {
778       SetPixelInfo(image,q,&pixel);
779       if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
780         SetPixelRed(image,ClampRedPixelComponent(clut_map+
781           ScaleQuantumToMap(GetPixelRed(image,q))),q);
782       if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
783         SetPixelGreen(image,ClampGreenPixelComponent(clut_map+
784           ScaleQuantumToMap(GetPixelGreen(image,q))),q);
785       if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
786         SetPixelBlue(image,ClampBluePixelComponent(clut_map+
787           ScaleQuantumToMap(GetPixelBlue(image,q))),q);
788       if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
789           (image->colorspace == CMYKColorspace))
790         SetPixelBlack(image,ClampBlackPixelComponent(clut_map+
791           ScaleQuantumToMap(GetPixelBlack(image,q))),q);
792       if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0)
793         {
794           if (clut_image->matte == MagickFalse)
795             SetPixelAlpha(image,GetPixelInfoIntensity(clut_map+
796               ScaleQuantumToMap((Quantum) GetPixelAlpha(image,q))),q);
797           else
798             if (image->matte == MagickFalse)
799               SetPixelAlpha(image,ClampAlphaPixelComponent(clut_map+
800                 ScaleQuantumToMap((Quantum) GetPixelInfoIntensity(&pixel))),q);
801             else
802               SetPixelAlpha(image,ClampAlphaPixelComponent(clut_map+
803                 ScaleQuantumToMap(GetPixelAlpha(image,q))),q);
804         }
805       q+=GetPixelComponents(image);
806     }
807     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
808       status=MagickFalse;
809     if (image->progress_monitor != (MagickProgressMonitor) NULL)
810       {
811         MagickBooleanType
812           proceed;
813
814 #if defined(MAGICKCORE_OPENMP_SUPPORT)
815   #pragma omp critical (MagickCore_ClutImage)
816 #endif
817         proceed=SetImageProgress(image,ClutImageTag,progress++,image->rows);
818         if (proceed == MagickFalse)
819           status=MagickFalse;
820       }
821   }
822   image_view=DestroyCacheView(image_view);
823   clut_map=(PixelInfo *) RelinquishMagickMemory(clut_map);
824   if ((clut_image->matte != MagickFalse) &&
825       ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0))
826     (void) SetImageAlphaChannel(image,ActivateAlphaChannel);
827   return(status);
828 }
829 \f
830 /*
831 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
832 %                                                                             %
833 %                                                                             %
834 %                                                                             %
835 %     C o n t r a s t I m a g e                                               %
836 %                                                                             %
837 %                                                                             %
838 %                                                                             %
839 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
840 %
841 %  ContrastImage() enhances the intensity differences between the lighter and
842 %  darker elements of the image.  Set sharpen to a MagickTrue to increase the
843 %  image contrast otherwise the contrast is reduced.
844 %
845 %  The format of the ContrastImage method is:
846 %
847 %      MagickBooleanType ContrastImage(Image *image,
848 %        const MagickBooleanType sharpen)
849 %
850 %  A description of each parameter follows:
851 %
852 %    o image: the image.
853 %
854 %    o sharpen: Increase or decrease image contrast.
855 %
856 */
857
858 static void Contrast(const int sign,Quantum *red,Quantum *green,Quantum *blue)
859 {
860   double
861     brightness,
862     hue,
863     saturation;
864
865   /*
866     Enhance contrast: dark color become darker, light color become lighter.
867   */
868   assert(red != (Quantum *) NULL);
869   assert(green != (Quantum *) NULL);
870   assert(blue != (Quantum *) NULL);
871   hue=0.0;
872   saturation=0.0;
873   brightness=0.0;
874   ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
875   brightness+=0.5*sign*(0.5*(sin((double) (MagickPI*(brightness-0.5)))+1.0)-
876     brightness);
877   if (brightness > 1.0)
878     brightness=1.0;
879   else
880     if (brightness < 0.0)
881       brightness=0.0;
882   ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
883 }
884
885 MagickExport MagickBooleanType ContrastImage(Image *image,
886   const MagickBooleanType sharpen)
887 {
888 #define ContrastImageTag  "Contrast/Image"
889
890   CacheView
891     *image_view;
892
893   ExceptionInfo
894     *exception;
895
896   int
897     sign;
898
899   MagickBooleanType
900     status;
901
902   MagickOffsetType
903     progress;
904
905   register ssize_t
906     i;
907
908   ssize_t
909     y;
910
911   assert(image != (Image *) NULL);
912   assert(image->signature == MagickSignature);
913   if (image->debug != MagickFalse)
914     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
915   sign=sharpen != MagickFalse ? 1 : -1;
916   if (image->storage_class == PseudoClass)
917     {
918       /*
919         Contrast enhance colormap.
920       */
921       for (i=0; i < (ssize_t) image->colors; i++)
922         Contrast(sign,&image->colormap[i].red,&image->colormap[i].green,
923           &image->colormap[i].blue);
924     }
925   /*
926     Contrast enhance image.
927   */
928   status=MagickTrue;
929   progress=0;
930   exception=(&image->exception);
931   image_view=AcquireCacheView(image);
932 #if defined(MAGICKCORE_OPENMP_SUPPORT)
933   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
934 #endif
935   for (y=0; y < (ssize_t) image->rows; y++)
936   {
937     Quantum
938       blue,
939       green,
940       red;
941
942     register Quantum
943       *restrict q;
944
945     register ssize_t
946       x;
947
948     if (status == MagickFalse)
949       continue;
950     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
951     if (q == (const Quantum *) NULL)
952       {
953         status=MagickFalse;
954         continue;
955       }
956     for (x=0; x < (ssize_t) image->columns; x++)
957     {
958       red=GetPixelRed(image,q);
959       green=GetPixelGreen(image,q);
960       blue=GetPixelBlue(image,q);
961       Contrast(sign,&red,&green,&blue);
962       SetPixelRed(image,red,q);
963       SetPixelGreen(image,green,q);
964       SetPixelBlue(image,blue,q);
965       q+=GetPixelComponents(image);
966     }
967     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
968       status=MagickFalse;
969     if (image->progress_monitor != (MagickProgressMonitor) NULL)
970       {
971         MagickBooleanType
972           proceed;
973
974 #if defined(MAGICKCORE_OPENMP_SUPPORT)
975   #pragma omp critical (MagickCore_ContrastImage)
976 #endif
977         proceed=SetImageProgress(image,ContrastImageTag,progress++,image->rows);
978         if (proceed == MagickFalse)
979           status=MagickFalse;
980       }
981   }
982   image_view=DestroyCacheView(image_view);
983   return(status);
984 }
985 \f
986 /*
987 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
988 %                                                                             %
989 %                                                                             %
990 %                                                                             %
991 %     C o n t r a s t S t r e t c h I m a g e                                 %
992 %                                                                             %
993 %                                                                             %
994 %                                                                             %
995 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
996 %
997 %  The ContrastStretchImage() is a simple image enhancement technique that
998 %  attempts to improve the contrast in an image by `stretching' the range of
999 %  intensity values it contains to span a desired range of values. It differs
1000 %  from the more sophisticated histogram equalization in that it can only
1001 %  apply %  a linear scaling function to the image pixel values.  As a result
1002 %  the `enhancement' is less harsh.
1003 %
1004 %  The format of the ContrastStretchImage method is:
1005 %
1006 %      MagickBooleanType ContrastStretchImage(Image *image,
1007 %        const char *levels)
1008 %
1009 %  A description of each parameter follows:
1010 %
1011 %    o image: the image.
1012 %
1013 %    o black_point: the black point.
1014 %
1015 %    o white_point: the white point.
1016 %
1017 %    o levels: Specify the levels where the black and white points have the
1018 %      range of 0 to number-of-pixels (e.g. 1%, 10x90%, etc.).
1019 %
1020 */
1021 MagickExport MagickBooleanType ContrastStretchImage(Image *image,
1022   const double black_point,const double white_point)
1023 {
1024 #define MaxRange(color)  ((MagickRealType) ScaleQuantumToMap((Quantum) (color)))
1025 #define ContrastStretchImageTag  "ContrastStretch/Image"
1026
1027   CacheView
1028     *image_view;
1029
1030   double
1031     intensity;
1032
1033   ExceptionInfo
1034     *exception;
1035
1036   MagickBooleanType
1037     status;
1038
1039   MagickOffsetType
1040     progress;
1041
1042   PixelInfo
1043     black,
1044     *histogram,
1045     *stretch_map,
1046     white;
1047
1048   register ssize_t
1049     i;
1050
1051   ssize_t
1052     y;
1053
1054   /*
1055     Allocate histogram and stretch map.
1056   */
1057   assert(image != (Image *) NULL);
1058   assert(image->signature == MagickSignature);
1059   if (image->debug != MagickFalse)
1060     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1061   histogram=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,
1062     sizeof(*histogram));
1063   stretch_map=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,
1064     sizeof(*stretch_map));
1065   if ((histogram == (PixelInfo *) NULL) ||
1066       (stretch_map == (PixelInfo *) NULL))
1067     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1068       image->filename);
1069   /*
1070     Form histogram.
1071   */
1072   status=MagickTrue;
1073   exception=(&image->exception);
1074   (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
1075   image_view=AcquireCacheView(image);
1076   for (y=0; y < (ssize_t) image->rows; y++)
1077   {
1078     register const Quantum
1079       *restrict p;
1080
1081     register ssize_t
1082       x;
1083
1084     if (status == MagickFalse)
1085       continue;
1086     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1087     if (p == (const Quantum *) NULL)
1088       {
1089         status=MagickFalse;
1090         continue;
1091       }
1092     if (image->sync != MagickFalse)
1093       for (x=0; x < (ssize_t) image->columns; x++)
1094       {
1095         Quantum
1096           intensity;
1097
1098         intensity=GetPixelIntensity(image,p);
1099         histogram[ScaleQuantumToMap(intensity)].red++;
1100         histogram[ScaleQuantumToMap(intensity)].green++;
1101         histogram[ScaleQuantumToMap(intensity)].blue++;
1102         histogram[ScaleQuantumToMap(intensity)].black++;
1103         p+=GetPixelComponents(image);
1104       }
1105     else
1106       for (x=0; x < (ssize_t) image->columns; x++)
1107       {
1108         if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
1109           histogram[ScaleQuantumToMap(GetPixelRed(image,p))].red++;
1110         if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
1111           histogram[ScaleQuantumToMap(GetPixelGreen(image,p))].green++;
1112         if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
1113           histogram[ScaleQuantumToMap(GetPixelBlue(image,p))].blue++;
1114         if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
1115             (image->colorspace == CMYKColorspace))
1116           histogram[ScaleQuantumToMap(GetPixelBlack(image,p))].black++;
1117         if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0)
1118           histogram[ScaleQuantumToMap(GetPixelAlpha(image,p))].alpha++;
1119         p+=GetPixelComponents(image);
1120       }
1121   }
1122   /*
1123     Find the histogram boundaries by locating the black/white levels.
1124   */
1125   black.red=0.0;
1126   white.red=MaxRange(QuantumRange);
1127   if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
1128     {
1129       intensity=0.0;
1130       for (i=0; i <= (ssize_t) MaxMap; i++)
1131       {
1132         intensity+=histogram[i].red;
1133         if (intensity > black_point)
1134           break;
1135       }
1136       black.red=(MagickRealType) i;
1137       intensity=0.0;
1138       for (i=(ssize_t) MaxMap; i != 0; i--)
1139       {
1140         intensity+=histogram[i].red;
1141         if (intensity > ((double) image->columns*image->rows-white_point))
1142           break;
1143       }
1144       white.red=(MagickRealType) i;
1145     }
1146   black.green=0.0;
1147   white.green=MaxRange(QuantumRange);
1148   if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
1149     {
1150       intensity=0.0;
1151       for (i=0; i <= (ssize_t) MaxMap; i++)
1152       {
1153         intensity+=histogram[i].green;
1154         if (intensity > black_point)
1155           break;
1156       }
1157       black.green=(MagickRealType) i;
1158       intensity=0.0;
1159       for (i=(ssize_t) MaxMap; i != 0; i--)
1160       {
1161         intensity+=histogram[i].green;
1162         if (intensity > ((double) image->columns*image->rows-white_point))
1163           break;
1164       }
1165       white.green=(MagickRealType) i;
1166     }
1167   black.blue=0.0;
1168   white.blue=MaxRange(QuantumRange);
1169   if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
1170     {
1171       intensity=0.0;
1172       for (i=0; i <= (ssize_t) MaxMap; i++)
1173       {
1174         intensity+=histogram[i].blue;
1175         if (intensity > black_point)
1176           break;
1177       }
1178       black.blue=(MagickRealType) i;
1179       intensity=0.0;
1180       for (i=(ssize_t) MaxMap; i != 0; i--)
1181       {
1182         intensity+=histogram[i].blue;
1183         if (intensity > ((double) image->columns*image->rows-white_point))
1184           break;
1185       }
1186       white.blue=(MagickRealType) i;
1187     }
1188   black.alpha=0.0;
1189   white.alpha=MaxRange(QuantumRange);
1190   if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0)
1191     {
1192       intensity=0.0;
1193       for (i=0; i <= (ssize_t) MaxMap; i++)
1194       {
1195         intensity+=histogram[i].alpha;
1196         if (intensity > black_point)
1197           break;
1198       }
1199       black.alpha=(MagickRealType) i;
1200       intensity=0.0;
1201       for (i=(ssize_t) MaxMap; i != 0; i--)
1202       {
1203         intensity+=histogram[i].alpha;
1204         if (intensity > ((double) image->columns*image->rows-white_point))
1205           break;
1206       }
1207       white.alpha=(MagickRealType) i;
1208     }
1209   black.black=0.0;
1210   white.black=MaxRange(QuantumRange);
1211   if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) && (image->colorspace == CMYKColorspace))
1212     {
1213       intensity=0.0;
1214       for (i=0; i <= (ssize_t) MaxMap; i++)
1215       {
1216         intensity+=histogram[i].black;
1217         if (intensity > black_point)
1218           break;
1219       }
1220       black.black=(MagickRealType) i;
1221       intensity=0.0;
1222       for (i=(ssize_t) MaxMap; i != 0; i--)
1223       {
1224         intensity+=histogram[i].black;
1225         if (intensity > ((double) image->columns*image->rows-white_point))
1226           break;
1227       }
1228       white.black=(MagickRealType) i;
1229     }
1230   histogram=(PixelInfo *) RelinquishMagickMemory(histogram);
1231   /*
1232     Stretch the histogram to create the stretched image mapping.
1233   */
1234   (void) ResetMagickMemory(stretch_map,0,(MaxMap+1)*sizeof(*stretch_map));
1235 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1236   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1237 #endif
1238   for (i=0; i <= (ssize_t) MaxMap; i++)
1239   {
1240     if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
1241       {
1242         if (i < (ssize_t) black.red)
1243           stretch_map[i].red=0.0;
1244         else
1245           if (i > (ssize_t) white.red)
1246             stretch_map[i].red=(MagickRealType) QuantumRange;
1247           else
1248             if (black.red != white.red)
1249               stretch_map[i].red=(MagickRealType) ScaleMapToQuantum(
1250                 (MagickRealType) (MaxMap*(i-black.red)/(white.red-black.red)));
1251       }
1252     if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
1253       {
1254         if (i < (ssize_t) black.green)
1255           stretch_map[i].green=0.0;
1256         else
1257           if (i > (ssize_t) white.green)
1258             stretch_map[i].green=(MagickRealType) QuantumRange;
1259           else
1260             if (black.green != white.green)
1261               stretch_map[i].green=(MagickRealType) ScaleMapToQuantum(
1262                 (MagickRealType) (MaxMap*(i-black.green)/(white.green-
1263                 black.green)));
1264       }
1265     if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
1266       {
1267         if (i < (ssize_t) black.blue)
1268           stretch_map[i].blue=0.0;
1269         else
1270           if (i > (ssize_t) white.blue)
1271             stretch_map[i].blue=(MagickRealType) QuantumRange;
1272           else
1273             if (black.blue != white.blue)
1274               stretch_map[i].blue=(MagickRealType) ScaleMapToQuantum(
1275                 (MagickRealType) (MaxMap*(i-black.blue)/(white.blue-
1276                 black.blue)));
1277       }
1278     if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0)
1279       {
1280         if (i < (ssize_t) black.alpha)
1281           stretch_map[i].alpha=0.0;
1282         else
1283           if (i > (ssize_t) white.alpha)
1284             stretch_map[i].alpha=(MagickRealType) QuantumRange;
1285           else
1286             if (black.alpha != white.alpha)
1287               stretch_map[i].alpha=(MagickRealType) ScaleMapToQuantum(
1288                 (MagickRealType) (MaxMap*(i-black.alpha)/(white.alpha-
1289                 black.alpha)));
1290       }
1291     if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
1292         (image->colorspace == CMYKColorspace))
1293       {
1294         if (i < (ssize_t) black.black)
1295           stretch_map[i].black=0.0;
1296         else
1297           if (i > (ssize_t) white.black)
1298             stretch_map[i].black=(MagickRealType) QuantumRange;
1299           else
1300             if (black.black != white.black)
1301               stretch_map[i].black=(MagickRealType) ScaleMapToQuantum(
1302                 (MagickRealType) (MaxMap*(i-black.black)/(white.black-
1303                 black.black)));
1304       }
1305   }
1306   /*
1307     Stretch the image.
1308   */
1309   if (((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0) || (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
1310       (image->colorspace == CMYKColorspace)))
1311     image->storage_class=DirectClass;
1312   if (image->storage_class == PseudoClass)
1313     {
1314       /*
1315         Stretch colormap.
1316       */
1317 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1318   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1319 #endif
1320       for (i=0; i < (ssize_t) image->colors; i++)
1321       {
1322         if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
1323           {
1324             if (black.red != white.red)
1325               image->colormap[i].red=ClampToQuantum(stretch_map[
1326                 ScaleQuantumToMap(image->colormap[i].red)].red);
1327           }
1328         if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
1329           {
1330             if (black.green != white.green)
1331               image->colormap[i].green=ClampToQuantum(stretch_map[
1332                 ScaleQuantumToMap(image->colormap[i].green)].green);
1333           }
1334         if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
1335           {
1336             if (black.blue != white.blue)
1337               image->colormap[i].blue=ClampToQuantum(stretch_map[
1338                 ScaleQuantumToMap(image->colormap[i].blue)].blue);
1339           }
1340         if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0)
1341           {
1342             if (black.alpha != white.alpha)
1343               image->colormap[i].alpha=ClampToQuantum(stretch_map[
1344                 ScaleQuantumToMap(image->colormap[i].alpha)].alpha);
1345           }
1346       }
1347     }
1348   /*
1349     Stretch image.
1350   */
1351   status=MagickTrue;
1352   progress=0;
1353 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1354   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1355 #endif
1356   for (y=0; y < (ssize_t) image->rows; y++)
1357   {
1358     register Quantum
1359       *restrict q;
1360
1361     register ssize_t
1362       x;
1363
1364     if (status == MagickFalse)
1365       continue;
1366     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1367     if (q == (const Quantum *) NULL)
1368       {
1369         status=MagickFalse;
1370         continue;
1371       }
1372     for (x=0; x < (ssize_t) image->columns; x++)
1373     {
1374       if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
1375         {
1376           if (black.red != white.red)
1377             SetPixelRed(image,ClampToQuantum(stretch_map[ScaleQuantumToMap(
1378               GetPixelRed(image,q))].red),q);
1379         }
1380       if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
1381         {
1382           if (black.green != white.green)
1383             SetPixelGreen(image,ClampToQuantum(stretch_map[ScaleQuantumToMap(
1384               GetPixelGreen(image,q))].green),q);
1385         }
1386       if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
1387         {
1388           if (black.blue != white.blue)
1389             SetPixelBlue(image,ClampToQuantum(stretch_map[ScaleQuantumToMap(
1390               GetPixelBlue(image,q))].blue),q);
1391         }
1392       if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
1393           (image->colorspace == CMYKColorspace))
1394         {
1395           if (black.black != white.black)
1396             SetPixelBlack(image,ClampToQuantum(stretch_map[ScaleQuantumToMap(
1397               GetPixelBlack(image,q))].black),q);
1398         }
1399       if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0)
1400         {
1401           if (black.alpha != white.alpha)
1402             SetPixelAlpha(image,ClampToQuantum(stretch_map[ScaleQuantumToMap(
1403               GetPixelAlpha(image,q))].alpha),q);
1404         }
1405       q+=GetPixelComponents(image);
1406     }
1407     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1408       status=MagickFalse;
1409     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1410       {
1411         MagickBooleanType
1412           proceed;
1413
1414 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1415   #pragma omp critical (MagickCore_ContrastStretchImage)
1416 #endif
1417         proceed=SetImageProgress(image,ContrastStretchImageTag,progress++,
1418           image->rows);
1419         if (proceed == MagickFalse)
1420           status=MagickFalse;
1421       }
1422   }
1423   image_view=DestroyCacheView(image_view);
1424   stretch_map=(PixelInfo *) RelinquishMagickMemory(stretch_map);
1425   return(status);
1426 }
1427 \f
1428 /*
1429 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1430 %                                                                             %
1431 %                                                                             %
1432 %                                                                             %
1433 %     E n h a n c e I m a g e                                                 %
1434 %                                                                             %
1435 %                                                                             %
1436 %                                                                             %
1437 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1438 %
1439 %  EnhanceImage() applies a digital filter that improves the quality of a
1440 %  noisy image.
1441 %
1442 %  The format of the EnhanceImage method is:
1443 %
1444 %      Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
1445 %
1446 %  A description of each parameter follows:
1447 %
1448 %    o image: the image.
1449 %
1450 %    o exception: return any errors or warnings in this structure.
1451 %
1452 */
1453 MagickExport Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
1454 {
1455 #define Enhance(weight) \
1456   mean=((MagickRealType) GetPixelRed(image,r)+pixel.red)/2; \
1457   distance=(MagickRealType) GetPixelRed(image,r)-(MagickRealType) pixel.red; \
1458   distance_squared=QuantumScale*(2.0*((MagickRealType) QuantumRange+1.0)+ \
1459      mean)*distance*distance; \
1460   mean=((MagickRealType) GetPixelGreen(image,r)+pixel.green)/2; \
1461   distance=(MagickRealType) GetPixelGreen(image,r)- \
1462     (MagickRealType) pixel.green; \
1463   distance_squared+=4.0*distance*distance; \
1464   mean=((MagickRealType) GetPixelBlue(image,r)+pixel.blue)/2; \
1465   distance=(MagickRealType) GetPixelBlue(image,r)- \
1466     (MagickRealType) pixel.blue; \
1467   distance_squared+=QuantumScale*(3.0*((MagickRealType) \
1468     QuantumRange+1.0)-1.0-mean)*distance*distance; \
1469   mean=((MagickRealType) GetPixelAlpha(image,r)+pixel.alpha)/2; \
1470   distance=(MagickRealType) GetPixelAlpha(image,r)-(MagickRealType) pixel.alpha; \
1471   distance_squared+=QuantumScale*(3.0*((MagickRealType) \
1472     QuantumRange+1.0)-1.0-mean)*distance*distance; \
1473   if (distance_squared < ((MagickRealType) QuantumRange*(MagickRealType) \
1474       QuantumRange/25.0f)) \
1475     { \
1476       aggregate.red+=(weight)*GetPixelRed(image,r); \
1477       aggregate.green+=(weight)*GetPixelGreen(image,r); \
1478       aggregate.blue+=(weight)*GetPixelBlue(image,r); \
1479       aggregate.alpha+=(weight)*GetPixelAlpha(image,r); \
1480       total_weight+=(weight); \
1481     } \
1482   r++;
1483 #define EnhanceImageTag  "Enhance/Image"
1484
1485   CacheView
1486     *enhance_view,
1487     *image_view;
1488
1489   Image
1490     *enhance_image;
1491
1492   MagickBooleanType
1493     status;
1494
1495   MagickOffsetType
1496     progress;
1497
1498   PixelInfo
1499     zero;
1500
1501   ssize_t
1502     y;
1503
1504   /*
1505     Initialize enhanced image attributes.
1506   */
1507   assert(image != (const Image *) NULL);
1508   assert(image->signature == MagickSignature);
1509   if (image->debug != MagickFalse)
1510     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1511   assert(exception != (ExceptionInfo *) NULL);
1512   assert(exception->signature == MagickSignature);
1513   if ((image->columns < 5) || (image->rows < 5))
1514     return((Image *) NULL);
1515   enhance_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1516     exception);
1517   if (enhance_image == (Image *) NULL)
1518     return((Image *) NULL);
1519   if (SetImageStorageClass(enhance_image,DirectClass) == MagickFalse)
1520     {
1521       InheritException(exception,&enhance_image->exception);
1522       enhance_image=DestroyImage(enhance_image);
1523       return((Image *) NULL);
1524     }
1525   /*
1526     Enhance image.
1527   */
1528   status=MagickTrue;
1529   progress=0;
1530   (void) ResetMagickMemory(&zero,0,sizeof(zero));
1531   image_view=AcquireCacheView(image);
1532   enhance_view=AcquireCacheView(enhance_image);
1533 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1534   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1535 #endif
1536   for (y=0; y < (ssize_t) image->rows; y++)
1537   {
1538     register const Quantum
1539       *restrict p;
1540
1541     register Quantum
1542       *restrict q;
1543
1544     register ssize_t
1545       x;
1546
1547     /*
1548       Read another scan line.
1549     */
1550     if (status == MagickFalse)
1551       continue;
1552     p=GetCacheViewVirtualPixels(image_view,-2,y-2,image->columns+4,5,exception);
1553     q=QueueCacheViewAuthenticPixels(enhance_view,0,y,enhance_image->columns,1,
1554       exception);
1555     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1556       {
1557         status=MagickFalse;
1558         continue;
1559       }
1560     for (x=0; x < (ssize_t) image->columns; x++)
1561     {
1562       PixelInfo
1563         aggregate;
1564
1565       MagickRealType
1566         distance,
1567         distance_squared,
1568         mean,
1569         total_weight;
1570
1571       PixelPacket
1572         pixel;
1573
1574       register const Quantum
1575         *restrict r;
1576
1577       /*
1578         Compute weighted average of target pixel color components.
1579       */
1580       aggregate=zero;
1581       total_weight=0.0;
1582       r=p+2*(image->columns+4)+2;
1583       GetPixelPacket(image,r,&pixel);
1584       r=p;
1585       Enhance(5.0); Enhance(8.0); Enhance(10.0); Enhance(8.0); Enhance(5.0);
1586       r=p+(image->columns+4);
1587       Enhance(8.0); Enhance(20.0); Enhance(40.0); Enhance(20.0); Enhance(8.0);
1588       r=p+2*(image->columns+4);
1589       Enhance(10.0); Enhance(40.0); Enhance(80.0); Enhance(40.0); Enhance(10.0);
1590       r=p+3*(image->columns+4);
1591       Enhance(8.0); Enhance(20.0); Enhance(40.0); Enhance(20.0); Enhance(8.0);
1592       r=p+4*(image->columns+4);
1593       Enhance(5.0); Enhance(8.0); Enhance(10.0); Enhance(8.0); Enhance(5.0);
1594       SetPixelRed(enhance_image,(Quantum) ((aggregate.red+
1595         (total_weight/2)-1)/total_weight),q);
1596       SetPixelGreen(enhance_image,(Quantum) ((aggregate.green+
1597         (total_weight/2)-1)/total_weight),q);
1598       SetPixelBlue(enhance_image,(Quantum) ((aggregate.blue+
1599         (total_weight/2)-1)/total_weight),q);
1600       SetPixelAlpha(enhance_image,(Quantum) ((aggregate.alpha+
1601         (total_weight/2)-1)/total_weight),q);
1602       p+=GetPixelComponents(image);
1603       q+=GetPixelComponents(enhance_image);
1604     }
1605     if (SyncCacheViewAuthenticPixels(enhance_view,exception) == MagickFalse)
1606       status=MagickFalse;
1607     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1608       {
1609         MagickBooleanType
1610           proceed;
1611
1612 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1613   #pragma omp critical (MagickCore_EnhanceImage)
1614 #endif
1615         proceed=SetImageProgress(image,EnhanceImageTag,progress++,image->rows);
1616         if (proceed == MagickFalse)
1617           status=MagickFalse;
1618       }
1619   }
1620   enhance_view=DestroyCacheView(enhance_view);
1621   image_view=DestroyCacheView(image_view);
1622   return(enhance_image);
1623 }
1624 \f
1625 /*
1626 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1627 %                                                                             %
1628 %                                                                             %
1629 %                                                                             %
1630 %     E q u a l i z e I m a g e                                               %
1631 %                                                                             %
1632 %                                                                             %
1633 %                                                                             %
1634 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1635 %
1636 %  EqualizeImage() applies a histogram equalization to the image.
1637 %
1638 %  The format of the EqualizeImage method is:
1639 %
1640 %      MagickBooleanType EqualizeImage(Image *image)
1641 %
1642 %  A description of each parameter follows:
1643 %
1644 %    o image: the image.
1645 %
1646 %    o channel: the channel.
1647 %
1648 */
1649 MagickExport MagickBooleanType EqualizeImage(Image *image)
1650 {
1651 #define EqualizeImageTag  "Equalize/Image"
1652
1653   CacheView
1654     *image_view;
1655
1656   ExceptionInfo
1657     *exception;
1658
1659   MagickBooleanType
1660     status;
1661
1662   MagickOffsetType
1663     progress;
1664
1665   PixelInfo
1666     black,
1667     *equalize_map,
1668     *histogram,
1669     intensity,
1670     *map,
1671     white;
1672
1673   register ssize_t
1674     i;
1675
1676   ssize_t
1677     y;
1678
1679   /*
1680     Allocate and initialize histogram arrays.
1681   */
1682   assert(image != (Image *) NULL);
1683   assert(image->signature == MagickSignature);
1684   if (image->debug != MagickFalse)
1685     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1686   equalize_map=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,
1687     sizeof(*equalize_map));
1688   histogram=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,
1689     sizeof(*histogram));
1690   map=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*map));
1691   if ((equalize_map == (PixelInfo *) NULL) ||
1692       (histogram == (PixelInfo *) NULL) ||
1693       (map == (PixelInfo *) NULL))
1694     {
1695       if (map != (PixelInfo *) NULL)
1696         map=(PixelInfo *) RelinquishMagickMemory(map);
1697       if (histogram != (PixelInfo *) NULL)
1698         histogram=(PixelInfo *) RelinquishMagickMemory(histogram);
1699       if (equalize_map != (PixelInfo *) NULL)
1700         equalize_map=(PixelInfo *) RelinquishMagickMemory(equalize_map);
1701       ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1702         image->filename);
1703     }
1704   /*
1705     Form histogram.
1706   */
1707   (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
1708   exception=(&image->exception);
1709   for (y=0; y < (ssize_t) image->rows; y++)
1710   {
1711     register const Quantum
1712       *restrict p;
1713
1714     register ssize_t
1715       x;
1716
1717     p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1718     if (p == (const Quantum *) NULL)
1719       break;
1720     for (x=0; x < (ssize_t) image->columns; x++)
1721     {
1722       if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
1723         histogram[ScaleQuantumToMap(GetPixelRed(image,p))].red++;
1724       if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
1725         histogram[ScaleQuantumToMap(GetPixelGreen(image,p))].green++;
1726       if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
1727         histogram[ScaleQuantumToMap(GetPixelBlue(image,p))].blue++;
1728       if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
1729           (image->colorspace == CMYKColorspace))
1730         histogram[ScaleQuantumToMap(GetPixelBlack(image,p))].black++;
1731       if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0)
1732         histogram[ScaleQuantumToMap(GetPixelAlpha(image,p))].alpha++;
1733       p+=GetPixelComponents(image);
1734     }
1735   }
1736   /*
1737     Integrate the histogram to get the equalization map.
1738   */
1739   (void) ResetMagickMemory(&intensity,0,sizeof(intensity));
1740   for (i=0; i <= (ssize_t) MaxMap; i++)
1741   {
1742     if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
1743       intensity.red+=histogram[i].red;
1744     if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
1745       intensity.green+=histogram[i].green;
1746     if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
1747       intensity.blue+=histogram[i].blue;
1748     if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
1749         (image->colorspace == CMYKColorspace))
1750       intensity.black+=histogram[i].black;
1751     if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0)
1752       intensity.alpha+=histogram[i].alpha;
1753     map[i]=intensity;
1754   }
1755   black=map[0];
1756   white=map[(int) MaxMap];
1757   (void) ResetMagickMemory(equalize_map,0,(MaxMap+1)*sizeof(*equalize_map));
1758 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1759   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1760 #endif
1761   for (i=0; i <= (ssize_t) MaxMap; i++)
1762   {
1763     if (((GetPixelRedTraits(image) & ActivePixelTrait) != 0) &&
1764         (white.red != black.red))
1765       equalize_map[i].red=(MagickRealType) ScaleMapToQuantum((MagickRealType)
1766         ((MaxMap*(map[i].red-black.red))/(white.red-black.red)));
1767     if (((GetPixelGreenTraits(image) & ActivePixelTrait) != 0) &&
1768         (white.green != black.green))
1769       equalize_map[i].green=(MagickRealType) ScaleMapToQuantum((MagickRealType)
1770         ((MaxMap*(map[i].green-black.green))/(white.green-black.green)));
1771     if (((GetPixelBlueTraits(image) & ActivePixelTrait) != 0) &&
1772         (white.blue != black.blue))
1773       equalize_map[i].blue=(MagickRealType) ScaleMapToQuantum((MagickRealType)
1774         ((MaxMap*(map[i].blue-black.blue))/(white.blue-black.blue)));
1775     if ((((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
1776         (image->colorspace == CMYKColorspace)) &&
1777         (white.black != black.black))
1778       equalize_map[i].black=(MagickRealType) ScaleMapToQuantum((MagickRealType)
1779         ((MaxMap*(map[i].black-black.black))/(white.black-black.black)));
1780     if (((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0) &&
1781         (white.alpha != black.alpha))
1782       equalize_map[i].alpha=(MagickRealType) ScaleMapToQuantum(
1783         (MagickRealType) ((MaxMap*(map[i].alpha-black.alpha))/
1784         (white.alpha-black.alpha)));
1785   }
1786   histogram=(PixelInfo *) RelinquishMagickMemory(histogram);
1787   map=(PixelInfo *) RelinquishMagickMemory(map);
1788   if (image->storage_class == PseudoClass)
1789     {
1790       /*
1791         Equalize colormap.
1792       */
1793 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1794   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1795 #endif
1796       for (i=0; i < (ssize_t) image->colors; i++)
1797       {
1798         if (((GetPixelRedTraits(image) & ActivePixelTrait) != 0) &&
1799             (white.red != black.red))
1800           image->colormap[i].red=ClampToQuantum(equalize_map[
1801             ScaleQuantumToMap(image->colormap[i].red)].red);
1802         if (((GetPixelGreenTraits(image) & ActivePixelTrait) != 0) &&
1803             (white.green != black.green))
1804           image->colormap[i].green=ClampToQuantum(equalize_map[
1805             ScaleQuantumToMap(image->colormap[i].green)].green);
1806         if (((GetPixelBlueTraits(image) & ActivePixelTrait) != 0) &&
1807             (white.blue != black.blue))
1808           image->colormap[i].blue=ClampToQuantum(equalize_map[
1809             ScaleQuantumToMap(image->colormap[i].blue)].blue);
1810         if (((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0) &&
1811             (white.alpha != black.alpha))
1812           image->colormap[i].alpha=ClampToQuantum(equalize_map[
1813             ScaleQuantumToMap(image->colormap[i].alpha)].alpha);
1814       }
1815     }
1816   /*
1817     Equalize image.
1818   */
1819   status=MagickTrue;
1820   progress=0;
1821   exception=(&image->exception);
1822   image_view=AcquireCacheView(image);
1823 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1824   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1825 #endif
1826   for (y=0; y < (ssize_t) image->rows; y++)
1827   {
1828     register Quantum
1829       *restrict q;
1830
1831     register ssize_t
1832       x;
1833
1834     if (status == MagickFalse)
1835       continue;
1836     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1837     if (q == (const Quantum *) NULL)
1838       {
1839         status=MagickFalse;
1840         continue;
1841       }
1842     for (x=0; x < (ssize_t) image->columns; x++)
1843     {
1844       if (((GetPixelRedTraits(image) & ActivePixelTrait) != 0) &&
1845           (white.red != black.red))
1846         SetPixelRed(image,ClampToQuantum(equalize_map[
1847           ScaleQuantumToMap(GetPixelRed(image,q))].red),q);
1848       if (((GetPixelGreenTraits(image) & ActivePixelTrait) != 0) &&
1849           (white.green != black.green))
1850         SetPixelGreen(image,ClampToQuantum(equalize_map[
1851           ScaleQuantumToMap(GetPixelGreen(image,q))].green),q);
1852       if (((GetPixelBlueTraits(image) & ActivePixelTrait) != 0) &&
1853           (white.blue != black.blue))
1854         SetPixelBlue(image,ClampToQuantum(equalize_map[
1855           ScaleQuantumToMap(GetPixelBlue(image,q))].blue),q);
1856       if ((((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
1857           (image->colorspace == CMYKColorspace)) &&
1858           (white.black != black.black))
1859         SetPixelBlack(image,ClampToQuantum(equalize_map[
1860           ScaleQuantumToMap(GetPixelBlack(image,q))].black),q);
1861       if (((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0) &&
1862           (white.alpha != black.alpha))
1863         SetPixelAlpha(image,ClampToQuantum(equalize_map[
1864           ScaleQuantumToMap(GetPixelAlpha(image,q))].alpha),q);
1865       q+=GetPixelComponents(image);
1866     }
1867     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1868       status=MagickFalse;
1869     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1870       {
1871         MagickBooleanType
1872           proceed;
1873
1874 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1875   #pragma omp critical (MagickCore_EqualizeImage)
1876 #endif
1877         proceed=SetImageProgress(image,EqualizeImageTag,progress++,image->rows);
1878         if (proceed == MagickFalse)
1879           status=MagickFalse;
1880       }
1881   }
1882   image_view=DestroyCacheView(image_view);
1883   equalize_map=(PixelInfo *) RelinquishMagickMemory(equalize_map);
1884   return(status);
1885 }
1886 \f
1887 /*
1888 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1889 %                                                                             %
1890 %                                                                             %
1891 %                                                                             %
1892 %     G a m m a I m a g e                                                     %
1893 %                                                                             %
1894 %                                                                             %
1895 %                                                                             %
1896 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1897 %
1898 %  GammaImage() gamma-corrects a particular image channel.  The same
1899 %  image viewed on different devices will have perceptual differences in the
1900 %  way the image's intensities are represented on the screen.  Specify
1901 %  individual gamma levels for the red, green, and blue channels, or adjust
1902 %  all three with the gamma parameter.  Values typically range from 0.8 to 2.3.
1903 %
1904 %  You can also reduce the influence of a particular channel with a gamma
1905 %  value of 0.
1906 %
1907 %  The format of the GammaImage method is:
1908 %
1909 %      MagickBooleanType GammaImage(Image *image,const double gamma)
1910 %
1911 %  A description of each parameter follows:
1912 %
1913 %    o image: the image.
1914 %
1915 %    o level: the image gamma as a string (e.g. 1.6,1.2,1.0).
1916 %
1917 %    o gamma: the image gamma.
1918 %
1919 */
1920 MagickExport MagickBooleanType GammaImage(Image *image,const double gamma)
1921 {
1922 #define GammaCorrectImageTag  "GammaCorrect/Image"
1923
1924   CacheView
1925     *image_view;
1926
1927   ExceptionInfo
1928     *exception;
1929
1930   MagickBooleanType
1931     status;
1932
1933   MagickOffsetType
1934     progress;
1935
1936   Quantum
1937     *gamma_map;
1938
1939   register ssize_t
1940     i;
1941
1942   ssize_t
1943     y;
1944
1945   /*
1946     Allocate and initialize gamma maps.
1947   */
1948   assert(image != (Image *) NULL);
1949   assert(image->signature == MagickSignature);
1950   if (image->debug != MagickFalse)
1951     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1952   if (gamma == 1.0)
1953     return(MagickTrue);
1954   gamma_map=(Quantum *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*gamma_map));
1955   if (gamma_map == (Quantum *) NULL)
1956     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1957       image->filename);
1958   (void) ResetMagickMemory(gamma_map,0,(MaxMap+1)*sizeof(*gamma_map));
1959   if (gamma != 0.0)
1960 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1961   #pragma omp parallel for schedule(dynamic,4)
1962 #endif
1963     for (i=0; i <= (ssize_t) MaxMap; i++)
1964       gamma_map[i]=ClampToQuantum((MagickRealType) ScaleMapToQuantum((
1965         MagickRealType) (MaxMap*pow((double) i/MaxMap,1.0/gamma))));
1966   if (image->storage_class == PseudoClass)
1967     {
1968       /*
1969         Gamma-correct colormap.
1970       */
1971 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1972   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1973 #endif
1974       for (i=0; i < (ssize_t) image->colors; i++)
1975       {
1976         if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
1977           image->colormap[i].red=gamma_map[
1978             ScaleQuantumToMap(image->colormap[i].red)];
1979         if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
1980           image->colormap[i].green=gamma_map[
1981             ScaleQuantumToMap(image->colormap[i].green)];
1982         if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
1983           image->colormap[i].blue=gamma_map[
1984             ScaleQuantumToMap(image->colormap[i].blue)];
1985         if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0)
1986           image->colormap[i].alpha=gamma_map[
1987             ScaleQuantumToMap(image->colormap[i].alpha)];
1988       }
1989     }
1990   /*
1991     Gamma-correct image.
1992   */
1993   status=MagickTrue;
1994   progress=0;
1995   exception=(&image->exception);
1996   image_view=AcquireCacheView(image);
1997 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1998   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1999 #endif
2000   for (y=0; y < (ssize_t) image->rows; y++)
2001   {
2002     register Quantum
2003       *restrict q;
2004
2005     register ssize_t
2006       x;
2007
2008     if (status == MagickFalse)
2009       continue;
2010     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2011     if (q == (const Quantum *) NULL)
2012       {
2013         status=MagickFalse;
2014         continue;
2015       }
2016     for (x=0; x < (ssize_t) image->columns; x++)
2017     {
2018       if (image->sync != MagickFalse)
2019         {
2020           SetPixelRed(image,gamma_map[ScaleQuantumToMap(
2021             GetPixelRed(image,q))],q);
2022           SetPixelGreen(image,gamma_map[ScaleQuantumToMap(
2023             GetPixelGreen(image,q))],q);
2024           SetPixelBlue(image,gamma_map[ScaleQuantumToMap(
2025             GetPixelBlue(image,q))],q);
2026         }
2027       else
2028         {
2029           if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
2030             SetPixelRed(image,gamma_map[ScaleQuantumToMap(
2031               GetPixelRed(image,q))],q);
2032           if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
2033             SetPixelGreen(image,gamma_map[
2034               ScaleQuantumToMap(GetPixelGreen(image,q))],q);
2035           if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
2036             SetPixelBlue(image,gamma_map[
2037               ScaleQuantumToMap(GetPixelBlue(image,q))],q);
2038           if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0)
2039             {
2040               if (image->matte == MagickFalse)
2041                 SetPixelAlpha(image,gamma_map[
2042                   ScaleQuantumToMap(GetPixelAlpha(image,q))],q);
2043               else
2044                 SetPixelAlpha(image,gamma_map[
2045                   ScaleQuantumToMap(GetPixelAlpha(image,q))],q);
2046             }
2047         }
2048       q+=GetPixelComponents(image);
2049     }
2050     if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
2051         (image->colorspace == CMYKColorspace))
2052       for (x=0; x < (ssize_t) image->columns; x++)
2053         SetPixelBlack(image,gamma_map[ScaleQuantumToMap(
2054           GetPixelBlack(image,q))],q);
2055     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2056       status=MagickFalse;
2057     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2058       {
2059         MagickBooleanType
2060           proceed;
2061
2062 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2063   #pragma omp critical (MagickCore_GammaImage)
2064 #endif
2065         proceed=SetImageProgress(image,GammaCorrectImageTag,progress++,
2066           image->rows);
2067         if (proceed == MagickFalse)
2068           status=MagickFalse;
2069       }
2070   }
2071   image_view=DestroyCacheView(image_view);
2072   gamma_map=(Quantum *) RelinquishMagickMemory(gamma_map);
2073   if (image->gamma != 0.0)
2074     image->gamma*=gamma;
2075   return(status);
2076 }
2077 \f
2078 /*
2079 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2080 %                                                                             %
2081 %                                                                             %
2082 %                                                                             %
2083 %     H a l d C l u t I m a g e                                               %
2084 %                                                                             %
2085 %                                                                             %
2086 %                                                                             %
2087 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2088 %
2089 %  HaldClutImage() applies a Hald color lookup table to the image.  A Hald
2090 %  color lookup table is a 3-dimensional color cube mapped to 2 dimensions.
2091 %  Create it with the HALD coder.  You can apply any color transformation to
2092 %  the Hald image and then use this method to apply the transform to the
2093 %  image.
2094 %
2095 %  The format of the HaldClutImage method is:
2096 %
2097 %      MagickBooleanType HaldClutImage(Image *image,Image *hald_image)
2098 %
2099 %  A description of each parameter follows:
2100 %
2101 %    o image: the image, which is replaced by indexed CLUT values
2102 %
2103 %    o hald_image: the color lookup table image for replacement color values.
2104 %
2105 */
2106
2107 static inline size_t MagickMin(const size_t x,const size_t y)
2108 {
2109   if (x < y)
2110     return(x);
2111   return(y);
2112 }
2113
2114 MagickExport MagickBooleanType HaldClutImage(Image *image,
2115   const Image *hald_image)
2116 {
2117 #define HaldClutImageTag  "Clut/Image"
2118
2119   typedef struct _HaldInfo
2120   {
2121     MagickRealType
2122       x,
2123       y,
2124       z;
2125   } HaldInfo;
2126
2127   CacheView
2128     *hald_view,
2129     *image_view;
2130
2131   double
2132     width;
2133
2134   ExceptionInfo
2135     *exception;
2136
2137   MagickBooleanType
2138     status;
2139
2140   MagickOffsetType
2141     progress;
2142
2143   PixelInfo
2144     zero;
2145
2146   size_t
2147     cube_size,
2148     length,
2149     level;
2150
2151   ssize_t
2152     y;
2153
2154   assert(image != (Image *) NULL);
2155   assert(image->signature == MagickSignature);
2156   if (image->debug != MagickFalse)
2157     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2158   assert(hald_image != (Image *) NULL);
2159   assert(hald_image->signature == MagickSignature);
2160   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
2161     return(MagickFalse);
2162   if (image->matte == MagickFalse)
2163     (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
2164   /*
2165     Hald clut image.
2166   */
2167   status=MagickTrue;
2168   progress=0;
2169   length=MagickMin(hald_image->columns,hald_image->rows);
2170   for (level=2; (level*level*level) < length; level++) ;
2171   level*=level;
2172   cube_size=level*level;
2173   width=(double) hald_image->columns;
2174   GetPixelInfo(hald_image,&zero);
2175   exception=(&image->exception);
2176   image_view=AcquireCacheView(image);
2177   hald_view=AcquireCacheView(hald_image);
2178 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2179   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2180 #endif
2181   for (y=0; y < (ssize_t) image->rows; y++)
2182   {
2183     double
2184       offset;
2185
2186     HaldInfo
2187       point;
2188
2189     PixelInfo
2190       pixel,
2191       pixel1,
2192       pixel2,
2193       pixel3,
2194       pixel4;
2195
2196     register Quantum
2197       *restrict q;
2198
2199     register ssize_t
2200       x;
2201
2202     if (status == MagickFalse)
2203       continue;
2204     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2205     if (q == (const Quantum *) NULL)
2206       {
2207         status=MagickFalse;
2208         continue;
2209       }
2210     pixel=zero;
2211     pixel1=zero;
2212     pixel2=zero;
2213     pixel3=zero;
2214     pixel4=zero;
2215     for (x=0; x < (ssize_t) image->columns; x++)
2216     {
2217       point.x=QuantumScale*(level-1.0)*GetPixelRed(image,q);
2218       point.y=QuantumScale*(level-1.0)*GetPixelGreen(image,q);
2219       point.z=QuantumScale*(level-1.0)*GetPixelBlue(image,q);
2220       offset=point.x+level*floor(point.y)+cube_size*floor(point.z);
2221       point.x-=floor(point.x);
2222       point.y-=floor(point.y);
2223       point.z-=floor(point.z);
2224       (void) InterpolatePixelInfo(image,hald_view,
2225         UndefinedInterpolatePixel,fmod(offset,width),floor(offset/width),
2226         &pixel1,exception);
2227       (void) InterpolatePixelInfo(image,hald_view,
2228         UndefinedInterpolatePixel,fmod(offset+level,width),floor((offset+level)/
2229         width),&pixel2,exception);
2230       CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,
2231         pixel2.alpha,point.y,&pixel3);
2232       offset+=cube_size;
2233       (void) InterpolatePixelInfo(image,hald_view,
2234         UndefinedInterpolatePixel,fmod(offset,width),floor(offset/width),
2235         &pixel1,exception);
2236       (void) InterpolatePixelInfo(image,hald_view,
2237         UndefinedInterpolatePixel,fmod(offset+level,width),floor((offset+level)/
2238         width),&pixel2,exception);
2239       CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,
2240         pixel2.alpha,point.y,&pixel4);
2241       CompositePixelInfoAreaBlend(&pixel3,pixel3.alpha,&pixel4,
2242         pixel4.alpha,point.z,&pixel);
2243       if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
2244         SetPixelRed(image,
2245           ClampToQuantum(pixel.red),q);
2246       if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
2247         SetPixelGreen(image,
2248           ClampToQuantum(pixel.green),q);
2249       if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
2250         SetPixelBlue(image,
2251           ClampToQuantum(pixel.blue),q);
2252       if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
2253           (image->colorspace == CMYKColorspace))
2254         SetPixelBlack(image,
2255           ClampToQuantum(pixel.black),q);
2256       if (((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0) && (image->matte != MagickFalse))
2257         SetPixelAlpha(image,
2258           ClampToQuantum(pixel.alpha),q);
2259       q+=GetPixelComponents(image);
2260     }
2261     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2262       status=MagickFalse;
2263     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2264       {
2265         MagickBooleanType
2266           proceed;
2267
2268 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2269   #pragma omp critical (MagickCore_HaldClutImage)
2270 #endif
2271         proceed=SetImageProgress(image,HaldClutImageTag,progress++,image->rows);
2272         if (proceed == MagickFalse)
2273           status=MagickFalse;
2274       }
2275   }
2276   hald_view=DestroyCacheView(hald_view);
2277   image_view=DestroyCacheView(image_view);
2278   return(status);
2279 }
2280 \f
2281 /*
2282 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2283 %                                                                             %
2284 %                                                                             %
2285 %                                                                             %
2286 %     L e v e l I m a g e                                                     %
2287 %                                                                             %
2288 %                                                                             %
2289 %                                                                             %
2290 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2291 %
2292 %  LevelImage() adjusts the levels of a particular image channel by
2293 %  scaling the colors falling between specified white and black points to
2294 %  the full available quantum range.
2295 %
2296 %  The parameters provided represent the black, and white points.  The black
2297 %  point specifies the darkest color in the image. Colors darker than the
2298 %  black point are set to zero.  White point specifies the lightest color in
2299 %  the image.  Colors brighter than the white point are set to the maximum
2300 %  quantum value.
2301 %
2302 %  If a '!' flag is given, map black and white colors to the given levels
2303 %  rather than mapping those levels to black and white.  See
2304 %  LevelizeImage() below.
2305 %
2306 %  Gamma specifies a gamma correction to apply to the image.
2307 %
2308 %  The format of the LevelImage method is:
2309 %
2310 %      MagickBooleanType LevelImage(Image *image,const char *levels)
2311 %
2312 %  A description of each parameter follows:
2313 %
2314 %    o image: the image.
2315 %
2316 %    o levels: Specify the levels where the black and white points have the
2317 %      range of 0-QuantumRange, and gamma has the range 0-10 (e.g. 10x90%+2).
2318 %      A '!' flag inverts the re-mapping.
2319 %
2320 */
2321 MagickExport MagickBooleanType LevelImage(Image *image,
2322   const double black_point,const double white_point,const double gamma)
2323 {
2324 #define LevelImageTag  "Level/Image"
2325 #define LevelQuantum(x) (ClampToQuantum((MagickRealType) QuantumRange* \
2326   pow(scale*((double) (x)-black_point),1.0/gamma)))
2327
2328   CacheView
2329     *image_view;
2330
2331   ExceptionInfo
2332     *exception;
2333
2334   MagickBooleanType
2335     status;
2336
2337   MagickOffsetType
2338     progress;
2339
2340   register double
2341     scale;
2342
2343   register ssize_t
2344     i;
2345
2346   ssize_t
2347     y;
2348
2349   /*
2350     Allocate and initialize levels map.
2351   */
2352   assert(image != (Image *) NULL);
2353   assert(image->signature == MagickSignature);
2354   if (image->debug != MagickFalse)
2355     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2356   scale=(white_point != black_point) ? 1.0/(white_point-black_point) : 1.0;
2357   if (image->storage_class == PseudoClass)
2358 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2359   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2360 #endif
2361     for (i=0; i < (ssize_t) image->colors; i++)
2362     {
2363       /*
2364         Level colormap.
2365       */
2366       if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
2367         image->colormap[i].red=LevelQuantum(image->colormap[i].red);
2368       if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
2369         image->colormap[i].green=LevelQuantum(image->colormap[i].green);
2370       if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
2371         image->colormap[i].blue=LevelQuantum(image->colormap[i].blue);
2372       if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0)
2373         image->colormap[i].alpha=LevelQuantum(image->colormap[i].alpha);
2374       }
2375   /*
2376     Level image.
2377   */
2378   status=MagickTrue;
2379   progress=0;
2380   exception=(&image->exception);
2381   image_view=AcquireCacheView(image);
2382 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2383   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2384 #endif
2385   for (y=0; y < (ssize_t) image->rows; y++)
2386   {
2387     register Quantum
2388       *restrict q;
2389
2390     register ssize_t
2391       x;
2392
2393     if (status == MagickFalse)
2394       continue;
2395     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2396     if (q == (const Quantum *) NULL)
2397       {
2398         status=MagickFalse;
2399         continue;
2400       }
2401     for (x=0; x < (ssize_t) image->columns; x++)
2402     {
2403       if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
2404         SetPixelRed(image,LevelQuantum(
2405           GetPixelRed(image,q)),q);
2406       if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
2407         SetPixelGreen(image,
2408           LevelQuantum(GetPixelGreen(image,q)),q);
2409       if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
2410         SetPixelBlue(image,
2411           LevelQuantum(GetPixelBlue(image,q)),q);
2412       if (((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0) &&
2413           (image->matte == MagickTrue))
2414         SetPixelAlpha(image,
2415           LevelQuantum(GetPixelAlpha(image,q)),q);
2416       if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
2417           (image->colorspace == CMYKColorspace))
2418         SetPixelBlack(image,
2419           LevelQuantum(GetPixelBlack(image,q)),q);
2420       q+=GetPixelComponents(image);
2421     }
2422     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2423       status=MagickFalse;
2424     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2425       {
2426         MagickBooleanType
2427           proceed;
2428
2429 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2430   #pragma omp critical (MagickCore_LevelImage)
2431 #endif
2432         proceed=SetImageProgress(image,LevelImageTag,progress++,image->rows);
2433         if (proceed == MagickFalse)
2434           status=MagickFalse;
2435       }
2436   }
2437   image_view=DestroyCacheView(image_view);
2438   return(status);
2439 }
2440 \f
2441 /*
2442 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2443 %                                                                             %
2444 %                                                                             %
2445 %                                                                             %
2446 %     L e v e l i z e I m a g e C h a n n e l                                 %
2447 %                                                                             %
2448 %                                                                             %
2449 %                                                                             %
2450 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2451 %
2452 %  LevelizeImage() applies the reversed LevelImage() operation to just
2453 %  the specific channels specified.  It compresses the full range of color
2454 %  values, so that they lie between the given black and white points. Gamma is
2455 %  applied before the values are mapped.
2456 %
2457 %  LevelizeImage() can be called with by using a +level command line
2458 %  API option, or using a '!' on a -level or LevelImage() geometry string.
2459 %
2460 %  It can be used for example de-contrast a greyscale image to the exact
2461 %  levels specified.  Or by using specific levels for each channel of an image
2462 %  you can convert a gray-scale image to any linear color gradient, according
2463 %  to those levels.
2464 %
2465 %  The format of the LevelizeImage method is:
2466 %
2467 %      MagickBooleanType LevelizeImage(Image *image,const double black_point,
2468 %        const double white_point,const double gamma)
2469 %
2470 %  A description of each parameter follows:
2471 %
2472 %    o image: the image.
2473 %
2474 %    o black_point: The level to map zero (black) to.
2475 %
2476 %    o white_point: The level to map QuantiumRange (white) to.
2477 %
2478 %    o gamma: adjust gamma by this factor before mapping values.
2479 %
2480 */
2481 MagickExport MagickBooleanType LevelizeImage(Image *image,
2482   const double black_point,const double white_point,const double gamma)
2483 {
2484 #define LevelizeImageTag  "Levelize/Image"
2485 #define LevelizeValue(x) (ClampToQuantum(((MagickRealType) \
2486   pow((double) (QuantumScale*(x)),1.0/gamma))*(white_point-black_point)+ \
2487   black_point))
2488
2489   CacheView
2490     *image_view;
2491
2492   ExceptionInfo
2493     *exception;
2494
2495   MagickBooleanType
2496     status;
2497
2498   MagickOffsetType
2499     progress;
2500
2501   register ssize_t
2502     i;
2503
2504   ssize_t
2505     y;
2506
2507   /*
2508     Allocate and initialize levels map.
2509   */
2510   assert(image != (Image *) NULL);
2511   assert(image->signature == MagickSignature);
2512   if (image->debug != MagickFalse)
2513     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2514   if (image->storage_class == PseudoClass)
2515 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2516   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2517 #endif
2518     for (i=0; i < (ssize_t) image->colors; i++)
2519     {
2520       /*
2521         Level colormap.
2522       */
2523       if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
2524         image->colormap[i].red=LevelizeValue(image->colormap[i].red);
2525       if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
2526         image->colormap[i].green=LevelizeValue(image->colormap[i].green);
2527       if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
2528         image->colormap[i].blue=LevelizeValue(image->colormap[i].blue);
2529       if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0)
2530         image->colormap[i].alpha=LevelizeValue(image->colormap[i].alpha);
2531     }
2532   /*
2533     Level image.
2534   */
2535   status=MagickTrue;
2536   progress=0;
2537   exception=(&image->exception);
2538   image_view=AcquireCacheView(image);
2539 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2540   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2541 #endif
2542   for (y=0; y < (ssize_t) image->rows; y++)
2543   {
2544     register Quantum
2545       *restrict q;
2546
2547     register ssize_t
2548       x;
2549
2550     if (status == MagickFalse)
2551       continue;
2552     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2553     if (q == (const Quantum *) NULL)
2554       {
2555         status=MagickFalse;
2556         continue;
2557       }
2558     for (x=0; x < (ssize_t) image->columns; x++)
2559     {
2560       if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
2561         SetPixelRed(image,LevelizeValue(GetPixelRed(image,q)),q);
2562       if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
2563         SetPixelGreen(image,LevelizeValue(GetPixelGreen(image,q)),q);
2564       if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
2565         SetPixelBlue(image,LevelizeValue(GetPixelBlue(image,q)),q);
2566       if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
2567           (image->colorspace == CMYKColorspace))
2568         SetPixelBlack(image,LevelizeValue(GetPixelBlack(image,q)),q);
2569       if (((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0) &&
2570           (image->matte == MagickTrue))
2571         SetPixelAlpha(image,LevelizeValue(GetPixelAlpha(image,q)),q);
2572       q+=GetPixelComponents(image);
2573     }
2574     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2575       status=MagickFalse;
2576     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2577       {
2578         MagickBooleanType
2579           proceed;
2580
2581 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2582   #pragma omp critical (MagickCore_LevelizeImage)
2583 #endif
2584         proceed=SetImageProgress(image,LevelizeImageTag,progress++,image->rows);
2585         if (proceed == MagickFalse)
2586           status=MagickFalse;
2587       }
2588   }
2589   image_view=DestroyCacheView(image_view);
2590   return(status);
2591 }
2592 \f
2593 /*
2594 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2595 %                                                                             %
2596 %                                                                             %
2597 %                                                                             %
2598 %     L e v e l I m a g e C o l o r s                                         %
2599 %                                                                             %
2600 %                                                                             %
2601 %                                                                             %
2602 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2603 %
2604 %  LevelImageColors() maps the given color to "black" and "white" values,
2605 %  linearly spreading out the colors, and level values on a channel by channel
2606 %  bases, as per LevelImage().  The given colors allows you to specify
2607 %  different level ranges for each of the color channels separately.
2608 %
2609 %  If the boolean 'invert' is set true the image values will modifyed in the
2610 %  reverse direction. That is any existing "black" and "white" colors in the
2611 %  image will become the color values given, with all other values compressed
2612 %  appropriatally.  This effectivally maps a greyscale gradient into the given
2613 %  color gradient.
2614 %
2615 %  The format of the LevelImageColors method is:
2616 %
2617 %    MagickBooleanType LevelImageColors(Image *image,
2618 %      const PixelInfo *black_color,const PixelInfo *white_color,
2619 %      const MagickBooleanType invert)
2620 %
2621 %  A description of each parameter follows:
2622 %
2623 %    o image: the image.
2624 %
2625 %    o black_color: The color to map black to/from
2626 %
2627 %    o white_point: The color to map white to/from
2628 %
2629 %    o invert: if true map the colors (levelize), rather than from (level)
2630 %
2631 */
2632 MagickExport MagickBooleanType LevelImageColors(Image *image,
2633   const PixelInfo *black_color,const PixelInfo *white_color,
2634   const MagickBooleanType invert)
2635 {
2636   MagickStatusType
2637     status;
2638
2639   /*
2640     Allocate and initialize levels map.
2641   */
2642   assert(image != (Image *) NULL);
2643   assert(image->signature == MagickSignature);
2644   if (image->debug != MagickFalse)
2645     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2646   status=MagickFalse;
2647   if (invert == MagickFalse)
2648     {
2649       if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
2650         {
2651           PushPixelComponentMap(image,RedChannel);
2652           status|=LevelImage(image,black_color->red,white_color->red,1.0);
2653           PopPixelComponentMap(image);
2654         }
2655       if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
2656         {
2657           PushPixelComponentMap(image,GreenChannel);
2658           status|=LevelImage(image,black_color->green,white_color->green,1.0);
2659           PopPixelComponentMap(image);
2660         }
2661       if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
2662         {
2663           PushPixelComponentMap(image,BlueChannel);
2664           status|=LevelImage(image,black_color->blue,white_color->blue,1.0);
2665           PopPixelComponentMap(image);
2666         }
2667       if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
2668           (image->colorspace == CMYKColorspace))
2669         {
2670           PushPixelComponentMap(image,BlackChannel);
2671           status|=LevelImage(image,black_color->black,white_color->black,1.0);
2672           PopPixelComponentMap(image);
2673         }
2674       if (((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0) &&
2675           (image->matte == MagickTrue))
2676         {
2677           PushPixelComponentMap(image,AlphaChannel);
2678           status|=LevelImage(image,black_color->alpha,white_color->alpha,1.0);
2679           PopPixelComponentMap(image);
2680         }
2681     }
2682   else
2683     {
2684       if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
2685         {
2686           PushPixelComponentMap(image,RedChannel);
2687           status|=LevelizeImage(image,black_color->red,white_color->red,1.0);
2688           PopPixelComponentMap(image);
2689         }
2690       if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
2691         {
2692           PushPixelComponentMap(image,GreenChannel);
2693           status|=LevelizeImage(image,black_color->green,white_color->green,
2694             1.0);
2695           PopPixelComponentMap(image);
2696         }
2697       if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
2698         {
2699           PushPixelComponentMap(image,BlueChannel);
2700           status|=LevelizeImage(image,black_color->blue,white_color->blue,1.0);
2701           PopPixelComponentMap(image);
2702         }
2703       if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
2704           (image->colorspace == CMYKColorspace))
2705         {
2706           PushPixelComponentMap(image,BlackChannel);
2707           status|=LevelizeImage(image,black_color->black,white_color->black,
2708             1.0);
2709           PopPixelComponentMap(image);
2710         }
2711       if (((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0) &&
2712           (image->matte == MagickTrue))
2713         {
2714           PushPixelComponentMap(image,AlphaChannel);
2715           status|=LevelizeImage(image,black_color->alpha,white_color->alpha,
2716             1.0);
2717           PopPixelComponentMap(image);
2718         }
2719     }
2720   return(status == 0 ? MagickFalse : MagickTrue);
2721 }
2722 \f
2723 /*
2724 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2725 %                                                                             %
2726 %                                                                             %
2727 %                                                                             %
2728 %     L i n e a r S t r e t c h I m a g e                                     %
2729 %                                                                             %
2730 %                                                                             %
2731 %                                                                             %
2732 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2733 %
2734 %  The LinearStretchImage() discards any pixels below the black point and
2735 %  above the white point and levels the remaining pixels.
2736 %
2737 %  The format of the LinearStretchImage method is:
2738 %
2739 %      MagickBooleanType LinearStretchImage(Image *image,
2740 %        const double black_point,const double white_point)
2741 %
2742 %  A description of each parameter follows:
2743 %
2744 %    o image: the image.
2745 %
2746 %    o black_point: the black point.
2747 %
2748 %    o white_point: the white point.
2749 %
2750 */
2751 MagickExport MagickBooleanType LinearStretchImage(Image *image,
2752   const double black_point,const double white_point)
2753 {
2754 #define LinearStretchImageTag  "LinearStretch/Image"
2755
2756   ExceptionInfo
2757     *exception;
2758
2759   MagickBooleanType
2760     status;
2761
2762   MagickRealType
2763     *histogram,
2764     intensity;
2765
2766   ssize_t
2767     black,
2768     white,
2769     y;
2770
2771   /*
2772     Allocate histogram and linear map.
2773   */
2774   assert(image != (Image *) NULL);
2775   assert(image->signature == MagickSignature);
2776   histogram=(MagickRealType *) AcquireQuantumMemory(MaxMap+1UL,
2777     sizeof(*histogram));
2778   if (histogram == (MagickRealType *) NULL)
2779     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2780       image->filename);
2781   /*
2782     Form histogram.
2783   */
2784   (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
2785   exception=(&image->exception);
2786   for (y=0; y < (ssize_t) image->rows; y++)
2787   {
2788     register const Quantum
2789       *restrict p;
2790
2791     register ssize_t
2792       x;
2793
2794     p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2795     if (p == (const Quantum *) NULL)
2796       break;
2797     for (x=(ssize_t) image->columns-1; x >= 0; x--)
2798     {
2799       histogram[ScaleQuantumToMap(GetPixelIntensity(image,p))]++;
2800       p+=GetPixelComponents(image);
2801     }
2802   }
2803   /*
2804     Find the histogram boundaries by locating the black and white point levels.
2805   */
2806   intensity=0.0;
2807   for (black=0; black < (ssize_t) MaxMap; black++)
2808   {
2809     intensity+=histogram[black];
2810     if (intensity >= black_point)
2811       break;
2812   }
2813   intensity=0.0;
2814   for (white=(ssize_t) MaxMap; white != 0; white--)
2815   {
2816     intensity+=histogram[white];
2817     if (intensity >= white_point)
2818       break;
2819   }
2820   histogram=(MagickRealType *) RelinquishMagickMemory(histogram);
2821   status=LevelImage(image,(double) black,(double) white,1.0);
2822   return(status);
2823 }
2824 \f
2825 /*
2826 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2827 %                                                                             %
2828 %                                                                             %
2829 %                                                                             %
2830 %     M o d u l a t e I m a g e                                               %
2831 %                                                                             %
2832 %                                                                             %
2833 %                                                                             %
2834 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2835 %
2836 %  ModulateImage() lets you control the brightness, saturation, and hue
2837 %  of an image.  Modulate represents the brightness, saturation, and hue
2838 %  as one parameter (e.g. 90,150,100).  If the image colorspace is HSL, the
2839 %  modulation is lightness, saturation, and hue.  And if the colorspace is
2840 %  HWB, use blackness, whiteness, and hue.
2841 %
2842 %  The format of the ModulateImage method is:
2843 %
2844 %      MagickBooleanType ModulateImage(Image *image,const char *modulate)
2845 %
2846 %  A description of each parameter follows:
2847 %
2848 %    o image: the image.
2849 %
2850 %    o modulate: Define the percent change in brightness, saturation, and
2851 %      hue.
2852 %
2853 */
2854
2855 static void ModulateHSB(const double percent_hue,
2856   const double percent_saturation,const double percent_brightness,
2857   Quantum *red,Quantum *green,Quantum *blue)
2858 {
2859   double
2860     brightness,
2861     hue,
2862     saturation;
2863
2864   /*
2865     Increase or decrease color brightness, saturation, or hue.
2866   */
2867   assert(red != (Quantum *) NULL);
2868   assert(green != (Quantum *) NULL);
2869   assert(blue != (Quantum *) NULL);
2870   ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
2871   hue+=0.5*(0.01*percent_hue-1.0);
2872   while (hue < 0.0)
2873     hue+=1.0;
2874   while (hue > 1.0)
2875     hue-=1.0;
2876   saturation*=0.01*percent_saturation;
2877   brightness*=0.01*percent_brightness;
2878   ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
2879 }
2880
2881 static void ModulateHSL(const double percent_hue,
2882   const double percent_saturation,const double percent_lightness,
2883   Quantum *red,Quantum *green,Quantum *blue)
2884 {
2885   double
2886     hue,
2887     lightness,
2888     saturation;
2889
2890   /*
2891     Increase or decrease color lightness, saturation, or hue.
2892   */
2893   assert(red != (Quantum *) NULL);
2894   assert(green != (Quantum *) NULL);
2895   assert(blue != (Quantum *) NULL);
2896   ConvertRGBToHSL(*red,*green,*blue,&hue,&saturation,&lightness);
2897   hue+=0.5*(0.01*percent_hue-1.0);
2898   while (hue < 0.0)
2899     hue+=1.0;
2900   while (hue > 1.0)
2901     hue-=1.0;
2902   saturation*=0.01*percent_saturation;
2903   lightness*=0.01*percent_lightness;
2904   ConvertHSLToRGB(hue,saturation,lightness,red,green,blue);
2905 }
2906
2907 static void ModulateHWB(const double percent_hue,const double percent_whiteness,  const double percent_blackness,Quantum *red,Quantum *green,Quantum *blue)
2908 {
2909   double
2910     blackness,
2911     hue,
2912     whiteness;
2913
2914   /*
2915     Increase or decrease color blackness, whiteness, or hue.
2916   */
2917   assert(red != (Quantum *) NULL);
2918   assert(green != (Quantum *) NULL);
2919   assert(blue != (Quantum *) NULL);
2920   ConvertRGBToHWB(*red,*green,*blue,&hue,&whiteness,&blackness);
2921   hue+=0.5*(0.01*percent_hue-1.0);
2922   while (hue < 0.0)
2923     hue+=1.0;
2924   while (hue > 1.0)
2925     hue-=1.0;
2926   blackness*=0.01*percent_blackness;
2927   whiteness*=0.01*percent_whiteness;
2928   ConvertHWBToRGB(hue,whiteness,blackness,red,green,blue);
2929 }
2930
2931 MagickExport MagickBooleanType ModulateImage(Image *image,const char *modulate)
2932 {
2933 #define ModulateImageTag  "Modulate/Image"
2934
2935   CacheView
2936     *image_view;
2937
2938   ColorspaceType
2939     colorspace;
2940
2941   const char
2942     *artifact;
2943
2944   double
2945     percent_brightness,
2946     percent_hue,
2947     percent_saturation;
2948
2949   ExceptionInfo
2950     *exception;
2951
2952   GeometryInfo
2953     geometry_info;
2954
2955   MagickBooleanType
2956     status;
2957
2958   MagickOffsetType
2959     progress;
2960
2961   MagickStatusType
2962     flags;
2963
2964   register ssize_t
2965     i;
2966
2967   ssize_t
2968     y;
2969
2970   /*
2971     Initialize modulate table.
2972   */
2973   assert(image != (Image *) NULL);
2974   assert(image->signature == MagickSignature);
2975   if (image->debug != MagickFalse)
2976     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2977   if (modulate == (char *) NULL)
2978     return(MagickFalse);
2979   flags=ParseGeometry(modulate,&geometry_info);
2980   percent_brightness=geometry_info.rho;
2981   percent_saturation=geometry_info.sigma;
2982   if ((flags & SigmaValue) == 0)
2983     percent_saturation=100.0;
2984   percent_hue=geometry_info.xi;
2985   if ((flags & XiValue) == 0)
2986     percent_hue=100.0;
2987   colorspace=UndefinedColorspace;
2988   artifact=GetImageArtifact(image,"modulate:colorspace");
2989   if (artifact != (const char *) NULL)
2990     colorspace=(ColorspaceType) ParseCommandOption(MagickColorspaceOptions,
2991       MagickFalse,artifact);
2992   if (image->storage_class == PseudoClass)
2993     {
2994       /*
2995         Modulate colormap.
2996       */
2997 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2998   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2999 #endif
3000       for (i=0; i < (ssize_t) image->colors; i++)
3001         switch (colorspace)
3002         {
3003           case HSBColorspace:
3004           {
3005             ModulateHSB(percent_hue,percent_saturation,percent_brightness,
3006               &image->colormap[i].red,&image->colormap[i].green,
3007               &image->colormap[i].blue);
3008             break;
3009           }
3010           case HSLColorspace:
3011           default:
3012           {
3013             ModulateHSL(percent_hue,percent_saturation,percent_brightness,
3014               &image->colormap[i].red,&image->colormap[i].green,
3015               &image->colormap[i].blue);
3016             break;
3017           }
3018           case HWBColorspace:
3019           {
3020             ModulateHWB(percent_hue,percent_saturation,percent_brightness,
3021               &image->colormap[i].red,&image->colormap[i].green,
3022               &image->colormap[i].blue);
3023             break;
3024           }
3025         }
3026     }
3027   /*
3028     Modulate image.
3029   */
3030   status=MagickTrue;
3031   progress=0;
3032   exception=(&image->exception);
3033   image_view=AcquireCacheView(image);
3034 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3035   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3036 #endif
3037   for (y=0; y < (ssize_t) image->rows; y++)
3038   {
3039     Quantum
3040       blue,
3041       green,
3042       red;
3043
3044     register Quantum
3045       *restrict q;
3046
3047     register ssize_t
3048       x;
3049
3050     if (status == MagickFalse)
3051       continue;
3052     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3053     if (q == (const Quantum *) NULL)
3054       {
3055         status=MagickFalse;
3056         continue;
3057       }
3058     for (x=0; x < (ssize_t) image->columns; x++)
3059     {
3060       red=GetPixelRed(image,q);
3061       green=GetPixelGreen(image,q);
3062       blue=GetPixelBlue(image,q);
3063       switch (colorspace)
3064       {
3065         case HSBColorspace:
3066         {
3067           ModulateHSB(percent_hue,percent_saturation,percent_brightness,
3068             &red,&green,&blue);
3069           break;
3070         }
3071         case HSLColorspace:
3072         default:
3073         {
3074           ModulateHSL(percent_hue,percent_saturation,percent_brightness,
3075             &red,&green,&blue);
3076           break;
3077         }
3078         case HWBColorspace:
3079         {
3080           ModulateHWB(percent_hue,percent_saturation,percent_brightness,
3081             &red,&green,&blue);
3082           break;
3083         }
3084       }
3085       SetPixelRed(image,red,q);
3086       SetPixelGreen(image,green,q);
3087       SetPixelBlue(image,blue,q);
3088       q+=GetPixelComponents(image);
3089     }
3090     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3091       status=MagickFalse;
3092     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3093       {
3094         MagickBooleanType
3095           proceed;
3096
3097 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3098   #pragma omp critical (MagickCore_ModulateImage)
3099 #endif
3100         proceed=SetImageProgress(image,ModulateImageTag,progress++,image->rows);
3101         if (proceed == MagickFalse)
3102           status=MagickFalse;
3103       }
3104   }
3105   image_view=DestroyCacheView(image_view);
3106   return(status);
3107 }
3108 \f
3109 /*
3110 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3111 %                                                                             %
3112 %                                                                             %
3113 %                                                                             %
3114 %     N e g a t e I m a g e                                                   %
3115 %                                                                             %
3116 %                                                                             %
3117 %                                                                             %
3118 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3119 %
3120 %  NegateImage() negates the colors in the reference image.  The grayscale
3121 %  option means that only grayscale values within the image are negated.
3122 %
3123 %  The format of the NegateImage method is:
3124 %
3125 %      MagickBooleanType NegateImage(Image *image,
3126 %        const MagickBooleanType grayscale)
3127 %
3128 %  A description of each parameter follows:
3129 %
3130 %    o image: the image.
3131 %
3132 %    o grayscale: If MagickTrue, only negate grayscale pixels within the image.
3133 %
3134 */
3135 MagickExport MagickBooleanType NegateImage(Image *image,
3136   const MagickBooleanType grayscale)
3137 {
3138 #define NegateImageTag  "Negate/Image"
3139
3140   CacheView
3141     *image_view;
3142
3143   ExceptionInfo
3144     *exception;
3145
3146   MagickBooleanType
3147     status;
3148
3149   MagickOffsetType
3150     progress;
3151
3152   register ssize_t
3153     i;
3154
3155   ssize_t
3156     y;
3157
3158   assert(image != (Image *) NULL);
3159   assert(image->signature == MagickSignature);
3160   if (image->debug != MagickFalse)
3161     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3162   if (image->storage_class == PseudoClass)
3163     {
3164       /*
3165         Negate colormap.
3166       */
3167 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3168   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3169 #endif
3170       for (i=0; i < (ssize_t) image->colors; i++)
3171       {
3172         if (grayscale != MagickFalse)
3173           if ((image->colormap[i].red != image->colormap[i].green) ||
3174               (image->colormap[i].green != image->colormap[i].blue))
3175             continue;
3176         if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
3177           image->colormap[i].red=(Quantum) QuantumRange-
3178             image->colormap[i].red;
3179         if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
3180           image->colormap[i].green=(Quantum) QuantumRange-
3181             image->colormap[i].green;
3182         if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
3183           image->colormap[i].blue=(Quantum) QuantumRange-
3184             image->colormap[i].blue;
3185       }
3186     }
3187   /*
3188     Negate image.
3189   */
3190   status=MagickTrue;
3191   progress=0;
3192   exception=(&image->exception);
3193   image_view=AcquireCacheView(image);
3194   if (grayscale != MagickFalse)
3195     {
3196 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3197   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3198 #endif
3199       for (y=0; y < (ssize_t) image->rows; y++)
3200       {
3201         MagickBooleanType
3202           sync;
3203
3204         register Quantum
3205           *restrict q;
3206
3207         register ssize_t
3208           x;
3209
3210         if (status == MagickFalse)
3211           continue;
3212         q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3213           exception);
3214         if (q == (const Quantum *) NULL)
3215           {
3216             status=MagickFalse;
3217             continue;
3218           }
3219         for (x=0; x < (ssize_t) image->columns; x++)
3220         {
3221           if ((GetPixelRed(image,q) != GetPixelGreen(image,q)) ||
3222               (GetPixelGreen(image,q) != GetPixelBlue(image,q)))
3223             {
3224               q+=GetPixelComponents(image);
3225               continue;
3226             }
3227           if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
3228             SetPixelRed(image,QuantumRange-GetPixelRed(image,q),q);
3229           if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
3230             SetPixelGreen(image,QuantumRange-GetPixelGreen(image,q),q);
3231           if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
3232             SetPixelBlue(image,QuantumRange-GetPixelBlue(image,q),q);
3233           if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
3234               (image->colorspace == CMYKColorspace))
3235             SetPixelBlack(image,QuantumRange-GetPixelBlack(image,q),q);
3236           if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0)
3237             SetPixelAlpha(image,QuantumRange-GetPixelAlpha(image,q),q);
3238           q+=GetPixelComponents(image);
3239         }
3240         sync=SyncCacheViewAuthenticPixels(image_view,exception);
3241         if (sync == MagickFalse)
3242           status=MagickFalse;
3243         if (image->progress_monitor != (MagickProgressMonitor) NULL)
3244           {
3245             MagickBooleanType
3246               proceed;
3247
3248 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3249   #pragma omp critical (MagickCore_NegateImage)
3250 #endif
3251             proceed=SetImageProgress(image,NegateImageTag,progress++,
3252               image->rows);
3253             if (proceed == MagickFalse)
3254               status=MagickFalse;
3255           }
3256       }
3257       image_view=DestroyCacheView(image_view);
3258       return(MagickTrue);
3259     }
3260   /*
3261     Negate image.
3262   */
3263 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3264   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3265 #endif
3266   for (y=0; y < (ssize_t) image->rows; y++)
3267   {
3268     register Quantum
3269       *restrict q;
3270
3271     register ssize_t
3272       x;
3273
3274     if (status == MagickFalse)
3275       continue;
3276     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3277     if (q == (const Quantum *) NULL)
3278       {
3279         status=MagickFalse;
3280         continue;
3281       }
3282     for (x=0; x < (ssize_t) image->columns; x++)
3283     {
3284       if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
3285         SetPixelRed(image,QuantumRange-GetPixelRed(image,q),q);
3286       if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
3287         SetPixelGreen(image,QuantumRange-GetPixelGreen(image,q),q);
3288       if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
3289         SetPixelBlue(image,QuantumRange-GetPixelBlue(image,q),q);
3290       if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
3291           (image->colorspace == CMYKColorspace))
3292         SetPixelBlack(image,QuantumRange-GetPixelBlack(image,q),q);
3293       if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0)
3294         SetPixelAlpha(image,QuantumRange-GetPixelAlpha(image,q),q);
3295       q+=GetPixelComponents(image);
3296     }
3297     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3298       status=MagickFalse;
3299     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3300       {
3301         MagickBooleanType
3302           proceed;
3303
3304 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3305   #pragma omp critical (MagickCore_NegateImage)
3306 #endif
3307         proceed=SetImageProgress(image,NegateImageTag,progress++,image->rows);
3308         if (proceed == MagickFalse)
3309           status=MagickFalse;
3310       }
3311   }
3312   image_view=DestroyCacheView(image_view);
3313   return(status);
3314 }
3315 \f
3316 /*
3317 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3318 %                                                                             %
3319 %                                                                             %
3320 %                                                                             %
3321 %     N o r m a l i z e I m a g e                                             %
3322 %                                                                             %
3323 %                                                                             %
3324 %                                                                             %
3325 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3326 %
3327 %  NormalizeImage() enhances the contrast of a color image by mapping the
3328 %  darkest 2 percent of all pixel to black and the brightest 1 percent to white.
3329 %
3330 %  The format of the NormalizeImage method is:
3331 %
3332 %      MagickBooleanType NormalizeImage(Image *image)
3333 %
3334 %  A description of each parameter follows:
3335 %
3336 %    o image: the image.
3337 %
3338 */
3339 MagickExport MagickBooleanType NormalizeImage(Image *image)
3340 {
3341   double
3342     black_point,
3343     white_point;
3344
3345   black_point=(double) image->columns*image->rows*0.0015;
3346   white_point=(double) image->columns*image->rows*0.9995;
3347   return(ContrastStretchImage(image,black_point,white_point));
3348 }
3349 \f
3350 /*
3351 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3352 %                                                                             %
3353 %                                                                             %
3354 %                                                                             %
3355 %     S i g m o i d a l C o n t r a s t I m a g e                             %
3356 %                                                                             %
3357 %                                                                             %
3358 %                                                                             %
3359 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3360 %
3361 %  SigmoidalContrastImage() adjusts the contrast of an image with a non-linear
3362 %  sigmoidal contrast algorithm.  Increase the contrast of the image using a
3363 %  sigmoidal transfer function without saturating highlights or shadows.
3364 %  Contrast indicates how much to increase the contrast (0 is none; 3 is
3365 %  typical; 20 is pushing it); mid-point indicates where midtones fall in the
3366 %  resultant image (0 is white; 50% is middle-gray; 100% is black).  Set
3367 %  sharpen to MagickTrue to increase the image contrast otherwise the contrast
3368 %  is reduced.
3369 %
3370 %  The format of the SigmoidalContrastImage method is:
3371 %
3372 %      MagickBooleanType SigmoidalContrastImage(Image *image,
3373 %        const MagickBooleanType sharpen,const char *levels)
3374 %
3375 %  A description of each parameter follows:
3376 %
3377 %    o image: the image.
3378 %
3379 %    o sharpen: Increase or decrease image contrast.
3380 %
3381 %    o alpha: strength of the contrast, the larger the number the more
3382 %      'threshold-like' it becomes.
3383 %
3384 %    o beta: midpoint of the function as a color value 0 to QuantumRange.
3385 %
3386 */
3387 MagickExport MagickBooleanType SigmoidalContrastImage(Image *image,
3388   const MagickBooleanType sharpen,const double contrast,const double midpoint)
3389 {
3390 #define SigmoidalContrastImageTag  "SigmoidalContrast/Image"
3391
3392   CacheView
3393     *image_view;
3394
3395   ExceptionInfo
3396     *exception;
3397
3398   MagickBooleanType
3399     status;
3400
3401   MagickOffsetType
3402     progress;
3403
3404   MagickRealType
3405     *sigmoidal_map;
3406
3407   register ssize_t
3408     i;
3409
3410   ssize_t
3411     y;
3412
3413   /*
3414     Allocate and initialize sigmoidal maps.
3415   */
3416   assert(image != (Image *) NULL);
3417   assert(image->signature == MagickSignature);
3418   if (image->debug != MagickFalse)
3419     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3420   sigmoidal_map=(MagickRealType *) AcquireQuantumMemory(MaxMap+1UL,
3421     sizeof(*sigmoidal_map));
3422   if (sigmoidal_map == (MagickRealType *) NULL)
3423     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
3424       image->filename);
3425   (void) ResetMagickMemory(sigmoidal_map,0,(MaxMap+1)*sizeof(*sigmoidal_map));
3426 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3427   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3428 #endif
3429   for (i=0; i <= (ssize_t) MaxMap; i++)
3430   {
3431     if (sharpen != MagickFalse)
3432       {
3433         sigmoidal_map[i]=(MagickRealType) ScaleMapToQuantum((MagickRealType)
3434           (MaxMap*((1.0/(1.0+exp(contrast*(midpoint/(double) QuantumRange-
3435           (double) i/MaxMap))))-(1.0/(1.0+exp(contrast*(midpoint/
3436           (double) QuantumRange)))))/((1.0/(1.0+exp(contrast*(midpoint/
3437           (double) QuantumRange-1.0))))-(1.0/(1.0+exp(contrast*(midpoint/
3438           (double) QuantumRange)))))+0.5));
3439         continue;
3440       }
3441     sigmoidal_map[i]=(MagickRealType) ScaleMapToQuantum((MagickRealType)
3442       (MaxMap*(QuantumScale*midpoint-log((1.0-(1.0/(1.0+exp(midpoint/
3443       (double) QuantumRange*contrast))+((double) i/MaxMap)*((1.0/
3444       (1.0+exp(contrast*(midpoint/(double) QuantumRange-1.0))))-(1.0/
3445       (1.0+exp(midpoint/(double) QuantumRange*contrast))))))/
3446       (1.0/(1.0+exp(midpoint/(double) QuantumRange*contrast))+
3447       ((double) i/MaxMap)*((1.0/(1.0+exp(contrast*(midpoint/
3448       (double) QuantumRange-1.0))))-(1.0/(1.0+exp(midpoint/
3449       (double) QuantumRange*contrast))))))/contrast)));
3450   }
3451   if (image->storage_class == PseudoClass)
3452     {
3453       /*
3454         Sigmoidal-contrast enhance colormap.
3455       */
3456 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3457   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3458 #endif
3459       for (i=0; i < (ssize_t) image->colors; i++)
3460       {
3461         if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
3462           image->colormap[i].red=ClampToQuantum(sigmoidal_map[
3463             ScaleQuantumToMap(image->colormap[i].red)]);
3464         if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
3465           image->colormap[i].green=ClampToQuantum(sigmoidal_map[
3466             ScaleQuantumToMap(image->colormap[i].green)]);
3467         if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
3468           image->colormap[i].blue=ClampToQuantum(sigmoidal_map[
3469             ScaleQuantumToMap(image->colormap[i].blue)]);
3470         if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0)
3471           image->colormap[i].alpha=ClampToQuantum(sigmoidal_map[
3472             ScaleQuantumToMap(image->colormap[i].alpha)]);
3473       }
3474     }
3475   /*
3476     Sigmoidal-contrast enhance image.
3477   */
3478   status=MagickTrue;
3479   progress=0;
3480   exception=(&image->exception);
3481   image_view=AcquireCacheView(image);
3482 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3483   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3484 #endif
3485   for (y=0; y < (ssize_t) image->rows; y++)
3486   {
3487     register Quantum
3488       *restrict q;
3489
3490     register ssize_t
3491       x;
3492
3493     if (status == MagickFalse)
3494       continue;
3495     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3496     if (q == (const Quantum *) NULL)
3497       {
3498         status=MagickFalse;
3499         continue;
3500       }
3501     for (x=0; x < (ssize_t) image->columns; x++)
3502     {
3503       if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
3504         SetPixelRed(image,ClampToQuantum(sigmoidal_map[ScaleQuantumToMap(
3505           GetPixelRed(image,q))]),q);
3506       if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
3507         SetPixelGreen(image,ClampToQuantum(sigmoidal_map[ScaleQuantumToMap(
3508           GetPixelGreen(image,q))]),q);
3509       if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
3510         SetPixelBlue(image,ClampToQuantum(sigmoidal_map[ScaleQuantumToMap(
3511           GetPixelBlue(image,q))]),q);
3512       if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
3513           (image->colorspace == CMYKColorspace))
3514         SetPixelBlack(image,ClampToQuantum(sigmoidal_map[ScaleQuantumToMap(
3515           GetPixelBlack(image,q))]),q);
3516       if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0)
3517         SetPixelAlpha(image,ClampToQuantum(sigmoidal_map[ScaleQuantumToMap(
3518           GetPixelAlpha(image,q))]),q);
3519       q+=GetPixelComponents(image);
3520     }
3521     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3522       status=MagickFalse;
3523     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3524       {
3525         MagickBooleanType
3526           proceed;
3527
3528 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3529   #pragma omp critical (MagickCore_SigmoidalContrastImage)
3530 #endif
3531         proceed=SetImageProgress(image,SigmoidalContrastImageTag,progress++,
3532           image->rows);
3533         if (proceed == MagickFalse)
3534           status=MagickFalse;
3535       }
3536   }
3537   image_view=DestroyCacheView(image_view);
3538   sigmoidal_map=(MagickRealType *) RelinquishMagickMemory(sigmoidal_map);
3539   return(status);
3540 }