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