/***************************************************************************
 *                                                                         *
 * Module  : skyconv.c                                                     *
 *                                                                         *
 * Purpose : Raster/Image conversion                                       *
 *                                                                         *
 **************************************************************************/

/***************************************************************************
 Includes
 */

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>

#include "batypes.h"
#include "balibtyp.h"
#include "baraster.h"
#include "baimage.h"
#include "barwtyp.h"

/* String abstraction API for unicode support */
#include "rwstring.h"

/* Generic conversion stuff */
#include "palquant.h"
#include "imrascnv.h"

/* This files header */
#include "banull.h"
#include "skyconv.h"

static const char rcsid[] __RWUNUSED__ =
    "@@(#)$Id: skyconv.c,v 1.1 2001/02/09 10:46:51 Rabin Exp $";

/****************************************************************************
 Local Types
 */

/****************************************************************************
 Local (Static) Prototypes
 */

/****************************************************************************
 Local Defines
 */
/* This define selects max alpha as 128 instead of 255 */
#define A128

/* SHOWLEVEL now has moved to common functionality file driver/common/imrascnv.c */

/* Macros for converting colors - sky has red at the bottom...! */

#define SKYCONVRGBATO1555(rgba) ((((RwUInt32)(rgba)->blue)  << 7) & 0x7c00) | \
                                ((((RwUInt32)(rgba)->green) << 2) & 0x03e0) | \
                                ((((RwUInt32)(rgba)->red)   >> 3)         ) | \
                                ((((RwUInt32)(rgba)->alpha) << 8) & 0x8000)
#define SKYCONV1555TORGBA(rgba, pixel) \
                                (rgba)->blue   = (RwUInt8)((pixel  >> 7) & 0xf8); \
                                (rgba)->green = (RwUInt8)((pixel  >> 2) & 0xf8); \
                                (rgba)->red  = (RwUInt8)((pixel  << 3) & 0xf8); \
                                (rgba)->alpha = (RwUInt8)((pixel & 0x8000) ? 255 : 0)

#define SKYCONVRGBATO888(rgba)  (((RwUInt32)0x80)          << 24) | \
                                (((RwUInt32)(rgba)->red)        ) | \
                                (((RwUInt32)(rgba)->green) <<  8) | \
                                (((RwUInt32)(rgba)->blue)  << 16)
#define SKYCONV888TORGBA(rgba, pixel) \
                                (rgba)->red   = ((RwUInt8)(pixel))&0xff; \
                                (rgba)->green = ((RwUInt8)(pixel>>8))&0xff; \
                                (rgba)->blue  = ((RwUInt8)(pixel>>16))&0xff; \
                                (rgba)->alpha = 0xff

#ifdef A128
/* A = 1.0 [255] -> 128 */
#define SKYCONVRGBATO8888(rgba) (((RwUInt32)(rgba)->blue)  << 16) | \
                                (((RwUInt32)(rgba)->green) <<  8) | \
                                (((RwUInt32)(rgba)->red)        ) | \
                                ((((RwUInt32)(rgba)->alpha) * 8421505) & 0xff000000)
#define SKYCONV8888TORGBA(rgba, pixel) \
                                (rgba)->blue   = (RwUInt8)(pixel >> 16); \
                                (rgba)->green = (RwUInt8)(pixel >> 8); \
                                (rgba)->red  = (RwUInt8)(pixel >> 0); \
                                (rgba)->alpha = (RwUInt8)((pixel & 0xff000000)/8421504)
#else /* A128 */
#define SKYCONVRGBATO8888(rgba) (((RwUInt32)(rgba)->blue)  << 16) | \
                                (((RwUInt32)(rgba)->green) <<  8) | \
                                (((RwUInt32)(rgba)->red)        ) | \
                                (((RwUInt32)(rgba)->alpha) << 24)
#define SKYCONV8888TORGBA(rgba, pixel) \
                                (rgba)->blue   = (RwUInt8)(pixel >> 16); \
                                (rgba)->green = (RwUInt8)(pixel >> 8); \
                                (rgba)->red  = (RwUInt8)(pixel >> 0); \
                                (rgba)->alpha = (RwUInt8)(pixel >> 24)
#endif /* A128 */

#define CONV8BITPALINDEX(x) (((x) & 0xE7) | swapPalIndexBits[((x) >> 3) & 3])

/****************************************************************************
 Globals (across program)
 */

/****************************************************************************
 Local (static) Globals
 */

/* This is a table used to swap two bits in the palette indices */
static const RwInt32 swapPalIndexBits[4] = {0, 0x10, 0x08, 0x18};

/****************************************************************************
 Functions
 */

/****************************************************************************
 _rwSkyRGBToPixel

 On entry   : palette value (OUT)
            : palette entry (IN)
            : 0
 On exit    : TRUE on success
 */
