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