0023272: Image comparison algorithm
[occt.git] / src / Image / Image_AlienPixMap.cxx
CommitLineData
692613e5 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
38namespace
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
106IMPLEMENT_STANDARD_HANDLE (Image_AlienPixMap, Image_PixMap)
107IMPLEMENT_STANDARD_RTTIEXT(Image_AlienPixMap, Image_PixMap)
108
109// =======================================================================
110// function : Image_AlienPixMap
111// purpose :
112// =======================================================================
113Image_AlienPixMap::Image_AlienPixMap()
114: myLibImage (NULL)
115{
116 SetTopDown (false);
117}
118
119// =======================================================================
120// function : ~Image_AlienPixMap
121// purpose :
122// =======================================================================
123Image_AlienPixMap::~Image_AlienPixMap()
124{
125 Clear();
126}
127
128// =======================================================================
129// function : InitWrapper
130// purpose :
131// =======================================================================
132bool 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// =======================================================================
146bool 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// =======================================================================
188bool 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// =======================================================================
229void 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// =======================================================================
245bool 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// =======================================================================
305bool 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// =======================================================================
348bool 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// =======================================================================
509Standard_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}