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
