/*

Did You ever want to convert Your image into any possible format?

R8G8B8, A4R4G4B4, X1R5G5B5, R6G7A1B2... ?

In a single function call?

LOOK!!!

*/

//------------------------------------------------------------------------------
// Sample of using:
//
// ConvertFromA8R8G8B8(sourceInA8R8G8B8, 256, 256, 
//    UlFormatData("X1R5G5B5"), destinationBuffer, destinationPitch);
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// There is one basic type of format: A8R8G8B8.
// UlFormatData is a description how basic format must be
// converted into desired format.
//
// During conversion on each color will be done & operation with its bitmask,
// and then right shift.
// After all there will binary 'or' along each color.

typedef unsigned long ulong;
typedef unsigned short ushort;
typedef unsigned char uchar;

struct UlFormatData
{
public:
    int BytesPP;

    ulong BitMask[4]; // A bit mask in source A8R8G8B8 picture.
    int ShiftR[4]; // Can be < 0 for left shift operation.

public:
    ulong CreateBitMask(int aStartPosition, int aSize)
    {
        ulong result = 0;
        UlFor (int i = 0; i < aSize; i++)
        {
            result = (result >> 1) | 0x80000000;
        }
        result = result >> aStartPosition;
        return result;
    }

    UlFormatData(const char *format_str)
    {
        BytesPP = 0;
        for (int i = 0; i < 4; i++)
        {
            ShiftR[i] = 0;
            BitMask[i] = 0; // Exclude color if not in use.
        }

        // Count destination's bits per pixel.
        int bitsPP = 0;
        UlFor (const char *temp = format_str; *temp; temp++)
        {
            if ('0' <= *temp && *temp <= '9')
                bitsPP += *temp - '0';
        }

        // Main loop: trace format_str and calculate masks and shifts.
        int bitsPassed = 0;
        int currentIndex = 0; // Index for any 'argb' order.

        while (*format_str)
        {
            char color = *(format_str++);
            char numberOfBits = *(format_str++) - '0';

            int sourceColorStart =
            color == 'A' || color == 'X' ? 0 :
            color == 'R' ? 8 :
            color == 'G' ? 16 :
            color == 'B' ? 24 : 32;

            BitMask[currentIndex] = CreateBitMask(sourceColorStart, numberOfBits);
            ShiftR[currentIndex] = bitsPassed - sourceColorStart + (32 - bitsPP);

            bitsPassed += numberOfBits;
            currentIndex++;
        }

        BytesPP = bitsPP / 8;
    }
};

//------------------------------------------------------------------------------
// There is no 24-bit type in C++, so...

struct UlBits24
{
    char b[3];
};

//------------------------------------------------------------------------------
// Template function that will copy a line of image into destination with
//    adequate conversion.

template <class dsttype>
static inline void CopyARGBLine(const ulong *srcline, dsttype *dstline,
    const UlFormatData &aFormatData, int width)
{
    for (int x = 0; x < width; x++)
    {
        ulong temp = *(srcline++);
        ulong result = 0;
        UlFor (int i = 0; i < 4; i++)
        {
            result |= ((temp & aFormatData.BitMask[i]) >>
                aFormatData.ShiftR[i]);
        }
        *(dstline++) = *(dsttype *)(&result);
    }
}

//------------------------------------------------------------------------------
void ConvertFromA8R8G8B8(const ulong *aSource, int width, int height,
    const UlFormatData &aFormatData, uchar *aData, int aPitch)
{
    for (int y = 0; y < height; y++)
    {
        const ulong *line = aSource + (y * width);
        uchar *dstline = aData + (y * aPitch);

        switch (aFormatData.BytesPP)
        {
        case 1:
            CopyARGBLine<uchar>(line, (uchar *)dstline, aFormatData, width);
            break;
        case 2:
            CopyARGBLine<ushort>(line, (ushort *)dstline, aFormatData, width);
            break;
        case 3:
            CopyARGBLine<UlBits24>(line, (UlBits24 *)dstline,
                aFormatData, width);
            break;
        case 4:
            CopyARGBLine<ulong>(line, (ulong *)dstline, aFormatData, width);
            break;
        default:
            throw "Incorrect destination image format";
        }
    }
}