1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-08-09 13:22:24 +03:00

0023272: Image comparison algorithm

A new class Image_Diff for comparison of images
and a draw-command "diffimage", which compares 2 images.

Image_PixMap redesigned to provide interface for low-level image operations.
New Image_AlienPixMap class now intended for Save/Load functionality.

Aspect_PixMap class dropped.
Xw_PixMap and WNT_PixMap classes now do not inherit from Aspect_PixMap and deprecated.

ToPixMap methods now retrieve Image_PixMap as argument.

Conflicts:
src/ViewerTest/ViewerTest.cxx
Remarks applied
Fix compilation (correct merging error)

Eliminated Aspect <-> Image cyclic dependency

Fixed GIF dump in case of BGR32 image format
This commit is contained in:
kgv
2012-09-10 14:30:46 +04:00
parent 567148d8f4
commit 692613e554
43 changed files with 3095 additions and 1289 deletions

View File

@@ -6,5 +6,11 @@ Image_PixelAddress.cxx
Image_PixelAddress.hxx
Image.edl
Image_CMPLRS.edl
Image_CRawBufferData.hxx
Image_HPrivateImage.hxx
Image_PixMap.hxx
Image_PixMap.cxx
Image_PixMapData.hxx
Image_Color.hxx
Image_AlienPixMap.hxx
Image_AlienPixMap.cxx
Image_Diff.hxx
Image_Diff.cxx

View File

@@ -88,18 +88,16 @@ is
IndexPixel from Aspect,
IndexPixelMapHasher );
class PixMap;
---Purpose: Aspect_PixMap implementation.
-----------------------------
---Category: Imported types:
-----------------------------
imported PixelAddress;
imported HPrivateImage;
imported CRawBufferData;
imported PixMap;
imported AlienPixMap;
imported PixMap_Handle;
imported AlienPixMap_Handle;
imported Diff;
-----------------------------
---Category: The Enumerations
@@ -132,12 +130,7 @@ is
---Purpose: Type of dithering method.
enumeration TypeOfImage is TOI_ColorImage,
TOI_PseudoColorImage,
TOI_RGB,
TOI_RGBA,
TOI_RGBF,
TOI_RGBAF,
TOI_FLOAT
TOI_PseudoColorImage
end TypeOfImage ;
Zoom ( aImage : mutable Image from Image ;

View File

@@ -0,0 +1,516 @@
// Created on: 2010-09-16
// Created by: KGV
// Copyright (c) 2010-2012 OPEN CASCADE SAS
//
// The content of this file is subject to the Open CASCADE Technology Public
// License Version 6.5 (the "License"). You may not use the content of this file
// except in compliance with the License. Please obtain a copy of the License
// at http://www.opencascade.org and read it completely before using this file.
//
// The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
// main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
//
// The Original Code and all software distributed under the License is
// distributed on an "AS IS" basis, without warranty of any kind, and the
// Initial Developer hereby disclaims all such warranties, including without
// limitation, any warranties of merchantability, fitness for a particular
// purpose or non-infringement. Please see the License for the specific terms
// and conditions governing the rights and limitations under the License.
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef HAVE_FREEIMAGE
#include <FreeImage.h>
#ifdef _MSC_VER
#pragma comment( lib, "FreeImage.lib" )
#endif
#endif
#include <Image_AlienPixMap.hxx>
#include <gp.hxx>
#include <TCollection_AsciiString.hxx>
#include <fstream>
#ifdef HAVE_FREEIMAGE
namespace
{
static Image_PixMap::ImgFormat convertFromFreeFormat (FREE_IMAGE_TYPE theFormatFI,
FREE_IMAGE_COLOR_TYPE theColorTypeFI,
unsigned theBitsPerPixel)
{
switch (theFormatFI)
{
case FIT_RGBF: return Image_PixMap::ImgRGBF;
case FIT_RGBAF: return Image_PixMap::ImgRGBAF;
case FIT_FLOAT: return Image_PixMap::ImgGrayF;
case FIT_BITMAP:
{
switch (theColorTypeFI)
{
case FIC_MINISBLACK:
{
return Image_PixMap::ImgGray;
}
case FIC_RGB:
{
if (Image_PixMap::IsBigEndianHost())
{
return (theBitsPerPixel == 32) ? Image_PixMap::ImgRGB32 : Image_PixMap::ImgRGB;
}
else
{
return (theBitsPerPixel == 32) ? Image_PixMap::ImgBGR32 : Image_PixMap::ImgBGR;
}
}
case FIC_RGBALPHA:
{
return Image_PixMap::IsBigEndianHost() ? Image_PixMap::ImgRGBA : Image_PixMap::ImgBGRA;
}
default:
return Image_PixMap::ImgUNKNOWN;
}
}
default:
return Image_PixMap::ImgUNKNOWN;
}
}
static FREE_IMAGE_TYPE convertToFreeFormat (Image_PixMap::ImgFormat theFormat)
{
switch (theFormat)
{
case Image_PixMap::ImgGrayF:
return FIT_FLOAT;
case Image_PixMap::ImgRGBAF:
return FIT_RGBAF;
case Image_PixMap::ImgRGBF:
return FIT_RGBF;
case Image_PixMap::ImgRGBA:
case Image_PixMap::ImgBGRA:
case Image_PixMap::ImgRGB32:
case Image_PixMap::ImgBGR32:
case Image_PixMap::ImgRGB:
case Image_PixMap::ImgBGR:
case Image_PixMap::ImgGray:
return FIT_BITMAP;
default:
return FIT_UNKNOWN;
}
}
};
#endif
IMPLEMENT_STANDARD_HANDLE (Image_AlienPixMap, Image_PixMap)
IMPLEMENT_STANDARD_RTTIEXT(Image_AlienPixMap, Image_PixMap)
// =======================================================================
// function : Image_AlienPixMap
// purpose :
// =======================================================================
Image_AlienPixMap::Image_AlienPixMap()
: myLibImage (NULL)
{
SetTopDown (false);
}
// =======================================================================
// function : ~Image_AlienPixMap
// purpose :
// =======================================================================
Image_AlienPixMap::~Image_AlienPixMap()
{
Clear();
}
// =======================================================================
// function : InitWrapper
// purpose :
// =======================================================================
bool Image_AlienPixMap::InitWrapper (ImgFormat thePixelFormat,
Standard_Byte* theDataPtr,
const Standard_Size theSizeX,
const Standard_Size theSizeY,
const Standard_Size theSizeRowBytes)
{
Clear();
return false;
}
// =======================================================================
// function : InitTrash
// purpose :
// =======================================================================
bool Image_AlienPixMap::InitTrash (ImgFormat thePixelFormat,
const Standard_Size theSizeX,
const Standard_Size theSizeY,
const Standard_Size theSizeRowBytes)
{
Clear();
#ifdef HAVE_FREEIMAGE
FREE_IMAGE_TYPE aFormatFI = convertToFreeFormat (thePixelFormat);
int aBitsPerPixel = (int )Image_PixMap::SizePixelBytes (thePixelFormat) * 8;
if (aFormatFI == FIT_UNKNOWN)
{
aFormatFI = FIT_BITMAP;
aBitsPerPixel = 24;
}
FIBITMAP* anImage = FreeImage_AllocateT (aFormatFI, (int )theSizeX, (int )theSizeY, aBitsPerPixel);
Image_PixMap::ImgFormat aFormat = convertFromFreeFormat (FreeImage_GetImageType (anImage),
FreeImage_GetColorType (anImage),
FreeImage_GetBPP (anImage));
if (thePixelFormat == Image_PixMap::ImgBGR32
|| thePixelFormat == Image_PixMap::ImgRGB32)
{
//FreeImage_SetTransparent (anImage, FALSE);
aFormat = (aFormat == Image_PixMap::ImgBGRA) ? Image_PixMap::ImgBGR32 : Image_PixMap::ImgRGB32;
}
Image_PixMap::InitWrapper (aFormat, FreeImage_GetBits (anImage),
FreeImage_GetWidth (anImage), FreeImage_GetHeight (anImage), FreeImage_GetPitch (anImage));
SetTopDown (false);
// assign image after wrapper initialization (virtual Clear() called inside)
myLibImage = anImage;
return true;
#else
return Image_PixMap::InitTrash (thePixelFormat, theSizeX, theSizeY, theSizeRowBytes);
#endif
}
// =======================================================================
// function : Clear
// purpose :
// =======================================================================
bool Image_AlienPixMap::InitCopy (const Image_PixMap& theCopy)
{
if (&theCopy == this)
{
// self-copying disallowed
return false;
}
if (!InitTrash (theCopy.Format(), theCopy.SizeX(), theCopy.SizeY(), theCopy.SizeRowBytes()))
{
return false;
}
if (myImgFormat == theCopy.Format())
{
if (myData.mySizeRowBytes == theCopy.SizeRowBytes()
&& myData.myTopToDown == theCopy.TopDownInc())
{
// copy with one call
memcpy (myData.myDataPtr, theCopy.Data(), theCopy.SizeBytes());
return true;
}
// copy row-by-row
const Standard_Size aRowSizeBytes = (myData.mySizeRowBytes > theCopy.SizeRowBytes())
? theCopy.SizeRowBytes() : myData.mySizeRowBytes;
for (Standard_Size aRow = 0; aRow < myData.mySizeY; ++aRow)
{
memcpy (ChangeRow (aRow), theCopy.Row (aRow), aRowSizeBytes);
}
return true;
}
// pixel format conversion required
Clear();
return false;
}
// =======================================================================
// function : Clear
// purpose :
// =======================================================================
void Image_AlienPixMap::Clear (ImgFormat thePixelFormat)
{
Image_PixMap::Clear (thePixelFormat);
#ifdef HAVE_FREEIMAGE
if (myLibImage != NULL)
{
FreeImage_Unload (myLibImage);
myLibImage = NULL;
}
#endif
}
// =======================================================================
// function : Load
// purpose :
// =======================================================================
bool Image_AlienPixMap::Load (const TCollection_AsciiString& theImagePath)
{
Clear();
#ifdef HAVE_FREEIMAGE
FREE_IMAGE_FORMAT aFIF = FreeImage_GetFileType (theImagePath.ToCString(), 0);
if (aFIF == FIF_UNKNOWN)
{
// no signature? try to guess the file format from the file extension
aFIF = FreeImage_GetFIFFromFilename (theImagePath.ToCString());
}
if ((aFIF == FIF_UNKNOWN) || !FreeImage_FIFSupportsReading (aFIF))
{
// unsupported image format
return false;
}
int aLoadFlags = 0;
if (aFIF == FIF_GIF)
{
// 'Play' the GIF to generate each frame (as 32bpp) instead of returning raw frame data when loading
aLoadFlags = GIF_PLAYBACK;
}
else if (aFIF == FIF_ICO)
{
// convert to 32bpp and create an alpha channel from the AND-mask when loading
aLoadFlags = ICO_MAKEALPHA;
}
FIBITMAP* anImage = FreeImage_Load (aFIF, theImagePath.ToCString(), aLoadFlags);
if (anImage == NULL)
{
return false;
}
Image_PixMap::ImgFormat aFormat = convertFromFreeFormat (FreeImage_GetImageType (anImage),
FreeImage_GetColorType (anImage),
FreeImage_GetBPP (anImage));
if (aFormat == Image_PixMap::ImgUNKNOWN)
{
//anImage = FreeImage_ConvertTo24Bits (anImage);
return false;
}
Image_PixMap::InitWrapper (aFormat, FreeImage_GetBits (anImage),
FreeImage_GetWidth (anImage), FreeImage_GetHeight (anImage), FreeImage_GetPitch (anImage));
SetTopDown (false);
// assign image after wrapper initialization (virtual Clear() called inside)
myLibImage = anImage;
return true;
#else
return false;
#endif
}
// =======================================================================
// function : savePPM
// purpose :
// =======================================================================
bool Image_AlienPixMap::savePPM (const TCollection_AsciiString& theFileName) const
{
if (IsEmpty())
{
return false;
}
// Open file
FILE* aFile = fopen (theFileName.ToCString(), "wb");
if (aFile == NULL)
{
return false;
}
// Write header
fprintf (aFile, "P6\n%d %d\n255\n", (int )SizeX(), (int )SizeY());
fprintf (aFile, "# Image stored by OpenCASCADE framework in linear RGB colorspace\n");
// Write pixel data
Quantity_Color aColor;
Quantity_Parameter aDummy;
Standard_Byte aByte;
for (Standard_Size aRow = 0; aRow < SizeY(); ++aRow)
{
for (Standard_Size aCol = 0; aCol < SizeY(); ++aCol)
{
// extremely SLOW but universal (implemented for all supported pixel formats)
aColor = PixelColor (aCol, aRow, aDummy);
aByte = Standard_Byte(aColor.Red() * 255.0); fwrite (&aByte, 1, 1, aFile);
aByte = Standard_Byte(aColor.Green() * 255.0); fwrite (&aByte, 1, 1, aFile);
aByte = Standard_Byte(aColor.Blue() * 255.0); fwrite (&aByte, 1, 1, aFile);
}
}
// Close file
fclose (aFile);
return true;
}
// =======================================================================
// function : Save
// purpose :
// =======================================================================
bool Image_AlienPixMap::Save (const TCollection_AsciiString& theFileName)
{
#ifdef HAVE_FREEIMAGE
if (myLibImage == NULL)
{
return false;
}
FREE_IMAGE_FORMAT anImageFormat = FreeImage_GetFIFFromFilename (theFileName.ToCString());
if (anImageFormat == FIF_UNKNOWN)
{
std::cerr << "Image_PixMap, image format doesn't supported!\n";
return false;
}
if (IsTopDown())
{
FreeImage_FlipVertical (myLibImage);
SetTopDown (false);
}
// FreeImage doesn't provide flexible format convertion API
// so we should perform multiple convertions in some cases!
Standard_Boolean isCopied = Standard_False;
FIBITMAP* anImageToDump = myLibImage;
switch (anImageFormat)
{
case FIF_PNG:
case FIF_BMP:
{
if (Format() == Image_PixMap::ImgBGR32
|| Format() == Image_PixMap::ImgRGB32)
{
// stupid FreeImage treats reserved byte as alpha if some bytes not set to 0xFF
Image_PixMapData<Image_ColorRGB32>& aData = Image_PixMap::EditData<Image_ColorRGB32>();
for (Standard_Size aRow = 0; aRow < SizeY(); ++aRow)
{
for (Standard_Size aCol = 0; aCol < SizeX(); ++aCol)
{
aData.ChangeValue (aRow, aCol).a_() = 0xFF;
}
}
}
else if (FreeImage_GetImageType (myLibImage) != FIT_BITMAP)
{
anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_BITMAP);
}
break;
}
case FIF_GIF:
{
FIBITMAP* aTmpBitmap = myLibImage;
if (FreeImage_GetImageType (myLibImage) != FIT_BITMAP)
{
aTmpBitmap = FreeImage_ConvertToType (myLibImage, FIT_BITMAP);
if (aTmpBitmap == NULL)
{
return false;
}
}
if (FreeImage_GetBPP (aTmpBitmap) != 24)
{
FIBITMAP* aTmpBitmap24 = FreeImage_ConvertTo24Bits (aTmpBitmap);
if (aTmpBitmap != myLibImage)
{
FreeImage_Unload (aTmpBitmap);
}
if (aTmpBitmap24 == NULL)
{
return false;
}
aTmpBitmap = aTmpBitmap24;
}
// need convertion to image with pallete (requires 24bit bitmap)
anImageToDump = FreeImage_ColorQuantize (aTmpBitmap, FIQ_NNQUANT);
if (aTmpBitmap != myLibImage)
{
FreeImage_Unload (aTmpBitmap);
}
break;
}
case FIF_EXR:
{
if (Format() == Image_PixMap::ImgGray)
{
anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_FLOAT);
}
else if (Format() == Image_PixMap::ImgRGBA
|| Format() == Image_PixMap::ImgBGRA)
{
anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_RGBAF);
}
else
{
FREE_IMAGE_TYPE aImgTypeFI = FreeImage_GetImageType (myLibImage);
if (aImgTypeFI != FIT_RGBF
&& aImgTypeFI != FIT_RGBAF
&& aImgTypeFI != FIT_FLOAT)
{
anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_RGBF);
}
}
break;
}
default:
{
if (FreeImage_GetImageType (myLibImage) != FIT_BITMAP)
{
anImageToDump = FreeImage_ConvertToType (myLibImage, FIT_BITMAP);
if (anImageToDump == NULL)
{
return false;
}
}
if (FreeImage_GetBPP (anImageToDump) != 24)
{
FIBITMAP* aTmpBitmap24 = FreeImage_ConvertTo24Bits (anImageToDump);
if (anImageToDump != myLibImage)
{
FreeImage_Unload (anImageToDump);
}
if (aTmpBitmap24 == NULL)
{
return false;
}
anImageToDump = aTmpBitmap24;
}
break;
}
}
if (anImageToDump == NULL)
{
return false;
}
bool isSaved = (FreeImage_Save (anImageFormat, anImageToDump, theFileName.ToCString()) != FALSE);
if (anImageToDump != myLibImage)
{
FreeImage_Unload (anImageToDump);
}
return isSaved;
#else
const Standard_Integer aLen = theFileName.Length();
if ((aLen >= 4) && (theFileName.Value (aLen - 3) == '.')
&& TCollection_AsciiString::ISSIMILAR (theFileName.SubString (aLen - 2, aLen), "ppm"))
{
return savePPM (theFileName);
}
std::cerr << "Image_PixMap, no image library available! Image saved in PPM format.\n";
return savePPM (theFileName);
#endif
}
// =======================================================================
// function : AdjustGamma
// purpose :
// =======================================================================
Standard_EXPORT bool Image_AlienPixMap::AdjustGamma (const Standard_Real theGammaCorr)
{
#ifdef HAVE_FREEIMAGE
return FreeImage_AdjustGamma (myLibImage, theGammaCorr) != FALSE;
#else
return false;
#endif
}

