diff options
author | Neil <nyamatongwe@gmail.com> | 2021-09-15 09:36:45 +1000 |
---|---|---|
committer | Neil <nyamatongwe@gmail.com> | 2021-09-15 09:36:45 +1000 |
commit | d4a5d7d6955982496ba523a439fc98276a28e2d8 (patch) | |
tree | f9cd5e2a57e247dc59f47404cfea8469cc74b35c | |
parent | da71ace9e135d3494fafa7d67483ff1c217faa04 (diff) | |
download | scintilla-mirror-d4a5d7d6955982496ba523a439fc98276a28e2d8.tar.gz |
Move colour mixing implementations into implementation file.
Avoids some warnings but drops constexpr.
Use MixedWith in PlatWin for GDI instead of local implementation.
Add unit tests for Geometry.
-rw-r--r-- | src/Geometry.cxx | 24 | ||||
-rw-r--r-- | src/Geometry.h | 20 | ||||
-rw-r--r-- | test/unit/UnitTester.vcxproj | 1 | ||||
-rw-r--r-- | test/unit/makefile | 1 | ||||
-rw-r--r-- | test/unit/test.mak | 1 | ||||
-rw-r--r-- | test/unit/testGeometry.cxx | 233 | ||||
-rw-r--r-- | win32/PlatWin.cxx | 14 |
7 files changed, 263 insertions, 31 deletions
diff --git a/src/Geometry.cxx b/src/Geometry.cxx index afdc5d161..97e99d431 100644 --- a/src/Geometry.cxx +++ b/src/Geometry.cxx @@ -12,6 +12,14 @@ #include "Geometry.h" +namespace { + +constexpr unsigned int Mixed(unsigned char a, unsigned char b, double proportion) noexcept { + return static_cast<unsigned int>(a + proportion * (b - a)); +} + +} + namespace Scintilla::Internal { PRectangle Clamp(PRectangle rc, Edge edge, XYPOSITION position) noexcept { @@ -95,4 +103,20 @@ PRectangle PixelAlignOutside(const PRectangle &rc, int pixelDivisions) noexcept std::floor(rc.bottom * pixelDivisions) / pixelDivisions); } +ColourRGBA ColourRGBA::MixedWith(ColourRGBA other) const noexcept { + const unsigned int red = (GetRed() + other.GetRed()) / 2; + const unsigned int green = (GetGreen() + other.GetGreen()) / 2; + const unsigned int blue = (GetBlue() + other.GetBlue()) / 2; + const unsigned int alpha = (GetAlpha() + other.GetAlpha()) / 2; + return ColourRGBA(red, green, blue, alpha); +} + +ColourRGBA ColourRGBA::MixedWith(ColourRGBA other, double proportion) const noexcept { + return ColourRGBA( + Mixed(GetRed(), other.GetRed(), proportion), + Mixed(GetGreen(), other.GetGreen(), proportion), + Mixed(GetBlue(), other.GetBlue(), proportion), + Mixed(GetAlpha(), other.GetAlpha(), proportion)); +} + } diff --git a/src/Geometry.h b/src/Geometry.h index 28241c5a9..5a9b3e291 100644 --- a/src/Geometry.h +++ b/src/Geometry.h @@ -161,9 +161,6 @@ PRectangle PixelAlignOutside(const PRectangle &rc, int pixelDivisions) noexcept; constexpr const float componentMaximum = 255.0f; class ColourRGBA { int co; - constexpr static unsigned int Mixed(unsigned char a, unsigned char b, double proportion) noexcept { - return static_cast<unsigned int>(a + proportion * (b - a)); - } public: constexpr explicit ColourRGBA(int co_ = 0) noexcept : co(co_) { } @@ -236,21 +233,8 @@ public: return GetAlpha() == 0xff; } - constexpr ColourRGBA MixedWith(ColourRGBA other) const noexcept { - const unsigned int red = (GetRed() + other.GetRed()) / 2; - const unsigned int green = (GetGreen() + other.GetGreen()) / 2; - const unsigned int blue = (GetBlue() + other.GetBlue()) / 2; - const unsigned int alpha = (GetAlpha() + other.GetAlpha()) / 2; - return ColourRGBA(red, green, blue, alpha); - } - - constexpr ColourRGBA MixedWith(ColourRGBA other, double proportion) const noexcept { - return ColourRGBA( - Mixed(GetRed(), other.GetRed(), proportion), - Mixed(GetGreen(), other.GetGreen(), proportion), - Mixed(GetBlue(), other.GetBlue(), proportion), - Mixed(GetAlpha(), other.GetAlpha(), proportion)); - } + ColourRGBA MixedWith(ColourRGBA other) const noexcept; + ColourRGBA MixedWith(ColourRGBA other, double proportion) const noexcept; }; /** diff --git a/test/unit/UnitTester.vcxproj b/test/unit/UnitTester.vcxproj index 2e130ec3d..8d41cf383 100644 --- a/test/unit/UnitTester.vcxproj +++ b/test/unit/UnitTester.vcxproj @@ -158,6 +158,7 @@ <ClCompile Include="..\..\src\ContractionState.cxx" />
<ClCompile Include="..\..\src\Decoration.cxx" />
<ClCompile Include="..\..\src\Document.cxx" />
+ <ClCompile Include="..\..\src\Geometry.cxx" />
<ClCompile Include="..\..\src\PerLine.cxx" />
<ClCompile Include="..\..\src\RESearch.cxx" />
<ClCompile Include="..\..\src\RunStyles.cxx" />
diff --git a/test/unit/makefile b/test/unit/makefile index fad312708..572ea1443 100644 --- a/test/unit/makefile +++ b/test/unit/makefile @@ -61,6 +61,7 @@ TESTEDSRC=\ ../../src/ContractionState.cxx \ ../../src/Decoration.cxx \ ../../src/Document.cxx \ + ../../src/Geometry.cxx \ ../../src/PerLine.cxx \ ../../src/RESearch.cxx \ ../../src/RunStyles.cxx \ diff --git a/test/unit/test.mak b/test/unit/test.mak index 9e1c9acf4..f75cfdbd3 100644 --- a/test/unit/test.mak +++ b/test/unit/test.mak @@ -20,6 +20,7 @@ TESTEDSRC=\ ../../src/ContractionState.cxx \ ../../src/Decoration.cxx \ ../../src/Document.cxx \ + ../../src/Geometry.cxx \ ../../src/PerLine.cxx \ ../../src/RESearch.cxx \ ../../src/RunStyles.cxx \ diff --git a/test/unit/testGeometry.cxx b/test/unit/testGeometry.cxx new file mode 100644 index 000000000..1dca8903c --- /dev/null +++ b/test/unit/testGeometry.cxx @@ -0,0 +1,233 @@ +/** @file testDocument.cxx + ** Unit Tests for Scintilla internal data structures + **/ + +#include <cstdint> + +#include "Geometry.h" + +#include "catch.hpp" + +using namespace Scintilla; +using namespace Scintilla::Internal; + +// Test Geometry. + +TEST_CASE("Point") { + + SECTION("Point") { + constexpr Point pt(1.0, 2.0); + REQUIRE(pt.x == 1.0); + REQUIRE(pt.y == 2.0); + + constexpr Point pti = Point::FromInts(1, 2); + REQUIRE(pt == pti); + + constexpr Point pt2 = pt + pt; + REQUIRE(pt != pt2); + + constexpr Point ptBack = pt2 - pt; + REQUIRE(pt == ptBack); + } + +} + +TEST_CASE("Interval") { + + SECTION("Interval") { + constexpr Interval interval { 1.0, 2.0 }; + REQUIRE(interval.left == 1.0); + REQUIRE(interval.right == 2.0); + REQUIRE(interval.Width() == 1.0); + REQUIRE(!interval.Empty()); + + constexpr Interval empty { 4.0, 4.0 }; + REQUIRE(empty.Empty()); + REQUIRE(empty.Width() == 0.0); + REQUIRE(!(interval == empty)); + REQUIRE(!(interval.Intersects(empty))); + + constexpr Interval inside { 1.7, 1.8 }; + REQUIRE(interval.Intersects(inside)); + constexpr Interval straddles { 1.7, 2.8 }; + REQUIRE(interval.Intersects(straddles)); + } + + SECTION("Interval") { + constexpr Interval interval1{ 1.0, 3.0 }; + constexpr Interval interval2{ 2.0, 4.0 }; + REQUIRE(Intersection(interval1, interval2) == Interval{ 2.0, 3.0 }); + } +} + +TEST_CASE("PRectangle") { + + SECTION("PRectangle") { + constexpr PRectangle rc(1.0, 2.0, 3.0, 4.0); + REQUIRE(rc.left == 1.0); + REQUIRE(rc.top == 2.0); + REQUIRE(rc.right == 3.0); + REQUIRE(rc.bottom == 4.0); + REQUIRE(rc.Width() == 2.0); + REQUIRE(rc.Height() == 2.0); + REQUIRE(!rc.Empty()); + + constexpr PRectangle rci = PRectangle::FromInts(1, 2, 3, 4); + REQUIRE(rc == rci); + + constexpr PRectangle rcEmpty; + REQUIRE(rcEmpty.Empty()); + } + + SECTION("Contains") { + constexpr PRectangle rc(1.0, 2.0, 3.0, 4.0); + constexpr Point pt(1.5, 2.5); + REQUIRE(rc.Contains(pt)); + REQUIRE(rc.ContainsWholePixel(pt)); + constexpr Point ptNearEdge(2.9, 2.5); + REQUIRE(rc.Contains(ptNearEdge)); + REQUIRE(!rc.ContainsWholePixel(ptNearEdge)); + constexpr Point ptOutside(1.5, 20.5); + REQUIRE(!rc.Contains(ptOutside)); + REQUIRE(!rc.ContainsWholePixel(ptOutside)); + + constexpr PRectangle rcInside(1.5, 2.0, 2.5, 4.0); + REQUIRE(rc.Contains(rcInside)); + REQUIRE(rc.Intersects(rcInside)); + + constexpr PRectangle rcIntersects(1.5, 2.0, 3.5, 4.0); + REQUIRE(!rc.Contains(rcIntersects)); + REQUIRE(rc.Intersects(rcIntersects)); + + constexpr PRectangle rcSeparate(11.0, 12.0, 13.0, 14.0); + REQUIRE(!rc.Contains(rcSeparate)); + REQUIRE(!rc.Intersects(rcSeparate)); + + constexpr Point ptCentre = rc.Centre(); + REQUIRE(ptCentre == Point(2.0, 3.0)); + } + + SECTION("Move") { + PRectangle rc(1.0, 2.0, 3.0, 4.0); + rc.Move(1.0, 10.0); + REQUIRE(rc == PRectangle(2.0, 12.0, 4.0, 14.0)); + } + + SECTION("Inset") { + constexpr PRectangle rc(1.0, 2.0, 3.0, 4.0); + constexpr PRectangle rcInset = rc.Inset(0.5); + REQUIRE(rcInset == PRectangle(1.5, 2.5, 2.5, 3.5)); + + constexpr PRectangle rcInsetPt = rc.Inset(Point(0.25, 0.5)); + REQUIRE(rcInsetPt == PRectangle(1.25, 2.5, 2.75, 3.5)); + } + + SECTION("Clamp") { + constexpr PRectangle rc(1.0, 2.0, 3.0, 4.0); + + const PRectangle cutBottom = Clamp(rc, Edge::bottom, 3.5); + REQUIRE(cutBottom == PRectangle(1.0, 2.0, 3.0, 3.5)); + + const PRectangle justBottom = Side(rc, Edge::bottom, 0.5); + REQUIRE(justBottom == PRectangle(1.0, 3.5, 3.0, 4.0)); + + constexpr PRectangle rcInset = rc.Inset(0.5); + REQUIRE(rcInset == PRectangle(1.5, 2.5, 2.5, 3.5)); + + constexpr PRectangle rcInsetPt = rc.Inset(Point(0.25, 0.5)); + REQUIRE(rcInsetPt == PRectangle(1.25, 2.5, 2.75, 3.5)); + + const Interval bounds = HorizontalBounds(rcInsetPt); + REQUIRE(bounds == Interval{ 1.25, 2.75 }); + + const PRectangle applyBounds = Intersection(rc, bounds); + REQUIRE(applyBounds == PRectangle(1.25, 2.0, 2.75, 4.0)); + } + + SECTION("PixelAlign") { + // Whole pixels + REQUIRE(PixelAlign(1.0, 1) == 1.0); + REQUIRE(PixelAlignFloor(1.0, 1) == 1.0); + + REQUIRE(PixelAlign(1.25, 1) == 1.0); + REQUIRE(PixelAlignFloor(1.25, 1) == 1.0); + + REQUIRE(PixelAlign(1.5, 1) == 2.0); + REQUIRE(PixelAlignFloor(1.5, 1) == 1.0); + + REQUIRE(PixelAlign(1.75, 1) == 2.0); + REQUIRE(PixelAlignFloor(1.75, 1) == 1.0); + + // Half pixels + REQUIRE(PixelAlign(1.0, 2) == 1.0); + REQUIRE(PixelAlignFloor(1.0, 2) == 1.0); + + REQUIRE(PixelAlign(1.25, 2) == 1.5); + REQUIRE(PixelAlignFloor(1.25, 2) == 1.0); + + REQUIRE(PixelAlign(1.5, 2) == 1.5); + REQUIRE(PixelAlignFloor(1.5, 2) == 1.5); + + REQUIRE(PixelAlign(1.75, 2) == 2.0); + REQUIRE(PixelAlignFloor(1.75, 2) == 1.5); + + REQUIRE(PixelAlign(Point(1.75, 1.25), 2) == Point(2.0, 1.5)); + REQUIRE(PixelAlign(Point(1.5, 1.0), 2) == Point(1.5, 1.0)); + + // x->round, y->floored + REQUIRE(PixelAlign(PRectangle(1.0, 1.25, 1.5, 1.75), 2) == PRectangle(1.0, 1.0, 1.5, 1.5)); + + // x->outside(floor left, ceil right), y->floored + REQUIRE(PixelAlignOutside(PRectangle(1.1, 1.25, 1.6, 1.75), 2) == PRectangle(1.0, 1.0, 2.0, 1.5)); + } + +} + +TEST_CASE("ColourRGBA") { + + SECTION("ColourRGBA") { + constexpr ColourRGBA colour(0x10203040); + constexpr ColourRGBA colourFromRGB(0x40, 0x30, 0x20, 0x10); + + REQUIRE(colour == colourFromRGB); + REQUIRE(colour.GetRed() == 0x40); + REQUIRE(colour.GetGreen() == 0x30); + REQUIRE(colour.GetBlue() == 0x20); + REQUIRE(colour.GetAlpha() == 0x10); + REQUIRE(!colour.IsOpaque()); + REQUIRE(colour.AsInteger() == 0x10203040); + + REQUIRE(ColourRGBA(colour, 0x80) == ColourRGBA(0x40, 0x30, 0x20, 0x80)); + REQUIRE(ColourRGBA::FromRGB(0x203040) == ColourRGBA(0x40, 0x30, 0x20, 0xff)); + REQUIRE(ColourRGBA::FromIpRGB(0x203040) == ColourRGBA(0x40, 0x30, 0x20, 0xff)); + + constexpr ColourRGBA colour01(0x00ff00ff); + REQUIRE(colour01.GetRedComponent() == 1.0); + REQUIRE(colour01.GetGreenComponent() == 0.0); + REQUIRE(colour01.GetBlueComponent() == 1.0); + REQUIRE(colour01.GetAlphaComponent() == 0.0); + + // Opaque colours + constexpr ColourRGBA colourRGB(0xff203040); + REQUIRE(colourRGB.IsOpaque()); + constexpr ColourRGBA colourOpaque(0x40, 0x30, 0x20, 0xff); + REQUIRE(colourRGB == colourOpaque); + + REQUIRE(colourRGB.OpaqueRGB() == 0x203040); + REQUIRE(colourRGB.WithoutAlpha() == ColourRGBA(0x40, 0x30, 0x20, 0)); + } + + SECTION("Mixing") { + constexpr ColourRGBA colourMin(0x10, 0x20, 0x30, 0x40); + constexpr ColourRGBA colourMax(0x30, 0x60, 0x90, 0xC0); + + constexpr ColourRGBA colourMid(0x20, 0x40, 0x60, 0x80); + REQUIRE(colourMin.MixedWith(colourMax) == colourMid); + REQUIRE(colourMin.MixedWith(colourMax, 0.5) == colourMid); + + // 3/4 between min and max + constexpr ColourRGBA colour75(0x28, 0x50, 0x78, 0xA0); + REQUIRE(colourMin.MixedWith(colourMax, 0.75) == colour75); + } + +} diff --git a/win32/PlatWin.cxx b/win32/PlatWin.cxx index 3e966b4d3..c4f08b798 100644 --- a/win32/PlatWin.cxx +++ b/win32/PlatWin.cxx @@ -865,18 +865,6 @@ void DIBSection::SetSymmetric(LONG x, LONG y, DWORD value) noexcept { SetPixel(xSymmetric, ySymmetric, value); } -constexpr unsigned int Proportional(unsigned char a, unsigned char b, XYPOSITION t) noexcept { - return static_cast<unsigned int>(a + t * (b - a)); -} - -ColourRGBA Proportional(ColourRGBA a, ColourRGBA b, XYPOSITION t) noexcept { - return ColourRGBA( - Proportional(a.GetRed(), b.GetRed(), t), - Proportional(a.GetGreen(), b.GetGreen(), t), - Proportional(a.GetBlue(), b.GetBlue(), t), - Proportional(a.GetAlpha(), b.GetAlpha(), t)); -} - ColourRGBA GradientValue(const std::vector<ColourStop> &stops, XYPOSITION proportion) noexcept { for (size_t stop = 0; stop < stops.size() - 1; stop++) { // Loop through each pair of stops @@ -885,7 +873,7 @@ ColourRGBA GradientValue(const std::vector<ColourStop> &stops, XYPOSITION propor if ((proportion >= positionStart) && (proportion <= positionEnd)) { const XYPOSITION proportionInPair = (proportion - positionStart) / (positionEnd - positionStart); - return Proportional(stops[stop].colour, stops[stop + 1].colour, proportionInPair); + return stops[stop].colour.MixedWith(stops[stop + 1].colour, proportionInPair); } } // Loop should always find a value |