RwBool
_rwSkyRGBToPixel(void *pPixel,void *pCol,RwInt32 nFormat)
{
    RwRGBA *rpCol = (RwRGBA *)pCol;
    RWFUNCTION(RWSTRING("_rwSkyRGBToPixel"));

    /* WARNING: red and blue swapped */
    switch (nFormat & (RwInt32)rwRASTERFORMATPIXELFORMATMASK)
    {
        case rwRASTERFORMAT1555:
        case rwRASTERFORMATDEFAULT:
        {
            *((RwUInt32 *)pPixel) = SKYCONVRGBATO1555(rpCol);
            break;
        }
        case rwRASTERFORMAT8888:
        {
            *((RwUInt32 *)pPixel) = SKYCONVRGBATO8888(rpCol);
            break;
        }
        case rwRASTERFORMAT888:
        {
            *((RwUInt32 *)pPixel) = SKYCONVRGBATO888(rpCol);
            break;
        }
        default:
        {
            RWRETURN(FALSE);
        }
    }

    /* All done */
    RWRETURN(TRUE);
}

/****************************************************************************
 _rwSkyPixelToRGB

 On entry   : Palette entry (OUT)
            : NULL
            : Palette entry (IN)
 On exit    : TRUE on success
 */
RwBool
_rwSkyPixelToRGB(void *pRGB,void *pPixel,RwInt32 nFormat)
{
    RwRGBA *rpRGB = (RwRGBA *)pRGB;
    RwUInt32 nPixel = *(RwInt32 *)pPixel;

    RWFUNCTION(RWSTRING("_rwSkyPixelToRGB"));
    RWASSERT(pPixel);
    RWASSERT(pRGB);

    /* WARNING: red and blue swapped */
    switch (nFormat & (RwInt32)rwRASTERFORMATPIXELFORMATMASK)
    {
        case rwRASTERFORMAT1555:
        case rwRASTERFORMATDEFAULT:
        {
            SKYCONV1555TORGBA(rpRGB, nPixel);
            break;
        }
        case rwRASTERFORMAT8888:
        {
            SKYCONV8888TORGBA(rpRGB, nPixel);
            break;
        }
        case rwRASTERFORMAT888:
        {
            SKYCONV888TORGBA(rpRGB, nPixel);
            break;
        }
        default:
        {
            RWRETURN(FALSE);
        }
    }

    RWRETURN(TRUE);
}

/****************************************************************************
 SkyImageFindFormat

 On entry   : Image
 On exit    : Format
 */
static RwInt32
SkyImageFindFormat(RwImage *image)
{
    rwImageAlphaType alphaType;
    RwInt32 format, modeIndex;
    RwVideoMode mode;

    RWFUNCTION(RWSTRING("SkyImageFindFormat"));
    RWASSERT(image);

    alphaType = rwImageFindAlphaType(image);

    modeIndex = RwEngineGetCurrentVideoMode();
    RwEngineGetVideoModeInfo(&mode, modeIndex);

    format = rwRASTERFORMAT8888;
    if ((mode.depth == 16) && (alphaType != rwIMAGEALPHA_SMOOTH))
    {
        format = rwRASTERFORMAT1555;
    }

    /* Choose 4 bit if the source is 4 bit, else choose 8 bit */
    if (image->depth == 4)
    {
        format |= rwRASTERFORMATPAL4;
    }
    else if (image->depth == 8)
    {
        format |= rwRASTERFORMATPAL8;
    }

    RWRETURN(format);
}

/****************************************************************************
 _rwSkyImageFindRasterFormat

 On entry   : Raster (OUT)
            : Image
            : Flags
 On exit    : TRUE on success
 */
RwBool
_rwSkyImageFindRasterFormat(void *pRaster,void *pImage,RwInt32 flags)
{
    RwRaster  *raster = (RwRaster *)pRaster;
    RwImage   *image = (RwImage *)pImage;
    RwUInt32  mipmapFlag = (flags & (rwRASTERFORMATMIPMAP | rwRASTERFORMATAUTOMIPMAP));

    RWFUNCTION(RWSTRING("_rwSkyImageFindRasterFormat"));

    /* Do the default thing first */
    raster->width = image->width;
    raster->height = image->height;
    raster->depth = 0;         /* Use the default depth for now... */

    raster->cType    = (RwUInt8)(flags & (RwInt32)rwRASTERTYPEMASK);
    raster->cFlags   = (RwUInt8)(flags & (RwInt32)(~rwRASTERTYPEMASK));

    /* The format is already set up */

    /* See whats possible */
    switch (raster->cType)
    {
        case rwRASTERTYPETEXTURE:
        {
            /* Look at the image and decide on a format */
            raster->cFormat = (RwUInt8)(SkyImageFindFormat(image) >> 8);

#if 0
            /* For testing, set mipmap flag */
            mipmapFlag = rwRASTERFORMATMIPMAP;
#endif

            /* Preserve the mipmap option */
            raster->cFormat |= (RwUInt8)(mipmapFlag >> 8);

            /* Well only power2 upto 1024 is good enough for us here */
            raster->width  = 1 << _rwSkyFindMSB(image->width);
            if (raster->width > 1024)
            {
                raster->width = 1024;
            }
            raster->height = 1 << _rwSkyFindMSB(image->height);
            if (raster->height > 1024)
            {
                raster->height = 1024;
            }

            RWRETURN(TRUE);
        }
        case rwRASTERTYPENORMAL:
        case rwRASTERTYPECAMERA:
        case rwRASTERTYPEZBUFFER:
        {
            /* Just take the default case */
            raster->cFormat = (RwUInt8)(rwRASTERFORMATDEFAULT >> 8);

            RWRETURN(TRUE);
        }
        default:
        {
            /* Don't know what one of those is... */

            RWRETURN(FALSE);
        }
    }
}

