]> OCCT Git - occt.git/commitdiff
0031956: Visualization - provide Image_AlienPixMap::Save() writing into a memory...
authormzernova <mzernova@opencascade.com>
Fri, 19 Nov 2021 07:11:21 +0000 (07:11 +0000)
committermzernova <mzernova@opencascade.com>
Mon, 12 Jun 2023 10:20:44 +0000 (11:20 +0100)
Added two new Image_AlienPixMap::Save() overloads, taking std::ostream or memory buffer arguments.

src/Image/Image_AlienPixMap.cxx
src/Image/Image_AlienPixMap.hxx
src/QABugs/QABugs_1.cxx
tests/v3d/bugs/bug31956 [new file with mode: 0644]

index 99b84acebda1952d1d696cc38e2f2273e0c31269..f869e8fc16dc868ac2aec7db76292b503aaeeba2 100644 (file)
@@ -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 T> 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<IWICBitmap> anSrcImg;
+  Image_ComPtr<IWICFormatConverter> 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,129 +1087,39 @@ bool Image_AlienPixMap::Save (const TCollection_AsciiString& theFileName)
     SetTopDown (false);
   }
 
-  // FreeImage doesn't provide flexible format conversion API
-  // so we should perform multiple conversions in some cases!
-  FIBITMAP* anImageToDump = myLibImage;
-  switch (anImageFormat)
-  {
-    case FIF_PNG:
-    case FIF_BMP:
-    {
-      if (Format() == Image_Format_BGR32
-       || Format() == Image_Format_RGB32)
-      {
-        // stupid FreeImage treats reserved byte as alpha if some bytes not set to 0xFF
-        for (Standard_Size aRow = 0; aRow < SizeY(); ++aRow)
-        {
-          for (Standard_Size aCol = 0; aCol < SizeX(); ++aCol)
-          {
-            myData.ChangeValue (aRow, aCol)[3] = 0xFF;
-          }
-        }
-      }
-      else if (FreeImage_GetImageType (myLibImage) != FIT_BITMAP)
-      {
-        anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_BITMAP);
-      }
-      break;
-    }
-    case FIF_GIF:
-    {
-      FIBITMAP* aTmpBitmap = myLibImage;
-      if (FreeImage_GetImageType (myLibImage) != FIT_BITMAP)
-      {
-        aTmpBitmap = FreeImage_ConvertToType (myLibImage, FIT_BITMAP);
-        if (aTmpBitmap == NULL)
-        {
-          return false;
-        }
-      }
-
-      if (FreeImage_GetBPP (aTmpBitmap) != 24)
-      {
-        FIBITMAP* aTmpBitmap24 = FreeImage_ConvertTo24Bits (aTmpBitmap);
-        if (aTmpBitmap != myLibImage)
-        {
-          FreeImage_Unload (aTmpBitmap);
-        }
-        if (aTmpBitmap24 == NULL)
-        {
-          return false;
-        }
-        aTmpBitmap = aTmpBitmap24;
-      }
-
-      // need conversion to image with palette (requires 24bit bitmap)
-      anImageToDump = FreeImage_ColorQuantize (aTmpBitmap, FIQ_NNQUANT);
-      if (aTmpBitmap != myLibImage)
-      {
-        FreeImage_Unload (aTmpBitmap);
-      }
-      break;
-    }
-    case FIF_HDR:
-    case FIF_EXR:
-    {
-      if (Format() == Image_Format_Gray
-       || Format() == Image_Format_Alpha)
-      {
-        anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_FLOAT);
-      }
-      else if (Format() == Image_Format_RGBA
-            || Format() == Image_Format_BGRA)
-      {
-        anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_RGBAF);
-      }
-      else
-      {
-        FREE_IMAGE_TYPE aImgTypeFI = FreeImage_GetImageType (myLibImage);
-        if (aImgTypeFI != FIT_RGBF
-         && aImgTypeFI != FIT_RGBAF
-         && aImgTypeFI != FIT_FLOAT)
-        {
-          anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_RGBF);
-        }
-      }
-      break;
-    }
-    default:
-    {
-      if (FreeImage_GetImageType (myLibImage) != FIT_BITMAP)
-      {
-        anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_BITMAP);
-        if (anImageToDump == NULL)
-        {
-          return false;
-        }
-      }
-
-      if (FreeImage_GetBPP (anImageToDump) != 24)
-      {
-        FIBITMAP* aTmpBitmap24 = FreeImage_ConvertTo24Bits (anImageToDump);
-        if (anImageToDump != myLibImage)
-        {
-          FreeImage_Unload (anImageToDump);
-        }
-        if (aTmpBitmap24 == NULL)
-        {
-          return false;
-        }
-        anImageToDump = aTmpBitmap24;
-      }
-      break;
-    }
-  }
+  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
-  bool isSaved = (FreeImage_SaveU (anImageFormat, anImageToDump, aFileNameW.ToWideString()) != FALSE);
+    isSaved = (FreeImage_SaveU (anImageFormat, anImageToDump, aFileNameW.ToWideString ()) != FALSE);
 #else
