0030946: Visualization - Image_AlienPixMap ignores 1-bit pixelformat when using FreeImage
[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   if (FreeImage_GetBPP (anImage) == 1)
593   {
594     FIBITMAP* aTmpImage = FreeImage_ConvertTo8Bits (anImage);
595     FreeImage_Unload (anImage);
596     anImage = aTmpImage;
597   }
598
599   Image_Format aFormat = convertFromFreeFormat (FreeImage_GetImageType(anImage),
600                                                 FreeImage_GetColorType(anImage),
601                                                 FreeImage_GetBPP      (anImage));
602   if (aFormat == Image_Format_UNKNOWN)
603   {
604     //anImage = FreeImage_ConvertTo24Bits (anImage);
605     ::Message::DefaultMessenger()->Send (    TCollection_AsciiString ("Error: image '") + theImagePath + "' has unsupported pixel format.",
606                                          Message_Fail);
607     return false;
608   }
609
610   Image_PixMap::InitWrapper (aFormat, FreeImage_GetBits (anImage),
611                              FreeImage_GetWidth (anImage), FreeImage_GetHeight (anImage), FreeImage_GetPitch (anImage));
612   SetTopDown (false);
613
614   // assign image after wrapper initialization (virtual Clear() called inside)
615   myLibImage = anImage;
616   return true;
617 }
618
619 bool Image_AlienPixMap::Load (std::istream& theStream,
620                               const TCollection_AsciiString& theFileName)
621 {
622   Clear();
623
624   Image_FreeImageStream aStream (theStream);
625   FreeImageIO aFiIO = aStream.GetFiIO();
626
627   FREE_IMAGE_FORMAT aFIF = FreeImage_GetFileTypeFromHandle (&aFiIO, &aStream, 0);
628   if (aFIF == FIF_UNKNOWN)
629   {
630     // no signature? try to guess the file format from the file extension
631     aFIF = FreeImage_GetFIFFromFilename (theFileName.ToCString());
632   }
633   if ((aFIF == FIF_UNKNOWN) || !FreeImage_FIFSupportsReading (aFIF))
634   {
635     ::Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: image stream '") + theFileName + "' has unsupported file format.",
636                                          Message_Fail);
637     return false;
638   }
639
640   int aLoadFlags = 0;
641   if (aFIF == FIF_GIF)
642   {
643     // 'Play' the GIF to generate each frame (as 32bpp) instead of returning raw frame data when loading
644     aLoadFlags = GIF_PLAYBACK;
645   }
646   else if (aFIF == FIF_ICO)
647   {
648     // convert to 32bpp and create an alpha channel from the AND-mask when loading
649     aLoadFlags = ICO_MAKEALPHA;
650   }
651
652   FIBITMAP* anImage = FreeImage_LoadFromHandle (aFIF, &aFiIO, &aStream, aLoadFlags);
653   if (anImage == NULL)
654   {
655     ::Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: image stream '") + theFileName + "' is missing or invalid.",
656                                          Message_Fail);
657     return false;
658   }
659
660   Image_Format aFormat = convertFromFreeFormat (FreeImage_GetImageType(anImage),
661                                                 FreeImage_GetColorType(anImage),
662                                                 FreeImage_GetBPP      (anImage));
663   if (aFormat == Image_Format_UNKNOWN)
664   {
665     ::Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: image stream '") + theFileName + "' has unsupported pixel format.",
666                                          Message_Fail);
667     return false;
668   }
669
670   Image_PixMap::InitWrapper (aFormat, FreeImage_GetBits (anImage),
671                              FreeImage_GetWidth (anImage), FreeImage_GetHeight (anImage), FreeImage_GetPitch (anImage));
672   SetTopDown (false);
673
674   // assign image after wrapper initialization (virtual Clear() called inside)
675   myLibImage = anImage;
676   return true;
677 }
678
679 #elif defined(HAVE_WINCODEC)
680 bool Image_AlienPixMap::Load (const Standard_Byte* theData,
681                               Standard_Size theLength,
682                               const TCollection_AsciiString& theFileName)
683 {
684   Clear();
685
686   Image_ComPtr<IWICImagingFactory> aWicImgFactory;
687   CoInitializeEx (NULL, COINIT_MULTITHREADED);
688   if (CoCreateInstance (CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&aWicImgFactory.ChangePtr())) != S_OK)
689   {
690     Message::DefaultMessenger()->Send ("Error: cannot initialize WIC Imaging Factory", Message_Fail);
691     return false;
692   }
693
694   Image_ComPtr<IWICBitmapDecoder> aWicDecoder;
695   Image_ComPtr<IWICStream> aWicStream;
696   if (theData != NULL)
697   {
698     if (aWicImgFactory->CreateStream (&aWicStream.ChangePtr()) != S_OK
699      || aWicStream->InitializeFromMemory ((BYTE* )theData, (DWORD )theLength) != S_OK)
700     {
701       Message::DefaultMessenger()->Send ("Error: cannot initialize WIC Stream", Message_Fail);
702       return false;
703     }
704     if (aWicImgFactory->CreateDecoderFromStream (aWicStream.get(), NULL, WICDecodeMetadataCacheOnDemand, &aWicDecoder.ChangePtr()) != S_OK)
705     {
706       Message::DefaultMessenger()->Send ("Error: cannot create WIC Image Decoder", Message_Fail);
707       return false;
708     }
709   }
710   else
711   {
712     const TCollection_ExtendedString aFileNameW (theFileName);
713     if (aWicImgFactory->CreateDecoderFromFilename (aFileNameW.ToWideString(), NULL, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &aWicDecoder.ChangePtr()) != S_OK)
714     {
715       Message::DefaultMessenger()->Send ("Error: cannot create WIC Image Decoder", Message_Fail);
716       return false;
717     }
718   }
719
720   UINT aFrameCount = 0, aFrameSizeX = 0, aFrameSizeY = 0;
721   WICPixelFormatGUID aWicPixelFormat = getNullGuid();
722   Image_ComPtr<IWICBitmapFrameDecode> aWicFrameDecode;
723   if (aWicDecoder->GetFrameCount (&aFrameCount) != S_OK
724    || aFrameCount < 1
725    || aWicDecoder->GetFrame (0, &aWicFrameDecode.ChangePtr()) != S_OK
726    || aWicFrameDecode->GetSize (&aFrameSizeX, &aFrameSizeY) != S_OK
727    || aWicFrameDecode->GetPixelFormat (&aWicPixelFormat))
728   {
729     Message::DefaultMessenger()->Send ("Error: cannot get WIC Image Frame", Message_Fail);
730     return false;
731   }
732
733   Image_ComPtr<IWICFormatConverter> aWicConvertedFrame;
734   Image_Format aPixelFormat = convertFromWicFormat (aWicPixelFormat);
735   if (aPixelFormat == Image_Format_UNKNOWN)
736   {
737     aPixelFormat = Image_Format_RGB;
738     if (aWicImgFactory->CreateFormatConverter (&aWicConvertedFrame.ChangePtr()) != S_OK
739      || aWicConvertedFrame->Initialize (aWicFrameDecode.get(), convertToWicFormat (aPixelFormat), WICBitmapDitherTypeNone, NULL, 0.0f, WICBitmapPaletteTypeCustom) != S_OK)
740     {
741       Message::DefaultMessenger()->Send ("Error: cannot convert WIC Image Frame to RGB format", Message_Fail);
742       return false;
743     }
744     aWicFrameDecode.Nullify();
745   }
746
747   if (!Image_PixMap::InitTrash (aPixelFormat, aFrameSizeX, aFrameSizeY))
748   {
749     Message::DefaultMessenger()->Send ("Error: cannot initialize memory for image", Message_Fail);
750     return false;
751   }
752
753   IWICBitmapSource* aWicSrc = aWicFrameDecode.get();
754   if(!aWicConvertedFrame.IsNull())
755   {
756     aWicSrc = aWicConvertedFrame.get();
757   }
758   if (aWicSrc->CopyPixels (NULL, (UINT )SizeRowBytes(), (UINT )SizeBytes(), ChangeData()) != S_OK)
759   {
760     Message::DefaultMessenger()->Send ("Error: cannot copy pixels from WIC Image", Message_Fail);
761     return false;
762   }
763   SetTopDown (true);
764   return true;
765 }
766 bool Image_AlienPixMap::Load (std::istream& theStream,
767                               const TCollection_AsciiString& theFilePath)
768 {
769   Clear();
770
771   // fallback copying stream data into transient buffer
772   const std::streamoff aStart = theStream.tellg();
773   theStream.seekg (0, std::ios::end);
774   const Standard_Integer aLen = Standard_Integer(theStream.tellg() - aStart);
775   theStream.seekg (aStart);
776   if (aLen <= 0)
777   {
778     Message::DefaultMessenger()->Send ("Error: empty stream", Message_Fail);
779     return false;
780   }
781
782   NCollection_Array1<Standard_Byte> aBuff (1, aLen);
783   if (!theStream.read ((char* )&aBuff.ChangeFirst(), aBuff.Size()))
784   {
785     Message::DefaultMessenger()->Send ("Error: unable to read stream", Message_Fail);
786     return false;
787   }
788
789   return Load (&aBuff.ChangeFirst(), aBuff.Size(), theFilePath);
790 }
791 #else
792 bool Image_AlienPixMap::Load (std::istream& ,
793                               const TCollection_AsciiString& )
794 {
795   Clear();
796   Message::DefaultMessenger()->Send ("Error: no image library available", Message_Fail);
797   return false;
798 }
799 bool Image_AlienPixMap::Load (const Standard_Byte* ,
800                               Standard_Size ,
801                               const TCollection_AsciiString& )
802 {
803   Clear();
804   Message::DefaultMessenger()->Send ("Error: no image library available", Message_Fail);
805   return false;
806 }
807 #endif
808
809 // =======================================================================
810 // function : savePPM
811 // purpose  :
812 // =======================================================================
813 bool Image_AlienPixMap::savePPM (const TCollection_AsciiString& theFileName) const
814 {
815   if (IsEmpty())
816   {
817     return false;
818   }
819
820   // Open file
821   FILE* aFile = OSD_OpenFile (theFileName.ToCString(), "wb");
822   if (aFile == NULL)
823   {
824     return false;
825   }
826
827   // Write header
828   fprintf (aFile, "P6\n%d %d\n255\n", (int )SizeX(), (int )SizeY());
829   fprintf (aFile, "# Image stored by OpenCASCADE framework in linear RGB colorspace\n");
830
831   // Write pixel data
832   Standard_Byte aByte;
833   for (Standard_Size aRow = 0; aRow < SizeY(); ++aRow)
834   {
835     for (Standard_Size aCol = 0; aCol < SizeX(); ++aCol)
836     {
837       // extremely SLOW but universal (implemented for all supported pixel formats)
838       const Quantity_ColorRGBA aColor = PixelColor ((Standard_Integer )aCol, (Standard_Integer )aRow);
839       aByte = Standard_Byte(aColor.GetRGB().Red()   * 255.0); fwrite (&aByte, 1, 1, aFile);
840       aByte = Standard_Byte(aColor.GetRGB().Green() * 255.0); fwrite (&aByte, 1, 1, aFile);
841       aByte = Standard_Byte(aColor.GetRGB().Blue()  * 255.0); fwrite (&aByte, 1, 1, aFile);
842     }
843   }
844
845   // Close file
846   fclose (aFile);
847   return true;
848 }
849
850 // =======================================================================
851 // function : Save
852 // purpose  :
853 // =======================================================================
854 bool Image_AlienPixMap::Save (const TCollection_AsciiString& theFileName)
855 {
856 #ifdef HAVE_FREEIMAGE
857   if (myLibImage == NULL)
858   {
859     return false;
860   }
861
862 #ifdef _WIN32
863   const TCollection_ExtendedString aFileNameW (theFileName.ToCString(), Standard_True);
864   FREE_IMAGE_FORMAT anImageFormat = FreeImage_GetFIFFromFilenameU (aFileNameW.ToWideString());
865 #else
866   FREE_IMAGE_FORMAT anImageFormat = FreeImage_GetFIFFromFilename (theFileName.ToCString());
867 #endif
868   if (anImageFormat == FIF_UNKNOWN)
869   {
870 #ifdef OCCT_DEBUG
871     std::cerr << "Image_PixMap, image format doesn't supported!\n";
872 #endif
873     return false;
874   }
875
876   if (IsTopDown())
877   {
878     FreeImage_FlipVertical (myLibImage);
879     SetTopDown (false);
880   }
881
882   // FreeImage doesn't provide flexible format conversion API
883   // so we should perform multiple conversions in some cases!
884   FIBITMAP* anImageToDump = myLibImage;
885   switch (anImageFormat)
886   {
887     case FIF_PNG:
888     case FIF_BMP:
889     {
890       if (Format() == Image_Format_BGR32
891        || Format() == Image_Format_RGB32)
892       {
893         // stupid FreeImage treats reserved byte as alpha if some bytes not set to 0xFF
894         for (Standard_Size aRow = 0; aRow < SizeY(); ++aRow)
895         {
896           for (Standard_Size aCol = 0; aCol < SizeX(); ++aCol)
897           {
898             myData.ChangeValue (aRow, aCol)[3] = 0xFF;
899           }
900         }
901       }
902       else if (FreeImage_GetImageType (myLibImage) != FIT_BITMAP)
903       {
904         anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_BITMAP);
905       }
906       break;
907     }
908     case FIF_GIF:
909     {
910       FIBITMAP* aTmpBitmap = myLibImage;
911       if (FreeImage_GetImageType (myLibImage) != FIT_BITMAP)
912       {
913         aTmpBitmap = FreeImage_ConvertToType (myLibImage, FIT_BITMAP);
914         if (aTmpBitmap == NULL)
915         {
916           return false;
917         }
918       }
919
920       if (FreeImage_GetBPP (aTmpBitmap) != 24)
921       {
922         FIBITMAP* aTmpBitmap24 = FreeImage_ConvertTo24Bits (aTmpBitmap);
923         if (aTmpBitmap != myLibImage)
924         {
925           FreeImage_Unload (aTmpBitmap);
926         }
927         if (aTmpBitmap24 == NULL)
928         {
929           return false;
930         }
931         aTmpBitmap = aTmpBitmap24;
932       }
933
934       // need conversion to image with palette (requires 24bit bitmap)
935       anImageToDump = FreeImage_ColorQuantize (aTmpBitmap, FIQ_NNQUANT);
936       if (aTmpBitmap != myLibImage)
937       {
938         FreeImage_Unload (aTmpBitmap);
939       }
940       break;
941     }
942     case FIF_HDR:
943     case FIF_EXR:
944     {
945       if (Format() == Image_Format_Gray
946        || Format() == Image_Format_Alpha)
947       {
948         anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_FLOAT);
949       }
950       else if (Format() == Image_Format_RGBA
951             || Format() == Image_Format_BGRA)
952       {
953         anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_RGBAF);
954       }
955       else
956       {
957         FREE_IMAGE_TYPE aImgTypeFI = FreeImage_GetImageType (myLibImage);
958         if (aImgTypeFI != FIT_RGBF
959          && aImgTypeFI != FIT_RGBAF
960          && aImgTypeFI != FIT_FLOAT)
961         {
962           anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_RGBF);
963         }
964       }
965       break;
966     }
967     default:
968     {
969       if (FreeImage_GetImageType (myLibImage) != FIT_BITMAP)
970       {
971         anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_BITMAP);
972         if (anImageToDump == NULL)
973         {
974           return false;
975         }
976       }
977
978       if (FreeImage_GetBPP (anImageToDump) != 24)
979       {
980         FIBITMAP* aTmpBitmap24 = FreeImage_ConvertTo24Bits (anImageToDump);
981         if (anImageToDump != myLibImage)
982         {
983           FreeImage_Unload (anImageToDump);
984         }
985         if (aTmpBitmap24 == NULL)
986         {
987           return false;
988         }
989         anImageToDump = aTmpBitmap24;
990       }
991       break;
992     }
993   }
994
995   if (anImageToDump == NULL)
996   {
997     return false;
998   }
999
1000 #ifdef _WIN32
1001   bool isSaved = (FreeImage_SaveU (anImageFormat, anImageToDump, aFileNameW.ToWideString()) != FALSE);
1002 #else
1003   bool isSaved = (FreeImage_Save  (anImageFormat, anImageToDump, theFileName.ToCString()) != FALSE);
1004 #endif
1005   if (anImageToDump != myLibImage)
1006   {
1007     FreeImage_Unload (anImageToDump);
1008   }
1009   return isSaved;
1010
1011 #elif defined(HAVE_WINCODEC)
1012
1013   TCollection_AsciiString aFileNameLower = theFileName;
1014   aFileNameLower.LowerCase();
1015   GUID aFileFormat = getNullGuid();
1016   if (aFileNameLower.EndsWith (".ppm"))
1017   {
1018     return savePPM (theFileName);
1019   }
1020   else if (aFileNameLower.EndsWith (".bmp"))
1021   {
1022     aFileFormat = GUID_ContainerFormatBmp;
1023   }
1024   else if (aFileNameLower.EndsWith (".png"))
1025   {
1026     aFileFormat = GUID_ContainerFormatPng;
1027   }
1028   else if (aFileNameLower.EndsWith (".jpg")
1029         || aFileNameLower.EndsWith (".jpeg"))
1030   {
1031     aFileFormat = GUID_ContainerFormatJpeg;
1032   }
1033   else if (aFileNameLower.EndsWith (".tiff"))
1034   {
1035     aFileFormat = GUID_ContainerFormatTiff;
1036   }
1037   else if (aFileNameLower.EndsWith (".gif"))
1038   {
1039     aFileFormat = GUID_ContainerFormatGif;
1040   }
1041
1042   if (aFileFormat == getNullGuid())
1043   {
1044     Message::DefaultMessenger()->Send ("Error: unsupported image format", Message_Fail);
1045     return false;
1046   }
1047
1048   Image_ComPtr<IWICImagingFactory> aWicImgFactory;
1049   CoInitializeEx (NULL, COINIT_MULTITHREADED);
1050   if (CoCreateInstance (CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&aWicImgFactory.ChangePtr())) != S_OK)
1051   {
1052     Message::DefaultMessenger()->Send ("Error: cannot initialize WIC Imaging Factory", Message_Fail);
1053     return false;
1054   }
1055
1056   Image_ComPtr<IWICStream> aWicFileStream;
1057   Image_ComPtr<IWICBitmapEncoder> aWicEncoder;
1058   const TCollection_ExtendedString aFileNameW (theFileName);
1059   if (aWicImgFactory->CreateStream (&aWicFileStream.ChangePtr()) != S_OK
1060    || aWicFileStream->InitializeFromFilename (aFileNameW.ToWideString(), GENERIC_WRITE) != S_OK)
1061   {
1062     Message::DefaultMessenger()->Send ("Error: cannot create WIC File Stream", Message_Fail);
1063     return false;
1064   }
1065   if (aWicImgFactory->CreateEncoder (aFileFormat, NULL, &aWicEncoder.ChangePtr()) != S_OK
1066    || aWicEncoder->Initialize (aWicFileStream.get(), WICBitmapEncoderNoCache) != S_OK)
1067   {
1068     Message::DefaultMessenger()->Send ("Error: cannot create WIC Encoder", Message_Fail);
1069     return false;
1070   }
1071
1072   const WICPixelFormatGUID aWicPixelFormat = convertToWicFormat (myImgFormat);
1073   if (aWicPixelFormat == getNullGuid())
1074   {
1075     Message::DefaultMessenger()->Send ("Error: unsupported pixel format", Message_Fail);
1076     return false;
1077   }
1078
1079   WICPixelFormatGUID aWicPixelFormatRes = aWicPixelFormat;
1080   Image_ComPtr<IWICBitmapFrameEncode> aWicFrameEncode;
1081   if (aWicEncoder->CreateNewFrame (&aWicFrameEncode.ChangePtr(), NULL) != S_OK
1082    || aWicFrameEncode->Initialize (NULL) != S_OK
1083    || aWicFrameEncode->SetSize ((UINT )SizeX(), (UINT )SizeY()) != S_OK
1084    || aWicFrameEncode->SetPixelFormat (&aWicPixelFormatRes) != S_OK)
1085   {
1086     Message::DefaultMessenger()->Send ("Error: cannot create WIC Frame", Message_Fail);
1087     return false;
1088   }
1089
1090   if (aWicPixelFormatRes != aWicPixelFormat)
1091   {
1092     Message::DefaultMessenger()->Send ("Error: pixel format is unsupported by image format", Message_Fail);
1093     return false;
1094   }
1095
1096   if (IsTopDown())
1097   {
1098     if (aWicFrameEncode->WritePixels ((UINT )SizeY(), (UINT )SizeRowBytes(), (UINT )SizeBytes(), (BYTE* )Data()) != S_OK)
1099     {
1100       Message::DefaultMessenger()->Send ("Error: cannot write pixels to WIC Frame", Message_Fail);
1101       return false;
1102     }
1103   }
1104   else
1105   {
1106     for (Standard_Size aRow = 0; aRow < SizeY(); ++aRow)
1107     {
1108       if (aWicFrameEncode->WritePixels (1, (UINT )SizeRowBytes(), (UINT )SizeRowBytes(), (BYTE* )Row (aRow)) != S_OK)
1109       {
1110         Message::DefaultMessenger()->Send ("Error: cannot write pixels to WIC Frame", Message_Fail);
1111         return false;
1112       }
1113     }
1114   }
1115
1116   if (aWicFrameEncode->Commit() != S_OK
1117    || aWicEncoder->Commit() != S_OK)
1118   {
1119     Message::DefaultMessenger()->Send ("Error: cannot commit data to WIC Frame", Message_Fail);
1120     return false;
1121   }
1122   if (aWicFileStream->Commit (STGC_DEFAULT) != S_OK)
1123   {
1124     //Message::DefaultMessenger()->Send ("Error: cannot commit data to WIC File Stream", Message_Fail);
1125     //return false;
1126   }
1127   return true;
1128 #else
1129   const Standard_Integer aLen = theFileName.Length();
1130   if ((aLen >= 4) && (theFileName.Value (aLen - 3) == '.')
1131       && strcasecmp( theFileName.ToCString() + aLen - 3, "ppm") == 0 )
1132   {
1133     return savePPM (theFileName);
1134   }
1135 #ifdef OCCT_DEBUG
1136   std::cerr << "Image_PixMap, no image library available! Image saved in PPM format.\n";
1137 #endif
1138   return savePPM (theFileName);
1139 #endif
1140 }
1141
1142 // =======================================================================
1143 // function : AdjustGamma
1144 // purpose  :
1145 // =======================================================================
1146 bool Image_AlienPixMap::AdjustGamma (const Standard_Real theGammaCorr)
1147 {
1148 #ifdef HAVE_FREEIMAGE
1149   return FreeImage_AdjustGamma (myLibImage, theGammaCorr) != FALSE;
1150 #else
1151   (void )theGammaCorr;
1152   return false;
1153 #endif
1154 }