From e958a649c6af1231315e462d8bdbfdf4c78229a4 Mon Sep 17 00:00:00 2001 From: kgv Date: Mon, 3 Jul 2017 12:27:08 +0300 Subject: [PATCH] 0028876: Tests, Image_Diff - the image difference is unavailable for test case bugs vis bug28205_1 Quantity_ColorRGBA - added method SetValues(). Image_PixMap::PixelColor() now returns Quantity_ColorRGBA instead of Quantity_Color. Image_PixMap::SetPixelColor() now takes Quantity_ColorRGBA instead of NCollection_Vec4. Image_Diff has been improved to support Image_Format_Gray. Image_Diff::SaveDiffImage() now saves image difference in Image_Format_Gray format to reduce size of image file. Image_Diff now uses TColStd_HPackedMapOfInteger instead of TColStd_MapOfInteger with manual memory allocation. --- dox/dev_guides/upgrade/upgrade.md | 1 + src/Graphic3d/Graphic3d_MarkerImage.cxx | 21 +- src/Image/Image_AlienPixMap.cxx | 10 +- src/Image/Image_Diff.cxx | 507 ++++++++++--------- src/Image/Image_Diff.hxx | 41 +- src/Image/Image_PixMap.cxx | 201 +++++--- src/Image/Image_PixMap.hxx | 39 +- src/NCollection/NCollection_Vec2.hxx | 8 + src/NCollection/NCollection_Vec3.hxx | 10 + src/NCollection/NCollection_Vec4.hxx | 12 + src/Quantity/Quantity_ColorRGBA.hxx | 30 +- src/StdSelect/StdSelect_ViewerSelector3d.cxx | 8 +- src/ViewerTest/ViewerTest_ViewerCommands.cxx | 15 +- 13 files changed, 510 insertions(+), 393 deletions(-) diff --git a/dox/dev_guides/upgrade/upgrade.md b/dox/dev_guides/upgrade/upgrade.md index ce24c92cdd..430046770a 100644 --- a/dox/dev_guides/upgrade/upgrade.md +++ b/dox/dev_guides/upgrade/upgrade.md @@ -1252,6 +1252,7 @@ In most cases this change should be transparent, however applications implementi * Enumeration *Image_PixMap::ImgFormat*, previously declared as nested enumeration within class *Image_PixMap*, has been moved to global namespace as *Image_Format* following OCCT coding rules. The enumeration values have suffix Image_Format_ and preserve previous name scheme for easy renaming of old values - e.g. Image_PixMap::ImgGray become Image_Format_Gray. Old definitions are preserved as depreacated aliases to the new ones; +* Methods *Image_PixMap::PixelColor()* and *Image_PixMap::SetPixelColor()* now take/return Quantity_ColorRGBA instead of Quantity_Color/NCollection_Vec4. * The method BOPAlgo_Builder::Origins() returns BOPCol_DataMapOfShapeListOfShape instead of BOPCol_DataMapOfShapeShape. * The methods BOPDS_DS::IsToSort(const Handle(BOPDS_CommonBlock)&, Standard_Integer&) and BOPDS_DS::SortPaveBlocks(const Handle(BOPDS_CommonBlock)&) have been removed. The sorting is now performed during the addition of the Pave Blocks into Common Block. * The methods BOPAlgo_Tools::MakeBlocks() and BOPAlgo_Tools::MakeBlocksCnx() have been replaced with the single template method BOPAlgo_Tools::MakeBlocks(). The chains of connected elements are now stored into the list of list instead of data map. diff --git a/src/Graphic3d/Graphic3d_MarkerImage.cxx b/src/Graphic3d/Graphic3d_MarkerImage.cxx index 8063a1af68..a8b51dca6c 100755 --- a/src/Graphic3d/Graphic3d_MarkerImage.cxx +++ b/src/Graphic3d/Graphic3d_MarkerImage.cxx @@ -90,21 +90,15 @@ Handle(TColStd_HArray1OfByte) Graphic3d_MarkerImage::GetBitMapArray (const Stand { for (Standard_Integer aColumn = 0; aColumn < aWidth; aColumn++) { - Standard_Real anAlphaValue; - Quantity_Color aColor = myImage->PixelColor (aColumn, aRow, anAlphaValue); + const Quantity_ColorRGBA aColor = myImage->PixelColor (aColumn, aRow); Standard_Boolean aBitOn = Standard_False; - - if (myImage->Format() == Image_Format_Alpha) + if (myImage->Format() == Image_Format_Gray) { - aBitOn = anAlphaValue > theAlphaValue; + aBitOn = aColor.GetRGB().Red() > theAlphaValue; } - else if (myImage->Format() == Image_Format_Gray) + else //if (myImage->Format() == Image_Format_Alpha) { - aBitOn = aColor.Red() > theAlphaValue; - } - else - { - aBitOn = anAlphaValue > theAlphaValue; + aBitOn = aColor.Alpha() > theAlphaValue; } Standard_Integer anIndex = aNumOfBytesInRow * aRow + aColumn / 8; @@ -179,14 +173,13 @@ const Handle(Image_PixMap)& Graphic3d_MarkerImage::GetImageAlpha() myImageAlpha = new Image_PixMap(); myImageAlpha->InitZero (Image_Format_Alpha, myImage->Width(), myImage->Height()); myImageAlpha->SetTopDown (Standard_False); - Standard_Real anAlpha; for (Standard_Size aRowIter = 0; aRowIter < myImage->Height(); aRowIter++) { Standard_Byte* anImageRow = myImageAlpha->ChangeRow (aRowIter); for (Standard_Size aColumnIter = 0; aColumnIter < myImage->Width(); aColumnIter++) { - myImage->PixelColor ((Standard_Integer)aColumnIter, (Standard_Integer)aRowIter, anAlpha); - anImageRow[aColumnIter] = Standard_Byte (255.0 * anAlpha); + const Quantity_ColorRGBA aColor = myImage->PixelColor ((Standard_Integer)aColumnIter, (Standard_Integer)aRowIter); + anImageRow[aColumnIter] = Standard_Byte (255.0 * aColor.Alpha()); } } } diff --git a/src/Image/Image_AlienPixMap.cxx b/src/Image/Image_AlienPixMap.cxx index cb795aca7f..bf00775c22 100644 --- a/src/Image/Image_AlienPixMap.cxx +++ b/src/Image/Image_AlienPixMap.cxx @@ -349,18 +349,16 @@ bool Image_AlienPixMap::savePPM (const TCollection_AsciiString& theFileName) con fprintf (aFile, "# Image stored by OpenCASCADE framework in linear RGB colorspace\n"); // Write pixel data - Quantity_Color aColor; - Standard_Real aDummy; Standard_Byte aByte; for (Standard_Size aRow = 0; aRow < SizeY(); ++aRow) { for (Standard_Size aCol = 0; aCol < SizeX(); ++aCol) { // extremely SLOW but universal (implemented for all supported pixel formats) - aColor = PixelColor ((Standard_Integer )aCol, (Standard_Integer )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); + const Quantity_ColorRGBA aColor = PixelColor ((Standard_Integer )aCol, (Standard_Integer )aRow); + aByte = Standard_Byte(aColor.GetRGB().Red() * 255.0); fwrite (&aByte, 1, 1, aFile); + aByte = Standard_Byte(aColor.GetRGB().Green() * 255.0); fwrite (&aByte, 1, 1, aFile); + aByte = Standard_Byte(aColor.GetRGB().Blue() * 255.0); fwrite (&aByte, 1, 1, aFile); } } diff --git a/src/Image/Image_Diff.cxx b/src/Image/Image_Diff.cxx index 6d6f0fb315..a1c51833be 100644 --- a/src/Image/Image_Diff.cxx +++ b/src/Image/Image_Diff.cxx @@ -14,120 +14,88 @@ // commercial license or contractual agreement. #include -#include -#include +#include +#include +#include +#include #include - IMPLEMENT_STANDARD_RTTIEXT(Image_Diff,Standard_Transient) -//! POD structure for packed RGB color value (3 bytes) -struct Image_ColorXXX24 -{ - Standard_Byte v[3]; - typedef Standard_Byte ComponentType_t; //!< Component type - static Standard_Integer Length() { return 3; } //!< Returns the number of components -}; - -inline Image_ColorXXX24 operator- (const Image_ColorXXX24& theA, - const Image_ColorXXX24& theB) -{ - return Image_ColorSub3 (theA, theB); -} - -//! Dot squared for difference of two colors -inline Standard_Integer dotSquared (const Image_ColorXXX24& theColor) -{ - // explicitly convert to integer - const Standard_Integer r = theColor.v[0]; - const Standard_Integer g = theColor.v[1]; - const Standard_Integer b = theColor.v[2]; - return r * r + g * g + b * b; -} - -//! @return true if pixel is black -inline bool isBlack (const Image_ColorXXX24& theColor) -{ - return theColor.v[0] == 0 - && theColor.v[1] == 0 - && theColor.v[2] == 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 { - inline ptrdiff_t getAbs (const ptrdiff_t theValue) + //! POD structure for packed RGB color value (3 bytes) + struct Image_ColorXXX24 { - return theValue >= 0 ? theValue : -theValue; - } - - static const Standard_Size NEIGHBOR_PIXELS_NB = 8; - static 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_PixMap& theData, - const Standard_Size theRowCenter, - const Standard_Size theColCenter) const - { - return ::isBlack (theData.Value (theRowCenter + Standard_Size(row_inc), - theColCenter + Standard_Size(col_inc))); - } - - inline bool isValid (const Image_PixMap& theData, - const Standard_Size theRowCenter, - const Standard_Size theColCenter) const - { - const Standard_Size aRow = theRowCenter + Standard_Size(row_inc); - const Standard_Size aCol = theColCenter + Standard_Size(col_inc); - return aRow < theData.SizeX() // this unsigned math checks Standard_Size(-1) at-once - && aCol < theData.SizeY(); - } - } - const NEIGHBOR_PIXELS[NEIGHBOR_PIXELS_NB] = - { - {-1, -1}, {-1, 0}, {-1, 1}, - { 0, -1}, { 0, 1}, - { 1, -1}, { 1, 0}, { 1, 1} + Standard_Byte v[3]; + typedef Standard_Byte ComponentType_t; //!< Component type + static Standard_Integer Length() { return 3; } //!< Returns the number of components }; - static bool isSupportedFormat (const Image_Format theFormat) + static Image_ColorXXX24 operator- (const Image_ColorXXX24& theA, + const Image_ColorXXX24& theB) { - return theFormat == Image_Format_RGB - || theFormat == Image_Format_BGR - || theFormat == Image_Format_RGB32 - || theFormat == Image_Format_BGR32 - || theFormat == Image_Format_RGBA - || theFormat == Image_Format_BGRA; + return Image_ColorSub3 (theA, theB); } -} // anonymous namespace + //! Dot squared for difference of two colors + static Standard_Integer dotSquared (const Image_ColorXXX24& theColor) + { + // explicitly convert to integer + const Standard_Integer r = theColor.v[0]; + const Standard_Integer g = theColor.v[1]; + const Standard_Integer b = theColor.v[2]; + return r * r + g * g + b * b; + } + + //! Number of neighbor pixels. + static const Standard_Size Image_Diff_NbOfNeighborPixels = 8; + + //! List of neighbor pixels (offsets). + static const int Image_Diff_NEIGHBOR_PIXELS[Image_Diff_NbOfNeighborPixels][2] = + { + {-1, -1}, {0, -1}, {1, -1}, + {-1, 0}, {1, 0}, + {-1, 1}, {0, 1}, {1, 1} + }; + + //! @return true if pixel is black + static bool isBlackPixel (const Image_PixMap& theData, Standard_Size theY, Standard_Size theX) + { + switch (theData.Format()) + { + case Image_Format_Gray: + case Image_Format_Alpha: + { + return theData.Value (theY, theX) == 0; + } + case Image_Format_RGB: + case Image_Format_BGR: + case Image_Format_RGB32: + case Image_Format_BGR32: + case Image_Format_RGBA: + case Image_Format_BGRA: + { + const Image_ColorXXX24& aColor = theData.Value (theY, theX); + return aColor.v[0] == 0 + && aColor.v[1] == 0 + && aColor.v[2] == 0; + } + default: + { + const Quantity_ColorRGBA aPixelRgba = theData.PixelColor ((int)theY, (int)theX); + const NCollection_Vec4& aPixel = aPixelRgba; + return aPixel.r() == 0.0f + && aPixel.g() == 0.0f + && aPixel.b() == 0.0f; + } + } + } + +} // ======================================================================= // function : Image_Diff @@ -167,56 +135,26 @@ Standard_Boolean Image_Diff::Init (const Handle(Image_PixMap)& theImageRef, || theImageRef->SizeY() != theImageNew->SizeY() || theImageRef->Format() != theImageNew->Format()) { -#ifdef OCCT_DEBUG - std::cerr << "Images has different format or dimensions\n"; -#endif - return Standard_False; - } - else if (!isSupportedFormat (theImageRef->Format())) - { -#ifdef OCCT_DEBUG - std::cerr << "Images has unsupported pixel format\n"; -#endif + Message::DefaultMessenger()->Send ("Error: Images have different format or dimensions", Message_Fail); return Standard_False; } else if (theImageRef->SizeX() >= 0xFFFF || theImageRef->SizeY() >= 0xFFFF) { -#ifdef OCCT_DEBUG - std::cerr << "Image too large\n"; -#endif + Message::DefaultMessenger()->Send ("Error: Images are too large", Message_Fail); return Standard_False; } myImageRef = theImageRef; myImageNew = theImageNew; - if (theToBlackWhite) { - // Convert the images to white/black - const Image_ColorXXX24 aWhite = {{255, 255, 255}}; - for (Standard_Size aRow = 0; aRow < myImageRef->SizeY(); ++aRow) - { - for (Standard_Size aCol = 0; aCol < myImageRef->SizeX(); ++aCol) - { - Image_ColorXXX24& aPixel1 = myImageRef->ChangeValue (aRow, aCol); - if (!isBlack (aPixel1)) - { - aPixel1 = aWhite; - } - Image_ColorXXX24& aPixel2 = myImageNew->ChangeValue (aRow, aCol); - if (!isBlack (aPixel2)) - { - aPixel2 = aWhite; - } - } - } + Image_PixMap::ToBlackWhite (*myImageRef); + Image_PixMap::ToBlackWhite (*myImageNew); } - return Standard_True; } - // ======================================================================= // function : Init // purpose : @@ -230,50 +168,12 @@ Standard_Boolean Image_Diff::Init (const TCollection_AsciiString& theImgPathRef, if (!anImgRef->Load (theImgPathRef) || !anImgNew->Load (theImgPathNew)) { -#ifdef OCCT_DEBUG - std::cerr << "Failed to load image(s) file(s)\n"; -#endif + Message::DefaultMessenger()->Send ("Error: Failed to load image(s) file(s)", Message_Fail); 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 : @@ -289,38 +189,94 @@ Standard_Integer Image_Diff::Compare() return -1; } - // first check if images are exactly teh same - if (! memcmp (myImageNew->Data(), myImageRef->Data(), myImageRef->SizeBytes())) + // first check if images are exactly the same + if (myImageNew->SizeBytes() == myImageRef->SizeBytes() + && memcmp (myImageNew->Data(), myImageRef->Data(), myImageRef->SizeBytes()) == 0) { return 0; } - // Tolerance of comparison operation for color - // Maximum difference between colors (white - black) = 100% - Image_ColorXXX24 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 - - // compare colors of each pixel - for (Standard_Size aRow = 0; aRow < myImageRef->SizeY(); ++aRow) + switch (myImageRef->Format()) { - for (Standard_Size aCol = 0; aCol < myImageRef->SizeX(); ++aCol) + case Image_Format_Gray: + case Image_Format_Alpha: { - aDiff = myImageNew->Value (aRow, aCol) - myImageRef->Value (aRow, aCol); - if (dotSquared (aDiff) > aDiffThreshold) + // Tolerance of comparison operation for color + Standard_Integer aDiff = 255; + const Standard_Real aMaxDiffColor = aDiff * aDiff; + const Standard_Integer aDiffThreshold = Standard_Integer(aMaxDiffColor * myColorTolerance); + for (Standard_Size aRow = 0; aRow < myImageRef->SizeY(); ++aRow) { - const Standard_Size aValue = pixel2Int (aRow, aCol); - myDiffPixels.Append (aValue); - ++aNbDiffColors; + for (Standard_Size aCol = 0; aCol < myImageRef->SizeX(); ++aCol) + { + aDiff = Standard_Integer(myImageNew->Value (aRow, aCol)) - Standard_Integer(myImageRef->Value (aRow, aCol)); + if (aDiff * aDiff > aDiffThreshold) + { + myDiffPixels.Append (PackXY ((uint16_t)aCol, (uint16_t)aRow)); + ++aNbDiffColors; + } + } } + break; + } + case Image_Format_RGB: + case Image_Format_BGR: + case Image_Format_RGB32: + case Image_Format_BGR32: + case Image_Format_RGBA: + case Image_Format_BGRA: + { + // Tolerance of comparison operation for color + // Maximum difference between colors (white - black) = 100% + Image_ColorXXX24 aDiff = {{255, 255, 255}}; + const Standard_Real aMaxDiffColor = dotSquared (aDiff); + const Standard_Integer aDiffThreshold = Standard_Integer(aMaxDiffColor * myColorTolerance); + + // we don't care about RGB/BGR/RGBA/BGRA/RGB32/BGR32 differences + // because we just compute summ of r g b components + for (Standard_Size aRow = 0; aRow < myImageRef->SizeY(); ++aRow) + { + for (Standard_Size aCol = 0; aCol < myImageRef->SizeX(); ++aCol) + { + aDiff = myImageNew->Value (aRow, aCol) - myImageRef->Value (aRow, aCol); + if (dotSquared (aDiff) > aDiffThreshold) + { + myDiffPixels.Append (PackXY ((uint16_t)aCol, (uint16_t)aRow)); + ++aNbDiffColors; + } + } + } + break; + } + default: + { + // Tolerance of comparison operation for color + // Maximum difference between colors (white - black) = 100% + NCollection_Vec3 aDiff (1.0f, 1.0f, 1.0f); + const Standard_Real aMaxDiffColor = aDiff.SquareModulus(); + const Standard_Integer aDiffThreshold = Standard_Integer(aMaxDiffColor * myColorTolerance); + for (Standard_Size aRow = 0; aRow < myImageRef->SizeY(); ++aRow) + { + for (Standard_Size aCol = 0; aCol < myImageRef->SizeX(); ++aCol) + { + const Quantity_ColorRGBA aPixel1Rgba = myImageRef->PixelColor (Standard_Integer(aCol), Standard_Integer(aRow)); + const Quantity_ColorRGBA aPixel2Rgba = myImageNew->PixelColor (Standard_Integer(aCol), Standard_Integer(aRow)); + const NCollection_Vec3& aPixel1 = aPixel1Rgba.GetRGB(); + const NCollection_Vec3& aPixel2 = aPixel2Rgba.GetRGB(); + aDiff = aPixel2 - aPixel1; + if (aDiff.SquareModulus() > aDiffThreshold) + { + myDiffPixels.Append (PackXY ((uint16_t)aCol, (uint16_t)aRow)); + ++aNbDiffColors; + } + } + } + break; } } // take into account a border effect - if (myIsBorderFilterOn && myDiffPixels.Length() > 0) + if (myIsBorderFilterOn && !myDiffPixels.IsEmpty()) { aNbDiffColors = ignoreBorderEffect(); } @@ -341,17 +297,16 @@ Standard_Boolean Image_Diff::SaveDiffImage (Image_PixMap& theDiffImage) const if (theDiffImage.IsEmpty() || theDiffImage.SizeX() != myImageRef->SizeX() - || theDiffImage.SizeY() != myImageRef->SizeY() - || !isSupportedFormat (theDiffImage.Format())) + || theDiffImage.SizeY() != myImageRef->SizeY()) { - if (!theDiffImage.InitTrash (Image_Format_RGB, myImageRef->SizeX(), myImageRef->SizeY())) + if (!theDiffImage.InitTrash (Image_Format_Gray, myImageRef->SizeX(), myImageRef->SizeY())) { return Standard_False; } } - Standard_Size aRow, aCol; - const Image_ColorXXX24 aWhite = {{255, 255, 255}}; + const Image_ColorXXX24 aWhite24 = {{255, 255, 255}}; + const Quantity_ColorRGBA aWhiteRgba (1.0f, 1.0f, 1.0f, 1.0f); // initialize black image for dump memset (theDiffImage.ChangeData(), 0, theDiffImage.SizeBytes()); @@ -362,30 +317,86 @@ Standard_Boolean Image_Diff::SaveDiffImage (Image_PixMap& theDiffImage) const return Standard_True; } - for (Standard_Integer aPixelId = 0; aPixelId < myDiffPixels.Length(); ++aPixelId) + switch (theDiffImage.Format()) { - const Standard_Size aValue = myDiffPixels.Value (aPixelId); - int2Pixel (aValue, aRow, aCol); - theDiffImage.ChangeValue (aRow, aCol) = aWhite; + case Image_Format_Gray: + case Image_Format_Alpha: + { + for (NCollection_Vector::Iterator aPixelIter (myDiffPixels); aPixelIter.More(); aPixelIter.Next()) + { + theDiffImage.ChangeValue (UnpackY(aPixelIter.Value()), UnpackX(aPixelIter.Value())) = 255; + } + break; + } + case Image_Format_RGB: + case Image_Format_BGR: + case Image_Format_RGB32: + case Image_Format_BGR32: + case Image_Format_RGBA: + case Image_Format_BGRA: + { + for (NCollection_Vector::Iterator aPixelIter (myDiffPixels); aPixelIter.More(); aPixelIter.Next()) + { + theDiffImage.ChangeValue (UnpackY(aPixelIter.Value()), UnpackX(aPixelIter.Value())) = aWhite24; + } + break; + } + default: + { + for (NCollection_Vector::Iterator aPixelIter (myDiffPixels); aPixelIter.More(); aPixelIter.Next()) + { + theDiffImage.SetPixelColor (UnpackX(aPixelIter.Value()), UnpackY(aPixelIter.Value()), aWhiteRgba); + } + break; + } } - return Standard_True; } Standard_Integer aGroupId = 1; - for (ListOfMapOfInteger::Iterator aGrIter (myGroupsOfDiffPixels); aGrIter.More(); aGrIter.Next(), ++aGroupId) + for (NCollection_List::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()) + const Handle(TColStd_HPackedMapOfInteger)& aGroup = aGrIter.Value(); + switch (theDiffImage.Format()) { - int2Pixel (aPixelIter.Key(), aRow, aCol); - theDiffImage.ChangeValue (aRow, aCol) = aWhite; + case Image_Format_Gray: + case Image_Format_Alpha: + { + for (TColStd_MapIteratorOfPackedMapOfInteger aPixelIter (aGroup->Map()); aPixelIter.More(); aPixelIter.Next()) + { + Standard_Integer aDiffPixel (aPixelIter.Key()); + theDiffImage.ChangeValue (UnpackY(aDiffPixel), UnpackX(aDiffPixel)) = 255; + } + break; + } + case Image_Format_RGB: + case Image_Format_BGR: + case Image_Format_RGB32: + case Image_Format_BGR32: + case Image_Format_RGBA: + case Image_Format_BGRA: + { + for (TColStd_MapIteratorOfPackedMapOfInteger aPixelIter (aGroup->Map()); aPixelIter.More(); aPixelIter.Next()) + { + Standard_Integer aDiffPixel (aPixelIter.Key()); + theDiffImage.ChangeValue (UnpackY(aDiffPixel), UnpackX(aDiffPixel)) = aWhite24; + } + break; + } + default: + { + for (TColStd_MapIteratorOfPackedMapOfInteger aPixelIter (aGroup->Map()); aPixelIter.More(); aPixelIter.Next()) + { + Standard_Integer aDiffPixel (aPixelIter.Key()); + theDiffImage.SetPixelColor (UnpackX(aDiffPixel), UnpackY(aDiffPixel), aWhiteRgba); + } + break; + } } } @@ -404,7 +415,7 @@ Standard_Boolean Image_Diff::SaveDiffImage (const TCollection_AsciiString& theDi } Image_AlienPixMap aDiff; - if (!aDiff.InitTrash (Image_Format_RGB, myImageRef->SizeX(), myImageRef->SizeY()) + if (!aDiff.InitTrash (Image_Format_Gray, myImageRef->SizeX(), myImageRef->SizeY()) || !SaveDiffImage (aDiff)) { return Standard_False; @@ -430,39 +441,36 @@ Standard_Integer Image_Diff::ignoreBorderEffect() // 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 = 0, aCol1 = 0, aRow2, aCol2; - Standard_Integer aLen1 = (myDiffPixels.Length() > 0) ? (myDiffPixels.Length() - 1) : 0; + const Standard_Integer aLen1 = !myDiffPixels.IsEmpty() ? (myDiffPixels.Length() - 1) : 0; for (Standard_Integer aPixelId1 = 0; aPixelId1 < aLen1; ++aPixelId1) { - const Standard_Size aValue1 = myDiffPixels.Value (aPixelId1); - int2Pixel (aValue1, aRow1, aCol1); + Standard_Integer aValue1 = myDiffPixels.Value (aPixelId1); // 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 (getAbs (ptrdiff_t (aCol1 - aCol2)) <= 1 && - getAbs (ptrdiff_t (aRow1 - aRow2)) <= 1) + Standard_Integer aValue2 = myDiffPixels.Value (aPixelId2); + if (Abs (Standard_Integer(UnpackX(aValue1)) - Standard_Integer(UnpackX(aValue2))) <= 1 + && Abs (Standard_Integer(UnpackY(aValue1)) - Standard_Integer(UnpackY(aValue2))) <= 1) { // A neighbour is found. Create a new group and add both pixels. if (myGroupsOfDiffPixels.IsEmpty()) { - TColStd_MapOfInteger* aGroup = new TColStd_MapOfInteger(); - aGroup->Add ((Standard_Integer)aValue1); - aGroup->Add ((Standard_Integer)aValue2); + Handle(TColStd_HPackedMapOfInteger) aGroup = new TColStd_HPackedMapOfInteger(); + aGroup->ChangeMap().Add (aValue1); + aGroup->ChangeMap().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()) + for (NCollection_List::Iterator aGrIter (myGroupsOfDiffPixels); aGrIter.More(); aGrIter.Next()) { - TColStd_MapOfInteger*& aGroup = aGrIter.ChangeValue(); - if (aGroup->Contains ((Standard_Integer)aValue1)) + const Handle(TColStd_HPackedMapOfInteger)& aGroup = aGrIter.ChangeValue(); + if (aGroup->Map().Contains (aValue1)) { - aGroup->Add ((Standard_Integer)aValue2); + aGroup->ChangeMap().Add (aValue2); isFound = Standard_True; break; } @@ -471,9 +479,9 @@ Standard_Integer Image_Diff::ignoreBorderEffect() if (!isFound) { // Create a new group - TColStd_MapOfInteger* aGroup = new TColStd_MapOfInteger(); - aGroup->Add ((Standard_Integer)aValue1); - aGroup->Add ((Standard_Integer)aValue2); + Handle(TColStd_HPackedMapOfInteger) aGroup = new TColStd_HPackedMapOfInteger(); + aGroup->ChangeMap().Add (aValue1); + aGroup->ChangeMap().Add (aValue2); myGroupsOfDiffPixels.Append (aGroup); } } @@ -483,22 +491,31 @@ Standard_Integer Image_Diff::ignoreBorderEffect() // filter linear groups which represent border of a solid shape Standard_Integer aGroupId = 1; - for (ListOfMapOfInteger::Iterator aGrIter (myGroupsOfDiffPixels); aGrIter.More(); aGrIter.Next(), ++aGroupId) + for (NCollection_List::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()) + const Handle(TColStd_HPackedMapOfInteger)& aGroup = aGrIter.Value(); + if (aGroup->Map().IsEmpty()) { - int2Pixel (aPixelIter.Key(), aRow1, aCol1); + continue; + } + + Standard_Integer aDiffPixel = 0; + for (TColStd_MapIteratorOfPackedMapOfInteger aPixelIter (aGroup->Map()); aPixelIter.More(); aPixelIter.Next()) + { + aDiffPixel = aPixelIter.Key(); 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) + for (Standard_Size aNgbrIter = 0; aNgbrIter < Image_Diff_NbOfNeighborPixels; ++aNgbrIter) { - if (NEIGHBOR_PIXELS[aNgbrIter].isValid (*myImageRef, aRow1, aCol1) - && aGroup->Contains ((Standard_Integer)NEIGHBOR_PIXELS[aNgbrIter].pixel2Int (aRow1, aCol1))) + Standard_Integer anX = UnpackX(aDiffPixel) + Image_Diff_NEIGHBOR_PIXELS[aNgbrIter][0]; + Standard_Integer anY = UnpackY(aDiffPixel) + Image_Diff_NEIGHBOR_PIXELS[aNgbrIter][1]; + if (Standard_Size(anX) < myImageRef->SizeX() // this unsigned math checks Standard_Size(-1) at-once + && Standard_Size(anY) < myImageRef->SizeY() + && aGroup->Map().Contains (PackXY((uint16_t)anX, (uint16_t)anY))) { ++aNeighboursNb; } @@ -509,7 +526,7 @@ Standard_Integer Image_Diff::ignoreBorderEffect() isLine = Standard_False; break; } - } // for pixels inside group... + } if (isLine) { @@ -518,10 +535,13 @@ Standard_Integer Image_Diff::ignoreBorderEffect() // 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) + for (Standard_Size aNgbrIter = 0; aNgbrIter < Image_Diff_NbOfNeighborPixels; ++aNgbrIter) { - if ( NEIGHBOR_PIXELS[aNgbrIter].isValid (*myImageRef, aRow1, aCol1) - && !NEIGHBOR_PIXELS[aNgbrIter].isBlack (*myImageRef, aRow1, aCol1)) + Standard_Integer anX = UnpackX(aDiffPixel) + Image_Diff_NEIGHBOR_PIXELS[aNgbrIter][0]; + Standard_Integer anY = UnpackY(aDiffPixel) + Image_Diff_NEIGHBOR_PIXELS[aNgbrIter][1]; + if (Standard_Size(anX) < myImageRef->SizeX() // this unsigned math checks Standard_Size(-1) at-once + && Standard_Size(anY) < myImageRef->SizeY() + && !isBlackPixel (*myImageRef, Standard_Size(anY), Standard_Size(anX))) { ++aNeighboursNb; } @@ -532,15 +552,17 @@ Standard_Integer Image_Diff::ignoreBorderEffect() 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) + for (NCollection_List::Iterator aGrIter (myGroupsOfDiffPixels); aGrIter.More(); aGrIter.Next(), ++aGroupId) { if (!myLinearGroups.Contains (aGroupId)) + { ++aNbDiffColors; + } } return aNbDiffColors; @@ -552,11 +574,6 @@ Standard_Integer Image_Diff::ignoreBorderEffect() // ======================================================================= void Image_Diff::releaseGroupsOfDiffPixels() { - for (ListOfMapOfInteger::Iterator aGrIter (myGroupsOfDiffPixels); aGrIter.More(); aGrIter.Next()) - { - TColStd_MapOfInteger*& aGroup = aGrIter.ChangeValue(); - delete aGroup; - } myGroupsOfDiffPixels.Clear(); myLinearGroups.Clear(); } diff --git a/src/Image/Image_Diff.hxx b/src/Image/Image_Diff.hxx index 548c6ba9e9..79f2046c4e 100644 --- a/src/Image/Image_Diff.hxx +++ b/src/Image/Image_Diff.hxx @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include @@ -81,10 +81,10 @@ public: //! 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); + void SetColorTolerance (const Standard_Real theTolerance) { myColorTolerance = theTolerance; } //! Color tolerance for equality check. - Standard_EXPORT Standard_Real ColorTolerance() const; + Standard_Real ColorTolerance() const { return myColorTolerance; } //! 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. @@ -92,10 +92,10 @@ public: //! 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); + void SetBorderFilterOn (const Standard_Boolean theToIgnore) { myIsBorderFilterOn = theToIgnore; } //! Returns a flag of taking into account (ignoring) a border effect in comparison of images. - Standard_EXPORT Standard_Boolean IsBorderFilterOn() const; + Standard_Boolean IsBorderFilterOn() const { return myIsBorderFilterOn; } //! Compares two images. It returns a number of different pixels (or groups of pixels). //! It returns -1 if algorithm not initialized before. @@ -117,15 +117,38 @@ protected: protected: - typedef NCollection_List ListOfMapOfInteger; + //! Map two pixel coordinates to 32-bit integer + static Standard_Integer PackXY (uint16_t theX, uint16_t theY) + { + return Standard_Integer((unsigned int)theY | + ((unsigned int)theX << 16)); + } + + //! Get pixel X coordinate from 32-bit packed integer + static uint16_t UnpackX (Standard_Integer theXY) + { + return (uint16_t)(((unsigned int)theXY & 0xffff0000) >> 16); + } + + //! Get pixel Y coordinate from 32-bit packed integer + static uint16_t UnpackY (Standard_Integer theXY) + { + return (uint16_t)((unsigned int)theXY & 0xffff); + } + +protected: 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 myDiffPixels; //!< different pixels (position packed into integer) - TColStd_MapOfInteger myLinearGroups; + + //! coordinates of different pixels, packed in one int using 16-bit integers to save memory + NCollection_Vector myDiffPixels; + TColStd_PackedMapOfInteger myLinearGroups; + NCollection_List + myGroupsOfDiffPixels; public: diff --git a/src/Image/Image_PixMap.cxx b/src/Image/Image_PixMap.cxx index 7538d363a1..638324d424 100644 --- a/src/Image/Image_PixMap.cxx +++ b/src/Image/Image_PixMap.cxx @@ -191,16 +191,14 @@ void Image_PixMap::Clear() // function : PixelColor // purpose : // ======================================================================= -Quantity_Color Image_PixMap::PixelColor (const Standard_Integer theX, - const Standard_Integer theY, - Standard_Real& theAlpha) const +Quantity_ColorRGBA Image_PixMap::PixelColor (const Standard_Integer theX, + const Standard_Integer theY) const { if (IsEmpty() || theX < 0 || (Standard_Size )theX >= SizeX() || theY < 0 || (Standard_Size )theY >= SizeY()) { - theAlpha = 0.0; // transparent - return Quantity_Color (0.0, 0.0, 0.0, Quantity_TOC_RGB); + return Quantity_ColorRGBA (0.0f, 0.0f, 0.0f, 0.0f); // transparent } switch (myImgFormat) @@ -208,86 +206,72 @@ Quantity_Color Image_PixMap::PixelColor (const Standard_Integer theX, case Image_Format_GrayF: { const Standard_ShortReal& aPixel = Value (theY, theX); - theAlpha = 1.0; // opaque - return Quantity_Color (aPixel, aPixel, aPixel, Quantity_TOC_RGB); + return Quantity_ColorRGBA (NCollection_Vec4 (aPixel, aPixel, aPixel, 1.0f)); // opaque } case Image_Format_AlphaF: { const Standard_ShortReal& aPixel = Value (theY, theX); - theAlpha = aPixel; - return Quantity_Color (1.0, 1.0, 1.0, Quantity_TOC_RGB); + return Quantity_ColorRGBA (NCollection_Vec4 (1.0f, 1.0f, 1.0f, aPixel)); } case Image_Format_RGBAF: { const Image_ColorRGBAF& aPixel = Value (theY, theX); - theAlpha = aPixel.a(); - return Quantity_Color (aPixel.r(), aPixel.g(), aPixel.b(), Quantity_TOC_RGB); + return Quantity_ColorRGBA (NCollection_Vec4 (aPixel.r(), aPixel.g(), aPixel.b(), aPixel.a())); } case Image_Format_BGRAF: { const Image_ColorBGRAF& aPixel = Value (theY, theX); - theAlpha = aPixel.a(); - return Quantity_Color (aPixel.r(), aPixel.g(), aPixel.b(), Quantity_TOC_RGB); + return Quantity_ColorRGBA (NCollection_Vec4 (aPixel.r(), aPixel.g(), aPixel.b(), aPixel.a())); } case Image_Format_RGBF: { const Image_ColorRGBF& aPixel = Value (theY, theX); - theAlpha = 1.0; // opaque - return Quantity_Color (aPixel.r(), aPixel.g(), aPixel.b(), Quantity_TOC_RGB); + return Quantity_ColorRGBA (NCollection_Vec4 (aPixel.r(), aPixel.g(), aPixel.b(), 1.0f)); // opaque } case Image_Format_BGRF: { const Image_ColorBGRF& aPixel = Value (theY, theX); - theAlpha = 1.0; // opaque - return Quantity_Color (aPixel.r(), aPixel.g(), aPixel.b(), Quantity_TOC_RGB); + return Quantity_ColorRGBA (NCollection_Vec4 (aPixel.r(), aPixel.g(), aPixel.b(), 1.0f)); // opaque } case Image_Format_RGBA: { const Image_ColorRGBA& aPixel = Value (theY, theX); - theAlpha = Standard_Real (aPixel.a()) / 255.0; - return Quantity_Color (Standard_Real (aPixel.r()) / 255.0, Standard_Real (aPixel.g()) / 255.0, Standard_Real (aPixel.b()) / 255.0, Quantity_TOC_RGB); + return Quantity_ColorRGBA (float(aPixel.r()) / 255.0f, float(aPixel.g()) / 255.0f, float(aPixel.b()) / 255.0f, float(aPixel.a()) / 255.0f); } case Image_Format_BGRA: { const Image_ColorBGRA& aPixel = Value (theY, theX); - theAlpha = Standard_Real (aPixel.a()) / 255.0; - return Quantity_Color (Standard_Real (aPixel.r()) / 255.0, Standard_Real (aPixel.g()) / 255.0, Standard_Real (aPixel.b() / 255.0), Quantity_TOC_RGB); + return Quantity_ColorRGBA (float(aPixel.r()) / 255.0f, float(aPixel.g()) / 255.0f, float(aPixel.b()) / 255.0f, float(aPixel.a()) / 255.0f); } case Image_Format_RGB32: { const Image_ColorRGB32& aPixel = Value (theY, theX); - theAlpha = 1.0; // opaque - return Quantity_Color (Standard_Real (aPixel.r()) / 255.0, Standard_Real (aPixel.g()) / 255.0, Standard_Real (aPixel.b()) / 255.0, Quantity_TOC_RGB); + return Quantity_ColorRGBA (float(aPixel.r()) / 255.0f, float(aPixel.g()) / 255.0f, float(aPixel.b()) / 255.0f, 1.0f); // opaque } case Image_Format_BGR32: { const Image_ColorBGR32& aPixel = Value (theY, theX); - theAlpha = 1.0; // opaque - return Quantity_Color (Standard_Real (aPixel.r()) / 255.0, Standard_Real (aPixel.g()) / 255.0, Standard_Real (aPixel.b()) / 255.0, Quantity_TOC_RGB); + return Quantity_ColorRGBA (float(aPixel.r()) / 255.0f, float(aPixel.g()) / 255.0f, float(aPixel.b()) / 255.0f, 1.0f); // opaque } case Image_Format_RGB: { const Image_ColorRGB& aPixel = Value (theY, theX); - theAlpha = 1.0; // opaque - return Quantity_Color (Standard_Real (aPixel.r()) / 255.0, Standard_Real (aPixel.g()) / 255.0, Standard_Real (aPixel.b()) / 255.0, Quantity_TOC_RGB); + return Quantity_ColorRGBA (float(aPixel.r()) / 255.0f, float(aPixel.g()) / 255.0f, float(aPixel.b()) / 255.0f, 1.0f); // opaque } case Image_Format_BGR: { const Image_ColorBGR& aPixel = Value (theY, theX); - theAlpha = 1.0; // opaque - return Quantity_Color (Standard_Real (aPixel.r()) / 255.0, Standard_Real (aPixel.g()) / 255.0, Standard_Real (aPixel.b()) / 255.0, Quantity_TOC_RGB); + return Quantity_ColorRGBA (float(aPixel.r()) / 255.0f, float(aPixel.g()) / 255.0f, float(aPixel.b()) / 255.0f, 1.0f); // opaque } case Image_Format_Gray: { const Standard_Byte& aPixel = Value (theY, theX); - theAlpha = 1.0; // opaque - return Quantity_Color (Standard_Real (aPixel) / 255.0, Standard_Real (aPixel) / 255.0, Standard_Real (aPixel) / 255.0, Quantity_TOC_RGB); + return Quantity_ColorRGBA (float(aPixel) / 255.0f, float(aPixel) / 255.0f, float(aPixel) / 255.0f, 1.0f); // opaque } case Image_Format_Alpha: { const Standard_Byte& aPixel = Value (theY, theX); - theAlpha = Standard_Real (aPixel) / 255.0; - return Quantity_Color (1.0, 1.0, 1.0, Quantity_TOC_RGB); + return Quantity_ColorRGBA (1.0f, 1.0f, 1.0f, float(aPixel) / 255.0f); } case Image_Format_UNKNOWN: { @@ -296,8 +280,7 @@ Quantity_Color Image_PixMap::PixelColor (const Standard_Integer theX, } // unsupported image type - theAlpha = 0.0; // transparent - return Quantity_Color (0.0, 0.0, 0.0, Quantity_TOC_RGB); + return Quantity_ColorRGBA (0.0f, 0.0f, 0.0f, 0.0f); // transparent } // ======================================================================= @@ -306,7 +289,7 @@ Quantity_Color Image_PixMap::PixelColor (const Standard_Integer theX, // ======================================================================= void Image_PixMap::SetPixelColor (const Standard_Integer theX, const Standard_Integer theY, - const NCollection_Vec4& theColor) + const Quantity_ColorRGBA& theColor) { if (IsEmpty() || theX < 0 || Standard_Size(theX) >= SizeX() @@ -315,112 +298,113 @@ void Image_PixMap::SetPixelColor (const Standard_Integer theX, return; } + const NCollection_Vec4& aColor = theColor; switch (myImgFormat) { case Image_Format_GrayF: { - ChangeValue (theY, theX) = theColor.r(); + ChangeValue (theY, theX) = aColor.r(); return; } case Image_Format_AlphaF: { - ChangeValue (theY, theX) = theColor.a(); + ChangeValue (theY, theX) = aColor.a(); return; } case Image_Format_RGBAF: { Image_ColorRGBAF& aPixel = ChangeValue (theY, theX); - aPixel.r() = theColor.r(); - aPixel.g() = theColor.g(); - aPixel.b() = theColor.b(); - aPixel.a() = theColor.a(); + aPixel.r() = aColor.r(); + aPixel.g() = aColor.g(); + aPixel.b() = aColor.b(); + aPixel.a() = aColor.a(); return; } case Image_Format_BGRAF: { Image_ColorBGRAF& aPixel = ChangeValue (theY, theX); - aPixel.r() = theColor.r(); - aPixel.g() = theColor.g(); - aPixel.b() = theColor.b(); - aPixel.a() = theColor.a(); + aPixel.r() = aColor.r(); + aPixel.g() = aColor.g(); + aPixel.b() = aColor.b(); + aPixel.a() = aColor.a(); return; } case Image_Format_RGBF: { Image_ColorRGBF& aPixel = ChangeValue (theY, theX); - aPixel.r() = theColor.r(); - aPixel.g() = theColor.g(); - aPixel.b() = theColor.b(); + aPixel.r() = aColor.r(); + aPixel.g() = aColor.g(); + aPixel.b() = aColor.b(); return; } case Image_Format_BGRF: { Image_ColorBGRF& aPixel = ChangeValue (theY, theX); - aPixel.r() = theColor.r(); - aPixel.g() = theColor.g(); - aPixel.b() = theColor.b(); + aPixel.r() = aColor.r(); + aPixel.g() = aColor.g(); + aPixel.b() = aColor.b(); return; } case Image_Format_RGBA: { Image_ColorRGBA& aPixel = ChangeValue (theY, theX); - aPixel.r() = Standard_Byte(theColor.r() * 255.0f); - aPixel.g() = Standard_Byte(theColor.g() * 255.0f); - aPixel.b() = Standard_Byte(theColor.b() * 255.0f); - aPixel.a() = Standard_Byte(theColor.a() * 255.0f); + aPixel.r() = Standard_Byte(aColor.r() * 255.0f); + aPixel.g() = Standard_Byte(aColor.g() * 255.0f); + aPixel.b() = Standard_Byte(aColor.b() * 255.0f); + aPixel.a() = Standard_Byte(aColor.a() * 255.0f); return; } case Image_Format_BGRA: { Image_ColorBGRA& aPixel = ChangeValue (theY, theX); - aPixel.r() = Standard_Byte(theColor.r() * 255.0f); - aPixel.g() = Standard_Byte(theColor.g() * 255.0f); - aPixel.b() = Standard_Byte(theColor.b() * 255.0f); - aPixel.a() = Standard_Byte(theColor.a() * 255.0f); + aPixel.r() = Standard_Byte(aColor.r() * 255.0f); + aPixel.g() = Standard_Byte(aColor.g() * 255.0f); + aPixel.b() = Standard_Byte(aColor.b() * 255.0f); + aPixel.a() = Standard_Byte(aColor.a() * 255.0f); return; } case Image_Format_RGB32: { Image_ColorRGB32& aPixel = ChangeValue (theY, theX); - aPixel.r() = Standard_Byte(theColor.r() * 255.0f); - aPixel.g() = Standard_Byte(theColor.g() * 255.0f); - aPixel.b() = Standard_Byte(theColor.b() * 255.0f); + aPixel.r() = Standard_Byte(aColor.r() * 255.0f); + aPixel.g() = Standard_Byte(aColor.g() * 255.0f); + aPixel.b() = Standard_Byte(aColor.b() * 255.0f); aPixel.a_() = 255; return; } case Image_Format_BGR32: { Image_ColorBGR32& aPixel = ChangeValue (theY, theX); - aPixel.r() = Standard_Byte(theColor.r() * 255.0f); - aPixel.g() = Standard_Byte(theColor.g() * 255.0f); - aPixel.b() = Standard_Byte(theColor.b() * 255.0f); + aPixel.r() = Standard_Byte(aColor.r() * 255.0f); + aPixel.g() = Standard_Byte(aColor.g() * 255.0f); + aPixel.b() = Standard_Byte(aColor.b() * 255.0f); aPixel.a_() = 255; return; } case Image_Format_RGB: { Image_ColorRGB& aPixel = ChangeValue (theY, theX); - aPixel.r() = Standard_Byte(theColor.r() * 255.0f); - aPixel.g() = Standard_Byte(theColor.g() * 255.0f); - aPixel.b() = Standard_Byte(theColor.b() * 255.0f); + aPixel.r() = Standard_Byte(aColor.r() * 255.0f); + aPixel.g() = Standard_Byte(aColor.g() * 255.0f); + aPixel.b() = Standard_Byte(aColor.b() * 255.0f); return; } case Image_Format_BGR: { Image_ColorBGR& aPixel = ChangeValue (theY, theX); - aPixel.r() = Standard_Byte(theColor.r() * 255.0f); - aPixel.g() = Standard_Byte(theColor.g() * 255.0f); - aPixel.b() = Standard_Byte(theColor.b() * 255.0f); + aPixel.r() = Standard_Byte(aColor.r() * 255.0f); + aPixel.g() = Standard_Byte(aColor.g() * 255.0f); + aPixel.b() = Standard_Byte(aColor.b() * 255.0f); return; } case Image_Format_Gray: { - ChangeValue (theY, theX) = Standard_Byte(theColor.r() * 255.0f); + ChangeValue (theY, theX) = Standard_Byte(aColor.r() * 255.0f); return; } case Image_Format_Alpha: { - ChangeValue (theY, theX) = Standard_Byte(theColor.a() * 255.0f); + ChangeValue (theY, theX) = Standard_Byte(aColor.a() * 255.0f); return; } case Image_Format_UNKNOWN: @@ -499,3 +483,72 @@ bool Image_PixMap::SwapRgbaBgra (Image_PixMap& theImage) default: return false; } } + +// ======================================================================= +// function : ToBlackWhite +// purpose : +// ======================================================================= +void Image_PixMap::ToBlackWhite (Image_PixMap& theImage) +{ + switch (theImage.Format()) + { + case Image_Format_Gray: + case Image_Format_Alpha: + { + for (Standard_Size aRow = 0; aRow < theImage.SizeY(); ++aRow) + { + for (Standard_Size aCol = 0; aCol < theImage.SizeX(); ++aCol) + { + unsigned char& aPixel = theImage.ChangeValue (aRow, aCol); + if (aPixel != 0) + { + aPixel = 255; + } + } + } + break; + } + case Image_Format_RGB: + case Image_Format_BGR: + case Image_Format_RGB32: + case Image_Format_BGR32: + case Image_Format_RGBA: + case Image_Format_BGRA: + { + const NCollection_Vec3 aWhite24 (255, 255, 255); + for (Standard_Size aRow = 0; aRow < theImage.SizeY(); ++aRow) + { + for (Standard_Size aCol = 0; aCol < theImage.SizeX(); ++aCol) + { + NCollection_Vec3& aPixel = theImage.ChangeValue< NCollection_Vec3 > (aRow, aCol); + if (aPixel[0] != 0 + || aPixel[1] != 0 + || aPixel[2] != 0) + { + aPixel = aWhite24; + } + } + } + break; + } + default: + { + const Quantity_ColorRGBA aWhiteRgba (1.0f, 1.0f, 1.0f, 1.0f); + for (Standard_Size aRow = 0; aRow < theImage.SizeY(); ++aRow) + { + for (Standard_Size aCol = 0; aCol < theImage.SizeX(); ++aCol) + { + const Quantity_ColorRGBA aPixelRgba = theImage.PixelColor (Standard_Integer(aCol), Standard_Integer(aRow)); + const NCollection_Vec4& aPixel = aPixelRgba; + if (aPixel[0] != 0.0f + || aPixel[1] != 0.0f + || aPixel[2] != 0.0f) + { + theImage.SetPixelColor (int(aCol), int(aRow), aWhiteRgba); + } + } + } + break; + } + } +} diff --git a/src/Image/Image_PixMap.hxx b/src/Image/Image_PixMap.hxx index 39f3630229..7394af9828 100644 --- a/src/Image/Image_PixMap.hxx +++ b/src/Image/Image_PixMap.hxx @@ -45,6 +45,9 @@ public: //! - Image_Format_RGBAF / Image_Format_BGRAF Standard_EXPORT static bool SwapRgbaBgra (Image_PixMap& theImage); + //! Convert image to Black/White. + Standard_EXPORT static void ToBlackWhite (Image_PixMap& theImage); + public: // high-level API Image_Format Format() const { return myImgFormat; } @@ -102,45 +105,23 @@ public: // high-level API //! @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 - { - Standard_Real 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) - //! Beware that this method takes coordinates in opposite order in contrast to ::Value() and ::ChangeValue(). - Standard_EXPORT Quantity_Color PixelColor (const Standard_Integer theX, - const Standard_Integer theY, - Standard_Real& theAlpha) const; + Standard_EXPORT Quantity_ColorRGBA PixelColor (const Standard_Integer theX, + const Standard_Integer theY) const; //! Sets the pixel color. This function is relatively slow. //! Beware that this method takes coordinates in opposite order in contrast to ::Value() and ::ChangeValue(). - void SetPixelColor (const Standard_Integer theX, - const Standard_Integer theY, - const Quantity_ColorRGBA& theColor) + void SetPixelColor (const Standard_Integer theX, + const Standard_Integer theY, + const Quantity_Color& theColor) { - const NCollection_Vec4 aColor = theColor; - SetPixelColor (theX, theY, aColor); - } - - //! Sets the pixel color. This function is relatively slow. - //! Beware that this method takes coordinates in opposite order in contrast to ::Value() and ::ChangeValue(). - void SetPixelColor(const Standard_Integer theX, - const Standard_Integer theY, - const Quantity_Color& theColor) - { - const NCollection_Vec3 aColor = theColor; - SetPixelColor (theX, theY, NCollection_Vec4 (aColor, 1.0f)); + SetPixelColor (theX, theY, Quantity_ColorRGBA (theColor, 1.0f)); } //! Sets the pixel color. This function is relatively slow. //! Beware that this method takes coordinates in opposite order in contrast to ::Value() and ::ChangeValue(). Standard_EXPORT void SetPixelColor (const Standard_Integer theX, const Standard_Integer theY, - const NCollection_Vec4& theColor); + const Quantity_ColorRGBA& theColor); //! Initialize image plane as wrapper over alien data. //! Data will not be copied! Notice that caller should ensure diff --git a/src/NCollection/NCollection_Vec2.hxx b/src/NCollection/NCollection_Vec2.hxx index 132b6db9be..bb74d07def 100644 --- a/src/NCollection/NCollection_Vec2.hxx +++ b/src/NCollection/NCollection_Vec2.hxx @@ -57,6 +57,14 @@ public: v[1] = theY; } + //! Assign new values to the vector. + void SetValues (const Element_t theX, + const Element_t theY) + { + v[0] = theX; + v[1] = theY; + } + //! Alias to 1st component as X coordinate in XY. Element_t x() const { return v[0]; } diff --git a/src/NCollection/NCollection_Vec3.hxx b/src/NCollection/NCollection_Vec3.hxx index 91e2468af0..ed6cf98b5e 100644 --- a/src/NCollection/NCollection_Vec3.hxx +++ b/src/NCollection/NCollection_Vec3.hxx @@ -73,6 +73,16 @@ public: v[2] = Element_t(0); } + //! Assign new values to the vector. + void SetValues (const Element_t theX, + const Element_t theY, + const Element_t theZ) + { + v[0] = theX; + v[1] = theY; + v[2] = theZ; + } + //! Alias to 1st component as X coordinate in XYZ. Element_t x() const { return v[0]; } diff --git a/src/NCollection/NCollection_Vec4.hxx b/src/NCollection/NCollection_Vec4.hxx index 9bc63f930f..08704121e3 100644 --- a/src/NCollection/NCollection_Vec4.hxx +++ b/src/NCollection/NCollection_Vec4.hxx @@ -80,6 +80,18 @@ public: v[3] = theAlpha; } + //! Assign new values to the vector. + void SetValues (const Element_t theX, + const Element_t theY, + const Element_t theZ, + const Element_t theW) + { + v[0] = theX; + v[1] = theY; + v[2] = theZ; + v[3] = theW; + } + //! Alias to 1st component as X coordinate in XYZW. Element_t x() const { return v[0]; } diff --git a/src/Quantity/Quantity_ColorRGBA.hxx b/src/Quantity/Quantity_ColorRGBA.hxx index 5e5f9dadc0..8f91d9fea3 100644 --- a/src/Quantity/Quantity_ColorRGBA.hxx +++ b/src/Quantity/Quantity_ColorRGBA.hxx @@ -26,10 +26,32 @@ public: Quantity_ColorRGBA() : myAlpha (1.0f) {} //! Creates the color with specified RGB value. - explicit Quantity_ColorRGBA (const Quantity_Color& theRgb) : myRgb (theRgb), myAlpha (1.0f) {} + explicit Quantity_ColorRGBA (const Quantity_Color& theRgb) + : myRgb (theRgb), myAlpha (1.0f) + {} + + //! Creates the color with specified RGBA values. + Quantity_ColorRGBA (const Quantity_Color& theRgb, float theAlpha) + : myRgb (theRgb), myAlpha (theAlpha) + {} //! Creates the color from RGBA vector. - explicit Quantity_ColorRGBA (const NCollection_Vec4& theRgba) : myRgb (theRgba.rgb()), myAlpha (theRgba.a()) {} + explicit Quantity_ColorRGBA (const NCollection_Vec4& theRgba) + : myRgb (theRgba.rgb()), myAlpha (theRgba.a()) + {} + + //! Creates the color from RGBA values. + Quantity_ColorRGBA (float theRed, float theGreen, float theBlue, float theAlpha) + : myRgb (theRed, theGreen, theBlue, Quantity_TOC_RGB), + myAlpha (theAlpha) + {} + + //! Assign new values to the color. + void SetValues (float theRed, float theGreen, float theBlue, float theAlpha) + { + myRgb.SetValues (theRed, theGreen, theBlue, Quantity_TOC_RGB); + myAlpha = theAlpha; + } //! Return RGB color value. const Quantity_Color& GetRGB() const { return myRgb; } @@ -71,8 +93,8 @@ public: private: - static void __testSize3() { Standard_STATIC_ASSERT (sizeof(float) * 3 == sizeof(Quantity_Color)); } - static void __testSize4() { Standard_STATIC_ASSERT (sizeof(float) * 4 == sizeof(Quantity_ColorRGBA)); } + static void myTestSize3() { Standard_STATIC_ASSERT (sizeof(float) * 3 == sizeof(Quantity_Color)); } + static void myTestSize4() { Standard_STATIC_ASSERT (sizeof(float) * 4 == sizeof(Quantity_ColorRGBA)); } private: diff --git a/src/StdSelect/StdSelect_ViewerSelector3d.cxx b/src/StdSelect/StdSelect_ViewerSelector3d.cxx index 8e259be38c..e6663530bb 100644 --- a/src/StdSelect/StdSelect_ViewerSelector3d.cxx +++ b/src/StdSelect/StdSelect_ViewerSelector3d.cxx @@ -840,7 +840,7 @@ namespace || aDepth >= ShortRealLast()) { myImage->SetPixelColor (Standard_Integer(aColIter), Standard_Integer(aRowIter), - NCollection_Vec4 (0.0f, 0.0f, 0.0f, 1.0f)); + Quantity_ColorRGBA (0.0f, 0.0f, 0.0f, 1.0f)); continue; } @@ -850,7 +850,7 @@ namespace aNormDepth = 1.0f - aNormDepth; } myImage->SetPixelColor (Standard_Integer(aColIter), Standard_Integer(aRowIter), - NCollection_Vec4 (aNormDepth, aNormDepth, aNormDepth, 1.0f)); + Quantity_ColorRGBA (aNormDepth, aNormDepth, aNormDepth, 1.0f)); } } } @@ -878,13 +878,13 @@ namespace if (thePicked < 1 || thePicked > myMainSel->NbPicked()) { - myImage->SetPixelColor (theCol, theRow, NCollection_Vec4 (0.0f, 0.0f, 0.0f, 1.0f)); + myImage->SetPixelColor (theCol, theRow, Quantity_ColorRGBA (0.0f, 0.0f, 0.0f, 1.0f)); return; } const SelectMgr_SortCriterion& aSortCriterion = myMainSel->PickedData (thePicked); const float aDepth = float(aSortCriterion.Depth); - myImage->SetPixelColor (theCol, theRow, NCollection_Vec4 (aDepth, aDepth, aDepth, 1.0f)); + myImage->SetPixelColor (theCol, theRow, Quantity_ColorRGBA (aDepth, aDepth, aDepth, 1.0f)); } }; diff --git a/src/ViewerTest/ViewerTest_ViewerCommands.cxx b/src/ViewerTest/ViewerTest_ViewerCommands.cxx index 6ac6cc65bf..cb2ce783c3 100644 --- a/src/ViewerTest/ViewerTest_ViewerCommands.cxx +++ b/src/ViewerTest/ViewerTest_ViewerCommands.cxx @@ -5848,17 +5848,16 @@ static int VReadPixel (Draw_Interpretor& theDI, return 1; } - Standard_Real anAlpha; - Quantity_Color aColor = anImage.PixelColor (anX, anY, anAlpha); + Quantity_ColorRGBA aColor = anImage.PixelColor (anX, anY); if (toShowName) { if (aBufferType == Graphic3d_BT_RGBA) { - theDI << Quantity_Color::StringName (aColor.Name()) << " " << anAlpha; + theDI << Quantity_Color::StringName (aColor.GetRGB().Name()) << " " << aColor.Alpha(); } else { - theDI << Quantity_Color::StringName (aColor.Name()); + theDI << Quantity_Color::StringName (aColor.GetRGB().Name()); } } else @@ -5870,22 +5869,22 @@ static int VReadPixel (Draw_Interpretor& theDI, { if (toShowHls) { - theDI << aColor.Hue() << " " << aColor.Light() << " " << aColor.Saturation(); + theDI << aColor.GetRGB().Hue() << " " << aColor.GetRGB().Light() << " " << aColor.GetRGB().Saturation(); } else { - theDI << aColor.Red() << " " << aColor.Green() << " " << aColor.Blue(); + theDI << aColor.GetRGB().Red() << " " << aColor.GetRGB().Green() << " " << aColor.GetRGB().Blue(); } break; } case Graphic3d_BT_RGBA: { - theDI << aColor.Red() << " " << aColor.Green() << " " << aColor.Blue() << " " << anAlpha; + theDI << aColor.GetRGB().Red() << " " << aColor.GetRGB().Green() << " " << aColor.GetRGB().Blue() << " " << aColor.Alpha(); break; } case Graphic3d_BT_Depth: { - theDI << aColor.Red(); + theDI << aColor.GetRGB().Red(); break; } }