From 8faef7ccbc4b4e192da06de7e1a2a7f1dff7ec91 Mon Sep 17 00:00:00 2001 From: Francesco Montorsi Date: Mon, 1 Jun 2009 11:43:36 +0000 Subject: [PATCH] document that CanRead() and GetImageCount() functions of wxImageHandlers do NOT modify the current stream position and that they require seekable streams; rename current GetImageCount() functions to DoGetImageCount() and put save-and-restore stream position logic in GetImageCount(); add comments in the various DoCanRead() and in DoGetImageCount() where the stream position is modified; remove unneeded SeekI(0) calls from DoCanRead() and DoGetImageCount() functions: they didn't allow to load images from non-seekable streams; implement forward-seeking in wxInputStream::SeekI() also for non-seekable streams git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@60852 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/anidecod.h | 6 +++- include/wx/animdecod.h | 29 ++++++++++++++++++- include/wx/gifdecod.h | 14 +++++---- include/wx/imagbmp.h | 6 ++-- include/wx/image.h | 22 +++++++++++--- include/wx/imaggif.h | 3 +- include/wx/imagtiff.h | 3 +- include/wx/xpmdecod.h | 7 ++++- interface/wx/image.h | 43 ++++++++++++++++++++++++--- interface/wx/stream.h | 11 +++++-- src/common/anidecod.cpp | 8 ++---- src/common/gifdecod.cpp | 5 +--- src/common/imagbmp.cpp | 64 ++++++++++++++++++++--------------------- src/common/image.cpp | 40 +++++++++++++++----------- src/common/imaggif.cpp | 6 +++- src/common/imagiff.cpp | 7 +++-- src/common/imagjpeg.cpp | 2 +- src/common/imagpcx.cpp | 2 +- src/common/imagpng.cpp | 2 +- src/common/imagpnm.cpp | 1 + src/common/imagtga.cpp | 2 +- src/common/imagtiff.cpp | 7 +++-- src/common/imagxpm.cpp | 1 + src/common/stream.cpp | 37 +++++++++++++++++++++++- src/common/xpmdecod.cpp | 3 -- 25 files changed, 237 insertions(+), 94 deletions(-) diff --git a/include/wx/anidecod.h b/include/wx/anidecod.h index 5d4a062be1..df5be1a60b 100644 --- a/include/wx/anidecod.h +++ b/include/wx/anidecod.h @@ -44,7 +44,7 @@ public: virtual wxColour GetTransparentColour(unsigned int frame) const; // implementation of wxAnimationDecoder's pure virtuals - virtual bool CanRead( wxInputStream& stream ) const; + virtual bool Load( wxInputStream& stream ); bool ConvertToImage(unsigned int frame, wxImage *image) const; @@ -55,6 +55,10 @@ public: { return wxANIMATION_TYPE_ANI; } private: + // wxAnimationDecoder pure virtual: + virtual bool DoCanRead( wxInputStream& stream ) const; + // modifies current stream position (see wxAnimationDecoder::CanRead) + // frames stored as wxImage(s): ANI files are meant to be used mostly for animated // cursors and thus they do not use any optimization to encode differences between // two frames: they are just a list of images to display sequentially. diff --git a/include/wx/animdecod.h b/include/wx/animdecod.h index a4c864c365..e803953fe2 100644 --- a/include/wx/animdecod.h +++ b/include/wx/animdecod.h @@ -93,7 +93,28 @@ public: } virtual bool Load( wxInputStream& stream ) = 0; - virtual bool CanRead( wxInputStream& stream ) const = 0; + + bool CanRead( wxInputStream& stream ) const + { + // NOTE: this code is the same of wxImageHandler::CallDoCanRead + + if ( !stream.IsSeekable() ) + return false; // can't test unseekable stream + + wxFileOffset posOld = stream.TellI(); + bool ok = DoCanRead(stream); + + // restore the old position to be able to test other formats and so on + if ( stream.SeekI(posOld) == wxInvalidOffset ) + { + wxLogDebug(_T("Failed to rewind the stream in wxAnimationDecoder!")); + + // reading would fail anyhow as we're not at the right position + return false; + } + + return ok; + } virtual wxAnimationDecoder *Clone() const = 0; virtual wxAnimationType GetType() const = 0; @@ -129,6 +150,12 @@ public: unsigned int GetFrameCount() const { return m_nFrames; } protected: + // checks the signature of the data in the given stream and returns true if it + // appears to be a valid animation format recognized by the animation decoder; + // this function should modify the stream current position without taking care + // of restoring it since CanRead() will do it. + virtual bool DoCanRead(wxInputStream& stream) const = 0; + wxSize m_szAnimation; unsigned int m_nFrames; diff --git a/include/wx/gifdecod.h b/include/wx/gifdecod.h index 443af47c8b..1520823157 100644 --- a/include/wx/gifdecod.h +++ b/include/wx/gifdecod.h @@ -76,7 +76,6 @@ public: void Destroy(); // implementation of wxAnimationDecoder's pure virtuals - virtual bool CanRead( wxInputStream& stream ) const; virtual bool Load( wxInputStream& stream ) { return LoadGIF(stream) == wxGIF_OK; } @@ -88,6 +87,15 @@ public: { return wxANIMATION_TYPE_GIF; } private: + // wxAnimationDecoder pure virtual + virtual bool DoCanRead( wxInputStream& stream ) const; + // modifies current stream position (see wxAnimationDecoder::CanRead) + + int getcode(wxInputStream& stream, int bits, int abfin); + wxGIFErrorCode dgif(wxInputStream& stream, + GIFImage *img, int interl, int bits); + + // array of all frames wxArrayPtrVoid m_frames; @@ -98,10 +106,6 @@ private: unsigned char m_buffer[256]; // buffer for reading unsigned char *m_bufp; // pointer to next byte in buffer - int getcode(wxInputStream& stream, int bits, int abfin); - wxGIFErrorCode dgif(wxInputStream& stream, - GIFImage *img, int interl, int bits); - wxDECLARE_NO_COPY_CLASS(wxGIFDecoder); }; diff --git a/include/wx/imagbmp.h b/include/wx/imagbmp.h index 1bcebe1d18..b83c1df700 100644 --- a/include/wx/imagbmp.h +++ b/include/wx/imagbmp.h @@ -89,8 +89,9 @@ public: virtual bool SaveFile( wxImage *image, wxOutputStream& stream, bool verbose=true ); virtual bool LoadFile( wxImage *image, wxInputStream& stream, bool verbose=true, int index=-1 ); virtual bool DoLoadFile( wxImage *image, wxInputStream& stream, bool verbose, int index ); - virtual int GetImageCount( wxInputStream& stream ); + protected: + virtual int DoGetImageCount( wxInputStream& stream ); virtual bool DoCanRead( wxInputStream& stream ); #endif // wxUSE_STREAMS @@ -145,8 +146,9 @@ public: #if wxUSE_STREAMS virtual bool SaveFile( wxImage *WXUNUSED(image), wxOutputStream& WXUNUSED(stream), bool WXUNUSED(verbose=true) ){return false ;} virtual bool LoadFile( wxImage *image, wxInputStream& stream, bool verbose=true, int index=-1 ); - virtual int GetImageCount( wxInputStream& stream ); + protected: + virtual int DoGetImageCount( wxInputStream& stream ); virtual bool DoCanRead( wxInputStream& stream ); #endif // wxUSE_STREAMS diff --git a/include/wx/image.h b/include/wx/image.h index 49ea7b2229..bf3e3e78fb 100644 --- a/include/wx/image.h +++ b/include/wx/image.h @@ -97,10 +97,17 @@ public: { } #if wxUSE_STREAMS - virtual bool LoadFile( wxImage *image, wxInputStream& stream, bool verbose=true, int index=-1 ); - virtual bool SaveFile( wxImage *image, wxOutputStream& stream, bool verbose=true ); - - virtual int GetImageCount( wxInputStream& stream ); + // NOTE: LoadFile and SaveFile are not pure virtuals to allow derived classes + // to implement only one of the two + virtual bool LoadFile( wxImage *WXUNUSED(image), wxInputStream& WXUNUSED(stream), + bool WXUNUSED(verbose)=true, int WXUNUSED(index)=-1 ) + { return false; } + virtual bool SaveFile( wxImage *WXUNUSED(image), wxOutputStream& WXUNUSED(stream), + bool WXUNUSED(verbose)=true ) + { return false; } + + int GetImageCount( wxInputStream& stream ); + // save the stream position, call DoGetImageCount() and restore the position bool CanRead( wxInputStream& stream ) { return CallDoCanRead(stream); } bool CanRead( const wxString& name ); @@ -125,6 +132,13 @@ public: protected: #if wxUSE_STREAMS + // NOTE: this function is allowed to change the current stream position + // since GetImageCount() will take care of restoring it later + virtual int DoGetImageCount( wxInputStream& WXUNUSED(stream) ) + { return 1; } // default return value is 1 image + + // NOTE: this function is allowed to change the current stream position + // since CallDoCanRead() will take care of restoring it later virtual bool DoCanRead( wxInputStream& stream ) = 0; // save the stream position, call DoCanRead() and restore the position diff --git a/include/wx/imaggif.h b/include/wx/imaggif.h index 7912cf956f..602c2e3233 100644 --- a/include/wx/imaggif.h +++ b/include/wx/imaggif.h @@ -35,8 +35,9 @@ public: bool verbose = true, int index = -1); virtual bool SaveFile(wxImage *image, wxOutputStream& stream, bool verbose=true); - virtual int GetImageCount(wxInputStream& stream); + protected: + virtual int DoGetImageCount(wxInputStream& stream); virtual bool DoCanRead(wxInputStream& stream); #endif // wxUSE_STREAMS diff --git a/include/wx/imagtiff.h b/include/wx/imagtiff.h index 75bfe80a14..4beb66c6ce 100644 --- a/include/wx/imagtiff.h +++ b/include/wx/imagtiff.h @@ -34,8 +34,9 @@ public: #if wxUSE_STREAMS virtual bool LoadFile( wxImage *image, wxInputStream& stream, bool verbose=true, int index=-1 ); virtual bool SaveFile( wxImage *image, wxOutputStream& stream, bool verbose=true ); - virtual int GetImageCount( wxInputStream& stream ); + protected: + virtual int DoGetImageCount( wxInputStream& stream ); virtual bool DoCanRead( wxInputStream& stream ); #endif diff --git a/include/wx/xpmdecod.h b/include/wx/xpmdecod.h index 00e33a728f..1bd8f1c7f1 100644 --- a/include/wx/xpmdecod.h +++ b/include/wx/xpmdecod.h @@ -30,15 +30,20 @@ public: #if wxUSE_STREAMS // Is the stream XPM file? + // NOTE: this function modifies the current stream position bool CanRead(wxInputStream& stream); + // Read XPM file from the stream, parse it and create image from it wxImage ReadFile(wxInputStream& stream); #endif + // Read directly from XPM data (as passed to wxBitmap ctor): wxImage ReadData(const char* const* xpm_data); + #ifdef __BORLANDC__ // needed for Borland 5.5 - wxImage ReadData(char** xpm_data) { return ReadData(const_cast(xpm_data)); } + wxImage ReadData(char** xpm_data) + { return ReadData(const_cast(xpm_data)); } #endif }; diff --git a/interface/wx/image.h b/interface/wx/image.h index 80c8100f2c..92cd47e917 100644 --- a/interface/wx/image.h +++ b/interface/wx/image.h @@ -83,6 +83,26 @@ public: */ virtual ~wxImageHandler(); + /** + Returns @true if this handler supports the image format contained in the + given stream. + + This function doesn't modify the current stream position (because it + restores the original position before returning; this however requires the + stream to be seekable; see wxStreamBase::IsSeekable). + */ + bool CanRead( wxInputStream& stream ); + + /** + Returns @true if this handler supports the image format contained in the + file with the given name. + + This function doesn't modify the current stream position (because it + restores the original position before returning; this however requires the + stream to be seekable; see wxStreamBase::IsSeekable). + */ + bool CanRead( const wxString& filename ); + /** Gets the preferred file extension associated with this handler. @@ -106,7 +126,9 @@ public: @param stream Opened input stream for reading image data. - Currently, the stream must support seeking. + This function doesn't modify the current stream position (because it + restores the original position before returning; this however requires the + stream to be seekable; see wxStreamBase::IsSeekable). @return Number of available images. For most image handlers, this is 1 (exceptions are TIFF and ICO formats as well as animated GIFs @@ -1574,9 +1596,20 @@ public: /** - Returns @true if the current image handlers can read this file + Returns @true if at least one of the available image handlers can read + the file with the given name. + + See wxImageHandler::CanRead for more info. */ static bool CanRead(const wxString& filename); + + /** + Returns @true if at least one of the available image handlers can read + the data in the given stream. + + See wxImageHandler::CanRead for more info. + */ + static bool CanRead(wxInputStream& stream); //@{ /** @@ -1586,8 +1619,10 @@ public: For the overload taking the parameter @a filename, that's the name of the file to query. - For the overload taking the parameter @a stream, that's the ppened input - stream with image data. Currently, the stream must support seeking. + For the overload taking the parameter @a stream, that's the opened input + stream with image data. + + See wxImageHandler::GetImageCount() for more info. The parameter @a type may be one of the following values: @li wxBITMAP_TYPE_BMP: Load a Windows bitmap file. diff --git a/interface/wx/stream.h b/interface/wx/stream.h index 7900f3ef0b..51db34912d 100644 --- a/interface/wx/stream.h +++ b/interface/wx/stream.h @@ -76,7 +76,7 @@ public: virtual bool IsOk() const; /** - Returns @true if the streams supports seeking to arbitrary offsets. + Returns @true if the stream supports seeking to arbitrary offsets. */ virtual bool IsSeekable() const; @@ -613,6 +613,11 @@ public: /** Changes the stream current position. + This operation in general is possible only for seekable streams + (see wxStreamBase::IsSeekable()); non-seekable streams support only + seeking positive amounts in mode @c wxFromCurrent (this is implemented + by reading data and simply discarding it). + @param pos Offset to seek to. @param mode @@ -623,7 +628,9 @@ public: virtual wxFileOffset SeekI(wxFileOffset pos, wxSeekMode mode = wxFromStart); /** - Returns the current stream position. + Returns the current stream position or ::wxInvalidOffset if it's not + available (e.g. socket streams do not have a size nor a current stream + position). */ virtual wxFileOffset TellI() const; diff --git a/src/common/anidecod.cpp b/src/common/anidecod.cpp index 8ff3071623..9d202672c6 100644 --- a/src/common/anidecod.cpp +++ b/src/common/anidecod.cpp @@ -113,10 +113,10 @@ wxColour wxANIDecoder::GetTransparentColour(unsigned int frame) const // ANI reading and decoding //--------------------------------------------------------------------------- -bool wxANIDecoder::CanRead(wxInputStream& stream) const +bool wxANIDecoder::DoCanRead(wxInputStream& stream) const { wxInt32 FCC1, FCC2; - wxUint32 datalen ; + wxUint32 datalen; wxInt32 riff32; memcpy( &riff32, "RIFF", 4 ); @@ -127,8 +127,6 @@ bool wxANIDecoder::CanRead(wxInputStream& stream) const wxInt32 anih32; memcpy( &anih32, "anih", 4 ); - if ( stream.SeekI(0) == wxInvalidOffset ) - return false; if ( !stream.Read(&FCC1, 4) ) return false; @@ -222,8 +220,6 @@ bool wxANIDecoder::Load( wxInputStream& stream ) wxInt32 seq32; memcpy( &seq32, "seq ", 4 ); - if ( stream.SeekI(0) == wxInvalidOffset) - return false; if ( !stream.Read(&FCC1, 4) ) return false; if ( FCC1 != riff32 ) diff --git a/src/common/gifdecod.cpp b/src/common/gifdecod.cpp index 76450239a3..27669b8d2a 100644 --- a/src/common/gifdecod.cpp +++ b/src/common/gifdecod.cpp @@ -575,16 +575,13 @@ as an End of Information itself) // CanRead: // Returns true if the file looks like a valid GIF, false otherwise. // -bool wxGIFDecoder::CanRead(wxInputStream &stream) const +bool wxGIFDecoder::DoCanRead(wxInputStream &stream) const { unsigned char buf[3]; if ( !stream.Read(buf, WXSIZEOF(buf)) ) return false; - if (stream.SeekI(-(wxFileOffset)WXSIZEOF(buf), wxFromCurrent) == wxInvalidOffset) - return false; // this happens e.g. for non-seekable streams - return memcmp(buf, "GIF", WXSIZEOF(buf)) == 0; } diff --git a/src/common/imagbmp.cpp b/src/common/imagbmp.cpp index 6f3a298d18..9693a0d85e 100644 --- a/src/common/imagbmp.cpp +++ b/src/common/imagbmp.cpp @@ -641,7 +641,9 @@ bool wxBMPHandler::DoLoadDib(wxImage * image, int width, int height, */ if ( IsBmp ) { - if (stream.SeekI(bmpOffset) == wxInvalidOffset) + // NOTE: seeking a positive amount in wxFromCurrent mode allows us to + // load even non-seekable streams (see wxInputStream::SeekI docs)! + if (stream.SeekI(bmpOffset, wxFromCurrent) == wxInvalidOffset) return false; //else: icon, just carry on } @@ -899,15 +901,9 @@ bool wxBMPHandler::LoadDib(wxImage *image, wxInputStream& stream, wxInt32 dbuf[4]; wxInt8 bbuf[4]; - wxFileOffset offset = 0; // keep gcc quiet if ( IsBmp ) { // read the header off the .BMP format file - - offset = stream.TellI(); - if (offset == wxInvalidOffset) - offset = 0; - stream.Read(bbuf, 2); stream.Read(dbuf, 16); } @@ -918,7 +914,7 @@ bool wxBMPHandler::LoadDib(wxImage *image, wxInputStream& stream, #if 0 // unused wxInt32 size = wxINT32_SWAP_ON_BE(dbuf[0]); #endif - offset = offset + wxINT32_SWAP_ON_BE(dbuf[2]); + wxFileOffset offset = wxINT32_SWAP_ON_BE(dbuf[2]); stream.Read(dbuf, 4 * 2); int width = wxINT32_SWAP_ON_BE((int)dbuf[0]); @@ -1021,7 +1017,7 @@ bool wxBMPHandler::DoCanRead(wxInputStream& stream) { unsigned char hdr[2]; - if ( !stream.Read(hdr, WXSIZEOF(hdr)) ) + if ( !stream.Read(hdr, WXSIZEOF(hdr)) ) // it's ok to modify the stream position here return false; // do we have the BMP file signature? @@ -1259,8 +1255,6 @@ bool wxICOHandler::SaveFile(wxImage *image, bool wxICOHandler::LoadFile(wxImage *image, wxInputStream& stream, bool verbose, int index) { - if (stream.SeekI(0) == wxInvalidOffset) - return false; return DoLoadFile(image, stream, verbose, index); } @@ -1272,9 +1266,9 @@ bool wxICOHandler::DoLoadFile(wxImage *image, wxInputStream& stream, ICONDIR IconDir; - wxFileOffset iPos = stream.TellI(); stream.Read(&IconDir, sizeof(IconDir)); wxUint16 nIcons = wxUINT16_SWAP_ON_BE(IconDir.idCount); + // nType is 1 for Icons, 2 for Cursors: wxUint16 nType = wxUINT16_SWAP_ON_BE(IconDir.idType); @@ -1285,9 +1279,13 @@ bool wxICOHandler::DoLoadFile(wxImage *image, wxInputStream& stream, int colmax = 0; int iSel = wxNOT_FOUND; - for (int i = 0; i < nIcons; i++ ) + // remember how many bytes we read from the stream: + wxFileOffset offset = sizeof(IconDir); + + for (unsigned int i = 0; i < nIcons; i++ ) { - stream.Read(pCurrentEntry, sizeof(ICONDIRENTRY)); + offset += stream.Read(pCurrentEntry, sizeof(ICONDIRENTRY)).LastRead(); + // bHeight and bColorCount are wxUint8 if ( pCurrentEntry->bWidth >= wMax ) { @@ -1301,6 +1299,7 @@ bool wxICOHandler::DoLoadFile(wxImage *image, wxInputStream& stream, colmax = pCurrentEntry->bColorCount; } } + pCurrentEntry++; } @@ -1320,8 +1319,13 @@ bool wxICOHandler::DoLoadFile(wxImage *image, wxInputStream& stream, { // seek to selected icon: pCurrentEntry = pIconDirEntry + iSel; - if (stream.SeekI(iPos + wxUINT32_SWAP_ON_BE(pCurrentEntry->dwImageOffset), wxFromStart) == wxInvalidOffset) + + // NOTE: seeking a positive amount in wxFromCurrent mode allows us to + // load even non-seekable streams (see wxInputStream::SeekI docs)! + if (stream.SeekI(wxUINT32_SWAP_ON_BE(pCurrentEntry->dwImageOffset) - offset, + wxFromCurrent) == wxInvalidOffset) return false; + bResult = LoadDib(image, stream, true, IsBmp); bool bIsCursorType = (this->GetType() == wxBITMAP_TYPE_CUR) || (this->GetType() == wxBITMAP_TYPE_ANI); if ( bResult && bIsCursorType && nType == 2 ) @@ -1331,30 +1335,27 @@ bool wxICOHandler::DoLoadFile(wxImage *image, wxInputStream& stream, image->SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y, wxUINT16_SWAP_ON_BE(pCurrentEntry->wBitCount)); } } - delete[] pIconDirEntry; + + delete [] pIconDirEntry; + return bResult; } -int wxICOHandler::GetImageCount(wxInputStream& stream) +int wxICOHandler::DoGetImageCount(wxInputStream& stream) { ICONDIR IconDir; - wxFileOffset iPos = stream.TellI(); - if (stream.SeekI(0) == wxInvalidOffset) - return 0; + if (stream.Read(&IconDir, sizeof(IconDir)).LastRead() != sizeof(IconDir)) + // it's ok to modify the stream position here return 0; - wxUint16 nIcons = wxUINT16_SWAP_ON_BE(IconDir.idCount); - if (stream.SeekI(iPos) == wxInvalidOffset) - return 0; - return (int)nIcons; + + return (int)wxUINT16_SWAP_ON_BE(IconDir.idCount); } bool wxICOHandler::DoCanRead(wxInputStream& stream) { - if (stream.SeekI(0) == wxInvalidOffset) - return false; unsigned char hdr[4]; - if ( !stream.Read(hdr, WXSIZEOF(hdr)) ) + if ( !stream.Read(hdr, WXSIZEOF(hdr)) ) // it's ok to modify the stream position here return false; // hdr[2] is one for an icon and two for a cursor @@ -1374,10 +1375,8 @@ IMPLEMENT_DYNAMIC_CLASS(wxCURHandler, wxICOHandler) bool wxCURHandler::DoCanRead(wxInputStream& stream) { - if (stream.SeekI(0) == wxInvalidOffset) - return false; unsigned char hdr[4]; - if ( !stream.Read(hdr, WXSIZEOF(hdr)) ) + if ( !stream.Read(hdr, WXSIZEOF(hdr)) ) // it's ok to modify the stream position here return false; // hdr[2] is one for an icon and two for a cursor @@ -1408,12 +1407,13 @@ bool wxANIHandler::DoCanRead(wxInputStream& stream) { wxANIDecoder decod; return decod.CanRead(stream); + // it's ok to modify the stream position here } -int wxANIHandler::GetImageCount(wxInputStream& stream) +int wxANIHandler::DoGetImageCount(wxInputStream& stream) { wxANIDecoder decoder; - if (!decoder.Load(stream)) + if (!decoder.Load(stream)) // it's ok to modify the stream position here return wxNOT_FOUND; return decoder.GetFrameCount(); diff --git a/src/common/image.cpp b/src/common/image.cpp index cbe275bc19..d8cbaa2cfd 100644 --- a/src/common/image.cpp +++ b/src/common/image.cpp @@ -2658,19 +2658,27 @@ void wxImage::RotateHue(double angle) IMPLEMENT_ABSTRACT_CLASS(wxImageHandler,wxObject) #if wxUSE_STREAMS -bool wxImageHandler::LoadFile( wxImage *WXUNUSED(image), wxInputStream& WXUNUSED(stream), bool WXUNUSED(verbose), int WXUNUSED(index) ) +int wxImageHandler::GetImageCount( wxInputStream& stream ) { - return false; -} + // NOTE: this code is the same of wxAnimationDecoder::CanRead and + // wxImageHandler::CallDoCanRead -bool wxImageHandler::SaveFile( wxImage *WXUNUSED(image), wxOutputStream& WXUNUSED(stream), bool WXUNUSED(verbose) ) -{ - return false; -} + if ( !stream.IsSeekable() ) + return false; // can't test unseekable stream -int wxImageHandler::GetImageCount( wxInputStream& WXUNUSED(stream) ) -{ - return 1; + wxFileOffset posOld = stream.TellI(); + int n = DoGetImageCount(stream); + + // restore the old position to be able to test other formats and so on + if ( stream.SeekI(posOld) == wxInvalidOffset ) + { + wxLogDebug(_T("Failed to rewind the stream in wxImageHandler!")); + + // reading would fail anyhow as we're not at the right position + return false; + } + + return n; } bool wxImageHandler::CanRead( const wxString& name ) @@ -2688,13 +2696,13 @@ bool wxImageHandler::CanRead( const wxString& name ) bool wxImageHandler::CallDoCanRead(wxInputStream& stream) { - wxFileOffset posOld = stream.TellI(); - if ( posOld == wxInvalidOffset ) - { - // can't test unseekable stream - return false; - } + // NOTE: this code is the same of wxAnimationDecoder::CanRead and + // wxImageHandler::GetImageCount + + if ( !stream.IsSeekable() ) + return false; // can't test unseekable stream + wxFileOffset posOld = stream.TellI(); bool ok = DoCanRead(stream); // restore the old position to be able to test other formats and so on diff --git a/src/common/imaggif.cpp b/src/common/imaggif.cpp index 7b241bd6e1..cd81463b49 100644 --- a/src/common/imaggif.cpp +++ b/src/common/imaggif.cpp @@ -98,14 +98,18 @@ bool wxGIFHandler::DoCanRead( wxInputStream& stream ) { wxGIFDecoder decod; return decod.CanRead(stream); + // it's ok to modify the stream position here } -int wxGIFHandler::GetImageCount( wxInputStream& stream ) +int wxGIFHandler::DoGetImageCount( wxInputStream& stream ) { wxGIFDecoder decod; wxGIFErrorCode error = decod.LoadGIF(stream); if ( (error != wxGIF_OK) && (error != wxGIF_TRUNCATED) ) return -1; + + // NOTE: this function modifies the current stream position but it's ok + // (see wxImageHandler::GetImageCount) return decod.GetFrameCount(); } diff --git a/src/common/imagiff.cpp b/src/common/imagiff.cpp index 9fd15214d4..2ea52b1403 100644 --- a/src/common/imagiff.cpp +++ b/src/common/imagiff.cpp @@ -98,7 +98,10 @@ public: // constructor, destructor, etc. wxIFFDecoder(wxInputStream *s); ~wxIFFDecoder() { Destroy(); } + + // NOTE: this function modifies the current stream position bool CanRead(); + int ReadIFF(); bool ConvertToImage(wxImage *image) const; }; @@ -234,9 +237,6 @@ bool wxIFFDecoder::CanRead() if ( !m_f->Read(buf, WXSIZEOF(buf)) ) return false; - if ( m_f->SeekI(-(wxFileOffset)WXSIZEOF(buf), wxFromCurrent) == wxInvalidOffset ) - return false; - return (memcmp(buf, "FORM", 4) == 0) && (memcmp(buf+8, "ILBM", 4) == 0); } @@ -787,6 +787,7 @@ bool wxIFFHandler::DoCanRead(wxInputStream& stream) wxIFFDecoder decod(&stream); return decod.CanRead(); + // it's ok to modify the stream position here } #endif // wxUSE_STREAMS diff --git a/src/common/imagjpeg.cpp b/src/common/imagjpeg.cpp index f14027cb78..a06f0dd8f2 100644 --- a/src/common/imagjpeg.cpp +++ b/src/common/imagjpeg.cpp @@ -479,7 +479,7 @@ bool wxJPEGHandler::DoCanRead( wxInputStream& stream ) { unsigned char hdr[2]; - if ( !stream.Read(hdr, WXSIZEOF(hdr)) ) + if ( !stream.Read(hdr, WXSIZEOF(hdr)) ) // it's ok to modify the stream position here return false; return hdr[0] == 0xFF && hdr[1] == 0xD8; diff --git a/src/common/imagpcx.cpp b/src/common/imagpcx.cpp index 05f1d66a5e..71f3b1e7a5 100644 --- a/src/common/imagpcx.cpp +++ b/src/common/imagpcx.cpp @@ -487,7 +487,7 @@ bool wxPCXHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbos bool wxPCXHandler::DoCanRead( wxInputStream& stream ) { - unsigned char c = stream.GetC(); + unsigned char c = stream.GetC(); // it's ok to modify the stream position here if ( !stream ) return false; diff --git a/src/common/imagpng.cpp b/src/common/imagpng.cpp index 474221ae11..528168a609 100644 --- a/src/common/imagpng.cpp +++ b/src/common/imagpng.cpp @@ -302,7 +302,7 @@ bool wxPNGHandler::DoCanRead( wxInputStream& stream ) { unsigned char hdr[4]; - if ( !stream.Read(hdr, WXSIZEOF(hdr)) ) + if ( !stream.Read(hdr, WXSIZEOF(hdr)) ) // it's ok to modify the stream position here return false; return memcmp(hdr, "\211PNG", WXSIZEOF(hdr)) == 0; diff --git a/src/common/imagpnm.cpp b/src/common/imagpnm.cpp index 85fbdc08a9..2b3e75f979 100644 --- a/src/common/imagpnm.cpp +++ b/src/common/imagpnm.cpp @@ -180,6 +180,7 @@ bool wxPNMHandler::DoCanRead( wxInputStream& stream ) { Skip_Comment(stream); + // it's ok to modify the stream position here if ( stream.GetC() == 'P' ) { switch ( stream.GetC() ) diff --git a/src/common/imagtga.cpp b/src/common/imagtga.cpp index 9fd65003a7..d98ff01800 100644 --- a/src/common/imagtga.cpp +++ b/src/common/imagtga.cpp @@ -741,7 +741,7 @@ bool wxTGAHandler::DoCanRead(wxInputStream& stream) { // read the fixed-size TGA headers unsigned char hdr[HDR_SIZE]; - stream.Read(hdr, HDR_SIZE); + stream.Read(hdr, HDR_SIZE); // it's ok to modify the stream position here // Check wether we can read the file or not. diff --git a/src/common/imagtiff.cpp b/src/common/imagtiff.cpp index 91b70afbae..fed6679246 100644 --- a/src/common/imagtiff.cpp +++ b/src/common/imagtiff.cpp @@ -413,7 +413,7 @@ bool wxTIFFHandler::LoadFile( wxImage *image, wxInputStream& stream, bool verbos return true; } -int wxTIFFHandler::GetImageCount( wxInputStream& stream ) +int wxTIFFHandler::DoGetImageCount( wxInputStream& stream ) { TIFF *tif = TIFFwxOpen( stream, "image", "r" ); @@ -426,6 +426,9 @@ int wxTIFFHandler::GetImageCount( wxInputStream& stream ) } while (TIFFReadDirectory(tif)); TIFFClose( tif ); + + // NOTE: this function modifies the current stream position but it's ok + // (see wxImageHandler::GetImageCount) return dircount; } @@ -587,7 +590,7 @@ bool wxTIFFHandler::DoCanRead( wxInputStream& stream ) { unsigned char hdr[2]; - if ( !stream.Read(&hdr[0], WXSIZEOF(hdr)) ) + if ( !stream.Read(&hdr[0], WXSIZEOF(hdr)) ) // it's ok to modify the stream position here return false; return (hdr[0] == 'I' && hdr[1] == 'I') || diff --git a/src/common/imagxpm.cpp b/src/common/imagxpm.cpp index 3f1caeaf46..2850e770e6 100644 --- a/src/common/imagxpm.cpp +++ b/src/common/imagxpm.cpp @@ -218,6 +218,7 @@ bool wxXPMHandler::DoCanRead(wxInputStream& stream) { wxXPMDecoder decoder; return decoder.CanRead(stream); + // it's ok to modify the stream position here } #endif // wxUSE_STREAMS diff --git a/src/common/stream.cpp b/src/common/stream.cpp index 8d8a42c870..357499b047 100644 --- a/src/common/stream.cpp +++ b/src/common/stream.cpp @@ -916,13 +916,48 @@ wxFileOffset wxInputStream::SeekI(wxFileOffset pos, wxSeekMode mode) { // RR: This code is duplicated in wxBufferedInputStream. This is // not really a good design, but buffered stream are different - // from all other in that they handle two stream-related objects, + // from all others in that they handle two stream-related objects: // the stream buffer and parent stream. // I don't know whether it should be put as well in wxFileInputStream::OnSysSeek if (m_lasterror==wxSTREAM_EOF) m_lasterror=wxSTREAM_NO_ERROR; + // avoid unnecessary seek operations (optimization) + wxFileOffset currentPos = TellI(), size = GetLength(); + if ((mode == wxFromStart && currentPos == pos) || + (mode == wxFromCurrent && pos == 0) || + (mode == wxFromEnd && size != wxInvalidOffset && currentPos == size-pos)) + return currentPos; + + if (!IsSeekable() && mode == wxFromCurrent && pos > 0) + { + // rather than seeking, we can just read data and discard it; + // this allows to forward-seek also non-seekable streams! + char buf[BUF_TEMP_SIZE]; + size_t bytes_read; + + // read chunks of BUF_TEMP_SIZE bytes until we reach the new position + for ( ; pos >= BUF_TEMP_SIZE; pos -= bytes_read) + { + bytes_read = Read(buf, WXSIZEOF(buf)).LastRead(); + if ( m_lasterror != wxSTREAM_NO_ERROR ) + return wxInvalidOffset; + + wxASSERT(bytes_read == WXSIZEOF(buf)); + } + + // read the last 'pos' bytes + bytes_read = Read(buf, (size_t)pos).LastRead(); + if ( m_lasterror != wxSTREAM_NO_ERROR ) + return wxInvalidOffset; + + wxASSERT(bytes_read == (size_t)pos); + + // we should now have seeked to the right position... + return TellI(); + } + /* RR: A call to SeekI() will automatically invalidate any previous call to Ungetch(), otherwise it would be possible to SeekI() to one position, unread some bytes there, SeekI() to another position diff --git a/src/common/xpmdecod.cpp b/src/common/xpmdecod.cpp index 54e54c5221..82a415f696 100644 --- a/src/common/xpmdecod.cpp +++ b/src/common/xpmdecod.cpp @@ -122,9 +122,6 @@ bool wxXPMDecoder::CanRead(wxInputStream& stream) if ( !stream.Read(buf, WXSIZEOF(buf)) ) return false; - if (stream.SeekI(-(wxFileOffset)WXSIZEOF(buf), wxFromCurrent) == wxInvalidOffset) - return false; - return memcmp(buf, "/* XPM */", WXSIZEOF(buf)) == 0; } -- 2.45.2