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