Hi Robert,

Attachment is the implementation of the writing operation of the TGA
format. I wrote it just for one of my client. At present it only
outputs uncompressed RGBA images, but the OSG community can go deeper
at any time.

I'd like to continue working on the improvment of osgParticle and hope
it can catch up with the coming 3.0. :)

Cheers,

Wang Rui
#include <osg/Image>
#include <osg/Notify>
#include <osg/Geode>
#include <osg/GL>

#include <osgDB/Registry>
#include <osgDB/FileNameUtils>
#include <osgDB/FileUtils>
#include <osgDB/fstream>

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

/****************************************************************************
 *
 * Follows is code extracted from the simage library.  Original Authors:
 *
 *      Systems in Motion,
 *      <URL:http://www.sim.no>
 *
 *      Peder Blekken <[email protected]>
 *      Morten Eriksen <[email protected]>
 *      Marius Bugge Monsen <[email protected]>
 *
 * The original COPYING notice
 *
 *      All files in this library are public domain, except simage_rgb.cpp 
which is
 *      Copyright (c) Mark J Kilgard <[email protected]>. I will contact Mark
 *      very soon to hear if this source also can become public domain.
 *
 *      Please send patches for bugs and new features to: <[email protected]>.
 *
 *      Peder Blekken
 *
 *
 * Ported into the OSG as a plugin, Robert Osfield Decemeber 2000.
 * Note, reference above to license of simage_rgb is not relevent to the OSG
 * as the OSG does not use it.  Also for patches, bugs and new features
 * please send them direct to the OSG dev team rather than address above.
 *
 **********************************************************************/

/* */
/* Supported types: */
/*   */
/*  1 (Uncompressed, color-mapped images) */
/*  2 (RGB uncompressed) */
/*  9 RLE color-mapped */
/* 10 RLE RGB */
/* */

#define ERR_NO_ERROR     0
#define ERR_OPEN         1
#define ERR_READ         2
#define ERR_MEM          3
#define ERR_UNSUPPORTED  4

static int tgaerror = ERR_NO_ERROR;
int
simage_tga_error(char * buffer, int buflen)
{
    switch (tgaerror)
    {
        case ERR_OPEN:
            strncpy(buffer, "TGA loader: Error opening file", buflen);
            break;
        case ERR_READ:
            strncpy(buffer, "TGA loader: Error reading file", buflen);
            break;
        case ERR_MEM:
            strncpy(buffer, "TGA loader: Out of memory error", buflen);
            break;
    }
    return tgaerror;
}


/* TODO: */
/* - bottom-up images */
/* - huffman, delta encoding */
static void
convert_16_to_24(const unsigned char * const src, unsigned char * const dest)
{
    /* RGB for opengl, lo-hi 16 bit for TGA */
    unsigned int t0 = src[0];
    unsigned int t1 = src[1];
                                 /* r */
    dest[0] = (unsigned char) (t0 & 0x1f) << 2;
                                 /* g */
    dest[1] = (unsigned char) (t1 & 0x7c) >> 2;
                                 /*b */
    dest[2] = (unsigned char) ((t1 & 0x3)<<3) | ((t0&0xe)>>5);
}


static void
convert_16_to_32(const unsigned char * const src, unsigned char * const dest)
{
    /* RGBA for opengl, lo-hi 16 bit for TGA */
    unsigned int t0 = src[0];
    unsigned int t1 = src[1];
                                 /* r */
    dest[0] = (unsigned char) (t0 & 0x1f) << 2;
                                 /* g */
    dest[1] = (unsigned char) (t1 & 0x7c) >> 2;
                                 /*b */
    dest[2] = (unsigned char) ((t1 & 0x3)<<3) | ((t0&0xe)>>5);
    dest[3] = (t1&0x70)?255:0;   /* a */
}


static void
convert_24_to_24(const unsigned char * const src, unsigned char * const dest)
{
    /* RGB for opengl */
    /* BGR for TGA */
    dest[0] = src[2];
    dest[1] = src[1];
    dest[2] = src[0];
}


static void
convert_32_to_32(const unsigned char * const src, unsigned char * const dest)
{
    /* opengl image format is RGBA, not ARGB */
    /* TGA image format is BGRA for 32 bit */
    dest[0] = src[2];
    dest[1] = src[1];
    dest[2] = src[0];
    dest[3] = src[3];
}


