/*------------------------------------------------------------------------------
 src/lib/djImage/djImageIoDpx.h
 
 Copyright (c) 2004-2006 Darby Johnston.
 All rights reserved.
 Use is subject to license terms.

 Supported:

 * DPX v2.0.
 * Channels: L, RGB, RGBA, L|A, R|G|B, R|G|B|A.
 * Bit depths: U8, U10, U12, U16.
 * Attributes: big or little endian, packed or filled data.

 Unsupported:
 
 * Floating point types.
 * Image elements with different bit depths.
 * Type "B" data packing.
 * Data compression.

 To Do:

 * Verify print LUTs and fix softclip.
 * Preserve user data.
 * Add more colorspaces, YUV support.

 References:

 * SMPTE 268M-2003, "File Format for Digital Moving-Picture Exchange (DPX),
   Version 2.0"
 * Eastman Kodak Company, "Conversion of 10 bit Log Film to 8 bit Linear or
   Video Data, Version 2.1"
 * Cinesite, "Grayscale Transformations of Cineon Digital Film Data,
   Version 1.1"
 * James D. Murray, William vanRyper, "Encyclopedia of Graphics File Formats,
   Second Edition"
------------------------------------------------------------------------------*/

#ifndef DJ_IMAGE_IO_DPX_H
#define DJ_IMAGE_IO_DPX_H
#include <djFileIo.h>
#include <djImageIo.h>

namespace dj {
namespace ImageIoDpx {

// Plugin name.
//
inline const Str name() { return "DPX"; }

// Plugin attributes.
//
enum COLOR { COLOR_AUTO, COLOR_LINEAR, COLOR_PRINT };
enum BIT_DEPTH { BIT_DEPTH_AUTO, U8, U10, U12, U16 };
enum CHANNELS { INTERLEAVE, PLANAR };

// File extensions.
//
static const StrList extensionList() { return StrList() + Str(".dpx"); }

// Image tags.
//
enum TAG {
  TAG_PROJECT,

  TAG_SOURCE_OFFSET, TAG_SOURCE_CENTER, TAG_SOURCE_SIZE, TAG_SOURCE_FILE_NAME,
  TAG_SOURCE_INPUT_DEVICE, TAG_SOURCE_INPUT_SERIAL, TAG_SOURCE_BORDER,
  TAG_SOURCE_PIXEL_ASPECT,
    
  TAG_FILM_FORMAT, TAG_FILM_FRAME, TAG_FILM_SEQUENCE, TAG_FILM_HOLD,
  TAG_FILM_FRAME_RATE, TAG_FILM_SHUTTER, TAG_FILM_FRAME_ID, TAG_FILM_SLATE,
    
