Coverage Report

Created: 2025-06-16 07:00

/src/imagemagick/MagickCore/constitute.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3
%                                                                             %
4
%                                                                             %
5
%                                                                             %
6
%     CCCC   OOO   N   N  SSSSS  TTTTT  IIIII  TTTTT  U   U  TTTTT  EEEEE     %
7
%    C      O   O  NN  N  SS       T      I      T    U   U    T    E         %
8
%    C      O   O  N N N  ESSS     T      I      T    U   U    T    EEE       %
9
%    C      O   O  N  NN     SS    T      I      T    U   U    T    E         %
10
%     CCCC   OOO   N   N  SSSSS    T    IIIII    T     UUU     T    EEEEE     %
11
%                                                                             %
12
%                                                                             %
13
%                  MagickCore Methods to Constitute an Image                  %
14
%                                                                             %
15
%                             Software Design                                 %
16
%                                  Cristy                                     %
17
%                               October 1998                                  %
18
%                                                                             %
19
%                                                                             %
20
%  Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization         %
21
%  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
%    https://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 "MagickCore/studio.h"
43
#include "MagickCore/attribute.h"
44
#include "MagickCore/blob.h"
45
#include "MagickCore/blob-private.h"
46
#include "MagickCore/exception.h"
47
#include "MagickCore/exception-private.h"
48
#include "MagickCore/cache.h"
49
#include "MagickCore/client.h"
50
#include "MagickCore/coder-private.h"
51
#include "MagickCore/colorspace-private.h"
52
#include "MagickCore/constitute.h"
53
#include "MagickCore/constitute-private.h"
54
#include "MagickCore/delegate.h"
55
#include "MagickCore/geometry.h"
56
#include "MagickCore/identify.h"
57
#include "MagickCore/image-private.h"
58
#include "MagickCore/list.h"
59
#include "MagickCore/magick.h"
60
#include "MagickCore/memory_.h"
61
#include "MagickCore/monitor.h"
62
#include "MagickCore/monitor-private.h"
63
#include "MagickCore/option.h"
64
#include "MagickCore/pixel.h"
65
#include "MagickCore/pixel-accessor.h"
66
#include "MagickCore/policy.h"
67
#include "MagickCore/profile.h"
68
#include "MagickCore/profile-private.h"
69
#include "MagickCore/property.h"
70
#include "MagickCore/quantum.h"
71
#include "MagickCore/resize.h"
72
#include "MagickCore/resource_.h"
73
#include "MagickCore/semaphore.h"
74
#include "MagickCore/statistic.h"
75
#include "MagickCore/stream.h"
76
#include "MagickCore/string_.h"
77
#include "MagickCore/string-private.h"
78
#include "MagickCore/timer.h"
79
#include "MagickCore/timer-private.h"
80
#include "MagickCore/token.h"
81
#include "MagickCore/transform.h"
82
#include "MagickCore/utility.h"
83
#include "MagickCore/utility-private.h"
84

85
/*
86
  Define declarations.
87
*/
88
#define MaxReadRecursionDepth  100
89

90
/*
91
  Typedef declarations.
92
*/
93
typedef struct _ConstituteInfo
94
{
95
  const char
96
    *caption,
97
    *comment,
98
    *dispose,
99
    *label;
100
101
  MagickBooleanType
102
    sync_from_exif,
103
    sync_from_tiff;
104
105
  MagickStatusType
106
    delay_flags;
107
108
  size_t
109
    delay;
110
111
  ssize_t
112
    ticks_per_second;
113
} ConstituteInfo;
114

115
/*
116
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
117
%                                                                             %
118
%                                                                             %
119
%                                                                             %
120
%   C o n s t i t u t e I m a g e                                             %
121
%                                                                             %
122
%                                                                             %
123
%                                                                             %
124
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
125
%
126
%  ConstituteImage() returns an image from the pixel data you supply.
127
%  The pixel data must be in scanline order top-to-bottom.  The data can be
128
%  char, short int, int, float, or double.  Float and double require the
129
%  pixels to be normalized [0..1], otherwise [0..QuantumRange].  For example, to
130
%  create a 640x480 image from unsigned red-green-blue character data, use:
131
%
132
%      image = ConstituteImage(640,480,"RGB",CharPixel,pixels,&exception);
133
%
134
%  The format of the ConstituteImage method is:
135
%
136
%      Image *ConstituteImage(const size_t columns,const size_t rows,
137
%        const char *map,const StorageType storage,const void *pixels,
138
%        ExceptionInfo *exception)
139
%
140
%  A description of each parameter follows:
141
%
142
%    o columns: width in pixels of the image.
143
%
144
%    o rows: height in pixels of the image.
145
%
146
%    o map:  This string reflects the expected ordering of the pixel array.
147
%      It can be any combination or order of R = red, G = green, B = blue,
148
%      A = alpha (0 is transparent), O = opacity (0 is opaque), C = cyan,
149
%      Y = yellow, M = magenta, K = black, I = intensity (for grayscale),
150
%      P = pad.
151
%
152
%    o storage: Define the data type of the pixels.  Float and double types are
153
%      expected to be normalized [0..1] otherwise [0..QuantumRange].  Choose
154
%      from these types: CharPixel, DoublePixel, FloatPixel, IntegerPixel,
155
%      LongPixel, QuantumPixel, or ShortPixel.
156
%
157
%    o pixels: This array of values contain the pixel components as defined by
158
%      map and type.  You must preallocate this array where the expected
159
%      length varies depending on the values of width, height, map, and type.
160
%
161
%    o exception: return any errors or warnings in this structure.
162
%
163
*/
164
MagickExport Image *ConstituteImage(const size_t columns,const size_t rows,
165
  const char *map,const StorageType storage,const void *pixels,
166
  ExceptionInfo *exception)
167
110
{
168
110
  Image
169
110
    *image;
170
171
110
  MagickBooleanType
172
110
    status;
173
174
110
  ssize_t
175
110
    i;
176
177
110
  size_t
178
110
    length;
179
180
  /*
181
    Allocate image structure.
182
  */
183
110
  assert(map != (const char *) NULL);
184
110
  assert(pixels != (void *) NULL);
185
110
  assert(exception != (ExceptionInfo *) NULL);
186
110
  assert(exception->signature == MagickCoreSignature);
187
110
  if (IsEventLogging() != MagickFalse)
188
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",map);
189
110
  image=AcquireImage((ImageInfo *) NULL,exception);
190
110
  if (image == (Image *) NULL)
191
0
    return((Image *) NULL);
192
110
  switch (storage)
193
110
  {
194
110
    case CharPixel: image->depth=8*sizeof(unsigned char); break;
195
0
    case DoublePixel: image->depth=8*sizeof(double); break;
196
0
    case FloatPixel: image->depth=8*sizeof(float); break;
197
0
    case LongPixel: image->depth=8*sizeof(unsigned long); break;
198
0
    case LongLongPixel: image->depth=8*sizeof(MagickSizeType); break;
199
0
    case ShortPixel: image->depth=8*sizeof(unsigned short); break;
200
0
    default: break;
201
110
  }
202
110
  length=strlen(map);
203
440
  for (i=0; i < (ssize_t) length; i++)
204
330
  {
205
330
    switch (map[i])
206
330
    {
207
0
      case 'a':
208
0
      case 'A':
209
0
      case 'O':
210
0
      case 'o':
211
0
      {
212
0
        image->alpha_trait=BlendPixelTrait;
213
0
        break;
214
0
      }
215
0
      case 'C':
216
0
      case 'c':
217
0
      case 'm':
218
0
      case 'M':
219
0
      case 'Y':
220
0
      case 'y':
221
0
      case 'K':
222
0
      case 'k':
223
0
      {
224
0
        image->colorspace=CMYKColorspace;
225
0
        break;
226
0
      }
227
0
      case 'I':
228
0
      case 'i':
229
0
      {
230
0
        image->colorspace=GRAYColorspace;
231
0
        break;
232
0
      }
233
330
      default:
234
330
      {
235
330
        if (length == 1)
236
0
          image->colorspace=GRAYColorspace;
237
330
        break;
238
0
      }
239
330
    }
240
330
  }
241
110
  status=SetImageExtent(image,columns,rows,exception);
242
110
  if (status == MagickFalse)
243
0
    return(DestroyImageList(image));
244
110
  status=ResetImagePixels(image,exception);
245
110
  if (status == MagickFalse)
246
0
    return(DestroyImageList(image));
247
110
  status=ImportImagePixels(image,0,0,columns,rows,map,storage,pixels,exception);
248
110
  if (status == MagickFalse)
249
0
    image=DestroyImage(image);
250
110
  return(image);
251
110
}
252