/****************************************************************************
 _rwSkyImageGetRaster

 For now everything is 1555
 On Sky red is at the bottom

 On entry   : Image (MODIFY)
            : Raster
 On exit    : TRUE on success
 */
RwBool
_rwSkyImageGetRaster(void *pImage,
                     void *pRaster,
                     RwInt32 __RWUNUSED__ nNum)
{
    RwRaster    *raster = (RwRaster *)pRaster;
    RwImage     *image = (RwImage *)pImage;
    RwInt32     x, y;
    RwUInt8     *rasPixels, *imagePixels;
    RwRGBA      *imagePal;
    RwBool      alreadyPixelLocked = FALSE;
    RwBool      alreadyPaletteLocked = FALSE;

    RWFUNCTION(RWSTRING("_rwSkyImageGetRaster"));
    RWASSERT(image);
    RWASSERT(raster);

    if (raster->privateFlags & rwRASTERPIXELLOCKEDREAD)
    {
        alreadyPixelLocked = TRUE;
    }
    if (!alreadyPixelLocked)
    {
        if (!RwRasterLock(raster, 0, rwRASTERLOCKREAD))
        {
            RWRETURN(FALSE);
        }
    }
    if (raster->cFormat & ((rwRASTERFORMATPAL4 | rwRASTERFORMATPAL8) >> 8))
    {
        if (raster->privateFlags & rwRASTERPALETTELOCKEDREAD)
        {
            alreadyPaletteLocked = TRUE;
        }
        if (!alreadyPaletteLocked)
        {
            if (!RwRasterLockPalette(raster, rwRASTERLOCKREAD))
            {
                RWRETURN(FALSE);
            }
        }
    }

    /* Copy pixel pointers */
    rasPixels = raster->cpPixels;
    imagePixels = image->cpPixels;
    imagePal = image->palette;

    /* Copy the palette */
    switch (image->depth)
    {
        case 4:
        {
            /* We can only do this if the raster is 4 bit palettised */
            switch (raster->cFormat & (rwRASTERFORMATPAL4>>8))
            {
                case (rwRASTERFORMATPAL4>>8):
                {
                    /* Need to copy across the palette */
                    switch (raster->cFormat & (rwRASTERFORMATPIXELFORMATMASK>>8))
                    {
                        case (rwRASTERFORMAT1555>>8):
                        {
                            RwUInt16 *rasPal = (RwUInt16 *)raster->palette;
                            for (x = 0; x < 16; x++)
                            {
                                RwUInt16 pixel = rasPal[x];
                                SKYCONV1555TORGBA(imagePal, pixel);
                                imagePal++;
                            }
                            break;
                        }
                        case (rwRASTERFORMAT8888>>8):
                        {
                            RwUInt32 *rasPal = (RwUInt32 *)raster->palette;
                            for (x = 0; x < 16; x++)
                            {
                                RwUInt32 pixel = rasPal[x];
                                SKYCONV8888TORGBA(imagePal, pixel);
                                imagePal++;
                            }
                            break;
                        }
                        default:
                        {
                            /* Should never happen */
                            break;
                        }
                    }

                    /* Now copy the pixel data */
                    for (y = 0; y < raster->height; y++)
                    {
                        RwUInt8 *rasLinePixels = rasPixels;
                        RwUInt8 *imageLinePixels = imagePixels;

                        for (x = 0; x < raster->width; x++)
                        {
                            /* Pick the pixel from the correct half */
                            if ((x+raster->nOffsetX) & 1)
                            {
                                *imageLinePixels = (*rasLinePixels >> 4) & 0x0F;
                                rasLinePixels++;
                            }
                            else
                            {
                                *imageLinePixels = *rasLinePixels & 0x0F;
                            }
                            imageLinePixels++;
                        }

                        imagePixels += image->stride;
                        rasPixels += raster->stride;
                    }

                    /* All done */
                    break;
                }
                default:
                {
                    /* Can't remap unpalettised rasters to palettised image */
                    /* Can't remap PAL8 rasters to a depth == 4 image either! */

                    /* Remove the lock on the raster */
                    if (raster->cFormat & ((rwRASTERFORMATPAL4 | rwRASTERFORMATPAL8) >> 8))
                    {
                        if (!alreadyPaletteLocked)
                        {
                            RwRasterUnlockPalette(raster);
                        }
                    }
                    if (!alreadyPixelLocked)
                    {
                        RwRasterUnlock(raster);
                    }
                    RWRETURN(FALSE);
                }
            }
            break;
        }
        case 8:
        {
            /* We can only do this if the raster is 4 or 8 bit palettised */
            switch (raster->cFormat & ((rwRASTERFORMATPAL4|rwRASTERFORMATPAL8)>>8))
            {
                case (rwRASTERFORMATPAL4>>8):
                {
                    /* Need to copy across the palette */
                    switch (raster->cFormat & (rwRASTERFORMATPIXELFORMATMASK>>8))
                    {
                        case (rwRASTERFORMAT1555>>8):
                        {
                            RwUInt16 *rasPal = (RwUInt16 *)raster->palette;
                            for (x = 0; x < 16; x++)
                            {
                                RwUInt16 pixel = rasPal[x];
                                SKYCONV1555TORGBA(imagePal, pixel);
                                imagePal++;
                            }
                            break;
                        }
                        case (rwRASTERFORMAT8888>>8):
                        {
                            RwUInt32 *rasPal = (RwUInt32 *)raster->palette;
                            for (x = 0; x < 16; x++)
                            {
                                RwUInt32 pixel = rasPal[x];
                                SKYCONV8888TORGBA(imagePal, pixel);
                                imagePal++;
                            }
                            break;
                        }
                        default:
                        {
                            /* Should never happen */
                            break;
                        }
                    }

                    /* Now copy the pixel data */
                    for (y = 0; y < raster->height; y++)
                    {
                        RwUInt8 *rasLinePixels = rasPixels;
                        RwUInt8 *imageLinePixels = imagePixels;

                        for (x = 0; x < raster->width; x++)
                        {
                            /* Pick the pixel from the correct half */
                            if ((x+raster->nOffsetX) & 1)
                            {
                                *imageLinePixels = (*rasLinePixels >> 4) & 0x0F;
                                rasLinePixels++;
                            }
                            else
                            {
                                *imageLinePixels = *rasLinePixels & 0x0F;
                            }
                            imageLinePixels++;
                        }

                        imagePixels += image->stride;
                        rasPixels += raster->stride;
                    }

                    /* All done */
                    break;
                }
                case (rwRASTERFORMATPAL8>>8):
                {
                    /* Need to copy across the palette */
                    switch (raster->cFormat & (rwRASTERFORMATPIXELFORMATMASK>>8))
                    {
                        case (rwRASTERFORMAT1555>>8):
                        {
                            RwUInt16 *rasPal = (RwUInt16 *)raster->palette;
                            for (x = 0; x < 256; x++)
                            {
                                /* Swap bits 3 and 4 using LUT for weird palette format */
                                RwUInt16 pixel = rasPal[CONV8BITPALINDEX(x)];
                                SKYCONV1555TORGBA(imagePal, pixel);
                                imagePal++;
                            }
                            break;
                        }
                        case (rwRASTERFORMAT8888>>8):
                        {
                            RwUInt32 *rasPal = (RwUInt32 *)raster->palette;
                            for (x = 0; x < 256; x++)
                            {
                                /* Swap bits 3 and 4 using LUT for weird palette format */
                                RwUInt32 pixel = rasPal[CONV8BITPALINDEX(x)];
                                SKYCONV8888TORGBA(imagePal, pixel);
                                imagePal++;
                            }
                            break;
                        }
                        default:
                        {
                            /* Should never happen */
                            break;
                        }
                    }

                    /* Now copy the pixel data */
                    for (y = 0; y < raster->height; y++)
                    {
                        memcpy(imagePixels, rasPixels, raster->width * sizeof(RwUInt8));
                        imagePixels += image->stride;
                        rasPixels += raster->stride;
                    }

                    /* All done */
                    break;
                }
                default:
                {
                    /* Can't remap unpalettised rasters to palettised image */

                    /* Remove the lock on the raster */
                    if (raster->cFormat & ((rwRASTERFORMATPAL4 | rwRASTERFORMATPAL8) >> 8))
                    {
                        if (!alreadyPaletteLocked)
                        {
                            RwRasterUnlockPalette(raster);
                        }
                    }
                    if (!alreadyPixelLocked)
                    {
                        RwRasterUnlock(raster);
                    }
                    RWRETURN(FALSE);
                }
            }
            break;
        }
        case 32:
        {
            /* 32 bit image */
            for (y = 0; y < raster->height; y++)
            {
                RwRGBA *imageLinePixels = (RwRGBA *)imagePixels;

                switch (raster->cFormat &
                        ((rwRASTERFORMATPIXELFORMATMASK|rwRASTERFORMATPAL4|rwRASTERFORMATPAL8)>>8))
                {
                    case ((rwRASTERFORMATDEFAULT|rwRASTERFORMATPAL4)>>8):
                    case ((rwRASTERFORMAT1555|rwRASTERFORMATPAL4)>>8):
                    {
                        /* We can copy into the image, no problem, just do the palette lookup */
                        RwUInt16 *rasPal = (RwUInt16 *)raster->palette;
                        RwUInt8  *rasLinePixels = (RwUInt8 *)rasPixels;
                        for (x = 0; x < raster->width; x++)
                        {
                            RwUInt16 pixel;
                            if ((x + raster->nOffsetX) & 1)
                            {
                                pixel = (*rasLinePixels >> 4) & 0xF;
                                rasLinePixels++;
                            }
                            else
                            {
                                pixel = *rasLinePixels & 0xF;
                            }
                            pixel = rasPal[pixel];
                            SKYCONV1555TORGBA(imageLinePixels, pixel);
                            imageLinePixels++;
                        }
                        break;
                    }
                    case ((rwRASTERFORMAT8888|rwRASTERFORMATPAL4)>>8):
                    {
                        /* We can copy into the image, no problem, just do the palette lookup */
                        RwUInt32 *rasPal = (RwUInt32 *)raster->palette;
                        RwUInt8  *rasLinePixels = (RwUInt8 *)rasPixels;
                        for (x = 0; x < raster->width; x++)
                        {
                            RwUInt32 pixel;
                            if ((x + raster->nOffsetX) & 1)
                            {
                                pixel = (*rasLinePixels >> 4) & 0xF;
                                rasLinePixels++;
                            }
                            else
                            {
                                pixel = *rasLinePixels & 0xF;
                            }
                            pixel = rasPal[pixel];
                            SKYCONV8888TORGBA(imageLinePixels, pixel);
                            imageLinePixels++;
                        }
                        break;
                    }
                    case ((rwRASTERFORMATDEFAULT|rwRASTERFORMATPAL8)>>8):
                    case ((rwRASTERFORMAT1555|rwRASTERFORMATPAL8)>>8):
                    {
                        /* We can copy into the image, no problem, just do the palette lookup */
                        RwUInt16 *rasPal = (RwUInt16 *)raster->palette;
                        RwUInt8  *rasLinePixels = (RwUInt8 *)rasPixels;
                        for (x = 0; x < raster->width; x++)
                        {
                            RwUInt16 pixel = *rasLinePixels++;
                            pixel = rasPal[CONV8BITPALINDEX(pixel)];
                            SKYCONV1555TORGBA(imageLinePixels, pixel);
                            imageLinePixels++;
                        }
                        break;
                    }
                    case ((rwRASTERFORMAT8888|rwRASTERFORMATPAL8)>>8):
                    {
                        /* We can copy into the image, no problem, just do the palette lookup */
                        RwUInt32 *rasPal = (RwUInt32 *)raster->palette;
                        RwUInt8  *rasLinePixels = (RwUInt8 *)rasPixels;
                        for (x = 0; x < raster->width; x++)
                        {
                            RwUInt32 pixel = *rasLinePixels++;
                            pixel = rasPal[CONV8BITPALINDEX(pixel)];
                            SKYCONV8888TORGBA(imageLinePixels, pixel);
                            imageLinePixels++;
                        }
                        break;
                    }
                    case (rwRASTERFORMATDEFAULT>>8):
                    case (rwRASTERFORMAT1555>>8):
                    {
                        RwUInt16 *rasLinePixels = (RwUInt16 *)rasPixels;
                        for (x = 0; x < raster->width; x++)
                        {
                            RwUInt16 pixel = *rasLinePixels++;
                            SKYCONV1555TORGBA(imageLinePixels, pixel);
                            imageLinePixels++;
                        }
                        break;
                    }
                    case (rwRASTERFORMAT888>>8):
                    {
                        RwUInt8 *rasLinePixels = (RwUInt8 *)rasPixels;
                        for (x = 0; x < raster->width; x++)
                        {
                            RwUInt32 pixel;

                            pixel = (((RwUInt32)rasLinePixels[0])      ) |
                                    (((RwUInt32)rasLinePixels[1]) <<  8) |
                                    (((RwUInt32)rasLinePixels[2]) << 16);
                            rasLinePixels += 3;

                            SKYCONV888TORGBA(imageLinePixels, pixel);
                            imageLinePixels++;
                        }
                        break;
                    }
                    case (rwRASTERFORMAT8888>>8):
                    {
                        RwUInt32 *rasLinePixels = (RwUInt32 *)rasPixels;
                        for (x = 0; x < raster->width; x++)
                        {
                            RwUInt32 pixel = *rasLinePixels++;
                            SKYCONV8888TORGBA(imageLinePixels, pixel);
                            imageLinePixels++;
                        }
                        break;
                    }
                    default:
                    {
                        /* Don't recognise this one */

                        /* Remove the lock on the raster */
                        if (raster->cFormat & ((rwRASTERFORMATPAL4 | rwRASTERFORMATPAL8) >> 8))
                        {
                            if (!alreadyPaletteLocked)
                            {
                                RwRasterUnlockPalette(raster);
                            }
                        }
                        if (!alreadyPixelLocked)
                        {
                            RwRasterUnlock(raster);
                        }
                        RWRETURN(FALSE);
                    }
                }

                imagePixels += image->stride;
                rasPixels += raster->stride;
            }

            break;
        }
        default:
        {
            /* Can't do any others */

            /* Remove the lock on the raster */
            if (raster->cFormat & ((rwRASTERFORMATPAL4 | rwRASTERFORMATPAL8) >> 8))
            {
                if (!alreadyPaletteLocked)
                {
                    RwRasterUnlockPalette(raster);
                }
            }
            if (!alreadyPixelLocked)
            {
                RwRasterUnlock(raster);
            }
            RWRETURN(FALSE);
        }
    }

    /* Remove the lock on the raster */
    if (raster->cFormat & ((rwRASTERFORMATPAL4 | rwRASTERFORMATPAL8) >> 8))
    {
        if (!alreadyPaletteLocked)
        {
            RwRasterUnlockPalette(raster);
        }
    }
    if (!alreadyPixelLocked)
    {
        RwRasterUnlock(raster);
    }

    RWRETURN(TRUE);
}

