0028876: Tests, Image_Diff - the image difference is unavailable for test case bugs...
[occt.git] / src / Image / Image_AlienPixMap.cxx
CommitLineData
692613e5 1// Created on: 2010-09-16
2// Created by: KGV
973c2be1 3// Copyright (c) 2010-2014 OPEN CASCADE SAS
692613e5 4//
973c2be1 5// This file is part of Open CASCADE Technology software library.
692613e5 6//
d5f74e42 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
973c2be1 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.
692613e5 12//
973c2be1 13// Alternatively, this file may be used under the terms of Open CASCADE
14// commercial license or contractual agreement.
692613e5 15
692613e5 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>
0c015ee2 26#include <Message.hxx>
27#include <Message_Messenger.hxx>
692613e5 28#include <TCollection_AsciiString.hxx>
7aa1b65c 29#include <TCollection_ExtendedString.hxx>
94708556 30#include <OSD_OpenFile.hxx>
692613e5 31#include <fstream>
a096a7a5 32#include <algorithm>
692613e5 33
f5f4ebd0 34IMPLEMENT_STANDARD_RTTIEXT(Image_AlienPixMap,Image_PixMap)
35
692613e5 36#ifdef HAVE_FREEIMAGE
37namespace
38{
dc858f4c 39 static Image_Format convertFromFreeFormat (FREE_IMAGE_TYPE theFormatFI,
40 FREE_IMAGE_COLOR_TYPE theColorTypeFI,
41 unsigned theBitsPerPixel)
692613e5 42 {
43 switch (theFormatFI)
44 {
dc858f4c 45 case FIT_RGBF: return Image_Format_RGBF;
46 case FIT_RGBAF: return Image_Format_RGBAF;
47 case FIT_FLOAT: return Image_Format_GrayF;
692613e5 48 case FIT_BITMAP:
49 {
50 switch (theColorTypeFI)
51 {
52 case FIC_MINISBLACK:
53 {
dc858f4c 54 return Image_Format_Gray;
692613e5 55 }
56 case FIC_RGB:
57 {
58 if (Image_PixMap::IsBigEndianHost())
59 {
dc858f4c 60 return (theBitsPerPixel == 32) ? Image_Format_RGB32 : Image_Format_RGB;
692613e5 61 }
62 else
63 {
dc858f4c 64 return (theBitsPerPixel == 32) ? Image_Format_BGR32 : Image_Format_BGR;
692613e5 65 }
66 }
67 case FIC_RGBALPHA:
68 {
dc858f4c 69 return Image_PixMap::IsBigEndianHost() ? Image_Format_RGBA : Image_Format_BGRA;
692613e5 70 }
71 default:
dc858f4c 72 return Image_Format_UNKNOWN;
692613e5 73 }
74 }
75 default:
dc858f4c 76 return Image_Format_UNKNOWN;
692613e5 77 }
78 }
79
dc858f4c 80 static FREE_IMAGE_TYPE convertToFreeFormat (Image_Format theFormat)
692613e5 81 {
82 switch (theFormat)
83 {
dc858f4c 84 case Image_Format_GrayF:
85 case Image_Format_AlphaF:
692613e5 86 return FIT_FLOAT;
dc858f4c 87 case Image_Format_RGBAF:
692613e5 88 return FIT_RGBAF;
dc858f4c 89 case Image_Format_RGBF:
692613e5 90 return FIT_RGBF;
dc858f4c 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:
692613e5 99 return FIT_BITMAP;
100 default:
101 return FIT_UNKNOWN;
102 }
103 }
68858c7d 104}
692613e5 105#endif
106
692613e5 107
108// =======================================================================
109// function : Image_AlienPixMap
110// purpose :
111// =======================================================================
112Image_AlienPixMap::Image_AlienPixMap()
113: myLibImage (NULL)
114{
115 SetTopDown (false);
116}
117
118// =======================================================================
119// function : ~Image_AlienPixMap
120// purpose :
121// =======================================================================
122Image_AlienPixMap::~Image_AlienPixMap()
123{
124 Clear();
125}
126
127// =======================================================================
128// function : InitWrapper
129// purpose :
130// =======================================================================
dc858f4c 131bool Image_AlienPixMap::InitWrapper (Image_Format,
35e08fe8 132 Standard_Byte*,
133 const Standard_Size,
134 const Standard_Size,
135 const Standard_Size)
692613e5 136{
137 Clear();
138 return false;
139}
140
141// =======================================================================
142// function : InitTrash
143// purpose :
144// =======================================================================
498ce76b 145#ifdef HAVE_FREEIMAGE
dc858f4c 146bool Image_AlienPixMap::InitTrash (Image_Format thePixelFormat,
692613e5 147 const Standard_Size theSizeX,
148 const Standard_Size theSizeY,
498ce76b 149 const Standard_Size /*theSizeRowBytes*/)
692613e5 150{
151 Clear();
692613e5 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);
dc858f4c 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)
692613e5 166 {
167 //FreeImage_SetTransparent (anImage, FALSE);
dc858f4c 168 aFormat = (aFormat == Image_Format_BGRA) ? Image_Format_BGR32 : Image_Format_RGB32;
692613e5 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;
498ce76b 178}
692613e5 179#else
dc858f4c 180bool Image_AlienPixMap::InitTrash (Image_Format thePixelFormat,
498ce76b 181 const Standard_Size theSizeX,
182 const Standard_Size theSizeY,
183 const Standard_Size theSizeRowBytes)
184{
692613e5 185 return Image_PixMap::InitTrash (thePixelFormat, theSizeX, theSizeY, theSizeRowBytes);
692613e5 186}
498ce76b 187#endif
692613e5 188
189// =======================================================================
3c3131a0 190// function : InitCopy
692613e5 191// purpose :
192// =======================================================================
193bool 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 {
ca0c0b11 207 if (SizeRowBytes() == theCopy.SizeRowBytes()
208 && TopDownInc() == theCopy.TopDownInc())
692613e5 209 {
210 // copy with one call
ca0c0b11 211 memcpy (ChangeData(), theCopy.Data(), std::min (SizeBytes(), theCopy.SizeBytes()));
692613e5 212 return true;
213 }
214
215 // copy row-by-row
ca0c0b11 216 const Standard_Size aRowSizeBytes = std::min (SizeRowBytes(), theCopy.SizeRowBytes());
217 for (Standard_Size aRow = 0; aRow < myData.SizeY; ++aRow)
692613e5 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// =======================================================================
ca0c0b11 233void Image_AlienPixMap::Clear()
692613e5 234{
ca0c0b11 235 Image_PixMap::Clear();
692613e5 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// =======================================================================
35e08fe8 249#ifdef HAVE_FREEIMAGE
692613e5 250bool Image_AlienPixMap::Load (const TCollection_AsciiString& theImagePath)
251{
252 Clear();
7aa1b65c 253
254#ifdef _WIN32
255 const TCollection_ExtendedString aFileNameW (theImagePath.ToCString(), Standard_True);
fb0b0531 256 FREE_IMAGE_FORMAT aFIF = FreeImage_GetFileTypeU (aFileNameW.ToWideString(), 0);
7aa1b65c 257#else
692613e5 258 FREE_IMAGE_FORMAT aFIF = FreeImage_GetFileType (theImagePath.ToCString(), 0);
7aa1b65c 259#endif
692613e5 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 {
0c015ee2 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);
692613e5 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
7aa1b65c 286#ifdef _WIN32
fb0b0531 287 FIBITMAP* anImage = FreeImage_LoadU (aFIF, aFileNameW.ToWideString(), aLoadFlags);
7aa1b65c 288#else
289 FIBITMAP* anImage = FreeImage_Load (aFIF, theImagePath.ToCString(), aLoadFlags);
290#endif
692613e5 291 if (anImage == NULL)
292 {
0c015ee2 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);
692613e5 297 return false;
298 }
299
dc858f4c 300 Image_Format aFormat = convertFromFreeFormat (FreeImage_GetImageType(anImage),
301 FreeImage_GetColorType(anImage),
302 FreeImage_GetBPP (anImage));
303 if (aFormat == Image_Format_UNKNOWN)
692613e5 304 {
305 //anImage = FreeImage_ConvertTo24Bits (anImage);
0c015ee2 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);
692613e5 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;
35e08fe8 320}
692613e5 321#else
35e08fe8 322bool Image_AlienPixMap::Load (const TCollection_AsciiString&)
323{
324 Clear();
692613e5 325 return false;
692613e5 326}
35e08fe8 327#endif
692613e5 328
329// =======================================================================
330// function : savePPM
331// purpose :
332// =======================================================================
333bool Image_AlienPixMap::savePPM (const TCollection_AsciiString& theFileName) const
334{
335 if (IsEmpty())
336 {
337 return false;
338 }
339
340 // Open file
94708556 341 FILE* aFile = OSD_OpenFile (theFileName.ToCString(), "wb");
692613e5 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
692613e5 352 Standard_Byte aByte;
353 for (Standard_Size aRow = 0; aRow < SizeY(); ++aRow)
354 {
a7b491fc 355 for (Standard_Size aCol = 0; aCol < SizeX(); ++aCol)
692613e5 356 {
357 // extremely SLOW but universal (implemented for all supported pixel formats)
e958a649 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);
692613e5 362 }
363 }
364
365 // Close file
366 fclose (aFile);
367 return true;
368}
369
370// =======================================================================
371// function : Save
372// purpose :
373// =======================================================================
374bool Image_AlienPixMap::Save (const TCollection_AsciiString& theFileName)
375{
376#ifdef HAVE_FREEIMAGE
377 if (myLibImage == NULL)
378 {
379 return false;
380 }
381
7aa1b65c 382#ifdef _WIN32
383 const TCollection_ExtendedString aFileNameW (theFileName.ToCString(), Standard_True);
fb0b0531 384 FREE_IMAGE_FORMAT anImageFormat = FreeImage_GetFIFFromFilenameU (aFileNameW.ToWideString());
7aa1b65c 385#else
692613e5 386 FREE_IMAGE_FORMAT anImageFormat = FreeImage_GetFIFFromFilename (theFileName.ToCString());
7aa1b65c 387#endif
692613e5 388 if (anImageFormat == FIF_UNKNOWN)
389 {
0797d9d3 390#ifdef OCCT_DEBUG
692613e5 391 std::cerr << "Image_PixMap, image format doesn't supported!\n";
63c629aa 392#endif
692613e5 393 return false;
394 }
395
396 if (IsTopDown())
397 {
398 FreeImage_FlipVertical (myLibImage);
399 SetTopDown (false);
400 }
401
dc858f4c 402 // FreeImage doesn't provide flexible format conversion API
403 // so we should perform multiple conversions in some cases!
692613e5 404 FIBITMAP* anImageToDump = myLibImage;
405 switch (anImageFormat)
406 {
407 case FIF_PNG:
408 case FIF_BMP:
409 {
dc858f4c 410 if (Format() == Image_Format_BGR32
411 || Format() == Image_Format_RGB32)
692613e5 412 {
413 // stupid FreeImage treats reserved byte as alpha if some bytes not set to 0xFF
692613e5 414 for (Standard_Size aRow = 0; aRow < SizeY(); ++aRow)
415 {
416 for (Standard_Size aCol = 0; aCol < SizeX(); ++aCol)
417 {
ca0c0b11 418 myData.ChangeValue (aRow, aCol)[3] = 0xFF;
692613e5 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
dc858f4c 454 // need conversion to image with palette (requires 24bit bitmap)
692613e5 455 anImageToDump = FreeImage_ColorQuantize (aTmpBitmap, FIQ_NNQUANT);
456 if (aTmpBitmap != myLibImage)
457 {
458 FreeImage_Unload (aTmpBitmap);
459 }
460 break;
461 }
38d90bb3 462 case FIF_HDR:
692613e5 463 case FIF_EXR:
464 {
dc858f4c 465 if (Format() == Image_Format_Gray
466 || Format() == Image_Format_Alpha)
692613e5 467 {
468 anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_FLOAT);
469 }
dc858f4c 470 else if (Format() == Image_Format_RGBA
471 || Format() == Image_Format_BGRA)
692613e5 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
7aa1b65c 520#ifdef _WIN32
fb0b0531 521 bool isSaved = (FreeImage_SaveU (anImageFormat, anImageToDump, aFileNameW.ToWideString()) != FALSE);
7aa1b65c 522#else
523 bool isSaved = (FreeImage_Save (anImageFormat, anImageToDump, theFileName.ToCString()) != FALSE);
524#endif
692613e5 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) == '.')
29cb310a 533 && strcasecmp( theFileName.ToCString() + aLen - 3, "ppm") == 0 )
692613e5 534 {
535 return savePPM (theFileName);
536 }
0797d9d3 537#ifdef OCCT_DEBUG
692613e5 538 std::cerr << "Image_PixMap, no image library available! Image saved in PPM format.\n";
63c629aa 539#endif
692613e5 540 return savePPM (theFileName);
541#endif
542}
543
544// =======================================================================
545// function : AdjustGamma
546// purpose :
547// =======================================================================
7aa1b65c 548bool Image_AlienPixMap::AdjustGamma (const Standard_Real theGammaCorr)
692613e5 549{
7aa1b65c 550#ifdef HAVE_FREEIMAGE
692613e5 551 return FreeImage_AdjustGamma (myLibImage, theGammaCorr) != FALSE;
552#else
7aa1b65c 553 (void )theGammaCorr;
554 return false;
35e08fe8 555#endif
7aa1b65c 556}