static void
convert_data(const unsigned char * const src, unsigned char * const dest,
const int x, const int srcformat,
const int destformat)
{
    if (srcformat == 2)
    {
        if (destformat == 3)
            convert_16_to_24(src+x*srcformat,
                dest+x*destformat);
        else
        {
            assert(destformat == 4);
            convert_16_to_32(src+x*srcformat,
                dest+x*destformat);
        }
    }
    else if (srcformat == 3)
    {
        assert(destformat == 3);
        convert_24_to_24(src+x*srcformat,
            dest+x*destformat);
    }
    else
    {
        assert(srcformat == 4 && destformat == 4);
        convert_32_to_32(src+x*srcformat,
            dest+x*destformat);
    }
}


/* Intel byte order workaround */
static int getInt16(unsigned char *ptr)
{
    int res = ptr[0];
    int tmp = ptr[1];
    return res | (tmp<<8);
}


/* */
/* decode a new rle packet */
/* */
static void
rle_new_packet(unsigned char ** src,
int * rleRemaining,
int * rleIsCompressed,
unsigned char *rleCurrent,
const int rleEntrySize)
{
    int i;
    unsigned char code = *(*src)++;
                                 /* number of bytes left in this packet */
    *rleRemaining = (code & 127) + 1;
    if (code & 128)              /* rle */
    {
        *rleIsCompressed = 1;
        for (i = 0; i < rleEntrySize; i++)
            rleCurrent[i] = *(*src)++;
    }
    else                         /* uncompressed */
    {
        *rleIsCompressed = 0;
    }
}


/* */
/* decode the # of specified bytes */
/* */
static void
rle_decode(unsigned char ** src,
unsigned char *dest,
const int numbytes,
int * rleRemaining,
int * rleIsCompressed,
unsigned char *rleCurrent,
const int rleEntrySize)
{
    int i;
    int size = rleEntrySize;
    unsigned char *stop = dest + numbytes;
    while (dest < stop)
    {
        if (*rleRemaining == 0)  /* start new packet */
            rle_new_packet(src, rleRemaining, rleIsCompressed,
                rleCurrent, rleEntrySize);

        if (*rleIsCompressed)
        {
            for (i = 0; i < size; i++)
            {
                *dest++ = rleCurrent[i];
            }
        }
        else
        {
            for (i = 0; i < size; i++)
            {
                *dest++ = *(*src)++;
            }
        }
        // original code : *rleRemaining)--;
        (*rleRemaining)--;
    }
}


unsigned char *
simage_tga_load(std::istream& fin,
int *width_ret,
int *height_ret,
int *numComponents_ret)
{
    unsigned char header[18];
    int type;
    int width;
    int height;
    int depth;
    int flags;
    int format;
    unsigned char *colormap;
    int indexsize;
    int rleIsCompressed;
    int rleRemaining;
    int rleEntrySize;
    unsigned char rleCurrent[4];
    unsigned char *buffer;
    unsigned char *dest;
    int bpr;
    unsigned char *linebuf;

    tgaerror = ERR_NO_ERROR;     /* clear error */

    fin.read((char*)header,18);
    if (fin.gcount() != 18)
    {
        tgaerror = ERR_READ;
        return NULL;
    }

    type = header[2];
    width = getInt16(&header[12]);
    height = getInt16(&header[14]);
    depth = header[16] >> 3;
    flags = header[17];

    /* check for reasonable values in case this is not a tga file */
    if ((type != 2 && type != 10) ||
        (width < 0 || width > 4096) ||
        (height < 0 || height > 4096) ||
        (depth < 2 || depth > 4))
    {
        tgaerror = ERR_UNSUPPORTED;
        return NULL;
    }

    if (header[0])               /* skip identification field */
        fin.seekg(header[0],std::ios::cur);

    colormap = NULL;
    if (header[1] == 1)          /* there is a colormap */
    {
        int len = getInt16(&header[5]);
        indexsize = header[7]>>3;
        colormap = new unsigned char [len*indexsize];
        fin.read((char*)colormap,len*indexsize);
    }

    if (depth == 2)              /* 16 bits */
    {
        if (flags & 1) format = 4;
        else format = 3;
    }
    else format = depth;

    /*    SoDebugError::postInfo("simage_tga_load", "TARGA file: %d %d %d %d 
%d\n",  */
    /*               type, width, height, depth, format); */

    rleIsCompressed = 0;
    rleRemaining = 0;
    rleEntrySize = depth;
    buffer = new unsigned char [width*height*format];
    dest = buffer;
    bpr = format * width;
    linebuf = new unsigned char [width*depth];

    //check the intended image orientation
    bool bLeftToRight = (flags&0x10)==0;
    bool bTopToBottom = (flags&0x20)!=0;
    int lineoffset = bTopToBottom ? -bpr : bpr;
    if (bTopToBottom) //move start point to last line in buffer
        dest += (bpr*(height-1));

    switch(type)
    {
        case 1:                  /* colormap, uncompressed */
        {
            /* FIXME: write code */
            /* should never get here because simage_tga_identify returns 0 */
            /* for this filetype */
            /* I need example files in this format to write the code... */
            tgaerror = ERR_UNSUPPORTED;
        }
        break;
        case 2:                  /* RGB, uncompressed */
        {
            int x, y;
            for (y = 0; y < height; y++)
            {
                fin.read((char*)linebuf,width*depth);
                if (fin.gcount() != (std::streamsize) (width*depth))
                {
                    tgaerror = ERR_READ;
                    break;
                }
                for (x = 0; x < width; x++)
                {
                    convert_data(linebuf, dest, bLeftToRight ? x : (width-1) - 
x, depth, format);
                }
                dest += lineoffset;
            }
        }
        break;
        case 9:                  /* colormap, compressed */
        {
            /* FIXME: write code */
            /* should never get here because simage_tga_identify returns 0 */
            /* for this filetype */
            /* I need example files in this format to write the code... */
            tgaerror = ERR_UNSUPPORTED;
        }
        break;
        case 10:                 /* RGB, compressed */
        {
            int size, x, y;
            unsigned char *buf;
            unsigned char *src;
            int pos = fin.tellg();
            fin.seekg(0,std::ios::end);
            size = (int)fin.tellg() - pos;
            fin.seekg(pos,std::ios::beg);
            buf = new unsigned char [size];
            if (buf == NULL)
            {
                tgaerror = ERR_MEM;
                break;
            }
            src = buf;
            fin.read((char*)buf,size);
            if (fin.gcount() != (std::streamsize) size)
            {
                tgaerror = ERR_READ;
                break;
            }
            for (y = 0; y < height; y++)
            {
                rle_decode(&src, linebuf, width*depth, &rleRemaining,
                    &rleIsCompressed, rleCurrent, rleEntrySize);
                assert(src <= buf + size);
                for (x = 0; x < width; x++)
                {
                    convert_data(linebuf, dest,  bLeftToRight ? x : (width-1) - 
x, depth, format);
                }
                dest += lineoffset;
            }
            if (buf) delete [] buf;
        }
        break;
        default:
            tgaerror = ERR_UNSUPPORTED;
    }

    if (linebuf) delete [] linebuf;

    if (tgaerror)
    {
        if (buffer) delete [] buffer;
        return NULL;
    }

    *width_ret = width;
    *height_ret = height;
    *numComponents_ret = format;
    return buffer;
}


