0024947: Redesign OCCT legacy type system -- automatic
[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 #ifdef HAVE_FREEIMAGE
17   #include <FreeImage.h>
18
19   #ifdef _MSC_VER
20     #pragma comment( lib, "FreeImage.lib" )
21   #endif
22 #endif
23
24 #include <Image_AlienPixMap.hxx>
25 #include <gp.hxx>
26 #include <Message.hxx>
27 #include <Message_Messenger.hxx>
28 #include <TCollection_AsciiString.hxx>
29 #include <TCollection_ExtendedString.hxx>
30 #include <OSD_OpenFile.hxx>
31 #include <fstream>
32 #include <algorithm>
33
34 #ifdef HAVE_FREEIMAGE
35 namespace
36 {
37   static Image_PixMap::ImgFormat convertFromFreeFormat (FREE_IMAGE_TYPE       theFormatFI,
38                                                         FREE_IMAGE_COLOR_TYPE theColorTypeFI,
39                                                         unsigned              theBitsPerPixel)
40   {
41     switch (theFormatFI)
42     {
43       case FIT_RGBF:   return Image_PixMap::ImgRGBF;
44       case FIT_RGBAF:  return Image_PixMap::ImgRGBAF;
45       case FIT_FLOAT:  return Image_PixMap::ImgGrayF;
46       case FIT_BITMAP:
47       {
48         switch (theColorTypeFI)
49         {
50           case FIC_MINISBLACK:
51           {
52             return Image_PixMap::ImgGray;
53           }
54           case FIC_RGB:
55           {
56             if (Image_PixMap::IsBigEndianHost())
57             {
58               return (theBitsPerPixel == 32) ? Image_PixMap::ImgRGB32 : Image_PixMap::ImgRGB;
59             }
60             else
61             {
62               return (theBitsPerPixel == 32) ? Image_PixMap::ImgBGR32 : Image_PixMap::ImgBGR;
63             }
64           }
65           case FIC_RGBALPHA:
66           {
67             return Image_PixMap::IsBigEndianHost() ? Image_PixMap::ImgRGBA : Image_PixMap::ImgBGRA;
68           }
69           default:
70             return Image_PixMap::ImgUNKNOWN;
71         }
72       }
73       default:
74         return Image_PixMap::ImgUNKNOWN;
75     }
76   }
77
78   static FREE_IMAGE_TYPE convertToFreeFormat (Image_PixMap::ImgFormat theFormat)
79   {
80     switch (theFormat)
81     {
82       case Image_PixMap::ImgGrayF:
83       case Image_PixMap::ImgAlphaF:
84         return FIT_FLOAT;
85       case Image_PixMap::ImgRGBAF:
86         return FIT_RGBAF;
87       case Image_PixMap::ImgRGBF:
88         return FIT_RGBF;
89       case Image_PixMap::ImgRGBA:
90       case Image_PixMap::ImgBGRA:
91       case Image_PixMap::ImgRGB32:
92       case Image_PixMap::ImgBGR32:
93       case Image_PixMap::ImgRGB:
94       case Image_PixMap::ImgBGR:
95       case Image_PixMap::ImgGray:
96       case Image_PixMap::ImgAlpha:
97         return FIT_BITMAP;
98       default:
99         return FIT_UNKNOWN;
100     }
101   }
102 };
103 #endif
104
105
106 // =======================================================================
107 // function : Image_AlienPixMap
108 // purpose  :
109 // =======================================================================
110 Image_AlienPixMap::Image_AlienPixMap()
111 : myLibImage (NULL)
112 {
113   SetTopDown (false);
114 }
115
116 // =======================================================================
117 // function : ~Image_AlienPixMap
118 // purpose  :
119 // =======================================================================
120 Image_AlienPixMap::~Image_AlienPixMap()
121 {
122   Clear();
123 }
124
125 // =======================================================================
126 // function : InitWrapper
127 // purpose  :
128 // =======================================================================
129 bool Image_AlienPixMap::InitWrapper (ImgFormat,
130                                      Standard_Byte*,
131                                      const Standard_Size,
132                                      const Standard_Size,
133                                      const Standard_Size)
134 {
135   Clear();
136   return false;
137 }
138
139 // =======================================================================
140 // function : InitTrash
141 // purpose  :
142 // =======================================================================
143 #ifdef HAVE_FREEIMAGE
144 bool Image_AlienPixMap::InitTrash (ImgFormat           thePixelFormat,
145                                    const Standard_Size theSizeX,
146                                    const Standard_Size theSizeY,
147                                    const Standard_Size /*theSizeRowBytes*/)
148 {
149   Clear();
150   FREE_IMAGE_TYPE aFormatFI = convertToFreeFormat (thePixelFormat);
151   int aBitsPerPixel = (int )Image_PixMap::SizePixelBytes (thePixelFormat) * 8;
152   if (aFormatFI == FIT_UNKNOWN)
153   {
154     aFormatFI     = FIT_BITMAP;
155     aBitsPerPixel = 24;
156   }
157
158   FIBITMAP* anImage = FreeImage_AllocateT (aFormatFI, (int )theSizeX, (int )theSizeY, aBitsPerPixel);
159   Image_PixMap::ImgFormat aFormat = convertFromFreeFormat (FreeImage_GetImageType (anImage),
160                                                            FreeImage_GetColorType (anImage),
161                                                            FreeImage_GetBPP (anImage));
162   if (thePixelFormat == Image_PixMap::ImgBGR32
163    || thePixelFormat == Image_PixMap::ImgRGB32)
164   {
165     //FreeImage_SetTransparent (anImage, FALSE);
166     aFormat = (aFormat == Image_PixMap::ImgBGRA) ? Image_PixMap::ImgBGR32 : Image_PixMap::ImgRGB32;
167   }
168
169   Image_PixMap::InitWrapper (aFormat, FreeImage_GetBits (anImage),
170                              FreeImage_GetWidth (anImage), FreeImage_GetHeight (anImage), FreeImage_GetPitch (anImage));
171   SetTopDown (false);
172
173   // assign image after wrapper initialization (virtual Clear() called inside)
174   myLibImage = anImage;
175   return true;
176 }
177 #else
178 bool Image_AlienPixMap::InitTrash (ImgFormat           thePixelFormat,
179                                    const Standard_Size theSizeX,
180                                    const Standard_Size theSizeY,
181                                    const Standard_Size theSizeRowBytes)
182 {
183   return Image_PixMap::InitTrash (thePixelFormat, theSizeX, theSizeY, theSizeRowBytes);
184 }
185 #endif
186
187 // =======================================================================
188 // function : InitCopy
189 // purpose  :
190 // =======================================================================
191 bool Image_AlienPixMap::InitCopy (const Image_PixMap& theCopy)
192 {
193   if (&theCopy == this)
194   {
195     // self-copying disallowed
196     return false;
197   }
198   if (!InitTrash (theCopy.Format(), theCopy.SizeX(), theCopy.SizeY(), theCopy.SizeRowBytes()))
199   {
200     return false;
201   }
202
203   if (myImgFormat == theCopy.Format())
204   {
205     if (SizeRowBytes() == theCopy.SizeRowBytes()
206      && TopDownInc()   == theCopy.TopDownInc())
207     {
208       // copy with one call
209       memcpy (ChangeData(), theCopy.Data(), std::min (SizeBytes(), theCopy.SizeBytes()));
210       return true;
211     }
212
213     // copy row-by-row
214     const Standard_Size aRowSizeBytes = std::min (SizeRowBytes(), theCopy.SizeRowBytes());
215     for (Standard_Size aRow = 0; aRow < myData.SizeY; ++aRow)
216     {
217       memcpy (ChangeRow (aRow), theCopy.Row (aRow), aRowSizeBytes);
218     }
219     return true;
220   }
221
222   // pixel format conversion required
223   Clear();
224   return false;
225 }
226
227 // =======================================================================
228 // function : Clear
229 // purpose  :
230 // =======================================================================
231 void Image_AlienPixMap::Clear()
232 {
233   Image_PixMap::Clear();
234 #ifdef HAVE_FREEIMAGE
235   if (myLibImage != NULL)
236   {
237     FreeImage_Unload (myLibImage);
238     myLibImage = NULL;
239   }
240 #endif
241 }
242
243 // =======================================================================
244 // function : Load
245 // purpose  :
246 // =======================================================================
247 #ifdef HAVE_FREEIMAGE
248 bool Image_AlienPixMap::Load (const TCollection_AsciiString& theImagePath)
249 {
250   Clear();
251
252 #ifdef _WIN32
253   const TCollection_ExtendedString aFileNameW (theImagePath.ToCString(), Standard_True);
254   FREE_IMAGE_FORMAT aFIF = FreeImage_GetFileTypeU ((const wchar_t* )aFileNameW.ToExtString(), 0);
255 #else
256   FREE_IMAGE_FORMAT aFIF = FreeImage_GetFileType (theImagePath.ToCString(), 0);
257 #endif
258   if (aFIF == FIF_UNKNOWN)
259   {
260     // no signature? try to guess the file format from the file extension
261     aFIF = FreeImage_GetFIFFromFilename (theImagePath.ToCString());
262   }
263   if ((aFIF == FIF_UNKNOWN) || !FreeImage_FIFSupportsReading (aFIF))
264   {
265     TCollection_AsciiString aMessage = "Error: image file '";
266     aMessage.AssignCat (theImagePath);
267     aMessage.AssignCat ("' has unsupported file format.");
268     ::Message::DefaultMessenger()->Send (aMessage, Message_Fail);
269     return false;
270   }
271
272   int aLoadFlags = 0;
273   if (aFIF == FIF_GIF)
274   {
275     // 'Play' the GIF to generate each frame (as 32bpp) instead of returning raw frame data when loading
276     aLoadFlags = GIF_PLAYBACK;
277   }
278   else if (aFIF == FIF_ICO)
279   {
280     // convert to 32bpp and create an alpha channel from the AND-mask when loading
281     aLoadFlags = ICO_MAKEALPHA;
282   }
283
284 #ifdef _WIN32
285   FIBITMAP* anImage = FreeImage_LoadU (aFIF, (const wchar_t* )aFileNameW.ToExtString(), aLoadFlags);
286 #else
287   FIBITMAP* anImage = FreeImage_Load  (aFIF, theImagePath.ToCString(), aLoadFlags);
288 #endif
289   if (anImage == NULL)
290   {
291     TCollection_AsciiString aMessage = "Error: image file '";
292     aMessage.AssignCat (theImagePath);
293     aMessage.AssignCat ("' is missing or invalid.");
294     ::Message::DefaultMessenger()->Send (aMessage, Message_Fail);
295     return false;
296   }
297
298   Image_PixMap::ImgFormat aFormat = convertFromFreeFormat (FreeImage_GetImageType (anImage),
299                                                            FreeImage_GetColorType (anImage),
300                                                            FreeImage_GetBPP (anImage));
301   if (aFormat == Image_PixMap::ImgUNKNOWN)
302   {
303     //anImage = FreeImage_ConvertTo24Bits (anImage);
304     TCollection_AsciiString aMessage = "Error: image file '";
305     aMessage.AssignCat (theImagePath);
306     aMessage.AssignCat ("' has unsupported pixel format.");
307     ::Message::DefaultMessenger()->Send (aMessage, Message_Fail);
308     return false;
309   }
310
311   Image_PixMap::InitWrapper (aFormat, FreeImage_GetBits (anImage),
312                              FreeImage_GetWidth (anImage), FreeImage_GetHeight (anImage), FreeImage_GetPitch (anImage));
313   SetTopDown (false);
314
315   // assign image after wrapper initialization (virtual Clear() called inside)
316   myLibImage = anImage;
317   return true;
318 }
319 #else
320 bool Image_AlienPixMap::Load (const TCollection_AsciiString&)
321 {
322   Clear();
323   return false;
324 }
325 #endif
326
327 // =======================================================================
328 // function : savePPM
329 // purpose  :
330 // =======================================================================
331 bool Image_AlienPixMap::savePPM (const TCollection_AsciiString& theFileName) const
332 {
333   if (IsEmpty())
334   {
335     return false;
336   }
337
338   // Open file
339   FILE* aFile = OSD_OpenFile (theFileName.ToCString(), "wb");
340   if (aFile == NULL)
341   {
342     return false;
343   }
344
345   // Write header
346   fprintf (aFile, "P6\n%d %d\n255\n", (int )SizeX(), (int )SizeY());
347   fprintf (aFile, "# Image stored by OpenCASCADE framework in linear RGB colorspace\n");
348
349   // Write pixel data
350   Quantity_Color aColor;
351   Quantity_Parameter aDummy;
352   Standard_Byte aByte;
353   for (Standard_Size aRow = 0; aRow < SizeY(); ++aRow)
354   {
355     for (Standard_Size aCol = 0; aCol < SizeX(); ++aCol)
356     {
357       // extremely SLOW but universal (implemented for all supported pixel formats)
358       aColor = PixelColor ((Standard_Integer )aCol, (Standard_Integer )aRow, aDummy);
359       aByte = Standard_Byte(aColor.Red() * 255.0);   fwrite (&aByte, 1, 1, aFile);
360       aByte = Standard_Byte(aColor.Green() * 255.0); fwrite (&aByte, 1, 1, aFile);
361       aByte = Standard_Byte(aColor.Blue() * 255.0);  fwrite (&aByte, 1, 1, aFile);
362     }
363   }
364
365   // Close file
366   fclose (aFile);
367   return true;
368 }
369
370 // =======================================================================
371 // function : Save
372 // purpose  :
373 // =======================================================================
374 bool Image_AlienPixMap::Save (const TCollection_AsciiString& theFileName)
375 {
376 #ifdef HAVE_FREEIMAGE
377   if (myLibImage == NULL)
378   {
379     return false;
380   }
381
382 #ifdef _WIN32
383   const TCollection_ExtendedString aFileNameW (theFileName.ToCString(), Standard_True);
384   FREE_IMAGE_FORMAT anImageFormat = FreeImage_GetFIFFromFilenameU ((const wchar_t* )aFileNameW.ToExtString());
385 #else
386   FREE_IMAGE_FORMAT anImageFormat = FreeImage_GetFIFFromFilename (theFileName.ToCString());
387 #endif
388   if (anImageFormat == FIF_UNKNOWN)
389   {
390 #ifdef OCCT_DEBUG
391     std::cerr << "Image_PixMap, image format doesn't supported!\n";
392 #endif
393     return false;
394   }
395
396   if (IsTopDown())
397   {
398     FreeImage_FlipVertical (myLibImage);
399     SetTopDown (false);
400   }
401
402   // FreeImage doesn't provide flexible format convertion API
403   // so we should perform multiple convertions in some cases!
404   FIBITMAP* anImageToDump = myLibImage;
405   switch (anImageFormat)
406   {
407     case FIF_PNG:
408     case FIF_BMP:
409     {
410       if (Format() == Image_PixMap::ImgBGR32
411        || Format() == Image_PixMap::ImgRGB32)
412       {
413         // stupid FreeImage treats reserved byte as alpha if some bytes not set to 0xFF
414         for (Standard_Size aRow = 0; aRow < SizeY(); ++aRow)
415         {
416           for (Standard_Size aCol = 0; aCol < SizeX(); ++aCol)
417           {
418             myData.ChangeValue (aRow, aCol)[3] = 0xFF;
419           }
420         }
421       }
422       else if (FreeImage_GetImageType (myLibImage) != FIT_BITMAP)
423       {
424         anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_BITMAP);
425       }
426       break;
427     }
428     case FIF_GIF:
429     {
430       FIBITMAP* aTmpBitmap = myLibImage;
431       if (FreeImage_GetImageType (myLibImage) != FIT_BITMAP)
432       {
433         aTmpBitmap = FreeImage_ConvertToType (myLibImage, FIT_BITMAP);
434         if (aTmpBitmap == NULL)
435         {
436           return false;
437         }
438       }
439
440       if (FreeImage_GetBPP (aTmpBitmap) != 24)
441       {
442         FIBITMAP* aTmpBitmap24 = FreeImage_ConvertTo24Bits (aTmpBitmap);
443         if (aTmpBitmap != myLibImage)
444         {
445           FreeImage_Unload (aTmpBitmap);
446         }
447         if (aTmpBitmap24 == NULL)
448         {
449           return false;
450         }
451         aTmpBitmap = aTmpBitmap24;
452       }
453
454       // need convertion to image with pallete (requires 24bit bitmap)
455       anImageToDump = FreeImage_ColorQuantize (aTmpBitmap, FIQ_NNQUANT);
456       if (aTmpBitmap != myLibImage)
457       {
458         FreeImage_Unload (aTmpBitmap);
459       }
460       break;
461     }
462     case FIF_EXR:
463     {
464       if (Format() == Image_PixMap::ImgGray
465        || Format() == Image_PixMap::ImgAlpha)
466       {
467         anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_FLOAT);
468       }
469       else if (Format() == Image_PixMap::ImgRGBA
470             || Format() == Image_PixMap::ImgBGRA)
471       {
472         anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_RGBAF);
473       }
474       else
475       {
476         FREE_IMAGE_TYPE aImgTypeFI = FreeImage_GetImageType (myLibImage);
477         if (aImgTypeFI != FIT_RGBF
478          && aImgTypeFI != FIT_RGBAF
479          && aImgTypeFI != FIT_FLOAT)
480         {
481           anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_RGBF);
482         }
483       }
484       break;
485     }
486     default:
487     {
488       if (FreeImage_GetImageType (myLibImage) != FIT_BITMAP)
489       {
490         anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_BITMAP);
491         if (anImageToDump == NULL)
492         {
493           return false;
494         }
495       }
496
497       if (FreeImage_GetBPP (anImageToDump) != 24)
498       {
499         FIBITMAP* aTmpBitmap24 = FreeImage_ConvertTo24Bits (anImageToDump);
500         if (anImageToDump != myLibImage)
501         {
502           FreeImage_Unload (anImageToDump);
503         }
504         if (aTmpBitmap24 == NULL)
505         {
506           return false;
507         }
508         anImageToDump = aTmpBitmap24;
509       }
510       break;
511     }
512   }
513
514   if (anImageToDump == NULL)
515   {
516     return false;
517   }
518
519 #ifdef _WIN32
520   bool isSaved = (FreeImage_SaveU (anImageFormat, anImageToDump, (const wchar_t* )aFileNameW.ToExtString()) != FALSE);
521 #else
522   bool isSaved = (FreeImage_Save  (anImageFormat, anImageToDump, theFileName.ToCString()) != FALSE);
523 #endif
524   if (anImageToDump != myLibImage)
525   {
526     FreeImage_Unload (anImageToDump);
527   }
528   return isSaved;
529 #else
530   const Standard_Integer aLen = theFileName.Length();
531   if ((aLen >= 4) && (theFileName.Value (aLen - 3) == '.')
532       && strcasecmp( theFileName.ToCString() + aLen - 3, "ppm") == 0 )
533   {
534     return savePPM (theFileName);
535   }
536 #ifdef OCCT_DEBUG
537   std::cerr << "Image_PixMap, no image library available! Image saved in PPM format.\n";
538 #endif
539   return savePPM (theFileName);
540 #endif
541 }
542
543 // =======================================================================
544 // function : AdjustGamma
545 // purpose  :
546 // =======================================================================
547 bool Image_AlienPixMap::AdjustGamma (const Standard_Real theGammaCorr)
548 {
549 #ifdef HAVE_FREEIMAGE
550   return FreeImage_AdjustGamma (myLibImage, theGammaCorr) != FALSE;
551 #else
552   (void )theGammaCorr;
553   return false;
554 #endif
555 }