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