/*

LoadPicture.txt
by Edward "CGameProgrammer" Resnick (cgp@gdnmail.net)
This file was finished at 7:11 am eastern time, Febuary 4th, 2002.

You may use it however you wish and modify it as you wish, though
credit would be appreciated... or even better, e-mailing me if you
use it. But it is just the code for loading file formats other
people developed, nothing special really, so no big deal.


        The JPEG-loading code is based on the CxImageJPG implementation.
        Credits pasted from ximajpg.h:

PASTE:  CxImageJPG (c) 07/Aug/2001 <ing.davide.pizzolato@libero.it>
PASTE:  Special thanks to Troels Knakkergaard for new features, enhancements and bugfixes

PASTE:  original CImageJPG  and CImageIterator implementation are:
PASTE:  Copyright:  (c) 1995, Alejandro Aguilar Sierra <asierra@servidor.unam.mx>

PASTE:  This software is based in part on the work of the Independent JPEG Group.
PASTE:  Copyright (C) 1991-1998, Thomas G. Lane.


---------------- BEGIN SOURCE CODE -------------------

*/

///////////////////////////////////////
// HEADERS YOU WOULDN'T NORMALLY USE //
///////////////////////////////////////

#include <jpeglib.h>    // The JPEG library
#include <setjmp.h>     // We need this to use the JPEG library.

///////////
// FLAGS //
///////////

#define PICTURE_NONE                0x0000  // No picture loaded

// File Format Flags
#define PICTURE_BITMAP              0x1000  // Bitmap (*.bmp)
#define PICTURE_JPEG                0x2000  // JPEG (*.jpg)
#define PICTURE_TARGA               0x4000  // Targa (*.tga)

// Compression Flags
#define PICTURE_UNCOMPRESSED        0x0100  // Always true with bitmaps, valid for targas
#define PICTURE_COMPRESSED          0x0200  // Always true with jpegs, valid with targas

// Bits-Per-Pixel Flags
#define PICTURE_1BIT                0x0001  // Valid with bitmaps
#define PICTURE_MONOCHROME          0x0001
#define PICTURE_4BIT                0x0002  // Valid with bitmaps
#define PICTURE_16COLORS            0x0002
#define PICTURE_8BIT                0x0004  // Valid with bitmaps, jpegs, and targas
#define PICTURE_256COLORS           0x0004
#define PICTURE_16BIT               0x0008  // Valid with bitmaps
#define PICTURE_HIGHCOLOR           0x0008
#define PICTURE_24BIT               0x0010  // Most common bit depth, valid with bitmaps, jpegs, and targas
#define PICTURE_TRUECOLOR           0x0010
#define PICTURE_32BIT               0x0020  // Valid with bitmaps

// Gray-Scale Flag
#define PICTURE_GRAYSCALE           0x0040  // Valid with jpegs and targas

/////////////////////////
// CLASSES AND STRUCTS //
/////////////////////////

// This is our picture class
struct Picture
{
    int   Flags;    // Example: PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_24BIT
    int   Width;
    int   Height;
    BYTE* RGB;      // The RGB data array. Size is 3*Width*Height. Order is Red-Green-Blue
};

// We need these structs to use the JPEG library.
struct JPGError
{
    jpeg_error_mgr  Public;
    jmp_buf         SetJmp;
};

static void JPEG_OnError ( j_common_ptr JPGInfo )
{
    JPGError*   Error;
    char        Buffer [JMSG_LENGTH_MAX];

    Error = (JPGError*)JPGInfo->err;
    Error->Public.format_message( JPGInfo, Buffer );

    longjmp( Error->SetJmp, 1 );
}

/////////////////
// THE BIG ONE //
/////////////////

/* Note that this function requires as its only parameters
   a pointer to a Stream and, optionally, the size of the picture's
   part of that stream. Stream is a class I wrote, and
   you can modify the function to use either your file
   class, or a FILE*, or a filename, or whatever.

   Picture::Load() uses only four of its functions:

        bool Stream::Input( LPVOID Pointer, int Size )
            This reads Size bytes and puts it in Pointer.

        bool Stream::Seek( int Position, int Flag )
            ::Seek(0,STREAM_BEGINNING) is like fseek( File, 0, SEEK_SET )
            ::Seek(0,STREAM_END) is like fseek( File, 0, SEEK_END )

        int Stream::Position()
            This works like ftell. It returns the caret position.

        int Stream::Size()
            This returns the file size, and is only called if
            you don't specify the file size to Picture::Load().

    So it's pretty easy to integrate this code into your own
    project, I hope.

    You would use this function like this:

    File.Open( STREAM_FILE, STREAM_READ, "Test.jpg" );
    Pic.Load( &File, 0 );
    File.Close( );
*/

