blob: 619d80e7a4a94c876f31ceb8c07a14934fc70c3f [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
cristy1bd4a3a2010-04-16 12:38:47 +0000183 char
cristy787c09c2010-04-16 18:18:34 +0000184 filename[MaxTextExtent];
cristy1bd4a3a2010-04-16 12:38:47 +0000185
cristy787c09c2010-04-16 18:18:34 +0000186 const char
187 *option;
188
189 ExceptionInfo
190 *exception;
191
192 Image
193 *histogram_image;
194
195 ImageInfo
196 *write_info;
197
cristy1bd4a3a2010-04-16 12:38:47 +0000198 MagickBooleanType
199 status;
200
cristy4c08aed2011-07-01 19:47:50 +0000201 PixelInfo
cristy787c09c2010-04-16 18:18:34 +0000202 *histogram;
cristy1bd4a3a2010-04-16 12:38:47 +0000203
cristy787c09c2010-04-16 18:18:34 +0000204 MagickRealType
205 maximum,
206 scale;
207
208 RectangleInfo
209 geometry;
210
cristy4c08aed2011-07-01 19:47:50 +0000211 register const Quantum
cristy1bd4a3a2010-04-16 12:38:47 +0000212 *p;
213
cristy4c08aed2011-07-01 19:47:50 +0000214 register Quantum
cristy787c09c2010-04-16 18:18:34 +0000215 *q,
216 *r;
cristy1bd4a3a2010-04-16 12:38:47 +0000217
cristy31ac0f02011-03-12 02:04:47 +0000218 register ssize_t
219 x;
220
cristy787c09c2010-04-16 18:18:34 +0000221 size_t
222 length;
cristy1bd4a3a2010-04-16 12:38:47 +0000223
cristy31ac0f02011-03-12 02:04:47 +0000224 ssize_t
225 y;
226
cristy1bd4a3a2010-04-16 12:38:47 +0000227 /*
cristy787c09c2010-04-16 18:18:34 +0000228 Allocate histogram image.
cristy1bd4a3a2010-04-16 12:38:47 +0000229 */
cristy787c09c2010-04-16 18:18:34 +0000230 assert(image_info != (const ImageInfo *) NULL);
231 assert(image_info->signature == MagickSignature);
232 assert(image != (Image *) NULL);
233 assert(image->signature == MagickSignature);
234 if (image->debug != MagickFalse)
235 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
236 image_info->filename);
237 SetGeometry(image,&geometry);
238 if (image_info->density == (char *) NULL)
239 (void) ParseAbsoluteGeometry(HistogramDensity,&geometry);
240 else
241 (void) ParseAbsoluteGeometry(image_info->density,&geometry);
242 histogram_image=CloneImage(image,geometry.width,geometry.height,MagickTrue,
243 &image->exception);
244 if (histogram_image == (Image *) NULL)
245 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy574cc262011-08-05 01:23:58 +0000246 (void) SetImageStorageClass(histogram_image,DirectClass,&image->exception);
cristy787c09c2010-04-16 18:18:34 +0000247 /*
248 Allocate histogram count arrays.
249 */
250 length=MagickMax((size_t) ScaleQuantumToChar((Quantum) QuantumRange)+1UL,
251 histogram_image->columns);
cristy4c08aed2011-07-01 19:47:50 +0000252 histogram=(PixelInfo *) AcquireQuantumMemory(length,
cristy787c09c2010-04-16 18:18:34 +0000253 sizeof(*histogram));
cristy4c08aed2011-07-01 19:47:50 +0000254 if (histogram == (PixelInfo *) NULL)
cristy1bd4a3a2010-04-16 12:38:47 +0000255 {
cristy787c09c2010-04-16 18:18:34 +0000256 histogram_image=DestroyImage(histogram_image);
257 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
258 }
259 /*
260 Initialize histogram count arrays.
261 */
cristy787c09c2010-04-16 18:18:34 +0000262 (void) ResetMagickMemory(histogram,0,length*sizeof(*histogram));
cristybb503372010-05-27 20:51:26 +0000263 for (y=0; y < (ssize_t) image->rows; y++)
cristy787c09c2010-04-16 18:18:34 +0000264 {
265 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +0000266 if (p == (const Quantum *) NULL)
cristy787c09c2010-04-16 18:18:34 +0000267 break;
cristybb503372010-05-27 20:51:26 +0000268 for (x=0; x < (ssize_t) image->columns; x++)
cristy787c09c2010-04-16 18:18:34 +0000269 {
cristyed231572011-07-14 02:18:59 +0000270 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000271 histogram[ScaleQuantumToChar(GetPixelRed(image,p))].red++;
cristyed231572011-07-14 02:18:59 +0000272 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000273 histogram[ScaleQuantumToChar(GetPixelGreen(image,p))].green++;
cristyed231572011-07-14 02:18:59 +0000274 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000275 histogram[ScaleQuantumToChar(GetPixelBlue(image,p))].blue++;
cristyed231572011-07-14 02:18:59 +0000276 p+=GetPixelChannels(image);
cristy787c09c2010-04-16 18:18:34 +0000277 }
278 }
279 maximum=histogram[0].red;
cristybb503372010-05-27 20:51:26 +0000280 for (x=0; x < (ssize_t) histogram_image->columns; x++)
cristy787c09c2010-04-16 18:18:34 +0000281 {
cristyed231572011-07-14 02:18:59 +0000282 if (((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) &&
cristy2b9582a2011-07-04 17:38:56 +0000283 (maximum < histogram[x].red))
cristy787c09c2010-04-16 18:18:34 +0000284 maximum=histogram[x].red;
cristyed231572011-07-14 02:18:59 +0000285 if (((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) &&
cristy2b9582a2011-07-04 17:38:56 +0000286 (maximum < histogram[x].green))
cristy787c09c2010-04-16 18:18:34 +0000287 maximum=histogram[x].green;
cristyed231572011-07-14 02:18:59 +0000288 if (((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) &&
cristy2b9582a2011-07-04 17:38:56 +0000289 (maximum < histogram[x].blue))
cristy787c09c2010-04-16 18:18:34 +0000290 maximum=histogram[x].blue;
291 }
292 scale=(MagickRealType) histogram_image->rows/maximum;
293 /*
294 Initialize histogram image.
295 */
296 exception=(&image->exception);
297 (void) QueryColorDatabase("#000000",&histogram_image->background_color,
298 &image->exception);
299 (void) SetImageBackgroundColor(histogram_image);
cristybb503372010-05-27 20:51:26 +0000300 for (x=0; x < (ssize_t) histogram_image->columns; x++)
cristy787c09c2010-04-16 18:18:34 +0000301 {
302 q=GetAuthenticPixels(histogram_image,x,0,1,histogram_image->rows,exception);
cristy4c08aed2011-07-01 19:47:50 +0000303 if (q == (const Quantum *) NULL)
cristy787c09c2010-04-16 18:18:34 +0000304 break;
cristyed231572011-07-14 02:18:59 +0000305 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy1bd4a3a2010-04-16 12:38:47 +0000306 {
cristybb503372010-05-27 20:51:26 +0000307 y=(ssize_t) ceil(histogram_image->rows-scale*histogram[x].red-0.5);
cristy787c09c2010-04-16 18:18:34 +0000308 r=q+y;
cristybb503372010-05-27 20:51:26 +0000309 for ( ; y < (ssize_t) histogram_image->rows; y++)
cristy1bd4a3a2010-04-16 12:38:47 +0000310 {
cristy4c08aed2011-07-01 19:47:50 +0000311 SetPixelRed(histogram_image,QuantumRange,r);
cristy787c09c2010-04-16 18:18:34 +0000312 r++;
cristy1bd4a3a2010-04-16 12:38:47 +0000313 }
cristy787c09c2010-04-16 18:18:34 +0000314 }
cristyed231572011-07-14 02:18:59 +0000315 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy787c09c2010-04-16 18:18:34 +0000316 {
cristybb503372010-05-27 20:51:26 +0000317 y=(ssize_t) ceil(histogram_image->rows-scale*histogram[x].green-0.5);
cristy787c09c2010-04-16 18:18:34 +0000318 r=q+y;
cristybb503372010-05-27 20:51:26 +0000319 for ( ; y < (ssize_t) histogram_image->rows; y++)
cristy787c09c2010-04-16 18:18:34 +0000320 {
cristy4c08aed2011-07-01 19:47:50 +0000321 SetPixelGreen(histogram_image,QuantumRange,r);
cristy787c09c2010-04-16 18:18:34 +0000322 r++;
323 }
324 }
cristyed231572011-07-14 02:18:59 +0000325 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy787c09c2010-04-16 18:18:34 +0000326 {
cristybb503372010-05-27 20:51:26 +0000327 y=(ssize_t) ceil(histogram_image->rows-scale*histogram[x].blue-0.5);
cristy787c09c2010-04-16 18:18:34 +0000328 r=q+y;
cristybb503372010-05-27 20:51:26 +0000329 for ( ; y < (ssize_t) histogram_image->rows; y++)
cristy787c09c2010-04-16 18:18:34 +0000330 {
cristy4c08aed2011-07-01 19:47:50 +0000331 SetPixelBlue(histogram_image,QuantumRange,r);
cristy787c09c2010-04-16 18:18:34 +0000332 r++;
333 }
334 }
335 if (SyncAuthenticPixels(histogram_image,exception) == MagickFalse)
336 break;
337 status=SetImageProgress(image,SaveImageTag,y,histogram_image->rows);
338 if (status == MagickFalse)
339 break;
340 }
341 /*
342 Relinquish resources.
343 */
cristy4c08aed2011-07-01 19:47:50 +0000344 histogram=(PixelInfo *) RelinquishMagickMemory(histogram);
cristy787c09c2010-04-16 18:18:34 +0000345 option=GetImageOption(image_info,"histogram:unique-colors");
cristy62bd7a22010-04-22 01:27:07 +0000346 if ((option == (const char *) NULL) || (IsMagickTrue(option) != MagickFalse))
cristy1bd4a3a2010-04-16 12:38:47 +0000347 {
cristy787c09c2010-04-16 18:18:34 +0000348 FILE
349 *file;
cristy1bd4a3a2010-04-16 12:38:47 +0000350
cristy787c09c2010-04-16 18:18:34 +0000351 int
352 unique_file;
353
354 /*
355 Add a unique colors as an image comment.
356 */
357 file=(FILE *) NULL;
358 unique_file=AcquireUniqueFileResource(filename);
359 if (unique_file != -1)
360 file=fdopen(unique_file,"wb");
361 if ((unique_file != -1) && (file != (FILE *) NULL))
362 {
363 char
364 *property;
365
366 (void) GetNumberColors(image,file,&image->exception);
367 (void) fclose(file);
368 property=FileToString(filename,~0UL,&image->exception);
369 if (property != (char *) NULL)
370 {
371 (void) SetImageProperty(histogram_image,"comment",property);
372 property=DestroyString(property);
373 }
374 }
375 (void) RelinquishUniqueFileResource(filename);
cristy1bd4a3a2010-04-16 12:38:47 +0000376 }
cristy787c09c2010-04-16 18:18:34 +0000377 /*
378 Write Histogram image.
379 */
380 (void) CopyMagickString(histogram_image->filename,image_info->filename,
381 MaxTextExtent);
382 write_info=CloneImageInfo(image_info);
383 (void) SetImageInfo(write_info,1,&image->exception);
384 if (LocaleCompare(write_info->magick,"HISTOGRAM") == 0)
cristyb51dff52011-05-19 16:55:47 +0000385 (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
cristy787c09c2010-04-16 18:18:34 +0000386 "miff:%s",write_info->filename);
387 status=WriteImage(write_info,histogram_image);
388 histogram_image=DestroyImage(histogram_image);
389 write_info=DestroyImageInfo(write_info);
390 return(status);
cristy1bd4a3a2010-04-16 12:38:47 +0000391}