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