View File

@@ -0,0 +1,96 @@
// Created on: 2012-07-18
// Created by: Kirill GAVRILOV
// Copyright (c) 2012 OPEN CASCADE SAS
//
// The content of this file is subject to the Open CASCADE Technology Public
// License Version 6.5 (the "License"). You may not use the content of this file
// except in compliance with the License. Please obtain a copy of the License
// at http://www.opencascade.org and read it completely before using this file.
//
// The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
// main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
//
// The Original Code and all software distributed under the License is
// distributed on an "AS IS" basis, without warranty of any kind, and the
// Initial Developer hereby disclaims all such warranties, including without
// limitation, any warranties of merchantability, fitness for a particular
// purpose or non-infringement. Please see the License for the specific terms
// and conditions governing the rights and limitations under the License.
#ifndef _Image_AlienPixMap_H__
#define _Image_AlienPixMap_H__
#include <Image_PixMap.hxx>
#include <Image_TypeOfImage.hxx>
class TCollection_AsciiString;
struct FIBITMAP;
//! Image class that support file reading/writing operations using auxiliary image library.
//! Notice that supported images format could be limited.
class Image_AlienPixMap : public Image_PixMap
{
public:
//! Empty constructor.
Standard_EXPORT Image_AlienPixMap();
//! Destructor
Standard_EXPORT virtual ~Image_AlienPixMap();
//! Read image data from file.
Standard_EXPORT bool Load (const TCollection_AsciiString& theFileName);
//! Write image data to file using file extension to determine compression format.
Standard_EXPORT bool Save (const TCollection_AsciiString& theFileName);
//! Initialize image plane with required dimensions.
//! thePixelFormat - if specified pixel format doesn't supported by image library
//! than nearest supported will be used instead!
//! theSizeRowBytes - may be ignored by this class and required alignemnt will be used instead!
Standard_EXPORT virtual bool InitTrash (ImgFormat thePixelFormat,
const Standard_Size theSizeX,
const Standard_Size theSizeY,
const Standard_Size theSizeRowBytes = 0);
//! Initialize by copying data.
Standard_EXPORT virtual bool InitCopy (const Image_PixMap& theCopy);
//! Method correctly deallocate internal buffer.
Standard_EXPORT virtual void Clear (ImgFormat thePixelFormat = ImgGray);
//! Performs gamma correction on image.
//! theGamma - gamma value to use; a value of 1.0 leaves the image alone
Standard_EXPORT bool AdjustGamma (const Standard_Real theGammaCorr);
private:
FIBITMAP* myLibImage;
private:
//! Copying allowed only within Handles
Image_AlienPixMap (const Image_AlienPixMap& );
Image_AlienPixMap& operator= (const Image_AlienPixMap& );
//! Wrapper initialization is disallowed for this class (will return false in any case)!
//! Use only copying and allocation initializers.
Standard_EXPORT virtual bool InitWrapper (ImgFormat thePixelFormat,
Standard_Byte* theDataPtr,
const Standard_Size theSizeX,
const Standard_Size theSizeY,
const Standard_Size theSizeRowBytes);
//! Built-in PPM export
Standard_EXPORT bool savePPM (const TCollection_AsciiString& theFileName) const;
public:
DEFINE_STANDARD_RTTI(Image_AlienPixMap) // Type definition
};
DEFINE_STANDARD_HANDLE(Image_AlienPixMap, Image_PixMap)
#endif // _Image_AlienPixMap_H__

View File

@@ -1,30 +0,0 @@
// Copyright (c) 1999-2012 OPEN CASCADE SAS
//
// The content of this file is subject to the Open CASCADE Technology Public
// License Version 6.5 (the "License"). You may not use the content of this file
// except in compliance with the License. Please obtain a copy of the License
// at http://www.opencascade.org and read it completely before using this file.
//
// The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
// main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
//
// The Original Code and all software distributed under the License is
// distributed on an "AS IS" basis, without warranty of any kind, and the
// Initial Developer hereby disclaims all such warranties, including without
// limitation, any warranties of merchantability, fitness for a particular
// purpose or non-infringement. Please see the License for the specific terms
// and conditions governing the rights and limitations under the License.
#ifndef _Image_CRawBufferData_HeaderFile
#define _Image_CRawBufferData_HeaderFile
#include <InterfaceGraphic_RawBufferData.hxx>
typedef TRawBufferData Image_CRawBufferData;
#if defined(__cplusplus) || defined(c_plusplus)
#include <Standard_Type.hxx>
const Handle(Standard_Type)& TYPE(Image_CRawBufferData);
#endif
#endif /*_Image_CRawBufferData_HeaderFile*/

543
src/Image/Image_Color.hxx Normal file
View File

