0030182: Visualization, Image_AlienPixMap - support reading encoded image from memory...
[occt.git] / src / Image / Image_AlienPixMap.cxx
index a264254..ba166a5 100644 (file)
 // Alternatively, this file may be used under the terms of Open CASCADE
 // commercial license or contractual agreement.
 
+#if !defined(HAVE_FREEIMAGE) && defined(_WIN32)
+  #define HAVE_WINCODEC
+#endif
+
 #ifdef HAVE_FREEIMAGE
   #include <FreeImage.h>
 
   #ifdef _MSC_VER
     #pragma comment( lib, "FreeImage.lib" )
   #endif
+#elif defined(HAVE_WINCODEC)
+  //#include <initguid.h>
+  #include <wincodec.h>
+  #undef min
+  #undef max
 #endif
 
 #include <Image_AlienPixMap.hxx>
 #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>
 #include <fstream>
 #include <algorithm>
 
-#ifdef HAVE_FREEIMAGE
+IMPLEMENT_STANDARD_RTTIEXT(Image_AlienPixMap,Image_PixMap)
+
 namespace
 {
-  static Image_PixMap::ImgFormat convertFromFreeFormat (FREE_IMAGE_TYPE       theFormatFI,
-                                                        FREE_IMAGE_COLOR_TYPE theColorTypeFI,
-                                                        unsigned              theBitsPerPixel)
+#ifdef HAVE_FREEIMAGE
+  static Image_Format convertFromFreeFormat (FREE_IMAGE_TYPE       theFormatFI,
+                                             FREE_IMAGE_COLOR_TYPE theColorTypeFI,
+                                             unsigned              theBitsPerPixel)
   {
     switch (theFormatFI)
     {
-      case FIT_RGBF:   return Image_PixMap::ImgRGBF;
-      case FIT_RGBAF:  return Image_PixMap::ImgRGBAF;
-      case FIT_FLOAT:  return Image_PixMap::ImgGrayF;
+      case FIT_RGBF:   return Image_Format_RGBF;
+      case FIT_RGBAF:  return Image_Format_RGBAF;
+      case FIT_FLOAT:  return Image_Format_GrayF;
       case FIT_BITMAP:
       {
         switch (theColorTypeFI)
         {
           case FIC_MINISBLACK:
           {
-            return Image_PixMap::ImgGray;
+            return Image_Format_Gray;
           }
           case FIC_RGB:
           {
             if (Image_PixMap::IsBigEndianHost())
             {
-              return (theBitsPerPixel == 32) ? Image_PixMap::ImgRGB32 : Image_PixMap::ImgRGB;
+              return (theBitsPerPixel == 32) ? Image_Format_RGB32 : Image_Format_RGB;
             }
             else
             {
-              return (theBitsPerPixel == 32) ? Image_PixMap::ImgBGR32 : Image_PixMap::ImgBGR;
+              return (theBitsPerPixel == 32) ? Image_Format_BGR32 : Image_Format_BGR;
             }
           }
           case FIC_RGBALPHA:
           {
-            return Image_PixMap::IsBigEndianHost() ? Image_PixMap::ImgRGBA : Image_PixMap::ImgBGRA;
+            return Image_PixMap::IsBigEndianHost() ? Image_Format_RGBA : Image_Format_BGRA;
           }
           default:
-            return Image_PixMap::ImgUNKNOWN;
+            return Image_Format_UNKNOWN;
         }
       }
       default:
-        return Image_PixMap::ImgUNKNOWN;
+        return Image_Format_UNKNOWN;
     }
   }
 
-  static FREE_IMAGE_TYPE convertToFreeFormat (Image_PixMap::ImgFormat theFormat)
+  static FREE_IMAGE_TYPE convertToFreeFormat (Image_Format theFormat)
   {
     switch (theFormat)
     {
-      case Image_PixMap::ImgGrayF:
+      case Image_Format_GrayF:
+      case Image_Format_AlphaF:
         return FIT_FLOAT;
-      case Image_PixMap::ImgRGBAF:
+      case Image_Format_RGBAF:
         return FIT_RGBAF;
-      case Image_PixMap::ImgRGBF:
+      case Image_Format_RGBF:
         return FIT_RGBF;
-      case Image_PixMap::ImgRGBA:
-      case Image_PixMap::ImgBGRA:
-      case Image_PixMap::ImgRGB32:
-      case Image_PixMap::ImgBGR32:
-      case Image_PixMap::ImgRGB:
-      case Image_PixMap::ImgBGR:
-      case Image_PixMap::ImgGray:
+      case Image_Format_RGBA:
+      case Image_Format_BGRA:
+      case Image_Format_RGB32:
+      case Image_Format_BGR32:
+      case Image_Format_RGB:
+      case Image_Format_BGR:
+      case Image_Format_Gray:
+      case Image_Format_Alpha:
         return FIT_BITMAP;
       default:
         return FIT_UNKNOWN;
     }
   }
-};
-#endif
 
