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