-  bool isSaved = (FreeImage_Save  (anImageFormat, anImageToDump, theFileName.ToCString()) != FALSE);
+    isSaved = (FreeImage_Save (anImageFormat, anImageToDump, theFileName.ToCString ()) != FALSE);
 #endif
+  }
   if (anImageToDump != myLibImage)
   {
     FreeImage_Unload (anImageToDump);
@@ -1072,34 +1130,191 @@ bool Image_AlienPixMap::Save (const TCollection_AsciiString& theFileName)
 
   TCollection_AsciiString aFileNameLower = theFileName;
   aFileNameLower.LowerCase();
-  GUID aFileFormat = getNullGuid();
   if (aFileNameLower.EndsWith (".ppm"))
   {
     return savePPM (theFileName);
   }
-  else if (aFileNameLower.EndsWith (".bmp"))
+
+  GUID aFileFormat = getFileFormatFromName (theFileName);
+  if (aFileFormat == getNullGuid())
+  {
+    Message::SendFail ("Error: unsupported image format");
+    return false;
+  }
+
+  Image_ComPtr<IWICImagingFactory> 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<IWICStream> aWicStream;
+  Image_ComPtr<IWICBitmapEncoder> 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<IWICBitmapFrameEncode> 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)
   {
-    aFileFormat = GUID_ContainerFormatBmp;
+    //Message::Send ("Error: cannot commit data to WIC File Stream", Message_Fail);
+    //return false;
   }
-  else if (aFileNameLower.EndsWith (".png"))
+  return true;
+#else
+  if (theBuffer != NULL)
   {
-    aFileFormat = GUID_ContainerFormatPng;
+    Message::SendFail ("Error: no image library available");
+    return false;
   }
-  else if (aFileNameLower.EndsWith (".jpg")
-        || aFileNameLower.EndsWith (".jpeg"))
+  const Standard_Integer aLen = theFileName.Length();
+  if ((aLen >= 4) && (theFileName.Value (aLen - 3) == '.')
+      && strcasecmp( theFileName.ToCString() + aLen - 3, "ppm") == 0 )
   {
-    aFileFormat = GUID_ContainerFormatJpeg;
+    return savePPM (theFileName);
   }
-  else if (aFileNameLower.EndsWith (".tiff")
-        || aFileNameLower.EndsWith (".tif"))
+  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)
   {
-    aFileFormat = GUID_ContainerFormatTiff;
+    return false;
   }
-  else if (aFileNameLower.EndsWith (".gif"))
+
+#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)
   {
-    aFileFormat = GUID_ContainerFormatGif;
+#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");
@@ -1114,26 +1329,35 @@ bool Image_AlienPixMap::Save (const TCollection_AsciiString& theFileName)
     return false;
   }
 
