0cbd6534d27ba32759fedbb9c7c2086bd7984856
[occt.git] / src / Image / Image_AlienPixMap.cxx
1 // Created on: 2010-09-16
2 // Created by: KGV
3 // Copyright (c) 2010-2014 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 #if !defined(HAVE_FREEIMAGE) && defined(_WIN32)
17   #define HAVE_WINCODEC
18 #endif
19
20 #ifdef HAVE_FREEIMAGE
21   #include <FreeImage.h>
22
23   #ifdef _MSC_VER
24     #pragma comment( lib, "FreeImage.lib" )
25   #endif
26 #elif defined(HAVE_WINCODEC)
27   #include <wincodec.h>
28   // prevent warnings on MSVC10
29   #include <Standard_WarningsDisable.hxx>
30   #include <Standard_TypeDef.hxx>
31   #include <Standard_WarningsRestore.hxx>
32   #undef min
33   #undef max
34 #endif
35
36 #include <Image_AlienPixMap.hxx>
37 #include <gp.hxx>
38 #include <Message.hxx>
39 #include <Message_Messenger.hxx>
40 #include <NCollection_Array1.hxx>
41 #include <Standard_ArrayStreamBuffer.hxx>
42 #include <TCollection_AsciiString.hxx>
43 #include <TCollection_ExtendedString.hxx>
44 #include <OSD_OpenFile.hxx>
45
46 #include <fstream>
47 #include <algorithm>
48
49 IMPLEMENT_STANDARD_RTTIEXT(Image_AlienPixMap,Image_PixMap)
50
51 namespace
52 {
53 #ifdef HAVE_FREEIMAGE
54   static Image_Format convertFromFreeFormat (FREE_IMAGE_TYPE       theFormatFI,
55                                              FREE_IMAGE_COLOR_TYPE theColorTypeFI,
56                                              unsigned              theBitsPerPixel)
57   {
58     switch (theFormatFI)
59     {
60       case FIT_RGBF:   return Image_Format_RGBF;
61       case FIT_RGBAF:  return Image_Format_RGBAF;
62       case FIT_FLOAT:  return Image_Format_GrayF;
63       case FIT_BITMAP:
64       {
65         switch (theColorTypeFI)
66         {
67           case FIC_MINISBLACK:
68           {
69             return Image_Format_Gray;
70           }
71           case FIC_RGB:
72           {
73             if (Image_PixMap::IsBigEndianHost())
74             {
75               return (theBitsPerPixel == 32) ? Image_Format_RGB32 : Image_Format_RGB;
76             }
77             else
78             {
79               return (theBitsPerPixel == 32) ? Image_Format_BGR32 : Image_Format_BGR;
80             }
81           }
82           case FIC_RGBALPHA:
83           {
84             return Image_PixMap::IsBigEndianHost() ? Image_Format_RGBA : Image_Format_BGRA;
85           }
86           default:
87             return Image_Format_UNKNOWN;
88         }
89       }
90       default:
91         return Image_Format_UNKNOWN;
92     }
93   }
94
95   static FREE_IMAGE_TYPE convertToFreeFormat (Image_Format theFormat)
96   {
97     switch (theFormat)
98     {
99       case Image_Format_GrayF:
100       case Image_Format_AlphaF:
101         return FIT_FLOAT;
102       case Image_Format_RGBAF:
103         return FIT_RGBAF;
104       case Image_Format_RGBF:
105         return FIT_RGBF;
106       case Image_Format_RGBA:
107       case Image_Format_BGRA:
108       case Image_Format_RGB32:
109       case Image_Format_BGR32:
110       case Image_Format_RGB:
111       case Image_Format_BGR:
112       case Image_Format_Gray:
113       case Image_Format_Alpha:
114         return FIT_BITMAP;
115       default:
116         return FIT_UNKNOWN;
117     }
118   }
119
120   //! Wrapper for accessing C++ stream from FreeImage.
121   class Image_FreeImageStream
122   {
123   public:
124     //! Construct wrapper over input stream.
125     Image_FreeImageStream (std::istream& theStream)
126     : myIStream (&theStream), myOStream (NULL), myInitPos (theStream.tellg()) {}
127
128     //! Get io object.
129     FreeImageIO GetFiIO() const
130     {
131       FreeImageIO anIo;
132       memset (&anIo, 0, sizeof(anIo));
133       if (myIStream != NULL)
134       {
135         anIo.read_proc = readProc;
136         anIo.seek_proc = seekProc;
137         anIo.tell_proc = tellProc;
138       }
139       if (myOStream != NULL)
140       {
141         anIo.write_proc = writeProc;
142       }
143       return anIo;
144     }
145   public:
146     //! Simulate fread().
147     static unsigned int DLL_CALLCONV readProc (void* theBuffer, unsigned int theSize, unsigned int theCount, fi_handle theHandle)
148     {
149       Image_FreeImageStream* aThis = (Image_FreeImageStream* )theHandle;
150       if (aThis->myIStream == NULL)
151       {
152         return 0;
153       }
154
155       if (!aThis->myIStream->read ((char* )theBuffer, std::streamsize(theSize) * std::streamsize(theCount)))
156       {
157         //aThis->myIStream->clear();
158       }
159       const std::streamsize aNbRead = aThis->myIStream->gcount();
160       return (unsigned int )(aNbRead / theSize);
161     }
162
163     //! Simulate fwrite().
164     static unsigned int DLL_CALLCONV writeProc (void* theBuffer, unsigned int theSize, unsigned int theCount, fi_handle theHandle)
165     {
166       Image_FreeImageStream* aThis = (Image_FreeImageStream* )theHandle;
167       if (aThis->myOStream != NULL
168        && aThis->myOStream->write ((const char* )theBuffer, std::streamsize(theSize) * std::streamsize(theCount)))
169       {
170         return theCount;
171       }
172       return 0;
173     }
174
175     //! Simulate fseek().
176     static int DLL_CALLCONV seekProc (fi_handle theHandle, long theOffset, int theOrigin)
177     {
178       Image_FreeImageStream* aThis = (Image_FreeImageStream* )theHandle;
179       if (aThis->myIStream == NULL)
180       {
181         return -1;
182       }
183
184       bool isSeekDone = false;
185       switch (theOrigin)
186       {
187         case SEEK_SET:
188           if (aThis->myIStream->seekg ((std::streamoff )aThis->myInitPos + theOffset, std::ios::beg))
189           {
190             isSeekDone = true;
191           }
192           break;
193         case SEEK_CUR:
194           if (aThis->myIStream->seekg (theOffset, std::ios::cur))
195           {
196             isSeekDone = true;
197           }
198           break;
199         case SEEK_END:
200           if (aThis->myIStream->seekg (theOffset, std::ios::end))
201           {
202             isSeekDone = true;
203           }
204           break;
205       }
206       return isSeekDone ? 0 : -1;
207     }
208
209     //! Simulate ftell().
210     static long DLL_CALLCONV tellProc (fi_handle theHandle)
211     {
212       Image_FreeImageStream* aThis = (Image_FreeImageStream* )theHandle;
213       const long aPos = aThis->myIStream != NULL ? (long )(aThis->myIStream->tellg() - aThis->myInitPos) : 0;
214       return aPos;
215     }
216   private:
217     std::istream*  myIStream;
218     std::ostream*  myOStream;
219     std::streampos myInitPos;
220   };
221
222 #elif defined(HAVE_WINCODEC)
223
224   //! Return a zero GUID
225   static GUID getNullGuid()
226   {
227     GUID aGuid = { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0 } };
228     return aGuid;
229   }
230
231   //! Sentry over IUnknown pointer.
232   template<class T> class Image_ComPtr
233   {
234   public:
235     //! Empty constructor.
236     Image_ComPtr()
237     : myPtr (NULL) {}
238
239     //! Destructor.
240     ~Image_ComPtr()
241     {
242       Nullify();
243     }
244
245     //! Return TRUE if pointer is NULL.
246     bool IsNull() const { return myPtr == NULL; }
247
248     //! Release the pointer.
249     void Nullify()
250     {
251       if (myPtr != NULL)
252       {
253         myPtr->Release();
254         myPtr = NULL;
255       }
256     }
257
258     //! Return pointer for initialization.
259     T*& ChangePtr()
260     {
261       Standard_ASSERT_RAISE (myPtr == NULL, "Pointer cannot be initialized twice!");
262       return myPtr;
263     }
264
265     //! Return pointer.
266     T* get() { return myPtr; }
267
268     //! Return pointer.
269     T* operator->() { return get(); }
270
271     //! Cast handle to contained type
272     T& operator*() { return *get(); }
273
274   private:
275     T* myPtr;
276   };
277
278   //! Convert WIC GUID to Image_Format.
279   static Image_Format convertFromWicFormat (const WICPixelFormatGUID& theFormat)
280   {
281     if (theFormat == GUID_WICPixelFormat32bppBGRA)
282     {
283       return Image_Format_BGRA;
284     }
285     else if (theFormat == GUID_WICPixelFormat32bppBGR)
286     {
287       return Image_Format_BGR32;
288     }
289     else if (theFormat == GUID_WICPixelFormat24bppRGB)
290     {
291       return Image_Format_RGB;
292     }
293     else if (theFormat == GUID_WICPixelFormat24bppBGR)
294     {
295       return Image_Format_BGR;
296     }
297     else if (theFormat == GUID_WICPixelFormat8bppGray)
298     {
299       return Image_Format_Gray;
300     }
301     return Image_Format_UNKNOWN;
302   }
303
304   //! Convert Image_Format to WIC GUID.
305   static WICPixelFormatGUID convertToWicFormat (Image_Format theFormat)
306   {
307     switch (theFormat)
308     {
309       case Image_Format_BGRA:   return GUID_WICPixelFormat32bppBGRA;
310       case Image_Format_BGR32:  return GUID_WICPixelFormat32bppBGR;
311       case Image_Format_RGB:    return GUID_WICPixelFormat24bppRGB;
312       case Image_Format_BGR:    return GUID_WICPixelFormat24bppBGR;
313       case Image_Format_Gray:   return GUID_WICPixelFormat8bppGray;
314       case Image_Format_Alpha:  return GUID_WICPixelFormat8bppGray; // GUID_WICPixelFormat8bppAlpha
315       case Image_Format_GrayF:  // GUID_WICPixelFormat32bppGrayFloat
316       case Image_Format_AlphaF:
317       case Image_Format_RGBAF:  // GUID_WICPixelFormat128bppRGBAFloat
318       case Image_Format_RGBF:   // GUID_WICPixelFormat96bppRGBFloat
319       case Image_Format_RGBA:   // GUID_WICPixelFormat32bppRGBA
320       case Image_Format_RGB32:  // GUID_WICPixelFormat32bppRGB
321       default:
322         return getNullGuid();
323     }
324   }
325
326 #endif
327 }
328
329 // =======================================================================
330 // function : Image_AlienPixMap
331 // purpose  :
332 // =======================================================================
333 Image_AlienPixMap::Image_AlienPixMap()
334 : myLibImage (NULL)
335 {
336   SetTopDown (false);
337 }
338
339 // =======================================================================
340 // function : ~Image_AlienPixMap
341 // purpose  :
342 // =======================================================================
343 Image_AlienPixMap::~Image_AlienPixMap()
344 {
345   Clear();
346 }
347
348 // =======================================================================
349 // function : InitWrapper
350 // purpose  :
351 // =======================================================================
352 bool Image_AlienPixMap::InitWrapper (Image_Format,
353                                      Standard_Byte*,
354                                      const Standard_Size,
355                                      const Standard_Size,
356                                      const Standard_Size)
357 {
358   Clear();
359   return false;
360 }
361
362 // =======================================================================
363 // function : InitTrash
364 // purpose  :
365 // =======================================================================
366 #ifdef HAVE_FREEIMAGE
367 bool Image_AlienPixMap::InitTrash (Image_Format        thePixelFormat,
368                                    const Standard_Size theSizeX,
369                                    const Standard_Size theSizeY,
370                                    const Standard_Size /*theSizeRowBytes*/)
371 {
372   Clear();
373   FREE_IMAGE_TYPE aFormatFI = convertToFreeFormat (thePixelFormat);
374   int aBitsPerPixel = (int )Image_PixMap::SizePixelBytes (thePixelFormat) * 8;
375   if (aFormatFI == FIT_UNKNOWN)
376   {
377     aFormatFI     = FIT_BITMAP;
378     aBitsPerPixel = 24;
379   }
380
381   FIBITMAP* anImage = FreeImage_AllocateT (aFormatFI, (int )theSizeX, (int )theSizeY, aBitsPerPixel);
382   Image_Format aFormat = convertFromFreeFormat (FreeImage_GetImageType(anImage),
383                                                 FreeImage_GetColorType(anImage),
384                                                 FreeImage_GetBPP      (anImage));
385   if (thePixelFormat == Image_Format_BGR32
386    || thePixelFormat == Image_Format_RGB32)
387   {
388     //FreeImage_SetTransparent (anImage, FALSE);
389     aFormat = (aFormat == Image_Format_BGRA) ? Image_Format_BGR32 : Image_Format_RGB32;
390   }
391
392   Image_PixMap::InitWrapper (aFormat, FreeImage_GetBits (anImage),
393                              FreeImage_GetWidth (anImage), FreeImage_GetHeight (anImage), FreeImage_GetPitch (anImage));
394   SetTopDown (false);
395
396   // assign image after wrapper initialization (virtual Clear() called inside)
397   myLibImage = anImage;
398   return true;
399 }
400 #elif defined(HAVE_WINCODEC)
401 bool Image_AlienPixMap::InitTrash (Image_Format        thePixelFormat,
402                                    const Standard_Size theSizeX,
403                                    const Standard_Size theSizeY,
404                                    const Standard_Size theSizeRowBytes)
405 {
406   Clear();
407   Image_Format aFormat = thePixelFormat;
408   switch (aFormat)
409   {
410     case Image_Format_RGB:
411       aFormat = Image_Format_BGR;
412       break;
413     case Image_Format_RGB32:
414       aFormat = Image_Format_BGR32;
415       break;
416     case Image_Format_RGBA:
417       aFormat = Image_Format_BGRA;
418       break;
419     default:
420       break;
421   }
422
423   if (!Image_PixMap::InitTrash (aFormat, theSizeX, theSizeY, theSizeRowBytes))
424   {
425     return false;
426   }
427   SetTopDown (true);
428   return true;
429 }
430 #else
431 bool Image_AlienPixMap::InitTrash (Image_Format        thePixelFormat,
432                                    const Standard_Size theSizeX,
433                                    const Standard_Size theSizeY,
434                                    const Standard_Size theSizeRowBytes)
435 {
436   return Image_PixMap::InitTrash (thePixelFormat, theSizeX, theSizeY, theSizeRowBytes);
437 }
438 #endif
439
440 // =======================================================================
441 // function : InitCopy
442 // purpose  :
443 // =======================================================================
444 bool Image_AlienPixMap::InitCopy (const Image_PixMap& theCopy)
445 {
446   if (&theCopy == this)
447   {
448     // self-copying disallowed
449     return false;
450   }
451   if (!InitTrash (theCopy.Format(), theCopy.SizeX(), theCopy.SizeY(), theCopy.SizeRowBytes()))
452   {
453     return false;
454   }
455
456   if (myImgFormat == theCopy.Format())
457   {
458     if (SizeRowBytes() == theCopy.SizeRowBytes()
459      && TopDownInc()   == theCopy.TopDownInc())
460     {
461       // copy with one call
462       memcpy (ChangeData(), theCopy.Data(), std::min (SizeBytes(), theCopy.SizeBytes()));
463       return true;
464     }
465
466     // copy row-by-row
467     const Standard_Size aRowSizeBytes = std::min (SizeRowBytes(), theCopy.SizeRowBytes());
468     for (Standard_Size aRow = 0; aRow < myData.SizeY; ++aRow)
469     {
470       memcpy (ChangeRow (aRow), theCopy.Row (aRow), aRowSizeBytes);
471     }
472     return true;
473   }
474
475   // pixel format conversion required
476   Clear();
477   return false;
478 }
479
480 // =======================================================================
481 // function : Clear
482 // purpose  :
483 // =======================================================================
484 void Image_AlienPixMap::Clear()
485 {
486   Image_PixMap::Clear();
487 #ifdef HAVE_FREEIMAGE
488   if (myLibImage != NULL)
489   {
490     FreeImage_Unload (myLibImage);
491     myLibImage = NULL;
492   }
493 #endif
494 }
495
496 // =======================================================================
497 // function : IsTopDownDefault
498 // purpose  :
499 // =======================================================================
500 bool Image_AlienPixMap::IsTopDownDefault()
501 {
502 #ifdef HAVE_FREEIMAGE
503   return false;
504 #elif defined(HAVE_WINCODEC)
505   return true;
506 #else
507   return false;
508 #endif
509 }
510
511 // =======================================================================
512 // function : Load
513 // purpose  :
514 // =======================================================================
515 #ifdef HAVE_FREEIMAGE
516 bool Image_AlienPixMap::Load (const Standard_Byte* theData,
517                               Standard_Size theLength,
518                               const TCollection_AsciiString& theImagePath)
519 {
520   Clear();
521
522 #ifdef _WIN32
523   const TCollection_ExtendedString aFileNameW (theImagePath);
524 #endif
525   FREE_IMAGE_FORMAT aFIF = FIF_UNKNOWN;
526   FIMEMORY* aFiMem = NULL;
527   if (theData != NULL)
528   {
529     aFiMem = FreeImage_OpenMemory ((BYTE* )theData, (DWORD )theLength);
530     aFIF = FreeImage_GetFileTypeFromMemory (aFiMem, 0);
531   }
532   else
533   {
534   #ifdef _WIN32
535     aFIF = FreeImage_GetFileTypeU (aFileNameW.ToWideString(), 0);
536   #else
537     aFIF = FreeImage_GetFileType (theImagePath.ToCString(), 0);
538   #endif
539   }
540   if (aFIF == FIF_UNKNOWN)
541   {
542     // no signature? try to guess the file format from the file extension
543     aFIF = FreeImage_GetFIFFromFilename (theImagePath.ToCString());
544   }
545   if ((aFIF == FIF_UNKNOWN) || !FreeImage_FIFSupportsReading (aFIF))
546   {
547     ::Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: image '") + theImagePath + "' has unsupported file format.",
548                                          Message_Fail);
549     if (aFiMem != NULL)
550     {
551       FreeImage_CloseMemory (aFiMem);
552     }
553     return false;
554   }
555
556   int aLoadFlags = 0;
557   if (aFIF == FIF_GIF)
558   {
559     // 'Play' the GIF to generate each frame (as 32bpp) instead of returning raw frame data when loading
560     aLoadFlags = GIF_PLAYBACK;
561   }
562   else if (aFIF == FIF_ICO)
563   {
564     // convert to 32bpp and create an alpha channel from the AND-mask when loading
565     aLoadFlags = ICO_MAKEALPHA;
566   }
567
568   FIBITMAP* anImage = NULL;
569   if (theData != NULL)
570   {
571     anImage = FreeImage_LoadFromMemory (aFIF, aFiMem, aLoadFlags);
572     FreeImage_CloseMemory (aFiMem);
573     aFiMem = NULL;
574   }
575   else
576   {
577   #ifdef _WIN32
578     anImage = FreeImage_LoadU (aFIF, aFileNameW.ToWideString(), aLoadFlags);
579   #else
580     anImage = FreeImage_Load  (aFIF, theImagePath.ToCString(), aLoadFlags);
581   #endif
582   }
583   if (anImage == NULL)
584   {
585     TCollection_AsciiString aMessage = "Error: image file '";
586     aMessage.AssignCat (theImagePath);
587     aMessage.AssignCat ("' is missing or invalid.");
588     ::Message::DefaultMessenger()->Send (aMessage, Message_Fail);
589     return false;
590   }
591
592   Image_Format aFormat = convertFromFreeFormat (FreeImage_GetImageType(anImage),
593                                                 FreeImage_GetColorType(anImage),
594                                                 FreeImage_GetBPP      (anImage));
595   if (aFormat == Image_Format_UNKNOWN)
596   {
597     //anImage = FreeImage_ConvertTo24Bits (anImage);
598     ::Message::DefaultMessenger()->Send (    TCollection_AsciiString ("Error: image '") + theImagePath + "' has unsupported pixel format.",
599                                          Message_Fail);
600     return false;
601   }
602
603   Image_PixMap::InitWrapper (aFormat, FreeImage_GetBits (anImage),
604                              FreeImage_GetWidth (anImage), FreeImage_GetHeight (anImage), FreeImage_GetPitch (anImage));
605   SetTopDown (false);
606
607   // assign image after wrapper initialization (virtual Clear() called inside)
608   myLibImage = anImage;
609   return true;
610 }
611
612 bool Image_AlienPixMap::Load (std::istream& theStream,
613                               const TCollection_AsciiString& theFileName)
614 {
615   Clear();
616
617   Image_FreeImageStream aStream (theStream);
618   FreeImageIO aFiIO = aStream.GetFiIO();
619
620   FREE_IMAGE_FORMAT aFIF = FreeImage_GetFileTypeFromHandle (&aFiIO, &aStream, 0);
621   if (aFIF == FIF_UNKNOWN)
622   {
623     // no signature? try to guess the file format from the file extension
624     aFIF = FreeImage_GetFIFFromFilename (theFileName.ToCString());
625   }
626   if ((aFIF == FIF_UNKNOWN) || !FreeImage_FIFSupportsReading (aFIF))
627   {
628     ::Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: image stream '") + theFileName + "' has unsupported file format.",
629                                          Message_Fail);
630     return false;
631   }
632
633   int aLoadFlags = 0;
634   if (aFIF == FIF_GIF)
635   {
636     // 'Play' the GIF to generate each frame (as 32bpp) instead of returning raw frame data when loading
637     aLoadFlags = GIF_PLAYBACK;
638   }
639   else if (aFIF == FIF_ICO)
640   {
641     // convert to 32bpp and create an alpha channel from the AND-mask when loading
642     aLoadFlags = ICO_MAKEALPHA;
643   }
644
645   FIBITMAP* anImage = FreeImage_LoadFromHandle (aFIF, &aFiIO, &aStream, aLoadFlags);
646   if (anImage == NULL)
647   {
648     ::Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: image stream '") + theFileName + "' is missing or invalid.",
649                                          Message_Fail);
650     return false;
651   }
652
653   Image_Format aFormat = convertFromFreeFormat (FreeImage_GetImageType(anImage),
654                                                 FreeImage_GetColorType(anImage),
655                                                 FreeImage_GetBPP      (anImage));
656   if (aFormat == Image_Format_UNKNOWN)
657   {
658     ::Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: image stream '") + theFileName + "' has unsupported pixel format.",
659                                          Message_Fail);
660     return false;
661   }
662
663   Image_PixMap::InitWrapper (aFormat, FreeImage_GetBits (anImage),
664                              FreeImage_GetWidth (anImage), FreeImage_GetHeight (anImage), FreeImage_GetPitch (anImage));
665   SetTopDown (false);
666
667   // assign image after wrapper initialization (virtual Clear() called inside)
668   myLibImage = anImage;
669   return true;
670 }
671
672 #elif defined(HAVE_WINCODEC)
673 bool Image_AlienPixMap::Load (const Standard_Byte* theData,
674                               Standard_Size theLength,
675                               const TCollection_AsciiString& theFileName)
676 {
677   Clear();
678
679   Image_ComPtr<IWICImagingFactory> aWicImgFactory;
680   CoInitializeEx (NULL, COINIT_MULTITHREADED);
681   if (CoCreateInstance (CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&aWicImgFactory.ChangePtr())) != S_OK)
682   {
683     Message::DefaultMessenger()->Send ("Error: cannot initialize WIC Imaging Factory", Message_Fail);
684     return false;
685   }
686
687   Image_ComPtr<IWICBitmapDecoder> aWicDecoder;
688   Image_ComPtr<IWICStream> aWicStream;
689   if (theData != NULL)
690   {
691     if (aWicImgFactory->CreateStream (&aWicStream.ChangePtr()) != S_OK
692      || aWicStream->InitializeFromMemory ((BYTE* )theData, (DWORD )theLength) != S_OK)
693     {
694       Message::DefaultMessenger()->Send ("Error: cannot initialize WIC Stream", Message_Fail);
695       return false;
696     }
697     if (aWicImgFactory->CreateDecoderFromStream (aWicStream.get(), NULL, WICDecodeMetadataCacheOnDemand, &aWicDecoder.ChangePtr()) != S_OK)
698     {
699       Message::DefaultMessenger()->Send ("Error: cannot create WIC Image Decoder", Message_Fail);
700       return false;
701     }
702   }
703   else
704   {
705     const TCollection_ExtendedString aFileNameW (theFileName);
706     if (aWicImgFactory->CreateDecoderFromFilename (aFileNameW.ToWideString(), NULL, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &aWicDecoder.ChangePtr()) != S_OK)
707     {
708       Message::DefaultMessenger()->Send ("Error: cannot create WIC Image Decoder", Message_Fail);
709       return false;
710     }
711   }
712
713   UINT aFrameCount = 0, aFrameSizeX = 0, aFrameSizeY = 0;
714   WICPixelFormatGUID aWicPixelFormat = getNullGuid();
715   Image_ComPtr<IWICBitmapFrameDecode> aWicFrameDecode;
716   if (aWicDecoder->GetFrameCount (&aFrameCount) != S_OK
717    || aFrameCount < 1
718    || aWicDecoder->GetFrame (0, &aWicFrameDecode.ChangePtr()) != S_OK
719    || aWicFrameDecode->GetSize (&aFrameSizeX, &aFrameSizeY) != S_OK
720    || aWicFrameDecode->GetPixelFormat (&aWicPixelFormat))
721   {
722     Message::DefaultMessenger()->Send ("Error: cannot get WIC Image Frame", Message_Fail);
723     return false;
724   }
725
726   Image_ComPtr<IWICFormatConverter> aWicConvertedFrame;
727   Image_Format aPixelFormat = convertFromWicFormat (aWicPixelFormat);
728   if (aPixelFormat == Image_Format_UNKNOWN)
729   {
730     aPixelFormat = Image_Format_RGB;
731     if (aWicImgFactory->CreateFormatConverter (&aWicConvertedFrame.ChangePtr()) != S_OK
732      || aWicConvertedFrame->Initialize (aWicFrameDecode.get(), convertToWicFormat (aPixelFormat), WICBitmapDitherTypeNone, NULL, 0.0f, WICBitmapPaletteTypeCustom) != S_OK)
733     {
734       Message::DefaultMessenger()->Send ("Error: cannot convert WIC Image Frame to RGB format", Message_Fail);
735       return false;
736     }
737     aWicFrameDecode.Nullify();
738   }
739
740   if (!Image_PixMap::InitTrash (aPixelFormat, aFrameSizeX, aFrameSizeY))
741   {
742     Message::DefaultMessenger()->Send ("Error: cannot initialize memory for image", Message_Fail);
743     return false;
744   }
745
746   IWICBitmapSource* aWicSrc = aWicFrameDecode.get();
747   if(!aWicConvertedFrame.IsNull())
748   {
749     aWicSrc = aWicConvertedFrame.get();
750   }
751   if (aWicSrc->CopyPixels (NULL, (UINT )SizeRowBytes(), (UINT )SizeBytes(), ChangeData()) != S_OK)
752   {
753     Message::DefaultMessenger()->Send ("Error: cannot copy pixels from WIC Image", Message_Fail);
754     return false;
755   }
756   SetTopDown (true);
757   return true;
758 }
759 bool Image_AlienPixMap::Load (std::istream& theStream,
760                               const TCollection_AsciiString& theFilePath)
761 {
762   Clear();
763
764   // fallback copying stream data into transient buffer
765   const std::streamoff aStart = theStream.tellg();
766   theStream.seekg (0, std::ios::end);
767   const Standard_Integer aLen = Standard_Integer(theStream.tellg() - aStart);
768   theStream.seekg (aStart);
769   if (aLen <= 0)
770   {
771     Message::DefaultMessenger()->Send ("Error: empty stream", Message_Fail);
772     return false;
773   }
774
775   NCollection_Array1<Standard_Byte> aBuff (1, aLen);
776   if (!theStream.read ((char* )&aBuff.ChangeFirst(), aBuff.Size()))
777   {
778     Message::DefaultMessenger()->Send ("Error: unable to read stream", Message_Fail);
779     return false;
780   }
781
782   return Load (&aBuff.ChangeFirst(), aBuff.Size(), theFilePath);
783 }
784 #else
785 bool Image_AlienPixMap::Load (std::istream& ,
786                               const TCollection_AsciiString& )
787 {
788   Clear();
789   Message::DefaultMessenger()->Send ("Error: no image library available", Message_Fail);
790   return false;
791 }
792 bool Image_AlienPixMap::Load (const Standard_Byte* ,
793                               Standard_Size ,
794                               const TCollection_AsciiString& )
795 {
796   Clear();
797   Message::DefaultMessenger()->Send ("Error: no image library available", Message_Fail);
798   return false;
799 }
800 #endif
801
802 // =======================================================================
803 // function : savePPM
804 // purpose  :
805 // =======================================================================
806 bool Image_AlienPixMap::savePPM (const TCollection_AsciiString& theFileName) const
807 {
808   if (IsEmpty())
809   {
810     return false;
811   }
812
813   // Open file
814   FILE* aFile = OSD_OpenFile (theFileName.ToCString(), "wb");
815   if (aFile == NULL)
816   {
817     return false;
818   }
819
820   // Write header
821   fprintf (aFile, "P6\n%d %d\n255\n", (int )SizeX(), (int )SizeY());
822   fprintf (aFile, "# Image stored by OpenCASCADE framework in linear RGB colorspace\n");
823
824   // Write pixel data
825   Standard_Byte aByte;
826   for (Standard_Size aRow = 0; aRow < SizeY(); ++aRow)
827   {
828     for (Standard_Size aCol = 0; aCol < SizeX(); ++aCol)
829     {
830       // extremely SLOW but universal (implemented for all supported pixel formats)
831       const Quantity_ColorRGBA aColor = PixelColor ((Standard_Integer )aCol, (Standard_Integer )aRow);
832       aByte = Standard_Byte(aColor.GetRGB().Red()   * 255.0); fwrite (&aByte, 1, 1, aFile);
833       aByte = Standard_Byte(aColor.GetRGB().Green() * 255.0); fwrite (&aByte, 1, 1, aFile);
834       aByte = Standard_Byte(aColor.GetRGB().Blue()  * 255.0); fwrite (&aByte, 1, 1, aFile);
835     }
836   }
837
838   // Close file
839   fclose (aFile);
840   return true;
841 }
842
843 // =======================================================================
844 // function : Save
845 // purpose  :
846 // =======================================================================
847 bool Image_AlienPixMap::Save (const TCollection_AsciiString& theFileName)
848 {
849 #ifdef HAVE_FREEIMAGE
850   if (myLibImage == NULL)
851   {
852     return false;
853   }
854
855 #ifdef _WIN32
856   const TCollection_ExtendedString aFileNameW (theFileName.ToCString(), Standard_True);
857   FREE_IMAGE_FORMAT anImageFormat = FreeImage_GetFIFFromFilenameU (aFileNameW.ToWideString());
858 #else
859   FREE_IMAGE_FORMAT anImageFormat = FreeImage_GetFIFFromFilename (theFileName.ToCString());
860 #endif
861   if (anImageFormat == FIF_UNKNOWN)
862   {
863 #ifdef OCCT_DEBUG
864     std::cerr << "Image_PixMap, image format doesn't supported!\n";
865 #endif
866     return false;
867   }
868
869   if (IsTopDown())
870   {
871     FreeImage_FlipVertical (myLibImage);
872     SetTopDown (false);
873   }
874
875   // FreeImage doesn't provide flexible format conversion API
876   // so we should perform multiple conversions in some cases!
877   FIBITMAP* anImageToDump = myLibImage;
878   switch (anImageFormat)
879   {
880     case FIF_PNG:
881     case FIF_BMP:
882     {
883       if (Format() == Image_Format_BGR32
884        || Format() == Image_Format_RGB32)
885       {
886         // stupid FreeImage treats reserved byte as alpha if some bytes not set to 0xFF
887         for (Standard_Size aRow = 0; aRow < SizeY(); ++aRow)
888         {
889           for (Standard_Size aCol = 0; aCol < SizeX(); ++aCol)
890           {
891             myData.ChangeValue (aRow, aCol)[3] = 0xFF;
892           }
893         }
894       }
895       else if (FreeImage_GetImageType (myLibImage) != FIT_BITMAP)
896       {
897         anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_BITMAP);
898       }
899       break;
900     }
901     case FIF_GIF:
902     {
903       FIBITMAP* aTmpBitmap = myLibImage;
904       if (FreeImage_GetImageType (myLibImage) != FIT_BITMAP)
905       {
906         aTmpBitmap = FreeImage_ConvertToType (myLibImage, FIT_BITMAP);
907         if (aTmpBitmap == NULL)
908         {
909           return false;
910         }
911       }
912
913       if (FreeImage_GetBPP (aTmpBitmap) != 24)
914       {
915         FIBITMAP* aTmpBitmap24 = FreeImage_ConvertTo24Bits (aTmpBitmap);
916         if (aTmpBitmap != myLibImage)
917         {
918           FreeImage_Unload (aTmpBitmap);
919         }
920         if (aTmpBitmap24 == NULL)
921         {
922           return false;
923         }
924         aTmpBitmap = aTmpBitmap24;
925       }
926
927       // need conversion to image with palette (requires 24bit bitmap)
928       anImageToDump = FreeImage_ColorQuantize (aTmpBitmap, FIQ_NNQUANT);
929       if (aTmpBitmap != myLibImage)
930       {
931         FreeImage_Unload (aTmpBitmap);
932       }
933       break;
934     }
935     case FIF_HDR:
936     case FIF_EXR:
937     {
938       if (Format() == Image_Format_Gray
939        || Format() == Image_Format_Alpha)
940       {
941         anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_FLOAT);
942       }
943       else if (Format() == Image_Format_RGBA
944             || Format() == Image_Format_BGRA)
945       {
946         anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_RGBAF);
947       }
948       else
949       {
950         FREE_IMAGE_TYPE aImgTypeFI = FreeImage_GetImageType (myLibImage);
951         if (aImgTypeFI != FIT_RGBF
952          && aImgTypeFI != FIT_RGBAF
953          && aImgTypeFI != FIT_FLOAT)
954         {
955           anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_RGBF);
956         }
957       }
958       break;
959     }
960     default:
961     {
962       if (FreeImage_GetImageType (myLibImage) != FIT_BITMAP)
963       {
964         anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_BITMAP);
965         if (anImageToDump == NULL)
966         {
967           return false;
968         }
969       }
970
971       if (FreeImage_GetBPP (anImageToDump) != 24)
972       {
973         FIBITMAP* aTmpBitmap24 = FreeImage_ConvertTo24Bits (anImageToDump);
974         if (anImageToDump != myLibImage)
975         {
976           FreeImage_Unload (anImageToDump);
977         }
978         if (aTmpBitmap24 == NULL)
979         {
980           return false;
981         }
982         anImageToDump = aTmpBitmap24;
983       }
984       break;
985     }
986   }
987
988   if (anImageToDump == NULL)
989   {
990     return false;
991   }
992
993 #ifdef _WIN32
994   bool isSaved = (FreeImage_SaveU (anImageFormat, anImageToDump, aFileNameW.ToWideString()) != FALSE);
995 #else
996   bool isSaved = (FreeImage_Save  (anImageFormat, anImageToDump, theFileName.ToCString()) != FALSE);
997 #endif
998   if (anImageToDump != myLibImage)
999   {
1000     FreeImage_Unload (anImageToDump);
1001   }
1002   return isSaved;
1003
1004 #elif defined(HAVE_WINCODEC)
1005
1006   TCollection_AsciiString aFileNameLower = theFileName;
1007   aFileNameLower.LowerCase();
1008   GUID aFileFormat = getNullGuid();
1009   if (aFileNameLower.EndsWith (".ppm"))
1010   {
1011     return savePPM (theFileName);
1012   }
1013   else if (aFileNameLower.EndsWith (".bmp"))
1014   {
1015     aFileFormat = GUID_ContainerFormatBmp;
1016   }
1017   else if (aFileNameLower.EndsWith (".png"))
1018   {
1019     aFileFormat = GUID_ContainerFormatPng;
1020   }
1021   else if (aFileNameLower.EndsWith (".jpg")
1022         || aFileNameLower.EndsWith (".jpeg"))
1023   {
1024     aFileFormat = GUID_ContainerFormatJpeg;
1025   }
1026   else if (aFileNameLower.EndsWith (".tiff"))
1027   {
1028     aFileFormat = GUID_ContainerFormatTiff;
1029   }
1030   else if (aFileNameLower.EndsWith (".gif"))
1031   {
1032     aFileFormat = GUID_ContainerFormatGif;
1033   }
1034
1035   if (aFileFormat == getNullGuid())
1036   {
1037     Message::DefaultMessenger()->Send ("Error: unsupported image format", Message_Fail);
1038     return false;
1039   }
1040
1041   Image_ComPtr<IWICImagingFactory> aWicImgFactory;
1042   CoInitializeEx (NULL, COINIT_MULTITHREADED);
1043   if (CoCreateInstance (CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&aWicImgFactory.ChangePtr())) != S_OK)
1044   {
1045     Message::DefaultMessenger()->Send ("Error: cannot initialize WIC Imaging Factory", Message_Fail);
1046     return false;
1047   }
1048
1049   Image_ComPtr<IWICStream> aWicFileStream;
1050   Image_ComPtr<IWICBitmapEncoder> aWicEncoder;
1051   const TCollection_ExtendedString aFileNameW (theFileName);
1052   if (aWicImgFactory->CreateStream (&aWicFileStream.ChangePtr()) != S_OK
1053    || aWicFileStream->InitializeFromFilename (aFileNameW.ToWideString(), GENERIC_WRITE) != S_OK)
1054   {
1055     Message::DefaultMessenger()->Send ("Error: cannot create WIC File Stream", Message_Fail);
1056     return false;
1057   }
1058   if (aWicImgFactory->CreateEncoder (aFileFormat, NULL, &aWicEncoder.ChangePtr()) != S_OK
1059    || aWicEncoder->Initialize (aWicFileStream.get(), WICBitmapEncoderNoCache) != S_OK)
1060   {
1061     Message::DefaultMessenger()->Send ("Error: cannot create WIC Encoder", Message_Fail);
1062     return false;
1063   }
1064
1065   const WICPixelFormatGUID aWicPixelFormat = convertToWicFormat (myImgFormat);
1066   if (aWicPixelFormat == getNullGuid())
1067   {
1068     Message::DefaultMessenger()->Send ("Error: unsupported pixel format", Message_Fail);
1069     return false;
1070   }
1071
1072   WICPixelFormatGUID aWicPixelFormatRes = aWicPixelFormat;
1073   Image_ComPtr<IWICBitmapFrameEncode> aWicFrameEncode;
1074   if (aWicEncoder->CreateNewFrame (&aWicFrameEncode.ChangePtr(), NULL) != S_OK
1075    || aWicFrameEncode->Initialize (NULL) != S_OK
1076    || aWicFrameEncode->SetSize ((UINT )SizeX(), (UINT )SizeY()) != S_OK
1077    || aWicFrameEncode->SetPixelFormat (&aWicPixelFormatRes) != S_OK)
1078   {
1079     Message::DefaultMessenger()->Send ("Error: cannot create WIC Frame", Message_Fail);
1080     return false;
1081   }
1082
1083   if (aWicPixelFormatRes != aWicPixelFormat)
1084   {
1085     Message::DefaultMessenger()->Send ("Error: pixel format is unsupported by image format", Message_Fail);
1086     return false;
1087   }
1088
1089   if (IsTopDown())
1090   {
1091     if (aWicFrameEncode->WritePixels ((UINT )SizeY(), (UINT )SizeRowBytes(), (UINT )SizeBytes(), (BYTE* )Data()) != S_OK)
1092     {
1093       Message::DefaultMessenger()->Send ("Error: cannot write pixels to WIC Frame", Message_Fail);
1094       return false;
1095     }
1096   }
1097   else
1098   {
1099     for (Standard_Size aRow = 0; aRow < SizeY(); ++aRow)
1100     {
1101       if (aWicFrameEncode->WritePixels (1, (UINT )SizeRowBytes(), (UINT )SizeRowBytes(), (BYTE* )Row (aRow)) != S_OK)
1102       {
1103         Message::DefaultMessenger()->Send ("Error: cannot write pixels to WIC Frame", Message_Fail);
1104         return false;
1105       }
1106     }
1107   }
1108
1109   if (aWicFrameEncode->Commit() != S_OK
1110    || aWicEncoder->Commit() != S_OK)
1111   {
1112     Message::DefaultMessenger()->Send ("Error: cannot commit data to WIC Frame", Message_Fail);
1113     return false;
1114   }
1115   if (aWicFileStream->Commit (STGC_DEFAULT) != S_OK)
1116   {
1117     //Message::DefaultMessenger()->Send ("Error: cannot commit data to WIC File Stream", Message_Fail);
1118     //return false;
1119   }
1120   return true;
1121 #else
1122   const Standard_Integer aLen = theFileName.Length();
1123   if ((aLen >= 4) && (theFileName.Value (aLen - 3) == '.')
1124       && strcasecmp( theFileName.ToCString() + aLen - 3, "ppm") == 0 )
1125   {
1126     return savePPM (theFileName);
1127   }
1128 #ifdef OCCT_DEBUG
1129   std::cerr << "Image_PixMap, no image library available! Image saved in PPM format.\n";
1130 #endif
1131   return savePPM (theFileName);
1132 #endif
1133 }
1134
1135 // =======================================================================
1136 // function : AdjustGamma
1137 // purpose  :
1138 // =======================================================================
1139 bool Image_AlienPixMap::AdjustGamma (const Standard_Real theGammaCorr)
1140 {
1141 #ifdef HAVE_FREEIMAGE
1142   return FreeImage_AdjustGamma (myLibImage, theGammaCorr) != FALSE;
1143 #else
1144   (void )theGammaCorr;
1145   return false;
1146 #endif
1147 }