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