//**************************************************************************/
// Copyright (c) 2008 Autodesk, Inc.
// All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk license
// agreement provided at the time of installation or download, or which
// otherwise accompanies this software in either electronic or hard copy form.
//
//**************************************************************************/
// DESCRIPTION:
// CREATED: October 2008
//**************************************************************************/

#if !defined IMAGE_H_
#define IMAGE_H_

namespace mudbox {


class Image;

/// \brief Half precision (16 bit) float class. Supports fast conversion
/// between float and half precision.
///
/// Format is compatible with openEXRs half precision type.
/// 1 sign bit, 5 exponent bits (bias = 15), 10 fraction bits.
class MBDLL_DECL half_ {

// These member fcns are inlined for a reason; THESE ARE EXTREMELY PERFORMANCE CRITICAL!
// DO NOT change this file or "clean it up" by removing inlines. Unless you really know
// what you are doing, you will make it significantly slower.
//
private:
    unsigned short m_bits;
    void setBits(unsigned short newbits) { m_bits = newbits; }

public:
    half_ () {};
    half_ (float f)
    {
        unsigned int   x    = *(unsigned int *)&f;
        unsigned int   frac = (x >> 13) & 0x03ff;
        int            exp  = (int)((x >> 23) & 0xff) - 127;

        m_bits = (x >> 16) & 0x8000;  // extract the sign bit

        if (exp > 16) {      // handle overflows/underflows
            exp  = 16;
            frac = 0x03ff;
        } else if (exp <= -15) {
            frac = 0;
            exp  = -15;
        }

        m_bits |= ((exp + 15) << 10) | frac;
    }

public:
    // Conversion to float ~ 15 cycles
    #define ExponentBiasDelta (127-15)
    operator float () const
    {
        int           exp  = (m_bits >> 10) & 0x1f;
        unsigned int  f    = (m_bits & 0x8000) << 16; // extract the sign bit
        unsigned int  frac = (m_bits & 0x03ff) << 13;

        if (exp == 0x1f) {      // convert 16-bit FP inf/NaN to 32-bit inf/NaN value
            exp =  0xff - ExponentBiasDelta;
        } else if (exp == 0) {
            exp = -ExponentBiasDelta;  // convert 16-bit FP zero/denorm to 32-bit zero/denorm value
        }

        f |= ((exp + ExponentBiasDelta) << 23) | frac;
        return *(float *)&f;
    }

    half_ &operator = (float f)
    {
        *this = half_ (f);
        return *this;
    }
};

//------------------------------------------------------------------------------
typedef unsigned char  uInt8;
typedef unsigned short uInt16;


class uInt8Channel;
class uInt16Channel;
class float16Channel;
class float32Channel;

//------------------------------------------------------------------------------
/// The data type for a 8 bit image channel. 0-255 maps to 0.0 to 1.0
/// in floating point or 0-65535 in 16 bit fixed point.
class uInt8Channel {
private:
    uInt8 m_data;

public:
    inline uInt8Channel(const float32Channel &d);
    inline uInt8Channel(const float16Channel &d);
    inline uInt8Channel(const uInt16Channel  &d);
    uInt8Channel(const uInt8Channel   &d) { m_data = uInt8(d); }

    uInt8Channel(uInt8 d)  { m_data = (d); }
    uInt8Channel() {}

    inline uInt8Channel & operator = (const float32Channel &d);
    inline uInt8Channel & operator = (const float16Channel &d);
    inline uInt8Channel & operator = (const uInt16Channel  &d);
    inline uInt8Channel & operator = (const uInt8Channel   &d)
    {
        m_data = uInt8(d);
        return *this;
    }

    inline uInt8Channel & operator = (uInt8 d)
    {
        m_data = uInt8(d);
        return *this;
    }

    operator float ()  const { return m_data * (1.0f/255.0f); }
    operator half_ ()  const { return half_(m_data * (1.0f/255.0f)); }
    operator uInt16 () const { return (m_data << 8) + m_data; }
    operator uInt8 ()  const { return m_data; }
};


//------------------------------------------------------------------------------
/// The data type for a 16 bit image channel. 0-65535 maps to 0.0 to 1.0
class uInt16Channel {
private:
    uInt16 m_data;

public:
    inline uInt16Channel(const float32Channel &d);
    inline uInt16Channel(const float16Channel &d);
    uInt16Channel(const uInt16Channel  &d) { m_data = uInt16(d); }
    uInt16Channel(const uInt8Channel   &d) { m_data = uInt16(d); }
    uInt16Channel(uInt16 d)                { m_data = (d); }
    uInt16Channel() {}

    inline uInt16Channel & operator = (const float32Channel &d);
    inline uInt16Channel & operator = (const float16Channel &d);

    inline uInt16Channel & operator = (const uInt16Channel &d)
    {
        m_data = uInt16(d);
        return *this;
    }

    inline uInt16Channel & operator = (uInt16 d)
    {
        m_data = uInt16(d);
        return *this;
    }

    inline uInt16Channel & operator = (const uInt8Channel &d)
    {
        m_data = uInt16(d);
        return *this;
    }

    operator float ()  const { return m_data * (1.0f/65535.0f); }
    operator half_ ()  const { return half_(m_data * (1.0f/65535.0f)); }
    operator uInt16 () const { return m_data;               }
    operator uInt8 ()  const { return uInt8(m_data >> 8);   }
};


//------------------------------------------------------------------------------
// The data type for a 32 bit float image channel.
class float32Channel {
private:
    float m_data;

public:
    float32Channel(const float32Channel &d) { m_data = float(d); }
    inline float32Channel(const float16Channel &d);
    float32Channel(const uInt16Channel  &d) { m_data = float(d); }
    float32Channel(const uInt8Channel   &d) { m_data = float(d); }
    float32Channel(float                d)  { m_data = (d); }
    float32Channel() {}

    inline float32Channel & operator = (const float32Channel &d)
    {
        *(float *)this = *(float *)&d;
        return *this;
    }

    inline float32Channel & operator = (const float16Channel &d)
    {
        *this = float32Channel(d);
        return *this;
    }

    inline float32Channel & operator = (const uInt16Channel &d)
    {
        *this = float32Channel(d);
        return *this;
    }

    inline float32Channel & operator = (float d)
    {
        *this = float32Channel(d);
        return *this;
    }

    inline float32Channel & operator = (const uInt8Channel &d)
    {
        *this = float32Channel(d);
        return *this;
    }

    operator float ()  const { return m_data; }
    operator half_ ()  const { return half_(m_data); }
    operator uInt16 () const
        {
            if (m_data < 0.0f) return 0;
            if (m_data > 1.0f) return 65535;
            return uInt16(m_data * 65535.0f);
        }
    operator uInt8 ()  const
        {
            if (m_data < 0.0f) return 0;
            if (m_data > 1.0f) return 255;
            return uInt8(m_data * 255.0f);
        }
};


//------------------------------------------------------------------------------
/// The data type for a 16 bit float image channel.
class float16Channel {
private:
    half_ m_data;

public:
    float16Channel(const float32Channel &d) { m_data = float(d); }
    float16Channel(const float16Channel &d) { m_data = float(d); }
    float16Channel(const uInt16Channel  &d) { m_data = float(d); }
    float16Channel(const uInt8Channel   &d) { m_data = float(d); }
    float16Channel(const half_          &d) { m_data = (d); }
    float16Channel() {}

    inline float16Channel & operator = (const float32Channel &d)
    {
        *this = float16Channel(d);
        return *this;
    }

    inline float16Channel & operator = (const float16Channel &d)
    {
        *(uInt16 *)this = *(uInt16 *)&d;
        return *this;
    }

    inline float16Channel & operator = (const uInt16Channel &d)
    {
        *this = float16Channel(d);
        return *this;
    }

    inline float16Channel & operator = (const half_ &d)
    {
        *this = float16Channel(d);
        return *this;
    }

    inline float16Channel & operator = (const uInt8Channel &d)
    {
        *this = float16Channel(d);
        return *this;
    }

    operator float ()  const { return float(m_data); }
    operator half_ ()  const { return m_data; }

    operator uInt16 () const
        {   float d = float(m_data);
            if (d < 0.0f) return 0;
            if (d > 1.0f) return 65535;
            return uInt16(d * 65535.0f);
        }
    operator uInt8 ()  const
        {   float d = float(m_data);
            if (d < 0.0f) return 0;
            if (d > 1.0f) return 255;
            return uInt8(d * 255.0f);
        }
};


//------------------------------------------------------------------------------
inline uInt8Channel::uInt8Channel(const float32Channel &d) { m_data = uInt8(d); }
inline uInt8Channel::uInt8Channel(const float16Channel &d) { m_data = uInt8(d); }
inline uInt8Channel::uInt8Channel(const uInt16Channel  &d) { m_data = uInt8(d); }

//------------------------------------------------------------------------------
inline uInt8Channel & uInt8Channel::operator = (const float32Channel &d)
{
    m_data = uInt8(d);
    return *this;
}

//------------------------------------------------------------------------------
inline uInt8Channel & uInt8Channel::operator = (const float16Channel &d)
{
    m_data = uInt8(d);
    return *this;
}

//------------------------------------------------------------------------------
inline uInt8Channel & uInt8Channel::operator = (const uInt16Channel &d)
{
    m_data = uInt8(d);
    return *this;
}


//------------------------------------------------------------------------------
inline uInt16Channel::uInt16Channel(const float32Channel &d) { m_data = uInt16(d); }
inline uInt16Channel::uInt16Channel(const float16Channel &d) { m_data = uInt16(d); }

//------------------------------------------------------------------------------
inline uInt16Channel & uInt16Channel::operator = (const float32Channel &d)
{
    m_data = uInt16(d);
    return *this;
}

//------------------------------------------------------------------------------
inline uInt16Channel & uInt16Channel::operator = (const float16Channel &d)
{
    m_data = uInt16(d);
    return *this;
}


//------------------------------------------------------------------------------
inline float32Channel::float32Channel(const float16Channel &d) { m_data = float(d); }

//----------------------------------------------------------------------------------
// Now we define the pixel types -- 1, 3, and 4 channels. templated by
// one of the above channel types -- the channel types handle converting between
// each other, and the pixel types handle converting between them.
// 1 -> 3  or 4 channel -- replicates the single channel through the target channels
// 3 -> 4 channel copies the rgb, and sets the a to 1.0. 4 -> 3 channel copies the
// RGB. 4 & 3 -> 1 channel takes the luminance of the rgb channels and stores it
// in the single channel.
//

/// A templated pixel type class, where the channel type and number of channels
/// are arguments. Supports the defined  channel types and channel counts of
/// 1, 3, and 4. It can convert between any  one type of pixel and any other
/// directly, and provides constructors to achieve this. It also supports
/// swapping the r and b channels and converting to inverse luminance. The
/// channel types handle converting between each other, while the pixel types
/// handle converting between each other.
template <class ChanType, const unsigned char NumChans>
class MBDLL_TEMPLATE_DECL PixelType {

    // allow the various instantiations of this class to
    // access each others private data members... Also has the
    // beneficial side effect of causing a compile error if
    // someone tries to instantiate one that is not listed here.
    friend class PixelType<uInt8Channel,   1>;
    friend class PixelType<uInt8Channel,   3>;
    friend class PixelType<uInt8Channel,   4>;
    friend class PixelType<uInt16Channel,  1>;
    friend class PixelType<uInt16Channel,  3>;
    friend class PixelType<uInt16Channel,  4>;
    friend class PixelType<float16Channel, 1>;
    friend class PixelType<float16Channel, 3>;
    friend class PixelType<float16Channel, 4>;
    friend class PixelType<float32Channel, 1>;
    friend class PixelType<float32Channel, 3>;
    friend class PixelType<float32Channel, 4>;

private:
    ChanType m_pixel[NumChans];

public:

    /** Swap the red and blue channels -- converts RGBA to BGRA and back */
    PixelType<ChanType, NumChans> &swapRB() {
        if (NumChans > 2) { // conditional will be resolved at compile time.
            ChanType t = m_pixel[0];
            m_pixel[0] = m_pixel[2];
            m_pixel[2] = t;
        }
        return *this;
    }

    /** conversion constructor */
    PixelType<ChanType, NumChans>(const PixelType<uInt8Channel, 4> &p)
    {
        switch (NumChans) { // this switch gets resolved at compile time
        case 1: m_pixel[0] = ChanType(uInt16Channel((uInt8)p.m_pixel[0]*77  +
                                                    (uInt8)p.m_pixel[1]*150 +
                                                    (uInt8)p.m_pixel[2]*28));
        break;
        case 3: m_pixel[0] = p.m_pixel[0]; m_pixel[1] = p.m_pixel[1];
                m_pixel[2] = p.m_pixel[2];
        break;
        case 4: m_pixel[0] = p.m_pixel[0]; m_pixel[1] = p.m_pixel[1];
                m_pixel[2] = p.m_pixel[2]; m_pixel[3] = p.m_pixel[3];
        break;
        }
    }

