text: /*
  % Copyright (C) 2003-2021 GraphicsMagick Group
  %
  % This program is covered by multiple licenses, which are described in
  % Copyright.txt. You should have received a copy of Copyright.txt with this
  % package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
  %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                                                                             %
  %                                                                             %
  %                                                                             %
  %                   CCCC  IIIII  N   N  EEEEE  OOO   N   N                    %
  %                  C        I    NN  N  E     O   O  NN  N                    %
  %                  C        I    N N N  EEEE  O   O  N N N                    %
  %                  C        I    N  NN  E     O   O  N  NN                    %
  %                   CCCC  IIIII  N   N  EEEEE  OOO   N   N                    %
  %                                                                             %
  %                                                                             %
  %                    Read/Write Kodak Cineon Image Format.                    %
  %                 Cineon Image Format is similar to SMTPE DPX                 %
  %                                                                             %
  %                                                                             %
  %                              Software Design                                %
  %                              Bob Friesenhahn                                %
  %                                 May 2007                                    %
  %                                                                             %
  %                                                                             %
  %                                                                             %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %
  %  Cineon image file format draft is available at
  %  http://www.cineon.com/ff_draft.php.
  %
  %
  */
  
  /*
    Include declarations.
  */
  #include "magick/studio.h"
  #include "magick/attribute.h"
  #include "magick/blob.h"
  #include "magick/bit_stream.h"
  #include "magick/colorspace.h"
  #include "magick/enum_strings.h"
  #include "magick/pixel_cache.h"
  #include "magick/magick_endian.h"
  #include "magick/error.h"
  #include "magick/list.h"
  #include "magick/log.h"
  #include "magick/magick.h"
  #include "magick/monitor.h"
  #include "magick/profile.h"
  #include "magick/utility.h"
  
  /*
    Forward declaractions.
  */
  static unsigned int
    WriteCINEONImage(const ImageInfo *,Image *);
  
  typedef char ASCII;
  typedef magick_uint8_t U8;
  typedef magick_uint32_t U32;
  typedef magick_int32_t S32;
  typedef union _R32_u
  {
    magick_uint32_t u;
    float f;
  } R32;
  
  #define SET_UNDEFINED_U8(value)  (value=0xFFU)
  #define SET_UNDEFINED_U32(value) (value=0xFFFFFFFFU)
  #define SET_UNDEFINED_S32(value) (value=0x80000000)
  
  #define SET_UNDEFINED_R32(value) (value.u=0x7F800000U);
  #define SET_UNDEFINED_ASCII(value) ((void) memset(value,0,sizeof(value)))
  
  #define IS_UNDEFINED_U8(value) (value == ((U8) 0xFFU))
  #define IS_UNDEFINED_U32(value) (value == ((U32) 0xFFFFFFFFU))
  #define IS_UNDEFINED_S32(value) (value == ((S32) 0x80000000))
  #define IS_UNDEFINED_R32(value) (value.u == ((U32) 0x7F800000U))
  #define IS_UNDEFINED_ASCII(value) (!(value[0] > 0))
  
  typedef struct _CineonFileInfo
  {
    U32   magic;                     /* Magick number (0x802A5FD7) */
    U32   image_data_offset;         /* Offset to image data in bytes */
    U32   generic_section_length;    /* Generic section header length in bytes */
    U32   industry_section_length;   /* Industry specific header length in bytes */
    U32   user_defined_length;       /* User defined header length in bytes */
    U32   file_size;                 /* Total image file size in bytes  */
    ASCII header_format_version[8];  /* Version number of header format */
    ASCII image_filename[100];       /* Image filename */
    ASCII creation_date[12];         /* Creation date: yyyy:mm:dd */
    ASCII creation_time[12];         /* Creation time: hh:mm:ssLTZ */
    ASCII reserved[36];              /* Reserved for future use */
  } CineonFileInfo;
  
  typedef struct _CineonImageChannel
  {
    U8    designator_byte_0;         /* 0 = universal metric */
    U8    designator_byte_1;         /* */
    U8    bits_per_sample;           /* Bit depth */
    U8    unused;
    U32   pixels_per_line;           /* Pixels per line (columns) */
    U32   lines_per_image;           /* Lines per image (rows) */
    R32   reference_low_data_code;   /* Reference low data code value */
    R32   reference_low_quantity;    /* Low quantity represented */
    R32   reference_high_data_code;  /* Reference high data code value */
    R32   reference_high_quantity;   /* Reference high quantity represented */
  } DPXImageChannel;
  
  typedef struct _CineonImageInfo
  {
    U8    orientation;               /* Image orientation */
    U8    channels;                  /* Number of image channels (1-8) */
    U8    pad[2];                    /* Unused (2 byte space for word allignment */
    DPXImageChannel channel_info[8]; /* Description of channels */
    R32   white_point[2];            /* White point (color temperature) - x,y pair */
    R32   red_primary_chromaticity[2]; /* Red primary chromaticity - x,y pair */
    R32   green_primary_chromaticity[2]; /* Green primary chromaticity - x,y pair */
    R32   blue_primary_chromaticity[2]; /* Blue primary chromaticity - x,y pair */
    ASCII label_text[200];           /* Label text */
    ASCII reserved[28];              /* Reserved for future use */
    /* Image Data Format Information */
    U8    data_interleave;           /* Data interleave */
    U8    packing;                   /* Packing method */
    U8    sign;                      /* Data sign: 0=unsigned, 1=signed */
    U8    sense;                     /* Image sense: 0=positive, 1=negative */
    U32   eol_pad;                   /* End of line padding */
    U32   eoc_pad;                   /* End of channel padding */
    ASCII reserved2[20];             /* Reserved for future use */
  } CineonImageInfo;
  
  typedef struct _CineonImageOriginationInfo
  {
    S32   x_offset;                  /* X offset */
    S32   y_offset;                  /* Y offset */
    ASCII source_image_filename[100];/* Source image filename */
    ASCII creation_date[12];         /* Creation date: yyyy:mm:dd */
    ASCII creation_time[12];         /* Creation time: hh:mm:ssLTZ */
    ASCII input_device[64];          /* Input device */
    ASCII input_device_model[32];    /* Input device model number */
    ASCII input_device_serial[32];   /* Input device serial number */
    R32   input_device_pitch_x;      /* Input device pitch for X (samples/mm) */
    R32   input_device_pitch_y;      /* Input device pitch for Y (samples/mm) */
    R32   gamma;                     /* Image gamma of capture device */
    ASCII reserved[40];              /* Reserved for future use */
  } CineonImageOriginationInfo;
  
  typedef struct _CineonFilmInfo
  {
    U8    film_mfg_id_code;          /* Film mfg. ID code (2 digits from film edge code) */
    U8    film_type;                 /* Film type (2 digits from film edge code) */
    U8    perfs_offset;              /* Offset in perfs (2 digits from film edge code) */
    U8    unused;                    /* Unused, for word allignment */
    U32   prefix;                    /* Prefix (6 digits from film edge code) */
    U32   count;                     /* Count (4 digits from film edge code) */
    ASCII format[32];                /* Format -- e.g. Academy */
    U32   frame_position;            /* Frame position in sequence */
    R32   frame_rate;                /* Frame rate of original (frames/s) */
    ASCII frame_id[32];              /* Frame identification - e.g. keyframe */
    ASCII slate_info[200];           /* Slate information */
    ASCII reserved[740];             /* Reserved for future use */
  } CineonFilmInfo;
  
  
  /*
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                                                                             %
  %                                                                             %
  %                                                                             %
  %   I s C I N E O N                                                           %
  %                                                                             %
  %                                                                             %
  %                                                                             %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %
  %  IsCINEON() returns True if the image format type, identified by the magick
  %  string, is CINEON.
  %
  %  The format of the IsCINEON method is:
  %
  %      unsigned int IsCINEON(const unsigned char *magick,const size_t length)
  %
  %  A description of each parameter follows:
  %
  %    o magick: This string is generally the first few bytes of an image file
  %      or blob.
  %
  %    o length: Specifies the length of the magick string.
  %
  %
  */
  static unsigned int IsCINEON(const unsigned char *magick,const size_t length)
  {
    if (length < 4)
      return(False);
    if (memcmp(magick,"\200\052\137\327",4) == 0)
      return(True);
    return(False);
  }
  
  /*
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                                                                             %
  %                                                                             %
  %                                                                             %
  %   R e a d C I N E O N I m a g e                                             %
  %                                                                             %
  %                                                                             %
  %                                                                             %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %
  %  ReadCINEONImage() reads an CINEON X image file and returns it.  It allocates
  %  the memory necessary for the new Image structure and returns a point to the
  %  new image.
  %
  %  The format of the ReadCINEONImage method is:
  %
  %      Image *ReadCINEONImage(const ImageInfo *image_info,
  %        ExceptionInfo *exception)
  %
  %  A description of each parameter follows:
  %
  %    o image_info: The image info.
  %
  %    o exception: return any errors or warnings in this structure.
  %
  %
  */
  #define LogSetImageAttribute(name,value) \
  { \
    (void) LogMagickEvent(CoderEvent,GetMagickModule(), \
                          "Attribute \"%s\" set to \"%s\"", \
                          name,value); \
  }
  /*
    Null terminate ASCII fields which provide space for a terminating
    NULL.
  */
  #define NULLTerminateASCIIField(field) \
  { \
      field[sizeof(field)-1]='\0'; \
  }
  /*
    Can't use strlcpy() since strlcpy() only handles NULL terminated
    strings.  This is not so necessary due to use of
    NULLTerminateASCIIField() above.
  */
  #define StringToAttribute(image,name,member) \
  { \
    char \
      buffer_[MaxTextExtent]; \
  \
    if (!IS_UNDEFINED_ASCII(member)) \
      { \
        (void) memcpy(buffer_,member,Min(sizeof(member),MaxTextExtent)); \
        buffer_[Min(sizeof(member),MaxTextExtent-1)]='\0';             \
        (void) SetImageAttribute(image,name,buffer_); \
        LogSetImageAttribute(name,buffer_); \
      } \
  }
  #define U8ToAttribute(image,name,member) \
  { \
    char \
      buffer[MaxTextExtent]; \
  \
    if (!IS_UNDEFINED_U8(member)) \
      { \
        FormatString(buffer,"%u",(unsigned int) member); \
        (void) SetImageAttribute(image,name,buffer); \
        LogSetImageAttribute(name,buffer); \
      } \
  }
  #define U32ToAttribute(image,name,member) \
  { \
    char \
      buffer[MaxTextExtent]; \
  \
    if (!IS_UNDEFINED_U32(member)) \
      { \
        FormatString(buffer,"%u",member); \
        (void) SetImageAttribute(image,name,buffer); \
        LogSetImageAttribute(name,buffer); \
      } \
  }
  #define R32ToAttribute(image,name,member) \
  { \
    char \
      buffer[MaxTextExtent]; \
  \
    if (!IS_UNDEFINED_R32(member)) \
      { \
        FormatString(buffer,"%g",member.f); \
        (void) SetImageAttribute(image,name,buffer); \
        LogSetImageAttribute(name,buffer); \
      } \
  }
  #define S32ToAttribute(image,name,member) \
  { \
    char \
      buffer[MaxTextExtent]; \
  \
    if (!IS_UNDEFINED_S32(member)) \
      { \
        FormatString(buffer,"%d",member); \
        (void) SetImageAttribute(image,name,buffer); \
        LogSetImageAttribute(name,buffer); \
      } \
  }
  static void SwabCineonFileInfo(CineonFileInfo *file_info)
  {
    MagickSwabUInt32(&file_info->magic);
    MagickSwabUInt32(&file_info->image_data_offset);
    MagickSwabUInt32(&file_info->generic_section_length);
    MagickSwabUInt32(&file_info->industry_section_length);
    MagickSwabUInt32(&file_info->user_defined_length);
    MagickSwabUInt32(&file_info->file_size);
  }
  
  static void SwabCineonImageInfo(CineonImageInfo *image_info)
  {
    size_t
      i;
  
    for (i=0 ;
         i < Min((size_t) image_info->channels,
                 (sizeof(image_info->channel_info) /
                  sizeof(image_info->channel_info[0])));
         i++)
      {
        MagickSwabUInt32(&image_info->channel_info[i].pixels_per_line);
        MagickSwabUInt32(&image_info->channel_info[i].lines_per_image);
        MagickSwabFloat(&image_info->channel_info[i].reference_low_data_code.f);
        MagickSwabFloat(&image_info->channel_info[i].reference_low_quantity.f);
        MagickSwabFloat(&image_info->channel_info[i].reference_high_data_code.f);
        MagickSwabFloat(&image_info->channel_info[i].reference_high_quantity.f);
      }
  
    MagickSwabFloat(&image_info->white_point[0].f);
    MagickSwabFloat(&image_info->white_point[1].f);
    MagickSwabFloat(&image_info->red_primary_chromaticity[0].f);
    MagickSwabFloat(&image_info->red_primary_chromaticity[1].f);
    MagickSwabFloat(&image_info->green_primary_chromaticity[0].f);
    MagickSwabFloat(&image_info->green_primary_chromaticity[1].f);
    MagickSwabFloat(&image_info->blue_primary_chromaticity[0].f);
    MagickSwabFloat(&image_info->blue_primary_chromaticity[1].f);
    MagickSwabUInt32(&image_info->eol_pad);
    MagickSwabUInt32(&image_info->eoc_pad);
  }
  
  static void SwabCineonImageOriginationInfo(CineonImageOriginationInfo *image_info)
  {
    MagickSwabUInt32((U32 *) &image_info->x_offset);
    MagickSwabUInt32((U32 *) &image_info->y_offset);
    MagickSwabFloat(&image_info->input_device_pitch_x.f);
    MagickSwabFloat(&image_info->input_device_pitch_y.f);
    MagickSwabFloat(&image_info->gamma.f);
  }
  
  static void SwabCineonFilmInfo(CineonFilmInfo *mp_info)
  {
    MagickSwabUInt32(&mp_info->prefix);
    MagickSwabUInt32(&mp_info->count);
    MagickSwabUInt32(&mp_info->frame_position);
    MagickSwabFloat(&mp_info->frame_rate.f);
  }
  
  static OrientationType
  CineonOrientationToOrientationType(const unsigned int orientation)
  {
    OrientationType
      orientation_type = UndefinedOrientation;
  
    switch (orientation)
      {
      case 0:
        orientation_type=TopLeftOrientation;
        break;
      case 1:
        orientation_type=TopRightOrientation;
        break;
      case 2:
        orientation_type=BottomLeftOrientation;
        break;
      case 3:
        orientation_type=BottomRightOrientation;
        break;
      case 4:
        orientation_type=LeftTopOrientation;
        break;
      case 5:
        orientation_type=RightTopOrientation;
        break;
      case 6:
        orientation_type=LeftBottomOrientation;
        break;
      case 7:
        orientation_type=RightBottomOrientation;
        break;
      }
  
    return orientation_type;
  }
  
  static U8 OrientationTypeToCineonOrientation(const OrientationType orientation_type)
  {
    U8
      orientation = 0U;
  
    switch (orientation_type)
      {
      case UndefinedOrientation:
      case TopLeftOrientation:
        orientation=0U;
        break;
      case TopRightOrientation:
        orientation=1U;
        break;
      case BottomLeftOrientation:
        orientation=2U;
        break;
      case BottomRightOrientation:
        orientation=3U;
        break;
      case LeftTopOrientation:
        orientation=4U;
        break;
      case RightTopOrientation:
        orientation=5U;
        break;
      case LeftBottomOrientation:
        orientation=6U;
        break;
      case RightBottomOrientation:
        orientation=7U;
        break;
      }
    return orientation;
  }
  
  static Image *ReadCINEONImage(const ImageInfo *image_info,
    ExceptionInfo *exception)
  {
    CineonFileInfo
      cin_file_info;
  
    CineonImageInfo
      cin_image_info;
  
    CineonImageOriginationInfo
      cin_source_info;
  
    CineonFilmInfo
      cin_mp_info;
  
    Image
      *image;
  
    unsigned long
      y;
  
    register unsigned long
      x;
  
    register PixelPacket
      *q;
  
    register long
      i;
  
    size_t
      offset;
  
    unsigned int
      channel,
      max_bits_per_sample,
      max_lines_per_image,
      max_pixels_per_line,
      number_of_channels;
  
    MagickPassFail
      status;
  
    MagickBool
      swab=MagickFalse;
  
    unsigned long
      pixels_offset;
  
    unsigned char
      *scandata;
  
    void
      *scanline;
  
    const char *
      definition_value;
  
    BitStreamReadHandle
      bit_stream;
  
    /*
      Open image file.
    */
    assert(image_info != (const ImageInfo *) NULL);
    assert(image_info->signature == MagickSignature);
    assert(exception != (ExceptionInfo *) NULL);
    assert(exception->signature == MagickSignature);
  #if !defined(WORDS_BIGENDIAN)
    swab=MagickTrue;
  #endif /* !defined(WORDS_BIGENDIAN) */
    image=AllocateImage(image_info);
    status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
    if (status == False)
      {
        DestroyImageList(image);
        return((Image *) NULL);
      }
    /*
      Read CINEON image.
    */
    offset=(ReadBlob(image,sizeof(cin_file_info),&cin_file_info));
    if (offset != sizeof(cin_file_info))
      ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
  
    if (swab)
      SwabCineonFileInfo(&cin_file_info);
    NULLTerminateASCIIField(cin_file_info.header_format_version);
    NULLTerminateASCIIField(cin_file_info.image_filename);
    NULLTerminateASCIIField(cin_file_info.creation_date);
    NULLTerminateASCIIField(cin_file_info.creation_time);
    NULLTerminateASCIIField(cin_file_info.reserved);
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                          "File magic 0x%04X",cin_file_info.magic);
  
    if (cin_file_info.magic != 0x802A5FD7U)
      ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
  
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                          "Image offset %u, Generic length %u, Industry length %u, User length %u, File size %u",
                          cin_file_info.image_data_offset,
                          cin_file_info.generic_section_length,
                          cin_file_info.industry_section_length,
                          cin_file_info.user_defined_length,
                          cin_file_info.file_size);
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                          "Header format version \"%s\"", cin_file_info.header_format_version);
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                          "Image file name \"%s\"", cin_file_info.image_filename);
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                          "Creation date \"%s\"", cin_file_info.creation_date);
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                          "Creation time \"%s\"", cin_file_info.creation_time);
    /*
      Place arbitary limit on user defined length.
    */
    if (cin_file_info.user_defined_length > 16777216U)
      ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
    StringToAttribute(image,"document",cin_file_info.image_filename);
    StringToAttribute(image,"DPX:file.filename",cin_file_info.image_filename);
    {
      char
        creation_datetime[26]; /* 12+1+12+1 */
  
      (void) strlcpy(creation_datetime,cin_file_info.creation_date,
                     Min(sizeof(creation_datetime),sizeof(cin_file_info.creation_date)));
      if (creation_datetime[0]!='\0')
        (void) strlcat(creation_datetime,":",sizeof(creation_datetime));
      (void) strlcat(creation_datetime,cin_file_info.creation_time,
                     Min(sizeof(creation_datetime),sizeof(creation_datetime)));
      StringToAttribute(image,"timestamp",creation_datetime);
      StringToAttribute(image,"DPX:file.creation.datetime",creation_datetime);
    }
  
    /*
      Obtain offset to pixels.
    */
    pixels_offset=cin_file_info.image_data_offset & 0xffffffff;
    if (pixels_offset < 712)
      ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
  
    /*
      Read image information header.
    */
    offset += ReadBlob(image,sizeof(cin_image_info),&cin_image_info);
    if (offset != (size_t) 712L)
      ThrowReaderException(CorruptImageError,UnexpectedEndOfFile,image);
    if (cin_image_info.channels >
        (sizeof(cin_image_info.channel_info)/sizeof(cin_image_info.channel_info[0])))
      ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
    if (swab)
      SwabCineonImageInfo(&cin_image_info);
    NULLTerminateASCIIField(cin_image_info.label_text);
    NULLTerminateASCIIField(cin_image_info.reserved);
    NULLTerminateASCIIField(cin_image_info.reserved2);
    number_of_channels=cin_image_info.channels;
    if (number_of_channels >
        (sizeof(cin_image_info.channel_info)/sizeof(cin_image_info.channel_info[0])))
      ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
    U8ToAttribute(image,"DPX:image.orientation",cin_image_info.orientation);
    image->orientation=CineonOrientationToOrientationType(cin_image_info.orientation);
    max_bits_per_sample=0;
    max_pixels_per_line=0;
    max_lines_per_image=0;
    for (channel=0; channel < number_of_channels; channel++)
      {
        max_bits_per_sample=Max(max_bits_per_sample,
                                cin_image_info.channel_info[channel].bits_per_sample);
        max_pixels_per_line=Max(max_pixels_per_line,
                                cin_image_info.channel_info[channel].pixels_per_line);
        max_lines_per_image=Max(max_lines_per_image,
                                cin_image_info.channel_info[channel].lines_per_image);
      }
  
    image->depth=max_bits_per_sample;
    image->columns=max_pixels_per_line;
    image->rows=max_lines_per_image;
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                          "Columns %ld, Rows %ld, Channels %u",
                          image->columns, image->rows,
                          (unsigned int) cin_image_info.channels);
    if (!IS_UNDEFINED_R32(cin_image_info.white_point[0]))
      image->chromaticity.white_point.x = cin_image_info.white_point[0].f;
    if (!IS_UNDEFINED_R32(cin_image_info.white_point[1]))
      image->chromaticity.white_point.y = cin_image_info.white_point[1].f;
    if (!IS_UNDEFINED_R32(cin_image_info.red_primary_chromaticity[0]))
      image->chromaticity.red_primary.x = cin_image_info.red_primary_chromaticity[0].f;
    if (!IS_UNDEFINED_R32(cin_image_info.red_primary_chromaticity[1]))
      image->chromaticity.red_primary.y = cin_image_info.red_primary_chromaticity[1].f;
    if (!IS_UNDEFINED_R32(cin_image_info.green_primary_chromaticity[0]))
      image->chromaticity.green_primary.x = cin_image_info.green_primary_chromaticity[0].f;
    if (!IS_UNDEFINED_R32(cin_image_info.green_primary_chromaticity[1]))
      image->chromaticity.green_primary.y = cin_image_info.green_primary_chromaticity[1].f;
    if (!IS_UNDEFINED_R32(cin_image_info.blue_primary_chromaticity[0]))
      image->chromaticity.blue_primary.x = cin_image_info.blue_primary_chromaticity[0].f;
    if (!IS_UNDEFINED_R32(cin_image_info.blue_primary_chromaticity[1]))
      image->chromaticity.blue_primary.y = cin_image_info.blue_primary_chromaticity[1].f;
    StringToAttribute(image,"DPX:file.project.name",cin_image_info.label_text);
  
    /*
      Read image origination header.
    */
    offset += ReadBlob(image,sizeof(cin_source_info),&cin_source_info);
    if (offset != (size_t) 1024L)
      ThrowReaderException(CorruptImageError,UnexpectedEndOfFile,image);
    if (swab)
      SwabCineonImageOriginationInfo(&cin_source_info);
    NULLTerminateASCIIField(cin_source_info.source_image_filename);
    NULLTerminateASCIIField(cin_source_info.creation_date);
    NULLTerminateASCIIField(cin_source_info.creation_time);
    NULLTerminateASCIIField(cin_source_info.input_device);
    NULLTerminateASCIIField(cin_source_info.input_device_model);
    NULLTerminateASCIIField(cin_source_info.input_device_serial);
    S32ToAttribute(image,"DPX:source.x-offset",cin_source_info.x_offset);
    S32ToAttribute(image,"DPX:source.y-offset",cin_source_info.y_offset);
    StringToAttribute(image,"DPX:source.filename",cin_source_info.source_image_filename);
    {
      char
        source_creation_datetime[MaxTextExtent];
  
      source_creation_datetime[0]='\0';
      (void) strlcat(source_creation_datetime,cin_file_info.creation_date,sizeof(cin_file_info.creation_date)+1);
      if (source_creation_datetime[0]!='\0')
        (void) strlcat(source_creation_datetime,":",sizeof(source_creation_datetime));
      (void) strlcat(source_creation_datetime,cin_file_info.creation_time,sizeof(source_creation_datetime));
      StringToAttribute(image,"DPX:source.creation.datetime",source_creation_datetime);
    }
    StringToAttribute(image,"DPX:source.device.name",cin_source_info.input_device);
    StringToAttribute(image,"DPX:source.device.model",cin_source_info.input_device_model);
    StringToAttribute(image,"DPX:source.device.serialnumber",cin_source_info.input_device_serial);
    R32ToAttribute(image,"DPX:source.device.pitch.x",cin_source_info.input_device_pitch_x);
    R32ToAttribute(image,"DPX:source.device.pitch.y",cin_source_info.input_device_pitch_y);
    R32ToAttribute(image,"DPX:source.device.gamma",cin_source_info.gamma);
  
    if ((pixels_offset >= 1024) && (cin_file_info.industry_section_length > 0))
      {
        /*
          Read Motion-picture film information header.
        */
        offset += ReadBlob(image,sizeof(cin_mp_info),&cin_mp_info);
        if (offset != (size_t) 2048L)
          ThrowReaderException(CorruptImageError,UnexpectedEndOfFile,image);
        if (swab)
          SwabCineonFilmInfo(&cin_mp_info);
        NULLTerminateASCIIField(cin_mp_info.format);
        NULLTerminateASCIIField(cin_mp_info.frame_id);
        NULLTerminateASCIIField(cin_mp_info.slate_info);
        NULLTerminateASCIIField(cin_mp_info.reserved);
        U8ToAttribute(image,"DPX:mp.film.manufacturer.id",cin_mp_info.film_mfg_id_code);
        U8ToAttribute(image,"DPX:mp.film.type",cin_mp_info.film_type);
        U8ToAttribute(image,"DPX:mp.perfs.offset",cin_mp_info.perfs_offset);
        U32ToAttribute(image,"DPX:mp.prefix",cin_mp_info.prefix);
        U32ToAttribute(image,"DPX:mp.count",cin_mp_info.count);
        StringToAttribute(image,"DPX:mp.format",cin_mp_info.format);
        U32ToAttribute(image,"DPX:mp.frame.position",cin_mp_info.frame_position);
        R32ToAttribute(image,"DPX:mp.frame.rate",cin_mp_info.frame_rate);
        StringToAttribute(image,"DPX:mp.frame.id",cin_mp_info.frame_id);
        StringToAttribute(image,"DPX:mp.slate.info",cin_mp_info.slate_info);
      }
  
    if ((pixels_offset >= 2048) && (cin_file_info.user_defined_length > 0))
      {
        unsigned char
          *user_data;
  
        const size_t
          block_size = 65536UL;
  
        size_t
          read_size,
          user_data_length;
  
        user_data_length=0UL;
        user_data=(unsigned char *) NULL;
        while (user_data_length < cin_file_info.user_defined_length)
          {
            unsigned char *new_user_data;
            read_size=Min(block_size,cin_file_info.user_defined_length-user_data_length);
            new_user_data=MagickReallocateResourceLimitedMemory(unsigned char *,user_data,user_data_length+read_size);
            if (new_user_data == (unsigned char *) NULL)
              {
                MagickFreeResourceLimitedMemory(user_data);
                ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image);
              }
            user_data=new_user_data;
            if (ReadBlob(image,read_size,user_data+user_data_length) != read_size)
              {
                MagickFreeResourceLimitedMemory(user_data);
                ThrowReaderException(CorruptImageError,UnexpectedEndOfFile,image);
              }
            user_data_length += read_size;
            offset += read_size;
          }
        if (!SetImageProfile(image,"CINEONUSERDATA",user_data,user_data_length))
          {
            MagickFreeResourceLimitedMemory(user_data);
            ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image);
          }
        MagickFreeResourceLimitedMemory(user_data);
      }
  
    if (image_info->ping)
      {
        CloseBlob(image);
        return(image);
      }
  
    if (CheckImagePixelLimits(image, exception) != MagickPass)
      ThrowReaderException(ResourceLimitError,ImagePixelLimitExceeded,image);
  
    /*
      Read remainder of header.
    */
    for ( ; offset < pixels_offset ; offset++ )
      if (ReadBlobByte(image) == EOF)
        ThrowReaderException(CorruptImageError,UnexpectedEndOfFile,image);
  
    /*
      Verify that remaining data is sufficient for claimed dimensions.
    */
    if (BlobIsSeekable(image))
      {
        magick_off_t file_size;
        magick_int64_t expected_file_size;
  
        file_size = GetBlobSize(image);
  
        expected_file_size=pixels_offset+
          ((((((magick_int64_t) image->rows*image->columns*number_of_channels+2UL)/3UL)
             *sizeof(U32)*8UL)+31UL)/32UL)*sizeof(U32);
        if (image->logging)
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                                "Expected file size %" MAGICK_UINT64_F "u bytes"
                                " (have %" MAGICK_OFF_F "d bytes)",
                                expected_file_size, file_size);
  
        if ((file_size > 0) && ((magick_int64_t) file_size < expected_file_size))
          ThrowReaderException(CorruptImageError,InsufficientImageDataInFile,image);
      }
  
    if (image->logging)
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                            "Reading Cineon pixels starting at offset %ld",(long) TellBlob(image));
  
    /*
      Convert CINEON raster image to pixel packets.
    */
    {
      size_t
        scandata_bytes;
  
      unsigned int
        scale_to_short=0;
  
      switch (number_of_channels)
        {
        case 1:
          {
            scandata_bytes=4;
            scale_to_short=64;
            scandata=MagickAllocateResourceLimitedMemory(unsigned char *,scandata_bytes);
            if (scandata == (unsigned char *) NULL)
              ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image);
            scanline=scandata;
            MagickBitStreamInitializeRead(&bit_stream,scanline);
            for (y=0; y < image->rows; y++)
              {
                q=SetImagePixels(image,0,y,image->columns,1);
                if (q == (PixelPacket *) NULL)
                  break;
                /*
                  Packed 10 bit samples with 2 bit pad at end of 32-bit word.
                */
                scanline=scandata;
                i=3;
                for (x=0; x < image->columns; x++, i++)
                  {
                    if (i > 2)
                      {
                        scanline=scandata;
                        if (ReadBlobZC(image,scandata_bytes,&scanline) !=
                            scandata_bytes)
                          break;
                        MagickBitStreamInitializeRead(&bit_stream,scanline);
                        i=0;
                      }
                    q->red=q->green=q->blue=
                      ScaleShortToQuantum(MagickBitStreamMSBRead(&bit_stream,10)*scale_to_short);
                    q->opacity=0U;
                    q++;
                  }
                if (x < image->columns)
                  break;
                if (!SyncImagePixels(image))
                  break;
                if (image->previous == (Image *) NULL)
                  if (QuantumTick(y,image->rows))
                    if (!MagickMonitorFormatted(y,image->rows,exception,
                                                LoadImageText,image->filename,
                                                image->columns,image->rows))
                      break;
              }
            MagickFreeResourceLimitedMemory(scandata);
            break;
          }
        case 3:
          {
            scandata_bytes=MagickArraySize(image->columns,4);
            scale_to_short=64;
            scandata=MagickAllocateResourceLimitedMemory(unsigned char *,scandata_bytes);
            if (scandata == (unsigned char *) NULL)
              ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image);
            for (y=0; y < image->rows; y++)
              {
                magick_uint32_t red;
                magick_uint32_t green;
                magick_uint32_t blue;
  
                q=SetImagePixels(image,0,y,image->columns,1);
                if (q == (PixelPacket *) NULL)
                  break;
                scanline=scandata;
                if (ReadBlobZC(image,scandata_bytes,&scanline) != scandata_bytes)
                  break;
                MagickBitStreamInitializeRead(&bit_stream,scanline);
                for (x=0 ; x < image->columns; x++)
                  {
                    /*
                      Packed 10 bit samples with 2 bit pad at end of 32-bit word.
                    */
                    red   = MagickBitStreamMSBRead(&bit_stream,10);
                    green = MagickBitStreamMSBRead(&bit_stream,10);
                    blue  = MagickBitStreamMSBRead(&bit_stream,10);
                    (void) MagickBitStreamMSBRead(&bit_stream,2);
  
                    q->red     = ScaleShortToQuantum(red*scale_to_short);
                    q->green   = ScaleShortToQuantum(green*scale_to_short);
                    q->blue    = ScaleShortToQuantum(blue*scale_to_short);
                    q->opacity = 0U;
  
  /*                   printf("i:%u,%u,%u --> %u,%u,%u\n", red, green, blue, */
  /*                          (unsigned int)q->red, (unsigned int)q->green, (unsigned int)q->blue); */
                    q++;
                  }
                if (x < image->columns)
                  break;
                if (!SyncImagePixels(image))
                  break;
                if (image->previous == (Image *) NULL)
                  if (QuantumTick(y,image->rows))
                    if (!MagickMonitorFormatted(y,image->rows,exception,
                                                LoadImageText,image->filename,
                                                image->columns,image->rows))
                      break;
              }
            MagickFreeResourceLimitedMemory(scandata);
            break;
          }
        default:
          ThrowReaderException(CorruptImageError,ImageTypeNotSupported,image);
        }
    }
    if (image->logging)
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                            "EOF at offset %" MAGICK_OFF_F "d", TellBlob(image));
    image->depth=Min(image->depth,QuantumDepth);
    image->colorspace=CineonLogRGBColorspace;
  
    if ((definition_value=AccessDefinition(image_info,"cineon","colorspace")))
      {
        ColorspaceType
          colorspace;
  
        colorspace=StringToColorspaceType(definition_value);
        if (colorspace != UndefinedColorspace)
          {
            image->colorspace=colorspace;
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                                  "Explicitly set colorspace to %s",
                                  ColorspaceTypeToString(image->colorspace));
          }
        else
          {
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                                  "Unrecognized source colorspace \"%s\"\n",
                                  definition_value);
            ThrowException(&image->exception,OptionError,UnrecognizedColorspace,
                           definition_value);
          }
      }
  
    if (EOFBlob(image))
      ThrowException(exception,CorruptImageError,UnexpectedEndOfFile,image->filename);
    CloseBlob(image);
    StopTimer(&image->timer);
    return(GetFirstImageInList(image));
  }
  
  /*
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                                                                             %
  %                                                                             %
  %                                                                             %
  %   R e g i s t e r C I N E O N I m a g e                                     %
  %                                                                             %
  %                                                                             %
  %                                                                             %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %
  %  Method RegisterCINEONImage adds attributes for the CINEON image format to
  %  the list of supported formats.  The attributes include the image format
  %  tag, a method to read and/or write the format, whether the format
  %  supports the saving of more than one frame to the same file or blob,
  %  whether the format supports native in-memory I/O, and a brief
  %  description of the format.
  %
  %  The format of the RegisterCINEONImage method is:
  %
  %      RegisterCINEONImage(void)
  %
  */
  ModuleExport void RegisterCINEONImage(void)
  {
    MagickInfo
      *entry;
  
    entry=SetMagickInfo("CIN");
    entry->decoder=(DecoderHandler) ReadCINEONImage;
    entry->encoder=(EncoderHandler) WriteCINEONImage;
    entry->magick=(MagickHandler) IsCINEON;
    entry->description="Cineon Image File";
    entry->module="CINEON";
    entry->adjoin=MagickFalse;
    (void) RegisterMagickInfo(entry);
  }
  
  /*
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                                                                             %
  %                                                                             %
  %                                                                             %
  %   U n r e g i s t e r C I N E O N I m a g e                                 %
  %                                                                             %
  %                                                                             %
  %                                                                             %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %
  %  Method UnregisterCINEONImage removes format registrations made by the
  %  CINEON module from the list of supported formats.
  %
  %  The format of the UnregisterCINEONImage method is:
  %
  %      UnregisterCINEONImage(void)
  %
  */
  ModuleExport void UnregisterCINEONImage(void)
  {
    (void) UnregisterMagickInfo("CIN");
  }
  
  /*
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                                                                             %
  %                                                                             %
  %                                                                             %
  %   W r i t e C I N E O N I m a g e                                           %
  %                                                                             %
  %                                                                             %
  %                                                                             %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %
  %  WriteCINEONImage() writes an image in CINEON encoded image format.
  %
  %  The format of the WriteCINEONImage method is:
  %
  %      unsigned int WriteCINEONImage(const ImageInfo *image_info,Image *image)
  %
  %  A description of each parameter follows.
  %
  %    o image_info: The image info.
  %
  %    o image:  The image.
  %
  %
  */
  
  #define AttributeToU8(image_info,image,key,member) \
  { \
    const ImageAttribute \
      *attribute; \
  \
    const char \
      *definition_value; \
  \
    if ((definition_value=AccessDefinition(image_info,"dpx",&key[4]))) \
      member=(U8) strtol(definition_value, (char **) NULL, 10); \
    else if ((attribute=GetImageAttribute(image,key))) \
      member=(U8) strtol(attribute->value, (char **) NULL, 10); \
    else \
      SET_UNDEFINED_U8(member); \
  }
  
  #define AttributeToU32(image_info,image,key,member) \
  { \
    const ImageAttribute \
      *attribute; \
  \
    const char \
      *definition_value; \
  \
    if ((definition_value=AccessDefinition(image_info,"dpx",&key[4]))) \
      member=(U32) strtol(definition_value, (char **) NULL, 10); \
    else if ((attribute=GetImageAttribute(image,key))) \
      member=(U32) strtol(attribute->value, (char **) NULL, 10); \
    else \
      SET_UNDEFINED_U32(member); \
  }
  
  #define AttributeToS32(image_info,image,key,member) \
  { \
    const ImageAttribute \
      *attribute; \
  \
    const char \
      *definition_value; \
  \
    if ((definition_value=AccessDefinition(image_info,"dpx",&key[4]))) \
      member=(S32) strtol(definition_value, (char **) NULL, 10); \
    else if ((attribute=GetImageAttribute(image,key))) \
      member=(S32) strtol(attribute->value, (char **) NULL, 10); \
    else \
      SET_UNDEFINED_S32(member); \
  }
  
  #define AttributeToR32(image_info,image,key,member) \
  { \
    const ImageAttribute \
      *attribute; \
  \
    const char \
      *definition_value; \
  \
    if ((definition_value=AccessDefinition(image_info,"dpx",&key[4]))) \
      member.f=(float) strtod(definition_value, (char **) NULL); \
    else if ((attribute=GetImageAttribute(image,key))) \
      member.f=(float) strtod(attribute->value, (char **) NULL); \
    else \
      SET_UNDEFINED_R32(member); \
  }
  
  /*
    Header string values are not required to be null terminated, but any
    unused space should be filled with nulls.
  */
  #define AttributeToString(image_info,image,key,member) \
  { \
    const ImageAttribute \
      *attribute; \
  \
    const char \
      *definition_value; \
  \
    SET_UNDEFINED_ASCII(member); \
    if ((definition_value=AccessDefinition(image_info,"dpx",&key[4]))) \
      (void) memcpy(member,definition_value,Min(sizeof(member),strlen(definition_value))); \
    else if ((attribute=GetImageAttribute(image,key)) && (attribute->value)) \
      (void) memcpy(member,attribute->value,Min(sizeof(member),strlen(attribute->value))); \
  }
  
  static void GenerateCineonTimeStamp(char *date_str, size_t date_str_length, char*time_str, size_t time_str_length)
  {
    char timestamp[MaxTextExtent];
  
    time_t
      current_time;
  
  #if defined(HAVE_LOCALTIME_R)
    struct tm
      tm_buf;
  #endif /* if defined(HAVE_LOCALTIME_R) */
  
    const struct tm
      *t;
  
    current_time=time((time_t *) NULL);
  #if defined(HAVE_LOCALTIME_R)
    t=localtime_r(&current_time, &tm_buf);
  #else
    t=localtime(&current_time); /* Thread-unsafe version */
  #endif  /* if defined(HAVE_LOCALTIME_R) */
  
    (void) memset(timestamp,0,sizeof(timestamp));
    (void) strftime(timestamp,MaxTextExtent,"%Y:%m:%d:%H:%M:%S%Z",t);
    timestamp[MaxTextExtent-1]='\0';
    (void) memset(date_str,0,date_str_length);
    (void) strlcpy(date_str,timestamp,11);
    (void) memset(time_str,0,time_str_length);
    (void) strlcpy(time_str,timestamp+11,15);
  }
  
  
  static unsigned int WriteCINEONImage(const ImageInfo *image_info,Image *image)
  {
    CineonFileInfo
      cin_file_info;
  
    CineonImageInfo
      cin_image_info;
  
    CineonImageOriginationInfo
      cin_source_info;
  
    CineonFilmInfo
      cin_mp_info;
  
    const unsigned char
      *user_data;
  
    size_t
      user_data_length=0,
      offset=0;
  
    size_t
      written=0;
  
    MagickBool
      swab=MagickFalse;
  
    long
      y;
  
    register const PixelPacket
      *p;
  
    register long
      x;
  
    unsigned int
      status;
  
    assert(image_info != (const ImageInfo *) NULL);
    assert(image_info->signature == MagickSignature);
    assert(image != (Image *) NULL);
    assert(image->signature == MagickSignature);
  
    /*
      Cineon files are written in Cineon Log color space.
    */
    if (image->colorspace != CineonLogRGBColorspace)
      (void) TransformColorspace(image,CineonLogRGBColorspace);
  
  #if !defined(WORDS_BIGENDIAN)
    swab=MagickTrue;
  #endif /* !defined(WORDS_BIGENDIAN) */
  
    /*
      File information header
    */
    (void) memset(&cin_file_info,0,sizeof(cin_file_info));
  
    /* Magick number (0x802A5FD7) */
    cin_file_info.magic = 0x802A5FD7;
    /* Generic section header length in bytes */
    cin_file_info.generic_section_length = sizeof(cin_file_info)+sizeof(cin_image_info)+sizeof(cin_source_info);
    /* Industry specific header length in bytes */
    cin_file_info.industry_section_length = sizeof(cin_mp_info);
    /* User defined header length in bytes */
    cin_file_info.user_defined_length = 0;
    user_data=GetImageProfile(image,"CINEONUSERDATA",&user_data_length);
    if (user_data && user_data_length)
      cin_file_info.user_defined_length = (U32) user_data_length;
    /* Offset to image data in bytes */
    cin_file_info.image_data_offset =
      cin_file_info.generic_section_length +
      cin_file_info.industry_section_length +
      cin_file_info.user_defined_length;
    /* Total image file size in bytes  */
    cin_file_info.file_size =
      cin_file_info.generic_section_length +
      cin_file_info.industry_section_length +
      cin_file_info.user_defined_length +
      image->rows*image->columns*4;
    /* Version number of header format */
    (void) strlcpy(cin_file_info.header_format_version,"V4.5",sizeof(cin_file_info.header_format_version));
    /* Image filename */
    (void) strlcpy(cin_file_info.image_filename,image->filename,sizeof(cin_file_info.image_filename));
    /* Creation date "yyyy:mm:dd", and time "hh:mm:ssLTZ" */
    GenerateCineonTimeStamp(cin_file_info.creation_date,sizeof(cin_file_info.creation_date),
                            cin_file_info.creation_time,sizeof(cin_file_info.creation_time));
  
    /*
      Image information header
    */
    (void) memset(&cin_image_info,0,sizeof(cin_image_info));
    /* Image orientation */
    cin_image_info.orientation = OrientationTypeToCineonOrientation(image->orientation);
    /* Number of image channels (1-8) */
    cin_image_info.channels = 3; /* RGB */
  
    /* Channel 0 (Red) */
    cin_image_info.channel_info[0].designator_byte_0 = 0;  /* 0 = universal metric */
    cin_image_info.channel_info[0].designator_byte_1 = 1;  /* Red, Printing Density */
    /* Bit depth */
    cin_image_info.channel_info[0].bits_per_sample = 10;
    /* Pixels per line (columns) */
    cin_image_info.channel_info[0].pixels_per_line = image->columns;
    /* Lines per image (rows) */
    cin_image_info.channel_info[0].lines_per_image = image->rows;
    /* Reference low data code value */
    cin_image_info.channel_info[0].reference_low_data_code.f = 0;
    /* Low quantity represented */
    cin_image_info.channel_info[0].reference_low_quantity.f = 0.00;
    /* Reference high data code value */
    cin_image_info.channel_info[0].reference_high_data_code.f = 1023;
    /* Reference high quantity represented */
    cin_image_info.channel_info[0].reference_high_quantity.f = 2.048F;
  
    /* Channel 1 (Green) */
    cin_image_info.channel_info[1] = cin_image_info.channel_info[0];
    cin_image_info.channel_info[1].designator_byte_1 = 2; /* Green, Printing Density */
  
    /* Channel 2  (Blue) */
    cin_image_info.channel_info[2] = cin_image_info.channel_info[0];
    cin_image_info.channel_info[2].designator_byte_1 = 3; /* Blue, Printing Density */
  
    /* White point (color temperature) - x,y pair */
    SET_UNDEFINED_R32(cin_image_info.white_point[0]);
    SET_UNDEFINED_R32(cin_image_info.white_point[1]);
    if ( image->chromaticity.white_point.x != 0.0 && image->chromaticity.white_point.y != 0.0 )
      {
        cin_image_info.white_point[0].f = (float) image->chromaticity.white_point.x;
        cin_image_info.white_point[1].f = (float) image->chromaticity.white_point.y;
      }
    /* Red primary chromaticity - x,y pair */
    SET_UNDEFINED_R32(cin_image_info.red_primary_chromaticity[0]);
    SET_UNDEFINED_R32(cin_image_info.red_primary_chromaticity[1]);
    if ( image->chromaticity.red_primary.x != 0.0 &&  image->chromaticity.red_primary.y != 0.0)
      {
        cin_image_info.red_primary_chromaticity[0].f = (float) image->chromaticity.red_primary.x;
        cin_image_info.red_primary_chromaticity[1].f = (float) image->chromaticity.red_primary.y;
      }
    /* Green primary chromaticity - x,y pair */
    SET_UNDEFINED_R32(cin_image_info.green_primary_chromaticity[0]);
    SET_UNDEFINED_R32(cin_image_info.green_primary_chromaticity[1]);
    if ( image->chromaticity.green_primary.x != 0.0 && image->chromaticity.green_primary.y != 0.0 )
      {
        cin_image_info.green_primary_chromaticity[0].f = (float) image->chromaticity.green_primary.x;
        cin_image_info.green_primary_chromaticity[1].f = (float) image->chromaticity.green_primary.y;
      }
    /* Blue primary chromaticity - x,y pair */
    SET_UNDEFINED_R32(cin_image_info.blue_primary_chromaticity[0]);
    SET_UNDEFINED_R32(cin_image_info.blue_primary_chromaticity[1]);
    if ( image->chromaticity.blue_primary.x != 0.0 && image->chromaticity.blue_primary.y != 0.0 )
      {
        cin_image_info.blue_primary_chromaticity[0].f = (float) image->chromaticity.blue_primary.x;
        cin_image_info.blue_primary_chromaticity[1].f = (float) image->chromaticity.blue_primary.y;
      }
    /* Label text */
    AttributeToString(image_info,image,"DPX:file.project.name",cin_image_info.label_text);
    /* Data interleave */
    cin_image_info.data_interleave = 0; /* rgbrgbrgb ... */
    /* Packing method */
    cin_image_info.packing = 5; /* longword (32-bit) boundaries - left justified */
    /* Data sign: 0=unsigned, 1=signed */
    cin_image_info.sign = 0;
    /* Image sense: 0=positive, 1=negative */
    cin_image_info.sense = 0;
    /* End of line padding */
    cin_image_info.eol_pad = 0;
    /* End of channel padding */
    cin_image_info.eoc_pad = 0;
  
    /*
      Image origination header.
    */
    (void) memset(&cin_source_info,0,sizeof(cin_source_info));
    AttributeToS32(image_info,image,"DPX:source.x-offset",cin_source_info.x_offset); /* X offset */
    AttributeToS32(image_info,image,"DPX:source.y-offset",cin_source_info.y_offset); /* Y offset */
    /* Source image filename */
    AttributeToString(image_info,image,"DPX:source.filename",cin_source_info.source_image_filename);
    {
      /*
        DPX stores creation date and time in the format
        "yyyy:mm:dd:hh:mm:ssLTZ" but Cineon wants date and time to be split into
        yyyy:mm:dd & hh:mm:ssLTZ
      */
      ASCII date_and_time[25];
      AttributeToString(image_info,image,"DPX:source.creation.datetime",date_and_time);
      SET_UNDEFINED_ASCII(cin_source_info.creation_date);
      SET_UNDEFINED_ASCII(cin_source_info.creation_time);
  
      if (!IS_UNDEFINED_ASCII(date_and_time))
        {
          /* Creation date: yyyy:mm:dd */
          (void) strlcpy(cin_source_info.creation_date,date_and_time,11);
          /* Creation time: hh:mm:ssLTZ */
          (void) strlcpy(cin_source_info.creation_time,date_and_time+11,sizeof(cin_source_info.creation_time));
        }
    }
    /* Input device */
    AttributeToString(image_info,image,"DPX:source.device.name",cin_source_info.input_device);
    /* Input device model number */
    AttributeToString(image_info,image,"DPX:source.device.model",cin_source_info.input_device_model);
    /* Input device serial number */
    AttributeToString(image_info,image,"DPX:source.device.serialnumber",cin_source_info.input_device_serial);
    /* Input device pitch for X (samples/mm) */
    AttributeToR32(image_info,image,"DPX:source.device.pitch.x",cin_source_info.input_device_pitch_x);
    /* Input device pitch for Y (samples/mm) */
    AttributeToR32(image_info,image,"DPX:source.device.pitch.y",cin_source_info.input_device_pitch_y);
    /* Image gamma of capture device */
    AttributeToR32(image_info,image,"DPX:source.device.gamma",cin_source_info.gamma);
  
    /*
      Film/Frame Information
    */
    (void) memset(&cin_mp_info,0,sizeof(cin_mp_info));
    /* Film mfg. ID code (2 digits from film edge code) */
    AttributeToU8(image_info,image,"DPX:mp.film.manufacturer.id",cin_mp_info.film_mfg_id_code);
    /* Film type (2 digits from film edge code) */
    AttributeToU8(image_info,image,"DPX:mp.film.type",cin_mp_info.film_type);
    /* Offset in perfs (2 digits from film edge code) */
    AttributeToU8(image_info,image,"DPX:mp.perfs.offset",cin_mp_info.perfs_offset);
    /* Prefix (6 digits from film edge code) */
    AttributeToU32(image_info,image,"DPX:mp.prefix",cin_mp_info.prefix);
    /* Count (4 digits from film edge code) */
    AttributeToU32(image_info,image,"DPX:mp.count",cin_mp_info.count);
    /* Format -- e.g. Academy */
    AttributeToString(image_info,image,"DPX:mp.format",cin_mp_info.format);
    /* Frame position in sequence */
    AttributeToU32(image_info,image,"DPX:mp.frame.position",cin_mp_info.frame_position);
    /* Frame rate of original (frames/s) */
    AttributeToR32(image_info,image,"DPX:mp.frame.rate",cin_mp_info.frame_rate);
    /* Frame identification - e.g. keyframe */
    AttributeToString(image_info,image,"DPX:mp.frame.id",cin_mp_info.frame_id);
    /* Slate information */
    AttributeToString(image_info,image,"DPX:mp.slate.info",cin_mp_info.slate_info);
  
    /*
      Open output image file.
    */
  
    status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
    if (status == MagickFail)
      return (status);
  
    if (swab)
      {
        /*
          Swap byte order.
        */
        SwabCineonFileInfo(&cin_file_info);
        SwabCineonImageInfo(&cin_image_info);
        SwabCineonImageOriginationInfo(&cin_source_info);
        SwabCineonFilmInfo(&cin_mp_info);
      }
  
    written = WriteBlob(image,sizeof(cin_file_info),&cin_file_info);
    if (written != sizeof(cin_file_info))
      {
        CloseBlob(image);
        return (MagickFail);
      }
    offset += written;
  
    written = WriteBlob(image,sizeof(cin_image_info),&cin_image_info);
    if (written != sizeof(cin_image_info))
      {
        CloseBlob(image);
        return (MagickFail);
      }
    offset += written;
  
    written = WriteBlob(image,sizeof(cin_source_info),&cin_source_info);
    if (written != sizeof(cin_source_info))
      {
        CloseBlob(image);
        return (MagickFail);
      }
    offset += written;
  
    written = WriteBlob(image,sizeof(cin_mp_info),&cin_mp_info);
    if (written != sizeof(cin_mp_info))
      {
        CloseBlob(image);
        return (MagickFail);
      }
    offset += written;
  
    if (swab)
      {
        /*
          Swap byte order back to original.
        */
        SwabCineonFileInfo(&cin_file_info);
        SwabCineonImageInfo(&cin_image_info);
        SwabCineonImageOriginationInfo(&cin_source_info);
        SwabCineonFilmInfo(&cin_mp_info);
      }
  
    if (user_data)
      {
        written = WriteBlob(image,user_data_length,user_data);
        if (written != user_data_length)
          {
            CloseBlob(image);
            return (MagickFail);
          }
        offset += written;
      }
  
    if (image->logging)
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                            "Writing Cineon pixels starting at offset %ld",(long) TellBlob(image));
  
    /*
      Convert pixel packets to CINEON raster image.
    */
    {
      unsigned char
        *scanline;
  
      size_t
        scanline_bytes;
  
      BitStreamWriteHandle
        bit_stream;
  
      unsigned int
        red,
        green,
        blue;
  
      unsigned int
        bits_per_sample,
        scale_from_short;
  
      bits_per_sample = cin_image_info.channel_info[0].bits_per_sample;
      scale_from_short=(65535U / (65535U >> (16-bits_per_sample)));
  
      scanline_bytes=MagickArraySize(image->columns,4);
      scanline=MagickAllocateResourceLimitedMemory(unsigned char *,scanline_bytes);
      if (scanline == (unsigned char *) NULL)
        ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,image);
      (void) memset(scanline,0,scanline_bytes);
  
      for (y=0; y < (long) image->rows; y++)
        {
          p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception);
          if (p == (const PixelPacket *) NULL)
            break;
  
          MagickBitStreamInitializeWrite(&bit_stream,scanline);
  
          for (x=0; x < (long) image->columns; x++)
            {
              red   = ScaleQuantumToShort(p->red)/scale_from_short;
              green = ScaleQuantumToShort(p->green)/scale_from_short;
              blue  = ScaleQuantumToShort(p->blue)/scale_from_short;
  
  /*             printf("o:%u,%u,%u --> %u,%u,%u\n",(unsigned int)p->red, (unsigned int) p->green, (unsigned int) p->blue, */
  /*                    red, green, blue); */
  
              MagickBitStreamMSBWrite(&bit_stream,10,red);
              MagickBitStreamMSBWrite(&bit_stream,10,green);
              MagickBitStreamMSBWrite(&bit_stream,10,blue);
              MagickBitStreamMSBWrite(&bit_stream,2,0);
              p++;
            }
          written = WriteBlob(image,scanline_bytes,scanline);
          if (written != scanline_bytes)
            {
              status = MagickFail;
              break;
            }
          offset += written;
  
          if (image->previous == (Image *) NULL)
            if (QuantumTick(y,image->rows))
              if (!MagickMonitorFormatted(y,image->rows,&image->exception,
                                          SaveImageText,image->filename,
                                          image->columns,image->rows))
                break;
        }
      MagickFreeResourceLimitedMemory(scanline);
    }
  
    if ((magick_off_t) cin_file_info.file_size != TellBlob(image))
      {
        (void) printf("### File length %u, TellBlob says %u\n",
                      cin_file_info.file_size,
                      (unsigned int) TellBlob(image));
      }
  
    CloseBlob(image);
    return(status);
  }
  