-IMPLEMENT_STANDARD_HANDLE (Image_AlienPixMap, Image_PixMap)
-IMPLEMENT_STANDARD_RTTIEXT(Image_AlienPixMap, Image_PixMap)
+  //! 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
+  static GUID getNullGuid()
+  {
+    GUID aGuid = { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0 } };
+    return aGuid;
+  }
+
+  //! Sentry over IUnknown pointer.
+  template<class T> class Image_ComPtr
+  {
+  public:
+    //! Empty constructor.
+    Image_ComPtr()
+    : myPtr (NULL) {}
+
+    //! Destructor.
+    ~Image_ComPtr()
+    {
+      Nullify();
+    }
+
+    //! Return TRUE if pointer is NULL.
+    bool IsNull() const { return myPtr == NULL; }
+
+    //! Release the pointer.
+    void Nullify()
+    {
+      if (myPtr != NULL)
+      {
+        myPtr->Release();
+        myPtr = NULL;
+      }
+    }
+
+    //! Return pointer for initialization.
+    T*& ChangePtr()
+    {
+      Standard_ASSERT_RAISE (myPtr == NULL, "Pointer cannot be initialized twice!");
+      return myPtr;
+    }
+
+    //! Return pointer.
+    T* get() { return myPtr; }
+
+    //! Return pointer.
+    T* operator->() { return get(); }
+
+    //! Cast handle to contained type
+    T& operator*() { return *get(); }
+
+  private:
+    T* myPtr;
+  };
+
+  //! Convert WIC GUID to Image_Format.
+  static Image_Format convertFromWicFormat (const WICPixelFormatGUID& theFormat)
+  {
+    if (theFormat == GUID_WICPixelFormat32bppBGRA)
+    {
+      return Image_Format_BGRA;
+    }
+    else if (theFormat == GUID_WICPixelFormat32bppBGR)
+    {
+      return Image_Format_BGR32;
+    }
+    else if (theFormat == GUID_WICPixelFormat24bppRGB)
+    {
+      return Image_Format_RGB;
+    }
+    else if (theFormat == GUID_WICPixelFormat24bppBGR)
+    {
+      return Image_Format_BGR;
+    }
+    else if (theFormat == GUID_WICPixelFormat8bppGray)
+    {
+      return Image_Format_Gray;
+    }
+    return Image_Format_UNKNOWN;
+  }
+
+  //! Convert Image_Format to WIC GUID.
+  static WICPixelFormatGUID convertToWicFormat (Image_Format theFormat)
+  {
+    switch (theFormat)
+    {
+      case Image_Format_BGRA:   return GUID_WICPixelFormat32bppBGRA;
+      case Image_Format_BGR32:  return GUID_WICPixelFormat32bppBGR;
+      case Image_Format_RGB:    return GUID_WICPixelFormat24bppRGB;
+      case Image_Format_BGR:    return GUID_WICPixelFormat24bppBGR;
+      case Image_Format_Gray:   return GUID_WICPixelFormat8bppGray;
+      case Image_Format_Alpha:  return GUID_WICPixelFormat8bppGray; // GUID_WICPixelFormat8bppAlpha
+      case Image_Format_GrayF:  // GUID_WICPixelFormat32bppGrayFloat
+      case Image_Format_AlphaF:
+      case Image_Format_RGBAF:  // GUID_WICPixelFormat128bppRGBAFloat
+      case Image_Format_RGBF:   // GUID_WICPixelFormat96bppRGBFloat
+      case Image_Format_RGBA:   // GUID_WICPixelFormat32bppRGBA
+      case Image_Format_RGB32:  // GUID_WICPixelFormat32bppRGB
+      default:
+        return getNullGuid();
+    }
+  }
+
+#endif
+}
 
 // =======================================================================
 // function : Image_AlienPixMap