    /** conversion constructor */
    PixelType<ChanType, NumChans>(const PixelType<uInt8Channel, 3> &p)
    {
        switch (NumChans) { // this switch gets resolved at compile time
        case 1: m_pixel[0] = ChanType(uInt16Channel((uInt8)p.m_pixel[0]*77  +
                                                    (uInt8)p.m_pixel[1]*150 +
                                                    (uInt8)p.m_pixel[2]*28));
        break;
        case 3: m_pixel[0] = p.m_pixel[0]; m_pixel[1] = p.m_pixel[1];
                m_pixel[2] = p.m_pixel[2];
        break;
        case 4: m_pixel[0] = p.m_pixel[0]; m_pixel[1] = p.m_pixel[1];
                m_pixel[2] = p.m_pixel[2];
                m_pixel[3] = ChanType(float32Channel(1.0f));
        break;
        }
    }

    /** conversion constructor */
    PixelType<ChanType, NumChans>(const PixelType<uInt8Channel, 1> &p)
    {
        switch (NumChans) { // this switch gets resolved at compile time
        case 1: m_pixel[0] = p.m_pixel[0];
        break;
        case 3: m_pixel[0] = p.m_pixel[0]; m_pixel[1] = p.m_pixel[0];
                m_pixel[2] = p.m_pixel[0];
        break;
        case 4: m_pixel[0] = p.m_pixel[0]; m_pixel[1] = p.m_pixel[0];
                m_pixel[2] = p.m_pixel[0]; m_pixel[3] = p.m_pixel[0];
        break;
        }
    }


    /** conversion constructor */
    PixelType<ChanType, NumChans>(const PixelType<uInt16Channel, 4> &p)
    {
        switch (NumChans) { // this switch gets resolved at compile time
        case 1: m_pixel[0] = ChanType(uInt16Channel((((uInt16)p.m_pixel[0]*19660)>>16) +
                                                    (((uInt16)p.m_pixel[1]*38666)>>16) +
                                                    (((uInt16)p.m_pixel[2]*7209 )>>16)));
        break;
        case 3: m_pixel[0] = p.m_pixel[0]; m_pixel[1] = p.m_pixel[1];
                m_pixel[2] = p.m_pixel[2];
        break;
        case 4: m_pixel[0] = p.m_pixel[0]; m_pixel[1] = p.m_pixel[1];
                m_pixel[2] = p.m_pixel[2]; m_pixel[3] = p.m_pixel[3];
        break;
        }
    }

    /** conversion constructor */
    PixelType<ChanType, NumChans>(const PixelType<uInt16Channel, 3> &p)
    {
        switch (NumChans) { // this switch gets resolved at compile time
        case 1: m_pixel[0] = ChanType(uInt16Channel((((uInt16)p.m_pixel[0]*19660)>>16) +
                                                    (((uInt16)p.m_pixel[1]*38666)>>16) +
                                                    (((uInt16)p.m_pixel[2]*7209 )>>16)));
        break;
        case 3: m_pixel[0] = p.m_pixel[0]; m_pixel[1] = p.m_pixel[1];
                m_pixel[2] = p.m_pixel[2];
        break;
        case 4: m_pixel[0] = p.m_pixel[0]; m_pixel[1] = p.m_pixel[1];
                m_pixel[2] = p.m_pixel[2];
                m_pixel[3] = ChanType(float32Channel(1.0f));
        break;
        }
    }

    /** conversion constructor */
    PixelType<ChanType, NumChans>(const PixelType<uInt16Channel, 1> &p)
    {
        switch (NumChans) { // this switch gets resolved at compile time
        case 1: m_pixel[0] = p.m_pixel[0];
        break;
        case 3: m_pixel[0] = p.m_pixel[0]; m_pixel[1] = p.m_pixel[0];
                m_pixel[2] = p.m_pixel[0];
        break;
        case 4: m_pixel[0] = p.m_pixel[0]; m_pixel[1] = p.m_pixel[0];
                m_pixel[2] = p.m_pixel[0]; m_pixel[3] = p.m_pixel[0];
        break;
        }
    }


    /** conversion constructor */
    PixelType<ChanType, NumChans>(const PixelType<float16Channel, 4> &p)
    {
        switch (NumChans) { // this switch gets resolved at compile time
        case 1: m_pixel[0] = ChanType(float32Channel(((float)p.m_pixel[0]*0.30f) +
                                                     ((float)p.m_pixel[1]*0.59f) +
                                                     ((float)p.m_pixel[2]*0.11f)));
        break;
        case 3: m_pixel[0] = p.m_pixel[0]; m_pixel[1] = p.m_pixel[1];
                m_pixel[2] = p.m_pixel[2];
        break;
        case 4: m_pixel[0] = p.m_pixel[0]; m_pixel[1] = p.m_pixel[1];
                m_pixel[2] = p.m_pixel[2]; m_pixel[3] = p.m_pixel[3];
        break;
        }
    }

    /** conversion constructor */
    PixelType<ChanType, NumChans>(const PixelType<float16Channel, 3> &p)
    {
        switch (NumChans) { // this switch gets resolved at compile time
        case 1: m_pixel[0] = ChanType(float32Channel(((float)p.m_pixel[0]*0.30f) +
                                                     ((float)p.m_pixel[1]*0.59f) +
                                                     ((float)p.m_pixel[2]*0.11f)));
        break;
        case 3: m_pixel[0] = p.m_pixel[0]; m_pixel[1] = p.m_pixel[1];
                m_pixel[2] = p.m_pixel[2];
        break;
        case 4: m_pixel[0] = p.m_pixel[0]; m_pixel[1] = p.m_pixel[1];
                m_pixel[2] = p.m_pixel[2];
                m_pixel[3] = ChanType(float32Channel(1.0f));
        break;
        }
    }

    /** conversion constructor */
    PixelType<ChanType, NumChans>(const PixelType<float16Channel, 1> &p)
    {
        switch (NumChans) { // this switch gets resolved at compile time
        case 1: m_pixel[0] = p.m_pixel[0];
        break;
        case 3: m_pixel[0] = p.m_pixel[0]; m_pixel[1] = p.m_pixel[0];
                m_pixel[2] = p.m_pixel[0];
        break;
        case 4: m_pixel[0] = p.m_pixel[0]; m_pixel[1] = p.m_pixel[0];
                m_pixel[2] = p.m_pixel[0]; m_pixel[3] = p.m_pixel[0];
        break;
        }
    }


    /** conversion constructor */
    PixelType<ChanType, NumChans>(const PixelType<float32Channel, 4> &p)
    {
        switch (NumChans) { // this switch gets resolved at compile time
        case 1: m_pixel[0] = ChanType(float32Channel(((float)p.m_pixel[0]*0.30f) +
                                                     ((float)p.m_pixel[1]*0.59f) +
                                                     ((float)p.m_pixel[2]*0.11f)));
        break;
        case 3: m_pixel[0] = p.m_pixel[0]; m_pixel[1] = p.m_pixel[1];
                m_pixel[2] = p.m_pixel[2];
        break;
        case 4: m_pixel[0] = p.m_pixel[0]; m_pixel[1] = p.m_pixel[1];
                m_pixel[2] = p.m_pixel[2]; m_pixel[3] = p.m_pixel[3];
        break;
        }
    }

    /** conversion constructor */
    PixelType<ChanType, NumChans>(const PixelType<float32Channel, 3> &p)
    {
        switch (NumChans) { // this switch gets resolved at compile time
        case 1: m_pixel[0] = ChanType(float32Channel(((float)p.m_pixel[0]*0.30f) +
                                                     ((float)p.m_pixel[1]*0.59f) +
                                                     ((float)p.m_pixel[2]*0.11f)));
        break;
        case 3: m_pixel[0] = p.m_pixel[0]; m_pixel[1] = p.m_pixel[1];
                m_pixel[2] = p.m_pixel[2];
        break;
        case 4: m_pixel[0] = p.m_pixel[0]; m_pixel[1] = p.m_pixel[1];
                m_pixel[2] = p.m_pixel[2];
                m_pixel[3] = ChanType(float32Channel(1.0f));
        break;
        }
    }

    /** conversion constructor */
    PixelType<ChanType, NumChans>(const PixelType<float32Channel, 1> &p)
    {
        switch (NumChans) { // this switch gets resolved at compile time
        case 1: m_pixel[0] = p.m_pixel[0];
        break;
        case 3: m_pixel[0] = p.m_pixel[0]; m_pixel[1] = p.m_pixel[0];
                m_pixel[2] = p.m_pixel[0];
        break;
        case 4: m_pixel[0] = p.m_pixel[0]; m_pixel[1] = p.m_pixel[0];
                m_pixel[2] = p.m_pixel[0]; m_pixel[3] = p.m_pixel[0];
        break;
        }
    }


    /** RGB = 1, A = Luminance of input. */
    void SetLuminance(const PixelType<uInt8Channel, 4> &p)
    {
        if (NumChans == 4) {
            m_pixel[0] = uInt8Channel(255);
            m_pixel[1] = uInt8Channel(255);
            m_pixel[2] = uInt8Channel(255);
            m_pixel[3] = ChanType(uInt16Channel(((uInt8)p.m_pixel[0]*77  +
                                                 (uInt8)p.m_pixel[1]*150 +
                                                 (uInt8)p.m_pixel[2]*28)));
        }
    }

    /** RGB = 1, A = Luminance of input. */
    void SetLuminance(const PixelType<uInt8Channel, 3> &p)
    {
        if (NumChans == 4) {
            m_pixel[0] = uInt8Channel(255);
            m_pixel[1] = uInt8Channel(255);
            m_pixel[2] = uInt8Channel(255);
            m_pixel[3] = ChanType(uInt16Channel(((uInt8)p.m_pixel[0]*77  +
                                                 (uInt8)p.m_pixel[1]*150 +
                                                 (uInt8)p.m_pixel[2]*28)));
        }
    }

    /** RGB = 1, A = Luminance of input. */
    void SetLuminance(const PixelType<uInt16Channel, 4> &p)
    {
        if (NumChans == 4) {
            m_pixel[0] = uInt8Channel(255);
            m_pixel[1] = uInt8Channel(255);
            m_pixel[2] = uInt8Channel(255);
            m_pixel[3] = ChanType(uInt16Channel(((((uInt16)p.m_pixel[0]*19660)>>16) +
                                                 (((uInt16)p.m_pixel[1]*38666)>>16) +
                                                 (((uInt16)p.m_pixel[2]*7209 )>>16))));
        }
    }

    /** RGB = 1, A = Luminance of input. */
    void SetLuminance(const PixelType<uInt16Channel, 3> &p)
    {
        if (NumChans == 4) {
            m_pixel[0] = uInt8Channel(255);
            m_pixel[1] = uInt8Channel(255);
            m_pixel[2] = uInt8Channel(255);
            m_pixel[3] = ChanType(uInt16Channel(((((uInt16)p.m_pixel[0]*19660)>>16) +
                                                 (((uInt16)p.m_pixel[1]*38666)>>16) +
                                                 (((uInt16)p.m_pixel[2]*7209 )>>16))));
        }
    }


    /** RGB = 1, A = Luminance of input. */
    void SetLuminance(const PixelType<float16Channel, 4> &p)
    {
        if (NumChans == 4) {
            m_pixel[0] = uInt8Channel(255);
            m_pixel[1] = uInt8Channel(255);
            m_pixel[2] = uInt8Channel(255);
            m_pixel[3] = ChanType(float32Channel((((float)p.m_pixel[0]*0.30f) +
                                                  ((float)p.m_pixel[1]*0.59f) +
                                                  ((float)p.m_pixel[2]*0.11f))));
        }
    }

    /** RGB = 1, A = Luminance of input. */
    void SetLuminance(const PixelType<float16Channel, 3> &p)
    {
        if (NumChans == 4) {
            m_pixel[0] = uInt8Channel(255);
            m_pixel[1] = uInt8Channel(255);
            m_pixel[2] = uInt8Channel(255);
            m_pixel[3] = ChanType(float32Channel((((float)p.m_pixel[0]*0.30f) +
                                                  ((float)p.m_pixel[1]*0.59f) +
                                                  ((float)p.m_pixel[2]*0.11f))));
        }
    }

