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/cache-view.h"
00045 #include "magick/color-private.h"
00046 #include "magick/enhance.h"
00047 #include "magick/exception.h"
00048 #include "magick/exception-private.h"
00049 #include "magick/hashmap.h"
00050 #include "magick/histogram.h"
00051 #include "magick/image.h"
00052 #include "magick/list.h"
00053 #include "magick/memory_.h"
00054 #include "magick/monitor-private.h"
00055 #include "magick/pixel-private.h"
00056 #include "magick/prepress.h"
00057 #include "magick/quantize.h"
00058 #include "magick/registry.h"
00059 #include "magick/semaphore.h"
00060 #include "magick/splay-tree.h"
00061 #include "magick/statistic.h"
00062 #include "magick/string_.h"
00063
00064
00065
00066
00067 #define MaxTreeDepth 8
00068 #define NodesInAList 1536
00069
00070
00071
00072
00073 typedef struct _NodeInfo
00074 {
00075 struct _NodeInfo
00076 *child[16];
00077
00078 ColorPacket
00079 *list;
00080
00081 MagickSizeType
00082 number_unique;
00083
00084 unsigned long
00085 level;
00086 } NodeInfo;
00087
00088 typedef struct _Nodes
00089 {
00090 NodeInfo
00091 nodes[NodesInAList];
00092
00093 struct _Nodes
00094 *next;
00095 } Nodes;
00096
00097 typedef struct _CubeInfo
00098 {
00099 NodeInfo
00100 *root;
00101
00102 long
00103 x,
00104 progress;
00105
00106 unsigned long
00107 colors,
00108 free_nodes;
00109
00110 NodeInfo
00111 *node_info;
00112
00113 Nodes
00114 *node_queue;
00115 } CubeInfo;
00116
00117
00118
00119
00120 static CubeInfo
00121 *GetCubeInfo(void);
00122
00123 static NodeInfo
00124 *GetNodeInfo(CubeInfo *,const unsigned long);
00125
00126 static void
00127 DestroyColorCube(const Image *,NodeInfo *);
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 static inline unsigned long ColorToNodeId(const Image *image,
00158 const MagickPixelPacket *pixel,unsigned long index)
00159 {
00160 unsigned long
00161 id;
00162
00163 id=(unsigned long) (
00164 ((ScaleQuantumToChar(ClampToQuantum(pixel->red)) >> index) & 0x01) |
00165 ((ScaleQuantumToChar(ClampToQuantum(pixel->green)) >> index) & 0x01) << 1 |
00166 ((ScaleQuantumToChar(ClampToQuantum(pixel->blue)) >> index) & 0x01) << 2);
00167 if (image->matte != MagickFalse)
00168 id|=((ScaleQuantumToChar(ClampToQuantum(pixel->opacity)) >> index) &
00169 0x01) << 3;
00170 return(id);
00171 }
00172
00173 static CubeInfo *ClassifyImageColors(const Image *image,
00174 ExceptionInfo *exception)
00175 {
00176 #define EvaluateImageTag " Compute image colors... "
00177
00178 CacheView
00179 *image_view;
00180
00181 CubeInfo
00182 *cube_info;
00183
00184 long
00185 y;
00186
00187 MagickBooleanType
00188 proceed;
00189
00190 MagickPixelPacket
00191 pixel,
00192 target;
00193
00194 NodeInfo
00195 *node_info;
00196
00197 register const IndexPacket
00198 *indexes;
00199
00200 register const PixelPacket
00201 *p;
00202
00203 register long
00204 i,
00205 x;
00206
00207 register unsigned long
00208 id,
00209 index,
00210 level;
00211
00212
00213
00214
00215 assert(image != (const Image *) NULL);
00216 assert(image->signature == MagickSignature);
00217 if (image->debug != MagickFalse)
00218 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00219 cube_info=GetCubeInfo();
00220 if (cube_info == (CubeInfo *) NULL)
00221 {
00222 (void) ThrowMagickException(exception,GetMagickModule(),
00223 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
00224 return(cube_info);
00225 }
00226 GetMagickPixelPacket(image,&pixel);
00227 GetMagickPixelPacket(image,&target);
00228 image_view=AcquireCacheView(image);
00229 for (y=0; y < (long) image->rows; y++)
00230 {
00231 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
00232 if (p == (const PixelPacket *) NULL)
00233 break;
00234 indexes=GetCacheViewVirtualIndexQueue(image_view);
00235 for (x=0; x < (long) image->columns; x++)
00236 {
00237
00238
00239
00240 node_info=cube_info->root;
00241 index=MaxTreeDepth-1;
00242 for (level=1; level < MaxTreeDepth; level++)
00243 {
00244 SetMagickPixelPacket(image,p,indexes+x,&pixel);
00245 id=ColorToNodeId(image,&pixel,index);
00246 if (node_info->child[id] == (NodeInfo *) NULL)
00247 {
00248 node_info->child[id]=GetNodeInfo(cube_info,level);
00249 if (node_info->child[id] == (NodeInfo *) NULL)
00250 {
00251 (void) ThrowMagickException(exception,GetMagickModule(),
00252 ResourceLimitError,"MemoryAllocationFailed","`%s'",
00253 image->filename);
00254 return(0);
00255 }
00256 }
00257 node_info=node_info->child[id];
00258 index--;
00259 }
00260 for (i=0; i < (long) node_info->number_unique; i++)
00261 {
00262 SetMagickPixelPacket(image,&node_info->list[i].pixel,
00263 &node_info->list[i].index,&target);
00264 if (IsMagickColorEqual(&pixel,&target) != MagickFalse)
00265 break;
00266 }
00267 if (i < (long) node_info->number_unique)
00268 node_info->list[i].count++;
00269 else
00270 {
00271 if (node_info->number_unique == 0)
00272 node_info->list=(ColorPacket *) AcquireMagickMemory(
00273 sizeof(*node_info->list));
00274 else
00275 node_info->list=(ColorPacket *) ResizeQuantumMemory(node_info->list,
00276 (size_t) (i+1),sizeof(*node_info->list));
00277 if (node_info->list == (ColorPacket *) NULL)
00278 {
00279 (void) ThrowMagickException(exception,GetMagickModule(),
00280 ResourceLimitError,"MemoryAllocationFailed","`%s'",
00281 image->filename);
00282 return(0);
00283 }
00284 node_info->list[i].pixel=(*p);
00285 if ((image->colorspace == CMYKColorspace) ||
00286 (image->storage_class == PseudoClass))
00287 node_info->list[i].index=indexes[x];
00288 node_info->list[i].count=1;
00289 node_info->number_unique++;
00290 cube_info->colors++;
00291 }
00292 p++;
00293 }
00294 proceed=SetImageProgress(image,EvaluateImageTag,y,image->rows);
00295 if (proceed == MagickFalse)
00296 break;
00297 }
00298 image_view=DestroyCacheView(image_view);
00299 return(cube_info);
00300 }
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332 static void DefineImageHistogram(const Image *image,NodeInfo *node_info,
00333 ColorPacket **histogram)
00334 {
00335 register long
00336 i;
00337
00338 unsigned long
00339 number_children;
00340
00341
00342
00343
00344 number_children=image->matte == MagickFalse ? 8UL : 16UL;
00345 for (i=0; i < (long) number_children; i++)
00346 if (node_info->child[i] != (NodeInfo *) NULL)
00347 DefineImageHistogram(image,node_info->child[i],histogram);
00348 if (node_info->level == (MaxTreeDepth-1))
00349 {
00350 register ColorPacket
00351 *p;
00352
00353 p=node_info->list;
00354 for (i=0; i < (long) node_info->number_unique; i++)
00355 {
00356 (*histogram)->pixel=p->pixel;
00357 (*histogram)->index=p->index;
00358 (*histogram)->count=p->count;
00359 (*histogram)++;
00360 p++;
00361 }
00362 }
00363 }
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389 static CubeInfo *DestroyCubeInfo(const Image *image,CubeInfo *cube_info)
00390 {
00391 register Nodes
00392 *nodes;
00393
00394
00395
00396
00397 DestroyColorCube(image,cube_info->root);
00398 do
00399 {
00400 nodes=cube_info->node_queue->next;
00401 cube_info->node_queue=(Nodes *)
00402 RelinquishMagickMemory(cube_info->node_queue);
00403 cube_info->node_queue=nodes;
00404 } while (cube_info->node_queue != (Nodes *) NULL);
00405 return((CubeInfo *) RelinquishMagickMemory(cube_info));
00406 }
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434 static void DestroyColorCube(const Image *image,NodeInfo *node_info)
00435 {
00436 register long
00437 i;
00438
00439 unsigned long
00440 number_children;
00441
00442
00443
00444
00445 number_children=image->matte == MagickFalse ? 8UL : 16UL;
00446 for (i=0; i < (long) number_children; i++)
00447 if (node_info->child[i] != (NodeInfo *) NULL)
00448 DestroyColorCube(image,node_info->child[i]);
00449 if (node_info->list != (ColorPacket *) NULL)
00450 node_info->list=(ColorPacket *) RelinquishMagickMemory(node_info->list);
00451 }
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475 static CubeInfo *GetCubeInfo(void)
00476 {
00477 CubeInfo
00478 *cube_info;
00479
00480
00481
00482
00483 cube_info=(CubeInfo *) AcquireAlignedMemory(1,sizeof(*cube_info));
00484 if (cube_info == (CubeInfo *) NULL)
00485 return((CubeInfo *) NULL);
00486 (void) ResetMagickMemory(cube_info,0,sizeof(*cube_info));
00487
00488
00489
00490 cube_info->root=GetNodeInfo(cube_info,0);
00491 if (cube_info->root == (NodeInfo *) NULL)
00492 return((CubeInfo *) NULL);
00493 return(cube_info);
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 MagickExport ColorPacket *GetImageHistogram(const Image *image,
00524 unsigned long *number_colors,ExceptionInfo *exception)
00525 {
00526 ColorPacket
00527 *histogram;
00528
00529 CubeInfo
00530 *cube_info;
00531
00532 *number_colors=0;
00533 histogram=(ColorPacket *) NULL;
00534 cube_info=ClassifyImageColors(image,exception);
00535 if (cube_info != (CubeInfo *) NULL)
00536 {
00537 histogram=(ColorPacket *) AcquireQuantumMemory((size_t) cube_info->colors,
00538 sizeof(*histogram));
00539 if (histogram == (ColorPacket *) NULL)
00540 (void) ThrowMagickException(exception,GetMagickModule(),
00541 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
00542 else
00543 {
00544 ColorPacket
00545 *root;
00546
00547 *number_colors=cube_info->colors;
00548 root=histogram;
00549 DefineImageHistogram(image,cube_info->root,&root);
00550 }
00551 }
00552 cube_info=DestroyCubeInfo(image,cube_info);
00553 return(histogram);
00554 }
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 static NodeInfo *GetNodeInfo(CubeInfo *cube_info,const unsigned long level)
00582 {
00583 NodeInfo
00584 *node_info;
00585
00586 if (cube_info->free_nodes == 0)
00587 {
00588 Nodes
00589 *nodes;
00590
00591
00592
00593
00594 nodes=(Nodes *) AcquireAlignedMemory(1,sizeof(*nodes));
00595 if (nodes == (Nodes *) NULL)
00596 return((NodeInfo *) NULL);
00597 nodes->next=cube_info->node_queue;
00598 cube_info->node_queue=nodes;
00599 cube_info->node_info=nodes->nodes;
00600 cube_info->free_nodes=NodesInAList;
00601 }
00602 cube_info->free_nodes--;
00603 node_info=cube_info->node_info++;
00604 (void) ResetMagickMemory(node_info,0,sizeof(*node_info));
00605 node_info->level=level;
00606 return(node_info);
00607 }
00608
00609
00610
00611
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634
00635 MagickExport MagickBooleanType IsHistogramImage(const Image *image,
00636 ExceptionInfo *exception)
00637 {
00638 #define MaximumUniqueColors 1024
00639
00640 CacheView
00641 *image_view;
00642
00643 CubeInfo
00644 *cube_info;
00645
00646 long
00647 y;
00648
00649 MagickPixelPacket
00650 pixel,
00651 target;
00652
00653 register const IndexPacket
00654 *indexes;
00655
00656 register const PixelPacket
00657 *p;
00658
00659 register long
00660 x;
00661
00662 register NodeInfo
00663 *node_info;
00664
00665 register long
00666 i;
00667
00668 unsigned long
00669 id,
00670 index,
00671 level;
00672
00673 assert(image != (Image *) NULL);
00674 assert(image->signature == MagickSignature);
00675 if (image->debug != MagickFalse)
00676 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00677 if ((image->storage_class == PseudoClass) && (image->colors <= 256))
00678 return(MagickTrue);
00679 if (image->storage_class == PseudoClass)
00680 return(MagickFalse);
00681
00682
00683
00684 cube_info=GetCubeInfo();
00685 if (cube_info == (CubeInfo *) NULL)
00686 {
00687 (void) ThrowMagickException(exception,GetMagickModule(),
00688 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
00689 return(MagickFalse);
00690 }
00691 GetMagickPixelPacket(image,&pixel);
00692 GetMagickPixelPacket(image,&target);
00693 image_view=AcquireCacheView(image);
00694 for (y=0; y < (long) image->rows; y++)
00695 {
00696 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
00697 if (p == (const PixelPacket *) NULL)
00698 break;
00699 indexes=GetCacheViewVirtualIndexQueue(image_view);
00700 for (x=0; x < (long) image->columns; x++)
00701 {
00702
00703
00704
00705 node_info=cube_info->root;
00706 index=MaxTreeDepth-1;
00707 for (level=1; level < MaxTreeDepth; level++)
00708 {
00709 SetMagickPixelPacket(image,p,indexes+x,&pixel);
00710 id=ColorToNodeId(image,&pixel,index);
00711 if (node_info->child[id] == (NodeInfo *) NULL)
00712 {
00713 node_info->child[id]=GetNodeInfo(cube_info,level);
00714 if (node_info->child[id] == (NodeInfo *) NULL)
00715 {
00716 (void) ThrowMagickException(exception,GetMagickModule(),
00717 ResourceLimitError,"MemoryAllocationFailed","`%s'",
00718 image->filename);
00719 break;
00720 }
00721 }
00722 node_info=node_info->child[id];
00723 index--;
00724 }
00725 if (level < MaxTreeDepth)
00726 break;
00727 for (i=0; i < (long) node_info->number_unique; i++)
00728 {
00729 SetMagickPixelPacket(image,&node_info->list[i].pixel,
00730 &node_info->list[i].index,&target);
00731 if (IsMagickColorEqual(&pixel,&target) != MagickFalse)
00732 break;
00733 }
00734 if (i < (long) node_info->number_unique)
00735 node_info->list[i].count++;
00736 else
00737 {
00738
00739
00740
00741 if (node_info->number_unique == 0)
00742 node_info->list=(ColorPacket *) AcquireMagickMemory(
00743 sizeof(*node_info->list));
00744 else
00745 node_info->list=(ColorPacket *) ResizeQuantumMemory(node_info->list,
00746 (size_t) (i+1),sizeof(*node_info->list));
00747 if (node_info->list == (ColorPacket *) NULL)
00748 {
00749 (void) ThrowMagickException(exception,GetMagickModule(),
00750 ResourceLimitError,"MemoryAllocationFailed","`%s'",
00751 image->filename);
00752 break;
00753 }
00754 node_info->list[i].pixel=(*p);
00755 if ((image->colorspace == CMYKColorspace) ||
00756 (image->storage_class == PseudoClass))
00757 node_info->list[i].index=indexes[x];
00758 node_info->list[i].count=1;
00759 node_info->number_unique++;
00760 cube_info->colors++;
00761 if (cube_info->colors > MaximumUniqueColors)
00762 break;
00763 }
00764 p++;
00765 }
00766 if (x < (long) image->columns)
00767 break;
00768 }
00769 image_view=DestroyCacheView(image_view);
00770 cube_info=DestroyCubeInfo(image,cube_info);
00771 return(y < (long) image->rows ? MagickFalse : MagickTrue);
00772 }
00773
00774
00775
00776
00777
00778
00779
00780
00781
00782
00783
00784
00785
00786
00787
00788
00789
00790
00791
00792
00793
00794
00795
00796
00797
00798
00799
00800 MagickExport MagickBooleanType IsPaletteImage(const Image *image,
00801 ExceptionInfo *exception)
00802 {
00803 CacheView
00804 *image_view;
00805
00806 CubeInfo
00807 *cube_info;
00808
00809 long
00810 y;
00811
00812 MagickPixelPacket
00813 pixel,
00814 target;
00815
00816 register const IndexPacket
00817 *indexes;
00818
00819 register const PixelPacket
00820 *p;
00821
00822 register long
00823 x;
00824
00825 register NodeInfo
00826 *node_info;
00827
00828 register long
00829 i;
00830
00831 unsigned long
00832 id,
00833 index,
00834 level;
00835
00836 assert(image != (Image *) NULL);
00837 assert(image->signature == MagickSignature);
00838 if (image->debug != MagickFalse)
00839 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00840 if ((image->storage_class == PseudoClass) && (image->colors <= 256))
00841 return(MagickTrue);
00842 if (image->storage_class == PseudoClass)
00843 return(MagickFalse);
00844
00845
00846
00847 cube_info=GetCubeInfo();
00848 if (cube_info == (CubeInfo *) NULL)
00849 {
00850 (void) ThrowMagickException(exception,GetMagickModule(),
00851 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
00852 return(MagickFalse);
00853 }
00854 GetMagickPixelPacket(image,&pixel);
00855 GetMagickPixelPacket(image,&target);
00856 image_view=AcquireCacheView(image);
00857 for (y=0; y < (long) image->rows; y++)
00858 {
00859 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
00860 if (p == (const PixelPacket *) NULL)
00861 break;
00862 indexes=GetCacheViewVirtualIndexQueue(image_view);
00863 for (x=0; x < (long) image->columns; x++)
00864 {
00865
00866
00867
00868 node_info=cube_info->root;
00869 index=MaxTreeDepth-1;
00870 for (level=1; level < MaxTreeDepth; level++)
00871 {
00872 SetMagickPixelPacket(image,p,indexes+x,&pixel);
00873 id=ColorToNodeId(image,&pixel,index);
00874 if (node_info->child[id] == (NodeInfo *) NULL)
00875 {
00876 node_info->child[id]=GetNodeInfo(cube_info,level);
00877 if (node_info->child[id] == (NodeInfo *) NULL)
00878 {
00879 (void) ThrowMagickException(exception,GetMagickModule(),
00880 ResourceLimitError,"MemoryAllocationFailed","`%s'",
00881 image->filename);
00882 break;
00883 }
00884 }
00885 node_info=node_info->child[id];
00886 index--;
00887 }
00888 if (level < MaxTreeDepth)
00889 break;
00890 for (i=0; i < (long) node_info->number_unique; i++)
00891 {
00892 SetMagickPixelPacket(image,&node_info->list[i].pixel,
00893 &node_info->list[i].index,&target);
00894 if (IsMagickColorEqual(&pixel,&target) != MagickFalse)
00895 break;
00896 }
00897 if (i < (long) node_info->number_unique)
00898 node_info->list[i].count++;
00899 else
00900 {
00901
00902
00903
00904 if (node_info->number_unique == 0)
00905 node_info->list=(ColorPacket *) AcquireMagickMemory(
00906 sizeof(*node_info->list));
00907 else
00908 node_info->list=(ColorPacket *) ResizeQuantumMemory(node_info->list,
00909 (size_t) (i+1),sizeof(*node_info->list));
00910 if (node_info->list == (ColorPacket *) NULL)
00911 {
00912 (void) ThrowMagickException(exception,GetMagickModule(),
00913 ResourceLimitError,"MemoryAllocationFailed","`%s'",
00914 image->filename);
00915 break;
00916 }
00917 node_info->list[i].pixel=(*p);
00918 if ((image->colorspace == CMYKColorspace) ||
00919 (image->storage_class == PseudoClass))
00920 node_info->list[i].index=indexes[x];
00921 node_info->list[i].count=1;
00922 node_info->number_unique++;
00923 cube_info->colors++;
00924 if (cube_info->colors > 256)
00925 break;
00926 }
00927 p++;
00928 }
00929 if (x < (long) image->columns)
00930 break;
00931 }
00932 image_view=DestroyCacheView(image_view);
00933 cube_info=DestroyCubeInfo(image,cube_info);
00934 return(y < (long) image->rows ? MagickFalse : MagickTrue);
00935 }
00936
00937
00938
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
00968
00969
00970
00971
00972
00973
00974
00975
00976
00977
00978
00979
00980
00981
00982 MagickExport MagickBooleanType MinMaxStretchImage(Image *image,
00983 const ChannelType channel,const double black_value,const double white_value)
00984 {
00985 double
00986 min,max;
00987
00988 MagickStatusType
00989 status;
00990
00991 if ((channel & SyncChannels) != 0)
00992 {
00993
00994
00995
00996 (void) GetImageChannelRange(image,channel,&min,&max,&image->exception);
00997 min+=black_value;
00998 max-=white_value;
00999 return(LevelImageChannel(image,channel,min,max,1.0));
01000 }
01001
01002
01003
01004 status=MagickTrue;
01005 if ((channel & RedChannel) != 0)
01006 {
01007 (void) GetImageChannelRange(image,RedChannel,&min,&max,&image->exception);
01008 min+=black_value;
01009 max-=white_value;
01010 status&=LevelImageChannel(image,RedChannel,min,max,1.0);
01011 }
01012 if ((channel & GreenChannel) != 0)
01013 {
01014 (void) GetImageChannelRange(image,GreenChannel,&min,&max,
01015 &image->exception);
01016 min+=black_value;
01017 max-=white_value;
01018 status&=LevelImageChannel(image,GreenChannel,min,max,1.0);
01019 }
01020 if ((channel & BlueChannel) != 0)
01021 {
01022 (void) GetImageChannelRange(image,BlueChannel,&min,&max,
01023 &image->exception);
01024 min+=black_value;
01025 max-=white_value;
01026 status&=LevelImageChannel(image,BlueChannel,min,max,1.0);
01027 }
01028 if (((channel & OpacityChannel) != 0) &&
01029 (image->matte == MagickTrue))
01030 {
01031 (void) GetImageChannelRange(image,OpacityChannel,&min,&max,
01032 &image->exception);
01033 min+=black_value;
01034 max-=white_value;
01035 status&=LevelImageChannel(image,OpacityChannel,min,max,1.0);
01036 }
01037 if (((channel & IndexChannel) != 0) &&
01038 (image->colorspace == CMYKColorspace))
01039 {
01040 (void) GetImageChannelRange(image,IndexChannel,&min,&max,
01041 &image->exception);
01042 min+=black_value;
01043 max-=white_value;
01044 status&=LevelImageChannel(image,IndexChannel,min,max,1.0);
01045 }
01046 return(status != 0 ? MagickTrue : MagickFalse);
01047 }
01048
01049
01050
01051
01052
01053
01054
01055
01056
01057
01058
01059
01060
01061
01062
01063
01064
01065
01066
01067
01068
01069
01070
01071
01072
01073
01074
01075
01076
01077 #if defined(__cplusplus) || defined(c_plusplus)
01078 extern "C" {
01079 #endif
01080
01081 static int HistogramCompare(const void *x,const void *y)
01082 {
01083 const ColorPacket
01084 *color_1,
01085 *color_2;
01086
01087 color_1=(const ColorPacket *) x;
01088 color_2=(const ColorPacket *) y;
01089 if (color_2->pixel.red != color_1->pixel.red)
01090 return((int) color_1->pixel.red-(int) color_2->pixel.red);
01091 if (color_2->pixel.green != color_1->pixel.green)
01092 return((int) color_1->pixel.green-(int) color_2->pixel.green);
01093 if (color_2->pixel.blue != color_1->pixel.blue)
01094 return((int) color_1->pixel.blue-(int) color_2->pixel.blue);
01095 return((int) color_2->count-(int) color_1->count);
01096 }
01097
01098 #if defined(__cplusplus) || defined(c_plusplus)
01099 }
01100 #endif
01101
01102 MagickExport unsigned long GetNumberColors(const Image *image,FILE *file,
01103 ExceptionInfo *exception)
01104 {
01105 #define HistogramImageTag "Histogram/Image"
01106
01107 char
01108 color[MaxTextExtent],
01109 hex[MaxTextExtent],
01110 tuple[MaxTextExtent];
01111
01112 ColorPacket
01113 *histogram;
01114
01115 MagickBooleanType
01116 status;
01117
01118 MagickPixelPacket
01119 pixel;
01120
01121 register ColorPacket
01122 *p;
01123
01124 register long
01125 i;
01126
01127 unsigned long
01128 number_colors;
01129
01130 number_colors=0;
01131 if (file == (FILE *) NULL)
01132 {
01133 CubeInfo
01134 *cube_info;
01135
01136 cube_info=ClassifyImageColors(image,exception);
01137 if (cube_info != (CubeInfo *) NULL)
01138 number_colors=cube_info->colors;
01139 cube_info=DestroyCubeInfo(image,cube_info);
01140 return(number_colors);
01141 }
01142 histogram=GetImageHistogram(image,&number_colors,exception);
01143 if (histogram == (ColorPacket *) NULL)
01144 return(number_colors);
01145 qsort((void *) histogram,(size_t) number_colors,sizeof(*histogram),
01146 HistogramCompare);
01147 GetMagickPixelPacket(image,&pixel);
01148 p=histogram;
01149 for (i=0; i < (long) number_colors; i++)
01150 {
01151 SetMagickPixelPacket(image,&p->pixel,&p->index,&pixel);
01152 (void) CopyMagickString(tuple,"(",MaxTextExtent);
01153 ConcatenateColorComponent(&pixel,RedChannel,X11Compliance,tuple);
01154 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
01155 ConcatenateColorComponent(&pixel,GreenChannel,X11Compliance,tuple);
01156 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
01157 ConcatenateColorComponent(&pixel,BlueChannel,X11Compliance,tuple);
01158 if (pixel.colorspace == CMYKColorspace)
01159 {
01160 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
01161 ConcatenateColorComponent(&pixel,IndexChannel,X11Compliance,tuple);
01162 }
01163 if (pixel.matte != MagickFalse)
01164 {
01165 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
01166 ConcatenateColorComponent(&pixel,OpacityChannel,X11Compliance,tuple);
01167 }
01168 (void) ConcatenateMagickString(tuple,")",MaxTextExtent);
01169 (void) QueryMagickColorname(image,&pixel,SVGCompliance,color,exception);
01170 GetColorTuple(&pixel,MagickTrue,hex);
01171 (void) fprintf(file,MagickSizeFormat,p->count);
01172 (void) fprintf(file,": %s %s %s\n",tuple,hex,color);
01173 if (image->progress_monitor != (MagickProgressMonitor) NULL)
01174 {
01175 MagickBooleanType
01176 proceed;
01177
01178 proceed=SetImageProgress(image,HistogramImageTag,i,number_colors);
01179 if (proceed == MagickFalse)
01180 status=MagickFalse;
01181 }
01182 p++;
01183 }
01184 (void) fflush(file);
01185 histogram=(ColorPacket *) RelinquishMagickMemory(histogram);
01186 return(number_colors);
01187 }
01188
01189
01190
01191
01192
01193
01194
01195
01196
01197
01198
01199
01200
01201
01202
01203
01204
01205
01206
01207
01208
01209
01210
01211
01212
01213
01214 static void UniqueColorsToImage(Image *image,CubeInfo *cube_info,
01215 const NodeInfo *node_info,ExceptionInfo *exception)
01216 {
01217 #define UniqueColorsImageTag "UniqueColors/Image"
01218
01219 MagickBooleanType
01220 status;
01221
01222 register long
01223 i;
01224
01225 unsigned long
01226 number_children;
01227
01228
01229
01230
01231 number_children=image->matte == MagickFalse ? 8UL : 16UL;
01232 for (i=0; i < (long) number_children; i++)
01233 if (node_info->child[i] != (NodeInfo *) NULL)
01234 UniqueColorsToImage(image,cube_info,node_info->child[i],exception);
01235 if (node_info->level == (MaxTreeDepth-1))
01236 {
01237 register ColorPacket
01238 *p;
01239
01240 register IndexPacket
01241 *restrict indexes;
01242
01243 register PixelPacket
01244 *restrict q;
01245
01246 p=node_info->list;
01247 for (i=0; i < (long) node_info->number_unique; i++)
01248 {
01249 q=QueueAuthenticPixels(image,cube_info->x,0,1,1,exception);
01250 if (q == (PixelPacket *) NULL)
01251 continue;
01252 indexes=GetAuthenticIndexQueue(image);
01253 *q=p->pixel;
01254 if (image->colorspace == CMYKColorspace)
01255 *indexes=p->index;
01256 if (SyncAuthenticPixels(image,exception) == MagickFalse)
01257 break;
01258 cube_info->x++;
01259 p++;
01260 }
01261 if (image->progress_monitor != (MagickProgressMonitor) NULL)
01262 {
01263 MagickBooleanType
01264 proceed;
01265
01266 proceed=SetImageProgress(image,UniqueColorsImageTag,
01267 cube_info->progress,cube_info->colors);
01268 if (proceed == MagickFalse)
01269 status=MagickFalse;
01270 }
01271 cube_info->progress++;
01272 }
01273 }
01274
01275 MagickExport Image *UniqueImageColors(const Image *image,
01276 ExceptionInfo *exception)
01277 {
01278 CubeInfo
01279 *cube_info;
01280
01281 Image
01282 *unique_image;
01283
01284 cube_info=ClassifyImageColors(image,exception);
01285 if (cube_info == (CubeInfo *) NULL)
01286 return((Image *) NULL);
01287 unique_image=CloneImage(image,cube_info->colors,1,MagickTrue,exception);
01288 if (unique_image == (Image *) NULL)
01289 return(unique_image);
01290 if (SetImageStorageClass(unique_image,DirectClass) == MagickFalse)
01291 {
01292 InheritException(exception,&unique_image->exception);
01293 unique_image=DestroyImage(unique_image);
01294 return((Image *) NULL);
01295 }
01296 UniqueColorsToImage(unique_image,cube_info,cube_info->root,exception);
01297 if (cube_info->colors < MaxColormapSize)
01298 {
01299 QuantizeInfo
01300 *quantize_info;
01301
01302 quantize_info=AcquireQuantizeInfo((ImageInfo *) NULL);
01303 quantize_info->number_colors=MaxColormapSize;
01304 quantize_info->dither=MagickFalse;
01305 quantize_info->tree_depth=8;
01306 (void) QuantizeImage(quantize_info,unique_image);
01307 quantize_info=DestroyQuantizeInfo(quantize_info);
01308 }
01309 cube_info=DestroyCubeInfo(image,cube_info);
01310 return(unique_image);
01311 }