/****************************************************************************
 copyRGBAPalette

 Copy and convert an RGBA palette

 On entry   : raster pointer
            : RGBA palette pointer
 On exit    : None
 */

static void
copyRGBAPalette(RwRaster *raster, RwRGBA *palette)
{
    RwInt32 i;

    RWFUNCTION(RWSTRING("copyRGBAPalette"));
    RWASSERT(raster);
    RWASSERT(palette);

    /* Copy the palette */
    switch (raster->cFormat & ((rwRASTERFORMATPIXELFORMATMASK|rwRASTERFORMATPAL4|rwRASTERFORMATPAL8)>>8))
    {
        case ((rwRASTERFORMATDEFAULT|rwRASTERFORMATPAL4)>>8):
        case ((rwRASTERFORMAT1555|rwRASTERFORMATPAL4)>>8):
        {
            RwUInt16 *rasPal = (RwUInt16 *)raster->palette;
            for (i = 0; i < 16; i++)
            {
                const RwRGBA *color = &palette[i];
                *rasPal++ = SKYCONVRGBATO1555(color);
            }
            rasPal = (RwUInt16 *)raster->palette;
            //SyncDCache(rasPal, SCESYNCDCACHEROUNDUP(rasPal + 16));
            break;
        }
        case ((rwRASTERFORMAT8888|rwRASTERFORMATPAL4)>>8):
        {
            RwUInt32 *rasPal = (RwUInt32 *)raster->palette;
            for (i = 0; i < 16; i++)
            {
                const RwRGBA *color = &palette[i];
                *rasPal++ = SKYCONVRGBATO8888(color);
            }
            rasPal = (RwUInt32 *)raster->palette;
            //SyncDCache(rasPal, SCESYNCDCACHEROUNDUP(rasPal + 16));
            break;
        }
        case ((rwRASTERFORMATDEFAULT|rwRASTERFORMATPAL8)>>8):
        case ((rwRASTERFORMAT1555|rwRASTERFORMATPAL8)>>8):
        {
            RwUInt16 *rasPal = (RwUInt16 *)raster->palette;
            for (i = 0; i < 256; i++)
            {
                const RwRGBA *color = &palette[CONV8BITPALINDEX(i)];
                *rasPal++ = SKYCONVRGBATO1555(color);
            }
            rasPal = (RwUInt16 *)raster->palette;
            //SyncDCache(rasPal, SCESYNCDCACHEROUNDUP(rasPal + 256));
            break;
        }
        case ((rwRASTERFORMAT8888|rwRASTERFORMATPAL8)>>8):
        {
            RwUInt32 *rasPal = (RwUInt32 *)raster->palette;
            for (i = 0; i < 256; i++)
            {
                const RwRGBA *color = &palette[CONV8BITPALINDEX(i)];
                *rasPal++ = SKYCONVRGBATO8888(color);
            }
            rasPal = (RwUInt32 *)raster->palette;
            //SyncDCache(rasPal, SCESYNCDCACHEROUNDUP(rasPal + 256));
            break;
        }
        default:
        {
            /* Should never happen */
            break;
        }
    }

    RWRETURNVOID();
}

