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