253
/*
254
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
255
%                                                                             %
256
%                                                                             %
257
%                                                                             %
258
%   P i n g I m a g e                                                         %
259
%                                                                             %
260
%                                                                             %
261
%                                                                             %
262
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
263
%
264
%  PingImage() returns all the properties of an image or image sequence
265
%  except for the pixels.  It is much faster and consumes far less memory
266
%  than ReadImage().  On failure, a NULL image is returned and exception
267
%  describes the reason for the failure.
268
%
269
%  The format of the PingImage method is:
270
%
271
%      Image *PingImage(const ImageInfo *image_info,ExceptionInfo *exception)
272
%
273
%  A description of each parameter follows:
274
%
275
%    o image_info: Ping the image defined by the file or filename members of
276
%      this structure.
277
%
278
%    o exception: return any errors or warnings in this structure.
279
%
280
*/
281
282
#if defined(__cplusplus) || defined(c_plusplus)
283
extern "C" {
284
#endif
285
286
static size_t PingStream(const Image *magick_unused(image),
287
  const void *magick_unused(pixels),const size_t columns)
288
0
{
289
0
  magick_unreferenced(image);
290
0
  magick_unreferenced(pixels);
291
0
  return(columns);
292
0
}
293
294
#if defined(__cplusplus) || defined(c_plusplus)
295
}
296
#endif
297
298
MagickExport Image *PingImage(const ImageInfo *image_info,
299
  ExceptionInfo *exception)
300
0
{
301
0
  Image
302
0
    *image;
303
304
0
  ImageInfo
305
0
    *ping_info;
306
307
0
  assert(image_info != (ImageInfo *) NULL);
308
0
  assert(image_info->signature == MagickCoreSignature);
309
0
  assert(exception != (ExceptionInfo *) NULL);
310
0
  if (IsEventLogging() != MagickFalse)
311
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
312
0
      image_info->filename);
313
0
  ping_info=CloneImageInfo(image_info);
314
0
  ping_info->ping=MagickTrue;
315
0
  image=ReadStream(ping_info,&PingStream,exception);
316
0
  if (image != (Image *) NULL)
317
0
    {
318
0
      ResetTimer(&image->timer);
319
0
      if (ping_info->verbose != MagickFalse)
320
0
        (void) IdentifyImage(image,stdout,MagickFalse,exception);
321
0
    }
322
0
  ping_info=DestroyImageInfo(ping_info);
323
0
  return(image);
324
0
}
325

326
/*
327
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
328
%                                                                             %
329
%                                                                             %
330
%                                                                             %
331
%   P i n g I m a g e s                                                       %
332
%                                                                             %
333
%                                                                             %
334
%                                                                             %
335
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
336
%
337
%  PingImages() pings one or more images and returns them as an image list.
338
%
339
%  The format of the PingImage method is:
340
%
341
%      Image *PingImages(ImageInfo *image_info,const char *filename,
342
%        ExceptionInfo *exception)
343
%
344
%  A description of each parameter follows:
345
%
346
%    o image_info: the image info.
347
%
348
%    o filename: the image filename.
349
%
350
%    o exception: return any errors or warnings in this structure.
351
%
352
*/
353
MagickExport Image *PingImages(ImageInfo *image_info,const char *filename,
354
  ExceptionInfo *exception)
355
0
{
356
0
  char
357
0
    ping_filename[MagickPathExtent];
358
359
0
  Image
360
0
    *image,
361
0
    *images;
362
363
0
  ImageInfo
364
0
    *read_info;
365
366
  /*
367
    Ping image list from a file.
368
  */
369
0
  assert(image_info != (ImageInfo *) NULL);
370
0
  assert(image_info->signature == MagickCoreSignature);
371
0
  assert(exception != (ExceptionInfo *) NULL);
372
0
  if (IsEventLogging() != MagickFalse)
373
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
374
0
      image_info->filename);
375
0
  (void) SetImageOption(image_info,"filename",filename);
376
0
  (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
377
0
  (void) InterpretImageFilename(image_info,(Image *) NULL,image_info->filename,
378
0
    (int) image_info->scene,ping_filename,exception);
379
0
  if (LocaleCompare(ping_filename,image_info->filename) != 0)
380
0
    {
381
0
      ExceptionInfo
382
0
        *sans;
383
384
0
      ssize_t
385
0
        extent,
386
0
        scene;
387
388
      /*
389
        Images of the form image-%d.png[1-5].
390
      */
391
0
      read_info=CloneImageInfo(image_info);
392
0
      sans=AcquireExceptionInfo();
393
0
      (void) SetImageInfo(read_info,0,sans);
394
0
      sans=DestroyExceptionInfo(sans);
395
0
      if (read_info->number_scenes == 0)
396
0
        {
397
0
          read_info=DestroyImageInfo(read_info);
398
0
          return(PingImage(image_info,exception));
399
0
        }
400
0
      (void) CopyMagickString(ping_filename,read_info->filename,
401
0
        MagickPathExtent);
402
0
      images=NewImageList();
403
0
      extent=(ssize_t) (read_info->scene+read_info->number_scenes);
404
0
      for (scene=(ssize_t) read_info->scene; scene < (ssize_t) extent; scene++)
405
0
      {
406
0
        (void) InterpretImageFilename(image_info,(Image *) NULL,ping_filename,
407
0
          (int) scene,read_info->filename,exception);
408
0
        image=PingImage(read_info,exception);
409
0
        if (image == (Image *) NULL)
410
0
          continue;
411
0
        AppendImageToList(&images,image);
412
0
      }
413
0
      read_info=DestroyImageInfo(read_info);
414
0
      return(images);
415
0
    }
416
0
  return(PingImage(image_info,exception));
417
0
}
418

419
/*
420
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
421
%                                                                             %
422
%                                                                             %
423
%                                                                             %
424
%   R e a d I m a g e                                                         %
425
%                                                                             %
426
%                                                                             %
427
%                                                                             %
428
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
429
%
430
%  ReadImage() reads an image or image sequence from a file or file handle.
431
%  The method returns a NULL if there is a memory shortage or if the image
432
%  cannot be read.  On failure, a NULL image is returned and exception
433
%  describes the reason for the failure.
434
%
435
%  The format of the ReadImage method is:
436
%
437
%      Image *ReadImage(const ImageInfo *image_info,ExceptionInfo *exception)
438
%
439
%  A description of each parameter follows:
440
%
441
%    o image_info: Read the image defined by the file or filename members of
442
%      this structure.
443
%
444
%    o exception: return any errors or warnings in this structure.
445
%
446
*/
447
448
static MagickBooleanType IsCoderAuthorized(const char *coder,
449
  const PolicyRights rights,ExceptionInfo *exception)
450
1.11M
{
451
1.11M
  if (IsRightsAuthorized(CoderPolicyDomain,rights,coder) == MagickFalse)
452
0
    {
453
0
      errno=EPERM;
454
0
      (void) ThrowMagickException(exception,GetMagickModule(),PolicyError,
455
0
        "NotAuthorized","`%s'",coder);
456
0
      return(MagickFalse);
457
0
    }
458
1.11M
  return(MagickTrue);
459
1.11M
}
460
461
static void InitializeConstituteInfo(const ImageInfo *image_info,
462
  ConstituteInfo *constitute_info)
463
403k
{
464
403k
  const char
465
403k
    *option;
466
467
403k
  memset(constitute_info,0,sizeof(*constitute_info));
468
403k
  constitute_info->sync_from_exif=MagickTrue;
469
403k
  constitute_info->sync_from_tiff=MagickTrue;
470
403k
  option=GetImageOption(image_info,"exif:sync-image");
471
403k
  if (IsStringFalse(option) != MagickFalse)
472
0
    constitute_info->sync_from_exif=MagickFalse;
473
403k
  option=GetImageOption(image_info,"tiff:sync-image");
474
403k
  if (IsStringFalse(option) != MagickFalse)
475
0
    constitute_info->sync_from_tiff=MagickFalse;
476
403k
  constitute_info->caption=GetImageOption(image_info,"caption");
477
403k
  constitute_info->comment=GetImageOption(image_info,"comment");
478
403k
  constitute_info->label=GetImageOption(image_info,"label");
479
403k
  option=GetImageOption(image_info,"delay");
480
403k
  if (option != (const char *) NULL)
481
0
    {
482
0
      GeometryInfo
483
0
        geometry_info;
484
485
0
      constitute_info->delay_flags=ParseGeometry(option,&geometry_info);
486
0
      if (constitute_info->delay_flags != NoValue)
487
0
        {
488
0
          constitute_info->delay=(size_t) floor(geometry_info.rho+0.5);
489
0
          if ((constitute_info->delay_flags & SigmaValue) != 0)
490
0
            constitute_info->ticks_per_second=CastDoubleToSsizeT(floor(
491
0
              geometry_info.sigma+0.5));
492
0
        }
493
0
    }
494
403k
}
495
496
static void SyncOrientationFromProperties(Image *image,
497
  ConstituteInfo *constitute_info,ExceptionInfo *exception)
