Compare commits

...

35 commits

Author SHA1 Message Date
Michael Niksa 6005dd863d Merge branch 'master' into dev/miniksa/bitmap 2020-03-16 14:41:16 -07:00
Michael Niksa 954e23c126 Add tests for rectangle. Add some supporting methods (and tests) for some. 2020-03-13 14:28:49 -07:00
Michael Niksa 3e257594ad most of the tests except intersect, union, and subtract. 2020-03-13 13:04:27 -07:00
Michael Niksa 04dc2190b2 more tests for rectangle. 2020-03-12 17:02:27 -07:00
Michael Niksa ef8aef4c26 Stub out all the tests and start writing some. 2020-03-12 16:50:38 -07:00
Michael Niksa 120ad1f12d Clean up rectangle and methods, run code format. 2020-03-12 14:31:30 -07:00
Michael Niksa 3f8e8184f7 split iterators out of rectangle and clean up rectangle in preparation for check-in/split. 2020-03-12 13:36:22 -07:00
Michael Niksa d16ebd2669 Round out point to prepare for PR. Remove weird point vs integer operators that could be unclear. 2020-03-12 13:12:48 -07:00
Michael Niksa 6de06001f3 Merge branch 'master' into dev/miniksa/bitmap 2020-03-12 12:52:45 -07:00
Michael Niksa 1f91789cfb fix overzealous resizing that invalidated the whole bitmap. 2020-03-12 10:00:33 -07:00
Michael Niksa 001c2d3cb2 Collapse back to one function, forget the estimates, just grow if we miss and keep it. 2020-03-11 14:25:17 -07:00
Michael Niksa 06bfc9607d Take the magic static out. 2020-03-11 14:25:06 -07:00
Michael Niksa f16c26482a Update VT renderer format string to use proposed guess sizes and cached buffer to save performance of alloc/freeing for lots of formatted output. 2020-03-11 12:18:27 -07:00
Michael Niksa ca287fe519 Make pty use the bitmap and not log the cursor movement when no one is listening. 2020-03-10 16:46:59 -07:00
Michael Niksa b339fd7926 fix some inclusive/exclusive things. add a bool test to the bitmap so it doesn't failboat on startup before it exists. readd tracing for invalid areas, remove invalidateall on beginpaint. 2020-03-10 16:17:48 -07:00
Michael Niksa 7fd7aa4c3b oh yeah check that it doesn't glom two around a row. 2020-03-10 14:07:03 -07:00
Michael Niksa 1d3b2c78b0 runerator 2020-03-10 14:05:30 -07:00
Michael Niksa d43d888bd7 remove point access from the outside. 2020-03-09 16:57:15 -07:00
Michael Niksa 27c1a6daa4 move it one more step to til::rectangle. 2020-03-09 14:59:39 -07:00
Michael Niksa ddd13141cc Merge branch 'dev/miniksa/plural_dirty_rects' into dev/miniksa/bitmap 2020-03-09 14:26:57 -07:00
Michael Niksa 69235d8dd3 Take noexcept off GetDirtyArea. 2020-03-09 14:26:30 -07:00
Michael Niksa 76b1356d9e Merge branch 'dev/miniksa/plural_dirty_rects' into dev/miniksa/bitmap 2020-03-09 14:24:09 -07:00
Michael Niksa 5f9bb815b1 code format 2020-03-09 13:40:43 -07:00
Michael Niksa 5079c0f79f Turn singular dirty rect into a vector of rects so it can be returned in smaller segments if need be. 2020-03-09 13:40:14 -07:00
Michael Niksa 29d8857066 a bit more progress on stuff. 2020-03-09 11:56:45 -07:00
Michael Niksa d8d35ef3da Add a bunch of operators, integrate into renderer, fight std::vector<bool> over its complete failure of a sane implementation. 2020-03-06 15:07:22 -08:00
Michael Niksa fbb7334c5c safe convert of width/heights, integrate more into renderer. 2020-03-05 16:56:57 -08:00
Michael Niksa a41ede3f17 Adapt rectangle functions. 2020-03-05 15:21:48 -08:00
Michael Niksa 895e71d840 cleanup of point,size,rectangle 2020-03-05 14:20:00 -08:00
Michael Niksa 66606e0a8a A lot of stuff... TIL support libraries. Tests for them. Changes to invalidation. 2020-03-05 14:07:44 -08:00
Michael Niksa 35183a4e58 begin to introduce into renderer, add more provisions to bitmaps and rectangles so we can accept the rectangles straight in. 2020-03-03 15:45:21 -08:00
Michael Niksa 7cdc3187fe OK, commit point/rectangle/size split out. Bitmap now has an iterator and is vaguely tested. Also added row-based ranges to it to make things a little easier. 2020-03-03 14:09:43 -08:00
Michael Niksa e09f0ace37 An initial idea for bitmap. 2020-03-03 11:25:28 -08:00
Michael Niksa e3e788b52b initial bitmap header. 2020-03-03 11:15:48 -08:00
Michael Niksa 136817e337 Initial tests. 2020-03-03 11:14:18 -08:00
32 changed files with 1351 additions and 222 deletions

View file

@ -6,11 +6,13 @@
#include "til/at.h"
#include "til/color.h"
#include "til/some.h"
#include "til/size.h"
#include "til/point.h"
#include "til/size.h"
#include "til/rectangle.h"
#include "til/u8u16convert.h"
//#include "til/operators.h"
namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
}

347
src/inc/til/bitmap.h Normal file
View file