@@ -0,0 +1,543 @@
// Created on: 2012-07-18
// Created by: Kirill GAVRILOV
// Copyright (c) 2012 OPEN CASCADE SAS
//
// The content of this file is subject to the Open CASCADE Technology Public
// License Version 6.5 (the "License"). You may not use the content of this file
// except in compliance with the License. Please obtain a copy of the License
// at http://www.opencascade.org and read it completely before using this file.
//
// The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
// main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
//
// The Original Code and all software distributed under the License is
// distributed on an "AS IS" basis, without warranty of any kind, and the
// Initial Developer hereby disclaims all such warranties, including without
// limitation, any warranties of merchantability, fitness for a particular
// purpose or non-infringement. Please see the License for the specific terms
// and conditions governing the rights and limitations under the License.
#ifndef _Image_Color_H__
#define _Image_Color_H__
#include <Standard.hxx>
//! POD structure for packed RGB color value (3 bytes)
struct Image_ColorRGB
{
//! Returns the number of components.
static Standard_Integer Length()
{
return 3;
}
public: // access methods
//! Alias to 1st component (red intensity).
Standard_Byte r() const { return v[0]; }
//! Alias to 2nd component (green intensity).
Standard_Byte g() const { return v[1]; }
//! Alias to 3rd component (blue intensity).
Standard_Byte b() const { return v[2]; }
//! Alias to 1st component (red intensity).
Standard_Byte& r() { return v[0]; }
//! Alias to 2nd component (green intensity).
Standard_Byte& g() { return v[1]; }
//! Alias to 3rd component (blue intensity).
Standard_Byte& b() { return v[2]; }
public:
Standard_Byte v[3];
};
//! POD structure for packed RGB color value (4 bytes with extra byte for alignment)
struct Image_ColorRGB32
{
//! Returns the number of components.
static Standard_Integer Length()
{
return 3;
}
//! Alias to 1st component (red intensity).
Standard_Byte r() const { return v[0]; }
//! Alias to 2nd component (green intensity).
Standard_Byte g() const { return v[1]; }
//! Alias to 3rd component (blue intensity).
Standard_Byte b() const { return v[2]; }
//! Alias to 4th component (dummy).
Standard_Byte a_() const { return v[3]; }
//! Alias to 1st component (red intensity).
Standard_Byte& r() { return v[0]; }
//! Alias to 2nd component (green intensity).
Standard_Byte& g() { return v[1]; }
//! Alias to 3rd component (blue intensity).
Standard_Byte& b() { return v[2]; }
//! Alias to 4th component (dummy).
Standard_Byte& a_() { return v[3]; }
public:
Standard_Byte v[4];
};
//! POD structure for packed RGBA color value (4 bytes)
struct Image_ColorRGBA
{
//! Returns the number of components.
static Standard_Integer Length()
{
return 4;
}
//! Alias to 1st component (red intensity).
Standard_Byte r() const { return v[0]; }
//! Alias to 2nd component (green intensity).
Standard_Byte g() const { return v[1]; }
//! Alias to 3rd component (blue intensity).
Standard_Byte b() const { return v[2]; }
//! Alias to 4th component (alpha value).
Standard_Byte a() const { return v[3]; }
//! Alias to 1st component (red intensity).
Standard_Byte& r() { return v[0]; }
//! Alias to 2nd component (green intensity).
Standard_Byte& g() { return v[1]; }
//! Alias to 3rd component (blue intensity).
Standard_Byte& b() { return v[2]; }
//! Alias to 4th component (alpha value).
Standard_Byte& a() { return v[3]; }
public:
Standard_Byte v[4];
};
//! POD structure for packed BGR color value (3 bytes)
struct Image_ColorBGR
{
//! Returns the number of components.
static Standard_Integer Length()
{
return 3;
}
//! Alias to 3rd component (red intensity).
Standard_Byte r() const { return v[2]; }
//! Alias to 2nd component (green intensity).
Standard_Byte g() const { return v[1]; }
//! Alias to 1st component (blue intensity).
Standard_Byte b() const { return v[0]; }
//! Alias to 3rd component (red intensity).
Standard_Byte& r() { return v[2]; }
//! Alias to 2nd component (green intensity).
Standard_Byte& g() { return v[1]; }
//! Alias to 1st component (blue intensity).
Standard_Byte& b() { return v[0]; }
public:
Standard_Byte v[3];
};
//! POD structure for packed BGR color value (4 bytes with extra byte for alignment)
struct Image_ColorBGR32
{
//! Returns the number of components.
static Standard_Integer Length()
{
return 3;
}
//! Alias to 3rd component (red intensity).
Standard_Byte r() const { return v[2]; }
//! Alias to 2nd component (green intensity).
Standard_Byte g() const { return v[1]; }
//! Alias to 1st component (blue intensity).
Standard_Byte b() const { return v[0]; }
//! Alias to 4th component (dummy).
Standard_Byte a_() const { return v[3]; }
//! Alias to 3rd component (red intensity).
Standard_Byte& r() { return v[2]; }
//! Alias to 2nd component (green intensity).
Standard_Byte& g() { return v[1]; }
//! Alias to 1st component (blue intensity).
Standard_Byte& b() { return v[0]; }
//! Alias to 4th component (dummy).
Standard_Byte& a_() { return v[3]; }
public:
Standard_Byte v[4];
};
//! POD structure for packed BGRA color value (4 bytes)
struct Image_ColorBGRA
{
//! Returns the number of components.
static Standard_Integer Length()
{
return 4;
}
//! Alias to 3rd component (red intensity).
Standard_Byte r() const { return v[2]; }
//! Alias to 2nd component (green intensity).
Standard_Byte g() const { return v[1]; }
//! Alias to 1st component (blue intensity).
Standard_Byte b() const { return v[0]; }
//! Alias to 4th component (alpha value).
Standard_Byte a() const { return v[3]; }
//! Alias to 3rd component (red intensity).
Standard_Byte& r() { return v[2]; }
//! Alias to 2nd component (green intensity).
Standard_Byte& g() { return v[1]; }
//! Alias to 1st component (blue intensity).
Standard_Byte& b() { return v[0]; }
//! Alias to 4th component (alpha value).
Standard_Byte& a() { return v[3]; }
public:
Standard_Byte v[4];
};
//! POD structure for packed float RGB color value (3 floats)
struct Image_ColorRGBF
{
//! Returns the number of components.
static Standard_Integer Length()
{
return 3;
}
//! Alias to 1st component (red intensity).
Standard_ShortReal r() const { return v[0]; }
//! Alias to 2nd component (green intensity).
Standard_ShortReal g() const { return v[1]; }
//! Alias to 3rd component (blue intensity).
Standard_ShortReal b() const { return v[2]; }
//! Alias to 1st component (red intensity).
Standard_ShortReal& r() { return v[0]; }
//! Alias to 2nd component (green intensity).
Standard_ShortReal& g() { return v[1]; }
//! Alias to 3rd component (blue intensity).
Standard_ShortReal& b() { return v[2]; }
public:
Standard_ShortReal v[3];
};
//! POD structure for packed BGR float color value (3 floats)
struct Image_ColorBGRF
{
//! Returns the number of components.
static Standard_Integer Length()
{
return 3;
}
//! Alias to 3rd component (red intensity).
Standard_ShortReal r() const { return v[2]; }
//! Alias to 2nd component (green intensity).
Standard_ShortReal g() const { return v[1]; }
//! Alias to 1st component (blue intensity).
Standard_ShortReal b() const { return v[0]; }
//! Alias to 3rd component (red intensity).
Standard_ShortReal& r() { return v[2]; }
//! Alias to 2nd component (green intensity).
Standard_ShortReal& g() { return v[1]; }
//! Alias to 1st component (blue intensity).
Standard_ShortReal& b() { return v[0]; }
public:
Standard_ShortReal v[3];
};
//! POD structure for packed RGBA color value (4 floats)
struct Image_ColorRGBAF
{
//! Returns the number of components.
static Standard_Integer Length()
{
return 4;
}
//! Alias to 1st component (red intensity).
Standard_ShortReal r() const { return v[0]; }
//! Alias to 2nd component (green intensity).
Standard_ShortReal g() const { return v[1]; }
//! Alias to 3rd component (blue intensity).
Standard_ShortReal b() const { return v[2]; }
//! Alias to 4th component (alpha value).
Standard_ShortReal a() const { return v[3]; }
//! Alias to 1st component (red intensity).
Standard_ShortReal& r() { return v[0]; }
//! Alias to 2nd component (green intensity).
Standard_ShortReal& g() { return v[1]; }
//! Alias to 3rd component (blue intensity).
Standard_ShortReal& b() { return v[2]; }
//! Alias to 4th component (alpha value).
Standard_ShortReal& a() { return v[3]; }
public:
Standard_ShortReal v[4];
};
//! POD structure for packed float BGRA color value (4 floats)
struct Image_ColorBGRAF
{
//! Returns the number of components.
static Standard_Integer Length()
{
return 4;
}
//! Alias to 3rd component (red intensity).
Standard_ShortReal r() const { return v[2]; }
//! Alias to 2nd component (green intensity).
Standard_ShortReal g() const { return v[1]; }
//! Alias to 1st component (blue intensity).
Standard_ShortReal b() const { return v[0]; }
//! Alias to 4th component (alpha value).
Standard_ShortReal a() const { return v[3]; }
//! Alias to 3rd component (red intensity).
Standard_ShortReal& r() { return v[2]; }
//! Alias to 2nd component (green intensity).
Standard_ShortReal& g() { return v[1]; }
//! Alias to 1st component (blue intensity).
Standard_ShortReal& b() { return v[0]; }
//! Alias to 4th component (alpha value).
Standard_ShortReal& a() { return v[3]; }
public:
Standard_ShortReal v[4];
};
//! Addition operator
template<typename ColorType_t>
inline ColorType_t Image_ColorSumm3 (const ColorType_t& theA, const ColorType_t& theB)
{
ColorType_t aRes = { theA.v[0] + theB.v[0],
theA.v[1] + theB.v[1],
theA.v[2] + theB.v[2] };
return aRes;
}
inline Image_ColorRGB operator+ (const Image_ColorRGB& theA, const Image_ColorRGB& theB)
{
return Image_ColorSumm3 (theA, theB);
}
inline Image_ColorBGR operator+ (const Image_ColorBGR& theA, const Image_ColorBGR& theB)
{
return Image_ColorSumm3 (theA, theB);
}
inline Image_ColorRGBF operator+ (const Image_ColorRGBF& theA, const Image_ColorRGBF& theB)
{
return Image_ColorSumm3 (theA, theB);
}
inline Image_ColorBGRF operator+ (const Image_ColorBGRF& theA, const Image_ColorBGRF& theB)
{
return Image_ColorSumm3 (theA, theB);
}
template<typename ColorType_t>
inline ColorType_t Image_ColorSumm4 (const ColorType_t& theA, const ColorType_t& theB)
{
ColorType_t aRes = { theA.v[0] + theB.v[0],
theA.v[1] + theB.v[1],
theA.v[2] + theB.v[2],
theA.v[3] + theB.v[3] };
return aRes;
}
inline Image_ColorRGBA operator+ (const Image_ColorRGBA& theA, const Image_ColorRGBA& theB)
{
return Image_ColorSumm4 (theA, theB);
}
inline Image_ColorBGRA operator+ (const Image_ColorBGRA& theA, const Image_ColorBGRA& theB)
{
return Image_ColorSumm4 (theA, theB);
}
inline Image_ColorRGB32 operator+ (const Image_ColorRGB32& theA, const Image_ColorRGB32& theB)
{
return Image_ColorSumm4 (theA, theB);
}
inline Image_ColorBGR32 operator+ (const Image_ColorBGR32& theA, const Image_ColorBGR32& theB)
{
return Image_ColorSumm4 (theA, theB);
}
inline Image_ColorRGBAF operator+ (const Image_ColorRGBAF& theA, const Image_ColorRGBAF& theB)
{
return Image_ColorSumm4 (theA, theB);
}
inline Image_ColorBGRAF operator+ (const Image_ColorBGRAF& theA, const Image_ColorBGRAF& theB)
{
return Image_ColorSumm4 (theA, theB);
}
//! Subtraction operator
template<typename ColorType_t>
inline ColorType_t Image_ColorSub3 (const ColorType_t& theA, const ColorType_t& theB)
{
ColorType_t aRes = { theA.v[0] - theB.v[0],
theA.v[1] - theB.v[1],
theA.v[2] - theB.v[2] };
return aRes;
}
inline Image_ColorRGB operator- (const Image_ColorRGB& theA, const Image_ColorRGB& theB)
{
return Image_ColorSub3 (theA, theB);
}
inline Image_ColorBGR operator- (const Image_ColorBGR& theA, const Image_ColorBGR& theB)
{
return Image_ColorSub3 (theA, theB);
}
inline Image_ColorRGBF operator- (const Image_ColorRGBF& theA, const Image_ColorRGBF& theB)
{
return Image_ColorSub3 (theA, theB);
}
inline Image_ColorBGRF operator- (const Image_ColorBGRF& theA, const Image_ColorBGRF& theB)
{
return Image_ColorSub3 (theA, theB);
}
template<typename ColorType_t>
inline ColorType_t Image_ColorSub4 (const ColorType_t& theA, const ColorType_t& theB)
{
ColorType_t aRes = { theA.v[0] - theB.v[0],
theA.v[1] - theB.v[1],
theA.v[2] - theB.v[2],
theA.v[3] - theB.v[3] };
return aRes;
}
inline Image_ColorRGBA operator- (const Image_ColorRGBA& theA, const Image_ColorRGBA& theB)
{
return Image_ColorSub4 (theA, theB);
}
inline Image_ColorBGRA operator- (const Image_ColorBGRA& theA, const Image_ColorBGRA& theB)
{
return Image_ColorSub4 (theA, theB);
}
inline Image_ColorRGB32 operator- (const Image_ColorRGB32& theA, const Image_ColorRGB32& theB)
{
return Image_ColorSub4 (theA, theB);
}
inline Image_ColorBGR32 operator- (const Image_ColorBGR32& theA, const Image_ColorBGR32& theB)
{
return Image_ColorSub4 (theA, theB);
}
inline Image_ColorRGBAF operator- (const Image_ColorRGBAF& theA, const Image_ColorRGBAF& theB)
{
return Image_ColorSub4 (theA, theB);
}
inline Image_ColorBGRAF operator- (const Image_ColorBGRAF& theA, const Image_ColorBGRAF& theB)
{
return Image_ColorSub4 (theA, theB);
}
#endif // _Image_Color_H__

526
src/Image/Image_Diff.cxx Normal file
View File