498
489k
{
499
489k
  const char
500
489k
    *orientation;
501
502
489k
  orientation=(const char *) NULL;
503
489k
  if (constitute_info->sync_from_exif != MagickFalse)
504
489k
    {
505
489k
      orientation=GetImageProperty(image,"exif:Orientation",exception);
506
489k
      if (orientation != (const char *) NULL)
507
3.76k
        {
508
3.76k
          image->orientation=(OrientationType) StringToLong(orientation);
509
3.76k
          (void) DeleteImageProperty(image,"exif:Orientation");
510
3.76k
        }
511
489k
    }
512
489k
  if ((orientation == (const char *) NULL) &&
513
489k
      (constitute_info->sync_from_tiff != MagickFalse))
514
486k
    {
515
486k
      orientation=GetImageProperty(image,"tiff:Orientation",exception);
516
486k
      if (orientation != (const char *) NULL)
517
0
        {
518
0
          image->orientation=(OrientationType) StringToLong(orientation);
519
0
          (void) DeleteImageProperty(image,"tiff:Orientation");
520
0
        }
521
486k
    }
522
489k
}
523
524
static void SyncResolutionFromProperties(Image *image,
525
  ConstituteInfo *constitute_info, ExceptionInfo *exception)
526
489k
{
527
489k
  const char
528
489k
    *resolution_x,
529
489k
    *resolution_y,
530
489k
    *resolution_units;
531
532
489k
  MagickBooleanType
533
489k
    used_tiff;
534
535
489k
  resolution_x=(const char *) NULL;
536
489k
  resolution_y=(const char *) NULL;
537
489k
  resolution_units=(const char *) NULL;
538
489k
  used_tiff=MagickFalse;
539
489k
  if (constitute_info->sync_from_exif != MagickFalse)
540
489k
    {
541
489k
      resolution_x=GetImageProperty(image,"exif:XResolution",exception);
542
489k
      resolution_y=GetImageProperty(image,"exif:YResolution",exception);
543
489k
      if ((resolution_x != (const char *) NULL) &&
544
489k
          (resolution_y != (const char *) NULL))
545
12.6k
        resolution_units=GetImageProperty(image,"exif:ResolutionUnit",
546
12.6k
          exception);
547
489k
    }
548
489k
  if ((resolution_x == (const char *) NULL) &&
549
489k
      (resolution_y == (const char *) NULL) &&
550
489k
      (constitute_info->sync_from_tiff != MagickFalse))
551
473k
    {
552
473k
      resolution_x=GetImageProperty(image,"tiff:XResolution",exception);
553
473k
      resolution_y=GetImageProperty(image,"tiff:YResolution",exception);
554
473k
      if ((resolution_x != (const char *) NULL) &&
555
473k
          (resolution_y != (const char *) NULL))
556
3
        {
557
3
          used_tiff=MagickTrue;
558
3
          resolution_units=GetImageProperty(image,"tiff:ResolutionUnit",
559
3
            exception);
560
3
        }
561
473k
    }
562
489k
  if ((resolution_x != (const char *) NULL) &&
563
489k
      (resolution_y != (const char *) NULL))
564
12.6k
    {
565
12.6k
      GeometryInfo
566
12.6k
        geometry_info;
567
568
12.6k
      ssize_t
569
12.6k
        option_type;
570
571
12.6k
      geometry_info.rho=image->resolution.x;
572
12.6k
      geometry_info.sigma=1.0;
573
12.6k
      (void) ParseGeometry(resolution_x,&geometry_info);
574
12.6k
      if (geometry_info.sigma != 0)
575
4.18k
        image->resolution.x=geometry_info.rho/geometry_info.sigma;
576
12.6k
      if (strchr(resolution_x,',') != (char *) NULL)
577
3.20k
        image->resolution.x=geometry_info.rho+geometry_info.sigma/1000.0;
578
12.6k
      geometry_info.rho=image->resolution.y;
579
12.6k
      geometry_info.sigma=1.0;
580
12.6k
      (void) ParseGeometry(resolution_y,&geometry_info);
581
12.6k
      if (geometry_info.sigma != 0)
582
3.95k
        image->resolution.y=geometry_info.rho/geometry_info.sigma;
583
12.6k
      if (strchr(resolution_y,',') != (char *) NULL)
584
3.37k
        image->resolution.y=geometry_info.rho+geometry_info.sigma/1000.0;
585
12.6k
      if (resolution_units != (char *) NULL)
586
2.48k
        {
587
2.48k
          option_type=ParseCommandOption(MagickResolutionOptions,MagickFalse,
588
2.48k
            resolution_units);
589
2.48k
          if (option_type >= 0)
590
369
            image->units=(ResolutionType) option_type;
591
2.48k
        }
592
12.6k
      if (used_tiff == MagickFalse)
593
12.6k
        {
594
12.6k
          (void) DeleteImageProperty(image,"exif:XResolution");
595
12.6k
          (void) DeleteImageProperty(image,"exif:YResolution");
596
12.6k
          (void) DeleteImageProperty(image,"exif:ResolutionUnit");
597
12.6k
        }
598
3
      else
599
3
        {
600
3
          (void) DeleteImageProperty(image,"tiff:XResolution");
601
3
          (void) DeleteImageProperty(image,"tiff:YResolution");
602
3
          (void) DeleteImageProperty(image,"tiff:ResolutionUnit");
603
3
        }
604
12.6k
    }
605
489k
}
606
607
MagickExport Image *ReadImage(const ImageInfo *image_info,
608
  ExceptionInfo *exception)
609
1.06M
{
610
1.06M
  char
611
1.06M
    filename[MagickPathExtent],
612
1.06M
    magick[MagickPathExtent],
613
1.06M
    magick_filename[MagickPathExtent];
614
615
1.06M
  ConstituteInfo
616
1.06M
    constitute_info;
617
618
1.06M
  const DelegateInfo
619
1.06M
    *delegate_info;
620
621
1.06M
  const MagickInfo
622
1.06M
    *magick_info;
623
624
1.06M
  DecodeImageHandler
625
1.06M
    *decoder;
626
627
1.06M
  ExceptionInfo
628
1.06M
    *sans_exception;
629
630
1.06M
  Image
631
1.06M
    *image,
632
1.06M
    *next;
633
634
1.06M
  ImageInfo
635
1.06M
    *read_info;
636
637
1.06M
  MagickBooleanType
638
1.06M
    status;
639
640
  /*
641
    Determine image type from filename prefix or suffix (e.g. image.jpg).
642
  */
643
1.06M
  assert(image_info != (ImageInfo *) NULL);
644
1.06M
  assert(image_info->signature == MagickCoreSignature);
645
1.06M
  assert(image_info->filename != (char *) NULL);
646
1.06M
  if (IsEventLogging() != MagickFalse)
647
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
648
0
      image_info->filename);
649
1.06M
  assert(exception != (ExceptionInfo *) NULL);
650
1.06M
  read_info=CloneImageInfo(image_info);
651
1.06M
  (void) CopyMagickString(magick_filename,read_info->filename,MagickPathExtent);
652
1.06M
  (void) SetImageInfo(read_info,0,exception);
653
1.06M
  (void) CopyMagickString(filename,read_info->filename,MagickPathExtent);
654
1.06M
  (void) CopyMagickString(magick,read_info->magick,MagickPathExtent);
655
  /*
656
    Call appropriate image reader based on image type.
657
  */
658
1.06M
  sans_exception=AcquireExceptionInfo();
659
1.06M
  magick_info=GetMagickInfo(read_info->magick,sans_exception);
660
1.06M
  if (sans_exception->severity == PolicyError)
661
0
    InheritException(exception,sans_exception);
662
1.06M
  sans_exception=DestroyExceptionInfo(sans_exception);
663
1.06M
  if (magick_info != (const MagickInfo *) NULL)
