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(RoundToQuantum(pixel->red)) >> index) & 0x01) |
00165 ((ScaleQuantumToChar(RoundToQuantum(pixel->green)) >> index) & 0x01) << 1 |
00166 ((ScaleQuantumToChar(RoundToQuantum(pixel->blue)) >> index) & 0x01) << 2);
00167 if (image->matte != MagickFalse)
00168 id|=((ScaleQuantumToChar(RoundToQuantum(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 CubeInfo
00179 *cube_info;
00180
00181 long
00182 y;
00183
00184 MagickBooleanType
00185 proceed;
00186
00187 MagickPixelPacket
00188 pixel,
00189 target;
00190
00191 NodeInfo
00192 *node_info;
00193
00194 register const IndexPacket
00195 *indexes;
00196
00197 register const PixelPacket
00198 *p;
00199
00200 register long
00201 i,
00202 x;
00203
00204 register unsigned long
00205 id,
00206 index,
00207 level;
00208
00209 CacheView
00210 *image_view;
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 *) AcquireMagickMemory(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 *) AcquireMagickMemory(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 CubeInfo
00641 *cube_info;
00642
00643 long
00644 y;
00645
00646 MagickPixelPacket
00647 pixel,
00648 target;
00649
00650 register const IndexPacket
00651 *indexes;
00652
00653 register const PixelPacket
00654 *p;
00655
00656 register long
00657 x;
00658
00659 register NodeInfo
00660 *node_info;
00661
00662 register long
00663 i;
00664
00665 unsigned long
00666 id,
00667 index,
00668 level;
00669
00670 CacheView
00671 *image_view;
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 CubeInfo
00804 *cube_info;
00805
00806 long
00807 y;
00808
00809 MagickPixelPacket
00810 pixel,
00811 target;
00812
00813 register const IndexPacket
00814 *indexes;
00815
00816 register const PixelPacket
00817 *p;
00818
00819 register long
00820 x;
00821
00822 register NodeInfo
00823 *node_info;
00824
00825 register long
00826 i;
00827
00828 unsigned long
00829 id,
00830 index,
00831 level;
00832
00833 CacheView
00834 *image_view;
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 MagickPixelPacket
01116 pixel;
01117
01118 register ColorPacket
01119 *p;
01120
01121 register long
01122 i;
01123
01124 unsigned long
01125 number_colors;
01126
01127 number_colors=0;
01128 if (file == (FILE *) NULL)
01129 {
01130 CubeInfo
01131 *cube_info;
01132
01133 cube_info=ClassifyImageColors(image,exception);
01134 if (cube_info != (CubeInfo *) NULL)
01135 number_colors=cube_info->colors;
01136 cube_info=DestroyCubeInfo(image,cube_info);
01137 return(number_colors);
01138 }
01139 histogram=GetImageHistogram(image,&number_colors,exception);
01140 if (histogram == (ColorPacket *) NULL)
01141 return(number_colors);
01142 qsort((void *) histogram,(size_t) number_colors,sizeof(*histogram),
01143 HistogramCompare);
01144 GetMagickPixelPacket(image,&pixel);
01145 p=histogram;
01146 for (i=0; i < (long) number_colors; i++)
01147 {
01148 SetMagickPixelPacket(image,&p->pixel,&p->index,&pixel);
01149 (void) CopyMagickString(tuple,"(",MaxTextExtent);
01150 ConcatenateColorComponent(&pixel,RedChannel,X11Compliance,tuple);
01151 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
01152 ConcatenateColorComponent(&pixel,GreenChannel,X11Compliance,tuple);
01153 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
01154 ConcatenateColorComponent(&pixel,BlueChannel,X11Compliance,tuple);
01155 if (pixel.colorspace == CMYKColorspace)
01156 {
01157 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
01158 ConcatenateColorComponent(&pixel,IndexChannel,X11Compliance,tuple);
01159 }
01160 if (pixel.matte != MagickFalse)
01161 {
01162 (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
01163 ConcatenateColorComponent(&pixel,OpacityChannel,X11Compliance,tuple);
01164 }
01165 (void) ConcatenateMagickString(tuple,")",MaxTextExtent);
01166 (void) QueryMagickColorname(image,&pixel,SVGCompliance,color,exception);
01167 GetColorTuple(&pixel,MagickTrue,hex);
01168 (void) fprintf(file,MagickSizeFormat,p->count);
01169 (void) fprintf(file,": %s %s %s\n",tuple,hex,color);
01170 if ((image->progress_monitor != (MagickProgressMonitor) NULL) &&
01171 (QuantumTick(i,number_colors) != MagickFalse))
01172 (void) image->progress_monitor(HistogramImageTag,i,number_colors,
01173 image->client_data);
01174 p++;
01175 }
01176 (void) fflush(file);
01177 histogram=(ColorPacket *) RelinquishMagickMemory(histogram);
01178 return(number_colors);
01179 }
01180
01181
01182
01183
01184
01185
01186
01187
01188
01189
01190
01191
01192
01193
01194
01195
01196
01197
01198
01199
01200
01201
01202
01203
01204
01205
01206 static void UniqueColorsToImage(Image *image,CubeInfo *cube_info,
01207 const NodeInfo *node_info,ExceptionInfo *exception)
01208 {
01209 #define UniqueColorsImageTag "UniqueColors/Image"
01210
01211 register long
01212 i;
01213
01214 unsigned long
01215 number_children;
01216
01217
01218
01219
01220 number_children=image->matte == MagickFalse ? 8UL : 16UL;
01221 for (i=0; i < (long) number_children; i++)
01222 if (node_info->child[i] != (NodeInfo *) NULL)
01223 UniqueColorsToImage(image,cube_info,node_info->child[i],exception);
01224 if (node_info->level == (MaxTreeDepth-1))
01225 {
01226 register ColorPacket
01227 *p;
01228
01229 register IndexPacket
01230 *__restrict indexes;
01231
01232 register PixelPacket
01233 *__restrict q;
01234
01235 p=node_info->list;
01236 for (i=0; i < (long) node_info->number_unique; i++)
01237 {
01238 q=QueueAuthenticPixels(image,cube_info->x,0,1,1,exception);
01239 if (q == (PixelPacket *) NULL)
01240 continue;
01241 indexes=GetAuthenticIndexQueue(image);
01242 *q=p->pixel;
01243 if (image->colorspace == CMYKColorspace)
01244 *indexes=p->index;
01245 if (SyncAuthenticPixels(image,exception) == MagickFalse)
01246 break;
01247 cube_info->x++;
01248 p++;
01249 }
01250 if ((image->progress_monitor != (MagickProgressMonitor) NULL) &&
01251 (QuantumTick(cube_info->progress,cube_info->colors) != MagickFalse))
01252 (void) image->progress_monitor(UniqueColorsImageTag,cube_info->progress,
01253 cube_info->colors,image->client_data);
01254 cube_info->progress++;
01255 }
01256 }
01257
01258 MagickExport Image *UniqueImageColors(const Image *image,
01259 ExceptionInfo *exception)
01260 {
01261 CubeInfo
01262 *cube_info;
01263
01264 Image
01265 *unique_image;
01266
01267 cube_info=ClassifyImageColors(image,exception);
01268 if (cube_info == (CubeInfo *) NULL)
01269 return((Image *) NULL);
01270 unique_image=CloneImage(image,cube_info->colors,1,MagickTrue,exception);
01271 if (unique_image == (Image *) NULL)
01272 return(unique_image);
01273 if (SetImageStorageClass(unique_image,DirectClass) == MagickFalse)
01274 {
01275 InheritException(exception,&unique_image->exception);
01276 unique_image=DestroyImage(unique_image);
01277 return((Image *) NULL);
01278 }
01279 UniqueColorsToImage(unique_image,cube_info,cube_info->root,exception);
01280 if (cube_info->colors < MaxColormapSize)
01281 {
01282 QuantizeInfo
01283 *quantize_info;
01284
01285 quantize_info=AcquireQuantizeInfo((ImageInfo *) NULL);
01286 quantize_info->number_colors=MaxColormapSize;
01287 quantize_info->dither=MagickFalse;
01288 quantize_info->tree_depth=8;
01289 (void) QuantizeImage(quantize_info,unique_image);
01290 quantize_info=DestroyQuantizeInfo(quantize_info);
01291 }
01292 cube_info=DestroyCubeInfo(image,cube_info);
01293 return(unique_image);
01294 }