@@ -124,7 +345,7 @@ Image_AlienPixMap::~Image_AlienPixMap()
 // function : InitWrapper
 // purpose  :
 // =======================================================================
-bool Image_AlienPixMap::InitWrapper (ImgFormat,
+bool Image_AlienPixMap::InitWrapper (Image_Format,
                                      Standard_Byte*,
                                      const Standard_Size,
                                      const Standard_Size,
@@ -139,7 +360,7 @@ bool Image_AlienPixMap::InitWrapper (ImgFormat,
 // purpose  :
 // =======================================================================
 #ifdef HAVE_FREEIMAGE
-bool Image_AlienPixMap::InitTrash (ImgFormat           thePixelFormat,
+bool Image_AlienPixMap::InitTrash (Image_Format        thePixelFormat,
                                    const Standard_Size theSizeX,
                                    const Standard_Size theSizeY,
                                    const Standard_Size /*theSizeRowBytes*/)
@@ -154,14 +375,14 @@ bool Image_AlienPixMap::InitTrash (ImgFormat           thePixelFormat,
   }
 
   FIBITMAP* anImage = FreeImage_AllocateT (aFormatFI, (int )theSizeX, (int )theSizeY, aBitsPerPixel);
-  Image_PixMap::ImgFormat aFormat = convertFromFreeFormat (FreeImage_GetImageType (anImage),
-                                                           FreeImage_GetColorType (anImage),
-                                                           FreeImage_GetBPP (anImage));
-  if (thePixelFormat == Image_PixMap::ImgBGR32
-   || thePixelFormat == Image_PixMap::ImgRGB32)
+  Image_Format aFormat = convertFromFreeFormat (FreeImage_GetImageType(anImage),
+                                                FreeImage_GetColorType(anImage),
+                                                FreeImage_GetBPP      (anImage));
+  if (thePixelFormat == Image_Format_BGR32
+   || thePixelFormat == Image_Format_RGB32)
   {
     //FreeImage_SetTransparent (anImage, FALSE);
-    aFormat = (aFormat == Image_PixMap::ImgBGRA) ? Image_PixMap::ImgBGR32 : Image_PixMap::ImgRGB32;
+    aFormat = (aFormat == Image_Format_BGRA) ? Image_Format_BGR32 : Image_Format_RGB32;
   }
 
   Image_PixMap::InitWrapper (aFormat, FreeImage_GetBits (anImage),
@@ -172,8 +393,38 @@ bool Image_AlienPixMap::InitTrash (ImgFormat           thePixelFormat,
   myLibImage = anImage;
   return true;
 }
+#elif defined(HAVE_WINCODEC)
+bool Image_AlienPixMap::InitTrash (Image_Format        thePixelFormat,
+                                   const Standard_Size theSizeX,
+                                   const Standard_Size theSizeY,
+                                   const Standard_Size theSizeRowBytes)
+{
+  Clear();
+  Image_Format aFormat = thePixelFormat;
+  switch (aFormat)
+  {
+    case Image_Format_RGB:
+      aFormat = Image_Format_BGR;
+      break;
+    case Image_Format_RGB32:
+      aFormat = Image_Format_BGR32;
+      break;
+    case Image_Format_RGBA:
+      aFormat = Image_Format_BGRA;
+      break;
+    default:
+      break;
+  }
+
+  if (!Image_PixMap::InitTrash (aFormat, theSizeX, theSizeY, theSizeRowBytes))
+  {
+    return false;
+  }
+  SetTopDown (true);
+  return true;
+}
 #else
-bool Image_AlienPixMap::InitTrash (ImgFormat           thePixelFormat,
+bool Image_AlienPixMap::InitTrash (Image_Format        thePixelFormat,
                                    const Standard_Size theSizeX,
                                    const Standard_Size theSizeY,
                                    const Standard_Size theSizeRowBytes)
@@ -238,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.ToCString(), Standard_True);
-  FREE_IMAGE_FORMAT aFIF = FreeImage_GetFileTypeU ((const wchar_t* )aFileNameW.ToExtString(), 0);
-#else
-  FREE_IMAGE_FORMAT aFIF = FreeImage_GetFileType (theImagePath.ToCString(), 0);
+  const TCollection_ExtendedString aFileNameW (theImagePath);
 #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
@@ -260,7 +540,12 @@ bool Image_AlienPixMap::Load (const TCollection_AsciiString& theImagePath)
   }
   if ((aFIF == FIF_UNKNOWN) || !FreeImage_FIFSupportsReading (aFIF))
   {
-    // unsupported image format
+    ::Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: image '") + theImagePath + "' has unsupported file format.",
+                                         Message_Fail);
+    if (aFiMem != NULL)
+    {
+      FreeImage_CloseMemory (aFiMem);
+    }
     return false;
   }
 
@@ -276,22 +561,38 @@ bool Image_AlienPixMap::Load (const TCollection_AsciiString& theImagePath)
     aLoadFlags = ICO_MAKEALPHA;
   }
 
-#ifdef _WIN32
-  FIBITMAP* anImage = FreeImage_LoadU (aFIF, (const wchar_t* )aFileNameW.ToExtString(), 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 '";
+    aMessage.AssignCat (theImagePath);
+    aMessage.AssignCat ("' is missing or invalid.");
+    ::Message::DefaultMessenger()->Send (aMessage, Message_Fail);
     return false;
   }
 
-  Image_PixMap::ImgFormat aFormat = convertFromFreeFormat (FreeImage_GetImageType (anImage),
-                                                           FreeImage_GetColorType (anImage),
-                                                           FreeImage_GetBPP (anImage));
-  if (aFormat == Image_PixMap::ImgUNKNOWN)
+  Image_Format aFormat = convertFromFreeFormat (FreeImage_GetImageType(anImage),
+                                                FreeImage_GetColorType(anImage),
+                                                FreeImage_GetBPP      (anImage));
+  if (aFormat == Image_Format_UNKNOWN)
   {
     //anImage = FreeImage_ConvertTo24Bits (anImage);
+    ::Message::DefaultMessenger()->Send (    TCollection_AsciiString ("Error: image '") + theImagePath + "' has unsupported pixel format.",
+                                         Message_Fail);
     return false;
   }
 
@@ -303,10 +604,193 @@ bool Image_AlienPixMap::Load (const TCollection_AsciiString& theImagePath)
   myLibImage = anImage;
   return true;
 }
+
+bool Image_AlienPixMap::Load (std::istream& theStream,
+                              const TCollection_AsciiString& theFileName)
+{
+  Clear();
+
+  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.ChangePtr())) != S_OK)
+  {
+    Message::DefaultMessenger()->Send ("Error: cannot initialize WIC Imaging Factory", Message_Fail);
+    return false;
+  }
+
+  Image_ComPtr<IWICBitmapDecoder> aWicDecoder;
+  Image_ComPtr<IWICStream> aWicStream;
+  if (theData != NULL)
+  {
+    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;
+  WICPixelFormatGUID aWicPixelFormat = getNullGuid();
+  Image_ComPtr<IWICBitmapFrameDecode> aWicFrameDecode;
+  if (aWicDecoder->GetFrameCount (&aFrameCount) != S_OK
+   || aFrameCount < 1
+   || aWicDecoder->GetFrame (0, &aWicFrameDecode.ChangePtr()) != S_OK
+   || aWicFrameDecode->GetSize (&aFrameSizeX, &aFrameSizeY) != S_OK
+   || aWicFrameDecode->GetPixelFormat (&aWicPixelFormat))
+  {
+    Message::DefaultMessenger()->Send ("Error: cannot get WIC Image Frame", Message_Fail);
+    return false;
+  }
+
+  Image_ComPtr<IWICFormatConverter> aWicConvertedFrame;
+  Image_Format aPixelFormat = convertFromWicFormat (aWicPixelFormat);
+  if (aPixelFormat == Image_Format_UNKNOWN)
+  {
+    aPixelFormat = Image_Format_RGB;
+    if (aWicImgFactory->CreateFormatConverter (&aWicConvertedFrame.ChangePtr()) != S_OK
+     || aWicConvertedFrame->Initialize (aWicFrameDecode.get(), convertToWicFormat (aPixelFormat), WICBitmapDitherTypeNone, NULL, 0.0f, WICBitmapPaletteTypeCustom) != S_OK)
+    {
+      Message::DefaultMessenger()->Send ("Error: cannot convert WIC Image Frame to RGB format", Message_Fail);
+      return false;
+    }
+    aWicFrameDecode.Nullify();
+  }
+
+  if (!Image_PixMap::InitTrash (aPixelFormat, aFrameSizeX, aFrameSizeY))
+  {
+    Message::DefaultMessenger()->Send ("Error: cannot initialize memory for image", Message_Fail);
+    return false;
+  }
+
+  IWICBitmapSource* aWicSrc = aWicFrameDecode.get();
+  if(!aWicConvertedFrame.IsNull())
+  {
+    aWicSrc = aWicConvertedFrame.get();
+  }
+  if (aWicSrc->CopyPixels (NULL, (UINT )SizeRowBytes(), (UINT )SizeBytes(), ChangeData()) != S_OK)
+  {
+    Message::DefaultMessenger()->Send ("Error: cannot copy pixels from WIC Image", Message_Fail);
+    return false;
+  }
+  SetTopDown (true);
+  return true;
+}
+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 (const TCollection_AsciiString&)
+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
@@ -334,18 +818,16 @@ bool Image_AlienPixMap::savePPM (const TCollection_AsciiString& theFileName) con
   fprintf (aFile, "# Image stored by OpenCASCADE framework in linear RGB colorspace\n");
 
   // Write pixel data