664
1.05M
    {
665
1.05M
      if (GetMagickEndianSupport(magick_info) == MagickFalse)
666
867k
        read_info->endian=UndefinedEndian;
667
190k
      else
668
190k
        if ((image_info->endian == UndefinedEndian) &&
669
190k
            (GetMagickRawSupport(magick_info) != MagickFalse))
670
17.8k
          {
671
17.8k
            unsigned long
672
17.8k
              lsb_first;
673
674
17.8k
            lsb_first=1;
675
17.8k
            read_info->endian=(*(char *) &lsb_first) == 1 ? LSBEndian :
676
17.8k
              MSBEndian;
677
17.8k
         }
678
1.05M
    }
679
1.06M
  if ((magick_info != (const MagickInfo *) NULL) &&
680
1.06M
      (GetMagickDecoderSeekableStream(magick_info) != MagickFalse))
681
842k
    {
682
842k
      image=AcquireImage(read_info,exception);
683
842k
      (void) CopyMagickString(image->filename,read_info->filename,
684
842k
        MagickPathExtent);
685
842k
      status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
686
842k
      if (status == MagickFalse)
687
10.8k
        {
688
10.8k
          read_info=DestroyImageInfo(read_info);
689
10.8k
          image=DestroyImage(image);
690
10.8k
          return((Image *) NULL);
691
10.8k
        }
692
831k
      if (IsBlobSeekable(image) == MagickFalse)
693
5.71k
        {
694
          /*
695
            Coder requires a seekable stream.
696
          */
697
5.71k
          *read_info->filename='\0';
698
5.71k
          status=ImageToFile(image,read_info->filename,exception);
699
5.71k
          if (status == MagickFalse)
700
0
            {
701
0
              (void) CloseBlob(image);
702
0
              read_info=DestroyImageInfo(read_info);
703
0
              image=DestroyImage(image);
704
0
              return((Image *) NULL);
705
0
            }
706
5.71k
          read_info->temporary=MagickTrue;
707
5.71k
        }
708
831k
      (void) CloseBlob(image);
709
831k
      image=DestroyImage(image);
710
831k
    }
711
1.05M
  image=NewImageList();
712
1.05M
  decoder=GetImageDecoder(magick_info);
713
1.05M
  if (decoder == (DecodeImageHandler *) NULL)
714
13.7k
    {
715
13.7k
      delegate_info=GetDelegateInfo(read_info->magick,(char *) NULL,exception);
716
13.7k
      if (delegate_info == (const DelegateInfo *) NULL)
717
12.3k
        {
718
12.3k
          (void) SetImageInfo(read_info,0,exception);
719
12.3k
          (void) CopyMagickString(read_info->filename,filename,
720
12.3k
            MagickPathExtent);
721
12.3k
          magick_info=GetMagickInfo(read_info->magick,exception);
722
12.3k
          decoder=GetImageDecoder(magick_info);
723
12.3k
        }
724
13.7k
    }
725
1.05M
  if (decoder != (DecodeImageHandler *) NULL)
726
1.04M
    {
727
      /*
728
        Call appropriate image reader based on image type.
729
      */
730
1.04M
      if ((magick_info != (const MagickInfo *) NULL) &&
731
1.04M
          (GetMagickDecoderThreadSupport(magick_info) == MagickFalse))
732
2.86k
        LockSemaphoreInfo(magick_info->semaphore);
733
1.04M
      status=IsCoderAuthorized(read_info->magick,ReadPolicyRights,exception);
734
1.04M
      image=(Image *) NULL;
735
1.04M
      if (status != MagickFalse)
736
1.04M
        image=decoder(read_info,exception);
737
1.04M
      if ((magick_info != (const MagickInfo *) NULL) &&
738
1.04M
          (GetMagickDecoderThreadSupport(magick_info) == MagickFalse))
739
2.86k
        UnlockSemaphoreInfo(magick_info->semaphore);
740
1.04M
    }
741
11.0k
  else
742
11.0k
    {
743
11.0k
      delegate_info=GetDelegateInfo(read_info->magick,(char *) NULL,exception);
744
11.0k
      if (delegate_info == (const DelegateInfo *) NULL)
745
9.58k
        {
746
9.58k
          (void) ThrowMagickException(exception,GetMagickModule(),
747
9.58k
            MissingDelegateError,"NoDecodeDelegateForThisImageFormat","`%s'",
748
9.58k
            read_info->magick);
749
9.58k
          if (read_info->temporary != MagickFalse)
750
954
            (void) RelinquishUniqueFileResource(read_info->filename);
751
9.58k
          read_info=DestroyImageInfo(read_info);
752
9.58k
          return((Image *) NULL);
753
9.58k
        }
754
      /*
755
        Let our decoding delegate process the image.
756
      */
757
1.46k
      image=AcquireImage(read_info,exception);
758
1.46k
      if (image == (Image *) NULL)
759
0
        {
760
0
          read_info=DestroyImageInfo(read_info);
761
0
          return((Image *) NULL);
762
0
        }
763
1.46k
      (void) CopyMagickString(image->filename,read_info->filename,
764
1.46k
        MagickPathExtent);
765
1.46k
      *read_info->filename='\0';
766
1.46k
      if (GetDelegateThreadSupport(delegate_info) == MagickFalse)
767
0
        LockSemaphoreInfo(delegate_info->semaphore);
768
1.46k
      status=InvokeDelegate(read_info,image,read_info->magick,(char *) NULL,
769
1.46k
        exception);
770
1.46k
      if (GetDelegateThreadSupport(delegate_info) == MagickFalse)
771
0
        UnlockSemaphoreInfo(delegate_info->semaphore);
772
1.46k
      image=DestroyImageList(image);
773
1.46k
      read_info->temporary=MagickTrue;
774
1.46k
      if (status != MagickFalse)
775
0
        (void) SetImageInfo(read_info,0,exception);
776
1.46k
      magick_info=GetMagickInfo(read_info->magick,exception);
777
1.46k
      decoder=GetImageDecoder(magick_info);
778
1.46k
      if (decoder == (DecodeImageHandler *) NULL)
779
1.46k
        {
780
1.46k
          if (IsPathAccessible(read_info->filename) != MagickFalse)
781
0
            (void) ThrowMagickException(exception,GetMagickModule(),
782
0
              MissingDelegateError,"NoDecodeDelegateForThisImageFormat","`%s'",
783
0
              read_info->magick);
784
1.46k
          else
785
1.46k
            ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
786
1.46k
              read_info->filename);
787
1.46k
          read_info=DestroyImageInfo(read_info);
788
1.46k
          return((Image *) NULL);
789
1.46k
        }
790
      /*
791
        Call appropriate image reader based on image type.
792
      */
793
0
      if (GetMagickDecoderThreadSupport(magick_info) == MagickFalse)
794
0
        LockSemaphoreInfo(magick_info->semaphore);
795
0
      status=IsCoderAuthorized(read_info->magick,ReadPolicyRights,exception);
796
0
      image=(Image *) NULL;
797
0
      if (status != MagickFalse)
798
0
        image=(decoder)(read_info,exception);
799
0
      if (GetMagickDecoderThreadSupport(magick_info) == MagickFalse)
800
0
        UnlockSemaphoreInfo(magick_info->semaphore);
801
0
    }
802
1.04M
  if (read_info->temporary != MagickFalse)
803
5.59k
    {
804
5.59k
      (void) RelinquishUniqueFileResource(read_info->filename);
805
5.59k
      read_info->temporary=MagickFalse;
806
5.59k
      if (image != (Image *) NULL)
807
744
        (void) CopyMagickString(image->filename,filename,MagickPathExtent);
808
5.59k
    }
809
1.04M
  if (image == (Image *) NULL)
810
639k
    {
811
639k
      read_info=DestroyImageInfo(read_info);
812
639k
      return(image);
813
639k
    }
814
403k
  if (exception->severity >= ErrorException)
815
171k
    (void) LogMagickEvent(ExceptionEvent,GetMagickModule(),
816
171k
      "Coder (%s) generated an image despite an error (%d), "
817
171k
      "notify the developers",image->magick,exception->severity);
818
403k
  if (IsBlobTemporary(image) != MagickFalse)
819
0
    (void) RelinquishUniqueFileResource(read_info->filename);
820
403k
  if ((IsSceneGeometry(read_info->scenes,MagickFalse) != MagickFalse) &&
821
403k
      (GetImageListLength(image) != 1))
822
282
    {
823
282
      Image
824
282
        *clones;
825
826
282
      clones=CloneImages(image,read_info->scenes,exception);
827
282
      image=DestroyImageList(image);
828
282
      if (clones != (Image *) NULL)
829
253
        image=GetFirstImageInList(clones);
830
282
      if (image == (Image *) NULL)
831
29
        {
832
29
          read_info=DestroyImageInfo(read_info);
833
29
          return(image);
834
29
        }
835
282
    }