@@ -0,0 +1,526 @@
// Created on: 2012-07-10
// Created by: VRO
// Copyright (c) 2012 OPEN CASCADE SAS
//
// The content of this file is subject to the Open CASCADE Technology Public
// License Version 6.5 (the "License"). You may not use the content of this file
// except in compliance with the License. Please obtain a copy of the License
// at http://www.opencascade.org and read it completely before using this file.
//
// The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
// main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
//
// The Original Code and all software distributed under the License is
// distributed on an "AS IS" basis, without warranty of any kind, and the
// Initial Developer hereby disclaims all such warranties, including without
// limitation, any warranties of merchantability, fitness for a particular
// purpose or non-infringement. Please see the License for the specific terms
// and conditions governing the rights and limitations under the License.
#include <Image_Diff.hxx>
#include <Image_AlienPixMap.hxx>
#include <TColStd_MapIteratorOfMapOfInteger.hxx>
#include <cstdlib>
IMPLEMENT_STANDARD_HANDLE (Image_Diff, Standard_Transient)
IMPLEMENT_STANDARD_RTTIEXT(Image_Diff, Standard_Transient)
//! Dot squared for difference of two colors
inline Standard_Integer dotSquared (const Image_ColorRGB& theColor)
{
// explicitly convert to integer
const Standard_Integer r = theColor.r();
const Standard_Integer g = theColor.g();
const Standard_Integer b = theColor.b();
return r * r + g * g + b * b;
}
//! @return true if pixel is black
inline bool isBlack (const Image_ColorRGB& theColor)
{
return theColor.r() == 0
&& theColor.g() == 0
&& theColor.b() == 0;
}
//! Converts a pixel position (row, column) to one integer value
inline Standard_Size pixel2Int (const Standard_Size aRow,
const Standard_Size aCol)
{
return aCol + (aRow << 15);
}
//! Converts an integer value to pixel coordinates (row, column)
inline void int2Pixel (const Standard_Size theValue,
Standard_Size& theRow,
Standard_Size& theCol)
{
theRow = (theValue >> 15);
theCol = theValue - (theRow << 15);
}
namespace
{
static const Standard_Size NEIGHBOR_PIXELS_NB = 8;
struct
{
Standard_Integer row_inc;
Standard_Integer col_inc;
inline Standard_Size pixel2Int (const Standard_Size theRowCenter,
const Standard_Size theColCenter) const
{
return ::pixel2Int (theRowCenter + Standard_Size(row_inc),
theColCenter + Standard_Size(col_inc));
}
inline bool isBlack (const Image_PixMapData<Image_ColorRGB>& theData,
const Standard_Size theRowCenter,
const Standard_Size theColCenter) const
{
return ::isBlack (theData.Value (theRowCenter + Standard_Size(row_inc),
theColCenter + Standard_Size(col_inc)));
}
}
static const NEIGHBOR_PIXELS[NEIGHBOR_PIXELS_NB] =
{
{-1, -1}, {-1, 0}, {-1, 1},
{ 0, -1}, { 0, 1},
{ 1, -1}, { 1, 0}, { 1, 1}
};
static bool isSupportedFormat (const Image_PixMap::ImgFormat theFormat)
{
return theFormat == Image_PixMap::ImgRGB
|| theFormat == Image_PixMap::ImgBGR
|| theFormat == Image_PixMap::ImgRGB32
|| theFormat == Image_PixMap::ImgBGR32
|| theFormat == Image_PixMap::ImgRGBA
|| theFormat == Image_PixMap::ImgBGRA;
}
};
// =======================================================================
// function : Image_Diff
// purpose :
// =======================================================================
Image_Diff::Image_Diff()
: myColorTolerance (0.0),
myIsBorderFilterOn (Standard_False)
{
//
}
// =======================================================================
// function : ~Image_Diff
// purpose :
// =======================================================================
Image_Diff::~Image_Diff()
{
releaseGroupsOfDiffPixels();
}
// =======================================================================
// function : Init
// purpose :
// =======================================================================
Standard_Boolean Image_Diff::Init (const Handle(Image_PixMap)& theImageRef,
const Handle(Image_PixMap)& theImageNew,
const Standard_Boolean theToBlackWhite)
{
myImageRef.Nullify();
myImageNew.Nullify();
myDiffPixels.Clear();
releaseGroupsOfDiffPixels();
if (theImageRef.IsNull() || theImageNew.IsNull()
|| theImageRef->IsEmpty() || theImageNew->IsEmpty()
|| theImageRef->SizeX() != theImageNew->SizeX()
|| theImageRef->SizeY() != theImageNew->SizeY()
|| theImageRef->Format() != theImageNew->Format())
{
std::cerr << "Images has different format or dimensions\n";
return Standard_False;
}
else if (!isSupportedFormat (theImageRef->Format()))
{
std::cerr << "Images has unsupported pixel format\n";
return Standard_False;
}
else if (theImageRef->SizeX() >= 0xFFFF
|| theImageRef->SizeY() >= 0xFFFF)
{
std::cerr << "Image too large\n";
return Standard_False;
}
myImageRef = theImageRef;
myImageNew = theImageNew;
if (theToBlackWhite)
{
// Convert the images to white/black
const Image_ColorRGB aWhite = {{255, 255, 255}};
Image_PixMapData<Image_ColorRGB>& aDataRef = myImageRef->EditData<Image_ColorRGB>();
Image_PixMapData<Image_ColorRGB>& aDataNew = myImageNew->EditData<Image_ColorRGB>();
for (Standard_Size aRow = 0; aRow < aDataRef.SizeY(); ++aRow)
{
for (Standard_Size aCol = 0; aCol < aDataRef.SizeY(); ++aCol)
{
Image_ColorRGB& aPixel1 = aDataRef.ChangeValue (aRow, aCol);
Image_ColorRGB& aPixel2 = aDataNew.ChangeValue (aRow, aCol);
if (!isBlack (aPixel1))
{
aPixel1 = aWhite;
}
if (!isBlack (aPixel2))
{
aPixel2 = aWhite;
}
}
}
}
return Standard_True;
}
// =======================================================================
// function : Init
// purpose :
// =======================================================================
Standard_Boolean Image_Diff::Init (const TCollection_AsciiString& theImgPathRef,
const TCollection_AsciiString& theImgPathNew,
const Standard_Boolean theToBlackWhite)
{
Handle(Image_AlienPixMap) anImgRef = new Image_AlienPixMap();
Handle(Image_AlienPixMap) anImgNew = new Image_AlienPixMap();
if (!anImgRef->Load (theImgPathRef)
|| !anImgNew->Load (theImgPathNew))
{
std::cerr << "Failed to load image(s) file(s)\n";
return Standard_False;
}
return Init (anImgRef, anImgNew, theToBlackWhite);
}
// =======================================================================
// function : SetColorTolerance
// purpose :
// =======================================================================
void Image_Diff::SetColorTolerance (const Standard_Real theTolerance)
{
myColorTolerance = theTolerance;
}
// =======================================================================
// function : ColorTolerance
// purpose :
// =======================================================================
Standard_Real Image_Diff::ColorTolerance() const
{
return myColorTolerance;
}
// =======================================================================
// function : SetBorderFilterOn
// purpose :
// =======================================================================
void Image_Diff::SetBorderFilterOn (const Standard_Boolean theToIgnore)
{
myIsBorderFilterOn = theToIgnore;
}
// =======================================================================
// function : IsBorderFilterOn
// purpose :
// =======================================================================
Standard_Boolean Image_Diff::IsBorderFilterOn() const
{
return myIsBorderFilterOn;
}
// =======================================================================
// function : Compare
// purpose :
// =======================================================================
Standard_Integer Image_Diff::Compare()
{
// Number of different pixels (by color)
Standard_Integer aNbDiffColors = 0;
myDiffPixels.Clear();
if (myImageRef.IsNull() || myImageNew.IsNull())
{
return -1;
}
// Tolerance of comparison operation for color
// Maximum difference between colors (white - black) = 100%
Image_ColorRGB aDiff = {{255, 255, 255}};
const Standard_Integer aMaxDiffColor = dotSquared (aDiff);
const Standard_Integer aDiffThreshold = Standard_Integer(Standard_Real(aMaxDiffColor) * myColorTolerance);
// we don't care about RGB/BGR/RGBA/BGRA/RGB32/BGR32 differences
// because we just compute summ of r g b components
const Image_PixMapData<Image_ColorRGB>& aDataRef = myImageRef->ReadData<Image_ColorRGB>();
const Image_PixMapData<Image_ColorRGB>& aDataNew = myImageNew->ReadData<Image_ColorRGB>();
// compare colors of each pixel
for (Standard_Size aRow = 0; aRow < myImageRef->SizeY(); ++aRow)
{
for (Standard_Size aCol = 0; aCol < myImageRef->SizeX(); ++aCol)
{
aDiff = aDataNew.Value (aRow, aCol) - aDataRef.Value (aRow, aCol);
if (dotSquared (aDiff) > aDiffThreshold)
{
const Standard_Size aValue = pixel2Int (aRow, aCol);
myDiffPixels.Append (aValue);
++aNbDiffColors;
}
}
}
// take into account a border effect
if (myIsBorderFilterOn && myDiffPixels.Length() > 0)
{
aNbDiffColors = ignoreBorderEffect();
}
return aNbDiffColors;
}
// =======================================================================
// function : SaveDiffImage
// purpose :
// =======================================================================
Standard_Boolean Image_Diff::SaveDiffImage (Image_PixMap& theDiffImage) const
{
if (myImageRef.IsNull() || myImageNew.IsNull())
{
return Standard_False;
}
if (theDiffImage.IsEmpty()
|| theDiffImage.SizeX() != myImageRef->SizeX()
|| theDiffImage.SizeY() != myImageRef->SizeY()
|| !isSupportedFormat (theDiffImage.Format()))
{
if (!theDiffImage.InitTrash (Image_PixMap::ImgRGB, myImageRef->SizeX(), myImageRef->SizeY()))
{
return Standard_False;
}
}
Standard_Size aRow, aCol;
const Image_ColorRGB aWhite = {{255, 255, 255}};
Image_PixMapData<Image_ColorRGB>& aDataOut = theDiffImage.EditData<Image_ColorRGB>();
// initialize black image for dump
memset (theDiffImage.ChangeData(), 0, theDiffImage.SizeBytes());
if (myGroupsOfDiffPixels.IsEmpty())
{
if (myIsBorderFilterOn)
{
return Standard_True;
}
for (Standard_Integer aPixelId = 0; aPixelId < myDiffPixels.Length(); ++aPixelId)
{
const Standard_Size aValue = myDiffPixels.Value (aPixelId);
int2Pixel (aValue, aRow, aCol);
aDataOut.ChangeValue (aRow, aCol) = aWhite;
}
return Standard_True;
}
Standard_Integer aGroupId = 1;
for (ListOfMapOfInteger::Iterator aGrIter (myGroupsOfDiffPixels); aGrIter.More(); aGrIter.Next(), ++aGroupId)
{
if (myLinearGroups.Contains (aGroupId))
{
continue; // skip linear groups
}
const TColStd_MapOfInteger* aGroup = aGrIter.Value();
for (TColStd_MapIteratorOfMapOfInteger aPixelIter(*aGroup);
aPixelIter.More(); aPixelIter.Next())
{
int2Pixel (aPixelIter.Key(), aRow, aCol);
aDataOut.ChangeValue (aRow, aCol) = aWhite;
}
}
return Standard_True;
}
// =======================================================================
// function : SaveDiffImage
// purpose :
// =======================================================================
Standard_Boolean Image_Diff::SaveDiffImage (const TCollection_AsciiString& theDiffPath) const
{
if (myImageRef.IsNull() || myImageNew.IsNull() || theDiffPath.IsEmpty())
{
return Standard_False;
}
Image_AlienPixMap aDiff;
if (!aDiff.InitTrash (Image_PixMap::ImgRGB, myImageRef->SizeX(), myImageRef->SizeY())
|| !SaveDiffImage (aDiff))
{
return Standard_False;
}
// save image
return aDiff.Save (theDiffPath);
}
// =======================================================================
// function : ignoreBorderEffect
// purpose :
// =======================================================================
Standard_Integer Image_Diff::ignoreBorderEffect()
{
if (myImageRef.IsNull() || myImageNew.IsNull())
{
return 0;
}
const Image_PixMapData<Image_ColorRGB>& aDataRef = myImageRef->ReadData<Image_ColorRGB>();
// allocate groups of different pixels
releaseGroupsOfDiffPixels();
// Find a different area (a set of close to each other pixels which colors differ in both images).
// It filters alone pixels with different color.
Standard_Size aRow1, aCol1, aRow2, aCol2;
Standard_Integer aLen1 = (myDiffPixels.Length() > 0) ? (myDiffPixels.Length() - 1) : 0;
for (Standard_Integer aPixelId1 = 0; aPixelId1 < aLen1; ++aPixelId1)
{
const Standard_Size aValue1 = myDiffPixels.Value (aPixelId1);
int2Pixel (aValue1, aRow1, aCol1);
// Check other pixels in the list looking for a neighbour of this one
for (Standard_Integer aPixelId2 = aPixelId1 + 1; aPixelId2 < myDiffPixels.Length(); ++aPixelId2)
{
const Standard_Size aValue2 = myDiffPixels.Value (aPixelId2);
int2Pixel (aValue2, aRow2, aCol2);
if (std::abs (ptrdiff_t (aCol1 - aCol2)) <= 1 &&
std::abs (ptrdiff_t (aRow1 - aRow2)) <= 1)
{
// A neighbour is found. Create a new group and add both pixels.
if (myGroupsOfDiffPixels.IsEmpty())
{
TColStd_MapOfInteger* aGroup = new TColStd_MapOfInteger();
aGroup->Add (aValue1);
aGroup->Add (aValue2);
myGroupsOfDiffPixels.Append (aGroup);
}
else
{
// Find a group the pixels belong to.
Standard_Boolean isFound = Standard_False;
for (ListOfMapOfInteger::Iterator aGrIter (myGroupsOfDiffPixels); aGrIter.More(); aGrIter.Next())
{
TColStd_MapOfInteger*& aGroup = aGrIter.ChangeValue();
if (aGroup->Contains (aValue1))
{
aGroup->Add (aValue2);
isFound = Standard_True;
break;
}
}
if (!isFound)
{
// Create a new group
TColStd_MapOfInteger* aGroup = new TColStd_MapOfInteger();
aGroup->Add (aValue1);
aGroup->Add (aValue2);
myGroupsOfDiffPixels.Append (aGroup);
}
}
}
}
}
// filter linear groups which represent border of a solid shape
Standard_Integer aGroupId = 1;
for (ListOfMapOfInteger::Iterator aGrIter (myGroupsOfDiffPixels); aGrIter.More(); aGrIter.Next(), ++aGroupId)
{
Standard_Integer aNeighboursNb = 0;
Standard_Boolean isLine = Standard_True;
const TColStd_MapOfInteger* aGroup = aGrIter.Value();
for (TColStd_MapIteratorOfMapOfInteger aPixelIter (*aGroup); aPixelIter.More(); aPixelIter.Next())
{
int2Pixel (aPixelIter.Key(), aRow1, aCol1);
aNeighboursNb = 0;
// pixels of a line have only 1 or 2 neighbour pixels inside the same group
// check all neighbour pixels on presence in the group
for (Standard_Size aNgbrIter = 0; aNgbrIter < NEIGHBOR_PIXELS_NB; ++aNgbrIter)
{
if (aGroup->Contains (NEIGHBOR_PIXELS[aNgbrIter].pixel2Int (aRow1, aCol1)))
{
++aNeighboursNb;
}
}
if (aNeighboursNb > 2)
{
isLine = Standard_False;
break;
}
} // for pixels inside group...
if (isLine)
{
// Test a pixel of the linear group on belonging to a solid shape.
// Consider neighbour pixels of the last pixel of the linear group in the 1st image.
// If the pixel has greater than 1 not black neighbour pixel, it is a border of a shape.
// Otherwise, it may be a topological edge, for example.
aNeighboursNb = 0;
for (Standard_Size aNgbrIter = 0; aNgbrIter < NEIGHBOR_PIXELS_NB; ++aNgbrIter)
{
if (!NEIGHBOR_PIXELS[aNgbrIter].isBlack (aDataRef, aRow1, aCol1))
{
++aNeighboursNb;
}
}
if (aNeighboursNb > 1)
{
myLinearGroups.Add (aGroupId);
}
}
} // for groups...
// number of different groups of pixels (except linear groups)
Standard_Integer aNbDiffColors = 0;
aGroupId = 1;
for (ListOfMapOfInteger::Iterator aGrIter (myGroupsOfDiffPixels); aGrIter.More(); aGrIter.Next(), ++aGroupId)
{
if (!myLinearGroups.Contains (aGroupId))
++aNbDiffColors;
}
return aNbDiffColors;
}
// =======================================================================
// function : releaseGroupsOfDiffPixels
// purpose :
// =======================================================================
void Image_Diff::releaseGroupsOfDiffPixels()
{
for (ListOfMapOfInteger::Iterator aGrIter (myGroupsOfDiffPixels); aGrIter.More(); aGrIter.Next())
{
TColStd_MapOfInteger*& aGroup = aGrIter.ChangeValue();
delete aGroup;
}
myGroupsOfDiffPixels.Clear();
myLinearGroups.Clear();
}