bool Picture :: Load ( Stream* File, int PictureSize )
{
//  General-purpose variables
    BYTE                    TryToGetType    [2];
    BYTE                    Palette         [768];
    int                     NumColors;
    int                     CurrPixel;
    int                     Dest;
    int                     Src;
    int                     Front;
    int                     HStart;
    int                     HEnd;
    int                     HDir;

//  Bitmap variables
    BYTE                    BMRed;
    BYTE                    BMGreen;
    BYTE                    BMBlue;
    BYTE                    BMBlank;
    BITMAPFILEHEADER        BMFile;
    BITMAPINFOHEADER        BMInfo;
    int                     BMPadding;
    int                     BMPad;
    BYTE                    BMGroup;
    BYTE                    BMPixel8;
    WORD                    BMPixel16;
    int                     BMScanWidth;

//  JPEG variables
    jpeg_decompress_struct  JPGInfo;
    JPGError                JPGErr;
    JSAMPARRAY              JPGBuffer;
    int                     JPGStride;
    BYTE*                   JPGRed;
    BYTE*                   JPGGreen;
    BYTE*                   JPGBlue;
    BYTE*                   JPGSource;
    FILE*                   JPGTempFile;
    int                     JPGImageSize;
    BYTE*                   JPGImage;
    BYTE*                   JPGPixel;
    BYTE*                   JPGContents;

//  Targa variables
    BYTE                    TGAFile         [26];
    BYTE                    TGAHeader       [18];
    BYTE                    TGAID           [256];
    int                     TGANumColors;
    BYTE                    TGAPacket;
    int                     TGANumPixels;
    BYTE                    TGAScanline     [384];
    BYTE*                   TGAImage;

    Flags = PICTURE_NONE;
    Width = 0;
    Height = 0;
    RGB = NULL;

    if( !FileSize )
        FileSize = File->Size( );

//  Find out where in the file we are. Do this in case it's an archive
//  and the picture might start in the middle somewhere:
    Front = File->Position( );

//  Read the first two bytes to figure out what image this is:
    if( !File->Input( TryToGetType, 2 ) )
    {
//      You will see this alot. When an error occurs, I
//      seek to what was supposed to be the end of the image data,
//      in case the stream is an archive. So the next item in
//      the archive can be loaded.
        File->Seek( Front + FileSize, STREAM_BEGINNING );
        return false;
    }

//  Go back to where we were
    if( !File->Seek( Front, STREAM_BEGINNING ) )
        return false;

    if( TryToGetType[0] == 'B' &&
        TryToGetType[1] == 'M' )
    {
//      The file is a bitmap

        if( !File->Input( &BMFile, sizeof BMFile ) ||
            !File->Input( &BMInfo, sizeof BMInfo ) )
        {
            File->Seek( Front + FileSize, STREAM_BEGINNING );
            return false;
        }

//      Only uncompressed bitmaps are supported by this code
        if( BMInfo.biCompression != BI_RGB )
        {
            File->Seek( Front + FileSize, STREAM_BEGINNING );
            return false;
        }

//      Create the palette now, if we must
        if( BMInfo.biBitCount <= 8 )
        {
            NumColors = 1 << BMInfo.biBitCount;

            for( int C = 0; C < NumColors * 3; C += 3 )
            {
/*              Palette information in bitmaps is stored in BGR_ format.
                That means it's blue-green-red-blank, for each entry.
*/
                if( !File->Input( &BMBlue, 1 ) ||
                    !File->Input( &BMGreen, 1 ) ||
                    !File->Input( &BMRed, 1 ) ||
                    !File->Input( &BMBlank, 1 ) )
                {
                    File->Seek( Front + FileSize, STREAM_BEGINNING );
                    return false;
                }

                Palette[C] = BMRed;
                Palette[C+1] = BMGreen;
                Palette[C+2] = BMBlue;
            }
        }

        Width = BMInfo.biWidth;

        if( BMInfo.biHeight < 0 )
        {
            Height = -BMInfo.biHeight;
            HStart = 0;
            HEnd = Height;
            HDir = 1;
        }
        else
        {
            Height = BMInfo.biHeight;
            HStart = Height - 1;
            HEnd = -1;
            HDir = -1;
        }

        RGB = new BYTE [Width * Height * 3];

        if( BMInfo.biBitCount == 1 )
        {
//          Note that this file is not necessarily grayscale, since it's possible
//          the palette is blue-and-white, or whatever. But of course most image
//          programs only write 1-bit images if they're black-and-white.
            Flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_MONOCHROME;

//          For bitmaps, each scanline is dword-aligned.
            BMScanWidth = (Width+7) >> 3;
            if( BMScanWidth & 3 )
                BMScanWidth += 4 - (BMScanWidth & 3);

            for( int H = HStart; H != HEnd; H += HDir )
            {
                CurrPixel = 0;

                for( int W = 0; W < BMScanWidth; W ++ )
                {
                    if( !File->Input( &BMGroup, 1 ) )
                    {
                        delete [] RGB;
                        File->Seek( Front + FileSize, STREAM_BEGINNING );
                        return false;
                    }

/*                  Now we read the pixels. Usually there are
                    eight pixels per byte, since each pixel
                    is represented by one bit, but if the width
                    is not a multiple of eight, the last byte
                    will have some bits set, with the others just
                    being extra. Plus there's the dword-alignment
                    padding. So we keep checking to see if we've
                    already read "Width" number of pixels.
*/
                    if( CurrPixel < Width )
                    {
                        Dest = 3 * ((H * Width) + CurrPixel);
                        Src = 3 * ((BMGroup & 0x80) >> 7);
                        RGB[Dest] = Palette[Src];
                        RGB[Dest+1] = Palette[Src+1];
                        RGB[Dest+2] = Palette[Src+2];
                        CurrPixel ++;
                    }

                    if( CurrPixel < Width )
                    {
                        Dest = 3 * ((H * Width) + CurrPixel);
                        Src = 3 * ((BMGroup & 0x40) >> 6);
                        RGB[Dest] = Palette[Src];
                        RGB[Dest+1] = Palette[Src+1];
                        RGB[Dest+2] = Palette[Src+2];
                        CurrPixel ++;
                    }

                    if( CurrPixel < Width )
                    {
                        Dest = 3 * ((H * Width) + CurrPixel);
                        Src = 3 * ((BMGroup & 0x20) >> 5);
                        RGB[Dest] = Palette[Src];
                        RGB[Dest+1] = Palette[Src+1];
                        RGB[Dest+2] = Palette[Src+2];
                        CurrPixel ++;
                    }

                    if( CurrPixel < Width )
                    {
                        Dest = 3 * ((H * Width) + CurrPixel);
                        Src = 3 * ((BMGroup & 0x10) >> 4);
                        RGB[Dest] = Palette[Src];
                        RGB[Dest+1] = Palette[Src+1];
                        RGB[Dest+2] = Palette[Src+2];
                        CurrPixel ++;
                    }

                    if( CurrPixel < Width )
                    {
                        Dest = 3 * ((H * Width) + CurrPixel);
                        Src = 3 * ((BMGroup & 0x08) >> 3);
                        RGB[Dest] = Palette[Src];
                        RGB[Dest+1] = Palette[Src+1];
                        RGB[Dest+2] = Palette[Src+2];
                        CurrPixel ++;
                    }

                    if( CurrPixel < Width )
                    {
                        Dest = 3 * ((H * Width) + CurrPixel);
                        Src = 3 * ((BMGroup & 0x04) >> 2);
                        RGB[Dest] = Palette[Src];
                        RGB[Dest+1] = Palette[Src+1];
                        RGB[Dest+2] = Palette[Src+2];
                        CurrPixel ++;
                    }

                    if( CurrPixel < Width )
                    {
                        Dest = 3 * ((H * Width) + CurrPixel);
                        Src = 3 * ((BMGroup & 0x02) >> 1);
                        RGB[Dest] = Palette[Src];
                        RGB[Dest+1] = Palette[Src+1];
                        RGB[Dest+2] = Palette[Src+2];
                        CurrPixel ++;
                    }

                    if( CurrPixel < Width )
                    {
                        Dest = 3 * ((H * Width) + CurrPixel);
                        Src = 3 * (BMGroup & 0x01);
                        RGB[Dest] = Palette[Src];
                        RGB[Dest+1] = Palette[Src+1];
                        RGB[Dest+2] = Palette[Src+2];
                        CurrPixel ++;
                    }
                }
            }
        }
        else if( BMInfo.biBitCount == 4 )
        {
            Flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_4BIT;

//          For bitmaps, each scanline is dword-aligned.
            BMScanWidth = (Width+1) >> 1;
            if( BMScanWidth & 3 )
                BMScanWidth += 4 - (BMScanWidth & 3);

            for( int H = HStart; H != HEnd; H += HDir )
            {
                CurrPixel = 0;

                for( int W = 0; W < BMScanWidth; W ++ )
                {
                    if( !File->Input( &BMGroup, 1 ) )
                    {
                        delete [] RGB;
                        File->Seek( Front + FileSize, STREAM_BEGINNING );
                        return false;
                    }

/*                  Now we read the pixels. Usually there are
                    two pixels per byte, since each pixel
                    is represented by four bits, but if the width
                    is not a multiple of two, the last byte
                    will have only four bits set, with the others just
                    being extra. Plus there's the dword-alignment
                    padding. So we keep checking to see if we've
                    already read "Width" number of pixels.
*/
                    if( CurrPixel < Width )
                    {
                        Dest = 3 * ((H * Width) + CurrPixel);
                        Src = 3 * ((BMGroup & 0xF0) >> 4);
                        RGB[Dest] = Palette[Src];
                        RGB[Dest+1] = Palette[Src+1];
                        RGB[Dest+2] = Palette[Src+2];
                        CurrPixel ++;
                    }

                    if( CurrPixel < Width )
                    {
                        Dest = 3 * ((H * Width) + CurrPixel);
                        Src = 3 * (BMGroup & 0x0F);
                        RGB[Dest] = Palette[Src];
                        RGB[Dest+1] = Palette[Src+1];
                        RGB[Dest+2] = Palette[Src+2];
                        CurrPixel ++;
                    }
                }
            }
        }
        else if( BMInfo.biBitCount == 8 )
        {
            Flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_8BIT;

//          For bitmaps, each scanline is dword-aligned.
            BMScanWidth = Width;
            if( BMScanWidth & 3 )
                BMScanWidth += 4 - (BMScanWidth & 3);

            for( int H = HStart; H != HEnd; H += HDir )
            {
                CurrPixel = 0;

                for( int W = 0; W < BMScanWidth; W ++ )
                {
                    if( !File->Input( &BMPixel8, 1 ) )
                    {
                        delete [] RGB;
                        File->Seek( Front + FileSize, STREAM_BEGINNING );
                        return false;
                    }

                    if( CurrPixel < Width )
                    {
                        Dest = 3 * ((H * Width) + CurrPixel);
                        Src = 3 * BMPixel8;
                        RGB[Dest] = Palette[Src];
                        RGB[Dest+1] = Palette[Src+1];
                        RGB[Dest+2] = Palette[Src+2];
                        CurrPixel ++;
                    }
                }
            }
        }
        else if( BMInfo.biBitCount == 16 )
        {
            Flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_16BIT;

//          For bitmaps, each scanline is dword-aligned.
            BMScanWidth = Width << 1;

            if( BMScanWidth & 3 )
                BMPadding = 4 - (BMScanWidth & 3);
            else
                BMPadding = 0;

            for( int H = HStart; H != HEnd; H += HDir )
            {
                for( int W = 0; W < Width; W ++ )
                {
                    if( !File->Input( &BMPixel16, 2 ) )
                    {
                        delete [] RGB;
                        File->Seek( Front + FileSize, STREAM_BEGINNING );
                        return false;
                    }

/*                  Now we seperate each WORD into the correct RGB values.
                    The highest bit is zero, the next 5 are red, the next
                    5 are green, and the lowest 5 are blue.
*/
                    BMRed = ((BMPixel16 >> 10) & 0x1F) << 3;
                    BMGreen = ((BMPixel16 >> 5) & 0x1F) << 3;
                    BMBlue = (BMPixel16 & 0x1F) << 3;

                    Dest = ((H * Width) + W) << 1;
                    RGB[Dest] = BMRed;
                    RGB[Dest+1] = BMGreen;
                    RGB[Dest+2] = BMBlue;
                }

                if( BMPadding )
                    File->Input( &BMPad, BMPadding );
            }
        }
        else if( BMInfo.biBitCount == 24 )
        {
            Flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_24BIT;

//          For bitmaps, each scanline is dword-aligned.
            BMScanWidth = Width * 3;

            if( BMScanWidth & 3 )
                BMPadding = 4 - (BMScanWidth & 3);
            else
                BMPadding = 0;

            for( int H = HStart; H != HEnd; H += HDir )
            {
                for( int W = 0; W < Width; W ++ )
                {
                    if( !File->Input( &BMBlue, 1 ) ||
                        !File->Input( &BMGreen, 1 ) ||
                        !File->Input( &BMRed, 1 ) )
                    {
                        delete [] RGB;
                        File->Seek( Front + FileSize, STREAM_BEGINNING );
                        return false;
                    }

                    Dest = 3 * ((H * Width) + W);
                    RGB[Dest] = BMRed;
                    RGB[Dest+1] = BMGreen;
                    RGB[Dest+2] = BMBlue;
                }

                if( BMPadding )
                    File->Input( &BMPad, BMPadding );
            }
        }
        else if( BMInfo.biBitCount == 32 )
        {
            Flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_32BIT;

//          Since each pixel is a dword, it is obviously dword-aligned

            for( int H = HStart; H != HEnd; H += HDir )
            {
                for( int W = 0; W < Width; W ++ )
                {
                    if( !File->Input( &BMBlank, 1 ) ||
                        !File->Input( &BMBlue, 1 ) ||
                        !File->Input( &BMGreen, 1 ) ||
                        !File->Input( &BMRed, 1 ) )
                    {
                        delete [] RGB;
                        File->Seek( Front + FileSize, STREAM_BEGINNING );
                        return false;
                    }

                    Dest = 3 * ((H * Width) + W);
                    RGB[Dest] = BMRed;
                    RGB[Dest+1] = BMGreen;
                    RGB[Dest+2] = BMBlue;
                }
            }
        }
        else // We support all possible bit depths, so if the
        {//     code gets here, it's not even a real bitmap.
            delete [] RGB;
            File->Seek( Front + FileSize, STREAM_BEGINNING );
            return false;
        }
    }
    else if( TryToGetType[0] == 0xFF && // These unlikely-looking characters are
             TryToGetType[1] == 0xD8 )  // used to identify JPEGs
    {
/*      Unfortunately, the JPEG library seems to require a FILE*
        passed as a parameter, as far as I can tell. I may be wrong,
        but for now I save the JPEG to a temporary file and load
        from that. This is necessary if it's loaded from a resource,
        for example.
*/
        JPGContents = new BYTE [FileSize];

        if( !File->Input( JPGContents, FileSize ) )
        {
            delete [] JPGContents;
            File->Seek( Front + FileSize, STREAM_BEGINNING );
            return false;
        }

        File->Seek( Front + FileSize, STREAM_BEGINNING );

//      JPGContents now holds the contents of the JPEG.

        JPGInfo.err = jpeg_std_error( &JPGErr.Public );
        JPGErr.Public.error_exit = JPEG_OnError;

        if( setjmp( JPGErr.SetJmp ) )
        {
//          This part of the code is only called when a JPEG
//          library function fails. When it does, it will jump
//          to this point.

            jpeg_destroy_decompress( &JPGInfo );
            File->Seek( Front + FileSize, STREAM_BEGINNING );
            fclose( JPGTempFile );
            return false;
        }

        JPGTempFile = fopen( "__PicLoad.jpg", "wb" ); // Create the temporary File.
        fwrite( JPGContents, 1, FileSize, JPGTempFile );
        fclose( JPGTempFile );

        delete [] JPGContents; // No longer needed
        JPGTempFile = fopen( "__PicLoad.jpg", "rb" );

        jpeg_create_decompress( &JPGInfo );
        jpeg_stdio_src( &JPGInfo, JPGTempFile );
        jpeg_read_header( &JPGInfo, TRUE );
        jpeg_start_decompress( &JPGInfo );

        Width = JPGInfo.image_width;
        Height = JPGInfo.image_height;

        if( JPGInfo.jpeg_color_space == JCS_GRAYSCALE )
        {
            Flags = PICTURE_JPEG | PICTURE_COMPRESSED | PICTURE_8BIT | PICTURE_GRAYSCALE;

            for( int N = 0; N < 256; N ++ )
            {
                Palette[N*3] = N;
                Palette[(N*3)+1] = N;
                Palette[(N*3)+2] = N;
            }
        }
        else
        {
            if( JPGInfo.num_components == 1 )
                Flags = PICTURE_JPEG | PICTURE_COMPRESSED | PICTURE_8BIT;
            else
                Flags = PICTURE_JPEG | PICTURE_COMPRESSED | PICTURE_24BIT;

            if( JPGInfo.quantize_colors && JPGInfo.num_components == 1 )
            {
                JPGRed = JPGInfo.colormap[0];
                JPGGreen = JPGInfo.colormap[1];
                JPGBlue = JPGInfo.colormap[2];

                if( !JPGRed )
                {
                    fclose( JPGTempFile );
                    jpeg_destroy_decompress( &JPGInfo );
                    return false;
                }

                if( !JPGGreen )
                    JPGGreen = JPGRed;
                if( !JPGBlue )
                    JPGBlue = JPGGreen;

                for( int N = 0; N < 256; N ++ )
                {
                    Palette[N*3] = JPGRed[N];
                    Palette[(N*3)+1] = JPGGreen[N];
                    Palette[(N*3)+2] = JPGBlue[N];
                }
            }
        }

        JPGStride = JPGInfo.output_width * JPGInfo.num_components;
        JPGBuffer = (*JPGInfo.mem->alloc_sarray)( (j_common_ptr)&JPGInfo, JPOOL_IMAGE, JPGStride, 1 );

        JPGImageSize = JPGInfo.num_components * Width * Height;
        JPGImage = new BYTE [JPGImageSize];
        JPGPixel = JPGImage;

        while( JPGInfo.output_scanline < JPGInfo.output_height )
        {
            jpeg_read_scanlines( &JPGInfo, JPGBuffer, 1 );

            if( JPGInfo.num_components == 4 &&
                !JPGInfo.quantize_colors )
            {
                JPGSource = JPGBuffer[0];

                for( int X1=0, X2=0; X1 < JPGInfo.num_components * Width && X2 < JPGStride; X1 += 3, X2 += 4 )
                {
                    JPGPixel[X1] = (JPGSource[X2+3] * JPGSource[X2+2]) / 255;
                    JPGPixel[X1+1] = (JPGSource[X2+3] * JPGSource[X2+1]) / 255;
                    JPGPixel[X1+2] = (JPGSource[X2+3] * JPGSource[X2]) / 255;
                }
            }
            else
            {
                if( JPGStride < JPGInfo.num_components * Width )
                    memcpy( JPGPixel, JPGBuffer[0], JPGStride );
                else
                    memcpy( JPGPixel, JPGBuffer[0], JPGInfo.num_components * Width );
            }

            JPGPixel += JPGInfo.num_components * Width;
        }

        jpeg_finish_decompress( &JPGInfo );
        jpeg_destroy_decompress( &JPGInfo );

        if( JPGInfo.num_components == 1 )
        {
            RGB = new BYTE [Width * Height * 3];

            for( int N = 0; N < JPGImageSize; N ++ )
            {
                RGB[N*3] = Palette[JPGImage[N]*3];
                RGB[(N*3)+1] = Palette[(JPGImage[N]*3)+1];
                RGB[(N*3)+2] = Palette[(JPGImage[N]*3)+2];
            }

            delete [] JPGImage;
        }
        else
            RGB = JPGImage;

        fclose( JPGTempFile );
        remove( "__PicLoad.jpg" );
    }
    else
    {
//      The image is either a Targa or unknown

/*      We want to read the last 26 bytes of the File-> We could
        seek to the end, but in the case of archives this is
        not desirable. So we do it this way.
*/
        if( !File->Seek( Front + FileSize - 26, STREAM_BEGINNING ) )
            return false;

        if( !File->Input( TGAFile, 26 ) )
        {
            File->Seek( Front + FileSize, STREAM_BEGINNING );
            return false;
        }

        if( !File->Seek( Front, STREAM_BEGINNING ) )
        {
            File->Seek( Front + FileSize, STREAM_BEGINNING );
            return false;
        }

        if( memcmp( &TGAFile[8], "TRUEVISION-XFILE", 16 ) )
        {
//          The image is not a Targa
            File->Seek( Front + FileSize, STREAM_BEGINNING );
            return false;
        }

//      The image is a Targa

        if( !File->Input( TGAHeader, 18 ) )
        {
            File->Seek( Front + FileSize, STREAM_BEGINNING );
            return false;
        }

        Width = ((WORD*)TGAHeader)[6];
        Height = ((WORD*)TGAHeader)[7];

/*      The ID field is an optional string that you can use to give
        the image a name or description or whatever.
*/
        if( TGAHeader[0] && !File->Input( TGAID, TGAHeader[0] ) )
        {
            File->Seek( Front + FileSize, STREAM_BEGINNING );
            return false;
        }

        if( TGAHeader[2] == 1 )
        {
            Flags = PICTURE_TARGA | PICTURE_UNCOMPRESSED | PICTURE_8BIT;

            TGANumColors = ((WORD*)(TGAHeader+1))[2];

/*          Unlike bitmaps, Targas support palettes that are in other
            bit depths besides 24-bit. But I haven't seen any and I am
            lazy so this code ignores them.
*/
            if( TGAHeader[7] != 24 )
            {
                File->Seek( Front + FileSize, STREAM_BEGINNING );
                return false;
            }

            if( !File->Input( Palette, 3 * TGANumColors ) )
            {
                File->Seek( Front + FileSize, STREAM_BEGINNING );
                return false;
            }

            TGAImage = new BYTE [Width * Height];

            if( !File->Input( TGAImage, Width * Height ) )
            {
                delete [] TGAImage;
                File->Seek( Front + FileSize, STREAM_BEGINNING );
                return false;
            }

            RGB = new BYTE [Width * Height * 3];

            for( int N = 0; N < Width * Height; N ++ )
            {
                Src = 3 * TGAImage[N];

//              The palette is in blue-green-red order, so we read it backwards
                RGB[N*3] = Palette[Src+2];
                RGB[(N*3)+1] = Palette[Src+1];
                RGB[(N*3)+2] = Palette[Src];
            }

            delete [] TGAImage;
        }
        else if( TGAHeader[2] == 2 )
        {
            Flags = PICTURE_TARGA | PICTURE_UNCOMPRESSED | PICTURE_24BIT;

            TGAImage = new BYTE [Width * Height * 3];
            if( !File->Input( TGAImage, Width * Height * 3 ) )
            {
                delete [] TGAImage;
                File->Seek( Front + FileSize, STREAM_BEGINNING );
                return false;
            }

            RGB = new BYTE [Width * Height * 3];

            for( long N = 0; N < Width * Height * 3; N += 3 )
            {
//              The image data is in blue-green-red format, so reverse it
                RGB[N] = TGAImage[N+2];
                RGB[N+1] = TGAImage[N+1];
                RGB[N+2] = TGAImage[N];
            }

            delete [] TGAImage;
        }
        else if( TGAHeader[2] == 3 )
        {
            Flags = PICTURE_TARGA | PICTURE_UNCOMPRESSED | PICTURE_8BIT | PICTURE_GRAYSCALE;

            TGAImage = new BYTE [Width * Height];
            if( !File->Input( TGAImage, Width * Height ) )
            {
                delete [] TGAImage;
                File->Seek( Front + FileSize, STREAM_BEGINNING );
                return false;
            }

            RGB = new BYTE [Width * Height * 3];

            for( long N = 0; N < Width * Height; N ++ )
            {
                RGB[N*3] = TGAImage[N];
                RGB[(N*3)+1] = TGAImage[N];
                RGB[(N*3)+2] = TGAImage[N];
            }

            delete [] TGAImage;
        }

/*      The following TGA formats are RLE encoded. This means the image
        data comes in two types of packets: RLE packets and raw packets.

        RLE Packets: If there is a row of 12 red pixels in a 24-bit
                     image, the RLE packet consists of one byte whose
                     value is 12, and three more bytes whose values are
                     255,0,0. Red=255, Green=0, Blue=0.

        Raw Packets: For parts of the image that don't have nice long rows
                     of the same color, rather than use RLE packets for
                     each individual pixel, it's fastest to do it with raw
                     packets. If, after the last packet read, there is a
                     red pixel, a green pixel, and a yellow pixel, and then
                     a row of blue pixels, the first byte of the raw packet
                     will be 3, and it will be followed by the pixel data
                     for the red, green, and yellow pixels. Then a RLE
                     packet will be written for the blue pixels.

        That's basically the theory behind RLE encoding. The way TGAs use it,
        the highest bit of the first byte of the packet is used to tell whether
        it's a RLE or raw packet. Then the other 7 bits are used for the length
        of the packet pixels. Also packets cannot span several scanlines. So if
        you have a 8x8 image that's pure white, then theoretically you'd be able
        to use one RLE packet for the entire thing. But TGAs don't allow that.
        You'd need 8 RLE packets, one for each scanline.
*/
        else if( TGAHeader[2] == 9 )
        {
            Flags = PICTURE_TARGA | PICTURE_COMPRESSED | PICTURE_8BIT;

            TGANumColors = ((WORD*)(TGAHeader+1))[2];

/*          Unlike bitmaps, Targas support palettes that are in other
            bit depths besides 24-bit. But I haven't seen any and I am
            lazy so this code ignores them.
*/
            if( TGAHeader[7] != 24 )
            {
                File->Seek( Front + FileSize, STREAM_BEGINNING );
                return false;
            }

            if( !File->Input( Palette, 3 * TGANumColors ) )
            {
                File->Seek( Front + FileSize, STREAM_BEGINNING );
                return false;
            }

            TGAImage = new BYTE [Width * Height];

            for( int H = 0; H < Height; H ++ )
            {
                CurrPixel = 0;

                while( CurrPixel < Width )
                {
                    if( !File->Input( &TGAPacket, 1 ) )
                    {
                        File->Seek( Front + FileSize, STREAM_BEGINNING );
                        delete [] TGAImage;
                        return false;
                    }

                    TGANumPixels = 1 + (TGAPacket & 0x7F);

                    if( TGAPacket & 0x80 ) // Run-length packet
                    {
                        if( !File->Input( TGAScanline, 1 ) )
                        {
                            File->Seek( Front + FileSize, STREAM_BEGINNING );
                            delete [] TGAImage;
                            return false;
                        }

                        for( int N = 0; N < TGANumPixels; N ++ )
                        {
                            TGAImage[(H*Width) + CurrPixel] = TGAScanline[0];
                            CurrPixel ++;
                        }
                    }
                    else // Raw packet
                    {
                        if( !File->Input( TGAScanline, TGANumPixels ) )
                        {
                            File->Seek( Front + FileSize, STREAM_BEGINNING );
                            delete [] TGAImage;
                            return false;
                        }

                        for( int N = 0; N < TGANumPixels; N ++ )
                        {
                            TGAImage[(H*Width) + CurrPixel] = TGAScanline[N];
                            CurrPixel ++;
                        }
                    }
                }
            }

            RGB = new BYTE [Width * Height * 3];
            for( int N = 0; N < Width * Height; N ++ )
            {
                Src = 3 * TGAImage[N];

                RGB[N*3] = Palette[Src+2];
                RGB[(N*3)+1] = Palette[Src+1];
                RGB[(N*3)+2] = Palette[Src];
            }

            delete [] TGAImage;
        }
        else if( TGAHeader[2] == 10 )
        {
            Flags = PICTURE_TARGA | PICTURE_COMPRESSED | PICTURE_24BIT;

            TGAImage = new BYTE [Width * Height * 3];

            for( int H = 0; H < Height; H ++ )
            {
                CurrPixel = 0;

                while( CurrPixel < Width * 3 )
                {
                    if( !File->Input( &TGAPacket, 1 ) )
                    {
                        File->Seek( Front + FileSize, STREAM_BEGINNING );
                        delete [] TGAImage;
                        return false;
                    }

                    TGANumPixels = 1 + (TGAPacket & 0x7F);

                    if( TGAPacket & 0x80 ) // Run-length packet
                    {
                        if( !File->Input( TGAScanline, 3 ) )
                        {
                            File->Seek( Front + FileSize, STREAM_BEGINNING );
                            delete [] TGAImage;
                            return false;
                        }

                        for( int N = 0; N < TGANumPixels; N ++ )
                        {
                            TGAImage[(H*Width*3) + CurrPixel] = TGAScanline[0];
                            TGAImage[(H*Width*3) + CurrPixel+1] = TGAScanline[1];
                            TGAImage[(H*Width*3) + CurrPixel+2] = TGAScanline[2];

                            CurrPixel += 3;
                        }
                    }
                    else // Raw packet
                    {
                        if( !File->Input( TGAScanline, TGANumPixels * 3 ) )
                        {
                            File->Seek( Front + FileSize, STREAM_BEGINNING );
                            delete [] TGAImage;
                            return false;
                        }

                        for( int N = 0; N < TGANumPixels * 3; N += 3 )
                        {
                            TGAImage[(H*Width*3) + CurrPixel] = TGAScanline[N];
                            TGAImage[(H*Width*3) + CurrPixel+1] = TGAScanline[N+1];
                            TGAImage[(H*Width*3) + CurrPixel+2] = TGAScanline[N+2];

                            CurrPixel += 3;
                        }
                    }
                }
            }

            RGB = new BYTE [Width * Height * 3];
            for( int N = 0; N < Width * Height * 3; N += 3 )
            {
                RGB[N] = TGAImage[N+2];
                RGB[N+1] = TGAImage[N+1];
                RGB[N+2] = TGAImage[N];
            }

            delete [] TGAImage;
        }
        else if( TGAHeader[2] == 11 )
        {
            Flags = PICTURE_TARGA | PICTURE_COMPRESSED | PICTURE_8BIT | PICTURE_GRAYSCALE;

            TGAImage = new BYTE [Width * Height];

            for( int H = 0; H < Height; H ++ )
            {
                CurrPixel = 0;

                while( CurrPixel < Width )
                {
                    if( !File->Input( &TGAPacket, 1 ) )
                    {
                        File->Seek( Front + FileSize, STREAM_BEGINNING );
                        delete [] TGAImage;
                        return false;
                    }

                    TGANumPixels = 1 + (TGAPacket & 0x7F);

                    if( TGAPacket & 0x80 ) // Run-length packet
                    {
                        if( !File->Input( TGAScanline, 1 ) )
                        {
                            File->Seek( Front + FileSize, STREAM_BEGINNING );
                            delete [] TGAImage;
                            return false;
                        }

                        for( int N = 0; N < TGANumPixels; N ++ )
                        {
                            TGAImage[(H*Width) + CurrPixel] = TGAScanline[0];
                            CurrPixel ++;
                        }
                    }
                    else // Raw packet
                    {
                        if( !File->Input( TGAScanline, TGANumPixels ) )
                        {
                            File->Seek( Front + FileSize, STREAM_BEGINNING );
                            delete [] TGAImage;
                            return false;
                        }

                        for( int N = 0; N < TGANumPixels; N ++ )
                        {
                            TGAImage[(H*Width) + CurrPixel] = TGAScanline[N];
                            CurrPixel ++;
                        }
                    }
                }
            }

            RGB = new BYTE [Width * Height * 3];
            for( int N = 0; N < Width * Height; N ++ )
            {
                RGB[N*3] = TGAImage[N];
                RGB[(N*3)+1] = TGAImage[N];
                RGB[(N*3)+2] = TGAImage[N];
            }

            delete [] TGAImage;
        }
        else
        {
//          Unsupported image type, but we supported all six,
//          so this should never happen.
            File->Seek( Front + FileSize, STREAM_BEGINNING );
            return false;
        }
    }

    return true; // Success! Yay!
}