836
403k
  InitializeConstituteInfo(read_info,&constitute_info);
837
893k
  for (next=image; next != (Image *) NULL; next=GetNextImageInList(next))
838
489k
  {
839
489k
    char
840
489k
      magick_path[MagickPathExtent],
841
489k
      *property;
842
843
489k
    const StringInfo
844
489k
      *profile;
845
846
489k
    next->taint=MagickFalse;
847
489k
    GetPathComponent(magick_filename,MagickPath,magick_path);
848
489k
    if ((*magick_path == '\0') && (*next->magick == '\0'))
849
0
      (void) CopyMagickString(next->magick,magick,MagickPathExtent);
850
489k
    (void) CopyMagickString(next->magick_filename,magick_filename,
851
489k
      MagickPathExtent);
852
489k
    if (IsBlobTemporary(image) != MagickFalse)
853
0
      (void) CopyMagickString(next->filename,filename,MagickPathExtent);
854
489k
    if (next->magick_columns == 0)
855
405k
      next->magick_columns=next->columns;
856
489k
    if (next->magick_rows == 0)
857
405k
      next->magick_rows=next->rows;
858
489k
    (void) GetImageProperty(next,"exif:*",exception);
859
489k
    (void) GetImageProperty(next,"icc:*",exception);
860
489k
    (void) GetImageProperty(next,"iptc:*",exception);
861
489k
    (void) GetImageProperty(next,"xmp:*",exception);
862
489k
    SyncOrientationFromProperties(next,&constitute_info,exception);
863
489k
    SyncResolutionFromProperties(next,&constitute_info,exception);
864
489k
    if (next->page.width == 0)
865
379k
      next->page.width=next->columns;
866
489k
    if (next->page.height == 0)
867
379k
      next->page.height=next->rows;
868
489k
    if (constitute_info.caption != (const char *) NULL)
869
0
      {
870
0
        property=InterpretImageProperties(read_info,next,
871
0
          constitute_info.caption,exception);
872
0
        (void) SetImageProperty(next,"caption",property,exception);
873
0
        property=DestroyString(property);
874
0
      }
875
489k
    if (constitute_info.comment != (const char *) NULL)
876
0
      {
877
0
        property=InterpretImageProperties(read_info,next,
878
0
          constitute_info.comment,exception);
879
0
        (void) SetImageProperty(next,"comment",property,exception);
880
0
        property=DestroyString(property);
881
0
      }
882
489k
    if (constitute_info.label != (const char *) NULL)
883
0
      {
884
0
        property=InterpretImageProperties(read_info,next,
885
0
          constitute_info.label,exception);
886
0
        (void) SetImageProperty(next,"label",property,exception);
887
0
        property=DestroyString(property);
888
0
      }
889
489k
    if (LocaleCompare(next->magick,"TEXT") == 0)
890
0
      (void) ParseAbsoluteGeometry("0x0+0+0",&next->page);
891
489k
    if ((read_info->extract != (char *) NULL) &&
892
489k
        (read_info->stream == (StreamHandler) NULL))
893
17.3k
      {
894
17.3k
        RectangleInfo
895
17.3k
          geometry;
896
897
17.3k
        MagickStatusType
898
17.3k
          flags;
899
900
17.3k
        SetGeometry(next,&geometry);
901
17.3k
        flags=ParseAbsoluteGeometry(read_info->extract,&geometry);
902
17.3k
        if ((next->columns != geometry.width) ||
903
17.3k
            (next->rows != geometry.height))
904
14.4k
          {
905
14.4k
            if (((flags & XValue) != 0) || ((flags & YValue) != 0))
906
3.58k
              {
907
3.58k
                Image
908
3.58k
                  *crop_image;
909
910
3.58k
                crop_image=CropImage(next,&geometry,exception);
911
3.58k
                if (crop_image != (Image *) NULL)
912
2.49k
                  ReplaceImageInList(&next,crop_image);
913
3.58k
              }
914
10.8k
            else
915
10.8k
              if (((flags & WidthValue) != 0) || ((flags & HeightValue) != 0))
916
10.6k
                {
917
10.6k
                  flags=ParseRegionGeometry(next,read_info->extract,&geometry,
918
10.6k
                    exception);
919
10.6k
                  if ((geometry.width != 0) && (geometry.height != 0))
920
8.61k
                    {
921
8.61k
                      Image *resize_image = ResizeImage(next,geometry.width,
922
8.61k
                        geometry.height,next->filter,exception);
923
8.61k
                      if (resize_image != (Image *) NULL)
924
5.43k
                        ReplaceImageInList(&next,resize_image);
925
8.61k
                    }
926
10.6k
                }
927
14.4k
          }
928
17.3k
      }
929
489k
    profile=GetImageProfile(next,"icc");
930
489k
    if (profile == (const StringInfo *) NULL)
931
468k
      profile=GetImageProfile(next,"icm");
932
489k
    profile=GetImageProfile(next,"iptc");
933
489k
    if (profile == (const StringInfo *) NULL)
934
486k
      profile=GetImageProfile(next,"8bim");
935
489k
    if (IsSourceDataEpochSet() == MagickFalse)
936
489k
      {
937
489k
        char
938
489k
          timestamp[MagickTimeExtent];
939
940
489k
        (void) FormatMagickTime(next->timestamp,sizeof(timestamp),timestamp);
941
489k
        (void) SetImageProperty(next,"date:timestamp",timestamp,exception);
942
489k
        (void) FormatMagickTime((time_t) GetBlobProperties(next)->st_mtime,
943
489k
          sizeof(timestamp),timestamp);
944
489k
        (void) SetImageProperty(next,"date:modify",timestamp,exception);
945
489k
        (void) FormatMagickTime((time_t) GetBlobProperties(next)->st_ctime,
946
489k
          sizeof(timestamp),timestamp);
947
489k
        (void) SetImageProperty(next,"date:create",timestamp,exception);
948
489k
      }
949
489k
    if (constitute_info.delay_flags != NoValue)
950
0
      {
951
0
        if ((constitute_info.delay_flags & GreaterValue) != 0)
952
0
          {
953
0
            if (next->delay > constitute_info.delay)
954
0
              next->delay=constitute_info.delay;
955
0
          }
956
0
        else
957
0
          if ((constitute_info.delay_flags & LessValue) != 0)
958
0
            {
959
0
              if (next->delay < constitute_info.delay)
960
0
                next->delay=constitute_info.delay;
961
0
            }
962
0
          else
963
0
            next->delay=constitute_info.delay;
964
0
        if ((constitute_info.delay_flags & SigmaValue) != 0)
965
0
          next->ticks_per_second=constitute_info.ticks_per_second;
966
0
      }
967
489k
    if (constitute_info.dispose != (const char *) NULL)
968
0
      {
969
0
        ssize_t
970
0
          option_type;
971
972
0
        option_type=ParseCommandOption(MagickDisposeOptions,MagickFalse,
973
0
          constitute_info.dispose);
974
0
        if (option_type >= 0)
975
0
          next->dispose=(DisposeType) option_type;
976
0
      }
977
489k
    if (read_info->verbose != MagickFalse)
978
0
      (void) IdentifyImage(next,stderr,MagickFalse,exception);
979
489k
    image=next;
980
489k
  }
981
403k
  read_info=DestroyImageInfo(read_info);
982
403k
  if (GetBlobError(image) != MagickFalse)
983
403k
    ThrowReaderException(CorruptImageError,"UnableToReadImageData");
984
403k
  return(GetFirstImageInList(image));
985
403k
}
986

987
/*
988
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
989
%                                                                             %
990
%                                                                             %
991
%                                                                             %
992
%   R e a d I m a g e s                                                       %
993
%                                                                             %
994
%                                                                             %
995
%                                                                             %
996
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
997
%
998
%  ReadImages() reads one or more images and returns them as an image list.
999
%
1000
%  The format of the ReadImage method is:
1001
%
1002
%      Image *ReadImages(ImageInfo *image_info,const char *filename,
1003
%        ExceptionInfo *exception)
1004
%
1005
%  A description of each parameter follows:
1006
%
1007
%    o image_info: the image info.
1008
%
1009
%    o filename: the image filename.
1010
%
1011
%    o exception: return any errors or warnings in this structure.
1012
%
1013
*/
1014
MagickExport Image *ReadImages(ImageInfo *image_info,const char *filename,
1015
  ExceptionInfo *exception)
