0023272: Image comparison algorithm
[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            thePixelFormat,
133                                      Standard_Byte*       theDataPtr,
134                                      const Standard_Size  theSizeX,
135                                      const Standard_Size  theSizeY,
136                                      const Standard_Size  theSizeRowBytes)
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 : Clear
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 bool Image_AlienPixMap::Load (const TCollection_AsciiString& theImagePath)
246 {
247   Clear();
248 #ifdef HAVE_FREEIMAGE
249
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 #else
297   return false;
298 #endif
299 }
300
301 // =======================================================================
302 // function : savePPM
303 // purpose  :
304 // =======================================================================
305 bool Image_AlienPixMap::savePPM (const TCollection_AsciiString& theFileName) const
306 {
307   if (IsEmpty())
308   {
309     return false;
310   }
311
312   // Open file
313   FILE* aFile = fopen (theFileName.ToCString(), "wb");
314   if (aFile == NULL)
315   {
316     return false;
317   }
318
319   // Write header
320   fprintf (aFile, "P6\n%d %d\n255\n", (int )SizeX(), (int )SizeY());
321   fprintf (aFile, "# Image stored by OpenCASCADE framework in linear RGB colorspace\n");
322
323   // Write pixel data
324   Quantity_Color aColor;
325   Quantity_Parameter aDummy;
326   Standard_Byte aByte;
327   for (Standard_Size aRow = 0; aRow < SizeY(); ++aRow)
328   {
329     for (Standard_Size aCol = 0; aCol < SizeY(); ++aCol)
330     {
331       // extremely SLOW but universal (implemented for all supported pixel formats)
332       aColor = PixelColor (aCol, aRow, aDummy);
333       aByte = Standard_Byte(aColor.Red() * 255.0);   fwrite (&aByte, 1, 1, aFile);
334       aByte = Standard_Byte(aColor.Green() * 255.0); fwrite (&aByte, 1, 1, aFile);
335       aByte = Standard_Byte(aColor.Blue() * 255.0);  fwrite (&aByte, 1, 1, aFile);
336     }
337   }
338
339   // Close file
340   fclose (aFile);
341   return true;
342 }
343
344 // =======================================================================
345 // function : Save
346 // purpose  :
347 // =======================================================================
348 bool Image_AlienPixMap::Save (const TCollection_AsciiString& theFileName)
349 {
350 #ifdef HAVE_FREEIMAGE
351   if (myLibImage == NULL)
352   {
353     return false;
354   }
355
356   FREE_IMAGE_FORMAT anImageFormat = FreeImage_GetFIFFromFilename (theFileName.ToCString());
357   if (anImageFormat == FIF_UNKNOWN)
358   {
359     std::cerr << "Image_PixMap, image format doesn't supported!\n";
360     return false;
361   }
362
363   if (IsTopDown())
364   {
365     FreeImage_FlipVertical (myLibImage);
366     SetTopDown (false);
367   }
368
369   // FreeImage doesn't provide flexible format convertion API
370   // so we should perform multiple convertions in some cases!
371   Standard_Boolean isCopied = Standard_False;
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         Image_PixMapData<Image_ColorRGB32>& aData = Image_PixMap::EditData<Image_ColorRGB32>();
383         for (Standard_Size aRow = 0; aRow < SizeY(); ++aRow)
384         {
385           for (Standard_Size aCol = 0; aCol < SizeX(); ++aCol)
386           {
387             aData.ChangeValue (aRow, aCol).a_() = 0xFF;
388           }
389         }
390       }
391       else if (FreeImage_GetImageType (myLibImage) != FIT_BITMAP)
392       {
393         anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_BITMAP);
394       }
395       break;
396     }
397     case FIF_GIF:
398     {
399       FIBITMAP* aTmpBitmap = myLibImage;
400       if (FreeImage_GetImageType (myLibImage) != FIT_BITMAP)
401       {
402         aTmpBitmap = FreeImage_ConvertToType (myLibImage, FIT_BITMAP);
403         if (aTmpBitmap == NULL)
404         {
405           return false;
406         }
407       }
408
409       if (FreeImage_GetBPP (aTmpBitmap) != 24)
410       {
411         FIBITMAP* aTmpBitmap24 = FreeImage_ConvertTo24Bits (aTmpBitmap);
412         if (aTmpBitmap != myLibImage)
413         {
414           FreeImage_Unload (aTmpBitmap);
415         }
416         if (aTmpBitmap24 == NULL)
417         {
418           return false;
419         }
420         aTmpBitmap = aTmpBitmap24;
421       }
422
423       // need convertion to image with pallete (requires 24bit bitmap)
424       anImageToDump = FreeImage_ColorQuantize (aTmpBitmap, FIQ_NNQUANT);
425       if (aTmpBitmap != myLibImage)
426       {
427         FreeImage_Unload (aTmpBitmap);
428       }
429       break;
430     }
431     case FIF_EXR:
432     {
433       if (Format() == Image_PixMap::ImgGray)
434       {
435         anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_FLOAT);
436       }
437       else if (Format() == Image_PixMap::ImgRGBA
438             || Format() == Image_PixMap::ImgBGRA)
439       {
440         anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_RGBAF);
441       }
442       else
443       {
444         FREE_IMAGE_TYPE aImgTypeFI = FreeImage_GetImageType (myLibImage);
445         if (aImgTypeFI != FIT_RGBF
446          && aImgTypeFI != FIT_RGBAF
447          && aImgTypeFI != FIT_FLOAT)
448         {
449           anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_RGBF);
450         }
451       }
452       break;
453     }
454     default:
455     {
456       if (FreeImage_GetImageType (myLibImage) != FIT_BITMAP)
457       {
458         anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_BITMAP);
459         if (anImageToDump == NULL)
460         {
461           return false;
462         }
463       }
464
465       if (FreeImage_GetBPP (anImageToDump) != 24)
466       {
467         FIBITMAP* aTmpBitmap24 = FreeImage_ConvertTo24Bits (anImageToDump);
468         if (anImageToDump != myLibImage)
469         {
470           FreeImage_Unload (anImageToDump);
471         }
472         if (aTmpBitmap24 == NULL)
473         {
474           return false;
475         }
476         anImageToDump = aTmpBitmap24;
477       }
478       break;
479     }
480   }
481
482   if (anImageToDump == NULL)
483   {
484     return false;
485   }
486
487   bool isSaved = (FreeImage_Save (anImageFormat, anImageToDump, theFileName.ToCString()) != FALSE);
488   if (anImageToDump != myLibImage)
489   {
490     FreeImage_Unload (anImageToDump);
491   }
492   return isSaved;
493 #else
494   const Standard_Integer aLen = theFileName.Length();
495   if ((aLen >= 4) && (theFileName.Value (aLen - 3) == '.')
496    && TCollection_AsciiString::ISSIMILAR (theFileName.SubString (aLen - 2, aLen), "ppm"))
497   {
498     return savePPM (theFileName);
499   }
500   std::cerr << "Image_PixMap, no image library available! Image saved in PPM format.\n";
501   return savePPM (theFileName);
502 #endif
503 }
504
505 // =======================================================================
506 // function : AdjustGamma
507 // purpose  :
508 // =======================================================================
509 Standard_EXPORT bool Image_AlienPixMap::AdjustGamma (const Standard_Real theGammaCorr)
510 {
511 #ifdef HAVE_FREEIMAGE
512   return FreeImage_AdjustGamma (myLibImage, theGammaCorr) != FALSE;
513 #else
514   return false;
515 #endif
516 }