135
src/Image/Image_Diff.hxx Normal file
View File

@@ -0,0 +1,135 @@
// Created on: 2012-07-10
// Created by: VRO
// Copyright (c) 2012 OPEN CASCADE SAS
//
// The content of this file is subject to the Open CASCADE Technology Public
// License Version 6.5 (the "License"). You may not use the content of this file
// except in compliance with the License. Please obtain a copy of the License
// at http://www.opencascade.org and read it completely before using this file.
//
// The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
// main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
//
// The Original Code and all software distributed under the License is
// distributed on an "AS IS" basis, without warranty of any kind, and the
// Initial Developer hereby disclaims all such warranties, including without
// limitation, any warranties of merchantability, fitness for a particular
// purpose or non-infringement. Please see the License for the specific terms
// and conditions governing the rights and limitations under the License.
#ifndef _Image_Diff_H__
#define _Image_Diff_H__
#include <Image_PixMap.hxx>
#include <TCollection_AsciiString.hxx>
#include <TColStd_MapOfInteger.hxx>
#include <NCollection_List.hxx>
#include <NCollection_Vector.hxx>
//! This class compares two images pixel-by-pixel.
//! It uses the following methods to ignore the difference between images:
//! - Black/White comparison. It makes the images 2-colored before the comparison.
//! - Equality with tolerance. Colors of two pixels are considered the same if the
//! differnce of their color is less than a tolerance.
//! - Border filter. The algorithm ignores alone independent pixels,
//! which are different on both images, ignores the "border effect" -
//! the difference caused by triangles located at angle about 0 or 90 degrees to the user.
//!
//! Border filter ignores a difference in implementation of
//! anti-aliasing and other effects on boundary of a shape.
//! The triangles of a boundary zone are usually located so that their normals point aside the user
//! (about 90 degree between the normal and the direction to the user's eye).
//! Deflection of the light for such a triangle depends on implementation of the video driver.
//! In order to skip this difference the following algorithm is used:
//! a) "Different" pixels are groupped and checked on "one-pixel width line".
//! indeed, the pixels may represent not a line, but any curve.
//! But the width of this curve should be not more than a pixel.
//! This group of pixels become a candidate to be ignored because of boundary effect.
//! b) The group of pixels is checked on belonging to a "shape".
//! Neighbour pixels are checked from the reference image.
//! This test confirms a fact that the group of pixels belongs to a shape and
//! represent a boundary of the shape.
//! In this case the whole group of pixels is ignored (considered as same).
//! Otherwise, the group of pixels may represent a geometrical curve in the viewer 3D
//! and should be considered as "different".
class Image_Diff : public Standard_Transient
{
public:
//! An empty constructor. Init() should be called for initialization.
Standard_EXPORT Image_Diff();
//! Desctructor.
Standard_EXPORT virtual ~Image_Diff();
//! Initialize algorithm by two images.
//! @return false if images has different or unsupported pixel format.
Standard_EXPORT Standard_Boolean Init (const Handle(Image_PixMap)& theImageRef,
const Handle(Image_PixMap)& theImageNew,
const Standard_Boolean theToBlackWhite = Standard_False);
//! Initialize algorithm by two images (will be loaded from files).
//! @return false if images couldn't be opened or their format is unsupported.
Standard_EXPORT Standard_Boolean Init (const TCollection_AsciiString& theImgPathRef,
const TCollection_AsciiString& theImgPathNew,
const Standard_Boolean theToBlackWhite = Standard_False);
//! Color tolerance for equality check. Should be within range 0..1:
//! Corresponds to a difference between white and black colors (maximum difference).
//! By default, the tolerance is equal to 0 thus equality check will return false for any different colors.
Standard_EXPORT void SetColorTolerance (const Standard_Real theTolerance);
//! Color tolerance for equality check.
Standard_EXPORT Standard_Real ColorTolerance() const;
//! Sets taking into account (ignoring) a "border effect" on comparison of images.
//! The border effect is caused by a border of shaded shapes in the viewer 3d.
//! Triangles of this area are located at about 0 or 90 degrees to the user.
//! Therefore, they deflect light differently according to implementation of a video card driver.
//! This flag allows to detect such a "border" area and skip it from comparison of images.
//! Filter turned OFF by default.
Standard_EXPORT void SetBorderFilterOn (const Standard_Boolean theToIgnore);
//! Returns a flag of taking into account (ignoring) a border effect in comparison of images.
Standard_EXPORT Standard_Boolean IsBorderFilterOn() const;
//! Compares two images. It returns a number of different pixels (or groups of pixels).
//! It returns -1 if algorithm not initialized before.
Standard_EXPORT Standard_Integer Compare();
//! Saves a difference between two images as white pixels on black backgroud.
Standard_EXPORT Standard_Boolean SaveDiffImage (Image_PixMap& theDiffImage) const;
//! Saves a difference between two images as white pixels on black backgroud.
Standard_EXPORT Standard_Boolean SaveDiffImage (const TCollection_AsciiString& theDiffPath) const;
protected:
//! Perform border filter algorithm.
Standard_EXPORT Standard_Integer ignoreBorderEffect();
//! Release dynamically allocated memory.
Standard_EXPORT void releaseGroupsOfDiffPixels();
protected:
typedef NCollection_List<TColStd_MapOfInteger* > ListOfMapOfInteger;
Handle(Image_PixMap) myImageRef; //!< reference image to compare (from)
Handle(Image_PixMap) myImageNew; //!< new image to compare (to)
Standard_Real myColorTolerance; //!< tolerance for equality check (0..1, 0 - any not equal, 1 - opposite colors)
Standard_Boolean myIsBorderFilterOn; //!< perform algorithm with border effect filter
ListOfMapOfInteger myGroupsOfDiffPixels;
NCollection_Vector<Standard_Size> myDiffPixels; //!< different pixels (position packed into integer)
TColStd_MapOfInteger myLinearGroups;
public:
DEFINE_STANDARD_RTTI(Image_Diff) // Type definition
};
DEFINE_STANDARD_HANDLE(Image_Diff, Standard_Transient)
#endif // _Image_Diff_H__

View File

@@ -1,29 +0,0 @@
// Copyright (c) 1999-2012 OPEN CASCADE SAS
//
// The content of this file is subject to the Open CASCADE Technology Public
// License Version 6.5 (the "License"). You may not use the content of this file
// except in compliance with the License. Please obtain a copy of the License
// at http://www.opencascade.org and read it completely before using this file.
//
// The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
// main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
//
// The Original Code and all software distributed under the License is
// distributed on an "AS IS" basis, without warranty of any kind, and the
// Initial Developer hereby disclaims all such warranties, including without
// limitation, any warranties of merchantability, fitness for a particular
// purpose or non-infringement. Please see the License for the specific terms
// and conditions governing the rights and limitations under the License.
#ifndef _Image_HPrivateImage_HeaderFile
#define _Image_HPrivateImage_HeaderFile
#include <NCollection_Handle.hxx>
// This typedef shadows the private image storage class
// Currently FreeImagePlus is used
class fipImage;
typedef NCollection_Handle<fipImage> Image_HPrivateImage;
#endif /*_Image_HPrivateImage_HeaderFile*/

View File

@@ -1,147 +0,0 @@
-- Created on: 2010-09-16
-- Created by: KGV
-- Copyright (c) 2010-2012 OPEN CASCADE SAS
--
-- The content of this file is subject to the Open CASCADE Technology Public
-- License Version 6.5 (the "License"). You may not use the content of this file
-- except in compliance with the License. Please obtain a copy of the License
-- at http://www.opencascade.org and read it completely before using this file.
--
-- The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
-- main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
--
-- The Original Code and all software distributed under the License is
-- distributed on an "AS IS" basis, without warranty of any kind, and the
-- Initial Developer hereby disclaims all such warranties, including without
-- limitation, any warranties of merchantability, fitness for a particular
-- purpose or non-infringement. Please see the License for the specific terms
-- and conditions governing the rights and limitations under the License.
class PixMap from Image
---Version:
---Purpose: This class defines a system-independent bitmap
---Keywords: Bitmap, Pixmap
inherits
PixMap from Aspect
uses
Handle from Aspect,
TypeOfImage from Image,
HPrivateImage from Image,
CRawBufferData from Image,
Color from Quantity,
Parameter from Quantity
raises
PixmapDefinitionError from Aspect,
PixmapError from Aspect
is
Create ( theWidth, theHeight : Integer from Standard;
theType : TypeOfImage from Image )
returns mutable PixMap from Image
raises PixmapDefinitionError from Aspect;
---Level: Public
---Purpose:
-- Allocate the bitmap with requested dimensions.
-- Allowed image types:
-- - Image_TOI_RGB (color image, 1 byte per component);
-- - Image_TOI_RGBA (color image with alpha channel);
-- - Image_TOI_RGBF (color image, 1 float per component);
-- - Image_TOI_RGBAF (color image with alpha channel);
-- - Image_TOI_FLOAT (grey image, 1 float per pixel).
Create ( theDataPtr : PByte from Standard;
theWidth, theHeight : Integer from Standard;
thePitch : Integer from Standard;
theBitsPerPixel : Integer from Standard;
theIsTopDown : Boolean from Standard )
returns mutable PixMap from Image
raises PixmapDefinitionError from Aspect;
---Level: Public
---Purpose:
-- Create a bitmap by copying an existing buffer.
---------------------------------------------------
-- Category: Methods to modify the class definition
---------------------------------------------------
Destroy ( me : mutable )
---Level: Advanced
---Purpose:
-- Destroies the Bitmap
---C++: alias ~
---Trigger: Raises if Bitmap is not defined properly
raises PixmapError from Aspect is virtual;
Dump ( me;
theFilename : CString from Standard;
theGammaCorr : Real from Standard = 1.0 )
returns Boolean from Standard
---Level: Advanced
---Purpose:
-- Dumps the Bitmap to an image file with
-- an optional gamma correction value
-- and returns TRUE if the dump occurs normaly.
raises PixmapError from Aspect is virtual;
----------------------------
-- Category: Inquire methods
----------------------------
PixmapID ( me ) returns Handle from Aspect is virtual;
---Level: Advanced
---Purpose:
-- Returns NULL handle
---Category: Inquire methods
----------------------------
-- Category: Access methods
----------------------------
AccessBuffer ( me : in;
theBufferInfo : in out CRawBufferData from Image )
is static;
---Purpose:
-- Fill the structure for low-level access to the bitmap data.
-- It is up to you to interpret these bytes correctly!
-- Important notice: image stored upside-down in the memory,
-- first image row is an last scanline in
-- the memory buffer.
-- If image was created with type Image_TOI_FLOAT buffer
-- format will be set to TDepthComponent. You can override
-- this field with another one-channel buffer format because
-- it useless for bitmap definition.
PixelColor ( me : in;
theX, theY : in Integer from Standard )
returns Color from Quantity
is virtual;
---Purpose:
-- Returns the pixel color. This function is relatively slow,
-- use AccessBuffer() instead for stream operations.
-- Note that this function convert input theY coordinate
-- to count off from top of an image (while in memory it stored
-- upside-down).
PixelColor ( me : in;
theX, theY : in Integer from Standard;
theAlpha : out Parameter from Quantity )
returns Color from Quantity;
---Purpose:
-- Returns the pixel color. This function is relatively slow,
-- use AccessBuffer() instead for stream operations.
-- theAlpha argument is set to color intensity (0 - transparent, 1 - opaque)
-- Note that this function convert input theY coordinate
-- to count off from top of an image (while in memory it stored
-- upside-down).
fields
myImage : HPrivateImage from Image is protected;
end PixMap;

