blob: eeab38c770490bcdd10be5592d31bc61fc52e740 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% PPPP AAA IIIII N N TTTTT %
7% P P A A I NN N T %
8% PPPP AAAAA I N N N T %
9% P A A I N NN T %
10% P A A IIIII N N T %
11% %
12% %
13% Methods to Paint on an Image %
14% %
15% Software Design %
16% John Cristy %
17% July 1998 %
18% %
19% %
cristy16af1cb2009-12-11 21:38:29 +000020% Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% 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/*
40 Include declarations.
41*/
42#include "magick/studio.h"
43#include "magick/color.h"
44#include "magick/color-private.h"
45#include "magick/colorspace-private.h"
46#include "magick/composite.h"
47#include "magick/composite-private.h"
48#include "magick/draw.h"
49#include "magick/draw-private.h"
50#include "magick/exception.h"
51#include "magick/exception-private.h"
52#include "magick/gem.h"
53#include "magick/monitor.h"
54#include "magick/monitor-private.h"
55#include "magick/paint.h"
56#include "magick/pixel-private.h"
57#include "magick/string_.h"
58#include "magick/thread-private.h"
59
60static inline double MagickMax(const double x,const double y)
61{
62 return( x > y ? x : y);
63}
64/*
65%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
66% %
67% %
68% %
69% F l o o d f i l l P a i n t I m a g e %
70% %
71% %
72% %
73%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
74%
75% FloodfillPaintImage() changes the color value of any pixel that matches
76% target and is an immediate neighbor. If the method FillToBorderMethod is
77% specified, the color value is changed for any neighbor pixel that does not
78% match the bordercolor member of image.
79%
80% By default target must match a particular pixel color exactly.
81% However, in many cases two colors may differ by a small amount. The
82% fuzz member of image defines how much tolerance is acceptable to
83% consider two colors as the same. For example, set fuzz to 10 and the
84% color red at intensities of 100 and 102 respectively are now
85% interpreted as the same color for the purposes of the floodfill.
86%
87% The format of the FloodfillPaintImage method is:
88%
89% MagickBooleanType FloodfillPaintImage(Image *image,
90% const ChannelType channel,const DrawInfo *draw_info,
91% const MagickPixelPacket target,const long x_offset,const long y_offset,
92% const MagickBooleanType invert)
93%
94% A description of each parameter follows:
95%
96% o image: the image.
97%
98% o channel: the channel(s).
99%
100% o draw_info: the draw info.
101%
102% o target: the RGB value of the target color.
103%
104% o x_offset,y_offset: the starting location of the operation.
105%
106% o invert: paint any pixel that does not match the target color.
107%
108*/
109MagickExport MagickBooleanType FloodfillPaintImage(Image *image,
110 const ChannelType channel,const DrawInfo *draw_info,
111 const MagickPixelPacket *target,const long x_offset,const long y_offset,
112 const MagickBooleanType invert)
113{
114#define MaxStacksize (1UL << 15)
115#define PushSegmentStack(up,left,right,delta) \
116{ \
117 if (s >= (segment_stack+MaxStacksize)) \
118 ThrowBinaryException(DrawError,"SegmentStackOverflow",image->filename) \
119 else \
120 { \
121 if ((((up)+(delta)) >= 0) && (((up)+(delta)) < (long) image->rows)) \
122 { \
123 s->x1=(double) (left); \
124 s->y1=(double) (up); \
125 s->x2=(double) (right); \
126 s->y2=(double) (delta); \
127 s++; \
128 } \
129 } \
130}
131
132 ExceptionInfo
133 *exception;
134
135 Image
136 *floodplane_image;
137
138 long
139 offset,
140 start,
141 x,
142 x1,
143 x2,
144 y;
145
146 MagickBooleanType
147 skip;
148
149 MagickPixelPacket
150 fill,
151 pixel;
152
153 PixelPacket
154 fill_color;
155
156 register SegmentInfo
157 *s;
158
159 SegmentInfo
160 *segment_stack;
161
162 /*
163 Check boundary conditions.
164 */
165 assert(image != (Image *) NULL);
166 assert(image->signature == MagickSignature);
167 if (image->debug != MagickFalse)
168 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
169 assert(draw_info != (DrawInfo *) NULL);
170 assert(draw_info->signature == MagickSignature);
171 if ((x_offset < 0) || (x_offset >= (long) image->columns))
172 return(MagickFalse);
173 if ((y_offset < 0) || (y_offset >= (long) image->rows))
174 return(MagickFalse);
175 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
176 return(MagickFalse);
177 if (image->matte == MagickFalse)
178 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
179 /*
180 Set floodfill state.
181 */
182 floodplane_image=CloneImage(image,0,0,MagickTrue,&image->exception);
183 if (floodplane_image == (Image *) NULL)
184 return(MagickFalse);
185 (void) SetImageAlphaChannel(floodplane_image,OpaqueAlphaChannel);
186 segment_stack=(SegmentInfo *) AcquireQuantumMemory(MaxStacksize,
187 sizeof(*segment_stack));
188 if (segment_stack == (SegmentInfo *) NULL)
189 {
190 floodplane_image=DestroyImage(floodplane_image);
191 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
192 image->filename);
193 }
194 /*
195 Push initial segment on stack.
196 */
197 exception=(&image->exception);
198 x=x_offset;
199 y=y_offset;
200 start=0;
201 s=segment_stack;
202 PushSegmentStack(y,x,x,1);
203 PushSegmentStack(y+1,x,x,-1);
204 GetMagickPixelPacket(image,&fill);
205 GetMagickPixelPacket(image,&pixel);
206 while (s > segment_stack)
207 {
208 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000209 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000210
211 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000212 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000213
214 register long
215 x;
216
217 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000218 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000219
220 /*
221 Pop segment off stack.
222 */
223 s--;
224 x1=(long) s->x1;
225 x2=(long) s->x2;
226 offset=(long) s->y2;
227 y=(long) s->y1+offset;
228 /*
229 Recolor neighboring pixels.
230 */
231 p=GetVirtualPixels(image,0,y,(unsigned long) (x1+1),1,exception);
232 q=GetAuthenticPixels(floodplane_image,0,y,(unsigned long) (x1+1),1,
233 exception);
234 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
235 break;
236 indexes=GetVirtualIndexQueue(image);
237 p+=x1;
238 q+=x1;
239 for (x=x1; x >= 0; x--)
240 {
241 if (q->opacity == (Quantum) TransparentOpacity)
242 break;
243 SetMagickPixelPacket(image,p,indexes+x,&pixel);
244 if (IsMagickColorSimilar(&pixel,target) == invert)
245 break;
246 q->opacity=(Quantum) TransparentOpacity;
247 p--;
248 q--;
249 }
250 if (SyncAuthenticPixels(floodplane_image,exception) == MagickFalse)
251 break;
252 skip=x >= x1 ? MagickTrue : MagickFalse;
253 if (skip == MagickFalse)
254 {
255 start=x+1;
256 if (start < x1)
257 PushSegmentStack(y,start,x1-1,-offset);
258 x=x1+1;
259 }
260 do
261 {
262 if (skip == MagickFalse)
263 {
264 if (x < (long) image->columns)
265 {
266 p=GetVirtualPixels(image,x,y,image->columns-x,1,exception);
267 q=GetAuthenticPixels(floodplane_image,x,y,image->columns-x,1,
268 exception);
269 if ((p == (const PixelPacket *) NULL) ||
270 (q == (PixelPacket *) NULL))
271 break;
272 indexes=GetVirtualIndexQueue(image);
273 for ( ; x < (long) image->columns; x++)
274 {
275 if (q->opacity == (Quantum) TransparentOpacity)
276 break;
277 SetMagickPixelPacket(image,p,indexes+x,&pixel);
278 if (IsMagickColorSimilar(&pixel,target) == invert)
279 break;
280 q->opacity=(Quantum) TransparentOpacity;
281 p++;
282 q++;
283 }
284 if (SyncAuthenticPixels(floodplane_image,exception) == MagickFalse)
285 break;
286 }
287 PushSegmentStack(y,start,x-1,offset);
288 if (x > (x2+1))
289 PushSegmentStack(y,x2+1,x-1,-offset);
290 }
291 skip=MagickFalse;
292 x++;
293 if (x <= x2)
294 {
295 p=GetVirtualPixels(image,x,y,(unsigned long) (x2-x+1),1,exception);
296 q=GetAuthenticPixels(floodplane_image,x,y,(unsigned long) (x2-x+1),1,
297 exception);
298 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
299 break;
300 indexes=GetVirtualIndexQueue(image);
301 for ( ; x <= x2; x++)
302 {
303 if (q->opacity == (Quantum) TransparentOpacity)
304 break;
305 SetMagickPixelPacket(image,p,indexes+x,&pixel);
306 if (IsMagickColorSimilar(&pixel,target) != invert)
307 break;
308 p++;
309 q++;
310 }
311 }
312 start=x;
313 } while (x <= x2);
314 }
315 for (y=0; y < (long) image->rows; y++)
316 {
317 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000318 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000319
320 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000321 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000322
323 register long
324 x;
325
326 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000327 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000328
329 /*
330 Tile fill color onto floodplane.
331 */
332 p=GetVirtualPixels(floodplane_image,0,y,image->columns,1,exception);
333 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
334 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
335 break;
336 indexes=GetAuthenticIndexQueue(image);
337 for (x=0; x < (long) image->columns; x++)
338 {
339 if (p->opacity != OpaqueOpacity)
340 {
341 (void) GetFillColor(draw_info,x,y,&fill_color);
342 SetMagickPixelPacket(image,&fill_color,(IndexPacket *) NULL,&fill);
343 if (image->colorspace == CMYKColorspace)
344 ConvertRGBToCMYK(&fill);
345 if ((channel & RedChannel) != 0)
346 q->red=RoundToQuantum(fill.red);
347 if ((channel & GreenChannel) != 0)
348 q->green=RoundToQuantum(fill.green);
349 if ((channel & BlueChannel) != 0)
350 q->blue=RoundToQuantum(fill.blue);
351 if ((channel & OpacityChannel) != 0)
352 q->opacity=RoundToQuantum(fill.opacity);
353 if (((channel & IndexChannel) != 0) &&
354 (image->colorspace == CMYKColorspace))
355 indexes[x]=RoundToQuantum(fill.index);
356 }
357 p++;
358 q++;
359 }
360 if (SyncAuthenticPixels(image,exception) == MagickFalse)
361 break;
362 }
363 segment_stack=(SegmentInfo *) RelinquishMagickMemory(segment_stack);
364 floodplane_image=DestroyImage(floodplane_image);
365 return(y == (long) image->rows ? MagickTrue : MagickFalse);
366}
367
368/*
369%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
370% %
371% %
372% %
373+ G r a d i e n t I m a g e %
374% %
375% %
376% %
377%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
378%
379% GradientImage() applies a continuously smooth color transitions along a
380% vector from one color to another.
381%
382% Note, the interface of this method will change in the future to support
383% more than one transistion.
384%
385% The format of the GradientImage method is:
386%
387% MagickBooleanType GradientImage(Image *image,const GradientType type,
388% const SpreadMethod method,const PixelPacket *start_color,
389% const PixelPacket *stop_color)
390%
391% A description of each parameter follows:
392%
393% o image: the image.
394%
395% o type: the gradient type: linear or radial.
396%
397% o spread: the gradient spread meathod: pad, reflect, or repeat.
398%
399% o start_color: the start color.
400%
401% o stop_color: the stop color.
402%
403% This provides a good example of making use of the DrawGradientImage
404% function and the gradient structure in draw_info.
405*/
406MagickExport MagickBooleanType GradientImage(Image *image,
407 const GradientType type,const SpreadMethod method,
408 const PixelPacket *start_color,const PixelPacket *stop_color)
409{
410 DrawInfo
411 *draw_info;
412
413 GradientInfo
414 *gradient;
415
416 MagickBooleanType
417 status;
418
419 register long
420 i;
421
422 /*
423 Set gradient start-stop end points.
424 */
425 assert(image != (const Image *) NULL);
426 assert(image->signature == MagickSignature);
427 if (image->debug != MagickFalse)
428 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
429 assert(start_color != (const PixelPacket *) NULL);
430 assert(stop_color != (const PixelPacket *) NULL);
431 draw_info=AcquireDrawInfo();
432 gradient=(&draw_info->gradient);
433 gradient->type=type;
434 gradient->bounding_box.width=image->columns;
435 gradient->bounding_box.height=image->rows;
436 gradient->gradient_vector.x2=(double) image->columns-1.0;
437 gradient->gradient_vector.y2=(double) image->rows-1.0;
438 if ((type == LinearGradient) && (gradient->gradient_vector.y2 != 0.0))
439 gradient->gradient_vector.x2=0.0;
440 gradient->center.x=(double) gradient->gradient_vector.x2/2.0;
441 gradient->center.y=(double) gradient->gradient_vector.y2/2.0;
442 gradient->radius=MagickMax(gradient->center.x,gradient->center.y);
443 gradient->spread=method;
444 /*
445 Define the gradient to fill between the stops.
446 */
447 gradient->number_stops=2;
448 gradient->stops=(StopInfo *) AcquireQuantumMemory(gradient->number_stops,
449 sizeof(*gradient->stops));
450 if (gradient->stops == (StopInfo *) NULL)
451 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
452 image->filename);
453 (void) ResetMagickMemory(gradient->stops,0,gradient->number_stops*
454 sizeof(*gradient->stops));
455 for (i=0; i < (long) gradient->number_stops; i++)
456 GetMagickPixelPacket(image,&gradient->stops[i].color);
457 SetMagickPixelPacket(image,start_color,(IndexPacket *) NULL,
458 &gradient->stops[0].color);
459 gradient->stops[0].offset=0.0;
460 SetMagickPixelPacket(image,stop_color,(IndexPacket *) NULL,
461 &gradient->stops[1].color);
462 gradient->stops[1].offset=1.0;
463 /*
464 Draw a gradient on the image.
465 */
466 status=DrawGradientImage(image,draw_info);
467 draw_info=DestroyDrawInfo(draw_info);
468 if ((start_color->opacity == OpaqueOpacity) &&
469 (stop_color->opacity == OpaqueOpacity))
470 image->matte=MagickFalse;
471 if ((IsGrayPixel(start_color) != MagickFalse) &&
472 (IsGrayPixel(stop_color) != MagickFalse))
473 image->type=GrayscaleType;
474 return(status);
475}
476
477/*
478%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
479% %
480% %
481% %
482% O i l P a i n t I m a g e %
483% %
484% %
485% %
486%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
487%
488% OilPaintImage() applies a special effect filter that simulates an oil
489% painting. Each pixel is replaced by the most frequent color occurring
490% in a circular region defined by radius.
491%
492% The format of the OilPaintImage method is:
493%
494% Image *OilPaintImage(const Image *image,const double radius,
495% ExceptionInfo *exception)
496%
497% A description of each parameter follows:
498%
499% o image: the image.
500%
501% o radius: the radius of the circular neighborhood.
502%
503% o exception: return any errors or warnings in this structure.
504%
505*/
506
507static unsigned long **DestroyHistogramThreadSet(unsigned long **histogram)
508{
509 register long
510 i;
511
512 assert(histogram != (unsigned long **) NULL);
513 for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
514 if (histogram[i] != (unsigned long *) NULL)
515 histogram[i]=(unsigned long *) RelinquishMagickMemory(histogram[i]);
516 histogram=(unsigned long **) RelinquishAlignedMemory(histogram);
517 return(histogram);
518}
519
520static unsigned long **AcquireHistogramThreadSet(const size_t count)
521{
522 register long
523 i;
524
525 unsigned long
526 **histogram,
527 number_threads;
528
529 number_threads=GetOpenMPMaximumThreads();
530 histogram=(unsigned long **) AcquireAlignedMemory(number_threads,
531 sizeof(*histogram));
532 if (histogram == (unsigned long **) NULL)
533 return((unsigned long **) NULL);
534 (void) ResetMagickMemory(histogram,0,number_threads*sizeof(*histogram));
535 for (i=0; i < (long) number_threads; i++)
536 {
537 histogram[i]=(unsigned long *) AcquireQuantumMemory(count,
538 sizeof(**histogram));
539 if (histogram[i] == (unsigned long *) NULL)
540 return(DestroyHistogramThreadSet(histogram));
541 }
542 return(histogram);
543}
544
545MagickExport Image *OilPaintImage(const Image *image,const double radius,
546 ExceptionInfo *exception)
547{
548#define NumberPaintBins 256
549#define OilPaintImageTag "OilPaint/Image"
550
cristyfa112112010-01-04 17:48:07 +0000551 CacheView
552 *image_view,
553 *paint_view;
554
cristy3ed852e2009-09-05 21:47:34 +0000555 Image
556 *paint_image;
557
558 long
559 progress,
560 y;
561
562 MagickBooleanType
563 status;
564
565 unsigned long
cristyfa112112010-01-04 17:48:07 +0000566 **restrict histograms,
cristy3ed852e2009-09-05 21:47:34 +0000567 width;
568
cristy3ed852e2009-09-05 21:47:34 +0000569 /*
570 Initialize painted image attributes.
571 */
572 assert(image != (const Image *) NULL);
573 assert(image->signature == MagickSignature);
574 if (image->debug != MagickFalse)
575 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
576 assert(exception != (ExceptionInfo *) NULL);
577 assert(exception->signature == MagickSignature);
578 width=GetOptimalKernelWidth2D(radius,0.5);
579 if ((image->columns < width) || (image->rows < width))
580 ThrowImageException(OptionError,"ImageSmallerThanRadius");
581 paint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
582 if (paint_image == (Image *) NULL)
583 return((Image *) NULL);
584 if (SetImageStorageClass(paint_image,DirectClass) == MagickFalse)
585 {
586 InheritException(exception,&paint_image->exception);
587 paint_image=DestroyImage(paint_image);
588 return((Image *) NULL);
589 }
590 histograms=AcquireHistogramThreadSet(NumberPaintBins);
591 if (histograms == (unsigned long **) NULL)
592 {
593 paint_image=DestroyImage(paint_image);
594 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
595 }
596 /*
597 Oil paint image.
598 */
599 status=MagickTrue;
600 progress=0;
601 image_view=AcquireCacheView(image);
602 paint_view=AcquireCacheView(paint_image);
cristyb5d5f722009-11-04 03:03:49 +0000603#if defined(MAGICKCORE_OPENMP_SUPPORT)
604 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000605#endif
606 for (y=0; y < (long) image->rows; y++)
607 {
608 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000609 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000610
611 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000612 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000613
614 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000615 *restrict paint_indexes;
cristy3ed852e2009-09-05 21:47:34 +0000616
617 register long
618 x;
619
620 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000621 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000622
623 register unsigned long
624 *histogram;
625
626 if (status == MagickFalse)
627 continue;
628 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
629 2L),image->columns+width,width,exception);
630 q=QueueCacheViewAuthenticPixels(paint_view,0,y,paint_image->columns,1,
631 exception);
632 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
633 {
634 status=MagickFalse;
635 continue;
636 }
637 indexes=GetCacheViewVirtualIndexQueue(image_view);
638 paint_indexes=GetCacheViewAuthenticIndexQueue(paint_view);
639 histogram=histograms[GetOpenMPThreadId()];
640 for (x=0; x < (long) image->columns; x++)
641 {
642 long
643 j,
644 k,
645 v;
646
647 register long
648 i,
649 u;
650
651 unsigned long
652 count;
653
654 /*
655 Assign most frequent color.
656 */
657 i=0;
658 j=0;
659 count=0;
660 (void) ResetMagickMemory(histogram,0,NumberPaintBins*sizeof(*histogram));
661 for (v=0; v < (long) width; v++)
662 {
663 for (u=0; u < (long) width; u++)
664 {
665 k=(long) ScaleQuantumToChar(PixelIntensityToQuantum(p+u+i));
666 histogram[k]++;
667 if (histogram[k] > count)
668 {
669 j=i+u;
670 count=histogram[k];
671 }
672 }
673 i+=image->columns+width;
674 }
675 *q=(*(p+j));
676 if (image->colorspace == CMYKColorspace)
677 paint_indexes[x]=indexes[x+j];
678 p++;
679 q++;
680 }
681 if (SyncCacheViewAuthenticPixels(paint_view,exception) == MagickFalse)
682 status=MagickFalse;
683 if (image->progress_monitor != (MagickProgressMonitor) NULL)
684 {
685 MagickBooleanType
686 proceed;
687
cristyb5d5f722009-11-04 03:03:49 +0000688#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000689 #pragma omp critical (MagickCore_OilPaintImage)
690#endif
691 proceed=SetImageProgress(image,OilPaintImageTag,progress++,image->rows);
692 if (proceed == MagickFalse)
693 status=MagickFalse;
694 }
695 }
696 paint_view=DestroyCacheView(paint_view);
697 image_view=DestroyCacheView(image_view);
698 histograms=DestroyHistogramThreadSet(histograms);
699 if (status == MagickFalse)
700 paint_image=DestroyImage(paint_image);
701 return(paint_image);
702}
703
704/*
705%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
706% %
707% %
708% %
709% O p a q u e P a i n t I m a g e %
710% %
711% %
712% %
713%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
714%
715% OpaquePaintImage() changes any pixel that matches color with the color
716% defined by fill.
717%
718% By default color must match a particular pixel color exactly. However,
719% in many cases two colors may differ by a small amount. Fuzz defines
720% how much tolerance is acceptable to consider two colors as the same.
721% For example, set fuzz to 10 and the color red at intensities of 100 and
722% 102 respectively are now interpreted as the same color.
723%
724% The format of the OpaquePaintImage method is:
725%
726% MagickBooleanType OpaquePaintImage(Image *image,
727% const PixelPacket *target,const PixelPacket *fill,
728% const MagickBooleanType invert)
729% MagickBooleanType OpaquePaintImageChannel(Image *image,
730% const ChannelType channel,const PixelPacket *target,
731% const PixelPacket *fill,const MagickBooleanType invert)
732%
733% A description of each parameter follows:
734%
735% o image: the image.
736%
737% o channel: the channel(s).
738%
739% o target: the RGB value of the target color.
740%
741% o fill: the replacement color.
742%
743% o invert: paint any pixel that does not match the target color.
744%
745*/
746
747MagickExport MagickBooleanType OpaquePaintImage(Image *image,
748 const MagickPixelPacket *target,const MagickPixelPacket *fill,
749 const MagickBooleanType invert)
750{
751 return(OpaquePaintImageChannel(image,AllChannels,target,fill,invert));
752}
753
754MagickExport MagickBooleanType OpaquePaintImageChannel(Image *image,
755 const ChannelType channel,const MagickPixelPacket *target,
756 const MagickPixelPacket *fill,const MagickBooleanType invert)
757{
758#define OpaquePaintImageTag "Opaque/Image"
759
cristyc4c8d132010-01-07 01:58:38 +0000760 CacheView
761 *image_view;
762
cristy3ed852e2009-09-05 21:47:34 +0000763 ExceptionInfo
764 *exception;
765
766 long
767 progress,
768 y;
769
770 MagickBooleanType
771 status;
772
773 MagickPixelPacket
774 zero;
775
cristy3ed852e2009-09-05 21:47:34 +0000776 assert(image != (Image *) NULL);
777 assert(image->signature == MagickSignature);
778 assert(target != (MagickPixelPacket *) NULL);
779 assert(fill != (MagickPixelPacket *) NULL);
780 if (image->debug != MagickFalse)
781 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
782 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
783 return(MagickFalse);
784 /*
785 Make image color opaque.
786 */
787 status=MagickTrue;
788 progress=0;
789 exception=(&image->exception);
790 GetMagickPixelPacket(image,&zero);
791 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000792#if defined(MAGICKCORE_OPENMP_SUPPORT)
793 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000794#endif
795 for (y=0; y < (long) image->rows; y++)
796 {
797 MagickPixelPacket
798 pixel;
799
800 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000801 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000802
803 register long
804 x;
805
806 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000807 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000808
809 if (status == MagickFalse)
810 continue;
811 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
812 if (q == (PixelPacket *) NULL)
813 {
814 status=MagickFalse;
815 continue;
816 }
817 indexes=GetCacheViewAuthenticIndexQueue(image_view);
818 pixel=zero;
819 for (x=0; x < (long) image->columns; x++)
820 {
821 SetMagickPixelPacket(image,q,indexes+x,&pixel);
822 if (IsMagickColorSimilar(&pixel,target) != invert)
823 {
824 if ((channel & RedChannel) != 0)
825 q->red=RoundToQuantum(fill->red);
826 if ((channel & GreenChannel) != 0)
827 q->green=RoundToQuantum(fill->green);
828 if ((channel & BlueChannel) != 0)
829 q->blue=RoundToQuantum(fill->blue);
830 if ((channel & OpacityChannel) != 0)
831 q->opacity=RoundToQuantum(fill->opacity);
832 if (((channel & IndexChannel) != 0) &&
833 (image->colorspace == CMYKColorspace))
834 indexes[x]=RoundToQuantum(fill->index);
835 }
836 q++;
837 }
838 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
839 status=MagickFalse;
840 if (image->progress_monitor != (MagickProgressMonitor) NULL)
841 {
842 MagickBooleanType
843 proceed;
844
cristyb5d5f722009-11-04 03:03:49 +0000845#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000846 #pragma omp critical (MagickCore_OpaquePaintImageChannel)
847#endif
848 proceed=SetImageProgress(image,OpaquePaintImageTag,progress++,
849 image->rows);
850 if (proceed == MagickFalse)
851 status=MagickFalse;
852 }
853 }
854 image_view=DestroyCacheView(image_view);
855 return(status);
856}
857
858/*
859%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
860% %
861% %
862% %
863% T r a n s p a r e n t P a i n t I m a g e %
864% %
865% %
866% %
867%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
868%
869% TransparentPaintImage() changes the opacity value associated with any pixel
870% that matches color to the value defined by opacity.
871%
872% By default color must match a particular pixel color exactly. However,
873% in many cases two colors may differ by a small amount. Fuzz defines
874% how much tolerance is acceptable to consider two colors as the same.
875% For example, set fuzz to 10 and the color red at intensities of 100 and
876% 102 respectively are now interpreted as the same color.
877%
878% The format of the TransparentPaintImage method is:
879%
880% MagickBooleanType TransparentPaintImage(Image *image,
881% const MagickPixelPacket *target,const Quantum opacity,
882% const MagickBooleanType invert)
883%
884% A description of each parameter follows:
885%
886% o image: the image.
887%
888% o target: the target color.
889%
890% o opacity: the replacement opacity value.
891%
892% o invert: paint any pixel that does not match the target color.
893%
894*/
895MagickExport MagickBooleanType TransparentPaintImage(Image *image,
896 const MagickPixelPacket *target,const Quantum opacity,
897 const MagickBooleanType invert)
898{
899#define TransparentPaintImageTag "Transparent/Image"
900
cristyc4c8d132010-01-07 01:58:38 +0000901 CacheView
902 *image_view;
903
cristy3ed852e2009-09-05 21:47:34 +0000904 ExceptionInfo
905 *exception;
906
907 long
908 progress,
909 y;
910
911 MagickBooleanType
912 status;
913
914 MagickPixelPacket
915 zero;
916
cristy3ed852e2009-09-05 21:47:34 +0000917 assert(image != (Image *) NULL);
918 assert(image->signature == MagickSignature);
919 assert(target != (MagickPixelPacket *) NULL);
920 if (image->debug != MagickFalse)
921 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
922 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
923 return(MagickFalse);
924 if (image->matte == MagickFalse)
925 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
926 /*
927 Make image color transparent.
928 */
929 status=MagickTrue;
930 progress=0;
931 exception=(&image->exception);
932 GetMagickPixelPacket(image,&zero);
933 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000934#if defined(MAGICKCORE_OPENMP_SUPPORT)
935 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000936#endif
937 for (y=0; y < (long) image->rows; y++)
938 {
939 MagickPixelPacket
940 pixel;
941
942 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000943 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000944
945 register long
946 x;
947
948 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000949 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000950
951 if (status == MagickFalse)
952 continue;
953 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
954 if (q == (PixelPacket *) NULL)
955 {
956 status=MagickFalse;
957 continue;
958 }
959 indexes=GetCacheViewAuthenticIndexQueue(image_view);
960 pixel=zero;
961 for (x=0; x < (long) image->columns; x++)
962 {
963 SetMagickPixelPacket(image,q,indexes+x,&pixel);
964 if (IsMagickColorSimilar(&pixel,target) != invert)
965 q->opacity=opacity;
966 q++;
967 }
968 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
969 status=MagickFalse;
970 if (image->progress_monitor != (MagickProgressMonitor) NULL)
971 {
972 MagickBooleanType
973 proceed;
974
cristyb5d5f722009-11-04 03:03:49 +0000975#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000976 #pragma omp critical (MagickCore_TransparentPaintImage)
977#endif
978 proceed=SetImageProgress(image,TransparentPaintImageTag,progress++,
979 image->rows);
980 if (proceed == MagickFalse)
981 status=MagickFalse;
982 }
983 }
984 image_view=DestroyCacheView(image_view);
985 return(status);
986}
987
988/*
989%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
990% %
991% %
992% %
993% T r a n s p a r e n t P a i n t I m a g e C h r o m a %
994% %
995% %
996% %
997%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
998%
999% TransparentPaintImageChroma() changes the opacity value associated with any
1000% pixel that matches color to the value defined by opacity.
1001%
1002% As there is one fuzz value for the all the channels, the
1003% TransparentPaintImage() API is not suitable for the operations like chroma,
1004% where the tolerance for similarity of two color component (RGB) can be
1005% different, Thus we define this method take two target pixels (one
1006% low and one hight) and all the pixels of an image which are lying between
1007% these two pixels are made transparent.
1008%
1009% The format of the TransparentPaintImage method is:
1010%
1011% MagickBooleanType TransparentPaintImage(Image *image,
1012% const MagickPixelPacket *low,const MagickPixelPacket *hight,
1013% const Quantum opacity,const MagickBooleanType invert)
1014%
1015% A description of each parameter follows:
1016%
1017% o image: the image.
1018%
1019% o low: the low target color.
1020%
1021% o high: the high target color.
1022%
1023% o opacity: the replacement opacity value.
1024%
1025% o invert: paint any pixel that does not match the target color.
1026%
1027*/
1028MagickExport MagickBooleanType TransparentPaintImageChroma(Image *image,
1029 const MagickPixelPacket *low,const MagickPixelPacket *high,
1030 const Quantum opacity,const MagickBooleanType invert)
1031{
1032#define TransparentPaintImageTag "Transparent/Image"
1033
cristyc4c8d132010-01-07 01:58:38 +00001034 CacheView
1035 *image_view;
1036
cristy3ed852e2009-09-05 21:47:34 +00001037 ExceptionInfo
1038 *exception;
1039
1040 long
1041 progress,
1042 y;
1043
1044 MagickBooleanType
1045 status;
1046
cristy3ed852e2009-09-05 21:47:34 +00001047 assert(image != (Image *) NULL);
1048 assert(image->signature == MagickSignature);
1049 assert(high != (MagickPixelPacket *) NULL);
1050 assert(low != (MagickPixelPacket *) NULL);
1051 if (image->debug != MagickFalse)
1052 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1053 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1054 return(MagickFalse);
1055 if (image->matte == MagickFalse)
1056 (void) SetImageAlphaChannel(image,ResetAlphaChannel);
1057 /*
1058 Make image color transparent.
1059 */
1060 status=MagickTrue;
1061 progress=0;
1062 exception=(&image->exception);
1063 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001064#if defined(MAGICKCORE_OPENMP_SUPPORT)
1065 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001066#endif
1067 for (y=0; y < (long) image->rows; y++)
1068 {
1069 MagickBooleanType
1070 match;
1071
1072 MagickPixelPacket
1073 pixel;
1074
1075 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001076 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001077
1078 register long
1079 x;
1080
1081 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001082 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001083
1084 if (status == MagickFalse)
1085 continue;
1086 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1087 if (q == (PixelPacket *) NULL)
1088 {
1089 status=MagickFalse;
1090 continue;
1091 }
1092 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1093 GetMagickPixelPacket(image,&pixel);
1094 for (x=0; x < (long) image->columns; x++)
1095 {
1096 SetMagickPixelPacket(image,q,indexes+x,&pixel);
1097 match=((pixel.red >= low->red) && (pixel.red <= high->red) &&
1098 (pixel.green >= low->green) && (pixel.green <= high->green) &&
1099 (pixel.blue >= low->blue) && (pixel.blue <= high->blue)) ?
1100 MagickTrue : MagickFalse;
1101 if (match != invert)
1102 q->opacity=opacity;
1103 q++;
1104 }
1105 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1106 status=MagickFalse;
1107 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1108 {
1109 MagickBooleanType
1110 proceed;
1111
cristyb5d5f722009-11-04 03:03:49 +00001112#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001113 #pragma omp critical (MagickCore_TransparentPaintImageChroma)
1114#endif
1115 proceed=SetImageProgress(image,TransparentPaintImageTag,progress++,
1116 image->rows);
1117 if (proceed == MagickFalse)
1118 status=MagickFalse;
1119 }
1120 }
1121 image_view=DestroyCacheView(image_view);
1122 return(status);
1123}