@ -0,0 +1,347 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include <vector>
#include "size.h"
#ifdef UNIT_TESTING
class BitmapTests;
#endif
namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
class const_bitterator // Bit Iterator. Bitterator.
{
public:
const_bitterator(const std::vector<bool>& values, size_t pos) :
_map(values),
_pos(pos)
{
}
const_bitterator operator+(const ptrdiff_t movement)
{
auto copy = *this;
copy += movement;
return copy;
}
const_bitterator operator-(const ptrdiff_t movement)
{
auto copy = *this;
copy -= movement;
return copy;
}
const_bitterator& operator++()
{
++_pos;
return (*this);
}
const_bitterator& operator--()
{
--_pos;
return (*this);
}
const_bitterator& operator+=(const ptrdiff_t& movement)
{
_pos += movement;
return (*this);
}
const_bitterator& operator-=(const ptrdiff_t& movement)
{
_pos -= movement;
return (*this);
}
bool operator==(const const_bitterator& other) const
{
return _pos == other._pos && _map == other._map;
}
bool operator!=(const const_bitterator& other) const
{
return !(*this == other);
}
bool operator<(const const_bitterator& other) const
{
return _pos < other._pos;
}
bool operator>(const const_bitterator& other) const
{
return _pos > other._pos;
}
bool operator*() const
{
return _map[_pos];
}
private:
size_t _pos;
const std::vector<bool>& _map;
};
class const_runerator // Run Iterator. Runerator.
{
public:
const_runerator(const std::vector<bool>& values, til::size sz, size_t pos) :
_values(values),
_size(sz),
_pos(pos)
{
_calculateArea();
}
const_runerator& operator++()
{
_pos = _nextPos;
_calculateArea();
return (*this);
}
bool operator==(const const_runerator& other) const
{
return _pos == other._pos && _values == other._values;
}
bool operator!=(const const_runerator& other) const
{
return !(*this == other);
}
bool operator<(const const_runerator& other) const
{
return _pos < other._pos;
}
bool operator>(const const_runerator& other) const
{
return _pos > other._pos;
}
til::rectangle operator*() const
{
return _run;
}
private:
const std::vector<bool>& _values;
const til::size _size;
size_t _pos;
size_t _nextPos;
til::rectangle _run;
til::point _indexToPoint(size_t index)
{
return til::point{ (ptrdiff_t)index % _size.width(), (ptrdiff_t)index / _size.width() };
}
void _calculateArea()
{
const size_t end = (size_t)_size.area();
_nextPos = _pos;
while (_nextPos < end && !_values.at(_nextPos))
{
++_nextPos;
}
if (_nextPos < end)
{
// pos is now at the first on bit.
const auto runStart = _indexToPoint(_nextPos);
const size_t rowEndIndex = (size_t)((runStart.y() + 1) * _size.width());
ptrdiff_t runLength = 0;
do
{
++_nextPos;
++runLength;
} while (_nextPos < end && _nextPos < rowEndIndex && _values.at(_nextPos));
_run = til::rectangle{ runStart, til::size{ runLength, static_cast<ptrdiff_t>(1) } };
}
else
{
_pos = _nextPos;
_run = til::rectangle{};
}
}
};
class bitmap
{
public:
using const_iterator = const const_bitterator;
bitmap() :
bitmap(0, 0)
{
}
bitmap(size_t width, size_t height) :
bitmap(til::size{ width, height })
{
}
bitmap(til::size sz) :
_size(sz),
_bits(sz.area(), true),
_empty(false)
{
}
const_iterator begin() const
{
return const_bitterator(_bits, 0);
}
const_iterator end() const
{
return const_bitterator(_bits, _size.area());
}
const_iterator begin_row(size_t row) const
{
return const_bitterator(_bits, row * _size.width());
}
const_iterator end_row(size_t row) const
{
return const_bitterator(_bits, (row + 1) * _size.width());
}
const_runerator begin_runs() const
{
return const_runerator(_bits, _size, 0);
}
const_runerator end_runs() const
{
return const_runerator(_bits, _size, _size.area());
}
void set(til::point pt)
{
_bits[pt.y() * _size.width() + pt.x()] = true;
_empty = false;
}
void reset(til::point pt)
{
_bits[pt.y() * _size.width() + pt.x()] = false;
}
void set(til::rectangle rc)
{
for (auto pt : rc)
{
set(pt);
}
}
void reset(til::rectangle rc)
{
for (auto pt : rc)
{
reset(pt);
}
}
void set_all()
{
// .clear() then .resize(_size(), true) throws an assert (unsupported operation)
// .assign(_size(), true) throws an assert (unsupported operation)
set(til::rectangle{ til::point{ 0, 0 }, _size });
}
void reset_all()
{
// .clear() then .resize(_size(), false) throws an assert (unsupported operation)
// .assign(_size(), false) throws an assert (unsupported operation)
reset(til::rectangle{ til::point{ 0, 0 }, _size });
_empty = true;
}
void resize(til::size size)
{
// Don't resize if it's not different as we mark the whole thing dirty on resize.
// TODO: marking it dirty might not be necessary or we should be smart about it
// (mark none of it dirty on resize down, mark just the edges on up?)
if (_size != size)
{
_size = size;
// .resize(_size(), true) throws an assert (unsupported operation)
_bits = std::vector<bool>(_size.area(), true);
}
}
void resize(size_t width, size_t height)
{
resize(til::size{ width, height });
}
constexpr bool empty() const
{
return _empty;
}
const til::size& size() const
{
return _size;
}
operator bool() const noexcept
{
return !_bits.empty();
}
bitmap operator+(const point& pt) const
{
auto temp = *this;
return temp += pt;
}
bitmap& operator+=(const point& pt)
{
// early return if nothing to do.
if (pt.x() == 0 && pt.y() == 0)
{
return (*this);
}
// If we're told to shift the whole thing by an entire width or height,
// the effect is to just clear the whole bitmap.
if (pt.x() >= _size.width() || pt.y() >= _size.height())
{
reset_all();
return (*this);
}
// TODO: any way to reconcile this with walk directions from scrolling apis?
// TODO: actually implement translation.
return (*this);
}
#ifdef UNIT_TESTING
friend class ::BitmapTests;
#endif
private:
bool _empty;
til::size _size;
std::vector<bool> _bits;
};
}

12
src/inc/til/bitterator.h Normal file
View file

@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#ifdef UNIT_TESTING
class BitteratorTests;
#endif
namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
};

256
src/inc/til/operators.h Normal file
View file

@ -0,0 +1,256 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "rectangle.h"
#include "size.h"
#include "bitmap.h"
#define _TIL_INLINEPREFIX __declspec(noinline) inline
namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
// RECTANGLE VS SIZE
// ADD will grow the total area of the rectangle. The sign is the direction to grow.
_TIL_INLINEPREFIX rectangle operator+(const rectangle& lhs, const size& rhs)
{
// Fetch the pieces of the rectangle.
auto l = lhs.left();
auto r = lhs.right();
auto t = lhs.top();
auto b = lhs.bottom();
// Fetch the scale factors we're using.
const auto width = rhs.width();
const auto height = rhs.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 } };
}
_TIL_INLINEPREFIX rectangle& operator+=(rectangle& lhs, const size& rhs)
{
lhs = lhs + rhs;
return lhs;
}
// SUB will shrink the total area of the rectangle. The sign is the direction to shrink.
_TIL_INLINEPREFIX rectangle operator-(const rectangle& lhs, const size& rhs)
{
// Fetch the pieces of the rectangle.
auto l = lhs.left();
auto r = lhs.right();
auto t = lhs.top();
auto b = lhs.bottom();
// Fetch the scale factors we're using.
const auto width = rhs.width();
const auto height = rhs.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 } };
}
_TIL_INLINEPREFIX rectangle& operator-=(rectangle& lhs, const size& rhs)
{
lhs = lhs - rhs;
return lhs;
}
// MUL will scale the entire rectangle by the size L/R * WIDTH and T/B * HEIGHT.
_TIL_INLINEPREFIX rectangle operator*(const rectangle& lhs, const size& rhs)
{
ptrdiff_t l;
THROW_HR_IF(E_ABORT, !(base::MakeCheckedNum(lhs.left()) * rhs.width()).AssignIfValid(&l));
ptrdiff_t t;
THROW_HR_IF(E_ABORT, !(base::MakeCheckedNum(lhs.top()) * rhs.height()).AssignIfValid(&t));
ptrdiff_t r;
THROW_HR_IF(E_ABORT, !(base::MakeCheckedNum(lhs.right()) * rhs.width()).AssignIfValid(&r));
ptrdiff_t b;
THROW_HR_IF(E_ABORT, !(base::MakeCheckedNum(lhs.bottom()) * rhs.height()).AssignIfValid(&b));
return til::rectangle{ l, t, r, b };
}
// 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() };
}
// 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());
}
}

View file

@ -39,14 +39,14 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
}
// This template will convert to size from anything that has an X and a Y field that appear convertible to an integer value
// This template will convert to point from anything that has an X and a Y field that appear convertible to an integer value
template<typename TOther>
constexpr point(const TOther& other, std::enable_if_t<std::is_integral_v<decltype(std::declval<TOther>().X)> && std::is_integral_v<decltype(std::declval<TOther>().Y)>, int> /*sentinel*/ = 0) :
point(static_cast<ptrdiff_t>(other.X), static_cast<ptrdiff_t>(other.Y))
{
}
// This template will convert to size from anything that has a x and a y field that appear convertible to an integer value
// This template will convert to point from anything that has a x and a y field that appear convertible to an integer value
template<typename TOther>
constexpr point(const TOther& other, std::enable_if_t<std::is_integral_v<decltype(std::declval<TOther>().x)> && std::is_integral_v<decltype(std::declval<TOther>().y)>, int> /*sentinel*/ = 0) :
point(static_cast<ptrdiff_t>(other.x), static_cast<ptrdiff_t>(other.y))
@ -64,7 +64,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
return !(*this == other);
}
operator bool() const noexcept
constexpr operator bool() const noexcept
{
return _x != 0 || _y != 0;
}