-  Quantity_Color aColor;
-  Quantity_Parameter aDummy;
   Standard_Byte aByte;
   for (Standard_Size aRow = 0; aRow < SizeY(); ++aRow)
   {
     for (Standard_Size aCol = 0; aCol < SizeX(); ++aCol)
     {
       // extremely SLOW but universal (implemented for all supported pixel formats)
-      aColor = PixelColor ((Standard_Integer )aCol, (Standard_Integer )aRow, aDummy);
-      aByte = Standard_Byte(aColor.Red() * 255.0);   fwrite (&aByte, 1, 1, aFile);
-      aByte = Standard_Byte(aColor.Green() * 255.0); fwrite (&aByte, 1, 1, aFile);
-      aByte = Standard_Byte(aColor.Blue() * 255.0);  fwrite (&aByte, 1, 1, aFile);
+      const Quantity_ColorRGBA aColor = PixelColor ((Standard_Integer )aCol, (Standard_Integer )aRow);
+      aByte = Standard_Byte(aColor.GetRGB().Red()   * 255.0); fwrite (&aByte, 1, 1, aFile);
+      aByte = Standard_Byte(aColor.GetRGB().Green() * 255.0); fwrite (&aByte, 1, 1, aFile);
+      aByte = Standard_Byte(aColor.GetRGB().Blue()  * 255.0); fwrite (&aByte, 1, 1, aFile);
     }
   }
 