/****************************************************************************
 SkyRasterPalQuantImage

 Set a non mipmapped palettised raster from an image

 SKY has red at the bottom, green in the middle and blue at the top

 On entry   : Raster (MODIFY)
            : Image
 On exit    : TRUE on success
 */

static RwBool
SkyRasterPalQuantImage(RwRaster *raster, RwImage *image)
{
    RwUInt8     *rasPixels;
    RwInt32     palSize;
    RwBool      result = FALSE;
    rwPalQuant  palQuant;

    RWFUNCTION(RWSTRING("SkyRasterPalQuantImage"));
    RWASSERT(raster);
    RWASSERT(image);


    palSize = 1 << RwRasterGetDepth(raster);

    /* Setup the color quantisation structure */
    if (rwPalQuantInit(&palQuant))
    {
		RwRGBA palette[256];

        /* This is the image we need to generate a palette for */
        rwPalQuantAddImage(&palQuant, image, 1.0f);

        /* Generate the palette */
        rwPalQuantResolvePalette(palette, palSize, &palQuant);

        /* Generate the palettised image now */
        rasPixels = (RwUInt8 *)raster->cpPixels;
        rwPalQuantMatchImage(rasPixels, raster->stride, raster->depth, TRUE, &palQuant, image);
        //SyncDCache(rasPixels, SCESYNCDCACHEROUNDUP(rasPixels + (raster->stride*raster->height)));

        /* Now copy the palette across */
        copyRGBAPalette(raster, palette);

        rwPalQuantTerm(&palQuant);
        result = TRUE;
    }

    RWRETURN(result);
}

