root/OpenSceneGraph/trunk/src/osgPlugins/tga/ReaderWriterTGA.cpp @ 13041

Revision 13041, 18.1 kB (checked in by robert, 3 years ago)

Ran script to remove trailing spaces and tabs

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1#include <osg/Image>
2#include <osg/Notify>
3#include <osg/Geode>
4#include <osg/GL>
5
6#include <osgDB/Registry>
7#include <osgDB/FileNameUtils>
8#include <osgDB/FileUtils>
9#include <osgDB/fstream>
10
11#include <stdio.h>
12#include <assert.h>
13#include <string.h>
14#include <stdlib.h>
15
16/****************************************************************************
17 *
18 * Follows is code extracted from the simage library.  Original Authors:
19 *
20 *      Systems in Motion,
21 *      <URL:http://www.sim.no>
22 *
23 *      Peder Blekken <pederb@sim.no>
24 *      Morten Eriksen <mortene@sim.no>
25 *      Marius Bugge Monsen <mariusbu@sim.no>
26 *
27 * The original COPYING notice
28 *
29 *      All files in this library are public domain, except simage_rgb.cpp which is
30 *      Copyright (c) Mark J Kilgard <mjk@nvidia.com>. I will contact Mark
31 *      very soon to hear if this source also can become public domain.
32 *
33 *      Please send patches for bugs and new features to: <pederb@sim.no>.
34 *
35 *      Peder Blekken
36 *
37 *
38 * Ported into the OSG as a plugin, Robert Osfield Decemeber 2000.
39 * Note, reference above to license of simage_rgb is not relevent to the OSG
40 * as the OSG does not use it.  Also for patches, bugs and new features
41 * please send them direct to the OSG dev team rather than address above.
42 *
43 **********************************************************************/
44
45/* */
46/* Supported types: */
47/*   */
48/*  1 (Uncompressed, color-mapped images) */
49/*  2 (RGB uncompressed) */
50/*  9 RLE color-mapped */
51/* 10 RLE RGB */
52/* */
53
54#define ERR_NO_ERROR     0
55#define ERR_OPEN         1
56#define ERR_READ         2
57#define ERR_MEM          3
58#define ERR_UNSUPPORTED  4
59
60static int tgaerror = ERR_NO_ERROR;
61int
62simage_tga_error(char * buffer, int buflen)
63{
64    switch (tgaerror)
65    {
66        case ERR_OPEN:
67            strncpy(buffer, "TGA loader: Error opening file", buflen);
68            break;
69        case ERR_READ:
70            strncpy(buffer, "TGA loader: Error reading file", buflen);
71            break;
72        case ERR_MEM:
73            strncpy(buffer, "TGA loader: Out of memory error", buflen);
74            break;
75    }
76    return tgaerror;
77}
78
79
80/* TODO: */
81/* - bottom-up images */
82/* - huffman, delta encoding */
83static void
84convert_16_to_24(const unsigned char * const src, unsigned char * const dest)
85{
86    /* RGB for opengl, lo-hi 16 bit for TGA */
87    unsigned int t0 = src[0];
88    unsigned int t1 = src[1];
89                                 /* r */
90    dest[0] = (unsigned char) (t0 & 0x1f) << 2;
91                                 /* g */
92    dest[1] = (unsigned char) (t1 & 0x7c) >> 2;
93                                 /*b */
94    dest[2] = (unsigned char) ((t1 & 0x3)<<3) | ((t0&0xe)>>5);
95}
96
97
98static void
99convert_16_to_32(const unsigned char * const src, unsigned char * const dest)
100{
101    /* RGBA for opengl, lo-hi 16 bit for TGA */
102    unsigned int t0 = src[0];
103    unsigned int t1 = src[1];
104                                 /* r */
105    dest[0] = (unsigned char) (t0 & 0x1f) << 2;
106                                 /* g */
107    dest[1] = (unsigned char) (t1 & 0x7c) >> 2;
108                                 /*b */
109    dest[2] = (unsigned char) ((t1 & 0x3)<<3) | ((t0&0xe)>>5);
110    dest[3] = (t1&0x70)?255:0;   /* a */
111}
112
113
114static void
115convert_24_to_24(const unsigned char * const src, unsigned char * const dest)
116{
117    /* RGB for opengl */
118    /* BGR for TGA */
119    dest[0] = src[2];
120    dest[1] = src[1];
121    dest[2] = src[0];
122}
123
124
125static void
126convert_32_to_32(const unsigned char * const src, unsigned char * const dest)
127{
128    /* opengl image format is RGBA, not ARGB */
129    /* TGA image format is BGRA for 32 bit */
130    dest[0] = src[2];
131    dest[1] = src[1];
132    dest[2] = src[0];
133    dest[3] = src[3];
134}
135
136
137static void
138convert_data(const unsigned char * const src, unsigned char * const dest,
139const int x, const int srcformat,
140const int destformat)
141{
142    if (srcformat == 2)
143    {
144        if (destformat == 3)
145            convert_16_to_24(src+x*srcformat,
146                dest+x*destformat);
147        else
148        {
149            assert(destformat == 4);
150            convert_16_to_32(src+x*srcformat,
151                dest+x*destformat);
152        }
153    }
154    else if (srcformat == 3)
155    {
156        assert(destformat == 3);
157        convert_24_to_24(src+x*srcformat,
158            dest+x*destformat);
159    }
160    else
161    {
162        assert(srcformat == 4 && destformat == 4);
163        convert_32_to_32(src+x*srcformat,
164            dest+x*destformat);
165    }
166}
167
168
169/* Intel byte order workaround */
170static int getInt16(unsigned char *ptr)
171{
172    int res = ptr[0];
173    int tmp = ptr[1];
174    return res | (tmp<<8);
175}
176
177
178/* */
179/* decode a new rle packet */
180/* */
181static void
182rle_new_packet(unsigned char ** src,
183int * rleRemaining,
184int * rleIsCompressed,
185unsigned char *rleCurrent,
186const int rleEntrySize)
187{
188    int i;
189    unsigned char code = *(*src)++;
190                                 /* number of bytes left in this packet */
191    *rleRemaining = (code & 127) + 1;
192    if (code & 128)              /* rle */
193    {
194        *rleIsCompressed = 1;
195        for (i = 0; i < rleEntrySize; i++)
196            rleCurrent[i] = *(*src)++;
197    }
198    else                         /* uncompressed */
199    {
200        *rleIsCompressed = 0;
201    }
202}
203
204
205/* */
206/* decode the # of specified bytes */
207/* */
208static void
209rle_decode(unsigned char ** src,
210unsigned char *dest,
211const int numbytes,
212int * rleRemaining,
213int * rleIsCompressed,
214unsigned char *rleCurrent,
215const int rleEntrySize)
216{
217    int i;
218    int size = rleEntrySize;
219    unsigned char *stop = dest + numbytes;
220    while (dest < stop)
221    {
222        if (*rleRemaining == 0)  /* start new packet */
223            rle_new_packet(src, rleRemaining, rleIsCompressed,
224                rleCurrent, rleEntrySize);
225
226        if (*rleIsCompressed)
227        {
228            for (i = 0; i < size; i++)
229            {
230                *dest++ = rleCurrent[i];
231            }
232        }
233        else
234        {
235            for (i = 0; i < size; i++)
236            {
237                *dest++ = *(*src)++;
238            }
239        }
240        // original code : *rleRemaining)--;
241        (*rleRemaining)--;
242    }
243}
244
245
246unsigned char *
247simage_tga_load(std::istream& fin,
248int *width_ret,
249int *height_ret,
250int *numComponents_ret)
251{
252    unsigned char header[18];
253    int type;
254    int width;
255    int height;
256    int depth;
257    int flags;
258    int format;
259    unsigned char *colormap;
260    int indexsize;
261    int rleIsCompressed;
262    int rleRemaining;
263    int rleEntrySize;
264    unsigned char rleCurrent[4];
265    unsigned char *buffer;
266    unsigned char *dest;
267    int bpr;
268    unsigned char *linebuf;
269
270    tgaerror = ERR_NO_ERROR;     /* clear error */
271
272    fin.read((char*)header,18);
273    if (fin.gcount() != 18)
274    {
275        tgaerror = ERR_READ;
276        return NULL;
277    }
278
279    type = header[2];
280    width = getInt16(&header[12]);
281    height = getInt16(&header[14]);
282    depth = header[16] >> 3;
283    flags = header[17];
284
285    /* check for reasonable values in case this is not a tga file */
286    if ((type != 2 && type != 10) ||
287        (width < 0 || width > 4096) ||
288        (height < 0 || height > 4096) ||
289        (depth < 2 || depth > 4))
290    {
291        tgaerror = ERR_UNSUPPORTED;
292        return NULL;
293    }
294
295    if (header[0])               /* skip identification field */
296        fin.seekg(header[0],std::ios::cur);
297
298    colormap = NULL;
299    if (header[1] == 1)          /* there is a colormap */
300    {
301        int len = getInt16(&header[5]);
302        indexsize = header[7]>>3;
303        colormap = new unsigned char [len*indexsize];
304        fin.read((char*)colormap,len*indexsize);
305    }
306
307    if (depth == 2)              /* 16 bits */
308    {
309        if (flags & 1) format = 4;
310        else format = 3;
311    }
312    else format = depth;
313
314    /*    SoDebugError::postInfo("simage_tga_load", "TARGA file: %d %d %d %d %d\n",  */
315    /*               type, width, height, depth, format); */
316
317    rleIsCompressed = 0;
318    rleRemaining = 0;
319    rleEntrySize = depth;
320    buffer = new unsigned char [width*height*format];
321    dest = buffer;
322    bpr = format * width;
323    linebuf = new unsigned char [width*depth];
324
325    //check the intended image orientation
326    bool bLeftToRight = (flags&0x10)==0;
327    bool bTopToBottom = (flags&0x20)!=0;
328    int lineoffset = bTopToBottom ? -bpr : bpr;
329    if (bTopToBottom) //move start point to last line in buffer
330        dest += (bpr*(height-1));
331
332    switch(type)
333    {
334        case 1:                  /* colormap, uncompressed */
335        {
336            /* FIXME: write code */
337            /* should never get here because simage_tga_identify returns 0 */
338            /* for this filetype */
339            /* I need example files in this format to write the code... */
340            tgaerror = ERR_UNSUPPORTED;
341        }
342        break;
343        case 2:                  /* RGB, uncompressed */
344        {
345            int x, y;
346            for (y = 0; y < height; y++)
347            {
348                fin.read((char*)linebuf,width*depth);
349                if (fin.gcount() != (std::streamsize) (width*depth))
350                {
351                    tgaerror = ERR_READ;
352                    break;
353                }
354                for (x = 0; x < width; x++)
355                {
356                    convert_data(linebuf, dest, bLeftToRight ? x : (width-1) - x, depth, format);
357                }
358                dest += lineoffset;
359            }
360        }
361        break;
362        case 9:                  /* colormap, compressed */
363        {
364            /* FIXME: write code */
365            /* should never get here because simage_tga_identify returns 0 */
366            /* for this filetype */
367            /* I need example files in this format to write the code... */
368            tgaerror = ERR_UNSUPPORTED;
369        }
370        break;
371        case 10:                 /* RGB, compressed */
372        {
373            int size, x, y;
374            unsigned char *buf;
375            unsigned char *src;
376            int pos = fin.tellg();
377            fin.seekg(0,std::ios::end);
378            size = (int)fin.tellg() - pos;
379            fin.seekg(pos,std::ios::beg);
380            buf = new unsigned char [size];
381            if (buf == NULL)
382            {
383                tgaerror = ERR_MEM;
384                break;
385            }
386            src = buf;
387            fin.read((char*)buf,size);
388            if (fin.gcount() != (std::streamsize) size)
389            {
390                tgaerror = ERR_READ;
391                break;
392            }
393            for (y = 0; y < height; y++)
394            {
395                rle_decode(&src, linebuf, width*depth, &rleRemaining,
396                    &rleIsCompressed, rleCurrent, rleEntrySize);
397                assert(src <= buf + size);
398                for (x = 0; x < width; x++)
399                {
400                    convert_data(linebuf, dest,  bLeftToRight ? x : (width-1) - x, depth, format);
401                }
402                dest += lineoffset;
403            }
404            if (buf) delete [] buf;
405        }
406        break;
407        default:
408            tgaerror = ERR_UNSUPPORTED;
409    }
410
411    if (linebuf) delete [] linebuf;
412
413    if (tgaerror)
414    {
415        if (buffer) delete [] buffer;
416        return NULL;
417    }
418
419    *width_ret = width;
420    *height_ret = height;
421    *numComponents_ret = format;
422    return buffer;
423}
424
425
426int
427simage_tga_identify(const char *filename,
428const unsigned char *buf,
429int headerlen)
430{
431    char * ptr;
432    if (headerlen < 18) return 0;
433    ptr = (char *)strrchr(filename, '.');
434    if (!ptr) return 0;          /* TGA files must end with .tga|.TGA */
435
436    if (strcmp(ptr, ".tga") && strcmp(ptr, ".TGA")) return 0;
437
438    if (buf[1] == 1 && buf[2] == 1 && buf[17] < 64)
439    {
440        /*      SoDebugError::postInfo("simage_tga_identify", */
441        /*                 "TARGA colormap file: %s\n", filename); */
442        return 0;
443    }
444    if ((buf[1] == 0 || buf[1] == 1) && buf[2] == 2 && buf[17] < 64) return 1;
445    if (buf[1] == 1 && buf[2] == 9 && buf[17] < 64)
446    {
447        /*      SoDebugError::postInfo("simage_tga_identity", */
448        /*                 "TARGA RLE and colormap file: %s\n", filename);  */
449
450        /* will soon be supported */
451        return 0;
452    }
453    if ((buf[1] == 0 || buf[1] == 1) && buf[2] == 10 && buf[17] < 64)
454    {
455        /* RLE and RGB */
456        return 1;
457    }
458    else                         /* unsupported */
459    {
460        /*      SoDebugError::postInfo("simage_tga_identify", */
461        /*                 "Unsupported TARGA type.\n"); */
462    }
463    /* not a TGA, or not supported type */
464    return 0;
465}
466
467
468class ReaderWriterTGA : public osgDB::ReaderWriter
469{
470    public:
471
472        ReaderWriterTGA()
473        {
474            supportsExtension("tga","Tga Image format");
475        }
476
477        virtual const char* className() const { return "TGA Image Reader"; }
478
479        ReadResult readTGAStream(std::istream& fin) const
480        {
481            unsigned char *imageData = NULL;
482            int width_ret;
483            int height_ret;
484            int numComponents_ret;
485
486            imageData = simage_tga_load(fin,&width_ret,&height_ret,&numComponents_ret);
487
488            if (imageData==NULL) return ReadResult::FILE_NOT_HANDLED;
489
490            int s = width_ret;
491            int t = height_ret;
492            int r = 1;
493
494            int internalFormat = numComponents_ret;
495
496            unsigned int pixelFormat =
497                numComponents_ret == 1 ? GL_LUMINANCE :
498            numComponents_ret == 2 ? GL_LUMINANCE_ALPHA :
499            numComponents_ret == 3 ? GL_RGB :
500            numComponents_ret == 4 ? GL_RGBA : (GLenum)-1;
501
502            unsigned int dataType = GL_UNSIGNED_BYTE;
503
504            osg::Image* pOsgImage = new osg::Image;
505            pOsgImage->setImage(s,t,r,
506                internalFormat,
507                pixelFormat,
508                dataType,
509                imageData,
510                osg::Image::USE_NEW_DELETE);
511
512            return pOsgImage;
513
514        }
515
516        virtual ReadResult readObject(std::istream& fin,const osgDB::ReaderWriter::Options* options =NULL) const
517        {
518            return readImage(fin, options);
519        }
520
521        virtual ReadResult readObject(const std::string& file, const osgDB::ReaderWriter::Options* options =NULL) const
522        {
523            return readImage(file, options);
524        }
525
526        virtual ReadResult readImage(std::istream& fin,const Options* =NULL) const
527        {
528            return readTGAStream(fin);
529        }
530
531        virtual ReadResult readImage(const std::string& file, const osgDB::ReaderWriter::Options* options) const
532        {
533            std::string ext = osgDB::getLowerCaseFileExtension(file);
534            if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
535
536            std::string fileName = osgDB::findDataFile( file, options );
537            if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
538
539            osgDB::ifstream istream(fileName.c_str(), std::ios::in | std::ios::binary);
540            if(!istream) return ReadResult::FILE_NOT_HANDLED;
541            ReadResult rr = readTGAStream(istream);
542            if(rr.validImage()) rr.getImage()->setFileName(file);
543            return rr;
544        }
545
546        bool saveTGAStream(const osg::Image& image, std::ostream& fout) const
547        {
548            if (!image.data()) return false;
549
550            // At present, I will only save the image to unmapped RGB format
551            // Other data types can be added soon with different options
552            // The format description can be found at:
553            // http://local.wasp.uwa.edu.au/~pbourke/dataformats/tga/
554            unsigned int pixelFormat = image.getPixelFormat();
555            int width = image.s(), height = image.t();
556            int numPerPixel = image.computeNumComponents(pixelFormat);
557            int pixelMultiplier = (image.getDataType()==GL_FLOAT ? 255 : 1);
558
559            // Headers
560            fout.put(0);  // Identification field size
561            fout.put(0);  // Color map type
562            fout.put(2);  // Image type
563            fout.put(0); fout.put(0);  // Color map origin
564            fout.put(0); fout.put(0);  // Color map length
565            fout.put(0);  // Color map entry size
566            fout.put(0); fout.put(0);  // X origin of image
567            fout.put(0); fout.put(0);  // Y origin of image
568            fout.put(width&0xff); fout.put((width&0xff00)>>8);  // Width of image
569            fout.put(height&0xff); fout.put((height&0xff00)>>8);  // Height of image
570            fout.put(numPerPixel * 8);  // Image pixel size
571            fout.put(0);  // Image descriptor
572
573            // Swap red/blue channels for BGR images
574            int r = 0, g = 1, b = 2;
575            if( pixelFormat == GL_BGR || pixelFormat == GL_BGRA )
576            {
577                r = 2;
578                b = 0;
579            }
580
581            // Data
582            for (int y=0; y<height; ++y)
583            {
584                const unsigned char* ptr = image.data(0,y);
585                for (int x=0; x<width; ++x)
586                {
587                    int off = x * numPerPixel;
588                    switch ( numPerPixel )
589                    {
590                    case 3// BGR
591                        fout.put(ptr[off+b] * pixelMultiplier); fout.put(ptr[off+g] * pixelMultiplier);
592                        fout.put(ptr[off+r] * pixelMultiplier);
593                        break;
594                    case 4// BGRA
595                        fout.put(ptr[off+b] * pixelMultiplier); fout.put(ptr[off+g] * pixelMultiplier);
596                        fout.put(ptr[off+r] * pixelMultiplier); fout.put(ptr[off+3] * pixelMultiplier);
597                        break;
598                    default:
599                        return false;
600                    }
601                }
602            }
603            return true;
604        }
605
606        virtual WriteResult writeImage(const osg::Image& image, std::ostream& fout, const Options*) const
607        {
608            if (saveTGAStream(image, fout))
609                return WriteResult::FILE_SAVED;
610            else
611                return WriteResult::ERROR_IN_WRITING_FILE;
612        }
613
614        virtual WriteResult writeImage(const osg::Image& image, const std::string& fileName, const Options* options) const
615        {
616            std::string ext = osgDB::getFileExtension(fileName);
617            if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;
618
619            osgDB::ofstream fout(fileName.c_str(), std::ios::out | std::ios::binary);
620            if (!fout) return WriteResult::ERROR_IN_WRITING_FILE;
621            return writeImage(image, fout, options);
622        }
623};
624
625// now register with Registry to instantiate the above
626// reader/writer.
627REGISTER_OSGPLUGIN(tga, ReaderWriterTGA)
Note: See TracBrowser for help on using the browser.