@@ -368,7 +850,7 @@ bool Image_AlienPixMap::Save (const TCollection_AsciiString& theFileName)
 
 #ifdef _WIN32
   const TCollection_ExtendedString aFileNameW (theFileName.ToCString(), Standard_True);
-  FREE_IMAGE_FORMAT anImageFormat = FreeImage_GetFIFFromFilenameU ((const wchar_t* )aFileNameW.ToExtString());
+  FREE_IMAGE_FORMAT anImageFormat = FreeImage_GetFIFFromFilenameU (aFileNameW.ToWideString());
 #else
   FREE_IMAGE_FORMAT anImageFormat = FreeImage_GetFIFFromFilename (theFileName.ToCString());
 #endif
@@ -386,16 +868,16 @@ bool Image_AlienPixMap::Save (const TCollection_AsciiString& theFileName)
     SetTopDown (false);
   }
 
-  // FreeImage doesn't provide flexible format convertion API
-  // so we should perform multiple convertions in some cases!
+  // 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_PixMap::ImgBGR32
-       || Format() == Image_PixMap::ImgRGB32)
+      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)
@@ -438,7 +920,7 @@ bool Image_AlienPixMap::Save (const TCollection_AsciiString& theFileName)
         aTmpBitmap = aTmpBitmap24;
       }
 