    /** RGB = 1, A = Luminance of input. */
    void SetLuminance(const PixelType<float32Channel, 4> &p)
    {
        if (NumChans == 4) {
            m_pixel[0] = uInt8Channel(255);
            m_pixel[1] = uInt8Channel(255);
            m_pixel[2] = uInt8Channel(255);
            m_pixel[3] = ChanType(float32Channel((((float)p.m_pixel[0]*0.30f) +
                                                  ((float)p.m_pixel[1]*0.59f) +
                                                  ((float)p.m_pixel[2]*0.11f))));
        }
    }

    /** RGB = 1, A = Luminance of input. */
    void SetLuminance(const PixelType<float32Channel, 3> &p)
    {
        if (NumChans == 4) {
            m_pixel[0] = uInt8Channel(255);
            m_pixel[1] = uInt8Channel(255);
            m_pixel[2] = uInt8Channel(255);
            m_pixel[3] = ChanType(float32Channel((((float)p.m_pixel[0]*0.30f) +
                                                  ((float)p.m_pixel[1]*0.59f) +
                                                  ((float)p.m_pixel[2]*0.11f))));
        }
    }

    /** RGB = 1, A = Luminance of input. */
    void SetLuminance(const PixelType<float32Channel, 1> &p)
    {
        if (NumChans == 4) {
            m_pixel[0] = uInt8Channel(255);
            m_pixel[1] = uInt8Channel(255);
            m_pixel[2] = uInt8Channel(255);
            m_pixel[3] = ChanType(float32Channel(1.0f - ((float)p.m_pixel[0])));
        }
    }

    /** RGB = 1, A = Luminance of input. */
    void SetLuminance(const PixelType<float16Channel, 1> &p)
    {
        if (NumChans == 4) {
            m_pixel[0] = uInt8Channel(255);
            m_pixel[1] = uInt8Channel(255);
            m_pixel[2] = uInt8Channel(255);
            m_pixel[3] = ChanType(float32Channel(((float)p.m_pixel[0])));
        }
    }

    /** RGB = 1, A = Luminance of input. */
    void SetLuminance(const PixelType<uInt16Channel, 1> &p)
    {
        if (NumChans == 4) {
            m_pixel[0] = uInt8Channel(255);
            m_pixel[1] = uInt8Channel(255);
            m_pixel[2] = uInt8Channel(255);
            m_pixel[3] = ChanType(uInt16Channel(((uInt16)p.m_pixel[0])));
        }
    }

    /** RGB = 1, A = Luminance of input. */
    void SetLuminance(const PixelType<uInt8Channel, 1> &p)
    {
        if (NumChans == 4) {
            m_pixel[0] = uInt8Channel(255);
            m_pixel[1] = uInt8Channel(255);
            m_pixel[2] = uInt8Channel(255);
            m_pixel[3] = ChanType(uInt8Channel(((uInt8)p.m_pixel[0])));
        }
    }


    /** RGB = 1, A = 1-Luminance of input. */
    void SetInvLuminance(const PixelType<uInt8Channel, 4> &p)
    {
        if (NumChans == 4) {
            m_pixel[0] = uInt8Channel(0);
            m_pixel[1] = uInt8Channel(0);
            m_pixel[2] = uInt8Channel(0);
            m_pixel[3] = ChanType(uInt16Channel(65535 -
                                                ((uInt8)p.m_pixel[0]*77  +
                                                 (uInt8)p.m_pixel[1]*150 +
                                                 (uInt8)p.m_pixel[2]*28)));
        }
    }

    /** RGB = 1, A = 1-Luminance of input. */
    void SetInvLuminance(const PixelType<uInt8Channel, 3> &p)
    {
        if (NumChans == 4) {
            m_pixel[0] = uInt8Channel(0);
            m_pixel[1] = uInt8Channel(0);
            m_pixel[2] = uInt8Channel(0);
            m_pixel[3] = ChanType(uInt16Channel(65535 -
                                                ((uInt8)p.m_pixel[0]*77  +
                                                 (uInt8)p.m_pixel[1]*150 +
                                                 (uInt8)p.m_pixel[2]*28)));
        }
    }

    /** RGB = 1, A = 1-Luminance of input. */
    void SetInvLuminance(const PixelType<uInt16Channel, 4> &p)
    {
        if (NumChans == 4) {
            m_pixel[0] = uInt8Channel(0);
            m_pixel[1] = uInt8Channel(0);
            m_pixel[2] = uInt8Channel(0);
            m_pixel[3] = ChanType(uInt16Channel(65535 -
                                                ((((uInt16)p.m_pixel[0]*19660)>>16) +
                                                 (((uInt16)p.m_pixel[1]*38666)>>16) +
                                                 (((uInt16)p.m_pixel[2]*7209 )>>16))));
        }
    }

    /** RGB = 1, A = 1-Luminance of input. */
    void SetInvLuminance(const PixelType<uInt16Channel, 3> &p)
    {
        if (NumChans == 4) {
            m_pixel[0] = uInt8Channel(0);
            m_pixel[1] = uInt8Channel(0);
            m_pixel[2] = uInt8Channel(0);
            m_pixel[3] = ChanType(uInt16Channel(65535 -
                                                ((((uInt16)p.m_pixel[0]*19660)>>16) +
                                                 (((uInt16)p.m_pixel[1]*38666)>>16) +
                                                 (((uInt16)p.m_pixel[2]*7209 )>>16))));
        }
    }


    /** RGB = 1, A = 1-Luminance of input. */
    void SetInvLuminance(const PixelType<float16Channel, 4> &p)
    {
        if (NumChans == 4) {
            m_pixel[0] = uInt8Channel(0);
            m_pixel[1] = uInt8Channel(0);
            m_pixel[2] = uInt8Channel(0);
            m_pixel[3] = ChanType(float32Channel(1.0f -
                                                 (((float)p.m_pixel[0]*0.30f) +
                                                  ((float)p.m_pixel[1]*0.59f) +
                                                  ((float)p.m_pixel[2]*0.11f))));
        }
    }

    /** RGB = 1, A = 1-Luminance of input. */
    void SetInvLuminance(const PixelType<float16Channel, 3> &p)
    {
        if (NumChans == 4) {
            m_pixel[0] = uInt8Channel(0);
            m_pixel[1] = uInt8Channel(0);
            m_pixel[2] = uInt8Channel(0);
            m_pixel[3] = ChanType(float32Channel(1.0f -
                                                 (((float)p.m_pixel[0]*0.30f) +
                                                  ((float)p.m_pixel[1]*0.59f) +
                                                  ((float)p.m_pixel[2]*0.11f))));
        }
    }

    /** RGB = 1, A = 1-Luminance of input. */
    void SetInvLuminance(const PixelType<float32Channel, 4> &p)
    {
        if (NumChans == 4) {
            m_pixel[0] = uInt8Channel(0);
            m_pixel[1] = uInt8Channel(0);
            m_pixel[2] = uInt8Channel(0);
            m_pixel[3] = ChanType(float32Channel(1.0f -
                                                 (((float)p.m_pixel[0]*0.30f) +
                                                  ((float)p.m_pixel[1]*0.59f) +
                                                  ((float)p.m_pixel[2]*0.11f))));
        }
    }

    /** RGB = 1, A = 1-Luminance of input. */
    void SetInvLuminance(const PixelType<float32Channel, 3> &p)
    {
        if (NumChans == 4) {
            m_pixel[0] = uInt8Channel(0);
            m_pixel[1] = uInt8Channel(0);
            m_pixel[2] = uInt8Channel(0);
            m_pixel[3] = ChanType(float32Channel(1.0f - (((float)p.m_pixel[0]*0.30f) +
                                                         ((float)p.m_pixel[1]*0.59f) +
                                                         ((float)p.m_pixel[2]*0.11f))));
        }
    }

    /** RGB = 1, A = 1-Luminance of input. */
    void SetInvLuminance(const PixelType<float32Channel, 1> &p)
    {
        if (NumChans == 4) {
            m_pixel[0] = uInt8Channel(0);
            m_pixel[1] = uInt8Channel(0);
            m_pixel[2] = uInt8Channel(0);
            m_pixel[3] = ChanType(float32Channel(1.0f - ((float)p.m_pixel[0])));
        }
    }

    /** RGB = 1, A = 1-Luminance of input. */
    void SetInvLuminance(const PixelType<float16Channel, 1> &p)
    {
        if (NumChans == 4) {
            m_pixel[0] = uInt8Channel(0);
            m_pixel[1] = uInt8Channel(0);
            m_pixel[2] = uInt8Channel(0);
            m_pixel[3] = ChanType(float32Channel(1.0f - ((float)p.m_pixel[0])));
        }
    }

    /** RGB = 1, A = 1-Luminance of input. */
    void SetInvLuminance(const PixelType<uInt16Channel, 1> &p)
    {
        if (NumChans == 4) {
            m_pixel[0] = uInt8Channel(0);
            m_pixel[1] = uInt8Channel(0);
            m_pixel[2] = uInt8Channel(0);
            m_pixel[3] = ChanType(uInt16Channel(65535 - ((uInt16)p.m_pixel[0])));
        }
    }

    /** RGB = 1, A = 1-Luminance of input. */
    void SetInvLuminance(const PixelType<uInt8Channel, 1> &p)
    {
        if (NumChans == 4) {
            m_pixel[0] = uInt8Channel(0);
            m_pixel[1] = uInt8Channel(0);
            m_pixel[2] = uInt8Channel(0);
            m_pixel[3] = ChanType(uInt8Channel(255 - ((uInt8)p.m_pixel[0])));
        }
    }
};


//------------------------------------------------------------------------------
/// Represents a rectangle. Used to specify rectangular regions of an image.
class MBDLL_DECL ImgTile
{
public:
    int  x;   /**< x origin of the tile        */
    int  y;   /**< y origin of the tile        */
    int  nx;  /**< x size of the tile (width)  */
    int  ny;  /**< y size of the tile (height) */

    /** Create a default empty tile */
    inline ImgTile() : x(0), y(0), nx(0), ny(0) {}

    /**
     * Create a tile as specified in the parameters
     * @param X      X origin
     * @param Y      y Origin
     * @param NX     x Size (width)
     * @param NY     y Size (height)
     */
    inline ImgTile(int X, int Y, int NX, int NY) : x(X), y(Y), nx(NX), ny(NY) {}

    #if !defined MAX
    #define MAX(a_, b_) (((a_) > (b_)) ? (a_) : (b_))
    #endif
    #if !defined MIN
    #define MIN(a_, b_) (((a_) < (b_)) ? (a_) : (b_))
    #endif

    /**
     * Construct a tile by computing the intersection fo the two input tiles
     * @param t1     First tile to be intersected
     * @param t2     Second tile to be intersected
     */
    ImgTile(const ImgTile &a, const ImgTile &b)
    {
        x  = MAX(a.x, b.x);
        y  = MAX(a.y, b.y);
        nx = MIN(a.x+a.nx, b.x+b.nx) - x;
        ny = MIN(a.y+a.ny, b.y+b.ny) - y;
        if (nx <= 0 || ny <= 0) x = y = nx = ny = 0;
    }

    inline bool isEqualTile(const ImgTile &t) const
    { return x == t.x && y == t.y && nx == t.nx && ny == t.ny; }

    inline bool operator ==(const ImgTile &t) const
    { return x == t.x && y == t.y && nx == t.nx && ny == t.ny; }

    inline bool operator !=(const ImgTile &t) const
    { return !(t == *this); }

    /** return true of this tile is empty */
    inline bool isEmpty() const { return nx <= 0 || ny <= 0; }

    inline void setEmpty() { x = y = nx = ny = 0; }

    /** return true if this tile includes the specified tile */
    inline bool contains(const ImgTile &a) const
    { return x <= a.x && a.x+a.nx <= x+nx &&
             y <= a.y && a.y+a.ny <= y+ny;
    }

    /** return true if this tile includes the specified point */
    inline bool contains(int x_, int y_) const
    { return x <= x_ && x_ < x+nx && y <= y_ && y_ < y+ny;
    }

    /** Clip the tile so it will not extend into the negative quadrants. */
    void clipPositive()
    {
        if (x < 0) {
            nx += x; x = 0;
            if (nx < 0) x = y = nx = ny = 0;
        }
        if (y < 0) {
            ny += y; y = 0;
            if (ny < 0) x = y = nx = ny = 0;
        }
    }

    /** Expand this tile to include the specified point. */
    void ExpandToInclude(int x_, int y_)
    {
        if (isEmpty()) {
            x = x_; y = y_; nx = 1; ny = 1;
        } else {
            if (x_ < x) { nx += (x - x_); x = x_; }
            if (y_ < y) { ny += (y - y_); y = y_; }

            if (x + nx < x_) nx = (x_ - x) + 1;
            if (y + ny < y_) ny = (y_ - y) + 1;
        }
    }