1016
0
{
1017
0
  char
1018
0
    read_filename[MagickPathExtent];
1019
1020
0
  Image
1021
0
    *image,
1022
0
    *images;
1023
1024
0
  ImageInfo
1025
0
    *read_info;
1026
1027
  /*
1028
    Read image list from a file.
1029
  */
1030
0
  assert(image_info != (ImageInfo *) NULL);
1031
0
  assert(image_info->signature == MagickCoreSignature);
1032
0
  assert(exception != (ExceptionInfo *) NULL);
1033
0
  if (IsEventLogging() != MagickFalse)
1034
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1035
0
      image_info->filename);
1036
0
  read_info=CloneImageInfo(image_info);
1037
0
  *read_info->magick='\0';
1038
0
  (void) SetImageOption(read_info,"filename",filename);
1039
0
  (void) CopyMagickString(read_info->filename,filename,MagickPathExtent);
1040
0
  (void) InterpretImageFilename(read_info,(Image *) NULL,filename,
1041
0
    (int) read_info->scene,read_filename,exception);
1042
0
  if (LocaleCompare(read_filename,read_info->filename) != 0)
1043
0
    {
1044
0
      ExceptionInfo
1045
0
        *sans;
1046
1047
0
      ssize_t
1048
0
        extent,
1049
0
        scene;
1050
1051
      /*
1052
        Images of the form image-%d.png[1-5].
1053
      */
1054
0
      sans=AcquireExceptionInfo();
1055
0
      (void) SetImageInfo(read_info,0,sans);
1056
0
      sans=DestroyExceptionInfo(sans);
1057
0
      if (read_info->number_scenes != 0)
1058
0
        {
1059
0
          (void) CopyMagickString(read_filename,read_info->filename,
1060
0
            MagickPathExtent);
1061
0
          images=NewImageList();
1062
0
          extent=(ssize_t) (read_info->scene+read_info->number_scenes);
1063
0
          scene=(ssize_t) read_info->scene;
1064
0
          for ( ; scene < (ssize_t) extent; scene++)
1065
0
          {
1066
0
            (void) InterpretImageFilename(image_info,(Image *) NULL,
1067
0
              read_filename,(int) scene,read_info->filename,exception);
1068
0
            image=ReadImage(read_info,exception);
1069
0
            if (image == (Image *) NULL)
1070
0
              continue;
1071
0
            AppendImageToList(&images,image);
1072
0
          }
1073
0
          read_info=DestroyImageInfo(read_info);
1074
0
          return(images);
1075
0
        }
1076
0
    }
1077
0
  (void) CopyMagickString(read_info->filename,filename,MagickPathExtent);
1078
0
  image=ReadImage(read_info,exception);
1079
0
  read_info=DestroyImageInfo(read_info);
1080
0
  return(image);
1081
0
}
1082

1083
/*
1084
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1085
%                                                                             %
1086
%                                                                             %
1087
%                                                                             %
1088
+   R e a d I n l i n e I m a g e                                             %
1089
%                                                                             %
1090
%                                                                             %
1091
%                                                                             %
1092
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1093
%
1094
%  ReadInlineImage() reads a Base64-encoded inline image or image sequence.
1095
%  The method returns a NULL if there is a memory shortage or if the image
1096
%  cannot be read.  On failure, a NULL image is returned and exception
1097
%  describes the reason for the failure.
1098
%
1099
%  The format of the ReadInlineImage method is:
1100
%
1101
%      Image *ReadInlineImage(const ImageInfo *image_info,const char *content,
1102
%        ExceptionInfo *exception)
1103
%
1104
%  A description of each parameter follows:
1105
%
1106
%    o image_info: the image info.
1107
%
1108
%    o content: the image encoded in Base64.
1109
%
1110
%    o exception: return any errors or warnings in this structure.
1111
%
1112
*/
1113
MagickExport Image *ReadInlineImage(const ImageInfo *image_info,
1114
  const char *content,ExceptionInfo *exception)
1115
38.9k
{
1116
38.9k
  Image
1117
38.9k
    *image;
1118
1119
38.9k
  ImageInfo
1120
38.9k
    *read_info;
1121
1122
38.9k
  unsigned char
1123
38.9k
    *blob;
1124
1125
38.9k
  size_t
1126
38.9k
    length;
1127
1128
38.9k
  const char
1129
38.9k
    *p;
1130
1131
  /*
1132
    Skip over header (e.g. data:image/gif;base64,).
1133
  */
1134
38.9k
  image=NewImageList();
1135
521k
  for (p=content; (*p != ',') && (*p != '\0'); p++) ;
1136
38.9k
  if (*p == '\0')
1137
37.9k
    ThrowReaderException(CorruptImageError,"CorruptImage");
1138
37.9k
  blob=Base64Decode(++p,&length);
1139
37.9k
  if (length == 0)
1140
2.72k
    {
1141
2.72k
      blob=(unsigned char *) RelinquishMagickMemory(blob);
1142
2.72k
      ThrowReaderException(CorruptImageError,"CorruptImage");
1143
0
    }
1144
35.2k
  read_info=CloneImageInfo(image_info);
1145
35.2k
  (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
1146
35.2k
    (void *) NULL);
1147
35.2k
  *read_info->filename='\0';
1148
35.2k
  *read_info->magick='\0';
1149
4.38M
  for (p=content; (*p != '/') && (*p != '\0'); p++) ;
1150
35.2k
  if (*p != '\0')
1151
26.7k
    {
1152
26.7k
      char
1153
26.7k
        *q;
1154
1155
26.7k
      ssize_t
1156
26.7k
        i;
1157
1158
      /*
1159
        Extract media type.
1160
      */
1161
26.7k
      if (LocaleNCompare(++p,"x-",2) == 0)
1162
2
        p+=(ptrdiff_t) 2;
1163
26.7k
      (void) CopyMagickString(read_info->filename,"data.",MagickPathExtent);
1164
26.7k
      q=read_info->filename+5;
1165
3.30M
      for (i=0; (*p != ';') && (*p != '\0') && (i < (MagickPathExtent-6)); i++)
1166
3.28M
        *q++=(*p++);
1167
26.7k
      *q++='\0';
1168
26.7k
    }
1169
35.2k
  image=BlobToImage(read_info,blob,length,exception);
1170
35.2k
  blob=(unsigned char *) RelinquishMagickMemory(blob);
1171
35.2k
  read_info=DestroyImageInfo(read_info);
1172
35.2k
  return(image);
1173
37.9k
}
1174

1175
/*
1176
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1177
%                                                                             %
1178
%                                                                             %
1179
%                                                                             %
1180
%   W r i t e I m a g e                                                       %
1181
%                                                                             %
1182
%                                                                             %
1183
%                                                                             %
1184
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1185
%
1186
%  WriteImage() writes an image or an image sequence to a file or file handle.
1187
%  If writing to a file is on disk, the name is defined by the filename member
1188
%  of the image structure.  WriteImage() returns MagickFalse is there is a
1189
%  memory shortage or if the image cannot be written.  Check the exception
1190
%  member of image to determine the cause for any failure.
1191
%
1192
%  The format of the WriteImage method is:
1193
%
1194
%      MagickBooleanType WriteImage(const ImageInfo *image_info,Image *image,
1195
%        ExceptionInfo *exception)
1196
%
1197
%  A description of each parameter follows:
1198
%
1199
%    o image_info: the image info.
1200
%
1201
%    o image: the image.
1202
%
1203
%    o exception: return any errors or warnings in this structure.
1204
%
1205
*/
1206
MagickExport MagickBooleanType WriteImage(const ImageInfo *image_info,
1207
  Image *image,ExceptionInfo *exception)
1208
75.7k
{
1209
75.7k
  char
1210
75.7k
    filename[MagickPathExtent];
1211
1212
75.7k
  const char
1213
75.7k
    *option;
1214
1215
75.7k
  const DelegateInfo
1216
75.7k
    *delegate_info;
1217
1218
75.7k
  const MagickInfo
1219
75.7k
    *magick_info;
1220
1221
75.7k
  EncodeImageHandler
1222
75.7k
    *encoder;
1223
1224
75.7k
  ExceptionInfo
1225
75.7k
    *sans_exception;
1226
1227
75.7k
  ImageInfo
1228
75.7k
    *write_info;
1229
1230
75.7k
  MagickBooleanType
1231
75.7k
    status,
1232
75.7k
    temporary;
1233
1234
  /*
1235
    Determine image type from filename prefix or suffix (e.g. image.jpg).
1236
  */
1237
75.7k
  assert(image_info != (ImageInfo *) NULL);
1238
75.7k
  assert(image_info->signature == MagickCoreSignature);
1239
75.7k
  assert(image != (Image *) NULL);
1240
75.7k
  if (IsEventLogging() != MagickFalse)
1241
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1242
0
      image_info->filename);
