|
MagickCore
6.7.5
|
00001 /* 00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00003 % % 00004 % % 00005 % % 00006 % PPPP AAA IIIII N N TTTTT % 00007 % P P A A I NN N T % 00008 % PPPP AAAAA I N N N T % 00009 % P A A I N NN T % 00010 % P A A IIIII N N T % 00011 % % 00012 % % 00013 % Methods to Paint on an Image % 00014 % % 00015 % Software Design % 00016 % John Cristy % 00017 % July 1998 % 00018 % % 00019 % % 00020 % Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization % 00021 % dedicated to making software imaging solutions freely available. % 00022 % % 00023 % You may not use this file except in compliance with the License. You may % 00024 % obtain a copy of the License at % 00025 % % 00026 % http://www.imagemagick.org/script/license.php % 00027 % % 00028 % Unless required by applicable law or agreed to in writing, software % 00029 % distributed under the License is distributed on an "AS IS" BASIS, % 00030 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % 00031 % See the License for the specific language governing permissions and % 00032 % limitations under the License. % 00033 % % 00034 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00035 % 00036 % 00037 */ 00038 00039 /* 00040 Include declarations. 00041 */ 00042 #include "MagickCore/studio.h" 00043 #include "MagickCore/color.h" 00044 #include "MagickCore/color-private.h" 00045 #include "MagickCore/colorspace-private.h" 00046 #include "MagickCore/composite.h" 00047 #include "MagickCore/composite-private.h" 00048 #include "MagickCore/draw.h" 00049 #include "MagickCore/draw-private.h" 00050 #include "MagickCore/exception.h" 00051 #include "MagickCore/exception-private.h" 00052 #include "MagickCore/gem.h" 00053 #include "MagickCore/gem-private.h" 00054 #include "MagickCore/monitor.h" 00055 #include "MagickCore/monitor-private.h" 00056 #include "MagickCore/paint.h" 00057 #include "MagickCore/pixel-accessor.h" 00058 #include "MagickCore/statistic.h" 00059 #include "MagickCore/string_.h" 00060 #include "MagickCore/thread-private.h" 00061 00062 /* 00063 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00064 % % 00065 % % 00066 % % 00067 % F l o o d f i l l P a i n t I m a g e % 00068 % % 00069 % % 00070 % % 00071 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00072 % 00073 % FloodfillPaintImage() changes the color value of any pixel that matches 00074 % target and is an immediate neighbor. If the method FillToBorderMethod is 00075 % specified, the color value is changed for any neighbor pixel that does not 00076 % match the bordercolor member of image. 00077 % 00078 % By default target must match a particular pixel color exactly. However, 00079 % in many cases two colors may differ by a small amount. The fuzz member of 00080 % image defines how much tolerance is acceptable to consider two colors as 00081 % the same. For example, set fuzz to 10 and the color red at intensities of 00082 % 100 and 102 respectively are now interpreted as the same color for the 00083 % purposes of the floodfill. 00084 % 00085 % The format of the FloodfillPaintImage method is: 00086 % 00087 % MagickBooleanType FloodfillPaintImage(Image *image, 00088 % const DrawInfo *draw_info,const PixelInfo target, 00089 % const ssize_t x_offset,const ssize_t y_offset, 00090 % const MagickBooleanType invert,ExceptionInfo *exception) 00091 % 00092 % A description of each parameter follows: 00093 % 00094 % o image: the image. 00095 % 00096 % o draw_info: the draw info. 00097 % 00098 % o target: the RGB value of the target color. 00099 % 00100 % o x_offset,y_offset: the starting location of the operation. 00101 % 00102 % o invert: paint any pixel that does not match the target color. 00103 % 00104 % o exception: return any errors or warnings in this structure. 00105 % 00106 */ 00107 MagickExport MagickBooleanType FloodfillPaintImage(Image *image, 00108 const DrawInfo *draw_info,const PixelInfo *target,const ssize_t x_offset, 00109 const ssize_t y_offset,const MagickBooleanType invert, 00110 ExceptionInfo *exception) 00111 { 00112 #define MaxStacksize (1UL << 15) 00113 #define PushSegmentStack(up,left,right,delta) \ 00114 { \ 00115 if (s >= (segment_stack+MaxStacksize)) \ 00116 ThrowBinaryException(DrawError,"SegmentStackOverflow",image->filename) \ 00117 else \ 00118 { \ 00119 if ((((up)+(delta)) >= 0) && (((up)+(delta)) < (ssize_t) image->rows)) \ 00120 { \ 00121 s->x1=(double) (left); \ 00122 s->y1=(double) (up); \ 00123 s->x2=(double) (right); \ 00124 s->y2=(double) (delta); \ 00125 s++; \ 00126 } \ 00127 } \ 00128 } 00129 00130 CacheView 00131 *floodplane_view, 00132 *image_view; 00133 00134 Image 00135 *floodplane_image; 00136 00137 MagickBooleanType 00138 skip, 00139 status; 00140 00141 PixelInfo 00142 fill_color, 00143 pixel; 00144 00145 register SegmentInfo 00146 *s; 00147 00148 SegmentInfo 00149 *segment_stack; 00150 00151 ssize_t 00152 offset, 00153 start, 00154 x, 00155 x1, 00156 x2, 00157 y; 00158 00159 /* 00160 Check boundary conditions. 00161 */ 00162 assert(image != (Image *) NULL); 00163 assert(image->signature == MagickSignature); 00164 if (image->debug != MagickFalse) 00165 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 00166 assert(draw_info != (DrawInfo *) NULL); 00167 assert(draw_info->signature == MagickSignature); 00168 if ((x_offset < 0) || (x_offset >= (ssize_t) image->columns)) 00169 return(MagickFalse); 00170 if ((y_offset < 0) || (y_offset >= (ssize_t) image->rows)) 00171 return(MagickFalse); 00172 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse) 00173 return(MagickFalse); 00174 if (image->matte == MagickFalse) 00175 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception); 00176 /* 00177 Set floodfill state. 00178 */ 00179 floodplane_image=CloneImage(image,image->columns,image->rows,MagickTrue, 00180 exception); 00181 if (floodplane_image == (Image *) NULL) 00182 return(MagickFalse); 00183 floodplane_image->colorspace=GRAYColorspace; 00184 (void) EvaluateImage(floodplane_image,SetEvaluateOperator,0.0,exception); 00185 segment_stack=(SegmentInfo *) AcquireQuantumMemory(MaxStacksize, 00186 sizeof(*segment_stack)); 00187 if (segment_stack == (SegmentInfo *) NULL) 00188 { 00189 floodplane_image=DestroyImage(floodplane_image); 00190 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 00191 image->filename); 00192 } 00193 /* 00194 Push initial segment on stack. 00195 */ 00196 status=MagickTrue; 00197 x=x_offset; 00198 y=y_offset; 00199 start=0; 00200 s=segment_stack; 00201 PushSegmentStack(y,x,x,1); 00202 PushSegmentStack(y+1,x,x,-1); 00203 GetPixelInfo(image,&pixel); 00204 image_view=AcquireCacheView(image); 00205 floodplane_view=AcquireCacheView(floodplane_image); 00206 while (s > segment_stack) 00207 { 00208 register const Quantum 00209 *restrict p; 00210 00211 register Quantum 00212 *restrict q; 00213 00214 register ssize_t 00215 x; 00216 00217 /* 00218 Pop segment off stack. 00219 */ 00220 s--; 00221 x1=(ssize_t) s->x1; 00222 x2=(ssize_t) s->x2; 00223 offset=(ssize_t) s->y2; 00224 y=(ssize_t) s->y1+offset; 00225 /* 00226 Recolor neighboring pixels. 00227 */ 00228 p=GetCacheViewVirtualPixels(image_view,0,y,(size_t) (x1+1),1,exception); 00229 q=GetCacheViewAuthenticPixels(floodplane_view,0,y,(size_t) (x1+1),1, 00230 exception); 00231 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 00232 break; 00233 p+=x1*GetPixelChannels(image); 00234 q+=x1*GetPixelChannels(floodplane_image); 00235 for (x=x1; x >= 0; x--) 00236 { 00237 if (GetPixelGray(floodplane_image,q) != 0) 00238 break; 00239 GetPixelInfoPixel(image,p,&pixel); 00240 if (IsFuzzyEquivalencePixelInfo(&pixel,target) == invert) 00241 break; 00242 SetPixelGray(floodplane_image,QuantumRange,q); 00243 p-=GetPixelChannels(image); 00244 q-=GetPixelChannels(floodplane_image); 00245 } 00246 if (SyncCacheViewAuthenticPixels(floodplane_view,exception) == MagickFalse) 00247 break; 00248 skip=x >= x1 ? MagickTrue : MagickFalse; 00249 if (skip == MagickFalse) 00250 { 00251 start=x+1; 00252 if (start < x1) 00253 PushSegmentStack(y,start,x1-1,-offset); 00254 x=x1+1; 00255 } 00256 do 00257 { 00258 if (skip == MagickFalse) 00259 { 00260 if (x < (ssize_t) image->columns) 00261 { 00262 p=GetCacheViewVirtualPixels(image_view,x,y,image->columns-x,1, 00263 exception); 00264 q=GetCacheViewAuthenticPixels(floodplane_view,x,y,image->columns- 00265 x,1,exception); 00266 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 00267 break; 00268 for ( ; x < (ssize_t) image->columns; x++) 00269 { 00270 if (GetPixelGray(floodplane_image,q) != 0) 00271 break; 00272 GetPixelInfoPixel(image,p,&pixel); 00273 if (IsFuzzyEquivalencePixelInfo(&pixel,target) == invert) 00274 break; 00275 SetPixelGray(floodplane_image,QuantumRange,q); 00276 p+=GetPixelChannels(image); 00277 q+=GetPixelChannels(floodplane_image); 00278 } 00279 status=SyncCacheViewAuthenticPixels(floodplane_view,exception); 00280 if (status == MagickFalse) 00281 break; 00282 } 00283 PushSegmentStack(y,start,x-1,offset); 00284 if (x > (x2+1)) 00285 PushSegmentStack(y,x2+1,x-1,-offset); 00286 } 00287 skip=MagickFalse; 00288 x++; 00289 if (x <= x2) 00290 { 00291 p=GetCacheViewVirtualPixels(image_view,x,y,(size_t) (x2-x+1),1, 00292 exception); 00293 q=GetCacheViewAuthenticPixels(floodplane_view,x,y,(size_t) (x2-x+1),1, 00294 exception); 00295 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 00296 break; 00297 for ( ; x <= x2; x++) 00298 { 00299 if (GetPixelGray(floodplane_image,q) != 0) 00300 break; 00301 GetPixelInfoPixel(image,p,&pixel); 00302 if (IsFuzzyEquivalencePixelInfo(&pixel,target) != invert) 00303 break; 00304 p+=GetPixelChannels(image); 00305 q+=GetPixelChannels(floodplane_image); 00306 } 00307 } 00308 start=x; 00309 } while (x <= x2); 00310 } 00311 for (y=0; y < (ssize_t) image->rows; y++) 00312 { 00313 register const Quantum 00314 *restrict p; 00315 00316 register Quantum 00317 *restrict q; 00318 00319 register ssize_t 00320 x; 00321 00322 /* 00323 Tile fill color onto floodplane. 00324 */ 00325 p=GetCacheViewVirtualPixels(floodplane_view,0,y,image->columns,1,exception); 00326 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 00327 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 00328 break; 00329 for (x=0; x < (ssize_t) image->columns; x++) 00330 { 00331 if (GetPixelGray(floodplane_image,p) != 0) 00332 { 00333 (void) GetFillColor(draw_info,x,y,&fill_color,exception); 00334 SetPixelInfoPixel(image,&fill_color,q); 00335 } 00336 p+=GetPixelChannels(floodplane_image); 00337 q+=GetPixelChannels(image); 00338 } 00339 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 00340 break; 00341 } 00342 floodplane_view=DestroyCacheView(floodplane_view); 00343 image_view=DestroyCacheView(image_view); 00344 segment_stack=(SegmentInfo *) RelinquishMagickMemory(segment_stack); 00345 floodplane_image=DestroyImage(floodplane_image); 00346 return(y == (ssize_t) image->rows ? MagickTrue : MagickFalse); 00347 } 00348 00349 /* 00350 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00351 % % 00352 % % 00353 % % 00354 + G r a d i e n t I m a g e % 00355 % % 00356 % % 00357 % % 00358 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00359 % 00360 % GradientImage() applies a continuously smooth color transitions along a 00361 % vector from one color to another. 00362 % 00363 % Note, the interface of this method will change in the future to support 00364 % more than one transistion. 00365 % 00366 % The format of the GradientImage method is: 00367 % 00368 % MagickBooleanType GradientImage(Image *image,const GradientType type, 00369 % const SpreadMethod method,const PixelInfo *start_color, 00370 % const PixelInfo *stop_color,ExceptionInfo *exception) 00371 % 00372 % A description of each parameter follows: 00373 % 00374 % o image: the image. 00375 % 00376 % o type: the gradient type: linear or radial. 00377 % 00378 % o spread: the gradient spread meathod: pad, reflect, or repeat. 00379 % 00380 % o start_color: the start color. 00381 % 00382 % o stop_color: the stop color. 00383 % 00384 % o exception: return any errors or warnings in this structure. 00385 % 00386 */ 00387 00388 static inline double MagickMax(const double x,const double y) 00389 { 00390 return(x > y ? x : y); 00391 } 00392 00393 MagickExport MagickBooleanType GradientImage(Image *image, 00394 const GradientType type,const SpreadMethod method, 00395 const PixelInfo *start_color,const PixelInfo *stop_color, 00396 ExceptionInfo *exception) 00397 { 00398 DrawInfo 00399 *draw_info; 00400 00401 GradientInfo 00402 *gradient; 00403 00404 MagickBooleanType 00405 status; 00406 00407 register ssize_t 00408 i; 00409 00410 /* 00411 Set gradient start-stop end points. 00412 */ 00413 assert(image != (const Image *) NULL); 00414 assert(image->signature == MagickSignature); 00415 if (image->debug != MagickFalse) 00416 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 00417 assert(start_color != (const PixelInfo *) NULL); 00418 assert(stop_color != (const PixelInfo *) NULL); 00419 draw_info=AcquireDrawInfo(); 00420 gradient=(&draw_info->gradient); 00421 gradient->type=type; 00422 gradient->bounding_box.width=image->columns; 00423 gradient->bounding_box.height=image->rows; 00424 gradient->gradient_vector.x2=(double) image->columns-1.0; 00425 gradient->gradient_vector.y2=(double) image->rows-1.0; 00426 if ((type == LinearGradient) && (gradient->gradient_vector.y2 != 0.0)) 00427 gradient->gradient_vector.x2=0.0; 00428 gradient->center.x=(double) gradient->gradient_vector.x2/2.0; 00429 gradient->center.y=(double) gradient->gradient_vector.y2/2.0; 00430 gradient->radius=MagickMax(gradient->center.x,gradient->center.y); 00431 gradient->spread=method; 00432 /* 00433 Define the gradient to fill between the stops. 00434 */ 00435 gradient->number_stops=2; 00436 gradient->stops=(StopInfo *) AcquireQuantumMemory(gradient->number_stops, 00437 sizeof(*gradient->stops)); 00438 if (gradient->stops == (StopInfo *) NULL) 00439 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 00440 image->filename); 00441 (void) ResetMagickMemory(gradient->stops,0,gradient->number_stops* 00442 sizeof(*gradient->stops)); 00443 for (i=0; i < (ssize_t) gradient->number_stops; i++) 00444 GetPixelInfo(image,&gradient->stops[i].color); 00445 gradient->stops[0].color=(*start_color); 00446 gradient->stops[0].offset=0.0; 00447 gradient->stops[1].color=(*stop_color); 00448 gradient->stops[1].offset=1.0; 00449 /* 00450 Draw a gradient on the image. 00451 */ 00452 status=DrawGradientImage(image,draw_info,exception); 00453 draw_info=DestroyDrawInfo(draw_info); 00454 if ((start_color->alpha == OpaqueAlpha) && (stop_color->alpha == OpaqueAlpha)) 00455 image->matte=MagickFalse; 00456 if ((IsPixelInfoGray(start_color) != MagickFalse) && 00457 (IsPixelInfoGray(stop_color) != MagickFalse)) 00458 image->type=GrayscaleType; 00459 return(status); 00460 } 00461 00462 /* 00463 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00464 % % 00465 % % 00466 % % 00467 % O i l P a i n t I m a g e % 00468 % % 00469 % % 00470 % % 00471 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00472 % 00473 % OilPaintImage() applies a special effect filter that simulates an oil 00474 % painting. Each pixel is replaced by the most frequent color occurring 00475 % in a circular region defined by radius. 00476 % 00477 % The format of the OilPaintImage method is: 00478 % 00479 % Image *OilPaintImage(const Image *image,const double radius, 00480 % const double sigma,ExceptionInfo *exception) 00481 % 00482 % A description of each parameter follows: 00483 % 00484 % o image: the image. 00485 % 00486 % o radius: the radius of the circular neighborhood. 00487 % 00488 % o sigma: the standard deviation of the Gaussian, in pixels. 00489 % 00490 % o exception: return any errors or warnings in this structure. 00491 % 00492 */ 00493 00494 static size_t **DestroyHistogramThreadSet(size_t **histogram) 00495 { 00496 register ssize_t 00497 i; 00498 00499 assert(histogram != (size_t **) NULL); 00500 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++) 00501 if (histogram[i] != (size_t *) NULL) 00502 histogram[i]=(size_t *) RelinquishMagickMemory(histogram[i]); 00503 histogram=(size_t **) RelinquishMagickMemory(histogram); 00504 return(histogram); 00505 } 00506 00507 static size_t **AcquireHistogramThreadSet(const size_t count) 00508 { 00509 register ssize_t 00510 i; 00511 00512 size_t 00513 **histogram, 00514 number_threads; 00515 00516 number_threads=GetOpenMPMaximumThreads(); 00517 histogram=(size_t **) AcquireQuantumMemory(number_threads,sizeof(*histogram)); 00518 if (histogram == (size_t **) NULL) 00519 return((size_t **) NULL); 00520 (void) ResetMagickMemory(histogram,0,number_threads*sizeof(*histogram)); 00521 for (i=0; i < (ssize_t) number_threads; i++) 00522 { 00523 histogram[i]=(size_t *) AcquireQuantumMemory(count,sizeof(**histogram)); 00524 if (histogram[i] == (size_t *) NULL) 00525 return(DestroyHistogramThreadSet(histogram)); 00526 } 00527 return(histogram); 00528 } 00529 00530 MagickExport Image *OilPaintImage(const Image *image,const double radius, 00531 const double sigma,ExceptionInfo *exception) 00532 { 00533 #define NumberPaintBins 256 00534 #define OilPaintImageTag "OilPaint/Image" 00535 00536 CacheView 00537 *image_view, 00538 *paint_view; 00539 00540 Image 00541 *paint_image; 00542 00543 MagickBooleanType 00544 status; 00545 00546 MagickOffsetType 00547 progress; 00548 00549 size_t 00550 **histograms, 00551 width; 00552 00553 ssize_t 00554 center, 00555 y; 00556 00557 /* 00558 Initialize painted image attributes. 00559 */ 00560 assert(image != (const Image *) NULL); 00561 assert(image->signature == MagickSignature); 00562 if (image->debug != MagickFalse) 00563 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 00564 assert(exception != (ExceptionInfo *) NULL); 00565 assert(exception->signature == MagickSignature); 00566 width=GetOptimalKernelWidth2D(radius,sigma); 00567 paint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception); 00568 if (paint_image == (Image *) NULL) 00569 return((Image *) NULL); 00570 if (SetImageStorageClass(paint_image,DirectClass,exception) == MagickFalse) 00571 { 00572 paint_image=DestroyImage(paint_image); 00573 return((Image *) NULL); 00574 } 00575 histograms=AcquireHistogramThreadSet(NumberPaintBins); 00576 if (histograms == (size_t **) NULL) 00577 { 00578 paint_image=DestroyImage(paint_image); 00579 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); 00580 } 00581 /* 00582 Oil paint image. 00583 */ 00584 status=MagickTrue; 00585 progress=0; 00586 center=(ssize_t) GetPixelChannels(image)*(image->columns+width)*(width/2L)+ 00587 GetPixelChannels(image)*(width/2L); 00588 image_view=AcquireCacheView(image); 00589 paint_view=AcquireCacheView(paint_image); 00590 #if defined(MAGICKCORE_OPENMP_SUPPORT) 00591 #pragma omp parallel for schedule(static,4) shared(progress,status) 00592 #endif 00593 for (y=0; y < (ssize_t) image->rows; y++) 00594 { 00595 register const Quantum 00596 *restrict p; 00597 00598 register Quantum 00599 *restrict q; 00600 00601 register size_t 00602 *histogram; 00603 00604 register ssize_t 00605 x; 00606 00607 if (status == MagickFalse) 00608 continue; 00609 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t) 00610 (width/2L),image->columns+width,width,exception); 00611 q=QueueCacheViewAuthenticPixels(paint_view,0,y,paint_image->columns,1, 00612 exception); 00613 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 00614 { 00615 status=MagickFalse; 00616 continue; 00617 } 00618 histogram=histograms[GetOpenMPThreadId()]; 00619 for (x=0; x < (ssize_t) image->columns; x++) 00620 { 00621 register ssize_t 00622 i, 00623 u; 00624 00625 size_t 00626 count; 00627 00628 ssize_t 00629 j, 00630 k, 00631 n, 00632 v; 00633 00634 /* 00635 Assign most frequent color. 00636 */ 00637 k=0; 00638 j=0; 00639 count=0; 00640 (void) ResetMagickMemory(histogram,0,NumberPaintBins* sizeof(*histogram)); 00641 for (v=0; v < (ssize_t) width; v++) 00642 { 00643 for (u=0; u < (ssize_t) width; u++) 00644 { 00645 n=(ssize_t) ScaleQuantumToChar(GetPixelIntensity(image,p+ 00646 GetPixelChannels(image)*(u+k))); 00647 histogram[n]++; 00648 if (histogram[n] > count) 00649 { 00650 j=k+u; 00651 count=histogram[n]; 00652 } 00653 } 00654 k+=(ssize_t) (image->columns+width); 00655 } 00656 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 00657 { 00658 PixelChannel 00659 channel; 00660 00661 PixelTrait 00662 paint_traits, 00663 traits; 00664 00665 channel=GetPixelChannelMapChannel(image,i); 00666 traits=GetPixelChannelMapTraits(image,channel); 00667 paint_traits=GetPixelChannelMapTraits(paint_image,channel); 00668 if ((traits == UndefinedPixelTrait) || 00669 (paint_traits == UndefinedPixelTrait)) 00670 continue; 00671 if (((paint_traits & CopyPixelTrait) != 0) || 00672 (GetPixelMask(image,p) != 0)) 00673 { 00674 SetPixelChannel(paint_image,channel,p[center+i],q); 00675 continue; 00676 } 00677 SetPixelChannel(paint_image,channel,p[j*GetPixelChannels(image)+i],q); 00678 } 00679 p+=GetPixelChannels(image); 00680 q+=GetPixelChannels(paint_image); 00681 } 00682 if (SyncCacheViewAuthenticPixels(paint_view,exception) == MagickFalse) 00683 status=MagickFalse; 00684 if (image->progress_monitor != (MagickProgressMonitor) NULL) 00685 { 00686 MagickBooleanType 00687 proceed; 00688 00689 #if defined(MAGICKCORE_OPENMP_SUPPORT) 00690 #pragma omp critical (MagickCore_OilPaintImage) 00691 #endif 00692 proceed=SetImageProgress(image,OilPaintImageTag,progress++,image->rows); 00693 if (proceed == MagickFalse) 00694 status=MagickFalse; 00695 } 00696 } 00697 paint_view=DestroyCacheView(paint_view); 00698 image_view=DestroyCacheView(image_view); 00699 histograms=DestroyHistogramThreadSet(histograms); 00700 if (status == MagickFalse) 00701 paint_image=DestroyImage(paint_image); 00702 return(paint_image); 00703 } 00704 00705 /* 00706 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00707 % % 00708 % % 00709 % % 00710 % O p a q u e P a i n t I m a g e % 00711 % % 00712 % % 00713 % % 00714 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00715 % 00716 % OpaquePaintImage() changes any pixel that matches color with the color 00717 % defined by fill. 00718 % 00719 % By default color must match a particular pixel color exactly. However, in 00720 % many cases two colors may differ by a small amount. Fuzz defines how much 00721 % tolerance is acceptable to consider two colors as the same. For example, 00722 % set fuzz to 10 and the color red at intensities of 100 and 102 respectively 00723 % are now interpreted as the same color. 00724 % 00725 % The format of the OpaquePaintImage method is: 00726 % 00727 % MagickBooleanType OpaquePaintImage(Image *image, 00728 % const PixelInfo *target,const PixelInfo *fill, 00729 % const MagickBooleanType invert,ExceptionInfo *exception) 00730 % 00731 % A description of each parameter follows: 00732 % 00733 % o image: the image. 00734 % 00735 % o target: the RGB value of the target color. 00736 % 00737 % o fill: the replacement color. 00738 % 00739 % o invert: paint any pixel that does not match the target color. 00740 % 00741 % o exception: return any errors or warnings in this structure. 00742 % 00743 */ 00744 MagickExport MagickBooleanType OpaquePaintImage(Image *image, 00745 const PixelInfo *target,const PixelInfo *fill,const MagickBooleanType invert, 00746 ExceptionInfo *exception) 00747 { 00748 #define OpaquePaintImageTag "Opaque/Image" 00749 00750 CacheView 00751 *image_view; 00752 00753 MagickBooleanType 00754 status; 00755 00756 MagickOffsetType 00757 progress; 00758 00759 PixelInfo 00760 zero; 00761 00762 ssize_t 00763 y; 00764 00765 assert(image != (Image *) NULL); 00766 assert(image->signature == MagickSignature); 00767 assert(target != (PixelInfo *) NULL); 00768 assert(fill != (PixelInfo *) NULL); 00769 if (image->debug != MagickFalse) 00770 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 00771 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse) 00772 return(MagickFalse); 00773 /* 00774 Make image color opaque. 00775 */ 00776 status=MagickTrue; 00777 progress=0; 00778 GetPixelInfo(image,&zero); 00779 image_view=AcquireCacheView(image); 00780 #if defined(MAGICKCORE_OPENMP_SUPPORT) 00781 #pragma omp parallel for schedule(static,4) shared(progress,status) 00782 #endif 00783 for (y=0; y < (ssize_t) image->rows; y++) 00784 { 00785 PixelInfo 00786 pixel; 00787 00788 register Quantum 00789 *restrict q; 00790 00791 register ssize_t 00792 x; 00793 00794 if (status == MagickFalse) 00795 continue; 00796 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 00797 if (q == (Quantum *) NULL) 00798 { 00799 status=MagickFalse; 00800 continue; 00801 } 00802 pixel=zero; 00803 for (x=0; x < (ssize_t) image->columns; x++) 00804 { 00805 GetPixelInfoPixel(image,q,&pixel); 00806 if (IsFuzzyEquivalencePixelInfo(&pixel,target) != invert) 00807 SetPixelInfoPixel(image,fill,q); 00808 q+=GetPixelChannels(image); 00809 } 00810 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 00811 status=MagickFalse; 00812 if (image->progress_monitor != (MagickProgressMonitor) NULL) 00813 { 00814 MagickBooleanType 00815 proceed; 00816 00817 #if defined(MAGICKCORE_OPENMP_SUPPORT) 00818 #pragma omp critical (MagickCore_OpaquePaintImage) 00819 #endif 00820 proceed=SetImageProgress(image,OpaquePaintImageTag,progress++, 00821 image->rows); 00822 if (proceed == MagickFalse) 00823 status=MagickFalse; 00824 } 00825 } 00826 image_view=DestroyCacheView(image_view); 00827 return(status); 00828 } 00829 00830 /* 00831 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00832 % % 00833 % % 00834 % % 00835 % T r a n s p a r e n t P a i n t I m a g e % 00836 % % 00837 % % 00838 % % 00839 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00840 % 00841 % TransparentPaintImage() changes the opacity value associated with any pixel 00842 % that matches color to the value defined by opacity. 00843 % 00844 % By default color must match a particular pixel color exactly. However, in 00845 % many cases two colors may differ by a small amount. Fuzz defines how much 00846 % tolerance is acceptable to consider two colors as the same. For example, 00847 % set fuzz to 10 and the color red at intensities of 100 and 102 respectively 00848 % are now interpreted as the same color. 00849 % 00850 % The format of the TransparentPaintImage method is: 00851 % 00852 % MagickBooleanType TransparentPaintImage(Image *image, 00853 % const PixelInfo *target,const Quantum opacity, 00854 % const MagickBooleanType invert,ExceptionInfo *exception) 00855 % 00856 % A description of each parameter follows: 00857 % 00858 % o image: the image. 00859 % 00860 % o target: the target color. 00861 % 00862 % o opacity: the replacement opacity value. 00863 % 00864 % o invert: paint any pixel that does not match the target color. 00865 % 00866 % o exception: return any errors or warnings in this structure. 00867 % 00868 */ 00869 MagickExport MagickBooleanType TransparentPaintImage(Image *image, 00870 const PixelInfo *target,const Quantum opacity,const MagickBooleanType invert, 00871 ExceptionInfo *exception) 00872 { 00873 #define TransparentPaintImageTag "Transparent/Image" 00874 00875 CacheView 00876 *image_view; 00877 00878 MagickBooleanType 00879 status; 00880 00881 MagickOffsetType 00882 progress; 00883 00884 PixelInfo 00885 zero; 00886 00887 ssize_t 00888 y; 00889 00890 assert(image != (Image *) NULL); 00891 assert(image->signature == MagickSignature); 00892 assert(target != (PixelInfo *) NULL); 00893 if (image->debug != MagickFalse) 00894 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 00895 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse) 00896 return(MagickFalse); 00897 if (image->matte == MagickFalse) 00898 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception); 00899 /* 00900 Make image color transparent. 00901 */ 00902 status=MagickTrue; 00903 progress=0; 00904 GetPixelInfo(image,&zero); 00905 image_view=AcquireCacheView(image); 00906 #if defined(MAGICKCORE_OPENMP_SUPPORT) 00907 #pragma omp parallel for schedule(static,4) shared(progress,status) 00908 #endif 00909 for (y=0; y < (ssize_t) image->rows; y++) 00910 { 00911 PixelInfo 00912 pixel; 00913 00914 register ssize_t 00915 x; 00916 00917 register Quantum 00918 *restrict q; 00919 00920 if (status == MagickFalse) 00921 continue; 00922 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 00923 if (q == (Quantum *) NULL) 00924 { 00925 status=MagickFalse; 00926 continue; 00927 } 00928 pixel=zero; 00929 for (x=0; x < (ssize_t) image->columns; x++) 00930 { 00931 GetPixelInfoPixel(image,q,&pixel); 00932 if (IsFuzzyEquivalencePixelInfo(&pixel,target) != invert) 00933 SetPixelAlpha(image,opacity,q); 00934 q+=GetPixelChannels(image); 00935 } 00936 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 00937 status=MagickFalse; 00938 if (image->progress_monitor != (MagickProgressMonitor) NULL) 00939 { 00940 MagickBooleanType 00941 proceed; 00942 00943 #if defined(MAGICKCORE_OPENMP_SUPPORT) 00944 #pragma omp critical (MagickCore_TransparentPaintImage) 00945 #endif 00946 proceed=SetImageProgress(image,TransparentPaintImageTag,progress++, 00947 image->rows); 00948 if (proceed == MagickFalse) 00949 status=MagickFalse; 00950 } 00951 } 00952 image_view=DestroyCacheView(image_view); 00953 return(status); 00954 } 00955 00956 /* 00957 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00958 % % 00959 % % 00960 % % 00961 % 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 % 00962 % % 00963 % % 00964 % % 00965 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00966 % 00967 % TransparentPaintImageChroma() changes the opacity value associated with any 00968 % pixel that matches color to the value defined by opacity. 00969 % 00970 % As there is one fuzz value for the all the channels, TransparentPaintImage() 00971 % is not suitable for the operations like chroma, where the tolerance for 00972 % similarity of two color component (RGB) can be different. Thus we define 00973 % this method to take two target pixels (one low and one high) and all the 00974 % pixels of an image which are lying between these two pixels are made 00975 % transparent. 00976 % 00977 % The format of the TransparentPaintImageChroma method is: 00978 % 00979 % MagickBooleanType TransparentPaintImageChroma(Image *image, 00980 % const PixelInfo *low,const PixelInfo *high,const Quantum opacity, 00981 % const MagickBooleanType invert,ExceptionInfo *exception) 00982 % 00983 % A description of each parameter follows: 00984 % 00985 % o image: the image. 00986 % 00987 % o low: the low target color. 00988 % 00989 % o high: the high target color. 00990 % 00991 % o opacity: the replacement opacity value. 00992 % 00993 % o invert: paint any pixel that does not match the target color. 00994 % 00995 % o exception: return any errors or warnings in this structure. 00996 % 00997 */ 00998 MagickExport MagickBooleanType TransparentPaintImageChroma(Image *image, 00999 const PixelInfo *low,const PixelInfo *high,const Quantum opacity, 01000 const MagickBooleanType invert,ExceptionInfo *exception) 01001 { 01002 #define TransparentPaintImageTag "Transparent/Image" 01003 01004 CacheView 01005 *image_view; 01006 01007 MagickBooleanType 01008 status; 01009 01010 MagickOffsetType 01011 progress; 01012 01013 ssize_t 01014 y; 01015 01016 assert(image != (Image *) NULL); 01017 assert(image->signature == MagickSignature); 01018 assert(high != (PixelInfo *) NULL); 01019 assert(low != (PixelInfo *) NULL); 01020 if (image->debug != MagickFalse) 01021 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 01022 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse) 01023 return(MagickFalse); 01024 if (image->matte == MagickFalse) 01025 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception); 01026 /* 01027 Make image color transparent. 01028 */ 01029 status=MagickTrue; 01030 progress=0; 01031 image_view=AcquireCacheView(image); 01032 #if defined(MAGICKCORE_OPENMP_SUPPORT) 01033 #pragma omp parallel for schedule(static,4) shared(progress,status) 01034 #endif 01035 for (y=0; y < (ssize_t) image->rows; y++) 01036 { 01037 MagickBooleanType 01038 match; 01039 01040 PixelInfo 01041 pixel; 01042 01043 register Quantum 01044 *restrict q; 01045 01046 register ssize_t 01047 x; 01048 01049 if (status == MagickFalse) 01050 continue; 01051 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 01052 if (q == (Quantum *) NULL) 01053 { 01054 status=MagickFalse; 01055 continue; 01056 } 01057 GetPixelInfo(image,&pixel); 01058 for (x=0; x < (ssize_t) image->columns; x++) 01059 { 01060 GetPixelInfoPixel(image,q,&pixel); 01061 match=((pixel.red >= low->red) && (pixel.red <= high->red) && 01062 (pixel.green >= low->green) && (pixel.green <= high->green) && 01063 (pixel.blue >= low->blue) && (pixel.blue <= high->blue)) ? MagickTrue : 01064 MagickFalse; 01065 if (match != invert) 01066 SetPixelAlpha(image,opacity,q); 01067 q+=GetPixelChannels(image); 01068 } 01069 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 01070 status=MagickFalse; 01071 if (image->progress_monitor != (MagickProgressMonitor) NULL) 01072 { 01073 MagickBooleanType 01074 proceed; 01075 01076 #if defined(MAGICKCORE_OPENMP_SUPPORT) 01077 #pragma omp critical (MagickCore_TransparentPaintImageChroma) 01078 #endif 01079 proceed=SetImageProgress(image,TransparentPaintImageTag,progress++, 01080 image->rows); 01081 if (proceed == MagickFalse) 01082 status=MagickFalse; 01083 } 01084 } 01085 image_view=DestroyCacheView(image_view); 01086 return(status); 01087 }