View File

@@ -1,6 +1,6 @@
// Created on: 2010-09-16
// Created by: KGV
// Copyright (c) 2010-2012 OPEN CASCADE SAS
// Created on: 2012-07-18
// Created by: Kirill GAVRILOV
// Copyright (c) 2012 OPEN CASCADE SAS
//
// The content of this file is subject to the Open CASCADE Technology Public
// License Version 6.5 (the "License"). You may not use the content of this file
@@ -17,414 +17,209 @@
// purpose or non-infringement. Please see the License for the specific terms
// and conditions governing the rights and limitations under the License.
#ifdef HAVE_CONFIG_H
# include <config.h>
#include <Image_PixMap.hxx>
#ifndef _MSC_VER
#include <mm_malloc.h>
#endif
#ifdef HAVE_FREEIMAGE
#include <FreeImagePlus.h>
#include <Image_PixMap.ixx>
/* OCC22216 NOTE: linker dependency can be switched off by undefining macro */
#ifdef _MSC_VER
#pragma comment( lib, "FreeImage.lib" )
#pragma comment( lib, "FreeImagePlus.lib" )
#endif
template<typename TypePtr>
inline TypePtr MemAllocAligned (const Standard_Size& theBytesCount,
const Standard_Size& theAlign = 16)
{
#if defined(_MSC_VER)
return (TypePtr )_aligned_malloc (theBytesCount, theAlign);
#else
#include <Image_PixMap.ixx>
#include <fstream>
#if (defined(BYTE_ORDER) && BYTE_ORDER==BIG_ENDIAN) || \
(defined(__BYTE_ORDER) && __BYTE_ORDER==__BIG_ENDIAN) || \
defined(__BIG_ENDIAN__)
#define THE_BIGENDIAN
#endif
// dummy class which can only dump to PPM format
class fipImage
{
public:
typedef struct tagRGBQuad {
#ifndef THE_BIGENDIAN
Standard_Byte rgbBlue;
Standard_Byte rgbGreen;
Standard_Byte rgbRed;
#else
Standard_Byte rgbRed;
Standard_Byte rgbGreen;
Standard_Byte rgbBlue;
#endif
Standard_Byte rgbReserved;
} RGBQuad_t;
public:
fipImage()
: myDataPtr(NULL),
mySizeX(0),
mySizeY(0),
myBytesPerLine(0),
myBytesPerPixel(3)
{
//
}
fipImage (const Standard_Integer theSizeX, const Standard_Integer theSizeY,
const Standard_Integer theBytesPerLine = 0, const Standard_Integer theBytesPerPixel = 3)
: myDataPtr(NULL),
mySizeX (theSizeX),
mySizeY (theSizeY),
myBytesPerLine (theBytesPerLine),
myBytesPerPixel (theBytesPerPixel)
{
if (myBytesPerLine == 0)
{
myBytesPerLine = mySizeX * myBytesPerPixel;
}
myDataPtr = new Standard_Byte[myBytesPerLine * mySizeY];
}
~fipImage()
{
delete[] myDataPtr;
}
Standard_Integer getHeight() const
{
return mySizeY;
}
Standard_Integer getWidth() const
{
return mySizeX;
}
Standard_Integer getBytesPerPixel() const
{
return myBytesPerPixel;
}
Standard_Integer getBytesPerLine() const
{
return myBytesPerLine;
}
Standard_Byte* getData() const
{
return myDataPtr;
}
Standard_Byte* getScanLine (const Standard_Integer theRow) const
{
return &myDataPtr[theRow * myBytesPerLine];
}
Quantity_Color getPixelColor (const Standard_Integer theCol,
const Standard_Integer theRow,
Quantity_Parameter& theAlpha) const
{
RGBQuad_t* aPixel = (RGBQuad_t* )&getScanLine (theRow)[theCol * myBytesPerPixel];
theAlpha = (myBytesPerPixel > 3) ? (Standard_Real (aPixel->rgbReserved) / 255.0) : 1.0;
return Quantity_Color (Standard_Real (aPixel->rgbRed) / 255.0,
Standard_Real (aPixel->rgbGreen) / 255.0,
Standard_Real (aPixel->rgbBlue) / 255.0,
Quantity_TOC_RGB);
}
Standard_Boolean savePPM (const Standard_CString theFileName) const
{
// Open file
FILE* pFile = fopen (theFileName, "wb");
if (pFile == NULL)
{
return Standard_False;
}
// Write header
fprintf (pFile, "P6\n%d %d\n255\n", mySizeX, mySizeY);
// Write pixel data
Standard_Byte* aScanLine;
RGBQuad_t* aPixel;
// image stored upside-down
for (Standard_Integer aRow = mySizeY - 1; aRow >= 0; --aRow)
{
aScanLine = getScanLine (aRow);
for (Standard_Integer aCol = 0; aCol < mySizeX; ++aCol)
{
aPixel = (RGBQuad_t* )&aScanLine[aCol * myBytesPerPixel];
fwrite (&aPixel->rgbRed, 1, 1, pFile);
fwrite (&aPixel->rgbGreen, 1, 1, pFile);
fwrite (&aPixel->rgbBlue, 1, 1, pFile);
}
}
// Close file
fclose (pFile);
return Standard_True;
}
Standard_Integer getMaxRowAligmentBytes() const
{
Standard_Integer aDeltaBytes = myBytesPerLine - myBytesPerPixel * mySizeX;
for (Standard_Integer anAligment = 16; anAligment > 1; anAligment /= 2)
{
if (isRowAlignedTo (anAligment, aDeltaBytes))
{
return anAligment;
}
}
return 1;
}
private:
Standard_Boolean isRowAlignedTo (const Standard_Integer theAligmentBytes,
const Standard_Integer theDeltaBytes) const
{
return (theDeltaBytes < theAligmentBytes) &&
((myBytesPerLine % theAligmentBytes) == 0);
}
private:
Standard_Byte* myDataPtr;
Standard_Integer mySizeX;
Standard_Integer mySizeY;
Standard_Integer myBytesPerLine;
Standard_Integer myBytesPerPixel;
};
return (TypePtr ) _mm_malloc (theBytesCount, theAlign);
#endif
}
#include <gp.hxx>
#include <TCollection_AsciiString.hxx>
//=======================================================================
//function : Image_PixMap
//purpose :
//=======================================================================
Image_PixMap::Image_PixMap (const Standard_Integer theWidth,
const Standard_Integer theHeight,
const Image_TypeOfImage theType)
: Aspect_PixMap (theWidth, theHeight, 1),
myImage()
inline void MemFreeAligned (void* thePtrAligned)
{
#ifdef HAVE_FREEIMAGE
FREE_IMAGE_TYPE aFIType = FIT_UNKNOWN;
int aBitsPerPixel = 0;
switch (theType)
{
case Image_TOI_RGBA:
aFIType = FIT_BITMAP;
aBitsPerPixel = 32;
break;
case Image_TOI_RGBF:
aFIType = FIT_RGBF;
aBitsPerPixel = 96;
break;
case Image_TOI_RGBAF:
aFIType = FIT_RGBAF;
aBitsPerPixel = 128;
break;
case Image_TOI_FLOAT:
aFIType = FIT_FLOAT;
aBitsPerPixel = 32;
break;
case Image_TOI_RGB:
default:
aFIType = FIT_BITMAP;
aBitsPerPixel = 24;
break;
}
myImage = new fipImage (aFIType, theWidth, theHeight, aBitsPerPixel);
#if defined(_MSC_VER)
_aligned_free (thePtrAligned);
#else
Standard_Integer aBytesPerPixel = 0;
switch (theType)
{
case Image_TOI_RGBAF:
std::cerr << "Float formats not supported\n";
case Image_TOI_RGBA:
aBytesPerPixel = 4;
break;
case Image_TOI_RGBF:
case Image_TOI_FLOAT:
std::cerr << "Float formats not supported\n";
case Image_TOI_RGB:
default:
aBytesPerPixel = 3;
break;
}
myImage = new fipImage (theWidth, theHeight, 0, aBytesPerPixel);
//
_mm_free (thePtrAligned);
#endif
}
//=======================================================================
//function : Image_PixMap
//purpose :
//=======================================================================
Image_PixMap::Image_PixMap (const Standard_PByte theDataPtr,
const Standard_Integer theWidth, const Standard_Integer theHeight,
const Standard_Integer thePitch, const Standard_Integer theBitsPerPixel,
const Standard_Boolean theIsTopDown)
: Aspect_PixMap (theWidth, theHeight, 1),
myImage (new fipImage())
{
#ifdef HAVE_FREEIMAGE
*myImage = FreeImage_ConvertFromRawBits (theDataPtr,
theWidth, theHeight,
thePitch, theBitsPerPixel,
0, 0, 0,
theIsTopDown);
if (theBitsPerPixel != 24)
{
myImage->convertTo24Bits();
}
#else
myImage = new fipImage (theWidth, theHeight, thePitch, theBitsPerPixel / 8);
Standard_Integer aRowStart = !theIsTopDown ? 0 : (theHeight - 1);
Standard_Integer aRowDelta = !theIsTopDown ? 1 : -1;
for (Standard_Integer aRowFrom (aRowStart), aRowTo (0);
aRowFrom >= 0 && aRowFrom < theHeight;
aRowFrom += aRowDelta, ++aRowTo)
{
memcpy (myImage->getScanLine (aRowTo),
&theDataPtr[aRowFrom * thePitch],
myImage->getBytesPerLine());
}
#endif
}
//=======================================================================
//function : Destroy
//purpose :
//=======================================================================
void Image_PixMap::Destroy()
{
myImage = Image_HPrivateImage();
}
//=======================================================================
//function : Dump
//purpose :
//=======================================================================
Standard_Boolean Image_PixMap::Dump (const Standard_CString theFilename,
const Standard_Real theGammaCorr) const
{
#ifdef HAVE_FREEIMAGE
FREE_IMAGE_FORMAT anImageFormat = FreeImage_GetFIFFromFilename (theFilename);
if (anImageFormat == FIF_UNKNOWN)
{
std::cerr << "Image_PixMap, image format doesn't supported!\n";
return Standard_False;
}
Standard_Boolean isCopied = Standard_False;
Image_HPrivateImage anImageToDump = myImage;
if (Abs (theGammaCorr - 1.0) > gp::Resolution())
{
if (!isCopied)
{
isCopied = Standard_True;
anImageToDump = new fipImage (*myImage);
}
anImageToDump->adjustGamma (theGammaCorr);
}
switch (anImageFormat)
{
case FIF_GIF:
if (!isCopied)
{
isCopied = Standard_True;
anImageToDump = new fipImage (*myImage);
}
// need convertion to image with pallete
anImageToDump->colorQuantize (FIQ_NNQUANT);
break;
case FIF_EXR:
if (myImage->getImageType() == FIT_BITMAP)
{
if (!isCopied)
{
isCopied = Standard_True;
anImageToDump = new fipImage (*myImage);
}
anImageToDump->convertToType (FIT_RGBF);
}
break;
default:
if (myImage->getImageType() != FIT_BITMAP)
{
if (!isCopied)
{
isCopied = Standard_True;
anImageToDump = new fipImage (*myImage);
}
anImageToDump->convertToType (FIT_BITMAP);
}
break;
}
return anImageToDump->save (theFilename);
#else
return myImage->savePPM (theFilename);
#endif
}
Aspect_Handle Image_PixMap::PixmapID() const
{
return Aspect_Handle();
}
void Image_PixMap::AccessBuffer (Image_CRawBufferData& theBuffer) const
{
theBuffer.widthPx = myImage->getWidth();
theBuffer.heightPx = myImage->getHeight();
#ifdef HAVE_FREEIMAGE
theBuffer.rowAligmentBytes = 4; // 32 bits according to FreeImage documentation
switch (myImage->getImageType())
{
case FIT_FLOAT:
theBuffer.format = TDepthComponent;
theBuffer.type = TFloat;
break;
case FIT_RGBF:
theBuffer.format = TRGB;
theBuffer.type = TFloat;
break;
case FIT_RGBAF:
theBuffer.format = TRGBA;
theBuffer.type = TFloat;
break;
case FIT_BITMAP:
default:
#if defined(FREEIMAGE_BIGENDIAN)
theBuffer.format = (myImage->getColorType() == FIC_RGBALPHA) ? TRGBA : TRGB;
#else
theBuffer.format = (myImage->getColorType() == FIC_RGBALPHA) ? TBGRA : TBGR;
#endif
theBuffer.type = TUByte;
break;
}
theBuffer.dataPtr = myImage->accessPixels();
#else
theBuffer.rowAligmentBytes = myImage->getMaxRowAligmentBytes();
theBuffer.format = (myImage->getBytesPerPixel() == 4) ? TBGRA : TBGR;
theBuffer.type = TUByte;
theBuffer.dataPtr = myImage->getData();
#endif
}
IMPLEMENT_STANDARD_HANDLE (Image_PixMap, Standard_Transient)
IMPLEMENT_STANDARD_RTTIEXT(Image_PixMap, Standard_Transient)
// =======================================================================
// function : PixelColor
// function : Image_PixMap
// purpose :
// =======================================================================
Quantity_Color Image_PixMap::PixelColor (const Standard_Integer theX,
const Standard_Integer theY) const
Image_PixMap::Image_PixMap()
: myImgFormat (Image_PixMap::ImgGray),
myIsOwnPointer (true)
{
Quantity_Parameter aDummy;
return PixelColor (theX, theY, aDummy);
memset (&myData, 0, sizeof(myData));
myData.mySizeBPP = 1;
myData.myTopToDown = 1;
setFormat (Image_PixMap::ImgGray);
}
// =======================================================================
// function : ~Image_PixMap
// purpose :
// =======================================================================
Image_PixMap::~Image_PixMap()
{
Clear();
}
Standard_Size Image_PixMap::SizePixelBytes (const Image_PixMap::ImgFormat thePixelFormat)
{
switch (thePixelFormat)
{
case ImgGrayF:
return sizeof(float);
case ImgRGBAF:
case ImgBGRAF:
return sizeof(float) * 4;
case ImgRGBF:
case ImgBGRF:
return sizeof(float) * 3;
case ImgRGBA:
case ImgBGRA:
return 4;
case ImgRGB32:
case ImgBGR32:
return 4;
case ImgRGB:
case ImgBGR:
return 3;
case ImgGray:
default:
return 1;
}
}
// =======================================================================
// function : setFormat
// purpose :
// =======================================================================
void Image_PixMap::setFormat (Image_PixMap::ImgFormat thePixelFormat)
{
myImgFormat = thePixelFormat;
myData.mySizeBPP = SizePixelBytes (myImgFormat);
}
// =======================================================================
// function : setTopDown
// purpose :
// =======================================================================
void Image_PixMap::setTopDown()
{
myData.myTopRowPtr = ((myData.myTopToDown == 1 || myData.myDataPtr == NULL)
? myData.myDataPtr : (myData.myDataPtr + myData.mySizeRowBytes * (myData.mySizeY - 1)));
}
// =======================================================================
// function : InitWrapper
// purpose :
// =======================================================================
bool Image_PixMap::InitWrapper (Image_PixMap::ImgFormat thePixelFormat,
Standard_Byte* theDataPtr,
const Standard_Size theSizeX,
const Standard_Size theSizeY,
const Standard_Size theSizeRowBytes)
{
Clear (thePixelFormat);
if ((theSizeX == 0) || (theSizeY == 0) || (theDataPtr == NULL))
{
return false;
}
myData.mySizeX = theSizeX;
myData.mySizeY = theSizeY;
myData.mySizeRowBytes = (theSizeRowBytes != 0) ? theSizeRowBytes : (theSizeX * myData.mySizeBPP);
myData.myDataPtr = theDataPtr;
myIsOwnPointer = false;
setTopDown();
return true;
}
// =======================================================================
// function : InitTrash
// purpose :
// =======================================================================
bool Image_PixMap::InitTrash (Image_PixMap::ImgFormat thePixelFormat,
const Standard_Size theSizeX,
const Standard_Size theSizeY,
const Standard_Size theSizeRowBytes)
{
Clear (thePixelFormat);
if ((theSizeX == 0) || (theSizeY == 0))
{
return false;
}
myData.mySizeX = theSizeX;
myData.mySizeY = theSizeY;
myData.mySizeRowBytes = myData.mySizeX * myData.mySizeBPP;
if (theSizeRowBytes > myData.mySizeRowBytes)
{
// use argument only if it greater
myData.mySizeRowBytes = theSizeRowBytes;
}
myData.myDataPtr = MemAllocAligned<Standard_Byte*> (SizeBytes());
myIsOwnPointer = true;
setTopDown();
return myData.myDataPtr != NULL;
}
// =======================================================================
// function : InitZero
// purpose :
// =======================================================================
bool Image_PixMap::InitZero (Image_PixMap::ImgFormat thePixelFormat,
const Standard_Size theSizeX,
const Standard_Size theSizeY,
const Standard_Size theSizeRowBytes,
const Standard_Byte theValue)
{
if (!InitTrash (thePixelFormat, theSizeX, theSizeY, theSizeRowBytes))
{
return false;
}
memset (myData.myDataPtr, (int )theValue, SizeBytes());
return true;
}
// =======================================================================
// function : InitCopy
// purpose :
// =======================================================================
bool Image_PixMap::InitCopy (const Image_PixMap& theCopy)
{
if (&theCopy == this)
{
// self-copying disallowed
return false;
}
if (InitTrash (theCopy.myImgFormat, theCopy.myData.mySizeX, theCopy.myData.mySizeY, theCopy.myData.mySizeRowBytes))
{
memcpy (myData.myDataPtr, theCopy.myData.myDataPtr, theCopy.SizeBytes());
return true;
}
return false;
}
// =======================================================================
// function : Clear
// purpose :
// =======================================================================
void Image_PixMap::Clear (Image_PixMap::ImgFormat thePixelFormat)
{
if (myIsOwnPointer && (myData.myDataPtr != NULL))
{
MemFreeAligned (myData.myDataPtr);
}
myData.myDataPtr = myData.myTopRowPtr = NULL;
myIsOwnPointer = true;
myData.mySizeX = myData.mySizeY = myData.mySizeRowBytes = 0;
setFormat (thePixelFormat);
myData.myTopToDown = 1;
}
// =======================================================================
@@ -435,64 +230,130 @@ Quantity_Color Image_PixMap::PixelColor (const Standard_Integer theX,
const Standard_Integer theY,
Quantity_Parameter& theAlpha) const
{
Standard_Integer aScanlineId = myImage->getHeight() - theY - 1;
if (theX < 0 || (unsigned int )theX >= (unsigned int )myImage->getWidth() ||
theY < 0 || (unsigned int )theY >= (unsigned int )myImage->getHeight())
if (IsEmpty() ||
theX < 0 || (Standard_Size )theX >= myData.mySizeX ||
theY < 0 || (Standard_Size )theY >= myData.mySizeY)
{
theAlpha = 0.0; // transparent
return Quantity_Color (0.0, 0.0, 0.0, Quantity_TOC_RGB);
}
#ifdef HAVE_FREEIMAGE
else if (myImage->getImageType() == FIT_BITMAP)
switch (myImgFormat)
{
RGBQUAD aValue; memset (&aValue, 0, sizeof(aValue));
myImage->getPixelColor (theX, aScanlineId, &aValue);
theAlpha = (myImage->getColorType() == FIC_RGBALPHA) ? (Standard_Real (aValue.rgbReserved) / 255.0) : 1.0;
return Quantity_Color (Standard_Real (aValue.rgbRed) / 255.0,
Standard_Real (aValue.rgbGreen) / 255.0,
Standard_Real (aValue.rgbBlue) / 255.0,
Quantity_TOC_RGB);
}
else
{
switch (myImage->getImageType())
case ImgGrayF:
{
case FIT_FLOAT:
{
float* aScanLine = (float* )myImage->getScanLine (aScanlineId);
Quantity_Parameter aValue = Quantity_Parameter (aScanLine[theX]);
theAlpha = 1.0; // opaque
return Quantity_Color (aValue, aValue, aValue, Quantity_TOC_RGB);
}
case FIT_RGBF:
{
FIRGBF* aScanLine = (FIRGBF* )myImage->getScanLine (aScanlineId);
FIRGBF* aPixel = &aScanLine[theX];
theAlpha = 1.0; // opaque
return Quantity_Color (Quantity_Parameter (aPixel->red),
Quantity_Parameter (aPixel->green),
Quantity_Parameter (aPixel->blue),
Quantity_TOC_RGB);
}
case FIT_RGBAF:
{
FIRGBAF* aScanLine = (FIRGBAF* )myImage->getScanLine (aScanlineId);
FIRGBAF* aPixel = &aScanLine[theX];
theAlpha = aPixel->alpha;
return Quantity_Color (Quantity_Parameter (aPixel->red),
Quantity_Parameter (aPixel->green),
Quantity_Parameter (aPixel->blue),
Quantity_TOC_RGB);
}
default:
{
// not supported image type
theAlpha = 0.0; // transparent
return Quantity_Color (0.0, 0.0, 0.0, Quantity_TOC_RGB);
}
const Standard_ShortReal& aPixel = Value<Standard_ShortReal> (theY, theX);
theAlpha = 1.0; // opaque
return Quantity_Color (Quantity_Parameter (Standard_Real (aPixel)),
Quantity_Parameter (Standard_Real (aPixel)),
Quantity_Parameter (Standard_Real (aPixel)),
Quantity_TOC_RGB);
break;
}
case ImgRGBAF:
{
const Image_ColorRGBAF& aPixel = Value<Image_ColorRGBAF> (theY, theX);
theAlpha = aPixel.a();
return Quantity_Color (Quantity_Parameter (aPixel.r()),
Quantity_Parameter (aPixel.g()),
Quantity_Parameter (aPixel.b()),
Quantity_TOC_RGB);
}
case ImgBGRAF:
{
const Image_ColorBGRAF& aPixel = Value<Image_ColorBGRAF> (theY, theX);
theAlpha = aPixel.a();
return Quantity_Color (Quantity_Parameter (aPixel.r()),
Quantity_Parameter (aPixel.g()),
Quantity_Parameter (aPixel.b()),
Quantity_TOC_RGB);
}
case ImgRGBF:
{
const Image_ColorRGBF& aPixel = Value<Image_ColorRGBF> (theY, theX);
theAlpha = 1.0; // opaque
return Quantity_Color (Quantity_Parameter (aPixel.r()),
Quantity_Parameter (aPixel.g()),
Quantity_Parameter (aPixel.b()),
Quantity_TOC_RGB);
}
case ImgBGRF:
{
const Image_ColorBGRF& aPixel = Value<Image_ColorBGRF> (theY, theX);
theAlpha = 1.0; // opaque
return Quantity_Color (Quantity_Parameter (aPixel.r()),
Quantity_Parameter (aPixel.g()),
Quantity_Parameter (aPixel.b()),
Quantity_TOC_RGB);
}
case ImgRGBA:
{
const Image_ColorRGBA& aPixel = Value<Image_ColorRGBA> (theY, theX);
theAlpha = Standard_Real (aPixel.a()) / 255.0;
return Quantity_Color (Quantity_Parameter (Standard_Real (aPixel.r()) / 255.0),
Quantity_Parameter (Standard_Real (aPixel.g()) / 255.0),
Quantity_Parameter (Standard_Real (aPixel.b()) / 255.0),
Quantity_TOC_RGB);
}
case ImgBGRA:
{
const Image_ColorBGRA& aPixel = Value<Image_ColorBGRA> (theY, theX);
theAlpha = Standard_Real (aPixel.a()) / 255.0;
return Quantity_Color (Quantity_Parameter (Standard_Real (aPixel.r()) / 255.0),
Quantity_Parameter (Standard_Real (aPixel.g()) / 255.0),
Quantity_Parameter (Standard_Real (aPixel.b()) / 255.0),
Quantity_TOC_RGB);
}
case ImgRGB32:
{
const Image_ColorRGB32& aPixel = Value<Image_ColorRGB32> (theY, theX);
theAlpha = 1.0; // opaque
return Quantity_Color (Quantity_Parameter (Standard_Real (aPixel.r()) / 255.0),
Quantity_Parameter (Standard_Real (aPixel.g()) / 255.0),
Quantity_Parameter (Standard_Real (aPixel.b()) / 255.0),
Quantity_TOC_RGB);
}
case ImgBGR32:
{
const Image_ColorBGR32& aPixel = Value<Image_ColorBGR32> (theY, theX);
theAlpha = 1.0; // opaque
return Quantity_Color (Quantity_Parameter (Standard_Real (aPixel.r()) / 255.0),
Quantity_Parameter (Standard_Real (aPixel.g()) / 255.0),
Quantity_Parameter (Standard_Real (aPixel.b()) / 255.0),
Quantity_TOC_RGB);
}
case ImgRGB:
{
const Image_ColorRGB& aPixel = Value<Image_ColorRGB> (theY, theX);
theAlpha = 1.0; // opaque
return Quantity_Color (Quantity_Parameter (Standard_Real (aPixel.r()) / 255.0),
Quantity_Parameter (Standard_Real (aPixel.g()) / 255.0),
Quantity_Parameter (Standard_Real (aPixel.b()) / 255.0),
Quantity_TOC_RGB);
}
case ImgBGR:
{
const Image_ColorBGR& aPixel = Value<Image_ColorBGR> (theY, theX);
theAlpha = 1.0; // opaque
return Quantity_Color (Quantity_Parameter (Standard_Real (aPixel.r()) / 255.0),
Quantity_Parameter (Standard_Real (aPixel.g()) / 255.0),
Quantity_Parameter (Standard_Real (aPixel.b()) / 255.0),
Quantity_TOC_RGB);
}
case ImgGray:
{
const Standard_Byte& aPixel = Value<Standard_Byte> (theY, theX);
theAlpha = 1.0; // opaque
return Quantity_Color (Quantity_Parameter (Standard_Real (aPixel) / 255.0),
Quantity_Parameter (Standard_Real (aPixel) / 255.0),
Quantity_Parameter (Standard_Real (aPixel) / 255.0),
Quantity_TOC_RGB);
}
default:
{
// not supported image type
theAlpha = 0.0; // transparent
return Quantity_Color (0.0, 0.0, 0.0, Quantity_TOC_RGB);
}
}
#else
return myImage->getPixelColor (theX, aScanlineId, theAlpha);
#endif
}