1243
75.7k
  assert(image->signature == MagickCoreSignature);
1244
75.7k
  assert(exception != (ExceptionInfo *) NULL);
1245
75.7k
  sans_exception=AcquireExceptionInfo();
1246
75.7k
  write_info=CloneImageInfo(image_info);
1247
75.7k
  (void) CopyMagickString(write_info->filename,image->filename,
1248
75.7k
    MagickPathExtent);
1249
75.7k
  (void) SetImageInfo(write_info,1,sans_exception);
1250
75.7k
  if (*write_info->magick == '\0')
1251
0
    (void) CopyMagickString(write_info->magick,image->magick,MagickPathExtent);
1252
75.7k
  (void) CopyMagickString(filename,image->filename,MagickPathExtent);
1253
75.7k
  (void) CopyMagickString(image->filename,write_info->filename,
1254
75.7k
    MagickPathExtent);
1255
  /*
1256
    Call appropriate image writer based on image type.
1257
  */
1258
75.7k
  magick_info=GetMagickInfo(write_info->magick,sans_exception);
1259
75.7k
  if (sans_exception->severity == PolicyError)
1260
0
    magick_info=GetMagickInfo(write_info->magick,exception);
1261
75.7k
  sans_exception=DestroyExceptionInfo(sans_exception);
1262
75.7k
  if (magick_info != (const MagickInfo *) NULL)
1263
75.7k
    {
1264
75.7k
      if (GetMagickEndianSupport(magick_info) == MagickFalse)
1265
66.2k
        image->endian=UndefinedEndian;
1266
9.43k
      else
1267
9.43k
        if ((image_info->endian == UndefinedEndian) &&
1268
9.43k
            (GetMagickRawSupport(magick_info) != MagickFalse))
1269
1.29k
          {
1270
1.29k
            unsigned long
1271
1.29k
              lsb_first;
1272
1273
1.29k
            lsb_first=1;
1274
1.29k
            image->endian=(*(char *) &lsb_first) == 1 ? LSBEndian : MSBEndian;
1275
1.29k
         }
1276
75.7k
    }
1277
75.7k
  SyncImageProfiles(image);
1278
75.7k
  DisassociateImageStream(image);
1279
75.7k
  option=GetImageOption(image_info,"delegate:bimodal");
1280
75.7k
  if ((IsStringTrue(option) != MagickFalse) &&
1281
75.7k
      (write_info->page == (char *) NULL) &&
1282
75.7k
      (GetPreviousImageInList(image) == (Image *) NULL) &&
1283
75.7k
      (GetNextImageInList(image) == (Image *) NULL) &&
1284
75.7k
      (IsTaintImage(image) == MagickFalse) )
1285
0
    {
1286
0
      delegate_info=GetDelegateInfo(image->magick,write_info->magick,exception);
1287
0
      if ((delegate_info != (const DelegateInfo *) NULL) &&
1288
0
          (GetDelegateMode(delegate_info) == 0) &&
1289
0
          (IsPathAccessible(image->magick_filename) != MagickFalse))
1290
0
        {
1291
          /*
1292
            Process image with bi-modal delegate.
1293
          */
1294
0
          (void) CopyMagickString(image->filename,image->magick_filename,
1295
0
            MagickPathExtent);
1296
0
          status=InvokeDelegate(write_info,image,image->magick,
1297
0
            write_info->magick,exception);
1298
0
          write_info=DestroyImageInfo(write_info);
1299
0
          (void) CopyMagickString(image->filename,filename,MagickPathExtent);
1300
0
          return(status);
1301
0
        }
1302
0
    }
1303
75.7k
  status=MagickFalse;
1304
75.7k
  temporary=MagickFalse;
1305
75.7k
  if ((magick_info != (const MagickInfo *) NULL) &&
1306
75.7k
      (GetMagickEncoderSeekableStream(magick_info) != MagickFalse))
1307
24.7k
    {
1308
24.7k
      char
1309
24.7k
        image_filename[MagickPathExtent];
1310
1311
24.7k
      (void) CopyMagickString(image_filename,image->filename,MagickPathExtent);
1312
24.7k
      status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
1313
24.7k
      (void) CopyMagickString(image->filename, image_filename,MagickPathExtent);
1314
24.7k
      if (status != MagickFalse)
1315
24.7k
        {
1316
24.7k
          if (IsBlobSeekable(image) == MagickFalse)
1317
0
            {
1318
              /*
1319
                A seekable stream is required by the encoder.
1320
              */
1321
0
              write_info->adjoin=MagickTrue;
1322
0
              (void) CopyMagickString(write_info->filename,image->filename,
1323
0
                MagickPathExtent);
1324
0
              (void) AcquireUniqueFilename(image->filename);
1325
0
              temporary=MagickTrue;
1326
0
            }
1327
24.7k
          if (CloseBlob(image) == MagickFalse)
1328
0
            status=MagickFalse;
1329
24.7k
        }
1330
24.7k
    }
1331
75.7k
  encoder=GetImageEncoder(magick_info);
1332
75.7k
  if (encoder != (EncodeImageHandler *) NULL)
1333
75.7k
    {
1334
      /*
1335
        Call appropriate image writer based on image type.
1336
      */
1337
75.7k
      if ((magick_info != (const MagickInfo *) NULL) &&
1338
75.7k
          (GetMagickEncoderThreadSupport(magick_info) == MagickFalse))
1339
0
        LockSemaphoreInfo(magick_info->semaphore);
1340
75.7k
      status=IsCoderAuthorized(write_info->magick,WritePolicyRights,exception);
1341
75.7k
      if (status != MagickFalse)
1342
75.7k
        status=encoder(write_info,image,exception);
1343
75.7k
      if ((magick_info != (const MagickInfo *) NULL) &&
1344
75.7k
          (GetMagickEncoderThreadSupport(magick_info) == MagickFalse))
1345
0
        UnlockSemaphoreInfo(magick_info->semaphore);
1346
75.7k
    }
1347
0
  else
1348
0
    {
1349
0
      delegate_info=GetDelegateInfo((char *) NULL,write_info->magick,exception);
1350
0
      if (delegate_info != (DelegateInfo *) NULL)
1351
0
        {
1352
          /*
1353
            Process the image with delegate.
1354
          */
1355
0
          *write_info->filename='\0';
1356
0
          if (GetDelegateThreadSupport(delegate_info) == MagickFalse)
1357
0
            LockSemaphoreInfo(delegate_info->semaphore);
1358
0
          status=InvokeDelegate(write_info,image,(char *) NULL,
1359
0
            write_info->magick,exception);
1360
0
          if (GetDelegateThreadSupport(delegate_info) == MagickFalse)
1361
0
            UnlockSemaphoreInfo(delegate_info->semaphore);
1362
0
          (void) CopyMagickString(image->filename,filename,MagickPathExtent);
1363
0
        }
1364
0
      else
1365
0
        {
1366
0
          sans_exception=AcquireExceptionInfo();
1367
0
          magick_info=GetMagickInfo(write_info->magick,sans_exception);
1368
0
          if (sans_exception->severity == PolicyError)
1369
0
            magick_info=GetMagickInfo(write_info->magick,exception);
1370
0
          sans_exception=DestroyExceptionInfo(sans_exception);
1371
0
          if ((write_info->affirm == MagickFalse) &&
1372
0
              (magick_info == (const MagickInfo *) NULL))
1373
0
            {
1374
0
              (void) CopyMagickString(write_info->magick,image->magick,
1375
0
                MagickPathExtent);
1376
0
              magick_info=GetMagickInfo(write_info->magick,exception);
1377
0
            }
1378
0
          encoder=GetImageEncoder(magick_info);
1379
0
          if (encoder == (EncodeImageHandler *) NULL)
1380
0
            {
1381
0
              char
1382
0
                extension[MagickPathExtent];
1383
1384
0
              GetPathComponent(image->filename,ExtensionPath,extension);
1385
0
              if (*extension != '\0')
1386
0
                magick_info=GetMagickInfo(extension,exception);
1387
0
              else
1388
0
                magick_info=GetMagickInfo(image->magick,exception);
1389
0
              (void) CopyMagickString(image->filename,filename,
1390
0
                MagickPathExtent);
1391
0
              encoder=GetImageEncoder(magick_info);
1392
0
              (void) ThrowMagickException(exception,GetMagickModule(),
1393
0
                MissingDelegateWarning,"NoEncodeDelegateForThisImageFormat",
1394
0
                "`%s'",write_info->magick);
1395
0
            }
1396
0
          if (encoder == (EncodeImageHandler *) NULL)
1397
0
            {
1398
0
              magick_info=GetMagickInfo(image->magick,exception);
1399
0
              encoder=GetImageEncoder(magick_info);
1400
0
              if (encoder == (EncodeImageHandler *) NULL)
1401
0
                (void) ThrowMagickException(exception,GetMagickModule(),
1402
0
                  MissingDelegateError,"NoEncodeDelegateForThisImageFormat",
1403
0
                  "`%s'",write_info->magick);
1404
0
            }
1405
0
          if (encoder != (EncodeImageHandler *) NULL)
1406
0
            {
1407
              /*
1408
                Call appropriate image writer based on image type.
1409
              */
1410
0
              if (GetMagickEncoderThreadSupport(magick_info) == MagickFalse)
1411
0
                LockSemaphoreInfo(magick_info->semaphore);
1412
0
              status=IsCoderAuthorized(write_info->magick,WritePolicyRights,
1413
0
                exception);
1414
0
              if (status != MagickFalse)
1415
0
                status=encoder(write_info,image,exception);
1416
0
              if (GetMagickEncoderThreadSupport(magick_info) == MagickFalse)
1417
0
                UnlockSemaphoreInfo(magick_info->semaphore);
1418
0
            }
1419
0
        }
1420
0
    }