    /** Grow the tile by the specified number of pixels on all 4 sides. */
    void Expand(int numPixels = 1)
    {
        x  -= numPixels; y  -= numPixels;
        numPixels <<= 1;
        nx += numPixels; ny += numPixels;
    }

    /** return the number of pixels contained in this tile */
    int numPixels() const { return isEmpty() ? 0 : nx * ny; }
};


//------------------------------------------------------------------------------
/// \brief Iterates over a the region of tile A that is outside
/// tile B. If tile A is contained inside tile B, this region will be empty.
class MBDLL_DECL ImgTile_AoutB_Iter : public ImgTile {
private:
    int            ax1, ax2, bx1, bx2;
    int            ay1, ay2, by1, by2;
    signed char    state;
    bool           ad,  bd;

public:
    ImgTile_AoutB_Iter(const ImgTile &A, const ImgTile &B) :
        ax1   (A.x), ax2 (A.x + A.nx - 1),
        bx1   (B.x), bx2 (B.x + B.nx - 1),
        ay1   (A.y), ay2 (A.y + A.ny - 1),
        by1   (B.y), by2 (B.y + B.ny - 1),
        state (0),
        ad    (A.nx <= 0 || A.ny <= 0),
        bd    (B.nx <= 0 || B.ny <= 0) {}

    virtual bool next();
};


//-----------------------------------------------------------------------------
class MBDLL_DECL ImgTileUnion : public ImgTile {
public:
    ImgTileUnion(const ImgTile &A, const ImgTile &B);
};


/// Iterates over a specified region of an image in optimal order.
/// It does not lock the region of the image or otherwise cause it to be present
/// in CPU memory -- for that, use ImgLockPageIterator...
/// Note that the regions returned are the tiles, stripes or pages that intersect
/// the requested region -- they may encompass a larger area than you request, or
/// even the entire image.
class MBDLL_DECL ImgPageIterator : public ImgTile {
protected:
    void    *m_BlindData;
    Image   *m_Image;
    int      m_Type;
    void    *m_PixelData;
    ImgTile  m_SrcTile;


public:
    /**
     * Create an iterator to cover the specified region of the image
     *
     * @param img     A pointer to the image that you are iterating over
     *
     * @param srcTile A const pointer to the region of the image
     *                to be iterated over -- NULL means the bounds of the image
     */
    ImgPageIterator(Image *img, const ImgTile *srcTile = 0, bool writing = false);
    virtual ~ImgPageIterator();
    virtual bool  next();
    void         *dataPtr() { return m_PixelData; }
};


//------------------------------------------------------------------------------
///  Iterate over a specified region of an image in optimal order, and locking it in CPU memory.
class MBDLL_DECL ImgLockPageIterator : public ImgPageIterator {
public:
    typedef enum {
        ReadOnly,   /**< A promise that the data in this page will not be modified */
        ReadWrite,  /**< self explanitory */
        WriteOnly   /**< The page data will be allocated bye the contents will be undefined */
    } AccessMode;

protected:
    bool        m_SmartImage;
    AccessMode  m_Mode;

public:
    ImgLockPageIterator(Image *img, const ImgTile *srcTile = 0, AccessMode mode = ReadOnly);
    ~ImgLockPageIterator();

    virtual bool  next();
    void         *dataPtr();
};

//------------------------------------------------------------------------------
/// Describe an image pixel; how to interpret a piece of pixel data.
///
/// \note The internal format preferred by MudBox is always PreMultiplied.
///
/// When you provide image data to these Image classes, it will be converted to
/// the preferred internal representation as specified above.
/// When image data is queried, it will almost always be
/// returned in the preferred internal channel ordering, and will always be
/// returned PreMultiplied.
class PixelDescriptor {
public:
    /** Describe the data type of a single channel of a pixel. */
    typedef enum {
        uChar   = 0,  /**< unsigned 8 bit char                 */
        uShort  = 1,  /**< unsigned 16 bit short.              */
        sHalf   = 2,  /**< S10E5 half precision floating point */
        sFloat  = 3,  /**< IEEE754 single precision            */

        sChar   = 4,  /**< signed 8 bit char        (not currently supported) */
        sShort  = 5,  /**< signed 16 bit short      (not currently supported) */
        uInt    = 6,  /**< unsigned 32 bit int      (not currently supported) */
        sInt    = 7,  /**< signed 32 bit int        (not currently supported) */
        sDouble = 8   /**< IEEE754 double precision (not currently supported) */
    } ChannelType;

    /** Describe channel ordering for 4 channel images -- these are the
     *  order of the channels in memory, not in registers or uInts */
    typedef enum {
        orderNONE = 0, /**< This is invalid for 4 channel images -- used for 1 channel */
        orderRGBA = 1, /**< standard RGBA                        */
        orderABGR = 2, /**< reverse of RGBA                      */
        orderBGRA = 3, /**< Optimal ordering on windows & Mac    */
        orderARGB = 4, /**< Reverse of BGRA                      */
        orderRGB  = 5, /**< RGB -- for 3 channel images          */
        orderBGR  = 6  /**< Reverse of RGB for 3 channel images  */
    } MemoryChannelOrder;

protected:
    char                m_cCount;        /**< number of channels in the image -- 1, 3, or 4 */
    ChannelType         m_channelType;   /**< Channel type     */
    MemoryChannelOrder  m_channelOrder;  /**< channel ordering */
    bool                m_PreMultiplied; /**< true if RGB is premultiplied by A */


public:

    PixelDescriptor(char                cCount,
                    ChannelType         channelType,
                    MemoryChannelOrder  channelOrder,
                    bool                PreMultiplied)
        : m_cCount       (cCount)
        , m_channelType  (channelType)
        , m_channelOrder (channelOrder)
        , m_PreMultiplied(PreMultiplied) {}

    /** return the size of a pixel channel in bytes */
    int channelSize() const {
        const unsigned char s_channelByteCount[] = {1, 2, 2, 4, 1, 2, 4, 4, 8};
        return s_channelByteCount[m_channelType];
    }

    /** return the size of a pixel channel in bits */
    int channelBitSize() const {
        return channelSize() * 8;
    }

    /** return the size of a pixel in bytes */
    int pixelSize() const {
        return channelSize() * m_cCount;
    }

    /** return the size of a pixel in bits */
    int pixelBitSize() const {
        return pixelSize() * 8;
    }

    /** Get the channel type of the image/pixel */
    ChannelType channelType() const { return m_channelType; }

    /** Get the channel ordering of the image/pixel */
    MemoryChannelOrder channelOrder() const { return m_channelOrder; }

    /** returns true if the RGB values are premultiplied by the A values,
     *  false otherwise. Default is true... */
    bool premultiplied() const { return m_PreMultiplied; }

    /** return the number of channels -- 1, 3, or 4 */
    int channelCount() const   { return m_cCount; }

    /** returns true if the pixel descriptors are equal */
    bool operator == (const PixelDescriptor &pd) const
    { return (m_cCount         == pd.m_cCount        &&
              m_channelType    == pd.m_channelType   &&
              m_channelOrder   == pd.m_channelOrder  &&
              m_PreMultiplied  == pd.m_PreMultiplied);
    }
};



//------------------------------------------------------------------------------
/// Describe an image, specifically how to interpret pixel data.
/// This class derives from PixelDescriptor and adds a bounding rectangle and
/// spatial resolution.
class ImageDescriptor : public PixelDescriptor {

public:
    /// Resolution units (mainly used so resolution is preserved in PSD & TIFF files)
    enum ResUnit { k_PixelsPerInch       = 1, /**< pixels per inch       */
                   k_PixelsPerCentimeter = 2  /**< pixels per centimeter */
    };

protected:
    ImgTile   m_Bounds; /**< Bounds of the image -- some images may not have a 0,0 origin */

    float     m_xResolution;      /**< horizontal resolution in pixels per unit */
    float     m_yResolution;      /**< vertical resolution in pixels per unit   */
    ResUnit   m_ResolutionUnits;  /**< units -- 1 == inch, 2 == centimeter (same as in PSD files) */

public:

    /**
     * Constructor -- make a descriptor...
     * @param x     x origin of the image
     * @param y     y origin of the image
     * @param nx    xSize of the image
     * @param ny    ySize of the image
     * @param order channel ordering of the image
     * @param premult Are the RGB channels premultiplied by the A?
     * @param nc    number of channels -- currently only 4 is supported
     * @param ct    data type of the channels -- currently only uChar is
     *              supported.
     */
    ImageDescriptor(int x, int y, int nx, int ny,
                    MemoryChannelOrder order = orderRGBA,
                    bool premult = true,
                    char nc = 4, ChannelType ct = uChar) :
        PixelDescriptor(nc, ct, order, premult),
        m_Bounds(x, y, nx, ny),
        m_xResolution(100.0f), m_yResolution(100.0f),
        m_ResolutionUnits(k_PixelsPerInch) {}

    /**
     * Constructor -- make a descriptor...
     * @param nx    xSize of the image
     * @param ny    ySize of the image
     * @param order channel ordering of the image
     * @param premult Are the RGB channels premultiplied by the A?
     * @param nc    number of channels -- currently only 4 is supported
     * @param ct    data type of the channels -- currently only uChar is
     *              supported.
     */
    ImageDescriptor(int nx, int ny,
                    MemoryChannelOrder order = orderRGBA,
                    bool premult = true,
                    char nc = 4, ChannelType ct = uChar) :
        PixelDescriptor(nc, ct, order, premult),
        m_Bounds(0, 0, nx, ny) {}

    /** default constructor -- RGBA, premult, 4 channel uChar, size 0 x 0 pixels. */
    ImageDescriptor() :
        PixelDescriptor(4, uChar,  orderRGBA, true),
        m_Bounds(0,0,0,0) {}

    /** return the number of bytes the raw image pixels will occupy -- not
     * necessarily accurate for VirtualImages */
    int numBytes() const    { return m_Bounds.nx * m_Bounds.ny * pixelSize(); }

    /** return the number of bytes wide that the image is */
    int strideBytes() const { return m_Bounds.nx * pixelSize(); }

    /** return the x Size of the image */
    int xSize() const { return m_Bounds.nx;  }

    /** return the y Size of the image */
    int ySize() const { return m_Bounds.ny;  }

    /** return the channel count of the image */
    int cSize() const { return m_cCount; }

    /** return true if the x and y dimensions are equal
     *  @param o the other descriptor to compare against.
     */
    bool sizeEqual(const ImageDescriptor &o) const
        { return ((xSize() == o.xSize()) && (ySize() == o.ySize())); }

    /** Set the size of the image in pixels. */
    void setSize(int nx, int ny) { m_Bounds.nx = nx; m_Bounds.ny = ny; }

    /** return the bounds of the image */
    const ImgTile &getBounds() const { return m_Bounds; }

    /** return the horizontal and vertical resolution of the image
     *
     * @param xRes   returned X Resolution in Pixels per unit
     * @param yRes   returned y Resolution in Pixels per unit
     * @param unit   returned resolution unit
     */
    void getResolutionInfo(float &xRes, float &yRes, ResUnit &unit) const
    {
        xRes = m_xResolution; yRes = m_yResolution, unit = m_ResolutionUnits;
    }

    /** set the horizontal and vertical resolution of the image
     *
     * @param xRes   new X Resolution in Pixels per unit
     * @param yRes   new y Resolution in Pixels per unit
     * @param unit   new resolution unit
     */
    void setResolutionInfo(float xRes, float yRes, ResUnit unit)
    {
        m_xResolution = xRes; m_yResolution = yRes, m_ResolutionUnits = unit;
    }

    friend class Image;
};

//------------------------------------------------------------------------------
/// \brief This is the base image type defining the interface to images.
///
/// There are three differend kinds of images;
/// \li Physically contiguous
/// \li Striped
/// \li Tiled/Virtual/Versioned
///
/// The first kind is the fastest, but least memory efficient. If the system is
/// not able to allocate the required memory in one piece, a striped image will
/// be created. A striped image consists of several stripes of 1 or more rows of
/// pixels.
///
/// The third type of image is the most versatile -- It is stored as 128 pixel
/// square tiles -- tiles are copy-on-write, versioned, and have their own
/// virtual memory system optimized for images. Texture layers are typically
/// stored in this third type of image so that paint operations on them are easily
/// undoable.
class MBDLL_DECL Image : public Node
{

	DECLARE_CLASS;

    friend class ImgPageIterator;
    friend class ImgLockPageIterator;


public:
    /** used to identify what type of image any particular instance is.  */
    typedef enum {
        Type_Invalid = 0,
        Type_Memory,       /**< physically contiguous image or striped */
        Type_Virtual       /**< Tiled & versioned, not contiguous      */
    } ImageType;

