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