static RwBool
SkyRasterSetImageNormalPalettised(RwRaster *raster, RwImage *image)
{
    RwUInt8     *rasPixels;
    RwUInt8     *imagePixels;
    RwRGBA      *imagePal;
    RwBool      result = TRUE;
    RwInt32     x;
    RwInt32     y;

    RWFUNCTION(RWSTRING("SkyRasterSetImageNormalPalettised"));
    RWASSERT(raster);
    RWASSERT(image);


    rasPixels = raster->cpPixels;
    imagePixels = image->cpPixels;
    imagePal = image->palette;

    switch (raster->cFormat & ((rwRASTERFORMATPAL4 | rwRASTERFORMATPAL8) >> 8))
    {
    case (rwRASTERFORMATPAL4 >> 8):
        {
            switch (image->depth)
            {
            case 4:
                {
                    /* copying the pixels */
                    for (y = 0; y < image->height; y++)
                    {
                        RwUInt8 *rasLinePixels = rasPixels;
                        RwUInt8 *imageLinePixels = imagePixels;

                        for (x = 0; x < image->width; x++)
                        {
                            if ((x+raster->nOffsetX) & 1)
                            {
                                *rasLinePixels &= 0x0F;
                                *rasLinePixels |= ((*imageLinePixels) & 0x0F) << 4;
                                rasLinePixels++;
                            }
                            else
                            {
                                *rasLinePixels &= 0xF0;
                                *rasLinePixels |= (*imageLinePixels) & 0x0F;
                            }

                            imageLinePixels++;
                        }
                        imagePixels += image->stride;
                        rasPixels += raster->stride;
                    }
                    copyRGBAPalette(raster, imagePal);
                    break;
                }
            case 8:
            case 32:
                {
                    result = SkyRasterPalQuantImage(raster, image);

                    break;
                }
            default:
                {
                    result = FALSE;

                    break;
                }
            }
            break;
        }
    case (rwRASTERFORMATPAL8 >> 8):
        {
            switch (image->depth)
            {
            case 4:
                {
                    /* copying the pixels */
                    for (y = 0; y < image->height; y++)
                    {
                        memcpy(rasPixels, imagePixels, image->width * sizeof(RwUInt8));
                        imagePixels += image->stride;
                        rasPixels += raster->stride;
                    }
                    copyRGBAPalette(raster, imagePal);
                    break;
                }
            case 8:
                {
                    /* copying the pixels */
                    for (y = 0; y < image->height; y++)
                    {
                        memcpy(rasPixels, imagePixels, image->width * sizeof(RwUInt8));
                        imagePixels += image->stride;
                        rasPixels += raster->stride;
                    }
                    copyRGBAPalette(raster, imagePal);
                    break;
                }
            case 32:
                {
                    result = SkyRasterPalQuantImage(raster, image);

                    break;
                }
            default:
                {
                    result = FALSE;

                    break;
                }
            }
            break;
        }
    default:
        {
            result = FALSE;
            break;
        }
    }

    RWRETURN(result);
}