    /// Image channel datatype type
	enum Format
	{
		e8integer = 0,
		e16integer,
		e32integer,
		e16float,
		e32float,
		e16depth,
		e24depth,
		e32depth,
		eUnknown,
		eInvalid,
	};

protected:

	Image( void );

    unsigned char    m_FillPixel[16]; // fill color -- largest supported pixel is 16 bytes.
    ImageDescriptor  m_Descriptor;    // how to interpret the pixel data

    ImageType        m_ImageType;

public:

    /// cause the image storage mechanisms to be tested randomly for the
    /// specified number of iterations.
    void UnitTest(int iterations);

    virtual bool isAllocated() const;

    /** return the image type.  */
    ImageType getType() const { return m_ImageType; }

    /** get the full descriptor for the image */
    const ImageDescriptor  &getDescriptor() const  { return m_Descriptor; }

    /** Get the x size of the image */
    int            xSize() const { return m_Descriptor.xSize(); }

    /** Get the y size of the image */
    int            ySize() const { return m_Descriptor.ySize(); }

    /** Get the channel count of the image */
    int            cSize() const { return m_Descriptor.cSize(); }

    /** Get the channel ordering of the image */
    ImageDescriptor::MemoryChannelOrder channelOrder() const { return m_Descriptor.m_channelOrder; }

    /** returns the bounds of the image -- the origin of this rect may not be at 0,0 */
    const ImgTile  &getBounds() const { return m_Descriptor.getBounds(); }

    //====================================================================
    /**
     * Reads the fill color into the provided buffer, in the specified
     * format -- if no format is provided, it uses the pixel format
     * of this image.
     *
     * @param fillPixel Pointer to the target where the fill color will
     *                  be written
     * @param pd        Desired pixelformat for the fill color -- if
     *                  0, it uses the pixel format of this image
     */
    void  getFillColor(void *fillPixel, const PixelDescriptor *pd = 0) const;

    //====================================================================
    /**
     * Sets the fill color into the provided buffer, in the specified
     * format -- if no format is provided, it uses the pixel format
     * of this image.
     *
     * @param fillPixel Pointer to the source where the fill color
     *                  will be read from
     * @param pd        Desired pixelformat for the fill color -- if
     *                  0, it uses the pixel format of this image
     */
    void  setFillColor(const void *fillPixel, const PixelDescriptor *pd = 0);

    //====================================================================
    /**
     * Reads a rectangular region of pixels from the image into a buffer.
     * The buffer must be exactly the size of the region being read.  If
     * the region is outside or partly outside the bounds of the image,
     * those areas of the target buffer will be filled with the image's
     * FillColor
     * @note this operation is atomic -- no other threads can write to
     * the image while this is being performed.
     *
     * @param x    x coord of the lower left corner of the region to be read.
     * @param y    y coord of the lower left corner of the region to be read.
     * @param nx   width of the region to be read.
     * @param ny   height of the region to be read.
     * @param data Pointer to the target buffer.
     * @param pd   Desired pixelformat for the buffer color -- if
     *             0, it uses the pixel format of this image
     */
    void  getTile(int x, int y, int nx, int ny, void *data,
                  const PixelDescriptor *pd = 0)
       { getTile(ImgTile(x, y, nx, ny), data, pd); }


    //====================================================================
    /**
     * Reads a rectangular region of pixels from the image into a buffer.
     * The buffer may be be different than the size of the region being
     * read.  If the region is outside or partly outside the bounds of the
     * image, those areas of the target buffer will be filled with the
     * image's FillColor
     * @note this operation is atomic -- no other threads can write to
     * the image while this is being performed.
     *
     * @param x    x coord of the lower left corner of the region to be read.
     * @param y    y coord of the lower left corner of the region to be read.
     * @param nx   width of the region to be read.
     * @param ny   height of the region to be read.
     * @param data Pointer to the target buffer.
     * @param dx   x coord of the lower left corner of the buffer
     *             (in image coords).
     * @param dy   y coord of the lower left corner of the buffer
     *             (in image coords).
     * @param dnx  width of the target buffer.
     * @param dny  height of the target buffer.
     * @param pd   Desired pixelformat for the buffer color -- if
     *             0, it uses the pixel format of this image
     */
    void  getSubTile(int x,  int y,  int nx,  int ny, void *data,
                     int dx, int dy, int dnx, int dny,
                     const PixelDescriptor *pd = 0)
       { getSubTile(ImgTile(x, y, nx, ny), data, ImgTile(dx, dy, dnx, dny), pd); }


    //====================================================================
    /**
     * Reads a rectangular region of pixels from the image into a buffer.
     * The buffer must be exactly the size of the region being read.  If
     * the region is outside or partly outside the bounds of the image,
     * those areas of the target buffer will be filled with the image's
     * FillColor
     * @note this operation is atomic -- no other threads can write to
     * the image while this is being performed.
     *
     * @param srcTile the rectangle of the image to be read into the
     *                buffer.
     * @param data Pointer to the target buffer.
     * @param pd   Desired pixelformat for the buffer color -- if 0,
     *             it uses the pixel format of this image
     */
    void getTile(const ImgTile &srcTile, void *data,
                 const PixelDescriptor *pd = 0)
       { getSubTile(srcTile, data, srcTile, pd); }

    //====================================================================
    /**
     * Reads a rectangular region of pixels from the image into a buffer.
     * The buffer may be be different than the size of the region being
     * read.  If the region is outside or partly outside the bounds of the
     * image, those areas of the target buffer will be filled with the
     * image's FillColor
     * @note this operation is atomic -- no other threads can write to
     * the image while this is being performed.
     * @note this is the only member of the getTile family that must be
     * provided by subclasses of this Image class.
     *
     * @param srcTile The rectangle of the image to be read into the
     *                buffer.
     * @param data    Pointer to the target buffer.
     * @param targetBounds bounds of the target buffer (in image coords).
     * @param pd      Desired pixelformat for the buffer color -- if
     *                0, it uses the pixel format of this image
     */
    virtual void getSubTile(const ImgTile &srcTile, void *data,
                            const ImgTile &targetBounds,
                            const PixelDescriptor *pd = 0);


    //====================================================================
    /**
     * Writes a rectangular region of pixels to the image from a buffer.
     * The buffer must be exactly the size of the region being written.
     * If the image has dynamic bounds, the image bounds will be expanded
     * as required so that the entire operation can complete.  Otherwise
     * the region that is outside the bounds of the image will not be
     * written.
     * @note this operation is atomic -- no other threads can read or
     * write to the image while this is being performed.
     *
     * @param x    x coord of the lower left corner of the region to
     *             be written.
     * @param y    y coord of the lower left corner of the region to
     *             be written.
     * @param nx   width of the region to be written.
     * @param ny   height of the region to be written.
     * @param data Pointer to the source buffer.
     * @param pd   Pixelformat for the buffer color -- if 0, it
     *             uses the pixel format of this image
     */
    void setTile(int x, int y, int nx, int ny, void *data,
                 const PixelDescriptor *pd = 0)
       { setTile(ImgTile(x, y, nx, ny), data, pd); }

    //====================================================================
    /**
     * Writes a rectangular region of pixels from the image into a buffer.
     * The buffer may be be different than the size of the region being
     * written.  If the image has dynamic bounds, the image bounds will be
     * expanded as required so that the entire operation can complete.
     * Otherwise the region that is outside the bounds of the image will
     * not be written.
     * @note this operation is atomic -- no other threads can read or
     * write to the image while this is being performed.
     *
     * @param x    x coord of the lower left corner of the region to
     *             be written.
     * @param y    y coord of the lower left corner of the region to
     *             be written.
     * @param nx   width of the region to be written.
     * @param ny   height of the region to be written.
     * @param data Pointer to the source buffer.
     * @param dx   x coord of the lower left corner of the buffer
     *             (in image coords).
     * @param dy   y coord of the lower left corner of the buffer
     *             (in image coords).
     * @param dnx  width of the target buffer.
     * @param dny  height of the target buffer.
     * @param pd   Pixelformat for the buffer color -- if 0, it
     *             uses the pixel format of this image
     */
    void setSubTile(int x,  int y,  int nx,  int ny, const void *data,
                    int dx, int dy, int dnx, int dny,
                    const PixelDescriptor *pd = 0)
       { setSubTile(ImgTile(x, y, nx, ny), data, ImgTile(dx, dy, dnx, dny), pd); }

    //====================================================================
    /**
     * Writes a rectangular region of pixels to the image from a buffer.
     * The buffer must be exactly the size of the region being written.
     * If the image has dynamic bounds, the image bounds will be expanded
     * as required so that the entire operation can complete.  Otherwise
     * the region that is outside the bounds of the image will not be
     * written.
     * @note this operation is atomic -- no other threads can read or
     * write to the image while this is being performed.
     *
     * @param dstTile rectangle of the image to be written.
     * @param data Pointer to the source buffer.
     * @param pd   Desired pixelformat for the buffer color -- if 0,
     *             it uses the pixel format of this image
     */
    void setTile(const ImgTile &dstTile, const void *data,
                 const PixelDescriptor *pd = 0)
       { setSubTile(dstTile, data, dstTile, pd); }

    //====================================================================
    /**
     * Writes a rectangular region of pixels from the image into a buffer.
     * The buffer may be be different than the size of the region being
     * written.  If the image has dynamic bounds, the image bounds will be
     * expanded as required so that the entire operation can complete.
     * Otherwise the region that is outside the bounds of the image will
     * not be written.
     * @note this operation is atomic -- no other threads can read or
     * write to the image while this is being performed.
     * @note this is the only member of the setTile family that must
     * be provided by subclasses of this Image class.
     *
     * @param dstTile rectangle of the image to be written.
     * @param data Pointer to the source buffer.
     * @param sourceBounds bounds of the source buffer (in image coords).
     * @param pd   Pixelformat for the buffer color -- if 0, it
     *             uses the pixel format of this image
     */
    virtual void setSubTile(const ImgTile &dstTile, const void *data,
                            const ImgTile &sourceBounds,
                            const PixelDescriptor *pd = 0);

    //====================================================================
    /**
     * Copies a rectangular region of pixels from a source image to this
     * image.  If the image has dynamic bounds, the image bounds will be
     * expanded as required so that the entire operation can complete.
     * Otherwise the region that is outside the bounds of the image will
     * not be written.  Regions outsude the bounds of the source image
     * will be filled with the FillColor of the source image.
     * @note this operation is atomic -- no other threads can read or
     * write to the target image while this is being performed, and no
     * other threads can write to the source image.
     * @note Format conversions are automatically performed as required.
     *
     * @param x    x target coord of the lower left corner of the region
     *             to be copied.
     * @param y    y target coord of the lower left corner of the region
     *             to be copied.
     * @param nx   width of the region to be copied.
     * @param ny   height of the region to be copied.
     * @param fromImg The source image.
     * @param ox   x source coord of the lower left corner of the region
     *             to be copied (in souce image coordinates).
     * @param oy   y source coord of the lower left corner of the region
     *             to be copied  (in souce image coordinates). .
     */
    void copyTile(int x, int y, int nx, int ny, Image &fromImg,
                  int ox, int oy)
       { copyTile(ImgTile(x, y, nx, ny), fromImg, ox, oy); }


    //====================================================================
    /**
     * Copies a rectangular region of pixels from a source image to this
     * image.  If the image has dynamic bounds, the image bounds will be
     * expanded as required so that the entire operation can complete.
     * Otherwise the region that is outside the bounds of the image will
     * not be written.  Regions outsude the bounds of the source image
     * will be filled with the FillColor of the source image.
     * @note this operation is atomic -- no other threads can read or
     * write  to the target image while this is being performed, and no
     * other threads  can write to the source image.
     * @note Format conversions are automatically performed as required.
     * @note this is the only member of the copyTile family that must
     * be provided  by subclasses of this Image class.
     *
     * @param dstTile target rectangle to be copied.
     * @param fromImage The source image.
     * @param ox   x source coord of the lower left corner of the region
     *             to be copied (in souce image coordinates).
     * @param oy   y source coord of the lower left corner of the region
     *             to be copied  (in souce image coordinates). .
     */
    virtual void copyTile(const ImgTile &dstTile, Image &fromImg,
                          int ox, int oy);

    /** Clear all the pixels of the image to 0 in all channels */
    virtual void Clear();

