blob: 7e0f11bf6472a15675a6aa899eb214ca1300c847 [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 %
cristy787c09c2010-04-16 18:18:34 +000016% John Cristy %
17% July 1992 %
cristy3ed852e2009-09-05 21:47:34 +000018% %
19% %
cristy7e41fe82010-12-04 23:12:08 +000020% Copyright 1999-2011 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"
43#include "MagickCore/property.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/quantum-private.h"
62#include "MagickCore/resource_.h"
63#include "MagickCore/static.h"
64#include "MagickCore/statistic.h"
65#include "MagickCore/string_.h"
66#include "MagickCore/module.h"
67#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000068
69/*
70 Forward declarations.
71*/
cristy787c09c2010-04-16 18:18:34 +000072static MagickBooleanType
73 WriteHISTOGRAMImage(const ImageInfo *,Image *);
cristy3ed852e2009-09-05 21:47:34 +000074
75/*
76%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
77% %
78% %
79% %
cristy787c09c2010-04-16 18:18:34 +000080% 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 +000081% %
82% %
83% %
84%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
85%
cristy787c09c2010-04-16 18:18:34 +000086% RegisterHISTOGRAMImage() adds attributes for the Histogram image format
87% to the list of supported formats. The attributes include the image format
88% tag, a method to read and/or write the format, whether the format
89% supports the saving of more than one frame to the same file or blob,
90% whether the format supports native in-memory I/O, and a brief
91% description of the format.
cristy3ed852e2009-09-05 21:47:34 +000092%
cristy787c09c2010-04-16 18:18:34 +000093% The format of the RegisterHISTOGRAMImage method is:
cristy3ed852e2009-09-05 21:47:34 +000094%
cristybb503372010-05-27 20:51:26 +000095% size_t RegisterHISTOGRAMImage(void)
cristy787c09c2010-04-16 18:18:34 +000096%
97*/
cristybb503372010-05-27 20:51:26 +000098ModuleExport size_t RegisterHISTOGRAMImage(void)
cristy787c09c2010-04-16 18:18:34 +000099{
100 MagickInfo
101 *entry;
102
103 entry=SetMagickInfo("HISTOGRAM");
104 entry->encoder=(EncodeImageHandler *) WriteHISTOGRAMImage;
105 entry->adjoin=MagickFalse;
cristy009d7392010-07-25 22:08:41 +0000106 entry->format_type=ImplicitFormatType;
cristy787c09c2010-04-16 18:18:34 +0000107 entry->description=ConstantString("Histogram of the image");
108 entry->module=ConstantString("HISTOGRAM");
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)
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%
169*/
170
cristy787c09c2010-04-16 18:18:34 +0000171static inline size_t MagickMax(const size_t x,const size_t y)
cristy3ed852e2009-09-05 21:47:34 +0000172{
cristy787c09c2010-04-16 18:18:34 +0000173 if (x > y)
174 return(x);
175 return(y);
cristy3ed852e2009-09-05 21:47:34 +0000176}
177
cristy787c09c2010-04-16 18:18:34 +0000178static MagickBooleanType WriteHISTOGRAMImage(const ImageInfo *image_info,
179 Image *image)
cristy3ed852e2009-09-05 21:47:34 +0000180{
cristy31ac0f02011-03-12 02:04:47 +0000181#define HistogramDensity "256x200"
cristy3ed852e2009-09-05 21:47:34 +0000182
cristy787c09c2010-04-16 18:18:34 +0000183 ChannelType
184 channel;
cristy1bd4a3a2010-04-16 12:38:47 +0000185
186 char
cristy787c09c2010-04-16 18:18:34 +0000187 filename[MaxTextExtent];
cristy1bd4a3a2010-04-16 12:38:47 +0000188
cristy787c09c2010-04-16 18:18:34 +0000189 const char
190 *option;
191
192 ExceptionInfo
193 *exception;
194
195 Image
196 *histogram_image;
197
198 ImageInfo
199 *write_info;
200
cristy1bd4a3a2010-04-16 12:38:47 +0000201 MagickBooleanType
202 status;
203
cristy4c08aed2011-07-01 19:47:50 +0000204 PixelInfo
cristy787c09c2010-04-16 18:18:34 +0000205 *histogram;
cristy1bd4a3a2010-04-16 12:38:47 +0000206
cristy787c09c2010-04-16 18:18:34 +0000207 MagickRealType
208 maximum,
209 scale;
210
211 RectangleInfo
212 geometry;
213
cristy4c08aed2011-07-01 19:47:50 +0000214 register const Quantum
cristy1bd4a3a2010-04-16 12:38:47 +0000215 *p;
216
cristy4c08aed2011-07-01 19:47:50 +0000217 register Quantum
cristy787c09c2010-04-16 18:18:34 +0000218 *q,
219 *r;
cristy1bd4a3a2010-04-16 12:38:47 +0000220
cristy31ac0f02011-03-12 02:04:47 +0000221 register ssize_t
222 x;
223
cristy787c09c2010-04-16 18:18:34 +0000224 size_t
225 length;
cristy1bd4a3a2010-04-16 12:38:47 +0000226
cristy31ac0f02011-03-12 02:04:47 +0000227 ssize_t
228 y;
229
cristy1bd4a3a2010-04-16 12:38:47 +0000230 /*
cristy787c09c2010-04-16 18:18:34 +0000231 Allocate histogram image.
cristy1bd4a3a2010-04-16 12:38:47 +0000232 */
cristy787c09c2010-04-16 18:18:34 +0000233 assert(image_info != (const ImageInfo *) NULL);
234 assert(image_info->signature == MagickSignature);
235 assert(image != (Image *) NULL);
236 assert(image->signature == MagickSignature);
237 if (image->debug != MagickFalse)
238 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
239 image_info->filename);
240 SetGeometry(image,&geometry);
241 if (image_info->density == (char *) NULL)
242 (void) ParseAbsoluteGeometry(HistogramDensity,&geometry);
243 else
244 (void) ParseAbsoluteGeometry(image_info->density,&geometry);
245 histogram_image=CloneImage(image,geometry.width,geometry.height,MagickTrue,
246 &image->exception);
247 if (histogram_image == (Image *) NULL)
248 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
249 (void) SetImageStorageClass(histogram_image,DirectClass);
250 /*
251 Allocate histogram count arrays.
252 */
253 length=MagickMax((size_t) ScaleQuantumToChar((Quantum) QuantumRange)+1UL,
254 histogram_image->columns);
cristy4c08aed2011-07-01 19:47:50 +0000255 histogram=(PixelInfo *) AcquireQuantumMemory(length,
cristy787c09c2010-04-16 18:18:34 +0000256 sizeof(*histogram));
cristy4c08aed2011-07-01 19:47:50 +0000257 if (histogram == (PixelInfo *) NULL)
cristy1bd4a3a2010-04-16 12:38:47 +0000258 {
cristy787c09c2010-04-16 18:18:34 +0000259 histogram_image=DestroyImage(histogram_image);
260 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
261 }
262 /*
263 Initialize histogram count arrays.
264 */
265 channel=image_info->channel;
266 (void) ResetMagickMemory(histogram,0,length*sizeof(*histogram));
cristybb503372010-05-27 20:51:26 +0000267 for (y=0; y < (ssize_t) image->rows; y++)
cristy787c09c2010-04-16 18:18:34 +0000268 {
269 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +0000270 if (p == (const Quantum *) NULL)
cristy787c09c2010-04-16 18:18:34 +0000271 break;
cristybb503372010-05-27 20:51:26 +0000272 for (x=0; x < (ssize_t) image->columns; x++)
cristy787c09c2010-04-16 18:18:34 +0000273 {
cristy2b9582a2011-07-04 17:38:56 +0000274 if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000275 histogram[ScaleQuantumToChar(GetPixelRed(image,p))].red++;
cristy2b9582a2011-07-04 17:38:56 +0000276 if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000277 histogram[ScaleQuantumToChar(GetPixelGreen(image,p))].green++;
cristy2b9582a2011-07-04 17:38:56 +0000278 if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000279 histogram[ScaleQuantumToChar(GetPixelBlue(image,p))].blue++;
cristydcfc1ad2011-07-07 16:25:41 +0000280 p+=GetPixelComponents(image);
cristy787c09c2010-04-16 18:18:34 +0000281 }
282 }
283 maximum=histogram[0].red;
cristybb503372010-05-27 20:51:26 +0000284 for (x=0; x < (ssize_t) histogram_image->columns; x++)
cristy787c09c2010-04-16 18:18:34 +0000285 {
cristy2b9582a2011-07-04 17:38:56 +0000286 if (((GetPixelRedTraits(image) & ActivePixelTrait) != 0) &&
287 (maximum < histogram[x].red))
cristy787c09c2010-04-16 18:18:34 +0000288 maximum=histogram[x].red;
cristy2b9582a2011-07-04 17:38:56 +0000289 if (((GetPixelGreenTraits(image) & ActivePixelTrait) != 0) &&
290 (maximum < histogram[x].green))
cristy787c09c2010-04-16 18:18:34 +0000291 maximum=histogram[x].green;
cristy2b9582a2011-07-04 17:38:56 +0000292 if (((GetPixelBlueTraits(image) & ActivePixelTrait) != 0) &&
293 (maximum < histogram[x].blue))
cristy787c09c2010-04-16 18:18:34 +0000294 maximum=histogram[x].blue;
295 }
296 scale=(MagickRealType) histogram_image->rows/maximum;
297 /*
298 Initialize histogram image.
299 */
300 exception=(&image->exception);
301 (void) QueryColorDatabase("#000000",&histogram_image->background_color,
302 &image->exception);
303 (void) SetImageBackgroundColor(histogram_image);
cristybb503372010-05-27 20:51:26 +0000304 for (x=0; x < (ssize_t) histogram_image->columns; x++)
cristy787c09c2010-04-16 18:18:34 +0000305 {
306 q=GetAuthenticPixels(histogram_image,x,0,1,histogram_image->rows,exception);
cristy4c08aed2011-07-01 19:47:50 +0000307 if (q == (const Quantum *) NULL)
cristy787c09c2010-04-16 18:18:34 +0000308 break;
cristy2b9582a2011-07-04 17:38:56 +0000309 if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
cristy1bd4a3a2010-04-16 12:38:47 +0000310 {
cristybb503372010-05-27 20:51:26 +0000311 y=(ssize_t) ceil(histogram_image->rows-scale*histogram[x].red-0.5);
cristy787c09c2010-04-16 18:18:34 +0000312 r=q+y;
cristybb503372010-05-27 20:51:26 +0000313 for ( ; y < (ssize_t) histogram_image->rows; y++)
cristy1bd4a3a2010-04-16 12:38:47 +0000314 {
cristy4c08aed2011-07-01 19:47:50 +0000315 SetPixelRed(histogram_image,QuantumRange,r);
cristy787c09c2010-04-16 18:18:34 +0000316 r++;
cristy1bd4a3a2010-04-16 12:38:47 +0000317 }
cristy787c09c2010-04-16 18:18:34 +0000318 }
cristy2b9582a2011-07-04 17:38:56 +0000319 if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
cristy787c09c2010-04-16 18:18:34 +0000320 {
cristybb503372010-05-27 20:51:26 +0000321 y=(ssize_t) ceil(histogram_image->rows-scale*histogram[x].green-0.5);
cristy787c09c2010-04-16 18:18:34 +0000322 r=q+y;
cristybb503372010-05-27 20:51:26 +0000323 for ( ; y < (ssize_t) histogram_image->rows; y++)
cristy787c09c2010-04-16 18:18:34 +0000324 {
cristy4c08aed2011-07-01 19:47:50 +0000325 SetPixelGreen(histogram_image,QuantumRange,r);
cristy787c09c2010-04-16 18:18:34 +0000326 r++;
327 }
328 }
cristy2b9582a2011-07-04 17:38:56 +0000329 if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
cristy787c09c2010-04-16 18:18:34 +0000330 {
cristybb503372010-05-27 20:51:26 +0000331 y=(ssize_t) ceil(histogram_image->rows-scale*histogram[x].blue-0.5);
cristy787c09c2010-04-16 18:18:34 +0000332 r=q+y;
cristybb503372010-05-27 20:51:26 +0000333 for ( ; y < (ssize_t) histogram_image->rows; y++)
cristy787c09c2010-04-16 18:18:34 +0000334 {
cristy4c08aed2011-07-01 19:47:50 +0000335 SetPixelBlue(histogram_image,QuantumRange,r);
cristy787c09c2010-04-16 18:18:34 +0000336 r++;
337 }
338 }
339 if (SyncAuthenticPixels(histogram_image,exception) == MagickFalse)
340 break;
341 status=SetImageProgress(image,SaveImageTag,y,histogram_image->rows);
342 if (status == MagickFalse)
343 break;
344 }
345 /*
346 Relinquish resources.
347 */
cristy4c08aed2011-07-01 19:47:50 +0000348 histogram=(PixelInfo *) RelinquishMagickMemory(histogram);
cristy787c09c2010-04-16 18:18:34 +0000349 option=GetImageOption(image_info,"histogram:unique-colors");
cristy62bd7a22010-04-22 01:27:07 +0000350 if ((option == (const char *) NULL) || (IsMagickTrue(option) != MagickFalse))
cristy1bd4a3a2010-04-16 12:38:47 +0000351 {
cristy787c09c2010-04-16 18:18:34 +0000352 FILE
353 *file;
cristy1bd4a3a2010-04-16 12:38:47 +0000354
cristy787c09c2010-04-16 18:18:34 +0000355 int
356 unique_file;
357
358 /*
359 Add a unique colors as an image comment.
360 */
361 file=(FILE *) NULL;
362 unique_file=AcquireUniqueFileResource(filename);
363 if (unique_file != -1)
364 file=fdopen(unique_file,"wb");
365 if ((unique_file != -1) && (file != (FILE *) NULL))
366 {
367 char
368 *property;
369
370 (void) GetNumberColors(image,file,&image->exception);
371 (void) fclose(file);
372 property=FileToString(filename,~0UL,&image->exception);
373 if (property != (char *) NULL)
374 {
375 (void) SetImageProperty(histogram_image,"comment",property);
376 property=DestroyString(property);
377 }
378 }
379 (void) RelinquishUniqueFileResource(filename);
cristy1bd4a3a2010-04-16 12:38:47 +0000380 }
cristy787c09c2010-04-16 18:18:34 +0000381 /*
382 Write Histogram image.
383 */
384 (void) CopyMagickString(histogram_image->filename,image_info->filename,
385 MaxTextExtent);
386 write_info=CloneImageInfo(image_info);
387 (void) SetImageInfo(write_info,1,&image->exception);
388 if (LocaleCompare(write_info->magick,"HISTOGRAM") == 0)
cristyb51dff52011-05-19 16:55:47 +0000389 (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
cristy787c09c2010-04-16 18:18:34 +0000390 "miff:%s",write_info->filename);
391 status=WriteImage(write_info,histogram_image);
392 histogram_image=DestroyImage(histogram_image);
393 write_info=DestroyImageInfo(write_info);
394 return(status);
cristy1bd4a3a2010-04-16 12:38:47 +0000395}