294
src/Image/Image_PixMap.hxx Normal file
View File

@@ -0,0 +1,294 @@
// Created on: 2012-07-18
// Created by: Kirill GAVRILOV
// Copyright (c) 2012 OPEN CASCADE SAS
//
// The content of this file is subject to the Open CASCADE Technology Public
// License Version 6.5 (the "License"). You may not use the content of this file
// except in compliance with the License. Please obtain a copy of the License
// at http://www.opencascade.org and read it completely before using this file.
//
// The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
// main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
//
// The Original Code and all software distributed under the License is
// distributed on an "AS IS" basis, without warranty of any kind, and the
// Initial Developer hereby disclaims all such warranties, including without
// limitation, any warranties of merchantability, fitness for a particular
// purpose or non-infringement. Please see the License for the specific terms
// and conditions governing the rights and limitations under the License.
#ifndef _Image_PixMap_H__
#define _Image_PixMap_H__
#include <Image_PixMapData.hxx>
#include <Standard_Transient.hxx>
#include <Quantity_Color.hxx>
//! Class represents packed image plane.
class Image_PixMap : public Standard_Transient
{
public:
//! This enumeration define packed image plane formats
typedef enum tagFormat {
ImgUNKNOWN = 0, //!< unsupported or unknown format
ImgGray = 1, //!< 1 byte per pixel
ImgRGB, //!< 3 bytes packed RGB image plane
ImgBGR, //!< same as RGB but with different components order
ImgRGB32, //!< 4 bytes packed RGB image plane (1 extra byte for alignment, may have undefined value)
ImgBGR32, //!< same as RGB but with different components order
ImgRGBA, //!< 4 bytes packed RGBA image plane
ImgBGRA, //!< same as RGBA but with different components order
ImgGrayF, //!< 1 float (4-bytes) per pixel (1-component plane)
ImgRGBF, //!< 3 floats (12-bytes) RGB image plane
ImgBGRF, //!< same as RGBF but with different components order
ImgRGBAF, //!< 4 floats (16-bytes) RGBA image plane
ImgBGRAF, //!< same as RGBAF but with different components order
} ImgFormat;
//! Determine Big-Endian at runtime
static inline bool IsBigEndianHost()
{
union { int myInt; char myChar[sizeof(int)]; } aUnion;
aUnion.myInt = 1;
return !aUnion.myChar[0];
}
public: // high-level API
inline ImgFormat Format() const
{
return myImgFormat;
}
//! @return image width in pixels
inline Standard_Size Width() const
{
return myData.mySizeX;
}
//! @return image height in pixels
inline Standard_Size Height() const
{
return myData.mySizeY;
}
//! @return image width in pixels
inline Standard_Size SizeX() const
{
return myData.mySizeX;
}
//! @return image height in pixels
inline Standard_Size SizeY() const
{
return myData.mySizeY;
}
//! @return width / height.
inline Standard_Real Ratio() const
{
return (myData.mySizeY > 0) ? (Standard_Real(myData.mySizeX) / Standard_Real(myData.mySizeY)) : 1.0;
}
//! @return true if data is NULL.
bool IsEmpty() const
{
return myData.myDataPtr == NULL;
}
//! Empty constructor. Initialize the NULL image plane.
Standard_EXPORT Image_PixMap();
//! Destructor
Standard_EXPORT virtual ~Image_PixMap();
//! Returns the pixel color. This function is relatively slow.
//! @param theX - column index from left
//! @param theY - row index from top
//! @return the pixel color
inline Quantity_Color PixelColor (const Standard_Integer theX,
const Standard_Integer theY) const
{
Quantity_Parameter aDummy;
return PixelColor (theX, theY, aDummy);
}
//! Returns the pixel color. This function is relatively slow.
//! theAlpha argument is set to color intensity (0 - transparent, 1 - opaque)
Standard_EXPORT Quantity_Color PixelColor (const Standard_Integer theX,
const Standard_Integer theY,
Quantity_Parameter& theAlpha) const;
//! Initialize image plane as wrapper over alien data.
//! Data will not be copied! Notice that caller should ensure
//! that data pointer will not be released during this wrapper lifetime.
//! You may call InitCopy() to perform data copying.
Standard_EXPORT virtual bool InitWrapper (ImgFormat thePixelFormat,
Standard_Byte* theDataPtr,
const Standard_Size theSizeX,
const Standard_Size theSizeY,
const Standard_Size theSizeRowBytes = 0);
//! Initialize image plane with required dimensions.
//! Memory will be left uninitialized (performance trick).
Standard_EXPORT virtual bool InitTrash (ImgFormat thePixelFormat,
const Standard_Size theSizeX,
const Standard_Size theSizeY,
const Standard_Size theSizeRowBytes = 0);
//! Initialize by copying data.
//! If you want to copy alien data you should create wrapper using InitWrapper() before.
Standard_EXPORT virtual bool InitCopy (const Image_PixMap& theCopy);
//! Initialize image plane with required dimensions.
//! Buffer will be zeroed (black color for most formats).
Standard_EXPORT bool InitZero (ImgFormat thePixelFormat,
const Standard_Size theSizeX,
const Standard_Size theSizeY,
const Standard_Size theSizeRowBytes = 0,
const Standard_Byte theValue = 0);
//! Method correctly deallocate internal buffer.
Standard_EXPORT virtual void Clear (ImgFormat thePixelFormat = ImgGray);
public: // low-level API for batch-processing (pixels reading / comparison / modification)
//! Returns true if image data stored from Top to the Down (default).
//! Some external APIs can return bottom-up data instead
//! (topmost scanlines starts from the bottom in memory).
//! Notice that access methods within this class automatically
//! convert input row-index to apply this flag!
//! You should use this flag only if interconnect with alien APIs and buffers.
//! @return true if image data is top-down.
inline bool IsTopDown() const
{
return myData.myTopToDown == 1;
}
//! Setup scanlines order in memory - top-down or bottom-up.
//! Drawers should explicitly specify this value if current state IsTopDown() was ignored!
//! @param theIsTopDown - top-down flag.
inline void SetTopDown (bool theIsTopDown)
{
myData.myTopToDown = (theIsTopDown ? 1 : Standard_Size(-1));
setTopDown();
}
//! Returns +1 if scanlines ordered in Top->Down order in memory and -1 otherwise.
//! @return scanline increment for Top->Down iteration
inline Standard_Size TopDownInc() const
{
return myData.myTopToDown;
}
//! @return data pointer for low-level operations (copying entire buffer, parsing with extra tools etc.).
inline const Standard_Byte* Data() const
{
return myData.myDataPtr;
}
//! @return data pointer for low-level operations (copying entire buffer, parsing with extra tools etc.).
inline Standard_Byte* ChangeData()
{
return myData.myDataPtr;
}
//! @return data pointer to requested row (first column).
inline const Standard_Byte* Row (const Standard_Size theRow) const
{
return myData.Row (theRow);
}
//! @return data pointer to requested row (first column).
inline Standard_Byte* ChangeRow (const Standard_Size theRow)
{
return myData.ChangeRow (theRow);
}
//! @return bytes reserved for one pixel (may include extra bytes for alignment).
inline Standard_Size SizePixelBytes() const
{
return myData.mySizeBPP;
}
//! @return bytes reserved for one pixel (may include extra bytes for alignment).
static Standard_Size SizePixelBytes (const Image_PixMap::ImgFormat thePixelFormat);
//! @return bytes reserved per row.
//! Could be larger than needed to store packed row (extra bytes for alignment etc.).
inline Standard_Size SizeRowBytes() const
{
return myData.mySizeRowBytes;
}
//! @return the extra bytes in the row.
inline Standard_Size RowExtraBytes() const
{
return myData.mySizeRowBytes - myData.mySizeX * myData.mySizeBPP;
}
//! Compute the maximal row alignment for current row size.
//! @return maximal row alignment in bytes (up to 16 bytes).
inline Standard_Size MaxRowAligmentBytes() const
{
return myData.MaxRowAligmentBytes();
}
inline Standard_Size SizeBytes() const
{
return myData.SizeBytes();
}
//! Access image buffer for write/read operations with specified color type.
template <typename ColorType_t>
inline Image_PixMapData<ColorType_t>& EditData()
{
return *(Image_PixMapData<ColorType_t>* )&myData;
}
//! Access image buffer for read operations with specified color type.
template <typename ColorType_t>
inline const Image_PixMapData<ColorType_t>& ReadData() const
{
return *(Image_PixMapData<ColorType_t>* )&myData;
}
//! Access image pixel with specified color type.
template <typename ColorType_t>
inline const ColorType_t& Value (const Standard_Size theRow,
const Standard_Size theCol) const
{
return ((Image_PixMapData<ColorType_t>* )&myData)->Value (theRow, theCol);
}
protected:
//! Setup pixel format
Standard_EXPORT void setFormat (ImgFormat thePixelFormat);
//! Auxiliary method to setup myTopRowPtr
Standard_EXPORT void setTopDown();
protected:
Image_PixMapData<Standard_Byte> myData;
ImgFormat myImgFormat; //!< pixel format
bool myIsOwnPointer; //!< if data was allocated by this class - flag is true
private:
//! Copying allowed only within Handles
Image_PixMap (const Image_PixMap& );
Image_PixMap& operator= (const Image_PixMap& );
public:
DEFINE_STANDARD_RTTI(Image_PixMap) // Type definition
};
DEFINE_STANDARD_HANDLE(Image_PixMap, Standard_Transient)
#endif // _Image_PixMap_H__

