[Libreoffice-commits] core.git: include/tools tools/CppunitTest_tools_test.mk tools/qa tools/source
Tomaž Vajngerl (via logerrit)
logerrit at kemper.freedesktop.org
Sun Apr 21 12:21:17 UTC 2019
include/tools/color.hxx | 85 +++++++++++++++------------
tools/CppunitTest_tools_test.mk | 1
tools/qa/cppunit/test_color.cxx | 124 ++++++++++++++++++++++++++++++++++++++++
tools/source/generic/color.cxx | 50 ++++++++--------
4 files changed, 197 insertions(+), 63 deletions(-)
New commits:
commit d487d6e082bc7ce652217578ffd37397a59cc3ca
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Sat Apr 20 11:06:11 2019 +0900
Commit: Tomaž Vajngerl <quikee at gmail.com>
CommitDate: Sun Apr 21 14:20:19 2019 +0200
rework Color to have R,G,B,A public variables
Color is a wrapper around a sal_uInt32 variable. With a union,
separate channels R, G, B, A sal_uInt8 variables can be added
that occupy the same memory. This makes it much easier to access
each color component separately, which is used quite a lot by
various algorithms. This also adds the variables to public so
everyone can enjoy the benefits.
Tests have been extended to make sure this doesn't break the
existing algroithms.
Change-Id: I2e78e12df68e8c7f0f49420eef5e659b335ee397
Reviewed-on: https://gerrit.libreoffice.org/71002
Tested-by: Jenkins
Reviewed-by: Tomaž Vajngerl <quikee at gmail.com>
diff --git a/include/tools/color.hxx b/include/tools/color.hxx
index 26d587b7e1f5..25032c5d29b9 100644
--- a/include/tools/color.hxx
+++ b/include/tools/color.hxx
@@ -26,29 +26,44 @@
class SvStream;
-constexpr sal_uInt8 COLORDATA_RED( sal_uInt32 n ) { return static_cast<sal_uInt8>(n>>16); }
-constexpr sal_uInt8 COLORDATA_GREEN( sal_uInt32 n ) { return static_cast<sal_uInt8>(static_cast<sal_uInt16>(n) >> 8); }
-constexpr sal_uInt8 COLORDATA_BLUE( sal_uInt32 n ) { return static_cast<sal_uInt8>(n); }
-constexpr sal_uInt8 COLORDATA_TRANSPARENCY( sal_uInt32 n ) { return static_cast<sal_uInt8>(n>>24); }
constexpr sal_uInt32 COLORDATA_RGB( sal_uInt32 n ) { return static_cast<sal_uInt32>(n & 0x00FFFFFF); }
// Color
class SAL_WARN_UNUSED TOOLS_DLLPUBLIC Color final
{
- sal_uInt32 mnColor;
-
public:
+ union
+ {
+ sal_uInt32 mValue;
+ struct
+ {
+ #ifdef OSL_BIGENDIAN
+ sal_uInt8 A;
+ sal_uInt8 R;
+ sal_uInt8 G;
+ sal_uInt8 B;
+ #else
+ sal_uInt8 B;
+ sal_uInt8 G;
+ sal_uInt8 R;
+ sal_uInt8 A;
+ #endif
+ };
+ };
+
constexpr Color()
- : mnColor(0) // black
+ : mValue(0) // black
{}
+
constexpr Color(sal_uInt32 nColor)
- : mnColor(nColor)
+ : mValue(nColor)
{}
+
constexpr Color(sal_uInt8 nTransparency, sal_uInt8 nRed, sal_uInt8 nGreen, sal_uInt8 nBlue)
- : mnColor( sal_uInt32(nBlue) | (sal_uInt32(nGreen) << 8) | (sal_uInt32(nRed) << 16)
- | (sal_uInt32(nTransparency) << 24) )
+ : mValue(sal_uInt32(nBlue) | (sal_uInt32(nGreen) << 8) | (sal_uInt32(nRed) << 16) | (sal_uInt32(nTransparency) << 24))
{}
+
constexpr Color(sal_uInt8 nRed, sal_uInt8 nGreen, sal_uInt8 nBlue)
: Color(0, nRed, nGreen, nBlue)
{}
@@ -56,51 +71,51 @@ public:
// constructor to create a tools-Color from ::basegfx::BColor
explicit Color(const basegfx::BColor& rBColor)
: Color(0,
- sal_uInt8((rBColor.getRed() * 255.0) + 0.5),
- sal_uInt8((rBColor.getGreen() * 255.0) + 0.5),
- sal_uInt8((rBColor.getBlue() * 255.0) + 0.5))
+ sal_uInt8(std::lround(rBColor.getRed() * 255.0)),
+ sal_uInt8(std::lround(rBColor.getGreen() * 255.0)),
+ sal_uInt8(std::lround(rBColor.getBlue() * 255.0)))
{}
/** Primarily used when passing Color objects to UNO API */
constexpr explicit operator sal_uInt32() const
{
- return mnColor;
+ return mValue;
}
constexpr explicit operator sal_Int32() const
{
- return sal_Int32(mnColor);
+ return sal_Int32(mValue);
}
- bool operator<(const Color& b) const
+ bool operator<(const Color& aCompareColor) const
{
- return mnColor < b.mnColor;
+ return mValue < aCompareColor.mValue;
}
void SetRed(sal_uInt8 nRed);
sal_uInt8 GetRed() const
{
- return COLORDATA_RED(mnColor);
+ return R;
}
void SetGreen(sal_uInt8 nGreen);
sal_uInt8 GetGreen() const
{
- return COLORDATA_GREEN(mnColor);
+ return G;
}
void SetBlue(sal_uInt8 nBlue);
sal_uInt8 GetBlue() const
{
- return COLORDATA_BLUE(mnColor);
+ return B;
}
void SetTransparency(sal_uInt8 nTransparency);
sal_uInt8 GetTransparency() const
{
- return COLORDATA_TRANSPARENCY(mnColor);
+ return A;
}
Color GetRGBColor() const
{
- return COLORDATA_RGB(mnColor);
+ return COLORDATA_RGB(mValue);
}
sal_uInt8 GetColorError(const Color& rCompareColor) const;
@@ -141,7 +156,7 @@ public:
bool operator==(const Color& rColor) const
{
- return mnColor == rColor.mnColor;
+ return mValue == rColor.mValue;
}
bool operator!=(const Color& rColor) const
{
@@ -167,38 +182,32 @@ public:
inline void Color::SetRed( sal_uInt8 nRed )
{
- mnColor &= 0xFF00FFFF;
- mnColor |= static_cast<sal_uInt32>(nRed)<<16;
+ R = nRed;
}
inline void Color::SetGreen( sal_uInt8 nGreen )
{
- mnColor &= 0xFFFF00FF;
- mnColor |= static_cast<sal_uInt16>(nGreen)<<8;
+ G = nGreen;
}
inline void Color::SetBlue( sal_uInt8 nBlue )
{
- mnColor &= 0xFFFFFF00;
- mnColor |= nBlue;
+ B = nBlue;
}
inline void Color::SetTransparency( sal_uInt8 nTransparency )
{
- mnColor &= 0x00FFFFFF;
- mnColor |= static_cast<sal_uInt32>(nTransparency)<<24;
+ A = nTransparency;
}
inline bool Color::IsRGBEqual( const Color& rColor ) const
{
- return COLORDATA_RGB( mnColor ) == COLORDATA_RGB(rColor.mnColor);
+ return COLORDATA_RGB(mValue) == COLORDATA_RGB(rColor.mValue);
}
inline sal_uInt8 Color::GetLuminance() const
{
- return static_cast<sal_uInt8>((COLORDATA_BLUE(mnColor) * 29UL +
- COLORDATA_GREEN(mnColor) * 151UL +
- COLORDATA_RED(mnColor) * 76UL) >> 8);
+ return sal_uInt8((B * 29UL + G * 151UL + R * 76UL) >> 8);
}
constexpr sal_uInt8 ColorChannelMerge(sal_uInt8 nDst, sal_uInt8 nSrc, sal_uInt8 nSrcTrans)
@@ -208,9 +217,9 @@ constexpr sal_uInt8 ColorChannelMerge(sal_uInt8 nDst, sal_uInt8 nSrc, sal_uInt8
inline void Color::Merge( const Color& rMergeColor, sal_uInt8 cTransparency )
{
- SetRed(ColorChannelMerge(COLORDATA_RED(mnColor), COLORDATA_RED(rMergeColor.mnColor), cTransparency));
- SetGreen(ColorChannelMerge(COLORDATA_GREEN(mnColor), COLORDATA_GREEN(rMergeColor.mnColor), cTransparency));
- SetBlue(ColorChannelMerge(COLORDATA_BLUE(mnColor), COLORDATA_BLUE(rMergeColor.mnColor), cTransparency));
+ R = ColorChannelMerge(R, rMergeColor.R, cTransparency);
+ G = ColorChannelMerge(G, rMergeColor.G, cTransparency);
+ B = ColorChannelMerge(B, rMergeColor.B, cTransparency);
}
// to reduce the noise when moving these into and out of Any
diff --git a/tools/CppunitTest_tools_test.mk b/tools/CppunitTest_tools_test.mk
index 3971606c9a9d..ad56d893ae80 100644
--- a/tools/CppunitTest_tools_test.mk
+++ b/tools/CppunitTest_tools_test.mk
@@ -34,6 +34,7 @@ $(eval $(call gb_CppunitTest_add_exception_objects,tools_test, \
$(eval $(call gb_CppunitTest_use_sdk_api,tools_test))
$(eval $(call gb_CppunitTest_use_libraries,tools_test, \
+ basegfx \
sal \
tl \
test \
diff --git a/tools/qa/cppunit/test_color.cxx b/tools/qa/cppunit/test_color.cxx
index 10da454b9497..571a1f37291d 100644
--- a/tools/qa/cppunit/test_color.cxx
+++ b/tools/qa/cppunit/test_color.cxx
@@ -21,17 +21,85 @@ namespace
class Test: public CppUnit::TestFixture
{
public:
+ void testConstruction();
+ void testVariables();
void test_asRGBColor();
void test_readAndWriteStream();
void test_ApplyTintOrShade();
+ void testGetColorError();
+ void testInvert();
+ void testBColor();
CPPUNIT_TEST_SUITE(Test);
+ CPPUNIT_TEST(testConstruction);
+ CPPUNIT_TEST(testVariables);
CPPUNIT_TEST(test_asRGBColor);
CPPUNIT_TEST(test_readAndWriteStream);
CPPUNIT_TEST(test_ApplyTintOrShade);
+ CPPUNIT_TEST(testGetColorError);
+ CPPUNIT_TEST(testInvert);
+ CPPUNIT_TEST(testBColor);
CPPUNIT_TEST_SUITE_END();
};
+void Test::testConstruction()
+{
+ // Compile time construction of the Color and representation as a sal_uInt32
+
+ Color aColor = Color(0xFF, 0xFF, 0x00);
+
+ switch (sal_uInt32(aColor))
+ {
+ case sal_uInt32(Color(0xFF, 0xFF, 0x00)):
+ break;
+ case sal_uInt32(Color(0x00, 0x00, 0xFF, 0xFF)):
+ break;
+ default:
+ CPPUNIT_ASSERT(false);
+ break;
+ }
+}
+
+void Test::testVariables()
+{
+ Color aColor(0x44, 0x88, 0xAA);
+ CPPUNIT_ASSERT_EQUAL(int(0x00), int(aColor.A));
+ CPPUNIT_ASSERT_EQUAL(int(0x44), int(aColor.R));
+ CPPUNIT_ASSERT_EQUAL(int(0x88), int(aColor.G));
+ CPPUNIT_ASSERT_EQUAL(int(0xAA), int(aColor.B));
+ CPPUNIT_ASSERT_EQUAL(int(0x004488AA), int(aColor.mValue));
+
+ aColor.mValue = 0xAABBCCDD;
+ CPPUNIT_ASSERT_EQUAL(int(0xAA), int(aColor.A));
+ CPPUNIT_ASSERT_EQUAL(int(0xBB), int(aColor.R));
+ CPPUNIT_ASSERT_EQUAL(int(0xCC), int(aColor.G));
+ CPPUNIT_ASSERT_EQUAL(int(0xDD), int(aColor.B));
+
+ aColor.A = 0x11;
+ CPPUNIT_ASSERT_EQUAL(int(0x11BBCCDD), int(aColor.mValue));
+
+ aColor.R = 0x22;
+ CPPUNIT_ASSERT_EQUAL(int(0x1122CCDD), int(aColor.mValue));
+
+ aColor.G = 0x33;
+ CPPUNIT_ASSERT_EQUAL(int(0x112233DD), int(aColor.mValue));
+
+ aColor.B = 0x44;
+ CPPUNIT_ASSERT_EQUAL(int(0x11223344), int(aColor.mValue));
+
+ aColor.SetTransparency(0x77);
+ CPPUNIT_ASSERT_EQUAL(int(0x77223344), int(aColor.mValue));
+
+ aColor.SetRed(0x88);
+ CPPUNIT_ASSERT_EQUAL(int(0x77883344), int(aColor.mValue));
+
+ aColor.SetGreen(0x99);
+ CPPUNIT_ASSERT_EQUAL(int(0x77889944), int(aColor.mValue));
+
+ aColor.SetBlue(0xAA);
+ CPPUNIT_ASSERT_EQUAL(int(0x778899AA), int(aColor.mValue));
+}
+
void Test::test_asRGBColor()
{
Color aColor;
@@ -131,6 +199,62 @@ void Test::test_ApplyTintOrShade()
CPPUNIT_ASSERT_EQUAL(OUString("404040"), createTintShade(0x80, 0x80, 0x80, "808080", -5000));
// 100% shade
CPPUNIT_ASSERT_EQUAL(OUString("000000"), createTintShade(0x80, 0x80, 0x80, "808080", -10000));
+}
+
+void Test::testGetColorError()
+{
+ CPPUNIT_ASSERT_EQUAL(sal_uInt8(0), Color(0xAA, 0xBB, 0xCC).GetColorError(Color(0xAA, 0xBB, 0xCC)));
+
+ CPPUNIT_ASSERT_EQUAL(sal_uInt8(0), Color(0xA0, 0xB0, 0xC0).GetColorError(Color(0xA1, 0xB0, 0xC0)));
+ CPPUNIT_ASSERT_EQUAL(sal_uInt8(0), Color(0xA0, 0xB0, 0xC0).GetColorError(Color(0xA0, 0xB1, 0xC0)));
+ CPPUNIT_ASSERT_EQUAL(sal_uInt8(0), Color(0xA0, 0xB0, 0xC0).GetColorError(Color(0xA0, 0xB0, 0xC1)));
+
+ CPPUNIT_ASSERT_EQUAL(sal_uInt8(1), Color(0xA0, 0xB0, 0xC0).GetColorError(Color(0xA1, 0xB1, 0xC0)));
+ CPPUNIT_ASSERT_EQUAL(sal_uInt8(1), Color(0xA0, 0xB0, 0xC0).GetColorError(Color(0xA0, 0xB1, 0xC1)));
+ CPPUNIT_ASSERT_EQUAL(sal_uInt8(1), Color(0xA0, 0xB0, 0xC0).GetColorError(Color(0xA1, 0xB0, 0xC1)));
+
+ CPPUNIT_ASSERT_EQUAL(sal_uInt8(1), Color(0xA0, 0xB0, 0xC0).GetColorError(Color(0xA1, 0xB1, 0xC1)));
+ CPPUNIT_ASSERT_EQUAL(sal_uInt8(1), Color(0xA0, 0xB0, 0xC0).GetColorError(Color(0xA1, 0xB1, 0xC1)));
+ CPPUNIT_ASSERT_EQUAL(sal_uInt8(1), Color(0xA0, 0xB0, 0xC0).GetColorError(Color(0xA1, 0xB1, 0xC1)));
+}
+
+void Test::testInvert()
+{
+ Color aColor = Color(0xFF, 0x00, 0x88);
+ aColor.Invert();
+ CPPUNIT_ASSERT_EQUAL(Color(0x00, 0xFF, 0x77).AsRGBHexString(), aColor.AsRGBHexString());
+
+ // Alpha should be unaffected
+ aColor = Color(0x22, 0xFF, 0x00, 0x88);
+ aColor.Invert();
+ CPPUNIT_ASSERT_EQUAL(Color(0x22, 0x00, 0xFF, 0x77).AsRGBHexString(), aColor.AsRGBHexString());
+}
+
+void Test::testBColor()
+{
+ Color aColor;
+
+ aColor = Color(basegfx::BColor(0.0, 0.0, 0.0));
+
+ CPPUNIT_ASSERT_EQUAL(Color(0x00, 0x00, 0x00).AsRGBHexString(), aColor.AsRGBHexString());
+ CPPUNIT_ASSERT_EQUAL(0.0, aColor.getBColor().getRed());
+ CPPUNIT_ASSERT_EQUAL(0.0, aColor.getBColor().getGreen());
+ CPPUNIT_ASSERT_EQUAL(0.0, aColor.getBColor().getBlue());
+
+ aColor = Color(basegfx::BColor(1.0, 1.0, 1.0));
+
+ CPPUNIT_ASSERT_EQUAL(Color(0xFF, 0xFF, 0xFF).AsRGBHexString(), aColor.AsRGBHexString());
+ CPPUNIT_ASSERT_EQUAL(1.0, aColor.getBColor().getRed());
+ CPPUNIT_ASSERT_EQUAL(1.0, aColor.getBColor().getGreen());
+ CPPUNIT_ASSERT_EQUAL(1.0, aColor.getBColor().getBlue());
+
+ aColor = Color(basegfx::BColor(0.5, 0.25, 0.125));
+
+ CPPUNIT_ASSERT_EQUAL(Color(0x80, 0x40, 0x20).AsRGBHexString(), aColor.AsRGBHexString());
+ // FP error is rather big, but that's normal
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(0.500, aColor.getBColor().getRed(), 1E-2);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(0.250, aColor.getBColor().getGreen(), 1E-2);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(0.125, aColor.getBColor().getBlue(), 1E-2);
}
diff --git a/tools/source/generic/color.cxx b/tools/source/generic/color.cxx
index 4c26d76e98c8..4bb5221c13d6 100644
--- a/tools/source/generic/color.cxx
+++ b/tools/source/generic/color.cxx
@@ -31,25 +31,25 @@
sal_uInt8 Color::GetColorError( const Color& rCompareColor ) const
{
- const long nErrAbs = labs( static_cast<long>(rCompareColor.GetRed()) - GetRed() ) +
- labs( static_cast<long>(rCompareColor.GetGreen()) - GetGreen() ) +
- labs( static_cast<long>(rCompareColor.GetBlue()) - GetBlue() );
+ const long nErrAbs = labs(long(rCompareColor.R) - R) +
+ labs(long(rCompareColor.G) - G) +
+ labs(long(rCompareColor.B) - B);
return static_cast<sal_uInt8>(FRound( nErrAbs * 0.3333333333 ));
}
-void Color::IncreaseLuminance( sal_uInt8 cLumInc )
+void Color::IncreaseLuminance(sal_uInt8 cLumInc)
{
- SetRed( static_cast<sal_uInt8>(std::clamp( static_cast<long>(COLORDATA_RED( mnColor )) + cLumInc, 0L, 255L )) );
- SetGreen( static_cast<sal_uInt8>(std::clamp( static_cast<long>(COLORDATA_GREEN( mnColor )) + cLumInc, 0L, 255L )) );
- SetBlue( static_cast<sal_uInt8>(std::clamp( static_cast<long>(COLORDATA_BLUE( mnColor )) + cLumInc, 0L, 255L )) );
+ R = sal_uInt8(std::clamp(long(R) + cLumInc, 0L, 255L));
+ G = sal_uInt8(std::clamp(long(G) + cLumInc, 0L, 255L));
+ B = sal_uInt8(std::clamp(long(B) + cLumInc, 0L, 255L));
}
-void Color::DecreaseLuminance( sal_uInt8 cLumDec )
+void Color::DecreaseLuminance(sal_uInt8 cLumDec)
{
- SetRed( static_cast<sal_uInt8>(std::clamp( static_cast<long>(COLORDATA_RED( mnColor )) - cLumDec, 0L, 255L )) );
- SetGreen( static_cast<sal_uInt8>(std::clamp( static_cast<long>(COLORDATA_GREEN( mnColor )) - cLumDec, 0L, 255L )) );
- SetBlue( static_cast<sal_uInt8>(std::clamp( static_cast<long>(COLORDATA_BLUE( mnColor )) - cLumDec, 0L, 255L )) );
+ R = sal_uInt8(std::clamp(long(R) - cLumDec, 0L, 255L));
+ G = sal_uInt8(std::clamp(long(G) - cLumDec, 0L, 255L));
+ B = sal_uInt8(std::clamp(long(B) - cLumDec, 0L, 255L));
}
void Color::DecreaseContrast( sal_uInt8 cContDec )
@@ -59,17 +59,17 @@ void Color::DecreaseContrast( sal_uInt8 cContDec )
const double fM = ( 128.0 - 0.4985 * cContDec ) / 128.0;
const double fOff = 128.0 - fM * 128.0;
- SetRed( static_cast<sal_uInt8>(std::clamp( FRound( COLORDATA_RED( mnColor ) * fM + fOff ), 0L, 255L )) );
- SetGreen( static_cast<sal_uInt8>(std::clamp( FRound( COLORDATA_GREEN( mnColor ) * fM + fOff ), 0L, 255L )) );
- SetBlue( static_cast<sal_uInt8>(std::clamp( FRound( COLORDATA_BLUE( mnColor ) * fM + fOff ), 0L, 255L )) );
+ R = sal_uInt8(std::clamp(FRound(R * fM + fOff), 0L, 255L));
+ G = sal_uInt8(std::clamp(FRound(G * fM + fOff), 0L, 255L));
+ B = sal_uInt8(std::clamp(FRound(B * fM + fOff), 0L, 255L));
}
}
void Color::Invert()
{
- SetRed( ~COLORDATA_RED( mnColor ) );
- SetGreen( ~COLORDATA_GREEN( mnColor ) );
- SetBlue( ~COLORDATA_BLUE( mnColor ) );
+ R = ~R;
+ G = ~G;
+ B = ~B;
}
bool Color::IsDark() const
@@ -89,9 +89,9 @@ void Color::RGBtoHSB( sal_uInt16& nHue, sal_uInt16& nSat, sal_uInt16& nBri ) con
sal_uInt8 c[3];
sal_uInt8 cMax, cMin;
- c[0] = GetRed();
- c[1] = GetGreen();
- c[2] = GetBlue();
+ c[0] = R;
+ c[1] = G;
+ c[2] = B;
cMax = c[0];
if( c[1] > cMax )
@@ -186,13 +186,13 @@ Color Color::HSBtoRGB( sal_uInt16 nHue, sal_uInt16 nSat, sal_uInt16 nBri )
SvStream& Color::Read( SvStream& rIStm )
{
- rIStm.ReadUInt32( mnColor );
+ rIStm.ReadUInt32(mValue);
return rIStm;
}
SvStream& Color::Write( SvStream& rOStm ) const
{
- rOStm.WriteUInt32( mnColor );
+ rOStm.WriteUInt32(mValue);
return rOStm;
}
@@ -290,9 +290,9 @@ void Color::ApplyTintOrShade(sal_Int16 n100thPercent)
aBColor.setBlue(fResult);
aBColor = basegfx::utils::hsl2rgb(aBColor);
- SetRed(sal_uInt8(( aBColor.getRed() * 255.0) + 0.5));
- SetGreen(sal_uInt8((aBColor.getGreen() * 255.0) + 0.5));
- SetBlue(sal_uInt8(( aBColor.getBlue() * 255.0) + 0.5));
+ R = sal_uInt8(std::lround(aBColor.getRed() * 255.0));
+ G = sal_uInt8(std::lround(aBColor.getGreen() * 255.0));
+ B = sal_uInt8(std::lround(aBColor.getBlue() * 255.0));
}
SvStream& WriteColor( SvStream& rOStream, const Color& rColor )
More information about the Libreoffice-commits
mailing list