0028876: Tests, Image_Diff - the image difference is unavailable for test case bugs...
[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   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       const Quantity_ColorRGBA aColor = PixelColor ((Standard_Integer )aCol, (Standard_Integer )aRow);
359       aByte = Standard_Byte(aColor.GetRGB().Red()   * 255.0); fwrite (&aByte, 1, 1, aFile);
360       aByte = Standard_Byte(aColor.GetRGB().Green() * 255.0); fwrite (&aByte, 1, 1, aFile);
361       aByte = Standard_Byte(aColor.GetRGB().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 (aFileNameW.ToWideString());
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 conversion API
403   // so we should perform multiple conversions in some cases!
404   FIBITMAP* anImageToDump = myLibImage;
405   switch (anImageFormat)
406   {
407     case FIF_PNG:
408     case FIF_BMP:
409     {
410       if (Format() == Image_Format_BGR32
411        || Format() == Image_Format_RGB32)
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 conversion to image with palette (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_HDR:
463     case FIF_EXR:
464     {
465       if (Format() == Image_Format_Gray
466        || Format() == Image_Format_Alpha)
467       {
468         anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_FLOAT);
469       }
470       else if (Format() == Image_Format_RGBA
471             || Format() == Image_Format_BGRA)
472       {
473         anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_RGBAF);
474       }
475       else
476       {
477         FREE_IMAGE_TYPE aImgTypeFI = FreeImage_GetImageType (myLibImage);
478         if (aImgTypeFI != FIT_RGBF
479          && aImgTypeFI != FIT_RGBAF
480          && aImgTypeFI != FIT_FLOAT)
481         {
482           anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_RGBF);
483         }
484       }
485       break;
486     }
487     default:
488     {
489       if (FreeImage_GetImageType (myLibImage) != FIT_BITMAP)
490       {
491         anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_BITMAP);
492         if (anImageToDump == NULL)
493         {
494           return false;
495         }
496       }
497
498       if (FreeImage_GetBPP (anImageToDump) != 24)
499       {
500         FIBITMAP* aTmpBitmap24 = FreeImage_ConvertTo24Bits (anImageToDump);
501         if (anImageToDump != myLibImage)
502         {
503           FreeImage_Unload (anImageToDump);
504         }
505         if (aTmpBitmap24 == NULL)
506         {
507           return false;
508         }
509         anImageToDump = aTmpBitmap24;
510       }
511       break;
512     }
513   }
514
515   if (anImageToDump == NULL)
516   {
517     return false;
518   }
519
520 #ifdef _WIN32
521   bool isSaved = (FreeImage_SaveU (anImageFormat, anImageToDump, aFileNameW.ToWideString()) != FALSE);
522 #else
523   bool isSaved = (FreeImage_Save  (anImageFormat, anImageToDump, theFileName.ToCString()) != FALSE);
524 #endif
525   if (anImageToDump != myLibImage)
526   {
527     FreeImage_Unload (anImageToDump);
528   }
529   return isSaved;
530 #else
531   const Standard_Integer aLen = theFileName.Length();
532   if ((aLen >= 4) && (theFileName.Value (aLen - 3) == '.')
533       && strcasecmp( theFileName.ToCString() + aLen - 3, "ppm") == 0 )
534   {
535     return savePPM (theFileName);
536   }
537 #ifdef OCCT_DEBUG
538   std::cerr << "Image_PixMap, no image library available! Image saved in PPM format.\n";
539 #endif
540   return savePPM (theFileName);
541 #endif
542 }
543
544 // =======================================================================
545 // function : AdjustGamma
546 // purpose  :
547 // =======================================================================
548 bool Image_AlienPixMap::AdjustGamma (const Standard_Real theGammaCorr)
549 {
550 #ifdef HAVE_FREEIMAGE
551   return FreeImage_AdjustGamma (myLibImage, theGammaCorr) != FALSE;
552 #else
553   (void )theGammaCorr;
554   return false;
555 #endif
556 }