From b47b7e69f7936aef76be71ff69f722675a5476b6 Mon Sep 17 00:00:00 2001 From: mzernova Date: Fri, 19 Nov 2021 07:11:21 +0000 Subject: [PATCH] 0031956: Visualization - provide Image_AlienPixMap::Save() writing into a memory buffer instead of a file Added two new Image_AlienPixMap::Save() overloads, taking std::ostream or memory buffer arguments. --- src/Image/Image_AlienPixMap.cxx | 732 ++++++++++++++++++++++++-------- src/Image/Image_AlienPixMap.hxx | 57 ++- src/QABugs/QABugs_1.cxx | 117 ++++- tests/v3d/bugs/bug31956 | 41 ++ 4 files changed, 750 insertions(+), 197 deletions(-) create mode 100644 tests/v3d/bugs/bug31956 diff --git a/src/Image/Image_AlienPixMap.cxx b/src/Image/Image_AlienPixMap.cxx index 99b84acebd..f869e8fc16 100644 --- a/src/Image/Image_AlienPixMap.cxx +++ b/src/Image/Image_AlienPixMap.cxx @@ -135,6 +135,10 @@ namespace Image_FreeImageStream (std::istream& theStream) : myIStream (&theStream), myOStream (NULL), myInitPos (theStream.tellg()) {} + //! Construct wrapper over output stream. + Image_FreeImageStream (std::ostream& theStream) + : myIStream (NULL), myOStream (&theStream), myInitPos (theStream.tellp()) {} + //! Get io object. FreeImageIO GetFiIO() const { @@ -143,12 +147,15 @@ namespace if (myIStream != NULL) { anIo.read_proc = readProc; - anIo.seek_proc = seekProc; - anIo.tell_proc = tellProc; + anIo.seek_proc = seekProcIn; + anIo.tell_proc = tellProcIn; } if (myOStream != NULL) { anIo.write_proc = writeProc; + // seek and tell are also used for saving in some formats (.tif for example) + anIo.seek_proc = seekProcOut; + anIo.tell_proc = tellProcOut; } return anIo; } @@ -183,7 +190,7 @@ namespace } //! Simulate fseek(). - static int DLL_CALLCONV seekProc (fi_handle theHandle, long theOffset, int theOrigin) + static int DLL_CALLCONV seekProcIn (fi_handle theHandle, long theOffset, int theOrigin) { Image_FreeImageStream* aThis = (Image_FreeImageStream* )theHandle; if (aThis->myIStream == NULL) @@ -216,13 +223,53 @@ namespace return isSeekDone ? 0 : -1; } + static int DLL_CALLCONV seekProcOut (fi_handle theHandle, long theOffset, int theOrigin) + { + Image_FreeImageStream* aThis = (Image_FreeImageStream* )theHandle; + if (aThis->myOStream == NULL) + { + return -1; + } + + bool isSeekDone = false; + switch (theOrigin) + { + case SEEK_SET: + if (aThis->myOStream->seekp ((std::streamoff )aThis->myInitPos + theOffset, std::ios::beg)) + { + isSeekDone = true; + } + break; + case SEEK_CUR: + if (aThis->myOStream->seekp (theOffset, std::ios::cur)) + { + isSeekDone = true; + } + break; + case SEEK_END: + if (aThis->myOStream->seekp (theOffset, std::ios::end)) + { + isSeekDone = true; + } + break; + } + return isSeekDone ? 0 : -1; + } + //! Simulate ftell(). - static long DLL_CALLCONV tellProc (fi_handle theHandle) + static long DLL_CALLCONV tellProcIn (fi_handle theHandle) { Image_FreeImageStream* aThis = (Image_FreeImageStream* )theHandle; const long aPos = aThis->myIStream != NULL ? (long )(aThis->myIStream->tellg() - aThis->myInitPos) : 0; return aPos; } + + static long DLL_CALLCONV tellProcOut (fi_handle theHandle) + { + Image_FreeImageStream* aThis = (Image_FreeImageStream* )theHandle; + const long aPos = aThis->myOStream != NULL ? (long )(aThis->myOStream->tellp() - aThis->myInitPos) : 0; + return aPos; + } private: std::istream* myIStream; std::ostream* myOStream; @@ -238,6 +285,37 @@ namespace return aGuid; } + //! Returns GUID of image format from file name + static GUID getFileFormatFromName (const TCollection_AsciiString& theFileName) + { + TCollection_AsciiString aFileNameLower = theFileName; + aFileNameLower.LowerCase(); + GUID aFileFormat = getNullGuid(); + if (aFileNameLower.EndsWith (".bmp")) + { + aFileFormat = GUID_ContainerFormatBmp; + } + else if (aFileNameLower.EndsWith (".png")) + { + aFileFormat = GUID_ContainerFormatPng; + } + else if (aFileNameLower.EndsWith (".jpg") + || aFileNameLower.EndsWith (".jpeg")) + { + aFileFormat = GUID_ContainerFormatJpeg; + } + else if (aFileNameLower.EndsWith (".tiff") + || aFileNameLower.EndsWith (".tif")) + { + aFileFormat = GUID_ContainerFormatTiff; + } + else if (aFileNameLower.EndsWith (".gif")) + { + aFileFormat = GUID_ContainerFormatGif; + } + return aFileFormat; + } + //! Sentry over IUnknown pointer. template class Image_ComPtr { @@ -346,7 +424,11 @@ namespace // purpose : // ======================================================================= Image_AlienPixMap::Image_AlienPixMap() +#ifdef HAVE_WINCODEC +: myPalette (NULL) +#else : myLibImage (NULL) +#endif { SetTopDown (false); } @@ -505,6 +587,12 @@ void Image_AlienPixMap::Clear() FreeImage_Unload (myLibImage); myLibImage = NULL; } +#elif defined(HAVE_WINCODEC) + if (myPalette != NULL) + { + myPalette->Release(); + myPalette = NULL; + } #elif defined(__EMSCRIPTEN__) if (myLibImage != NULL) { @@ -535,7 +623,7 @@ bool Image_AlienPixMap::IsTopDownDefault() // ======================================================================= #ifdef HAVE_FREEIMAGE bool Image_AlienPixMap::Load (const Standard_Byte* theData, - Standard_Size theLength, + const Standard_Size theLength, const TCollection_AsciiString& theImagePath) { Clear(); @@ -705,7 +793,7 @@ bool Image_AlienPixMap::Load (std::istream& theStream, #elif defined(HAVE_WINCODEC) bool Image_AlienPixMap::Load (const Standard_Byte* theData, - Standard_Size theLength, + const Standard_Size theLength, const TCollection_AsciiString& theFileName) { Clear(); @@ -751,7 +839,7 @@ bool Image_AlienPixMap::Load (const Standard_Byte* theData, || aFrameCount < 1 || aWicDecoder->GetFrame (0, &aWicFrameDecode.ChangePtr()) != S_OK || aWicFrameDecode->GetSize (&aFrameSizeX, &aFrameSizeY) != S_OK - || aWicFrameDecode->GetPixelFormat (&aWicPixelFormat)) + || aWicFrameDecode->GetPixelFormat (&aWicPixelFormat) != S_OK) { Message::SendFail ("Error: cannot get WIC Image Frame"); return false; @@ -768,7 +856,6 @@ bool Image_AlienPixMap::Load (const Standard_Byte* theData, Message::SendFail ("Error: cannot convert WIC Image Frame to RGB format"); return false; } - aWicFrameDecode.Nullify(); } if (!Image_PixMap::InitTrash (aPixelFormat, aFrameSizeX, aFrameSizeY)) @@ -777,17 +864,37 @@ bool Image_AlienPixMap::Load (const Standard_Byte* theData, return false; } + TCollection_AsciiString aFileNameLower = theFileName; + aFileNameLower.LowerCase(); + if (aFileNameLower.EndsWith (".gif") + && (aWicImgFactory->CreatePalette (&myPalette) != S_OK + || aWicFrameDecode->CopyPalette (myPalette) != S_OK)) + { + Message::SendFail ("Error: cannot get palette for GIF image"); + return false; + } + IWICBitmapSource* aWicSrc = aWicFrameDecode.get(); if(!aWicConvertedFrame.IsNull()) { aWicSrc = aWicConvertedFrame.get(); } + + IWICBitmapFlipRotator* aRotator; + bool isTopDown = true; + if (aWicImgFactory->CreateBitmapFlipRotator (&aRotator) == S_OK + && aRotator->Initialize (aWicSrc, WICBitmapTransformFlipVertical) == S_OK) + { + isTopDown = false; + aWicSrc = aRotator; + } + if (aWicSrc->CopyPixels (NULL, (UINT )SizeRowBytes(), (UINT )SizeBytes(), ChangeData()) != S_OK) { Message::SendFail ("Error: cannot copy pixels from WIC Image"); return false; } - SetTopDown (true); + SetTopDown (isTopDown); return true; } bool Image_AlienPixMap::Load (std::istream& theStream, @@ -824,7 +931,7 @@ bool Image_AlienPixMap::Load (std::istream& , return false; } bool Image_AlienPixMap::Load (const Standard_Byte* theData, - Standard_Size theLength, + const Standard_Size theLength, const TCollection_AsciiString& theImagePath) { Clear(); @@ -857,7 +964,7 @@ bool Image_AlienPixMap::Load (std::istream& , return false; } bool Image_AlienPixMap::Load (const Standard_Byte* , - Standard_Size , + const Standard_Size , const TCollection_AsciiString& ) { Clear(); @@ -907,11 +1014,52 @@ bool Image_AlienPixMap::savePPM (const TCollection_AsciiString& theFileName) con return true; } +// ======================================================================= +// function : convertData +// purpose : +// ======================================================================= +#ifdef HAVE_WINCODEC +static bool convertData (const Image_AlienPixMap& theSrcPixMapData, + const WICPixelFormatGUID& theFormat, + IWICImagingFactory& theWicImgFactory, + Image_PixMapData& theDstPixMapData) +{ + const UINT aSizeRowBytes = (UINT)theSrcPixMapData.SizeRowBytes(); + const UINT aSizeBytes = (UINT)theSrcPixMapData.SizeBytes(); + + Image_ComPtr anSrcImg; + Image_ComPtr aWicFormatConverter; + HRESULT anHResult = theWicImgFactory.CreateBitmapFromMemory ((UINT)theSrcPixMapData.SizeX(), (UINT)theSrcPixMapData.SizeY(), + convertToWicFormat (theSrcPixMapData.Format()), + aSizeRowBytes, aSizeBytes, + (BYTE*)theSrcPixMapData.Data(), &anSrcImg.ChangePtr()); + if (anHResult != S_OK + || theWicImgFactory.CreateFormatConverter (&aWicFormatConverter.ChangePtr()) != S_OK + || aWicFormatConverter->Initialize (anSrcImg.get(), theFormat, WICBitmapDitherTypeNone, theSrcPixMapData.GetPalette(), 0.0f, WICBitmapPaletteTypeCustom) != S_OK) + { + Message::SendFail ("Error: cannot convert WIC Image Frame to required format"); + return false; + } + + theDstPixMapData.Init (Image_PixMap::DefaultAllocator(), 1, theSrcPixMapData.SizeXYZ(), aSizeRowBytes, NULL); + + if (aWicFormatConverter->CopyPixels (NULL, aSizeRowBytes, aSizeBytes, theDstPixMapData.ChangeData()) != S_OK) + { + Message::SendFail ("Error: cannot copy pixels from WIC Image"); + return false; + } + + return true; +} +#endif + // ======================================================================= // function : Save // purpose : // ======================================================================= -bool Image_AlienPixMap::Save (const TCollection_AsciiString& theFileName) +bool Image_AlienPixMap::Save (Standard_Byte* theBuffer, + const Standard_Size theLength, + const TCollection_AsciiString& theFileName) { #ifdef HAVE_FREEIMAGE if (myLibImage == NULL) @@ -939,10 +1087,399 @@ bool Image_AlienPixMap::Save (const TCollection_AsciiString& theFileName) SetTopDown (false); } + FIBITMAP* anImageToDump = getImageToDump (anImageFormat); + + if (anImageToDump == NULL) + { + return false; + } + + bool isSaved = false; + if (theBuffer != NULL) + { + // a memory buffer wrapped by FreeImage is read only (images can be loaded but not be saved) + // so we call FreeImage_OpenMemory() with default arguments and just memcpy in requsted buffer. + FIMEMORY* aFiMem = FreeImage_OpenMemory(); + isSaved = (FreeImage_SaveToMemory (anImageFormat, anImageToDump, aFiMem) != FALSE); + BYTE* aData = NULL; + DWORD aSize; + FreeImage_AcquireMemory (aFiMem, &aData, &aSize); + if (aSize > theLength) + { + Message::SendFail ("Error: memory buffer too small for storing image"); + return false; + } + memcpy (theBuffer, aData, aSize); + FreeImage_CloseMemory (aFiMem); + } + else + { +#ifdef _WIN32 + isSaved = (FreeImage_SaveU (anImageFormat, anImageToDump, aFileNameW.ToWideString ()) != FALSE); +#else + isSaved = (FreeImage_Save (anImageFormat, anImageToDump, theFileName.ToCString ()) != FALSE); +#endif + } + if (anImageToDump != myLibImage) + { + FreeImage_Unload (anImageToDump); + } + return isSaved; + +#elif defined(HAVE_WINCODEC) + + TCollection_AsciiString aFileNameLower = theFileName; + aFileNameLower.LowerCase(); + if (aFileNameLower.EndsWith (".ppm")) + { + return savePPM (theFileName); + } + + GUID aFileFormat = getFileFormatFromName (theFileName); + if (aFileFormat == getNullGuid()) + { + Message::SendFail ("Error: unsupported image format"); + return false; + } + + Image_ComPtr aWicImgFactory; + CoInitializeEx (NULL, COINIT_MULTITHREADED); + if (CoCreateInstance (CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&aWicImgFactory.ChangePtr())) != S_OK) + { + Message::SendFail ("Error: cannot initialize WIC Imaging Factory"); + return false; + } + + WICPixelFormatGUID aWicPixelFormat = convertToWicFormat (myImgFormat); + if (aWicPixelFormat == getNullGuid()) + { + Message::SendFail ("Error: unsupported pixel format"); + return false; + } + + Image_PixMapData* aPixMapData = &myData; + Image_PixMapData aConvertedData; + if (aFileFormat == GUID_ContainerFormatGif) + { + aWicPixelFormat = GUID_WICPixelFormat8bppIndexed; + convertData (*this, aWicPixelFormat, *aWicImgFactory, aConvertedData); + aPixMapData = &aConvertedData; + } + + Image_ComPtr aWicStream; + Image_ComPtr aWicEncoder; + const TCollection_ExtendedString aFileNameW (theFileName); + if (theBuffer != NULL) + { + if (aWicImgFactory->CreateStream (&aWicStream.ChangePtr()) != S_OK + || aWicStream->InitializeFromMemory (theBuffer,(DWORD )theLength) != S_OK) + { + Message::SendFail ("Error: cannot create WIC Memory Stream"); + return false; + } + } + else + { + if (aWicImgFactory->CreateStream (&aWicStream.ChangePtr()) != S_OK + || aWicStream->InitializeFromFilename (aFileNameW.ToWideString(), GENERIC_WRITE) != S_OK) + { + Message::SendFail ("Error: cannot create WIC File Stream"); + return false; + } + } + if (aWicImgFactory->CreateEncoder (aFileFormat, NULL, &aWicEncoder.ChangePtr()) != S_OK + || aWicEncoder->Initialize (aWicStream.get(), WICBitmapEncoderNoCache) != S_OK) + { + Message::SendFail ("Error: cannot create WIC Encoder"); + return false; + } + + WICPixelFormatGUID aWicPixelFormatRes = aWicPixelFormat; + Image_ComPtr aWicFrameEncode; + if (aWicEncoder->CreateNewFrame (&aWicFrameEncode.ChangePtr(), NULL) != S_OK + || aWicFrameEncode->Initialize (NULL) != S_OK + || aWicFrameEncode->SetSize ((UINT )SizeX(), (UINT )SizeY()) != S_OK + || aWicFrameEncode->SetPixelFormat (&aWicPixelFormatRes) != S_OK) + { + Message::SendFail ("Error: cannot create WIC Frame"); + return false; + } + + if (aFileFormat == GUID_ContainerFormatGif + && (myPalette == NULL + || aWicFrameEncode->SetPalette (myPalette) != S_OK)) + { + Message::SendFail ("Error: cannot set palette"); + return false; + } + + if (aWicPixelFormatRes != aWicPixelFormat) + { + Message::SendFail ("Error: pixel format is unsupported by image format"); + return false; + } + + if (IsTopDown()) + { + if (aWicFrameEncode->WritePixels ((UINT )SizeY(), (UINT )SizeRowBytes(), (UINT )SizeBytes(), (BYTE* )aPixMapData->Data()) != S_OK) + { + Message::SendFail ("Error: cannot write pixels to WIC Frame"); + return false; + } + } + else + { + for (Standard_Size aRow = 0; aRow < SizeY(); ++aRow) + { + if (aWicFrameEncode->WritePixels (1, (UINT )SizeRowBytes(), (UINT )SizeRowBytes(), (BYTE* )aPixMapData->Row (aRow)) != S_OK) + { + Message::SendFail ("Error: cannot write pixels to WIC Frame"); + return false; + } + } + } + if (aWicFrameEncode->Commit() != S_OK + || aWicEncoder->Commit() != S_OK) + { + Message::SendFail ("Error: cannot commit data to WIC Frame"); + return false; + } + if (aWicStream->Commit (STGC_DEFAULT) != S_OK) + { + //Message::Send ("Error: cannot commit data to WIC File Stream", Message_Fail); + //return false; + } + return true; +#else + if (theBuffer != NULL) + { + Message::SendFail ("Error: no image library available"); + return false; + } + const Standard_Integer aLen = theFileName.Length(); + if ((aLen >= 4) && (theFileName.Value (aLen - 3) == '.') + && strcasecmp( theFileName.ToCString() + aLen - 3, "ppm") == 0 ) + { + return savePPM (theFileName); + } + Message::SendTrace ("Image_PixMap, no image library available! Image saved in PPM format"); + return savePPM (theFileName); +#endif +} + +bool Image_AlienPixMap::Save (std::ostream& theStream, const TCollection_AsciiString& theExtension) +{ +#ifdef HAVE_FREEIMAGE + if (myLibImage == NULL) + { + return false; + } + +#ifdef _WIN32 + const TCollection_ExtendedString anExtW (theExtension.ToCString(), Standard_True); + FREE_IMAGE_FORMAT anImageFormat = FreeImage_GetFIFFromFilenameU (anExtW.ToWideString()); +#else + FREE_IMAGE_FORMAT anImageFormat = FreeImage_GetFIFFromFilename (theExtension.ToCString()); +#endif + if (anImageFormat == FIF_UNKNOWN) + { +#ifdef OCCT_DEBUG + std::cerr << "Image_PixMap, image format doesn't supported!\n"; +#endif + return false; + } + + if (IsTopDown()) + { + FreeImage_FlipVertical (myLibImage); + SetTopDown (false); + } + + FIBITMAP* anImageToDump = getImageToDump (anImageFormat); + + if (anImageToDump == NULL) + { + return false; + } + + bool isSaved = false; + Image_FreeImageStream aStream (theStream); + FreeImageIO anIO = aStream.GetFiIO(); + + isSaved = (FreeImage_SaveToHandle(anImageFormat, anImageToDump, &anIO, &aStream) != FALSE); + + if (anImageToDump != myLibImage) + { + FreeImage_Unload (anImageToDump); + } + return isSaved; +#elif defined(HAVE_WINCODEC) + GUID aFileFormat = getFileFormatFromName (theExtension); + if (aFileFormat == getNullGuid()) + { + Message::SendFail ("Error: unsupported image format"); + return false; + } + + Image_ComPtr aWicImgFactory; + CoInitializeEx (NULL, COINIT_MULTITHREADED); + if (CoCreateInstance (CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&aWicImgFactory.ChangePtr())) != S_OK) + { + Message::SendFail ("Error: cannot initialize WIC Imaging Factory"); + return false; + } + + WICPixelFormatGUID aWicPixelFormat = convertToWicFormat (myImgFormat); + if (aWicPixelFormat == getNullGuid()) + { + Message::SendFail ("Error: unsupported pixel format"); + return false; + } + + Image_PixMapData* aPixMapData = &myData; + Image_PixMapData aConvertedData; + if (aFileFormat == GUID_ContainerFormatGif) + { + aWicPixelFormat = GUID_WICPixelFormat8bppIndexed; + convertData (*this, aWicPixelFormat, *aWicImgFactory, aConvertedData); + aPixMapData = &aConvertedData; + } + + Image_ComPtr aStream; + Image_ComPtr aWicEncoder; + + if (CreateStreamOnHGlobal (NULL, Standard_True, &aStream.ChangePtr()) != S_OK) + { + Message::SendFail ("Error: cannot create Stream on global"); + return false; + } + + if (aWicImgFactory->CreateEncoder (aFileFormat, NULL, &aWicEncoder.ChangePtr()) != S_OK + || aWicEncoder->Initialize (aStream.get(), WICBitmapEncoderNoCache) != S_OK) + { + Message::SendFail ("Error: cannot create WIC Encoder"); + return false; + } + + WICPixelFormatGUID aWicPixelFormatRes = aWicPixelFormat; + Image_ComPtr aWicFrameEncode; + if (aWicEncoder->CreateNewFrame (&aWicFrameEncode.ChangePtr(), NULL) != S_OK + || aWicFrameEncode->Initialize (NULL) != S_OK + || aWicFrameEncode->SetSize ((UINT )SizeX(), (UINT )SizeY()) != S_OK + || aWicFrameEncode->SetPixelFormat (&aWicPixelFormatRes) != S_OK) + { + Message::SendFail ("Error: cannot create WIC Frame"); + return false; + } + + if (aFileFormat == GUID_ContainerFormatGif + && (myPalette == NULL + || aWicFrameEncode->SetPalette (myPalette) != S_OK)) + { + Message::SendFail ("Error: cannot set palette"); + return false; + } + + if (aWicPixelFormatRes != aWicPixelFormat) + { + Message::SendFail ("Error: pixel format is unsupported by image format"); + return false; + } + + if (IsTopDown()) + { + if (aWicFrameEncode->WritePixels ((UINT )SizeY(), (UINT )SizeRowBytes(), (UINT )SizeBytes(), (BYTE* )aPixMapData->Data()) != S_OK) + { + Message::SendFail ("Error: cannot write pixels to WIC Frame"); + return false; + } + } + else + { + for (Standard_Size aRow = 0; aRow < SizeY(); ++aRow) + { + if (aWicFrameEncode->WritePixels (1, (UINT )SizeRowBytes(), (UINT )SizeRowBytes(), (BYTE* )aPixMapData->Row (aRow)) != S_OK) + { + Message::SendFail ("Error: cannot write pixels to WIC Frame"); + return false; + } + } + } + if (aWicFrameEncode->Commit() != S_OK + || aWicEncoder->Commit() != S_OK) + { + Message::SendFail ("Error: cannot commit data to WIC Frame"); + return false; + } + if (aStream->Commit (STGC_DEFAULT) != S_OK) + { + //Message::Send ("Error: cannot commit data to Stream", Message_Fail); + //return false; + } + + // WIC doesn't have the way to encode image directly in std::ostream + // so we use a workaround to transfer data from IStream to std::ostream + STATSTG aStat; + if (aStream->Stat (&aStat, STATFLAG_NONAME) != S_OK) + { + Message::SendFail ("Error: cannot get stat from stream"); + return false; + } + HGLOBAL aMem; + if (GetHGlobalFromStream (aStream.get(), &aMem) != S_OK) + { + Message::SendFail ("Error: cannot get global from stream"); + return false; + } + + LPVOID aData = GlobalLock (aMem); + if (aData == NULL) + { + Message::SendFail ("Error: cannot lock global"); + return false; + } + if (!theStream.write ((const char* )aData, aStat.cbSize.QuadPart)) + { + Message::SendFail ("Error: cannot write data to ostream"); + return false; + } + if (GlobalUnlock (aMem) == 0 && GetLastError() != NO_ERROR) + { + Message::SendFail ("Error: cannot unlock global"); + return false; + } + return true; +#else + Message::SendFail ("Error: no image library available"); + return false; +#endif +} + +// ======================================================================= +// function : AdjustGamma +// purpose : +// ======================================================================= +bool Image_AlienPixMap::AdjustGamma (const Standard_Real theGammaCorr) +{ +#ifdef HAVE_FREEIMAGE + return FreeImage_AdjustGamma (myLibImage, theGammaCorr) != FALSE; +#else + (void )theGammaCorr; + return false; +#endif +} + +#ifdef HAVE_FREEIMAGE +// ======================================================================= +// function : GetImageToDump +// purpose : +// ======================================================================= +FIBITMAP* Image_AlienPixMap::getImageToDump (const Standard_Integer theFormat) +{ + FIBITMAP* anImageToDump = myLibImage; // FreeImage doesn't provide flexible format conversion API // so we should perform multiple conversions in some cases! - FIBITMAP* anImageToDump = myLibImage; - switch (anImageFormat) + switch (theFormat) { case FIF_PNG: case FIF_BMP: @@ -973,7 +1510,7 @@ bool Image_AlienPixMap::Save (const TCollection_AsciiString& theFileName) aTmpBitmap = FreeImage_ConvertToType (myLibImage, FIT_BITMAP); if (aTmpBitmap == NULL) { - return false; + return NULL; } } @@ -986,7 +1523,7 @@ bool Image_AlienPixMap::Save (const TCollection_AsciiString& theFileName) } if (aTmpBitmap24 == NULL) { - return false; + return NULL; } aTmpBitmap = aTmpBitmap24; } @@ -1031,7 +1568,7 @@ bool Image_AlienPixMap::Save (const TCollection_AsciiString& theFileName) anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_BITMAP); if (anImageToDump == NULL) { - return false; + return NULL; } } @@ -1044,170 +1581,13 @@ bool Image_AlienPixMap::Save (const TCollection_AsciiString& theFileName) } if (aTmpBitmap24 == NULL) { - return false; + return NULL; } anImageToDump = aTmpBitmap24; } break; } } - - if (anImageToDump == NULL) - { - return false; - } - -#ifdef _WIN32 - bool isSaved = (FreeImage_SaveU (anImageFormat, anImageToDump, aFileNameW.ToWideString()) != FALSE); -#else - bool isSaved = (FreeImage_Save (anImageFormat, anImageToDump, theFileName.ToCString()) != FALSE); -#endif - if (anImageToDump != myLibImage) - { - FreeImage_Unload (anImageToDump); - } - return isSaved; - -#elif defined(HAVE_WINCODEC) - - TCollection_AsciiString aFileNameLower = theFileName; - aFileNameLower.LowerCase(); - GUID aFileFormat = getNullGuid(); - if (aFileNameLower.EndsWith (".ppm")) - { - return savePPM (theFileName); - } - else if (aFileNameLower.EndsWith (".bmp")) - { - aFileFormat = GUID_ContainerFormatBmp; - } - else if (aFileNameLower.EndsWith (".png")) - { - aFileFormat = GUID_ContainerFormatPng; - } - else if (aFileNameLower.EndsWith (".jpg") - || aFileNameLower.EndsWith (".jpeg")) - { - aFileFormat = GUID_ContainerFormatJpeg; - } - else if (aFileNameLower.EndsWith (".tiff") - || aFileNameLower.EndsWith (".tif")) - { - aFileFormat = GUID_ContainerFormatTiff; - } - else if (aFileNameLower.EndsWith (".gif")) - { - aFileFormat = GUID_ContainerFormatGif; - } - - if (aFileFormat == getNullGuid()) - { - Message::SendFail ("Error: unsupported image format"); - return false; - } - - Image_ComPtr aWicImgFactory; - CoInitializeEx (NULL, COINIT_MULTITHREADED); - if (CoCreateInstance (CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&aWicImgFactory.ChangePtr())) != S_OK) - { - Message::SendFail ("Error: cannot initialize WIC Imaging Factory"); - return false; - } - - Image_ComPtr aWicFileStream; - Image_ComPtr aWicEncoder; - const TCollection_ExtendedString aFileNameW (theFileName); - if (aWicImgFactory->CreateStream (&aWicFileStream.ChangePtr()) != S_OK - || aWicFileStream->InitializeFromFilename (aFileNameW.ToWideString(), GENERIC_WRITE) != S_OK) - { - Message::SendFail ("Error: cannot create WIC File Stream"); - return false; - } - if (aWicImgFactory->CreateEncoder (aFileFormat, NULL, &aWicEncoder.ChangePtr()) != S_OK - || aWicEncoder->Initialize (aWicFileStream.get(), WICBitmapEncoderNoCache) != S_OK) - { - Message::SendFail ("Error: cannot create WIC Encoder"); - return false; - } - - const WICPixelFormatGUID aWicPixelFormat = convertToWicFormat (myImgFormat); - if (aWicPixelFormat == getNullGuid()) - { - Message::SendFail ("Error: unsupported pixel format"); - return false; - } - - WICPixelFormatGUID aWicPixelFormatRes = aWicPixelFormat; - Image_ComPtr aWicFrameEncode; - if (aWicEncoder->CreateNewFrame (&aWicFrameEncode.ChangePtr(), NULL) != S_OK - || aWicFrameEncode->Initialize (NULL) != S_OK - || aWicFrameEncode->SetSize ((UINT )SizeX(), (UINT )SizeY()) != S_OK - || aWicFrameEncode->SetPixelFormat (&aWicPixelFormatRes) != S_OK) - { - Message::SendFail ("Error: cannot create WIC Frame"); - return false; - } - - if (aWicPixelFormatRes != aWicPixelFormat) - { - Message::SendFail ("Error: pixel format is unsupported by image format"); - return false; - } - - if (IsTopDown()) - { - if (aWicFrameEncode->WritePixels ((UINT )SizeY(), (UINT )SizeRowBytes(), (UINT )SizeBytes(), (BYTE* )Data()) != S_OK) - { - Message::SendFail ("Error: cannot write pixels to WIC Frame"); - return false; - } - } - else - { - for (Standard_Size aRow = 0; aRow < SizeY(); ++aRow) - { - if (aWicFrameEncode->WritePixels (1, (UINT )SizeRowBytes(), (UINT )SizeRowBytes(), (BYTE* )Row (aRow)) != S_OK) - { - Message::SendFail ("Error: cannot write pixels to WIC Frame"); - return false; - } - } - } - - if (aWicFrameEncode->Commit() != S_OK - || aWicEncoder->Commit() != S_OK) - { - Message::SendFail ("Error: cannot commit data to WIC Frame"); - return false; - } - if (aWicFileStream->Commit (STGC_DEFAULT) != S_OK) - { - //Message::Send ("Error: cannot commit data to WIC File Stream", Message_Fail); - //return false; - } - return true; -#else - const Standard_Integer aLen = theFileName.Length(); - if ((aLen >= 4) && (theFileName.Value (aLen - 3) == '.') - && strcasecmp( theFileName.ToCString() + aLen - 3, "ppm") == 0 ) - { - return savePPM (theFileName); - } - Message::SendTrace ("Image_PixMap, no image library available! Image saved in PPM format"); - return savePPM (theFileName); -#endif + return anImageToDump; } - -// ======================================================================= -// function : AdjustGamma -// purpose : -// ======================================================================= -bool Image_AlienPixMap::AdjustGamma (const Standard_Real theGammaCorr) -{ -#ifdef HAVE_FREEIMAGE - return FreeImage_AdjustGamma (myLibImage, theGammaCorr) != FALSE; -#else - (void )theGammaCorr; - return false; #endif -} diff --git a/src/Image/Image_AlienPixMap.hxx b/src/Image/Image_AlienPixMap.hxx index 9c61d08ef7..dc986796f6 100644 --- a/src/Image/Image_AlienPixMap.hxx +++ b/src/Image/Image_AlienPixMap.hxx @@ -19,6 +19,7 @@ #include class TCollection_AsciiString; +struct IWICPalette; struct FIBITMAP; //! Image class that support file reading/writing operations using auxiliary image library. @@ -57,21 +58,41 @@ public: 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 + //! @param[in] theData memory pointer to read from; + //! when NULL, function will attempt to open theFileName file + //! @param[in] theLength memory buffer length + //! @param[in] theFileName optional file name Standard_EXPORT bool Load (const Standard_Byte* theData, - Standard_Size theLength, + const 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); + //! Write image data to file. + //! @param[in] theFileName file name to save + bool Save (const TCollection_AsciiString& theFileName) + { + return Save (NULL, 0, theFileName); + } + + //! Write image data to stream. + //! @param[out] theStream stream where to write + //! @param[in] theExtension image format + Standard_EXPORT bool Save (std::ostream& theStream, + const TCollection_AsciiString& theExtension); + + //! Write image data to file or memory buffer using file extension to determine format. + //! @param[out] theBuffer buffer pointer where to write + //! when NULL, function write image data to theFileName file + //! @param[in] theLength memory buffer length + //! @param[in] theFileName file name to save; + //! when theBuffer isn't NULL used only to determine format + Standard_EXPORT bool Save (Standard_Byte* theBuffer, + const Standard_Size theLength, + const TCollection_AsciiString& theFileName); //! Initialize image plane with required dimensions. - //! thePixelFormat - if specified pixel format doesn't supported by image library - //! than nearest supported will be used instead! - //! theSizeRowBytes - may be ignored by this class and required alignment will be used instead! + //! @param[in] thePixelFormat if specified pixel format doesn't supported by image library + //! than nearest supported will be used instead! + //! @param[in] theSizeRowBytes may be ignored by this class and required alignment will be used instead! Standard_EXPORT virtual bool InitTrash (Image_Format thePixelFormat, const Standard_Size theSizeX, const Standard_Size theSizeY, @@ -84,12 +105,13 @@ public: Standard_EXPORT virtual void Clear() Standard_OVERRIDE; //! Performs gamma correction on image. - //! theGamma - gamma value to use; a value of 1.0 leaves the image alone + //! @param[in] theGamma - gamma value to use; a value of 1.0 leaves the image alone Standard_EXPORT bool AdjustGamma (const Standard_Real theGammaCorr); -private: - - FIBITMAP* myLibImage; +#if !defined(HAVE_FREEIMAGE) && defined(_WIN32) + //! Returns image palette. + IWICPalette* GetPalette() const { return myPalette; } +#endif private: @@ -108,6 +130,13 @@ private: //! Built-in PPM export Standard_EXPORT bool savePPM (const TCollection_AsciiString& theFileName) const; + FIBITMAP* getImageToDump (const Standard_Integer theFormat); + +private: + + FIBITMAP* myLibImage; + IWICPalette* myPalette; + }; DEFINE_STANDARD_HANDLE(Image_AlienPixMap, Image_PixMap) diff --git a/src/QABugs/QABugs_1.cxx b/src/QABugs/QABugs_1.cxx index de3d1d74f8..a7a78896ee 100644 --- a/src/QABugs/QABugs_1.cxx +++ b/src/QABugs/QABugs_1.cxx @@ -387,11 +387,11 @@ static Standard_Integer OCC361bug (Draw_Interpretor& di, Standard_Integer nb, co //function : OCC30182 //purpose : Testing different interfaces of Image_AlienPixMap::Load() //======================================================================= -static Standard_Integer OCC30182 (Draw_Interpretor& , Standard_Integer theNbArgs, const char** theArgVec) +static Standard_Integer OCC30182 (Draw_Interpretor& di, Standard_Integer theNbArgs, const char** theArgVec) { if (ViewerTest::CurrentView().IsNull()) { - std::cout << "Error: no active view\n"; + di << "Error: no active view\n"; return 1; } @@ -430,13 +430,13 @@ static Standard_Integer OCC30182 (Draw_Interpretor& , Standard_Integer theNbArgs } else { - std::cout << "Syntax error at '" << anArg << "'\n"; + di << "Syntax error at '" << anArg << "'\n"; return 1; } } if (anImgPath.IsEmpty()) { - std::cout << "Syntax error: wrong number of arguments\n"; + di << "Syntax error: wrong number of arguments\n"; return 1; } @@ -454,7 +454,7 @@ static Standard_Integer OCC30182 (Draw_Interpretor& , Standard_Integer theNbArgs std::shared_ptr aFile = aFileSystem->OpenIStream (anImgPath, std::ios::in | std::ios::binary); if (aFile.get() == NULL) { - std::cout << "Syntax error: image file '" << anImgPath << "' cannot be found\n"; + di << "Syntax error: image file '" << anImgPath << "' cannot be found\n"; return 1; } if (anOffset != 0) @@ -469,13 +469,13 @@ static Standard_Integer OCC30182 (Draw_Interpretor& , Standard_Integer theNbArgs aFile->seekg (anOffset); if (aLen <= 0) { - std::cout << "Syntax error: wrong offset\n"; + di << "Syntax error: wrong offset\n"; return 1; } NCollection_Array1 aBuff (1, aLen); if (!aFile->read ((char* )&aBuff.ChangeFirst(), aBuff.Size())) { - std::cout << "Error: unable to read file\n"; + di << "Error: unable to read file\n"; return 1; } if (!anImage->Load (&aBuff.ChangeFirst(), aBuff.Size(), anImgPath)) @@ -510,6 +510,107 @@ static Standard_Integer OCC30182 (Draw_Interpretor& , Standard_Integer theNbArgs return 0; } +//======================================================================= +//function : OCC31956 +//purpose : Testing Image_AlienPixMap::Save() overload for saving into a memory buffer or stream +//======================================================================= +static Standard_Integer OCC31956 (Draw_Interpretor& di, Standard_Integer theNbArgs, const char** theArgVec) +{ + if (ViewerTest::CurrentView().IsNull()) + { + di << "Error: no active view\n"; + return 1; + } + if (theNbArgs != 3 && theNbArgs != 5) + { + di << "Syntax error: wrong number of arguments\n"; + return 1; + } + + bool useStream = false; + TCollection_AsciiString aTempImgPath; + if (theNbArgs == 5) + { + TCollection_AsciiString anArg (theArgVec[3]); + anArg.LowerCase(); + if (anArg == "-stream") + { + useStream = true; + aTempImgPath = theArgVec[4]; + } + else + { + di << "Syntax error at '" << anArg << "'\n"; + return 1; + } + } + + TCollection_AsciiString aPrsName, anImgPath; + aPrsName = theArgVec[1]; + anImgPath = theArgVec[2]; + Handle(Image_AlienPixMap) anImage = new Image_AlienPixMap(); + const Handle(OSD_FileSystem)& aFileSystem = OSD_FileSystem::DefaultFileSystem(); + opencascade::std::shared_ptr aFile = aFileSystem->OpenIStream (anImgPath, std::ios::in | std::ios::binary); + if (aFile.get() == NULL) + { + di << "Syntax error: image file '" << anImgPath << "' cannot be found\n"; + return 1; + } + + aFile->seekg (0, std::ios::end); + Standard_Integer aLen = (Standard_Integer )aFile->tellg(); + aFile->seekg (0); + if (!anImage->Load (*aFile, anImgPath)) + { + return 0; + } + + Handle(Image_AlienPixMap) aControlImg = new Image_AlienPixMap(); + if (useStream) + { + opencascade::std::shared_ptr aTempFile = aFileSystem->OpenOStream (aTempImgPath, std::ios::out | std::ios::binary); + if (aTempFile.get() == NULL) + { + di << "Error: image file '" << aTempImgPath << "' cannot be open\n"; + return 0; + } + if (!anImage->Save (*aTempFile, aTempImgPath)) + { + di << "Error: failed saving file using stream '" << aTempImgPath << "'\n"; + return 0; + } + aTempFile.reset(); + aControlImg->Load (aTempImgPath); + } + else + { + NCollection_Array1 aBuff (1, aLen + 2048); + if (!anImage->Save (&aBuff.ChangeFirst(), aBuff.Size(), anImgPath)) + { + di << "Error: failed saving file using buffer'" << anImgPath << "'\n"; + return 0; + } + aControlImg->Load (&aBuff.ChangeFirst(), aBuff.Size(), anImgPath); + } + + TopoDS_Shape aShape = BRepPrimAPI_MakeBox (100.0 * aControlImg->Ratio(), 100.0, 1.0).Shape(); + Handle(AIS_Shape) aPrs = new AIS_Shape (aShape); + aPrs->SetDisplayMode (AIS_Shaded); + aPrs->Attributes()->SetupOwnShadingAspect(); + const Handle(Graphic3d_AspectFillArea3d)& anAspect = aPrs->Attributes()->ShadingAspect()->Aspect(); + anAspect->SetShadingModel (Graphic3d_TOSM_UNLIT); + anAspect->SetTextureMapOn (true); + anAspect->SetTextureMap (new Graphic3d_Texture2D(aControlImg)); + if (aControlImg->IsTopDown()) + { + anAspect->TextureMap()->GetParams()->SetTranslation (Graphic3d_Vec2 (0.0f, -1.0f)); + anAspect->TextureMap()->GetParams()->SetScale (Graphic3d_Vec2 (1.0f, -1.0f)); + } + + ViewerTest::Display (aPrsName, aPrs, true, true); + return 0; +} + void QABugs::Commands_1(Draw_Interpretor& theCommands) { const char *group = "QABugs"; @@ -527,5 +628,7 @@ void QABugs::Commands_1(Draw_Interpretor& theCommands) { theCommands.Add ("OCC30182", "OCC30182 name image [-offset Start] [-fileName] [-stream] [-memory]\n" "Decodes image either by passing file name, file stream or memory stream", __FILE__, OCC30182, group); + theCommands.Add ("OCC31956", "OCC31956 name image [-stream tempImage]\n" + "Loads image and saves it into memory buffer or stream then loads it back", __FILE__, OCC31956, group); return; } diff --git a/tests/v3d/bugs/bug31956 b/tests/v3d/bugs/bug31956 new file mode 100644 index 0000000000..1d5eb1af83 --- /dev/null +++ b/tests/v3d/bugs/bug31956 @@ -0,0 +1,41 @@ +puts "============" +puts "0031956: Visualization - provide Image_AlienPixMap::Save() writing into a memory buffer instead of a file" +puts "============" +puts "" + +set anImg1 [locate_data_file chataignier.gif] +set anImg2 [locate_data_file hatch_1.png] + +pload VISUALIZATION QAcommands +vclear +vinit View1 +vtop + +OCC30182 t $anImg1 -fileName; vfit +vdump $imagedir/${casename}_1.png + +OCC30182 t $anImg2 -fileName; vfit +vdump $imagedir/${casename}_2.png + +vclear + +OCC31956 t $anImg1; vfit +vdump $imagedir/${casename}_test_1.png + +OCC31956 t $anImg2; vfit +vdump $imagedir/${casename}_test_2.png + +vclear + +OCC31956 t $anImg1 -stream $imagedir/${casename}_temp_1.gif; vfit +vdump $imagedir/${casename}_test_1s.png + +OCC31956 t $anImg2 -stream $imagedir/${casename}_temp_2.png; vfit +vdump $imagedir/${casename}_test_2s.png + +if { [diffimage $imagedir/${casename}_test_1.png $imagedir/${casename}_1.png -toleranceOfColor 0.1] != 0 } { puts "Error difference in 1st image" } +if { [diffimage $imagedir/${casename}_test_2.png $imagedir/${casename}_2.png -toleranceOfColor 0.1] != 0 } { puts "Error difference in 2nd image" } + +if { [diffimage $imagedir/${casename}_test_1s.png $imagedir/${casename}_1.png -toleranceOfColor 0.1] != 0 } { puts "Error difference in 1st image (using stream)" } +if { [diffimage $imagedir/${casename}_test_2s.png $imagedir/${casename}_2.png -toleranceOfColor 0.1] != 0 } { puts "Error difference in 2nd image (using stream)" } +