/****************************************************************************
 SkyRasterSetImageNormal

 Set a non mipmapped raster

 SKY has red at the bottom, green in the middle and blue at the top

 On entry   : Raster (MODIFY)
            : Image
 On exit    : TRUE on success
 */
static RwBool
SkyRasterSetImageNormal(RwRaster *raster, RwImage *image)
{
    RwInt32     y, x;
    RwUInt8     *rasPixels = (RwUInt8 *)raster->cpPixels;
    RwUInt8     *imagePixels = (RwUInt8 *) image->cpPixels;

    RWFUNCTION(RWSTRING("SkyRasterSetImageNormal"));
    RWASSERT(raster);
    RWASSERT(image);


    switch (image->depth)
    {
        case 4:
        case 8:
        {
            RwRGBA *rpPal = image->palette;

            /* Its a 4 or 8 bit image */
            for (y = 0; y < raster->height; y++)
            {
                switch (raster->cFormat & ((rwRASTERFORMATPIXELFORMATMASK|rwRASTERFORMATPAL4|rwRASTERFORMATPAL8)>>8))
                {
                    case (rwRASTERFORMATDEFAULT>>8):
                    case (rwRASTERFORMAT1555>>8):
                    {
                        RwUInt16 *npOut = (RwUInt16 *)rasPixels;

                        for (x = 0; x < raster->width; x++)
                        {
                            RwRGBA *rpIn = &rpPal[imagePixels[x]];
                            *npOut++ = SKYCONVRGBATO1555(rpIn);
                        }
                        break;
                    }
                    case (rwRASTERFORMAT888>>8):
                    {
                        RwUInt8 *npOut = (RwUInt8 *)rasPixels;

                        for (x = 0; x < raster->width; x++)
                        {
                            RwRGBA *rpIn = &rpPal[imagePixels[x]];
                            RwUInt32 pixel = SKYCONVRGBATO888(rpIn);
                            *npOut++ = (pixel      ) & 0xFF;
                            *npOut++ = (pixel >>  8) & 0xFF;
                            *npOut++ = (pixel >> 16) & 0xFF;
                        }
                        break;
                    }
                    case (rwRASTERFORMAT8888>>8):
                    {
                        RwUInt32 *npOut = (RwUInt32 *)rasPixels;

                        for (x = 0; x < raster->width; x++)
                        {
                            RwRGBA *rpIn = &rpPal[imagePixels[x]];
                            *npOut++ = SKYCONVRGBATO8888(rpIn);
                        }
                        break;
                    }
                    default:
                    {
                        RWRETURN(FALSE);
                    }
                }

                //SyncDCache( rasPixels, SCESYNCDCACHEROUNDUP( rasPixels + raster->stride ) );

                rasPixels += raster->stride;
                imagePixels += image->stride;
            }

            break;
        }
        case 32:
        {
            /* Its a 32 bit image */

            for (y = 0; y < raster->height; y++)
            {
                RwRGBA *rpIn = (RwRGBA *)imagePixels;

                switch (raster->cFormat & ((rwRASTERFORMATPIXELFORMATMASK|rwRASTERFORMATPAL4|rwRASTERFORMATPAL8)>>8))
                {
                    case (rwRASTERFORMATDEFAULT>>8):
                    case (rwRASTERFORMAT1555>>8):
                    {
                        RwUInt16 *npOut = (RwUInt16 *)rasPixels;

                        for (x=0;x<raster->width;x++)
                        {
                            *npOut++ = SKYCONVRGBATO1555(rpIn);
                            rpIn++;
                        }
                        break;
                    }
                    case (rwRASTERFORMAT888>>8):
                    {
                        RwUInt8 *npOut = (RwUInt8 *)rasPixels;

                        for (x=0;x<raster->width;x++)
                        {
                            RwUInt32 pixel = SKYCONVRGBATO888(rpIn);
                            *npOut++ = (pixel      ) & 0xFF;
                            *npOut++ = (pixel >>  8) & 0xFF;
                            *npOut++ = (pixel >> 16) & 0xFF;
                            rpIn++;
                        }
                        break;
                    }
                    case (rwRASTERFORMAT8888>>8):
                    {
                        RwUInt32 *npOut = (RwUInt32 *)rasPixels;

                        for (x=0;x<raster->width;x++)
                        {
                            *npOut++ = SKYCONVRGBATO8888(rpIn);
                            rpIn++;
                        }
                        break;
                    }
                    default:
                    {
                        RWRETURN(FALSE);
                    }
                }
                rasPixels += raster->stride;
                imagePixels += image->stride;
            }

            break;
        }
        default:
        {
            RWRETURN(FALSE);
        }
    }

    RWRETURN(TRUE);
}