1421
75.7k
  if (temporary != MagickFalse)
1422
0
    {
1423
      /*
1424
        Copy temporary image file to permanent.
1425
      */
1426
0
      status=OpenBlob(write_info,image,ReadBinaryBlobMode,exception);
1427
0
      if (status != MagickFalse)
1428
0
        {
1429
0
          (void) RelinquishUniqueFileResource(write_info->filename);
1430
0
          status=ImageToFile(image,write_info->filename,exception);
1431
0
        }
1432
0
      if (CloseBlob(image) == MagickFalse)
1433
0
        status=MagickFalse;
1434
0
      (void) RelinquishUniqueFileResource(image->filename);
1435
0
      (void) CopyMagickString(image->filename,write_info->filename,
1436
0
        MagickPathExtent);
1437
0
    }
1438
75.7k
  if ((LocaleCompare(write_info->magick,"info") != 0) &&
1439
75.7k
      (write_info->verbose != MagickFalse))
1440
0
    (void) IdentifyImage(image,stdout,MagickFalse,exception);
1441
75.7k
  write_info=DestroyImageInfo(write_info);
1442
75.7k
  if (GetBlobError(image) != MagickFalse)
1443
75.7k
    ThrowWriterException(FileOpenError,"UnableToWriteFile");
1444
75.7k
  return(status);
1445
75.7k
}
1446

1447
/*
1448
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1449
%                                                                             %
1450
%                                                                             %
1451
%                                                                             %
1452
%   W r i t e I m a g e s                                                     %
1453
%                                                                             %
1454
%                                                                             %
1455
%                                                                             %
1456
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1457
%
1458
%  WriteImages() writes an image sequence into one or more files.  While
1459
%  WriteImage() can write an image sequence, it is limited to writing
1460
%  the sequence into a single file using a format which supports multiple
1461
%  frames.   WriteImages(), however, does not have this limitation, instead it
1462
%  generates multiple output files if necessary (or when requested).  When
1463
%  ImageInfo's adjoin flag is set to MagickFalse, the file name is expected
1464
%  to include a printf-style formatting string for the frame number (e.g.
1465
%  "image%02d.png").
1466
%
1467
%  The format of the WriteImages method is:
1468
%
1469
%      MagickBooleanType WriteImages(const ImageInfo *image_info,Image *images,
1470
%        const char *filename,ExceptionInfo *exception)
1471
%
1472
%  A description of each parameter follows:
1473
%
1474
%    o image_info: the image info.
1475
%
1476
%    o images: the image list.
1477
%
1478
%    o filename: the image filename.
1479
%
1480
%    o exception: return any errors or warnings in this structure.
1481
%
1482
*/
1483
MagickExport MagickBooleanType WriteImages(const ImageInfo *image_info,
1484
  Image *images,const char *filename,ExceptionInfo *exception)
1485
37.1k
{
1486
37.1k
#define WriteImageTag  "Write/Image"
1487
1488
37.1k
  ExceptionInfo
1489
37.1k
    *sans_exception;
1490
1491
37.1k
  ImageInfo
1492
37.1k
    *write_info;
1493
1494
37.1k
  MagickBooleanType
1495
37.1k
    proceed;
1496
1497
37.1k
  MagickOffsetType
1498
37.1k
    progress;
1499
1500
37.1k
  MagickProgressMonitor
1501
37.1k
    progress_monitor;
1502
1503
37.1k
  MagickSizeType
1504
37.1k
    number_images;
1505
1506
37.1k
  MagickStatusType
1507
37.1k
    status;
1508
1509
37.1k
  Image
1510
37.1k
    *p;
1511
1512
37.1k
  assert(image_info != (const ImageInfo *) NULL);
1513
37.1k
  assert(image_info->signature == MagickCoreSignature);
1514
37.1k
  assert(images != (Image *) NULL);
1515
37.1k
  assert(images->signature == MagickCoreSignature);
1516
37.1k
  if (IsEventLogging() != MagickFalse)
1517
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1518
37.1k
  assert(exception != (ExceptionInfo *) NULL);
1519
37.1k
  write_info=CloneImageInfo(image_info);
1520
37.1k
  *write_info->magick='\0';
1521
37.1k
  images=GetFirstImageInList(images);
1522
37.1k
  if (images == (Image *) NULL)
1523
0
    return(MagickFalse);
1524
37.1k
  if (filename != (const char *) NULL)
1525
74.2k
    for (p=images; p != (Image *) NULL; p=GetNextImageInList(p))
1526
37.1k
      (void) CopyMagickString(p->filename,filename,MagickPathExtent);
1527
37.1k
  (void) CopyMagickString(write_info->filename,images->filename,
1528
37.1k
    MagickPathExtent);
1529
37.1k
  sans_exception=AcquireExceptionInfo();
1530
37.1k
  (void) SetImageInfo(write_info,(unsigned int) GetImageListLength(images),
1531
37.1k
    sans_exception);
1532
37.1k
  sans_exception=DestroyExceptionInfo(sans_exception);
1533
37.1k
  if (*write_info->magick == '\0')
1534
36.2k
    (void) CopyMagickString(write_info->magick,images->magick,MagickPathExtent);
1535
37.1k
  p=images;
1536
37.1k
  for ( ; GetNextImageInList(p) != (Image *) NULL; p=GetNextImageInList(p))
1537
0
  {
1538
0
    if (p->scene >= GetNextImageInList(p)->scene)
1539
0
      {
1540
0
        ssize_t
1541
0
          i;
1542
1543
        /*
1544
          Generate consistent scene numbers.
1545
        */
1546
0
        i=(ssize_t) images->scene;
1547
0
        for (p=images; p != (Image *) NULL; p=GetNextImageInList(p))
1548
0
          p->scene=(size_t) i++;
1549
0
        break;
1550
0
      }
1551
0
  }
1552
  /*
1553
    Write images.
1554
  */
1555
37.1k
  status=MagickTrue;
1556
37.1k
  progress_monitor=(MagickProgressMonitor) NULL;
1557
37.1k
  progress=0;
1558
37.1k
  number_images=GetImageListLength(images);
1559
37.1k
  for (p=images; p != (Image *) NULL; p=GetNextImageInList(p))
1560
37.1k
  {
1561
37.1k
    if (number_images != 1)
1562
0
      progress_monitor=SetImageProgressMonitor(p,(MagickProgressMonitor) NULL,
1563
0
        p->client_data);
1564
37.1k
    status&=(MagickStatusType) WriteImage(write_info,p,exception);
1565
37.1k
    if (number_images != 1)
1566
0
      (void) SetImageProgressMonitor(p,progress_monitor,p->client_data);
1567
37.1k
    if (write_info->adjoin != MagickFalse)
1568
37.1k
      break;
1569
0
    if (number_images != 1)
1570
0
      {
1571
#if defined(MAGICKCORE_OPENMP_SUPPORT)
1572
        #pragma omp atomic
1573
#endif
1574
0
        progress++;
1575
0
        proceed=SetImageProgress(p,WriteImageTag,progress,number_images);
1576
0
        if (proceed == MagickFalse)
1577
0
          break;
1578
0
      }
1579
0
  }
1580
37.1k
  write_info=DestroyImageInfo(write_info);
1581
37.1k
  return(status != 0 ? MagickTrue : MagickFalse);
1582
37.1k
}