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

Revision 12912, 18.2 kB (checked in by robert, 2 years ago)

Added support for using GL_UNPACK_ROW_LENGTH in conjunction with texture's + osg::Image via new RowLength?
parameter in osg::Image. To support this Image::setData(..) now has a new optional rowLength parameter which
defaults to 0, which provides the original behaviour, Image::setRowLength(int) and int Image::getRowLength() are also provided.

With the introduction of RowLength? support in osg::Image it is now possible to create a sub image where
the t size of the image are smaller than the row length, useful for when you have a large image on the CPU
and which to use a small portion of it on the GPU. However, when these sub images are created the data
within the image is no longer contiguous so data access can no longer assume that all the data is in
one block. The new method Image::isDataContiguous() enables the user to check whether the data is contiguous,
and if not one can either access the data row by row using Image::data(column,row,image) accessor, or use the
new Image::DataIterator? for stepping through each block on memory assocatied with the image.

To support the possibility of non contiguous osg::Image usage of image objects has had to be updated to
check DataContiguous? and handle the case or use access via the DataIerator? or by row by row. To achieve
this a relatively large number of files has had to be modified, in particular the texture classes and
image plugins that doing writing.

  • 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.