  TAG_TV_INTERLACE, TAG_TV_FIELD, TAG_TV_VIDEO_SIGNAL, TAG_TV_SAMPLE_RATE,
  TAG_TV_FRAME_RATE, TAG_TV_TIME_OFFSET, TAG_TV_GAMMA, TAG_TV_BLACK_LEVEL,
  TAG_TV_BLACK_GAIN, TAG_TV_BREAK_POINT, TAG_TV_WHITE_LEVEL,
  TAG_TV_INTEGRATION_TIMES
};

// DPX information.
//
enum DPX_ORIENT {
  DPX_ORIENT_LEFT_RIGHT_TOP_BOTTOM,
  DPX_ORIENT_RIGHT_LEFT_TOP_BOTTOM,
  DPX_ORIENT_LEFT_RIGHT_BOTTOM_TOP,
  DPX_ORIENT_RIGHT_LEFT_BOTTOM_TOP,
  DPX_ORIENT_TOP_BOTTOM_LEFT_RIGHT,
  DPX_ORIENT_TOP_BOTTOM_RIGHT_LEFT,
  DPX_ORIENT_BOTTOM_TOP_LEFT_RIGHT,
  DPX_ORIENT_BOTTOM_TOP_RIGHT_LEFT
};
enum DPX_DESCRIPTOR {
  DPX_DESCRIPTOR_USER = 0,
  DPX_DESCRIPTOR_R = 1,
  DPX_DESCRIPTOR_G = 2,
  DPX_DESCRIPTOR_B = 3,
  DPX_DESCRIPTOR_A = 4,
  DPX_DESCRIPTOR_L = 6,
  DPX_DESCRIPTOR_CHROMA = 7,
  DPX_DESCRIPTOR_Z = 8,
  DPX_DESCRIPTOR_COMPOSITE_VIDEO = 9,
  DPX_DESCRIPTOR_RGB = 50,
  DPX_DESCRIPTOR_RGBA = 51,
  DPX_DESCRIPTOR_ABGR = 52,
  DPX_DESCRIPTOR_CBYCRY = 100,
  DPX_DESCRIPTOR_CBYACRYA = 101,
  DPX_DESCRIPTOR_CBYCR = 102,
  DPX_DESCRIPTOR_CBYCRA = 103,
  DPX_DESCRIPTOR_USER_2 = 150,
  DPX_DESCRIPTOR_USER_3 = 151,
  DPX_DESCRIPTOR_USER_4 = 152,
  DPX_DESCRIPTOR_USER_5 = 153,
  DPX_DESCRIPTOR_USER_6 = 154,
  DPX_DESCRIPTOR_USER_7 = 155,
  DPX_DESCRIPTOR_USER_8 = 156
};
enum DPX_TRANSFER {
  DPX_TRANSFER_USER,
  DPX_TRANSFER_PRINT,
  DPX_TRANSFER_LINEAR,
  DPX_TRANSFER_LOG,
  DPX_TRANSFER_VIDEO,
  DPX_TRANSFER_SMPTE_274M,
  DPX_TRANSFER_ITU_R_709_4,
  DPX_TRANSFER_ITU_R_601_5_B_OR_G,
  DPX_TRANSFER_ITU_R_601_5_M,
  DPX_TRANSFER_NTSC,
  DPX_TRANSFER_PAL,
  DPX_TRANSFER_Z,
  DPX_TRANSFER_Z_HOMOGENEOUS
};
enum DPX_COLORIMETRIC {
  DPX_COLORIMETRIC_USER = 0,
  DPX_COLORIMETRIC_PRINT = 1,
  DPX_COLORIMETRIC_VIDEO = 4,
  DPX_COLORIMETRIC_SMPTE_274M = 5,
  DPX_COLORIMETRIC_ITU_R_709_4 = 6,
  DPX_COLORIMETRIC_ITU_R_601_5_B_OR_G = 7,
  DPX_COLORIMETRIC_ITU_R_601_5_M = 8,
  DPX_COLORIMETRIC_NTSC = 9,
  DPX_COLORIMETRIC_PAL = 10
};
  
// File header.
//
class Header {
public:

  Header();
  
  // Functions.
  //
  bool set(
    FileIo *, const PixelDataInfo &, const ImageTagList &,
    COLOR, BIT_DEPTH, CHANNELS, bool pack
  );
  bool get(FileIo *, ImageTagList *);
  void zero();
  
  // Generic information.
  //
  uint32_t imageOffset;
  char version [8 + 1];
  uint32_t fileSize;
  uint32_t dittoKey;
  uint32_t genericHeaderSize;
  uint32_t industryHeaderSize;
  uint32_t userHeaderSize;
  char fileName [100 + 1];
  char dateTime [24 + 1];
  char creator [100 + 1];
  char project [200 + 1];
  char copyright [200 + 1];
  uint32_t encryptionKey;
  
  // Image information.
  //
  struct Image {
    uint16_t orient;
    uint16_t elemSize;
    uint32_t size [2];
  } image;

  // Image elements.
  //
  struct ImageElem {
    uint32_t dataSign;
    uint32_t lowData;
    float lowQuantity;
    uint32_t highData;
    float highQuantity;
    uint8_t descriptor, transfer, colorimetric;
    uint8_t bitDepth;
    uint16_t packing, encoding;
    uint32_t dataOffset, linePadding, imagePadding;
    char description [32 + 1];
  } imageElem [8];

  // Source information.
  //
  struct Source {
    uint32_t offset [2];
    float center [2];
    uint32_t size [2];
    char fileName [100 + 1];
    char dateTime [24 + 1];
    char inputDevice [32 + 1];
    char inputSerial [32 + 1];
    uint16_t border [4];
    uint32_t pixelAspect [2];
  } source;

  // Film information.
  //
  struct Film {
    char id [2 + 1];
    char type [2 + 1];
    char offset [2 + 1];
    char prefix [6 + 1];
    char count [4 + 1];
    char format [32 + 1];
    uint32_t frame;
    uint32_t sequence;
    uint32_t hold;
    float frameRate;
    float shutter;
    char frameId [32 + 1];
    char slate [100 + 1];
  } film;

  // TV information.
  //
  struct Tv {
    uint32_t timeCode;
    uint32_t userBits;
    uint8_t interlace;
    uint8_t field;
    uint8_t videoSignal;
    uint8_t padding;
    float sampleRate [2];
    float frameRate;
    float timeOffset;
    float gamma, blackLevel, blackGain, breakpoint, whiteLevel,
      integrationTimes;
  } tv;
};

// Image load plugin.
//
class Load : public ImageLoad {
public:

  Load();
  
  const Str name() const { return ImageIoDpx::name(); }
  Plugin * copy() const { return new Load(*this); }
  const StrList extensionList() const { return ImageIoDpx::extensionList(); }
  
  enum ATTR {
    ATTR_COLOR, ATTR_PRINT_BLACK, ATTR_PRINT_WHITE, ATTR_PRINT_GAMMA,
    ATTR_PRINT_SOFT_CLIP, ATTR_LUT
  };
  const StrList attrNameList() const;
  bool attr(const Str &, const StrList &);
  const StrList attr(const Str &) const;
  
  bool open(const Str & fileName, ImageIoInfo *);
  
protected:

  bool imageOpen(const Str & fileName, ImageIoInfo *);
  bool imageBegin(int frame, ImageIoInfo *);
  bool imageGet(PixelData *, int proxyScale);
  
private:

  COLOR _color;
  double _printBlack, _printWhite, _printGamma, _printSoftClip;
  Str _lutFileName;
  
  ENDIAN _endian;
  FileInfo _fileInfo;
  FileIo _f;
  Header _header;
  Pixel _pixel;
  CHANNELS _channels;
};

// Image save plugin.
//
class Save : public ImageSave {
public:

  Save();
  
  const Str name() const { return ImageIoDpx::name(); }
  Plugin * copy() const { return new Save(*this); }
  const StrList extensionList() const { return ImageIoDpx::extensionList(); }
  
  enum ATTR {
    ATTR_COLOR, ATTR_PRINT_BLACK, ATTR_PRINT_WHITE, ATTR_PRINT_GAMMA,
    ATTR_LUT, ATTR_BIT_DEPTH, ATTR_CHANNELS, ATTR_PACK, ATTR_ENDIAN
  };
  const StrList attrNameList() const;
  bool attr(const Str &, const StrList &);
  const StrList attr(const Str &) const;
  
  bool open(const Str & fileName, const ImageIoInfo &);

protected:

  bool imageOpen(const Str & fileName, const Image &);
  bool imageBegin(const Image &, int frame);
  bool imageSet(const PixelData &);
  
private:

  PixelDataInfo _info;
  COLOR _color;
  double _printBlack, _printWhite, _printGamma;
  Str _lutFileName;
  BIT_DEPTH _bitDepth;
  CHANNELS _channels;
  bool _pack;
  ENDIAN _endian;
  
  FileInfo _fileInfo;
  FileIo _f;
  Header _header;
};

// Functions.
//
int dpxScanlineSize(
  int width, int channels, int bitDepth, bool packing, int linePadding
);
void dpxScanlinePack(
  const uint8_t *, uint8_t *, const uint8_t * lut,
  int size, PIXEL_TYPE, int bitDepth, bool endian
);
void dpxScanlineUnpack(
  const uint8_t *, uint8_t *, const uint8_t * lut,
  int size, PIXEL_TYPE, int bitDepth, bool endian
);
void dpxScanlineFill(
  const uint8_t *, uint8_t *, const uint8_t * lut,
  int size, PIXEL_TYPE, int bitDepth, bool endian
);
void dpxScanlineUnfill(
  const uint8_t *, uint8_t *, const uint8_t * lut,
  int size, PIXEL_TYPE, int bitDepth, bool endian
);

void dpxLutUnprint(
  PixelF32 *, int size,
  double black, double white, double gamma, double softClip
);
void dpxLutPrint(
  PixelF32 *, int size,
  double black, double white, double gamma
);

// Labels.
//
const StrList tagStr();
inline const StrList colorStr()
  { return StrList() + Str("Auto") + Str("Linear") + Str("Print"); }
inline const StrList bitDepthStr()
  { return StrList() + Str("Auto") + Str("U8") + Str("U10") + Str("U12") +
    Str("U16"); }
inline const StrList channelsStr()
  { return StrList() + Str("Interleave") + Str("Planar"); }
inline const StrList loadAttrStr()
  { return StrList() + Str("Color") + Str("Print Black") + Str("Print White") +
    Str("Print Gamma") + Str("Print Soft Clip") + Str("LUT"); }
inline const StrList saveAttrStr()
  { return StrList() + Str("Color") + Str("Print Black") + Str("Print White") +
    Str("Print Gamma") + Str("LUT") + Str("Bit Depth") + Str("Channels") +
    Str("Pack") + Str("Endian"); }

// Debugging.
//
void dbg(const Header &);

}
}

#endif