View file

@ -7,6 +7,8 @@
#include "size.h"
#include "some.h"
#include "recterator.h"
#ifdef UNIT_TESTING
class RectangleTests;
#endif
@ -16,6 +18,8 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
class rectangle
{
public:
using const_iterator = recterator;
constexpr rectangle() noexcept :
rectangle(til::point{ 0, 0 }, til::point{ 0, 0 })
{
@ -98,6 +102,13 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
}
constexpr rectangle& operator=(const rectangle other) noexcept
{
_topLeft = other._topLeft;
_bottomRight = other._bottomRight;
return (*this);
}
constexpr bool operator==(const rectangle& other) const noexcept
{
return _topLeft == other._topLeft &&
@ -115,6 +126,16 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
_topLeft.y() < _bottomRight.y();
}
const_iterator begin() const
{
return recterator(_topLeft, size());
}
const_iterator end() const
{
return recterator(_topLeft, size(), { _topLeft.x(), _topLeft.y() + height() });
}
// OR = union
constexpr rectangle operator|(const rectangle& other) const noexcept
{

119
src/inc/til/recterator.h Normal file
View file

@ -0,0 +1,119 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "point.h"
#include "size.h"
#ifdef UNIT_TESTING
class RecteratorTests;
#endif
namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
class recterator
{
public:
recterator(point topLeft, size size) :
_topLeft(topLeft),
_size(size),
_current(topLeft)
{
}
recterator(point topLeft, size size, point start) :
_topLeft(topLeft),
_size(size),
_current(start)
{
}
recterator& operator++()
{
if (_current.x() + 1 >= _topLeft.x() + _size.width())
{
_current = { _topLeft.x(), _current.y() + 1 };
}
else
{
_current = { _current.x() + 1, _current.y() };
}
return (*this);
}
bool operator==(const recterator& other) const
{
return _current == other._current &&
_topLeft == other._topLeft &&
_size == other._size;
}
bool operator!=(const recterator& other) const
{
return !(*this == other);
}
bool operator<(const recterator& other) const
{
return _current < other._current;
}
bool operator>(const recterator& other) const
{
return _current > other._current;
}
point operator*() const
{
return _current;
}
protected:
point _current;
const point _topLeft;
const size _size;
#ifdef UNIT_TESTING
friend class ::RecteratorTests;
#endif
};
};
#ifdef __WEX_COMMON_H__
namespace WEX::TestExecution
{
template<>
class VerifyOutputTraits<::til::recterator>
{
public:
static WEX::Common::NoThrowString ToString(const ::til::recterator& /*rect*/)
{
return WEX::Common::NoThrowString().Format(L"Yep that's a recterator.");
}
};
template<>
class VerifyCompareTraits<::til::recterator, ::til::recterator>
{
public:
static bool AreEqual(const ::til::recterator& expected, const ::til::recterator& actual) noexcept
{
return expected == actual;
}
static bool AreSame(const ::til::recterator& expected, const ::til::recterator& actual) noexcept
{
return &expected == &actual;
}
static bool IsLessThan(const ::til::recterator& expectedLess, const ::til::recterator& expectedGreater) = delete;
static bool IsGreaterThan(const ::til::recterator& expectedGreater, const ::til::recterator& expectedLess) = delete;
static bool IsNull(const ::til::recterator& object) noexcept = delete;
};
};
#endif

View file

@ -64,6 +64,11 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
return !(*this == other);
}
constexpr operator bool() const noexcept
{
return _width != 0 || _height != 0;
}
size operator+(const size& other) const
{
ptrdiff_t width;

View file

@ -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;

View file

@ -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;

View file

@ -862,8 +862,10 @@ 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 (auto rect : engine.GetDirtyArea())
{
SMALL_RECT srDirty = rect;
// Dirty is an inclusive rectangle, but oddly enough the IME was an exclusive one, so correct it.
srDirty.Bottom++;
srDirty.Right++;

View file

@ -65,26 +65,27 @@ using namespace Microsoft::Console::Types;
// TODO GH 2683: The default constructor should not throw.
DxEngine::DxEngine() :
RenderEngineBase(),
_isInvalidUsed{ false },
_invalidRect{ 0 },
_invalidMap{},
/*_isInvalidUsed{ false },
_invalidRect{ 0 },*/
_invalidScroll{ 0 },
_presentParams{ 0 },
_presentReady{ false },
_presentScroll{ 0 },
_presentDirty{ 0 },
/*_presentDirty{ 0 },*/
_presentOffset{ 0 },
_isEnabled{ false },
_isPainting{ false },
_displaySizePixels{ 0 },
_displaySizePixels{},
_foregroundColor{ 0 },
_backgroundColor{ 0 },
_selectionBackground{},
_glyphCell{ 0 },
_glyphCell{ 1, 1 },
_haveDeviceResources{ false },
_retroTerminalEffects{ false },
_antialiasingMode{ D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE },
_hwndTarget{ static_cast<HWND>(INVALID_HANDLE_VALUE) },
_sizeTarget{ 0 },
_sizeTarget{},
_dpi{ USER_DEFAULT_SCREEN_DPI },
_scale{ 1.0f },
_chainMode{ SwapChainMode::ForComposition },
@ -238,8 +239,8 @@ HRESULT DxEngine::_SetupTerminalEffects()
// Setup the viewport.
D3D11_VIEWPORT vp;
vp.Width = static_cast<FLOAT>(_displaySizePixels.cx);
vp.Height = static_cast<FLOAT>(_displaySizePixels.cy);
vp.Width = _displaySizePixels.width<FLOAT>();
vp.Height = _displaySizePixels.height<FLOAT>();
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = 0;
@ -409,6 +410,8 @@ void DxEngine::_ComputePixelShaderSettings() noexcept
_displaySizePixels = _GetClientSize();
_invalidMap.resize(_displaySizePixels / _glyphCell);
if (createSwapChain)
{
DXGI_SWAP_CHAIN_DESC1 SwapChainDesc = { 0 };
@ -427,11 +430,15 @@ void DxEngine::_ComputePixelShaderSettings() noexcept
case SwapChainMode::ForHwnd:
{
// use the HWND's dimensions for the swap chain dimensions.
RECT rect = { 0 };
RETURN_IF_WIN32_BOOL_FALSE(GetClientRect(_hwndTarget, &rect));
til::rectangle clientRect;
{
RECT rect = { 0 };
RETURN_IF_WIN32_BOOL_FALSE(GetClientRect(_hwndTarget, &rect));
clientRect = rect;
}
SwapChainDesc.Width = rect.right - rect.left;
SwapChainDesc.Height = rect.bottom - rect.top;
SwapChainDesc.Width = clientRect.width<UINT>();
SwapChainDesc.Height = clientRect.height<UINT>();
// We can't do alpha for HWNDs. Set to ignore. It will fail otherwise.
SwapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
@ -457,8 +464,8 @@ void DxEngine::_ComputePixelShaderSettings() noexcept
case SwapChainMode::ForComposition:
{
// Use the given target size for compositions.
SwapChainDesc.Width = _displaySizePixels.cx;
SwapChainDesc.Height = _displaySizePixels.cy;
SwapChainDesc.Width = _displaySizePixels.width<UINT>();
SwapChainDesc.Height = _displaySizePixels.height<UINT>();
// We're doing advanced composition pretty much for the purpose of pretty alpha, so turn it on.
SwapChainDesc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
@ -628,8 +635,8 @@ void DxEngine::_ReleaseDeviceResources() noexcept
return _dwriteFactory->CreateTextLayout(string,
gsl::narrow<UINT32>(stringLength),
_dwriteTextFormat.Get(),
gsl::narrow<float>(_displaySizePixels.cx),
_glyphCell.cy != 0 ? _glyphCell.cy : gsl::narrow<float>(_displaySizePixels.cy),
_displaySizePixels.width<float>(),
_glyphCell.height() != 0 ? _glyphCell.height<float>() : _displaySizePixels.height<float>(),
ppTextLayout);
}
@ -686,7 +693,11 @@ Microsoft::WRL::ComPtr<IDXGISwapChain1> DxEngine::GetSwapChain()
{
RETURN_HR_IF_NULL(E_INVALIDARG, psrRegion);
_InvalidOr(*psrRegion);
SMALL_RECT inclusive = *psrRegion;
inclusive.Right--;
inclusive.Bottom--;
_InvalidOr(inclusive);
return S_OK;
}
@ -700,7 +711,7 @@ Microsoft::WRL::ComPtr<IDXGISwapChain1> DxEngine::GetSwapChain()
{
RETURN_HR_IF_NULL(E_INVALIDARG, pcoordCursor);
const SMALL_RECT sr = Microsoft::Console::Types::Viewport::FromCoord(*pcoordCursor).ToInclusive();
const SMALL_RECT sr = Microsoft::Console::Types::Viewport::FromCoord(*pcoordCursor).ToExclusive();
return Invalidate(&sr);
}
@ -748,39 +759,42 @@ Microsoft::WRL::ComPtr<IDXGISwapChain1> DxEngine::GetSwapChain()
{
try
{
POINT delta = { 0 };
delta.x = pcoordDelta->X * _glyphCell.cx;
delta.y = pcoordDelta->Y * _glyphCell.cy;
/*til::point delta(*pcoordDelta);*/
// TODO: do this.
_InvalidOffset(delta);
//POINT delta = { 0 };
//delta.x = pcoordDelta->X * _glyphCell.cx;
//delta.y = pcoordDelta->Y * _glyphCell.cy;
_invalidScroll.cx += delta.x;
_invalidScroll.cy += delta.y;
//_InvalidOffset(delta);
// Add the revealed portion of the screen from the scroll to the invalid area.
const RECT display = _GetDisplayRect();
RECT reveal = display;
//_invalidScroll.cx += delta.x;
//_invalidScroll.cy += delta.y;
// X delta first
OffsetRect(&reveal, delta.x, 0);
IntersectRect(&reveal, &reveal, &display);
SubtractRect(&reveal, &display, &reveal);
//// Add the revealed portion of the screen from the scroll to the invalid area.
//const RECT display = _GetDisplayRect();
//RECT reveal = display;
if (!IsRectEmpty(&reveal))
{
_InvalidOr(reveal);
}
//// X delta first
//OffsetRect(&reveal, delta.x, 0);
//IntersectRect(&reveal, &reveal, &display);
//SubtractRect(&reveal, &display, &reveal);
// Y delta second (subtract rect won't work if you move both)
reveal = display;
OffsetRect(&reveal, 0, delta.y);
IntersectRect(&reveal, &reveal, &display);
SubtractRect(&reveal, &display, &reveal);
//if (!IsRectEmpty(&reveal))
//{
// _InvalidOr(reveal);
//}
if (!IsRectEmpty(&reveal))
{
_InvalidOr(reveal);
}
//// Y delta second (subtract rect won't work if you move both)
//reveal = display;
//OffsetRect(&reveal, 0, delta.y);
//IntersectRect(&reveal, &reveal, &display);
//SubtractRect(&reveal, &display, &reveal);
//if (!IsRectEmpty(&reveal))
//{
// _InvalidOr(reveal);
//}
}
CATCH_RETURN();
}
@ -796,8 +810,9 @@ Microsoft::WRL::ComPtr<IDXGISwapChain1> DxEngine::GetSwapChain()
// - S_OK
[[nodiscard]] HRESULT DxEngine::InvalidateAll() noexcept
{
const RECT screen = _GetDisplayRect();
_InvalidOr(screen);
_invalidMap.set_all();
/*const RECT screen = _GetDisplayRect();
_InvalidOr(screen);*/
return S_OK;
}
@ -822,7 +837,7 @@ Microsoft::WRL::ComPtr<IDXGISwapChain1> DxEngine::GetSwapChain()
// - <none>
// Return Value:
// - X by Y area in pixels of the surface
[[nodiscard]] SIZE DxEngine::_GetClientSize() const noexcept
[[nodiscard]] til::size DxEngine::_GetClientSize() const noexcept
{
switch (_chainMode)
{
@ -831,39 +846,42 @@ Microsoft::WRL::ComPtr<IDXGISwapChain1> DxEngine::GetSwapChain()
RECT clientRect = { 0 };
LOG_IF_WIN32_BOOL_FALSE(GetClientRect(_hwndTarget, &clientRect));
SIZE clientSize = { 0 };
clientSize.cx = clientRect.right - clientRect.left;
clientSize.cy = clientRect.bottom - clientRect.top;
til::rectangle client{ clientRect };
return clientSize;
return client.size();
}
case SwapChainMode::ForComposition:
{
SIZE size = _sizeTarget;
size.cx = static_cast<LONG>(size.cx * _scale);
size.cy = static_cast<LONG>(size.cy * _scale);
return size;
try
{
// TODO: fix scale
/*return _sizeTarget * _scale;*/
return _sizeTarget;
}
CATCH_LOG();
return _sizeTarget;
}
default:
FAIL_FAST_HR(E_NOTIMPL);
}
}
// Routine Description:
// - Helper to multiply all parameters of a rectangle by the font size
// to convert from characters to pixels.
// Arguments:
// - cellsToPixels - rectangle to update
// - fontSize - scaling factors
// Return Value:
// - <none> - Updates reference
void _ScaleByFont(RECT& cellsToPixels, SIZE fontSize) noexcept
{
cellsToPixels.left *= fontSize.cx;
cellsToPixels.right *= fontSize.cx;
cellsToPixels.top *= fontSize.cy;
cellsToPixels.bottom *= fontSize.cy;
}
//// Routine Description:
//// - Helper to multiply all parameters of a rectangle by the font size
//// to convert from characters to pixels.
//// Arguments:
//// - cellsToPixels - rectangle to update
//// - fontSize - scaling factors
//// Return Value:
//// - <none> - Updates reference
//void _ScaleByFont(RECT& cellsToPixels, SIZE fontSize) noexcept
//{
// cellsToPixels.left *= fontSize.cx;
// cellsToPixels.right *= fontSize.cx;
// cellsToPixels.top *= fontSize.cy;
// cellsToPixels.bottom *= fontSize.cy;
//}
// Routine Description:
// - Retrieves a rectangle representation of the pixel size of the
@ -872,9 +890,9 @@ void _ScaleByFont(RECT& cellsToPixels, SIZE fontSize) noexcept
// - <none>
// Return Value;
// - Origin-placed rectangle representing the pixel size of the surface
[[nodiscard]] RECT DxEngine::_GetDisplayRect() const noexcept
[[nodiscard]] til::rectangle DxEngine::_GetDisplayRect() const noexcept
{
return { 0, 0, _displaySizePixels.cx, _displaySizePixels.cy };
return til::rectangle{ til::point{ 0, 0 }, _displaySizePixels };
}
// Routine Description:
@ -887,22 +905,25 @@ void _ScaleByFont(RECT& cellsToPixels, SIZE fontSize) noexcept
// - <none>
void DxEngine::_InvalidOffset(POINT delta)
{
if (_isInvalidUsed)
{
// Copy the existing invalid rect
RECT invalidNew = _invalidRect;
til::point pt{ delta };
_invalidMap += pt;
// Offset it to the new position
THROW_IF_WIN32_BOOL_FALSE(OffsetRect(&invalidNew, delta.x, delta.y));
//if (_isInvalidUsed)
//{
// // Copy the existing invalid rect
// RECT invalidNew = _invalidRect;
// Get the rect representing the display
const RECT rectScreen = _GetDisplayRect();
// // Offset it to the new position
// THROW_IF_WIN32_BOOL_FALSE(OffsetRect(&invalidNew, delta.x, delta.y));
// Ensure that the new invalid rectangle is still on the display
IntersectRect(&invalidNew, &invalidNew, &rectScreen);
// // Get the rect representing the display
// const RECT rectScreen = _GetDisplayRect();
_invalidRect = invalidNew;
}
// // Ensure that the new invalid rectangle is still on the display
// IntersectRect(&invalidNew, &invalidNew, &rectScreen);
// _invalidRect = invalidNew;
//}
}
// Routine description:
@ -914,17 +935,22 @@ void DxEngine::_InvalidOffset(POINT delta)
// - <none>
void DxEngine::_InvalidOr(SMALL_RECT sr) noexcept
{
RECT region;
region.left = sr.Left;
region.top = sr.Top;
region.right = sr.Right;
region.bottom = sr.Bottom;
_ScaleByFont(region, _glyphCell);
if (_invalidMap)
{
_invalidMap.set(sr);
}
region.right += _glyphCell.cx;
region.bottom += _glyphCell.cy;
//RECT region;
//region.left = sr.Left;
//region.top = sr.Top;
//region.right = sr.Right;
//region.bottom = sr.Bottom;
//_ScaleByFont(region, _glyphCell);
_InvalidOr(region);
//region.right += _glyphCell.cx;
//region.bottom += _glyphCell.cy;
//_InvalidOr(region);
}
// Routine Description:
@ -933,20 +959,20 @@ void DxEngine::_InvalidOr(SMALL_RECT sr) noexcept
// - rc - Dirty pixel rectangle
// Return Value:
// - <none>
void DxEngine::_InvalidOr(RECT rc) noexcept
void DxEngine::_InvalidOr(RECT /*rc*/) noexcept
{
if (_isInvalidUsed)
{
UnionRect(&_invalidRect, &_invalidRect, &rc);
//if (_isInvalidUsed)
//{
// UnionRect(&_invalidRect, &_invalidRect, &rc);
const RECT rcScreen = _GetDisplayRect();
IntersectRect(&_invalidRect, &_invalidRect, &rcScreen);
}
else
{
_invalidRect = rc;
_isInvalidUsed = true;
}
// const RECT rcScreen = _GetDisplayRect();
// IntersectRect(&_invalidRect, &_invalidRect, &rcScreen);
//}
//else
//{
// _invalidRect = rc;
// _isInvalidUsed = true;
//}
}
// Routine Description:
@ -971,25 +997,8 @@ void DxEngine::_InvalidOr(RECT rc) noexcept
// - Any DirectX error, a memory error, etc.
[[nodiscard]] HRESULT DxEngine::StartPaint() noexcept
{
FAIL_FAST_IF_FAILED(InvalidateAll());
RETURN_HR_IF(E_NOT_VALID_STATE, _isPainting); // invalid to start a paint while painting.
#pragma warning(suppress : 26477 26485 26494 26482 26446 26447) // We don't control TraceLoggingWrite
TraceLoggingWrite(g_hDxRenderProvider,
"Invalid",
TraceLoggingInt32(_invalidRect.bottom - _invalidRect.top, "InvalidHeight"),
TraceLoggingInt32((_invalidRect.bottom - _invalidRect.top) / _glyphCell.cy, "InvalidHeightChars"),
TraceLoggingInt32(_invalidRect.right - _invalidRect.left, "InvalidWidth"),
TraceLoggingInt32((_invalidRect.right - _invalidRect.left) / _glyphCell.cx, "InvalidWidthChars"),
TraceLoggingInt32(_invalidRect.left, "InvalidX"),
TraceLoggingInt32(_invalidRect.left / _glyphCell.cx, "InvalidXChars"),
TraceLoggingInt32(_invalidRect.top, "InvalidY"),
TraceLoggingInt32(_invalidRect.top / _glyphCell.cy, "InvalidYChars"),
TraceLoggingInt32(_invalidScroll.cx, "ScrollWidth"),
TraceLoggingInt32(_invalidScroll.cx / _glyphCell.cx, "ScrollWidthChars"),
TraceLoggingInt32(_invalidScroll.cy, "ScrollHeight"),
TraceLoggingInt32(_invalidScroll.cy / _glyphCell.cy, "ScrollHeightChars"));
if (_isEnabled)
{
try
@ -999,8 +1008,7 @@ void DxEngine::_InvalidOr(RECT rc) noexcept
{
RETURN_IF_FAILED(_CreateDeviceResources(true));
}
else if (_displaySizePixels.cy != clientSize.cy ||
_displaySizePixels.cx != clientSize.cx)
else if (_displaySizePixels != clientSize)
{
// OK, we're going to play a dangerous game here for the sake of optimizing resize
// First, set up a complete clear of all device resources if something goes terribly wrong.
@ -1013,7 +1021,7 @@ void DxEngine::_InvalidOr(RECT rc) noexcept
_d2dRenderTarget.Reset();
// Change the buffer size and recreate the render target (and surface)
RETURN_IF_FAILED(_dxgiSwapChain->ResizeBuffers(2, clientSize.cx, clientSize.cy, DXGI_FORMAT_B8G8R8A8_UNORM, 0));
RETURN_IF_FAILED(_dxgiSwapChain->ResizeBuffers(2, clientSize.width<UINT>(), clientSize.height<UINT>(), DXGI_FORMAT_B8G8R8A8_UNORM, 0));
RETURN_IF_FAILED(_PrepareRenderTarget());
// OK we made it past the parts that can cause errors. We can release our failure handler.
@ -1021,10 +1029,32 @@ void DxEngine::_InvalidOr(RECT rc) noexcept
// And persist the new size.
_displaySizePixels = clientSize;
_invalidMap.resize(_displaySizePixels / _glyphCell);
}
_d2dRenderTarget->BeginDraw();
_isPainting = true;
// Walk the map for dirty rectangles and store them up.
// We're going to have to go over them multiple times, so don't spend all the iteration
// work multiple times.
for (auto it = _invalidMap.begin_runs(); it < _invalidMap.end_runs(); ++it)
{
auto rect = *it;
#pragma warning(suppress : 26477 26485 26494 26482 26446 26447) // We don't control TraceLoggingWrite
TraceLoggingWrite(g_hDxRenderProvider,
"Invalid",
TraceLoggingInt32((LONG)rect.height(), "InvalidHeightChars"),
TraceLoggingInt32((LONG)rect.width(), "InvalidWidthChars"),
TraceLoggingInt32((LONG)rect.left(), "InvalidXChars"),
TraceLoggingInt32((LONG)rect.top(), "InvalidYChars"),
TraceLoggingInt32(_invalidScroll.cx, "ScrollWidthChars"),
TraceLoggingInt32(_invalidScroll.cy, "ScrollHeightChars"));
_dirtyRects.push_back(rect);
}
}
CATCH_RETURN();
}
@ -1054,15 +1084,19 @@ void DxEngine::_InvalidOr(RECT rc) noexcept
{
if (_invalidScroll.cy != 0 || _invalidScroll.cx != 0)
{
_presentDirty = _invalidRect;
// The scroll rect is the entire screen minus the revealed areas.
// Get the entire screen into a rectangle.
til::rectangle scrollArea = _GetDisplayRect();
const RECT display = _GetDisplayRect();
SubtractRect(&_presentScroll, &display, &_presentDirty);
// Reduce the size of the rectangle by the scroll
scrollArea -= _invalidScroll;
_presentScroll = scrollArea;
_presentOffset.x = _invalidScroll.cx;
_presentOffset.y = _invalidScroll.cy;
_presentParams.DirtyRectsCount = 1;
_presentParams.pDirtyRects = &_presentDirty;
_presentParams.DirtyRectsCount = gsl::narrow<UINT>(_dirtyRectRects.size());
_presentParams.pDirtyRects = _dirtyRectRects.data();
_presentParams.pScrollOffset = &_presentOffset;
_presentParams.pScrollRect = &_presentScroll;
@ -1083,8 +1117,11 @@ void DxEngine::_InvalidOr(RECT rc) noexcept
}
}
_invalidRect = { 0 };
_isInvalidUsed = false;
_dirtyRects.clear();
_invalidMap.reset_all();
/*_invalidRect = { 0 };
_isInvalidUsed = false;*/
_invalidScroll = { 0 };
@ -1161,7 +1198,8 @@ void DxEngine::_InvalidOr(RECT rc) noexcept
RETURN_IF_FAILED(_CopyFrontToBack());
_presentReady = false;
_presentDirty = { 0 };
_dirtyRectRects.clear();
/*_presentDirty = { 0 };*/
_presentOffset = { 0 };
_presentScroll = { 0 };
_presentParams = { 0 };
@ -1184,27 +1222,22 @@ void DxEngine::_InvalidOr(RECT rc) noexcept
}
// Routine Description:
// - This paints in the back most layer of the frame with the background color.
// - This paints in the back most layer of the frame with clear/nothing so it can
// be transparent if it wants to be.
// Arguments:
// - <none>
// Return Value:
// - S_OK
[[nodiscard]] HRESULT DxEngine::PaintBackground() noexcept
{
switch (_chainMode)
const D2D1_COLOR_F nothing = { 0 }; // 0 alpha and color is black.
for (const D2D1_RECT_F rect : _dirtyRects)
{
case SwapChainMode::ForHwnd:
_d2dRenderTarget->FillRectangle(D2D1::RectF(static_cast<float>(_invalidRect.left),
static_cast<float>(_invalidRect.top),
static_cast<float>(_invalidRect.right),
static_cast<float>(_invalidRect.bottom)),
_d2dBrushBackground.Get());
break;
case SwapChainMode::ForComposition:
D2D1_COLOR_F nothing = { 0 };
_d2dRenderTarget->SetTransform(D2D1::Matrix3x2F::Scale(_glyphCell));
_d2dRenderTarget->PushAxisAlignedClip(rect, D2D1_ANTIALIAS_MODE_ALIASED);
_d2dRenderTarget->Clear(nothing);
break;
_d2dRenderTarget->PopAxisAlignedClip();
_d2dRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
}
return S_OK;
@ -1225,10 +1258,10 @@ void DxEngine::_InvalidOr(RECT rc) noexcept
{
try
{
const til::point cellPoint{ coord };
// Calculate positioning of our origin.
D2D1_POINT_2F origin;
origin.x = static_cast<float>(coord.X * _glyphCell.cx);
origin.y = static_cast<float>(coord.Y * _glyphCell.cy);
const D2D1_POINT_2F origin = til::point{ coord } * _glyphCell;
// Create the text layout
CustomTextLayout layout(_dwriteFactory.Get(),
@ -1236,7 +1269,7 @@ void DxEngine::_InvalidOr(RECT rc) noexcept
_dwriteTextFormat.Get(),
_dwriteFontFace.Get(),
clusters,
_glyphCell.cx);
_glyphCell.width());
// Get the baseline for this font as that's where we draw from
DWRITE_LINE_SPACING spacing;
@ -1248,7 +1281,7 @@ void DxEngine::_InvalidOr(RECT rc) noexcept
_d2dBrushBackground.Get(),
_dwriteFactory.Get(),
spacing,
D2D1::SizeF(gsl::narrow<FLOAT>(_glyphCell.cx), gsl::narrow<FLOAT>(_glyphCell.cy)),
_glyphCell,
D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT);
// Layout then render the text
@ -1349,25 +1382,18 @@ void DxEngine::_InvalidOr(RECT rc) noexcept
// - rect - Rectangle to invert or highlight to make the selection area
// Return Value:
// - S_OK or relevant DirectX error.
[[nodiscard]] HRESULT DxEngine::PaintSelection(const SMALL_RECT rect) noexcept
[[nodiscard]] HRESULT DxEngine::PaintSelection(SMALL_RECT rect) noexcept
{
const auto existingColor = _d2dBrushForeground->GetColor();
_d2dBrushForeground->SetColor(_selectionBackground);
const auto resetColorOnExit = wil::scope_exit([&]() noexcept { _d2dBrushForeground->SetColor(existingColor); });
RECT pixels;
pixels.left = rect.Left * _glyphCell.cx;
pixels.top = rect.Top * _glyphCell.cy;
pixels.right = rect.Right * _glyphCell.cx;
pixels.bottom = rect.Bottom * _glyphCell.cy;
D2D1_RECT_F draw = { 0 };
draw.left = static_cast<float>(pixels.left);
draw.top = static_cast<float>(pixels.top);
draw.right = static_cast<float>(pixels.right);
draw.bottom = static_cast<float>(pixels.bottom);
rect.Bottom--;
rect.Right--;
/* rect is SMALL_RECT */
const D2D1_RECT_F draw = til::rectangle{ rect } * _glyphCell;
_d2dRenderTarget->FillRectangle(draw, _d2dBrushForeground.Get());
return S_OK;
@ -1395,16 +1421,12 @@ enum class CursorPaintType
return S_FALSE;
}
// Create rectangular block representing where the cursor can fill.
D2D1_RECT_F rect = { 0 };
rect.left = static_cast<float>(options.coordCursor.X * _glyphCell.cx);
rect.top = static_cast<float>(options.coordCursor.Y * _glyphCell.cy);
rect.right = static_cast<float>(rect.left + _glyphCell.cx);
rect.bottom = static_cast<float>(rect.top + _glyphCell.cy);
D2D1_RECT_F rect = til::rectangle{ til::point{ options.coordCursor } } * _glyphCell;
// If we're double-width, make it one extra glyph wider
if (options.fIsDoubleWidth)
{
rect.right += _glyphCell.cx;
rect.right += _glyphCell.width();
}
CursorPaintType paintType = CursorPaintType::Fill;
@ -1416,7 +1438,7 @@ enum class CursorPaintType
// Enforce min/max cursor height
ULONG ulHeight = std::clamp(options.ulCursorHeightPercent, s_ulMinCursorHeightPercent, s_ulMaxCursorHeightPercent);
ulHeight = gsl::narrow<ULONG>((_glyphCell.cy * ulHeight) / 100);
ulHeight = gsl::narrow<ULONG>((_glyphCell.height() * ulHeight) / 100);
rect.top = rect.bottom - ulHeight;
break;
}
@ -1586,10 +1608,9 @@ CATCH_RETURN()
try
{
const auto size = fiFontInfo.GetSize();
_glyphCell = fiFontInfo.GetSize();
_glyphCell.cx = size.X;
_glyphCell.cy = size.Y;
_invalidMap.resize(_displaySizePixels / _glyphCell);
}
CATCH_RETURN();
@ -1598,10 +1619,9 @@ CATCH_RETURN()
[[nodiscard]] Viewport DxEngine::GetViewportInCharacters(const Viewport& viewInPixels) noexcept
{
const short widthInChars = gsl::narrow_cast<short>(viewInPixels.Width() / _glyphCell.cx);
const short heightInChars = gsl::narrow_cast<short>(viewInPixels.Height() / _glyphCell.cy);
const auto cellSize = til::size{ viewInPixels.Dimensions() } / _glyphCell;
return Viewport::FromDimensions(viewInPixels.Origin(), { widthInChars, heightInChars });
return Viewport::FromDimensions(viewInPixels.Origin(), cellSize);
}
// Routine Description:
@ -1686,19 +1706,10 @@ float DxEngine::GetScaling() const noexcept
// - <none>
// Return Value:
// - Rectangle describing dirty area in characters.
[[nodiscard]] std::vector<SMALL_RECT> DxEngine::GetDirtyArea()
// TODO: maybe this should be returning a ref... not a copy...
[[nodiscard]] std::vector<til::rectangle> DxEngine::GetDirtyArea()
{
SMALL_RECT r;
r.Top = gsl::narrow<SHORT>(floor(_invalidRect.top / _glyphCell.cy));
r.Left = gsl::narrow<SHORT>(floor(_invalidRect.left / _glyphCell.cx));
r.Bottom = gsl::narrow<SHORT>(floor(_invalidRect.bottom / _glyphCell.cy));
r.Right = gsl::narrow<SHORT>(floor(_invalidRect.right / _glyphCell.cx));
// Exclusive to inclusive
r.Bottom--;
r.Right--;
return { r };
return _dirtyRects;
}
// Routine Description:
@ -1710,7 +1721,7 @@ float DxEngine::GetScaling() const noexcept
// - Nearest integer short x and y values for each cell.
[[nodiscard]] COORD DxEngine::_GetFontSize() const noexcept
{
return { gsl::narrow<SHORT>(_glyphCell.cx), gsl::narrow<SHORT>(_glyphCell.cy) };
return _glyphCell;
}
// Routine Description:
@ -1746,7 +1757,7 @@ float DxEngine::GetScaling() const noexcept
_dwriteTextFormat.Get(),
_dwriteFontFace.Get(),
{ &cluster, 1 },
_glyphCell.cx);
_glyphCell.width());
UINT32 columns = 0;
RETURN_IF_FAILED(layout.GetColumns(&columns));

