0029055: Visualization, Image_AlienPixMap - fallback using Wincodec
authorkgv <kgv@opencascade.com>
Fri, 25 Aug 2017 21:48:23 +0000 (00:48 +0300)
committerbugmaster <bugmaster@opencascade.com>
Wed, 30 Aug 2017 09:42:53 +0000 (12:42 +0300)
Possibility to read and write images in BMP, PNG, JPEG formats without using FreeImage library is provided on Windows using system image library.

adm/cmake/occt_csf.cmake
adm/genproj.tcl
src/Image/Image_AlienPixMap.cxx

index 7e978bb..c62425d 100644 (file)
@@ -35,7 +35,11 @@ endif()
 if (USE_FREEIMAGE)
   set (CSF_FreeImagePlus "freeimage")
 else()
-  set (CSF_FreeImagePlus)
+  if (WIN32)
+    set (CSF_FreeImagePlus "windowscodecs")
+  else()
+    set (CSF_FreeImagePlus)
+  endif()
 endif()
 
 # TCL/TK
index d331b68..2bc4f38 100644 (file)
@@ -1312,6 +1312,8 @@ proc osutils:csfList { theOS theCsfLibsMap theCsfFrmsMap } {
     } else {
       set aLibsMap(CSF_FreeImagePlus) "freeimage"
     }
+  } elseif { "$theOS" == "wnt" } {
+    set aLibsMap(CSF_FreeImagePlus) "windowscodecs"
   }
   if { "$::HAVE_FFMPEG" == "true" } {
     set aLibsMap(CSF_FFmpeg) "avcodec avformat swscale avutil"
index bf00775..f7ed44a 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>
@@ -33,9 +42,9 @@
 
 IMPLEMENT_STANDARD_RTTIEXT(Image_AlienPixMap,Image_PixMap)
 
-#ifdef HAVE_FREEIMAGE
 namespace
 {
+#ifdef HAVE_FREEIMAGE
   static Image_Format convertFromFreeFormat (FREE_IMAGE_TYPE       theFormatFI,
                                              FREE_IMAGE_COLOR_TYPE theColorTypeFI,
                                              unsigned              theBitsPerPixel)
@@ -101,9 +110,112 @@ namespace
         return FIT_UNKNOWN;
     }
   }
-}
-#endif
+#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
@@ -176,6 +288,36 @@ bool Image_AlienPixMap::InitTrash (Image_Format        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 (Image_Format        thePixelFormat,
                                    const Standard_Size theSizeX,
@@ -252,7 +394,7 @@ bool Image_AlienPixMap::Load (const TCollection_AsciiString& theImagePath)
   Clear();
 
 #ifdef _WIN32
-  const TCollection_ExtendedString aFileNameW (theImagePath.ToCString(), Standard_True);
+  const TCollection_ExtendedString aFileNameW (theImagePath);
   FREE_IMAGE_FORMAT aFIF = FreeImage_GetFileTypeU (aFileNameW.ToWideString(), 0);
 #else
   FREE_IMAGE_FORMAT aFIF = FreeImage_GetFileType (theImagePath.ToCString(), 0);
@@ -318,6 +460,73 @@ bool Image_AlienPixMap::Load (const TCollection_AsciiString& theImagePath)
   myLibImage = anImage;
   return true;
 }
+#elif defined(HAVE_WINCODEC)
+bool Image_AlienPixMap::Load (const TCollection_AsciiString& theImagePath)
+{
+  Clear();
+
+  IWICImagingFactory* aWicImgFactory = NULL;
+  CoInitializeEx (NULL, COINIT_MULTITHREADED);
+  if (CoCreateInstance (CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&aWicImgFactory)) != S_OK)
+  {
+    Message::DefaultMessenger()->Send ("Error: cannot initialize WIC Imaging Factory", Message_Fail);
+    return false;
+  }
+
+  Image_ComPtr<IWICBitmapDecoder> aWicDecoder;
+  const TCollection_ExtendedString aFileNameW (theImagePath);
+  if (aWicImgFactory->CreateDecoderFromFilename (aFileNameW.ToWideString(), NULL, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &aWicDecoder.ChangePtr()) != S_OK)
+  {
+    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;
+}
 #else
 bool Image_AlienPixMap::Load (const TCollection_AsciiString&)
 {
@@ -527,6 +736,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;
+  }
+
+  IWICImagingFactory* aWicImgFactory = NULL;
+  CoInitializeEx (NULL, COINIT_MULTITHREADED);
+  if (CoCreateInstance (CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&aWicImgFactory)) != 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) == '.')