    //====================================================================
    /**
     * Fills a rectangular region of pixels in the image with a constant
     * color. Optionally a rectangular region can be "masked off" and not
     * filled. If the image has dynamic bounds, the image bounds will be
     * expanded as required so that the entire operation can complete.
     * Otherwise the region that is outside the bounds of the image will
     * not be written.
     * @note this operation is atomic -- no other threads can read or
     * write to the image while this is being performed.
     *
     * @param x    x target coord of the lower left corner of the region
     *             to be filled.
     * @param y    y target coord of the lower left corner of the region
     *             to be filled.
     * @param nx   width of the region to be filled.
     * @param ny   height of the region to be filled.
     * @param pixelData Pointer to the source pixel data. If 0, all 0s
     *                  are used instead.
     * @param pd   Pixelformat for the source pixel color -- if 0, it
     *             uses the pixel format of this image
     * @param masktTile an optional pointer to a mask rectangle. If
     *                  present, the contents of this rectangle of the
     *                  image will remain unchanged by this operation.
     */
    void fillTile(int x, int y, int nx, int ny, void *pixelData,
                  const PixelDescriptor *pd       = 0,
                  const ImgTile         *maskTile = 0)
    { fillTile(ImgTile(x, y, nx, ny), pixelData, pd, maskTile); }

    //====================================================================
    /**
     * Fills a rectangular region of pixels in the image with a constant
     * color. Optionally a rectangular region can be "masked off" and not
     * filled. If the image has dynamic bounds, the image bounds will be
     * expanded as required so that the entire operation can complete.
     * Otherwise the region that is outside the bounds of the image will
     * not be written.
     * @note this operation is atomic -- no other threads can read or
     * write to the image while this is being performed.
     *
     * @param dstTile   Rectangle of the image to be written.
     * @param pixelData Pointer to the source pixel data. If 0, all 0s
     *                  are used instead.
     * @param pd   Pixelformat for the source pixel color -- if 0, it
     *             uses the pixel format of this image
     * @param masktTile an optional pointer to a mask rectangle. If
     *                  present, the contents of this rectangle of the
     *                  image will remain unchanged by this operation.
     */
    void fillTile(const ImgTile &dstTile, const void *pixelData,
                  const PixelDescriptor *pd,
                  const ImgTile         *maskTile);

    //====================================================================
    /**
     * Fills a rectangular region of pixels in the image with a constant
     * color. Optionally a rectangular region can be "masked off" and not
     * filled. If the image has dynamic bounds, the image bounds will be
     * expanded as required so that the entire operation can complete.
     * Otherwise the region that is outside the bounds of the image will
     * not be written.
     * @note this operation is atomic -- no other threads can read or
     * write to the image while this is being performed.
     * @note this is the only member of the fillTile family that must
     * be provided by subclasses of this Image class.
     *
     * @param dstTile   Rectangle of the image to be written.
     * @param pixelData Pointer to the source pixel data. If 0, all 0s
     *                  are used instead.
     * @param pd   Pixelformat for the source pixel color -- if 0, it
     *             uses the pixel format of this image
     */
    virtual void fillTile(const ImgTile         &dstTile,
                          const void            *pixelData,
                          const PixelDescriptor *pd = 0);

    //====================================================================
    /**
     * Returns the size of an image page. Some images may be carved up
     * into 1 or more image pages -- this method returns the page size
     * for this image. MemoryImages will be a single page that is the size of the
     * entire images. virtualImages will have have a page size of 128 x 128
     * pixels.
     * @param nx width of a page
     * @param ny height of a page
     */
    virtual void getPageSize(int &nx, int &ny);

    //====================================================================
    /**
     * Returns a tile iterator that will iterate over the pages of this
     * image in an optimal order. For MemoryImages and other non paged
     * images this iterator will only produce one tile.
     * @param x  x origin of the region to iterate over
     * @param y  yorigin of the region to iterate over
     * @param nx width of the region to iterate over
     * @param ny height of the region to iterate over
     * @return the tileIterator
     * @note this function allocates the iterator -- when the caller is
     * finished with it, the caller must delete it.
     */
    ImgPageIterator *getPageIterator(int x, int y, int nx, int ny)
    { return getPageIterator(ImgTile(x, y, nx, ny)); }

    //====================================================================
    /**
     * Returns a tile iterator that will iterate over the pages of this
     * image in an optimal order. For MemoryImages and other non paged
     * images this iterator will only produce one tile.
     * @param srcTile the region to iterate over
     * @return the tileIterator
     * @note this function allocates the iterator -- when the caller is
     * finished with it, the caller must delete it.
     */
    virtual ImgPageIterator *getPageIterator(const ImgTile &srcTile);

    //====================================================================
    /**
     * Returns a tile iterator that will iterate over the pages of this
     * image in an optimal order. These pages are locked at the start of
     * this operation, and unlocked 1 by one as they are iterated over.
     * For MemoryImages and other non paged images this iterator will only
     * produce one tile.
     * @param x  x origin of the region to lock and iterate over
     * @param y  yorigin of the region to lock and iterate over
     * @param nx width of the region to lock and iterate over
     * @param ny height of the region to lock and iterate over
     * @param mode the access mode -- read only, read/write or write only.
     * @return the tileIterator
     * @note this function allocates the iterator -- when the caller is
     * finished with it, the caller must delete it.
     */
    ImgLockPageIterator *lockPageSet(int x, int y, int nx, int ny,
                                     ImgLockPageIterator::AccessMode mode)
    { return lockPageSet(ImgTile(x, y, nx, ny), mode); }

    //====================================================================
    /**
     * Returns a tile iterator that will iterate over the pages of this
     * image in an optimal order. These pages are locked at the start of
     * this operation, and unlocked 1 by one as they are iterated over.
     * For MemoryImages and other non paged images this iterator will only
     * produce one tile.
     * @param srcTile the region to lock and iterate over
     * @param mode the access mode -- read only, read/write or write only.
     * @return the tileIterator
     * @note this function allocates the iterator -- when the caller is
     * finished with it, the caller must delete it.
     */
    virtual ImgLockPageIterator *lockPageSet(const ImgTile &srcTile,
                                             ImgLockPageIterator::AccessMode mode);

    static ImageDescriptor::MemoryChannelOrder optimizedChannelOrder();

    static void ConvertPixels(void *dst, const void *src, int numPix,
                              const PixelDescriptor &dstDesc,
                              const PixelDescriptor *srcDesc = 0);

    /// copies 1-luminance from the source image into this image
    void CopyInverseLuminance(const Image &srcImage);

    /// copies the luminance of the source image into this image
    void CopyLuminance(const Image &srcImage);

	/// Returns the supported file extensions, separated by ';'.
	/// @param bRGBAOnly true to return a list of file formats that support
	///					 4 channels. false to return a list of all supported
    ///                  formats.
	///					 Not all returned formats may support 4 channels
    ///                  (like JPEG).
    ///
	/// @param bRead	true to return list of readable formats, false for
    ///                 writable formats.
	///					Not all readable formats may be writable and vice versa.
    ///
	/// @param bDesc	true to prepend descriptive text to each extension.
    ///                 i.e "JPG [8 bit, RGB]"
    ///
    /// @param bPSD     true to support PSD files...
    ///
	virtual QString SupportedExtensions( bool bRGBAOnly = false,
                                        bool bRead     = true,
                                        bool bDesc     = false,
                                        bool bPSD      = true,
					QString sDelimiter = ";" ) const;

	/// Create a new image. Data will be undefined.
    /// The Tiled argument creates a tiled versioned image.
    /// (the Data method will not work on these type of images, you must use
    /// get/set/copy tile or page locking to access them)
	virtual void Create( unsigned int iWidth, unsigned int iHeight,
                         unsigned int iChannelCount, Format eFormat = e8integer,
                         bool Tiled = false,
                        ImageDescriptor::MemoryChannelOrder eOrder = ImageDescriptor::orderRGBA);

	/// Load a whole image from an external file. The extension
    /// in the filename must be included. If the file is a PSD file,
    /// the bottom bost image layer is loaded into this image.
	virtual void Load( const QString &sFileName, int iLayerIndex = 0 );

    /// Load a single channel from an external file.
	virtual void LoadChannel( const QString  &sFileName,
                              unsigned int   iChannelIndex,
                              int            iLayerIndex = 0 );

    virtual bool GetPSDLayerMeta ( void *pPSDFile, int iLayerIndex, 
                                   QString &layerName, int &blendMode,
                                   float &opacity, bool &locked, 
                                   bool &visible, bool &transLocked);

    /// Load this image from a layer in a Photoshop file. The image will be
    /// created at the bit-depth and format of the PSD file (either 8 bit int,
    /// 16 bit int, or 32 bit float), and will be the size of the PSD file
    /// (not the size of the layer, which can be smaller). The image will be
    /// tiled as specified. The pPSDFile must have been created with OpenPSDFile.
    /// 0 is the bottom layer, and they increase as you go up in the
    /// layer stack.
    virtual bool LoadPSDLayer ( void *pPSDFile,
                                int   iLayerIndex,
                                bool  Tiled = false,
                                bool  premult = true );


    virtual Image *ComputeDifferenceMask(Image *otherImg, int expansionRadius,
                                         int AARadius, bool &anyDiffs);

    /// Save this image metadata to the layer metadata in a Photoshop file.
    /// The image must be the same size and bit-depth as the PSD file
    /// The pPSDFile must have been created with PSDOpen.
    /// 0 is the bottom layer, and they increase as you go up in the layer stack
    /// -1 for the layer index indicates that you want to save the RGB of this image
    /// into the composite image in the PSD file. In that case, the remaining
    /// parameters are ignored.
    /// All layer metadata must be set before any layer images are written
    virtual bool SavePSDLayerMeta ( void         *pPSDFile,
                                    int           iLayerIndex,
                                    const char   *pLayerName,
                                    float         opacity = 1.0f,
                                    bool          visible = true,
                                    bool          locked  = false,
                                    int           xOff    = 0,
                                    int           yOff    = 0,
                                    int           blendMode = -1);

    /// Save this image to a layer in a Photoshop file.
    /// The image must be the same size and bit-depth as the PSD file
    /// The pPSDFile must have been created with PSDOpen.
    /// 0 is the bottom layer, and they increase as you go up in the layer stack
    /// -1 for the layer index indicates that you want to save the RGB of this image
    /// into the composite image in the PSD file. In that case, the remaining
    /// parameters are ignored.
    /// The layer metata for this and all other layers must have been set
    /// before calling this function.
    virtual bool SavePSDLayer ( void         *pPSDFile,
                                int           iLayerIndex,
                                const char   *pLayerName,
                                float         opacity = 1.0f,
                                bool          visible = true,
                                bool          locked  = false,
                                bool          needs_unpremult = true,
                                int           xOff    = 0,
                                int           yOff    = 0);

    /// Open a PSD file for reading or writing -- returns a pointer to blind data representing the
    /// open file, or NULL if it was unable to open the file.
    /// When creating a PSD file, first open it, then set the FileInfo, then save the layes
    /// from the bottom up (index 0 to n), and lastly save the composite layer (index -1.) Then
    /// close the file.
    virtual void *OpenPSDFile(const QString &pFileName, bool writing = false);

    /// Returns the basic file scope metadata for a PSD file. Returns true if successful.
    /// (if any of the pointers are NULL, the informaton is not retrieved)
    virtual bool  GetPSDFileInfo(void *psdFile, int *width, int *height, int *numChannels,
                                 int *bitDepth, int *numLayers, int *curLayer);

    /// Sets the file scope metadata for a writable PSD file. Returns true if successful.
    /// This must be called before saving any layers.
    virtual bool  SetPSDFileInfo(void *psdFile, int width, int height, int numChannels,
                                 int bitDepth, int numLayers, int curLayer);

    /// Close a PSD file.
    virtual bool ClosePSDFile(void *psdFile);


    /// Save the image to an external file. The extension of the file
    /// will determine the file type. If bForce is true the image
    /// should be saved even it means data loss. Otherwise
    /// an exception should drop.
    /// Optionally, when saving to PSD, if you specify a material
    /// and a uv offset for this texture, it can draw the base uv
    /// mesh on a layer above the image layer.
	virtual void Save( const QString &sFileName, bool bForce = true,
                       Material *pMaterial = NULL,
                       float uStart = 0.0f, float vStart = 0.0f );

	/// Return the width of the image in pixels.
	virtual unsigned int Width( void ) const;

    /// Return the height of the image in pixels.
	virtual unsigned int Height( void ) const;

    /// Return the format of the image.
	virtual Format Format( void ) const;

    /// Return the number of channels in the image.
	virtual unsigned int ChannelCount( void ) const;

	/// Return the number of bytes used by a pixel
	virtual unsigned int BytesPerPixel( void ) const;

	/// Return the number of bytes used by the whole image
	virtual unsigned int TotalBytes( void ) const;

	/// Set value for a specified texel. For integer maps 1 should be mapped
    /// for the maximum value (255 for 8 bit images and 65535 for 16 bit
    /// images) This fcn is very slow and deprecated. Use LockPageSet, or
    /// the fast  templated pixel accessor classes.
	virtual void SetValueAt( unsigned int iXPos, unsigned int iYPos,
                             unsigned int iChannel, float fValue );