View file

@ -27,6 +27,9 @@
#include <TraceLoggingProvider.h>
#include "til/bitmap.h"
#include "til/operators.h"
TRACELOGGING_DECLARE_PROVIDER(g_hDxRenderProvider);
namespace Microsoft::Console::Render
@ -95,7 +98,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;
@ -121,7 +124,7 @@ namespace Microsoft::Console::Render
SwapChainMode _chainMode;
HWND _hwndTarget;
SIZE _sizeTarget;
til::size _sizeTarget;
int _dpi;
float _scale;
@ -130,8 +133,8 @@ namespace Microsoft::Console::Render
bool _isEnabled;
bool _isPainting;
SIZE _displaySizePixels;
SIZE _glyphCell;
til::size _displaySizePixels;
til::size _glyphCell;
D2D1_COLOR_F _defaultForegroundColor;
D2D1_COLOR_F _defaultBackgroundColor;
@ -140,10 +143,14 @@ namespace Microsoft::Console::Render
D2D1_COLOR_F _backgroundColor;
D2D1_COLOR_F _selectionBackground;
[[nodiscard]] RECT _GetDisplayRect() const noexcept;
[[nodiscard]] til::rectangle _GetDisplayRect() const noexcept;
bool _isInvalidUsed;
RECT _invalidRect;
til::bitmap _invalidMap;
std::vector<til::rectangle> _dirtyRects;
std::vector<RECT> _dirtyRectRects;
//bool _isInvalidUsed;
//RECT _invalidRect;
SIZE _invalidScroll;
void _InvalidOr(SMALL_RECT sr) noexcept;
@ -152,7 +159,7 @@ namespace Microsoft::Console::Render
void _InvalidOffset(POINT pt);
bool _presentReady;
RECT _presentDirty;
/*RECT _presentDirty;*/
RECT _presentScroll;
POINT _presentOffset;
DXGI_PRESENT_PARAMETERS _presentParams;
@ -246,7 +253,7 @@ namespace Microsoft::Console::Render
[[nodiscard]] COORD _GetFontSize() const noexcept;
[[nodiscard]] SIZE _GetClientSize() const noexcept;
[[nodiscard]] til::size _GetClientSize() const noexcept;
[[nodiscard]] D2D1_COLOR_F _ColorFFromColorRef(const COLORREF color) noexcept;

