fd00cfb822c7d04b3bf7e66082006bfa2c209584
[occt.git] / src / Image / Image_AlienPixMap.cxx
1 // Created on: 2010-09-16
2 // Created by: KGV
3 // Copyright (c) 2010-2012 OPEN CASCADE SAS
4 //
5 // The content of this file is subject to the Open CASCADE Technology Public
6 // License Version 6.5 (the "License"). You may not use the content of this file
7 // except in compliance with the License. Please obtain a copy of the License
8 // at http://www.opencascade.org and read it completely before using this file.
9 //
10 // The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
11 // main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
12 //
13 // The Original Code and all software distributed under the License is
14 // distributed on an "AS IS" basis, without warranty of any kind, and the
15 // Initial Developer hereby disclaims all such warranties, including without
16 // limitation, any warranties of merchantability, fitness for a particular
17 // purpose or non-infringement. Please see the License for the specific terms
18 // and conditions governing the rights and limitations under the License.
19
20 #ifdef HAVE_CONFIG_H
21   #include <config.h>
22 #endif
23
24 #ifdef HAVE_FREEIMAGE
25   #include <FreeImage.h>
26
27   #ifdef _MSC_VER
28     #pragma comment( lib, "FreeImage.lib" )
29   #endif
30 #endif
31
32 #include <Image_AlienPixMap.hxx>
33 #include <gp.hxx>
34 #include <TCollection_AsciiString.hxx>
35 #include <fstream>
36
37 #ifdef HAVE_FREEIMAGE
38 namespace
39 {
40   static Image_PixMap::ImgFormat convertFromFreeFormat (FREE_IMAGE_TYPE       theFormatFI,
41                                                         FREE_IMAGE_COLOR_TYPE theColorTypeFI,
42                                                         unsigned              theBitsPerPixel)
43   {
44     switch (theFormatFI)
45     {
46       case FIT_RGBF:   return Image_PixMap::ImgRGBF;
47       case FIT_RGBAF:  return Image_PixMap::ImgRGBAF;
48       case FIT_FLOAT:  return Image_PixMap::ImgGrayF;
49       case FIT_BITMAP:
50       {
51         switch (theColorTypeFI)
52         {
53           case FIC_MINISBLACK:
54           {
55             return Image_PixMap::ImgGray;
56           }
57           case FIC_RGB:
58           {
59             if (Image_PixMap::IsBigEndianHost())
60             {
61               return (theBitsPerPixel == 32) ? Image_PixMap::ImgRGB32 : Image_PixMap::ImgRGB;
62             }
63             else
64             {
65               return (theBitsPerPixel == 32) ? Image_PixMap::ImgBGR32 : Image_PixMap::ImgBGR;
66             }
67           }
68           case FIC_RGBALPHA:
69           {
70             return Image_PixMap::IsBigEndianHost() ? Image_PixMap::ImgRGBA : Image_PixMap::ImgBGRA;
71           }
72           default:
73             return Image_PixMap::ImgUNKNOWN;
74         }
75       }
76       default:
77         return Image_PixMap::ImgUNKNOWN;
78     }
79   }
80
81   static FREE_IMAGE_TYPE convertToFreeFormat (Image_PixMap::ImgFormat theFormat)
82   {
83     switch (theFormat)
84     {
85       case Image_PixMap::ImgGrayF:
86         return FIT_FLOAT;
87       case Image_PixMap::ImgRGBAF:
88         return FIT_RGBAF;
89       case Image_PixMap::ImgRGBF:
90         return FIT_RGBF;
91       case Image_PixMap::ImgRGBA:
92       case Image_PixMap::ImgBGRA:
93       case Image_PixMap::ImgRGB32:
94       case Image_PixMap::ImgBGR32:
95       case Image_PixMap::ImgRGB:
96       case Image_PixMap::ImgBGR:
97       case Image_PixMap::ImgGray:
98         return FIT_BITMAP;
99       default:
100         return FIT_UNKNOWN;
101     }
102   }
103 };
104 #endif
105
106 IMPLEMENT_STANDARD_HANDLE (Image_AlienPixMap, Image_PixMap)
107 IMPLEMENT_STANDARD_RTTIEXT(Image_AlienPixMap, Image_PixMap)
108
109 // =======================================================================
110 // function : Image_AlienPixMap
111 // purpose  :
112 // =======================================================================
113 Image_AlienPixMap::Image_AlienPixMap()
114 : myLibImage (NULL)
115 {
116   SetTopDown (false);
117 }
118
119 // =======================================================================
120 // function : ~Image_AlienPixMap
121 // purpose  :
122 // =======================================================================
123 Image_AlienPixMap::~Image_AlienPixMap()
124 {
125   Clear();
126 }
127
128 // =======================================================================
129 // function : InitWrapper
130 // purpose  :
131 // =======================================================================
132 bool Image_AlienPixMap::InitWrapper (ImgFormat,
133                                      Standard_Byte*,
134                                      const Standard_Size,
135                                      const Standard_Size,
136                                      const Standard_Size)
137 {
138   Clear();
139   return false;
140 }
141
142 // =======================================================================
143 // function : InitTrash
144 // purpose  :
145 // =======================================================================
146 bool Image_AlienPixMap::InitTrash (ImgFormat           thePixelFormat,
147                                    const Standard_Size theSizeX,
148                                    const Standard_Size theSizeY,
149                                    const Standard_Size theSizeRowBytes)
150 {
151   Clear();
152 #ifdef HAVE_FREEIMAGE
153   FREE_IMAGE_TYPE aFormatFI = convertToFreeFormat (thePixelFormat);
154   int aBitsPerPixel = (int )Image_PixMap::SizePixelBytes (thePixelFormat) * 8;
155   if (aFormatFI == FIT_UNKNOWN)
156   {
157     aFormatFI     = FIT_BITMAP;
158     aBitsPerPixel = 24;
159   }
160
161   FIBITMAP* anImage = FreeImage_AllocateT (aFormatFI, (int )theSizeX, (int )theSizeY, aBitsPerPixel);
162   Image_PixMap::ImgFormat aFormat = convertFromFreeFormat (FreeImage_GetImageType (anImage),
163                                                            FreeImage_GetColorType (anImage),
164                                                            FreeImage_GetBPP (anImage));
165   if (thePixelFormat == Image_PixMap::ImgBGR32
166    || thePixelFormat == Image_PixMap::ImgRGB32)
167   {
168     //FreeImage_SetTransparent (anImage, FALSE);
169     aFormat = (aFormat == Image_PixMap::ImgBGRA) ? Image_PixMap::ImgBGR32 : Image_PixMap::ImgRGB32;
170   }
171
172   Image_PixMap::InitWrapper (aFormat, FreeImage_GetBits (anImage),
173                              FreeImage_GetWidth (anImage), FreeImage_GetHeight (anImage), FreeImage_GetPitch (anImage));
174   SetTopDown (false);
175
176   // assign image after wrapper initialization (virtual Clear() called inside)
177   myLibImage = anImage;
178   return true;
179 #else
180   return Image_PixMap::InitTrash (thePixelFormat, theSizeX, theSizeY, theSizeRowBytes);
181 #endif
182 }
183
184 // =======================================================================
185 // function : InitCopy
186 // purpose  :
187 // =======================================================================
188 bool Image_AlienPixMap::InitCopy (const Image_PixMap& theCopy)
189 {
190   if (&theCopy == this)
191   {
192     // self-copying disallowed
193     return false;
194   }
195   if (!InitTrash (theCopy.Format(), theCopy.SizeX(), theCopy.SizeY(), theCopy.SizeRowBytes()))
196   {
197     return false;
198   }
199
200   if (myImgFormat == theCopy.Format())
201   {
202     if (myData.mySizeRowBytes == theCopy.SizeRowBytes()
203      && myData.myTopToDown    == theCopy.TopDownInc())
204     {
205       // copy with one call
206       memcpy (myData.myDataPtr, theCopy.Data(), theCopy.SizeBytes());
207       return true;
208     }
209
210     // copy row-by-row
211     const Standard_Size aRowSizeBytes = (myData.mySizeRowBytes > theCopy.SizeRowBytes())
212                                       ? theCopy.SizeRowBytes() : myData.mySizeRowBytes;
213     for (Standard_Size aRow = 0; aRow < myData.mySizeY; ++aRow)
214     {
215       memcpy (ChangeRow (aRow), theCopy.Row (aRow), aRowSizeBytes);
216     }
217     return true;
218   }
219
220   // pixel format conversion required
221   Clear();
222   return false;
223 }
224
225 // =======================================================================
226 // function : Clear
227 // purpose  :
228 // =======================================================================
229 void Image_AlienPixMap::Clear (ImgFormat thePixelFormat)
230 {
231   Image_PixMap::Clear (thePixelFormat);
232 #ifdef HAVE_FREEIMAGE
233   if (myLibImage != NULL)
234   {
235     FreeImage_Unload (myLibImage);
236     myLibImage = NULL;
237   }
238 #endif
239 }
240
241 // =======================================================================
242 // function : Load
243 // purpose  :
244 // =======================================================================
245 #ifdef HAVE_FREEIMAGE
246 bool Image_AlienPixMap::Load (const TCollection_AsciiString& theImagePath)
247 {
248   Clear();
249   FREE_IMAGE_FORMAT aFIF = FreeImage_GetFileType (theImagePath.ToCString(), 0);
250   if (aFIF == FIF_UNKNOWN)
251   {
252     // no signature? try to guess the file format from the file extension
253     aFIF = FreeImage_GetFIFFromFilename (theImagePath.ToCString());
254   }
255   if ((aFIF == FIF_UNKNOWN) || !FreeImage_FIFSupportsReading (aFIF))
256   {
257     // unsupported image format
258     return false;
259   }
260
261   int aLoadFlags = 0;
262   if (aFIF == FIF_GIF)
263   {
264     // 'Play' the GIF to generate each frame (as 32bpp) instead of returning raw frame data when loading
265     aLoadFlags = GIF_PLAYBACK;
266   }
267   else if (aFIF == FIF_ICO)
268   {
269     // convert to 32bpp and create an alpha channel from the AND-mask when loading
270     aLoadFlags = ICO_MAKEALPHA;
271   }
272
273   FIBITMAP* anImage = FreeImage_Load (aFIF, theImagePath.ToCString(), aLoadFlags);
274   if (anImage == NULL)
275   {
276     return false;
277   }
278
279   Image_PixMap::ImgFormat aFormat = convertFromFreeFormat (FreeImage_GetImageType (anImage),
280                                                            FreeImage_GetColorType (anImage),
281                                                            FreeImage_GetBPP (anImage));
282   if (aFormat == Image_PixMap::ImgUNKNOWN)
283   {
284     //anImage = FreeImage_ConvertTo24Bits (anImage);
285     return false;
286   }
287
288   Image_PixMap::InitWrapper (aFormat, FreeImage_GetBits (anImage),
289                              FreeImage_GetWidth (anImage), FreeImage_GetHeight (anImage), FreeImage_GetPitch (anImage));
290   SetTopDown (false);
291
292   // assign image after wrapper initialization (virtual Clear() called inside)
293   myLibImage = anImage;
294   return true;
295 }
296 #else
297 bool Image_AlienPixMap::Load (const TCollection_AsciiString&)
298 {
299   Clear();
300   return false;
301 }
302 #endif
303
304 // =======================================================================
305 // function : savePPM
306 // purpose  :
307 // =======================================================================
308 bool Image_AlienPixMap::savePPM (const TCollection_AsciiString& theFileName) const
309 {
310   if (IsEmpty())
311   {
312     return false;
313   }
314
315   // Open file
316   FILE* aFile = fopen (theFileName.ToCString(), "wb");
317   if (aFile == NULL)
318   {
319     return false;
320   }
321
322   // Write header
323   fprintf (aFile, "P6\n%d %d\n255\n", (int )SizeX(), (int )SizeY());
324   fprintf (aFile, "# Image stored by OpenCASCADE framework in linear RGB colorspace\n");
325
326   // Write pixel data
327   Quantity_Color aColor;
328   Quantity_Parameter aDummy;
329   Standard_Byte aByte;
330   for (Standard_Size aRow = 0; aRow < SizeY(); ++aRow)
331   {
332     for (Standard_Size aCol = 0; aCol < SizeY(); ++aCol)
333     {
334       // extremely SLOW but universal (implemented for all supported pixel formats)
335       aColor = PixelColor ((Standard_Integer )aCol, (Standard_Integer )aRow, aDummy);
336       aByte = Standard_Byte(aColor.Red() * 255.0);   fwrite (&aByte, 1, 1, aFile);
337       aByte = Standard_Byte(aColor.Green() * 255.0); fwrite (&aByte, 1, 1, aFile);
338       aByte = Standard_Byte(aColor.Blue() * 255.0);  fwrite (&aByte, 1, 1, aFile);
339     }
340   }
341
342   // Close file
343   fclose (aFile);
344   return true;
345 }
346
347 // =======================================================================
348 // function : Save
349 // purpose  :
350 // =======================================================================
351 bool Image_AlienPixMap::Save (const TCollection_AsciiString& theFileName)
352 {
353 #ifdef HAVE_FREEIMAGE
354   if (myLibImage == NULL)
355   {
356     return false;
357   }
358
359   FREE_IMAGE_FORMAT anImageFormat = FreeImage_GetFIFFromFilename (theFileName.ToCString());
360   if (anImageFormat == FIF_UNKNOWN)
361   {
362     std::cerr << "Image_PixMap, image format doesn't supported!\n";
363     return false;
364   }
365
366   if (IsTopDown())
367   {
368     FreeImage_FlipVertical (myLibImage);
369     SetTopDown (false);
370   }
371
372   // FreeImage doesn't provide flexible format convertion API
373   // so we should perform multiple convertions in some cases!
374   FIBITMAP* anImageToDump = myLibImage;
375   switch (anImageFormat)
376   {
377     case FIF_PNG:
378     case FIF_BMP:
379     {
380       if (Format() == Image_PixMap::ImgBGR32
381        || Format() == Image_PixMap::ImgRGB32)
382       {
383         // stupid FreeImage treats reserved byte as alpha if some bytes not set to 0xFF
384         Image_PixMapData<Image_ColorRGB32>& aData = Image_PixMap::EditData<Image_ColorRGB32>();
385         for (Standard_Size aRow = 0; aRow < SizeY(); ++aRow)
386         {
387           for (Standard_Size aCol = 0; aCol < SizeX(); ++aCol)
388           {
389             aData.ChangeValue (aRow, aCol).a_() = 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