00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043 #include "magick/studio.h"
00044 #include "magick/annotate.h"
00045 #include "magick/cache.h"
00046 #include "magick/cache-view.h"
00047 #include "magick/color.h"
00048 #include "magick/color-private.h"
00049 #include "magick/composite.h"
00050 #include "magick/decorate.h"
00051 #include "magick/draw.h"
00052 #include "magick/effect.h"
00053 #include "magick/enhance.h"
00054 #include "magick/exception.h"
00055 #include "magick/exception-private.h"
00056 #include "magick/fx.h"
00057 #include "magick/fx-private.h"
00058 #include "magick/gem.h"
00059 #include "magick/geometry.h"
00060 #include "magick/layer.h"
00061 #include "magick/list.h"
00062 #include "magick/log.h"
00063 #include "magick/image.h"
00064 #include "magick/image-private.h"
00065 #include "magick/memory_.h"
00066 #include "magick/monitor.h"
00067 #include "magick/monitor-private.h"
00068 #include "magick/option.h"
00069 #include "magick/pixel-private.h"
00070 #include "magick/property.h"
00071 #include "magick/quantum.h"
00072 #include "magick/random_.h"
00073 #include "magick/resample.h"
00074 #include "magick/resample-private.h"
00075 #include "magick/resize.h"
00076 #include "magick/shear.h"
00077 #include "magick/splay-tree.h"
00078 #include "magick/statistic.h"
00079 #include "magick/string_.h"
00080 #include "magick/transform.h"
00081 #include "magick/utility.h"
00082
00083
00084
00085
00086 #define LeftShiftOperator 0xf5
00087 #define RightShiftOperator 0xf6
00088 #define LessThanEqualOperator 0xf7
00089 #define GreaterThanEqualOperator 0xf8
00090 #define EqualOperator 0xf9
00091 #define NotEqualOperator 0xfa
00092 #define LogicalAndOperator 0xfb
00093 #define LogicalOrOperator 0xfc
00094
00095 struct _FxInfo
00096 {
00097 const Image
00098 *images;
00099
00100 MagickBooleanType
00101 matte;
00102
00103 char
00104 *expression;
00105
00106 SplayTreeInfo
00107 *colors,
00108 *symbols;
00109
00110 ResampleFilter
00111 **resample_filter;
00112
00113 ExceptionInfo
00114 *exception;
00115 };
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140 MagickExport FxInfo *AcquireFxInfo(const Image *image,const char *expression)
00141 {
00142 char
00143 fx_op[2];
00144
00145 FxInfo
00146 *fx_info;
00147
00148 register long
00149 i;
00150
00151 fx_info=(FxInfo *) AcquireMagickMemory(sizeof(*fx_info));
00152 if (fx_info == (FxInfo *) NULL)
00153 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
00154 (void) ResetMagickMemory(fx_info,0,sizeof(*fx_info));
00155 fx_info->exception=AcquireExceptionInfo();
00156 fx_info->images=image;
00157 fx_info->matte=image->matte;
00158 fx_info->colors=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
00159 RelinquishMagickMemory);
00160 fx_info->symbols=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
00161 RelinquishMagickMemory);
00162 fx_info->resample_filter=(ResampleFilter **) AcquireQuantumMemory(
00163 GetImageListLength(fx_info->images),sizeof(*fx_info->resample_filter));
00164 if (fx_info->resample_filter == (ResampleFilter **) NULL)
00165 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
00166 for (i=0; i < (long) GetImageListLength(fx_info->images); i++)
00167 fx_info->resample_filter[i]=AcquireResampleFilter(GetImageFromList(
00168 fx_info->images,i),fx_info->exception);
00169 fx_info->expression=ConstantString(expression);
00170 (void) SubstituteString(&fx_info->expression," ","");
00171 if ((strstr(fx_info->expression,"e+") != (char *) NULL) ||
00172 (strstr(fx_info->expression,"e-") != (char *) NULL))
00173 {
00174
00175
00176
00177 (void) SubstituteString(&fx_info->expression,"0e+","0*10^");
00178 (void) SubstituteString(&fx_info->expression,"1e+","1*10^");
00179 (void) SubstituteString(&fx_info->expression,"2e+","2*10^");
00180 (void) SubstituteString(&fx_info->expression,"3e+","3*10^");
00181 (void) SubstituteString(&fx_info->expression,"4e+","4*10^");
00182 (void) SubstituteString(&fx_info->expression,"5e+","5*10^");
00183 (void) SubstituteString(&fx_info->expression,"6e+","6*10^");
00184 (void) SubstituteString(&fx_info->expression,"7e+","7*10^");
00185 (void) SubstituteString(&fx_info->expression,"8e+","8*10^");
00186 (void) SubstituteString(&fx_info->expression,"9e+","9*10^");
00187 (void) SubstituteString(&fx_info->expression,"0e-","0*10^-");
00188 (void) SubstituteString(&fx_info->expression,"1e-","1*10^-");
00189 (void) SubstituteString(&fx_info->expression,"2e-","2*10^-");
00190 (void) SubstituteString(&fx_info->expression,"3e-","3*10^-");
00191 (void) SubstituteString(&fx_info->expression,"4e-","4*10^-");
00192 (void) SubstituteString(&fx_info->expression,"5e-","5*10^-");
00193 (void) SubstituteString(&fx_info->expression,"6e-","6*10^-");
00194 (void) SubstituteString(&fx_info->expression,"7e-","7*10^-");
00195 (void) SubstituteString(&fx_info->expression,"8e-","8*10^-");
00196 (void) SubstituteString(&fx_info->expression,"9e-","9*10^-");
00197 }
00198
00199
00200
00201 fx_op[1]='\0';
00202 *fx_op=(char) LeftShiftOperator;
00203 (void) SubstituteString(&fx_info->expression,"<<",fx_op);
00204 *fx_op=(char) RightShiftOperator;
00205 (void) SubstituteString(&fx_info->expression,">>",fx_op);
00206 *fx_op=(char) LessThanEqualOperator;
00207 (void) SubstituteString(&fx_info->expression,"<=",fx_op);
00208 *fx_op=(char) GreaterThanEqualOperator;
00209 (void) SubstituteString(&fx_info->expression,">=",fx_op);
00210 *fx_op=(char) EqualOperator;
00211 (void) SubstituteString(&fx_info->expression,"==",fx_op);
00212 *fx_op=(char) NotEqualOperator;
00213 (void) SubstituteString(&fx_info->expression,"!=",fx_op);
00214 *fx_op=(char) LogicalAndOperator;
00215 (void) SubstituteString(&fx_info->expression,"&&",fx_op);
00216 *fx_op=(char) LogicalOrOperator;
00217 (void) SubstituteString(&fx_info->expression,"||",fx_op);
00218 return(fx_info);
00219 }
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254 static Quantum GenerateNoise(const Quantum pixel,const NoiseType noise_type,
00255 const MagickRealType attenuate)
00256 {
00257 #define NoiseEpsilon (attenuate*1.0e-5)
00258 #define SigmaUniform ScaleCharToQuantum((unsigned char) (attenuate*4.0+0.5))
00259 #define SigmaGaussian ScaleCharToQuantum((unsigned char) (attenuate*4.0+0.5))
00260 #define SigmaImpulse (attenuate*0.10)
00261 #define SigmaLaplacian ScaleCharToQuantum((unsigned char) (attenuate*10.0+0.5))
00262 #define SigmaMultiplicativeGaussian \
00263 ScaleCharToQuantum((unsigned char) (attenuate*1.0+0.5))
00264 #define SigmaPoisson (attenuate*0.05)
00265 #define TauGaussian ScaleCharToQuantum((unsigned char) (attenuate*20.0+0.5))
00266
00267 MagickRealType
00268 alpha,
00269 beta,
00270 noise,
00271 sigma;
00272
00273 alpha=GetPseudoRandomValue();
00274 if (alpha == 0.0)
00275 alpha=1.0;
00276 switch (noise_type)
00277 {
00278 case UniformNoise:
00279 default:
00280 {
00281 noise=(MagickRealType) pixel+SigmaUniform*(alpha-0.5);
00282 break;
00283 }
00284 case GaussianNoise:
00285 {
00286 MagickRealType
00287 tau;
00288
00289 beta=GetPseudoRandomValue();
00290 sigma=sqrt(-2.0*log((double) alpha))*cos((double) (2.0*MagickPI*beta));
00291 tau=sqrt(-2.0*log((double) alpha))*sin((double) (2.0*MagickPI*beta));
00292 noise=(MagickRealType) pixel+sqrt((double) pixel)*SigmaGaussian*sigma+
00293 TauGaussian*tau;
00294 break;
00295 }
00296 case MultiplicativeGaussianNoise:
00297 {
00298 if (alpha <= NoiseEpsilon)
00299 sigma=(MagickRealType) QuantumRange;
00300 else
00301 sigma=sqrt(-2.0*log((double) alpha));
00302 beta=GetPseudoRandomValue();
00303 noise=(MagickRealType) pixel+pixel*SigmaMultiplicativeGaussian*sigma/2.0*
00304 cos((double) (2.0*MagickPI*beta));
00305 break;
00306 }
00307 case ImpulseNoise:
00308 {
00309 if (alpha < (SigmaImpulse/2.0))
00310 noise=0.0;
00311 else
00312 if (alpha >= (1.0-(SigmaImpulse/2.0)))
00313 noise=(MagickRealType) QuantumRange;
00314 else
00315 noise=(MagickRealType) pixel;
00316 break;
00317 }
00318 case LaplacianNoise:
00319 {
00320 if (alpha <= 0.5)
00321 {
00322 if (alpha <= NoiseEpsilon)
00323 noise=(MagickRealType) pixel-(MagickRealType) QuantumRange;
00324 else
00325 noise=(MagickRealType) pixel+ScaleCharToQuantum((unsigned char)
00326 (SigmaLaplacian*log((double) (2.0*alpha))+0.5));
00327 break;
00328 }
00329 beta=1.0-alpha;
00330 if (beta <= (0.5*NoiseEpsilon))
00331 noise=(MagickRealType) (pixel+QuantumRange);
00332 else
00333 noise=(MagickRealType) pixel-ScaleCharToQuantum((unsigned char)
00334 (SigmaLaplacian*log((double) (2.0*beta))+0.5));
00335 break;
00336 }
00337 case PoissonNoise:
00338 {
00339 MagickRealType
00340 poisson;
00341
00342 register long
00343 i;
00344
00345 poisson=exp(-SigmaPoisson*(double) ScaleQuantumToChar(pixel));
00346 for (i=0; alpha > poisson; i++)
00347 {
00348 beta=GetPseudoRandomValue();
00349 alpha=alpha*beta;
00350 }
00351 noise=(MagickRealType) ScaleCharToQuantum((unsigned char)
00352 (i/SigmaPoisson));
00353 break;
00354 }
00355 case RandomNoise:
00356 {
00357 noise=(MagickRealType) QuantumRange*GetPseudoRandomValue();
00358 break;
00359 }
00360 }
00361 return(RoundToQuantum(noise));
00362 }
00363
00364 MagickExport Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
00365 ExceptionInfo *exception)
00366 {
00367 Image
00368 *noise_image;
00369
00370 noise_image=AddNoiseImageChannel(image,DefaultChannels,noise_type,exception);
00371 return(noise_image);
00372 }
00373
00374 MagickExport Image *AddNoiseImageChannel(const Image *image,
00375 const ChannelType channel,const NoiseType noise_type,ExceptionInfo *exception)
00376 {
00377 #define AddNoiseImageTag "AddNoise/Image"
00378
00379 const char
00380 *option;
00381
00382 Image
00383 *noise_image;
00384
00385 long
00386 progress,
00387 y;
00388
00389 MagickBooleanType
00390 status;
00391
00392 MagickRealType
00393 attenuate;
00394
00395 ViewInfo
00396 *image_view,
00397 *noise_view;
00398
00399
00400
00401
00402 assert(image != (const Image *) NULL);
00403 assert(image->signature == MagickSignature);
00404 if (image->debug != MagickFalse)
00405 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00406 assert(exception != (ExceptionInfo *) NULL);
00407 assert(exception->signature == MagickSignature);
00408 noise_image=CloneImage(image,0,0,MagickTrue,exception);
00409 if (noise_image == (Image *) NULL)
00410 return((Image *) NULL);
00411 if (SetImageStorageClass(noise_image,DirectClass) == MagickFalse)
00412 {
00413 InheritException(exception,&noise_image->exception);
00414 noise_image=DestroyImage(noise_image);
00415 return((Image *) NULL);
00416 }
00417
00418
00419
00420 attenuate=1.0;
00421 option=GetImageProperty(image,"attenuate");
00422 if (option != (char *) NULL)
00423 attenuate=atof(option);
00424 status=MagickTrue;
00425 progress=0;
00426 image_view=AcquireCacheView(image);
00427 noise_view=AcquireCacheView(noise_image);
00428 for (y=0; y < (long) image->rows; y++)
00429 {
00430 register const IndexPacket
00431 *indexes;
00432
00433 register const PixelPacket
00434 *p;
00435
00436 register IndexPacket
00437 *noise_indexes;
00438
00439 register long
00440 x;
00441
00442 register PixelPacket
00443 *q;
00444
00445 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
00446 q=GetCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
00447 exception);
00448 if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00449 {
00450 status=MagickFalse;
00451 break;
00452 }
00453 indexes=GetCacheViewVirtualIndexes(image_view);
00454 noise_indexes=GetCacheViewAuthenticIndexes(noise_view);
00455 for (x=0; x < (long) image->columns; x++)
00456 {
00457 if ((channel & RedChannel) != 0)
00458 q->red=GenerateNoise(p->red,noise_type,attenuate);
00459 if ((channel & GreenChannel) != 0)
00460 q->green=GenerateNoise(p->green,noise_type,attenuate);
00461 if ((channel & BlueChannel) != 0)
00462 q->blue=GenerateNoise(p->blue,noise_type,attenuate);
00463 if ((channel & OpacityChannel) != 0)
00464 q->opacity=GenerateNoise(p->opacity,noise_type,attenuate);
00465 if (((channel & IndexChannel) != 0) &&
00466 (image->colorspace == CMYKColorspace))
00467 noise_indexes[x]=(IndexPacket) GenerateNoise(indexes[x],noise_type,
00468 attenuate);
00469 p++;
00470 q++;
00471 }
00472 if (SyncCacheViewAuthenticPixels(noise_view,exception) == MagickFalse)
00473 status=MagickFalse;
00474 if (image->progress_monitor != (MagickProgressMonitor) NULL)
00475 {
00476 MagickBooleanType
00477 proceed;
00478
00479 proceed=SetImageProgress(image,AddNoiseImageTag,progress++,
00480 image->rows);
00481 if (proceed == MagickFalse)
00482 status=MagickFalse;
00483 }
00484 if (status == MagickFalse)
00485 break;
00486 }
00487 noise_view=DestroyCacheView(noise_view);
00488 image_view=DestroyCacheView(image_view);
00489 if (status == MagickFalse)
00490 noise_image=DestroyImage(noise_image);
00491 return(noise_image);
00492 }
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514
00515
00516
00517
00518
00519
00520
00521
00522
00523
00524
00525 MagickExport Image *CharcoalImage(const Image *image,const double radius,
00526 const double sigma,ExceptionInfo *exception)
00527 {
00528 Image
00529 *charcoal_image,
00530 *clone_image,
00531 *edge_image;
00532
00533 assert(image != (Image *) NULL);
00534 assert(image->signature == MagickSignature);
00535 if (image->debug != MagickFalse)
00536 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00537 assert(exception != (ExceptionInfo *) NULL);
00538 assert(exception->signature == MagickSignature);
00539 clone_image=CloneImage(image,0,0,MagickTrue,exception);
00540 if (clone_image == (Image *) NULL)
00541 return((Image *) NULL);
00542 (void) SetImageType(clone_image,GrayscaleType);
00543 edge_image=EdgeImage(clone_image,radius,exception);
00544 clone_image=DestroyImage(clone_image);
00545 if (edge_image == (Image *) NULL)
00546 return((Image *) NULL);
00547 charcoal_image=BlurImage(edge_image,radius,sigma,exception);
00548 edge_image=DestroyImage(edge_image);
00549 if (charcoal_image == (Image *) NULL)
00550 return((Image *) NULL);
00551 (void) NormalizeImage(charcoal_image);
00552 (void) NegateImage(charcoal_image,MagickFalse);
00553 (void) SetImageType(charcoal_image,GrayscaleType);
00554 return(charcoal_image);
00555 }
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585
00586
00587
00588
00589
00590 MagickExport Image *ColorizeImage(const Image *image,const char *opacity,
00591 const PixelPacket colorize,ExceptionInfo *exception)
00592 {
00593 #define ColorizeImageTag "Colorize/Image"
00594
00595 GeometryInfo
00596 geometry_info;
00597
00598 Image
00599 *colorize_image;
00600
00601 long
00602 progress,
00603 y;
00604
00605 MagickBooleanType
00606 status;
00607
00608 MagickPixelPacket
00609 pixel;
00610
00611 MagickStatusType
00612 flags;
00613
00614 ViewInfo
00615 **colorize_view,
00616 **image_view;
00617
00618
00619
00620
00621 assert(image != (const Image *) NULL);
00622 assert(image->signature == MagickSignature);
00623 if (image->debug != MagickFalse)
00624 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00625 assert(exception != (ExceptionInfo *) NULL);
00626 assert(exception->signature == MagickSignature);
00627 colorize_image=CloneImage(image,image->columns,image->rows,MagickTrue,
00628 exception);
00629 if (colorize_image == (Image *) NULL)
00630 return((Image *) NULL);
00631 if (SetImageStorageClass(colorize_image,DirectClass) == MagickFalse)
00632 {
00633 InheritException(exception,&colorize_image->exception);
00634 colorize_image=DestroyImage(colorize_image);
00635 return((Image *) NULL);
00636 }
00637 if (opacity == (const char *) NULL)
00638 return(colorize_image);
00639
00640
00641
00642 flags=ParseGeometry(opacity,&geometry_info);
00643 pixel.red=geometry_info.rho;
00644 pixel.green=geometry_info.rho;
00645 pixel.blue=geometry_info.rho;
00646 pixel.opacity=(MagickRealType) OpaqueOpacity;
00647 if ((flags & SigmaValue) != 0)
00648 pixel.green=geometry_info.sigma;
00649 if ((flags & XiValue) != 0)
00650 pixel.blue=geometry_info.xi;
00651 if ((flags & PsiValue) != 0)
00652 pixel.opacity=geometry_info.psi;
00653
00654
00655
00656 status=MagickTrue;
00657 progress=0;
00658 image_view=AcquireCacheViewThreadSet(image);
00659 colorize_view=AcquireCacheViewThreadSet(colorize_image);
00660 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00661 #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
00662 #endif
00663 for (y=0; y < (long) image->rows; y++)
00664 {
00665 MagickBooleanType
00666 sync;
00667
00668 register const PixelPacket
00669 *p;
00670
00671 register long
00672 id,
00673 x;
00674
00675 register PixelPacket
00676 *q;
00677
00678 if (status == MagickFalse)
00679 continue;
00680 id=GetCacheViewThreadId();
00681 p=GetCacheViewVirtualPixels(image_view[id],0,y,image->columns,1,exception);
00682 q=QueueCacheViewAuthenticPixels(colorize_view[id],0,y,
00683 colorize_image->columns,1,exception);
00684 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00685 {
00686 status=MagickFalse;
00687 continue;
00688 }
00689 for (x=0; x < (long) image->columns; x++)
00690 {
00691 q->red=(Quantum) ((p->red*(100.0-pixel.red)+
00692 colorize.red*pixel.red)/100.0);
00693 q->green=(Quantum) ((p->green*(100.0-pixel.green)+
00694 colorize.green*pixel.green)/100.0);
00695 q->blue=(Quantum) ((p->blue*(100.0-pixel.blue)+
00696 colorize.blue*pixel.blue)/100.0);
00697 q->opacity=(Quantum) ((p->opacity*(100.0-pixel.opacity)+
00698 colorize.opacity*pixel.opacity)/100.0);
00699 p++;
00700 q++;
00701 }
00702 sync=SyncCacheViewAuthenticPixels(colorize_view[id],exception);
00703 if (sync == MagickFalse)
00704 status=MagickFalse;
00705 if (image->progress_monitor != (MagickProgressMonitor) NULL)
00706 {
00707 MagickBooleanType
00708 proceed;
00709
00710 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00711 #pragma omp critical
00712 #endif
00713 proceed=SetImageProgress(image,ColorizeImageTag,progress++,image->rows);
00714 if (proceed == MagickFalse)
00715 status=MagickFalse;
00716 }
00717 }
00718 image_view=DestroyCacheViewThreadSet(image_view);
00719 colorize_view=DestroyCacheViewThreadSet(colorize_view);
00720 if (status == MagickFalse)
00721 colorize_image=DestroyImage(colorize_image);
00722 return(colorize_image);
00723 }
00724
00725
00726
00727
00728
00729
00730
00731
00732
00733
00734
00735
00736
00737
00738
00739
00740
00741
00742
00743
00744
00745
00746
00747
00748
00749
00750
00751
00752
00753
00754
00755
00756
00757
00758
00759
00760 MagickExport Image *ConvolveImage(const Image *image,const unsigned long order,
00761 const double *kernel,ExceptionInfo *exception)
00762 {
00763 Image
00764 *convolve_image;
00765
00766 convolve_image=ConvolveImageChannel(image,DefaultChannels,order,kernel,
00767 exception);
00768 return(convolve_image);
00769 }
00770
00771 MagickExport Image *ConvolveImageChannel(const Image *image,
00772 const ChannelType channel,const unsigned long order,const double *kernel,
00773 ExceptionInfo *exception)
00774 {
00775 #define ConvolveImageTag "Convolve/Image"
00776
00777 double
00778 *normal_kernel;
00779
00780 Image
00781 *convolve_image;
00782
00783 long
00784 progress,
00785 y;
00786
00787 MagickBooleanType
00788 status;
00789
00790 MagickPixelPacket
00791 zero;
00792
00793 MagickRealType
00794 bias,
00795 gamma;
00796
00797 register long
00798 i;
00799
00800 unsigned long
00801 width;
00802
00803
00804
00805
00806 assert(image != (Image *) NULL);
00807 assert(image->signature == MagickSignature);
00808 if (image->debug != MagickFalse)
00809 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00810 assert(exception != (ExceptionInfo *) NULL);
00811 assert(exception->signature == MagickSignature);
00812 width=order;
00813 if ((width % 2) == 0)
00814 ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
00815 convolve_image=CloneImage(image,0,0,MagickTrue,exception);
00816 if (convolve_image == (Image *) NULL)
00817 return((Image *) NULL);
00818 if (SetImageStorageClass(convolve_image,DirectClass) == MagickFalse)
00819 {
00820 InheritException(exception,&convolve_image->exception);
00821 convolve_image=DestroyImage(convolve_image);
00822 return((Image *) NULL);
00823 }
00824 if (image->debug != MagickFalse)
00825 {
00826 char
00827 format[MaxTextExtent],
00828 *message;
00829
00830 long
00831 u,
00832 v;
00833
00834 register const double
00835 *k;
00836
00837 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
00838 " ConvolveImage with %ldx%ld kernel:",width,width);
00839 message=AcquireString("");
00840 k=kernel;
00841 for (v=0; v < (long) width; v++)
00842 {
00843 *message='\0';
00844 (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
00845 (void) ConcatenateString(&message,format);
00846 for (u=0; u < (long) width; u++)
00847 {
00848 (void) FormatMagickString(format,MaxTextExtent,"%+f ",*k++);
00849 (void) ConcatenateString(&message,format);
00850 }
00851 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
00852 }
00853 message=DestroyString(message);
00854 }
00855
00856
00857
00858 normal_kernel=(double *) AcquireQuantumMemory(width*width,
00859 sizeof(*normal_kernel));
00860 if (normal_kernel == (double *) NULL)
00861 {
00862 convolve_image=DestroyImage(convolve_image);
00863 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00864 }
00865 gamma=0.0;
00866 for (i=0; i < (long) (width*width); i++)
00867 gamma+=kernel[i];
00868 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
00869 for (i=0; i < (long) (width*width); i++)
00870 normal_kernel[i]=gamma*kernel[i];
00871
00872
00873
00874 status=MagickTrue;
00875 progress=0;
00876 GetMagickPixelPacket(image,&zero);
00877 bias=image->bias;
00878 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00879 #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
00880 #endif
00881 for (y=0; y < (long) image->rows; y++)
00882 {
00883 MagickBooleanType
00884 sync;
00885
00886 register const IndexPacket
00887 *indexes;
00888
00889 register const PixelPacket
00890 *p;
00891
00892 register IndexPacket
00893 *convolve_indexes;
00894
00895 register long
00896 x;
00897
00898 register PixelPacket
00899 *q;
00900
00901 if (status == MagickFalse)
00902 continue;
00903 p=GetVirtualPixels(image,-((long) width/2L),y-(long) (width/2L),
00904 image->columns+width,width,exception);
00905 q=GetAuthenticPixels(convolve_image,0,y,convolve_image->columns,1,
00906 exception);
00907 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00908 {
00909 status=MagickFalse;
00910 continue;
00911 }
00912 indexes=GetVirtualIndexQueue(image);
00913 convolve_indexes=GetAuthenticIndexQueue(convolve_image);
00914 for (x=0; x < (long) image->columns; x++)
00915 {
00916 long
00917 j,
00918 v;
00919
00920 MagickPixelPacket
00921 pixel;
00922
00923 register const double
00924 *k;
00925
00926 register long
00927 u;
00928
00929 pixel=zero;
00930 k=normal_kernel;
00931 j=0;
00932 if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
00933 {
00934 for (v=0; v < (long) width; v++)
00935 {
00936 for (u=0; u < (long) width; u++)
00937 {
00938 pixel.red+=(*k)*(p+u+j)->red;
00939 pixel.green+=(*k)*(p+u+j)->green;
00940 pixel.blue+=(*k)*(p+u+j)->blue;
00941 pixel.opacity+=(*k)*(p+u+j)->opacity;
00942 k++;
00943 }
00944 j+=image->columns+width;
00945 }
00946 if ((channel & RedChannel) != 0)
00947 q->red=RoundToQuantum(pixel.red+bias);
00948 if ((channel & GreenChannel) != 0)
00949 q->green=RoundToQuantum(pixel.green+bias);
00950 if ((channel & BlueChannel) != 0)
00951 q->blue=RoundToQuantum(pixel.blue+bias);
00952 if ((channel & OpacityChannel) != 0)
00953 q->opacity=RoundToQuantum(pixel.opacity+bias);
00954 if (((channel & IndexChannel) != 0) &&
00955 (image->colorspace == CMYKColorspace))
00956 {
00957 k=normal_kernel;
00958 j=0;
00959 for (v=0; v < (long) width; v++)
00960 {
00961 for (u=0; u < (long) width; u++)
00962 {
00963 pixel.index+=(*k)*indexes[x+u+j];
00964 k++;
00965 }
00966 j+=image->columns+width;
00967 }
00968 convolve_indexes[x]=RoundToQuantum(pixel.index+bias);
00969 }
00970 }
00971 else
00972 {
00973 MagickRealType
00974 alpha,
00975 gamma;
00976
00977 gamma=0.0;
00978 for (v=0; v < (long) width; v++)
00979 {
00980 for (u=0; u < (long) width; u++)
00981 {
00982 alpha=(MagickRealType) (QuantumScale*(QuantumRange-
00983 (p+u+j)->opacity));
00984 pixel.red+=(*k)*alpha*(p+u+j)->red;
00985 pixel.green+=(*k)*alpha*(p+u+j)->green;
00986 pixel.blue+=(*k)*alpha*(p+u+j)->blue;
00987 pixel.opacity+=(*k)*(p+u+j)->opacity;
00988 gamma+=alpha;
00989 k++;
00990 }
00991 j+=image->columns+width;
00992 }
00993 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
00994 if ((channel & RedChannel) != 0)
00995 q->red=RoundToQuantum(gamma*pixel.red+bias);
00996 if ((channel & GreenChannel) != 0)
00997 q->green=RoundToQuantum(gamma*pixel.green+bias);
00998 if ((channel & BlueChannel) != 0)
00999 q->blue=RoundToQuantum(gamma*pixel.blue+bias);
01000 if ((channel & OpacityChannel) != 0)
01001 q->opacity=RoundToQuantum(pixel.opacity+bias);
01002 if (((channel & IndexChannel) != 0) &&
01003 (image->colorspace == CMYKColorspace))
01004 {
01005 k=normal_kernel;
01006 j=0;
01007 for (v=0; v < (long) width; v++)
01008 {
01009