int
simage_tga_identify(const char *filename,
const unsigned char *buf,
int headerlen)
{
    char * ptr;
    if (headerlen < 18) return 0;
    ptr = (char *)strrchr(filename, '.');
    if (!ptr) return 0;          /* TGA files must end with .tga|.TGA */

    if (strcmp(ptr, ".tga") && strcmp(ptr, ".TGA")) return 0;

    if (buf[1] == 1 && buf[2] == 1 && buf[17] < 64)
    {
        /*      SoDebugError::postInfo("simage_tga_identify", */
        /*                 "TARGA colormap file: %s\n", filename); */
        return 0;
    }
    if ((buf[1] == 0 || buf[1] == 1) && buf[2] == 2 && buf[17] < 64) return 1;
    if (buf[1] == 1 && buf[2] == 9 && buf[17] < 64)
    {
        /*      SoDebugError::postInfo("simage_tga_identity", */
        /*                 "TARGA RLE and colormap file: %s\n", filename);  */

        /* will soon be supported */
        return 0;
    }
    if ((buf[1] == 0 || buf[1] == 1) && buf[2] == 10 && buf[17] < 64)
    {
        /* RLE and RGB */
        return 1;
    }
    else                         /* unsupported */
    {
        /*      SoDebugError::postInfo("simage_tga_identify", */
        /*                 "Unsupported TARGA type.\n"); */
    }
    /* not a TGA, or not supported type */
    return 0;
}


class ReaderWriterTGA : public osgDB::ReaderWriter
{
    public:
    
        ReaderWriterTGA()
        {
            supportsExtension("tga","Tga Image format");
        }
        
        virtual const char* className() const { return "TGA Image Reader"; }