View File

@@ -0,0 +1,113 @@
// Created on: 2012-07-18
// Created by: Kirill GAVRILOV
// Copyright (c) 2012 OPEN CASCADE SAS
//
// The content of this file is subject to the Open CASCADE Technology Public
// License Version 6.5 (the "License"). You may not use the content of this file
// except in compliance with the License. Please obtain a copy of the License
// at http://www.opencascade.org and read it completely before using this file.
//
// The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
// main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
//
// The Original Code and all software distributed under the License is
// distributed on an "AS IS" basis, without warranty of any kind, and the
// Initial Developer hereby disclaims all such warranties, including without
// limitation, any warranties of merchantability, fitness for a particular
// purpose or non-infringement. Please see the License for the specific terms
// and conditions governing the rights and limitations under the License.
#ifndef _Image_PixMapData_H__
#define _Image_PixMapData_H__
#include <Image_Color.hxx>
//! POD template structure to access image buffer
template<typename ColorType_t>
struct Image_PixMapData
{
//! @return data pointer for low-level operations (copying entire buffer, parsing with extra tools etc.).
inline const ColorType_t* Data() const
{
return (const ColorType_t* )myDataPtr;
}
//! @return data pointer for low-level operations (copying entire buffer, parsing with extra tools etc.).
inline ColorType_t* ChangeData()
{
return (ColorType_t* )myDataPtr;
}
//! @return data pointer to requested row (first column).
inline const ColorType_t* Row (const Standard_Size theRow) const
{
return (ColorType_t* )(myTopRowPtr + mySizeRowBytes * theRow * myTopToDown);
}
//! @return data pointer to requested row (first column).
inline ColorType_t* ChangeRow (const Standard_Size theRow)
{
return (ColorType_t* )(myTopRowPtr + mySizeRowBytes * theRow * myTopToDown);
}
//! @return data pointer to requested position.
inline const ColorType_t& Value (const Standard_Size theRow,
const Standard_Size theCol) const
{
return *(const ColorType_t* )(myTopRowPtr + mySizeRowBytes * theRow * myTopToDown + mySizeBPP * theCol);
}
//! @return data pointer to requested position.
inline ColorType_t& ChangeValue (const Standard_Size theRow,
const Standard_Size theCol)
{
return *(ColorType_t* )(myTopRowPtr + mySizeRowBytes * theRow * myTopToDown + mySizeBPP * theCol);
}
//! Compute the maximal row alignment for current row size.
//! @return maximal row alignment in bytes (up to 16 bytes).
inline Standard_Size MaxRowAligmentBytes() const
{
Standard_Size anAlignment = 2;
for (; anAlignment <= 16; anAlignment <<= 1)
{
if ((mySizeRowBytes % anAlignment) != 0 || (Standard_Size(myDataPtr) % anAlignment) != 0)
{
return (anAlignment >> 1);
}
}
return anAlignment;
}
//! @return bytes allocated for the whole image plane.
inline Standard_Size SizeBytes() const
{
return mySizeRowBytes * mySizeY;
}
//! @return image width in pixels
inline Standard_Size SizeX() const
{
return mySizeX;
}
//! @return image height in pixels
inline Standard_Size SizeY() const
{
return mySizeY;
}
public:
Standard_Byte* myDataPtr; //!< pointer to the data
Standard_Byte* myTopRowPtr; //!< pointer to the topmost row (depending on scanlines order in memory)
Standard_Size mySizeBPP; //!< bytes per pixel
Standard_Size mySizeX; //!< width in pixels
Standard_Size mySizeY; //!< height in pixels
Standard_Size mySizeRowBytes; //!< number of bytes per line (in most cases equal to 3 * sizeX)
Standard_Size myTopToDown; //!< image scanlines direction in memory from Top to the Down
};
#endif // _Image_PixMapData_H__