blob: 405e645bce761cf5fe868ec3c23d128a8e9fcab6 [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
cristy1e178e72011-08-28 19:44:34 +000073 WriteHISTOGRAMImage(const ImageInfo *,Image *,ExceptionInfo *);
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,
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*/
172
cristy787c09c2010-04-16 18:18:34 +0000173static inline size_t MagickMax(const size_t x,const size_t y)
cristy3ed852e2009-09-05 21:47:34 +0000174{
cristy787c09c2010-04-16 18:18:34 +0000175 if (x > y)
176 return(x);
177 return(y);
cristy3ed852e2009-09-05 21:47:34 +0000178}
179
cristy787c09c2010-04-16 18:18:34 +0000180static MagickBooleanType WriteHISTOGRAMImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +0000181 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000182{
cristy31ac0f02011-03-12 02:04:47 +0000183#define HistogramDensity "256x200"
cristy3ed852e2009-09-05 21:47:34 +0000184
cristy1bd4a3a2010-04-16 12:38:47 +0000185 char
cristy787c09c2010-04-16 18:18:34 +0000186 filename[MaxTextExtent];
cristy1bd4a3a2010-04-16 12:38:47 +0000187
cristy787c09c2010-04-16 18:18:34 +0000188 const char
189 *option;
190
cristy787c09c2010-04-16 18:18:34 +0000191 Image
192 *histogram_image;
193
194 ImageInfo
195 *write_info;
196
cristy1bd4a3a2010-04-16 12:38:47 +0000197 MagickBooleanType
198 status;
199
cristy4c08aed2011-07-01 19:47:50 +0000200 PixelInfo
cristy787c09c2010-04-16 18:18:34 +0000201 *histogram;
cristy1bd4a3a2010-04-16 12:38:47 +0000202
cristy787c09c2010-04-16 18:18:34 +0000203 MagickRealType
204 maximum,
205 scale;
206
207 RectangleInfo
208 geometry;
209
cristy4c08aed2011-07-01 19:47:50 +0000210 register const Quantum
cristy1bd4a3a2010-04-16 12:38:47 +0000211 *p;
212
cristy4c08aed2011-07-01 19:47:50 +0000213 register Quantum
cristy787c09c2010-04-16 18:18:34 +0000214 *q,
215 *r;
cristy1bd4a3a2010-04-16 12:38:47 +0000216
cristy31ac0f02011-03-12 02:04:47 +0000217 register ssize_t
218 x;
219
cristy787c09c2010-04-16 18:18:34 +0000220 size_t
221 length;
cristy1bd4a3a2010-04-16 12:38:47 +0000222
cristy31ac0f02011-03-12 02:04:47 +0000223 ssize_t
224 y;
225
cristy1bd4a3a2010-04-16 12:38:47 +0000226 /*
cristy787c09c2010-04-16 18:18:34 +0000227 Allocate histogram image.
cristy1bd4a3a2010-04-16 12:38:47 +0000228 */
cristy787c09c2010-04-16 18:18:34 +0000229 assert(image_info != (const ImageInfo *) NULL);
230 assert(image_info->signature == MagickSignature);
231 assert(image != (Image *) NULL);
232 assert(image->signature == MagickSignature);
233 if (image->debug != MagickFalse)
234 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
235 image_info->filename);
236 SetGeometry(image,&geometry);
237 if (image_info->density == (char *) NULL)
238 (void) ParseAbsoluteGeometry(HistogramDensity,&geometry);
239 else
240 (void) ParseAbsoluteGeometry(image_info->density,&geometry);
241 histogram_image=CloneImage(image,geometry.width,geometry.height,MagickTrue,
cristy1e178e72011-08-28 19:44:34 +0000242 exception);
cristy787c09c2010-04-16 18:18:34 +0000243 if (histogram_image == (Image *) NULL)
244 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy1e178e72011-08-28 19:44:34 +0000245 (void) SetImageStorageClass(histogram_image,DirectClass,exception);
cristy787c09c2010-04-16 18:18:34 +0000246 /*
247 Allocate histogram count arrays.
248 */
249 length=MagickMax((size_t) ScaleQuantumToChar((Quantum) QuantumRange)+1UL,
250 histogram_image->columns);
cristy4c08aed2011-07-01 19:47:50 +0000251 histogram=(PixelInfo *) AcquireQuantumMemory(length,
cristy787c09c2010-04-16 18:18:34 +0000252 sizeof(*histogram));
cristy4c08aed2011-07-01 19:47:50 +0000253 if (histogram == (PixelInfo *) NULL)
cristy1bd4a3a2010-04-16 12:38:47 +0000254 {
cristy787c09c2010-04-16 18:18:34 +0000255 histogram_image=DestroyImage(histogram_image);
256 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
257 }
258 /*
259 Initialize histogram count arrays.
260 */
cristy787c09c2010-04-16 18:18:34 +0000261 (void) ResetMagickMemory(histogram,0,length*sizeof(*histogram));
cristybb503372010-05-27 20:51:26 +0000262 for (y=0; y < (ssize_t) image->rows; y++)
cristy787c09c2010-04-16 18:18:34 +0000263 {
cristy1e178e72011-08-28 19:44:34 +0000264 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000265 if (p == (const Quantum *) NULL)
cristy787c09c2010-04-16 18:18:34 +0000266 break;
cristybb503372010-05-27 20:51:26 +0000267 for (x=0; x < (ssize_t) image->columns; x++)
cristy787c09c2010-04-16 18:18:34 +0000268 {
cristyed231572011-07-14 02:18:59 +0000269 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000270 histogram[ScaleQuantumToChar(GetPixelRed(image,p))].red++;
cristyed231572011-07-14 02:18:59 +0000271 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000272 histogram[ScaleQuantumToChar(GetPixelGreen(image,p))].green++;
cristyed231572011-07-14 02:18:59 +0000273 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000274 histogram[ScaleQuantumToChar(GetPixelBlue(image,p))].blue++;
cristyed231572011-07-14 02:18:59 +0000275 p+=GetPixelChannels(image);
cristy787c09c2010-04-16 18:18:34 +0000276 }
277 }
278 maximum=histogram[0].red;
cristybb503372010-05-27 20:51:26 +0000279 for (x=0; x < (ssize_t) histogram_image->columns; x++)
cristy787c09c2010-04-16 18:18:34 +0000280 {
cristyed231572011-07-14 02:18:59 +0000281 if (((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) &&
cristy2b9582a2011-07-04 17:38:56 +0000282 (maximum < histogram[x].red))
cristy787c09c2010-04-16 18:18:34 +0000283 maximum=histogram[x].red;
cristyed231572011-07-14 02:18:59 +0000284 if (((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) &&
cristy2b9582a2011-07-04 17:38:56 +0000285 (maximum < histogram[x].green))
cristy787c09c2010-04-16 18:18:34 +0000286 maximum=histogram[x].green;
cristyed231572011-07-14 02:18:59 +0000287 if (((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) &&
cristy2b9582a2011-07-04 17:38:56 +0000288 (maximum < histogram[x].blue))
cristy787c09c2010-04-16 18:18:34 +0000289 maximum=histogram[x].blue;
290 }
291 scale=(MagickRealType) histogram_image->rows/maximum;
292 /*
293 Initialize histogram image.
294 */
cristy787c09c2010-04-16 18:18:34 +0000295 (void) QueryColorDatabase("#000000",&histogram_image->background_color,
cristy1e178e72011-08-28 19:44:34 +0000296 exception);
cristy787c09c2010-04-16 18:18:34 +0000297 (void) SetImageBackgroundColor(histogram_image);
cristybb503372010-05-27 20:51:26 +0000298 for (x=0; x < (ssize_t) histogram_image->columns; x++)
cristy787c09c2010-04-16 18:18:34 +0000299 {
300 q=GetAuthenticPixels(histogram_image,x,0,1,histogram_image->rows,exception);
cristyacd2ed22011-08-30 01:44:23 +0000301 if (q == (Quantum *) NULL)
cristy787c09c2010-04-16 18:18:34 +0000302 break;
cristyed231572011-07-14 02:18:59 +0000303 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy1bd4a3a2010-04-16 12:38:47 +0000304 {
cristybb503372010-05-27 20:51:26 +0000305 y=(ssize_t) ceil(histogram_image->rows-scale*histogram[x].red-0.5);
cristy787c09c2010-04-16 18:18:34 +0000306 r=q+y;
cristybb503372010-05-27 20:51:26 +0000307 for ( ; y < (ssize_t) histogram_image->rows; y++)
cristy1bd4a3a2010-04-16 12:38:47 +0000308 {
cristy4c08aed2011-07-01 19:47:50 +0000309 SetPixelRed(histogram_image,QuantumRange,r);
cristy787c09c2010-04-16 18:18:34 +0000310 r++;
cristy1bd4a3a2010-04-16 12:38:47 +0000311 }
cristy787c09c2010-04-16 18:18:34 +0000312 }
cristyed231572011-07-14 02:18:59 +0000313 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy787c09c2010-04-16 18:18:34 +0000314 {
cristybb503372010-05-27 20:51:26 +0000315 y=(ssize_t) ceil(histogram_image->rows-scale*histogram[x].green-0.5);
cristy787c09c2010-04-16 18:18:34 +0000316 r=q+y;
cristybb503372010-05-27 20:51:26 +0000317 for ( ; y < (ssize_t) histogram_image->rows; y++)
cristy787c09c2010-04-16 18:18:34 +0000318 {
cristy4c08aed2011-07-01 19:47:50 +0000319 SetPixelGreen(histogram_image,QuantumRange,r);
cristy787c09c2010-04-16 18:18:34 +0000320 r++;
321 }
322 }
cristyed231572011-07-14 02:18:59 +0000323 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy787c09c2010-04-16 18:18:34 +0000324 {
cristybb503372010-05-27 20:51:26 +0000325 y=(ssize_t) ceil(histogram_image->rows-scale*histogram[x].blue-0.5);
cristy787c09c2010-04-16 18:18:34 +0000326 r=q+y;
cristybb503372010-05-27 20:51:26 +0000327 for ( ; y < (ssize_t) histogram_image->rows; y++)
cristy787c09c2010-04-16 18:18:34 +0000328 {
cristy4c08aed2011-07-01 19:47:50 +0000329 SetPixelBlue(histogram_image,QuantumRange,r);
cristy787c09c2010-04-16 18:18:34 +0000330 r++;
331 }
332 }
333 if (SyncAuthenticPixels(histogram_image,exception) == MagickFalse)
334 break;
335 status=SetImageProgress(image,SaveImageTag,y,histogram_image->rows);
336 if (status == MagickFalse)
337 break;
338 }
339 /*
340 Relinquish resources.
341 */
cristy4c08aed2011-07-01 19:47:50 +0000342 histogram=(PixelInfo *) RelinquishMagickMemory(histogram);
cristy787c09c2010-04-16 18:18:34 +0000343 option=GetImageOption(image_info,"histogram:unique-colors");
cristy62bd7a22010-04-22 01:27:07 +0000344 if ((option == (const char *) NULL) || (IsMagickTrue(option) != MagickFalse))
cristy1bd4a3a2010-04-16 12:38:47 +0000345 {
cristy787c09c2010-04-16 18:18:34 +0000346 FILE
347 *file;
cristy1bd4a3a2010-04-16 12:38:47 +0000348
cristy787c09c2010-04-16 18:18:34 +0000349 int
350 unique_file;
351
352 /*
353 Add a unique colors as an image comment.
354 */
355 file=(FILE *) NULL;
356 unique_file=AcquireUniqueFileResource(filename);
357 if (unique_file != -1)
358 file=fdopen(unique_file,"wb");
359 if ((unique_file != -1) && (file != (FILE *) NULL))
360 {
361 char
362 *property;
363
cristy1e178e72011-08-28 19:44:34 +0000364 (void) GetNumberColors(image,file,exception);
cristy787c09c2010-04-16 18:18:34 +0000365 (void) fclose(file);
cristy1e178e72011-08-28 19:44:34 +0000366 property=FileToString(filename,~0UL,exception);
cristy787c09c2010-04-16 18:18:34 +0000367 if (property != (char *) NULL)
368 {
369 (void) SetImageProperty(histogram_image,"comment",property);
370 property=DestroyString(property);
371 }
372 }
373 (void) RelinquishUniqueFileResource(filename);
cristy1bd4a3a2010-04-16 12:38:47 +0000374 }
cristy787c09c2010-04-16 18:18:34 +0000375 /*
376 Write Histogram image.
377 */
378 (void) CopyMagickString(histogram_image->filename,image_info->filename,
379 MaxTextExtent);
380 write_info=CloneImageInfo(image_info);
cristy1e178e72011-08-28 19:44:34 +0000381 (void) SetImageInfo(write_info,1,exception);
cristy787c09c2010-04-16 18:18:34 +0000382 if (LocaleCompare(write_info->magick,"HISTOGRAM") == 0)
cristyb51dff52011-05-19 16:55:47 +0000383 (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
cristy787c09c2010-04-16 18:18:34 +0000384 "miff:%s",write_info->filename);
cristy1e178e72011-08-28 19:44:34 +0000385 status=WriteImage(write_info,histogram_image,exception);
cristy787c09c2010-04-16 18:18:34 +0000386 histogram_image=DestroyImage(histogram_image);
387 write_info=DestroyImageInfo(write_info);
388 return(status);
cristy1bd4a3a2010-04-16 12:38:47 +0000389}