View file

@ -4,6 +4,7 @@
#pragma once
// This includes support libraries from the CRT, STL, WIL, and GSL
#define BLOCK_TIL
#include "LibraryIncludes.h"
#include <windows.h>
@ -34,4 +35,7 @@
#include <dwrite_2.h>
#include <dwrite_3.h>
// Include TIL after DX so we can use the DX conversions.
#include "til.h"
#pragma hdrstop

View file

@ -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;

View file

@ -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;

View file

@ -14,6 +14,7 @@ Author(s):
#pragma once
#include "til/rectangle.h"
#include "../../inc/conattrs.hpp"
#include "Cluster.hpp"
#include "FontInfoDesired.hpp"
@ -117,7 +118,7 @@ namespace Microsoft::Console::Render
_Out_ FontInfo& FontInfo,
const int iDpi) noexcept = 0;
virtual std::vector<SMALL_RECT> GetDirtyArea() = 0;
[[nodiscard]] 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;

View file

@ -403,7 +403,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() };
}

View file

@ -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;

View file

@ -144,6 +144,11 @@ using namespace Microsoft::Console::Render;
// - S_OK, else an appropriate HRESULT for failing to allocate or write.
[[nodiscard]] HRESULT VtEngine::_InvalidCombine(const Viewport invalid) noexcept
{
if (_invalidMap)
{
_invalidMap.set(invalid.ToInclusive());
}
if (!_fInvalidRectUsed)
{
_invalidRect = invalid;

View file

@ -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 _invalidRects;
}
// Routine Description:

View file

@ -27,6 +27,7 @@ using namespace Microsoft::Console::Types;
// If there's nothing to do, quick return
bool somethingToDo = _fInvalidRectUsed ||
!_invalidMap.empty() ||
(_scrollDelta.X != 0 || _scrollDelta.Y != 0) ||
_cursorMoved ||
_titleChanged;
@ -34,6 +35,14 @@ using namespace Microsoft::Console::Types;
_quickReturn = !somethingToDo;
_trace.TraceStartPaint(_quickReturn, _fInvalidRectUsed, _invalidRect, _lastViewport, _scrollDelta, _cursorMoved);
if (somethingToDo)
{
for (auto it = _invalidMap.begin_runs(); it < _invalidMap.end_runs(); ++it)
{
_invalidRects.push_back(*it);
}
}
return _quickReturn ? S_FALSE : S_OK;
}
@ -50,6 +59,8 @@ using namespace Microsoft::Console::Types;
{
_trace.TraceEndPaint();
_invalidMap.reset_all();
_invalidRects.clear();
_invalidRect = Viewport::Empty();
_fInvalidRectUsed = false;
_scrollDelta = { 0 };

View file

@ -36,6 +36,8 @@ VtEngine::VtEngine(_In_ wil::unique_hfile pipe,
_lastWasBold(false),
_lastViewport(initialViewport),
_invalidRect(Viewport::Empty()),
_invalidMap(initialViewport.Dimensions()),
_invalidRects(),
_fInvalidRectUsed(false),
_lastRealCursor({ 0 }),
_lastText({ 0 }),
@ -293,6 +295,8 @@ CATCH_RETURN();
if ((oldView.Height() != newView.Height()) || (oldView.Width() != newView.Width()))
{
_invalidMap.resize(_lastViewport.Dimensions());
// Don't emit a resize event if we've requested it be suppressed
if (!_suppressResizeRepaint)
{

View file

@ -24,6 +24,8 @@ Author(s):
#include <string>
#include <functional>
#include "til/bitmap.h"
// fwdecl unittest classes
#ifdef UNIT_TESTING
namespace TerminalCoreUnitTests
@ -89,7 +91,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;
@ -120,6 +122,8 @@ namespace Microsoft::Console::Render
COLORREF _LastBG;
bool _lastWasBold;
til::bitmap _invalidMap;
std::vector<til::rectangle> _invalidRects;
Microsoft::Console::Types::Viewport _lastViewport;
Microsoft::Console::Types::Viewport _invalidRect;

View file

@ -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;

View file

@ -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;

View file

@ -0,0 +1,134 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "til/bitmap.h"
using namespace WEX::Common;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
class BitmapTests
{
TEST_CLASS(BitmapTests);
TEST_METHOD(Construct)
{
COORD foo;
foo.X = 12;
foo.Y = 14;
til::point p(foo);
VERIFY_ARE_EQUAL(foo.X, p.x());
VERIFY_ARE_EQUAL(foo.Y, p.y());
POINT pt;
pt.x = 88;
pt.y = 98;
til::point q(pt);
VERIFY_ARE_EQUAL(pt.x, q.x());
VERIFY_ARE_EQUAL(pt.y, q.y());
SIZE sz;
sz.cx = 11;
sz.cy = 13;
til::size r(sz);
VERIFY_ARE_EQUAL(sz.cx, r.width());
VERIFY_ARE_EQUAL(sz.cy, r.height());
COORD bar;
bar.X = 57;
bar.Y = 15;
til::size s(bar);
VERIFY_ARE_EQUAL(bar.X, s.width());
VERIFY_ARE_EQUAL(bar.Y, s.height());
SIZE mapSize{ 10, 10 };
til::bitmap x(mapSize);
x.set({ 4, 4 });
til::rectangle area(til::point{ 5, 5 }, til::size{ 2, 2 });
x.set(area);
Log::Comment(L"Row 4!");
for (auto it = x.begin_row(4); it < x.end_row(4); ++it)
{
if (*it)
{
Log::Comment(L"True");
}
else
{
Log::Comment(L"False");
}
}
Log::Comment(L"All!");
auto start = x.begin();
auto end = x.end();
for (const auto& y : x)
{
if (y)
{
Log::Comment(L"True");
}
else
{
Log::Comment(L"False");
}
}
SMALL_RECT smrc;
smrc.Top = 31;
smrc.Bottom = 41;
smrc.Left = 59;
smrc.Right = 265;
til::rectangle smrectangle(smrc);
VERIFY_ARE_EQUAL(smrc.Top, smrectangle.top());
VERIFY_ARE_EQUAL(smrc.Bottom, smrectangle.bottom());
VERIFY_ARE_EQUAL(smrc.Left, smrectangle.left());
VERIFY_ARE_EQUAL(smrc.Right, smrectangle.right());
RECT bgrc;
bgrc.top = 3;
bgrc.bottom = 5;
bgrc.left = 8;
bgrc.right = 9;
til::rectangle bgrectangle(bgrc);
VERIFY_ARE_EQUAL(bgrc.top, bgrectangle.top());
VERIFY_ARE_EQUAL(bgrc.bottom, bgrectangle.bottom());
VERIFY_ARE_EQUAL(bgrc.left, bgrectangle.left());
VERIFY_ARE_EQUAL(bgrc.right, bgrectangle.right());
}
TEST_METHOD(Runerator)
{
til::bitmap foo{ til::size{ 4, 8 } };
foo.reset_all();
foo.set(til::rectangle{ til::point{ 1, 1 }, til::size{ 2, 2 } });
foo.set(til::rectangle{ til::point{ 3, 5 } });
foo.set(til::rectangle{ til::point{ 0, 6 } });
std::deque<til::rectangle> expectedRects;
expectedRects.push_back(til::rectangle{ til::point{ 1, 1 }, til::size{ 2, 1 } });
expectedRects.push_back(til::rectangle{ til::point{ 1, 2 }, til::size{ 2, 1 } });
expectedRects.push_back(til::rectangle{ til::point{ 3, 5 } });
expectedRects.push_back(til::rectangle{ til::point{ 0, 6 } });
for (auto it = foo.begin_runs(); it < foo.end_runs(); ++it)
{
const auto actual = *it;
const auto expected = expectedRects.front();
VERIFY_ARE_EQUAL(expected, actual);
expectedRects.pop_front();
}
}
};

View file

@ -0,0 +1,171 @@
// 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(RectangleAdditionSize)
{
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(RectangleInplaceAdditionSize)
{
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(RectangleSubtractionSize)
{
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(RectangleInplaceSubtractionSize)
{
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);
}
}
};

View file

@ -168,6 +168,21 @@ class SizeTests
}
}
TEST_METHOD(Boolean)
{
const til::size empty;
VERIFY_IS_FALSE(empty);
const til::size heightOnly{ 0, 10 };
VERIFY_IS_TRUE(heightOnly);
const til::size widthOnly{ 10, 0 };
VERIFY_IS_TRUE(widthOnly);
const til::size both{ 10, 10 };
VERIFY_IS_TRUE(both);
}
TEST_METHOD(Addition)
{
Log::Comment(L"0.) Addition of two things that should be in bounds.");

View file

@ -10,6 +10,8 @@
</PropertyGroup>
<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" />

View file

@ -4,10 +4,12 @@
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="BitmapTests.cpp" />
<ClCompile Include="SomeTests.cpp" />
<ClCompile Include="..\precomp.cpp" />
<ClCompile Include="u8u16convertTests.cpp" />
<ClCompile Include="SizeTests.cpp" />
<ClCompile Include="OperatorTests.cpp" />
<ClCompile Include="ColorTests.cpp" />
<ClCompile Include="PointTests.cpp" />
<ClCompile Include="RectangleTests.cpp" />

View file

@ -2,7 +2,6 @@
// Licensed under the MIT license.
#include "precomp.h"
#include "WexTestClass.h"
using namespace WEX::Common;
using namespace WEX::Logging;