blob: 87154589e65beca52a381786bb3e9672ce7a81f4 [file] [log] [blame]
cristy3f07aa32014-01-18 01:16:33 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% JJJJJ SSSSS OOO N N %
7% J SS O O NN N %
8% J SSS O O N N N %
9% J J SS O O N NN %
10% JJJ SSSSS OOO N N %
11% %
12% %
13% Write Info About the Image in JSON Format. %
14% %
15% Software Design %
16% Cristy %
17% January 2014 %
18% %
19% %
Cristyf6ff9ea2016-12-05 09:53:35 -050020% Copyright 1999-2017 ImageMagick Studio LLC, a non-profit organization %
cristy3f07aa32014-01-18 01:16:33 +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% %
Cristyf19d4142017-04-24 11:34:30 -040026% https://www.imagemagick.org/script/license.php %
cristy3f07aa32014-01-18 01:16:33 +000027% %
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 "MagickCore/studio.h"
43#include "MagickCore/artifact.h"
cristy9e818982014-01-18 14:54:37 +000044#include "MagickCore/attribute.h"
cristy3f07aa32014-01-18 01:16:33 +000045#include "MagickCore/blob.h"
46#include "MagickCore/blob-private.h"
cristy9e818982014-01-18 14:54:37 +000047#include "MagickCore/cache.h"
cristy43602772014-01-29 14:07:29 +000048#include "MagickCore/colorspace.h"
49#include "MagickCore/colorspace-private.h"
cristy9e818982014-01-18 14:54:37 +000050#include "MagickCore/constitute.h"
cristy3f07aa32014-01-18 01:16:33 +000051#include "MagickCore/exception.h"
52#include "MagickCore/exception-private.h"
cristy9e818982014-01-18 14:54:37 +000053#include "MagickCore/feature.h"
cristy3f07aa32014-01-18 01:16:33 +000054#include "MagickCore/image.h"
55#include "MagickCore/image-private.h"
56#include "MagickCore/list.h"
57#include "MagickCore/magick.h"
58#include "MagickCore/memory_.h"
59#include "MagickCore/monitor.h"
60#include "MagickCore/monitor-private.h"
61#include "MagickCore/option.h"
cristy43602772014-01-29 14:07:29 +000062#include "MagickCore/pixel.h"
cristy9e818982014-01-18 14:54:37 +000063#include "MagickCore/pixel-accessor.h"
cristy43602772014-01-29 14:07:29 +000064#include "MagickCore/pixel-private.h"
cristy9e818982014-01-18 14:54:37 +000065#include "MagickCore/prepress.h"
cristy3f07aa32014-01-18 01:16:33 +000066#include "MagickCore/property.h"
cristy43602772014-01-29 14:07:29 +000067#include "MagickCore/quantum-private.h"
cristy9e818982014-01-18 14:54:37 +000068#include "MagickCore/registry.h"
cristy9e818982014-01-18 14:54:37 +000069#include "MagickCore/signature.h"
cristy43602772014-01-29 14:07:29 +000070#include "MagickCore/static.h"
cristy9e818982014-01-18 14:54:37 +000071#include "MagickCore/statistic.h"
cristy3f07aa32014-01-18 01:16:33 +000072#include "MagickCore/string_.h"
cristy9e818982014-01-18 14:54:37 +000073#include "MagickCore/string-private.h"
cristy3f07aa32014-01-18 01:16:33 +000074#include "MagickCore/utility.h"
cristy9e818982014-01-18 14:54:37 +000075#include "MagickCore/version.h"
cristy43602772014-01-29 14:07:29 +000076#include "MagickCore/module.h"
Dirk Lemstra32b15502017-09-21 17:04:36 +020077
78/*
79 Typedef declarations.
80*/
81typedef struct _IptcValue
82{
83 long
84 dataset,
85 record;
86
87 size_t
88 values_length;
89
90 char
91 tag[32],
92 ***values;
93} IptcValue;
cristy3f07aa32014-01-18 01:16:33 +000094
95/*
96 Forward declarations.
97*/
98static MagickBooleanType
99 WriteJSONImage(const ImageInfo *,Image *,ExceptionInfo *);
100
101/*
102%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
103% %
104% %
105% %
106% R e g i s t e r J S O N I m a g e %
107% %
108% %
109% %
110%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
111%
112% RegisterJSONImage() adds attributes for the JSON image format to
113% the list of supported formats. The attributes include the image format
114% tag, a method to read and/or write the format, whether the format
115% supports the saving of more than one frame to the same file or blob,
116% whether the format supports native in-memory I/O, and a brief
117% description of the format.
118%
119% The format of the RegisterJSONImage method is:
120%
121% size_t RegisterJSONImage(void)
122%
123*/
124ModuleExport size_t RegisterJSONImage(void)
125{
126 MagickInfo
127 *entry;
128
cristye1c94d92015-06-28 12:16:33 +0000129 entry=AcquireMagickInfo("JSON","JSON","The image format and characteristics");
cristy3f07aa32014-01-18 01:16:33 +0000130 entry->encoder=(EncodeImageHandler *) WriteJSONImage;
dirk2aa15e62015-09-14 21:33:54 +0200131 entry->mime_type=ConstantString("application/json");
dirk08e9a112015-02-22 01:51:41 +0000132 entry->flags^=CoderBlobSupportFlag;
cristy3f07aa32014-01-18 01:16:33 +0000133 (void) RegisterMagickInfo(entry);
134 return(MagickImageCoderSignature);
135}
136
137/*
138%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
139% %
140% %
141% %
142% U n r e g i s t e r J S O N I m a g e %
143% %
144% %
145% %
146%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
147%
148% UnregisterJSONImage() removes format registrations made by the
149% JSON module from the list of supported formats.
150%
151% The format of the UnregisterJSONImage method is:
152%
153% UnregisterJSONImage(void)
154%
155*/
156ModuleExport void UnregisterJSONImage(void)
157{
158 (void) UnregisterMagickInfo("JSON");
159}
160
161/*
162%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
163% %
164% %
165% %
166% W r i t e J S O N I m a g e %
167% %
168% %
169% %
170%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
171%
172% WriteJSONImage writes the image attributes in the JSON format.
173%
174% The format of the WriteJSONImage method is:
175%
176% MagickBooleanType WriteJSONImage(const ImageInfo *image_info,
177% Image *image,ExceptionInfo *exception)
178%
179% A description of each parameter follows.
180%
181% o image_info: the image info.
182%
183% o image: The image.
184%
185% o exception: return any errors or warnings in this structure.
186%
187*/
cristy9e818982014-01-18 14:54:37 +0000188
dirk6c316f22015-06-27 15:44:29 +0000189static void JsonFormatLocaleFile(FILE *file,const char *format,const char *value)
190{
191 char
192 *escaped_json;
193
194 register char
195 *q;
196
197 register const char
198 *p;
199
200 size_t
201 length;
202
203 assert(format != (const char *) NULL);
204 if (value == (char *) NULL || *value == '\0')
205 {
206 (void) FormatLocaleFile(file,format,"null");
207 return;
208 }
209 length=strlen(value)+2;
210 /*
211 Find all the chars that need escaping and increase the dest length counter
212 */
213 for (p=value; *p != '\0'; p++)
214 {
215 switch (*p)
216 {
217 case '"':
218 case '\b':
219 case '\f':
220 case '\n':
221 case '\r':
222 case '\t':
223 case '\\':
224 if (~length < 1)
225 return;
226 length++;
227 break;
228 default:
229 break;
230 }
231 }
232 escaped_json=(char *) NULL;
233 if (~length >= (MagickPathExtent-1))
234 escaped_json=(char *) AcquireQuantumMemory(length+MagickPathExtent,
235 sizeof(*escaped_json));
236 if (escaped_json == (char *) NULL)
237 {
238 (void) FormatLocaleFile(file,format,"null");
239 return;
240 }
241 q=escaped_json;
242 *q++='"';
243 for (p=value; *p != '\0'; p++)
244 {
245 switch (*p)
246 {
247 case '"':
248 *q++='\\';
249 *q++=(*p);
250 break;
251 case '\b':
252 *q++='\\';
253 *q++='b';
254 break;
255 case '\f':
256 *q++='\\';
257 *q++='f';
258 break;
259 case '\n':
260 *q++='\\';
261 *q++='n';
262 break;
263 case '\r':
264 *q++='\\';
265 *q++='r';
266 break;
267 case '\t':
268 *q++='\\';
269 *q++='t';
270 break;
271 case '\\':
272 *q++='\\';
273 *q++='\\';
274 break;
275 default:
276 *q++=(*p);
277 break;
278 }
279 }
280 *q++='"';
281 *q='\0';
282 (void) FormatLocaleFile(file,format,escaped_json);
283 (void) DestroyString(escaped_json);
284}
285
cristy9e818982014-01-18 14:54:37 +0000286static ChannelStatistics *GetLocationStatistics(const Image *image,
287 const StatisticType type,ExceptionInfo *exception)
288{
289 ChannelStatistics
290 *channel_statistics;
291
292 register ssize_t
293 i;
294
295 ssize_t
296 y;
297
298 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000299 assert(image->signature == MagickCoreSignature);
cristy9e818982014-01-18 14:54:37 +0000300 if (image->debug != MagickFalse)
301 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
302 channel_statistics=(ChannelStatistics *) AcquireQuantumMemory(
303 MaxPixelChannels+1,sizeof(*channel_statistics));
304 if (channel_statistics == (ChannelStatistics *) NULL)
305 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
306 (void) ResetMagickMemory(channel_statistics,0,(MaxPixelChannels+1)*
307 sizeof(*channel_statistics));
308 for (i=0; i <= (ssize_t) MaxPixelChannels; i++)
309 {
310 switch (type)
311 {
312 case MaximumStatistic:
313 default:
314 {
cristyfe181a72014-02-02 21:17:43 +0000315 channel_statistics[i].maxima=(-MagickMaximumValue);
cristy9e818982014-01-18 14:54:37 +0000316 break;
317 }
318 case MinimumStatistic:
319 {
cristyfe181a72014-02-02 21:17:43 +0000320 channel_statistics[i].minima=MagickMaximumValue;
cristy9e818982014-01-18 14:54:37 +0000321 break;
322 }
323 }
324 }
325 for (y=0; y < (ssize_t) image->rows; y++)
326 {
327 register const Quantum
dirk05d2ff72015-11-18 23:13:43 +0100328 *magick_restrict p;
cristy9e818982014-01-18 14:54:37 +0000329
330 register ssize_t
331 x;
332
333 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
334 if (p == (const Quantum *) NULL)
335 break;
336 for (x=0; x < (ssize_t) image->columns; x++)
337 {
338 register ssize_t
339 i;
340
Cristyd05ae9a2017-08-12 07:55:19 -0400341 if (GetPixelReadMask(image,p) <= (QuantumRange/2))
cristy9e818982014-01-18 14:54:37 +0000342 {
343 p+=GetPixelChannels(image);
344 continue;
345 }
346 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
347 {
Cristy3c296d62017-08-19 20:09:26 -0400348 PixelChannel channel = GetPixelChannelChannel(image,i);
349 PixelTrait traits = GetPixelChannelTraits(image,channel);
cristy9e818982014-01-18 14:54:37 +0000350 if (traits == UndefinedPixelTrait)
351 continue;
352 switch (type)
353 {
354 case MaximumStatistic:
355 default:
356 {
357 if ((double) p[i] > channel_statistics[channel].maxima)
358 channel_statistics[channel].maxima=(double) p[i];
359 break;
360 }
361 case MinimumStatistic:
362 {
363 if ((double) p[i] < channel_statistics[channel].minima)
364 channel_statistics[channel].minima=(double) p[i];
365 break;
366 }
367 }
368 }
369 p+=GetPixelChannels(image);
370 }
371 }
372 return(channel_statistics);
373}
374
375static ssize_t PrintChannelFeatures(FILE *file,const PixelChannel channel,
dirk6c316f22015-06-27 15:44:29 +0000376 const char *name,const MagickBooleanType separator,
377 const ChannelFeatures *channel_features)
cristy9e818982014-01-18 14:54:37 +0000378{
379#define PrintFeature(feature) \
380 GetMagickPrecision(),(feature)[0], \
381 GetMagickPrecision(),(feature)[1], \
382 GetMagickPrecision(),(feature)[2], \
383 GetMagickPrecision(),(feature)[3], \
384 GetMagickPrecision(),((feature)[0]+(feature)[1]+(feature)[2]+(feature)[3])/4.0 \
385
dirk6c316f22015-06-27 15:44:29 +0000386#define FeaturesFormat " \"%s\": {\n" \
387 " \"angularSecondMoment\": {\n" \
388 " \"horizontal\": \"%.*g\",\n" \
389 " \"vertical\": \"%.*g\",\n" \
390 " \"leftDiagonal\": \"%.*g\",\n" \
391 " \"rightDiagonal\": \"%.*g\",\n" \
392 " \"average\": \"%.*g\"\n" \
393 " },\n" \
394 " \"contrast\": {\n" \
395 " \"horizontal\": \"%.*g\",\n" \
396 " \"vertical\": \"%.*g\",\n" \
397 " \"leftDiagonal\": \"%.*g\",\n" \
398 " \"rightDiagonal\": \"%.*g\",\n" \
399 " \"average\": \"%.*g\"\n" \
400 " },\n" \
401 " \"correlation\": {\n" \
402 " \"horizontal\": \"%.*g\",\n" \
403 " \"vertical\": \"%.*g\",\n" \
404 " \"leftDiagonal\": \"%.*g\",\n" \
405 " \"rightDiagonal\": \"%.*g\",\n" \
406 " \"average\": \"%.*g\"\n" \
407 " },\n" \
408 " \"sumOfSquaresVariance\": {\n" \
409 " \"horizontal\": \"%.*g\",\n" \
410 " \"vertical\": \"%.*g\",\n" \
411 " \"leftDiagonal\": \"%.*g\",\n" \
412 " \"rightDiagonal\": \"%.*g\",\n" \
413 " \"average\": \"%.*g\"\n" \
414 " },\n" \
415 " \"inverseDifferenceMoment\": {\n" \
416 " \"horizontal\": \"%.*g\",\n" \
417 " \"vertical\": \"%.*g\",\n" \
418 " \"leftDiagonal\": \"%.*g\",\n" \
419 " \"rightDiagonal\": \"%.*g\",\n" \
420 " \"average\": \"%.*g\"\n" \
421 " },\n" \
422 " \"sumAverage\": {\n" \
423 " \"horizontal\": \"%.*g\",\n" \
424 " \"vertical\": \"%.*g\",\n" \
425 " \"leftDiagonal\": \"%.*g\",\n" \
426 " \"rightDiagonal\": \"%.*g\",\n" \
427 " \"average\": \"%.*g\"\n" \
428 " },\n" \
429 " \"sumVariance\": {\n" \
430 " \"horizontal\": \"%.*g\",\n" \
431 " \"vertical\": \"%.*g\",\n" \
432 " \"leftDiagonal\": \"%.*g\",\n" \
433 " \"rightDiagonal\": \"%.*g\",\n" \
434 " \"average\": \"%.*g\"\n" \
435 " },\n" \
436 " \"sumEntropy\": {\n" \
437 " \"horizontal\": \"%.*g\",\n" \
438 " \"vertical\": \"%.*g\",\n" \
439 " \"leftDiagonal\": \"%.*g\",\n" \
440 " \"rightDiagonal\": \"%.*g\",\n" \
441 " \"average\": \"%.*g\"\n" \
442 " },\n" \
443 " \"entropy\": {\n" \
444 " \"horizontal\": \"%.*g\",\n" \
445 " \"vertical\": \"%.*g\",\n" \
446 " \"leftDiagonal\": \"%.*g\",\n" \
447 " \"rightDiagonal\": \"%.*g\",\n" \
448 " \"average\": \"%.*g\"\n" \
449 " },\n" \
450 " \"differenceVariance\": {\n" \
451 " \"horizontal\": \"%.*g\",\n" \
452 " \"vertical\": \"%.*g\",\n" \
453 " \"leftDiagonal\": \"%.*g\",\n" \
454 " \"rightDiagonal\": \"%.*g\",\n" \
455 " \"average\": \"%.*g\"\n" \
456 " },\n" \
457 " \"differenceEntropy\": {\n" \
458 " \"horizontal\": \"%.*g\",\n" \
459 " \"vertical\": \"%.*g\",\n" \
460 " \"leftDiagonal\": \"%.*g\",\n" \
461 " \"rightDiagonal\": \"%.*g\",\n" \
462 " \"average\": \"%.*g\"\n" \
463 " },\n" \
464 " \"informationMeasureOfCorrelation1\": {\n" \
465 " \"horizontal\": \"%.*g\",\n" \
466 " \"vertical\": \"%.*g\",\n" \
467 " \"leftDiagonal\": \"%.*g\",\n" \
468 " \"rightDiagonal\": \"%.*g\",\n" \
469 " \"average\": \"%.*g\"\n" \
470 " },\n" \
471 " \"informationMeasureOfCorrelation2\": {\n" \
472 " \"horizontal\": \"%.*g\",\n" \
473 " \"vertical\": \"%.*g\",\n" \
474 " \"leftDiagonal\": \"%.*g\",\n" \
475 " \"rightDiagonal\": \"%.*g\",\n" \
476 " \"average\": \"%.*g\"\n" \
477 " },\n" \
478 " \"maximumCorrelationCoefficient\": {\n" \
479 " \"horizontal\": \"%.*g\",\n" \
480 " \"vertical\": \"%.*g\",\n" \
481 " \"leftDiagonal\": \"%.*g\",\n" \
482 " \"rightDiagonal\": \"%.*g\",\n" \
483 " \"average\": \"%.*g\"\n" \
484 " }\n"
cristy9e818982014-01-18 14:54:37 +0000485
486 ssize_t
487 n;
488
489 n=FormatLocaleFile(file,FeaturesFormat,name,
490 PrintFeature(channel_features[channel].angular_second_moment),
491 PrintFeature(channel_features[channel].contrast),
492 PrintFeature(channel_features[channel].correlation),
493 PrintFeature(channel_features[channel].variance_sum_of_squares),
494 PrintFeature(channel_features[channel].inverse_difference_moment),
495 PrintFeature(channel_features[channel].sum_average),
496 PrintFeature(channel_features[channel].sum_variance),
497 PrintFeature(channel_features[channel].sum_entropy),
498 PrintFeature(channel_features[channel].entropy),
499 PrintFeature(channel_features[channel].difference_variance),
500 PrintFeature(channel_features[channel].difference_entropy),
501 PrintFeature(channel_features[channel].measure_of_correlation_1),
502 PrintFeature(channel_features[channel].measure_of_correlation_2),
503 PrintFeature(channel_features[channel].maximum_correlation_coefficient));
dirk6c316f22015-06-27 15:44:29 +0000504 (void) FormatLocaleFile(file," }");
505 if (separator != MagickFalse)
506 (void) FormatLocaleFile(file,",");
507 (void) FormatLocaleFile(file,"\n");
cristy9e818982014-01-18 14:54:37 +0000508 return(n);
509}
510
511static ssize_t PrintChannelLocations(FILE *file,const Image *image,
512 const PixelChannel channel,const char *name,const StatisticType type,
dirk6c316f22015-06-27 15:44:29 +0000513 const size_t max_locations,const MagickBooleanType separator,
514 const ChannelStatistics *channel_statistics)
cristy9e818982014-01-18 14:54:37 +0000515{
516 double
517 target;
518
519 ExceptionInfo
520 *exception;
521
522 ssize_t
523 n,
524 y;
525
526 switch (type)
527 {
528 case MaximumStatistic:
529 default:
530 {
531 target=channel_statistics[channel].maxima;
532 break;
533 }
534 case MinimumStatistic:
535 {
536 target=channel_statistics[channel].minima;
537 break;
538 }
539 }
dirk6c316f22015-06-27 15:44:29 +0000540 (void) FormatLocaleFile(file," \"%s\": {\n \"intensity\": "
541 "\"%.*g\",\n",name,GetMagickPrecision(),QuantumScale*target);
cristy9e818982014-01-18 14:54:37 +0000542 exception=AcquireExceptionInfo();
543 n=0;
544 for (y=0; y < (ssize_t) image->rows; y++)
545 {
546 register const Quantum
547 *p;
548
549 ssize_t
550 offset,
551 x;
552
553 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
554 if (p == (const Quantum *) NULL)
555 break;
556 for (x=0; x < (ssize_t) image->columns; x++)
557 {
558 MagickBooleanType
559 match;
560
Cristy3c296d62017-08-19 20:09:26 -0400561 PixelTrait traits = GetPixelChannelTraits(image,channel);
cristy9e818982014-01-18 14:54:37 +0000562 if (traits == UndefinedPixelTrait)
563 continue;
564 offset=GetPixelChannelOffset(image,channel);
cristy3bdd9252014-12-21 20:01:43 +0000565 match=fabs((double) (p[offset]-target)) < 0.5 ? MagickTrue : MagickFalse;
cristy9e818982014-01-18 14:54:37 +0000566 if (match != MagickFalse)
567 {
568 if ((max_locations != 0) && (n >= (ssize_t) max_locations))
569 break;
dirk6c316f22015-06-27 15:44:29 +0000570 if (n != 0)
571 (void) FormatLocaleFile(file,",\n");
572 (void) FormatLocaleFile(file," \"location%.20g\": {\n"
573 " \"x\": %.20g,\n \"y\": %.20g\n"
574 " }",(double) n,(double) x,(double) y);
cristy9e818982014-01-18 14:54:37 +0000575 n++;
576 }
577 p+=GetPixelChannels(image);
578 }
579 if (x < (ssize_t) image->columns)
580 break;
581 }
dirk6c316f22015-06-27 15:44:29 +0000582 (void) FormatLocaleFile(file,"\n }");
583 if (separator != MagickFalse)
584 (void) FormatLocaleFile(file,",");
cristy9e818982014-01-18 14:54:37 +0000585 (void) FormatLocaleFile(file,"\n");
586 return(n);
587}
588
589static ssize_t PrintChannelMoments(FILE *file,const PixelChannel channel,
dirk6c316f22015-06-27 15:44:29 +0000590 const char *name,const MagickBooleanType separator,
591 const ChannelMoments *channel_moments)
cristy9e818982014-01-18 14:54:37 +0000592{
593 register ssize_t
594 i;
595
596 ssize_t
597 n;
598
dirk6c316f22015-06-27 15:44:29 +0000599 n=FormatLocaleFile(file," \"%s\": {\n",name);
600 n+=FormatLocaleFile(file," \"centroid\": {\n "
601 " \"x\": \"%.*g\",\n"
602 " \"y\": \"%.*g\"\n },\n",
cristy9e818982014-01-18 14:54:37 +0000603 GetMagickPrecision(),channel_moments[channel].centroid.x,
604 GetMagickPrecision(),channel_moments[channel].centroid.y);
dirk6c316f22015-06-27 15:44:29 +0000605 n+=FormatLocaleFile(file," \"ellipseSemiMajorMinorAxis\": {\n"
606 " \"x\": \"%.*g\",\n"
607 " \"y\": \"%.*g\"\n },\n",
cristy9e818982014-01-18 14:54:37 +0000608 GetMagickPrecision(),channel_moments[channel].ellipse_axis.x,
609 GetMagickPrecision(),channel_moments[channel].ellipse_axis.y);
dirk6c316f22015-06-27 15:44:29 +0000610 n+=FormatLocaleFile(file," \"ellipseAngle\": \"%.*g\",\n",
cristy9e818982014-01-18 14:54:37 +0000611 GetMagickPrecision(),channel_moments[channel].ellipse_angle);
dirk6c316f22015-06-27 15:44:29 +0000612 n+=FormatLocaleFile(file," \"ellipseEccentricity\": \"%.*g\",\n",
cristy9e818982014-01-18 14:54:37 +0000613 GetMagickPrecision(),channel_moments[channel].ellipse_eccentricity);
dirk6c316f22015-06-27 15:44:29 +0000614 n+=FormatLocaleFile(file," \"ellipseIntensity\": \"%.*g\",\n",
cristy9e818982014-01-18 14:54:37 +0000615 GetMagickPrecision(),channel_moments[channel].ellipse_intensity);
dirk6c316f22015-06-27 15:44:29 +0000616 for (i=0; i < 7; i++)
617 n+=FormatLocaleFile(file," \"I%.20g\": \"%.*g\",\n",i+1.0,
618 GetMagickPrecision(),channel_moments[channel].invariant[i]);
619 n+=FormatLocaleFile(file," \"I%.20g\": \"%.*g\"\n",i+1.0,
620 GetMagickPrecision(),channel_moments[channel].invariant[i]);
621 (void) FormatLocaleFile(file," }");
622 if (separator != MagickFalse)
623 (void) FormatLocaleFile(file,",");
624 (void) FormatLocaleFile(file,"\n");
cristy9e818982014-01-18 14:54:37 +0000625 return(n);
626}
627
Cristye6961802016-09-03 11:31:13 -0400628static ssize_t PrintChannelPerceptualHash(Image *image,FILE *file,
cristydc395172014-02-23 01:33:36 +0000629 const ChannelPerceptualHash *channel_phash)
630{
631 register ssize_t
632 i;
633
634 ssize_t
Cristy69a7c2b2016-12-24 11:22:36 -0500635 n = 0;
cristydc395172014-02-23 01:33:36 +0000636
Cristye6961802016-09-03 11:31:13 -0400637 (void) FormatLocaleFile(file," \"colorspaces\": [ ");
638 for (i=0; i < (ssize_t) channel_phash[0].number_colorspaces; i++)
639 {
640 (void) FormatLocaleFile(file,"\"%s\"",CommandOptionToMnemonic(
641 MagickColorspaceOptions,(ssize_t) channel_phash[0].colorspace[i]));
642 if (i < (ssize_t) (channel_phash[0].number_colorspaces-1))
643 (void) FormatLocaleFile(file,", ");
644 }
645 (void) FormatLocaleFile(file,"],\n");
646 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
647 {
648 register ssize_t
649 j;
650
Cristy3c296d62017-08-19 20:09:26 -0400651 PixelChannel channel = GetPixelChannelChannel(image,i);
652 PixelTrait traits = GetPixelChannelTraits(image,channel);
Cristye6961802016-09-03 11:31:13 -0400653 if (traits == UndefinedPixelTrait)
654 continue;
655 n=FormatLocaleFile(file," \"Channel%.20g\": {\n",(double) channel);
656 for (j=0; j < MaximumNumberOfPerceptualHashes; j++)
657 {
658 register ssize_t
659 k;
660
661 n+=FormatLocaleFile(file," \"PH%.20g\": [",(double) j+1);
662 for (k=0; k < (ssize_t) channel_phash[0].number_colorspaces; k++)
663 {
664 n+=FormatLocaleFile(file,"\"%.*g\"",GetMagickPrecision(),
665 channel_phash[channel].phash[k][j]);
666 if (k < (ssize_t) (channel_phash[0].number_colorspaces-1))
667 n+=FormatLocaleFile(file,", ");
668 }
669 n+=FormatLocaleFile(file,"]");
670 if (j < (MaximumNumberOfPerceptualHashes-1))
671 n+=FormatLocaleFile(file,",\n");
672 }
Cristy338f0882016-12-10 12:12:35 -0500673 if (i < (ssize_t) (GetPixelChannels(image)-1))
Cristye6961802016-09-03 11:31:13 -0400674 n+=FormatLocaleFile(file,"\n },\n");
675 }
676 n+=FormatLocaleFile(file,"\n }\n");
cristydc395172014-02-23 01:33:36 +0000677 return(n);
678}
679
cristy9e818982014-01-18 14:54:37 +0000680static ssize_t PrintChannelStatistics(FILE *file,const PixelChannel channel,
dirk6c316f22015-06-27 15:44:29 +0000681 const char *name,const double scale,const MagickBooleanType separator,
cristy9e818982014-01-18 14:54:37 +0000682 const ChannelStatistics *channel_statistics)
683{
dirk6c316f22015-06-27 15:44:29 +0000684#define StatisticsFormat " \"%s\": {\n \"min\": \"" QuantumFormat \
685 "\",\n \"max\": \"" QuantumFormat "\",\n" \
686 " \"mean\": \"%g\",\n \"standardDeviation\": " \
687 "\"%g\",\n \"kurtosis\": \"%g\",\n \"skewness\": " \
688 "\"%g\"\n }"
cristy9e818982014-01-18 14:54:37 +0000689
690 ssize_t
691 n;
692
693 n=FormatLocaleFile(file,StatisticsFormat,name,ClampToQuantum(scale*
cristye1c94d92015-06-28 12:16:33 +0000694 channel_statistics[channel].minima),ClampToQuantum(scale*
695 channel_statistics[channel].maxima),scale*channel_statistics[channel].mean,
696 scale*channel_statistics[channel].standard_deviation,
cristy9e818982014-01-18 14:54:37 +0000697 channel_statistics[channel].kurtosis,channel_statistics[channel].skewness);
dirk6c316f22015-06-27 15:44:29 +0000698 if (separator != MagickFalse)
cristye1c94d92015-06-28 12:16:33 +0000699 (void) FormatLocaleFile(file,",");
dirk6c316f22015-06-27 15:44:29 +0000700 (void) FormatLocaleFile(file,"\n");
cristy9e818982014-01-18 14:54:37 +0000701 return(n);
702}
703
Dirk Lemstra32b15502017-09-21 17:04:36 +0200704static void EncodeIptcProfile(FILE *file,const StringInfo *profile)
705{
706 char
707 *attribute,
708 **attribute_list;
709
710 const char
711 *tag;
712
713 IptcValue
714 *value,
715 **values;
716
717 long
718 dataset,
719 record,
720 sentinel;
721
722 register ssize_t
723 i,
724 j,
725 k;
726
727 size_t
728 count,
729 length,
730 profile_length;
731
732 values=(IptcValue **) NULL;
733 count=0;
734 profile_length=GetStringInfoLength(profile);
735 for (i=0; i < (ssize_t) profile_length; i+=(ssize_t) length)
736 {
737 length=1;
738 sentinel=GetStringInfoDatum(profile)[i++];
739 if (sentinel != 0x1c)
740 continue;
741 dataset=GetStringInfoDatum(profile)[i++];
742 record=GetStringInfoDatum(profile)[i++];
743 value=(IptcValue *) NULL;
744 for (j=0; j < count; j++)
745 {
746 if ((values[j]->record == record) && (values[j]->dataset == dataset))
747 value=values[j];
748 }
749 if (value == (IptcValue *) NULL)
750 {
751 values=(IptcValue **) ResizeQuantumMemory(values,count+1,
752 sizeof(*values));
753 if (values == (IptcValue **) NULL)
754 break;
755 value=AcquireMagickMemory(sizeof(*value));
756 if (value == (IptcValue *) NULL)
757 break;
758 /* Check the tag length in IptcValue when a new tag is added */
759 switch (record)
760 {
761 case 5: tag="Image Name"; break;
762 case 7: tag="Edit Status"; break;
763 case 10: tag="Priority"; break;
764 case 15: tag="Category"; break;
765 case 20: tag="Supplemental Category"; break;
766 case 22: tag="Fixture Identifier"; break;
767 case 25: tag="Keyword"; break;
768 case 30: tag="Release Date"; break;
769 case 35: tag="Release Time"; break;
770 case 40: tag="Special Instructions"; break;
771 case 45: tag="Reference Service"; break;
772 case 47: tag="Reference Date"; break;
773 case 50: tag="Reference Number"; break;
774 case 55: tag="Created Date"; break;
775 case 60: tag="Created Time"; break;
776 case 65: tag="Originating Program"; break;
777 case 70: tag="Program Version"; break;
778 case 75: tag="Object Cycle"; break;
779 case 80: tag="Byline"; break;
780 case 85: tag="Byline Title"; break;
781 case 90: tag="City"; break;
782 case 92: tag="Sub-Location"; break;
783 case 95: tag="Province State"; break;
784 case 100: tag="Country Code"; break;
785 case 101: tag="Country"; break;
786 case 103: tag="Original Transmission Reference"; break;
787 case 105: tag="Headline"; break;
788 case 110: tag="Credit"; break;
789 case 115: tag="Src"; break;
790 case 116: tag="Copyright String"; break;
791 case 120: tag="Caption"; break;
792 case 121: tag="Local Caption"; break;
793 case 122: tag="Caption Writer"; break;
794 case 200: tag="Custom Field 1"; break;
795 case 201: tag="Custom Field 2"; break;
796 case 202: tag="Custom Field 3"; break;
797 case 203: tag="Custom Field 4"; break;
798 case 204: tag="Custom Field 5"; break;
799 case 205: tag="Custom Field 6"; break;
800 case 206: tag="Custom Field 7"; break;
801 case 207: tag="Custom Field 8"; break;
802 case 208: tag="Custom Field 9"; break;
803 case 209: tag="Custom Field 10"; break;
804 case 210: tag="Custom Field 11"; break;
805 case 211: tag="Custom Field 12"; break;
806 case 212: tag="Custom Field 13"; break;
807 case 213: tag="Custom Field 14"; break;
808 case 214: tag="Custom Field 15"; break;
809 case 215: tag="Custom Field 16"; break;
810 case 216: tag="Custom Field 17"; break;
811 case 217: tag="Custom Field 18"; break;
812 case 218: tag="Custom Field 19"; break;
813 case 219: tag="Custom Field 20"; break;
814 default: tag="Unknown"; break;
815 }
816 (void) CopyMagickString(value->tag,tag,strlen(tag)+1);
817 value->record=record;
818 value->dataset=dataset;
819 value->values=(char ***) NULL;
820 value->values_length=0;
821 values[count++]=value;
822 }
823 length=(size_t) (GetStringInfoDatum(profile)[i++] << 8);
824 length|=GetStringInfoDatum(profile)[i++];
825 attribute=(char *) NULL;
826 if (~length >= (MagickPathExtent-1))
827 attribute=(char *) AcquireQuantumMemory(length+MagickPathExtent,
828 sizeof(*attribute));
829 if (attribute != (char *) NULL)
830 {
831 (void) CopyMagickString(attribute,(char *)
832 GetStringInfoDatum(profile)+i,length+1);
833 attribute_list=StringToList(attribute);
834 if (attribute_list != (char **) NULL)
835 {
836 value->values=(char ***) ResizeQuantumMemory(value->values,
837 value->values_length+1,
838 sizeof(*value->values));
839 if (value->values == (char ***) NULL)
840 break;
841 value->values[value->values_length++]=attribute_list;
842 }
843 attribute=DestroyString(attribute);
844 }
845 }
846 if (values != (IptcValue **) NULL)
847 {
848 for (i=0; i < count; i++)
849 {
850 value=values[i];
851 (void) FormatLocaleFile(file," \"%s[%.20g,%.20g]\": ",
852 value->tag,(double) value->dataset,(double) value->record);
853 if (value->values_length == 0)
854 (void) FormatLocaleFile(file,"null,");
855 else
856 {
857 (void) FormatLocaleFile(file,"[");
858 for (j=0; j < value->values_length; j++)
859 {
860 for (k=0; value->values[j][k] != (char *) NULL; k++)
861 {
862 if (j > 0 || k > 0)
863 (void) FormatLocaleFile(file,",");
864 JsonFormatLocaleFile(file,"%s",value->values[j][k]);
865 value->values[j][k]=(char *) RelinquishMagickMemory(
866 value->values[j][k]);
867 }
868 value->values[j]=(char **) RelinquishMagickMemory(
869 value->values[j]);
870 }
871 value->values=(char ***) RelinquishMagickMemory(value->values);
872 (void) FormatLocaleFile(file,"],\n");
873 }
874 values[i]=(IptcValue *) RelinquishMagickMemory(values[i]);
875 }
876 values=(IptcValue **) RelinquishMagickMemory(values);
877 }
878}
879
cristy9e818982014-01-18 14:54:37 +0000880static MagickBooleanType EncodeImageAttributes(Image *image,FILE *file,
881 ExceptionInfo *exception)
882{
cristy9e818982014-01-18 14:54:37 +0000883 char
cristy151b66d2015-04-15 10:50:31 +0000884 color[MagickPathExtent],
885 format[MagickPathExtent],
886 key[MagickPathExtent];
cristy9e818982014-01-18 14:54:37 +0000887
888 ChannelFeatures
889 *channel_features;
890
891 ChannelMoments
892 *channel_moments;
893
cristydc395172014-02-23 01:33:36 +0000894 ChannelPerceptualHash
895 *channel_phash;
896
cristy9e818982014-01-18 14:54:37 +0000897 ChannelStatistics
898 *channel_statistics;
899
Cristy519e0052016-10-16 15:28:40 -0400900 char
901 *url;
902
cristy9e818982014-01-18 14:54:37 +0000903 ColorspaceType
904 colorspace;
905
906 const char
907 *artifact,
908 *locate,
909 *name,
910 *property,
911 *registry,
912 *value;
913
914 const MagickInfo
915 *magick_info;
916
917 double
918 elapsed_time,
919 user_time;
920
921 ImageType
dirkab4f0bb2015-07-25 11:46:32 +0000922 base_type,
cristy9e818982014-01-18 14:54:37 +0000923 type;
924
925 MagickBooleanType
926 ping;
927
928 register const Quantum
929 *p;
930
931 register ssize_t
932 i,
933 x;
934
935 size_t
dirk6c316f22015-06-27 15:44:29 +0000936 depth,
cristy9e818982014-01-18 14:54:37 +0000937 distance,
938 scale;
939
940 ssize_t
941 y;
942
943 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000944 assert(image->signature == MagickCoreSignature);
cristy9e818982014-01-18 14:54:37 +0000945 if (image->debug != MagickFalse)
946 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristya10f7442014-01-19 18:32:15 +0000947 *format='\0';
948 elapsed_time=GetElapsedTime(&image->timer);
949 user_time=GetUserTime(&image->timer);
950 GetTimerInfo(&image->timer);
951 p=GetVirtualPixels(image,0,0,1,1,exception);
952 ping=p == (const Quantum *) NULL ? MagickTrue : MagickFalse;
cristye1c94d92015-06-28 12:16:33 +0000953 (void) ping;
cristya10f7442014-01-19 18:32:15 +0000954 (void) SignatureImage(image,exception);
dirk6c316f22015-06-27 15:44:29 +0000955 JsonFormatLocaleFile(file,"{\n \"image\": {\n \"name\": %s,\n",
956 image->filename);
cristya10f7442014-01-19 18:32:15 +0000957 if (*image->magick_filename != '\0')
958 if (LocaleCompare(image->magick_filename,image->filename) != 0)
959 {
960 char
cristy151b66d2015-04-15 10:50:31 +0000961 filename[MagickPathExtent];
cristya10f7442014-01-19 18:32:15 +0000962
963 GetPathComponent(image->magick_filename,TailPath,filename);
dirk6c316f22015-06-27 15:44:29 +0000964 JsonFormatLocaleFile(file," \"baseName\": %s,\n",filename);
cristya10f7442014-01-19 18:32:15 +0000965 }
dirk6c316f22015-06-27 15:44:29 +0000966 JsonFormatLocaleFile(file," \"format\": %s,\n",image->magick);
cristya10f7442014-01-19 18:32:15 +0000967 magick_info=GetMagickInfo(image->magick,exception);
dirk6c316f22015-06-27 15:44:29 +0000968 if ((magick_info != (const MagickInfo *) NULL) &&
969 (GetMagickDescription(magick_info) != (const char *) NULL))
970 JsonFormatLocaleFile(file," \"formatDescription\": %s,\n",
971 image->magick);
cristy12bcd3d2015-01-28 00:54:23 +0000972 if ((magick_info != (const MagickInfo *) NULL) &&
cristya10f7442014-01-19 18:32:15 +0000973 (GetMagickMimeType(magick_info) != (const char *) NULL))
dirk6c316f22015-06-27 15:44:29 +0000974 JsonFormatLocaleFile(file," \"mimeType\": %s,\n",GetMagickMimeType(
cristya10f7442014-01-19 18:32:15 +0000975 magick_info));
dirk6c316f22015-06-27 15:44:29 +0000976 JsonFormatLocaleFile(file," \"class\": %s,\n",CommandOptionToMnemonic(
cristya10f7442014-01-19 18:32:15 +0000977 MagickClassOptions,(ssize_t) image->storage_class));
dirk6c316f22015-06-27 15:44:29 +0000978 (void) FormatLocaleFile(file," \"geometry\": {\n"
979 " \"width\": %.20g,\n \"height\": %.20g,\n"
980 " \"x\": %.20g,\n \"y\": %.20g\n },\n",
981 (double) image->columns,(double) image->rows,(double) image->tile_offset.x,
982 (double) image->tile_offset.y);
cristya10f7442014-01-19 18:32:15 +0000983 if ((image->magick_columns != 0) || (image->magick_rows != 0))
984 if ((image->magick_columns != image->columns) ||
985 (image->magick_rows != image->rows))
dirk6c316f22015-06-27 15:44:29 +0000986 (void) FormatLocaleFile(file," \"baseGeometry\": {\n"
987 " \"width\": %.20g,\n \"height\": %.20g\n },\n",
988 (double) image->magick_columns,(double) image->magick_rows);
cristya10f7442014-01-19 18:32:15 +0000989 if ((image->resolution.x != 0.0) && (image->resolution.y != 0.0))
990 {
dirk6c316f22015-06-27 15:44:29 +0000991 (void) FormatLocaleFile(file," \"resolution\": {\n"
992 " \"x\": %.20g,\n \"y\": %.20g\n },\n",
993 image->resolution.x,image->resolution.y);
994 (void) FormatLocaleFile(file," \"printSize\": {\n"
995 " \"x\": %.20g,\n \"y\": %.20g\n },\n",
cristya10f7442014-01-19 18:32:15 +0000996 image->columns/image->resolution.x,(double) image->rows/
997 image->resolution.y);
998 }
dirk6c316f22015-06-27 15:44:29 +0000999 JsonFormatLocaleFile(file," \"units\": %s,\n",CommandOptionToMnemonic(
cristya10f7442014-01-19 18:32:15 +00001000 MagickResolutionOptions,(ssize_t) image->units));
dirkab4f0bb2015-07-25 11:46:32 +00001001 colorspace=image->colorspace;
1002 type=IdentifyImageType(image,exception);
1003 if ((type == BilevelType) || (type == GrayscaleType) ||
1004 (type == GrayscaleAlphaType))
1005 colorspace=GRAYColorspace;
dirk6c316f22015-06-27 15:44:29 +00001006 JsonFormatLocaleFile(file," \"type\": %s,\n",CommandOptionToMnemonic(
cristya10f7442014-01-19 18:32:15 +00001007 MagickTypeOptions,(ssize_t) type));
dirkab4f0bb2015-07-25 11:46:32 +00001008 base_type=GetImageType(image);
1009 if (type != base_type)
dirk6c316f22015-06-27 15:44:29 +00001010 JsonFormatLocaleFile(file," \"baseType\": %s,\n",
dirkab4f0bb2015-07-25 11:46:32 +00001011 CommandOptionToMnemonic(MagickTypeOptions,(ssize_t) base_type));
dirk6c316f22015-06-27 15:44:29 +00001012 JsonFormatLocaleFile(file," \"endianess\": %s,\n",
1013 CommandOptionToMnemonic(MagickEndianOptions,(ssize_t) image->endian));
cristy9e818982014-01-18 14:54:37 +00001014 locate=GetImageArtifact(image,"identify:locate");
cristy3e583dd2014-01-19 14:11:51 +00001015 if (locate == (const char *) NULL)
1016 locate=GetImageArtifact(image,"json:locate");
cristy9e818982014-01-18 14:54:37 +00001017 if (locate != (const char *) NULL)
1018 {
1019 const char
1020 *limit;
1021
1022 size_t
1023 max_locations;
1024
1025 StatisticType
1026 type;
1027
1028 /*
1029 Display minimum, maximum, or mean pixel locations.
1030 */
1031 type=(StatisticType) ParseCommandOption(MagickStatisticOptions,
1032 MagickFalse,locate);
1033 limit=GetImageArtifact(image,"identify:limit");
cristy3e583dd2014-01-19 14:11:51 +00001034 if (limit == (const char *) NULL)
1035 limit=GetImageArtifact(image,"json:limit");
cristy9e818982014-01-18 14:54:37 +00001036 max_locations=0;
1037 if (limit != (const char *) NULL)
1038 max_locations=StringToUnsignedLong(limit);
1039 channel_statistics=GetLocationStatistics(image,type,exception);
1040 if (channel_statistics == (ChannelStatistics *) NULL)
1041 return(MagickFalse);
dirk6c316f22015-06-27 15:44:29 +00001042 (void) FormatLocaleFile(file," \"channel%s\": {\n",locate);
1043 if (image->alpha_trait != UndefinedPixelTrait)
1044 (void) PrintChannelLocations(file,image,AlphaPixelChannel,"Alpha",
1045 type,max_locations,MagickTrue,channel_statistics);
cristy9e818982014-01-18 14:54:37 +00001046 switch (colorspace)
1047 {
1048 case RGBColorspace:
1049 default:
1050 {
1051 (void) PrintChannelLocations(file,image,RedPixelChannel,"Red",
dirk6c316f22015-06-27 15:44:29 +00001052 type,max_locations,MagickTrue,channel_statistics);
cristy9e818982014-01-18 14:54:37 +00001053 (void) PrintChannelLocations(file,image,GreenPixelChannel,"Green",
dirk6c316f22015-06-27 15:44:29 +00001054 type,max_locations,MagickTrue,channel_statistics);
cristy9e818982014-01-18 14:54:37 +00001055 (void) PrintChannelLocations(file,image,BluePixelChannel,"Blue",
dirk6c316f22015-06-27 15:44:29 +00001056 type,max_locations,MagickFalse,channel_statistics);
cristy9e818982014-01-18 14:54:37 +00001057 break;
1058 }
1059 case CMYKColorspace:
1060 {
1061 (void) PrintChannelLocations(file,image,CyanPixelChannel,"Cyan",
dirk6c316f22015-06-27 15:44:29 +00001062 type,max_locations,MagickTrue,channel_statistics);
cristy9e818982014-01-18 14:54:37 +00001063 (void) PrintChannelLocations(file,image,MagentaPixelChannel,"Magenta",
dirk6c316f22015-06-27 15:44:29 +00001064 type,max_locations,MagickTrue,channel_statistics);
cristy9e818982014-01-18 14:54:37 +00001065 (void) PrintChannelLocations(file,image,YellowPixelChannel,"Yellow",
dirk6c316f22015-06-27 15:44:29 +00001066 type,max_locations,MagickTrue,channel_statistics);
cristy9e818982014-01-18 14:54:37 +00001067 (void) PrintChannelLocations(file,image,BlackPixelChannel,"Black",
dirk6c316f22015-06-27 15:44:29 +00001068 type,max_locations,MagickFalse,channel_statistics);
cristy9e818982014-01-18 14:54:37 +00001069 break;
1070 }
1071 case GRAYColorspace:
1072 {
1073 (void) PrintChannelLocations(file,image,GrayPixelChannel,"Gray",
dirk6c316f22015-06-27 15:44:29 +00001074 type,max_locations,MagickFalse,channel_statistics);
cristy9e818982014-01-18 14:54:37 +00001075 break;
1076 }
1077 }
dirk6c316f22015-06-27 15:44:29 +00001078 (void) FormatLocaleFile(file," },\n");
cristy9e818982014-01-18 14:54:37 +00001079 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1080 channel_statistics);
cristy9e818982014-01-18 14:54:37 +00001081 }
cristy9e818982014-01-18 14:54:37 +00001082 /*
1083 Detail channel depth and extrema.
1084 */
dirk6c316f22015-06-27 15:44:29 +00001085 JsonFormatLocaleFile(file," \"colorspace\": %s,\n",
1086 CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
1087 image->colorspace));
cristy9e818982014-01-18 14:54:37 +00001088 channel_statistics=(ChannelStatistics *) NULL;
1089 channel_moments=(ChannelMoments *) NULL;
cristydc395172014-02-23 01:33:36 +00001090 channel_phash=(ChannelPerceptualHash *) NULL;
cristy9e818982014-01-18 14:54:37 +00001091 channel_features=(ChannelFeatures *) NULL;
cristy9e818982014-01-18 14:54:37 +00001092 scale=1;
dirk6c316f22015-06-27 15:44:29 +00001093 channel_statistics=GetImageStatistics(image,exception);
1094 if (channel_statistics == (ChannelStatistics *) NULL)
1095 return(MagickFalse);
1096 artifact=GetImageArtifact(image,"identify:moments");
1097 if (artifact == (const char *) NULL)
1098 artifact=GetImageArtifact(image,"json:moments");
1099 if (artifact != (const char *) NULL)
cristy9e818982014-01-18 14:54:37 +00001100 {
dirk6c316f22015-06-27 15:44:29 +00001101 channel_moments=GetImageMoments(image,exception);
1102 channel_phash=GetImagePerceptualHash(image,exception);
cristy9e818982014-01-18 14:54:37 +00001103 }
dirk6c316f22015-06-27 15:44:29 +00001104 artifact=GetImageArtifact(image,"identify:features");
1105 if (artifact == (const char *) NULL)
1106 artifact=GetImageArtifact(image,"json:features");
1107 if (artifact != (const char *) NULL)
1108 {
1109 distance=StringToUnsignedLong(artifact);
1110 channel_features=GetImageFeatures(image,distance,exception);
1111 }
1112 depth=GetImageDepth(image,exception);
1113 (void) FormatLocaleFile(file," \"depth\": %.20g,\n",(double) depth);
1114 (void) FormatLocaleFile(file," \"baseDepth\": %.20g,\n",(double)
1115 image->depth);
1116 (void) FormatLocaleFile(file," \"channelDepth\": {\n");
dirk6c316f22015-06-27 15:44:29 +00001117 if (image->alpha_trait != UndefinedPixelTrait)
1118 (void) FormatLocaleFile(file," \"alpha\": %.20g,\n",(double)
1119 channel_statistics[AlphaPixelChannel].depth);
1120 switch (colorspace)
1121 {
1122 case RGBColorspace:
1123 default:
1124 {
1125 (void) FormatLocaleFile(file," \"red\": %.20g,\n",(double)
1126 channel_statistics[RedChannel].depth);
1127 (void) FormatLocaleFile(file," \"green\": %.20g,\n",(double)
1128 channel_statistics[GreenChannel].depth);
1129 (void) FormatLocaleFile(file," \"blue\": %.20g\n",(double)
1130 channel_statistics[BlueChannel].depth);
1131 break;
1132 }
1133 case CMYKColorspace:
1134 {
1135 (void) FormatLocaleFile(file," \"cyan\": %.20g,\n",(double)
1136 channel_statistics[CyanChannel].depth);
1137 (void) FormatLocaleFile(file," \"magenta\": %.20g,\n",(double)
1138 channel_statistics[MagentaChannel].depth);
1139 (void) FormatLocaleFile(file," \"yellow\": %.20g,\n",(double)
1140 channel_statistics[YellowChannel].depth);
1141 (void) FormatLocaleFile(file," \"black\": %.20g\n",(double)
1142 channel_statistics[BlackChannel].depth);
1143 break;
1144 }
1145 case GRAYColorspace:
1146 {
1147 (void) FormatLocaleFile(file," \"gray\": %.20g\n",(double)
1148 channel_statistics[GrayChannel].depth);
1149 break;
1150 }
1151 }
1152 (void) FormatLocaleFile(file," },\n");
1153 scale=1;
1154 if (image->depth <= MAGICKCORE_QUANTUM_DEPTH)
1155 scale=QuantumRange/((size_t) QuantumRange >> ((size_t)
1156 MAGICKCORE_QUANTUM_DEPTH-image->depth));
cristy9e818982014-01-18 14:54:37 +00001157 if (channel_statistics != (ChannelStatistics *) NULL)
1158 {
dirk6c316f22015-06-27 15:44:29 +00001159 (void) FormatLocaleFile(file," \"pixels\": %.20g,\n",
cristy9e818982014-01-18 14:54:37 +00001160 channel_statistics[CompositePixelChannel].area);
dirk6c316f22015-06-27 15:44:29 +00001161 if (colorspace != GRAYColorspace)
1162 {
1163 (void) FormatLocaleFile(file," \"imageStatistics\": {\n");
1164 (void) PrintChannelStatistics(file,(PixelChannel) MaxPixelChannels,
1165 "Overall",1.0/scale,MagickFalse,channel_statistics);
1166 (void) FormatLocaleFile(file," },\n");
1167 }
1168 (void) FormatLocaleFile(file," \"channelStatistics\": {\n");
1169 if (image->alpha_trait != UndefinedPixelTrait)
1170 (void) PrintChannelStatistics(file,AlphaPixelChannel,"Alpha",1.0/scale,
1171 MagickTrue,channel_statistics);
cristy9e818982014-01-18 14:54:37 +00001172 switch (colorspace)
1173 {
1174 case RGBColorspace:
1175 default:
1176 {
dirk6c316f22015-06-27 15:44:29 +00001177 (void) PrintChannelStatistics(file,RedPixelChannel,"Red",1.0/scale,
1178 MagickTrue,channel_statistics);
cristy9e818982014-01-18 14:54:37 +00001179 (void) PrintChannelStatistics(file,GreenPixelChannel,"Green",1.0/
dirk6c316f22015-06-27 15:44:29 +00001180 scale,MagickTrue,channel_statistics);
1181 (void) PrintChannelStatistics(file,BluePixelChannel,"Blue",1.0/scale,
1182 MagickFalse,channel_statistics);
cristy9e818982014-01-18 14:54:37 +00001183 break;
1184 }
1185 case CMYKColorspace:
1186 {
dirk6c316f22015-06-27 15:44:29 +00001187 (void) PrintChannelStatistics(file,CyanPixelChannel,"Cyan",1.0/scale,
1188 MagickTrue,channel_statistics);
cristy9e818982014-01-18 14:54:37 +00001189 (void) PrintChannelStatistics(file,MagentaPixelChannel,"Magenta",1.0/
dirk6c316f22015-06-27 15:44:29 +00001190 scale,MagickTrue,channel_statistics);
cristy9e818982014-01-18 14:54:37 +00001191 (void) PrintChannelStatistics(file,YellowPixelChannel,"Yellow",1.0/
dirk6c316f22015-06-27 15:44:29 +00001192 scale,MagickTrue,channel_statistics);
cristy9e818982014-01-18 14:54:37 +00001193 (void) PrintChannelStatistics(file,BlackPixelChannel,"Black",1.0/
dirk6c316f22015-06-27 15:44:29 +00001194 scale,MagickFalse,channel_statistics);
cristy9e818982014-01-18 14:54:37 +00001195 break;
1196 }
1197 case GRAYColorspace:
1198 {
dirk6c316f22015-06-27 15:44:29 +00001199 (void) PrintChannelStatistics(file,GrayPixelChannel,"Gray",1.0/scale,
1200 MagickFalse,channel_statistics);
cristy9e818982014-01-18 14:54:37 +00001201 break;
1202 }
1203 }
dirk6c316f22015-06-27 15:44:29 +00001204 (void) FormatLocaleFile(file," },\n");
cristy9e818982014-01-18 14:54:37 +00001205 channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
1206 channel_statistics);
1207 }
1208 if (channel_moments != (ChannelMoments *) NULL)
1209 {
dirk6c316f22015-06-27 15:44:29 +00001210 (void) FormatLocaleFile(file," \"channelMoments\": {\n");
1211 if (image->alpha_trait != UndefinedPixelTrait)
1212 (void) PrintChannelMoments(file,AlphaPixelChannel,"Alpha",MagickTrue,
1213 channel_moments);
cristy9e818982014-01-18 14:54:37 +00001214 switch (colorspace)
1215 {
1216 case RGBColorspace:
1217 default:
1218 {
dirk6c316f22015-06-27 15:44:29 +00001219 (void) PrintChannelMoments(file,RedPixelChannel,"Red",MagickTrue,
cristy9e818982014-01-18 14:54:37 +00001220 channel_moments);
dirk6c316f22015-06-27 15:44:29 +00001221 (void) PrintChannelMoments(file,GreenPixelChannel,"Green",MagickTrue,
cristy9e818982014-01-18 14:54:37 +00001222 channel_moments);
dirk6c316f22015-06-27 15:44:29 +00001223 (void) PrintChannelMoments(file,BluePixelChannel,"Blue",MagickFalse,
cristy9e818982014-01-18 14:54:37 +00001224 channel_moments);
1225 break;
1226 }
1227 case CMYKColorspace:
1228 {
dirk6c316f22015-06-27 15:44:29 +00001229 (void) PrintChannelMoments(file,CyanPixelChannel,"Cyan",MagickTrue,
cristy9e818982014-01-18 14:54:37 +00001230 channel_moments);
1231 (void) PrintChannelMoments(file,MagentaPixelChannel,"Magenta",
dirk6c316f22015-06-27 15:44:29 +00001232 MagickTrue,channel_moments);
cristy9e818982014-01-18 14:54:37 +00001233 (void) PrintChannelMoments(file,YellowPixelChannel,"Yellow",
dirk6c316f22015-06-27 15:44:29 +00001234 MagickTrue,channel_moments);
cristy9e818982014-01-18 14:54:37 +00001235 (void) PrintChannelMoments(file,BlackPixelChannel,"Black",
dirk6c316f22015-06-27 15:44:29 +00001236 MagickFalse,channel_moments);
cristy9e818982014-01-18 14:54:37 +00001237 break;
1238 }
1239 case GRAYColorspace:
1240 {
dirk6c316f22015-06-27 15:44:29 +00001241 (void) PrintChannelMoments(file,GrayPixelChannel,"Gray",MagickFalse,
cristy9e818982014-01-18 14:54:37 +00001242 channel_moments);
1243 break;
1244 }
1245 }
dirk6c316f22015-06-27 15:44:29 +00001246 (void) FormatLocaleFile(file," },\n");
cristy9e818982014-01-18 14:54:37 +00001247 channel_moments=(ChannelMoments *) RelinquishMagickMemory(
1248 channel_moments);
1249 }
cristydc395172014-02-23 01:33:36 +00001250 if (channel_phash != (ChannelPerceptualHash *) NULL)
1251 {
1252 (void) FormatLocaleFile(file," \"channelPerceptualHash\": {\n");
Cristye6961802016-09-03 11:31:13 -04001253 (void) PrintChannelPerceptualHash(image,file,channel_phash);
cristydc395172014-02-23 01:33:36 +00001254 (void) FormatLocaleFile(file," },\n");
1255 channel_phash=(ChannelPerceptualHash *) RelinquishMagickMemory(
1256 channel_phash);
1257 }
cristy9e818982014-01-18 14:54:37 +00001258 if (channel_features != (ChannelFeatures *) NULL)
1259 {
dirk6c316f22015-06-27 15:44:29 +00001260 (void) FormatLocaleFile(file," \"channelFeatures\": {\n");
1261 if (image->alpha_trait != UndefinedPixelTrait)
1262 (void) PrintChannelFeatures(file,AlphaPixelChannel,"Alpha",MagickTrue,
1263 channel_features);
cristy9e818982014-01-18 14:54:37 +00001264 switch (colorspace)
1265 {
1266 case RGBColorspace:
1267 default:
1268 {
dirk6c316f22015-06-27 15:44:29 +00001269 (void) PrintChannelFeatures(file,RedPixelChannel,"Red",MagickTrue,
cristy9e818982014-01-18 14:54:37 +00001270 channel_features);
1271 (void) PrintChannelFeatures(file,GreenPixelChannel,"Green",
dirk6c316f22015-06-27 15:44:29 +00001272 MagickTrue,channel_features);
1273 (void) PrintChannelFeatures(file,BluePixelChannel,"Blue",MagickFalse,
cristy9e818982014-01-18 14:54:37 +00001274 channel_features);
1275 break;
1276 }
1277 case CMYKColorspace:
1278 {
dirk6c316f22015-06-27 15:44:29 +00001279 (void) PrintChannelFeatures(file,CyanPixelChannel,"Cyan",MagickTrue,
cristy9e818982014-01-18 14:54:37 +00001280 channel_features);
1281 (void) PrintChannelFeatures(file,MagentaPixelChannel,"Magenta",
dirk6c316f22015-06-27 15:44:29 +00001282 MagickTrue,channel_features);
cristy9e818982014-01-18 14:54:37 +00001283 (void) PrintChannelFeatures(file,YellowPixelChannel,"Yellow",
dirk6c316f22015-06-27 15:44:29 +00001284 MagickTrue,channel_features);
cristy9e818982014-01-18 14:54:37 +00001285 (void) PrintChannelFeatures(file,BlackPixelChannel,"Black",
dirk6c316f22015-06-27 15:44:29 +00001286 MagickFalse,channel_features);
cristy9e818982014-01-18 14:54:37 +00001287 break;
1288 }
1289 case GRAYColorspace:
1290 {
dirk6c316f22015-06-27 15:44:29 +00001291 (void) PrintChannelFeatures(file,GrayPixelChannel,"Gray",MagickFalse,
cristy9e818982014-01-18 14:54:37 +00001292 channel_features);
1293 break;
1294 }
1295 }
dirk6c316f22015-06-27 15:44:29 +00001296 (void) FormatLocaleFile(file," },\n");
cristy9e818982014-01-18 14:54:37 +00001297 channel_features=(ChannelFeatures *) RelinquishMagickMemory(
1298 channel_features);
1299 }
dirk6c316f22015-06-27 15:44:29 +00001300 if (image->colorspace == CMYKColorspace)
1301 (void) FormatLocaleFile(file," \"totalInkDensity\": \"%.*g%%\",\n",
cristye1c94d92015-06-28 12:16:33 +00001302 GetMagickPrecision(),100.0*GetImageTotalInkDensity(image,exception)/
1303 (double) QuantumRange);
dirk6c316f22015-06-27 15:44:29 +00001304 x=0;
1305 if (image->alpha_trait != UndefinedPixelTrait)
1306 {
1307 register const Quantum
1308 *p;
1309
1310 p=(const Quantum *) NULL;
1311 for (y=0; y < (ssize_t) image->rows; y++)
cristy9e818982014-01-18 14:54:37 +00001312 {
dirk6c316f22015-06-27 15:44:29 +00001313 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1314 if (p == (const Quantum *) NULL)
1315 break;
1316 for (x=0; x < (ssize_t) image->columns; x++)
cristy9e818982014-01-18 14:54:37 +00001317 {
dirk6c316f22015-06-27 15:44:29 +00001318 if (GetPixelAlpha(image,p) == (Quantum) TransparentAlpha)
cristy9e818982014-01-18 14:54:37 +00001319 break;
dirk6c316f22015-06-27 15:44:29 +00001320 p+=GetPixelChannels(image);
cristy9e818982014-01-18 14:54:37 +00001321 }
dirk6c316f22015-06-27 15:44:29 +00001322 if (x < (ssize_t) image->columns)
1323 break;
cristy9e818982014-01-18 14:54:37 +00001324 }
dirk6c316f22015-06-27 15:44:29 +00001325 if ((x < (ssize_t) image->columns) || (y < (ssize_t) image->rows))
1326 {
1327 PixelInfo
1328 pixel;
1329
1330 GetPixelInfo(image,&pixel);
1331 GetPixelInfoPixel(image,p,&pixel);
1332 GetColorTuple(&pixel,MagickTrue,color);
1333 (void) FormatLocaleFile(file," \"alpha\": \"%s\",\n",color);
1334 }
1335 }
cristy9e818982014-01-18 14:54:37 +00001336 if (image->storage_class == PseudoClass)
1337 {
dirk6c316f22015-06-27 15:44:29 +00001338 register PixelInfo
dirk05d2ff72015-11-18 23:13:43 +01001339 *magick_restrict p;
cristy9e818982014-01-18 14:54:37 +00001340
dirk6c316f22015-06-27 15:44:29 +00001341 (void) FormatLocaleFile(file," \"colormapEntries\": %.20g,\n",
1342 (double) image->colors);
1343 (void) FormatLocaleFile(file," \"colormap\": [\n ");
1344 p=image->colormap;
1345 for (i=0; i < (ssize_t) image->colors; i++)
1346 {
1347 GetColorTuple(p,MagickTrue,color);
1348 (void) FormatLocaleFile(file,"\"%s\"",color);
1349 if (i < (ssize_t) (image->colors-1))
1350 (void) FormatLocaleFile(file,",");
1351 if (((i+1) % 5) == 0)
1352 (void) FormatLocaleFile(file,"\n ");
1353 p++;
1354 }
1355 (void) FormatLocaleFile(file,"\n ],\n");
cristy9e818982014-01-18 14:54:37 +00001356 }
1357 if (image->error.mean_error_per_pixel != 0.0)
dirk6c316f22015-06-27 15:44:29 +00001358 (void) FormatLocaleFile(file," \"meanErrorPerPixel\": \"%g\",\n",
cristy9e818982014-01-18 14:54:37 +00001359 image->error.mean_error_per_pixel);
1360 if (image->error.normalized_mean_error != 0.0)
dirk6c316f22015-06-27 15:44:29 +00001361 (void) FormatLocaleFile(file," \"normalizedMeanError\": \"%g\",\n",
cristy9e818982014-01-18 14:54:37 +00001362 image->error.normalized_mean_error);
1363 if (image->error.normalized_maximum_error != 0.0)
dirk6c316f22015-06-27 15:44:29 +00001364 (void) FormatLocaleFile(file," \"normalizedMaximumError\": \"%g\",\n",
cristy9e818982014-01-18 14:54:37 +00001365 image->error.normalized_maximum_error);
dirk6c316f22015-06-27 15:44:29 +00001366 JsonFormatLocaleFile(file," \"renderingIntent\": %s,\n",
cristy9e818982014-01-18 14:54:37 +00001367 CommandOptionToMnemonic(MagickIntentOptions,(ssize_t)
1368 image->rendering_intent));
1369 if (image->gamma != 0.0)
dirk6c316f22015-06-27 15:44:29 +00001370 (void) FormatLocaleFile(file," \"gamma\": %g,\n",image->gamma);
cristy9e818982014-01-18 14:54:37 +00001371 if ((image->chromaticity.red_primary.x != 0.0) ||
1372 (image->chromaticity.green_primary.x != 0.0) ||
1373 (image->chromaticity.blue_primary.x != 0.0) ||
1374 (image->chromaticity.white_point.x != 0.0))
1375 {
1376 /*
1377 Display image chromaticity.
1378 */
dirk6c316f22015-06-27 15:44:29 +00001379 (void) FormatLocaleFile(file," \"chromaticity\": {\n");
1380 (void) FormatLocaleFile(file," \"redPrimary\": {\n"
1381 " \"x\": %g,\n \"y\": %g\n },\n",
cristy9e818982014-01-18 14:54:37 +00001382 image->chromaticity.red_primary.x,image->chromaticity.red_primary.y);
dirk6c316f22015-06-27 15:44:29 +00001383 (void) FormatLocaleFile(file," \"greenPrimary\": {\n"
1384 " \"x\": %g,\n \"y\": %g\n },\n",
cristy9e818982014-01-18 14:54:37 +00001385 image->chromaticity.green_primary.x,
1386 image->chromaticity.green_primary.y);
dirk6c316f22015-06-27 15:44:29 +00001387 (void) FormatLocaleFile(file," \"bluePrimary\": {\n"
1388 " \"x\": %g,\n \"y\": %g\n },\n",
cristy9e818982014-01-18 14:54:37 +00001389 image->chromaticity.blue_primary.x,image->chromaticity.blue_primary.y);
dirk6c316f22015-06-27 15:44:29 +00001390 (void) FormatLocaleFile(file," \"whitePrimary\": {\n"
1391 " \"x\": %g,\n \"y\": %g\n }\n",
cristy9e818982014-01-18 14:54:37 +00001392 image->chromaticity.white_point.x,image->chromaticity.white_point.y);
dirk6c316f22015-06-27 15:44:29 +00001393 (void) FormatLocaleFile(file," },\n");
cristy9e818982014-01-18 14:54:37 +00001394 }
1395 if ((image->extract_info.width*image->extract_info.height) != 0)
dirk6c316f22015-06-27 15:44:29 +00001396 (void) FormatLocaleFile(file," \"tileGeometry\": {\n"
1397 " \"width\": %.20g,\n \"height\": %.20g,\n"
1398 " \"x\": %.20g,\n \"y\": %.20g\n },\n",
cristy9e818982014-01-18 14:54:37 +00001399 (double) image->extract_info.width,(double) image->extract_info.height,
1400 (double) image->extract_info.x,(double) image->extract_info.y);
Cristy18b27502017-02-16 07:29:19 -05001401 GetColorTuple(&image->matte_color,MagickTrue,color);
1402 (void) FormatLocaleFile(file," \"matteColor\": \"%s\",\n",color);
dirk6c316f22015-06-27 15:44:29 +00001403 GetColorTuple(&image->background_color,MagickTrue,color);
1404 (void) FormatLocaleFile(file," \"backgroundColor\": \"%s\",\n",color);
1405 GetColorTuple(&image->border_color,MagickTrue,color);
1406 (void) FormatLocaleFile(file," \"borderColor\": \"%s\",\n",color);
dirk6c316f22015-06-27 15:44:29 +00001407 GetColorTuple(&image->transparent_color,MagickTrue,color);
1408 (void) FormatLocaleFile(file," \"transparentColor\": \"%s\",\n",color);
1409 JsonFormatLocaleFile(file," \"interlace\": %s,\n",CommandOptionToMnemonic(
cristy9e818982014-01-18 14:54:37 +00001410 MagickInterlaceOptions,(ssize_t) image->interlace));
dirk6c316f22015-06-27 15:44:29 +00001411 JsonFormatLocaleFile(file," \"intensity\": %s,\n",CommandOptionToMnemonic(
cristy9e818982014-01-18 14:54:37 +00001412 MagickPixelIntensityOptions,(ssize_t) image->intensity));
dirk6c316f22015-06-27 15:44:29 +00001413 JsonFormatLocaleFile(file," \"compose\": %s,\n",
1414 CommandOptionToMnemonic(MagickComposeOptions,(ssize_t) image->compose));
cristy9e818982014-01-18 14:54:37 +00001415 if ((image->page.width != 0) || (image->page.height != 0) ||
1416 (image->page.x != 0) || (image->page.y != 0))
dirk6c316f22015-06-27 15:44:29 +00001417 (void) FormatLocaleFile(file," \"pageGeometry\": {\n"
1418 " \"width\": %.20g,\n \"height\": %.20g,\n"
1419 " \"x\": %.20g,\n \"y\": %.20g\n },\n",
1420 (double) image->page.width,(double) image->page.height,
1421 (double) image->page.x,(double) image->page.y);
cristy9e818982014-01-18 14:54:37 +00001422 if ((image->page.x != 0) || (image->page.y != 0))
dirk6c316f22015-06-27 15:44:29 +00001423 (void) FormatLocaleFile(file," \"originGeometry\": %+.20g%+.20g\n",
1424 (double) image->page.x,(double) image->page.y);
1425 JsonFormatLocaleFile(file," \"dispose\": %s,\n",
1426 CommandOptionToMnemonic(MagickDisposeOptions,(ssize_t) image->dispose));
cristy9e818982014-01-18 14:54:37 +00001427 if (image->delay != 0)
dirk6c316f22015-06-27 15:44:29 +00001428 (void) FormatLocaleFile(file," \"delay\": \"%.20gx%.20g\"\n",
1429 (double) image->delay,(double) image->ticks_per_second);
cristy9e818982014-01-18 14:54:37 +00001430 if (image->iterations != 1)
dirk6c316f22015-06-27 15:44:29 +00001431 (void) FormatLocaleFile(file," \"iterations\": %.20g,\n",(double)
cristy9e818982014-01-18 14:54:37 +00001432 image->iterations);
1433 if ((image->next != (Image *) NULL) || (image->previous != (Image *) NULL))
Dirk Lemstra3ea0d282017-09-21 13:31:12 +02001434 (void) FormatLocaleFile(file," \"scene\": %.20g,\n \"scenes\": "
1435 "%.20g,\n",(double) image->scene,(double) GetImageListLength(image));
cristy9e818982014-01-18 14:54:37 +00001436 else
1437 if (image->scene != 0)
dirk6c316f22015-06-27 15:44:29 +00001438 (void) FormatLocaleFile(file," \"scene\": %.20g,\n",(double)
1439 image->scene);
1440 JsonFormatLocaleFile(file," \"compression\": %s,\n",
1441 CommandOptionToMnemonic(MagickCompressOptions,(ssize_t)
1442 image->compression));
cristy9e818982014-01-18 14:54:37 +00001443 if (image->quality != UndefinedCompressionQuality)
dirk6c316f22015-06-27 15:44:29 +00001444 (void) FormatLocaleFile(file," \"quality\": %.20g,\n",(double)
1445 image->quality);
1446 JsonFormatLocaleFile(file," \"orientation\": %s,\n",
1447 CommandOptionToMnemonic(MagickOrientationOptions,(ssize_t)
1448 image->orientation));
cristy9e818982014-01-18 14:54:37 +00001449 if (image->montage != (char *) NULL)
dirk6c316f22015-06-27 15:44:29 +00001450 JsonFormatLocaleFile(file," \"montage\": \"%s\",\n",image->montage);
cristy9e818982014-01-18 14:54:37 +00001451 if (image->directory != (char *) NULL)
1452 {
1453 Image
1454 *tile;
1455
1456 ImageInfo
1457 *image_info;
1458
1459 register char
1460 *p,
1461 *q;
1462
1463 WarningHandler
1464 handler;
1465
1466 /*
1467 Display visual image directory.
1468 */
1469 image_info=AcquireImageInfo();
1470 (void) CloneString(&image_info->size,"64x64");
dirk6c316f22015-06-27 15:44:29 +00001471 (void) FormatLocaleFile(file," \"montageDirectory\": [");
1472 p=image->directory;
1473 while (*p != '\0')
cristy9e818982014-01-18 14:54:37 +00001474 {
1475 q=p;
1476 while ((*q != '\n') && (*q != '\0'))
1477 q++;
1478 (void) CopyMagickString(image_info->filename,p,(size_t) (q-p+1));
dirk6c316f22015-06-27 15:44:29 +00001479 p=q+1;
1480 JsonFormatLocaleFile(file,"{\n \"name\": %s",
1481 image_info->filename);
cristy9e818982014-01-18 14:54:37 +00001482 handler=SetWarningHandler((WarningHandler) NULL);
1483 tile=ReadImage(image_info,exception);
1484 (void) SetWarningHandler(handler);
1485 if (tile == (Image *) NULL)
1486 {
dirk6c316f22015-06-27 15:44:29 +00001487 (void) FormatLocaleFile(file," }");
cristy9e818982014-01-18 14:54:37 +00001488 continue;
1489 }
dirk6c316f22015-06-27 15:44:29 +00001490 (void) FormatLocaleFile(file,",\n \"info\": \"%.20gx%.20g %s\"",
1491 (double) tile->magick_columns,(double) tile->magick_rows,
1492 tile->magick);
cristy9e818982014-01-18 14:54:37 +00001493 (void) SignatureImage(tile,exception);
1494 ResetImagePropertyIterator(tile);
1495 property=GetNextImageProperty(tile);
1496 while (property != (const char *) NULL)
1497 {
dirk6c316f22015-06-27 15:44:29 +00001498 JsonFormatLocaleFile(file,",\n %s: ",property);
cristy9e818982014-01-18 14:54:37 +00001499 value=GetImageProperty(tile,property,exception);
dirk6c316f22015-06-27 15:44:29 +00001500 JsonFormatLocaleFile(file,"%s",value);
cristy9e818982014-01-18 14:54:37 +00001501 property=GetNextImageProperty(tile);
1502 }
1503 tile=DestroyImage(tile);
dirk6c316f22015-06-27 15:44:29 +00001504 if (*p != '\0')
1505 (void) FormatLocaleFile(file,"\n },");
1506 else
1507 (void) FormatLocaleFile(file,"\n }");
cristy9e818982014-01-18 14:54:37 +00001508 }
dirk6c316f22015-06-27 15:44:29 +00001509 (void) FormatLocaleFile(file,"],\n");
cristy9e818982014-01-18 14:54:37 +00001510 image_info=DestroyImageInfo(image_info);
1511 }
1512 (void) GetImageProperty(image,"exif:*",exception);
cristy55166322014-01-23 01:53:24 +00001513 (void) GetImageProperty(image,"icc:*",exception);
1514 (void) GetImageProperty(image,"iptc:*",exception);
1515 (void) GetImageProperty(image,"xmp:*",exception);
cristy9e818982014-01-18 14:54:37 +00001516 ResetImagePropertyIterator(image);
1517 property=GetNextImageProperty(image);
1518 if (property != (const char *) NULL)
1519 {
dirk6c316f22015-06-27 15:44:29 +00001520 size_t
1521 n;
1522
cristy9e818982014-01-18 14:54:37 +00001523 /*
1524 Display image properties.
1525 */
dirk6c316f22015-06-27 15:44:29 +00001526 n=0;
1527 (void) FormatLocaleFile(file," \"properties\": {\n");
cristy9e818982014-01-18 14:54:37 +00001528 while (property != (const char *) NULL)
1529 {
dirk6c316f22015-06-27 15:44:29 +00001530 if (n++ != 0)
1531 (void) FormatLocaleFile(file,",\n");
1532 JsonFormatLocaleFile(file," %s: ",property);
cristy9e818982014-01-18 14:54:37 +00001533 value=GetImageProperty(image,property,exception);
dirk6c316f22015-06-27 15:44:29 +00001534 JsonFormatLocaleFile(file,"%s",value);
cristy9e818982014-01-18 14:54:37 +00001535 property=GetNextImageProperty(image);
1536 }
dirk6c316f22015-06-27 15:44:29 +00001537 (void) FormatLocaleFile(file,"\n },\n");
cristy9e818982014-01-18 14:54:37 +00001538 }
cristy151b66d2015-04-15 10:50:31 +00001539 (void) FormatLocaleString(key,MagickPathExtent,"8BIM:1999,2998:#1");
cristy9e818982014-01-18 14:54:37 +00001540 value=GetImageProperty(image,key,exception);
1541 if (value != (const char *) NULL)
1542 {
1543 /*
1544 Display clipping path.
1545 */
dirk6c316f22015-06-27 15:44:29 +00001546 (void) FormatLocaleFile(file," \"clipping path\": {\n");
1547 JsonFormatLocaleFile(file,"%s\n",value);
1548 (void) FormatLocaleFile(file," },\n");
cristy9e818982014-01-18 14:54:37 +00001549 }
1550 ResetImageProfileIterator(image);
1551 name=GetNextImageProfile(image);
1552 if (name != (char *) NULL)
1553 {
1554 const StringInfo
1555 *profile;
1556
dirk6c316f22015-06-27 15:44:29 +00001557 size_t
1558 n;
1559
cristy9e818982014-01-18 14:54:37 +00001560 /*
1561 Identify image profiles.
1562 */
dirk6c316f22015-06-27 15:44:29 +00001563 n=0;
1564 (void) FormatLocaleFile(file," \"profiles\": {\n");
cristy9e818982014-01-18 14:54:37 +00001565 while (name != (char *) NULL)
1566 {
1567 profile=GetImageProfile(image,name);
1568 if (profile == (StringInfo *) NULL)
1569 continue;
dirk6c316f22015-06-27 15:44:29 +00001570 if (n++ != 0)
1571 (void) FormatLocaleFile(file,",\n");
1572 JsonFormatLocaleFile(file," %s: {\n",name);
cristy9e818982014-01-18 14:54:37 +00001573 if (LocaleCompare(name,"iptc") == 0)
Dirk Lemstra32b15502017-09-21 17:04:36 +02001574 EncodeIptcProfile(file,profile);
dirk6c316f22015-06-27 15:44:29 +00001575 (void) FormatLocaleFile(file," \"length\": \"%.20g\"",(double)
1576 GetStringInfoLength(profile));
1577 (void) FormatLocaleFile(file,"\n }");
cristy9e818982014-01-18 14:54:37 +00001578 name=GetNextImageProfile(image);
1579 }
dirk6c316f22015-06-27 15:44:29 +00001580 (void) FormatLocaleFile(file,"\n },\n");
cristy9e818982014-01-18 14:54:37 +00001581 }
1582 ResetImageArtifactIterator(image);
1583 artifact=GetNextImageArtifact(image);
1584 if (artifact != (const char *) NULL)
1585 {
dirk6c316f22015-06-27 15:44:29 +00001586 ssize_t
1587 n;
1588
cristy9e818982014-01-18 14:54:37 +00001589 /*
1590 Display image artifacts.
1591 */
dirk6c316f22015-06-27 15:44:29 +00001592 n=0;
1593 (void) FormatLocaleFile(file," \"artifacts\": {\n");
cristy9e818982014-01-18 14:54:37 +00001594 while (artifact != (const char *) NULL)
1595 {
dirk6c316f22015-06-27 15:44:29 +00001596 if (n++ != 0)
1597 (void) FormatLocaleFile(file,",\n");
1598 JsonFormatLocaleFile(file," %s: ",artifact);
cristy9e818982014-01-18 14:54:37 +00001599 value=GetImageArtifact(image,artifact);
dirk6c316f22015-06-27 15:44:29 +00001600 JsonFormatLocaleFile(file,"%s",value);
cristy9e818982014-01-18 14:54:37 +00001601 artifact=GetNextImageArtifact(image);
1602 }
dirk6c316f22015-06-27 15:44:29 +00001603 (void) FormatLocaleFile(file,"\n },\n");
cristy9e818982014-01-18 14:54:37 +00001604 }
1605 ResetImageRegistryIterator();
1606 registry=GetNextImageRegistry();
1607 if (registry != (const char *) NULL)
1608 {
dirk6c316f22015-06-27 15:44:29 +00001609 ssize_t
1610 n;
1611
cristy9e818982014-01-18 14:54:37 +00001612 /*
1613 Display image registry.
1614 */
dirk6c316f22015-06-27 15:44:29 +00001615 (void) FormatLocaleFile(file," \"registry\": {\n");
1616 n=0;
cristy9e818982014-01-18 14:54:37 +00001617 while (registry != (const char *) NULL)
1618 {
dirk6c316f22015-06-27 15:44:29 +00001619 if (n++ != 0)
1620 (void) FormatLocaleFile(file,",\n");
1621 JsonFormatLocaleFile(file," %s: ",registry);
cristy9e818982014-01-18 14:54:37 +00001622 value=(const char *) GetImageRegistry(StringRegistryType,registry,
1623 exception);
dirk6c316f22015-06-27 15:44:29 +00001624 JsonFormatLocaleFile(file,"%s",value);
cristy9e818982014-01-18 14:54:37 +00001625 registry=GetNextImageRegistry();
1626 }
dirk6c316f22015-06-27 15:44:29 +00001627 (void) FormatLocaleFile(file," },\n");
cristy9e818982014-01-18 14:54:37 +00001628 }
dirk6c316f22015-06-27 15:44:29 +00001629 (void) FormatLocaleFile(file," \"tainted\": %s,\n",
1630 image->taint != MagickFalse ? "true" : "false");
cristy151b66d2015-04-15 10:50:31 +00001631 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,"B",MagickPathExtent,
cristyd4618c02015-04-14 23:54:43 +00001632 format);
dirk6c316f22015-06-27 15:44:29 +00001633 JsonFormatLocaleFile(file," \"filesize\": %s,\n",format);
cristy9e818982014-01-18 14:54:37 +00001634 (void) FormatMagickSize((MagickSizeType) image->columns*image->rows,
cristy151b66d2015-04-15 10:50:31 +00001635 MagickFalse,"B",MagickPathExtent,format);
cristy9e818982014-01-18 14:54:37 +00001636 if (strlen(format) > 1)
1637 format[strlen(format)-1]='\0';
dirk6c316f22015-06-27 15:44:29 +00001638 JsonFormatLocaleFile(file," \"numberPixels\": %s,\n",format);
cristy9e818982014-01-18 14:54:37 +00001639 (void) FormatMagickSize((MagickSizeType) ((double) image->columns*image->rows/
cristy151b66d2015-04-15 10:50:31 +00001640 elapsed_time+0.5),MagickFalse,"B",MagickPathExtent,format);
dirk6c316f22015-06-27 15:44:29 +00001641 JsonFormatLocaleFile(file," \"pixelsPerSecond\": %s,\n",format);
1642 (void) FormatLocaleFile(file," \"userTime\": \"%0.3fu\",\n",user_time);
1643 (void) FormatLocaleFile(file," \"elapsedTime\": \"%lu:%02lu.%03lu\",\n",
1644 (unsigned long) (elapsed_time/60.0),(unsigned long) ceil(fmod(
1645 elapsed_time,60.0)),(unsigned long) (1000.0*(elapsed_time-floor(
1646 elapsed_time))));
Cristy519e0052016-10-16 15:28:40 -04001647 url=GetMagickHomeURL();
1648 JsonFormatLocaleFile(file," \"version\": %s\n",url);
1649 url=DestroyString(url);
dirk6c316f22015-06-27 15:44:29 +00001650 (void) FormatLocaleFile(file," }\n}\n");
cristy9e818982014-01-18 14:54:37 +00001651 (void) fflush(file);
1652 return(ferror(file) != 0 ? MagickFalse : MagickTrue);
1653}
1654
cristy3f07aa32014-01-18 01:16:33 +00001655static MagickBooleanType WriteJSONImage(const ImageInfo *image_info,
1656 Image *image,ExceptionInfo *exception)
1657{
dirk6c316f22015-06-27 15:44:29 +00001658 FILE
1659 *file;
1660
cristy3f07aa32014-01-18 01:16:33 +00001661 MagickBooleanType
1662 status;
1663
1664 MagickOffsetType
1665 scene;
1666
1667 /*
1668 Open output image file.
1669 */
1670 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001671 assert(image_info->signature == MagickCoreSignature);
cristy3f07aa32014-01-18 01:16:33 +00001672 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001673 assert(image->signature == MagickCoreSignature);
cristy3f07aa32014-01-18 01:16:33 +00001674 if (image->debug != MagickFalse)
1675 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1676 status=OpenBlob(image_info,image,WriteBlobMode,exception);
1677 if (status == MagickFalse)
1678 return(status);
dirk6c316f22015-06-27 15:44:29 +00001679 file=GetBlobFileHandle(image);
1680 if (file == (FILE *) NULL)
1681 file=stdout;
cristy3f07aa32014-01-18 01:16:33 +00001682 scene=0;
1683 do
1684 {
Dirk Lemstrac9105bd2017-09-21 13:39:00 +02001685 if (scene == 0)
1686 WriteBlobString(image,"[");
cristy9e818982014-01-18 14:54:37 +00001687 image->magick_columns=image->columns;
1688 image->magick_rows=image->rows;
dirk6c316f22015-06-27 15:44:29 +00001689 EncodeImageAttributes(image,file,exception);
cristy3f07aa32014-01-18 01:16:33 +00001690 if (GetNextImageInList(image) == (Image *) NULL)
dirk6c316f22015-06-27 15:44:29 +00001691 {
1692 WriteBlobString(image,"]");
1693 break;
1694 }
1695 WriteBlobString(image,",\n");
cristy3f07aa32014-01-18 01:16:33 +00001696 image=SyncNextImageInList(image);
1697 status=SetImageProgress(image,SaveImagesTag,scene++,
1698 GetImageListLength(image));
1699 if (status == MagickFalse)
1700 break;
1701 } while (image_info->adjoin != MagickFalse);
1702 (void) CloseBlob(image);
1703 return(MagickTrue);
1704}