1/* 2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3% % 4% % 5% % 6% H H IIIII SSSSS TTTTT OOO GGGG RRRR AAA M M % 7% H H I SS T O O G R R A A MM MM % 8% HHHHH I SSS T O O G GG RRRR AAAAA M M M % 9% H H I SS T O O G G R R A A M M % 10% H H IIIII SSSSS T OOO GGG R R A A M M % 11% % 12% % 13% Write A Histogram Image. % 14% % 15% Software Design % 16% Cristy % 17% July 1992 % 18% % 19% % 20% Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization % 21% dedicated to making software imaging solutions freely available. % 22% % 23% You may not use this file except in compliance with the License. You may % 24% obtain a copy of the License at % 25% % 26% http://www.imagemagick.org/script/license.php % 27% % 28% Unless required by applicable law or agreed to in writing, software % 29% distributed under the License is distributed on an "AS IS" BASIS, % 30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % 31% See the License for the specific language governing permissions and % 32% limitations under the License. % 33% % 34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 35% 36% 37*/ 38 39/* 40 Include declarations. 41*/ 42#include "MagickCore/studio.h" 43#include "MagickCore/artifact.h" 44#include "MagickCore/blob.h" 45#include "MagickCore/blob-private.h" 46#include "MagickCore/cache.h" 47#include "MagickCore/color.h" 48#include "MagickCore/color-private.h" 49#include "MagickCore/constitute.h" 50#include "MagickCore/exception.h" 51#include "MagickCore/exception-private.h" 52#include "MagickCore/geometry.h" 53#include "MagickCore/histogram.h" 54#include "MagickCore/image-private.h" 55#include "MagickCore/magick.h" 56#include "MagickCore/memory_.h" 57#include "MagickCore/monitor.h" 58#include "MagickCore/monitor-private.h" 59#include "MagickCore/option.h" 60#include "MagickCore/pixel-accessor.h" 61#include "MagickCore/property.h" 62#include "MagickCore/quantum-private.h" 63#include "MagickCore/resource_.h" 64#include "MagickCore/static.h" 65#include "MagickCore/statistic.h" 66#include "MagickCore/string_.h" 67#include "MagickCore/module.h" 68#include "MagickCore/token.h" 69#include "MagickCore/utility.h" 70 71/* 72 Forward declarations. 73*/ 74static MagickBooleanType 75 WriteHISTOGRAMImage(const ImageInfo *,Image *,ExceptionInfo *); 76 77/* 78%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 79% % 80% % 81% % 82% R e g i s t e r H I S T O G R A M I m a g e % 83% % 84% % 85% % 86%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 87% 88% RegisterHISTOGRAMImage() adds attributes for the Histogram image format 89% to the list of supported formats. The attributes include the image format 90% tag, a method to read and/or write the format, whether the format 91% supports the saving of more than one frame to the same file or blob, 92% whether the format supports native in-memory I/O, and a brief 93% description of the format. 94% 95% The format of the RegisterHISTOGRAMImage method is: 96% 97% size_t RegisterHISTOGRAMImage(void) 98% 99*/ 100ModuleExport size_t RegisterHISTOGRAMImage(void) 101{ 102 MagickInfo 103 *entry; 104 105 entry=AcquireMagickInfo("HISTOGRAM","HISTOGRAM","Histogram of the image"); 106 entry->encoder=(EncodeImageHandler *) WriteHISTOGRAMImage; 107 entry->flags^=CoderAdjoinFlag; 108 entry->format_type=ImplicitFormatType; 109 (void) RegisterMagickInfo(entry); 110 return(MagickImageCoderSignature); 111} 112 113/* 114%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 115% % 116% % 117% % 118% U n r e g i s t e r H I S T O G R A M I m a g e % 119% % 120% % 121% % 122%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 123% 124% UnregisterHISTOGRAMImage() removes format registrations made by the 125% HISTOGRAM module from the list of supported formats. 126% 127% The format of the UnregisterHISTOGRAMImage method is: 128% 129% UnregisterHISTOGRAMImage(void) 130% 131*/ 132ModuleExport void UnregisterHISTOGRAMImage(void) 133{ 134 (void) UnregisterMagickInfo("HISTOGRAM"); 135} 136 137/* 138%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 139% % 140% % 141% % 142% W r i t e H I S T O G R A M I m a g e % 143% % 144% % 145% % 146%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 147% 148% WriteHISTOGRAMImage() writes an image to a file in Histogram format. 149% The image shows a histogram of the color (or gray) values in the image. The 150% image consists of three overlaid histograms: a red one for the red channel, 151% a green one for the green channel, and a blue one for the blue channel. The 152% image comment contains a list of unique pixel values and the number of times 153% each occurs in the image. 154% 155% This method is strongly based on a similar one written by 156% muquit@warm.semcor.com which in turn is based on ppmhistmap of netpbm. 157% 158% The format of the WriteHISTOGRAMImage method is: 159% 160% MagickBooleanType WriteHISTOGRAMImage(const ImageInfo *image_info, 161% Image *image,ExceptionInfo *exception) 162% 163% A description of each parameter follows. 164% 165% o image_info: the image info. 166% 167% o image: The image. 168% 169% o exception: return any errors or warnings in this structure. 170% 171*/ 172static MagickBooleanType WriteHISTOGRAMImage(const ImageInfo *image_info, 173 Image *image,ExceptionInfo *exception) 174{ 175#define HistogramDensity "256x200" 176 177 char 178 filename[MagickPathExtent]; 179 180 const char 181 *option; 182 183 Image 184 *histogram_image; 185 186 ImageInfo 187 *write_info; 188 189 MagickBooleanType 190 status; 191 192 PixelInfo 193 *histogram; 194 195 double 196 maximum, 197 scale; 198 199 RectangleInfo 200 geometry; 201 202 register const Quantum 203 *p; 204 205 register Quantum 206 *q, 207 *r; 208 209 register ssize_t 210 x; 211 212 size_t 213 length; 214 215 ssize_t 216 y; 217 218 /* 219 Allocate histogram image. 220 */ 221 assert(image_info != (const ImageInfo *) NULL); 222 assert(image_info->signature == MagickCoreSignature); 223 assert(image != (Image *) NULL); 224 assert(image->signature == MagickCoreSignature); 225 if (image->debug != MagickFalse) 226 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", 227 image_info->filename); 228 SetGeometry(image,&geometry); 229 if (image_info->density == (char *) NULL) 230 (void) ParseAbsoluteGeometry(HistogramDensity,&geometry); 231 else 232 (void) ParseAbsoluteGeometry(image_info->density,&geometry); 233 histogram_image=CloneImage(image,geometry.width,geometry.height,MagickTrue, 234 exception); 235 if (histogram_image == (Image *) NULL) 236 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed"); 237 (void) SetImageStorageClass(histogram_image,DirectClass,exception); 238 /* 239 Allocate histogram count arrays. 240 */ 241 length=MagickMax((size_t) ScaleQuantumToChar(QuantumRange)+1UL, 242 histogram_image->columns); 243 histogram=(PixelInfo *) AcquireQuantumMemory(length,sizeof(*histogram)); 244 if (histogram == (PixelInfo *) NULL) 245 { 246 histogram_image=DestroyImage(histogram_image); 247 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed"); 248 } 249 /* 250 Initialize histogram count arrays. 251 */ 252 (void) ResetMagickMemory(histogram,0,length*sizeof(*histogram)); 253 for (y=0; y < (ssize_t) image->rows; y++) 254 { 255 p=GetVirtualPixels(image,0,y,image->columns,1,exception); 256 if (p == (const Quantum *) NULL) 257 break; 258 for (x=0; x < (ssize_t) image->columns; x++) 259 { 260 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) 261 histogram[ScaleQuantumToChar(GetPixelRed(image,p))].red++; 262 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) 263 histogram[ScaleQuantumToChar(GetPixelGreen(image,p))].green++; 264 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) 265 histogram[ScaleQuantumToChar(GetPixelBlue(image,p))].blue++; 266 p+=GetPixelChannels(image); 267 } 268 } 269 maximum=histogram[0].red; 270 for (x=0; x < (ssize_t) histogram_image->columns; x++) 271 { 272 if (((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) && 273 (maximum < histogram[x].red)) 274 maximum=histogram[x].red; 275 if (((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) && 276 (maximum < histogram[x].green)) 277 maximum=histogram[x].green; 278 if (((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) && 279 (maximum < histogram[x].blue)) 280 maximum=histogram[x].blue; 281 } 282 scale=0.0; 283 if (fabs(maximum) >= MagickEpsilon) 284 scale=(double) histogram_image->rows/maximum; 285 /* 286 Initialize histogram image. 287 */ 288 (void) QueryColorCompliance("#000000",AllCompliance, 289 &histogram_image->background_color,exception); 290 (void) SetImageBackgroundColor(histogram_image,exception); 291 for (x=0; x < (ssize_t) histogram_image->columns; x++) 292 { 293 q=GetAuthenticPixels(histogram_image,x,0,1,histogram_image->rows,exception); 294 if (q == (Quantum *) NULL) 295 break; 296 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) 297 { 298 y=(ssize_t) ceil(histogram_image->rows-scale*histogram[x].red-0.5); 299 r=q+y*GetPixelChannels(histogram_image); 300 for ( ; y < (ssize_t) histogram_image->rows; y++) 301 { 302 SetPixelRed(histogram_image,QuantumRange,r); 303 r+=GetPixelChannels(histogram_image); 304 } 305 } 306 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) 307 { 308 y=(ssize_t) ceil(histogram_image->rows-scale*histogram[x].green-0.5); 309 r=q+y*GetPixelChannels(histogram_image); 310 for ( ; y < (ssize_t) histogram_image->rows; y++) 311 { 312 SetPixelGreen(histogram_image,QuantumRange,r); 313 r+=GetPixelChannels(histogram_image); 314 } 315 } 316 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) 317 { 318 y=(ssize_t) ceil(histogram_image->rows-scale*histogram[x].blue-0.5); 319 r=q+y*GetPixelChannels(histogram_image); 320 for ( ; y < (ssize_t) histogram_image->rows; y++) 321 { 322 SetPixelBlue(histogram_image,QuantumRange,r); 323 r+=GetPixelChannels(histogram_image); 324 } 325 } 326 if (SyncAuthenticPixels(histogram_image,exception) == MagickFalse) 327 break; 328 status=SetImageProgress(image,SaveImageTag,y,histogram_image->rows); 329 if (status == MagickFalse) 330 break; 331 } 332 histogram=(PixelInfo *) RelinquishMagickMemory(histogram); 333 option=GetImageOption(image_info,"histogram:unique-colors"); 334 if ((option == (const char *) NULL) || (IsStringTrue(option) != MagickFalse)) 335 { 336 FILE 337 *file; 338 339 int 340 unique_file; 341 342 /* 343 Add a unique colors as an image comment. 344 */ 345 file=(FILE *) NULL; 346 unique_file=AcquireUniqueFileResource(filename); 347 if (unique_file != -1) 348 file=fdopen(unique_file,"wb"); 349 if ((unique_file != -1) && (file != (FILE *) NULL)) 350 { 351 char 352 *property; 353 354 (void) GetNumberColors(image,file,exception); 355 (void) fclose(file); 356 property=FileToString(filename,~0UL,exception); 357 if (property != (char *) NULL) 358 { 359 (void) SetImageProperty(histogram_image,"comment",property, 360 exception); 361 property=DestroyString(property); 362 } 363 } 364 (void) RelinquishUniqueFileResource(filename); 365 } 366 /* 367 Write Histogram image. 368 */ 369 (void) CopyMagickString(histogram_image->filename,image_info->filename, 370 MagickPathExtent); 371 write_info=CloneImageInfo(image_info); 372 *write_info->magick='\0'; 373 (void) SetImageInfo(write_info,1,exception); 374 if ((*write_info->magick == '\0') || 375 (LocaleCompare(write_info->magick,"HISTOGRAM") == 0)) 376 (void) FormatLocaleString(histogram_image->filename,MagickPathExtent, 377 "miff:%s",write_info->filename); 378 histogram_image->blob=DetachBlob(histogram_image->blob); 379 histogram_image->blob=CloneBlobInfo(image->blob); 380 status=WriteImage(write_info,histogram_image,exception); 381 image->blob=DetachBlob(image->blob); 382 image->blob=CloneBlobInfo(histogram_image->blob); 383 histogram_image=DestroyImage(histogram_image); 384 write_info=DestroyImageInfo(write_info); 385 return(status); 386} 387