1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-08-09 13:22:24 +03:00

0030182: Visualization, Image_AlienPixMap - support reading encoded image from memory buffer

Added two new Image_AlienPixMap::Load() methods, taking std::istream
and memory buffer (pointer, length) arguments.
This allows reading image from application memory or from file with non-zero offset.

Added Image_AlienPixMap::IsTopDownDefault() static property allowing to query rows order used by linked image library,
so that application might generate UV texture coordinates accordingly.

Added missing Release() to IWICImagingFactory instance.
This commit is contained in:
kgv
2019-03-01 01:26:20 +03:00
committed by apn
parent 7b93ae3c5e
commit 88b12b7c05
7 changed files with 523 additions and 101 deletions

View File

@@ -34,6 +34,8 @@
#include <gp.hxx>
#include <Message.hxx>
#include <Message_Messenger.hxx>
#include <NCollection_Array1.hxx>
#include <Standard_ArrayStreamBuffer.hxx>
#include <TCollection_AsciiString.hxx>
#include <TCollection_ExtendedString.hxx>
#include <OSD_OpenFile.hxx>
@@ -110,6 +112,109 @@ namespace
return FIT_UNKNOWN;
}
}
//! Wrapper for accessing C++ stream from FreeImage.
class Image_FreeImageStream
{
public:
//! Construct wrapper over input stream.
Image_FreeImageStream (std::istream& theStream)
: myIStream (&theStream), myOStream (NULL), myInitPos (theStream.tellg()) {}
//! Get io object.
FreeImageIO GetFiIO() const
{
FreeImageIO anIo;
memset (&anIo, 0, sizeof(anIo));
if (myIStream != NULL)
{
anIo.read_proc = readProc;
anIo.seek_proc = seekProc;
anIo.tell_proc = tellProc;
}
if (myOStream != NULL)
{
anIo.write_proc = writeProc;
}
return anIo;
}
public:
//! Simulate fread().
static unsigned int DLL_CALLCONV readProc (void* theBuffer, unsigned int theSize, unsigned int theCount, fi_handle theHandle)
{
Image_FreeImageStream* aThis = (Image_FreeImageStream* )theHandle;
if (aThis->myIStream == NULL)
{
return 0;
}
if (!aThis->myIStream->read ((char* )theBuffer, std::streamsize(theSize) * std::streamsize(theCount)))
{
//aThis->myIStream->clear();
}
const std::streamsize aNbRead = aThis->myIStream->gcount();
return (unsigned int )(aNbRead / theSize);
}
//! Simulate fwrite().
static unsigned int DLL_CALLCONV writeProc (void* theBuffer, unsigned int theSize, unsigned int theCount, fi_handle theHandle)
{
Image_FreeImageStream* aThis = (Image_FreeImageStream* )theHandle;
if (aThis->myOStream != NULL
&& aThis->myOStream->write ((const char* )theBuffer, std::streamsize(theSize) * std::streamsize(theCount)))
{
return theCount;
}
return 0;
}
//! Simulate fseek().
static int DLL_CALLCONV seekProc (fi_handle theHandle, long theOffset, int theOrigin)
{
Image_FreeImageStream* aThis = (Image_FreeImageStream* )theHandle;
if (aThis->myIStream == NULL)
{
return -1;
}
bool isSeekDone = false;
switch (theOrigin)
{
case SEEK_SET:
if (aThis->myIStream->seekg ((std::streamoff )aThis->myInitPos + theOffset, std::ios::beg))
{
isSeekDone = true;
}
break;
case SEEK_CUR:
if (aThis->myIStream->seekg (theOffset, std::ios::cur))
{
isSeekDone = true;
}
break;
case SEEK_END:
if (aThis->myIStream->seekg (theOffset, std::ios::end))
{
isSeekDone = true;
}
break;
}
return isSeekDone ? 0 : -1;
}
//! Simulate ftell().
static long DLL_CALLCONV tellProc (fi_handle theHandle)
{
Image_FreeImageStream* aThis = (Image_FreeImageStream* )theHandle;
const long aPos = aThis->myIStream != NULL ? (long )(aThis->myIStream->tellg() - aThis->myInitPos) : 0;
return aPos;
}
private:
std::istream* myIStream;
std::ostream* myOStream;
std::streampos myInitPos;
};
#elif defined(HAVE_WINCODEC)
//! Return a zero GUID
@@ -384,21 +489,50 @@ void Image_AlienPixMap::Clear()
#endif
}
// =======================================================================
// function : IsTopDownDefault
// purpose :
// =======================================================================
bool Image_AlienPixMap::IsTopDownDefault()
{
#ifdef HAVE_FREEIMAGE
return false;
#elif defined(HAVE_WINCODEC)
return true;
#else
return false;
#endif
}
// =======================================================================
// function : Load
// purpose :
// =======================================================================
#ifdef HAVE_FREEIMAGE
bool Image_AlienPixMap::Load (const TCollection_AsciiString& theImagePath)
bool Image_AlienPixMap::Load (const Standard_Byte* theData,
Standard_Size theLength,
const TCollection_AsciiString& theImagePath)
{
Clear();
#ifdef _WIN32
const TCollection_ExtendedString aFileNameW (theImagePath);
FREE_IMAGE_FORMAT aFIF = FreeImage_GetFileTypeU (aFileNameW.ToWideString(), 0);
#else
FREE_IMAGE_FORMAT aFIF = FreeImage_GetFileType (theImagePath.ToCString(), 0);
#endif
FREE_IMAGE_FORMAT aFIF = FIF_UNKNOWN;
FIMEMORY* aFiMem = NULL;
if (theData != NULL)
{
aFiMem = FreeImage_OpenMemory ((BYTE* )theData, (DWORD )theLength);
aFIF = FreeImage_GetFileTypeFromMemory (aFiMem, 0);
}
else
{
#ifdef _WIN32
aFIF = FreeImage_GetFileTypeU (aFileNameW.ToWideString(), 0);
#else
aFIF = FreeImage_GetFileType (theImagePath.ToCString(), 0);
#endif
}
if (aFIF == FIF_UNKNOWN)
{
// no signature? try to guess the file format from the file extension
@@ -406,10 +540,12 @@ bool Image_AlienPixMap::Load (const TCollection_AsciiString& theImagePath)
}
if ((aFIF == FIF_UNKNOWN) || !FreeImage_FIFSupportsReading (aFIF))
{
TCollection_AsciiString aMessage = "Error: image file '";
aMessage.AssignCat (theImagePath);
aMessage.AssignCat ("' has unsupported file format.");
::Message::DefaultMessenger()->Send (aMessage, Message_Fail);
::Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: image '") + theImagePath + "' has unsupported file format.",
Message_Fail);
if (aFiMem != NULL)
{
FreeImage_CloseMemory (aFiMem);
}
return false;
}
@@ -425,11 +561,21 @@ bool Image_AlienPixMap::Load (const TCollection_AsciiString& theImagePath)
aLoadFlags = ICO_MAKEALPHA;
}
#ifdef _WIN32
FIBITMAP* anImage = FreeImage_LoadU (aFIF, aFileNameW.ToWideString(), aLoadFlags);
#else
FIBITMAP* anImage = FreeImage_Load (aFIF, theImagePath.ToCString(), aLoadFlags);
#endif
FIBITMAP* anImage = NULL;
if (theData != NULL)
{
anImage = FreeImage_LoadFromMemory (aFIF, aFiMem, aLoadFlags);
FreeImage_CloseMemory (aFiMem);
aFiMem = NULL;
}
else
{
#ifdef _WIN32
anImage = FreeImage_LoadU (aFIF, aFileNameW.ToWideString(), aLoadFlags);
#else
anImage = FreeImage_Load (aFIF, theImagePath.ToCString(), aLoadFlags);
#endif
}
if (anImage == NULL)
{
TCollection_AsciiString aMessage = "Error: image file '";
@@ -445,10 +591,8 @@ bool Image_AlienPixMap::Load (const TCollection_AsciiString& theImagePath)
if (aFormat == Image_Format_UNKNOWN)
{
//anImage = FreeImage_ConvertTo24Bits (anImage);
TCollection_AsciiString aMessage = "Error: image file '";
aMessage.AssignCat (theImagePath);
aMessage.AssignCat ("' has unsupported pixel format.");
::Message::DefaultMessenger()->Send (aMessage, Message_Fail);
::Message::DefaultMessenger()->Send ( TCollection_AsciiString ("Error: image '") + theImagePath + "' has unsupported pixel format.",
Message_Fail);
return false;
}
@@ -460,25 +604,106 @@ bool Image_AlienPixMap::Load (const TCollection_AsciiString& theImagePath)
myLibImage = anImage;
return true;
}
#elif defined(HAVE_WINCODEC)
bool Image_AlienPixMap::Load (const TCollection_AsciiString& theImagePath)
bool Image_AlienPixMap::Load (std::istream& theStream,
const TCollection_AsciiString& theFileName)
{
Clear();
IWICImagingFactory* aWicImgFactory = NULL;
Image_FreeImageStream aStream (theStream);
FreeImageIO aFiIO = aStream.GetFiIO();
FREE_IMAGE_FORMAT aFIF = FreeImage_GetFileTypeFromHandle (&aFiIO, &aStream, 0);
if (aFIF == FIF_UNKNOWN)
{
// no signature? try to guess the file format from the file extension
aFIF = FreeImage_GetFIFFromFilename (theFileName.ToCString());
}
if ((aFIF == FIF_UNKNOWN) || !FreeImage_FIFSupportsReading (aFIF))
{
::Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: image stream '") + theFileName + "' has unsupported file format.",
Message_Fail);
return false;
}
int aLoadFlags = 0;
if (aFIF == FIF_GIF)
{
// 'Play' the GIF to generate each frame (as 32bpp) instead of returning raw frame data when loading
aLoadFlags = GIF_PLAYBACK;
}
else if (aFIF == FIF_ICO)
{
// convert to 32bpp and create an alpha channel from the AND-mask when loading
aLoadFlags = ICO_MAKEALPHA;
}
FIBITMAP* anImage = FreeImage_LoadFromHandle (aFIF, &aFiIO, &aStream, aLoadFlags);
if (anImage == NULL)
{
::Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: image stream '") + theFileName + "' is missing or invalid.",
Message_Fail);
return false;
}
Image_Format aFormat = convertFromFreeFormat (FreeImage_GetImageType(anImage),
FreeImage_GetColorType(anImage),
FreeImage_GetBPP (anImage));
if (aFormat == Image_Format_UNKNOWN)
{
::Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: image stream '") + theFileName + "' has unsupported pixel format.",
Message_Fail);
return false;
}
Image_PixMap::InitWrapper (aFormat, FreeImage_GetBits (anImage),
FreeImage_GetWidth (anImage), FreeImage_GetHeight (anImage), FreeImage_GetPitch (anImage));
SetTopDown (false);
// assign image after wrapper initialization (virtual Clear() called inside)
myLibImage = anImage;
return true;
}
#elif defined(HAVE_WINCODEC)
bool Image_AlienPixMap::Load (const Standard_Byte* theData,
Standard_Size theLength,
const TCollection_AsciiString& theFileName)
{
Clear();
Image_ComPtr<IWICImagingFactory> aWicImgFactory;
CoInitializeEx (NULL, COINIT_MULTITHREADED);
if (CoCreateInstance (CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&aWicImgFactory)) != S_OK)
if (CoCreateInstance (CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&aWicImgFactory.ChangePtr())) != S_OK)
{
Message::DefaultMessenger()->Send ("Error: cannot initialize WIC Imaging Factory", Message_Fail);
return false;
}
Image_ComPtr<IWICBitmapDecoder> aWicDecoder;
const TCollection_ExtendedString aFileNameW (theImagePath);
if (aWicImgFactory->CreateDecoderFromFilename (aFileNameW.ToWideString(), NULL, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &aWicDecoder.ChangePtr()) != S_OK)
Image_ComPtr<IWICStream> aWicStream;
if (theData != NULL)
{
Message::DefaultMessenger()->Send ("Error: cannot create WIC Image Decoder", Message_Fail);
return false;
if (aWicImgFactory->CreateStream (&aWicStream.ChangePtr()) != S_OK
|| aWicStream->InitializeFromMemory ((BYTE* )theData, (DWORD )theLength) != S_OK)
{
Message::DefaultMessenger()->Send ("Error: cannot initialize WIC Stream", Message_Fail);
return false;
}
if (aWicImgFactory->CreateDecoderFromStream (aWicStream.get(), NULL, WICDecodeMetadataCacheOnDemand, &aWicDecoder.ChangePtr()) != S_OK)
{
Message::DefaultMessenger()->Send ("Error: cannot create WIC Image Decoder", Message_Fail);
return false;
}
}
else
{
const TCollection_ExtendedString aFileNameW (theFileName);
if (aWicImgFactory->CreateDecoderFromFilename (aFileNameW.ToWideString(), NULL, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &aWicDecoder.ChangePtr()) != S_OK)
{
Message::DefaultMessenger()->Send ("Error: cannot create WIC Image Decoder", Message_Fail);
return false;
}
}
UINT aFrameCount = 0, aFrameSizeX = 0, aFrameSizeY = 0;
@@ -527,10 +752,45 @@ bool Image_AlienPixMap::Load (const TCollection_AsciiString& theImagePath)
SetTopDown (true);
return true;
}
#else
bool Image_AlienPixMap::Load (const TCollection_AsciiString&)
bool Image_AlienPixMap::Load (std::istream& theStream,
const TCollection_AsciiString& theFilePath)
{
Clear();
// fallback copying stream data into transient buffer
const std::streamoff aStart = theStream.tellg();
theStream.seekg (0, std::ios::end);
const Standard_Integer aLen = Standard_Integer(theStream.tellg() - aStart);
theStream.seekg (aStart);
if (aLen <= 0)
{
Message::DefaultMessenger()->Send ("Error: empty stream", Message_Fail);
return false;
}
NCollection_Array1<Standard_Byte> aBuff (1, aLen);
if (!theStream.read ((char* )&aBuff.ChangeFirst(), aBuff.Size()))
{
Message::DefaultMessenger()->Send ("Error: unable to read stream", Message_Fail);
return false;
}
return Load (&aBuff.ChangeFirst(), aBuff.Size(), theFilePath);
}
#else
bool Image_AlienPixMap::Load (std::istream& ,
const TCollection_AsciiString& )
{
Clear();
Message::DefaultMessenger()->Send ("Error: no image library available", Message_Fail);
return false;
}
bool Image_AlienPixMap::Load (const Standard_Byte* ,
Standard_Size ,
const TCollection_AsciiString& )
{
Clear();
Message::DefaultMessenger()->Send ("Error: no image library available", Message_Fail);
return false;
}
#endif
@@ -774,9 +1034,9 @@ bool Image_AlienPixMap::Save (const TCollection_AsciiString& theFileName)
return false;
}
IWICImagingFactory* aWicImgFactory = NULL;
Image_ComPtr<IWICImagingFactory> aWicImgFactory;
CoInitializeEx (NULL, COINIT_MULTITHREADED);
if (CoCreateInstance (CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&aWicImgFactory)) != S_OK)
if (CoCreateInstance (CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&aWicImgFactory.ChangePtr())) != S_OK)
{
Message::DefaultMessenger()->Send ("Error: cannot initialize WIC Imaging Factory", Message_Fail);
return false;

View File

@@ -36,6 +36,10 @@ class Image_AlienPixMap : public Image_PixMap
DEFINE_STANDARD_RTTIEXT(Image_AlienPixMap, Image_PixMap)
public:
//! Return default rows order used by underlying image library.
Standard_EXPORT static bool IsTopDownDefault();
public:
//! Empty constructor.
Standard_EXPORT Image_AlienPixMap();
@@ -43,7 +47,23 @@ public:
Standard_EXPORT virtual ~Image_AlienPixMap();
//! Read image data from file.
Standard_EXPORT bool Load (const TCollection_AsciiString& theFileName);
bool Load (const TCollection_AsciiString& theFileName)
{
return Load (NULL, 0, theFileName);
}
//! Read image data from stream.
Standard_EXPORT bool Load (std::istream& theStream,
const TCollection_AsciiString& theFileName);
//! Read image data from memory buffer.
//! @param theData memory pointer to read from;
//! when NULL, function will attempt to open theFileName file
//! @param theLength memory buffer length
//! @param theFileName optional file name
Standard_EXPORT bool Load (const Standard_Byte* theData,
Standard_Size theLength,
const TCollection_AsciiString& theFileName);
//! Write image data to file using file extension to determine compression format.
Standard_EXPORT bool Save (const TCollection_AsciiString& theFileName);