-  Image_ComPtr<IWICStream> aWicFileStream;
-  Image_ComPtr<IWICBitmapEncoder> aWicEncoder;
-  const TCollection_ExtendedString aFileNameW (theFileName);
-  if (aWicImgFactory->CreateStream (&aWicFileStream.ChangePtr()) != S_OK
-   || aWicFileStream->InitializeFromFilename (aFileNameW.ToWideString(), GENERIC_WRITE) != S_OK)
+  WICPixelFormatGUID aWicPixelFormat = convertToWicFormat (myImgFormat);
+  if (aWicPixelFormat == getNullGuid())
   {
-    Message::SendFail ("Error: cannot create WIC File Stream");
+    Message::SendFail ("Error: unsupported pixel format");
     return false;
   }
-  if (aWicImgFactory->CreateEncoder (aFileFormat, NULL, &aWicEncoder.ChangePtr()) != S_OK
-   || aWicEncoder->Initialize (aWicFileStream.get(), WICBitmapEncoderNoCache) != S_OK)
+
+  Image_PixMapData* aPixMapData = &myData;
+  Image_PixMapData aConvertedData;
+  if (aFileFormat == GUID_ContainerFormatGif)
   {
-    Message::SendFail ("Error: cannot create WIC Encoder");
+    aWicPixelFormat = GUID_WICPixelFormat8bppIndexed;
+    convertData (*this, aWicPixelFormat, *aWicImgFactory, aConvertedData);
+    aPixMapData = &aConvertedData;
+  }
+
+  Image_ComPtr<IStream> aStream;
+  Image_ComPtr<IWICBitmapEncoder> aWicEncoder;
+
+  if (CreateStreamOnHGlobal (NULL, Standard_True, &aStream.ChangePtr()) != S_OK)
+  {
+    Message::SendFail ("Error: cannot create Stream on global");
     return false;
   }
 
-  const WICPixelFormatGUID aWicPixelFormat = convertToWicFormat (myImgFormat);
-  if (aWicPixelFormat == getNullGuid())
+  if (aWicImgFactory->CreateEncoder (aFileFormat, NULL, &aWicEncoder.ChangePtr()) != S_OK
+   || aWicEncoder->Initialize (aStream.get(), WICBitmapEncoderNoCache) != S_OK)
   {
-    Message::SendFail ("Error: unsupported pixel format");
+    Message::SendFail ("Error: cannot create WIC Encoder");
     return false;
   }
 
@@ -1148,6 +1372,14 @@ bool Image_AlienPixMap::Save (const TCollection_AsciiString& theFileName)
     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");
