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
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
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
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177 #include "magick/studio.h"
00178 #include "magick/cache-view.h"
00179 #include "magick/color.h"
00180 #include "magick/color-private.h"
00181 #include "magick/colorspace.h"
00182 #include "magick/enhance.h"
00183 #include "magick/exception.h"
00184 #include "magick/exception-private.h"
00185 #include "magick/histogram.h"
00186 #include "magick/image.h"
00187 #include "magick/image-private.h"
00188 #include "magick/list.h"
00189 #include "magick/memory_.h"
00190 #include "magick/monitor.h"
00191 #include "magick/monitor-private.h"
00192 #include "magick/option.h"
00193 #include "magick/pixel-private.h"
00194 #include "magick/quantize.h"
00195 #include "magick/quantum.h"
00196 #include "magick/string_.h"
00197
00198
00199
00200
00201 #define CacheShift 2
00202 #define ErrorQueueLength 16
00203 #define MaxNodes 266817
00204 #define MaxTreeDepth 8
00205 #define NodesInAList 1920
00206
00207
00208
00209
00210 typedef struct _RealPixelPacket
00211 {
00212 MagickRealType
00213 red,
00214 green,
00215 blue,
00216 opacity;
00217 } RealPixelPacket;
00218
00219 typedef struct _NodeInfo
00220 {
00221 struct _NodeInfo
00222 *parent,
00223 *child[16];
00224
00225 MagickSizeType
00226 number_unique;
00227
00228 RealPixelPacket
00229 total_color;
00230
00231 MagickRealType
00232 quantize_error;
00233
00234 unsigned long
00235 color_number,
00236 id,
00237 level;
00238 } NodeInfo;
00239
00240 typedef struct _Nodes
00241 {
00242 NodeInfo
00243 *nodes;
00244
00245 struct _Nodes
00246 *next;
00247 } Nodes;
00248
00249 typedef struct _CubeInfo
00250 {
00251 NodeInfo
00252 *root;
00253
00254 unsigned long
00255 colors,
00256 maximum_colors;
00257
00258 long
00259 transparent_index;
00260
00261 MagickSizeType
00262 transparent_pixels;
00263
00264 RealPixelPacket
00265 target;
00266
00267 MagickRealType
00268 distance,
00269 pruning_threshold,
00270 next_threshold;
00271
00272 unsigned long
00273 nodes,
00274 free_nodes,
00275 color_number;
00276
00277 NodeInfo
00278 *next_node;
00279
00280 Nodes
00281 *node_queue;
00282
00283 long
00284 *cache;
00285
00286 RealPixelPacket
00287 error[ErrorQueueLength];
00288
00289 MagickRealType
00290 weights[ErrorQueueLength];
00291
00292 QuantizeInfo
00293 *quantize_info;
00294
00295 MagickBooleanType
00296 associate_alpha;
00297
00298 long
00299 x,
00300 y;
00301
00302 unsigned long
00303 depth;
00304
00305 MagickOffsetType
00306 offset;
00307
00308 MagickSizeType
00309 span;
00310 } CubeInfo;
00311
00312
00313
00314
00315 static CubeInfo
00316 *GetCubeInfo(const QuantizeInfo *,const unsigned long,const unsigned long);
00317
00318 static NodeInfo
00319 *GetNodeInfo(CubeInfo *,const unsigned long,const unsigned long,NodeInfo *);
00320
00321 static MagickBooleanType
00322 AssignImageColors(Image *,CubeInfo *),
00323 ClassifyImageColors(CubeInfo *,const Image *,ExceptionInfo *),
00324 DitherImage(Image *,CubeInfo *),
00325 SetGrayscaleImage(Image *);
00326
00327 static unsigned long
00328 DefineImageColormap(Image *,CubeInfo *,NodeInfo *);
00329
00330 static void
00331 ClosestColor(const Image *,CubeInfo *,const NodeInfo *),
00332 DestroyCubeInfo(CubeInfo *),
00333 PruneLevel(const Image *,CubeInfo *,const NodeInfo *),
00334 PruneToCubeDepth(const Image *,CubeInfo *,const NodeInfo *),
00335 ReduceImageColors(const Image *,CubeInfo *);
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359 MagickExport QuantizeInfo *AcquireQuantizeInfo(const ImageInfo *image_info)
00360 {
00361 QuantizeInfo
00362 *quantize_info;
00363
00364 quantize_info=(QuantizeInfo *) AcquireMagickMemory(sizeof(*quantize_info));
00365 if (quantize_info == (QuantizeInfo *) NULL)
00366 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
00367 GetQuantizeInfo(quantize_info);
00368 if (image_info != (ImageInfo *) NULL)
00369 {
00370 const char
00371 *option;
00372
00373 quantize_info->dither=image_info->dither;
00374 option=GetImageOption(image_info,"dither");
00375 if (option != (const char *) NULL)
00376 quantize_info->dither_method=(DitherMethod) ParseMagickOption(
00377 MagickDitherOptions,MagickFalse,option);
00378 quantize_info->measure_error=image_info->verbose;
00379 }
00380 return(quantize_info);
00381 }
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423 static inline void AssociateAlphaPixel(const CubeInfo *cube_info,
00424 const PixelPacket *pixel,RealPixelPacket *alpha_pixel)
00425 {
00426 MagickRealType
00427 alpha;
00428
00429 if ((cube_info->associate_alpha == MagickFalse) ||
00430 (pixel->opacity == OpaqueOpacity))
00431 {
00432 alpha_pixel->red=(MagickRealType) pixel->red;
00433 alpha_pixel->green=(MagickRealType) pixel->green;
00434 alpha_pixel->blue=(MagickRealType) pixel->blue;
00435 alpha_pixel->opacity=(MagickRealType) pixel->opacity;
00436 return;
00437 }
00438 alpha=(MagickRealType) (QuantumScale*(QuantumRange-pixel->opacity));
00439 alpha_pixel->red=alpha*pixel->red;
00440 alpha_pixel->green=alpha*pixel->green;
00441 alpha_pixel->blue=alpha*pixel->blue;
00442 alpha_pixel->opacity=(MagickRealType) pixel->opacity;
00443 }
00444
00445 static inline Quantum ClipToQuantum(const MagickRealType value)
00446 {
00447 if (value <= 0.0)
00448 return((Quantum) 0);
00449 if (value >= QuantumRange)
00450 return((Quantum) QuantumRange);
00451 return((Quantum) (value+0.5));
00452 }
00453
00454 static inline unsigned long ColorToNodeId(const CubeInfo *cube_info,
00455 const RealPixelPacket *pixel,unsigned long index)
00456 {
00457 unsigned long
00458 id;
00459
00460 id=(unsigned long) (
00461 ((ScaleQuantumToChar(ClipToQuantum(pixel->red)) >> index) & 0x1) |
00462 ((ScaleQuantumToChar(ClipToQuantum(pixel->green)) >> index) & 0x1) << 1 |
00463 ((ScaleQuantumToChar(ClipToQuantum(pixel->blue)) >> index) & 0x1) << 2);
00464 if (cube_info->associate_alpha != MagickFalse)
00465 id|=((ScaleQuantumToChar(ClipToQuantum(pixel->opacity)) >> index) & 0x1)
00466 << 3;
00467 return(id);
00468 }
00469
00470 static inline MagickBooleanType IsSameColor(const Image *image,
00471 const PixelPacket *p,const PixelPacket *q)
00472 {
00473 if ((p->red != q->red) || (p->green != q->green) || (p->blue != q->blue))
00474 return(MagickFalse);
00475 if ((image->matte != MagickFalse) && (p->opacity != q->opacity))
00476 return(MagickFalse);
00477 return(MagickTrue);
00478 }
00479
00480 static MagickBooleanType AssignImageColors(Image *image,CubeInfo *cube_info)
00481 {
00482 #define AssignImageTag "Assign/Image"
00483
00484 long
00485 y;
00486
00487 MagickBooleanType
00488 proceed;
00489
00490 RealPixelPacket
00491 pixel;
00492
00493 register long
00494 i,
00495 x;
00496
00497 register const NodeInfo
00498 *node_info;
00499
00500 ssize_t
00501 count;
00502
00503 unsigned long
00504 id,
00505 index;
00506
00507
00508
00509
00510 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
00511 (cube_info->quantize_info->colorspace != CMYKColorspace))
00512 (void) TransformImageColorspace((Image *) image,
00513 cube_info->quantize_info->colorspace);
00514 else
00515 if ((image->colorspace != GRAYColorspace) &&
00516 (image->colorspace != RGBColorspace) &&
00517 (image->colorspace != CMYColorspace))
00518 (void) TransformImageColorspace((Image *) image,RGBColorspace);
00519 if (AcquireImageColormap(image,cube_info->colors) == MagickFalse)
00520 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
00521 image->filename);
00522 image->colors=0;
00523 cube_info->transparent_pixels=0;
00524 cube_info->transparent_index=(-1);
00525 (void) DefineImageColormap(image,cube_info,cube_info->root);
00526
00527
00528
00529 if ((cube_info->quantize_info->dither != MagickFalse) &&
00530 (cube_info->quantize_info->dither_method != NoDitherMethod))
00531 (void) DitherImage(image,cube_info);
00532 else
00533 {
00534 ExceptionInfo
00535 *exception;
00536
00537 CacheView
00538 *image_view;
00539
00540 exception=(&image->exception);
00541 image_view=AcquireCacheView(image);
00542 for (y=0; y < (long) image->rows; y++)
00543 {
00544 register IndexPacket
00545 *__restrict indexes;
00546
00547 register PixelPacket
00548 *__restrict q;
00549
00550 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
00551 exception);
00552 if (q == (PixelPacket *) NULL)
00553 break;
00554 indexes=GetCacheViewAuthenticIndexQueue(image_view);
00555 for (x=0; x < (long) image->columns; x+=count)
00556 {
00557
00558
00559
00560 for (count=1; (x+count) < (long) image->columns; count++)
00561 if (IsSameColor(image,q,q+count) == MagickFalse)
00562 break;
00563 AssociateAlphaPixel(cube_info,q,&pixel);
00564 node_info=cube_info->root;
00565 for (index=MaxTreeDepth-1; (long) index > 0; index--)
00566 {
00567 id=ColorToNodeId(cube_info,&pixel,index);
00568 if (node_info->child[id] == (NodeInfo *) NULL)
00569 break;
00570 node_info=node_info->child[id];
00571 }
00572
00573
00574
00575 cube_info->target=pixel;
00576 cube_info->distance=(MagickRealType) (4.0*(QuantumRange+1.0)*
00577 (QuantumRange+1.0)+1.0);
00578 ClosestColor(image,cube_info,node_info->parent);
00579 index=cube_info->color_number;
00580 for (i=0; i < (long) count; i++)
00581 {
00582 if (image->storage_class == PseudoClass)
00583 indexes[x+i]=(IndexPacket) index;
00584 if (cube_info->quantize_info->measure_error == MagickFalse)
00585 {
00586 q->red=image->colormap[index].red;
00587 q->green=image->colormap[index].green;
00588 q->blue=image->colormap[index].blue;
00589 if (cube_info->associate_alpha != MagickFalse)
00590 q->opacity=image->colormap[index].opacity;
00591 }
00592 q++;
00593 }
00594 }
00595 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
00596 break;
00597 proceed=SetImageProgress(image,AssignImageTag,y,image->rows);
00598 if (proceed == MagickFalse)
00599 break;
00600 }
00601 image_view=DestroyCacheView(image_view);
00602 }
00603 if (cube_info->quantize_info->measure_error != MagickFalse)
00604 (void) GetImageQuantizeError(image);
00605 if ((cube_info->quantize_info->number_colors == 2) &&
00606 (cube_info->quantize_info->colorspace == GRAYColorspace))
00607 {
00608 Quantum
00609 intensity;
00610
00611 register PixelPacket
00612 *__restrict q;
00613
00614
00615
00616
00617 q=image->colormap;
00618 for (i=0; i < (long) image->colors; i++)
00619 {
00620 intensity=(Quantum) (PixelIntensity(q) < ((MagickRealType)
00621 QuantumRange/2.0) ? 0 : QuantumRange);
00622 q->red=intensity;
00623 q->green=intensity;
00624 q->blue=intensity;
00625 q++;
00626 }
00627 }
00628 (void) SyncImage(image);
00629 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
00630 (cube_info->quantize_info->colorspace != CMYKColorspace))
00631 (void) TransformImageColorspace((Image *) image,RGBColorspace);
00632 return(MagickTrue);
00633 }
00634
00635
00636
00637
00638
00639
00640
00641
00642
00643
00644
00645
00646
00647
00648
00649
00650
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661
00662
00663
00664
00665
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679
00680
00681
00682
00683
00684
00685
00686
00687
00688
00689
00690
00691
00692
00693
00694
00695
00696 static inline void SetAssociatedAlpha(const Image *image,CubeInfo *cube_info)
00697 {
00698 MagickBooleanType
00699 associate_alpha;
00700
00701 associate_alpha=image->matte;
00702 if (cube_info->quantize_info->colorspace == TransparentColorspace)
00703 associate_alpha=MagickFalse;
00704 if ((cube_info->quantize_info->number_colors == 2) &&
00705 (cube_info->quantize_info->colorspace == GRAYColorspace))
00706 associate_alpha=MagickFalse;
00707 cube_info->associate_alpha=associate_alpha;
00708 }
00709
00710 static MagickBooleanType ClassifyImageColors(CubeInfo *cube_info,
00711 const Image *image,ExceptionInfo *exception)
00712 {
00713 #define ClassifyImageTag "Classify/Image"
00714
00715 long
00716 y;
00717
00718 MagickBooleanType
00719 proceed;
00720
00721 MagickRealType
00722 bisect;
00723
00724 NodeInfo
00725 *node_info;
00726
00727 RealPixelPacket
00728 error,
00729 mid,
00730 midpoint,
00731 pixel;
00732
00733 size_t
00734 count;
00735
00736 unsigned long
00737 id,
00738 index,
00739 level;
00740
00741 CacheView
00742 *image_view;
00743
00744
00745
00746
00747 SetAssociatedAlpha(image,cube_info);
00748 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
00749 (cube_info->quantize_info->colorspace != CMYKColorspace))
00750 (void) TransformImageColorspace((Image *) image,
00751 cube_info->quantize_info->colorspace);
00752 else
00753 if ((image->colorspace != GRAYColorspace) &&
00754 (image->colorspace != CMYColorspace) &&
00755 (image->colorspace != RGBColorspace))
00756 (void) TransformImageColorspace((Image *) image,RGBColorspace);
00757 midpoint.red=(MagickRealType) QuantumRange/2.0;
00758 midpoint.green=(MagickRealType) QuantumRange/2.0;
00759 midpoint.blue=(MagickRealType) QuantumRange/2.0;
00760 midpoint.opacity=(MagickRealType) QuantumRange/2.0;
00761 error.opacity=0.0;
00762 image_view=AcquireCacheView(image);
00763 for (y=0; y < (long) image->rows; y++)
00764 {
00765 register const PixelPacket
00766 *__restrict p;
00767
00768 register long
00769 x;
00770
00771 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
00772 if (p == (const PixelPacket *) NULL)
00773 break;
00774 if (cube_info->nodes > MaxNodes)
00775 {
00776
00777
00778
00779 PruneLevel(image,cube_info,cube_info->root);
00780 cube_info->depth--;
00781 }
00782 for (x=0; x < (long) image->columns; x+=(long) count)
00783 {
00784
00785
00786
00787 for (count=1; (x+count) < image->columns; count++)
00788 if (IsSameColor(image,p,p+count) == MagickFalse)
00789 break;
00790 AssociateAlphaPixel(cube_info,p,&pixel);
00791 index=MaxTreeDepth-1;
00792 bisect=((MagickRealType) QuantumRange+1.0)/2.0;
00793 mid=midpoint;
00794 node_info=cube_info->root;
00795 for (level=1; level <= MaxTreeDepth; level++)
00796 {
00797 bisect*=0.5;
00798 id=ColorToNodeId(cube_info,&pixel,index);
00799 mid.red+=(id & 1) != 0 ? bisect : -bisect;
00800 mid.green+=(id & 2) != 0 ? bisect : -bisect;
00801 mid.blue+=(id & 4) != 0 ? bisect : -bisect;
00802 mid.opacity+=(id & 8) != 0 ? bisect : -bisect;
00803 if (node_info->child[id] == (NodeInfo *) NULL)
00804 {
00805
00806
00807
00808 node_info->child[id]=GetNodeInfo(cube_info,id,level,node_info);
00809 if (node_info->child[id] == (NodeInfo *) NULL)
00810 (void) ThrowMagickException(exception,GetMagickModule(),
00811 ResourceLimitError,"MemoryAllocationFailed","`%s'",
00812 image->filename);
00813 if (level == MaxTreeDepth)
00814 cube_info->colors++;
00815 }
00816
00817
00818
00819 node_info=node_info->child[id];
00820 error.red=QuantumScale*(pixel.red-mid.red);
00821 error.green=QuantumScale*(pixel.green-mid.green);
00822 error.blue=QuantumScale*(pixel.blue-mid.blue);
00823 if (cube_info->associate_alpha != MagickFalse)
00824 error.opacity=QuantumScale*(pixel.opacity-mid.opacity);
00825 node_info->quantize_error+=sqrt((double) (count*error.red*error.red+
00826 count*error.green*error.green+count*error.blue*error.blue+
00827 count*error.opacity*error.opacity));
00828 cube_info->root->quantize_error+=node_info->quantize_error;
00829 index--;
00830 }
00831
00832
00833
00834 node_info->number_unique+=count;
00835 node_info->total_color.red+=count*QuantumScale*pixel.red;
00836 node_info->total_color.green+=count*QuantumScale*pixel.green;
00837 node_info->total_color.blue+=count*QuantumScale*pixel.blue;
00838 if (cube_info->associate_alpha != MagickFalse)
00839 node_info->total_color.opacity+=count*QuantumScale*pixel.opacity;
00840 p+=count;
00841 }
00842 if (cube_info->colors > cube_info->maximum_colors)
00843 {
00844 PruneToCubeDepth(image,cube_info,cube_info->root);
00845 break;
00846 }
00847 proceed=SetImageProgress(image,ClassifyImageTag,y,image->rows);
00848 if (proceed == MagickFalse)
00849 break;
00850 }
00851 for (y++; y < (long) image->rows; y++)
00852 {
00853 register const PixelPacket
00854 *__restrict p;
00855
00856 register long
00857 x;
00858
00859 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
00860 if (p == (const PixelPacket *) NULL)
00861 break;
00862 if (cube_info->nodes > MaxNodes)
00863 {
00864
00865
00866
00867 PruneLevel(image,cube_info,cube_info->root);
00868 cube_info->depth--;
00869 }
00870 for (x=0; x < (long) image->columns; x+=(long) count)
00871 {
00872
00873
00874
00875 for (count=1; (x+count) < image->columns; count++)
00876 if (IsSameColor(image,p,p+count) == MagickFalse)
00877 break;
00878 AssociateAlphaPixel(cube_info,p,&pixel);
00879 index=MaxTreeDepth-1;
00880 bisect=((MagickRealType) QuantumRange+1.0)/2.0;
00881 mid=midpoint;
00882 node_info=cube_info->root;
00883 for (level=1; level <= cube_info->depth; level++)
00884 {
00885 bisect*=0.5;
00886 id=ColorToNodeId(cube_info,&pixel,index);
00887 mid.red+=(id & 1) != 0 ? bisect : -bisect;
00888 mid.green+=(id & 2) != 0 ? bisect : -bisect;
00889 mid.blue+=(id & 4) != 0 ? bisect : -bisect;
00890 mid.opacity+=(id & 8) != 0 ? bisect : -bisect;
00891 if (node_info->child[id] == (NodeInfo *) NULL)
00892 {
00893
00894
00895
00896 node_info->child[id]=GetNodeInfo(cube_info,id,level,node_info);
00897 if (node_info->child[id] == (NodeInfo *) NULL)
00898 (void) ThrowMagickException(exception,GetMagickModule(),
00899 ResourceLimitError,"MemoryAllocationFailed","%s",
00900 image->filename);
00901 if (level == cube_info->depth)
00902 cube_info->colors++;
00903 }
00904
00905
00906
00907 node_info=node_info->child[id];
00908 error.red=QuantumScale*(pixel.red-mid.red);
00909 error.green=QuantumScale*(pixel.green-mid.green);
00910 error.blue=QuantumScale*(pixel.blue-mid.blue);
00911 if (cube_info->associate_alpha != MagickFalse)
00912 error.opacity=QuantumScale*(pixel.opacity-mid.opacity);
00913 node_info->quantize_error+=sqrt((double) (count*error.red*error.red+
00914 count*error.green*error.green+error.blue*error.blue+
00915 count*error.opacity*error.opacity));
00916 cube_info->root->quantize_error+=node_info->quantize_error;
00917 index--;
00918 }
00919
00920
00921
00922 node_info->number_unique+=count;
00923 node_info->total_color.red+=count*QuantumScale*pixel.red;
00924 node_info->total_color.green+=count*QuantumScale*pixel.green;
00925 node_info->total_color.blue+=count*QuantumScale*pixel.blue;
00926 if (cube_info->associate_alpha != MagickFalse)
00927 node_info->total_color.opacity+=count*QuantumScale*pixel.opacity;
00928 p+=count;
00929 }
00930 proceed=SetImageProgress(image,ClassifyImageTag,y,image->rows);
00931 if (proceed == MagickFalse)
00932 break;
00933 }
00934 image_view=DestroyCacheView(image_view);
00935 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
00936 (cube_info->quantize_info->colorspace != CMYKColorspace))
00937 (void) TransformImageColorspace((Image *) image,RGBColorspace);
00938 return(MagickTrue);
00939 }
00940
00941
00942
00943
00944
00945
00946
00947
00948
00949
00950
00951
00952
00953
00954
00955
00956
00957
00958
00959
00960
00961
00962
00963
00964
00965
00966
00967 MagickExport QuantizeInfo *CloneQuantizeInfo(const QuantizeInfo *quantize_info)
00968 {
00969 QuantizeInfo
00970 *clone_info;
00971
00972 clone_info=(QuantizeInfo *) AcquireMagickMemory(sizeof(*clone_info));
00973 if (clone_info == (QuantizeInfo *) NULL)
00974 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
00975 GetQuantizeInfo(clone_info);
00976 if (quantize_info == (QuantizeInfo *) NULL)
00977 return(clone_info);
00978 clone_info->number_colors=quantize_info->number_colors;
00979 clone_info->tree_depth=quantize_info->tree_depth;
00980 clone_info->dither=quantize_info->dither;
00981 clone_info->dither_method=quantize_info->dither_method;
00982 clone_info->colorspace=quantize_info->colorspace;
00983 clone_info->measure_error=quantize_info->measure_error;
00984 return(clone_info);
00985 }
00986
00987
00988
00989
00990
00991
00992
00993
00994
00995
00996
00997
00998
00999
01000
01001
01002
01003
01004
01005
01006
01007
01008
01009
01010
01011
01012
01013
01014
01015
01016 static void ClosestColor(const Image *image,CubeInfo *cube_info,
01017 const NodeInfo *node_info)
01018 {
01019 register long
01020 i;
01021
01022 unsigned long
01023 number_children;
01024
01025
01026
01027
01028 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
01029 for (i=0; i < (long) number_children; i++)
01030 if (node_info->child[i] != (NodeInfo *) NULL)
01031 ClosestColor(image,cube_info,node_info->child[i]);
01032 if (node_info->number_unique != 0)
01033 {
01034 MagickRealType
01035 pixel;
01036
01037 register MagickRealType
01038 alpha,
01039 beta,
01040 distance;
01041
01042 register PixelPacket
01043 *__restrict p;
01044
01045 register RealPixelPacket
01046 *__restrict q;
01047
01048
01049
01050
01051 p=image->colormap+node_info->color_number;
01052 q=(&cube_info->target);
01053 alpha=1.0;
01054 beta=1.0;
01055 if (cube_info->associate_alpha == MagickFalse)
01056 {
01057 alpha=(MagickRealType) (QuantumScale*(QuantumRange-p->opacity));
01058 beta=(MagickRealType) (QuantumScale*(QuantumRange-q->opacity));
01059 }
01060 pixel=alpha*p->red-beta*q->red;
01061 distance=pixel*pixel;
01062 if (distance < cube_info->distance)
01063 {
01064 pixel=alpha*p->green-beta*q->green;
01065 distance+=pixel*pixel;
01066 if (distance < cube_info->distance)
01067 {
01068 pixel=alpha*p->blue-beta*q->blue;
01069 distance+=pixel*pixel;
01070 if (distance < cube_info->distance)
01071 {
01072 pixel=alpha-beta;
01073 distance+=pixel*pixel;
01074 if (distance < cube_info->distance)
01075 {
01076 cube_info->distance=distance;
01077 cube_info->color_number=node_info->color_number;
01078 }
01079 }
01080 }
01081 }
01082 }
01083 }
01084
01085
01086
01087
01088
01089
01090
01091
01092
01093
01094
01095
01096
01097
01098
01099
01100
01101
01102
01103
01104
01105
01106
01107
01108 MagickExport MagickBooleanType CompressImageColormap(Image *image)
01109 {
01110 QuantizeInfo
01111 quantize_info;
01112
01113 assert(image != (Image *) NULL);
01114 assert(image->signature == MagickSignature);
01115 if (image->debug != MagickFalse)
01116 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01117 if (IsPaletteImage(image,&image->exception) == MagickFalse)
01118 return(MagickFalse);
01119 GetQuantizeInfo(&quantize_info);
01120 quantize_info.number_colors=image->colors;
01121 quantize_info.tree_depth=MaxTreeDepth;
01122 return(QuantizeImage(&quantize_info,image));
01123 }
01124
01125
01126
01127
01128
01129
01130
01131
01132
01133
01134
01135
01136
01137
01138
01139
01140
01141
01142
01143
01144
01145
01146
01147
01148
01149
01150
01151
01152
01153
01154
01155
01156 static unsigned long DefineImageColormap(Image *image,CubeInfo *cube_info,
01157 NodeInfo *node_info)
01158 {
01159 register long
01160 i;
01161
01162 unsigned long
01163 number_children;
01164
01165
01166
01167
01168 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
01169 for (i=0; i < (long) number_children; i++)
01170 if (node_info->child[i] != (NodeInfo *) NULL)
01171 DefineImageColormap(image,cube_info,node_info->child[i]);
01172 if (node_info->number_unique != 0)
01173 {
01174 register MagickRealType
01175 alpha;
01176
01177 register PixelPacket
01178 *__restrict q;
01179
01180
01181
01182
01183 q=image->colormap+image->colors;
01184 alpha=(MagickRealType) ((MagickOffsetType) node_info->number_unique);
01185 alpha=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha);
01186 if (cube_info->associate_alpha == MagickFalse)
01187 {
01188 q->red=RoundToQuantum((MagickRealType) (alpha*QuantumRange*
01189 node_info->total_color.red));
01190 q->green=RoundToQuantum((MagickRealType) (alpha*QuantumRange*
01191 node_info->total_color.green));
01192 q->blue=RoundToQuantum((MagickRealType) (alpha*QuantumRange*
01193 node_info->total_color.blue));
01194 q->opacity=OpaqueOpacity;
01195 }
01196 else
01197 {
01198 MagickRealType
01199 opacity;
01200
01201 opacity=(MagickRealType) (alpha*QuantumRange*
01202 node_info->total_color.opacity);
01203 q->opacity=RoundToQuantum(opacity);
01204 if (q->opacity == OpaqueOpacity)
01205 {
01206 q->red=RoundToQuantum((MagickRealType) (alpha*QuantumRange*
01207 node_info->total_color.red));
01208 q->green=RoundToQuantum((MagickRealType) (alpha*QuantumRange*
01209 node_info->total_color.green));
01210 q->blue=RoundToQuantum((MagickRealType) (alpha*QuantumRange*
01211 node_info->total_color.blue));
01212 }
01213 else
01214 {
01215 MagickRealType
01216 gamma;
01217
01218 gamma=(MagickRealType) (QuantumScale*(QuantumRange-
01219 (MagickRealType) q->opacity));
01220 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
01221 q->red=RoundToQuantum((MagickRealType) (alpha*gamma*QuantumRange*
01222 node_info->total_color.red));
01223 q->green=RoundToQuantum((MagickRealType) (alpha*gamma*
01224 QuantumRange*node_info->total_color.green));
01225 q->blue=RoundToQuantum((MagickRealType) (alpha*gamma*QuantumRange*
01226 node_info->total_color.blue));
01227 if (node_info->number_unique > cube_info->transparent_pixels)
01228 {
01229 cube_info->transparent_pixels=node_info->number_unique;
01230 cube_info->transparent_index=(long) image->colors;
01231 }
01232 }
01233 }
01234 node_info->color_number=image->colors++;
01235 }
01236 return(image->colors);
01237 }
01238
01239
01240
01241
01242
01243
01244
01245
01246
01247
01248
01249
01250
01251
01252
01253
01254
01255
01256
01257
01258
01259
01260
01261 static void DestroyCubeInfo(CubeInfo *cube_info)
01262 {
01263 register Nodes
01264 *nodes;
01265
01266
01267
01268
01269 do
01270 {
01271 nodes=cube_info->node_queue->next;
01272 cube_info->node_queue->nodes=(NodeInfo *) RelinquishMagickMemory(
01273 cube_info->node_queue->nodes);
01274 cube_info->node_queue=(Nodes *) RelinquishMagickMemory(
01275 cube_info->node_queue);
01276 cube_info->node_queue=nodes;
01277 } while (cube_info->node_queue != (Nodes *) NULL);
01278 if (cube_info->cache != (long *) NULL)
01279 cube_info->cache=(long *) RelinquishMagickMemory(cube_info->cache);
01280 cube_info->quantize_info=DestroyQuantizeInfo(cube_info->quantize_info);
01281 cube_info=(CubeInfo *) RelinquishMagickMemory(cube_info);
01282 }
01283
01284
01285
01286
01287
01288
01289
01290
01291
01292
01293
01294
01295
01296
01297
01298
01299
01300
01301
01302
01303
01304
01305
01306
01307 MagickExport QuantizeInfo *DestroyQuantizeInfo(QuantizeInfo *quantize_info)
01308 {
01309 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
01310 assert(quantize_info != (QuantizeInfo *) NULL);
01311 assert(quantize_info->signature == MagickSignature);
01312 quantize_info->signature=(~MagickSignature);
01313 quantize_info=(QuantizeInfo *) RelinquishMagickMemory(quantize_info);
01314 return(quantize_info);
01315 }
01316
01317
01318
01319
01320
01321
01322
01323
01324
01325
01326
01327
01328
01329
01330
01331
01332
01333
01334
01335
01336
01337
01338
01339
01340
01341
01342
01343
01344
01345 static MagickBooleanType FloydSteinbergDither(Image *image,CubeInfo *cube_info)
01346 {
01347 #define DitherImageTag "Dither/Image"
01348
01349 ExceptionInfo
01350 *exception;
01351
01352 long
01353 u,
01354 v,
01355 y;
01356
01357 MagickBooleanType
01358 proceed;
01359
01360 RealPixelPacket
01361 color,
01362 *current,
01363 pixel,
01364 *previous,
01365 *scanlines;
01366
01367 register CubeInfo
01368 *p;
01369
01370 unsigned long
01371 index;
01372
01373 CacheView
01374 *image_view;
01375
01376
01377
01378
01379 scanlines=(RealPixelPacket *) AcquireQuantumMemory(image->columns,
01380 2*sizeof(*scanlines));
01381 if (scanlines == (RealPixelPacket *) NULL)
01382 return(MagickFalse);
01383 p=cube_info;
01384 exception=(&image->exception);
01385 image_view=AcquireCacheView(image);
01386 for (y=0; y < (long) image->rows; y++)
01387 {
01388 register IndexPacket
01389 *__restrict indexes;
01390
01391 register long
01392 i,
01393 x;
01394
01395 register PixelPacket
01396 *__restrict q;
01397
01398 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
01399 if (q == (PixelPacket *) NULL)
01400 return(MagickFalse);
01401 indexes=GetCacheViewAuthenticIndexQueue(image_view);
01402 current=scanlines+(y & 0x01)*image->columns;
01403 previous=scanlines+((y+1) & 0x01)*image->columns;
01404 v=(y & 0x01) ? -1 : 1;
01405 for (x=0; x < (long) image->columns; x++)
01406 {
01407 u=(y & 0x01) ? (long) image->columns-1-x : x;
01408 AssociateAlphaPixel(cube_info,q+u,&pixel);
01409 if (x > 0)
01410 {
01411 pixel.red+=7*current[u-v].red/16;
01412 pixel.green+=7*current[u-v].green/16;
01413 pixel.blue+=7*current[u-v].blue/16;
01414 if (cube_info->associate_alpha != MagickFalse)
01415 pixel.opacity+=7*current[u-v].opacity/16;
01416 }
01417 if (y > 0)
01418 {
01419 if (x < (long) (image->columns-1))
01420 {
01421 pixel.red+=previous[u+v].red/16;
01422 pixel.green+=previous[u+v].green/16;
01423 pixel.blue+=previous[u+v].blue/16;
01424 if (cube_info->associate_alpha != MagickFalse)
01425 pixel.opacity+=previous[u+v].opacity/16;
01426 }
01427 pixel.red+=5*previous[u].red/16;
01428 pixel.green+=5*previous[u].green/16;
01429 pixel.blue+=5*previous[u].blue/16;
01430 if (cube_info->associate_alpha != MagickFalse)
01431 pixel.opacity+=5*previous[u].opacity/16;
01432 if (x > 0)
01433 {
01434 pixel.red+=3*previous[u-v].red/16;
01435 pixel.green+=3*previous[u-v].green/16;
01436 pixel.blue+=3*previous[u-v].blue/16;
01437 if (cube_info->associate_alpha != MagickFalse)
01438 pixel.opacity+=3*previous[u-v].opacity/16;
01439 }
01440 }
01441 pixel.red=(MagickRealType) ClipToQuantum(pixel.red);
01442 pixel.green=(MagickRealType) ClipToQuantum(pixel.green);
01443 pixel.blue=(MagickRealType) ClipToQuantum(pixel.blue);
01444 if (cube_info->associate_alpha != MagickFalse)
01445 pixel.opacity=(MagickRealType) ClipToQuantum(pixel.opacity);
01446 i=(long) ((ScaleQuantumToChar(ClipToQuantum(pixel.red)) >> CacheShift) |
01447 (ScaleQuantumToChar(ClipToQuantum(pixel.green)) >> CacheShift) << 6 |
01448 (ScaleQuantumToChar(ClipToQuantum(pixel.blue)) >> CacheShift) << 12);
01449 if (cube_info->associate_alpha != MagickFalse)
01450 i|=((ScaleQuantumToChar(ClipToQuantum(pixel.opacity)) >> CacheShift)
01451 << 18);
01452 if (p->cache[i] < 0)
01453 {
01454 register NodeInfo
01455 *node_info;
01456
01457 register unsigned long
01458 id;
01459
01460
01461
01462
01463 node_info=p->root;
01464 for (index=MaxTreeDepth-1; (long) index > 0; index--)
01465 {
01466 id=ColorToNodeId(cube_info,&pixel,index);
01467 if (node_info->child[id] == (NodeInfo *) NULL)
01468 break;
01469 node_info=node_info->child[id];
01470 }
01471
01472
01473
01474 p->target=pixel;
01475 p->distance=(MagickRealType) (4.0*(QuantumRange+1.0)*(QuantumRange+
01476 1.0)+1.0);
01477 ClosestColor(image,p,node_info->parent);
01478 p->cache[i]=(long) p->color_number;
01479 }
01480
01481
01482
01483 index=(unsigned long) p->cache[i];
01484 if (image->storage_class == PseudoClass)
01485 indexes[u]=(IndexPacket) index;
01486 if (cube_info->quantize_info->measure_error == MagickFalse)
01487 {
01488 (q+u)->red=image->colormap[index].red;
01489 (q+u)->green=image->colormap[index].green;
01490 (q+u)->blue=image->colormap[index].blue;
01491 if (cube_info->associate_alpha != MagickFalse)
01492 (q+u)->opacity=image->colormap[index].opacity;
01493 }
01494 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
01495 return(MagickFalse);
01496
01497
01498
01499 AssociateAlphaPixel(cube_info,image->colormap+index,&color);
01500 current[u].red=pixel.red-color.red;
01501 current[u].green=pixel.green-color.green;
01502 current[u].blue=pixel.blue-color.blue;
01503 if (cube_info->associate_alpha != MagickFalse)
01504 current[u].opacity=pixel.opacity-color.opacity;
01505 proceed=SetImageProgress(image,DitherImageTag,p->offset,p->span);
01506 if (proceed == MagickFalse)
01507 return(MagickFalse);
01508 p->offset++;
01509 }
01510 }
01511 scanlines=(RealPixelPacket *) RelinquishMagickMemory(scanlines);
01512 image_view=DestroyCacheView(image_view);
01513 return(MagickTrue);
01514 }
01515
01516 static MagickBooleanType
01517 RiemersmaDither(Image *,CacheView *,CubeInfo *,const unsigned int);
01518
01519 static void Riemersma(Image *image,CacheView *image_view,CubeInfo *cube_info,
01520 const unsigned long level,const unsigned int direction)
01521 {
01522 if (level == 1)
01523 switch (direction)
01524 {
01525 case WestGravity:
01526 {
01527 (void) RiemersmaDither(image,image_view,cube_info,EastGravity);
01528 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity);
01529 (void) RiemersmaDither(image,image_view,cube_info,WestGravity);
01530 break;
01531 }
01532 case EastGravity:
01533 {
01534 (void) RiemersmaDither(image,image_view,cube_info,WestGravity);
01535 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity);
01536 (void) RiemersmaDither(image,image_view,cube_info,EastGravity);
01537 break;
01538 }
01539 case NorthGravity:
01540 {
01541 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity);
01542 (void) RiemersmaDither(image,image_view,cube_info,EastGravity);
01543 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity);
01544 break;
01545 }
01546 case SouthGravity:
01547 {
01548 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity);
01549 (void) RiemersmaDither(image,image_view,cube_info,WestGravity);
01550 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity);
01551 break;
01552 }
01553 default:
01554 break;
01555 }
01556 else
01557 switch (direction)
01558 {
01559 case WestGravity:
01560 {
01561 Riemersma(image,image_view,cube_info,level-1,NorthGravity);
01562 (void) RiemersmaDither(image,image_view,cube_info,EastGravity);
01563 Riemersma(image,image_view,cube_info,level-1,WestGravity);
01564 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity);
01565 Riemersma(image,image_view,cube_info,level-1,WestGravity);
01566 (void) RiemersmaDither(image,image_view,cube_info,WestGravity);
01567 Riemersma(image,image_view,cube_info,level-1,SouthGravity);
01568 break;
01569 }
01570 case EastGravity:
01571 {
01572 Riemersma(image,image_view,cube_info,level-1,SouthGravity);
01573 (void) RiemersmaDither(image,image_view,cube_info,WestGravity);
01574 Riemersma(image,image_view,cube_info,level-1,EastGravity);
01575 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity);
01576 Riemersma(image,image_view,cube_info,level-1,EastGravity);
01577 (void) RiemersmaDither(image,image_view,cube_info,EastGravity);
01578 Riemersma(image,image_view,cube_info,level-1,NorthGravity);
01579 break;
01580 }
01581 case NorthGravity:
01582 {
01583 Riemersma(image,image_view,cube_info,level-1,WestGravity);
01584 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity);
01585 Riemersma(image,image_view,cube_info,level-1,NorthGravity);
01586 (void) RiemersmaDither(image,image_view,cube_info,EastGravity);
01587 Riemersma(image,image_view,cube_info,level-1,NorthGravity);
01588 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity);
01589 Riemersma(image,image_view,cube_info,level-1,EastGravity);
01590 break;
01591 }
01592 case SouthGravity:
01593 {
01594 Riemersma(image,image_view,cube_info,level-1,EastGravity);
01595 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity);
01596 Riemersma(image,image_view,cube_info,level-1,SouthGravity);
01597 (void) RiemersmaDither(image,image_view,cube_info,WestGravity);
01598 Riemersma(image,image_view,cube_info,level-1,SouthGravity);
01599 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity);
01600 Riemersma(image,image_view,cube_info,level-1,WestGravity);
01601 break;
01602 }
01603 default:
01604 break;
01605 }
01606 }
01607
01608 static MagickBooleanType RiemersmaDither(Image *image,CacheView *image_view,
01609 CubeInfo *cube_info,const unsigned int direction)
01610 {
01611 #define DitherImageTag "Dither/Image"
01612
01613 MagickBooleanType
01614 proceed;
01615
01616 RealPixelPacket
01617 color,
01618 pixel;
01619
01620 register CubeInfo
01621 *p;
01622
01623 unsigned long
01624 index;
01625
01626 p=cube_info;
01627 if ((p->x >= 0) && (p->x < (long) image->columns) &&
01628 (p->y >= 0) && (p->y < (long) image->rows))
01629 {
01630 ExceptionInfo
01631 *exception;
01632
01633 register IndexPacket
01634 *__restrict indexes;
01635
01636 register long
01637 i;
01638
01639 register PixelPacket
01640 *__restrict q;
01641
01642
01643
01644
01645 exception=(&image->exception);
01646 q=GetCacheViewAuthenticPixels(image_view,p->x,p->y,1,1,exception);
01647 if (q == (PixelPacket *) NULL)
01648 return(MagickFalse);
01649 indexes=GetCacheViewAuthenticIndexQueue(image_view);
01650 AssociateAlphaPixel(cube_info,q,&pixel);
01651 for (i=0; i < ErrorQueueLength; i++)
01652 {
01653 pixel.red+=p->weights[i]*p->error[i].red;
01654 pixel.green+=p->weights[i]*p->error[i].green;
01655 pixel.blue+=p->weights[i]*p->error[i].blue;
01656 if (cube_info->associate_alpha != MagickFalse)
01657 pixel.opacity+=p->weights[i]*p->error[i].opacity;
01658 }
01659 pixel.red=(MagickRealType) ClipToQuantum(pixel.red);
01660 pixel.green=(MagickRealType) ClipToQuantum(pixel.green);
01661 pixel.blue=(MagickRealType) ClipToQuantum(pixel.blue);
01662 if (cube_info->associate_alpha != MagickFalse)
01663 pixel.opacity=(MagickRealType) ClipToQuantum(pixel.opacity);
01664 i=(long) ((ScaleQuantumToChar(ClipToQuantum(pixel.red)) >> CacheShift) |
01665 (ScaleQuantumToChar(ClipToQuantum(pixel.green)) >> CacheShift) << 6 |
01666 (ScaleQuantumToChar(ClipToQuantum(pixel.blue)) >> CacheShift) << 12);
01667 if (cube_info->associate_alpha != MagickFalse)
01668 i|=((ScaleQuantumToChar(ClipToQuantum(pixel.opacity)) >> CacheShift)
01669 << 18);
01670 if (p->cache[i] < 0)
01671 {
01672 register NodeInfo
01673 *node_info;
01674
01675 register unsigned long
01676 id;
01677
01678
01679
01680
01681 node_info=p->root;
01682 for (index=MaxTreeDepth-1; (long) index > 0; index--)
01683 {
01684 id=ColorToNodeId(cube_info,&pixel,index);
01685 if (node_info->child[id] == (NodeInfo *) NULL)
01686 break;
01687 node_info=node_info->child[id];
01688 }
01689
01690
01691
01692 p->target=pixel;
01693 p->distance=(MagickRealType) (4.0*(QuantumRange+1.0)*((MagickRealType)
01694 QuantumRange+1.0)+1.0);
01695 ClosestColor(image,p,node_info->parent);
01696 p->cache[i]=(long) p->color_number;
01697 }
01698
01699
01700
01701 index=(unsigned long) (1*p->cache[i]);
01702 if (image->storage_class == PseudoClass)
01703 *indexes=(IndexPacket) index;
01704 if (cube_info->quantize_info->measure_error == MagickFalse)
01705 {
01706 q->red=image->colormap[index].red;
01707 q->green=image->colormap[index].green;
01708 q->blue=image->colormap[index].blue;
01709 if (cube_info->associate_alpha != MagickFalse)
01710 q->opacity=image->colormap[index].opacity;
01711 }
01712 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
01713 return(MagickFalse);
01714
01715
01716
01717 (void) CopyMagickMemory(p->error,p->error+1,(ErrorQueueLength-1)*
01718 sizeof(p->error[0]));
01719 AssociateAlphaPixel(cube_info,image->colormap+index,&color);
01720 p->error[ErrorQueueLength-1].red=pixel.red-color.red;
01721 p->error[ErrorQueueLength-1].green=pixel.green-color.green;
01722 p->error[ErrorQueueLength-1].blue=pixel.blue-color.blue;
01723 if (cube_info->associate_alpha != MagickFalse)
01724 p->error[ErrorQueueLength-1].opacity=pixel.opacity-color.opacity;
01725 proceed=SetImageProgress(image,DitherImageTag,p->offset,p->span);
01726 if (proceed == MagickFalse)
01727 return(MagickFalse);
01728 p->offset++;
01729 }
01730 switch (direction)
01731 {
01732 case WestGravity: p->x--; break;
01733 case EastGravity: p->x++; break;
01734 case NorthGravity: p->y--; break;
01735 case SouthGravity: p->y++; break;
01736 }
01737 return(MagickTrue);
01738 }
01739
01740 static inline long MagickMax(const long x,const long y)
01741 {
01742 if (x > y)
01743 return(x);
01744 return(y);
01745 }
01746
01747 static inline long MagickMin(const long x,const long y)
01748 {
01749 if (x < y)
01750 return(x);
01751 return(y);
01752 }
01753
01754 static MagickBooleanType DitherImage(Image *image,CubeInfo *cube_info)
01755 {
01756 MagickBooleanType
01757 status;
01758
01759 register long
01760 i;
01761
01762 unsigned long
01763 depth;
01764
01765 CacheView
01766 *image_view;
01767
01768 if (cube_info->quantize_info->dither_method == FloydSteinbergDitherMethod)
01769 return(FloydSteinbergDither(image,cube_info));
01770
01771
01772
01773 (void) ResetMagickMemory(cube_info->error,0,ErrorQueueLength*
01774 sizeof(*cube_info->error));
01775 cube_info->x=0;
01776 cube_info->y=0;
01777 i=MagickMax((long) image->columns,(long) image->rows);
01778 for (depth=1; i != 0; depth++)
01779 i>>=1;
01780 if ((long) (1L << depth) < MagickMax((long) image->columns,(long) image->rows))
01781 depth++;
01782 cube_info->offset=0;
01783 cube_info->span=(MagickSizeType) image->columns*image->rows;
01784 image_view=AcquireCacheView(image);
01785 if (depth > 1)
01786 Riemersma(image,image_view,cube_info,depth-1,NorthGravity);
01787 status=RiemersmaDither(image,image_view,cube_info,ForgetGravity);
01788 image_view=DestroyCacheView(image_view);
01789 return(status);
01790 }
01791
01792
01793
01794
01795
01796
01797
01798
01799
01800
01801
01802
01803
01804
01805
01806
01807
01808
01809
01810
01811
01812
01813
01814
01815
01816
01817
01818
01819
01820
01821
01822
01823
01824
01825
01826 static CubeInfo *GetCubeInfo(const QuantizeInfo *quantize_info,
01827 const unsigned long depth,const unsigned long maximum_colors)
01828 {
01829 CubeInfo
01830 *cube_info;
01831
01832 MagickRealType
01833 sum,
01834 weight;
01835
01836 size_t
01837 length;
01838
01839 register long
01840 i;
01841
01842
01843
01844
01845 cube_info=(CubeInfo *) AcquireMagickMemory(sizeof(*cube_info));
01846 if (cube_info == (CubeInfo *) NULL)
01847 return((CubeInfo *) NULL);
01848 (void) ResetMagickMemory(cube_info,0,sizeof(*cube_info));
01849 cube_info->depth=depth;
01850 if (cube_info->depth > MaxTreeDepth)
01851 cube_info->depth=MaxTreeDepth;
01852 if (cube_info->depth < 2)
01853 cube_info->depth=2;
01854 cube_info->maximum_colors=maximum_colors;
01855
01856
01857
01858 cube_info->root=GetNodeInfo(cube_info,0,0,(NodeInfo *) NULL);
01859 if (cube_info->root == (NodeInfo *) NULL)
01860 return((CubeInfo *) NULL);
01861 cube_info->root->parent=cube_info->root;
01862 cube_info->quantize_info=CloneQuantizeInfo(quantize_info);
01863 if (cube_info->quantize_info->dither == MagickFalse)
01864 return(cube_info);
01865
01866
01867
01868 length=(size_t) (1UL << (4*(8-CacheShift)));
01869 cube_info->cache=(long *) AcquireQuantumMemory(length,
01870 sizeof(*cube_info->cache));
01871 if (cube_info->cache == (long *) NULL)
01872 return((CubeInfo *) NULL);
01873
01874
01875
01876 for (i=0; i < (long) length; i++)
01877 cube_info->cache[i]=(-1);
01878
01879
01880
01881 weight=1.0;
01882 for (i=0; i < ErrorQueueLength; i++)
01883 {
01884 cube_info->weights[ErrorQueueLength-i-1]=1.0/weight;
01885 weight*=exp(log(((double) QuantumRange+1.0))/(ErrorQueueLength-1.0));
01886 }
01887
01888
01889
01890 weight=0.0;
01891 for (i=0; i < ErrorQueueLength; i++)
01892 weight+=cube_info->weights[i];
01893 sum=0.0;
01894 for (i=0; i < ErrorQueueLength; i++)
01895 {
01896 cube_info->weights[i]/=weight;
01897 sum+=cube_info->weights[i];
01898 }
01899 cube_info->weights[0]+=1.0-sum;
01900 return(cube_info);
01901 }
01902
01903
01904
01905
01906
01907
01908
01909
01910
01911
01912
01913
01914
01915
01916
01917
01918
01919
01920
01921
01922
01923
01924
01925
01926
01927
01928
01929
01930
01931 static NodeInfo *GetNodeInfo(CubeInfo *cube_info,const unsigned long id,
01932 const unsigned long level,NodeInfo *parent)
01933 {
01934 NodeInfo
01935 *node_info;
01936
01937 if (cube_info->free_nodes == 0)
01938 {
01939 Nodes
01940 *nodes;
01941
01942
01943
01944
01945 nodes=(Nodes *) AcquireMagickMemory(sizeof(*nodes));
01946 if (nodes == (Nodes *) NULL)
01947 return((NodeInfo *) NULL);
01948 nodes->nodes=(NodeInfo *) AcquireQuantumMemory(NodesInAList,
01949 sizeof(*nodes->nodes));
01950 if (nodes->nodes == (NodeInfo *) NULL)
01951 return((NodeInfo *) NULL);
01952 nodes->next=cube_info->node_queue;
01953 cube_info->node_queue=nodes;
01954 cube_info->next_node=nodes->nodes;
01955 cube_info->free_nodes=NodesInAList;
01956 }
01957 cube_info->nodes++;
01958 cube_info->free_nodes--;
01959 node_info=cube_info->next_node++;
01960 (void) ResetMagickMemory(node_info,0,sizeof(*node_info));
01961 node_info->parent=parent;
01962 node_info->id=id;
01963 node_info->level=level;
01964 return(node_info);
01965 }
01966
01967
01968
01969
01970
01971
01972
01973
01974
01975
01976
01977
01978
01979
01980
01981
01982
01983
01984
01985
01986
01987
01988
01989
01990
01991
01992
01993
01994
01995
01996
01997
01998
01999
02000
02001
02002
02003
02004
02005
02006 MagickExport MagickBooleanType GetImageQuantizeError(Image *image)
02007 {
02008 ExceptionInfo
02009 *exception;
02010
02011 IndexPacket
02012 *indexes;
02013
02014 long
02015 y;
02016
02017 MagickRealType
02018 alpha,
02019 area,
02020 beta,
02021 distance,
02022 maximum_error,
02023 mean_error,
02024 mean_error_per_pixel;
02025
02026 unsigned long
02027 index;
02028
02029 CacheView
02030 *image_view;
02031
02032 assert(image != (Image *) NULL);
02033 assert(image->signature == MagickSignature);
02034 if (image->debug != MagickFalse)
02035 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02036 image->total_colors=GetNumberColors(image,(FILE *) NULL,&image->exception);
02037 (void) ResetMagickMemory(&image->error,0,sizeof(image->error));
02038 if (image->storage_class == DirectClass)
02039 return(MagickTrue);
02040 alpha=1.0;
02041 beta=1.0;
02042 area=3.0*image->columns*image->rows;
02043 maximum_error=0.0;
02044 mean_error_per_pixel=0.0;
02045 mean_error=0.0;
02046 exception=(&image->exception);
02047 image_view=AcquireCacheView(image);
02048 for (y=0; y < (long) image->rows; y++)
02049 {
02050 register const PixelPacket
02051 *__restrict p;
02052
02053 register long
02054 x;
02055
02056 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
02057 if (p == (const PixelPacket *) NULL)
02058 break;
02059 indexes=GetCacheViewAuthenticIndexQueue(image_view);
02060 for (x=0; x < (long) image->columns; x++)
02061 {
02062 index=1UL*indexes[x];
02063 if (image->matte != MagickFalse)
02064 {
02065 alpha=(MagickRealType) (QuantumScale*(QuantumRange-p->opacity));
02066 beta=(MagickRealType) (QuantumScale*(QuantumRange-
02067 image->colormap[index].opacity));
02068 }
02069 distance=fabs(alpha*p->red-beta*image->colormap[index].red);
02070 mean_error_per_pixel+=distance;
02071 mean_error+=distance*distance;
02072 if (distance > maximum_error)
02073 maximum_error=distance;
02074 distance=fabs(alpha*p->green-beta*image->colormap[index].green);
02075 mean_error_per_pixel+=distance;
02076 mean_error+=distance*distance;
02077 if (distance > maximum_error)
02078 maximum_error=distance;
02079 distance=fabs(alpha*p->blue-beta*image->colormap[index].blue);
02080 mean_error_per_pixel+=distance;
02081 mean_error+=distance*distance;
02082 if (distance > maximum_error)
02083 maximum_error=distance;
02084 p++;
02085 }
02086 }
02087 image_view=DestroyCacheView(image_view);
02088 image->error.mean_error_per_pixel=(double) mean_error_per_pixel/area;
02089 image->error.normalized_mean_error=(double) QuantumScale*QuantumScale*
02090 mean_error/area;
02091 image->error.normalized_maximum_error=(double) QuantumScale*maximum_error;
02092 return(MagickTrue);
02093 }
02094
02095
02096
02097
02098
02099
02100
02101
02102
02103
02104
02105
02106
02107
02108
02109
02110
02111
02112
02113
02114
02115
02116
02117 MagickExport void GetQuantizeInfo(QuantizeInfo *quantize_info)
02118 {
02119 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
02120 assert(quantize_info != (QuantizeInfo *) NULL);
02121 (void) ResetMagickMemory(quantize_info,0,sizeof(*quantize_info));
02122 quantize_info->number_colors=256;
02123 quantize_info->dither=MagickTrue;
02124 quantize_info->dither_method=RiemersmaDitherMethod;
02125 quantize_info->colorspace=UndefinedColorspace;
02126 quantize_info->measure_error=MagickFalse;
02127 quantize_info->signature=MagickSignature;
02128 }
02129
02130
02131
02132
02133
02134
02135
02136
02137
02138
02139
02140
02141
02142
02143
02144
02145
02146
02147
02148
02149
02150
02151
02152
02153
02154
02155
02156
02157
02158
02159
02160 MagickExport MagickBooleanType PosterizeImage(Image *image,
02161 const unsigned long levels,const MagickBooleanType dither)
02162 {
02163 ExceptionInfo
02164 *exception;
02165
02166 Image
02167 *posterize_image;
02168
02169 IndexPacket
02170 *indexes;
02171
02172 long
02173 j,
02174 k,
02175 l,
02176 n;
02177
02178 MagickBooleanType
02179 status;
02180
02181 QuantizeInfo
02182 *quantize_info;
02183
02184 register long
02185 i;
02186
02187 register PixelPacket
02188 *__restrict q;
02189
02190 CacheView
02191 *posterize_view;
02192
02193
02194
02195
02196 assert(image != (Image *) NULL);
02197 assert(image->signature == MagickSignature);
02198 if (image->debug != MagickFalse)
02199 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02200 posterize_image=AcquireImage((ImageInfo *) NULL);
02201 if (posterize_image == (Image *) NULL)
02202 return(MagickFalse);
02203 l=1;
02204 while ((l*l*l) < (long) MagickMin((long) levels*levels*levels,MaxColormapSize+1))
02205 l++;
02206 status=SetImageExtent(posterize_image,(unsigned long) (l*l*l),1);
02207 if (status == MagickFalse)
02208 {
02209 posterize_image=DestroyImage(posterize_image);
02210 return(MagickFalse);
02211 }
02212 status=AcquireImageColormap(posterize_image,levels*levels*levels);
02213 if (status == MagickFalse)
02214 {
02215 posterize_image=DestroyImage(posterize_image);
02216 return(MagickFalse);
02217 }
02218 posterize_view=AcquireCacheView(posterize_image);
02219 exception=(&image->exception);
02220 q=QueueCacheViewAuthenticPixels(posterize_view,0,0,posterize_image->columns,1,
02221 exception);
02222 if (q == (PixelPacket *) NULL)
02223 {
02224 posterize_view=DestroyCacheView(posterize_view);
02225 posterize_image=DestroyImage(posterize_image);
02226 return(MagickFalse);
02227 }
02228 indexes=GetCacheViewAuthenticIndexQueue(posterize_view);
02229 n=0;
02230 for (i=0; i < l; i++)
02231 for (j=0; j < l; j++)
02232 for (k=0; k < l; k++)
02233 {
02234 posterize_image->colormap[n].red=(Quantum) (QuantumRange*i/
02235 MagickMax(l-1L,1L));
02236 posterize_image->colormap[n].green=(Quantum)
02237 (QuantumRange*j/MagickMax(l-1L,1L));
02238 posterize_image->colormap[n].blue=(Quantum) (QuantumRange*k/
02239 MagickMax(l-1L,1L));
02240 posterize_image->colormap[n].opacity=OpaqueOpacity;
02241 *q++=posterize_image->colormap[n];
02242 indexes[n]=(IndexPacket) n;
02243 n++;
02244 }
02245 if (SyncCacheViewAuthenticPixels(posterize_view,exception) == MagickFalse)
02246 {
02247 posterize_view=DestroyCacheView(posterize_view);
02248 posterize_image=DestroyImage(posterize_image);
02249 return(MagickFalse);
02250 }
02251 posterize_view=DestroyCacheView(posterize_view);
02252 quantize_info=AcquireQuantizeInfo((ImageInfo *) NULL);
02253 quantize_info->dither=dither;
02254 status=RemapImage(quantize_info,image,posterize_image);
02255 quantize_info=DestroyQuantizeInfo(quantize_info);
02256 posterize_image=DestroyImage(posterize_image);
02257 return(status);
02258 }
02259
02260
02261
02262
02263
02264
02265
02266
02267
02268
02269
02270
02271
02272
02273
02274
02275
02276
02277
02278
02279
02280
02281
02282
02283
02284
02285
02286
02287
02288 static void PruneChild(const Image *image,CubeInfo *cube_info,
02289 const NodeInfo *node_info)
02290 {
02291 NodeInfo
02292 *parent;
02293
02294 register long
02295 i;
02296
02297 unsigned long
02298 number_children;
02299
02300
02301
02302
02303 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
02304 for (i=0; i < (long) number_children; i++)
02305 if (node_info->child[i] != (NodeInfo *) NULL)
02306 PruneChild(image,cube_info,node_info->child[i]);
02307
02308
02309
02310 parent=node_info->parent;
02311 parent->number_unique+=node_info->number_unique;
02312 parent->total_color.red+=node_info->total_color.red;
02313 parent->total_color.green+=node_info->total_color.green;
02314 parent->total_color.blue+=node_info->total_color.blue;
02315 parent->total_color.opacity+=node_info->total_color.opacity;
02316 parent->child[node_info->id]=(NodeInfo *) NULL;
02317 cube_info->nodes--;
02318 }
02319
02320
02321
02322
02323
02324
02325
02326
02327
02328
02329
02330
02331
02332
02333
02334
02335
02336
02337
02338
02339
02340
02341
02342
02343
02344
02345
02346
02347
02348 static void PruneLevel(const Image *image,CubeInfo *cube_info,
02349 const NodeInfo *node_info)
02350 {
02351 register long
02352 i;
02353
02354 unsigned long
02355 number_children;
02356
02357
02358
02359
02360 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
02361 for (i=0; i < (long) number_children; i++)
02362 if (node_info->child[i] != (NodeInfo *) NULL)
02363 PruneLevel(image,cube_info,node_info->child[i]);
02364 if (node_info->level == cube_info->depth)
02365 PruneChild(image,cube_info,node_info);
02366 }
02367
02368
02369
02370
02371
02372
02373
02374
02375
02376
02377
02378
02379
02380
02381
02382
02383
02384
02385
02386
02387
02388
02389
02390
02391
02392
02393
02394
02395 static void PruneToCubeDepth(const Image *image,CubeInfo *cube_info,
02396 const NodeInfo *node_info)
02397 {
02398 register long
02399 i;
02400
02401 unsigned long
02402 number_children;
02403
02404
02405
02406
02407 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
02408 for (i=0; i < (long) number_children; i++)
02409 if (node_info->child[i] != (NodeInfo *) NULL)
02410 PruneToCubeDepth(image,cube_info,node_info->child[i]);
02411 if (node_info->level > cube_info->depth)
02412 PruneChild(image,cube_info,node_info);
02413 }
02414
02415
02416
02417
02418
02419
02420
02421
02422
02423
02424
02425
02426
02427
02428
02429
02430
02431
02432
02433
02434
02435
02436
02437
02438
02439
02440
02441
02442
02443 MagickExport MagickBooleanType QuantizeImage(const QuantizeInfo *quantize_info,
02444 Image *image)
02445 {
02446 CubeInfo
02447 *cube_info;
02448
02449 MagickBooleanType
02450 status;
02451
02452 unsigned long
02453 depth,
02454 maximum_colors;
02455
02456 assert(quantize_info != (const QuantizeInfo *) NULL);
02457 assert(quantize_info->signature == MagickSignature);
02458 assert(image != (Image *) NULL);
02459 assert(image->signature == MagickSignature);
02460 if (image->debug != MagickFalse)
02461 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02462 maximum_colors=quantize_info->number_colors;
02463 if (maximum_colors == 0)
02464 maximum_colors=MaxColormapSize;
02465 if (maximum_colors > MaxColormapSize)
02466 maximum_colors=MaxColormapSize;
02467 if ((IsGrayImage(image,&image->exception) != MagickFalse) &&
02468 (image->matte == MagickFalse))
02469 (void) SetGrayscaleImage(image);
02470 if ((image->storage_class == PseudoClass) &&
02471 (image->colors <= maximum_colors))
02472 return(MagickTrue);
02473 depth=quantize_info->tree_depth;
02474 if (depth == 0)
02475 {
02476 unsigned long
02477 colors;
02478
02479
02480
02481
02482 colors=maximum_colors;
02483 for (depth=1; colors != 0; depth++)
02484 colors>>=2;
02485 if ((quantize_info->dither != MagickFalse) && (depth > 2))
02486 depth--;
02487 if ((image->matte != MagickFalse) && (depth > 5))
02488 depth--;
02489 }
02490
02491
02492
02493 cube_info=GetCubeInfo(quantize_info,depth,maximum_colors);
02494 if (cube_info == (CubeInfo *) NULL)
02495 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
02496 image->filename);
02497 status=ClassifyImageColors(cube_info,image,&image->exception);
02498 if (status != MagickFalse)
02499 {
02500
02501
02502
02503 ReduceImageColors(image,cube_info);
02504 status=AssignImageColors(image,cube_info);
02505 }
02506 DestroyCubeInfo(cube_info);
02507 return(status);
02508 }
02509
02510
02511
02512
02513
02514
02515
02516
02517
02518
02519
02520
02521
02522
02523
02524
02525
02526
02527
02528
02529
02530
02531
02532
02533
02534
02535
02536
02537
02538 MagickExport MagickBooleanType QuantizeImages(const QuantizeInfo *quantize_info,
02539 Image *images)
02540 {
02541 CubeInfo
02542 *cube_info;
02543
02544 Image
02545 *image;
02546
02547 MagickBooleanType
02548 proceed,
02549 status;
02550
02551 MagickProgressMonitor
02552 progress_monitor;
02553
02554 register long
02555 i;
02556
02557 unsigned long
02558 depth,
02559 maximum_colors,
02560 number_images;
02561
02562 assert(quantize_info != (const QuantizeInfo *) NULL);
02563 assert(quantize_info->signature == MagickSignature);
02564 assert(images != (Image *) NULL);
02565 assert(images->signature == MagickSignature);
02566 if (images->debug != MagickFalse)
02567 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
02568 if (GetNextImageInList(images) == (Image *) NULL)
02569 {
02570
02571
02572
02573 status=QuantizeImage(quantize_info,images);
02574 return(status);
02575 }
02576 status=MagickFalse;
02577 maximum_colors=quantize_info->number_colors;
02578 if (maximum_colors == 0)
02579 maximum_colors=MaxColormapSize;
02580 if (maximum_colors > MaxColormapSize)
02581 maximum_colors=MaxColormapSize;
02582 depth=quantize_info->tree_depth;
02583 if (depth == 0)
02584 {
02585 unsigned long
02586 colors;
02587
02588
02589
02590
02591 colors=maximum_colors;
02592 for (depth=1; colors != 0; depth++)
02593 colors>>=2;
02594 if (quantize_info->dither != MagickFalse)
02595 depth--;
02596 }
02597
02598
02599
02600 cube_info=GetCubeInfo(quantize_info,depth,maximum_colors);
02601 if (cube_info == (CubeInfo *) NULL)
02602 {
02603 (void) ThrowMagickException(&images->exception,GetMagickModule(),
02604 ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
02605 return(MagickFalse);
02606 }
02607 number_images=GetImageListLength(images);
02608 image=images;
02609 for (i=0; image != (Image *) NULL; i++)
02610 {
02611 progress_monitor=SetImageProgressMonitor(image,(MagickProgressMonitor) NULL,
02612 image->client_data);
02613 status=ClassifyImageColors(cube_info,image,&image->exception);
02614 if (status == MagickFalse)
02615 break;
02616 (void) SetImageProgressMonitor(image,progress_monitor,image->client_data);
02617 proceed=SetImageProgress(image,AssignImageTag,i,number_images);
02618 if (proceed == MagickFalse)
02619 break;
02620 image=GetNextImageInList(image);
02621 }
02622 if (status != MagickFalse)
02623 {
02624
02625
02626
02627 ReduceImageColors(images,cube_info);
02628 image=images;
02629 for (i=0; image != (Image *) NULL; i++)
02630 {
02631 progress_monitor=SetImageProgressMonitor(image,(MagickProgressMonitor)
02632 NULL,image->client_data);
02633 status=AssignImageColors(image,cube_info);
02634 if (status == MagickFalse)
02635 break;
02636 (void) SetImageProgressMonitor(image,progress_monitor,
02637 image->client_data);
02638 proceed=SetImageProgress(image,AssignImageTag,i,number_images);
02639 if (proceed == MagickFalse)
02640 break;
02641 image=GetNextImageInList(image);
02642 }
02643 }
02644 DestroyCubeInfo(cube_info);
02645 return(status);
02646 }
02647
02648
02649
02650
02651
02652
02653
02654
02655
02656
02657
02658
02659
02660
02661
02662
02663
02664
02665
02666
02667
02668
02669
02670
02671
02672
02673
02674
02675 static void Reduce(const Image *image,CubeInfo *cube_info,
02676 const NodeInfo *node_info)
02677 {
02678 register long
02679 i;
02680
02681 unsigned long
02682 number_children;
02683
02684
02685
02686
02687 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
02688 for (i=0; i < (long) number_children; i++)
02689 if (node_info->child[i] != (NodeInfo *) NULL)
02690 Reduce(image,cube_info,node_info->child[i]);
02691 if (node_info->quantize_error <= cube_info->pruning_threshold)
02692 PruneChild(image,cube_info,node_info);
02693 else
02694 {
02695
02696
02697
02698 if (node_info->number_unique > 0)
02699 cube_info->colors++;
02700 if (node_info->quantize_error < cube_info->next_threshold)
02701 cube_info->next_threshold=node_info->quantize_error;
02702 }
02703 }
02704
02705
02706
02707
02708
02709
02710
02711
02712
02713
02714
02715
02716
02717
02718
02719
02720
02721
02722
02723
02724
02725
02726
02727
02728
02729
02730
02731
02732
02733
02734
02735
02736
02737
02738
02739
02740
02741
02742
02743
02744
02745
02746
02747
02748
02749
02750
02751
02752
02753
02754
02755
02756
02757
02758
02759 static void ReduceImageColors(const Image *image,CubeInfo *cube_info)
02760 {
02761 #define ReduceImageTag "Reduce/Image"
02762
02763 MagickBooleanType
02764 proceed;
02765
02766 MagickOffsetType
02767 offset;
02768
02769 unsigned long
02770 span;
02771
02772 cube_info->next_threshold=0.0;
02773 for (span=cube_info->colors; cube_info->colors > cube_info->maximum_colors; )
02774 {
02775 cube_info->pruning_threshold=cube_info->next_threshold;
02776 cube_info->next_threshold=cube_info->root->quantize_error-1;
02777 cube_info->colors=0;
02778 Reduce(image,cube_info,cube_info->root);
02779 offset=(MagickOffsetType) span-cube_info->colors;
02780 proceed=SetImageProgress(image,ReduceImageTag,offset,span-
02781 cube_info->maximum_colors+1);
02782 if (proceed == MagickFalse)
02783 break;
02784 }
02785 }
02786
02787
02788
02789
02790
02791
02792
02793
02794
02795
02796
02797
02798
02799
02800
02801
02802
02803
02804
02805
02806
02807
02808
02809
02810
02811
02812
02813
02814
02815 MagickExport MagickBooleanType RemapImage(const QuantizeInfo *quantize_info,
02816 Image *image,const Image *remap_image)
02817 {
02818 CubeInfo
02819 *cube_info;
02820
02821 MagickBooleanType
02822 status;
02823
02824
02825
02826
02827 assert(image != (Image *) NULL);
02828 assert(image->signature == MagickSignature);
02829 if (image->debug != MagickFalse)
02830 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02831 assert(remap_image != (Image *) NULL);
02832 assert(remap_image->signature == MagickSignature);
02833 cube_info=GetCubeInfo(quantize_info,MaxTreeDepth,
02834 quantize_info->number_colors);
02835 if (cube_info == (CubeInfo *) NULL)
02836 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
02837 image->filename);
02838 status=ClassifyImageColors(cube_info,remap_image,&image->exception);
02839 if (status != MagickFalse)
02840 {
02841
02842
02843
02844 cube_info->quantize_info->number_colors=cube_info->colors;
02845 status=AssignImageColors(image,cube_info);
02846 }
02847 DestroyCubeInfo(cube_info);
02848 return(status);
02849 }
02850
02851
02852
02853
02854
02855
02856
02857
02858
02859
02860
02861
02862
02863
02864
02865
02866
02867
02868
02869
02870
02871
02872
02873
02874
02875
02876
02877
02878
02879 MagickExport MagickBooleanType RemapImages(const QuantizeInfo *quantize_info,
02880 Image *images,const Image *remap_image)
02881 {
02882 CubeInfo
02883 *cube_info;
02884
02885 Image
02886 *image;
02887
02888 MagickBooleanType
02889 status;
02890
02891 assert(images != (Image *) NULL);
02892 assert(images->signature == MagickSignature);
02893 if (images->debug != MagickFalse)
02894 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
02895 image=images;
02896 if (remap_image == (Image *) NULL)
02897 {
02898
02899
02900
02901 status=QuantizeImages(quantize_info,images);
02902 return(status);
02903 }
02904
02905
02906
02907 cube_info=GetCubeInfo(quantize_info,MaxTreeDepth,
02908 quantize_info->number_colors);
02909 if (cube_info == (CubeInfo *) NULL)
02910 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
02911 image->filename);
02912 status=ClassifyImageColors(cube_info,remap_image,&image->exception);
02913 if (status != MagickFalse)
02914 {
02915
02916
02917
02918 cube_info->quantize_info->number_colors=cube_info->colors;
02919 image=images;
02920 for ( ; image != (Image *) NULL; image=GetNextImageInList(image))
02921 {
02922 status=AssignImageColors(image,cube_info);
02923 if (status == MagickFalse)
02924 break;
02925 }
02926 }
02927 DestroyCubeInfo(cube_info);
02928 return(status);
02929 }
02930
02931
02932
02933
02934
02935
02936
02937
02938
02939
02940
02941
02942
02943
02944
02945
02946
02947
02948
02949
02950
02951
02952
02953
02954 #if defined(__cplusplus) || defined(c_plusplus)
02955 extern "C" {
02956 #endif
02957
02958 static int IntensityCompare(const void *x,const void *y)
02959 {
02960 long
02961 intensity;
02962
02963 PixelPacket
02964 *color_1,
02965 *color_2;
02966
02967 color_1=(PixelPacket *) x;
02968 color_2=(PixelPacket *) y;
02969 intensity=PixelIntensityToQuantum(color_1)-(long)
02970 PixelIntensityToQuantum(color_2);
02971 return(intensity);
02972 }
02973
02974 #if defined(__cplusplus) || defined(c_plusplus)
02975 }
02976 #endif
02977
02978 static MagickBooleanType SetGrayscaleImage(Image *image)
02979 {
02980 ExceptionInfo
02981 *exception;
02982
02983 long
02984 j,
02985 y;
02986
02987 PixelPacket
02988 *colormap;
02989
02990 long
02991 *colormap_index;
02992
02993 register long
02994 i;
02995
02996 MagickBooleanType
02997 status;
02998
02999 CacheView
03000 *image_view;
03001
03002 assert(image != (Image *) NULL);
03003 assert(image->signature == MagickSignature);
03004 if (image->type != GrayscaleType)
03005 (void) TransformImageColorspace(image,GRAYColorspace);
03006 colormap_index=(long *) AcquireQuantumMemory(MaxMap+1,
03007 sizeof(*colormap_index));
03008 if (colormap_index == (long *) NULL)
03009 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
03010 image->filename);
03011 if (image->storage_class != PseudoClass)
03012 {
03013 ExceptionInfo
03014 *exception;
03015
03016 for (i=0; i <= (long) MaxMap; i++)
03017 colormap_index[i]=(-1);
03018 if (AcquireImageColormap(image,MaxMap+1) == MagickFalse)
03019 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
03020 image->filename);
03021 image->colors=0;
03022 status=MagickTrue;
03023 exception=(&image->exception);
03024 image_view=AcquireCacheView(image);
03025 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03026 #pragma omp parallel for schedule(dynamic,4) shared(status)
03027 #endif
03028 for (y=0; y < (long) image->rows; y++)
03029 {
03030 register IndexPacket
03031 *__restrict indexes;
03032
03033 register long
03034 x;
03035
03036 register const PixelPacket
03037 *__restrict q;
03038
03039 if (status == MagickFalse)
03040 continue;
03041 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
03042 exception);
03043 if (q == (PixelPacket *) NULL)
03044 {
03045 status=MagickFalse;
03046 continue;
03047 }
03048 indexes=GetCacheViewAuthenticIndexQueue(image_view);
03049 for (x=0; x < (long) image->columns; x++)
03050 {
03051 register unsigned long
03052 intensity;
03053
03054 intensity=ScaleQuantumToMap(q->red);
03055 if (colormap_index[intensity] < 0)
03056 {
03057 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03058 #pragma omp critical (MagickCore_SetGrayscaleImage)
03059 #endif
03060 if (colormap_index[intensity] < 0)
03061 {
03062 colormap_index[intensity]=(long) image->colors;
03063 image->colormap[image->colors]=(*q);
03064 image->colors++;
03065 }
03066 }
03067 indexes[x]=(IndexPacket) colormap_index[intensity];
03068 q++;
03069 }
03070 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
03071 status=MagickFalse;
03072 }
03073 image_view=DestroyCacheView(image_view);
03074 }
03075 for (i=0; i < (long) image->colors; i++)
03076 image->colormap[i].opacity=(unsigned short) i;
03077 qsort((void *) image->colormap,image->colors,sizeof(PixelPacket),
03078 IntensityCompare);
03079 colormap=(PixelPacket *) AcquireQuantumMemory(image->colors,
03080 sizeof(*colormap));
03081 if (colormap == (PixelPacket *) NULL)
03082 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
03083 image->filename);
03084 j=0;
03085 colormap[j]=image->colormap[0];
03086 for (i=0; i < (long) image->colors; i++)
03087 {
03088 if (IsSameColor(image,&colormap[j],&image->colormap[i]) == MagickFalse)
03089 {
03090 j++;
03091 colormap[j]=image->colormap[i];
03092 }
03093 colormap_index[(long) image->colormap[i].opacity]=j;
03094 }
03095 image->colors=(unsigned long) (j+1);
03096 image->colormap=(PixelPacket *) RelinquishMagickMemory(image->colormap);
03097 image->colormap=colormap;
03098 status=MagickTrue;
03099 exception=(&image->exception);
03100 image_view=AcquireCacheView(image);
03101 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03102 #pragma omp parallel for schedule(dynamic,4) shared(status)
03103 #endif
03104 for (y=0; y < (long) image->rows; y++)
03105 {
03106 register IndexPacket
03107 *__restrict indexes;
03108
03109 register long
03110 x;
03111
03112 register const PixelPacket
03113 *__restrict q;
03114
03115 if (status == MagickFalse)
03116 continue;
03117 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
03118 if (q == (PixelPacket *) NULL)
03119 {
03120 status=MagickFalse;
03121 continue;
03122 }
03123 indexes=GetCacheViewAuthenticIndexQueue(image_view);
03124 for (x=0; x < (long) image->columns; x++)
03125 indexes[x]=(IndexPacket) colormap_index[ScaleQuantumToMap(indexes[x])];
03126 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
03127 status=MagickFalse;
03128 }
03129 image_view=DestroyCacheView(image_view);
03130 colormap_index=(long *) RelinquishMagickMemory(colormap_index);
03131 image->type=GrayscaleType;
03132 if (IsMonochromeImage(image,&image->exception) != MagickFalse)
03133 image->type=BilevelType;
03134 return(status);
03135 }