blob: 95ad0a3c416c7e561189be0058d95d237f1d2260 [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*/
42#include "magick/studio.h"
cristy787c09c2010-04-16 18:18:34 +000043#include "magick/property.h"
44#include "magick/blob.h"
45#include "magick/blob-private.h"
46#include "magick/cache.h"
47#include "magick/color.h"
cristy3ed852e2009-09-05 21:47:34 +000048#include "magick/color-private.h"
cristy787c09c2010-04-16 18:18:34 +000049#include "magick/constitute.h"
cristy3ed852e2009-09-05 21:47:34 +000050#include "magick/exception.h"
51#include "magick/exception-private.h"
cristy787c09c2010-04-16 18:18:34 +000052#include "magick/geometry.h"
cristyf2e11662009-10-14 01:24:43 +000053#include "magick/histogram.h"
cristy787c09c2010-04-16 18:18:34 +000054#include "magick/image-private.h"
55#include "magick/magick.h"
cristy3ed852e2009-09-05 21:47:34 +000056#include "magick/memory_.h"
cristy787c09c2010-04-16 18:18:34 +000057#include "magick/monitor.h"
cristy3ed852e2009-09-05 21:47:34 +000058#include "magick/monitor-private.h"
cristy1bd4a3a2010-04-16 12:38:47 +000059#include "magick/option.h"
cristy787c09c2010-04-16 18:18:34 +000060#include "magick/resource_.h"
61#include "magick/quantum-private.h"
62#include "magick/static.h"
cristy3ed852e2009-09-05 21:47:34 +000063#include "magick/statistic.h"
64#include "magick/string_.h"
cristy787c09c2010-04-16 18:18:34 +000065#include "magick/module.h"
cristy642ba1f2010-04-16 18:20:33 +000066#include "magick/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000067
68/*
69 Forward declarations.
70*/
cristy787c09c2010-04-16 18:18:34 +000071static MagickBooleanType
72 WriteHISTOGRAMImage(const ImageInfo *,Image *);
cristy3ed852e2009-09-05 21:47:34 +000073
74/*
75%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
76% %
77% %
78% %
cristy787c09c2010-04-16 18:18:34 +000079% 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 +000080% %
81% %
82% %
83%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
84%
cristy787c09c2010-04-16 18:18:34 +000085% RegisterHISTOGRAMImage() adds attributes for the Histogram image format
86% to the list of supported formats. The attributes include the image format
87% tag, a method to read and/or write the format, whether the format
88% supports the saving of more than one frame to the same file or blob,
89% whether the format supports native in-memory I/O, and a brief
90% description of the format.
cristy3ed852e2009-09-05 21:47:34 +000091%
cristy787c09c2010-04-16 18:18:34 +000092% The format of the RegisterHISTOGRAMImage method is:
cristy3ed852e2009-09-05 21:47:34 +000093%
cristybb503372010-05-27 20:51:26 +000094% size_t RegisterHISTOGRAMImage(void)
cristy787c09c2010-04-16 18:18:34 +000095%
96*/
cristybb503372010-05-27 20:51:26 +000097ModuleExport size_t RegisterHISTOGRAMImage(void)
cristy787c09c2010-04-16 18:18:34 +000098{
99 MagickInfo
100 *entry;
101
102 entry=SetMagickInfo("HISTOGRAM");
103 entry->encoder=(EncodeImageHandler *) WriteHISTOGRAMImage;
104 entry->adjoin=MagickFalse;
cristy009d7392010-07-25 22:08:41 +0000105 entry->format_type=ImplicitFormatType;
cristy787c09c2010-04-16 18:18:34 +0000106 entry->description=ConstantString("Histogram of the image");
107 entry->module=ConstantString("HISTOGRAM");
108 (void) RegisterMagickInfo(entry);
109 return(MagickImageCoderSignature);
110}
111
112/*
113%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
114% %
115% %
116% %
117% U n r e g i s t e r H I S T O G R A M I m a g e %
118% %
119% %
120% %
121%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
122%
123% UnregisterHISTOGRAMImage() removes format registrations made by the
124% HISTOGRAM module from the list of supported formats.
125%
126% The format of the UnregisterHISTOGRAMImage method is:
127%
128% UnregisterHISTOGRAMImage(void)
129%
130*/
131ModuleExport void UnregisterHISTOGRAMImage(void)
132{
133 (void) UnregisterMagickInfo("HISTOGRAM");
134}
135
136/*
137%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
138% %
139% %
140% %
141% W r i t e H I S T O G R A M I m a g e %
142% %
143% %
144% %
145%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
146%
147% WriteHISTOGRAMImage() writes an image to a file in Histogram format.
148% The image shows a histogram of the color (or gray) values in the image. The
149% image consists of three overlaid histograms: a red one for the red channel,
150% a green one for the green channel, and a blue one for the blue channel. The
151% image comment contains a list of unique pixel values and the number of times
152% each occurs in the image.
153%
154% This method is strongly based on a similar one written by
155% muquit@warm.semcor.com which in turn is based on ppmhistmap of netpbm.
156%
157% The format of the WriteHISTOGRAMImage method is:
158%
159% MagickBooleanType WriteHISTOGRAMImage(const ImageInfo *image_info,
160% Image *image)
cristy3ed852e2009-09-05 21:47:34 +0000161%
162% A description of each parameter follows.
163%
cristy787c09c2010-04-16 18:18:34 +0000164% o image_info: the image info.
cristy3ed852e2009-09-05 21:47:34 +0000165%
cristy787c09c2010-04-16 18:18:34 +0000166% o image: The image.
cristy3ed852e2009-09-05 21:47:34 +0000167%
168*/
169
cristy787c09c2010-04-16 18:18:34 +0000170static inline size_t MagickMax(const size_t x,const size_t y)
cristy3ed852e2009-09-05 21:47:34 +0000171{
cristy787c09c2010-04-16 18:18:34 +0000172 if (x > y)
173 return(x);
174 return(y);
cristy3ed852e2009-09-05 21:47:34 +0000175}
176
cristy787c09c2010-04-16 18:18:34 +0000177static MagickBooleanType WriteHISTOGRAMImage(const ImageInfo *image_info,
178 Image *image)
cristy3ed852e2009-09-05 21:47:34 +0000179{
cristy31ac0f02011-03-12 02:04:47 +0000180#define HistogramDensity "256x200"
cristy3ed852e2009-09-05 21:47:34 +0000181
cristy787c09c2010-04-16 18:18:34 +0000182 ChannelType
183 channel;
cristy1bd4a3a2010-04-16 12:38:47 +0000184
185 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
191 ExceptionInfo
192 *exception;
193
194 Image
195 *histogram_image;
196
197 ImageInfo
198 *write_info;
199
cristy1bd4a3a2010-04-16 12:38:47 +0000200 MagickBooleanType
201 status;
202
203 MagickPixelPacket
cristy787c09c2010-04-16 18:18:34 +0000204 *histogram;
cristy1bd4a3a2010-04-16 12:38:47 +0000205
cristy787c09c2010-04-16 18:18:34 +0000206 MagickRealType
207 maximum,
208 scale;
209
210 RectangleInfo
211 geometry;
212
213 register const PixelPacket
cristy1bd4a3a2010-04-16 12:38:47 +0000214 *p;
215
cristy787c09c2010-04-16 18:18:34 +0000216 register PixelPacket
217 *q,
218 *r;
cristy1bd4a3a2010-04-16 12:38:47 +0000219
cristy31ac0f02011-03-12 02:04:47 +0000220 register ssize_t
221 x;
222
cristy787c09c2010-04-16 18:18:34 +0000223 size_t
224 length;
cristy1bd4a3a2010-04-16 12:38:47 +0000225
cristy31ac0f02011-03-12 02:04:47 +0000226 ssize_t
227 y;
228
cristy1bd4a3a2010-04-16 12:38:47 +0000229 /*
cristy787c09c2010-04-16 18:18:34 +0000230 Allocate histogram image.
cristy1bd4a3a2010-04-16 12:38:47 +0000231 */
cristy787c09c2010-04-16 18:18:34 +0000232 assert(image_info != (const ImageInfo *) NULL);
233 assert(image_info->signature == MagickSignature);
234 assert(image != (Image *) NULL);
235 assert(image->signature == MagickSignature);
236 if (image->debug != MagickFalse)
237 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
238 image_info->filename);
239 SetGeometry(image,&geometry);
240 if (image_info->density == (char *) NULL)
241 (void) ParseAbsoluteGeometry(HistogramDensity,&geometry);
242 else
243 (void) ParseAbsoluteGeometry(image_info->density,&geometry);
244 histogram_image=CloneImage(image,geometry.width,geometry.height,MagickTrue,
245 &image->exception);
246 if (histogram_image == (Image *) NULL)
247 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
248 (void) SetImageStorageClass(histogram_image,DirectClass);
249 /*
250 Allocate histogram count arrays.
251 */
252 length=MagickMax((size_t) ScaleQuantumToChar((Quantum) QuantumRange)+1UL,
253 histogram_image->columns);
254 histogram=(MagickPixelPacket *) AcquireQuantumMemory(length,
255 sizeof(*histogram));
256 if (histogram == (MagickPixelPacket *) NULL)
cristy1bd4a3a2010-04-16 12:38:47 +0000257 {
cristy787c09c2010-04-16 18:18:34 +0000258 histogram_image=DestroyImage(histogram_image);
259 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
260 }
261 /*
262 Initialize histogram count arrays.
263 */
264 channel=image_info->channel;
265 (void) ResetMagickMemory(histogram,0,length*sizeof(*histogram));
cristybb503372010-05-27 20:51:26 +0000266 for (y=0; y < (ssize_t) image->rows; y++)
cristy787c09c2010-04-16 18:18:34 +0000267 {
268 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
269 if (p == (const PixelPacket *) NULL)
270 break;
cristybb503372010-05-27 20:51:26 +0000271 for (x=0; x < (ssize_t) image->columns; x++)
cristy787c09c2010-04-16 18:18:34 +0000272 {
273 if ((channel & RedChannel) != 0)
274 histogram[ScaleQuantumToChar(GetRedPixelComponent(p))].red++;
275 if ((channel & GreenChannel) != 0)
276 histogram[ScaleQuantumToChar(GetGreenPixelComponent(p))].green++;
277 if ((channel & BlueChannel) != 0)
278 histogram[ScaleQuantumToChar(GetBluePixelComponent(p))].blue++;
279 p++;
280 }
281 }
282 maximum=histogram[0].red;
cristybb503372010-05-27 20:51:26 +0000283 for (x=0; x < (ssize_t) histogram_image->columns; x++)
cristy787c09c2010-04-16 18:18:34 +0000284 {
285 if (((channel & RedChannel) != 0) && (maximum < histogram[x].red))
286 maximum=histogram[x].red;
287 if (((channel & GreenChannel) != 0) && (maximum < histogram[x].green))
288 maximum=histogram[x].green;
289 if (((channel & BlueChannel) != 0) && (maximum < histogram[x].blue))
290 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);
303 if (q == (PixelPacket *) NULL)
304 break;
305 if ((channel & RedChannel) != 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 {
cristyebc891a2011-04-24 23:04:16 +0000311 SetRedPixelComponent(r,QuantumRange);
cristy787c09c2010-04-16 18:18:34 +0000312 r++;
cristy1bd4a3a2010-04-16 12:38:47 +0000313 }
cristy787c09c2010-04-16 18:18:34 +0000314 }
315 if ((channel & GreenChannel) != 0)
316 {
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 {
cristyebc891a2011-04-24 23:04:16 +0000321 SetGreenPixelComponent(r,QuantumRange);
cristy787c09c2010-04-16 18:18:34 +0000322 r++;
323 }
324 }
325 if ((channel & BlueChannel) != 0)
326 {
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 {
cristyebc891a2011-04-24 23:04:16 +0000331 SetBluePixelComponent(r,QuantumRange);
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 */
344 histogram=(MagickPixelPacket *) RelinquishMagickMemory(histogram);
345 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}