	/// Get the value at a given coordinate.
    /// This fcn is very slow and deprecated. Use LockPageSet, or the fast
    /// templated pixel accessor classes.
	virtual float ValueAt( unsigned int iXPos, unsigned int iYPos,
                           unsigned int iChannel ) const;

	/// Get interpolated value at a given coordinate.
    /// This fcn is very slow and deprecated. Use LockPageSet, or the fast
    /// templated pixel accessor classes.
	virtual float ValueAt( float fXPos, float fYPos, unsigned int iChannel ) const;

	/// Set value for a specified texel. For integer maps 1 should be mapped for
    /// the maximum value (255 for 8 bit images and 65535 for 16 bit images)
    virtual void SetColorAt( unsigned int iXPos, unsigned int iYPos,
                             const Color &cColor );

    /// Get the value at a given coordinate.                                           
    virtual Color ColorAt( unsigned int iXPos, unsigned int iYPos ) const;

    /// Get interpolated value at a given coordinate.
    virtual Color ColorAt( float fXPos, float fYPos ) const;

	/// Create a QImage object from the image data for display purposes.
    /// This function allocates the QImage -- the caller will have to delete it
    /// when done with the image.
	virtual QImage *ConvertToQImage( );

    /// Creates the image from a QImage.
	virtual void ConvertFromQImage( QImage &qImg);

    /// returns true if the image is not stored in one physically contiguous
    /// piece of CPU visible memory -- If so, you must use lockPageSet to
    /// get at portions of the image data...
    virtual bool Fragmented() const;

    /// returns true if this is a tiled and versioned image
    virtual bool Tiled() const;

    /// returns true if this is a tiled and versioned image
    bool Versioned() const { return Tiled(); }

    /// checkpoint and create a new version of this image.
    /// returns true if it worked, false otherwise
    virtual bool NewVersion();

    /// makes the previous version current -- used for undo
    /// returns true if it worked, false otherwise
    virtual bool PrevVersion();

    /// makes the next version current -- used for redo
    /// returns true if it worked, false otherwise
    virtual bool NextVersion();

    /// returns the number of versions. returns 1 for unversioned images
    virtual int  NumVersions();

    /// merges the oldest two versions -- used to limit the number of
    /// items on the undo queue.
    /// returns true if it worked, false otherwise
    virtual bool MergeOldestVersions();

    /// Creating a new version will implicitly purge nerer versions, but if
    /// another operation takes place that puts a new item on the undo queue,
    /// this should be called to purge newer versions of this image
    /// returns true if it worked, false otherwise
    virtual bool PurgeNewerVersions();

    /// Releases all but the current version -- used to flush the undo queue.
    /// returns true if it worked, false otherwise
    virtual bool PurgeAllButCurrentVersion();

    /// Get a pointer to the raw data of a row of the image.  If the given row
    /// index is -1, then a pointer - if exists - to the whole data block is
    /// returned. These are deprecated and are going away shortly. DO NOT USE
    /// THEM IN NEW CODE. Use LockPageSet instead.
    /// These will not work on tiled images at all.
    virtual void *Data( int iRow = -1 );
	virtual const void *Data( int iRow = -1 ) const;


    /// Draw the uv mesh for a given material into this image -- either the
    /// base or active level. uv offset can be specified for tiled materials.
    /// These offsets should be 0.0, 1.0, 1.0 etc... The default is to draw
    /// from 0.0, 0.0 to 1.0, 1.0
    virtual void DrawUVs(Material *pMaterial,
                         float uStart = 0.0f, float vStart = 0.0f,
                         bool baseLevel = true);


    /// Fast, thread safe image scaler. Works only on 4 channel 8 bit/channel images
    /// Note: Not a permanent part of the API.
    virtual void scaleTileBilinear_4Chan_uchar(float x, float y, float nx, float ny, // src tile (from this image)
                                               unsigned int *data,                   // target buffer 
                                               int dx, int dy, int dnx, int dny,     // target location and size of target buffer
                                               float xScale, float yScale) const;    // scale factors



};

//------------------------------------------------------------------------------
MBDLL_DECL AttributeWidget *CreateNewImageWidget( QWidget *pParent, int iWidth,
                                                  AttributePointer<Image> *pImage );
template <> inline
AttributeWidget *AttributePointer<Image>::CreateEditorWidget( QWidget *pParent, int iWidth )
{ return CreateNewImageWidget( pParent, iWidth, this ); };

class MBDLL_DECL EnvironmentMap : public Node
{
	DECLARE_CLASS;

protected:

	EnvironmentMap( void );

public:
	/// Load an environment map from the given file. The format of the
    /// map must be determined from the file name, or the file content
    /// itself. Should return true when the loading was successfull.
	virtual bool Load( const QString &sFileName );
	/// Convert the loaded environment map into an opengl texture. This
    /// texture must remain valid after the environment map object is deleted.
	virtual unsigned int ConvertToOpenGLTexture( void );
};


//------------------------------------------------------------------------------
/// rapid random acessor for images

// these are just shorthand -- they're undefined later in this file
#define cu_int  const unsigned int
#define u_int   unsigned int
#define u_short unsigned short
#define u_char  unsigned char

#define inline __forceinline

/// The ImageAccessorBase class gives you very fast access to
/// pixels when the data type and channel count of an image are known at
/// compile time (and this is very often the case.)
template <class ChannelType, cu_int nch>
class MBDLL_TEMPLATE_DECL ImageAccessorBase
{
private:
    u_int        m_Stride;
    u_int        m_Height;
    u_int        m_chans;
    Image       *m_Image;
    ChannelType *m_pData;

public:
    ImageAccessorBase(Image *parent)
    {
        m_Stride = parent->Width();
        m_Height = parent->Height();
        m_chans  = parent->ChannelCount();
        m_Image  = parent;
        m_pData  = (ChannelType *)parent->Data();
    }

    u_int Width()        const { return m_Stride; }
    u_int Height()       const { return m_Height; }
    u_int ChannelCount() const { return m_chans;  }

	/// Set value for a specified texel. For integer maps 1 should be
    /// mapped for the maximum value (255 for 8 bit images and 65535
    /// for 16 bit images)
	inline void  SetValueAt(u_int X, u_int Y, u_int Ch, float fVal);

	/// Get the value at a given coordinate.
	inline float ValueAt(u_int X, u_int Y, u_int Ch) const;

	/// Set value for a specified texel. For integer maps 1 should be
    /// mapped for the maximum value (255 for 8 bit images and 65535
    /// for 16 bit images)
	inline void  SetColorAt( u_int X, u_int Y, const Color &cColor );

    inline void *AddrAt(u_int X, u_int Y, u_int Ch);
};

//------------------------------------------------------------------------------
template <class ChannelType, cu_int nch>
class MBDLL_TEMPLATE_DECL ImageAccessor : public ImageAccessorBase<ChannelType, nch>
{
public:
    ImageAccessor(Image *parent) : ImageAccessorBase<ChannelType, nch>(parent)
    {
	}

