root/OpenSceneGraph/trunk/src/osgPlugins/gif/ReaderWriterGIF.cpp @ 13041

Revision 13041, 19.4 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/ImageStream>
3#include <osg/Notify>
4#include <osg/Geode>
5#include <osg/GL>
6
7#include <osgDB/FileNameUtils>
8#include <osgDB/FileUtils>
9#include <osgDB/Registry>
10
11#include <OpenThreads/Thread>
12
13
14/****************************************************************************
15 *
16 * Follows is code extracted from the simage library.  Original Authors:
17 *
18 *      Systems in Motion,
19 *      <URL:http://www.sim.no>
20 *
21 *      Peder Blekken <pederb@sim.no>
22 *      Morten Eriksen <mortene@sim.no>
23 *      Marius Bugge Monsen <mariusbu@sim.no>
24 *
25 * The original COPYING notice
26 *
27 *      All files in this library are public domain, except simage_rgb.cpp which is
28 *      Copyright (c) Mark J Kilgard <mjk@nvidia.com>. I will contact Mark
29 *      very soon to hear if this source also can become public domain.
30 *
31 *      Please send patches for bugs and new features to: <pederb@sim.no>.
32 *
33 *      Peder Blekken
34 *
35 *
36 * Ported into the OSG as a plugin, Robert Osfield Decemeber 2000.
37 * Note, reference above to license of simage_rgb is not relevent to the OSG
38 * as the OSG does not use it.  Also for patches, bugs and new features
39 * please send them direct to the OSG dev team rather than address above.
40 *
41 **********************************************************************/
42
43/*!
44  GIF loader, using libungif
45  Based, in part, on source code found in libungif, gif2rgb.c
46*/
47#include <stdlib.h>
48#include <string.h>
49#include <stdio.h>
50
51extern  "C"
52{
53    #include <gif_lib.h>
54}
55
56#define ERR_NO_ERROR     0
57#define ERR_OPEN         1
58#define ERR_READ         2
59#define ERR_MEM          3
60
61#define MY_GIF_DEBUG 1
62
63// GifImageStream class
64class GifImageStream : public osg::ImageStream, public OpenThreads::Thread
65{
66public:
67    GifImageStream() :
68        osg::ImageStream(),
69        _multiplier(1.0),
70        _currentLength(0),
71        _length(0),
72        _frameNum(0),
73        _dataNum(0),
74        _done(false)
75    {
76        _status=PAUSED;
77    }
78
79    virtual Object* clone() const { return new GifImageStream; }
80    virtual bool isSameKindAs( const Object* obj ) const
81    { return dynamic_cast<const GifImageStream*>(obj) != NULL; }
82    virtual const char* className() const { return "GifImageStream"; }
83
84    virtual void play()
85    {
86        if (!isRunning())
87            start();
88        _status=PLAYING;
89    }
90
91    virtual void pause() { _status=PAUSED; }
92
93    virtual void rewind() { setReferenceTime( 0.0 ); }
94
95    virtual void quit( bool waitForThreadToExit=true )
96    {
97        _done = true;
98        if ( waitForThreadToExit )
99        {
100            while( isRunning() )
101                OpenThreads::Thread::YieldCurrentThread();
102            OSG_DEBUG<<"GifImageStream thread quitted"<<std::endl;
103        }
104    }
105
106    StreamStatus getStatus() { return _status; }
107    virtual double getLength() const { return _length*0.01*_multiplier; }
108
109    // Go to a specific position of stream
110    virtual void setReferenceTime( double time )
111    {
112        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
113
114        int i=1;
115        int framePos = static_cast<int>(time*100.0/_multiplier);
116        if ( framePos>=(int)_length )
117            framePos = _length;
118
119        std::vector<FrameData*>::iterator it;
120        for ( it=_dataList.begin(); it!=_dataList.end(); it++,i++ )
121        {
122            framePos -= (*it)->delay;
123            if ( framePos<0 )
124                break;
125        }
126        _dataNum = i-1;
127        _frameNum = (*it)->delay+framePos;
128        setNewImage();
129    }
130    virtual double getReferenceTime() const { return _currentLength*0.01*_multiplier; }
131
132    // Speed up, slow down or back to normal (1.0)
133    virtual void setTimeMultiplier( double m )
134    {
135        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
136        if ( m>0 )
137            _multiplier = m;
138    }
139    virtual double getTimeMultiplier() const { return _multiplier; }
140
141    // Not used in GIF animation
142    virtual void setVolume(float) {}
143    virtual float getVolume() const { return 0.0f; }
144
145    virtual void run()
146    {
147        _dataIter = _dataList.begin();
148
149        while ( !_done )
150        {
151            if ( _status==PLAYING && (*_dataIter) )
152            {
153                if ( _frameNum>=(*_dataIter)->delay )
154                {
155                    _frameNum = 0;
156                    if ( _dataNum>=_dataList.size()-1 )
157                    {
158                        if ( getLoopingMode()==LOOPING )
159                        {
160                            _dataNum = 0;
161                            _currentLength = 0;
162                        }
163                    }
164                    else
165                        _dataNum++;
166
167                    setNewImage();
168                }
169                else
170                {
171                    _frameNum++;
172                    _currentLength++;
173                }
174
175                OpenThreads::Thread::microSleep(static_cast<int>(10000.0f*_multiplier));
176            }
177            else
178                OpenThreads::Thread::microSleep(150000L);
179        }
180    }
181
182    void addToImageStream( int ss, int tt, int rr, int numComponents, int delayTime, unsigned char* imgData )
183    {
184        if ( isRunning() )
185        {
186            OSG_WARN<<"GifImageStream::addToImageStream: thread is running!"<<std::endl;
187            return;
188        }
189
190        GLint internalFormat = numComponents;
191        GLenum dataType = GL_UNSIGNED_BYTE;
192
193        GLenum pixelFormat =
194            numComponents == 1 ? GL_LUMINANCE :
195            numComponents == 2 ? GL_LUMINANCE_ALPHA :
196            numComponents == 3 ? GL_RGB :
197            numComponents == 4 ? GL_RGBA : (GLenum)-1;
198
199        if ( _dataList.empty() )
200        {
201            // Set image texture for the first time
202            setImage(ss, tt, rr, internalFormat, pixelFormat, dataType,
203                imgData,osg::Image::NO_DELETE,1);
204        }
205
206        FrameData* newData = new FrameData;
207        newData->delay = delayTime;
208        newData->data = imgData;
209        _dataList.push_back( newData );
210        _length += delayTime;
211    }
212
213protected:
214    typedef struct
215    {
216        unsigned int delay;
217        unsigned char* data;
218    } FrameData;
219
220    void setNewImage()
221    {
222        _dataIter = _dataList.begin()+_dataNum;
223
224        if ( *_dataIter )
225        {
226            unsigned char* image = (*_dataIter)->data;
227            setImage(_s,_t,_r,_internalTextureFormat,_pixelFormat,_dataType,
228                image,osg::Image::NO_DELETE,1);
229            dirty();
230        }
231    }
232
233    virtual ~GifImageStream()
234    {
235        if( isRunning() )
236            quit( true );
237
238        std::vector<FrameData*>::iterator it;
239        for ( it=_dataList.begin(); it!=_dataList.end(); it++ )
240        {
241            delete (*it)->data;
242            delete (*it);
243        }
244    }
245
246    double          _multiplier;
247    unsigned int    _currentLength;
248    unsigned int    _length;
249
250    unsigned int    _frameNum;
251    unsigned int    _dataNum;
252    std::vector<FrameData*> _dataList;
253    std::vector<FrameData*>::iterator _dataIter;
254
255    bool _done;
256    OpenThreads::Mutex _mutex;
257};
258
259static int giferror = ERR_NO_ERROR;
260
261int
262simage_gif_error(char * buffer, int buflen)
263{
264    switch (giferror)
265    {
266        case ERR_OPEN:
267            strncpy(buffer, "GIF loader: Error opening file", buflen);
268            break;
269        case ERR_READ:
270            strncpy(buffer, "GIF loader: Error reading file", buflen);
271            break;
272        case ERR_MEM:
273            strncpy(buffer, "GIF loader: Out of memory error", buflen);
274            break;
275    }
276    return giferror;
277}
278
279
280int
281simage_gif_identify(const char *,
282const unsigned char *header,
283int headerlen)
284{
285    static unsigned char gifcmp[] = {'G', 'I', 'F'};
286    if (headerlen < 3) return 0;
287    if (memcmp((const void*)header,
288        (const void*)gifcmp, 3) == 0) return 1;
289    return 0;
290}
291
292
293static void
294decode_row(GifFileType * giffile,
295unsigned char * buffer,
296unsigned char * rowdata,
297int x, int y, int len,
298int transparent)
299{
300    GifColorType * cmentry;
301    ColorMapObject * colormap;
302    int colormapsize;
303    unsigned char col;
304    unsigned char * ptr;
305    y = giffile->SHeight - (y+1);
306    ptr = buffer + (giffile->SWidth * y + x) * 4;
307
308    colormap = (giffile->Image.ColorMap
309        ? giffile->Image.ColorMap
310        : giffile->SColorMap);
311    colormapsize = colormap ? colormap->ColorCount : 255;
312
313    while (len--)
314    {
315        col = *rowdata++;
316                                 /* just in case */
317        if (col >= colormapsize) col = 0;
318
319        if ( col == transparent )
320        {
321            // keep pixels of last image if transparent mode is on
322            // this is necessary for GIF animating
323            ptr += 3;
324        }
325        else
326        {
327            cmentry = colormap ? &colormap->Colors[col] : NULL;
328            if (cmentry)
329            {
330                *ptr++ = cmentry->Red;
331                *ptr++ = cmentry->Green;
332                *ptr++ = cmentry->Blue;
333            }
334            else
335            {
336                *ptr++ = col;
337                *ptr++ = col;
338                *ptr++ = col;
339            }
340        }
341        *ptr++ = (col == transparent ? 0x00 : 0xff);
342    }
343}
344
345int gif_read_stream(GifFileType *gfile, GifByteType *gdata, int glength)
346{
347    std::istream *stream = (std::istream*)gfile->UserData; //Get pointer to istream
348    stream->read((char*)gdata,glength); //Read requested amount of data
349    return stream->gcount();
350}
351
352unsigned char *
353simage_gif_load(std::istream& fin,
354int *width_ret,
355int *height_ret,
356int *numComponents_ret,
357GifImageStream** obj)
358{
359    int i, j, n, row, col, width, height, extcode;
360    unsigned char * rowdata;
361    unsigned char * buffer, * ptr;
362    unsigned char bg;
363    int transparent, delaytime;
364    GifRecordType recordtype;
365    GifByteType * extension;
366    GifFileType * giffile;
367    GifColorType * bgcol;
368
369    /* The way an interlaced image should be read - offsets and jumps */
370    int interlacedoffset[] = { 0, 4, 2, 1 };
371    int interlacedjumps[] = { 8, 8, 4, 2 };
372
373    giffile = DGifOpen(&fin,gif_read_stream);
374    if (!giffile)
375    {
376        giferror = ERR_OPEN;
377        return NULL;
378    }
379
380    transparent = -1;            /* no transparent color by default */
381    delaytime = 8;               /* delay time of a frame  */
382
383    n = giffile->SHeight * giffile->SWidth;
384    buffer = new unsigned char [n * 4];
385    if (!buffer)
386    {
387        giferror = ERR_MEM;
388        return NULL;
389    }
390    rowdata = new unsigned char [giffile->SWidth];
391    if (!rowdata)
392    {
393        giferror = ERR_MEM;
394        delete [] buffer;
395        return NULL;
396    }
397
398    bg = giffile->SBackGroundColor;
399    if (giffile->SColorMap && bg < giffile->SColorMap->ColorCount)
400    {
401        bgcol = &giffile->SColorMap->Colors[bg];
402    }
403    else bgcol = NULL;
404    ptr = buffer;
405    for (i = 0; i < n; i++)
406    {
407        if (bgcol)
408        {
409            *ptr++ = bgcol->Red;
410            *ptr++ = bgcol->Green;
411            *ptr++ = bgcol->Blue;
412            *ptr++ = 0xff;
413        }
414        else
415        {
416            *ptr++ = 0x00;
417            *ptr++ = 0x00;
418            *ptr++ = 0x00;
419            *ptr++ = 0xff;
420        }
421    }
422
423    /* Scan the content of the GIF file and load the image(s) in: */
424    int gif_num=0;
425    do
426    {
427        if (DGifGetRecordType(giffile, &recordtype) == GIF_ERROR)
428        {
429            giferror = ERR_READ;
430            delete [] buffer;
431            delete [] rowdata;
432            return NULL;
433        }
434        switch (recordtype)
435        {
436            case IMAGE_DESC_RECORD_TYPE:
437                /* start recording image stream if more than one image found  */
438                gif_num++;
439                if ( gif_num==2 )
440                {
441                    *obj = new GifImageStream;
442                    (*obj)->addToImageStream( giffile->SWidth, giffile->SHeight, 1, 4, delaytime, buffer );
443                    unsigned char* destbuffer = new unsigned char [n * 4];
444                    buffer = (unsigned char*)memcpy( destbuffer, buffer, n*4 );
445                }
446
447                if (DGifGetImageDesc(giffile) == GIF_ERROR)
448                {
449                    giferror = ERR_READ;
450                    delete [] buffer;
451                    delete [] rowdata;
452                    return NULL;
453                }
454                /* subimage position in composite image */
455                row = giffile->Image.Top;
456                col = giffile->Image.Left;
457                width = giffile->Image.Width;
458                height = giffile->Image.Height;
459                if (giffile->Image.Left + giffile->Image.Width > giffile->SWidth ||
460                    giffile->Image.Top + giffile->Image.Height > giffile->SHeight)
461                {
462                    /* image is not confined to screen dimension */
463                    giferror = ERR_READ;
464                    delete [] buffer;
465                    delete [] rowdata;
466                    return NULL;
467                }
468                if (giffile->Image.Interlace)
469                {
470                    //fprintf(stderr,"interlace\n");
471                    /* Need to perform 4 passes on the images: */
472                    for (i = 0; i < 4; i++)
473                    {
474                        for (j = row + interlacedoffset[i]; j < row + height;
475                            j += interlacedjumps[i])
476                        {
477                            if (DGifGetLine(giffile, rowdata, width) == GIF_ERROR)
478                            {
479                                giferror = ERR_READ;
480                                delete [] buffer;
481                                delete [] rowdata;
482                                return NULL;
483                            }
484                            else decode_row(giffile, buffer, rowdata, col, j, width, transparent);
485                        }
486                    }
487                }
488                else
489                {
490                    for (i = 0; i < height; i++, row++)
491                    {
492                        if (DGifGetLine(giffile, rowdata, width) == GIF_ERROR)
493                        {
494                            giferror = ERR_READ;
495                            delete [] buffer;
496                            delete [] rowdata;
497                            return NULL;
498                        }
499                        else decode_row(giffile, buffer, rowdata, col, row, width, transparent);
500                    }
501                }
502
503                // Record gif image stream
504                if ( *obj && obj )
505                {
506                    (*obj)->addToImageStream( giffile->SWidth, giffile->SHeight, 1, 4, delaytime, buffer );
507                    unsigned char* destbuffer = new unsigned char [n * 4];
508                    buffer = (unsigned char*)memcpy( destbuffer, buffer, n*4 );
509                }
510
511
512                break;
513            case EXTENSION_RECORD_TYPE:
514                /* Skip any extension blocks in file: */
515                if (DGifGetExtension(giffile, &extcode, &extension) == GIF_ERROR)
516                {
517                    giferror = ERR_READ;
518                    delete [] buffer;
519                    delete [] rowdata;
520                    return NULL;
521                }
522                /* transparent test from the gimp gif-plugin. Open Source rulez! */
523                else if (extcode == 0xf9)
524                {
525                    if (extension[0] >= 4 && extension[1] & 0x1) transparent = extension[4];
526                    else transparent = -1;
527
528                    delaytime = (extension[3]<<8)+extension[2];    // minimum unit 1/100s, so 8 here means 8/100s
529                }
530                while (extension != NULL)
531                {
532                    if (DGifGetExtensionNext(giffile, &extension) == GIF_ERROR)
533                    {
534                        giferror = ERR_READ;
535                        delete [] buffer;
536                        delete [] rowdata;
537                        return NULL;
538                    }
539                }
540                break;
541            case TERMINATE_RECORD_TYPE:
542                break;
543            default:             /* Should be trapped by DGifGetRecordType. */
544                break;
545        }
546    }
547    while (recordtype != TERMINATE_RECORD_TYPE);
548
549    // Delete the last allocated buffer to avoid memory leaks if we using GifImageStream
550    if ( obj && *obj )
551    {
552        delete [] buffer;
553        buffer = 0;
554    }
555
556    delete [] rowdata;
557    *width_ret = giffile->SWidth;
558    *height_ret = giffile->SHeight;
559    *numComponents_ret = 4;
560    DGifCloseFile(giffile);
561    return buffer;
562}
563
564class ReaderWriterGIF : public osgDB::ReaderWriter
565{
566    public:
567
568        ReaderWriterGIF()
569        {
570            supportsExtension("gif","GIF Image format");
571        }
572
573        virtual const char* className() const { return "GIF Image Reader"; }
574
575        ReadResult readGIFStream(std::istream& fin) const
576        {
577            unsigned char *imageData = NULL;
578            int width_ret;
579            int height_ret;
580            int numComponents_ret;
581
582            GifImageStream* gifStream = NULL;
583            imageData = simage_gif_load( fin,&width_ret,&height_ret,&numComponents_ret, &gifStream );
584
585            switch (giferror)
586            {
587                case ERR_OPEN:
588                    return ReadResult("GIF loader: Error opening file");
589                case ERR_READ:
590                    return ReadResult("GIF loader: Error reading file");
591                case ERR_MEM:
592                    return ReadResult("GIF loader: Out of memory error");
593            }
594
595            // Use GifImageStream to display animate GIFs
596            if ( gifStream )
597            {
598                OSG_DEBUG<<"Using GifImageStream ..."<<std::endl;
599                return gifStream;
600            }
601
602            if (imageData==NULL) return ReadResult::FILE_NOT_HANDLED;
603
604            int s = width_ret;
605            int t = height_ret;
606            int r = 1;
607
608            int internalFormat = numComponents_ret;
609
610            unsigned int pixelFormat =
611                numComponents_ret == 1 ? GL_LUMINANCE :
612            numComponents_ret == 2 ? GL_LUMINANCE_ALPHA :
613            numComponents_ret == 3 ? GL_RGB :
614            numComponents_ret == 4 ? GL_RGBA : (GLenum)-1;
615
616            unsigned int dataType = GL_UNSIGNED_BYTE;
617
618            osg::Image* pOsgImage = new osg::Image;
619            pOsgImage->setImage(s,t,r,
620                internalFormat,
621                pixelFormat,
622                dataType,
623                imageData,
624                osg::Image::USE_NEW_DELETE);
625
626            return pOsgImage;
627        }
628
629        virtual ReadResult readObject(std::istream& fin,const osgDB::ReaderWriter::Options* options =NULL) const
630        {
631            return readImage(fin, options);
632        }
633
634        virtual ReadResult readObject(const std::string& file, const osgDB::ReaderWriter::Options* options =NULL) const
635        {
636            return readImage(file, options);
637        }
638
639        virtual ReadResult readImage(std::istream& fin,const osgDB::ReaderWriter::Options* =NULL) const
640        {
641            return readGIFStream(fin);
642        }
643
644        virtual ReadResult readImage(const std::string& file, const osgDB::ReaderWriter::Options* options) const
645        {
646            std::string ext = osgDB::getLowerCaseFileExtension(file);
647            if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
648
649            std::string fileName = osgDB::findDataFile( file, options );
650            if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
651
652            osgDB::ifstream istream(fileName.c_str(), std::ios::in | std::ios::binary);
653            if(!istream) return ReadResult::FILE_NOT_HANDLED;
654            ReadResult rr = readGIFStream(istream);
655            if(rr.validImage()) rr.getImage()->setFileName(file);
656            return rr;
657        }
658};
659
660// now register with Registry to instantiate the above
661// reader/writer.
662REGISTER_OSGPLUGIN(gif, ReaderWriterGIF)
Note: See TracBrowser for help on using the browser.