@@ -1156,7 +1388,7 @@ bool Image_AlienPixMap::Save (const TCollection_AsciiString& theFileName)
 
   if (IsTopDown())
   {
-    if (aWicFrameEncode->WritePixels ((UINT )SizeY(), (UINT )SizeRowBytes(), (UINT )SizeBytes(), (BYTE* )Data()) != S_OK)
+    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;
@@ -1166,35 +1398,60 @@ bool Image_AlienPixMap::Save (const TCollection_AsciiString& theFileName)
   {
     for (Standard_Size aRow = 0; aRow < SizeY(); ++aRow)
     {
-      if (aWicFrameEncode->WritePixels (1, (UINT )SizeRowBytes(), (UINT )SizeRowBytes(), (BYTE* )Row (aRow)) != S_OK)
+      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 (aWicFileStream->Commit (STGC_DEFAULT) != S_OK)
+  if (aStream->Commit (STGC_DEFAULT) != S_OK)
   {
-    //Message::Send ("Error: cannot commit data to WIC File Stream", Message_Fail);
+    //Message::Send ("Error: cannot commit data to 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 )
+
+  // 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)
   {
-    return savePPM (theFileName);
+    Message::SendFail ("Error: cannot get stat from stream");
+    return false;
   }
-  Message::SendTrace ("Image_PixMap, no image library available! Image saved in PPM format");
-  return savePPM (theFileName);
+  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
 }
 
@@ -1211,3 +1468,126 @@ bool Image_AlienPixMap::AdjustGamma (const Standard_Real 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!
+  switch (theFormat)
+  {
+    case FIF_PNG:
+    case FIF_BMP:
+    {
+      if (Format() == Image_Format_BGR32
+       || Format() == Image_Format_RGB32)
+      {
+        // stupid FreeImage treats reserved byte as alpha if some bytes not set to 0xFF
+        for (Standard_Size aRow = 0; aRow < SizeY(); ++aRow)
+        {
+          for (Standard_Size aCol = 0; aCol < SizeX(); ++aCol)
+          {
+            myData.ChangeValue (aRow, aCol)[3] = 0xFF;
+          }
+        }
+      }
+      else if (FreeImage_GetImageType (myLibImage) != FIT_BITMAP)
+      {
+        anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_BITMAP);
+      }
+      break;
+    }
+    case FIF_GIF:
+    {
+      FIBITMAP* aTmpBitmap = myLibImage;
+      if (FreeImage_GetImageType (myLibImage) != FIT_BITMAP)
+      {
+        aTmpBitmap = FreeImage_ConvertToType (myLibImage, FIT_BITMAP);
+        if (aTmpBitmap == NULL)
+        {
+          return NULL;
+        }
+      }
+
+      if (FreeImage_GetBPP (aTmpBitmap) != 24)
+      {
+        FIBITMAP* aTmpBitmap24 = FreeImage_ConvertTo24Bits (aTmpBitmap);
+        if (aTmpBitmap != myLibImage)
+        {
+          FreeImage_Unload (aTmpBitmap);
+        }
+        if (aTmpBitmap24 == NULL)
+        {
+          return NULL;
+        }
+        aTmpBitmap = aTmpBitmap24;
+      }
+
+      // need conversion to image with palette (requires 24bit bitmap)
+      anImageToDump = FreeImage_ColorQuantize (aTmpBitmap, FIQ_NNQUANT);
+      if (aTmpBitmap != myLibImage)
+      {
+        FreeImage_Unload (aTmpBitmap);
+      }
+      break;
+    }
+    case FIF_HDR:
+    case FIF_EXR:
+    {
+      if (Format() == Image_Format_Gray
+       || Format() == Image_Format_Alpha)
+      {
+        anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_FLOAT);
+      }
+      else if (Format() == Image_Format_RGBA
+            || Format() == Image_Format_BGRA)
+      {
+        anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_RGBAF);
+      }
+      else
+      {
+        FREE_IMAGE_TYPE aImgTypeFI = FreeImage_GetImageType (myLibImage);
+        if (aImgTypeFI != FIT_RGBF
+         && aImgTypeFI != FIT_RGBAF
+         && aImgTypeFI != FIT_FLOAT)
+        {
+          anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_RGBF);
+        }
+      }
+      break;
+    }
+    default:
+    {
+      if (FreeImage_GetImageType (myLibImage) != FIT_BITMAP)
+      {
+        anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_BITMAP);
+        if (anImageToDump == NULL)
+        {
+          return NULL;
+        }
+      }
+
+      if (FreeImage_GetBPP (anImageToDump) != 24)
+      {
+        FIBITMAP* aTmpBitmap24 = FreeImage_ConvertTo24Bits (anImageToDump);
+        if (anImageToDump != myLibImage)
+        {
+          FreeImage_Unload (anImageToDump);
+        }
+        if (aTmpBitmap24 == NULL)
+        {
+          return NULL;
+        }
+        anImageToDump = aTmpBitmap24;
+      }
+      break;
+    }
+  }
+  return anImageToDump;
+}
+#endif
index 9c61d08ef7eb150741f41d1e546edbd3822cbd52..dc986796f6d2ee09c7230c97e732d0c345d80b38 100644 (file)
@@ -19,6 +19,7 @@
 #include <Image_PixMap.hxx>
 
 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)
index de3d1d74f81b70ba86fb8eb7a67bc8b9b34a9f25..a7a78896ee336c6c042c3ce37a3bdab7149e8abb 100644 (file)
@@ -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<std::istream> 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<Standard_Byte> 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<std::istream> 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<std::ostream> 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<Standard_Byte> 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 (file)
index 0000000..1d5eb1a
--- /dev/null
@@ -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)" }
+