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/image.h"
00186 #include "magick/image-private.h"
00187 #include "magick/list.h"
00188 #include "magick/memory_.h"
00189 #include "magick/monitor.h"
00190 #include "magick/monitor-private.h"
00191 #include "magick/option.h"
00192 #include "magick/pixel-private.h"
00193 #include "magick/quantize.h"
00194 #include "magick/quantum.h"
00195 #include "magick/string_.h"
00196
00197
00198
00199
00200 #define CacheShift 2
00201 #define ErrorQueueLength 16
00202 #define MaxNodes 266817
00203 #define MaxTreeDepth 8
00204 #define NodesInAList 1920
00205
00206
00207
00208
00209 typedef struct _RealPixelPacket
00210 {
00211 MagickRealLongType
00212 red,
00213 green,
00214 blue,
00215 opacity;
00216 } RealPixelPacket;
00217
00218 typedef struct _NodeInfo
00219 {
00220 struct _NodeInfo
00221 *parent,
00222 *child[16];
00223
00224 MagickSizeType
00225 number_unique;
00226
00227 RealPixelPacket
00228 total_color;
00229
00230 MagickRealType
00231 quantize_error;
00232
00233 unsigned long
00234 color_number,
00235 id,
00236 level;
00237 } NodeInfo;
00238
00239 typedef struct _Nodes
00240 {
00241 NodeInfo
00242 *nodes;
00243
00244 struct _Nodes
00245 *next;
00246 } Nodes;
00247
00248 typedef struct _CubeInfo
00249 {
00250 NodeInfo
00251 *root;
00252
00253 unsigned long
00254 colors,
00255 maximum_colors;
00256
00257 long
00258 transparent_index;
00259
00260 MagickSizeType
00261 transparent_pixels;
00262
00263 RealPixelPacket
00264 target;
00265
00266 MagickRealType
00267 distance,
00268 pruning_threshold,
00269 next_threshold;
00270
00271 unsigned long
00272 nodes,
00273 free_nodes,
00274 color_number;
00275
00276 NodeInfo
00277 *next_node;
00278
00279 Nodes
00280 *node_queue;
00281
00282 long
00283 *cache;
00284
00285 RealPixelPacket
00286 error[ErrorQueueLength];
00287
00288 MagickRealType
00289 weights[ErrorQueueLength];
00290
00291 QuantizeInfo
00292 *quantize_info;
00293
00294 MagickBooleanType
00295 associate_alpha;
00296
00297 long
00298 x,
00299 y;
00300
00301 unsigned long
00302 depth;
00303
00304 MagickOffsetType
00305 offset;
00306
00307 MagickSizeType
00308 span;
00309 } CubeInfo;
00310
00311
00312
00313
00314 static CubeInfo
00315 *GetCubeInfo(const QuantizeInfo *,const unsigned long,const unsigned long);
00316
00317 static NodeInfo
00318 *GetNodeInfo(CubeInfo *,const unsigned long,const unsigned long,NodeInfo *);
00319
00320 static MagickBooleanType
00321 AssignImageColors(Image *,CubeInfo *),
00322 ClassifyImageColors(CubeInfo *,const Image *,ExceptionInfo *),
00323 DitherImage(Image *,CubeInfo *),
00324 SetGrayscaleImage(Image *);
00325
00326 static unsigned long
00327 DefineImageColormap(Image *,CubeInfo *,NodeInfo *);
00328
00329 static void
00330 ClosestColor(const Image *,CubeInfo *,const NodeInfo *),
00331 DestroyCubeInfo(CubeInfo *),
00332 PruneLevel(const Image *,CubeInfo *,const NodeInfo *),
00333 PruneToCubeDepth(const Image *,CubeInfo *,const NodeInfo *),
00334 ReduceImageColors(const Image *,CubeInfo *);
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358 MagickExport QuantizeInfo *AcquireQuantizeInfo(const ImageInfo *image_info)
00359 {
00360 QuantizeInfo
00361 *quantize_info;
00362
00363 quantize_info=(QuantizeInfo *) AcquireMagickMemory(sizeof(*quantize_info));
00364 if (quantize_info == (QuantizeInfo *) NULL)
00365 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
00366 GetQuantizeInfo(quantize_info);
00367 if (image_info != (ImageInfo *) NULL)
00368 {
00369 const char
00370 *option;
00371
00372 quantize_info->dither=image_info->dither;
00373 option=GetImageOption(image_info,"dither");
00374 if (option != (const char *) NULL)
00375 quantize_info->dither_method=(DitherMethod) ParseMagickOption(
00376 MagickDitherOptions,MagickFalse,option);
00377 quantize_info->measure_error=image_info->verbose;
00378 }
00379 return(quantize_info);
00380 }
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 static inline void AssociateAlphaPixel(const CubeInfo *cube_info,
00423 const PixelPacket *pixel,RealPixelPacket *alpha_pixel)
00424 {
00425 MagickRealType
00426 alpha;
00427
00428 if ((cube_info->associate_alpha == MagickFalse) ||
00429 (pixel->opacity == OpaqueOpacity))
00430 {
00431 alpha_pixel->red=(MagickRealType) pixel->red;
00432 alpha_pixel->green=(MagickRealType) pixel->green;
00433 alpha_pixel->blue=(MagickRealType) pixel->blue;
00434 alpha_pixel->opacity=(MagickRealType) pixel->opacity;
00435 return;
00436 }
00437 alpha=(MagickRealType) (QuantumScale*(QuantumRange-pixel->opacity));
00438 alpha_pixel->red=alpha*pixel->red;
00439 alpha_pixel->green=alpha*pixel->green;
00440 alpha_pixel->blue=alpha*pixel->blue;
00441 alpha_pixel->opacity=(MagickRealType) pixel->opacity;
00442 }
00443
00444 static inline Quantum ClipToQuantum(const MagickRealType value)
00445 {
00446 if (value <= 0.0)
00447 return((Quantum) 0);
00448 if (value >= QuantumRange)
00449 return((Quantum) QuantumRange);
00450 return((Quantum) (value+0.5));
00451 }
00452
00453 static inline unsigned long ColorToNodeId(const CubeInfo *cube_info,
00454 const RealPixelPacket *pixel,unsigned long index)
00455 {
00456 unsigned long
00457 id;
00458
00459 id=(unsigned long) (
00460 ((ScaleQuantumToChar(ClipToQuantum(pixel->red)) >> index) & 0x1) |
00461 ((ScaleQuantumToChar(ClipToQuantum(pixel->green)) >> index) & 0x1) << 1 |
00462 ((ScaleQuantumToChar(ClipToQuantum(pixel->blue)) >> index) & 0x1) << 2);
00463 if (cube_info->associate_alpha != MagickFalse)
00464 id|=((ScaleQuantumToChar(ClipToQuantum(pixel->opacity)) >> index) & 0x1)
00465 << 3;
00466 return(id);
00467 }
00468
00469 static inline MagickBooleanType IsSameColor(const Image *image,
00470 const PixelPacket *p,const PixelPacket *q)
00471 {
00472 if ((p->red != q->red) || (p->green != q->green) || (p->blue != q->blue))
00473 return(MagickFalse);
00474 if ((image->matte != MagickFalse) && (p->opacity != q->opacity))
00475 return(MagickFalse);
00476 return(MagickTrue);
00477 }
00478
00479 static MagickBooleanType AssignImageColors(Image *image,CubeInfo *cube_info)
00480 {
00481 #define AssignImageTag "Assign/Image"
00482
00483 long
00484 y;
00485
00486 MagickBooleanType
00487 proceed;
00488
00489 RealPixelPacket
00490 pixel;
00491
00492 register IndexPacket
00493 *indexes;
00494
00495 register long
00496 i,
00497 x;
00498
00499 register const NodeInfo
00500 *node_info;
00501
00502 register PixelPacket
00503 *q;
00504
00505 ssize_t
00506 count;
00507
00508 unsigned long
00509 id,
00510 index;
00511
00512
00513
00514
00515 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
00516 (cube_info->quantize_info->colorspace != CMYKColorspace))
00517 (void) SetImageColorspace((Image *) image,
00518 cube_info->quantize_info->colorspace);
00519 else
00520 if ((image->colorspace != GRAYColorspace) &&
00521 (image->colorspace != RGBColorspace) &&
00522 (image->colorspace != CMYColorspace))
00523 (void) SetImageColorspace((Image *) image,RGBColorspace);
00524 if (AcquireImageColormap(image,cube_info->colors) == MagickFalse)
00525 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
00526 image->filename);
00527 image->colors=0;
00528 cube_info->transparent_pixels=0;
00529 cube_info->transparent_index=(-1);
00530 (void) DefineImageColormap(image,cube_info,cube_info->root);
00531
00532
00533
00534 if ((cube_info->quantize_info->dither != MagickFalse) &&
00535 (cube_info->quantize_info->dither_method != NoDitherMethod))
00536 (void) DitherImage(image,cube_info);
00537 else
00538 {
00539 ExceptionInfo
00540 *exception;
00541
00542 ViewInfo
00543 *image_view;
00544
00545 exception=(&image->exception);
00546 image_view=AcquireCacheView(image);
00547 for (y=0; y < (long) image->rows; y++)
00548 {
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
00612
00613
00614 q=image->colormap;
00615 for (i=0; i < (long) image->colors; i++)
00616 {
00617 intensity=(Quantum) (PixelIntensity(q) < ((MagickRealType)
00618 QuantumRange/2.0) ? 0 : QuantumRange);
00619 q->red=intensity;
00620 q->green=intensity;
00621 q->blue=intensity;
00622 q++;
00623 }
00624 }
00625 (void) SyncImage(image);
00626 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
00627 (cube_info->quantize_info->colorspace != CMYKColorspace))
00628 (void) SetImageColorspace((Image *) image,RGBColorspace);
00629 return(MagickTrue);
00630 }
00631
00632
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 static inline void SetAssociatedAlpha(const Image *image,CubeInfo *cube_info)
00694 {
00695 MagickBooleanType
00696 associate_alpha;
00697
00698 associate_alpha=image->matte;
00699 if (cube_info->quantize_info->colorspace == TransparentColorspace)
00700 associate_alpha=MagickFalse;
00701 if ((cube_info->quantize_info->number_colors == 2) &&
00702 (cube_info->quantize_info->colorspace == GRAYColorspace))
00703 associate_alpha=MagickFalse;
00704 cube_info->associate_alpha=associate_alpha;
00705 }
00706
00707 static MagickBooleanType ClassifyImageColors(CubeInfo *cube_info,
00708 const Image *image,ExceptionInfo *exception)
00709 {
00710 #define ClassifyImageTag "Classify/Image"
00711
00712 long
00713 y;
00714
00715 MagickBooleanType
00716 proceed;
00717
00718 MagickRealType
00719 bisect;
00720
00721 NodeInfo
00722 *node_info;
00723
00724 RealPixelPacket
00725 error,
00726 mid,
00727 midpoint,
00728 pixel;
00729
00730 register long
00731 x;
00732
00733 register const PixelPacket
00734 *p;
00735
00736 size_t
00737 count;
00738
00739 unsigned long
00740 id,
00741 index,
00742 level;
00743
00744 ViewInfo
00745 *image_view;
00746
00747
00748
00749
00750 SetAssociatedAlpha(image,cube_info);
00751 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
00752 (cube_info->quantize_info->colorspace != CMYKColorspace))
00753 (void) SetImageColorspace((Image *) image,
00754 cube_info->quantize_info->colorspace);
00755 else
00756 if ((image->colorspace != GRAYColorspace) &&
00757 (image->colorspace != CMYColorspace) &&
00758 (image->colorspace != RGBColorspace))
00759 (void) SetImageColorspace((Image *) image,RGBColorspace);
00760 midpoint.red=(MagickRealType) QuantumRange/2.0;
00761 midpoint.green=(MagickRealType) QuantumRange/2.0;
00762 midpoint.blue=(MagickRealType) QuantumRange/2.0;
00763 midpoint.opacity=(MagickRealType) QuantumRange/2.0;
00764 error.opacity=0.0;
00765 image_view=AcquireCacheView(image);
00766 for (y=0; y < (long) image->rows; y++)
00767 {
00768 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
00769 if (p == (const PixelPacket *) NULL)
00770 break;
00771 if (cube_info->nodes > MaxNodes)
00772 {
00773
00774
00775
00776 PruneLevel(image,cube_info,cube_info->root);
00777 cube_info->depth--;
00778 }
00779 for (x=0; x < (long) image->columns; x+=(long) count)
00780 {
00781
00782
00783
00784 for (count=1; (x+count) < image->columns; count++)
00785 if (IsSameColor(image,p,p+count) == MagickFalse)
00786 break;
00787 AssociateAlphaPixel(cube_info,p,&pixel);
00788 index=MaxTreeDepth-1;
00789 bisect=((MagickRealType) QuantumRange+1.0)/2.0;
00790 mid=midpoint;
00791 node_info=cube_info->root;
00792 for (level=1; level <= MaxTreeDepth; level++)
00793 {
00794 bisect*=0.5;
00795 id=ColorToNodeId(cube_info,&pixel,index);
00796 mid.red+=(id & 1) != 0 ? bisect : -bisect;
00797 mid.green+=(id & 2) != 0 ? bisect : -bisect;
00798 mid.blue+=(id & 4) != 0 ? bisect : -bisect;
00799 mid.opacity+=(id & 8) != 0 ? bisect : -bisect;
00800 if (node_info->child[id] == (NodeInfo *) NULL)
00801 {
00802
00803
00804
00805 node_info->child[id]=GetNodeInfo(cube_info,id,level,node_info);
00806 if (node_info->child[id] == (NodeInfo *) NULL)
00807 (void) ThrowMagickException(exception,GetMagickModule(),
00808 ResourceLimitError,"MemoryAllocationFailed","`%s'",
00809 image->filename);
00810 if (level == MaxTreeDepth)
00811 cube_info->colors++;
00812 }
00813
00814
00815
00816 node_info=node_info->child[id];
00817 error.red=QuantumScale*(pixel.red-mid.red);
00818 error.green=QuantumScale*(pixel.green-mid.green);
00819 error.blue=QuantumScale*(pixel.blue-mid.blue);
00820 if (cube_info->associate_alpha != MagickFalse)
00821 error.opacity=QuantumScale*(pixel.opacity-mid.opacity);
00822 node_info->quantize_error+=sqrt((double) (count*error.red*error.red+
00823 count*error.green*error.green+count*error.blue*error.blue+
00824 count*error.opacity*error.opacity));
00825 cube_info->root->quantize_error+=node_info->quantize_error;
00826 index--;
00827 }
00828
00829
00830
00831 node_info->number_unique+=count;
00832 node_info->total_color.red+=count*QuantumScale*pixel.red;
00833 node_info->total_color.green+=count*QuantumScale*pixel.green;
00834 node_info->total_color.blue+=count*QuantumScale*pixel.blue;
00835 if (cube_info->associate_alpha != MagickFalse)
00836 node_info->total_color.opacity+=count*QuantumScale*pixel.opacity;
00837 p+=count;
00838 }
00839 if (cube_info->colors > cube_info->maximum_colors)
00840 {
00841 PruneToCubeDepth(image,cube_info,cube_info->root);
00842 break;
00843 }
00844 proceed=SetImageProgress(image,ClassifyImageTag,y,image->rows);
00845 if (proceed == MagickFalse)
00846 break;
00847 }
00848 for (y++; y < (long) image->rows; y++)
00849 {
00850 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
00851 if (p == (const PixelPacket *) NULL)
00852 break;
00853 if (cube_info->nodes > MaxNodes)
00854 {
00855
00856
00857
00858 PruneLevel(image,cube_info,cube_info->root);
00859 cube_info->depth--;
00860 }
00861 for (x=0; x < (long) image->columns; x+=(long) count)
00862 {
00863
00864
00865
00866 for (count=1; (x+count) < image->columns; count++)
00867 if (IsSameColor(image,p,p+count) == MagickFalse)
00868 break;
00869 AssociateAlphaPixel(cube_info,p,&pixel);
00870 index=MaxTreeDepth-1;
00871 bisect=((MagickRealType) QuantumRange+1.0)/2.0;
00872 mid=midpoint;
00873 node_info=cube_info->root;
00874 for (level=1; level <= cube_info->depth; level++)
00875 {
00876 bisect*=0.5;
00877 id=ColorToNodeId(cube_info,&pixel,index);
00878 mid.red+=(id & 1) != 0 ? bisect : -bisect;
00879 mid.green+=(id & 2) != 0 ? bisect : -bisect;
00880 mid.blue+=(id & 4) != 0 ? bisect : -bisect;
00881 mid.opacity+=(id & 8) != 0 ? bisect : -bisect;
00882 if (node_info->child[id] == (NodeInfo *) NULL)
00883 {
00884
00885
00886
00887 node_info->child[id]=GetNodeInfo(cube_info,id,level,node_info);
00888 if (node_info->child[id] == (NodeInfo *) NULL)
00889 (void) ThrowMagickException(exception,GetMagickModule(),
00890 ResourceLimitError,"MemoryAllocationFailed",image->filename);
00891 if (level == cube_info->depth)
00892 cube_info->colors++;
00893 }
00894
00895
00896
00897 node_info=node_info->child[id];
00898 error.red=QuantumScale*(pixel.red-mid.red);
00899 error.green=QuantumScale*(pixel.green-mid.green);
00900 error.blue=QuantumScale*(pixel.blue-mid.blue);
00901 if (cube_info->associate_alpha != MagickFalse)
00902 error.opacity=QuantumScale*(pixel.opacity-mid.opacity);
00903 node_info->quantize_error+=sqrt((double) (count*error.red*error.red+
00904 count*error.green*error.green+error.blue*error.blue+
00905 count*error.opacity*error.opacity));
00906 cube_info->root->quantize_error+=node_info->quantize_error;
00907 index--;
00908 }
00909
00910
00911
00912 node_info->number_unique+=count;
00913 node_info->total_color.red+=count*QuantumScale*pixel.red;
00914 node_info->total_color.green+=count*QuantumScale*pixel.green;
00915 node_info->total_color.blue+=count*QuantumScale*pixel.blue;
00916 if (cube_info->associate_alpha != MagickFalse)
00917 node_info->total_color.opacity+=count*QuantumScale*pixel.opacity;
00918 p+=count;
00919 }
00920 proceed=SetImageProgress(image,ClassifyImageTag,y,image->rows);
00921 if (proceed == MagickFalse)
00922 break;
00923 }
00924 image_view=DestroyCacheView(image_view);
00925 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
00926 (cube_info->quantize_info->colorspace != CMYKColorspace))
00927 (void) SetImageColorspace((Image *) image,RGBColorspace);
00928 return(MagickTrue);
00929 }
00930
00931
00932
00933
00934
00935