	/// Get the value at a given coordinate.
	inline Color ColorAt( u_int X, u_int Y ) const;
};

//------------------------------------------------------------------------------
template <>
inline void *ImageAccessorBase<u_char, 1>::AddrAt(u_int X, u_int Y, u_int Ch)
{ return (void *)&m_pData[Ch + ((X + m_Stride * Y)    )]; }
template <>
inline void *ImageAccessorBase<u_char, 2>::AddrAt(u_int X, u_int Y, u_int Ch)
{ return (void *)&m_pData[Ch + ((X + m_Stride * Y) * 2)]; }
template <>
inline void *ImageAccessorBase<u_char, 3>::AddrAt(u_int X, u_int Y, u_int Ch)
{ return (void *)&m_pData[Ch + ((X + m_Stride * Y) * 3)]; }
template <>
inline void *ImageAccessorBase<u_char, 4>::AddrAt(u_int X, u_int Y, u_int Ch)
{ return (void *)&m_pData[Ch + ((X + m_Stride * Y) * 4)]; }

template <>
inline void *ImageAccessorBase<u_short, 1>::AddrAt(u_int X, u_int Y, u_int Ch)
{ return (void *)&m_pData[Ch + ((X + m_Stride * Y)    )]; }
template <>
inline void *ImageAccessorBase<u_short, 2>::AddrAt(u_int X, u_int Y, u_int Ch)
{ return (void *)&m_pData[Ch + ((X + m_Stride * Y) * 2)]; }
template <>
inline void *ImageAccessorBase<u_short, 3>::AddrAt(u_int X, u_int Y, u_int Ch)
{ return (void *)&m_pData[Ch + ((X + m_Stride * Y) * 3)]; }
template <>
inline void *ImageAccessorBase<u_short, 4>::AddrAt(u_int X, u_int Y, u_int Ch)
{ return (void *)&m_pData[Ch + ((X + m_Stride * Y) * 4)]; }


template <>
inline void *ImageAccessorBase<float, 1>::AddrAt(u_int X, u_int Y, u_int Ch)
{ return (void *)&m_pData[Ch + ((X + m_Stride * Y)    )]; }
template <>
inline void *ImageAccessorBase<float, 2>::AddrAt(u_int X, u_int Y, u_int Ch)
{ return (void *)&m_pData[Ch + ((X + m_Stride * Y) * 2)]; }
template <>
inline void *ImageAccessorBase<float, 3>::AddrAt(u_int X, u_int Y, u_int Ch)
{ return (void *)&m_pData[Ch + ((X + m_Stride * Y) * 3)]; }
template <>
inline void *ImageAccessorBase<float, 4>::AddrAt(u_int X, u_int Y, u_int Ch)
{ return (void *)&m_pData[Ch + ((X + m_Stride * Y) * 4)]; }

template <>
inline void *ImageAccessorBase<half_, 1>::AddrAt(u_int X, u_int Y, u_int Ch)
{ return (void *)&m_pData[Ch + ((X + m_Stride * Y)    )]; }
template <>
inline void *ImageAccessorBase<half_, 2>::AddrAt(u_int X, u_int Y, u_int Ch)
{ return (void *)&m_pData[Ch + ((X + m_Stride * Y) * 2)]; }
template <>
inline void *ImageAccessorBase<half_, 3>::AddrAt(u_int X, u_int Y, u_int Ch)
{ return (void *)&m_pData[Ch + ((X + m_Stride * Y) * 3)]; }
template <>
inline void *ImageAccessorBase<half_, 4>::AddrAt(u_int X, u_int Y, u_int Ch)
{ return (void *)&m_pData[Ch + ((X + m_Stride * Y) * 4)]; }

// implement the inline member fcns with specialization...
template <>
inline void ImageAccessorBase<u_char, 1>::SetValueAt(u_int X, u_int Y, u_int Ch, float fVal)
{ m_pData[Ch + ((X + m_Stride * Y)    )] = (u_char)(255.0f * fVal); }
template <>
inline void ImageAccessorBase<u_char, 2>::SetValueAt(u_int X, u_int Y, u_int Ch, float fVal)
{ m_pData[Ch + ((X + m_Stride * Y) * 2)] = (u_char)(255.0f * fVal); }
template <>
inline void ImageAccessorBase<u_char, 3>::SetValueAt(u_int X, u_int Y, u_int Ch, float fVal)
{ m_pData[Ch + ((X + m_Stride * Y) * 3)] = (u_char)(255.0f * fVal); }
template <>
inline void ImageAccessorBase<u_char, 4>::SetValueAt(u_int X, u_int Y, u_int Ch, float fVal)
{ m_pData[Ch + ((X + m_Stride * Y) * 4)] = (u_char)(255.0f * fVal); }

template <>
inline void ImageAccessorBase<u_short, 1>::SetValueAt( u_int X, u_int Y, u_int Ch, float fVal )
{ m_pData[Ch + ((X + m_Stride * Y)    )] = (u_short)(65535.0f * fVal); }
template <>
inline void ImageAccessorBase<u_short, 2>::SetValueAt( u_int X, u_int Y, u_int Ch, float fVal )
{ m_pData[Ch + ((X + m_Stride * Y) * 2)] = (u_short)(65535.0f * fVal); }
template <>
inline void ImageAccessorBase<u_short, 3>::SetValueAt( u_int X, u_int Y, u_int Ch, float fVal )
{ m_pData[Ch + ((X + m_Stride * Y) * 3)] = (u_short)(65535.0f * fVal); }
template <>
inline void ImageAccessorBase<u_short, 4>::SetValueAt( u_int X, u_int Y, u_int Ch, float fVal )
{ m_pData[Ch + ((X + m_Stride * Y) * 4)] = (u_short)(65535.0f * fVal); }

//template <class ChannelType, cu_int nch>
template <>
inline void ImageAccessorBase<u_int, 1>::SetValueAt( u_int X, u_int Y, u_int Ch, float fVal )
{ m_pData[Ch + ((X + m_Stride * Y)    )] = (u_int)(0xffffffff * fVal); }
template <>
inline void ImageAccessorBase<u_int, 2>::SetValueAt( u_int X, u_int Y, u_int Ch, float fVal )
{ m_pData[Ch + ((X + m_Stride * Y) * 2)] = (u_int)(0xffffffff * fVal); }
template <>
inline void ImageAccessorBase<u_int, 3>::SetValueAt( u_int X, u_int Y, u_int Ch, float fVal )
{ m_pData[Ch + ((X + m_Stride * Y) * 3)] = (u_int)(0xffffffff * fVal); }
template <>
inline void ImageAccessorBase<u_int, 4>::SetValueAt( u_int X, u_int Y, u_int Ch, float fVal )
{ m_pData[Ch + ((X + m_Stride * Y) * 4)] = (u_int)(0xffffffff * fVal); }

template <>
inline void ImageAccessorBase<float, 1>::SetValueAt( u_int X, u_int Y, u_int Ch, float fVal )
{ m_pData[Ch + ((X + m_Stride * Y)    )] = fVal; }
template <>
inline void ImageAccessorBase<float, 2>::SetValueAt( u_int X, u_int Y, u_int Ch, float fVal )
{ m_pData[Ch + ((X + m_Stride * Y) * 2)] = fVal; }
template <>
inline void ImageAccessorBase<float, 3>::SetValueAt( u_int X, u_int Y, u_int Ch, float fVal )
{ m_pData[Ch + ((X + m_Stride * Y) * 3)] = fVal; }
template <>
inline void ImageAccessorBase<float, 4>::SetValueAt( u_int X, u_int Y, u_int Ch, float fVal )
{ m_pData[Ch + ((X + m_Stride * Y) * 4)] = fVal; }

template <>
inline float ImageAccessorBase<u_char, 1>::ValueAt( u_int X, u_int Y, u_int Ch ) const
{ return m_pData[Ch + ((X + m_Stride * Y)    )] * (1.0f/255.0f); }
template <>
inline float ImageAccessorBase<u_char, 2>::ValueAt( u_int X, u_int Y, u_int Ch ) const
{ return m_pData[Ch + ((X + m_Stride * Y) * 2)] * (1.0f/255.0f); }
template <>
inline float ImageAccessorBase<u_char, 3>::ValueAt( u_int X, u_int Y, u_int Ch ) const
{ return m_pData[Ch + ((X + m_Stride * Y) * 3)] * (1.0f/255.0f); }
template <>
inline float ImageAccessorBase<u_char, 4>::ValueAt( u_int X, u_int Y, u_int Ch ) const
{ return m_pData[Ch + ((X + m_Stride * Y) * 4)] * (1.0f/255.0f); }

template <>
inline float ImageAccessorBase<u_short, 1>::ValueAt( u_int X, u_int Y, u_int Ch ) const
{ return m_pData[Ch + ((X + m_Stride * Y)    )] * (1.0f/65535.0f); }
template <>
inline float ImageAccessorBase<u_short, 2>::ValueAt( u_int X, u_int Y, u_int Ch ) const
{ return m_pData[Ch + ((X + m_Stride * Y) * 2)] * (1.0f/65535.0f); }
template <>
inline float ImageAccessorBase<u_short, 3>::ValueAt( u_int X, u_int Y, u_int Ch ) const
{ return m_pData[Ch + ((X + m_Stride * Y) * 3)] * (1.0f/65535.0f); }
template <>
inline float ImageAccessorBase<u_short, 4>::ValueAt( u_int X, u_int Y, u_int Ch ) const
{ return m_pData[Ch + ((X + m_Stride * Y) * 4)] * (1.0f/65535.0f); }

template <>
inline float ImageAccessorBase<u_int, 1>::ValueAt( u_int X, u_int Y, u_int Ch ) const
{ return m_pData[Ch + ((X + m_Stride * Y)    )] * (1.0f/float(0xffffffff)); }
template <>
inline float ImageAccessorBase<u_int, 2>::ValueAt( u_int X, u_int Y, u_int Ch ) const
{ return m_pData[Ch + ((X + m_Stride * Y) * 2)] * (1.0f/float(0xffffffff)); }
template <>
inline float ImageAccessorBase<u_int, 3>::ValueAt( u_int X, u_int Y, u_int Ch ) const
{ return m_pData[Ch + ((X + m_Stride * Y) * 3)] * (1.0f/float(0xffffffff)); }
template <>
inline float ImageAccessorBase<u_int, 4>::ValueAt( u_int X, u_int Y, u_int Ch ) const
{ return m_pData[Ch + ((X + m_Stride * Y) * 4)] * (1.0f/float(0xffffffff)); }

template <>
inline float ImageAccessorBase<float, 1>::ValueAt( u_int X, u_int Y, u_int Ch ) const
{ return m_pData[Ch + ((X + m_Stride * Y)    )]; }
template <>
inline float ImageAccessorBase<float, 2>::ValueAt( u_int X, u_int Y, u_int Ch ) const
{ return m_pData[Ch + ((X + m_Stride * Y) * 2)]; }
template <>
inline float ImageAccessorBase<float, 3>::ValueAt( u_int X, u_int Y, u_int Ch ) const
{ return m_pData[Ch + ((X + m_Stride * Y) * 3)]; }
template <>
inline float ImageAccessorBase<float, 4>::ValueAt( u_int X, u_int Y, u_int Ch ) const
{ return m_pData[Ch + ((X + m_Stride * Y) * 4)]; }

//------------------------------------------------------------------------------
template <>
inline void ImageAccessorBase<float, 4>::SetColorAt(u_int X, u_int Y, const Color &c)
{
    SetValueAt(X, Y, 0, c.r);
    SetValueAt(X, Y, 1, c.g);
    SetValueAt(X, Y, 2, c.b);
	SetValueAt(X, Y, 3, c.a);
};

//------------------------------------------------------------------------------
template <>
inline void ImageAccessorBase<float, 3>::SetColorAt(u_int X, u_int Y, const Color &c)
{
    SetValueAt(X, Y, 0, c.r);
    SetValueAt(X, Y, 1, c.g);
    SetValueAt(X, Y, 2, c.b);
};

//------------------------------------------------------------------------------
template <>
inline void ImageAccessorBase<u_char, 4>::SetColorAt(u_int X, u_int Y, const Color &c)
{
    SetValueAt(X, Y, 0, c.r);
    SetValueAt(X, Y, 1, c.g);
    SetValueAt(X, Y, 2, c.b);
	SetValueAt(X, Y, 3, c.a);
};

//------------------------------------------------------------------------------
template <>
inline Color ImageAccessor<float, 4>::ColorAt(u_int X, u_int Y) const
{
    return Color(ValueAt(X,Y,0), ValueAt(X,Y,1), ValueAt(X,Y,2), ValueAt(X,Y,3));
};

//------------------------------------------------------------------------------
template <>
inline Color ImageAccessor<u_char, 4>::ColorAt(u_int X, u_int Y) const
{
    return Color(ValueAt(X,Y,0), ValueAt(X,Y,1), ValueAt(X,Y,2), ValueAt(X,Y,3));
};

//------------------------------------------------------------------------------
template <>
inline Color ImageAccessor<u_short, 4>::ColorAt(u_int X, u_int Y) const
{
    return Color(ValueAt(X,Y,0), ValueAt(X,Y,1), ValueAt(X,Y,2), ValueAt(X,Y,3));
};

//------------------------------------------------------------------------------
template <>
inline Color ImageAccessor<u_short, 3>::ColorAt(u_int X, u_int Y) const
{
    return Color(ValueAt(X,Y,0), ValueAt(X,Y,1), ValueAt(X,Y,2), 1.0f);
};

//------------------------------------------------------------------------------
template <>
inline Color ImageAccessor<float, 3>::ColorAt(u_int X, u_int Y) const
{
    return Color(ValueAt(X,Y,0), ValueAt(X,Y,1), ValueAt(X,Y,2), 1.0f);
};

//------------------------------------------------------------------------------
template <>
inline Color ImageAccessor<u_char, 3>::ColorAt(u_int X, u_int Y) const
{
    return Color(ValueAt(X,Y,0), ValueAt(X,Y,1), ValueAt(X,Y,2), 1.0f);
};

//------------------------------------------------------------------------------
template <>
inline float ImageAccessorBase<half_, 3>::ValueAt( u_int X, u_int Y, u_int Ch ) const
{ return float(m_pData[Ch + ((X + m_Stride * Y) * 3)]); }

//------------------------------------------------------------------------------
template <>
inline float ImageAccessorBase<half_, 4>::ValueAt( u_int X, u_int Y, u_int Ch ) const
{ return float(m_pData[Ch + ((X + m_Stride * Y) * 4)]); }

//------------------------------------------------------------------------------
template <>
inline void ImageAccessorBase<half_, 3>::SetValueAt( u_int X, u_int Y, u_int Ch, float fVal )
{ m_pData[Ch + ((X + m_Stride * Y) * 3)] = fVal; }

//------------------------------------------------------------------------------
template <>
inline void ImageAccessorBase<half_, 4>::SetValueAt( u_int X, u_int Y, u_int Ch, float fVal )
{ m_pData[Ch + ((X + m_Stride * Y) * 4)] = fVal; }


//------------------------------------------------------------------------------
template <>
inline void ImageAccessorBase<half_, 4>::SetColorAt(u_int X, u_int Y, const Color &c)
{
    SetValueAt(X, Y, 0, c.r);
    SetValueAt(X, Y, 1, c.g);
    SetValueAt(X, Y, 2, c.b);
	SetValueAt(X, Y, 3, c.a);
};

//------------------------------------------------------------------------------
template <>
inline void ImageAccessorBase<half_, 3>::SetColorAt(u_int X, u_int Y, const Color &c)
{
    SetValueAt(X, Y, 0, c.r);
    SetValueAt(X, Y, 1, c.g);
    SetValueAt(X, Y, 2, c.b);
};

//------------------------------------------------------------------------------
template <>
inline Color ImageAccessor<half_, 4>::ColorAt(u_int X, u_int Y) const
{
    return Color(ValueAt(X,Y,0), ValueAt(X,Y,1), ValueAt(X,Y,2), ValueAt(X,Y,3));
};

//------------------------------------------------------------------------------
template <>
inline Color ImageAccessor<half_, 3>::ColorAt(u_int X, u_int Y) const
{
    return Color(ValueAt(X,Y,0), ValueAt(X,Y,1), ValueAt(X,Y,2), 1.0f);
};

//------------------------------------------------------------------------------
template <class ChannelType>
class ImageAccessor<ChannelType, 3> : public ImageAccessorBase<ChannelType, 3>
{
public:
    ImageAccessor(Image *parent) : ImageAccessorBase<ChannelType, 3>(parent)
    {
	}

	/// Get the value at a given coordinate.
	inline Color ColorAt( u_int X, u_int Y ) const
	{
		return Color(
				ImageAccessorBase<ChannelType, 3>::ValueAt(X,Y,0),
				ImageAccessorBase<ChannelType, 3>::ValueAt(X,Y,1),
				ImageAccessorBase<ChannelType, 3>::ValueAt(X,Y,2),
				1);
	}
};

//------------------------------------------------------------------------------
template <class ChannelType>
class MBDLL_TEMPLATE_DECL ImageAccessor<ChannelType, 2> : public ImageAccessorBase<ChannelType, 2>
{
public:
    ImageAccessor(Image *parent) : ImageAccessorBase<ChannelType, 2>(parent)
    {
	}

	/// Get the value at a given coordinate.
	inline Color ColorAt( u_int X, u_int Y ) const
	{
		return Color(
				ImageAccessorBase<ChannelType, 2>::ValueAt(X,Y,0),
				ImageAccessorBase<ChannelType, 2>::ValueAt(X,Y,1),
				0, 1);
	}
};

//------------------------------------------------------------------------------
template <class ChannelType>
class MBDLL_TEMPLATE_DECL ImageAccessor<ChannelType, 1> : public ImageAccessorBase<ChannelType, 1>
{
public:
    ImageAccessor(Image *parent) : ImageAccessorBase<ChannelType, 1>(parent)
    {
	}

	/// Get the value at a given coordinate.
	inline Color ColorAt( u_int X, u_int Y ) const
	{
		return Color(ImageAccessorBase<ChannelType, 1>::ValueAt(X,Y,0), 0, 0, 1);
	}
};

//------------------------------------------------------------------------------
#undef cu_int
#undef u_int
#undef u_short
#undef u_char
#undef inline

#endif
//------------------------------------------------------------------------------

}; // end of namespace mudbox
