blob: 6bf6193088f62ef5462ce58a1010aed91eb1b0bc [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
cristy787c09c2010-04-16 18:18:34 +00006% 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 %
cristy3ed852e2009-09-05 21:47:34 +000011% %
12% %
cristy787c09c2010-04-16 18:18:34 +000013% Write A Histogram Image. %
cristy3ed852e2009-09-05 21:47:34 +000014% %
15% Software Design %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
cristy787c09c2010-04-16 18:18:34 +000017% July 1992 %
cristy3ed852e2009-09-05 21:47:34 +000018% %
19% %
Cristy7ce65e72015-12-12 18:03:16 -050020% Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% 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*/
cristy4c08aed2011-07-01 19:47:50 +000042#include "MagickCore/studio.h"
cristy76ce6e12013-04-05 14:33:38 +000043#include "MagickCore/artifact.h"
cristy4c08aed2011-07-01 19:47:50 +000044#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"
cristy76ce6e12013-04-05 14:33:38 +000061#include "MagickCore/property.h"
cristy4c08aed2011-07-01 19:47:50 +000062#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"
cristye40005d2012-03-23 12:18:45 +000068#include "MagickCore/token.h"
cristy4c08aed2011-07-01 19:47:50 +000069#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000070
71/*
72 Forward declarations.
73*/
cristy787c09c2010-04-16 18:18:34 +000074static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +000075 WriteHISTOGRAMImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +000076
77/*
78%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
79% %
80% %
81% %
cristy787c09c2010-04-16 18:18:34 +000082% R e g i s t e r H I S T O G R A M I m a g e %
cristy3ed852e2009-09-05 21:47:34 +000083% %
84% %
85% %
86%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
87%
cristy787c09c2010-04-16 18:18:34 +000088% 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.
cristy3ed852e2009-09-05 21:47:34 +000094%
cristy787c09c2010-04-16 18:18:34 +000095% The format of the RegisterHISTOGRAMImage method is:
cristy3ed852e2009-09-05 21:47:34 +000096%
cristybb503372010-05-27 20:51:26 +000097% size_t RegisterHISTOGRAMImage(void)
cristy787c09c2010-04-16 18:18:34 +000098%
99*/
cristybb503372010-05-27 20:51:26 +0000100ModuleExport size_t RegisterHISTOGRAMImage(void)
cristy787c09c2010-04-16 18:18:34 +0000101{
102 MagickInfo
103 *entry;
104
dirk06b627a2015-04-06 18:59:17 +0000105 entry=AcquireMagickInfo("HISTOGRAM","HISTOGRAM","Histogram of the image");
cristy787c09c2010-04-16 18:18:34 +0000106 entry->encoder=(EncodeImageHandler *) WriteHISTOGRAMImage;
dirk08e9a112015-02-22 01:51:41 +0000107 entry->flags^=CoderAdjoinFlag;
cristy009d7392010-07-25 22:08:41 +0000108 entry->format_type=ImplicitFormatType;
cristy787c09c2010-04-16 18:18:34 +0000109 (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,
cristy1e178e72011-08-28 19:44:34 +0000161% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000162%
163% A description of each parameter follows.
164%
cristy787c09c2010-04-16 18:18:34 +0000165% o image_info: the image info.
cristy3ed852e2009-09-05 21:47:34 +0000166%
cristy787c09c2010-04-16 18:18:34 +0000167% o image: The image.
cristy3ed852e2009-09-05 21:47:34 +0000168%
cristy1e178e72011-08-28 19:44:34 +0000169% o exception: return any errors or warnings in this structure.
170%
cristy3ed852e2009-09-05 21:47:34 +0000171*/
cristy787c09c2010-04-16 18:18:34 +0000172static MagickBooleanType WriteHISTOGRAMImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +0000173 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000174{
cristy31ac0f02011-03-12 02:04:47 +0000175#define HistogramDensity "256x200"
cristy3ed852e2009-09-05 21:47:34 +0000176
cristy1bd4a3a2010-04-16 12:38:47 +0000177 char
cristy151b66d2015-04-15 10:50:31 +0000178 filename[MagickPathExtent];
cristy1bd4a3a2010-04-16 12:38:47 +0000179
cristy6db52da2013-02-05 13:21:56 +0000180 const char
181 *option;
182
cristy787c09c2010-04-16 18:18:34 +0000183 Image
184 *histogram_image;
185
186 ImageInfo
187 *write_info;
188
cristy1bd4a3a2010-04-16 12:38:47 +0000189 MagickBooleanType
190 status;
191
cristy4c08aed2011-07-01 19:47:50 +0000192 PixelInfo
cristy787c09c2010-04-16 18:18:34 +0000193 *histogram;
cristy1bd4a3a2010-04-16 12:38:47 +0000194
cristya19f1d72012-08-07 18:24:38 +0000195 double
cristy787c09c2010-04-16 18:18:34 +0000196 maximum,
197 scale;
198
199 RectangleInfo
200 geometry;
201
cristy4c08aed2011-07-01 19:47:50 +0000202 register const Quantum
cristy1bd4a3a2010-04-16 12:38:47 +0000203 *p;
204
cristy4c08aed2011-07-01 19:47:50 +0000205 register Quantum
cristy787c09c2010-04-16 18:18:34 +0000206 *q,
207 *r;
cristy1bd4a3a2010-04-16 12:38:47 +0000208
cristy31ac0f02011-03-12 02:04:47 +0000209 register ssize_t
210 x;
211
cristy787c09c2010-04-16 18:18:34 +0000212 size_t
213 length;
cristy1bd4a3a2010-04-16 12:38:47 +0000214
cristy31ac0f02011-03-12 02:04:47 +0000215 ssize_t
216 y;
217
cristy1bd4a3a2010-04-16 12:38:47 +0000218 /*
cristy787c09c2010-04-16 18:18:34 +0000219 Allocate histogram image.
cristy1bd4a3a2010-04-16 12:38:47 +0000220 */
cristy787c09c2010-04-16 18:18:34 +0000221 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000222 assert(image_info->signature == MagickCoreSignature);
cristy787c09c2010-04-16 18:18:34 +0000223 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000224 assert(image->signature == MagickCoreSignature);
cristy787c09c2010-04-16 18:18:34 +0000225 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,
cristy1e178e72011-08-28 19:44:34 +0000234 exception);
cristy787c09c2010-04-16 18:18:34 +0000235 if (histogram_image == (Image *) NULL)
236 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy1e178e72011-08-28 19:44:34 +0000237 (void) SetImageStorageClass(histogram_image,DirectClass,exception);
cristy787c09c2010-04-16 18:18:34 +0000238 /*
239 Allocate histogram count arrays.
240 */
cristy6e963d82012-06-19 15:23:24 +0000241 length=MagickMax((size_t) ScaleQuantumToChar(QuantumRange)+1UL,
cristy787c09c2010-04-16 18:18:34 +0000242 histogram_image->columns);
cristy0553bd52013-06-30 15:53:50 +0000243 histogram=(PixelInfo *) AcquireQuantumMemory(length,sizeof(*histogram));
cristy4c08aed2011-07-01 19:47:50 +0000244 if (histogram == (PixelInfo *) NULL)
cristy1bd4a3a2010-04-16 12:38:47 +0000245 {
cristy787c09c2010-04-16 18:18:34 +0000246 histogram_image=DestroyImage(histogram_image);
247 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
248 }
249 /*
250 Initialize histogram count arrays.
251 */
cristy787c09c2010-04-16 18:18:34 +0000252 (void) ResetMagickMemory(histogram,0,length*sizeof(*histogram));
cristybb503372010-05-27 20:51:26 +0000253 for (y=0; y < (ssize_t) image->rows; y++)
cristy787c09c2010-04-16 18:18:34 +0000254 {
cristy1e178e72011-08-28 19:44:34 +0000255 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000256 if (p == (const Quantum *) NULL)
cristy787c09c2010-04-16 18:18:34 +0000257 break;
cristybb503372010-05-27 20:51:26 +0000258 for (x=0; x < (ssize_t) image->columns; x++)
cristy787c09c2010-04-16 18:18:34 +0000259 {
cristyed231572011-07-14 02:18:59 +0000260 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000261 histogram[ScaleQuantumToChar(GetPixelRed(image,p))].red++;
cristyed231572011-07-14 02:18:59 +0000262 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000263 histogram[ScaleQuantumToChar(GetPixelGreen(image,p))].green++;
cristyed231572011-07-14 02:18:59 +0000264 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000265 histogram[ScaleQuantumToChar(GetPixelBlue(image,p))].blue++;
cristyed231572011-07-14 02:18:59 +0000266 p+=GetPixelChannels(image);
cristy787c09c2010-04-16 18:18:34 +0000267 }
268 }
269 maximum=histogram[0].red;
cristybb503372010-05-27 20:51:26 +0000270 for (x=0; x < (ssize_t) histogram_image->columns; x++)
cristy787c09c2010-04-16 18:18:34 +0000271 {
cristyed231572011-07-14 02:18:59 +0000272 if (((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) &&
cristy2b9582a2011-07-04 17:38:56 +0000273 (maximum < histogram[x].red))
cristy787c09c2010-04-16 18:18:34 +0000274 maximum=histogram[x].red;
cristyed231572011-07-14 02:18:59 +0000275 if (((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) &&
cristy2b9582a2011-07-04 17:38:56 +0000276 (maximum < histogram[x].green))
cristy787c09c2010-04-16 18:18:34 +0000277 maximum=histogram[x].green;
cristyed231572011-07-14 02:18:59 +0000278 if (((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) &&
cristy2b9582a2011-07-04 17:38:56 +0000279 (maximum < histogram[x].blue))
cristy787c09c2010-04-16 18:18:34 +0000280 maximum=histogram[x].blue;
281 }
cristye6133492015-01-19 19:57:45 +0000282 scale=0.0;
283 if (fabs(maximum) >= MagickEpsilon)
284 scale=(double) histogram_image->rows/maximum;
cristy787c09c2010-04-16 18:18:34 +0000285 /*
286 Initialize histogram image.
287 */
cristyfad60c92012-01-19 18:32:39 +0000288 (void) QueryColorCompliance("#000000",AllCompliance,
cristy9950d572011-10-01 18:22:35 +0000289 &histogram_image->background_color,exception);
cristyea1a8aa2011-10-20 13:24:06 +0000290 (void) SetImageBackgroundColor(histogram_image,exception);
cristybb503372010-05-27 20:51:26 +0000291 for (x=0; x < (ssize_t) histogram_image->columns; x++)
cristy787c09c2010-04-16 18:18:34 +0000292 {
cristyfad60c92012-01-19 18:32:39 +0000293 q=GetAuthenticPixels(histogram_image,x,0,1,histogram_image->rows,exception);
cristyacd2ed22011-08-30 01:44:23 +0000294 if (q == (Quantum *) NULL)
cristy787c09c2010-04-16 18:18:34 +0000295 break;
cristyed231572011-07-14 02:18:59 +0000296 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy1bd4a3a2010-04-16 12:38:47 +0000297 {
cristybb503372010-05-27 20:51:26 +0000298 y=(ssize_t) ceil(histogram_image->rows-scale*histogram[x].red-0.5);
cristy6db52da2013-02-05 13:21:56 +0000299 r=q+y*GetPixelChannels(histogram_image);
cristybb503372010-05-27 20:51:26 +0000300 for ( ; y < (ssize_t) histogram_image->rows; y++)
cristy1bd4a3a2010-04-16 12:38:47 +0000301 {
cristy4c08aed2011-07-01 19:47:50 +0000302 SetPixelRed(histogram_image,QuantumRange,r);
cristy6db52da2013-02-05 13:21:56 +0000303 r+=GetPixelChannels(histogram_image);
cristy1bd4a3a2010-04-16 12:38:47 +0000304 }
cristy787c09c2010-04-16 18:18:34 +0000305 }
cristyed231572011-07-14 02:18:59 +0000306 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy787c09c2010-04-16 18:18:34 +0000307 {
cristybb503372010-05-27 20:51:26 +0000308 y=(ssize_t) ceil(histogram_image->rows-scale*histogram[x].green-0.5);
cristy6db52da2013-02-05 13:21:56 +0000309 r=q+y*GetPixelChannels(histogram_image);
cristybb503372010-05-27 20:51:26 +0000310 for ( ; y < (ssize_t) histogram_image->rows; y++)
cristy787c09c2010-04-16 18:18:34 +0000311 {
cristy4c08aed2011-07-01 19:47:50 +0000312 SetPixelGreen(histogram_image,QuantumRange,r);
cristy6db52da2013-02-05 13:21:56 +0000313 r+=GetPixelChannels(histogram_image);
cristy787c09c2010-04-16 18:18:34 +0000314 }
315 }
cristyed231572011-07-14 02:18:59 +0000316 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy787c09c2010-04-16 18:18:34 +0000317 {
cristybb503372010-05-27 20:51:26 +0000318 y=(ssize_t) ceil(histogram_image->rows-scale*histogram[x].blue-0.5);
cristy6db52da2013-02-05 13:21:56 +0000319 r=q+y*GetPixelChannels(histogram_image);
cristybb503372010-05-27 20:51:26 +0000320 for ( ; y < (ssize_t) histogram_image->rows; y++)
cristy787c09c2010-04-16 18:18:34 +0000321 {
cristy4c08aed2011-07-01 19:47:50 +0000322 SetPixelBlue(histogram_image,QuantumRange,r);
cristy6db52da2013-02-05 13:21:56 +0000323 r+=GetPixelChannels(histogram_image);
cristy787c09c2010-04-16 18:18:34 +0000324 }
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 }
cristy4c08aed2011-07-01 19:47:50 +0000332 histogram=(PixelInfo *) RelinquishMagickMemory(histogram);
cristy092ec8d2013-04-26 13:46:22 +0000333 option=GetImageOption(image_info,"histogram:unique-colors");
cristyd5f40a92013-02-05 14:07:59 +0000334 if ((option == (const char *) NULL) || (IsStringTrue(option) != MagickFalse))
cristy1bd4a3a2010-04-16 12:38:47 +0000335 {
cristy787c09c2010-04-16 18:18:34 +0000336 FILE
337 *file;
cristy1bd4a3a2010-04-16 12:38:47 +0000338
cristy787c09c2010-04-16 18:18:34 +0000339 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
cristy1e178e72011-08-28 19:44:34 +0000354 (void) GetNumberColors(image,file,exception);
cristy787c09c2010-04-16 18:18:34 +0000355 (void) fclose(file);
cristy1e178e72011-08-28 19:44:34 +0000356 property=FileToString(filename,~0UL,exception);
cristy787c09c2010-04-16 18:18:34 +0000357 if (property != (char *) NULL)
358 {
cristyd15e6592011-10-15 00:13:06 +0000359 (void) SetImageProperty(histogram_image,"comment",property,
360 exception);
cristy787c09c2010-04-16 18:18:34 +0000361 property=DestroyString(property);
362 }
363 }
364 (void) RelinquishUniqueFileResource(filename);
cristy1bd4a3a2010-04-16 12:38:47 +0000365 }
cristy787c09c2010-04-16 18:18:34 +0000366 /*
367 Write Histogram image.
368 */
369 (void) CopyMagickString(histogram_image->filename,image_info->filename,
cristy151b66d2015-04-15 10:50:31 +0000370 MagickPathExtent);
cristy787c09c2010-04-16 18:18:34 +0000371 write_info=CloneImageInfo(image_info);
Cristye6fb02d2015-09-27 10:47:45 -0400372 *write_info->magick='\0';
cristy1e178e72011-08-28 19:44:34 +0000373 (void) SetImageInfo(write_info,1,exception);
cristy787c09c2010-04-16 18:18:34 +0000374 if (LocaleCompare(write_info->magick,"HISTOGRAM") == 0)
Cristye6fb02d2015-09-27 10:47:45 -0400375 (void) FormatLocaleString(histogram_image->filename,MagickPathExtent,
376 "miff:%s",write_info->filename);
cristy1e178e72011-08-28 19:44:34 +0000377 status=WriteImage(write_info,histogram_image,exception);
cristy787c09c2010-04-16 18:18:34 +0000378 histogram_image=DestroyImage(histogram_image);
379 write_info=DestroyImageInfo(write_info);
380 return(status);
cristy1bd4a3a2010-04-16 12:38:47 +0000381}