-      // need convertion to image with pallete (requires 24bit bitmap)
+      // need conversion to image with palette (requires 24bit bitmap)
       anImageToDump = FreeImage_ColorQuantize (aTmpBitmap, FIQ_NNQUANT);
       if (aTmpBitmap != myLibImage)
       {
@@ -446,14 +928,16 @@ bool Image_AlienPixMap::Save (const TCollection_AsciiString& theFileName)
       }
       break;
     }
+    case FIF_HDR:
     case FIF_EXR:
     {
-      if (Format() == Image_PixMap::ImgGray)
+      if (Format() == Image_Format_Gray
+       || Format() == Image_Format_Alpha)
       {
         anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_FLOAT);
       }
-      else if (Format() == Image_PixMap::ImgRGBA
-            || Format() == Image_PixMap::ImgBGRA)
+      else if (Format() == Image_Format_RGBA
+            || Format() == Image_Format_BGRA)
       {
         anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_RGBAF);
       }
@@ -503,7 +987,7 @@ bool Image_AlienPixMap::Save (const TCollection_AsciiString& theFileName)
   }
 
 #ifdef _WIN32
-  bool isSaved = (FreeImage_SaveU (anImageFormat, anImageToDump, (const wchar_t* )aFileNameW.ToExtString()) != FALSE);
+  bool isSaved = (FreeImage_SaveU (anImageFormat, anImageToDump, aFileNameW.ToWideString()) != FALSE);
 #else
   bool isSaved = (FreeImage_Save  (anImageFormat, anImageToDump, theFileName.ToCString()) != FALSE);
 #endif
@@ -512,6 +996,124 @@ bool Image_AlienPixMap::Save (const TCollection_AsciiString& theFileName)
     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"))
+  {
+    aFileFormat = GUID_ContainerFormatTiff;
+  }
+  else if (aFileNameLower.EndsWith (".gif"))
+  {
+    aFileFormat = GUID_ContainerFormatGif;
+  }
+
+  if (aFileFormat == getNullGuid())
+  {
+    Message::DefaultMessenger()->Send ("Error: unsupported image format", Message_Fail);
+    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::DefaultMessenger()->Send ("Error: cannot initialize WIC Imaging Factory", Message_Fail);
+    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)
+  {
+    Message::DefaultMessenger()->Send ("Error: cannot create WIC File Stream", Message_Fail);
+    return false;
+  }
+  if (aWicImgFactory->CreateEncoder (aFileFormat, NULL, &aWicEncoder.ChangePtr()) != S_OK
+   || aWicEncoder->Initialize (aWicFileStream.get(), WICBitmapEncoderNoCache) != S_OK)
+  {
+    Message::DefaultMessenger()->Send ("Error: cannot create WIC Encoder", Message_Fail);
+    return false;
+  }
+
+  const WICPixelFormatGUID aWicPixelFormat = convertToWicFormat (myImgFormat);
+  if (aWicPixelFormat == getNullGuid())
+  {
+    Message::DefaultMessenger()->Send ("Error: unsupported pixel format", Message_Fail);
+    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::DefaultMessenger()->Send ("Error: cannot create WIC Frame", Message_Fail);
+    return false;
+  }
+
+  if (aWicPixelFormatRes != aWicPixelFormat)
+  {
+    Message::DefaultMessenger()->Send ("Error: pixel format is unsupported by image format", Message_Fail);
+    return false;
+  }
+
+  if (IsTopDown())
+  {
+    if (aWicFrameEncode->WritePixels ((UINT )SizeY(), (UINT )SizeRowBytes(), (UINT )SizeBytes(), (BYTE* )Data()) != S_OK)
+    {
+      Message::DefaultMessenger()->Send ("Error: cannot write pixels to WIC Frame", Message_Fail);
+      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::DefaultMessenger()->Send ("Error: cannot write pixels to WIC Frame", Message_Fail);
+        return false;
+      }
+    }
+  }
+
+  if (aWicFrameEncode->Commit() != S_OK
+   || aWicEncoder->Commit() != S_OK)
+  {
+    Message::DefaultMessenger()->Send ("Error: cannot commit data to WIC Frame", Message_Fail);
+    return false;
+  }
+  if (aWicFileStream->Commit (STGC_DEFAULT) != S_OK)
+  {
+    //Message::DefaultMessenger()->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) == '.')