|
MagickCore
6.7.5
|
00001 /* 00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00003 % % 00004 % % 00005 % % 00006 % QQQ U U AAA N N TTTTT IIIII ZZZZZ EEEEE % 00007 % Q Q U U A A NN N T I ZZ E % 00008 % Q Q U U AAAAA N N N T I ZZZ EEEEE % 00009 % Q QQ U U A A N NN T I ZZ E % 00010 % QQQQ UUU A A N N T IIIII ZZZZZ EEEEE % 00011 % % 00012 % % 00013 % MagickCore Methods to Reduce the Number of Unique Colors in an Image % 00014 % % 00015 % Software Design % 00016 % John Cristy % 00017 % July 1992 % 00018 % % 00019 % % 00020 % Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization % 00021 % dedicated to making software imaging solutions freely available. % 00022 % % 00023 % You may not use this file except in compliance with the License. You may % 00024 % obtain a copy of the License at % 00025 % % 00026 % http://www.imagemagick.org/script/license.php % 00027 % % 00028 % Unless required by applicable law or agreed to in writing, software % 00029 % distributed under the License is distributed on an "AS IS" BASIS, % 00030 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % 00031 % See the License for the specific language governing permissions and % 00032 % limitations under the License. % 00033 % % 00034 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00035 % 00036 % Realism in computer graphics typically requires using 24 bits/pixel to 00037 % generate an image. Yet many graphic display devices do not contain the 00038 % amount of memory necessary to match the spatial and color resolution of 00039 % the human eye. The Quantize methods takes a 24 bit image and reduces 00040 % the number of colors so it can be displayed on raster device with less 00041 % bits per pixel. In most instances, the quantized image closely 00042 % resembles the original reference image. 00043 % 00044 % A reduction of colors in an image is also desirable for image 00045 % transmission and real-time animation. 00046 % 00047 % QuantizeImage() takes a standard RGB or monochrome images and quantizes 00048 % them down to some fixed number of colors. 00049 % 00050 % For purposes of color allocation, an image is a set of n pixels, where 00051 % each pixel is a point in RGB space. RGB space is a 3-dimensional 00052 % vector space, and each pixel, Pi, is defined by an ordered triple of 00053 % red, green, and blue coordinates, (Ri, Gi, Bi). 00054 % 00055 % Each primary color component (red, green, or blue) represents an 00056 % intensity which varies linearly from 0 to a maximum value, Cmax, which 00057 % corresponds to full saturation of that color. Color allocation is 00058 % defined over a domain consisting of the cube in RGB space with opposite 00059 % vertices at (0,0,0) and (Cmax, Cmax, Cmax). QUANTIZE requires Cmax = 00060 % 255. 00061 % 00062 % The algorithm maps this domain onto a tree in which each node 00063 % represents a cube within that domain. In the following discussion 00064 % these cubes are defined by the coordinate of two opposite vertices: 00065 % The vertex nearest the origin in RGB space and the vertex farthest from 00066 % the origin. 00067 % 00068 % The tree's root node represents the entire domain, (0,0,0) through 00069 % (Cmax,Cmax,Cmax). Each lower level in the tree is generated by 00070 % subdividing one node's cube into eight smaller cubes of equal size. 00071 % This corresponds to bisecting the parent cube with planes passing 00072 % through the midpoints of each edge. 00073 % 00074 % The basic algorithm operates in three phases: Classification, 00075 % Reduction, and Assignment. Classification builds a color description 00076 % tree for the image. Reduction collapses the tree until the number it 00077 % represents, at most, the number of colors desired in the output image. 00078 % Assignment defines the output image's color map and sets each pixel's 00079 % color by restorage_class in the reduced tree. Our goal is to minimize 00080 % the numerical discrepancies between the original colors and quantized 00081 % colors (quantization error). 00082 % 00083 % Classification begins by initializing a color description tree of 00084 % sufficient depth to represent each possible input color in a leaf. 00085 % However, it is impractical to generate a fully-formed color description 00086 % tree in the storage_class phase for realistic values of Cmax. If 00087 % colors components in the input image are quantized to k-bit precision, 00088 % so that Cmax= 2k-1, the tree would need k levels below the root node to 00089 % allow representing each possible input color in a leaf. This becomes 00090 % prohibitive because the tree's total number of nodes is 1 + 00091 % sum(i=1, k, 8k). 00092 % 00093 % A complete tree would require 19,173,961 nodes for k = 8, Cmax = 255. 00094 % Therefore, to avoid building a fully populated tree, QUANTIZE: (1) 00095 % Initializes data structures for nodes only as they are needed; (2) 00096 % Chooses a maximum depth for the tree as a function of the desired 00097 % number of colors in the output image (currently log2(colormap size)). 00098 % 00099 % For each pixel in the input image, storage_class scans downward from 00100 % the root of the color description tree. At each level of the tree it 00101 % identifies the single node which represents a cube in RGB space 00102 % containing the pixel's color. It updates the following data for each 00103 % such node: 00104 % 00105 % n1: Number of pixels whose color is contained in the RGB cube which 00106 % this node represents; 00107 % 00108 % n2: Number of pixels whose color is not represented in a node at 00109 % lower depth in the tree; initially, n2 = 0 for all nodes except 00110 % leaves of the tree. 00111 % 00112 % Sr, Sg, Sb: Sums of the red, green, and blue component values for all 00113 % pixels not classified at a lower depth. The combination of these sums 00114 % and n2 will ultimately characterize the mean color of a set of 00115 % pixels represented by this node. 00116 % 00117 % E: the distance squared in RGB space between each pixel contained 00118 % within a node and the nodes' center. This represents the 00119 % quantization error for a node. 00120 % 00121 % Reduction repeatedly prunes the tree until the number of nodes with n2 00122 % > 0 is less than or equal to the maximum number of colors allowed in 00123 % the output image. On any given iteration over the tree, it selects 00124 % those nodes whose E count is minimal for pruning and merges their color 00125 % statistics upward. It uses a pruning threshold, Ep, to govern node 00126 % selection as follows: 00127 % 00128 % Ep = 0 00129 % while number of nodes with (n2 > 0) > required maximum number of colors 00130 % prune all nodes such that E <= Ep 00131 % Set Ep to minimum E in remaining nodes 00132 % 00133 % This has the effect of minimizing any quantization error when merging 00134 % two nodes together. 00135 % 00136 % When a node to be pruned has offspring, the pruning procedure invokes 00137 % itself recursively in order to prune the tree from the leaves upward. 00138 % n2, Sr, Sg, and Sb in a node being pruned are always added to the 00139 % corresponding data in that node's parent. This retains the pruned 00140 % node's color characteristics for later averaging. 00141 % 00142 % For each node, n2 pixels exist for which that node represents the 00143 % smallest volume in RGB space containing those pixel's colors. When n2 00144 % > 0 the node will uniquely define a color in the output image. At the 00145 % beginning of reduction, n2 = 0 for all nodes except a the leaves of 00146 % the tree which represent colors present in the input image. 00147 % 00148 % The other pixel count, n1, indicates the total number of colors within 00149 % the cubic volume which the node represents. This includes n1 - n2 00150 % pixels whose colors should be defined by nodes at a lower level in the 00151 % tree. 00152 % 00153 % Assignment generates the output image from the pruned tree. The output 00154 % image consists of two parts: (1) A color map, which is an array of 00155 % color descriptions (RGB triples) for each color present in the output 00156 % image; (2) A pixel array, which represents each pixel as an index 00157 % into the color map array. 00158 % 00159 % First, the assignment phase makes one pass over the pruned color 00160 % description tree to establish the image's color map. For each node 00161 % with n2 > 0, it divides Sr, Sg, and Sb by n2 . This produces the mean 00162 % color of all pixels that classify no lower than this node. Each of 00163 % these colors becomes an entry in the color map. 00164 % 00165 % Finally, the assignment phase reclassifies each pixel in the pruned 00166 % tree to identify the deepest node containing the pixel's color. The 00167 % pixel's value in the pixel array becomes the index of this node's mean 00168 % color in the color map. 00169 % 00170 % This method is based on a similar algorithm written by Paul Raveling. 00171 % 00172 */ 00173 00174 /* 00175 Include declarations. 00176 */ 00177 #include "MagickCore/studio.h" 00178 #include "MagickCore/attribute.h" 00179 #include "MagickCore/cache-view.h" 00180 #include "MagickCore/color.h" 00181 #include "MagickCore/color-private.h" 00182 #include "MagickCore/colormap.h" 00183 #include "MagickCore/colorspace.h" 00184 #include "MagickCore/colorspace-private.h" 00185 #include "MagickCore/enhance.h" 00186 #include "MagickCore/exception.h" 00187 #include "MagickCore/exception-private.h" 00188 #include "MagickCore/histogram.h" 00189 #include "MagickCore/image.h" 00190 #include "MagickCore/image-private.h" 00191 #include "MagickCore/list.h" 00192 #include "MagickCore/memory_.h" 00193 #include "MagickCore/monitor.h" 00194 #include "MagickCore/monitor-private.h" 00195 #include "MagickCore/option.h" 00196 #include "MagickCore/pixel-accessor.h" 00197 #include "MagickCore/quantize.h" 00198 #include "MagickCore/quantum.h" 00199 #include "MagickCore/quantum-private.h" 00200 #include "MagickCore/string_.h" 00201 #include "MagickCore/thread-private.h" 00202 00203 /* 00204 Define declarations. 00205 */ 00206 #if !defined(__APPLE__) && !defined(TARGET_OS_IPHONE) 00207 #define CacheShift 2 00208 #else 00209 #define CacheShift 3 00210 #endif 00211 #define ErrorQueueLength 16 00212 #define MaxNodes 266817 00213 #define MaxTreeDepth 8 00214 #define NodesInAList 1920 00215 00216 /* 00217 Typdef declarations. 00218 */ 00219 typedef struct _RealPixelInfo 00220 { 00221 MagickRealType 00222 red, 00223 green, 00224 blue, 00225 alpha; 00226 } RealPixelInfo; 00227 00228 typedef struct _NodeInfo 00229 { 00230 struct _NodeInfo 00231 *parent, 00232 *child[16]; 00233 00234 MagickSizeType 00235 number_unique; 00236 00237 RealPixelInfo 00238 total_color; 00239 00240 MagickRealType 00241 quantize_error; 00242 00243 size_t 00244 color_number, 00245 id, 00246 level; 00247 } NodeInfo; 00248 00249 typedef struct _Nodes 00250 { 00251 NodeInfo 00252 *nodes; 00253 00254 struct _Nodes 00255 *next; 00256 } Nodes; 00257 00258 typedef struct _CubeInfo 00259 { 00260 NodeInfo 00261 *root; 00262 00263 size_t 00264 colors, 00265 maximum_colors; 00266 00267 ssize_t 00268 transparent_index; 00269 00270 MagickSizeType 00271 transparent_pixels; 00272 00273 RealPixelInfo 00274 target; 00275 00276 MagickRealType 00277 distance, 00278 pruning_threshold, 00279 next_threshold; 00280 00281 size_t 00282 nodes, 00283 free_nodes, 00284 color_number; 00285 00286 NodeInfo 00287 *next_node; 00288 00289 Nodes 00290 *node_queue; 00291 00292 ssize_t 00293 *cache; 00294 00295 RealPixelInfo 00296 error[ErrorQueueLength]; 00297 00298 MagickRealType 00299 weights[ErrorQueueLength]; 00300 00301 QuantizeInfo 00302 *quantize_info; 00303 00304 MagickBooleanType 00305 associate_alpha; 00306 00307 ssize_t 00308 x, 00309 y; 00310 00311 size_t 00312 depth; 00313 00314 MagickOffsetType 00315 offset; 00316 00317 MagickSizeType 00318 span; 00319 } CubeInfo; 00320 00321 /* 00322 Method prototypes. 00323 */ 00324 static CubeInfo 00325 *GetCubeInfo(const QuantizeInfo *,const size_t,const size_t); 00326 00327 static NodeInfo 00328 *GetNodeInfo(CubeInfo *,const size_t,const size_t,NodeInfo *); 00329 00330 static MagickBooleanType 00331 AssignImageColors(Image *,CubeInfo *,ExceptionInfo *), 00332 ClassifyImageColors(CubeInfo *,const Image *,ExceptionInfo *), 00333 DitherImage(Image *,CubeInfo *,ExceptionInfo *), 00334 SetGrayscaleImage(Image *,ExceptionInfo *); 00335 00336 static size_t 00337 DefineImageColormap(Image *,CubeInfo *,NodeInfo *); 00338 00339 static void 00340 ClosestColor(const Image *,CubeInfo *,const NodeInfo *), 00341 DestroyCubeInfo(CubeInfo *), 00342 PruneLevel(const Image *,CubeInfo *,const NodeInfo *), 00343 PruneToCubeDepth(const Image *,CubeInfo *,const NodeInfo *), 00344 ReduceImageColors(const Image *,CubeInfo *); 00345 00346 /* 00347 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00348 % % 00349 % % 00350 % % 00351 % A c q u i r e Q u a n t i z e I n f o % 00352 % % 00353 % % 00354 % % 00355 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00356 % 00357 % AcquireQuantizeInfo() allocates the QuantizeInfo structure. 00358 % 00359 % The format of the AcquireQuantizeInfo method is: 00360 % 00361 % QuantizeInfo *AcquireQuantizeInfo(const ImageInfo *image_info) 00362 % 00363 % A description of each parameter follows: 00364 % 00365 % o image_info: the image info. 00366 % 00367 */ 00368 MagickExport QuantizeInfo *AcquireQuantizeInfo(const ImageInfo *image_info) 00369 { 00370 QuantizeInfo 00371 *quantize_info; 00372 00373 quantize_info=(QuantizeInfo *) AcquireMagickMemory(sizeof(*quantize_info)); 00374 if (quantize_info == (QuantizeInfo *) NULL) 00375 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); 00376 GetQuantizeInfo(quantize_info); 00377 if (image_info != (ImageInfo *) NULL) 00378 { 00379 const char 00380 *option; 00381 00382 quantize_info->dither=image_info->dither; 00383 option=GetImageOption(image_info,"dither"); 00384 if (option != (const char *) NULL) 00385 quantize_info->dither_method=(DitherMethod) ParseCommandOption( 00386 MagickDitherOptions,MagickFalse,option); 00387 quantize_info->measure_error=image_info->verbose; 00388 } 00389 return(quantize_info); 00390 } 00391 00392 /* 00393 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00394 % % 00395 % % 00396 % % 00397 + A s s i g n I m a g e C o l o r s % 00398 % % 00399 % % 00400 % % 00401 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00402 % 00403 % AssignImageColors() generates the output image from the pruned tree. The 00404 % output image consists of two parts: (1) A color map, which is an array 00405 % of color descriptions (RGB triples) for each color present in the 00406 % output image; (2) A pixel array, which represents each pixel as an 00407 % index into the color map array. 00408 % 00409 % First, the assignment phase makes one pass over the pruned color 00410 % description tree to establish the image's color map. For each node 00411 % with n2 > 0, it divides Sr, Sg, and Sb by n2 . This produces the mean 00412 % color of all pixels that classify no lower than this node. Each of 00413 % these colors becomes an entry in the color map. 00414 % 00415 % Finally, the assignment phase reclassifies each pixel in the pruned 00416 % tree to identify the deepest node containing the pixel's color. The 00417 % pixel's value in the pixel array becomes the index of this node's mean 00418 % color in the color map. 00419 % 00420 % The format of the AssignImageColors() method is: 00421 % 00422 % MagickBooleanType AssignImageColors(Image *image,CubeInfo *cube_info) 00423 % 00424 % A description of each parameter follows. 00425 % 00426 % o image: the image. 00427 % 00428 % o cube_info: A pointer to the Cube structure. 00429 % 00430 */ 00431 00432 static inline void AssociateAlphaPixel(const Image *image, 00433 const CubeInfo *cube_info,const Quantum *pixel,RealPixelInfo *alpha_pixel) 00434 { 00435 MagickRealType 00436 alpha; 00437 00438 if ((cube_info->associate_alpha == MagickFalse) || 00439 (GetPixelAlpha(image,pixel)== OpaqueAlpha)) 00440 { 00441 alpha_pixel->red=(MagickRealType) GetPixelRed(image,pixel); 00442 alpha_pixel->green=(MagickRealType) GetPixelGreen(image,pixel); 00443 alpha_pixel->blue=(MagickRealType) GetPixelBlue(image,pixel); 00444 alpha_pixel->alpha=(MagickRealType) GetPixelAlpha(image,pixel); 00445 return; 00446 } 00447 alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,pixel)); 00448 alpha_pixel->red=alpha*GetPixelRed(image,pixel); 00449 alpha_pixel->green=alpha*GetPixelGreen(image,pixel); 00450 alpha_pixel->blue=alpha*GetPixelBlue(image,pixel); 00451 alpha_pixel->alpha=(MagickRealType) GetPixelAlpha(image,pixel); 00452 } 00453 00454 static inline void AssociateAlphaPixelInfo(const Image *image, 00455 const CubeInfo *cube_info,const PixelInfo *pixel, 00456 RealPixelInfo *alpha_pixel) 00457 { 00458 MagickRealType 00459 alpha; 00460 00461 if ((cube_info->associate_alpha == MagickFalse) || 00462 (pixel->alpha == OpaqueAlpha)) 00463 { 00464 alpha_pixel->red=(MagickRealType) pixel->red; 00465 alpha_pixel->green=(MagickRealType) pixel->green; 00466 alpha_pixel->blue=(MagickRealType) pixel->blue; 00467 alpha_pixel->alpha=(MagickRealType) pixel->alpha; 00468 return; 00469 } 00470 alpha=(MagickRealType) (QuantumScale*pixel->alpha); 00471 alpha_pixel->red=alpha*pixel->red; 00472 alpha_pixel->green=alpha*pixel->green; 00473 alpha_pixel->blue=alpha*pixel->blue; 00474 alpha_pixel->alpha=(MagickRealType) pixel->alpha; 00475 } 00476 00477 static inline Quantum ClampToUnsignedQuantum(const MagickRealType value) 00478 { 00479 if (value <= 0.0) 00480 return((Quantum) 0); 00481 if (value >= QuantumRange) 00482 return((Quantum) QuantumRange); 00483 return((Quantum) (value+0.5)); 00484 } 00485 00486 static inline size_t ColorToNodeId(const CubeInfo *cube_info, 00487 const RealPixelInfo *pixel,size_t index) 00488 { 00489 size_t 00490 id; 00491 00492 id=(size_t) (((ScaleQuantumToChar(ClampToUnsignedQuantum(pixel->red)) >> index) & 0x01) | 00493 ((ScaleQuantumToChar(ClampToUnsignedQuantum(pixel->green)) >> index) & 0x01) << 1 | 00494 ((ScaleQuantumToChar(ClampToUnsignedQuantum(pixel->blue)) >> index) & 0x01) << 2); 00495 if (cube_info->associate_alpha != MagickFalse) 00496 id|=((ScaleQuantumToChar(ClampToUnsignedQuantum(pixel->alpha)) >> index) & 0x1) << 3; 00497 return(id); 00498 } 00499 00500 static MagickBooleanType AssignImageColors(Image *image,CubeInfo *cube_info, 00501 ExceptionInfo *exception) 00502 { 00503 #define AssignImageTag "Assign/Image" 00504 00505 ssize_t 00506 y; 00507 00508 /* 00509 Allocate image colormap. 00510 */ 00511 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) && 00512 (cube_info->quantize_info->colorspace != CMYKColorspace)) 00513 (void) TransformImageColorspace((Image *) image, 00514 cube_info->quantize_info->colorspace,exception); 00515 else 00516 if ((image->colorspace != GRAYColorspace) && 00517 (IsRGBColorspace(image->colorspace) == MagickFalse) && 00518 (image->colorspace != CMYColorspace)) 00519 (void) TransformImageColorspace((Image *) image,RGBColorspace,exception); 00520 if (AcquireImageColormap(image,cube_info->colors,exception) == MagickFalse) 00521 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 00522 image->filename); 00523 image->colors=0; 00524 cube_info->transparent_pixels=0; 00525 cube_info->transparent_index=(-1); 00526 (void) DefineImageColormap(image,cube_info,cube_info->root); 00527 /* 00528 Create a reduced color image. 00529 */ 00530 if ((cube_info->quantize_info->dither != MagickFalse) && 00531 (cube_info->quantize_info->dither_method != NoDitherMethod)) 00532 (void) DitherImage(image,cube_info,exception); 00533 else 00534 { 00535 CacheView 00536 *image_view; 00537 00538 ExceptionInfo 00539 *exception; 00540 00541 MagickBooleanType 00542 status; 00543 00544 status=MagickTrue; 00545 image_view=AcquireCacheView(image); 00546 #if defined(MAGICKCORE_OPENMP_SUPPORT) 00547 #pragma omp parallel for schedule(static,4) shared(status) 00548 #endif 00549 for (y=0; y < (ssize_t) image->rows; y++) 00550 { 00551 CubeInfo 00552 cube; 00553 00554 register Quantum 00555 *restrict q; 00556 00557 register ssize_t 00558 x; 00559 00560 ssize_t 00561 count; 00562 00563 if (status == MagickFalse) 00564 continue; 00565 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1, 00566 exception); 00567 if (q == (Quantum *) NULL) 00568 { 00569 status=MagickFalse; 00570 continue; 00571 } 00572 cube=(*cube_info); 00573 for (x=0; x < (ssize_t) image->columns; x+=count) 00574 { 00575 RealPixelInfo 00576 pixel; 00577 00578 register const NodeInfo 00579 *node_info; 00580 00581 register ssize_t 00582 i; 00583 00584 size_t 00585 id, 00586 index; 00587 00588 /* 00589 Identify the deepest node containing the pixel's color. 00590 */ 00591 for (count=1; (x+count) < (ssize_t) image->columns; count++) 00592 { 00593 PixelInfo 00594 packet; 00595 00596 GetPixelInfoPixel(image,q+count*GetPixelChannels(image),&packet); 00597 if (IsPixelEquivalent(image,q,&packet) == MagickFalse) 00598 break; 00599 } 00600 AssociateAlphaPixel(image,&cube,q,&pixel); 00601 node_info=cube.root; 00602 for (index=MaxTreeDepth-1; (ssize_t) index > 0; index--) 00603 { 00604 id=ColorToNodeId(&cube,&pixel,index); 00605 if (node_info->child[id] == (NodeInfo *) NULL) 00606 break; 00607 node_info=node_info->child[id]; 00608 } 00609 /* 00610 Find closest color among siblings and their children. 00611 */ 00612 cube.target=pixel; 00613 cube.distance=(MagickRealType) (4.0*(QuantumRange+1.0)* 00614 (QuantumRange+1.0)+1.0); 00615 ClosestColor(image,&cube,node_info->parent); 00616 index=cube.color_number; 00617 for (i=0; i < (ssize_t) count; i++) 00618 { 00619 if (image->storage_class == PseudoClass) 00620 SetPixelIndex(image,(Quantum) index,q); 00621 if (cube.quantize_info->measure_error == MagickFalse) 00622 { 00623 SetPixelRed(image,ClampToQuantum( 00624 image->colormap[index].red),q); 00625 SetPixelGreen(image,ClampToQuantum( 00626 image->colormap[index].green),q); 00627 SetPixelBlue(image,ClampToQuantum( 00628 image->colormap[index].blue),q); 00629 if (cube.associate_alpha != MagickFalse) 00630 SetPixelAlpha(image,ClampToQuantum( 00631 image->colormap[index].alpha),q); 00632 } 00633 q+=GetPixelChannels(image); 00634 } 00635 } 00636 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 00637 status=MagickFalse; 00638 if (image->progress_monitor != (MagickProgressMonitor) NULL) 00639 { 00640 MagickBooleanType 00641 proceed; 00642 00643 #if defined(MAGICKCORE_OPENMP_SUPPORT) 00644 #pragma omp critical (MagickCore_AssignImageColors) 00645 #endif 00646 proceed=SetImageProgress(image,AssignImageTag,(MagickOffsetType) y, 00647 image->rows); 00648 if (proceed == MagickFalse) 00649 status=MagickFalse; 00650 } 00651 } 00652 image_view=DestroyCacheView(image_view); 00653 } 00654 if (cube_info->quantize_info->measure_error != MagickFalse) 00655 (void) GetImageQuantizeError(image,exception); 00656 if ((cube_info->quantize_info->number_colors == 2) && 00657 (cube_info->quantize_info->colorspace == GRAYColorspace)) 00658 { 00659 double 00660 intensity; 00661 00662 register PixelInfo 00663 *restrict q; 00664 00665 register ssize_t 00666 i; 00667 00668 /* 00669 Monochrome image. 00670 */ 00671 q=image->colormap; 00672 for (i=0; i < (ssize_t) image->colors; i++) 00673 { 00674 intensity=(double) ((MagickRealType) GetPixelInfoIntensity(q) < 00675 ((MagickRealType) QuantumRange/2.0) ? 0 : QuantumRange); 00676 q->red=intensity; 00677 q->green=intensity; 00678 q->blue=intensity; 00679 q++; 00680 } 00681 } 00682 (void) SyncImage(image,exception); 00683 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) && 00684 (cube_info->quantize_info->colorspace != CMYKColorspace)) 00685 (void) TransformImageColorspace((Image *) image,RGBColorspace,exception); 00686 return(MagickTrue); 00687 } 00688 00689 /* 00690 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00691 % % 00692 % % 00693 % % 00694 + C l a s s i f y I m a g e C o l o r s % 00695 % % 00696 % % 00697 % % 00698 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00699 % 00700 % ClassifyImageColors() begins by initializing a color description tree 00701 % of sufficient depth to represent each possible input color in a leaf. 00702 % However, it is impractical to generate a fully-formed color 00703 % description tree in the storage_class phase for realistic values of 00704 % Cmax. If colors components in the input image are quantized to k-bit 00705 % precision, so that Cmax= 2k-1, the tree would need k levels below the 00706 % root node to allow representing each possible input color in a leaf. 00707 % This becomes prohibitive because the tree's total number of nodes is 00708 % 1 + sum(i=1,k,8k). 00709 % 00710 % A complete tree would require 19,173,961 nodes for k = 8, Cmax = 255. 00711 % Therefore, to avoid building a fully populated tree, QUANTIZE: (1) 00712 % Initializes data structures for nodes only as they are needed; (2) 00713 % Chooses a maximum depth for the tree as a function of the desired 00714 % number of colors in the output image (currently log2(colormap size)). 00715 % 00716 % For each pixel in the input image, storage_class scans downward from 00717 % the root of the color description tree. At each level of the tree it 00718 % identifies the single node which represents a cube in RGB space 00719 % containing It updates the following data for each such node: 00720 % 00721 % n1 : Number of pixels whose color is contained in the RGB cube 00722 % which this node represents; 00723 % 00724 % n2 : Number of pixels whose color is not represented in a node at 00725 % lower depth in the tree; initially, n2 = 0 for all nodes except 00726 % leaves of the tree. 00727 % 00728 % Sr, Sg, Sb : Sums of the red, green, and blue component values for 00729 % all pixels not classified at a lower depth. The combination of 00730 % these sums and n2 will ultimately characterize the mean color of a 00731 % set of pixels represented by this node. 00732 % 00733 % E: the distance squared in RGB space between each pixel contained 00734 % within a node and the nodes' center. This represents the quantization 00735 % error for a node. 00736 % 00737 % The format of the ClassifyImageColors() method is: 00738 % 00739 % MagickBooleanType ClassifyImageColors(CubeInfo *cube_info, 00740 % const Image *image,ExceptionInfo *exception) 00741 % 00742 % A description of each parameter follows. 00743 % 00744 % o cube_info: A pointer to the Cube structure. 00745 % 00746 % o image: the image. 00747 % 00748 */ 00749 00750 static inline void SetAssociatedAlpha(const Image *image,CubeInfo *cube_info) 00751 { 00752 MagickBooleanType 00753 associate_alpha; 00754 00755 associate_alpha=image->matte; 00756 if (cube_info->quantize_info->colorspace == TransparentColorspace) 00757 associate_alpha=MagickFalse; 00758 if ((cube_info->quantize_info->number_colors == 2) && 00759 (cube_info->quantize_info->colorspace == GRAYColorspace)) 00760 associate_alpha=MagickFalse; 00761 cube_info->associate_alpha=associate_alpha; 00762 } 00763 00764 static MagickBooleanType ClassifyImageColors(CubeInfo *cube_info, 00765 const Image *image,ExceptionInfo *exception) 00766 { 00767 #define ClassifyImageTag "Classify/Image" 00768 00769 CacheView 00770 *image_view; 00771 00772 MagickBooleanType 00773 proceed; 00774 00775 MagickRealType 00776 bisect; 00777 00778 NodeInfo 00779 *node_info; 00780 00781 RealPixelInfo 00782 error, 00783 mid, 00784 midpoint, 00785 pixel; 00786 00787 size_t 00788 count, 00789 id, 00790 index, 00791 level; 00792 00793 ssize_t 00794 y; 00795 00796 /* 00797 Classify the first cube_info->maximum_colors colors to a tree depth of 8. 00798 */ 00799 SetAssociatedAlpha(image,cube_info); 00800 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) && 00801 (cube_info->quantize_info->colorspace != CMYKColorspace)) 00802 (void) TransformImageColorspace((Image *) image, 00803 cube_info->quantize_info->colorspace,exception); 00804 else 00805 if ((image->colorspace != GRAYColorspace) && 00806 (image->colorspace != CMYColorspace) && 00807 (IsRGBColorspace(image->colorspace) == MagickFalse)) 00808 (void) TransformImageColorspace((Image *) image,RGBColorspace,exception); 00809 midpoint.red=(MagickRealType) QuantumRange/2.0; 00810 midpoint.green=(MagickRealType) QuantumRange/2.0; 00811 midpoint.blue=(MagickRealType) QuantumRange/2.0; 00812 midpoint.alpha=(MagickRealType) QuantumRange/2.0; 00813 error.alpha=0.0; 00814 image_view=AcquireCacheView(image); 00815 for (y=0; y < (ssize_t) image->rows; y++) 00816 { 00817 register const Quantum 00818 *restrict p; 00819 00820 register ssize_t 00821 x; 00822 00823 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); 00824 if (p == (const Quantum *) NULL) 00825 break; 00826 if (cube_info->nodes > MaxNodes) 00827 { 00828 /* 00829 Prune one level if the color tree is too large. 00830 */ 00831 PruneLevel(image,cube_info,cube_info->root); 00832 cube_info->depth--; 00833 } 00834 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) count) 00835 { 00836 /* 00837 Start at the root and descend the color cube tree. 00838 */ 00839 for (count=1; (x+(ssize_t) count) < (ssize_t) image->columns; count++) 00840 { 00841 PixelInfo 00842 packet; 00843 00844 GetPixelInfoPixel(image,p+count*GetPixelChannels(image),&packet); 00845 if (IsPixelEquivalent(image,p,&packet) == MagickFalse) 00846 break; 00847 } 00848 AssociateAlphaPixel(image,cube_info,p,&pixel); 00849 index=MaxTreeDepth-1; 00850 bisect=((MagickRealType) QuantumRange+1.0)/2.0; 00851 mid=midpoint; 00852 node_info=cube_info->root; 00853 for (level=1; level <= MaxTreeDepth; level++) 00854 { 00855 bisect*=0.5; 00856 id=ColorToNodeId(cube_info,&pixel,index); 00857 mid.red+=(id & 1) != 0 ? bisect : -bisect; 00858 mid.green+=(id & 2) != 0 ? bisect : -bisect; 00859 mid.blue+=(id & 4) != 0 ? bisect : -bisect; 00860 mid.alpha+=(id & 8) != 0 ? bisect : -bisect; 00861 if (node_info->child[id] == (NodeInfo *) NULL) 00862 { 00863 /* 00864 Set colors of new node to contain pixel. 00865 */ 00866 node_info->child[id]=GetNodeInfo(cube_info,id,level,node_info); 00867 if (node_info->child[id] == (NodeInfo *) NULL) 00868 (void) ThrowMagickException(exception,GetMagickModule(), 00869 ResourceLimitError,"MemoryAllocationFailed","`%s'", 00870 image->filename); 00871 if (level == MaxTreeDepth) 00872 cube_info->colors++; 00873 } 00874 /* 00875 Approximate the quantization error represented by this node. 00876 */ 00877 node_info=node_info->child[id]; 00878 error.red=QuantumScale*(pixel.red-mid.red); 00879 error.green=QuantumScale*(pixel.green-mid.green); 00880 error.blue=QuantumScale*(pixel.blue-mid.blue); 00881 if (cube_info->associate_alpha != MagickFalse) 00882 error.alpha=QuantumScale*(pixel.alpha-mid.alpha); 00883 node_info->quantize_error+=sqrt((double) (count*error.red*error.red+ 00884 count*error.green*error.green+count*error.blue*error.blue+ 00885 count*error.alpha*error.alpha)); 00886 cube_info->root->quantize_error+=node_info->quantize_error; 00887 index--; 00888 } 00889 /* 00890 Sum RGB for this leaf for later derivation of the mean cube color. 00891 */ 00892 node_info->number_unique+=count; 00893 node_info->total_color.red+=count*QuantumScale*pixel.red; 00894 node_info->total_color.green+=count*QuantumScale*pixel.green; 00895 node_info->total_color.blue+=count*QuantumScale*pixel.blue; 00896 if (cube_info->associate_alpha != MagickFalse) 00897 node_info->total_color.alpha+=count*QuantumScale*pixel.alpha; 00898 p+=count*GetPixelChannels(image); 00899 } 00900 if (cube_info->colors > cube_info->maximum_colors) 00901 { 00902 PruneToCubeDepth(image,cube_info,cube_info->root); 00903 break; 00904 } 00905 proceed=SetImageProgress(image,ClassifyImageTag,(MagickOffsetType) y, 00906 image->rows); 00907 if (proceed == MagickFalse) 00908 break; 00909 } 00910 for (y++; y < (ssize_t) image->rows; y++) 00911 { 00912 register const Quantum 00913 *restrict p; 00914 00915 register ssize_t 00916 x; 00917 00918 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); 00919 if (p == (const Quantum *) NULL) 00920 break; 00921 if (cube_info->nodes > MaxNodes) 00922 { 00923 /* 00924 Prune one level if the color tree is too large. 00925 */ 00926 PruneLevel(image,cube_info,cube_info->root); 00927 cube_info->depth--; 00928 } 00929 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) count) 00930 { 00931 /* 00932 Start at the root and descend the color cube tree. 00933 */ 00934 for (count=1; (x+(ssize_t) count) < (ssize_t) image->columns; count++) 00935 { 00936 PixelInfo 00937 packet; 00938 00939 GetPixelInfoPixel(image,p+count*GetPixelChannels(image),&packet); 00940 if (IsPixelEquivalent(image,p,&packet) == MagickFalse) 00941 break; 00942 } 00943 AssociateAlphaPixel(image,cube_info,p,&pixel); 00944 index=MaxTreeDepth-1; 00945 bisect=((MagickRealType) QuantumRange+1.0)/2.0; 00946 mid=midpoint; 00947 node_info=cube_info->root; 00948 for (level=1; level <= cube_info->depth; level++) 00949 { 00950 bisect*=0.5; 00951 id=ColorToNodeId(cube_info,&pixel,index); 00952 mid.red+=(id & 1) != 0 ? bisect : -bisect; 00953 mid.green+=(id & 2) != 0 ? bisect : -bisect; 00954 mid.blue+=(id & 4) != 0 ? bisect : -bisect; 00955 mid.alpha+=(id & 8) != 0 ? bisect : -bisect; 00956 if (node_info->child[id] == (NodeInfo *) NULL) 00957 { 00958 /* 00959 Set colors of new node to contain pixel. 00960 */ 00961 node_info->child[id]=GetNodeInfo(cube_info,id,level,node_info); 00962 if (node_info->child[id] == (NodeInfo *) NULL) 00963 (void) ThrowMagickException(exception,GetMagickModule(), 00964 ResourceLimitError,"MemoryAllocationFailed","%s", 00965 image->filename); 00966 if (level == cube_info->depth) 00967 cube_info->colors++; 00968 } 00969 /* 00970 Approximate the quantization error represented by this node. 00971 */ 00972 node_info=node_info->child[id]; 00973 error.red=QuantumScale*(pixel.red-mid.red); 00974 error.green=QuantumScale*(pixel.green-mid.green); 00975 error.blue=QuantumScale*(pixel.blue-mid.blue); 00976 if (cube_info->associate_alpha != MagickFalse) 00977 error.alpha=QuantumScale*(pixel.alpha-mid.alpha); 00978 node_info->quantize_error+=sqrt((double) (count*error.red*error.red+ 00979 count*error.green*error.green+count*error.blue*error.blue+ 00980 count*error.alpha*error.alpha)); 00981 cube_info->root->quantize_error+=node_info->quantize_error; 00982 index--; 00983 } 00984 /* 00985 Sum RGB for this leaf for later derivation of the mean cube color. 00986 */ 00987 node_info->number_unique+=count; 00988 node_info->total_color.red+=count*QuantumScale*pixel.red; 00989 node_info->total_color.green+=count*QuantumScale*pixel.green; 00990 node_info->total_color.blue+=count*QuantumScale*pixel.blue; 00991 if (cube_info->associate_alpha != MagickFalse) 00992 node_info->total_color.alpha+=count*QuantumScale*pixel.alpha; 00993 p+=count*GetPixelChannels(image); 00994 } 00995 proceed=SetImageProgress(image,ClassifyImageTag,(MagickOffsetType) y, 00996 image->rows); 00997 if (proceed == MagickFalse) 00998 break; 00999 } 01000 image_view=DestroyCacheView(image_view); 01001 if ((cube_info->quantize_info->colorspace != UndefinedColorspace) && 01002 (cube_info->quantize_info->colorspace != CMYKColorspace)) 01003 (void) TransformImageColorspace((Image *) image,RGBColorspace,exception); 01004 return(MagickTrue); 01005 } 01006 01007 /* 01008 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01009 % % 01010 % % 01011 % % 01012 % C l o n e Q u a n t i z e I n f o % 01013 % % 01014 % % 01015 % % 01016 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01017 % 01018 % CloneQuantizeInfo() makes a duplicate of the given quantize info structure, 01019 % or if quantize info is NULL, a new one. 01020 % 01021 % The format of the CloneQuantizeInfo method is: 01022 % 01023 % QuantizeInfo *CloneQuantizeInfo(const QuantizeInfo *quantize_info) 01024 % 01025 % A description of each parameter follows: 01026 % 01027 % o clone_info: Method CloneQuantizeInfo returns a duplicate of the given 01028 % quantize info, or if image info is NULL a new one. 01029 % 01030 % o quantize_info: a structure of type info. 01031 % 01032 */ 01033 MagickExport QuantizeInfo *CloneQuantizeInfo(const QuantizeInfo *quantize_info) 01034 { 01035 QuantizeInfo 01036 *clone_info; 01037 01038 clone_info=(QuantizeInfo *) AcquireMagickMemory(sizeof(*clone_info)); 01039 if (clone_info == (QuantizeInfo *) NULL) 01040 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); 01041 GetQuantizeInfo(clone_info); 01042 if (quantize_info == (QuantizeInfo *) NULL) 01043 return(clone_info); 01044 clone_info->number_colors=quantize_info->number_colors; 01045 clone_info->tree_depth=quantize_info->tree_depth; 01046 clone_info->dither=quantize_info->dither; 01047 clone_info->dither_method=quantize_info->dither_method; 01048 clone_info->colorspace=quantize_info->colorspace; 01049 clone_info->measure_error=quantize_info->measure_error; 01050 return(clone_info); 01051 } 01052 01053 /* 01054 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01055 % % 01056 % % 01057 % % 01058 + C l o s e s t C o l o r % 01059 % % 01060 % % 01061 % % 01062 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01063 % 01064 % ClosestColor() traverses the color cube tree at a particular node and 01065 % determines which colormap entry best represents the input color. 01066 % 01067 % The format of the ClosestColor method is: 01068 % 01069 % void ClosestColor(const Image *image,CubeInfo *cube_info, 01070 % const NodeInfo *node_info) 01071 % 01072 % A description of each parameter follows. 01073 % 01074 % o image: the image. 01075 % 01076 % o cube_info: A pointer to the Cube structure. 01077 % 01078 % o node_info: the address of a structure of type NodeInfo which points to a 01079 % node in the color cube tree that is to be pruned. 01080 % 01081 */ 01082 static void ClosestColor(const Image *image,CubeInfo *cube_info, 01083 const NodeInfo *node_info) 01084 { 01085 register ssize_t 01086 i; 01087 01088 size_t 01089 number_children; 01090 01091 /* 01092 Traverse any children. 01093 */ 01094 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL; 01095 for (i=0; i < (ssize_t) number_children; i++) 01096 if (node_info->child[i] != (NodeInfo *) NULL) 01097 ClosestColor(image,cube_info,node_info->child[i]); 01098 if (node_info->number_unique != 0) 01099 { 01100 MagickRealType 01101 pixel; 01102 01103 register MagickRealType 01104 alpha, 01105 beta, 01106 distance; 01107 01108 register PixelInfo 01109 *restrict p; 01110 01111 register RealPixelInfo 01112 *restrict q; 01113 01114 /* 01115 Determine if this color is "closest". 01116 */ 01117 p=image->colormap+node_info->color_number; 01118 q=(&cube_info->target); 01119 alpha=1.0; 01120 beta=1.0; 01121 if (cube_info->associate_alpha != MagickFalse) 01122 { 01123 alpha=(MagickRealType) (QuantumScale*p->alpha); 01124 beta=(MagickRealType) (QuantumScale*q->alpha); 01125 } 01126 pixel=alpha*p->red-beta*q->red; 01127 distance=pixel*pixel; 01128 if (distance <= cube_info->distance) 01129 { 01130 pixel=alpha*p->green-beta*q->green; 01131 distance+=pixel*pixel; 01132 if (distance <= cube_info->distance) 01133 { 01134 pixel=alpha*p->blue-beta*q->blue; 01135 distance+=pixel*pixel; 01136 if (distance <= cube_info->distance) 01137 { 01138 pixel=alpha-beta; 01139 distance+=pixel*pixel; 01140 if (distance <= cube_info->distance) 01141 { 01142 cube_info->distance=distance; 01143 cube_info->color_number=node_info->color_number; 01144 } 01145 } 01146 } 01147 } 01148 } 01149 } 01150 01151 /* 01152 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01153 % % 01154 % % 01155 % % 01156 % C o m p r e s s I m a g e C o l o r m a p % 01157 % % 01158 % % 01159 % % 01160 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01161 % 01162 % CompressImageColormap() compresses an image colormap by removing any 01163 % duplicate or unused color entries. 01164 % 01165 % The format of the CompressImageColormap method is: 01166 % 01167 % MagickBooleanType CompressImageColormap(Image *image, 01168 % ExceptionInfo *exception) 01169 % 01170 % A description of each parameter follows: 01171 % 01172 % o image: the image. 01173 % 01174 % o exception: return any errors or warnings in this structure. 01175 % 01176 */ 01177 MagickExport MagickBooleanType CompressImageColormap(Image *image, 01178 ExceptionInfo *exception) 01179 { 01180 QuantizeInfo 01181 quantize_info; 01182 01183 assert(image != (Image *) NULL); 01184 assert(image->signature == MagickSignature); 01185 if (image->debug != MagickFalse) 01186 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 01187 if (IsPaletteImage(image,exception) == MagickFalse) 01188 return(MagickFalse); 01189 GetQuantizeInfo(&quantize_info); 01190 quantize_info.number_colors=image->colors; 01191 quantize_info.tree_depth=MaxTreeDepth; 01192 return(QuantizeImage(&quantize_info,image,exception)); 01193 } 01194 01195 /* 01196 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01197 % % 01198 % % 01199 % % 01200 + D e f i n e I m a g e C o l o r m a p % 01201 % % 01202 % % 01203 % % 01204 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01205 % 01206 % DefineImageColormap() traverses the color cube tree and notes each colormap 01207 % entry. A colormap entry is any node in the color cube tree where the 01208 % of unique colors is not zero. DefineImageColormap() returns the number of 01209 % colors in the image colormap. 01210 % 01211 % The format of the DefineImageColormap method is: 01212 % 01213 % size_t DefineImageColormap(Image *image,CubeInfo *cube_info, 01214 % NodeInfo *node_info) 01215 % 01216 % A description of each parameter follows. 01217 % 01218 % o image: the image. 01219 % 01220 % o cube_info: A pointer to the Cube structure. 01221 % 01222 % o node_info: the address of a structure of type NodeInfo which points to a 01223 % node in the color cube tree that is to be pruned. 01224 % 01225 */ 01226 static size_t DefineImageColormap(Image *image,CubeInfo *cube_info, 01227 NodeInfo *node_info) 01228 { 01229 register ssize_t 01230 i; 01231 01232 size_t 01233 number_children; 01234 01235 /* 01236 Traverse any children. 01237 */ 01238 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL; 01239 for (i=0; i < (ssize_t) number_children; i++) 01240 if (node_info->child[i] != (NodeInfo *) NULL) 01241 (void) DefineImageColormap(image,cube_info,node_info->child[i]); 01242 if (node_info->number_unique != 0) 01243 { 01244 register MagickRealType 01245 alpha; 01246 01247 register PixelInfo 01248 *restrict q; 01249 01250 /* 01251 Colormap entry is defined by the mean color in this cube. 01252 */ 01253 q=image->colormap+image->colors; 01254 alpha=(MagickRealType) ((MagickOffsetType) node_info->number_unique); 01255 alpha=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha); 01256 if (cube_info->associate_alpha == MagickFalse) 01257 { 01258 q->red=(double) ClampToQuantum((MagickRealType) 01259 (alpha*QuantumRange*node_info->total_color.red)); 01260 q->green=(double) ClampToQuantum((MagickRealType) 01261 (alpha*QuantumRange*node_info->total_color.green)); 01262 q->blue=(double) ClampToQuantum((MagickRealType) 01263 (alpha*(double) QuantumRange*node_info->total_color.blue)); 01264 q->alpha=OpaqueAlpha; 01265 } 01266 else 01267 { 01268 MagickRealType 01269 opacity; 01270 01271 opacity=(MagickRealType) (alpha*QuantumRange* 01272 node_info->total_color.alpha); 01273 q->alpha=(double) ClampToQuantum(opacity); 01274 if (q->alpha == OpaqueAlpha) 01275 { 01276 q->red=(double) ClampToQuantum((MagickRealType) 01277 (alpha*QuantumRange*node_info->total_color.red)); 01278 q->green=(double) ClampToQuantum((MagickRealType) 01279 (alpha*QuantumRange*node_info->total_color.green)); 01280 q->blue=(double) ClampToQuantum((MagickRealType) 01281 (alpha*QuantumRange*node_info->total_color.blue)); 01282 } 01283 else 01284 { 01285 MagickRealType 01286 gamma; 01287 01288 gamma=(MagickRealType) (QuantumScale*q->alpha); 01289 gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma); 01290 q->red=(double) ClampToQuantum((MagickRealType) 01291 (alpha*gamma*QuantumRange*node_info->total_color.red)); 01292 q->green=(double) ClampToQuantum((MagickRealType) 01293 (alpha*gamma*QuantumRange*node_info->total_color.green)); 01294 q->blue=(double) ClampToQuantum((MagickRealType) 01295 (alpha*gamma*QuantumRange*node_info->total_color.blue)); 01296 if (node_info->number_unique > cube_info->transparent_pixels) 01297 { 01298 cube_info->transparent_pixels=node_info->number_unique; 01299 cube_info->transparent_index=(ssize_t) image->colors; 01300 } 01301 } 01302 } 01303 node_info->color_number=image->colors++; 01304 } 01305 return(image->colors); 01306 } 01307 01308 /* 01309 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01310 % % 01311 % % 01312 % % 01313 + D e s t r o y C u b e I n f o % 01314 % % 01315 % % 01316 % % 01317 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01318 % 01319 % DestroyCubeInfo() deallocates memory associated with an image. 01320 % 01321 % The format of the DestroyCubeInfo method is: 01322 % 01323 % DestroyCubeInfo(CubeInfo *cube_info) 01324 % 01325 % A description of each parameter follows: 01326 % 01327 % o cube_info: the address of a structure of type CubeInfo. 01328 % 01329 */ 01330 static void DestroyCubeInfo(CubeInfo *cube_info) 01331 { 01332 register Nodes 01333 *nodes; 01334 01335 /* 01336 Release color cube tree storage. 01337 */ 01338 do 01339 { 01340 nodes=cube_info->node_queue->next; 01341 cube_info->node_queue->nodes=(NodeInfo *) RelinquishMagickMemory( 01342 cube_info->node_queue->nodes); 01343 cube_info->node_queue=(Nodes *) RelinquishMagickMemory( 01344 cube_info->node_queue); 01345 cube_info->node_queue=nodes; 01346 } while (cube_info->node_queue != (Nodes *) NULL); 01347 if (cube_info->cache != (ssize_t *) NULL) 01348 cube_info->cache=(ssize_t *) RelinquishMagickMemory(cube_info->cache); 01349 cube_info->quantize_info=DestroyQuantizeInfo(cube_info->quantize_info); 01350 cube_info=(CubeInfo *) RelinquishMagickMemory(cube_info); 01351 } 01352 01353 /* 01354 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01355 % % 01356 % % 01357 % % 01358 % D e s t r o y Q u a n t i z e I n f o % 01359 % % 01360 % % 01361 % % 01362 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01363 % 01364 % DestroyQuantizeInfo() deallocates memory associated with an QuantizeInfo 01365 % structure. 01366 % 01367 % The format of the DestroyQuantizeInfo method is: 01368 % 01369 % QuantizeInfo *DestroyQuantizeInfo(QuantizeInfo *quantize_info) 01370 % 01371 % A description of each parameter follows: 01372 % 01373 % o quantize_info: Specifies a pointer to an QuantizeInfo structure. 01374 % 01375 */ 01376 MagickExport QuantizeInfo *DestroyQuantizeInfo(QuantizeInfo *quantize_info) 01377 { 01378 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); 01379 assert(quantize_info != (QuantizeInfo *) NULL); 01380 assert(quantize_info->signature == MagickSignature); 01381 quantize_info->signature=(~MagickSignature); 01382 quantize_info=(QuantizeInfo *) RelinquishMagickMemory(quantize_info); 01383 return(quantize_info); 01384 } 01385 01386 /* 01387 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01388 % % 01389 % % 01390 % % 01391 + D i t h e r I m a g e % 01392 % % 01393 % % 01394 % % 01395 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01396 % 01397 % DitherImage() distributes the difference between an original image and 01398 % the corresponding color reduced algorithm to neighboring pixels using 01399 % serpentine-scan Floyd-Steinberg error diffusion. DitherImage returns 01400 % MagickTrue if the image is dithered otherwise MagickFalse. 01401 % 01402 % The format of the DitherImage method is: 01403 % 01404 % MagickBooleanType DitherImage(Image *image,CubeInfo *cube_info, 01405 % ExceptionInfo *exception) 01406 % 01407 % A description of each parameter follows. 01408 % 01409 % o image: the image. 01410 % 01411 % o cube_info: A pointer to the Cube structure. 01412 % 01413 % o exception: return any errors or warnings in this structure. 01414 % 01415 */ 01416 01417 static RealPixelInfo **DestroyPixelThreadSet(RealPixelInfo **pixels) 01418 { 01419 register ssize_t 01420 i; 01421 01422 assert(pixels != (RealPixelInfo **) NULL); 01423 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++) 01424 if (pixels[i] != (RealPixelInfo *) NULL) 01425 pixels[i]=(RealPixelInfo *) RelinquishMagickMemory(pixels[i]); 01426 pixels=(RealPixelInfo **) RelinquishMagickMemory(pixels); 01427 return(pixels); 01428 } 01429 01430 static RealPixelInfo **AcquirePixelThreadSet(const size_t count) 01431 { 01432 RealPixelInfo 01433 **pixels; 01434 01435 register ssize_t 01436 i; 01437 01438 size_t 01439 number_threads; 01440 01441 number_threads=GetOpenMPMaximumThreads(); 01442 pixels=(RealPixelInfo **) AcquireQuantumMemory(number_threads, 01443 sizeof(*pixels)); 01444 if (pixels == (RealPixelInfo **) NULL) 01445 return((RealPixelInfo **) NULL); 01446 (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels)); 01447 for (i=0; i < (ssize_t) number_threads; i++) 01448 { 01449 pixels[i]=(RealPixelInfo *) AcquireQuantumMemory(count, 01450 2*sizeof(**pixels)); 01451 if (pixels[i] == (RealPixelInfo *) NULL) 01452 return(DestroyPixelThreadSet(pixels)); 01453 } 01454 return(pixels); 01455 } 01456 01457 static inline ssize_t CacheOffset(CubeInfo *cube_info, 01458 const RealPixelInfo *pixel) 01459 { 01460 #define RedShift(pixel) (((pixel) >> CacheShift) << (0*(8-CacheShift))) 01461 #define GreenShift(pixel) (((pixel) >> CacheShift) << (1*(8-CacheShift))) 01462 #define BlueShift(pixel) (((pixel) >> CacheShift) << (2*(8-CacheShift))) 01463 #define AlphaShift(pixel) (((pixel) >> CacheShift) << (3*(8-CacheShift))) 01464 01465 ssize_t 01466 offset; 01467 01468 offset=(ssize_t) 01469 (RedShift(ScaleQuantumToChar(ClampToUnsignedQuantum(pixel->red))) | 01470 GreenShift(ScaleQuantumToChar(ClampToUnsignedQuantum(pixel->green))) | 01471 BlueShift(ScaleQuantumToChar(ClampToUnsignedQuantum(pixel->blue)))); 01472 if (cube_info->associate_alpha != MagickFalse) 01473 offset|=AlphaShift(ScaleQuantumToChar(ClampToUnsignedQuantum( 01474 pixel->alpha))); 01475 return(offset); 01476 } 01477 01478 static MagickBooleanType FloydSteinbergDither(Image *image,CubeInfo *cube_info, 01479 ExceptionInfo *exception) 01480 { 01481 #define DitherImageTag "Dither/Image" 01482 01483 CacheView 01484 *image_view; 01485 01486 MagickBooleanType 01487 status; 01488 01489 RealPixelInfo 01490 **pixels; 01491 01492 ssize_t 01493 y; 01494 01495 /* 01496 Distribute quantization error using Floyd-Steinberg. 01497 */ 01498 pixels=AcquirePixelThreadSet(image->columns); 01499 if (pixels == (RealPixelInfo **) NULL) 01500 return(MagickFalse); 01501 status=MagickTrue; 01502 image_view=AcquireCacheView(image); 01503 for (y=0; y < (ssize_t) image->rows; y++) 01504 { 01505 const int 01506 id = GetOpenMPThreadId(); 01507 01508 CubeInfo 01509 cube; 01510 01511 RealPixelInfo 01512 *current, 01513 *previous; 01514 01515 register Quantum 01516 *restrict q; 01517 01518 register ssize_t 01519 x; 01520 01521 size_t 01522 index; 01523 01524 ssize_t 01525 v; 01526 01527 if (status == MagickFalse) 01528 continue; 01529 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 01530 if (q == (Quantum *) NULL) 01531 { 01532 status=MagickFalse; 01533 continue; 01534 } 01535 q+=(y & 0x01)*image->columns*GetPixelChannels(image); 01536 cube=(*cube_info); 01537 current=pixels[id]+(y & 0x01)*image->columns; 01538 previous=pixels[id]+((y+1) & 0x01)*image->columns; 01539 v=(ssize_t) ((y & 0x01) != 0 ? -1 : 1); 01540 for (x=0; x < (ssize_t) image->columns; x++) 01541 { 01542 RealPixelInfo 01543 color, 01544 pixel; 01545 01546 register ssize_t 01547 i; 01548 01549 ssize_t 01550 u; 01551 01552 q-=(y & 0x01)*GetPixelChannels(image); 01553 u=(y & 0x01) != 0 ? (ssize_t) image->columns-1-x : x; 01554 AssociateAlphaPixel(image,&cube,q,&pixel); 01555 if (x > 0) 01556 { 01557 pixel.red+=7*current[u-v].red/16; 01558 pixel.green+=7*current[u-v].green/16; 01559 pixel.blue+=7*current[u-v].blue/16; 01560 if (cube.associate_alpha != MagickFalse) 01561 pixel.alpha+=7*current[u-v].alpha/16; 01562 } 01563 if (y > 0) 01564 { 01565 if (x < (ssize_t) (image->columns-1)) 01566 { 01567 pixel.red+=previous[u+v].red/16; 01568 pixel.green+=previous[u+v].green/16; 01569 pixel.blue+=previous[u+v].blue/16; 01570 if (cube.associate_alpha != MagickFalse) 01571 pixel.alpha+=previous[u+v].alpha/16; 01572 } 01573 pixel.red+=5*previous[u].red/16; 01574 pixel.green+=5*previous[u].green/16; 01575 pixel.blue+=5*previous[u].blue/16; 01576 if (cube.associate_alpha != MagickFalse) 01577 pixel.alpha+=5*previous[u].alpha/16; 01578 if (x > 0) 01579 { 01580 pixel.red+=3*previous[u-v].red/16; 01581 pixel.green+=3*previous[u-v].green/16; 01582 pixel.blue+=3*previous[u-v].blue/16; 01583 if (cube.associate_alpha != MagickFalse) 01584 pixel.alpha+=3*previous[u-v].alpha/16; 01585 } 01586 } 01587 pixel.red=(MagickRealType) ClampToUnsignedQuantum(pixel.red); 01588 pixel.green=(MagickRealType) ClampToUnsignedQuantum(pixel.green); 01589 pixel.blue=(MagickRealType) ClampToUnsignedQuantum(pixel.blue); 01590 if (cube.associate_alpha != MagickFalse) 01591 pixel.alpha=(MagickRealType) ClampToUnsignedQuantum(pixel.alpha); 01592 i=CacheOffset(&cube,&pixel); 01593 if (cube.cache[i] < 0) 01594 { 01595 register NodeInfo 01596 *node_info; 01597 01598 register size_t 01599 id; 01600 01601 /* 01602 Identify the deepest node containing the pixel's color. 01603 */ 01604 node_info=cube.root; 01605 for (index=MaxTreeDepth-1; (ssize_t) index > 0; index--) 01606 { 01607 id=ColorToNodeId(&cube,&pixel,index); 01608 if (node_info->child[id] == (NodeInfo *) NULL) 01609 break; 01610 node_info=node_info->child[id]; 01611 } 01612 /* 01613 Find closest color among siblings and their children. 01614 */ 01615 cube.target=pixel; 01616 cube.distance=(MagickRealType) (4.0*(QuantumRange+1.0)*(QuantumRange+ 01617 1.0)+1.0); 01618 ClosestColor(image,&cube,node_info->parent); 01619 cube.cache[i]=(ssize_t) cube.color_number; 01620 } 01621 /* 01622 Assign pixel to closest colormap entry. 01623 */ 01624 index=(size_t) cube.cache[i]; 01625 if (image->storage_class == PseudoClass) 01626 SetPixelIndex(image,(Quantum) index,q); 01627 if (cube.quantize_info->measure_error == MagickFalse) 01628 { 01629 SetPixelRed(image,ClampToQuantum(image->colormap[index].red),q); 01630 SetPixelGreen(image,ClampToQuantum(image->colormap[index].green),q); 01631 SetPixelBlue(image,ClampToQuantum(image->colormap[index].blue),q); 01632 if (cube.associate_alpha != MagickFalse) 01633 SetPixelAlpha(image,ClampToQuantum(image->colormap[index].alpha),q); 01634 } 01635 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 01636 status=MagickFalse; 01637 /* 01638 Store the error. 01639 */ 01640 AssociateAlphaPixelInfo(image,&cube,image->colormap+index,&color); 01641 current[u].red=pixel.red-color.red; 01642 current[u].green=pixel.green-color.green; 01643 current[u].blue=pixel.blue-color.blue; 01644 if (cube.associate_alpha != MagickFalse) 01645 current[u].alpha=pixel.alpha-color.alpha; 01646 if (image->progress_monitor != (MagickProgressMonitor) NULL) 01647 { 01648 MagickBooleanType 01649 proceed; 01650 01651 #if defined(MAGICKCORE_OPENMP_SUPPORT) 01652 #pragma omp critical (MagickCore_FloydSteinbergDither) 01653 #endif 01654 proceed=SetImageProgress(image,DitherImageTag,(MagickOffsetType) y, 01655 image->rows); 01656 if (proceed == MagickFalse) 01657 status=MagickFalse; 01658 } 01659 q+=((y+1) & 0x01)*GetPixelChannels(image); 01660 } 01661 } 01662 image_view=DestroyCacheView(image_view); 01663 pixels=DestroyPixelThreadSet(pixels); 01664 return(MagickTrue); 01665 } 01666 01667 static MagickBooleanType 01668 RiemersmaDither(Image *,CacheView *,CubeInfo *,const unsigned int, 01669 ExceptionInfo *exception); 01670 01671 static void Riemersma(Image *image,CacheView *image_view,CubeInfo *cube_info, 01672 const size_t level,const unsigned int direction,ExceptionInfo *exception) 01673 { 01674 if (level == 1) 01675 switch (direction) 01676 { 01677 case WestGravity: 01678 { 01679 (void) RiemersmaDither(image,image_view,cube_info,EastGravity, 01680 exception); 01681 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity, 01682 exception); 01683 (void) RiemersmaDither(image,image_view,cube_info,WestGravity, 01684 exception); 01685 break; 01686 } 01687 case EastGravity: 01688 { 01689 (void) RiemersmaDither(image,image_view,cube_info,WestGravity, 01690 exception); 01691 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity, 01692 exception); 01693 (void) RiemersmaDither(image,image_view,cube_info,EastGravity, 01694 exception); 01695 break; 01696 } 01697 case NorthGravity: 01698 { 01699 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity, 01700 exception); 01701 (void) RiemersmaDither(image,image_view,cube_info,EastGravity, 01702 exception); 01703 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity, 01704 exception); 01705 break; 01706 } 01707 case SouthGravity: 01708 { 01709 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity, 01710 exception); 01711 (void) RiemersmaDither(image,image_view,cube_info,WestGravity, 01712 exception); 01713 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity, 01714 exception); 01715 break; 01716 } 01717 default: 01718 break; 01719 } 01720 else 01721 switch (direction) 01722 { 01723 case WestGravity: 01724 { 01725 Riemersma(image,image_view,cube_info,level-1,NorthGravity, 01726 exception); 01727 (void) RiemersmaDither(image,image_view,cube_info,EastGravity, 01728 exception); 01729 Riemersma(image,image_view,cube_info,level-1,WestGravity, 01730 exception); 01731 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity, 01732 exception); 01733 Riemersma(image,image_view,cube_info,level-1,WestGravity, 01734 exception); 01735 (void) RiemersmaDither(image,image_view,cube_info,WestGravity, 01736 exception); 01737 Riemersma(image,image_view,cube_info,level-1,SouthGravity, 01738 exception); 01739 break; 01740 } 01741 case EastGravity: 01742 { 01743 Riemersma(image,image_view,cube_info,level-1,SouthGravity, 01744 exception); 01745 (void) RiemersmaDither(image,image_view,cube_info,WestGravity, 01746 exception); 01747 Riemersma(image,image_view,cube_info,level-1,EastGravity, 01748 exception); 01749 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity, 01750 exception); 01751 Riemersma(image,image_view,cube_info,level-1,EastGravity, 01752 exception); 01753 (void) RiemersmaDither(image,image_view,cube_info,EastGravity, 01754 exception); 01755 Riemersma(image,image_view,cube_info,level-1,NorthGravity, 01756 exception); 01757 break; 01758 } 01759 case NorthGravity: 01760 { 01761 Riemersma(image,image_view,cube_info,level-1,WestGravity, 01762 exception); 01763 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity, 01764 exception); 01765 Riemersma(image,image_view,cube_info,level-1,NorthGravity, 01766 exception); 01767 (void) RiemersmaDither(image,image_view,cube_info,EastGravity, 01768 exception); 01769 Riemersma(image,image_view,cube_info,level-1,NorthGravity, 01770 exception); 01771 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity, 01772 exception); 01773 Riemersma(image,image_view,cube_info,level-1,EastGravity, 01774 exception); 01775 break; 01776 } 01777 case SouthGravity: 01778 { 01779 Riemersma(image,image_view,cube_info,level-1,EastGravity, 01780 exception); 01781 (void) RiemersmaDither(image,image_view,cube_info,NorthGravity, 01782 exception); 01783 Riemersma(image,image_view,cube_info,level-1,SouthGravity, 01784 exception); 01785 (void) RiemersmaDither(image,image_view,cube_info,WestGravity, 01786 exception); 01787 Riemersma(image,image_view,cube_info,level-1,SouthGravity, 01788 exception); 01789 (void) RiemersmaDither(image,image_view,cube_info,SouthGravity, 01790 exception); 01791 Riemersma(image,image_view,cube_info,level-1,WestGravity, 01792 exception); 01793 break; 01794 } 01795 default: 01796 break; 01797 } 01798 } 01799 01800 static MagickBooleanType RiemersmaDither(Image *image,CacheView *image_view, 01801 CubeInfo *cube_info,const unsigned int direction,ExceptionInfo *exception) 01802 { 01803 #define DitherImageTag "Dither/Image" 01804 01805 MagickBooleanType 01806 proceed; 01807 01808 RealPixelInfo 01809 color, 01810 pixel; 01811 01812 register CubeInfo 01813 *p; 01814 01815 size_t 01816 index; 01817 01818 p=cube_info; 01819 if ((p->x >= 0) && (p->x < (ssize_t) image->columns) && 01820 (p->y >= 0) && (p->y < (ssize_t) image->rows)) 01821 { 01822 register Quantum 01823 *restrict q; 01824 01825 register ssize_t 01826 i; 01827 01828 /* 01829 Distribute error. 01830 */ 01831 q=GetCacheViewAuthenticPixels(image_view,p->x,p->y,1,1,exception); 01832 if (q == (Quantum *) NULL) 01833 return(MagickFalse); 01834 AssociateAlphaPixel(image,cube_info,q,&pixel); 01835 for (i=0; i < ErrorQueueLength; i++) 01836 { 01837 pixel.red+=p->weights[i]*p->error[i].red; 01838 pixel.green+=p->weights[i]*p->error[i].green; 01839 pixel.blue+=p->weights[i]*p->error[i].blue; 01840 if (cube_info->associate_alpha != MagickFalse) 01841 pixel.alpha+=p->weights[i]*p->error[i].alpha; 01842 } 01843 pixel.red=(MagickRealType) ClampToUnsignedQuantum(pixel.red); 01844 pixel.green=(MagickRealType) ClampToUnsignedQuantum(pixel.green); 01845 pixel.blue=(MagickRealType) ClampToUnsignedQuantum(pixel.blue); 01846 if (cube_info->associate_alpha != MagickFalse) 01847 pixel.alpha=(MagickRealType) ClampToUnsignedQuantum(pixel.alpha); 01848 i=CacheOffset(cube_info,&pixel); 01849 if (p->cache[i] < 0) 01850 { 01851 register NodeInfo 01852 *node_info; 01853 01854 register size_t 01855 id; 01856 01857 /* 01858 Identify the deepest node containing the pixel's color. 01859 */ 01860 node_info=p->root; 01861 for (index=MaxTreeDepth-1; (ssize_t) index > 0; index--) 01862 { 01863 id=ColorToNodeId(cube_info,&pixel,index); 01864 if (node_info->child[id] == (NodeInfo *) NULL) 01865 break; 01866 node_info=node_info->child[id]; 01867 } 01868 node_info=node_info->parent; 01869 /* 01870 Find closest color among siblings and their children. 01871 */ 01872 p->target=pixel; 01873 p->distance=(MagickRealType) (4.0*(QuantumRange+1.0)*((MagickRealType) 01874 QuantumRange+1.0)+1.0); 01875 ClosestColor(image,p,node_info->parent); 01876 p->cache[i]=(ssize_t) p->color_number; 01877 } 01878 /* 01879 Assign pixel to closest colormap entry. 01880 */ 01881 index=(size_t) p->cache[i]; 01882 if (image->storage_class == PseudoClass) 01883 SetPixelIndex(image,(Quantum) index,q); 01884 if (cube_info->quantize_info->measure_error == MagickFalse) 01885 { 01886 SetPixelRed(image,ClampToQuantum(image->colormap[index].red),q); 01887 SetPixelGreen(image,ClampToQuantum(image->colormap[index].green),q); 01888 SetPixelBlue(image,ClampToQuantum(image->colormap[index].blue),q); 01889 if (cube_info->associate_alpha != MagickFalse) 01890 SetPixelAlpha(image,ClampToQuantum(image->colormap[index].alpha),q); 01891 } 01892 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 01893 return(MagickFalse); 01894 /* 01895 Propagate the error as the last entry of the error queue. 01896 */ 01897 (void) CopyMagickMemory(p->error,p->error+1,(ErrorQueueLength-1)* 01898 sizeof(p->error[0])); 01899 AssociateAlphaPixelInfo(image,cube_info,image->colormap+index,&color); 01900 p->error[ErrorQueueLength-1].red=pixel.red-color.red; 01901 p->error[ErrorQueueLength-1].green=pixel.green-color.green; 01902 p->error[ErrorQueueLength-1].blue=pixel.blue-color.blue; 01903 if (cube_info->associate_alpha != MagickFalse) 01904 p->error[ErrorQueueLength-1].alpha=pixel.alpha-color.alpha; 01905 proceed=SetImageProgress(image,DitherImageTag,p->offset,p->span); 01906 if (proceed == MagickFalse) 01907 return(MagickFalse); 01908 p->offset++; 01909 } 01910 switch (direction) 01911 { 01912 case WestGravity: p->x--; break; 01913 case EastGravity: p->x++; break; 01914 case NorthGravity: p->y--; break; 01915 case SouthGravity: p->y++; break; 01916 } 01917 return(MagickTrue); 01918 } 01919 01920 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y) 01921 { 01922 if (x > y) 01923 return(x); 01924 return(y); 01925 } 01926 01927 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y) 01928 { 01929 if (x < y) 01930 return(x); 01931 return(y); 01932 } 01933 01934 static MagickBooleanType DitherImage(Image *image,CubeInfo *cube_info, 01935 ExceptionInfo *exception) 01936 { 01937 CacheView 01938 *image_view; 01939 01940 MagickBooleanType 01941 status; 01942 01943 register ssize_t 01944 i; 01945 01946 size_t 01947 depth; 01948 01949 if (cube_info->quantize_info->dither_method != RiemersmaDitherMethod) 01950 return(FloydSteinbergDither(image,cube_info,exception)); 01951 /* 01952 Distribute quantization error along a Hilbert curve. 01953 */ 01954 (void) ResetMagickMemory(cube_info->error,0,ErrorQueueLength* 01955 sizeof(*cube_info->error)); 01956 cube_info->x=0; 01957 cube_info->y=0; 01958 i=MagickMax((ssize_t) image->columns,(ssize_t) image->rows); 01959 for (depth=1; i != 0; depth++) 01960 i>>=1; 01961 if ((ssize_t) (1L << depth) < MagickMax((ssize_t) image->columns,(ssize_t) image->rows)) 01962 depth++; 01963 cube_info->offset=0; 01964 cube_info->span=(MagickSizeType) image->columns*image->rows; 01965 image_view=AcquireCacheView(image); 01966 if (depth > 1) 01967 Riemersma(image,image_view,cube_info,depth-1,NorthGravity,exception); 01968 status=RiemersmaDither(image,image_view,cube_info,ForgetGravity,exception); 01969 image_view=DestroyCacheView(image_view); 01970 return(status); 01971 } 01972 01973 /* 01974 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01975 % % 01976 % % 01977 % % 01978 + G e t C u b e I n f o % 01979 % % 01980 % % 01981 % % 01982 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01983 % 01984 % GetCubeInfo() initialize the Cube data structure. 01985 % 01986 % The format of the GetCubeInfo method is: 01987 % 01988 % CubeInfo GetCubeInfo(const QuantizeInfo *quantize_info, 01989 % const size_t depth,const size_t maximum_colors) 01990 % 01991 % A description of each parameter follows. 01992 % 01993 % o quantize_info: Specifies a pointer to an QuantizeInfo structure. 01994 % 01995 % o depth: Normally, this integer value is zero or one. A zero or 01996 % one tells Quantize to choose a optimal tree depth of Log4(number_colors). 01997 % A tree of this depth generally allows the best representation of the 01998 % reference image with the least amount of memory and the fastest 01999 % computational speed. In some cases, such as an image with low color 02000 % dispersion (a few number of colors), a value other than 02001 % Log4(number_colors) is required. To expand the color tree completely, 02002 % use a value of 8. 02003 % 02004 % o maximum_colors: maximum colors. 02005 % 02006 */ 02007 static CubeInfo *GetCubeInfo(const QuantizeInfo *quantize_info, 02008 const size_t depth,const size_t maximum_colors) 02009 { 02010 CubeInfo 02011 *cube_info; 02012 02013 MagickRealType 02014 sum, 02015 weight; 02016 02017 register ssize_t 02018 i; 02019 02020 size_t 02021 length; 02022 02023 /* 02024 Initialize tree to describe color cube_info. 02025 */ 02026 cube_info=(CubeInfo *) AcquireMagickMemory(sizeof(*cube_info)); 02027 if (cube_info == (CubeInfo *) NULL) 02028 return((CubeInfo *) NULL); 02029 (void) ResetMagickMemory(cube_info,0,sizeof(*cube_info)); 02030 cube_info->depth=depth; 02031 if (cube_info->depth > MaxTreeDepth) 02032 cube_info->depth=MaxTreeDepth; 02033 if (cube_info->depth < 2) 02034 cube_info->depth=2; 02035 cube_info->maximum_colors=maximum_colors; 02036 /* 02037 Initialize root node. 02038 */ 02039 cube_info->root=GetNodeInfo(cube_info,0,0,(NodeInfo *) NULL); 02040 if (cube_info->root == (NodeInfo *) NULL) 02041 return((CubeInfo *) NULL); 02042 cube_info->root->parent=cube_info->root; 02043 cube_info->quantize_info=CloneQuantizeInfo(quantize_info); 02044 if (cube_info->quantize_info->dither == MagickFalse) 02045 return(cube_info); 02046 /* 02047 Initialize dither resources. 02048 */ 02049 length=(size_t) (1UL << (4*(8-CacheShift))); 02050 cube_info->cache=(ssize_t *) AcquireQuantumMemory(length, 02051 sizeof(*cube_info->cache)); 02052 if (cube_info->cache == (ssize_t *) NULL) 02053 return((CubeInfo *) NULL); 02054 /* 02055 Initialize color cache. 02056 */ 02057 for (i=0; i < (ssize_t) length; i++) 02058 cube_info->cache[i]=(-1); 02059 /* 02060 Distribute weights along a curve of exponential decay. 02061 */ 02062 weight=1.0; 02063 for (i=0; i < ErrorQueueLength; i++) 02064 { 02065 cube_info->weights[ErrorQueueLength-i-1]=1.0/weight; 02066 weight*=exp(log(((double) QuantumRange+1.0))/(ErrorQueueLength-1.0)); 02067 } 02068 /* 02069 Normalize the weighting factors. 02070 */ 02071 weight=0.0; 02072 for (i=0; i < ErrorQueueLength; i++) 02073 weight+=cube_info->weights[i]; 02074 sum=0.0; 02075 for (i=0; i < ErrorQueueLength; i++) 02076 { 02077 cube_info->weights[i]/=weight; 02078 sum+=cube_info->weights[i]; 02079 } 02080 cube_info->weights[0]+=1.0-sum; 02081 return(cube_info); 02082 } 02083 02084 /* 02085 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02086 % % 02087 % % 02088 % % 02089 + G e t N o d e I n f o % 02090 % % 02091 % % 02092 % % 02093 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02094 % 02095 % GetNodeInfo() allocates memory for a new node in the color cube tree and 02096 % presets all fields to zero. 02097 % 02098 % The format of the GetNodeInfo method is: 02099 % 02100 % NodeInfo *GetNodeInfo(CubeInfo *cube_info,const size_t id, 02101 % const size_t level,NodeInfo *parent) 02102 % 02103 % A description of each parameter follows. 02104 % 02105 % o node: The GetNodeInfo method returns a pointer to a queue of nodes. 02106 % 02107 % o id: Specifies the child number of the node. 02108 % 02109 % o level: Specifies the level in the storage_class the node resides. 02110 % 02111 */ 02112 static NodeInfo *GetNodeInfo(CubeInfo *cube_info,const size_t id, 02113 const size_t level,NodeInfo *parent) 02114 { 02115 NodeInfo 02116 *node_info; 02117 02118 if (cube_info->free_nodes == 0) 02119 { 02120 Nodes 02121 *nodes; 02122 02123 /* 02124 Allocate a new queue of nodes. 02125 */ 02126 nodes=(Nodes *) AcquireMagickMemory(sizeof(*nodes)); 02127 if (nodes == (Nodes *) NULL) 02128 return((NodeInfo *) NULL); 02129 nodes->nodes=(NodeInfo *) AcquireQuantumMemory(NodesInAList, 02130 sizeof(*nodes->nodes)); 02131 if (nodes->nodes == (NodeInfo *) NULL) 02132 return((NodeInfo *) NULL); 02133 nodes->next=cube_info->node_queue; 02134 cube_info->node_queue=nodes; 02135 cube_info->next_node=nodes->nodes; 02136 cube_info->free_nodes=NodesInAList; 02137 } 02138 cube_info->nodes++; 02139 cube_info->free_nodes--; 02140 node_info=cube_info->next_node++; 02141 (void) ResetMagickMemory(node_info,0,sizeof(*node_info)); 02142 node_info->parent=parent; 02143 node_info->id=id; 02144 node_info->level=level; 02145 return(node_info); 02146 } 02147 02148 /* 02149 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02150 % % 02151 % % 02152 % % 02153 % G e t I m a g e Q u a n t i z e E r r o r % 02154 % % 02155 % % 02156 % % 02157 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02158 % 02159 % GetImageQuantizeError() measures the difference between the original 02160 % and quantized images. This difference is the total quantization error. 02161 % The error is computed by summing over all pixels in an image the distance 02162 % squared in RGB space between each reference pixel value and its quantized 02163 % value. These values are computed: 02164 % 02165 % o mean_error_per_pixel: This value is the mean error for any single 02166 % pixel in the image. 02167 % 02168 % o normalized_mean_square_error: This value is the normalized mean 02169 % quantization error for any single pixel in the image. This distance 02170 % measure is normalized to a range between 0 and 1. It is independent 02171 % of the range of red, green, and blue values in the image. 02172 % 02173 % o normalized_maximum_square_error: Thsi value is the normalized 02174 % maximum quantization error for any single pixel in the image. This 02175 % distance measure is normalized to a range between 0 and 1. It is 02176 % independent of the range of red, green, and blue values in your image. 02177 % 02178 % The format of the GetImageQuantizeError method is: 02179 % 02180 % MagickBooleanType GetImageQuantizeError(Image *image, 02181 % ExceptionInfo *exception) 02182 % 02183 % A description of each parameter follows. 02184 % 02185 % o image: the image. 02186 % 02187 % o exception: return any errors or warnings in this structure. 02188 % 02189 */ 02190 MagickExport MagickBooleanType GetImageQuantizeError(Image *image, 02191 ExceptionInfo *exception) 02192 { 02193 CacheView 02194 *image_view; 02195 02196 MagickRealType 02197 alpha, 02198 area, 02199 beta, 02200 distance, 02201 maximum_error, 02202 mean_error, 02203 mean_error_per_pixel; 02204 02205 size_t 02206 index; 02207 02208 ssize_t 02209 y; 02210 02211 assert(image != (Image *) NULL); 02212 assert(image->signature == MagickSignature); 02213 if (image->debug != MagickFalse) 02214 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 02215 image->total_colors=GetNumberColors(image,(FILE *) NULL,exception); 02216 (void) ResetMagickMemory(&image->error,0,sizeof(image->error)); 02217 if (image->storage_class == DirectClass) 02218 return(MagickTrue); 02219 alpha=1.0; 02220 beta=1.0; 02221 area=3.0*image->columns*image->rows; 02222 maximum_error=0.0; 02223 mean_error_per_pixel=0.0; 02224 mean_error=0.0; 02225 image_view=AcquireCacheView(image); 02226 for (y=0; y < (ssize_t) image->rows; y++) 02227 { 02228 register const Quantum 02229 *restrict p; 02230 02231 register ssize_t 02232 x; 02233 02234 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); 02235 if (p == (const Quantum *) NULL) 02236 break; 02237 for (x=0; x < (ssize_t) image->columns; x++) 02238 { 02239 index=1UL*GetPixelIndex(image,p); 02240 if (image->matte != MagickFalse) 02241 { 02242 alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,p)); 02243 beta=(MagickRealType) (QuantumScale*image->colormap[index].alpha); 02244 } 02245 distance=fabs(alpha*GetPixelRed(image,p)-beta* 02246 image->colormap[index].red); 02247 mean_error_per_pixel+=distance; 02248 mean_error+=distance*distance; 02249 if (distance > maximum_error) 02250 maximum_error=distance; 02251 distance=fabs(alpha*GetPixelGreen(image,p)-beta* 02252 image->colormap[index].green); 02253 mean_error_per_pixel+=distance; 02254 mean_error+=distance*distance; 02255 if (distance > maximum_error) 02256 maximum_error=distance; 02257 distance=fabs(alpha*GetPixelBlue(image,p)-beta* 02258 image->colormap[index].blue); 02259 mean_error_per_pixel+=distance; 02260 mean_error+=distance*distance; 02261 if (distance > maximum_error) 02262 maximum_error=distance; 02263 p+=GetPixelChannels(image); 02264 } 02265 } 02266 image_view=DestroyCacheView(image_view); 02267 image->error.mean_error_per_pixel=(double) mean_error_per_pixel/area; 02268 image->error.normalized_mean_error=(double) QuantumScale*QuantumScale* 02269 mean_error/area; 02270 image->error.normalized_maximum_error=(double) QuantumScale*maximum_error; 02271 return(MagickTrue); 02272 } 02273 02274 /* 02275 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02276 % % 02277 % % 02278 % % 02279 % G e t Q u a n t i z e I n f o % 02280 % % 02281 % % 02282 % % 02283 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02284 % 02285 % GetQuantizeInfo() initializes the QuantizeInfo structure. 02286 % 02287 % The format of the GetQuantizeInfo method is: 02288 % 02289 % GetQuantizeInfo(QuantizeInfo *quantize_info) 02290 % 02291 % A description of each parameter follows: 02292 % 02293 % o quantize_info: Specifies a pointer to a QuantizeInfo structure. 02294 % 02295 */ 02296 MagickExport void GetQuantizeInfo(QuantizeInfo *quantize_info) 02297 { 02298 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); 02299 assert(quantize_info != (QuantizeInfo *) NULL); 02300 (void) ResetMagickMemory(quantize_info,0,sizeof(*quantize_info)); 02301 quantize_info->number_colors=256; 02302 quantize_info->dither=MagickTrue; 02303 quantize_info->dither_method=RiemersmaDitherMethod; 02304 quantize_info->colorspace=UndefinedColorspace; 02305 quantize_info->measure_error=MagickFalse; 02306 quantize_info->signature=MagickSignature; 02307 } 02308 02309 /* 02310 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02311 % % 02312 % % 02313 % % 02314 % P o s t e r i z e I m a g e % 02315 % % 02316 % % 02317 % % 02318 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02319 % 02320 % PosterizeImage() reduces the image to a limited number of colors for a 02321 % "poster" effect. 02322 % 02323 % The format of the PosterizeImage method is: 02324 % 02325 % MagickBooleanType PosterizeImage(Image *image,const size_t levels, 02326 % const MagickBooleanType dither,ExceptionInfo *exception) 02327 % 02328 % A description of each parameter follows: 02329 % 02330 % o image: Specifies a pointer to an Image structure. 02331 % 02332 % o levels: Number of color levels allowed in each channel. Very low values 02333 % (2, 3, or 4) have the most visible effect. 02334 % 02335 % o dither: Set this integer value to something other than zero to dither 02336 % the mapped image. 02337 % 02338 % o exception: return any errors or warnings in this structure. 02339 % 02340 */ 02341 02342 static inline ssize_t MagickRound(MagickRealType x) 02343 { 02344 /* 02345 Round the fraction to nearest integer. 02346 */ 02347 if (x >= 0.0) 02348 return((ssize_t) (x+0.5)); 02349 return((ssize_t) (x-0.5)); 02350 } 02351 02352 MagickExport MagickBooleanType PosterizeImage(Image *image,const size_t levels, 02353 const MagickBooleanType dither,ExceptionInfo *exception) 02354 { 02355 #define PosterizeImageTag "Posterize/Image" 02356 #define PosterizePixel(pixel) (Quantum) (QuantumRange*(MagickRound( \ 02357 QuantumScale*pixel*(levels-1)))/MagickMax((ssize_t) levels-1,1)) 02358 02359 CacheView 02360 *image_view; 02361 02362 MagickBooleanType 02363 status; 02364 02365 MagickOffsetType 02366 progress; 02367 02368 QuantizeInfo 02369 *quantize_info; 02370 02371 register ssize_t 02372 i; 02373 02374 ssize_t 02375 y; 02376 02377 assert(image != (Image *) NULL); 02378 assert(image->signature == MagickSignature); 02379 if (image->debug != MagickFalse) 02380 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 02381 if (image->storage_class == PseudoClass) 02382 #if defined(MAGICKCORE_OPENMP_SUPPORT) 02383 #pragma omp parallel for schedule(static,4) shared(progress,status) 02384 #endif 02385 for (i=0; i < (ssize_t) image->colors; i++) 02386 { 02387 /* 02388 Posterize colormap. 02389 */ 02390 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) 02391 image->colormap[i].red=(double) 02392 PosterizePixel(image->colormap[i].red); 02393 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) 02394 image->colormap[i].green=(double) 02395 PosterizePixel(image->colormap[i].green); 02396 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) 02397 image->colormap[i].blue=(double) 02398 PosterizePixel(image->colormap[i].blue); 02399 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) 02400 image->colormap[i].alpha=(double) 02401 PosterizePixel(image->colormap[i].alpha); 02402 } 02403 /* 02404 Posterize image. 02405 */ 02406 status=MagickTrue; 02407 progress=0; 02408 image_view=AcquireCacheView(image); 02409 #if defined(MAGICKCORE_OPENMP_SUPPORT) 02410 #pragma omp parallel for schedule(static,4) shared(progress,status) 02411 #endif 02412 for (y=0; y < (ssize_t) image->rows; y++) 02413 { 02414 register Quantum 02415 *restrict q; 02416 02417 register ssize_t 02418 x; 02419 02420 if (status == MagickFalse) 02421 continue; 02422 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 02423 if (q == (Quantum *) NULL) 02424 { 02425 status=MagickFalse; 02426 continue; 02427 } 02428 for (x=0; x < (ssize_t) image->columns; x++) 02429 { 02430 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) 02431 SetPixelRed(image,PosterizePixel(GetPixelRed(image,q)),q); 02432 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) 02433 SetPixelGreen(image,PosterizePixel(GetPixelGreen(image,q)),q); 02434 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) 02435 SetPixelBlue(image,PosterizePixel(GetPixelBlue(image,q)),q); 02436 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) && 02437 (image->colorspace == CMYKColorspace)) 02438 SetPixelBlack(image,PosterizePixel(GetPixelBlack(image,q)),q); 02439 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) && 02440 (image->matte == MagickTrue)) 02441 SetPixelAlpha(image,PosterizePixel(GetPixelAlpha(image,q)),q); 02442 q+=GetPixelChannels(image); 02443 } 02444 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 02445 status=MagickFalse; 02446 if (image->progress_monitor != (MagickProgressMonitor) NULL) 02447 { 02448 MagickBooleanType 02449 proceed; 02450 02451 #if defined(MAGICKCORE_OPENMP_SUPPORT) 02452 #pragma omp critical (MagickCore_PosterizeImage) 02453 #endif 02454 proceed=SetImageProgress(image,PosterizeImageTag,progress++, 02455 image->rows); 02456 if (proceed == MagickFalse) 02457 status=MagickFalse; 02458 } 02459 } 02460 image_view=DestroyCacheView(image_view); 02461 quantize_info=AcquireQuantizeInfo((ImageInfo *) NULL); 02462 quantize_info->number_colors=(size_t) MagickMin((ssize_t) levels*levels* 02463 levels,MaxColormapSize+1); 02464 quantize_info->dither=dither; 02465 quantize_info->tree_depth=MaxTreeDepth; 02466 status=QuantizeImage(quantize_info,image,exception); 02467 quantize_info=DestroyQuantizeInfo(quantize_info); 02468 return(status); 02469 } 02470 02471 /* 02472 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02473 % % 02474 % % 02475 % % 02476 + P r u n e C h i l d % 02477 % % 02478 % % 02479 % % 02480 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02481 % 02482 % PruneChild() deletes the given node and merges its statistics into its 02483 % parent. 02484 % 02485 % The format of the PruneSubtree method is: 02486 % 02487 % PruneChild(const Image *image,CubeInfo *cube_info, 02488 % const NodeInfo *node_info) 02489 % 02490 % A description of each parameter follows. 02491 % 02492 % o image: the image. 02493 % 02494 % o cube_info: A pointer to the Cube structure. 02495 % 02496 % o node_info: pointer to node in color cube tree that is to be pruned. 02497 % 02498 */ 02499 static void PruneChild(const Image *image,CubeInfo *cube_info, 02500 const NodeInfo *node_info) 02501 { 02502 NodeInfo 02503 *parent; 02504 02505 register ssize_t 02506 i; 02507 02508 size_t 02509 number_children; 02510 02511 /* 02512 Traverse any children. 02513 */ 02514 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL; 02515 for (i=0; i < (ssize_t) number_children; i++) 02516 if (node_info->child[i] != (NodeInfo *) NULL) 02517 PruneChild(image,cube_info,node_info->child[i]); 02518 /* 02519 Merge color statistics into parent. 02520 */ 02521 parent=node_info->parent; 02522 parent->number_unique+=node_info->number_unique; 02523 parent->total_color.red+=node_info->total_color.red; 02524 parent->total_color.green+=node_info->total_color.green; 02525 parent->total_color.blue+=node_info->total_color.blue; 02526 parent->total_color.alpha+=node_info->total_color.alpha; 02527 parent->child[node_info->id]=(NodeInfo *) NULL; 02528 cube_info->nodes--; 02529 } 02530 02531 /* 02532 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02533 % % 02534 % % 02535 % % 02536 + P r u n e L e v e l % 02537 % % 02538 % % 02539 % % 02540 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02541 % 02542 % PruneLevel() deletes all nodes at the bottom level of the color tree merging 02543 % their color statistics into their parent node. 02544 % 02545 % The format of the PruneLevel method is: 02546 % 02547 % PruneLevel(const Image *image,CubeInfo *cube_info, 02548 % const NodeInfo *node_info) 02549 % 02550 % A description of each parameter follows. 02551 % 02552 % o image: the image. 02553 % 02554 % o cube_info: A pointer to the Cube structure. 02555 % 02556 % o node_info: pointer to node in color cube tree that is to be pruned. 02557 % 02558 */ 02559 static void PruneLevel(const Image *image,CubeInfo *cube_info, 02560 const NodeInfo *node_info) 02561 { 02562 register ssize_t 02563 i; 02564 02565 size_t 02566 number_children; 02567 02568 /* 02569 Traverse any children. 02570 */ 02571 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL; 02572 for (i=0; i < (ssize_t) number_children; i++) 02573 if (node_info->child[i] != (NodeInfo *) NULL) 02574 PruneLevel(image,cube_info,node_info->child[i]); 02575 if (node_info->level == cube_info->depth) 02576 PruneChild(image,cube_info,node_info); 02577 } 02578 02579 /* 02580 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02581 % % 02582 % % 02583 % % 02584 + P r u n e T o C u b e D e p t h % 02585 % % 02586 % % 02587 % % 02588 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02589 % 02590 % PruneToCubeDepth() deletes any nodes at a depth greater than 02591 % cube_info->depth while merging their color statistics into their parent 02592 % node. 02593 % 02594 % The format of the PruneToCubeDepth method is: 02595 % 02596 % PruneToCubeDepth(const Image *image,CubeInfo *cube_info, 02597 % const NodeInfo *node_info) 02598 % 02599 % A description of each parameter follows. 02600 % 02601 % o cube_info: A pointer to the Cube structure. 02602 % 02603 % o node_info: pointer to node in color cube tree that is to be pruned. 02604 % 02605 */ 02606 static void PruneToCubeDepth(const Image *image,CubeInfo *cube_info, 02607 const NodeInfo *node_info) 02608 { 02609 register ssize_t 02610 i; 02611 02612 size_t 02613 number_children; 02614 02615 /* 02616 Traverse any children. 02617 */ 02618 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL; 02619 for (i=0; i < (ssize_t) number_children; i++) 02620 if (node_info->child[i] != (NodeInfo *) NULL) 02621 PruneToCubeDepth(image,cube_info,node_info->child[i]); 02622 if (node_info->level > cube_info->depth) 02623 PruneChild(image,cube_info,node_info); 02624 } 02625 02626 /* 02627 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02628 % % 02629 % % 02630 % % 02631 % Q u a n t i z e I m a g e % 02632 % % 02633 % % 02634 % % 02635 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02636 % 02637 % QuantizeImage() analyzes the colors within a reference image and chooses a 02638 % fixed number of colors to represent the image. The goal of the algorithm 02639 % is to minimize the color difference between the input and output image while 02640 % minimizing the processing time. 02641 % 02642 % The format of the QuantizeImage method is: 02643 % 02644 % MagickBooleanType QuantizeImage(const QuantizeInfo *quantize_info, 02645 % Image *image,ExceptionInfo *exception) 02646 % 02647 % A description of each parameter follows: 02648 % 02649 % o quantize_info: Specifies a pointer to an QuantizeInfo structure. 02650 % 02651 % o image: the image. 02652 % 02653 % o exception: return any errors or warnings in this structure. 02654 % 02655 */ 02656 02657 static MagickBooleanType DirectToColormapImage(Image *image, 02658 ExceptionInfo *exception) 02659 { 02660 CacheView 02661 *image_view; 02662 02663 MagickBooleanType 02664 status; 02665 02666 register ssize_t 02667 i; 02668 02669 size_t 02670 number_colors; 02671 02672 ssize_t 02673 y; 02674 02675 status=MagickTrue; 02676 number_colors=(size_t) (image->columns*image->rows); 02677 if (AcquireImageColormap(image,number_colors,exception) == MagickFalse) 02678 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 02679 image->filename); 02680 if (image->colors != number_colors) 02681 return(MagickFalse); 02682 i=0; 02683 image_view=AcquireCacheView(image); 02684 for (y=0; y < (ssize_t) image->rows; y++) 02685 { 02686 MagickBooleanType 02687 proceed; 02688 02689 register Quantum 02690 *restrict q; 02691 02692 register ssize_t 02693 x; 02694 02695 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 02696 if (q == (Quantum *) NULL) 02697 break; 02698 for (x=0; x < (ssize_t) image->columns; x++) 02699 { 02700 image->colormap[i].red=(double) GetPixelRed(image,q); 02701 image->colormap[i].green=(double) GetPixelGreen(image,q); 02702 image->colormap[i].blue=(double) GetPixelBlue(image,q); 02703 image->colormap[i].alpha=(double) GetPixelAlpha(image,q); 02704 SetPixelIndex(image,(Quantum) i,q); 02705 i++; 02706 q+=GetPixelChannels(image); 02707 } 02708 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 02709 break; 02710 proceed=SetImageProgress(image,AssignImageTag,(MagickOffsetType) y, 02711 image->rows); 02712 if (proceed == MagickFalse) 02713 status=MagickFalse; 02714 } 02715 image_view=DestroyCacheView(image_view); 02716 return(status); 02717 } 02718 02719 MagickExport MagickBooleanType QuantizeImage(const QuantizeInfo *quantize_info, 02720 Image *image,ExceptionInfo *exception) 02721 { 02722 CubeInfo 02723 *cube_info; 02724 02725 MagickBooleanType 02726 status; 02727 02728 size_t 02729 depth, 02730 maximum_colors; 02731 02732 assert(quantize_info != (const QuantizeInfo *) NULL); 02733 assert(quantize_info->signature == MagickSignature); 02734 assert(image != (Image *) NULL); 02735 assert(image->signature == MagickSignature); 02736 if (image->debug != MagickFalse) 02737 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 02738 maximum_colors=quantize_info->number_colors; 02739 if (maximum_colors == 0) 02740 maximum_colors=MaxColormapSize; 02741 if (maximum_colors > MaxColormapSize) 02742 maximum_colors=MaxColormapSize; 02743 if ((image->columns*image->rows) <= maximum_colors) 02744 (void) DirectToColormapImage(image,exception); 02745 if ((IsImageGray(image,exception) != MagickFalse) && 02746 (image->matte == MagickFalse)) 02747 (void) SetGrayscaleImage(image,exception); 02748 if ((image->storage_class == PseudoClass) && 02749 (image->colors <= maximum_colors)) 02750 return(MagickTrue); 02751 depth=quantize_info->tree_depth; 02752 if (depth == 0) 02753 { 02754 size_t 02755 colors; 02756 02757 /* 02758 Depth of color tree is: Log4(colormap size)+2. 02759 */ 02760 colors=maximum_colors; 02761 for (depth=1; colors != 0; depth++) 02762 colors>>=2; 02763 if ((quantize_info->dither != MagickFalse) && (depth > 2)) 02764 depth--; 02765 if ((image->matte != MagickFalse) && (depth > 5)) 02766 depth--; 02767 } 02768 /* 02769 Initialize color cube. 02770 */ 02771 cube_info=GetCubeInfo(quantize_info,depth,maximum_colors); 02772 if (cube_info == (CubeInfo *) NULL) 02773 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 02774 image->filename); 02775 status=ClassifyImageColors(cube_info,image,exception); 02776 if (status != MagickFalse) 02777 { 02778 /* 02779 Reduce the number of colors in the image. 02780 */ 02781 ReduceImageColors(image,cube_info); 02782 status=AssignImageColors(image,cube_info,exception); 02783 } 02784 DestroyCubeInfo(cube_info); 02785 return(status); 02786 } 02787 02788 /* 02789 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02790 % % 02791 % % 02792 % % 02793 % Q u a n t i z e I m a g e s % 02794 % % 02795 % % 02796 % % 02797 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02798 % 02799 % QuantizeImages() analyzes the colors within a set of reference images and 02800 % chooses a fixed number of colors to represent the set. The goal of the 02801 % algorithm is to minimize the color difference between the input and output 02802 % images while minimizing the processing time. 02803 % 02804 % The format of the QuantizeImages method is: 02805 % 02806 % MagickBooleanType QuantizeImages(const QuantizeInfo *quantize_info, 02807 % Image *images,ExceptionInfo *exception) 02808 % 02809 % A description of each parameter follows: 02810 % 02811 % o quantize_info: Specifies a pointer to an QuantizeInfo structure. 02812 % 02813 % o images: Specifies a pointer to a list of Image structures. 02814 % 02815 % o exception: return any errors or warnings in this structure. 02816 % 02817 */ 02818 MagickExport MagickBooleanType QuantizeImages(const QuantizeInfo *quantize_info, 02819 Image *images,ExceptionInfo *exception) 02820 { 02821 CubeInfo 02822 *cube_info; 02823 02824 Image 02825 *image; 02826 02827 MagickBooleanType 02828 proceed, 02829 status; 02830 02831 MagickProgressMonitor 02832 progress_monitor; 02833 02834 register ssize_t 02835 i; 02836 02837 size_t 02838 depth, 02839 maximum_colors, 02840 number_images; 02841 02842 assert(quantize_info != (const QuantizeInfo *) NULL); 02843 assert(quantize_info->signature == MagickSignature); 02844 assert(images != (Image *) NULL); 02845 assert(images->signature == MagickSignature); 02846 if (images->debug != MagickFalse) 02847 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename); 02848 if (GetNextImageInList(images) == (Image *) NULL) 02849 { 02850 /* 02851 Handle a single image with QuantizeImage. 02852 */ 02853 status=QuantizeImage(quantize_info,images,exception); 02854 return(status); 02855 } 02856 status=MagickFalse; 02857 maximum_colors=quantize_info->number_colors; 02858 if (maximum_colors == 0) 02859 maximum_colors=MaxColormapSize; 02860 if (maximum_colors > MaxColormapSize) 02861 maximum_colors=MaxColormapSize; 02862 depth=quantize_info->tree_depth; 02863 if (depth == 0) 02864 { 02865 size_t 02866 colors; 02867 02868 /* 02869 Depth of color tree is: Log4(colormap size)+2. 02870 */ 02871 colors=maximum_colors; 02872 for (depth=1; colors != 0; depth++) 02873 colors>>=2; 02874 if (quantize_info->dither != MagickFalse) 02875 depth--; 02876 } 02877 /* 02878 Initialize color cube. 02879 */ 02880 cube_info=GetCubeInfo(quantize_info,depth,maximum_colors); 02881 if (cube_info == (CubeInfo *) NULL) 02882 { 02883 (void) ThrowMagickException(exception,GetMagickModule(), 02884 ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename); 02885 return(MagickFalse); 02886 } 02887 number_images=GetImageListLength(images); 02888 image=images; 02889 for (i=0; image != (Image *) NULL; i++) 02890 { 02891 progress_monitor=SetImageProgressMonitor(image,(MagickProgressMonitor) NULL, 02892 image->client_data); 02893 status=ClassifyImageColors(cube_info,image,exception); 02894 if (status == MagickFalse) 02895 break; 02896 (void) SetImageProgressMonitor(image,progress_monitor,image->client_data); 02897 proceed=SetImageProgress(image,AssignImageTag,(MagickOffsetType) i, 02898 number_images); 02899 if (proceed == MagickFalse) 02900 break; 02901 image=GetNextImageInList(image); 02902 } 02903 if (status != MagickFalse) 02904 { 02905 /* 02906 Reduce the number of colors in an image sequence. 02907 */ 02908 ReduceImageColors(images,cube_info); 02909 image=images; 02910 for (i=0; image != (Image *) NULL; i++) 02911 { 02912 progress_monitor=SetImageProgressMonitor(image,(MagickProgressMonitor) 02913 NULL,image->client_data); 02914 status=AssignImageColors(image,cube_info,exception); 02915 if (status == MagickFalse) 02916 break; 02917 (void) SetImageProgressMonitor(image,progress_monitor, 02918 image->client_data); 02919 proceed=SetImageProgress(image,AssignImageTag,(MagickOffsetType) i, 02920 number_images); 02921 if (proceed == MagickFalse) 02922 break; 02923 image=GetNextImageInList(image); 02924 } 02925 } 02926 DestroyCubeInfo(cube_info); 02927 return(status); 02928 } 02929 02930 /* 02931 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02932 % % 02933 % % 02934 % % 02935 + R e d u c e % 02936 % % 02937 % % 02938 % % 02939 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02940 % 02941 % Reduce() traverses the color cube tree and prunes any node whose 02942 % quantization error falls below a particular threshold. 02943 % 02944 % The format of the Reduce method is: 02945 % 02946 % Reduce(const Image *image,CubeInfo *cube_info,const NodeInfo *node_info) 02947 % 02948 % A description of each parameter follows. 02949 % 02950 % o image: the image. 02951 % 02952 % o cube_info: A pointer to the Cube structure. 02953 % 02954 % o node_info: pointer to node in color cube tree that is to be pruned. 02955 % 02956 */ 02957 static void Reduce(const Image *image,CubeInfo *cube_info, 02958 const NodeInfo *node_info) 02959 { 02960 register ssize_t 02961 i; 02962 02963 size_t 02964 number_children; 02965 02966 /* 02967 Traverse any children. 02968 */ 02969 number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL; 02970 for (i=0; i < (ssize_t) number_children; i++) 02971 if (node_info->child[i] != (NodeInfo *) NULL) 02972 Reduce(image,cube_info,node_info->child[i]); 02973 if (node_info->quantize_error <= cube_info->pruning_threshold) 02974 PruneChild(image,cube_info,node_info); 02975 else 02976 { 02977 /* 02978 Find minimum pruning threshold. 02979 */ 02980 if (node_info->number_unique > 0) 02981 cube_info->colors++; 02982 if (node_info->quantize_error < cube_info->next_threshold) 02983 cube_info->next_threshold=node_info->quantize_error; 02984 } 02985 } 02986 02987 /* 02988 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02989 % % 02990 % % 02991 % % 02992 + R e d u c e I m a g e C o l o r s % 02993 % % 02994 % % 02995 % % 02996 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02997 % 02998 % ReduceImageColors() repeatedly prunes the tree until the number of nodes 02999 % with n2 > 0 is less than or equal to the maximum number of colors allowed 03000 % in the output image. On any given iteration over the tree, it selects 03001 % those nodes whose E value is minimal for pruning and merges their 03002 % color statistics upward. It uses a pruning threshold, Ep, to govern 03003 % node selection as follows: 03004 % 03005 % Ep = 0 03006 % while number of nodes with (n2 > 0) > required maximum number of colors 03007 % prune all nodes such that E <= Ep 03008 % Set Ep to minimum E in remaining nodes 03009 % 03010 % This has the effect of minimizing any quantization error when merging 03011 % two nodes together. 03012 % 03013 % When a node to be pruned has offspring, the pruning procedure invokes 03014 % itself recursively in order to prune the tree from the leaves upward. 03015 % n2, Sr, Sg, and Sb in a node being pruned are always added to the 03016 % corresponding data in that node's parent. This retains the pruned 03017 % node's color characteristics for later averaging. 03018 % 03019 % For each node, n2 pixels exist for which that node represents the 03020 % smallest volume in RGB space containing those pixel's colors. When n2 03021 % > 0 the node will uniquely define a color in the output image. At the 03022 % beginning of reduction, n2 = 0 for all nodes except a the leaves of 03023 % the tree which represent colors present in the input image. 03024 % 03025 % The other pixel count, n1, indicates the total number of colors 03026 % within the cubic volume which the node represents. This includes n1 - 03027 % n2 pixels whose colors should be defined by nodes at a lower level in 03028 % the tree. 03029 % 03030 % The format of the ReduceImageColors method is: 03031 % 03032 % ReduceImageColors(const Image *image,CubeInfo *cube_info) 03033 % 03034 % A description of each parameter follows. 03035 % 03036 % o image: the image. 03037 % 03038 % o cube_info: A pointer to the Cube structure. 03039 % 03040 */ 03041 static void ReduceImageColors(const Image *image,CubeInfo *cube_info) 03042 { 03043 #define ReduceImageTag "Reduce/Image" 03044 03045 MagickBooleanType 03046 proceed; 03047 03048 MagickOffsetType 03049 offset; 03050 03051 size_t 03052 span; 03053 03054 cube_info->next_threshold=0.0; 03055 for (span=cube_info->colors; cube_info->colors > cube_info->maximum_colors; ) 03056 { 03057 cube_info->pruning_threshold=cube_info->next_threshold; 03058 cube_info->next_threshold=cube_info->root->quantize_error-1; 03059 cube_info->colors=0; 03060 Reduce(image,cube_info,cube_info->root); 03061 offset=(MagickOffsetType) span-cube_info->colors; 03062 proceed=SetImageProgress(image,ReduceImageTag,offset,span- 03063 cube_info->maximum_colors+1); 03064 if (proceed == MagickFalse) 03065 break; 03066 } 03067 } 03068 03069 /* 03070 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 03071 % % 03072 % % 03073 % % 03074 % R e m a p I m a g e % 03075 % % 03076 % % 03077 % % 03078 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 03079 % 03080 % RemapImage() replaces the colors of an image with a dither of the colors 03081 % provided. 03082 % 03083 % The format of the RemapImage method is: 03084 % 03085 % MagickBooleanType RemapImage(const QuantizeInfo *quantize_info, 03086 % Image *image,const Image *remap_image,ExceptionInfo *exception) 03087 % 03088 % A description of each parameter follows: 03089 % 03090 % o quantize_info: Specifies a pointer to an QuantizeInfo structure. 03091 % 03092 % o image: the image. 03093 % 03094 % o remap_image: the reference image. 03095 % 03096 % o exception: return any errors or warnings in this structure. 03097 % 03098 */ 03099 MagickExport MagickBooleanType RemapImage(const QuantizeInfo *quantize_info, 03100 Image *image,const Image *remap_image,ExceptionInfo *exception) 03101 { 03102 CubeInfo 03103 *cube_info; 03104 03105 MagickBooleanType 03106 status; 03107 03108 /* 03109 Initialize color cube. 03110 */ 03111 assert(image != (Image *) NULL); 03112 assert(image->signature == MagickSignature); 03113 if (image->debug != MagickFalse) 03114 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 03115 assert(remap_image != (Image *) NULL); 03116 assert(remap_image->signature == MagickSignature); 03117 cube_info=GetCubeInfo(quantize_info,MaxTreeDepth, 03118 quantize_info->number_colors); 03119 if (cube_info == (CubeInfo *) NULL) 03120 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 03121 image->filename); 03122 status=ClassifyImageColors(cube_info,remap_image,exception); 03123 if (status != MagickFalse) 03124 { 03125 /* 03126 Classify image colors from the reference image. 03127 */ 03128 cube_info->quantize_info->number_colors=cube_info->colors; 03129 status=AssignImageColors(image,cube_info,exception); 03130 } 03131 DestroyCubeInfo(cube_info); 03132 return(status); 03133 } 03134 03135 /* 03136 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 03137 % % 03138 % % 03139 % % 03140 % R e m a p I m a g e s % 03141 % % 03142 % % 03143 % % 03144 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 03145 % 03146 % RemapImages() replaces the colors of a sequence of images with the 03147 % closest color from a reference image. 03148 % 03149 % The format of the RemapImage method is: 03150 % 03151 % MagickBooleanType RemapImages(const QuantizeInfo *quantize_info, 03152 % Image *images,Image *remap_image,ExceptionInfo *exception) 03153 % 03154 % A description of each parameter follows: 03155 % 03156 % o quantize_info: Specifies a pointer to an QuantizeInfo structure. 03157 % 03158 % o images: the image sequence. 03159 % 03160 % o remap_image: the reference image. 03161 % 03162 % o exception: return any errors or warnings in this structure. 03163 % 03164 */ 03165 MagickExport MagickBooleanType RemapImages(const QuantizeInfo *quantize_info, 03166 Image *images,const Image *remap_image,ExceptionInfo *exception) 03167 { 03168 CubeInfo 03169 *cube_info; 03170 03171 Image 03172 *image; 03173 03174 MagickBooleanType 03175 status; 03176 03177 assert(images != (Image *) NULL); 03178 assert(images->signature == MagickSignature); 03179 if (images->debug != MagickFalse) 03180 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename); 03181 image=images; 03182 if (remap_image == (Image *) NULL) 03183 { 03184 /* 03185 Create a global colormap for an image sequence. 03186 */ 03187 status=QuantizeImages(quantize_info,images,exception); 03188 return(status); 03189 } 03190 /* 03191 Classify image colors from the reference image. 03192 */ 03193 cube_info=GetCubeInfo(quantize_info,MaxTreeDepth, 03194 quantize_info->number_colors); 03195 if (cube_info == (CubeInfo *) NULL) 03196 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 03197 image->filename); 03198 status=ClassifyImageColors(cube_info,remap_image,exception); 03199 if (status != MagickFalse) 03200 { 03201 /* 03202 Classify image colors from the reference image. 03203 */ 03204 cube_info->quantize_info->number_colors=cube_info->colors; 03205 image=images; 03206 for ( ; image != (Image *) NULL; image=GetNextImageInList(image)) 03207 { 03208 status=AssignImageColors(image,cube_info,exception); 03209 if (status == MagickFalse) 03210 break; 03211 } 03212 } 03213 DestroyCubeInfo(cube_info); 03214 return(status); 03215 } 03216 03217 /* 03218 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 03219 % % 03220 % % 03221 % % 03222 % S e t G r a y s c a l e I m a g e % 03223 % % 03224 % % 03225 % % 03226 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 03227 % 03228 % SetGrayscaleImage() converts an image to a PseudoClass grayscale image. 03229 % 03230 % The format of the SetGrayscaleImage method is: 03231 % 03232 % MagickBooleanType SetGrayscaleImage(Image *image,ExceptionInfo *exeption) 03233 % 03234 % A description of each parameter follows: 03235 % 03236 % o image: The image. 03237 % 03238 % o exception: return any errors or warnings in this structure. 03239 % 03240 */ 03241 03242 #if defined(__cplusplus) || defined(c_plusplus) 03243 extern "C" { 03244 #endif 03245 03246 static int IntensityCompare(const void *x,const void *y) 03247 { 03248 PixelInfo 03249 *color_1, 03250 *color_2; 03251 03252 ssize_t 03253 intensity; 03254 03255 color_1=(PixelInfo *) x; 03256 color_2=(PixelInfo *) y; 03257 intensity=GetPixelInfoIntensity(color_1)-(ssize_t) 03258 GetPixelInfoIntensity(color_2); 03259 return((int) intensity); 03260 } 03261 03262 #if defined(__cplusplus) || defined(c_plusplus) 03263 } 03264 #endif 03265 03266 static MagickBooleanType SetGrayscaleImage(Image *image, 03267 ExceptionInfo *exception) 03268 { 03269 CacheView 03270 *image_view; 03271 03272 MagickBooleanType 03273 status; 03274 03275 PixelInfo 03276 *colormap; 03277 03278 register ssize_t 03279 i; 03280 03281 ssize_t 03282 *colormap_index, 03283 j, 03284 y; 03285 03286 assert(image != (Image *) NULL); 03287 assert(image->signature == MagickSignature); 03288 if (image->type != GrayscaleType) 03289 (void) TransformImageColorspace(image,GRAYColorspace,exception); 03290 colormap_index=(ssize_t *) AcquireQuantumMemory(MaxMap+1, 03291 sizeof(*colormap_index)); 03292 if (colormap_index == (ssize_t *) NULL) 03293 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 03294 image->filename); 03295 if (image->storage_class != PseudoClass) 03296 { 03297 for (i=0; i <= (ssize_t) MaxMap; i++) 03298 colormap_index[i]=(-1); 03299 if (AcquireImageColormap(image,MaxMap+1,exception) == MagickFalse) 03300 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 03301 image->filename); 03302 image->colors=0; 03303 status=MagickTrue; 03304 image_view=AcquireCacheView(image); 03305 #if defined(MAGICKCORE_OPENMP_SUPPORT) 03306 #pragma omp parallel for schedule(static,4) shared(status) 03307 #endif 03308 for (y=0; y < (ssize_t) image->rows; y++) 03309 { 03310 register Quantum 03311 *restrict q; 03312 03313 register ssize_t 03314 x; 03315 03316 if (status == MagickFalse) 03317 continue; 03318 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1, 03319 exception); 03320 if (q == (Quantum *) NULL) 03321 { 03322 status=MagickFalse; 03323 continue; 03324 } 03325 for (x=0; x < (ssize_t) image->columns; x++) 03326 { 03327 register size_t 03328 intensity; 03329 03330 intensity=ScaleQuantumToMap(GetPixelRed(image,q)); 03331 if (colormap_index[intensity] < 0) 03332 { 03333 #if defined(MAGICKCORE_OPENMP_SUPPORT) 03334 #pragma omp critical (MagickCore_SetGrayscaleImage) 03335 #endif 03336 if (colormap_index[intensity] < 0) 03337 { 03338 colormap_index[intensity]=(ssize_t) image->colors; 03339 image->colormap[image->colors].red=(double) 03340 GetPixelRed(image,q); 03341 image->colormap[image->colors].green=(double) 03342 GetPixelGreen(image,q); 03343 image->colormap[image->colors].blue=(double) 03344 GetPixelBlue(image,q); 03345 image->colors++; 03346 } 03347 } 03348 SetPixelIndex(image,(Quantum) 03349 colormap_index[intensity],q); 03350 q+=GetPixelChannels(image); 03351 } 03352 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 03353 status=MagickFalse; 03354 } 03355 image_view=DestroyCacheView(image_view); 03356 } 03357 for (i=0; i < (ssize_t) image->colors; i++) 03358 image->colormap[i].alpha=(double) i; 03359 qsort((void *) image->colormap,image->colors,sizeof(PixelInfo), 03360 IntensityCompare); 03361 colormap=(PixelInfo *) AcquireQuantumMemory(image->colors, 03362 sizeof(*colormap)); 03363 if (colormap == (PixelInfo *) NULL) 03364 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 03365 image->filename); 03366 j=0; 03367 colormap[j]=image->colormap[0]; 03368 for (i=0; i < (ssize_t) image->colors; i++) 03369 { 03370 if (IsPixelInfoEquivalent(&colormap[j],&image->colormap[i]) == MagickFalse) 03371 { 03372 j++; 03373 colormap[j]=image->colormap[i]; 03374 } 03375 colormap_index[(ssize_t) image->colormap[i].alpha]=j; 03376 } 03377 image->colors=(size_t) (j+1); 03378 image->colormap=(PixelInfo *) RelinquishMagickMemory(image->colormap); 03379 image->colormap=colormap; 03380 status=MagickTrue; 03381 image_view=AcquireCacheView(image); 03382 #if defined(MAGICKCORE_OPENMP_SUPPORT) 03383 #pragma omp parallel for schedule(static,4) shared(status) 03384 #endif 03385 for (y=0; y < (ssize_t) image->rows; y++) 03386 { 03387 register Quantum 03388 *restrict q; 03389 03390 register ssize_t 03391 x; 03392 03393 if (status == MagickFalse) 03394 continue; 03395 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); 03396 if (q == (Quantum *) NULL) 03397 { 03398 status=MagickFalse; 03399 continue; 03400 } 03401 for (x=0; x < (ssize_t) image->columns; x++) 03402 { 03403 SetPixelIndex(image,(Quantum) colormap_index[ScaleQuantumToMap( 03404 GetPixelIndex(image,q))],q); 03405 q+=GetPixelChannels(image); 03406 } 03407 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 03408 status=MagickFalse; 03409 } 03410 image_view=DestroyCacheView(image_view); 03411 colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index); 03412 image->type=GrayscaleType; 03413 if (IsImageMonochrome(image,exception) != MagickFalse) 03414 image->type=BilevelType; 03415 return(status); 03416 }