#ifndef CB__JFIF_H
#define CB__JFIF_H
//
// Copyright (c) 1999 Colosseum Builders, Inc.
// All rights reserved.
//
// Colosseum Builders, Inc. makes no warranty, expressed or implied
// with regards to this software. It is provided as is.
//
// See the README.TXT file that came with this software for restrictions
// on the use and redistribution of this file or send E-mail to
// info@colosseumbuilders.com
//

//
//  Title:  JPEG Definitions and Utility Functions
//
//  Author:  John M. Miano  miano@colosseumbuilders.com
//


//
//  JFIF Definitions
//
//  Author:  John M. Miano  miano@colosseumbuilders.com
//
//  These definitions match version 1.02 of the
//  "JPEG File Interchange Format Specification" by Eric Hamilton
//

#include <cstring>
#include "jpgpvt.h"

namespace Jfif
{
using namespace ColosseumPrivate ;

const int ScaleFactor = 12 ;
const int ScaleValue = (1<<ScaleFactor) ;
const int Rounding = (1<<(ScaleFactor-1)) ;

inline int Scaled (double value)
{
  return static_cast<int>(ScaleValue * value) ;
}

struct JfifHeader
{
  enum { size = 14u } ;
  UBYTE1 buffer [size] ;


  UBYTE1 *const identifier ; // 5 Bytes
  UBYTE1 *const version ; // 2 Bytes
  UBYTE1 *const units ;
  UBYTE2 *const xdensity ;
  UBYTE2 *const ydensity ;
  UBYTE1 *const xthumbnail ;
  UBYTE1 *const ythumbnail ;
  // 3 * xthumbnail * ythumbnail bytes of thumbnail data follow
  JfifHeader () ;
  JfifHeader (char *) ;
} ;

inline JfifHeader::JfifHeader ()
: identifier (&buffer [0])
, version (&buffer [5])
, units (&buffer [7])
, xdensity (reinterpret_cast<UBYTE2*>(&buffer [8]))
, ydensity (reinterpret_cast<UBYTE2*>(&buffer [10]))
, xthumbnail (&buffer [12])
, ythumbnail (&buffer [13])
{}

inline JfifHeader::JfifHeader (char *data)
: identifier (&buffer [0])
, version (&buffer [5])
, units (&buffer [7])
, xdensity (reinterpret_cast<UBYTE2*>(&buffer [8]))
, ydensity (reinterpret_cast<UBYTE2*>(&buffer [10]))
, xthumbnail (&buffer [12])
, ythumbnail (&buffer [13])
{
  std::memcpy (buffer, data, sizeof (buffer)) ;
}


struct JfifExtension
{
  enum { size = 8u } ;
  char buffer [size] ;
  UBYTE2 *const length ;
  UBYTE1 *const identifier ;
  UBYTE1 *const extension_code ;
  enum { JpegThumbnail = 0x10,
         OneByteThumbnail = 0x11,
         ThreeByteThumbnail = 0x13, } ;
  // Extension Data follows
  JfifExtension () ;
} ;

inline JfifExtension::JfifExtension ()
: length (reinterpret_cast <UBYTE2 *>(&buffer [0]))
, identifier(reinterpret_cast<UBYTE1 *>(&buffer [2]))
, extension_code (reinterpret_cast<UBYTE1 *>(&buffer [7]))
{}

// To implement 12-bit data the return values would have to be
// at least 12 bits wide.

// Functions for YCbCr/RGB colorspace conversion
extern JPEGSAMPLE YCbCrToR (int yy, int cb, int cr) ;
extern JPEGSAMPLE YCbCrToG (int yy, int cb, int cr) ;
extern JPEGSAMPLE YCbCrToB (int yy, int cb, int cr) ;

extern JPEGSAMPLE RgbToY (JPEGSAMPLE red, JPEGSAMPLE green, JPEGSAMPLE blue) ;
extern JPEGSAMPLE RgbToCb (JPEGSAMPLE red, JPEGSAMPLE green, JPEGSAMPLE blue) ;
extern JPEGSAMPLE RgbToCr (JPEGSAMPLE red, JPEGSAMPLE green, JPEGSAMPLE blue) ;

//
// The inlining of color conversion saves 7% in testing.
//
#define INLINECOLORCONVERSION
#if defined (INLINECOLORCONVERSION)

extern const long CrRTable [256] ;
extern const long CbGTable [256] ;
extern const long CrGTable [256] ;
extern const long CbBTable [256] ;

//
//  Tried using an array to limit the values to 0...255. Produced no improvement,
//  probably due to cache overflow.
//

inline JPEGSAMPLE YCbCrToR (int yy, int /*cb*/, int cr)
{
  long result = yy + CrRTable [cr] ;
  if (result < JPEGMINSAMPLEVALUE)
    result = JPEGMINSAMPLEVALUE ;
  else if (result > JPEGMAX8BITSAMPLEVALUE)
    result = JPEGMAX8BITSAMPLEVALUE ;
  return static_cast<JPEGSAMPLE>(result) ;
}

inline JPEGSAMPLE YCbCrToG (int yy, int cb, int cr)
{
  const int shift = 16 ;
  const int rounding = 1 << (shift-1) ;
  long result = yy - ((CbGTable [cb] + CrGTable [cr] + rounding) >> shift) ;

  if (result < JPEGMINSAMPLEVALUE)
    result = JPEGMINSAMPLEVALUE ;
  else if (result > JPEGMAX8BITSAMPLEVALUE)
    result = JPEGMAX8BITSAMPLEVALUE ;
  return static_cast<JPEGSAMPLE> (result) ;
}

inline JPEGSAMPLE YCbCrToB (int yy, int cb, int /*cr*/)
{
  long result = yy + CbBTable [cb] ;
  if (result < JPEGMINSAMPLEVALUE)
    result = JPEGMINSAMPLEVALUE ;
  else if (result > JPEGMAX8BITSAMPLEVALUE)
    result = JPEGMAX8BITSAMPLEVALUE ;
  return static_cast<JPEGSAMPLE> (result) ;
}

inline JPEGSAMPLE RgbToY (JPEGSAMPLE red, JPEGSAMPLE green, JPEGSAMPLE blue)
{
  int result = (Scaled (0.299) * red
             + Scaled (0.587) * green + Scaled (0.114) * blue
             + Rounding) >> ScaleFactor ;
  if (result > JPEGMAX8BITSAMPLEVALUE)
  	result = JPEGMAX8BITSAMPLEVALUE ;
  else if (result < JPEGMINSAMPLEVALUE)
  	result = JPEGMINSAMPLEVALUE ;
  return static_cast<JPEGSAMPLE>(result) ;
}

inline JPEGSAMPLE RgbToCb (JPEGSAMPLE red, JPEGSAMPLE green, JPEGSAMPLE blue)
{
  int result = ((JPEG8BITMIDPOINTSAMPLEVALUE<<ScaleFactor)
             + Rounding - Scaled (0.1687) * red
             - Scaled (0.3313) * green + Scaled (0.5) * blue) >> ScaleFactor  ;
  if (result > JPEGMAX8BITSAMPLEVALUE)
  	result = JPEGMAX8BITSAMPLEVALUE ;
  else if (result < JPEGMINSAMPLEVALUE)
  	result = JPEGMINSAMPLEVALUE ;
  return static_cast<JPEGSAMPLE>(result) ;
}

inline JPEGSAMPLE RgbToCr (JPEGSAMPLE red, JPEGSAMPLE green, JPEGSAMPLE blue)
{
  int result = ((JPEG8BITMIDPOINTSAMPLEVALUE<<ScaleFactor)
             + Rounding + Scaled (0.5) * red
             - Scaled (0.4187) * green - Scaled (0.0813) * blue) >> ScaleFactor ;
  if (result > JPEGMAX8BITSAMPLEVALUE)
  	result = JPEGMAX8BITSAMPLEVALUE ;
  else if (result < JPEGMINSAMPLEVALUE)
  	result = JPEGMINSAMPLEVALUE ;
  return static_cast<JPEGSAMPLE>(result) ;
}
#endif

} // End Namespace ColosseumPrivate

#endif