        ReadResult readTGAStream(std::istream& fin) const
        {
            unsigned char *imageData = NULL;
            int width_ret;
            int height_ret;
            int numComponents_ret;

            imageData = 
simage_tga_load(fin,&width_ret,&height_ret,&numComponents_ret);

            if (imageData==NULL) return ReadResult::FILE_NOT_HANDLED;

            int s = width_ret;
            int t = height_ret;
            int r = 1;

            int internalFormat = numComponents_ret;

            unsigned int pixelFormat =
                numComponents_ret == 1 ? GL_LUMINANCE :
            numComponents_ret == 2 ? GL_LUMINANCE_ALPHA :
            numComponents_ret == 3 ? GL_RGB :
            numComponents_ret == 4 ? GL_RGBA : (GLenum)-1;

            unsigned int dataType = GL_UNSIGNED_BYTE;

            osg::Image* pOsgImage = new osg::Image;
            pOsgImage->setImage(s,t,r,
                internalFormat,
                pixelFormat,
                dataType,
                imageData,
                osg::Image::USE_NEW_DELETE);

            return pOsgImage;

        }

        virtual ReadResult readObject(std::istream& fin,const 
osgDB::ReaderWriter::Options* options =NULL) const
        {
            return readImage(fin, options);
        }

        virtual ReadResult readObject(const std::string& file, const 
osgDB::ReaderWriter::Options* options =NULL) const
        {
            return readImage(file, options);
        }

        virtual ReadResult readImage(std::istream& fin,const Options* =NULL) 
const
        {
            return readTGAStream(fin);
        }

        virtual ReadResult readImage(const std::string& file, const 
osgDB::ReaderWriter::Options* options) const
        {
            std::string ext = osgDB::getLowerCaseFileExtension(file);
            if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;

            std::string fileName = osgDB::findDataFile( file, options );
            if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;

            osgDB::ifstream istream(fileName.c_str(), std::ios::in | 
std::ios::binary);
            if(!istream) return ReadResult::FILE_NOT_HANDLED;
            ReadResult rr = readTGAStream(istream);
            if(rr.validImage()) rr.getImage()->setFileName(file);
            return rr;
        }
        
        bool saveTGAStream(const osg::Image& image, std::ostream& fout) const
        {
            // At present, I will only save the image to unmapped RGB format
            // Other data types can be added soon with different options
            // The format description can be found at:
            // http://local.wasp.uwa.edu.au/~pbourke/dataformats/tga/
            int width = image.s(), height = image.t();
            int numPerPixel = 
image.computeNumComponents(image.getPixelFormat());
            int pixelMultiplier = (image.getDataType()==GL_FLOAT ? 255 : 1);
            const unsigned char* data = image.data();
            if ( !data ) return false;
            
            // Headers
            fout.put(0);  // Identification field size
            fout.put(0);  // Color map type
            fout.put(2);  // Image type
            fout.put(0); fout.put(0);  // Color map origin
            fout.put(0); fout.put(0);  // Color map length
            fout.put(0);  // Color map entry size
            fout.put(0); fout.put(0);  // X origin of image
            fout.put(0); fout.put(0);  // Y origin of image
            fout.put(width&0xff); fout.put((width&0xff00)>>8);  // Width of 
image
            fout.put(height&0xff); fout.put((height&0xff00)>>8);  // Height of 
image
            fout.put(numPerPixel * 8);  // Image pixel size
            fout.put(0);  // Image descriptor
            
            // Data
            for (int y=0; y<height; ++y)
            {
                const unsigned char* ptr = data + y * width * numPerPixel;
                for (int x=0; x<width; ++x)
                {
                    int off = x * numPerPixel;
                    switch ( numPerPixel )
                    {
                    case 3:  // BGR
                        fout.put(ptr[off+2] * pixelMultiplier); 
fout.put(ptr[off+1] * pixelMultiplier);
                        fout.put(ptr[off+0] * pixelMultiplier);
                        break;
                    case 4:  // BGRA
                        fout.put(ptr[off+2] * pixelMultiplier); 
fout.put(ptr[off+1] * pixelMultiplier);
                        fout.put(ptr[off+0] * pixelMultiplier); 
fout.put(ptr[off+3] * pixelMultiplier);
                        break;
                    default:
                        return false;
                    }
                }
            }
            return true;
        }
        
        virtual WriteResult writeImage(const osg::Image& image, std::ostream& 
fout, const Options*) const
        {
            if (saveTGAStream(image, fout))
                return WriteResult::FILE_SAVED;
            else
                return WriteResult::ERROR_IN_WRITING_FILE;
        }
        
        virtual WriteResult writeImage(const osg::Image& image, const 
std::string& fileName, const Options* options) const
        {
            std::string ext = osgDB::getFileExtension(fileName);
            if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;
            
            osgDB::ofstream fout(fileName.c_str(), std::ios::out | 
std::ios::binary);
            if (!fout) return WriteResult::ERROR_IN_WRITING_FILE;
            return writeImage(image, fout, options);
        }
};

// now register with Registry to instantiate the above
// reader/writer.
REGISTER_OSGPLUGIN(tga, ReaderWriterTGA)
_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org

Reply via email to