/****************************************************************************
 _rwSkyRasterSetImage

 The raster is only set if it has a valid pixel pointer

 On entry   : Raster (MODIFY)
            : Image
            : Flags
 On exit    : TRUE on success
 */
RwBool
_rwSkyRasterSetImage(void *pRaster,void *pImage,RwInt32 __RWUNUSED__ flags)
{
    RwRaster    *raster = (RwRaster *)pRaster;
    RwBool      success =  FALSE;
    RwUInt8     mode;
    RwBool      alreadyPixelLocked = FALSE;
    RwBool      alreadyPaletteLocked = FALSE;

    RWFUNCTION(RWSTRING("_rwSkyRasterSetImage"));

    if (raster->privateFlags & rwRASTERPIXELLOCKEDWRITE)
    {
        alreadyPixelLocked = TRUE;
    }
    if (!alreadyPixelLocked)
    {
        if (!RwRasterLock(raster, 0, rwRASTERLOCKWRITE))
        {
            RWRETURN(FALSE);
        }
    }
    if (raster->cFormat & ((rwRASTERFORMATPAL4 | rwRASTERFORMATPAL8) >> 8))
    {
        if (raster->privateFlags & rwRASTERPALETTELOCKEDWRITE)
        {
            alreadyPaletteLocked = TRUE;
        }
        if (!alreadyPaletteLocked)
        {
            if (!RwRasterLockPalette(raster, rwRASTERLOCKWRITE))
            {
                RWRETURN(FALSE);
            }
        }
    }

    /* Mip levels are set by a callback when the raster is unlocked if
     * rwRASTERFORMATAUTOMIPMAP is set .. otherwise it is up to the user
     * to set the mip levels explicitly. We assume for release that they
     * will pre-generate and stream in all the mip-maps. */
    mode = raster->cFormat & (((RwInt32)rwRASTERFORMATPAL4 | (RwInt32)rwRASTERFORMATPAL8) >> 8);
    switch (mode)
    {
    case ((rwRASTERFORMATPAL4) >> 8):
    case ((rwRASTERFORMATPAL8) >> 8):
        {
            success = SkyRasterSetImageNormalPalettised(raster, (RwImage *)pImage);
            break;
        }
    default:
        {
            success = SkyRasterSetImageNormal(raster, (RwImage *)pImage);
            break;
        }
    }

    if (raster->cFormat & ((rwRASTERFORMATPAL4 | rwRASTERFORMATPAL8) >> 8))
    {
        if (!alreadyPaletteLocked)
        {
            RwRasterUnlockPalette(raster);
        }
    }
    if (!alreadyPixelLocked)
    {
        RwRasterUnlock(raster);
    }

    RWRETURN(success);
}

