Move ConPTY to use til::bitmap (#5024)
## Summary of the Pull Request Moves the ConPTY drawing mechanism (`VtRenderer`) to use the fine-grained `til::bitmap` individual-dirty-bit tracking mechanism instead of coarse-grained rectangle unions to improve drawing performance by dramatically reducing the total area redrawn. ## PR Checklist * [x] Part of #778 and #1064 * [x] I work here * [x] Tests added and updated. * [x] I'm a core contributor ## Detailed Description of the Pull Request / Additional comments - Converted `GetDirtyArea()` interface from `IRenderEngine` to use a vector of `til::rectangle` instead of the `SMALL_RECT` to banhammer inclusive rectangles. - `VtEngine` now holds and operates on the `til::bitmap` for invalidation regions. All invalidation operation functions that used to be embedded inside `VtEngine` are deleted in favor of using the ones in `til::bitmap`. - Updated `VtEngine` tracing to use new `til::bitmap` on trace and the new `to_string()` methods detailed below. - Comparison operators for `til::bitmap` and complementary tests. - Fixed an issue where the dirty rectangle shortcut in `til::bitmap` was set to 0,0,0,0 by default which means that `|=` on it with each `set()` operation was stretching the rectangle from 0,0. Now it's a `std::optional` so it has no value after just being cleared and will build from whatever the first invalidated rectangle is. Complementary tests added. - Optional run caching for `til::bitmap` in the `runs()` method since both VT and DX renderers will likely want to generate the set of runs at the beginning of a frame and refer to them over and over through that frame. Saves the iteration and creation and caches inside `til::bitmap` where the chance of invalidation of the underlying data is known best. It is still possible to iterate manually with `begin()` and `end()` from the outside without caching, if desired. Complementary tests added. - WEX templates added for `til::bitmap` and used in tests. - `translate()` method for `til::bitmap` which will slide the dirty points in the direction specified by a `til::point` and optionally back-fill the uncovered area as dirty. Complementary tests added. - Moves all string generation for `til` types `size`, `point`, `rectangle`, and `some` into a `to_string` method on each object such that it can be used in both ETW tracing scenarios AND in the TAEF templates uniformly. Adds a similar method for `bitmap`. - Add tagging to `_bitmap_const_iterator` such that it appears as a valid **Input Iterator** to STL collections and can be used in a `std::vector` constructor as a range. Adds and cleans up operators on this iterator to match the theoretical requirements for an **Input Iterator**. Complementary tests added. - Add loose operators to `til` which will allow some basic math operations (+, -, *, /) between `til::size` and `til::point` and vice versa. Complementary tests added. Complementary tests added. - Adds operators to `til::rectangle` to allow scaling with basic math operations (+, -, *) versus `til::size` and translation with basic math operations (+, -) against `til::point`. Complementary tests added. - In-place variants of some operations added to assorted `til` objects. Complementary tests added. - Update VT tests to compare invalidation against the new map structure instead of raw rectangles where possible. ## Validation Steps Performed - Wrote additional til Unit Tests for all additional operators and functions added to the project to support this operation - Updated the existing VT renderer tests - Ran perf check
This commit is contained in:
parent
cb3bab4ea8
commit
ca33d895a3
|
@ -347,17 +347,14 @@ void ConptyRoundtripTests::WriteAFewSimpleLines()
|
|||
expectedOutput.push_back("AAA");
|
||||
expectedOutput.push_back("\r\n");
|
||||
expectedOutput.push_back("BBB");
|
||||
expectedOutput.push_back("\r\n");
|
||||
// Here, we're going to emit 3 spaces. The region that got invalidated was a
|
||||
// rectangle from 0,0 to 3,3, so the vt renderer will try to render the
|
||||
// region in between BBB and CCC as well, because it got included in the
|
||||
// rectangle Or() operation.
|
||||
// This behavior should not be seen as binding - if a future optimization
|
||||
// breaks this test, it wouldn't be the worst.
|
||||
expectedOutput.push_back(" ");
|
||||
expectedOutput.push_back("\r\n");
|
||||
// Jump down to the fourth line because emitting spaces didn't do anything
|
||||
// and we will skip to emitting the CCC segment.
|
||||
expectedOutput.push_back("\x1b[4;1H");
|
||||
expectedOutput.push_back("CCC");
|
||||
|
||||
// Cursor goes back on.
|
||||
expectedOutput.push_back("\x1b[?25h");
|
||||
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
|
||||
verifyData(termTb);
|
||||
|
@ -458,14 +455,10 @@ void ConptyRoundtripTests::TestAdvancedWrapping()
|
|||
expectedOutput.push_back(R"(!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnop)");
|
||||
// Without line breaking, write the remaining 20 chars
|
||||
expectedOutput.push_back(R"(qrstuvwxyz{|}~!"#$%&)");
|
||||
// Clear the rest of row 1
|
||||
expectedOutput.push_back("\x1b[K");
|
||||
// This is the hard line break
|
||||
expectedOutput.push_back("\r\n");
|
||||
// Now write row 2 of the buffer
|
||||
expectedOutput.push_back(" 1234567890");
|
||||
// and clear everything after the text, because the buffer is empty.
|
||||
expectedOutput.push_back("\x1b[K");
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
|
||||
verifyBuffer(termTb);
|
||||
|
@ -537,8 +530,6 @@ void ConptyRoundtripTests::TestExactWrappingWithoutSpaces()
|
|||
expectedOutput.push_back("\r\n");
|
||||
// Now write row 2 of the buffer
|
||||
expectedOutput.push_back("1234567890");
|
||||
// and clear everything after the text, because the buffer is empty.
|
||||
expectedOutput.push_back("\x1b[K");
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
|
||||
verifyBuffer(termTb);
|
||||
|
@ -601,8 +592,6 @@ void ConptyRoundtripTests::TestExactWrappingWithSpaces()
|
|||
expectedOutput.push_back("\r\n");
|
||||
// Now write row 2 of the buffer
|
||||
expectedOutput.push_back(" 1234567890");
|
||||
// and clear everything after the text, because the buffer is empty.
|
||||
expectedOutput.push_back("\x1b[K");
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
|
||||
verifyBuffer(termTb);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>UNIT_TESTING;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>INLINE_TEST_METHOD_MARKUP;UNIT_TESTING;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(MSBuildThisFileDirectory)..\packages\Taef.Redist.Wlk.10.51.200127004\build\Taef.Redist.Wlk.targets" Condition="Exists('$(MSBuildThisFileDirectory)..\packages\Taef.Redist.Wlk.10.51.200127004\build\Taef.Redist.Wlk.targets')" />
|
||||
|
|
|
@ -306,17 +306,14 @@ void ConptyOutputTests::WriteAFewSimpleLines()
|
|||
expectedOutput.push_back("AAA");
|
||||
expectedOutput.push_back("\r\n");
|
||||
expectedOutput.push_back("BBB");
|
||||
expectedOutput.push_back("\r\n");
|
||||
// Here, we're going to emit 3 spaces. The region that got invalidated was a
|
||||
// rectangle from 0,0 to 3,3, so the vt renderer will try to render the
|
||||
// region in between BBB and CCC as well, because it got included in the
|
||||
// rectangle Or() operation.
|
||||
// This behavior should not be seen as binding - if a future optimization
|
||||
// breaks this test, it wouldn't be the worst.
|
||||
expectedOutput.push_back(" ");
|
||||
expectedOutput.push_back("\r\n");
|
||||
// Jump down to the fourth line because emitting spaces didn't do anything
|
||||
// and we will skip to emitting the CCC segment.
|
||||
expectedOutput.push_back("\x1b[4;1H");
|
||||
expectedOutput.push_back("CCC");
|
||||
|
||||
// Cursor goes back on.
|
||||
expectedOutput.push_back("\x1b[?25h");
|
||||
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
}
|
||||
|
||||
|
|
|
@ -257,21 +257,22 @@ void VtRendererTest::Xterm256TestInvalidate()
|
|||
VERIFY_IS_FALSE(engine->_firstPaint);
|
||||
});
|
||||
|
||||
Viewport view = SetUpViewport();
|
||||
const Viewport view = SetUpViewport();
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Make sure that invalidating all invalidates the whole viewport."));
|
||||
VERIFY_SUCCEEDED(engine->InvalidateAll());
|
||||
TestPaint(*engine, [&]() {
|
||||
VERIFY_ARE_EQUAL(view, engine->_invalidRect);
|
||||
VERIFY_IS_TRUE(engine->_invalidMap.all());
|
||||
});
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Make sure that invalidating anything only invalidates that portion"));
|
||||
SMALL_RECT invalid = { 1, 1, 1, 1 };
|
||||
SMALL_RECT invalid = { 1, 1, 2, 2 };
|
||||
VERIFY_SUCCEEDED(engine->Invalidate(&invalid));
|
||||
TestPaint(*engine, [&]() {
|
||||
VERIFY_ARE_EQUAL(invalid, engine->_invalidRect.ToExclusive());
|
||||
VERIFY_IS_TRUE(engine->_invalidMap.one());
|
||||
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, *(engine->_invalidMap.begin()));
|
||||
});
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
|
@ -284,7 +285,9 @@ void VtRendererTest::Xterm256TestInvalidate()
|
|||
invalid = view.ToExclusive();
|
||||
invalid.Bottom = 1;
|
||||
|
||||
VERIFY_ARE_EQUAL(invalid, engine->_invalidRect.ToExclusive());
|
||||
const auto runs = engine->_invalidMap.runs();
|
||||
VERIFY_ARE_EQUAL(1u, runs.size());
|
||||
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, runs.front());
|
||||
qExpectedInput.push_back("\x1b[H"); // Go Home
|
||||
qExpectedInput.push_back("\x1b[L"); // insert a line
|
||||
|
||||
|
@ -300,7 +303,17 @@ void VtRendererTest::Xterm256TestInvalidate()
|
|||
invalid = view.ToExclusive();
|
||||
invalid.Bottom = 3;
|
||||
|
||||
VERIFY_ARE_EQUAL(invalid, engine->_invalidRect.ToExclusive());
|
||||
// we should have 3 runs and build a rectangle out of them
|
||||
const auto runs = engine->_invalidMap.runs();
|
||||
VERIFY_ARE_EQUAL(3u, runs.size());
|
||||
auto invalidRect = runs.front();
|
||||
for (size_t i = 1; i < runs.size(); ++i)
|
||||
{
|
||||
invalidRect |= runs[i];
|
||||
}
|
||||
|
||||
// verify the rect matches the invalid one.
|
||||
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, invalidRect);
|
||||
// We would expect a CUP here, but the cursor is already at the home position
|
||||
qExpectedInput.push_back("\x1b[3L"); // insert 3 lines
|
||||
VERIFY_SUCCEEDED(engine->ScrollFrame());
|
||||
|
@ -314,7 +327,9 @@ void VtRendererTest::Xterm256TestInvalidate()
|
|||
invalid = view.ToExclusive();
|
||||
invalid.Top = invalid.Bottom - 1;
|
||||
|
||||
VERIFY_ARE_EQUAL(invalid, engine->_invalidRect.ToExclusive());
|
||||
const auto runs = engine->_invalidMap.runs();
|
||||
VERIFY_ARE_EQUAL(1u, runs.size());
|
||||
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, runs.front());
|
||||
|
||||
qExpectedInput.push_back("\x1b[32;1H"); // Bottom of buffer
|
||||
qExpectedInput.push_back("\n"); // Scroll down once
|
||||
|
@ -329,7 +344,17 @@ void VtRendererTest::Xterm256TestInvalidate()
|
|||
invalid = view.ToExclusive();
|
||||
invalid.Top = invalid.Bottom - 3;
|
||||
|
||||
VERIFY_ARE_EQUAL(invalid, engine->_invalidRect.ToExclusive());
|
||||
// we should have 3 runs and build a rectangle out of them
|
||||
const auto runs = engine->_invalidMap.runs();
|
||||
VERIFY_ARE_EQUAL(3u, runs.size());
|
||||
auto invalidRect = runs.front();
|
||||
for (size_t i = 1; i < runs.size(); ++i)
|
||||
{
|
||||
invalidRect |= runs[i];
|
||||
}
|
||||
|
||||
// verify the rect matches the invalid one.
|
||||
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, invalidRect);
|
||||
|
||||
// We would expect a CUP here, but we're already at the bottom from the last call.
|
||||
qExpectedInput.push_back("\n\n\n"); // Scroll down three times
|
||||
|
@ -349,7 +374,18 @@ void VtRendererTest::Xterm256TestInvalidate()
|
|||
invalid = view.ToExclusive();
|
||||
invalid.Bottom = 3;
|
||||
|
||||
VERIFY_ARE_EQUAL(invalid, engine->_invalidRect.ToExclusive());
|
||||
// we should have 3 runs and build a rectangle out of them
|
||||
const auto runs = engine->_invalidMap.runs();
|
||||
VERIFY_ARE_EQUAL(3u, runs.size());
|
||||
auto invalidRect = runs.front();
|
||||
for (size_t i = 1; i < runs.size(); ++i)
|
||||
{
|
||||
invalidRect |= runs[i];
|
||||
}
|
||||
|
||||
// verify the rect matches the invalid one.
|
||||
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, invalidRect);
|
||||
|
||||
qExpectedInput.push_back("\x1b[H"); // Go to home
|
||||
qExpectedInput.push_back("\x1b[3L"); // insert 3 lines
|
||||
VERIFY_SUCCEEDED(engine->ScrollFrame());
|
||||
|
@ -357,21 +393,39 @@ void VtRendererTest::Xterm256TestInvalidate()
|
|||
|
||||
scrollDelta = { 0, 1 };
|
||||
VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta));
|
||||
Log::Comment(NoThrowString().Format(
|
||||
VerifyOutputTraits<SMALL_RECT>::ToString(engine->_invalidRect.ToExclusive())));
|
||||
Log::Comment(engine->_invalidMap.to_string().c_str());
|
||||
|
||||
scrollDelta = { 0, -1 };
|
||||
VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta));
|
||||
Log::Comment(NoThrowString().Format(
|
||||
VerifyOutputTraits<SMALL_RECT>::ToString(engine->_invalidRect.ToExclusive())));
|
||||
Log::Comment(engine->_invalidMap.to_string().c_str());
|
||||
|
||||
qExpectedInput.push_back("\x1b[2J");
|
||||
TestPaint(*engine, [&]() {
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"---- Scrolled one down and one up, nothing should change ----"
|
||||
L" But it still does for now MSFT:14169294"));
|
||||
invalid = view.ToExclusive();
|
||||
VERIFY_ARE_EQUAL(invalid, engine->_invalidRect.ToExclusive());
|
||||
|
||||
const auto runs = engine->_invalidMap.runs();
|
||||
auto invalidRect = runs.front();
|
||||
for (size_t i = 1; i < runs.size(); ++i)
|
||||
{
|
||||
invalidRect |= runs[i];
|
||||
}
|
||||
|
||||
// only the bottom line should be dirty.
|
||||
// When we scrolled down, the bitmap looked like this:
|
||||
// 1111
|
||||
// 0000
|
||||
// 0000
|
||||
// 0000
|
||||
// And then we scrolled up and the top line fell off and a bottom
|
||||
// line was filled in like this:
|
||||
// 0000
|
||||
// 0000
|
||||
// 0000
|
||||
// 1111
|
||||
const til::rectangle expected{ til::point{ view.Left(), view.BottomInclusive() }, til::size{ view.Width(), 1 } };
|
||||
VERIFY_ARE_EQUAL(expected, invalidRect);
|
||||
|
||||
VERIFY_SUCCEEDED(engine->ScrollFrame());
|
||||
});
|
||||
|
@ -730,15 +784,16 @@ void VtRendererTest::XtermTestInvalidate()
|
|||
L"Make sure that invalidating all invalidates the whole viewport."));
|
||||
VERIFY_SUCCEEDED(engine->InvalidateAll());
|
||||
TestPaint(*engine, [&]() {
|
||||
VERIFY_ARE_EQUAL(view, engine->_invalidRect);
|
||||
VERIFY_IS_TRUE(engine->_invalidMap.all());
|
||||
});
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Make sure that invalidating anything only invalidates that portion"));
|
||||
SMALL_RECT invalid = { 1, 1, 1, 1 };
|
||||
SMALL_RECT invalid = { 1, 1, 2, 2 };
|
||||
VERIFY_SUCCEEDED(engine->Invalidate(&invalid));
|
||||
TestPaint(*engine, [&]() {
|
||||
VERIFY_ARE_EQUAL(invalid, engine->_invalidRect.ToExclusive());
|
||||
VERIFY_IS_TRUE(engine->_invalidMap.one());
|
||||
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, *(engine->_invalidMap.begin()));
|
||||
});
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
|
@ -751,7 +806,9 @@ void VtRendererTest::XtermTestInvalidate()
|
|||
invalid = view.ToExclusive();
|
||||
invalid.Bottom = 1;
|
||||
|
||||
VERIFY_ARE_EQUAL(invalid, engine->_invalidRect.ToExclusive());
|
||||
const auto runs = engine->_invalidMap.runs();
|
||||
VERIFY_ARE_EQUAL(1u, runs.size());
|
||||
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, runs.front());
|
||||
|
||||
qExpectedInput.push_back("\x1b[H"); // Go Home
|
||||
qExpectedInput.push_back("\x1b[L"); // insert a line
|
||||
|
@ -766,7 +823,17 @@ void VtRendererTest::XtermTestInvalidate()
|
|||
invalid = view.ToExclusive();
|
||||
invalid.Bottom = 3;
|
||||
|
||||
VERIFY_ARE_EQUAL(invalid, engine->_invalidRect.ToExclusive());
|
||||
// we should have 3 runs and build a rectangle out of them
|
||||
const auto runs = engine->_invalidMap.runs();
|
||||
VERIFY_ARE_EQUAL(3u, runs.size());
|
||||
auto invalidRect = runs.front();
|
||||
for (size_t i = 1; i < runs.size(); ++i)
|
||||
{
|
||||
invalidRect |= runs[i];
|
||||
}
|
||||
|
||||
// verify the rect matches the invalid one.
|
||||
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, invalidRect);
|
||||
// We would expect a CUP here, but the cursor is already at the home position
|
||||
qExpectedInput.push_back("\x1b[3L"); // insert 3 lines
|
||||
VERIFY_SUCCEEDED(engine->ScrollFrame());
|
||||
|
@ -780,7 +847,9 @@ void VtRendererTest::XtermTestInvalidate()
|
|||
invalid = view.ToExclusive();
|
||||
invalid.Top = invalid.Bottom - 1;
|
||||
|
||||
VERIFY_ARE_EQUAL(invalid, engine->_invalidRect.ToExclusive());
|
||||
const auto runs = engine->_invalidMap.runs();
|
||||
VERIFY_ARE_EQUAL(1u, runs.size());
|
||||
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, runs.front());
|
||||
|
||||
qExpectedInput.push_back("\x1b[32;1H"); // Bottom of buffer
|
||||
qExpectedInput.push_back("\n"); // Scroll down once
|
||||
|
@ -795,7 +864,17 @@ void VtRendererTest::XtermTestInvalidate()
|
|||
invalid = view.ToExclusive();
|
||||
invalid.Top = invalid.Bottom - 3;
|
||||
|
||||
VERIFY_ARE_EQUAL(invalid, engine->_invalidRect.ToExclusive());
|
||||
// we should have 3 runs and build a rectangle out of them
|
||||
const auto runs = engine->_invalidMap.runs();
|
||||
VERIFY_ARE_EQUAL(3u, runs.size());
|
||||
auto invalidRect = runs.front();
|
||||
for (size_t i = 1; i < runs.size(); ++i)
|
||||
{
|
||||
invalidRect |= runs[i];
|
||||
}
|
||||
|
||||
// verify the rect matches the invalid one.
|
||||
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, invalidRect);
|
||||
|
||||
// We would expect a CUP here, but we're already at the bottom from the last call.
|
||||
qExpectedInput.push_back("\n\n\n"); // Scroll down three times
|
||||
|
@ -815,7 +894,18 @@ void VtRendererTest::XtermTestInvalidate()
|
|||
invalid = view.ToExclusive();
|
||||
invalid.Bottom = 3;
|
||||
|
||||
VERIFY_ARE_EQUAL(invalid, engine->_invalidRect.ToExclusive());
|
||||
// we should have 3 runs and build a rectangle out of them
|
||||
const auto runs = engine->_invalidMap.runs();
|
||||
VERIFY_ARE_EQUAL(3u, runs.size());
|
||||
auto invalidRect = runs.front();
|
||||
for (size_t i = 1; i < runs.size(); ++i)
|
||||
{
|
||||
invalidRect |= runs[i];
|
||||
}
|
||||
|
||||
// verify the rect matches the invalid one.
|
||||
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, invalidRect);
|
||||
|
||||
qExpectedInput.push_back("\x1b[H"); // Go to home
|
||||
qExpectedInput.push_back("\x1b[3L"); // insert 3 lines
|
||||
VERIFY_SUCCEEDED(engine->ScrollFrame());
|
||||
|
@ -823,21 +913,39 @@ void VtRendererTest::XtermTestInvalidate()
|
|||
|
||||
scrollDelta = { 0, 1 };
|
||||
VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta));
|
||||
Log::Comment(NoThrowString().Format(
|
||||
VerifyOutputTraits<SMALL_RECT>::ToString(engine->_invalidRect.ToExclusive())));
|
||||
Log::Comment(engine->_invalidMap.to_string().c_str());
|
||||
|
||||
scrollDelta = { 0, -1 };
|
||||
VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta));
|
||||
Log::Comment(NoThrowString().Format(
|
||||
VerifyOutputTraits<SMALL_RECT>::ToString(engine->_invalidRect.ToExclusive())));
|
||||
Log::Comment(engine->_invalidMap.to_string().c_str());
|
||||
|
||||
qExpectedInput.push_back("\x1b[2J");
|
||||
TestPaint(*engine, [&]() {
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"---- Scrolled one down and one up, nothing should change ----"
|
||||
L" But it still does for now MSFT:14169294"));
|
||||
invalid = view.ToExclusive();
|
||||
VERIFY_ARE_EQUAL(view, engine->_invalidRect);
|
||||
|
||||
const auto runs = engine->_invalidMap.runs();
|
||||
auto invalidRect = runs.front();
|
||||
for (size_t i = 1; i < runs.size(); ++i)
|
||||
{
|
||||
invalidRect |= runs[i];
|
||||
}
|
||||
|
||||
// only the bottom line should be dirty.
|
||||
// When we scrolled down, the bitmap looked like this:
|
||||
// 1111
|
||||
// 0000
|
||||
// 0000
|
||||
// 0000
|
||||
// And then we scrolled up and the top line fell off and a bottom
|
||||
// line was filled in like this:
|
||||
// 0000
|
||||
// 0000
|
||||
// 0000
|
||||
// 1111
|
||||
const til::rectangle expected{ til::point{ view.Left(), view.BottomInclusive() }, til::size{ view.Width(), 1 } };
|
||||
VERIFY_ARE_EQUAL(expected, invalidRect);
|
||||
|
||||
VERIFY_SUCCEEDED(engine->ScrollFrame());
|
||||
});
|
||||
|
@ -1051,15 +1159,15 @@ void VtRendererTest::WinTelnetTestInvalidate()
|
|||
L"Make sure that invalidating all invalidates the whole viewport."));
|
||||
VERIFY_SUCCEEDED(engine->InvalidateAll());
|
||||
TestPaint(*engine, [&]() {
|
||||
VERIFY_ARE_EQUAL(view, engine->_invalidRect);
|
||||
VERIFY_IS_TRUE(engine->_invalidMap.all());
|
||||
});
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Make sure that invalidating anything only invalidates that portion"));
|
||||
SMALL_RECT invalid = { 1, 1, 1, 1 };
|
||||
SMALL_RECT invalid = { 1, 1, 2, 2 };
|
||||
VERIFY_SUCCEEDED(engine->Invalidate(&invalid));
|
||||
TestPaint(*engine, [&]() {
|
||||
VERIFY_ARE_EQUAL(invalid, engine->_invalidRect.ToExclusive());
|
||||
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, *(engine->_invalidMap.begin()));
|
||||
});
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
|
@ -1067,7 +1175,7 @@ void VtRendererTest::WinTelnetTestInvalidate()
|
|||
COORD scrollDelta = { 0, 1 };
|
||||
VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta));
|
||||
TestPaint(*engine, [&]() {
|
||||
VERIFY_ARE_EQUAL(view, engine->_invalidRect);
|
||||
VERIFY_IS_TRUE(engine->_invalidMap.all());
|
||||
qExpectedInput.push_back(EMPTY_CALLBACK_SENTINEL); // sentinel
|
||||
VERIFY_SUCCEEDED(engine->ScrollFrame());
|
||||
WriteCallback(EMPTY_CALLBACK_SENTINEL, 1); // This will make sure nothing was written to the callback
|
||||
|
@ -1076,7 +1184,7 @@ void VtRendererTest::WinTelnetTestInvalidate()
|
|||
scrollDelta = { 0, -1 };
|
||||
VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta));
|
||||
TestPaint(*engine, [&]() {
|
||||
VERIFY_ARE_EQUAL(view, engine->_invalidRect);
|
||||
VERIFY_IS_TRUE(engine->_invalidMap.all());
|
||||
qExpectedInput.push_back(EMPTY_CALLBACK_SENTINEL);
|
||||
VERIFY_SUCCEEDED(engine->ScrollFrame());
|
||||
WriteCallback(EMPTY_CALLBACK_SENTINEL, 1); // This will make sure nothing was written to the callback
|
||||
|
@ -1085,7 +1193,7 @@ void VtRendererTest::WinTelnetTestInvalidate()
|
|||
scrollDelta = { 1, 0 };
|
||||
VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta));
|
||||
TestPaint(*engine, [&]() {
|
||||
VERIFY_ARE_EQUAL(view, engine->_invalidRect);
|
||||
VERIFY_IS_TRUE(engine->_invalidMap.all());
|
||||
qExpectedInput.push_back(EMPTY_CALLBACK_SENTINEL);
|
||||
VERIFY_SUCCEEDED(engine->ScrollFrame());
|
||||
WriteCallback(EMPTY_CALLBACK_SENTINEL, 1); // This will make sure nothing was written to the callback
|
||||
|
@ -1094,7 +1202,7 @@ void VtRendererTest::WinTelnetTestInvalidate()
|
|||
scrollDelta = { -1, 0 };
|
||||
VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta));
|
||||
TestPaint(*engine, [&]() {
|
||||
VERIFY_ARE_EQUAL(view, engine->_invalidRect);
|
||||
VERIFY_IS_TRUE(engine->_invalidMap.all());
|
||||
qExpectedInput.push_back(EMPTY_CALLBACK_SENTINEL);
|
||||
VERIFY_SUCCEEDED(engine->ScrollFrame());
|
||||
WriteCallback(EMPTY_CALLBACK_SENTINEL, 1); // This will make sure nothing was written to the callback
|
||||
|
@ -1103,7 +1211,7 @@ void VtRendererTest::WinTelnetTestInvalidate()
|
|||
scrollDelta = { 1, -1 };
|
||||
VERIFY_SUCCEEDED(engine->InvalidateScroll(&scrollDelta));
|
||||
TestPaint(*engine, [&]() {
|
||||
VERIFY_ARE_EQUAL(view, engine->_invalidRect);
|
||||
VERIFY_IS_TRUE(engine->_invalidMap.all());
|
||||
qExpectedInput.push_back(EMPTY_CALLBACK_SENTINEL);
|
||||
VERIFY_SUCCEEDED(engine->ScrollFrame());
|
||||
WriteCallback(EMPTY_CALLBACK_SENTINEL, 1); // This will make sure nothing was written to the callback
|
||||
|
@ -1351,7 +1459,7 @@ void VtRendererTest::TestResize()
|
|||
VERIFY_SUCCEEDED(engine->UpdateViewport(newView.ToInclusive()));
|
||||
|
||||
TestPaint(*engine, [&]() {
|
||||
VERIFY_ARE_EQUAL(newView, engine->_invalidRect);
|
||||
VERIFY_IS_TRUE(engine->_invalidMap.all());
|
||||
VERIFY_IS_FALSE(engine->_firstPaint);
|
||||
VERIFY_IS_FALSE(engine->_suppressResizeRepaint);
|
||||
});
|
||||
|
|
|
@ -85,6 +85,12 @@
|
|||
// WRL
|
||||
#include <wrl.h>
|
||||
|
||||
// WEX/TAEF testing
|
||||
// Include before TIL if we're unit testing so it can light up WEX/TAEF template extensions
|
||||
#ifdef UNIT_TESTING
|
||||
#include <WexTestClass.h>
|
||||
#endif
|
||||
|
||||
// TIL - Terminal Implementation Library
|
||||
#ifndef BLOCK_TIL // Certain projects may want to include TIL manually to gain superpowers
|
||||
#include "til.h"
|
||||
|
|
|
@ -3,12 +3,15 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#define _TIL_INLINEPREFIX __declspec(noinline) inline
|
||||
|
||||
#include "til/at.h"
|
||||
#include "til/color.h"
|
||||
#include "til/some.h"
|
||||
#include "til/size.h"
|
||||
#include "til/point.h"
|
||||
#include "til/rectangle.h"
|
||||
#include "til/operators.h"
|
||||
#include "til/bitmap.h"
|
||||
#include "til/u8u16convert.h"
|
||||
|
||||
|
|
|
@ -14,6 +14,12 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
|||
class _bitmap_const_iterator
|
||||
{
|
||||
public:
|
||||
using iterator_category = typename std::input_iterator_tag;
|
||||
using value_type = typename const til::rectangle;
|
||||
using difference_type = typename ptrdiff_t;
|
||||
using pointer = typename const til::rectangle*;
|
||||
using reference = typename const til::rectangle&;
|
||||
|
||||
_bitmap_const_iterator(const std::vector<bool>& values, til::rectangle rc, ptrdiff_t pos) :
|
||||
_values(values),
|
||||
_rc(rc),
|
||||
|
@ -30,6 +36,13 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
|||
return (*this);
|
||||
}
|
||||
|
||||
_bitmap_const_iterator operator++(int)
|
||||
{
|
||||
const auto prev = *this;
|
||||
++*this;
|
||||
return prev;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const _bitmap_const_iterator& other) const noexcept
|
||||
{
|
||||
return _pos == other._pos && _values == other._values;
|
||||
|
@ -50,11 +63,16 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
|||
return _pos > other._pos;
|
||||
}
|
||||
|
||||
constexpr til::rectangle operator*() const noexcept
|
||||
constexpr reference operator*() const noexcept
|
||||
{
|
||||
return _run;
|
||||
}
|
||||
|
||||
constexpr pointer operator->() const noexcept
|
||||
{
|
||||
return &_run;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::vector<bool>& _values;
|
||||
const til::rectangle _rc;
|
||||
|
@ -117,7 +135,8 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
|||
_sz{},
|
||||
_rc{},
|
||||
_bits{},
|
||||
_dirty{}
|
||||
_dirty{},
|
||||
_runs{}
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -130,10 +149,25 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
|||
_sz(sz),
|
||||
_rc(sz),
|
||||
_bits(sz.area(), fill),
|
||||
_dirty(fill ? sz : til::rectangle{})
|
||||
_dirty(fill ? sz : til::rectangle{}),
|
||||
_runs{}
|
||||
{
|
||||
}
|
||||
|
||||
constexpr bool operator==(const bitmap& other) const noexcept
|
||||
{
|
||||
return _sz == other._sz &&
|
||||
_rc == other._rc &&
|
||||
_dirty == other._dirty && // dirty is before bits because it's a rough estimate of bits and a faster comparison.
|
||||
_bits == other._bits;
|
||||
// _runs excluded because it's a cache of generated state.
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const bitmap& other) const noexcept
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
const_iterator begin() const
|
||||
{
|
||||
return const_iterator(_bits, _sz, 0);
|
||||
|
@ -144,17 +178,106 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
|||
return const_iterator(_bits, _sz, _sz.area());
|
||||
}
|
||||
|
||||
const std::vector<til::rectangle>& runs() const
|
||||
{
|
||||
// If we don't have cached runs, rebuild.
|
||||
if (!_runs.has_value())
|
||||
{
|
||||
// If there's only one square dirty, quick save it off and be done.
|
||||
if (one())
|
||||
{
|
||||
_runs.emplace({ _dirty });
|
||||
}
|
||||
else
|
||||
{
|
||||
_runs.emplace(begin(), end());
|
||||
}
|
||||
}
|
||||
|
||||
// Return a reference to the runs.
|
||||
return _runs.value();
|
||||
}
|
||||
|
||||
// optional fill the uncovered area with bits.
|
||||
void translate(const til::point delta, bool fill = false)
|
||||
{
|
||||
// FUTURE: PERF: GH #4015: This could use in-place walk semantics instead of a temporary.
|
||||
til::bitmap other{ _sz };
|
||||
|
||||
for (auto run : *this)
|
||||
{
|
||||
// Offset by the delta
|
||||
run += delta;
|
||||
|
||||
// Intersect with the bounds of our bitmap area
|
||||
// as part of it could have slid out of bounds.
|
||||
run &= _rc;
|
||||
|
||||
// Set it into the new bitmap.
|
||||
other.set(run);
|
||||
}
|
||||
|
||||
// If we were asked to fill... find the uncovered region.
|
||||
if (fill)
|
||||
{
|
||||
// Original Rect of As.
|
||||
//
|
||||
// X <-- origin
|
||||
// A A A A
|
||||
// A A A A
|
||||
// A A A A
|
||||
// A A A A
|
||||
const auto originalRect = _rc;
|
||||
|
||||
// If Delta = (2, 2)
|
||||
// Translated Rect of Bs.
|
||||
//
|
||||
// X <-- origin
|
||||
//
|
||||
//
|
||||
// B B B B
|
||||
// B B B B
|
||||
// B B B B
|
||||
// B B B B
|
||||
const auto translatedRect = _rc + delta;
|
||||
|
||||
// Subtract the B from the A one to see what wasn't filled by the move.
|
||||
// C is the overlap of A and B:
|
||||
//
|
||||
// X <-- origin
|
||||
// A A A A 1 1 1 1
|
||||
// A A A A 1 1 1 1
|
||||
// A A C C B B subtract 2 2
|
||||
// A A C C B B ---------> 2 2
|
||||
// B B B B A - B
|
||||
// B B B B
|
||||
//
|
||||
// 1 and 2 are the spaces to fill that are "uncovered".
|
||||
const auto fillRects = originalRect - translatedRect;
|
||||
for (const auto& f : fillRects)
|
||||
{
|
||||
other.set(f);
|
||||
}
|
||||
}
|
||||
|
||||
// Swap us with the temporary one.
|
||||
std::swap(other, *this);
|
||||
}
|
||||
|
||||
void set(const til::point pt)
|
||||
{
|
||||
THROW_HR_IF(E_INVALIDARG, !_rc.contains(pt));
|
||||
_runs.reset(); // reset cached runs on any non-const method
|
||||
|
||||
til::at(_bits, _rc.index_of(pt)) = true;
|
||||
|
||||
_dirty |= til::rectangle{ pt };
|
||||
}
|
||||
|
||||
void set(const til::rectangle rc)
|
||||
{
|
||||
THROW_HR_IF(E_INVALIDARG, !_rc.contains(rc));
|
||||
_runs.reset(); // reset cached runs on any non-const method
|
||||
|
||||
for (const auto pt : rc)
|
||||
{
|
||||
|
@ -166,6 +289,8 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
|||
|
||||
void set_all()
|
||||
{
|
||||
_runs.reset(); // reset cached runs on any non-const method
|
||||
|
||||
// .clear() then .resize(_size(), true) throws an assert (unsupported operation)
|
||||
// .assign(_size(), true) throws an assert (unsupported operation)
|
||||
_bits = std::vector<bool>(_sz.area(), true);
|
||||
|
@ -174,6 +299,8 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
|||
|
||||
void reset_all()
|
||||
{
|
||||
_runs.reset(); // reset cached runs on any non-const method
|
||||
|
||||
// .clear() then .resize(_size(), false) throws an assert (unsupported operation)
|
||||
// .assign(_size(), false) throws an assert (unsupported operation)
|
||||
|
||||
|
@ -185,6 +312,8 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
|||
// Set fill if you want the new region (on growing) to be marked dirty.
|
||||
bool resize(til::size size, bool fill = false)
|
||||
{
|
||||
_runs.reset(); // reset cached runs on any non-const method
|
||||
|
||||
// FYI .resize(_size(), true/false) throws an assert (unsupported operation)
|
||||
|
||||
// Don't resize if it's not different
|
||||
|
@ -254,14 +383,71 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
|||
return _dirty == _rc;
|
||||
}
|
||||
|
||||
std::wstring to_string() const
|
||||
{
|
||||
std::wstringstream wss;
|
||||
wss << std::endl
|
||||
<< L"Bitmap of size " << _sz.to_string() << " contains the following dirty regions:" << std::endl;
|
||||
wss << L"Runs:" << std::endl;
|
||||
|
||||
for (auto& item : *this)
|
||||
{
|
||||
wss << L"\t- " << item.to_string() << std::endl;
|
||||
}
|
||||
|
||||
return wss.str();
|
||||
}
|
||||
|
||||
private:
|
||||
til::rectangle _dirty;
|
||||
til::size _sz;
|
||||
til::rectangle _rc;
|
||||
std::vector<bool> _bits;
|
||||
|
||||
mutable std::optional<std::vector<til::rectangle>> _runs;
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class ::BitmapTests;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
#ifdef __WEX_COMMON_H__
|
||||
namespace WEX::TestExecution
|
||||
{
|
||||
template<>
|
||||
class VerifyOutputTraits<::til::bitmap>
|
||||
{
|
||||
public:
|
||||
static WEX::Common::NoThrowString ToString(const ::til::bitmap& rect)
|
||||
{
|
||||
return WEX::Common::NoThrowString(rect.to_string().c_str());
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class VerifyCompareTraits<::til::bitmap, ::til::bitmap>
|
||||
{
|
||||
public:
|
||||
static bool AreEqual(const ::til::bitmap& expected, const ::til::bitmap& actual) noexcept
|
||||
{
|
||||
return expected == actual;
|
||||
}
|
||||
|
||||
static bool AreSame(const ::til::bitmap& expected, const ::til::bitmap& actual) noexcept
|
||||
{
|
||||
return &expected == &actual;
|
||||
}
|
||||
|
||||
static bool IsLessThan(const ::til::bitmap& expectedLess, const ::til::bitmap& expectedGreater) = delete;
|
||||
|
||||
static bool IsGreaterThan(const ::til::bitmap& expectedGreater, const ::til::bitmap& expectedLess) = delete;
|
||||
|
||||
static bool IsNull(const ::til::bitmap& object) noexcept
|
||||
{
|
||||
return object == til::bitmap{};
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
#endif
|
||||
|
|
59
src/inc/til/operators.h
Normal file
59
src/inc/til/operators.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rectangle.h"
|
||||
#include "size.h"
|
||||
#include "bitmap.h"
|
||||
|
||||
namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
{
|
||||
// Operators go here when they involve two headers that can't/don't include each other.
|
||||
|
||||
#pragma region POINT VS SIZE
|
||||
// This is a convenience and will take X vs WIDTH and Y vs HEIGHT.
|
||||
_TIL_INLINEPREFIX point operator+(const point& lhs, const size& rhs)
|
||||
{
|
||||
return lhs + til::point{ rhs.width(), rhs.height() };
|
||||
}
|
||||
|
||||
_TIL_INLINEPREFIX point operator-(const point& lhs, const size& rhs)
|
||||
{
|
||||
return lhs - til::point{ rhs.width(), rhs.height() };
|
||||
}
|
||||
|
||||
_TIL_INLINEPREFIX point operator*(const point& lhs, const size& rhs)
|
||||
{
|
||||
return lhs * til::point{ rhs.width(), rhs.height() };
|
||||
}
|
||||
|
||||
_TIL_INLINEPREFIX point operator/(const point& lhs, const size& rhs)
|
||||
{
|
||||
return lhs / til::point{ rhs.width(), rhs.height() };
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
#pragma region SIZE VS POINT
|
||||
// This is a convenience and will take WIDTH vs X and HEIGHT vs Y.
|
||||
_TIL_INLINEPREFIX size operator+(const size& lhs, const point& rhs)
|
||||
{
|
||||
return lhs + til::size(rhs.x(), rhs.y());
|
||||
}
|
||||
|
||||
_TIL_INLINEPREFIX size operator-(const size& lhs, const point& rhs)
|
||||
{
|
||||
return lhs - til::size(rhs.x(), rhs.y());
|
||||
}
|
||||
|
||||
_TIL_INLINEPREFIX size operator*(const size& lhs, const point& rhs)
|
||||
{
|
||||
return lhs * til::size(rhs.x(), rhs.y());
|
||||
}
|
||||
|
||||
_TIL_INLINEPREFIX size operator/(const size& lhs, const point& rhs)
|
||||
{
|
||||
return lhs / til::size(rhs.x(), rhs.y());
|
||||
}
|
||||
#pragma endregion
|
||||
}
|
|
@ -193,6 +193,11 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
|||
}
|
||||
#endif
|
||||
|
||||
std::wstring to_string() const
|
||||
{
|
||||
return wil::str_printf<std::wstring>(L"(X:%td, Y:%td)", x(), y());
|
||||
}
|
||||
|
||||
protected:
|
||||
ptrdiff_t _x;
|
||||
ptrdiff_t _y;
|
||||
|
@ -212,7 +217,7 @@ namespace WEX::TestExecution
|
|||
public:
|
||||
static WEX::Common::NoThrowString ToString(const ::til::point& point)
|
||||
{
|
||||
return WEX::Common::NoThrowString().Format(L"(X:%td, Y:%td)", point.x(), point.y());
|
||||
return WEX::Common::NoThrowString(point.to_string().c_str());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -212,6 +212,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
|||
return const_iterator(_topLeft, _bottomRight, { _topLeft.x(), _bottomRight.y() });
|
||||
}
|
||||
|
||||
#pragma region RECTANGLE OPERATORS
|
||||
// OR = union
|
||||
constexpr rectangle operator|(const rectangle& other) const noexcept
|
||||
{
|
||||
|
@ -274,6 +275,12 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
|||
return rectangle{ l, t, r, b };
|
||||
}
|
||||
|
||||
constexpr rectangle& operator&=(const rectangle& other) noexcept
|
||||
{
|
||||
*this = *this & other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// - = subtract
|
||||
some<rectangle, 4> operator-(const rectangle& other) const
|
||||
{
|
||||
|
@ -405,6 +412,231 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
|||
|
||||
return result;
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
#pragma region RECTANGLE VS POINT
|
||||
// ADD will translate (offset) the rectangle by the point.
|
||||
rectangle operator+(const point& point) const
|
||||
{
|
||||
ptrdiff_t l, t, r, b;
|
||||
|
||||
THROW_HR_IF(E_ABORT, !::base::CheckAdd(left(), point.x()).AssignIfValid(&l));
|
||||
THROW_HR_IF(E_ABORT, !::base::CheckAdd(top(), point.y()).AssignIfValid(&t));
|
||||
THROW_HR_IF(E_ABORT, !::base::CheckAdd(right(), point.x()).AssignIfValid(&r));
|
||||
THROW_HR_IF(E_ABORT, !::base::CheckAdd(bottom(), point.y()).AssignIfValid(&b));
|
||||
|
||||
return til::rectangle{ til::point{ l, t }, til::point{ r, b } };
|
||||
}
|
||||
|
||||
rectangle& operator+=(const point& point)
|
||||
{
|
||||
*this = *this + point;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// SUB will translate (offset) the rectangle by the point.
|
||||
rectangle operator-(const point& point) const
|
||||
{
|
||||
ptrdiff_t l, t, r, b;
|
||||
|
||||
THROW_HR_IF(E_ABORT, !::base::CheckSub(left(), point.x()).AssignIfValid(&l));
|
||||
THROW_HR_IF(E_ABORT, !::base::CheckSub(top(), point.y()).AssignIfValid(&t));
|
||||
THROW_HR_IF(E_ABORT, !::base::CheckSub(right(), point.x()).AssignIfValid(&r));
|
||||
THROW_HR_IF(E_ABORT, !::base::CheckSub(bottom(), point.y()).AssignIfValid(&b));
|
||||
|
||||
return til::rectangle{ til::point{ l, t }, til::point{ r, b } };
|
||||
}
|
||||
|
||||
rectangle& operator-=(const point& point)
|
||||
{
|
||||
*this = *this - point;
|
||||
return *this;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region RECTANGLE VS SIZE
|
||||
// ADD will grow the total area of the rectangle. The sign is the direction to grow.
|
||||
rectangle operator+(const size& size) const
|
||||
{
|
||||
// Fetch the pieces of the rectangle.
|
||||
auto l = left();
|
||||
auto r = right();
|
||||
auto t = top();
|
||||
auto b = bottom();
|
||||
|
||||
// Fetch the scale factors we're using.
|
||||
const auto width = size.width();
|
||||
const auto height = size.height();
|
||||
|
||||
// Since this is the add operation versus a size, the result
|
||||
// should grow the total rectangle area.
|
||||
// The sign determines which edge of the rectangle moves.
|
||||
// We use the magnitude as how far to move.
|
||||
if (width > 0)
|
||||
{
|
||||
// Adding the positive makes the rectangle "grow"
|
||||
// because right stretches outward (to the right).
|
||||
//
|
||||
// Example with adding width 3...
|
||||
// |-- x = origin
|
||||
// V
|
||||
// x---------| x------------|
|
||||
// | | | |
|
||||
// | | | |
|
||||
// |---------| |------------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckAdd(r, width).AssignIfValid(&r));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Adding the negative makes the rectangle "grow"
|
||||
// because left stretches outward (to the left).
|
||||
//
|
||||
// Example with adding width -3...
|
||||
// |-- x = origin
|
||||
// V
|
||||
// x---------| |--x---------|
|
||||
// | | | |
|
||||
// | | | |
|
||||
// |---------| |------------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckAdd(l, width).AssignIfValid(&l));
|
||||
}
|
||||
|
||||
if (height > 0)
|
||||
{
|
||||
// Adding the positive makes the rectangle "grow"
|
||||
// because bottom stretches outward (to the down).
|
||||
//
|
||||
// Example with adding height 2...
|
||||
// |-- x = origin
|
||||
// V
|
||||
// x---------| x---------|
|
||||
// | | | |
|
||||
// | | | |
|
||||
// |---------| | |
|
||||
// | |
|
||||
// |---------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckAdd(b, height).AssignIfValid(&b));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Adding the negative makes the rectangle "grow"
|
||||
// because top stretches outward (to the up).
|
||||
//
|
||||
// Example with adding height -2...
|
||||
// |-- x = origin
|
||||
// |
|
||||
// | |---------|
|
||||
// V | |
|
||||
// x---------| x |
|
||||
// | | | |
|
||||
// | | | |
|
||||
// |---------| |---------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckAdd(t, height).AssignIfValid(&t));
|
||||
}
|
||||
|
||||
return rectangle{ til::point{ l, t }, til::point{ r, b } };
|
||||
}
|
||||
|
||||
rectangle& operator+=(const size& size)
|
||||
{
|
||||
*this = *this + size;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// SUB will shrink the total area of the rectangle. The sign is the direction to shrink.
|
||||
rectangle operator-(const size& size) const
|
||||
{
|
||||
// Fetch the pieces of the rectangle.
|
||||
auto l = left();
|
||||
auto r = right();
|
||||
auto t = top();
|
||||
auto b = bottom();
|
||||
|
||||
// Fetch the scale factors we're using.
|
||||
const auto width = size.width();
|
||||
const auto height = size.height();
|
||||
|
||||
// Since this is the subtract operation versus a size, the result
|
||||
// should shrink the total rectangle area.
|
||||
// The sign determines which edge of the rectangle moves.
|
||||
// We use the magnitude as how far to move.
|
||||
if (width > 0)
|
||||
{
|
||||
// Subtracting the positive makes the rectangle "shrink"
|
||||
// because right pulls inward (to the left).
|
||||
//
|
||||
// Example with subtracting width 3...
|
||||
// |-- x = origin
|
||||
// V
|
||||
// x---------| x------|
|
||||
// | | | |
|
||||
// | | | |
|
||||
// |---------| |------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckSub(r, width).AssignIfValid(&r));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Subtracting the negative makes the rectangle "shrink"
|
||||
// because left pulls inward (to the right).
|
||||
//
|
||||
// Example with subtracting width -3...
|
||||
// |-- x = origin
|
||||
// V
|
||||
// x---------| x |------|
|
||||
// | | | |
|
||||
// | | | |
|
||||
// |---------| |------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckSub(l, width).AssignIfValid(&l));
|
||||
}
|
||||
|
||||
if (height > 0)
|
||||
{
|
||||
// Subtracting the positive makes the rectangle "shrink"
|
||||
// because bottom pulls inward (to the up).
|
||||
//
|
||||
// Example with subtracting height 2...
|
||||
// |-- x = origin
|
||||
// V
|
||||
// x---------| x---------|
|
||||
// | | |---------|
|
||||
// | |
|
||||
// |---------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckSub(b, height).AssignIfValid(&b));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Subtracting the positive makes the rectangle "shrink"
|
||||
// because top pulls inward (to the down).
|
||||
//
|
||||
// Example with subtracting height -2...
|
||||
// |-- x = origin
|
||||
// V
|
||||
// x---------| x
|
||||
// | |
|
||||
// | | |---------|
|
||||
// |---------| |---------|
|
||||
// BEFORE AFTER
|
||||
THROW_HR_IF(E_ABORT, !base::CheckSub(t, height).AssignIfValid(&t));
|
||||
}
|
||||
|
||||
return rectangle{ til::point{ l, t }, til::point{ r, b } };
|
||||
}
|
||||
|
||||
rectangle& operator-=(const size& size)
|
||||
{
|
||||
*this = *this - size;
|
||||
return *this;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
constexpr ptrdiff_t top() const noexcept
|
||||
{
|
||||
|
@ -587,6 +819,11 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
|||
}
|
||||
#endif
|
||||
|
||||
std::wstring to_string() const
|
||||
{
|
||||
return wil::str_printf<std::wstring>(L"(L:%td, T:%td, R:%td, B:%td) [W:%td, H:%td]", left(), top(), right(), bottom(), width(), height());
|
||||
}
|
||||
|
||||
protected:
|
||||
til::point _topLeft;
|
||||
til::point _bottomRight;
|
||||
|
@ -606,7 +843,7 @@ namespace WEX::TestExecution
|
|||
public:
|
||||
static WEX::Common::NoThrowString ToString(const ::til::rectangle& rect)
|
||||
{
|
||||
return WEX::Common::NoThrowString().Format(L"(L:%td, T:%td, R:%td, B:%td) [W:%td, H:%td]", rect.left(), rect.top(), rect.right(), rect.bottom(), rect.width(), rect.height());
|
||||
return WEX::Common::NoThrowString(rect.to_string().c_str());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -220,6 +220,11 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
|||
}
|
||||
#endif
|
||||
|
||||
std::wstring to_string() const
|
||||
{
|
||||
return wil::str_printf<std::wstring>(L"[W:%td, H:%td]", width(), height());
|
||||
}
|
||||
|
||||
protected:
|
||||
ptrdiff_t _width;
|
||||
ptrdiff_t _height;
|
||||
|
@ -239,7 +244,7 @@ namespace WEX::TestExecution
|
|||
public:
|
||||
static WEX::Common::NoThrowString ToString(const ::til::size& size)
|
||||
{
|
||||
return WEX::Common::NoThrowString().Format(L"[W:%td, H:%td]", size.width(), size.height());
|
||||
return WEX::Common::NoThrowString(size.to_string().c_str());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -208,6 +208,21 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
|||
{
|
||||
throw std::out_of_range("invalid some<T, N> subscript");
|
||||
}
|
||||
|
||||
std::wstring to_string() const
|
||||
{
|
||||
std::wstringstream wss;
|
||||
wss << std::endl
|
||||
<< L"Some contains " << size() << " of max size " << max_size() << ":" << std::endl;
|
||||
wss << L"Elements:" << std::endl;
|
||||
|
||||
for (auto& item : *this)
|
||||
{
|
||||
wss << L"\t- " << item.to_string() << std::endl;
|
||||
}
|
||||
|
||||
return wss.str();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -220,15 +235,7 @@ namespace WEX::TestExecution
|
|||
public:
|
||||
static WEX::Common::NoThrowString ToString(const ::til::some<T, N>& some)
|
||||
{
|
||||
auto str = WEX::Common::NoThrowString().Format(L"\r\nSome contains %d of max size %d:\r\nElements:\r\n", some.size(), some.max_size());
|
||||
|
||||
for (auto& item : some)
|
||||
{
|
||||
const auto itemStr = WEX::TestExecution::VerifyOutputTraits<T>::ToString(item);
|
||||
str.AppendFormat(L"\t- %ws\r\n", (const wchar_t*)itemStr);
|
||||
}
|
||||
|
||||
return str;
|
||||
return WEX::Common::NoThrowString(some.to_string().c_str());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -232,7 +232,7 @@ BgfxEngine::BgfxEngine(PVOID SharedViewBase, LONG DisplayHeight, LONG DisplayWid
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
std::vector<SMALL_RECT> BgfxEngine::GetDirtyArea()
|
||||
std::vector<til::rectangle> BgfxEngine::GetDirtyArea()
|
||||
{
|
||||
SMALL_RECT r;
|
||||
r.Bottom = _displayHeight > 0 ? (SHORT)(_displayHeight - 1) : 0;
|
||||
|
|
|
@ -69,7 +69,7 @@ namespace Microsoft::Console::Render
|
|||
|
||||
[[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo, int const iDpi) noexcept override;
|
||||
|
||||
std::vector<SMALL_RECT> GetDirtyArea() override;
|
||||
std::vector<til::rectangle> GetDirtyArea() override;
|
||||
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
|
||||
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;
|
||||
|
||||
|
|
|
@ -862,7 +862,7 @@ void Renderer::_PaintOverlay(IRenderEngine& engine,
|
|||
// Set it up in a Viewport helper structure and trim it the IME viewport to be within the full console viewport.
|
||||
Viewport viewConv = Viewport::FromInclusive(srCaView);
|
||||
|
||||
for (auto srDirty : engine.GetDirtyArea())
|
||||
for (SMALL_RECT srDirty : engine.GetDirtyArea())
|
||||
{
|
||||
// Dirty is an inclusive rectangle, but oddly enough the IME was an exclusive one, so correct it.
|
||||
srDirty.Bottom++;
|
||||
|
|
|
@ -1686,7 +1686,7 @@ float DxEngine::GetScaling() const noexcept
|
|||
// - <none>
|
||||
// Return Value:
|
||||
// - Rectangle describing dirty area in characters.
|
||||
[[nodiscard]] std::vector<SMALL_RECT> DxEngine::GetDirtyArea()
|
||||
[[nodiscard]] std::vector<til::rectangle> DxEngine::GetDirtyArea()
|
||||
{
|
||||
SMALL_RECT r;
|
||||
r.Top = gsl::narrow<SHORT>(floor(_invalidRect.top / _glyphCell.cy));
|
||||
|
|
|
@ -95,7 +95,7 @@ namespace Microsoft::Console::Render
|
|||
|
||||
[[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo, int const iDpi) noexcept override;
|
||||
|
||||
[[nodiscard]] std::vector<SMALL_RECT> GetDirtyArea() override;
|
||||
[[nodiscard]] std::vector<til::rectangle> GetDirtyArea() override;
|
||||
|
||||
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
|
||||
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;
|
||||
|
|
|
@ -68,7 +68,7 @@ namespace Microsoft::Console::Render
|
|||
_Out_ FontInfo& Font,
|
||||
const int iDpi) noexcept override;
|
||||
|
||||
[[nodiscard]] std::vector<SMALL_RECT> GetDirtyArea() override;
|
||||
[[nodiscard]] std::vector<til::rectangle> GetDirtyArea() override;
|
||||
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
|
||||
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ using namespace Microsoft::Console::Render;
|
|||
// Return Value:
|
||||
// - The character dimensions of the current dirty area of the frame.
|
||||
// This is an Inclusive rect.
|
||||
std::vector<SMALL_RECT> GdiEngine::GetDirtyArea()
|
||||
std::vector<til::rectangle> GdiEngine::GetDirtyArea()
|
||||
{
|
||||
RECT rc = _psInvalidData.rcPaint;
|
||||
|
||||
|
|
|
@ -117,7 +117,7 @@ namespace Microsoft::Console::Render
|
|||
_Out_ FontInfo& FontInfo,
|
||||
const int iDpi) noexcept = 0;
|
||||
|
||||
virtual std::vector<SMALL_RECT> GetDirtyArea() = 0;
|
||||
virtual std::vector<til::rectangle> GetDirtyArea() = 0;
|
||||
[[nodiscard]] virtual HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept = 0;
|
||||
[[nodiscard]] virtual HRESULT UpdateTitle(const std::wstring& newTitle) noexcept = 0;
|
||||
|
|
|
@ -426,7 +426,7 @@ UiaEngine::UiaEngine(IUiaEventDispatcher* dispatcher) :
|
|||
// - <none>
|
||||
// Return Value:
|
||||
// - Rectangle describing dirty area in characters.
|
||||
[[nodiscard]] std::vector<SMALL_RECT> UiaEngine::GetDirtyArea()
|
||||
[[nodiscard]] std::vector<til::rectangle> UiaEngine::GetDirtyArea()
|
||||
{
|
||||
return { Viewport::Empty().ToInclusive() };
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ namespace Microsoft::Console::Render
|
|||
|
||||
[[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo, int const iDpi) noexcept override;
|
||||
|
||||
[[nodiscard]] std::vector<SMALL_RECT> GetDirtyArea() override;
|
||||
[[nodiscard]] std::vector<til::rectangle> GetDirtyArea() override;
|
||||
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
|
||||
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;
|
||||
|
||||
|
|
|
@ -408,21 +408,8 @@ XtermEngine::XtermEngine(_In_ wil::unique_hfile hPipe,
|
|||
|
||||
if (dx != 0 || dy != 0)
|
||||
{
|
||||
// Scroll the current offset
|
||||
RETURN_IF_FAILED(_InvalidOffset(pcoordDelta));
|
||||
|
||||
// Add the top/bottom of the window to the invalid area
|
||||
SMALL_RECT invalid = _lastViewport.ToOrigin().ToExclusive();
|
||||
|
||||
if (dy > 0)
|
||||
{
|
||||
invalid.Bottom = dy;
|
||||
}
|
||||
else if (dy < 0)
|
||||
{
|
||||
invalid.Top = invalid.Bottom + dy;
|
||||
}
|
||||
LOG_IF_FAILED(_InvalidCombine(Viewport::FromExclusive(invalid)));
|
||||
// Scroll the current offset and invalidate the revealed area
|
||||
_invalidMap.translate(til::point(*pcoordDelta), true);
|
||||
|
||||
COORD invalidScrollNew;
|
||||
RETURN_IF_FAILED(ShortAdd(_scrollDelta.X, dx, &invalidScrollNew.X));
|
||||
|
|
|
@ -47,12 +47,14 @@ using namespace Microsoft::Console::Render;
|
|||
// Return Value:
|
||||
// - S_OK, else an appropriate HRESULT for failing to allocate or write.
|
||||
[[nodiscard]] HRESULT VtEngine::Invalidate(const SMALL_RECT* const psrRegion) noexcept
|
||||
try
|
||||
{
|
||||
Viewport newInvalid = Viewport::FromExclusive(*psrRegion);
|
||||
_trace.TraceInvalidate(newInvalid);
|
||||
|
||||
return this->_InvalidCombine(newInvalid);
|
||||
const til::rectangle rect{ Viewport::FromExclusive(*psrRegion).ToInclusive() };
|
||||
_trace.TraceInvalidate(rect);
|
||||
_invalidMap.set(rect);
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
// Routine Description:
|
||||
// - Notifies us that the console has changed the position of the cursor.
|
||||
|
@ -87,10 +89,13 @@ using namespace Microsoft::Console::Render;
|
|||
// Return Value:
|
||||
// - S_OK, else an appropriate HRESULT for failing to allocate or write.
|
||||
[[nodiscard]] HRESULT VtEngine::InvalidateAll() noexcept
|
||||
try
|
||||
{
|
||||
_trace.TraceInvalidateAll(_lastViewport.ToOrigin());
|
||||
return this->_InvalidCombine(_lastViewport.ToOrigin());
|
||||
_trace.TraceInvalidateAll(_lastViewport.ToOrigin().ToInclusive());
|
||||
_invalidMap.set_all();
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
// Method Description:
|
||||
// - Notifies us that we're about to circle the buffer, giving us a chance to
|
||||
|
@ -132,75 +137,3 @@ using namespace Microsoft::Console::Render;
|
|||
*pForcePaint = true;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Helper to combine the given rectangle into the invalid region to be
|
||||
// updated on the next paint
|
||||
// Expects EXCLUSIVE rectangles.
|
||||
// Arguments:
|
||||
// - invalid - A viewport containing the character region that should be
|
||||
// repainted on the next frame
|
||||
// Return Value:
|
||||
// - S_OK, else an appropriate HRESULT for failing to allocate or write.
|
||||
[[nodiscard]] HRESULT VtEngine::_InvalidCombine(const Viewport invalid) noexcept
|
||||
{
|
||||
if (!_fInvalidRectUsed)
|
||||
{
|
||||
_invalidRect = invalid;
|
||||
_fInvalidRectUsed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_invalidRect = Viewport::Union(_invalidRect, invalid);
|
||||
}
|
||||
|
||||
// Ensure invalid areas remain within bounds of window.
|
||||
RETURN_IF_FAILED(_InvalidRestrict());
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Helper to adjust the invalid region by the given offset such as when a
|
||||
// scroll operation occurs.
|
||||
// Arguments:
|
||||
// - ppt - Distances by which we should move the invalid region in response to a scroll
|
||||
// Return Value:
|
||||
// - S_OK, else an appropriate HRESULT for failing to allocate or write.
|
||||
[[nodiscard]] HRESULT VtEngine::_InvalidOffset(const COORD* const pCoord) noexcept
|
||||
{
|
||||
if (_fInvalidRectUsed)
|
||||
{
|
||||
try
|
||||
{
|
||||
Viewport newInvalid = Viewport::Offset(_invalidRect, *pCoord);
|
||||
|
||||
// Add the scrolled invalid rectangle to what was left behind to get the new invalid area.
|
||||
// This is the equivalent of adding in the "update rectangle" that we would get out of ScrollWindowEx/ScrollDC.
|
||||
_invalidRect = Viewport::Union(_invalidRect, newInvalid);
|
||||
|
||||
// Ensure invalid areas remain within bounds of window.
|
||||
RETURN_IF_FAILED(_InvalidRestrict());
|
||||
}
|
||||
CATCH_RETURN();
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Helper to ensure the invalid region remains within the bounds of the viewport.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - S_OK, else an appropriate HRESULT for failing to allocate or safemath failure.
|
||||
[[nodiscard]] HRESULT VtEngine::_InvalidRestrict() noexcept
|
||||
{
|
||||
SMALL_RECT oldInvalid = _invalidRect.ToExclusive();
|
||||
|
||||
_lastViewport.ToOrigin().TrimToViewport(&oldInvalid);
|
||||
|
||||
_invalidRect = Viewport::FromExclusive(oldInvalid);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
|
|
@ -17,14 +17,9 @@ using namespace Microsoft::Console::Types;
|
|||
// Return Value:
|
||||
// - The character dimensions of the current dirty area of the frame.
|
||||
// This is an Inclusive rect.
|
||||
std::vector<SMALL_RECT> VtEngine::GetDirtyArea()
|
||||
std::vector<til::rectangle> VtEngine::GetDirtyArea()
|
||||
{
|
||||
SMALL_RECT dirty = _invalidRect.ToInclusive();
|
||||
if (dirty.Top < _virtualTop)
|
||||
{
|
||||
dirty.Top = _virtualTop;
|
||||
}
|
||||
return { dirty };
|
||||
return _invalidMap.runs();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
@ -68,17 +63,26 @@ void VtEngine::_OrRect(_Inout_ SMALL_RECT* const pRectExisting, const SMALL_RECT
|
|||
// - true iff only the next character is invalid
|
||||
bool VtEngine::_WillWriteSingleChar() const
|
||||
{
|
||||
COORD currentCursor = _lastText;
|
||||
SMALL_RECT _srcInvalid = _invalidRect.ToExclusive();
|
||||
bool noScrollDelta = (_scrollDelta.X == 0 && _scrollDelta.Y == 0);
|
||||
// If there is scroll delta, return false.
|
||||
if (til::point{ 0, 0 } != til::point{ _scrollDelta })
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there is more than one invalid char, return false.
|
||||
if (!_invalidMap.one())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the single point at which things are invalid.
|
||||
const auto invalidPoint = _invalidMap.runs().front().origin();
|
||||
|
||||
bool invalidIsOneChar = (_invalidRect.Width() == 1) &&
|
||||
(_invalidRect.Height() == 1);
|
||||
// Either the next character to the right or the immediately previous
|
||||
// character should follow this code path
|
||||
// (The immediate previous character would suggest a backspace)
|
||||
bool invalidIsNext = (_srcInvalid.Top == _lastText.Y) && (_srcInvalid.Left == _lastText.X);
|
||||
bool invalidIsLast = (_srcInvalid.Top == _lastText.Y) && (_srcInvalid.Left == (_lastText.X - 1));
|
||||
bool invalidIsNext = invalidPoint == til::point{ _lastText };
|
||||
bool invalidIsLast = invalidPoint == til::point{ _lastText.X - 1, _lastText.Y };
|
||||
|
||||
return noScrollDelta && invalidIsOneChar && (invalidIsNext || invalidIsLast);
|
||||
return invalidIsNext || invalidIsLast;
|
||||
}
|
||||
|
|
|
@ -26,13 +26,13 @@ using namespace Microsoft::Console::Types;
|
|||
}
|
||||
|
||||
// If there's nothing to do, quick return
|
||||
bool somethingToDo = _fInvalidRectUsed ||
|
||||
bool somethingToDo = _invalidMap.any() ||
|
||||
(_scrollDelta.X != 0 || _scrollDelta.Y != 0) ||
|
||||
_cursorMoved ||
|
||||
_titleChanged;
|
||||
|
||||
_quickReturn = !somethingToDo;
|
||||
_trace.TraceStartPaint(_quickReturn, _fInvalidRectUsed, _invalidRect, _lastViewport, _scrollDelta, _cursorMoved);
|
||||
_trace.TraceStartPaint(_quickReturn, _invalidMap, _lastViewport.ToInclusive(), _scrollDelta, _cursorMoved);
|
||||
|
||||
return _quickReturn ? S_FALSE : S_OK;
|
||||
}
|
||||
|
@ -50,8 +50,8 @@ using namespace Microsoft::Console::Types;
|
|||
{
|
||||
_trace.TraceEndPaint();
|
||||
|
||||
_invalidRect = Viewport::Empty();
|
||||
_fInvalidRectUsed = false;
|
||||
_invalidMap.reset_all();
|
||||
|
||||
_scrollDelta = { 0 };
|
||||
_clearedAllThisFrame = false;
|
||||
_cursorMoved = false;
|
||||
|
|
|
@ -35,8 +35,7 @@ VtEngine::VtEngine(_In_ wil::unique_hfile pipe,
|
|||
_LastBG(INVALID_COLOR),
|
||||
_lastWasBold(false),
|
||||
_lastViewport(initialViewport),
|
||||
_invalidRect(Viewport::Empty()),
|
||||
_fInvalidRectUsed(false),
|
||||
_invalidMap(initialViewport.Dimensions()),
|
||||
_lastRealCursor({ 0 }),
|
||||
_lastText({ 0 }),
|
||||
_scrollDelta({ 0 }),
|
||||
|
@ -317,40 +316,19 @@ CATCH_RETURN();
|
|||
// buffer will have triggered it's own invalidations for what it knows is
|
||||
// invalid. Previously, we'd invalidate everything if the width changed,
|
||||
// because we couldn't be sure if lines were reflowed.
|
||||
_invalidMap.resize(newView.Dimensions());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
_invalidMap.resize(newView.Dimensions(), true); // resize while filling in new space with repaint requests.
|
||||
|
||||
// Viewport is smaller now - just update it all.
|
||||
if (oldView.Height() > newView.Height() || oldView.Width() > newView.Width())
|
||||
{
|
||||
hr = InvalidateAll();
|
||||
}
|
||||
else
|
||||
{
|
||||
// At least one of the directions grew.
|
||||
// First try and add everything to the right of the old viewport,
|
||||
// then everything below where the old viewport ended.
|
||||
if (oldView.Width() < newView.Width())
|
||||
{
|
||||
short left = oldView.RightExclusive();
|
||||
short top = 0;
|
||||
short right = newView.RightInclusive();
|
||||
short bottom = oldView.BottomInclusive();
|
||||
Viewport rightOfOldViewport = Viewport::FromInclusive({ left, top, right, bottom });
|
||||
hr = _InvalidCombine(rightOfOldViewport);
|
||||
}
|
||||
if (SUCCEEDED(hr) && oldView.Height() < newView.Height())
|
||||
{
|
||||
short left = 0;
|
||||
short top = oldView.BottomExclusive();
|
||||
short right = newView.RightInclusive();
|
||||
short bottom = newView.BottomInclusive();
|
||||
Viewport belowOldViewport = Viewport::FromInclusive({ left, top, right, bottom });
|
||||
hr = _InvalidCombine(belowOldViewport);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -416,7 +394,7 @@ void VtEngine::SetTestCallback(_In_ std::function<bool(const char* const, size_t
|
|||
// - true if the entire viewport has been invalidated
|
||||
bool VtEngine::_AllIsInvalid() const
|
||||
{
|
||||
return _lastViewport == _invalidRect;
|
||||
return _invalidMap.all();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
|
|
@ -81,52 +81,16 @@ void RenderTracing::TraceString(const std::string_view& instr) const
|
|||
#endif UNIT_TESTING
|
||||
}
|
||||
|
||||
std::string _ViewportToString(const Viewport& view)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "{LT:(";
|
||||
ss << view.Left();
|
||||
ss << ",";
|
||||
ss << view.Top();
|
||||
ss << ") RB:(";
|
||||
ss << view.RightInclusive();
|
||||
ss << ",";
|
||||
ss << view.BottomInclusive();
|
||||
ss << ") [";
|
||||
ss << view.Width();
|
||||
ss << "x";
|
||||
ss << view.Height();
|
||||
ss << "]}";
|
||||
std::string myString = "";
|
||||
const auto s = ss.str();
|
||||
myString += s;
|
||||
return myString;
|
||||
}
|
||||
|
||||
std::string _CoordToString(const COORD& c)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "{";
|
||||
ss << c.X;
|
||||
ss << ",";
|
||||
ss << c.Y;
|
||||
ss << "}";
|
||||
const auto s = ss.str();
|
||||
// Make sure you actually place this value in a local after calling, don't
|
||||
// just inline it to _CoordToString().c_str()
|
||||
return s;
|
||||
}
|
||||
|
||||
void RenderTracing::TraceInvalidate(const Viewport invalidRect) const
|
||||
void RenderTracing::TraceInvalidate(const til::rectangle invalidRect) const
|
||||
{
|
||||
#ifndef UNIT_TESTING
|
||||
if (TraceLoggingProviderEnabled(g_hConsoleVtRendererTraceProvider, WINEVENT_LEVEL_VERBOSE, 0))
|
||||
{
|
||||
const auto invalidatedStr = _ViewportToString(invalidRect);
|
||||
const auto invalidatedStr = invalidRect.to_string();
|
||||
const auto invalidated = invalidatedStr.c_str();
|
||||
TraceLoggingWrite(g_hConsoleVtRendererTraceProvider,
|
||||
"VtEngine_TraceInvalidate",
|
||||
TraceLoggingString(invalidated),
|
||||
TraceLoggingWideString(invalidated),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
}
|
||||
#else
|
||||
|
@ -134,16 +98,16 @@ void RenderTracing::TraceInvalidate(const Viewport invalidRect) const
|
|||
#endif UNIT_TESTING
|
||||
}
|
||||
|
||||
void RenderTracing::TraceInvalidateAll(const Viewport viewport) const
|
||||
void RenderTracing::TraceInvalidateAll(const til::rectangle viewport) const
|
||||
{
|
||||
#ifndef UNIT_TESTING
|
||||
if (TraceLoggingProviderEnabled(g_hConsoleVtRendererTraceProvider, WINEVENT_LEVEL_VERBOSE, 0))
|
||||
{
|
||||
const auto invalidatedStr = _ViewportToString(viewport);
|
||||
const auto invalidatedStr = viewport.to_string();
|
||||
const auto invalidatedAll = invalidatedStr.c_str();
|
||||
TraceLoggingWrite(g_hConsoleVtRendererTraceProvider,
|
||||
"VtEngine_TraceInvalidateAll",
|
||||
TraceLoggingString(invalidatedAll),
|
||||
TraceLoggingWideString(invalidatedAll),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
}
|
||||
#else
|
||||
|
@ -164,35 +128,32 @@ void RenderTracing::TraceTriggerCircling(const bool newFrame) const
|
|||
}
|
||||
|
||||
void RenderTracing::TraceStartPaint(const bool quickReturn,
|
||||
const bool invalidRectUsed,
|
||||
const Microsoft::Console::Types::Viewport invalidRect,
|
||||
const Microsoft::Console::Types::Viewport lastViewport,
|
||||
const COORD scrollDelt,
|
||||
const til::bitmap invalidMap,
|
||||
const til::rectangle lastViewport,
|
||||
const til::point scrollDelt,
|
||||
const bool cursorMoved) const
|
||||
{
|
||||
#ifndef UNIT_TESTING
|
||||
if (TraceLoggingProviderEnabled(g_hConsoleVtRendererTraceProvider, WINEVENT_LEVEL_VERBOSE, 0))
|
||||
{
|
||||
const auto invalidatedStr = _ViewportToString(invalidRect);
|
||||
const auto invalidatedStr = invalidMap.to_string();
|
||||
const auto invalidated = invalidatedStr.c_str();
|
||||
const auto lastViewStr = _ViewportToString(lastViewport);
|
||||
const auto lastViewStr = lastViewport.to_string();
|
||||
const auto lastView = lastViewStr.c_str();
|
||||
const auto scrollDeltaStr = _CoordToString(scrollDelt);
|
||||
const auto scrollDeltaStr = scrollDelt.to_string();
|
||||
const auto scrollDelta = scrollDeltaStr.c_str();
|
||||
TraceLoggingWrite(g_hConsoleVtRendererTraceProvider,
|
||||
"VtEngine_TraceStartPaint",
|
||||
TraceLoggingBool(quickReturn),
|
||||
TraceLoggingBool(invalidRectUsed),
|
||||
TraceLoggingString(invalidated),
|
||||
TraceLoggingString(lastView),
|
||||
TraceLoggingString(scrollDelta),
|
||||
TraceLoggingWideString(invalidated),
|
||||
TraceLoggingWideString(lastView),
|
||||
TraceLoggingWideString(scrollDelta),
|
||||
TraceLoggingBool(cursorMoved),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
}
|
||||
#else
|
||||
UNREFERENCED_PARAMETER(quickReturn);
|
||||
UNREFERENCED_PARAMETER(invalidRectUsed);
|
||||
UNREFERENCED_PARAMETER(invalidRect);
|
||||
UNREFERENCED_PARAMETER(invalidMap);
|
||||
UNREFERENCED_PARAMETER(lastViewport);
|
||||
UNREFERENCED_PARAMETER(scrollDelt);
|
||||
UNREFERENCED_PARAMETER(cursorMoved);
|
||||
|
@ -209,37 +170,37 @@ void RenderTracing::TraceEndPaint() const
|
|||
#endif UNIT_TESTING
|
||||
}
|
||||
|
||||
void RenderTracing::TraceLastText(const COORD lastTextPos) const
|
||||
void RenderTracing::TraceLastText(const til::point lastTextPos) const
|
||||
{
|
||||
#ifndef UNIT_TESTING
|
||||
if (TraceLoggingProviderEnabled(g_hConsoleVtRendererTraceProvider, WINEVENT_LEVEL_VERBOSE, 0))
|
||||
{
|
||||
const auto lastTextStr = _CoordToString(lastTextPos);
|
||||
const auto lastTextStr = lastTextPos.to_string();
|
||||
const auto lastText = lastTextStr.c_str();
|
||||
TraceLoggingWrite(g_hConsoleVtRendererTraceProvider,
|
||||
"VtEngine_TraceLastText",
|
||||
TraceLoggingString(lastText),
|
||||
TraceLoggingWideString(lastText),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
}
|
||||
#else
|
||||
UNREFERENCED_PARAMETER(lastTextPos);
|
||||
#endif UNIT_TESTING
|
||||
}
|
||||
void RenderTracing::TraceMoveCursor(const COORD lastTextPos, const COORD cursor) const
|
||||
void RenderTracing::TraceMoveCursor(const til::point lastTextPos, const til::point cursor) const
|
||||
{
|
||||
#ifndef UNIT_TESTING
|
||||
if (TraceLoggingProviderEnabled(g_hConsoleVtRendererTraceProvider, WINEVENT_LEVEL_VERBOSE, 0))
|
||||
{
|
||||
const auto lastTextStr = _CoordToString(lastTextPos);
|
||||
const auto lastTextStr = lastTextPos.to_string();
|
||||
const auto lastText = lastTextStr.c_str();
|
||||
|
||||
const auto cursorStr = _CoordToString(cursor);
|
||||
const auto cursorStr = cursor.to_string();
|
||||
const auto cursorPos = cursorStr.c_str();
|
||||
|
||||
TraceLoggingWrite(g_hConsoleVtRendererTraceProvider,
|
||||
"VtEngine_TraceMoveCursor",
|
||||
TraceLoggingString(lastText),
|
||||
TraceLoggingString(cursorPos),
|
||||
TraceLoggingWideString(lastText),
|
||||
TraceLoggingWideString(cursorPos),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
}
|
||||
#else
|
||||
|
@ -263,16 +224,16 @@ void RenderTracing::TraceWrapped() const
|
|||
#endif UNIT_TESTING
|
||||
}
|
||||
|
||||
void RenderTracing::TracePaintCursor(const COORD coordCursor) const
|
||||
void RenderTracing::TracePaintCursor(const til::point coordCursor) const
|
||||
{
|
||||
#ifndef UNIT_TESTING
|
||||
if (TraceLoggingProviderEnabled(g_hConsoleVtRendererTraceProvider, WINEVENT_LEVEL_VERBOSE, 0))
|
||||
{
|
||||
const auto cursorPosString = _CoordToString(coordCursor);
|
||||
const auto cursorPosString = coordCursor.to_string();
|
||||
const auto cursorPos = cursorPosString.c_str();
|
||||
TraceLoggingWrite(g_hConsoleVtRendererTraceProvider,
|
||||
"VtEngine_TracePaintCursor",
|
||||
TraceLoggingString(cursorPos),
|
||||
TraceLoggingWideString(cursorPos),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
}
|
||||
#else
|
||||
|
|
|
@ -27,18 +27,17 @@ namespace Microsoft::Console::VirtualTerminal
|
|||
RenderTracing();
|
||||
~RenderTracing();
|
||||
void TraceString(const std::string_view& str) const;
|
||||
void TraceInvalidate(const Microsoft::Console::Types::Viewport view) const;
|
||||
void TraceLastText(const COORD lastText) const;
|
||||
void TraceMoveCursor(const COORD lastText, const COORD cursor) const;
|
||||
void TraceInvalidate(const til::rectangle view) const;
|
||||
void TraceLastText(const til::point lastText) const;
|
||||
void TraceMoveCursor(const til::point lastText, const til::point cursor) const;
|
||||
void TraceWrapped() const;
|
||||
void TracePaintCursor(const COORD coordCursor) const;
|
||||
void TraceInvalidateAll(const Microsoft::Console::Types::Viewport view) const;
|
||||
void TracePaintCursor(const til::point coordCursor) const;
|
||||
void TraceInvalidateAll(const til::rectangle view) const;
|
||||
void TraceTriggerCircling(const bool newFrame) const;
|
||||
void TraceStartPaint(const bool quickReturn,
|
||||
const bool invalidRectUsed,
|
||||
const Microsoft::Console::Types::Viewport invalidRect,
|
||||
const Microsoft::Console::Types::Viewport lastViewport,
|
||||
const COORD scrollDelta,
|
||||
const til::bitmap invalidMap,
|
||||
const til::rectangle lastViewport,
|
||||
const til::point scrollDelta,
|
||||
const bool cursorMoved) const;
|
||||
void TraceEndPaint() const;
|
||||
};
|
||||
|
|
|
@ -10,13 +10,8 @@
|
|||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
||||
<Import Project="$(SolutionDir)src\renderer\vt\vt-renderer-common.vcxitems" />
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>INLINE_TEST_METHOD_MARKUP;UNIT_TESTING;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<!-- DONT ADD NEW FILES HERE, ADD THEM TO vt-renderer-common.vcxitems -->
|
||||
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
|
||||
<Import Project="$(SolutionDir)src\common.build.post.props" />
|
||||
<!-- <Import Project="$(SolutionDir)src\common.build.tests.props" /> -->
|
||||
<Import Project="$(SolutionDir)src\common.build.tests.props" />
|
||||
</Project>
|
||||
|
|
|
@ -89,7 +89,7 @@ namespace Microsoft::Console::Render
|
|||
_Out_ FontInfo& Font,
|
||||
const int iDpi) noexcept override;
|
||||
|
||||
std::vector<SMALL_RECT> GetDirtyArea() override;
|
||||
std::vector<til::rectangle> GetDirtyArea() override;
|
||||
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
|
||||
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;
|
||||
|
||||
|
@ -121,9 +121,9 @@ namespace Microsoft::Console::Render
|
|||
bool _lastWasBold;
|
||||
|
||||
Microsoft::Console::Types::Viewport _lastViewport;
|
||||
Microsoft::Console::Types::Viewport _invalidRect;
|
||||
|
||||
bool _fInvalidRectUsed;
|
||||
til::bitmap _invalidMap;
|
||||
|
||||
COORD _lastRealCursor;
|
||||
COORD _lastText;
|
||||
COORD _scrollDelta;
|
||||
|
@ -160,9 +160,6 @@ namespace Microsoft::Console::Render
|
|||
[[nodiscard]] HRESULT _Flush() noexcept;
|
||||
|
||||
void _OrRect(_Inout_ SMALL_RECT* const pRectExisting, const SMALL_RECT* const pRectToOr) const;
|
||||
[[nodiscard]] HRESULT _InvalidCombine(const Microsoft::Console::Types::Viewport invalid) noexcept;
|
||||
[[nodiscard]] HRESULT _InvalidOffset(const COORD* const ppt) noexcept;
|
||||
[[nodiscard]] HRESULT _InvalidRestrict() noexcept;
|
||||
bool _AllIsInvalid() const;
|
||||
|
||||
[[nodiscard]] HRESULT _StopCursorBlinking() noexcept;
|
||||
|
|
|
@ -355,7 +355,7 @@ bool WddmConEngine::IsInitialized()
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
std::vector<SMALL_RECT> WddmConEngine::GetDirtyArea()
|
||||
std::vector<til::rectangle> WddmConEngine::GetDirtyArea()
|
||||
{
|
||||
SMALL_RECT r;
|
||||
r.Bottom = _displayHeight > 0 ? (SHORT)(_displayHeight - 1) : 0;
|
||||
|
|
|
@ -61,7 +61,7 @@ namespace Microsoft::Console::Render
|
|||
|
||||
[[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo, int const iDpi) noexcept override;
|
||||
|
||||
std::vector<SMALL_RECT> GetDirtyArea() override;
|
||||
std::vector<til::rectangle> GetDirtyArea() override;
|
||||
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
|
||||
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;
|
||||
|
||||
|
|
|
@ -25,14 +25,21 @@ class BitmapTests
|
|||
Log::Comment(L"Check dirty rectangles.");
|
||||
|
||||
// Union up all the dirty rectangles into one big one.
|
||||
auto dirtyExpected = bitsOn.front();
|
||||
for (auto it = bitsOn.cbegin() + 1; it < bitsOn.cend(); ++it)
|
||||
if (bitsOn.empty())
|
||||
{
|
||||
dirtyExpected |= *it;
|
||||
VERIFY_ARE_EQUAL(til::rectangle{}, map._dirty);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto dirtyExpected = bitsOn.front();
|
||||
for (auto it = bitsOn.cbegin() + 1; it < bitsOn.cend(); ++it)
|
||||
{
|
||||
dirtyExpected |= *it;
|
||||
}
|
||||
|
||||
// Check if it matches.
|
||||
VERIFY_ARE_EQUAL(dirtyExpected, map._dirty);
|
||||
// Check if it matches.
|
||||
VERIFY_ARE_EQUAL(dirtyExpected, map._dirty);
|
||||
}
|
||||
|
||||
Log::Comment(L"Check all bits in map.");
|
||||
// For every point in the map...
|
||||
|
@ -118,6 +125,486 @@ class BitmapTests
|
|||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(Equality)
|
||||
{
|
||||
Log::Comment(L"0.) Defaults are equal");
|
||||
{
|
||||
const til::bitmap one;
|
||||
const til::bitmap two;
|
||||
VERIFY_IS_TRUE(one == two);
|
||||
}
|
||||
|
||||
Log::Comment(L"1.) Different sizes are unequal");
|
||||
{
|
||||
const til::bitmap one{ til::size{ 2, 2 } };
|
||||
const til::bitmap two{ til::size{ 3, 3 } };
|
||||
VERIFY_IS_FALSE(one == two);
|
||||
}
|
||||
|
||||
Log::Comment(L"2.) Same bits set are equal");
|
||||
{
|
||||
til::bitmap one{ til::size{ 2, 2 } };
|
||||
til::bitmap two{ til::size{ 2, 2 } };
|
||||
one.set(til::point{ 0, 1 });
|
||||
one.set(til::point{ 1, 0 });
|
||||
two.set(til::point{ 0, 1 });
|
||||
two.set(til::point{ 1, 0 });
|
||||
VERIFY_IS_TRUE(one == two);
|
||||
}
|
||||
|
||||
Log::Comment(L"3.) Different bits set are not equal");
|
||||
{
|
||||
til::bitmap one{ til::size{ 2, 2 } };
|
||||
til::bitmap two{ til::size{ 2, 2 } };
|
||||
one.set(til::point{ 0, 1 });
|
||||
two.set(til::point{ 1, 0 });
|
||||
VERIFY_IS_FALSE(one == two);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(Inequality)
|
||||
{
|
||||
Log::Comment(L"0.) Defaults are equal");
|
||||
{
|
||||
const til::bitmap one;
|
||||
const til::bitmap two;
|
||||
VERIFY_IS_FALSE(one != two);
|
||||
}
|
||||
|
||||
Log::Comment(L"1.) Different sizes are unequal");
|
||||
{
|
||||
const til::bitmap one{ til::size{ 2, 2 } };
|
||||
const til::bitmap two{ til::size{ 3, 3 } };
|
||||
VERIFY_IS_TRUE(one != two);
|
||||
}
|
||||
|
||||
Log::Comment(L"2.) Same bits set are equal");
|
||||
{
|
||||
til::bitmap one{ til::size{ 2, 2 } };
|
||||
til::bitmap two{ til::size{ 2, 2 } };
|
||||
one.set(til::point{ 0, 1 });
|
||||
one.set(til::point{ 1, 0 });
|
||||
two.set(til::point{ 0, 1 });
|
||||
two.set(til::point{ 1, 0 });
|
||||
VERIFY_IS_FALSE(one != two);
|
||||
}
|
||||
|
||||
Log::Comment(L"3.) Different bits set are not equal");
|
||||
{
|
||||
til::bitmap one{ til::size{ 2, 2 } };
|
||||
til::bitmap two{ til::size{ 2, 2 } };
|
||||
one.set(til::point{ 0, 1 });
|
||||
two.set(til::point{ 1, 0 });
|
||||
VERIFY_IS_TRUE(one != two);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(Translate)
|
||||
{
|
||||
const til::size mapSize{ 4, 4 };
|
||||
til::bitmap map{ mapSize };
|
||||
|
||||
// set the middle four bits of the map.
|
||||
// 0 0 0 0
|
||||
// 0 1 1 0
|
||||
// 0 1 1 0
|
||||
// 0 0 0 0
|
||||
map.set(til::rectangle{ til::point{ 1, 1 }, til::size{ 2, 2 } });
|
||||
|
||||
Log::Comment(L"1.) Move down and right");
|
||||
{
|
||||
auto actual = map;
|
||||
// Move all contents
|
||||
// |
|
||||
// v
|
||||
// |
|
||||
// v --> -->
|
||||
const til::point delta{ 2, 2 };
|
||||
|
||||
til::bitmap expected{ mapSize };
|
||||
// Expected:
|
||||
// 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
// 0 1 1 0 0 0 0 0 0 0 0 0
|
||||
// 0 1 1 0 v --> 0 0 0 0 --> 0 0 0 0
|
||||
// 0 0 0 0 v 0 1 1 0 0 0 0 1
|
||||
// ->->
|
||||
expected.set(til::point{ 3, 3 });
|
||||
|
||||
actual.translate(delta);
|
||||
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"2.) Move down");
|
||||
{
|
||||
auto actual = map;
|
||||
// Move all contents
|
||||
// |
|
||||
// v
|
||||
// |
|
||||
// v
|
||||
const til::point delta{ 0, 2 };
|
||||
|
||||
til::bitmap expected{ mapSize };
|
||||
// Expected:
|
||||
// 0 0 0 0 0 0 0 0
|
||||
// 0 1 1 0 0 0 0 0
|
||||
// 0 1 1 0 v --> 0 0 0 0
|
||||
// 0 0 0 0 v 0 1 1 0
|
||||
expected.set(til::rectangle{ til::point{ 1, 3 }, til::size{ 2, 1 } });
|
||||
|
||||
actual.translate(delta);
|
||||
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"3.) Move down and left");
|
||||
{
|
||||
auto actual = map;
|
||||
// Move all contents
|
||||
// |
|
||||
// v
|
||||
// |
|
||||
// v <-- <--
|
||||
const til::point delta{ -2, 2 };
|
||||
|
||||
til::bitmap expected{ mapSize };
|
||||
// Expected:
|
||||
// 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
// 0 1 1 0 0 0 0 0 0 0 0 0
|
||||
// 0 1 1 0 v --> 0 0 0 0 --> 0 0 0 0
|
||||
// 0 0 0 0 v 0 1 1 0 1 0 0 0
|
||||
// <-<-
|
||||
expected.set(til::point{ 0, 3 });
|
||||
|
||||
actual.translate(delta);
|
||||
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"4.) Move left");
|
||||
{
|
||||
auto actual = map;
|
||||
// Move all contents
|
||||
// <-- <--
|
||||
const til::point delta{ -2, 0 };
|
||||
|
||||
til::bitmap expected{ mapSize };
|
||||
// Expected:
|
||||
// 0 0 0 0 0 0 0 0
|
||||
// 0 1 1 0 1 0 0 0
|
||||
// 0 1 1 0 --> 1 0 0 0
|
||||
// 0 0 0 0 0 0 0 0
|
||||
// <--<--
|
||||
expected.set(til::rectangle{ til::point{ 0, 1 }, til::size{ 1, 2 } });
|
||||
|
||||
actual.translate(delta);
|
||||
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"5.) Move up and left");
|
||||
{
|
||||
auto actual = map;
|
||||
// Move all contents
|
||||
// ^
|
||||
// |
|
||||
// ^
|
||||
// | <-- <--
|
||||
const til::point delta{ -2, -2 };
|
||||
|
||||
til::bitmap expected{ mapSize };
|
||||
// Expected:
|
||||
// 0 0 0 0 ^ 0 1 1 0 1 0 0 0
|
||||
// 0 1 1 0 ^ 0 0 0 0 0 0 0 0
|
||||
// 0 1 1 0 --> 0 0 0 0 --> 0 0 0 0
|
||||
// 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
// <-<-
|
||||
expected.set(til::point{ 0, 0 });
|
||||
|
||||
actual.translate(delta);
|
||||
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"6.) Move up");
|
||||
{
|
||||
auto actual = map;
|
||||
// Move all contents
|
||||
// ^
|
||||
// |
|
||||
// ^
|
||||
// |
|
||||
const til::point delta{ 0, -2 };
|
||||
|
||||
til::bitmap expected{ mapSize };
|
||||
// Expected:
|
||||
// 0 0 0 0 ^ 0 1 1 0
|
||||
// 0 1 1 0 ^ 0 0 0 0
|
||||
// 0 1 1 0 --> 0 0 0 0
|
||||
// 0 0 0 0 0 0 0 0
|
||||
expected.set(til::rectangle{ til::point{ 1, 0 }, til::size{ 2, 1 } });
|
||||
|
||||
actual.translate(delta);
|
||||
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"7.) Move up and right");
|
||||
{
|
||||
auto actual = map;
|
||||
// Move all contents
|
||||
// ^
|
||||
// |
|
||||
// ^
|
||||
// | --> -->
|
||||
const til::point delta{ 2, -2 };
|
||||
|
||||
til::bitmap expected{ mapSize };
|
||||
// Expected:
|
||||
// 0 0 0 0 ^ 0 1 1 0 0 0 0 1
|
||||
// 0 1 1 0 ^ 0 0 0 0 0 0 0 0
|
||||
// 0 1 1 0 --> 0 0 0 0 --> 0 0 0 0
|
||||
// 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
// ->->
|
||||
expected.set(til::point{ 3, 0 });
|
||||
|
||||
actual.translate(delta);
|
||||
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"8.) Move right");
|
||||
{
|
||||
auto actual = map;
|
||||
// Move all contents
|
||||
// --> -->
|
||||
const til::point delta{ 2, 0 };
|
||||
|
||||
til::bitmap expected{ mapSize };
|
||||
// Expected:
|
||||
// 0 0 0 0 0 0 0 0
|
||||
// 0 1 1 0 0 0 0 1
|
||||
// 0 1 1 0 --> 0 0 0 1
|
||||
// 0 0 0 0 0 0 0 0
|
||||
// ->->
|
||||
expected.set(til::rectangle{ til::point{ 3, 1 }, til::size{ 1, 2 } });
|
||||
|
||||
actual.translate(delta);
|
||||
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(TranslateWithFill)
|
||||
{
|
||||
const til::size mapSize{ 4, 4 };
|
||||
til::bitmap map{ mapSize };
|
||||
|
||||
// set the middle four bits of the map.
|
||||
// 0 0 0 0
|
||||
// 0 1 1 0
|
||||
// 0 1 1 0
|
||||
// 0 0 0 0
|
||||
map.set(til::rectangle{ til::point{ 1, 1 }, til::size{ 2, 2 } });
|
||||
|
||||
Log::Comment(L"1.) Move down and right");
|
||||
{
|
||||
auto actual = map;
|
||||
// Move all contents
|
||||
// |
|
||||
// v
|
||||
// |
|
||||
// v --> -->
|
||||
const til::point delta{ 2, 2 };
|
||||
|
||||
til::bitmap expected{ mapSize };
|
||||
// Expected: (F is filling uncovered value)
|
||||
// 0 0 0 0 F F F F F F F F
|
||||
// 0 1 1 0 F F F F F F F F
|
||||
// 0 1 1 0 v --> 0 0 0 0 --> F F 0 0
|
||||
// 0 0 0 0 v 0 1 1 0 F F 0 1
|
||||
// ->->
|
||||
expected.set(til::rectangle{ til::point{ 0, 0 }, til::size{ 4, 2 } });
|
||||
expected.set(til::rectangle{ til::point{ 0, 2 }, til::size{ 2, 2 } });
|
||||
expected.set(til::point{ 3, 3 });
|
||||
|
||||
actual.translate(delta, true);
|
||||
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"2.) Move down");
|
||||
{
|
||||
auto actual = map;
|
||||
// Move all contents
|
||||
// |
|
||||
// v
|
||||
// |
|
||||
// v
|
||||
const til::point delta{ 0, 2 };
|
||||
|
||||
til::bitmap expected{ mapSize };
|
||||
// Expected: (F is filling uncovered value)
|
||||
// 0 0 0 0 F F F F
|
||||
// 0 1 1 0 F F F F
|
||||
// 0 1 1 0 v --> 0 0 0 0
|
||||
// 0 0 0 0 v 0 1 1 0
|
||||
expected.set(til::rectangle{ til::point{ 0, 0 }, til::size{ 4, 2 } });
|
||||
expected.set(til::rectangle{ til::point{ 1, 3 }, til::size{ 2, 1 } });
|
||||
|
||||
actual.translate(delta, true);
|
||||
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"3.) Move down and left");
|
||||
{
|
||||
auto actual = map;
|
||||
// Move all contents
|
||||
// |
|
||||
// v
|
||||
// |
|
||||
// v <-- <--
|
||||
const til::point delta{ -2, 2 };
|
||||
|
||||
til::bitmap expected{ mapSize };
|
||||
// Expected: (F is filling uncovered value)
|
||||
// 0 0 0 0 F F F F F F F F
|
||||
// 0 1 1 0 F F F F F F F F
|
||||
// 0 1 1 0 v --> 0 0 0 0 --> 0 0 F F
|
||||
// 0 0 0 0 v 0 1 1 0 1 0 F F
|
||||
// <-<-
|
||||
expected.set(til::rectangle{ til::point{ 0, 0 }, til::size{ 4, 2 } });
|
||||
expected.set(til::rectangle{ til::point{ 2, 2 }, til::size{ 2, 2 } });
|
||||
expected.set(til::point{ 0, 3 });
|
||||
|
||||
actual.translate(delta, true);
|
||||
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"4.) Move left");
|
||||
{
|
||||
auto actual = map;
|
||||
// Move all contents
|
||||
// <-- <--
|
||||
const til::point delta{ -2, 0 };
|
||||
|
||||
til::bitmap expected{ mapSize };
|
||||
// Expected: (F is filling uncovered value)
|
||||
// 0 0 0 0 0 0 F F
|
||||
// 0 1 1 0 1 0 F F
|
||||
// 0 1 1 0 --> 1 0 F F
|
||||
// 0 0 0 0 0 0 F F
|
||||
// <--<--
|
||||
expected.set(til::rectangle{ til::point{ 2, 0 }, til::size{ 2, 4 } });
|
||||
expected.set(til::rectangle{ til::point{ 0, 1 }, til::size{ 1, 2 } });
|
||||
|
||||
actual.translate(delta, true);
|
||||
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"5.) Move up and left");
|
||||
{
|
||||
auto actual = map;
|
||||
// Move all contents
|
||||
// ^
|
||||
// |
|
||||
// ^
|
||||
// | <-- <--
|
||||
const til::point delta{ -2, -2 };
|
||||
|
||||
til::bitmap expected{ mapSize };
|
||||
// Expected: (F is filling uncovered value)
|
||||
// 0 0 0 0 ^ 0 1 1 0 1 0 F F
|
||||
// 0 1 1 0 ^ 0 0 0 0 0 0 F F
|
||||
// 0 1 1 0 --> F F F F --> F F F F
|
||||
// 0 0 0 0 F F F F F F F F
|
||||
// <-<-
|
||||
expected.set(til::rectangle{ til::point{ 2, 0 }, til::size{ 2, 2 } });
|
||||
expected.set(til::rectangle{ til::point{ 0, 2 }, til::size{ 4, 2 } });
|
||||
expected.set(til::point{ 0, 0 });
|
||||
|
||||
actual.translate(delta, true);
|
||||
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"6.) Move up");
|
||||
{
|
||||
auto actual = map;
|
||||
// Move all contents
|
||||
// ^
|
||||
// |
|
||||
// ^
|
||||
// |
|
||||
const til::point delta{ 0, -2 };
|
||||
|
||||
til::bitmap expected{ mapSize };
|
||||
// Expected: (F is filling uncovered value)
|
||||
// 0 0 0 0 ^ 0 1 1 0
|
||||
// 0 1 1 0 ^ 0 0 0 0
|
||||
// 0 1 1 0 --> F F F F
|
||||
// 0 0 0 0 F F F F
|
||||
expected.set(til::rectangle{ til::point{ 1, 0 }, til::size{ 2, 1 } });
|
||||
expected.set(til::rectangle{ til::point{ 0, 2 }, til::size{ 4, 2 } });
|
||||
|
||||
actual.translate(delta, true);
|
||||
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"7.) Move up and right");
|
||||
{
|
||||
auto actual = map;
|
||||
// Move all contents
|
||||
// ^
|
||||
// |
|
||||
// ^
|
||||
// | --> -->
|
||||
const til::point delta{ 2, -2 };
|
||||
|
||||
til::bitmap expected{ mapSize };
|
||||
// Expected: (F is filling uncovered value)
|
||||
// 0 0 0 0 ^ 0 1 1 0 F F 0 1
|
||||
// 0 1 1 0 ^ 0 0 0 0 F F 0 0
|
||||
// 0 1 1 0 --> F F F F --> F F F F
|
||||
// 0 0 0 0 F F F F F F F F
|
||||
// ->->
|
||||
expected.set(til::point{ 3, 0 });
|
||||
expected.set(til::rectangle{ til::point{ 0, 2 }, til::size{ 4, 2 } });
|
||||
expected.set(til::rectangle{ til::point{ 0, 0 }, til::size{ 2, 2 } });
|
||||
|
||||
actual.translate(delta, true);
|
||||
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"8.) Move right");
|
||||
{
|
||||
auto actual = map;
|
||||
// Move all contents
|
||||
// --> -->
|
||||
const til::point delta{ 2, 0 };
|
||||
|
||||
til::bitmap expected{ mapSize };
|
||||
// Expected: (F is filling uncovered value)
|
||||
// 0 0 0 0 F F 0 0
|
||||
// 0 1 1 0 F F 0 1
|
||||
// 0 1 1 0 --> F F 0 1
|
||||
// 0 0 0 0 F F 0 0
|
||||
// ->->
|
||||
expected.set(til::rectangle{ til::point{ 3, 1 }, til::size{ 1, 2 } });
|
||||
expected.set(til::rectangle{ til::point{ 0, 0 }, til::size{ 2, 4 } });
|
||||
|
||||
actual.translate(delta, true);
|
||||
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(SetReset)
|
||||
{
|
||||
const til::size sz{ 4, 4 };
|
||||
|
@ -133,7 +620,8 @@ class BitmapTests
|
|||
const til::point point{ 2, 2 };
|
||||
bitmap.set(point);
|
||||
|
||||
til::rectangle expectedSet{ point };
|
||||
std::vector<til::rectangle> expectedSet;
|
||||
expectedSet.emplace_back(til::rectangle{ point });
|
||||
|
||||
// Run through every bit. Only the one we set should be true.
|
||||
Log::Comment(L"Only the bit we set should be true.");
|
||||
|
@ -142,13 +630,14 @@ class BitmapTests
|
|||
Log::Comment(L"Setting all should mean they're all true.");
|
||||
bitmap.set_all();
|
||||
|
||||
expectedSet = til::rectangle{ bitmap._rc };
|
||||
expectedSet.clear();
|
||||
expectedSet.emplace_back(til::rectangle{ bitmap._rc });
|
||||
_checkBits(expectedSet, bitmap);
|
||||
|
||||
Log::Comment(L"Now reset them all.");
|
||||
bitmap.reset_all();
|
||||
|
||||
expectedSet = {};
|
||||
expectedSet.clear();
|
||||
_checkBits(expectedSet, bitmap);
|
||||
|
||||
til::rectangle totalZone{ sz };
|
||||
|
@ -160,13 +649,14 @@ class BitmapTests
|
|||
til::rectangle setZone{ til::point{ 0, 0 }, til::size{ 2, 3 } };
|
||||
bitmap.set(setZone);
|
||||
|
||||
expectedSet = setZone;
|
||||
expectedSet.clear();
|
||||
expectedSet.emplace_back(setZone);
|
||||
_checkBits(expectedSet, bitmap);
|
||||
|
||||
Log::Comment(L"Reset all.");
|
||||
bitmap.reset_all();
|
||||
|
||||
expectedSet = {};
|
||||
expectedSet.clear();
|
||||
_checkBits(expectedSet, bitmap);
|
||||
}
|
||||
|
||||
|
@ -361,7 +851,7 @@ class BitmapTests
|
|||
VERIFY_IS_FALSE(bitmap.all());
|
||||
}
|
||||
|
||||
TEST_METHOD(Iterate)
|
||||
TEST_METHOD(Runs)
|
||||
{
|
||||
// This map --> Those runs
|
||||
// 1 1 0 1 A A _ B
|
||||
|
@ -370,8 +860,6 @@ class BitmapTests
|
|||
// 0 1 1 0 _ F F _
|
||||
Log::Comment(L"Set up a bitmap with some runs.");
|
||||
|
||||
// Note: we'll test setting/resetting some overlapping rects and points.
|
||||
|
||||
til::bitmap map{ til::size{ 4, 4 } };
|
||||
|
||||
// 0 0 0 0 |1 1|0 0
|
||||
|
@ -421,7 +909,7 @@ class BitmapTests
|
|||
|
||||
Log::Comment(L"Run the iterator and collect the runs.");
|
||||
til::some<til::rectangle, 6> actual;
|
||||
for (auto run : map)
|
||||
for (auto run : map.runs())
|
||||
{
|
||||
actual.push_back(run);
|
||||
}
|
||||
|
@ -434,12 +922,67 @@ class BitmapTests
|
|||
|
||||
expected.clear();
|
||||
actual.clear();
|
||||
for (auto run : map)
|
||||
for (auto run : map.runs())
|
||||
{
|
||||
actual.push_back(run);
|
||||
}
|
||||
|
||||
Log::Comment(L"Verify they're empty.");
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
|
||||
Log::Comment(L"Set point and validate runs updated.");
|
||||
const til::point setPoint{ 2, 2 };
|
||||
expected.push_back(til::rectangle{ setPoint });
|
||||
map.set(setPoint);
|
||||
|
||||
for (auto run : map.runs())
|
||||
{
|
||||
actual.push_back(run);
|
||||
}
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
|
||||
Log::Comment(L"Set rectangle and validate runs updated.");
|
||||
const til::rectangle setRect{ setPoint, til::size{ 2, 2 } };
|
||||
expected.clear();
|
||||
expected.push_back(til::rectangle{ til::point{ 2, 2 }, til::size{ 2, 1 } });
|
||||
expected.push_back(til::rectangle{ til::point{ 2, 3 }, til::size{ 2, 1 } });
|
||||
map.set(setRect);
|
||||
|
||||
actual.clear();
|
||||
for (auto run : map.runs())
|
||||
{
|
||||
actual.push_back(run);
|
||||
}
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
|
||||
Log::Comment(L"Set all and validate runs updated.");
|
||||
expected.clear();
|
||||
expected.push_back(til::rectangle{ til::point{ 0, 0 }, til::size{ 4, 1 } });
|
||||
expected.push_back(til::rectangle{ til::point{ 0, 1 }, til::size{ 4, 1 } });
|
||||
expected.push_back(til::rectangle{ til::point{ 0, 2 }, til::size{ 4, 1 } });
|
||||
expected.push_back(til::rectangle{ til::point{ 0, 3 }, til::size{ 4, 1 } });
|
||||
map.set_all();
|
||||
|
||||
actual.clear();
|
||||
for (auto run : map.runs())
|
||||
{
|
||||
actual.push_back(run);
|
||||
}
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
|
||||
Log::Comment(L"Resize and validate runs updated.");
|
||||
const til::size newSize{ 3, 3 };
|
||||
expected.clear();
|
||||
expected.push_back(til::rectangle{ til::point{ 0, 0 }, til::size{ 3, 1 } });
|
||||
expected.push_back(til::rectangle{ til::point{ 0, 1 }, til::size{ 3, 1 } });
|
||||
expected.push_back(til::rectangle{ til::point{ 0, 2 }, til::size{ 3, 1 } });
|
||||
map.resize(newSize);
|
||||
|
||||
actual.clear();
|
||||
for (auto run : map.runs())
|
||||
{
|
||||
actual.push_back(run);
|
||||
}
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
};
|
||||
|
|
87
src/til/ut_til/OperatorTests.cpp
Normal file
87
src/til/ut_til/OperatorTests.cpp
Normal file
|
@ -0,0 +1,87 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "precomp.h"
|
||||
|
||||
#include "til/operators.h"
|
||||
|
||||
using namespace WEX::Common;
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
|
||||
class OperatorTests
|
||||
{
|
||||
TEST_CLASS(OperatorTests);
|
||||
|
||||
TEST_METHOD(PointAddSize)
|
||||
{
|
||||
const til::point pt{ 5, 10 };
|
||||
const til::size sz{ 2, 4 };
|
||||
const til::point expected{ 5 + 2, 10 + 4 };
|
||||
const auto actual = pt + sz;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
TEST_METHOD(PointSubSize)
|
||||
{
|
||||
const til::point pt{ 5, 10 };
|
||||
const til::size sz{ 2, 4 };
|
||||
const til::point expected{ 5 - 2, 10 - 4 };
|
||||
const auto actual = pt - sz;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
TEST_METHOD(PointMulSize)
|
||||
{
|
||||
const til::point pt{ 5, 10 };
|
||||
const til::size sz{ 2, 4 };
|
||||
const til::point expected{ 5 * 2, 10 * 4 };
|
||||
const auto actual = pt * sz;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
TEST_METHOD(PointDivSize)
|
||||
{
|
||||
const til::point pt{ 5, 10 };
|
||||
const til::size sz{ 2, 4 };
|
||||
const til::point expected{ 5 / 2, 10 / 4 };
|
||||
const auto actual = pt / sz;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
TEST_METHOD(SizeAddPoint)
|
||||
{
|
||||
const til::size pt{ 5, 10 };
|
||||
const til::point sz{ 2, 4 };
|
||||
const til::size expected{ 5 + 2, 10 + 4 };
|
||||
const auto actual = pt + sz;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
TEST_METHOD(SizeSubPoint)
|
||||
{
|
||||
const til::size pt{ 5, 10 };
|
||||
const til::point sz{ 2, 4 };
|
||||
const til::size expected{ 5 - 2, 10 - 4 };
|
||||
const auto actual = pt - sz;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
TEST_METHOD(SizeMulPoint)
|
||||
{
|
||||
const til::size pt{ 5, 10 };
|
||||
const til::point sz{ 2, 4 };
|
||||
const til::size expected{ 5 * 2, 10 * 4 };
|
||||
const auto actual = pt * sz;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
TEST_METHOD(SizeDivPoint)
|
||||
{
|
||||
const til::size pt{ 5, 10 };
|
||||
const til::point sz{ 2, 4 };
|
||||
const til::size expected{ 5 / 2, 10 / 4 };
|
||||
const auto actual = pt / sz;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
};
|
|
@ -440,6 +440,16 @@ class RectangleTests
|
|||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
TEST_METHOD(OrUnionInplace)
|
||||
{
|
||||
til::rectangle one{ 4, 6, 10, 14 };
|
||||
const til::rectangle two{ 5, 2, 13, 10 };
|
||||
|
||||
const til::rectangle expected{ 4, 2, 13, 14 };
|
||||
one |= two;
|
||||
VERIFY_ARE_EQUAL(expected, one);
|
||||
}
|
||||
|
||||
TEST_METHOD(AndIntersect)
|
||||
{
|
||||
const til::rectangle one{ 4, 6, 10, 14 };
|
||||
|
@ -450,6 +460,16 @@ class RectangleTests
|
|||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
TEST_METHOD(AndIntersectInplace)
|
||||
{
|
||||
til::rectangle one{ 4, 6, 10, 14 };
|
||||
const til::rectangle two{ 5, 2, 13, 10 };
|
||||
|
||||
const til::rectangle expected{ 5, 6, 10, 10 };
|
||||
one &= two;
|
||||
VERIFY_ARE_EQUAL(expected, one);
|
||||
}
|
||||
|
||||
TEST_METHOD(MinusSubtractSame)
|
||||
{
|
||||
const til::rectangle original{ 0, 0, 10, 10 };
|
||||
|
@ -585,6 +605,198 @@ class RectangleTests
|
|||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
TEST_METHOD(AdditionPoint)
|
||||
{
|
||||
const til::rectangle start{ 10, 20, 30, 40 };
|
||||
const til::point pt{ 3, 7 };
|
||||
const til::rectangle expected{ 10 + 3, 20 + 7, 30 + 3, 40 + 7 };
|
||||
const auto actual = start + pt;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
TEST_METHOD(AdditionPointInplace)
|
||||
{
|
||||
til::rectangle start{ 10, 20, 30, 40 };
|
||||
const til::point pt{ 3, 7 };
|
||||
const til::rectangle expected{ 10 + 3, 20 + 7, 30 + 3, 40 + 7 };
|
||||
start += pt;
|
||||
VERIFY_ARE_EQUAL(expected, start);
|
||||
}
|
||||
|
||||
TEST_METHOD(SubtractionPoint)
|
||||
{
|
||||
const til::rectangle start{ 10, 20, 30, 40 };
|
||||
const til::point pt{ 3, 7 };
|
||||
const til::rectangle expected{ 10 - 3, 20 - 7, 30 - 3, 40 - 7 };
|
||||
const auto actual = start - pt;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
TEST_METHOD(SubtractionPointInplace)
|
||||
{
|
||||
til::rectangle start{ 10, 20, 30, 40 };
|
||||
const til::point pt{ 3, 7 };
|
||||
const til::rectangle expected{ 10 - 3, 20 - 7, 30 - 3, 40 - 7 };
|
||||
start -= pt;
|
||||
VERIFY_ARE_EQUAL(expected, start);
|
||||
}
|
||||
|
||||
TEST_METHOD(AdditionSize)
|
||||
{
|
||||
const til::rectangle start{ 10, 20, 30, 40 };
|
||||
|
||||
Log::Comment(L"1.) Add size to bottom and right");
|
||||
{
|
||||
const til::size scale{ 3, 7 };
|
||||
const til::rectangle expected{ 10, 20, 33, 47 };
|
||||
const auto actual = start + scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"2.) Add size to top and left");
|
||||
{
|
||||
const til::size scale{ -3, -7 };
|
||||
const til::rectangle expected{ 7, 13, 30, 40 };
|
||||
const auto actual = start + scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"3.) Add size to bottom and left");
|
||||
{
|
||||
const til::size scale{ -3, 7 };
|
||||
const til::rectangle expected{ 7, 20, 30, 47 };
|
||||
const auto actual = start + scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"4.) Add size to top and right");
|
||||
{
|
||||
const til::size scale{ 3, -7 };
|
||||
const til::rectangle expected{ 10, 13, 33, 40 };
|
||||
const auto actual = start + scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(AdditionSizeInplace)
|
||||
{
|
||||
const til::rectangle start{ 10, 20, 30, 40 };
|
||||
|
||||
Log::Comment(L"1.) Add size to bottom and right");
|
||||
{
|
||||
auto actual = start;
|
||||
const til::size scale{ 3, 7 };
|
||||
const til::rectangle expected{ 10, 20, 33, 47 };
|
||||
actual += scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"2.) Add size to top and left");
|
||||
{
|
||||
auto actual = start;
|
||||
const til::size scale{ -3, -7 };
|
||||
const til::rectangle expected{ 7, 13, 30, 40 };
|
||||
actual += scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"3.) Add size to bottom and left");
|
||||
{
|
||||
auto actual = start;
|
||||
const til::size scale{ -3, 7 };
|
||||
const til::rectangle expected{ 7, 20, 30, 47 };
|
||||
actual += scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"4.) Add size to top and right");
|
||||
{
|
||||
auto actual = start;
|
||||
const til::size scale{ 3, -7 };
|
||||
const til::rectangle expected{ 10, 13, 33, 40 };
|
||||
actual += scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(SubtractionSize)
|
||||
{
|
||||
const til::rectangle start{ 10, 20, 30, 40 };
|
||||
|
||||
Log::Comment(L"1.) Subtract size from bottom and right");
|
||||
{
|
||||
const til::size scale{ 3, 7 };
|
||||
const til::rectangle expected{ 10, 20, 27, 33 };
|
||||
const auto actual = start - scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"2.) Subtract size from top and left");
|
||||
{
|
||||
const til::size scale{ -3, -7 };
|
||||
const til::rectangle expected{ 13, 27, 30, 40 };
|
||||
const auto actual = start - scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"3.) Subtract size from bottom and left");
|
||||
{
|
||||
const til::size scale{ -3, 7 };
|
||||
const til::rectangle expected{ 13, 20, 30, 33 };
|
||||
const auto actual = start - scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"4.) Subtract size from top and right");
|
||||
{
|
||||
const til::size scale{ 3, -6 };
|
||||
const til::rectangle expected{ 10, 26, 27, 40 };
|
||||
const auto actual = start - scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(SubtractionSizeInplace)
|
||||
{
|
||||
const til::rectangle start{ 10, 20, 30, 40 };
|
||||
|
||||
Log::Comment(L"1.) Subtract size from bottom and right");
|
||||
{
|
||||
auto actual = start;
|
||||
const til::size scale{ 3, 7 };
|
||||
const til::rectangle expected{ 10, 20, 27, 33 };
|
||||
actual -= scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"2.) Subtract size from top and left");
|
||||
{
|
||||
auto actual = start;
|
||||
const til::size scale{ -3, -7 };
|
||||
const til::rectangle expected{ 13, 27, 30, 40 };
|
||||
actual -= scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"3.) Subtract size from bottom and left");
|
||||
{
|
||||
auto actual = start;
|
||||
const til::size scale{ -3, 7 };
|
||||
const til::rectangle expected{ 13, 20, 30, 33 };
|
||||
actual -= scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
Log::Comment(L"4.) Subtract size from top and right");
|
||||
{
|
||||
auto actual = start;
|
||||
const til::size scale{ 3, -6 };
|
||||
const til::rectangle expected{ 10, 26, 27, 40 };
|
||||
actual -= scale;
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(Top)
|
||||
{
|
||||
const til::rectangle rc{ 5, 10, 15, 20 };
|
||||
|
|
|
@ -14,8 +14,14 @@ DLLDEF =
|
|||
|
||||
SOURCES = \
|
||||
$(SOURCES) \
|
||||
BitmapTests.cpp \
|
||||
ColorTests.cpp \
|
||||
OperatorTests.cpp \
|
||||
PointTests.cpp \
|
||||
RectangleTests.cpp \
|
||||
SizeTests.cpp \
|
||||
SomeTests.cpp \
|
||||
u8u16convertTests.cpp \
|
||||
DefaultResource.rc \
|
||||
|
||||
INCLUDES = \
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="BitmapTests.cpp" />
|
||||
<ClCompile Include="OperatorTests.cpp" />
|
||||
<ClCompile Include="PointTests.cpp" />
|
||||
<ClCompile Include="RectangleTests.cpp" />
|
||||
<ClCompile Include="SizeTests.cpp" />
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
<ClCompile Include="PointTests.cpp" />
|
||||
<ClCompile Include="RectangleTests.cpp" />
|
||||
<ClCompile Include="BitmapTests.cpp" />
|
||||
<ClCompile Include="OperatorTests.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\precomp.h" />
|
||||
|
|
Loading…
Reference in a new issue