1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%                             CCCC  IIIII  N   N                              %
7%                            C        I    NN  N                              %
8%                            C        I    N N N                              %
9%                            C        I    N  NN                              %
10%                             CCCC  IIIII  N   N                              %
11%                                                                             %
12%                                                                             %
13%                    Read/Write Kodak Cineon Image Format                     %
14%                Cineon Image Format is a subset of SMTPE CIN                 %
15%                                                                             %
16%                                                                             %
17%                              Software Design                                %
18%                                   Cristy                                    %
19%                             Kelly Bergougnoux                               %
20%                               October 2003                                  %
21%                                                                             %
22%                                                                             %
23%  Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization      %
24%  dedicated to making software imaging solutions freely available.           %
25%                                                                             %
26%  You may not use this file except in compliance with the License.  You may  %
27%  obtain a copy of the License at                                            %
28%                                                                             %
29%    http://www.imagemagick.org/script/license.php                            %
30%                                                                             %
31%  Unless required by applicable law or agreed to in writing, software        %
32%  distributed under the License is distributed on an "AS IS" BASIS,          %
33%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
34%  See the License for the specific language governing permissions and        %
35%  limitations under the License.                                             %
36%                                                                             %
37%                                                                             %
38%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
39%
40%  Cineon image file format draft is available at
41%  http://www.cineon.com/ff_draft.php.
42%
43%
44*/
45
46/*
47  Include declarations.
48*/
49#include "MagickCore/studio.h"
50#include "MagickCore/artifact.h"
51#include "MagickCore/blob.h"
52#include "MagickCore/blob-private.h"
53#include "MagickCore/cache.h"
54#include "MagickCore/colorspace.h"
55#include "MagickCore/exception.h"
56#include "MagickCore/exception-private.h"
57#include "MagickCore/image.h"
58#include "MagickCore/image-private.h"
59#include "MagickCore/list.h"
60#include "MagickCore/magick.h"
61#include "MagickCore/memory_.h"
62#include "MagickCore/monitor.h"
63#include "MagickCore/monitor-private.h"
64#include "MagickCore/option.h"
65#include "MagickCore/profile.h"
66#include "MagickCore/property.h"
67#include "MagickCore/quantum-private.h"
68#include "MagickCore/quantum-private.h"
69#include "MagickCore/static.h"
70#include "MagickCore/string_.h"
71#include "MagickCore/string-private.h"
72#include "MagickCore/module.h"
73
74/*
75  Typedef declaration.
76*/
77typedef struct _CINDataFormatInfo
78{
79  unsigned char
80    interleave,
81    packing,
82    sign,
83    sense;
84
85  size_t
86    line_pad,
87    channel_pad;
88
89  unsigned char
90    reserve[20];
91} CINDataFormatInfo;
92
93typedef struct _CINFileInfo
94{
95  size_t
96    magic,
97    image_offset,
98    generic_length,
99    industry_length,
100    user_length,
101    file_size;
102
103  char
104    version[8],
105    filename[100],
106    create_date[12],
107    create_time[12],
108    reserve[36];
109} CINFileInfo;
110
111typedef struct _CINFilmInfo
112{
113  char
114    id,
115    type,
116    offset,
117    reserve1;
118
119  size_t
120    prefix,
121    count;
122
123  char
124    format[32];
125
126  size_t
127    frame_position;
128
129  float
130    frame_rate;
131
132  char
133    frame_id[32],
134    slate_info[200],
135    reserve[740];
136} CINFilmInfo;
137
138typedef struct _CINImageChannel
139{
140  unsigned char
141    designator[2],
142    bits_per_pixel,
143    reserve;
144
145  size_t
146    pixels_per_line,
147    lines_per_image;
148
149  float
150    min_data,
151    min_quantity,
152    max_data,
153    max_quantity;
154} CINImageChannel;
155
156typedef struct _CINImageInfo
157{
158  unsigned char
159    orientation,
160    number_channels,
161    reserve1[2];
162
163  CINImageChannel
164    channel[8];
165
166  float
167    white_point[2],
168    red_primary_chromaticity[2],
169    green_primary_chromaticity[2],
170    blue_primary_chromaticity[2];
171
172  char
173    label[200],
174    reserve[28];
175} CINImageInfo;
176
177typedef struct _CINOriginationInfo
178{
179  ssize_t
180    x_offset,
181    y_offset;
182
183  char
184    filename[100],
185    create_date[12],
186    create_time[12],
187    device[64],
188    model[32],
189    serial[32];
190
191  float
192    x_pitch,
193    y_pitch,
194    gamma;
195
196  char
197    reserve[40];
198} CINOriginationInfo;
199
200typedef struct _CINUserInfo
201{
202  char
203    id[32];
204} CINUserInfo;
205
206typedef struct CINInfo
207{
208  CINFileInfo
209    file;
210
211  CINImageInfo
212    image;
213
214  CINDataFormatInfo
215    data_format;
216
217  CINOriginationInfo
218    origination;
219
220  CINFilmInfo
221    film;
222
223  CINUserInfo
224    user;
225} CINInfo;
226
227/*
228  Forward declaractions.
229*/
230static MagickBooleanType
231  WriteCINImage(const ImageInfo *,Image *,ExceptionInfo *);
232
233/*
234%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
235%                                                                             %
236%                                                                             %
237%                                                                             %
238%   I s C I N E O N                                                           %
239%                                                                             %
240%                                                                             %
241%                                                                             %
242%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
243%
244%  IsCIN() returns MagickTrue if the image format type, identified by the magick
245%  string, is CIN.
246%
247%  The format of the IsCIN method is:
248%
249%      MagickBooleanType IsCIN(const unsigned char *magick,const size_t length)
250%
251%  A description of each parameter follows:
252%
253%    o magick: compare image format pattern against these bytes.
254%
255%    o length: Specifies the length of the magick string.
256%
257*/
258static MagickBooleanType IsCIN(const unsigned char *magick,const size_t length)
259{
260  if (length < 4)
261    return(MagickFalse);
262  if (memcmp(magick,"\200\052\137\327",4) == 0)
263    return(MagickTrue);
264  return(MagickFalse);
265}
266
267/*
268%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
269%                                                                             %
270%                                                                             %
271%                                                                             %
272%   R e a d C I N E O N I m a g e                                             %
273%                                                                             %
274%                                                                             %
275%                                                                             %
276%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
277%
278%  ReadCINImage() reads an CIN X image file and returns it.  It allocates
279%  the memory necessary for the new Image structure and returns a point to the
280%  new image.
281%
282%  The format of the ReadCINImage method is:
283%
284%      Image *ReadCINImage(const ImageInfo *image_info,
285%        ExceptionInfo *exception)
286%
287%  A description of each parameter follows:
288%
289%    o image_info: the image info.
290%
291%    o exception: return any errors or warnings in this structure.
292%
293*/
294
295static size_t GetBytesPerRow(size_t columns,
296  size_t samples_per_pixel,size_t bits_per_pixel,
297  MagickBooleanType pad)
298{
299  size_t
300    bytes_per_row;
301
302  switch (bits_per_pixel)
303  {
304    case 1:
305    {
306      bytes_per_row=4*(((size_t) samples_per_pixel*columns*
307        bits_per_pixel+31)/32);
308      break;
309    }
310    case 8:
311    default:
312    {
313      bytes_per_row=4*(((size_t) samples_per_pixel*columns*
314        bits_per_pixel+31)/32);
315      break;
316    }
317    case 10:
318    {
319      if (pad == MagickFalse)
320        {
321          bytes_per_row=4*(((size_t) samples_per_pixel*columns*
322            bits_per_pixel+31)/32);
323          break;
324        }
325      bytes_per_row=4*(((size_t) (32*((samples_per_pixel*columns+2)/3))+31)/32);
326      break;
327    }
328    case 12:
329    {
330      if (pad == MagickFalse)
331        {
332          bytes_per_row=4*(((size_t) samples_per_pixel*columns*
333            bits_per_pixel+31)/32);
334          break;
335        }
336      bytes_per_row=2*(((size_t) (16*samples_per_pixel*columns)+15)/16);
337      break;
338    }
339    case 16:
340    {
341      bytes_per_row=2*(((size_t) samples_per_pixel*columns*
342        bits_per_pixel+8)/16);
343      break;
344    }
345    case 32:
346    {
347      bytes_per_row=4*(((size_t) samples_per_pixel*columns*
348        bits_per_pixel+31)/32);
349      break;
350    }
351    case 64:
352    {
353      bytes_per_row=8*(((size_t) samples_per_pixel*columns*
354        bits_per_pixel+63)/64);
355      break;
356    }
357  }
358  return(bytes_per_row);
359}
360
361static inline MagickBooleanType IsFloatDefined(const float value)
362{
363  union
364  {
365    unsigned int
366      unsigned_value;
367
368    double
369      float_value;
370  } quantum;
371
372  quantum.unsigned_value=0U;
373  quantum.float_value=value;
374  if (quantum.unsigned_value == 0U)
375    return(MagickFalse);
376  return(MagickTrue);
377}
378
379static Image *ReadCINImage(const ImageInfo *image_info,ExceptionInfo *exception)
380{
381#define MonoColorType  1
382#define RGBColorType  3
383
384  char
385    property[MagickPathExtent];
386
387  CINInfo
388    cin;
389
390  const unsigned char
391    *pixels;
392
393  Image
394    *image;
395
396  MagickBooleanType
397    status;
398
399  MagickOffsetType
400    offset;
401
402  QuantumInfo
403    *quantum_info;
404
405  QuantumType
406    quantum_type;
407
408  register ssize_t
409    i;
410
411  register Quantum
412    *q;
413
414  size_t
415    length;
416
417  ssize_t
418    count,
419    y;
420
421  unsigned char
422    magick[4];
423
424  /*
425    Open image file.
426  */
427  assert(image_info != (const ImageInfo *) NULL);
428  assert(image_info->signature == MagickCoreSignature);
429  if (image_info->debug != MagickFalse)
430    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
431      image_info->filename);
432  assert(exception != (ExceptionInfo *) NULL);
433  assert(exception->signature == MagickCoreSignature);
434  image=AcquireImage(image_info,exception);
435  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
436  if (status == MagickFalse)
437    {
438      image=DestroyImageList(image);
439      return((Image *) NULL);
440    }
441  /*
442    File information.
443  */
444  offset=0;
445  count=ReadBlob(image,4,magick);
446  offset+=count;
447  if ((count != 4) ||
448      ((LocaleNCompare((char *) magick,"\200\052\137\327",4) != 0)))
449    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
450  image->endian=(magick[0] == 0x80) && (magick[1] == 0x2a) &&
451    (magick[2] == 0x5f) && (magick[3] == 0xd7) ? MSBEndian : LSBEndian;
452  cin.file.image_offset=ReadBlobLong(image);
453  offset+=4;
454  cin.file.generic_length=ReadBlobLong(image);
455  offset+=4;
456  cin.file.industry_length=ReadBlobLong(image);
457  offset+=4;
458  cin.file.user_length=ReadBlobLong(image);
459  offset+=4;
460  cin.file.file_size=ReadBlobLong(image);
461  offset+=4;
462  offset+=ReadBlob(image,sizeof(cin.file.version),(unsigned char *)
463    cin.file.version);
464  (void) CopyMagickString(property,cin.file.version,sizeof(cin.file.version));
465  (void) SetImageProperty(image,"dpx:file.version",property,exception);
466  offset+=ReadBlob(image,sizeof(cin.file.filename),(unsigned char *)
467    cin.file.filename);
468  (void) CopyMagickString(property,cin.file.filename,sizeof(cin.file.filename));
469  (void) SetImageProperty(image,"dpx:file.filename",property,exception);
470  offset+=ReadBlob(image,sizeof(cin.file.create_date),(unsigned char *)
471    cin.file.create_date);
472  (void) CopyMagickString(property,cin.file.create_date,
473    sizeof(cin.file.create_date));
474  (void) SetImageProperty(image,"dpx:file.create_date",property,exception);
475  offset+=ReadBlob(image,sizeof(cin.file.create_time),(unsigned char *)
476    cin.file.create_time);
477  (void) CopyMagickString(property,cin.file.create_time,
478    sizeof(cin.file.create_time));
479  (void) SetImageProperty(image,"dpx:file.create_time",property,exception);
480  offset+=ReadBlob(image,sizeof(cin.file.reserve),(unsigned char *)
481    cin.file.reserve);
482  /*
483    Image information.
484  */
485  cin.image.orientation=(unsigned char) ReadBlobByte(image);
486  offset++;
487  if (cin.image.orientation != (unsigned char) (~0))
488    (void) FormatImageProperty(image,"dpx:image.orientation","%d",
489      cin.image.orientation);
490  switch (cin.image.orientation)
491  {
492    default:
493    case 0: image->orientation=TopLeftOrientation; break;
494    case 1: image->orientation=TopRightOrientation; break;
495    case 2: image->orientation=BottomLeftOrientation; break;
496    case 3: image->orientation=BottomRightOrientation; break;
497    case 4: image->orientation=LeftTopOrientation; break;
498    case 5: image->orientation=RightTopOrientation; break;
499    case 6: image->orientation=LeftBottomOrientation; break;
500    case 7: image->orientation=RightBottomOrientation; break;
501  }
502  cin.image.number_channels=(unsigned char) ReadBlobByte(image);
503  offset++;
504  offset+=ReadBlob(image,sizeof(cin.image.reserve1),(unsigned char *)
505    cin.image.reserve1);
506  for (i=0; i < 8; i++)
507  {
508    cin.image.channel[i].designator[0]=(unsigned char) ReadBlobByte(image);
509    offset++;
510    cin.image.channel[i].designator[1]=(unsigned char) ReadBlobByte(image);
511    offset++;
512    cin.image.channel[i].bits_per_pixel=(unsigned char) ReadBlobByte(image);
513    offset++;
514    cin.image.channel[i].reserve=(unsigned char) ReadBlobByte(image);
515    offset++;
516    cin.image.channel[i].pixels_per_line=ReadBlobLong(image);
517    offset+=4;
518    cin.image.channel[i].lines_per_image=ReadBlobLong(image);
519    offset+=4;
520    cin.image.channel[i].min_data=ReadBlobFloat(image);
521    offset+=4;
522    cin.image.channel[i].min_quantity=ReadBlobFloat(image);
523    offset+=4;
524    cin.image.channel[i].max_data=ReadBlobFloat(image);
525    offset+=4;
526    cin.image.channel[i].max_quantity=ReadBlobFloat(image);
527    offset+=4;
528  }
529  cin.image.white_point[0]=ReadBlobFloat(image);
530  offset+=4;
531  if (IsFloatDefined(cin.image.white_point[0]) != MagickFalse)
532    image->chromaticity.white_point.x=cin.image.white_point[0];
533  cin.image.white_point[1]=ReadBlobFloat(image);
534  offset+=4;
535  if (IsFloatDefined(cin.image.white_point[1]) != MagickFalse)
536    image->chromaticity.white_point.y=cin.image.white_point[1];
537  cin.image.red_primary_chromaticity[0]=ReadBlobFloat(image);
538  offset+=4;
539  if (IsFloatDefined(cin.image.red_primary_chromaticity[0]) != MagickFalse)
540    image->chromaticity.red_primary.x=cin.image.red_primary_chromaticity[0];
541  cin.image.red_primary_chromaticity[1]=ReadBlobFloat(image);
542  offset+=4;
543  if (IsFloatDefined(cin.image.red_primary_chromaticity[1]) != MagickFalse)
544    image->chromaticity.red_primary.y=cin.image.red_primary_chromaticity[1];
545  cin.image.green_primary_chromaticity[0]=ReadBlobFloat(image);
546  offset+=4;
547  if (IsFloatDefined(cin.image.green_primary_chromaticity[0]) != MagickFalse)
548    image->chromaticity.red_primary.x=cin.image.green_primary_chromaticity[0];
549  cin.image.green_primary_chromaticity[1]=ReadBlobFloat(image);
550  offset+=4;
551  if (IsFloatDefined(cin.image.green_primary_chromaticity[1]) != MagickFalse)
552    image->chromaticity.green_primary.y=cin.image.green_primary_chromaticity[1];
553  cin.image.blue_primary_chromaticity[0]=ReadBlobFloat(image);
554  offset+=4;
555  if (IsFloatDefined(cin.image.blue_primary_chromaticity[0]) != MagickFalse)
556    image->chromaticity.blue_primary.x=cin.image.blue_primary_chromaticity[0];
557  cin.image.blue_primary_chromaticity[1]=ReadBlobFloat(image);
558  offset+=4;
559  if (IsFloatDefined(cin.image.blue_primary_chromaticity[1]) != MagickFalse)
560    image->chromaticity.blue_primary.y=cin.image.blue_primary_chromaticity[1];
561  offset+=ReadBlob(image,sizeof(cin.image.label),(unsigned char *)
562    cin.image.label);
563  (void) CopyMagickString(property,cin.image.label,sizeof(cin.image.label));
564  (void) SetImageProperty(image,"dpx:image.label",property,exception);
565  offset+=ReadBlob(image,sizeof(cin.image.reserve),(unsigned char *)
566    cin.image.reserve);
567  /*
568    Image data format information.
569  */
570  cin.data_format.interleave=(unsigned char) ReadBlobByte(image);
571  offset++;
572  cin.data_format.packing=(unsigned char) ReadBlobByte(image);
573  offset++;
574  cin.data_format.sign=(unsigned char) ReadBlobByte(image);
575  offset++;
576  cin.data_format.sense=(unsigned char) ReadBlobByte(image);
577  offset++;
578  cin.data_format.line_pad=ReadBlobLong(image);
579  offset+=4;
580  cin.data_format.channel_pad=ReadBlobLong(image);
581  offset+=4;
582  offset+=ReadBlob(image,sizeof(cin.data_format.reserve),(unsigned char *)
583    cin.data_format.reserve);
584  /*
585    Image origination information.
586  */
587  cin.origination.x_offset=ReadBlobSignedLong(image);
588  offset+=4;
589  if ((size_t) cin.origination.x_offset != ~0UL)
590    (void) FormatImageProperty(image,"dpx:origination.x_offset","%.20g",
591      (double) cin.origination.x_offset);
592  cin.origination.y_offset=(ssize_t) ReadBlobLong(image);
593  offset+=4;
594  if ((size_t) cin.origination.y_offset != ~0UL)
595    (void) FormatImageProperty(image,"dpx:origination.y_offset","%.20g",
596      (double) cin.origination.y_offset);
597  offset+=ReadBlob(image,sizeof(cin.origination.filename),(unsigned char *)
598    cin.origination.filename);
599  (void) CopyMagickString(property,cin.origination.filename,
600    sizeof(cin.origination.filename));
601  (void) SetImageProperty(image,"dpx:origination.filename",property,exception);
602  offset+=ReadBlob(image,sizeof(cin.origination.create_date),(unsigned char *)
603    cin.origination.create_date);
604  (void) CopyMagickString(property,cin.origination.create_date,
605    sizeof(cin.origination.create_date));
606  (void) SetImageProperty(image,"dpx:origination.create_date",property,
607    exception);
608  offset+=ReadBlob(image,sizeof(cin.origination.create_time),(unsigned char *)
609    cin.origination.create_time);
610  (void) CopyMagickString(property,cin.origination.create_time,
611    sizeof(cin.origination.create_time));
612  (void) SetImageProperty(image,"dpx:origination.create_time",property,
613    exception);
614  offset+=ReadBlob(image,sizeof(cin.origination.device),(unsigned char *)
615    cin.origination.device);
616  (void) CopyMagickString(property,cin.origination.device,
617    sizeof(cin.origination.device));
618  (void) SetImageProperty(image,"dpx:origination.device",property,exception);
619  offset+=ReadBlob(image,sizeof(cin.origination.model),(unsigned char *)
620    cin.origination.model);
621  (void) CopyMagickString(property,cin.origination.model,
622    sizeof(cin.origination.model));
623  (void) SetImageProperty(image,"dpx:origination.model",property,exception);
624  (void) ResetMagickMemory(cin.origination.serial,0,
625    sizeof(cin.origination.serial));
626  offset+=ReadBlob(image,sizeof(cin.origination.serial),(unsigned char *)
627    cin.origination.serial);
628  (void) CopyMagickString(property,cin.origination.serial,
629    sizeof(cin.origination.serial));
630  (void) SetImageProperty(image,"dpx:origination.serial",property,exception);
631  cin.origination.x_pitch=ReadBlobFloat(image);
632  offset+=4;
633  cin.origination.y_pitch=ReadBlobFloat(image);
634  offset+=4;
635  cin.origination.gamma=ReadBlobFloat(image);
636  offset+=4;
637  if (IsFloatDefined(cin.origination.gamma) != MagickFalse)
638    image->gamma=cin.origination.gamma;
639  offset+=ReadBlob(image,sizeof(cin.origination.reserve),(unsigned char *)
640    cin.origination.reserve);
641  if ((cin.file.image_offset > 2048) && (cin.file.user_length != 0))
642    {
643      int
644        c;
645
646      /*
647        Image film information.
648      */
649      cin.film.id=ReadBlobByte(image);
650      offset++;
651      c=cin.film.id;
652      if (c != ~0)
653        (void) FormatImageProperty(image,"dpx:film.id","%d",cin.film.id);
654      cin.film.type=ReadBlobByte(image);
655      offset++;
656      c=cin.film.type;
657      if (c != ~0)
658        (void) FormatImageProperty(image,"dpx:film.type","%d",cin.film.type);
659      cin.film.offset=ReadBlobByte(image);
660      offset++;
661      c=cin.film.offset;
662      if (c != ~0)
663        (void) FormatImageProperty(image,"dpx:film.offset","%d",
664          cin.film.offset);
665      cin.film.reserve1=ReadBlobByte(image);
666      offset++;
667      cin.film.prefix=ReadBlobLong(image);
668      offset+=4;
669      if (cin.film.prefix != ~0UL)
670        (void) FormatImageProperty(image,"dpx:film.prefix","%.20g",(double)
671          cin.film.prefix);
672      cin.film.count=ReadBlobLong(image);
673      offset+=4;
674      offset+=ReadBlob(image,sizeof(cin.film.format),(unsigned char *)
675        cin.film.format);
676      (void) CopyMagickString(property,cin.film.format,sizeof(cin.film.format));
677      (void) SetImageProperty(image,"dpx:film.format",property,exception);
678      cin.film.frame_position=ReadBlobLong(image);
679      offset+=4;
680      if (cin.film.frame_position != ~0UL)
681        (void) FormatImageProperty(image,"dpx:film.frame_position","%.20g",
682          (double) cin.film.frame_position);
683      cin.film.frame_rate=ReadBlobFloat(image);
684      offset+=4;
685      if (IsFloatDefined(cin.film.frame_rate) != MagickFalse)
686        (void) FormatImageProperty(image,"dpx:film.frame_rate","%g",
687          cin.film.frame_rate);
688      offset+=ReadBlob(image,sizeof(cin.film.frame_id),(unsigned char *)
689        cin.film.frame_id);
690      (void) CopyMagickString(property,cin.film.frame_id,
691        sizeof(cin.film.frame_id));
692      (void) SetImageProperty(image,"dpx:film.frame_id",property,exception);
693      offset+=ReadBlob(image,sizeof(cin.film.slate_info),(unsigned char *)
694        cin.film.slate_info);
695      (void) CopyMagickString(property,cin.film.slate_info,
696        sizeof(cin.film.slate_info));
697      (void) SetImageProperty(image,"dpx:film.slate_info",property,exception);
698      offset+=ReadBlob(image,sizeof(cin.film.reserve),(unsigned char *)
699        cin.film.reserve);
700    }
701  if ((cin.file.image_offset > 2048) && (cin.file.user_length != 0))
702    {
703      StringInfo
704        *profile;
705
706      /*
707        User defined data.
708      */
709      profile=BlobToStringInfo((const unsigned char *) NULL,cin.file.user_length);
710      if (profile == (StringInfo *) NULL)
711        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
712      offset+=ReadBlob(image,GetStringInfoLength(profile),
713        GetStringInfoDatum(profile));
714      (void) SetImageProfile(image,"dpx:user.data",profile,exception);
715      profile=DestroyStringInfo(profile);
716    }
717  image->depth=cin.image.channel[0].bits_per_pixel;
718  image->columns=cin.image.channel[0].pixels_per_line;
719  image->rows=cin.image.channel[0].lines_per_image;
720  if (image_info->ping != MagickFalse)
721    {
722      (void) CloseBlob(image);
723      return(image);
724    }
725  for ( ; offset < (MagickOffsetType) cin.file.image_offset; offset++)
726  {
727    int
728      c;
729
730    c=ReadBlobByte(image);
731    if (c == EOF)
732      break;
733  }
734  if (offset < (MagickOffsetType) cin.file.image_offset)
735    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
736  status=SetImageExtent(image,image->columns,image->rows,exception);
737  if (status == MagickFalse)
738    return(DestroyImageList(image));
739  /*
740    Convert CIN raster image to pixel packets.
741  */
742  quantum_info=AcquireQuantumInfo(image_info,image);
743  if (quantum_info == (QuantumInfo *) NULL)
744    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
745  quantum_info->quantum=32;
746  quantum_info->pack=MagickFalse;
747  quantum_type=RGBQuantum;
748  length=GetQuantumExtent(image,quantum_info,quantum_type);
749  length=GetBytesPerRow(image->columns,3,image->depth,MagickTrue);
750  if (cin.image.number_channels == 1)
751    {
752      quantum_type=GrayQuantum;
753      length=GetBytesPerRow(image->columns,1,image->depth,MagickTrue);
754    }
755  for (y=0; y < (ssize_t) image->rows; y++)
756  {
757    q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
758    if (q == (Quantum *) NULL)
759      break;
760    pixels=(const unsigned char *) ReadBlobStream(image,length,
761      GetQuantumPixels(quantum_info),&count);
762    if ((size_t) count != length)
763      break;
764    (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
765      quantum_type,pixels,exception);
766    if (SyncAuthenticPixels(image,exception) == MagickFalse)
767      break;
768    if (image->previous == (Image *) NULL)
769      {
770        status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
771          image->rows);
772        if (status == MagickFalse)
773          break;
774      }
775  }
776  SetQuantumImageType(image,quantum_type);
777  quantum_info=DestroyQuantumInfo(quantum_info);
778  if (EOFBlob(image) != MagickFalse)
779    ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
780      image->filename);
781  SetImageColorspace(image,LogColorspace,exception);
782  (void) CloseBlob(image);
783  return(GetFirstImageInList(image));
784}
785
786/*
787%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
788%                                                                             %
789%                                                                             %
790%                                                                             %
791%   R e g i s t e r C I N E O N I m a g e                                     %
792%                                                                             %
793%                                                                             %
794%                                                                             %
795%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
796%
797%  RegisterCINImage() adds attributes for the CIN image format to the list of
798%  of supported formats.  The attributes include the image format tag, a method
799%  to read and/or write the format, whether the format supports the saving of
800%  more than one frame to the same file or blob, whether the format supports
801%  native in-memory I/O, and a brief description of the format.
802%
803%  The format of the RegisterCINImage method is:
804%
805%      size_t RegisterCINImage(void)
806%
807*/
808ModuleExport size_t RegisterCINImage(void)
809{
810  MagickInfo
811    *entry;
812
813  entry=AcquireMagickInfo("CIN","CIN","Cineon Image File");
814  entry->decoder=(DecodeImageHandler *) ReadCINImage;
815  entry->encoder=(EncodeImageHandler *) WriteCINImage;
816  entry->magick=(IsImageFormatHandler *) IsCIN;
817  entry->flags^=CoderAdjoinFlag;
818  (void) RegisterMagickInfo(entry);
819  return(MagickImageCoderSignature);
820}
821
822/*
823%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
824%                                                                             %
825%                                                                             %
826%                                                                             %
827%   U n r e g i s t e r C I N E O N I m a g e                                 %
828%                                                                             %
829%                                                                             %
830%                                                                             %
831%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
832%
833%  UnregisterCINImage() removes format registrations made by the CIN module
834%  from the list of supported formats.
835%
836%  The format of the UnregisterCINImage method is:
837%
838%      UnregisterCINImage(void)
839%
840*/
841ModuleExport void UnregisterCINImage(void)
842{
843  (void) UnregisterMagickInfo("CINEON");
844}
845
846/*
847%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
848%                                                                             %
849%                                                                             %
850%                                                                             %
851%   W r i t e C I N E O N I m a g e                                           %
852%                                                                             %
853%                                                                             %
854%                                                                             %
855%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
856%
857%  WriteCINImage() writes an image in CIN encoded image format.
858%
859%  The format of the WriteCINImage method is:
860%
861%      MagickBooleanType WriteCINImage(const ImageInfo *image_info,
862%        Image *image,ExceptionInfo *exception)
863%
864%  A description of each parameter follows.
865%
866%    o image_info: the image info.
867%
868%    o image:  The image.
869%
870%    o exception: return any errors or warnings in this structure.
871%
872*/
873
874static inline const char *GetCINProperty(const ImageInfo *image_info,
875  const Image *image,const char *property,ExceptionInfo *exception)
876{
877  const char
878    *value;
879
880  value=GetImageOption(image_info,property);
881  if (value != (const char *) NULL)
882    return(value);
883  return(GetImageProperty(image,property,exception));
884}
885
886static MagickBooleanType WriteCINImage(const ImageInfo *image_info,Image *image,
887  ExceptionInfo *exception)
888{
889  char
890    timestamp[MagickPathExtent];
891
892  const char
893    *value;
894
895  CINInfo
896    cin;
897
898  const StringInfo
899    *profile;
900
901  MagickBooleanType
902    status;
903
904  MagickOffsetType
905    offset;
906
907  QuantumInfo
908    *quantum_info;
909
910  QuantumType
911    quantum_type;
912
913  register const Quantum
914    *p;
915
916  register ssize_t
917    i;
918
919  size_t
920    length;
921
922  ssize_t
923    count,
924    y;
925
926  struct tm
927    local_time;
928
929  time_t
930    seconds;
931
932  unsigned char
933    *pixels;
934
935  /*
936    Open output image file.
937  */
938  assert(image_info != (const ImageInfo *) NULL);
939  assert(image_info->signature == MagickCoreSignature);
940  assert(image != (Image *) NULL);
941  assert(image->signature == MagickCoreSignature);
942  if (image->debug != MagickFalse)
943    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
944  assert(exception != (ExceptionInfo *) NULL);
945  assert(exception->signature == MagickCoreSignature);
946  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
947  if (status == MagickFalse)
948    return(status);
949  if (image->colorspace != LogColorspace)
950    (void) TransformImageColorspace(image,LogColorspace,exception);
951  /*
952    Write image information.
953  */
954  (void) ResetMagickMemory(&cin,0,sizeof(cin));
955  offset=0;
956  cin.file.magic=0x802A5FD7UL;
957  offset+=WriteBlobLong(image,(unsigned int) cin.file.magic);
958  cin.file.image_offset=0x800;
959  offset+=WriteBlobLong(image,(unsigned int) cin.file.image_offset);
960  cin.file.generic_length=0x400;
961  offset+=WriteBlobLong(image,(unsigned int) cin.file.generic_length);
962  cin.file.industry_length=0x400;
963  offset+=WriteBlobLong(image,(unsigned int) cin.file.industry_length);
964  cin.file.user_length=0x00;
965  profile=GetImageProfile(image,"dpx:user.data");
966  if (profile != (StringInfo *) NULL)
967    {
968      cin.file.user_length+=(size_t) GetStringInfoLength(profile);
969      cin.file.user_length=(((cin.file.user_length+0x2000-1)/0x2000)*0x2000);
970    }
971  offset+=WriteBlobLong(image,(unsigned int) cin.file.user_length);
972  cin.file.file_size=4*image->columns*image->rows+0x2000;
973  offset+=WriteBlobLong(image,(unsigned int) cin.file.file_size);
974  (void) CopyMagickString(cin.file.version,"V4.5",sizeof(cin.file.version));
975  offset+=WriteBlob(image,sizeof(cin.file.version),(unsigned char *)
976    cin.file.version);
977  value=GetCINProperty(image_info,image,"dpx:file.filename",exception);
978  if (value != (const char *) NULL)
979    (void) CopyMagickString(cin.file.filename,value,sizeof(cin.file.filename));
980  else
981    (void) CopyMagickString(cin.file.filename,image->filename,
982      sizeof(cin.file.filename));
983  offset+=WriteBlob(image,sizeof(cin.file.filename),(unsigned char *)
984    cin.file.filename);
985  seconds=time((time_t *) NULL);
986#if defined(MAGICKCORE_HAVE_LOCALTIME_R)
987  (void) localtime_r(&seconds,&local_time);
988#else
989  (void) memcpy(&local_time,localtime(&seconds),sizeof(local_time));
990#endif
991  (void) memset(timestamp,0,sizeof(timestamp));
992  (void) strftime(timestamp,MagickPathExtent,"%Y:%m:%d:%H:%M:%S%Z",&local_time);
993  (void) memset(cin.file.create_date,0,sizeof(cin.file.create_date));
994  (void) CopyMagickString(cin.file.create_date,timestamp,11);
995  offset+=WriteBlob(image,sizeof(cin.file.create_date),(unsigned char *)
996    cin.file.create_date);
997  (void) memset(cin.file.create_time,0,sizeof(cin.file.create_time));
998  (void) CopyMagickString(cin.file.create_time,timestamp+11,11);
999  offset+=WriteBlob(image,sizeof(cin.file.create_time),(unsigned char *)
1000    cin.file.create_time);
1001  offset+=WriteBlob(image,sizeof(cin.file.reserve),(unsigned char *)
1002    cin.file.reserve);
1003  cin.image.orientation=0x00;
1004  offset+=WriteBlobByte(image,cin.image.orientation);
1005  cin.image.number_channels=3;
1006  offset+=WriteBlobByte(image,cin.image.number_channels);
1007  offset+=WriteBlob(image,sizeof(cin.image.reserve1),(unsigned char *)
1008    cin.image.reserve1);
1009  for (i=0; i < 8; i++)
1010  {
1011    cin.image.channel[i].designator[0]=0; /* universal metric */
1012    offset+=WriteBlobByte(image,cin.image.channel[0].designator[0]);
1013    cin.image.channel[i].designator[1]=(unsigned char) (i > 3 ? 0 : i+1); /* channel color */;
1014    offset+=WriteBlobByte(image,cin.image.channel[1].designator[0]);
1015    cin.image.channel[i].bits_per_pixel=(unsigned char) image->depth;
1016    offset+=WriteBlobByte(image,cin.image.channel[0].bits_per_pixel);
1017    offset+=WriteBlobByte(image,cin.image.channel[0].reserve);
1018    cin.image.channel[i].pixels_per_line=image->columns;
1019    offset+=WriteBlobLong(image,(unsigned int)
1020      cin.image.channel[0].pixels_per_line);
1021    cin.image.channel[i].lines_per_image=image->rows;
1022    offset+=WriteBlobLong(image,(unsigned int)
1023      cin.image.channel[0].lines_per_image);
1024    cin.image.channel[i].min_data=0;
1025    offset+=WriteBlobFloat(image,cin.image.channel[0].min_data);
1026    cin.image.channel[i].min_quantity=0.0;
1027    offset+=WriteBlobFloat(image,cin.image.channel[0].min_quantity);
1028    cin.image.channel[i].max_data=(float) ((MagickOffsetType)
1029      GetQuantumRange(image->depth));
1030    offset+=WriteBlobFloat(image,cin.image.channel[0].max_data);
1031    cin.image.channel[i].max_quantity=2.048f;
1032    offset+=WriteBlobFloat(image,cin.image.channel[0].max_quantity);
1033  }
1034  offset+=WriteBlobFloat(image,image->chromaticity.white_point.x);
1035  offset+=WriteBlobFloat(image,image->chromaticity.white_point.y);
1036  offset+=WriteBlobFloat(image,image->chromaticity.red_primary.x);
1037  offset+=WriteBlobFloat(image,image->chromaticity.red_primary.y);
1038  offset+=WriteBlobFloat(image,image->chromaticity.green_primary.x);
1039  offset+=WriteBlobFloat(image,image->chromaticity.green_primary.y);
1040  offset+=WriteBlobFloat(image,image->chromaticity.blue_primary.x);
1041  offset+=WriteBlobFloat(image,image->chromaticity.blue_primary.y);
1042  value=GetCINProperty(image_info,image,"dpx:image.label",exception);
1043  if (value != (const char *) NULL)
1044    (void) CopyMagickString(cin.image.label,value,sizeof(cin.image.label));
1045  offset+=WriteBlob(image,sizeof(cin.image.label),(unsigned char *)
1046    cin.image.label);
1047  offset+=WriteBlob(image,sizeof(cin.image.reserve),(unsigned char *)
1048    cin.image.reserve);
1049  /*
1050    Write data format information.
1051  */
1052  cin.data_format.interleave=0; /* pixel interleave (rgbrgbr...) */
1053  offset+=WriteBlobByte(image,cin.data_format.interleave);
1054  cin.data_format.packing=5; /* packing ssize_tword (32bit) boundaries */
1055  offset+=WriteBlobByte(image,cin.data_format.packing);
1056  cin.data_format.sign=0; /* unsigned data */
1057  offset+=WriteBlobByte(image,cin.data_format.sign);
1058  cin.data_format.sense=0; /* image sense: positive image */
1059  offset+=WriteBlobByte(image,cin.data_format.sense);
1060  cin.data_format.line_pad=0;
1061  offset+=WriteBlobLong(image,(unsigned int) cin.data_format.line_pad);
1062  cin.data_format.channel_pad=0;
1063  offset+=WriteBlobLong(image,(unsigned int) cin.data_format.channel_pad);
1064  offset+=WriteBlob(image,sizeof(cin.data_format.reserve),(unsigned char *)
1065    cin.data_format.reserve);
1066  /*
1067    Write origination information.
1068  */
1069  cin.origination.x_offset=0UL;
1070  value=GetCINProperty(image_info,image,"dpx:origination.x_offset",exception);
1071  if (value != (const char *) NULL)
1072    cin.origination.x_offset=(ssize_t) StringToLong(value);
1073  offset+=WriteBlobLong(image,(unsigned int) cin.origination.x_offset);
1074  cin.origination.y_offset=0UL;
1075  value=GetCINProperty(image_info,image,"dpx:origination.y_offset",exception);
1076  if (value != (const char *) NULL)
1077    cin.origination.y_offset=(ssize_t) StringToLong(value);
1078  offset+=WriteBlobLong(image,(unsigned int) cin.origination.y_offset);
1079  value=GetCINProperty(image_info,image,"dpx:origination.filename",exception);
1080  if (value != (const char *) NULL)
1081    (void) CopyMagickString(cin.origination.filename,value,
1082      sizeof(cin.origination.filename));
1083  else
1084    (void) CopyMagickString(cin.origination.filename,image->filename,
1085      sizeof(cin.origination.filename));
1086  offset+=WriteBlob(image,sizeof(cin.origination.filename),(unsigned char *)
1087    cin.origination.filename);
1088  seconds=time((time_t *) NULL);
1089  (void) memset(timestamp,0,sizeof(timestamp));
1090  (void) strftime(timestamp,MagickPathExtent,"%Y:%m:%d:%H:%M:%S%Z",&local_time);
1091  (void) memset(cin.origination.create_date,0,
1092    sizeof(cin.origination.create_date));
1093  (void) CopyMagickString(cin.origination.create_date,timestamp,11);
1094  offset+=WriteBlob(image,sizeof(cin.origination.create_date),(unsigned char *)
1095    cin.origination.create_date);
1096  (void) memset(cin.origination.create_time,0,
1097     sizeof(cin.origination.create_time));
1098  (void) CopyMagickString(cin.origination.create_time,timestamp+11,15);
1099  offset+=WriteBlob(image,sizeof(cin.origination.create_time),(unsigned char *)
1100    cin.origination.create_time);
1101  value=GetCINProperty(image_info,image,"dpx:origination.device",exception);
1102  if (value != (const char *) NULL)
1103    (void) CopyMagickString(cin.origination.device,value,
1104      sizeof(cin.origination.device));
1105  offset+=WriteBlob(image,sizeof(cin.origination.device),(unsigned char *)
1106    cin.origination.device);
1107  value=GetCINProperty(image_info,image,"dpx:origination.model",exception);
1108  if (value != (const char *) NULL)
1109    (void) CopyMagickString(cin.origination.model,value,
1110      sizeof(cin.origination.model));
1111  offset+=WriteBlob(image,sizeof(cin.origination.model),(unsigned char *)
1112    cin.origination.model);
1113  value=GetCINProperty(image_info,image,"dpx:origination.serial",exception);
1114  if (value != (const char *) NULL)
1115    (void) CopyMagickString(cin.origination.serial,value,
1116      sizeof(cin.origination.serial));
1117  offset+=WriteBlob(image,sizeof(cin.origination.serial),(unsigned char *)
1118    cin.origination.serial);
1119  cin.origination.x_pitch=0.0f;
1120  value=GetCINProperty(image_info,image,"dpx:origination.x_pitch",exception);
1121  if (value != (const char *) NULL)
1122    cin.origination.x_pitch=StringToDouble(value,(char **) NULL);
1123  offset+=WriteBlobFloat(image,cin.origination.x_pitch);
1124  cin.origination.y_pitch=0.0f;
1125  value=GetCINProperty(image_info,image,"dpx:origination.y_pitch",exception);
1126  if (value != (const char *) NULL)
1127    cin.origination.y_pitch=StringToDouble(value,(char **) NULL);
1128  offset+=WriteBlobFloat(image,cin.origination.y_pitch);
1129  cin.origination.gamma=image->gamma;
1130  offset+=WriteBlobFloat(image,cin.origination.gamma);
1131  offset+=WriteBlob(image,sizeof(cin.origination.reserve),(unsigned char *)
1132    cin.origination.reserve);
1133  /*
1134    Image film information.
1135  */
1136  cin.film.id=0;
1137  value=GetCINProperty(image_info,image,"dpx:film.id",exception);
1138  if (value != (const char *) NULL)
1139    cin.film.id=(char) StringToLong(value);
1140  offset+=WriteBlobByte(image,(unsigned char) cin.film.id);
1141  cin.film.type=0;
1142  value=GetCINProperty(image_info,image,"dpx:film.type",exception);
1143  if (value != (const char *) NULL)
1144    cin.film.type=(char) StringToLong(value);
1145  offset+=WriteBlobByte(image,(unsigned char) cin.film.type);
1146  cin.film.offset=0;
1147  value=GetCINProperty(image_info,image,"dpx:film.offset",exception);
1148  if (value != (const char *) NULL)
1149    cin.film.offset=(char) StringToLong(value);
1150  offset+=WriteBlobByte(image,(unsigned char) cin.film.offset);
1151  offset+=WriteBlobByte(image,(unsigned char) cin.film.reserve1);
1152  cin.film.prefix=0UL;
1153  value=GetCINProperty(image_info,image,"dpx:film.prefix",exception);
1154  if (value != (const char *) NULL)
1155    cin.film.prefix=StringToUnsignedLong(value);
1156  offset+=WriteBlobLong(image,(unsigned int) cin.film.prefix);
1157  cin.film.count=0UL;
1158  value=GetCINProperty(image_info,image,"dpx:film.count",exception);
1159  if (value != (const char *) NULL)
1160    cin.film.count=StringToUnsignedLong(value);
1161  offset+=WriteBlobLong(image,(unsigned int) cin.film.count);
1162  value=GetCINProperty(image_info,image,"dpx:film.format",exception);
1163  if (value != (const char *) NULL)
1164    (void) CopyMagickString(cin.film.format,value,sizeof(cin.film.format));
1165  offset+=WriteBlob(image,sizeof(cin.film.format),(unsigned char *)
1166    cin.film.format);
1167  cin.film.frame_position=0UL;
1168  value=GetCINProperty(image_info,image,"dpx:film.frame_position",exception);
1169  if (value != (const char *) NULL)
1170    cin.film.frame_position=StringToUnsignedLong(value);
1171  offset+=WriteBlobLong(image,(unsigned int) cin.film.frame_position);
1172  cin.film.frame_rate=0.0f;
1173  value=GetCINProperty(image_info,image,"dpx:film.frame_rate",exception);
1174  if (value != (const char *) NULL)
1175    cin.film.frame_rate=StringToDouble(value,(char **) NULL);
1176  offset+=WriteBlobFloat(image,cin.film.frame_rate);
1177  value=GetCINProperty(image_info,image,"dpx:film.frame_id",exception);
1178  if (value != (const char *) NULL)
1179    (void) CopyMagickString(cin.film.frame_id,value,sizeof(cin.film.frame_id));
1180  offset+=WriteBlob(image,sizeof(cin.film.frame_id),(unsigned char *)
1181    cin.film.frame_id);
1182  value=GetCINProperty(image_info,image,"dpx:film.slate_info",exception);
1183  if (value != (const char *) NULL)
1184    (void) CopyMagickString(cin.film.slate_info,value,
1185      sizeof(cin.film.slate_info));
1186  offset+=WriteBlob(image,sizeof(cin.film.slate_info),(unsigned char *)
1187    cin.film.slate_info);
1188  offset+=WriteBlob(image,sizeof(cin.film.reserve),(unsigned char *)
1189    cin.film.reserve);
1190  if (profile != (StringInfo *) NULL)
1191    offset+=WriteBlob(image,GetStringInfoLength(profile),
1192      GetStringInfoDatum(profile));
1193  while (offset < (MagickOffsetType) cin.file.image_offset)
1194    offset+=WriteBlobByte(image,0x00);
1195  /*
1196    Convert pixel packets to CIN raster image.
1197  */
1198  quantum_info=AcquireQuantumInfo(image_info,image);
1199  if (quantum_info == (QuantumInfo *) NULL)
1200    ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1201  quantum_info->quantum=32;
1202  quantum_info->pack=MagickFalse;
1203  quantum_type=RGBQuantum;
1204  pixels=(unsigned char *) GetQuantumPixels(quantum_info);
1205  length=GetBytesPerRow(image->columns,3,image->depth,MagickTrue);
1206DisableMSCWarning(4127)
1207  if (0)
1208RestoreMSCWarning
1209    {
1210      quantum_type=GrayQuantum;
1211      length=GetBytesPerRow(image->columns,1,image->depth,MagickTrue);
1212    }
1213  for (y=0; y < (ssize_t) image->rows; y++)
1214  {
1215    p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1216    if (p == (const Quantum *) NULL)
1217      break;
1218    (void) ExportQuantumPixels(image,(CacheView *) NULL,quantum_info,
1219      quantum_type,pixels,exception);
1220    count=WriteBlob(image,length,pixels);
1221    if (count != (ssize_t) length)
1222      break;
1223    status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1224      image->rows);
1225    if (status == MagickFalse)
1226      break;
1227  }
1228  quantum_info=DestroyQuantumInfo(quantum_info);
1229  (void) CloseBlob(image);
1230  return(status);
1231}
1232