Compare commits

...

10 Commits

Author SHA1 Message Date
Michael Niksa
6cf04176e9 Madness I say. 2020-03-05 16:57:33 -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
20 changed files with 2252 additions and 161 deletions

View File

@@ -86,7 +86,9 @@
#include <wrl.h>
// TIL - Terminal Implementation Library
#ifndef BLOCK_TIL // Certain projects may want to include TIL manually to gain superpowers
#include "til.h"
#endif
#pragma warning(pop)

View File

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

206
src/inc/til/bitmap.h Normal file
View File

@@ -0,0 +1,206 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include <vector>
#include "size.h"
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 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.resize(_size.area());
}
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());
}
void set(til::point pt)
{
_bits[pt.y() * _size.width() + pt.x()] = true;
}
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()
{
_bits.assign(_size.area(), true);
}
void reset_all()
{
_bits.assign(_size.area(), false);
}
const til::size& size() const
{
return _size;
}
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:
const til::size _size;
std::vector<bool> _bits;
};
}

266
src/inc/til/operators.h Normal file
View File

@@ -0,0 +1,266 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "rectangle.h"
#include "size.h"
#include "bitmap.h"
#define _INLINEPREFIX __declspec(noinline) inline
namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
_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
r += width;
}
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
l += width;
}
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
b += height;
}
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
t += height;
}
return rectangle{ til::point{l, t}, til::point{r, b} };
}
_INLINEPREFIX rectangle& operator+=(rectangle& lhs, const size& rhs)
{
lhs = lhs + rhs;
return lhs;
}
_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
r -= width;
}
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
l -= width;
}
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
b -= height;
}
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
t -= height;
}
return rectangle{ til::point{l, t}, til::point{r, b} };
}
_INLINEPREFIX rectangle& operator-=(rectangle& lhs, const size& rhs)
{
lhs = lhs - rhs;
return lhs;
}
//rectangle operator+(const rectangle& lhs, const point& rhs)
//{
// const auto l = lhs.left() + rhs.x();
// const auto r = lhs.right() + rhs.x();
// const auto t = lhs.top() + rhs.y();
// const auto b = lhs.bottom() + rhs.y();
// return rectangle{ til::point{l, t}, til::point{r, b} };
//}
//rectangle& operator+=(rectangle& lhs, const point& rhs)
//{
// lhs = lhs + rhs;
// return lhs;
//}
//rectangle operator+(const rectangle& lhs, const rectangle& rhs)
//{
// const auto l = lhs.left() + rhs.left();
// const auto r = lhs.right() + rhs.right();
// const auto t = lhs.top() + rhs.top();
// const auto b = lhs.bottom() + rhs.bottom();
// return rectangle{ til::point{l, t}, til::point{r, b} };
//}
//rectangle& operator+=(rectangle& lhs, const rectangle& rhs)
//{
// lhs = lhs + rhs;
// return lhs;
//}
//rectangle operator/(const rectangle& rect, const size& size)
//{
// const auto l = rect.left() / size.width_signed();
// const auto r = rect.right() / size.width_signed();
// const auto t = rect.top() / size.height_signed();
// const auto b = rect.bottom() / size.height_signed();
// return rectangle{ til::point{l, t}, til::point{r, b} };
//}
//rectangle& operator/=(rectangle& lhs, const size& rhs)
//{
// lhs = lhs / rhs;
// return lhs;
//}
//rectangle operator%(const rectangle& rect, const size& size)
//{
// const auto l = rect.left() % size.width_signed();
// const auto r = rect.right() % size.width_signed();
// const auto t = rect.top() % size.height_signed();
// const auto b = rect.bottom() % size.height_signed();
// return rectangle{ til::point{l, t}, til::point{r, b} };
//}
//rectangle& operator%=(rectangle& lhs, const size& rhs)
//{
// lhs = lhs % rhs;
// return lhs;
//}
//rectangle operator*(const rectangle& rect, const bool& flag)
//{
// const auto l = static_cast<decltype(rect.left())>(!(flag ^ static_cast<bool>(rect.left())));
// const auto r = static_cast<decltype(rect.right())>(!(flag ^ static_cast<bool>(rect.right())));
// const auto t = static_cast<decltype(rect.top())>(!(flag ^ static_cast<bool>(rect.top())));
// const auto b = static_cast<decltype(rect.bottom())>(!(flag ^ static_cast<bool>(rect.bottom())));
// return rectangle{ til::point{l, t}, til::point{r, b} };
//}
//rectangle& operator*=(rectangle& lhs, const bool& rhs)
//{
// lhs = lhs * rhs;
// return lhs;
//}
}

180
src/inc/til/point.h Normal file
View File

@@ -0,0 +1,180 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#ifdef UNIT_TESTING
class PointTests;
#endif
namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
class point
{
public:
constexpr point() noexcept :
point(0, 0)
{
}
constexpr point(ptrdiff_t x, ptrdiff_t y) noexcept :
_x(x),
_y(y)
{
}
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))
{
}
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))
{
}
constexpr bool operator==(const point& other) const noexcept
{
return _x == other._x &&
_y == other._y;
}
constexpr bool operator!=(const point& other) const noexcept
{
return !(*this == other);
}
constexpr bool operator<(const point& other) const noexcept
{
if (_y < other._y)
{
return true;
}
else if (_y > other._y)
{
return false;
}
else
{
return _x < other._x;
}
}
constexpr bool operator>(const point& other) const noexcept
{
if (_y > other._y)
{
return true;
}
else if (_y < other._y)
{
return false;
}
else
{
return _x > other._x;
}
}
constexpr ptrdiff_t x() const noexcept
{
return _x;
}
constexpr ptrdiff_t& x() noexcept
{
return _x;
}
constexpr ptrdiff_t y() const noexcept
{
return _y;
}
constexpr ptrdiff_t& y() noexcept
{
return _y;
}
#ifdef _WINCONTYPES_
operator COORD() const
{
COORD ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(_x).AssignIfValid(&ret.X));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(_y).AssignIfValid(&ret.Y));
return ret;
}
#endif
#ifdef _WINDEF_
operator POINT() const
{
POINT ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(_x).AssignIfValid(&ret.x));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(_y).AssignIfValid(&ret.y));
return ret;
}
#endif
#ifdef DCOMMON_H_INCLUDED
constexpr operator D2D1_POINT_2F() const noexcept
{
return D2D1_POINT_2F{ gsl::narrow_cast<float>(_x), gsl::narrow_cast<float>(_y) };
}
#endif
#ifdef UNIT_TESTING
friend class ::PointTests;
#endif
protected:
ptrdiff_t _x;
ptrdiff_t _y;
};
}
#ifdef __WEX_COMMON_H__
namespace WEX::TestExecution
{
template<>
class VerifyOutputTraits<::til::point>
{
public:
static WEX::Common::NoThrowString ToString(const ::til::point& point)
{
return WEX::Common::NoThrowString().Format(L"(X:%td, Y:%td)", point.x(), point.y());
}
};
template<>
class VerifyCompareTraits<::til::point, ::til::point>
{
public:
static bool AreEqual(const ::til::point& expected, const ::til::point& actual)
{
return expected == actual;
}
static bool AreSame(const ::til::point& expected, const ::til::point& actual)
{
return &expected == &actual;
}
static bool IsLessThan(const ::til::point& expectedLess, const ::til::point& expectedGreater) = delete;
static bool IsGreaterThan(const ::til::point& expectedGreater, const ::til::point& expectedLess) = delete;
static bool IsNull(const ::til::point& object)
{
return object == til::point{};
}
};
};
#endif

513
src/inc/til/rectangle.h Normal file
View File

@@ -0,0 +1,513 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "point.h"
#include "size.h"
#include "some.h"
#ifdef UNIT_TESTING
class RectangleTests;
#endif
namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
class recterator
{
public:
recterator(til::point topLeft, til::size size) :
_topLeft(topLeft),
_size(size),
_current(topLeft)
{
}
recterator(til::point topLeft, til::size size, til::point start) :
_topLeft(topLeft),
_size(size),
_current(start)
{
}
recterator& operator++()
{
++_current.x();
if (_current.x() >= _topLeft.x() + _size.width())
{
_current.x() = _topLeft.x();
++_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;
}
til::point operator*() const
{
return _current;
}
protected:
til::point _current;
const til::point _topLeft;
const til::size _size;
};
class rectangle
{
public:
using const_iterator = recterator;
constexpr rectangle() noexcept :
rectangle(til::point{ 0, 0 }, til::point{ 0, 0 })
{
}
constexpr rectangle(ptrdiff_t left, ptrdiff_t top, ptrdiff_t right, ptrdiff_t bottom) noexcept:
rectangle(til::point{ left, top }, til::point{ right, bottom })
{
}
constexpr rectangle(til::point topLeft, til::point bottomRight) noexcept :
_topLeft(topLeft),
_bottomRight(bottomRight)
{
}
rectangle(til::point topLeft, til::size size) :
_topLeft(topLeft)
{
ptrdiff_t right;
ptrdiff_t bottom;
THROW_HR_IF(E_ABORT, !(base::MakeCheckedNum(topLeft.x()) + size.width()).AssignIfValid(&right));
THROW_HR_IF(E_ABORT, !(base::MakeCheckedNum(topLeft.y()) + size.height()).AssignIfValid(&bottom));
_bottomRight = til::point{ right, bottom };
}
template<typename TOther>
constexpr rectangle(const TOther& other, std::enable_if_t<std::is_integral_v<decltype(std::declval<TOther>().Top)> && std::is_integral_v<decltype(std::declval<TOther>().Left)> && std::is_integral_v<decltype(std::declval<TOther>().Bottom)> && std::is_integral_v<decltype(std::declval<TOther>().Right)>, int> /*sentinel*/ = 0) :
rectangle(til::point{static_cast<ptrdiff_t>(other.Left), static_cast<ptrdiff_t>(other.Top)}, til::point{static_cast<ptrdiff_t>(other.Right), static_cast<ptrdiff_t>(other.Bottom)})
{
}
template<typename TOther>
constexpr rectangle(const TOther& other, std::enable_if_t < std::is_integral_v<decltype(std::declval<TOther>().top)> && std::is_integral_v<decltype(std::declval<TOther>().left)> && std::is_integral_v<decltype(std::declval<TOther>().bottom)> && std::is_integral_v<decltype(std::declval<TOther>().right)>, int> /*sentinel*/ = 0) :
rectangle(til::point{ static_cast<ptrdiff_t>(other.left), static_cast<ptrdiff_t>(other.top) }, til::point{ static_cast<ptrdiff_t>(other.right), static_cast<ptrdiff_t>(other.bottom)})
{
}
const_iterator begin() const
{
return recterator(_topLeft, size());
}
const_iterator end() const
{
return recterator(_topLeft, size(), { _topLeft.x(), _topLeft.y() + height() });
}
constexpr ptrdiff_t top() const noexcept
{
return _topLeft.y();
}
constexpr ptrdiff_t bottom() const noexcept
{
return _bottomRight.y();
}
ptrdiff_t bottom_inclusive() const
{
ptrdiff_t ret;
THROW_HR_IF(E_ABORT, !(::base::MakeCheckedNum(bottom()) - 1).AssignIfValid(&ret));
return ret;
}
constexpr ptrdiff_t left() const noexcept
{
return _topLeft.x();
}
constexpr ptrdiff_t right() const noexcept
{
return _bottomRight.x();
}
ptrdiff_t right_inclusive() const
{
ptrdiff_t ret;
THROW_HR_IF(E_ABORT, !(::base::MakeCheckedNum(right()) - 1).AssignIfValid(&ret));
return ret;
}
ptrdiff_t width() const
{
ptrdiff_t ret;
THROW_HR_IF(E_ABORT, !(::base::MakeCheckedNum(right()) - left()).AssignIfValid(&ret));
return ret;
}
template<typename T>
T width() const
{
T ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(width()).AssignIfValid(&ret));
return ret;
}
ptrdiff_t height() const
{
ptrdiff_t ret;
THROW_HR_IF(E_ABORT, !(::base::MakeCheckedNum(bottom()) - top()).AssignIfValid(&ret));
return ret;
}
template<typename T>
T height() const
{
T ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(height()).AssignIfValid(&ret));
return ret;
}
template <typename T, typename... Args>
auto capture(T&& func, Args&&... args) -> decltype(func(args..., static_cast<RECT*>(nullptr))) {
RECT r{};
auto rv = func(std::forward<Args>(args)..., &r);
*this = static_cast<rectangle>(r);
return rv;
}
constexpr point origin() const noexcept
{
return _topLeft;
}
constexpr bool empty() const noexcept
{
return _topLeft.x() >= _bottomRight.x() ||
_topLeft.y() >= _bottomRight.y();
}
size size() const
{
return til::size{ width(), height() };
}
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 &&
_bottomRight == other._bottomRight;
}
constexpr bool operator!=(const rectangle& other) const noexcept
{
return !(*this == other);
}
// OR = union
constexpr rectangle operator|(const rectangle& other) const noexcept
{
const auto thisEmpty = empty();
const auto otherEmpty = other.empty();
// If both are empty, return empty rect.
if (thisEmpty && otherEmpty)
{
return rectangle{};
}
// If this is empty but not the other one, then give the other.
if (thisEmpty)
{
return other;
}
// If the other is empty but not this, give this.
if (otherEmpty)
{
return *this;
}
// If we get here, they're both not empty. Do math.
const auto l = std::min(left(), other.left());
const auto t = std::min(top(), other.top());
const auto r = std::max(right(), other.right());
const auto b = std::max(bottom(), other.bottom());
return rectangle{ l, t, r, b };
}
// AND = intersect
constexpr rectangle operator&(const rectangle& other) const noexcept
{
const auto l = std::max(left(), other.left());
const auto r = std::min(right(), other.right());
// If the width dimension would be empty, give back empty rectangle.
if (l >= r)
{
return rectangle{};
}
const auto t = std::max(top(), other.top());
const auto b = std::max(bottom(), other.bottom());
// If the height dimension would be empty, give back empty rectangle.
if (t >= b)
{
return rectangle{};
}
return rectangle{ l, t, r, b };
}
// - = subtract
some<rectangle, 4> operator-(const rectangle& other) const
{
some<rectangle, 4> result;
// We could have up to four rectangles describing the area resulting when you take removeMe out of main.
// Find the intersection of the two so we know which bits of removeMe are actually applicable
// to the original rectangle for subtraction purposes.
const auto intersect = *this & other;
// If there's no intersect, there's nothing to remove.
if (intersect.empty())
{
// Just put the original rectangle into the results and return early.
result.push_back(*this);
}
// If the original rectangle matches the intersect, there is nothing to return.
else if (*this != intersect)
{
// Generate our potential four viewports that represent the region of the original that falls outside of the remove area.
// We will bias toward generating wide rectangles over tall rectangles (if possible) so that optimizations that apply
// to manipulating an entire row at once can be realized by other parts of the console code. (i.e. Run Length Encoding)
// In the following examples, the found remaining regions are represented by:
// T = Top B = Bottom L = Left R = Right
//
// 4 Sides but Identical:
// |-----------this-----------| |-----------this-----------|
// | | | |
// | | | |
// | | | |
// | | ======> | intersect | ======> early return of nothing
// | | | |
// | | | |
// | | | |
// |-----------other----------| |--------------------------|
//
// 4 Sides:
// |-----------this-----------| |-----------this-----------| |--------------------------|
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
// | |---------| | | |---------| | |LLLLLLLL|---------|RRRRRRR|
// | |other | | ======> | |intersect| | ======> |LLLLLLLL| |RRRRRRR|
// | |---------| | | |---------| | |LLLLLLLL|---------|RRRRRRR|
// | | | | |BBBBBBBBBBBBBBBBBBBBBBBBBB|
// | | | | |BBBBBBBBBBBBBBBBBBBBBBBBBB|
// |--------------------------| |--------------------------| |--------------------------|
//
// 3 Sides:
// |-----------this-----------| |-----------this-----------| |--------------------------|
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
// | |--------------------| | |-----------------| |LLLLLLLL|-----------------|
// | |other | ======> | |intersect | ======> |LLLLLLLL| |
// | |--------------------| | |-----------------| |LLLLLLLL|-----------------|
// | | | | |BBBBBBBBBBBBBBBBBBBBBBBBBB|
// | | | | |BBBBBBBBBBBBBBBBBBBBBBBBBB|
// |--------------------------| |--------------------------| |--------------------------|
//
// 2 Sides:
// |-----------this-----------| |-----------this-----------| |--------------------------|
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
// | |--------------------| | |-----------------| |LLLLLLLL|-----------------|
// | |other | ======> | |intersect | ======> |LLLLLLLL| |
// | | | | | | |LLLLLLLL| |
// | | | | | | |LLLLLLLL| |
// | | | | | | |LLLLLLLL| |
// |--------| | |--------------------------| |--------------------------|
// | |
// |--------------------|
//
// 1 Side:
// |-----------this-----------| |-----------this-----------| |--------------------------|
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
// |-----------------------------| |--------------------------| |--------------------------|
// | other | ======> | intersect | ======> | |
// | | | | | |
// | | | | | |
// | | | | | |
// | | |--------------------------| |--------------------------|
// | |
// |-----------------------------|
//
// 0 Sides:
// |-----------this-----------| |-----------this-----------|
// | | | |
// | | | |
// | | | |
// | | ======> | | ======> early return of this
// | | | |
// | | | |
// | | | |
// |--------------------------| |--------------------------|
//
//
// |---------------|
// | other |
// |---------------|
// We generate these rectangles by the original and intersect points, but some of them might be empty when the intersect
// lines up with the edge of the original. That's OK. That just means that the subtraction didn't leave anything behind.
// We will filter those out below when adding them to the result.
const auto t = rectangle({ left(), top(), right(), intersect.top()});
const auto b = rectangle({ left(), intersect.bottom(), right(), bottom() });
const auto l = rectangle({ left(), intersect.top(), intersect.left(), intersect.bottom() });
const auto r = rectangle({ intersect.right(), intersect.top(), right(), intersect.bottom() });
if (!t.empty())
{
result.push_back(t);
}
if (!b.empty())
{
result.push_back(b);
}
if (!l.empty())
{
result.push_back(l);
}
if (!r.empty())
{
result.push_back(r);
}
}
return result;
}
#ifdef _WINCONTYPES_
operator SMALL_RECT() const
{
SMALL_RECT ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(left()).AssignIfValid(&ret.Left));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(top()).AssignIfValid(&ret.Top));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(right_inclusive()).AssignIfValid(&ret.Right));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(bottom_inclusive()).AssignIfValid(&ret.Bottom));
return ret;
}
#endif
#ifdef _WINDEF_
operator RECT() const
{
RECT ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(left()).AssignIfValid(&ret.left));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(top()).AssignIfValid(&ret.top));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(right()).AssignIfValid(&ret.right));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(bottom()).AssignIfValid(&ret.bottom));
return ret;
}
#endif
#ifdef DCOMMON_H_INCLUDED
constexpr operator D2D1_RECT_F() const noexcept
{
return D2D1_RECT_F{ gsl::narrow_cast<FLOAT>(left()), gsl::narrow_cast<FLOAT>(top()), gsl::narrow_cast<FLOAT>(right()), gsl::narrow_cast<FLOAT>(bottom()) };
}
#endif
#ifdef UNIT_TESTING
friend class ::RectangleTests;
#endif
protected:
til::point _topLeft;
til::point _bottomRight;
};
}
#ifdef __WEX_COMMON_H__
namespace WEX::TestExecution
{
template<>
class VerifyOutputTraits<::til::rectangle>
{
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());
}
};
template<>
class VerifyCompareTraits<::til::rectangle, ::til::rectangle>
{
public:
static bool AreEqual(const ::til::rectangle& expected, const ::til::rectangle& actual)
{
return expected == actual;
}
static bool AreSame(const ::til::rectangle& expected, const ::til::rectangle& actual)
{
return &expected == &actual;
}
static bool IsLessThan(const ::til::rectangle& expectedLess, const ::til::rectangle& expectedGreater) = delete;
static bool IsGreaterThan(const ::til::rectangle& expectedGreater, const ::til::rectangle& expectedLess) = delete;
static bool IsNull(const ::til::rectangle& object)
{
return object == til::rectangle{};
}
};
};
#endif

183
src/inc/til/size.h Normal file
View File

@@ -0,0 +1,183 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#ifdef UNIT_TESTING
class SizeTests;
#endif
namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
class size
{
public:
constexpr size() noexcept :
size(0, 0)
{
}
constexpr size(int width, int height) noexcept :
size(static_cast<ptrdiff_t>(width), static_cast<ptrdiff_t>(height))
{
}
size(size_t width, size_t height)
{
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(width).AssignIfValid(&_width));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(height).AssignIfValid(&_height));
}
constexpr size(ptrdiff_t width, ptrdiff_t height) noexcept :
_width(width),
_height(height)
{
}
template<typename TOther>
constexpr size(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) :
size(static_cast<ptrdiff_t>(other.X), static_cast<ptrdiff_t>(other.Y))
{
}
template<typename TOther>
constexpr size(const TOther& other, std::enable_if_t<std::is_integral_v<decltype(std::declval<TOther>().cx)> && std::is_integral_v<decltype(std::declval<TOther>().cy)>, int> /*sentinel*/ = 0) :
size(static_cast<ptrdiff_t>(other.cx), static_cast<ptrdiff_t>(other.cy))
{
}
constexpr bool operator==(const size& other) const noexcept
{
return _width == other._width &&
_height == other._height;
}
constexpr bool operator!=(const size& other) const noexcept
{
return !(*this == other);
}
constexpr ptrdiff_t width() const noexcept
{
return _width;
}
template<typename T>
T width() const
{
T ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(width()).AssignIfValid(&ret));
return ret;
}
constexpr ptrdiff_t height() const noexcept
{
return _height;
}
template<typename T>
T height() const
{
T ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(height()).AssignIfValid(&ret));
return ret;
}
size operator*(const float& other) const
{
ptrdiff_t w;
THROW_HR_IF(E_ABORT, !(base::MakeCheckedNum(width()) * other).AssignIfValid(&w));
ptrdiff_t h;
THROW_HR_IF(E_ABORT, !(base::MakeCheckedNum(height()) * other).AssignIfValid(&h));
return size{ w, h };
}
ptrdiff_t area() const
{
ptrdiff_t result;
THROW_HR_IF(E_ABORT, !base::CheckMul(_width, _height).AssignIfValid(&result));
return result;
}
#ifdef _WINCONTYPES_
operator COORD() const
{
COORD ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(_width).AssignIfValid(&ret.X));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(_height).AssignIfValid(&ret.Y));
return ret;
}
#endif
#ifdef _WINDEF_
operator SIZE() const
{
SIZE ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(_width).AssignIfValid(&ret.cx));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(_height).AssignIfValid(&ret.cy));
return ret;
}
#endif
#ifdef DCOMMON_H_INCLUDED
constexpr operator D2D1_SIZE_F() const noexcept
{
return D2D1_SIZE_F{ gsl::narrow_cast<float>(_width), gsl::narrow_cast<float>(_height) };
}
#endif
protected:
ptrdiff_t _width;
ptrdiff_t _height;
#ifdef UNIT_TESTING
friend class ::SizeTests;
#endif
};
};
#ifdef __WEX_COMMON_H__
namespace WEX::TestExecution
{
template<>
class VerifyOutputTraits<::til::size>
{
public:
static WEX::Common::NoThrowString ToString(const ::til::size& size)
{
return WEX::Common::NoThrowString().Format(L"[W:%td, H:%td]", size.width(), size.height());
}
};
template<>
class VerifyCompareTraits<::til::size, ::til::size>
{
public:
static bool AreEqual(const ::til::size& expected, const ::til::size& actual)
{
return expected == actual;
}
static bool AreSame(const ::til::size& expected, const ::til::size& actual)
{
return &expected == &actual;
}
static bool IsLessThan(const ::til::size& expectedLess, const ::til::size& expectedGreater) = delete;
static bool IsGreaterThan(const ::til::size& expectedGreater, const ::til::size& expectedLess) = delete;
static bool IsNull(const ::til::size& object)
{
return object == til::size{};
}
};
};
#endif

View File

@@ -65,17 +65,18 @@ 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{},
@@ -84,7 +85,7 @@ DxEngine::DxEngine() :
_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;
@@ -427,11 +428,11 @@ 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;
RETURN_IF_WIN32_BOOL_FALSE(clientRect.capture(&::GetClientRect, _hwndTarget));
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 +458,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 +629,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.cy != 0 ? _glyphCell.cy : _displaySizePixels.height<float>(),
ppTextLayout);
}
@@ -748,39 +749,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 +800,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 +827,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 +836,40 @@ 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
{
return _sizeTarget * _scale;
}
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 +878,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 +893,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 +923,19 @@ 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);
_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 +944,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:
@@ -974,21 +985,21 @@ void DxEngine::_InvalidOr(RECT rc) 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"));
//#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)
{
@@ -999,8 +1010,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 +1023,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.
@@ -1054,15 +1064,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 +1097,11 @@ void DxEngine::_InvalidOr(RECT rc) noexcept
}
}
_invalidRect = { 0 };
_isInvalidUsed = false;
_dirtyRects.clear();
_invalidMap.reset_all();
/*_invalidRect = { 0 };
_isInvalidUsed = false;*/
_invalidScroll = { 0 };
@@ -1161,7 +1178,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 +1202,20 @@ 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->PushAxisAlignedClip(rect, D2D1_ANTIALIAS_MODE_ALIASED);
_d2dRenderTarget->Clear(nothing);
break;
_d2dRenderTarget->PopAxisAlignedClip();
}
return S_OK;
@@ -1688,17 +1699,7 @@ float DxEngine::GetScaling() const noexcept
// - Rectangle describing dirty area in characters.
[[nodiscard]] SMALL_RECT DxEngine::GetDirtyRectInChars() noexcept
{
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 til::rectangle{};
}
// Routine Description:

View File

@@ -121,7 +121,7 @@ namespace Microsoft::Console::Render
SwapChainMode _chainMode;
HWND _hwndTarget;
SIZE _sizeTarget;
til::size _sizeTarget;
int _dpi;
float _scale;
@@ -130,7 +130,7 @@ namespace Microsoft::Console::Render
bool _isEnabled;
bool _isPainting;
SIZE _displaySizePixels;
til::size _displaySizePixels;
SIZE _glyphCell;
D2D1_COLOR_F _defaultForegroundColor;
@@ -140,10 +140,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 +156,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 +250,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

@@ -25,7 +25,17 @@ Abstract:
// Windows Header Files:
#include <windows.h>
#define BLOCK_TIL
// This includes support libraries from the CRT, STL, WIL, and GSL
#include "LibraryIncludes.h"
#include "WexTestClass.h"
// Include DirectX common structures so we can test them.
// (before TIL so its support lights up)
#include <dcommon.h>
// Include TIL after Wex to get test comparators.
#include "til.h"
// clang-format on

View File

@@ -0,0 +1,106 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.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());
}
};

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

@@ -0,0 +1,106 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "til/point.h"
using namespace WEX::Common;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
class PointTests
{
TEST_CLASS(PointTests);
TEST_METHOD(CastToCoord)
{
Log::Comment(L"0.) Typical situation.");
{
const til::point pt{ 5, 10 };
COORD val = pt;
VERIFY_ARE_EQUAL(5, val.X);
VERIFY_ARE_EQUAL(10, val.Y);
}
Log::Comment(L"1.) Overflow on width.");
{
constexpr ptrdiff_t x = std::numeric_limits<ptrdiff_t>().max();
const ptrdiff_t y = 10;
const til::point pt{ x, y};
auto fn = [&]()
{
COORD val = pt;
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
}
Log::Comment(L"2.) Overflow on height.");
{
constexpr ptrdiff_t y = std::numeric_limits<ptrdiff_t>().max();
const ptrdiff_t x = 10;
const til::point pt{ x, y};
auto fn = [&]()
{
COORD val = pt;
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
}
}
TEST_METHOD(CastToPoint)
{
Log::Comment(L"0.) Typical situation.");
{
const til::point pt{ 5, 10 };
POINT val = pt;
VERIFY_ARE_EQUAL(5, val.x);
VERIFY_ARE_EQUAL(10, val.y);
}
Log::Comment(L"1.) Overflow on width.");
{
constexpr ptrdiff_t x = std::numeric_limits<ptrdiff_t>().max();
const ptrdiff_t y = 10;
const til::point pt{ x, y};
auto fn = [&]()
{
POINT val = pt;
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
}
Log::Comment(L"2.) Overflow on height.");
{
constexpr ptrdiff_t y = std::numeric_limits<ptrdiff_t>().max();
const ptrdiff_t x = 10;
const til::point pt{ x, y};
auto fn = [&]()
{
POINT val = pt;
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
}
}
TEST_METHOD(CastToD2D1Point2F)
{
Log::Comment(L"0.) Typical situation.");
{
const til::point pt{ 5, 10 };
D2D1_POINT_2F val = pt;
VERIFY_ARE_EQUAL(5, val.x);
VERIFY_ARE_EQUAL(10, val.y);
}
// All ptrdiff_ts fit into a float, so there's no exception tests.
}
};

View File

@@ -0,0 +1,17 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "til/rectangle.h"
using namespace WEX::Common;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
class RectangleTests
{
TEST_CLASS(RectangleTests);
};

View File

@@ -0,0 +1,297 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "til/size.h"
using namespace WEX::Common;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
class SizeTests
{
TEST_CLASS(SizeTests);
TEST_METHOD(DefaultConstruct)
{
const til::size sz;
VERIFY_ARE_EQUAL(0, sz._width);
VERIFY_ARE_EQUAL(0, sz._height);
}
TEST_METHOD(RawConstruct)
{
const til::size sz{ 5, 10 };
VERIFY_ARE_EQUAL(5, sz._width);
VERIFY_ARE_EQUAL(10, sz._height);
}
TEST_METHOD(UnsignedConstruct)
{
Log::Comment(L"0.) Normal unsigned construct.");
{
const size_t width = 5;
const size_t height = 10;
const til::size sz{ width, height };
VERIFY_ARE_EQUAL(5, sz._width);
VERIFY_ARE_EQUAL(10, sz._height);
}
Log::Comment(L"1.) Unsigned construct overflow on width.");
{
constexpr size_t width = std::numeric_limits<size_t>().max();
const size_t height = 10;
auto fn = [&]()
{
til::size sz{ width, height };
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
}
Log::Comment(L"2.) Unsigned construct overflow on height.");
{
constexpr size_t height = std::numeric_limits<size_t>().max();
const size_t width = 10;
auto fn = [&]()
{
til::size sz{ width, height };
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
}
}
TEST_METHOD(SignedConstruct)
{
const ptrdiff_t width = -5;
const ptrdiff_t height = -10;
const til::size sz{ width, height };
VERIFY_ARE_EQUAL(width, sz._width);
VERIFY_ARE_EQUAL(height, sz._height);
}
TEST_METHOD(CoordConstruct)
{
COORD coord{ -5, 10 };
const til::size sz{ coord };
VERIFY_ARE_EQUAL(coord.X, sz._width);
VERIFY_ARE_EQUAL(coord.Y, sz._height);
}
TEST_METHOD(SizeConstruct)
{
SIZE size{ 5, -10 };
const til::size sz{ size };
VERIFY_ARE_EQUAL(size.cx, sz._width);
VERIFY_ARE_EQUAL(size.cy, sz._height);
}
TEST_METHOD(Equality)
{
Log::Comment(L"0.) Equal.");
{
const til::size s1{ 5, 10 };
const til::size s2{ 5, 10 };
VERIFY_IS_TRUE(s1 == s2);
}
Log::Comment(L"1.) Left Width changed.");
{
const til::size s1{ 4, 10 };
const til::size s2{ 5, 10 };
VERIFY_IS_FALSE(s1 == s2);
}
Log::Comment(L"2.) Right Width changed.");
{
const til::size s1{ 5, 10 };
const til::size s2{ 6, 10 };
VERIFY_IS_FALSE(s1 == s2);
}
Log::Comment(L"3.) Left Height changed.");
{
const til::size s1{ 5, 9 };
const til::size s2{ 5, 10 };
VERIFY_IS_FALSE(s1 == s2);
}
Log::Comment(L"4.) Right Height changed.");
{
const til::size s1{ 5, 10 };
const til::size s2{ 5, 11 };
VERIFY_IS_FALSE(s1 == s2);
}
}
TEST_METHOD(Inequality)
{
Log::Comment(L"0.) Equal.");
{
const til::size s1{ 5, 10 };
const til::size s2{ 5, 10 };
VERIFY_IS_FALSE(s1 != s2);
}
Log::Comment(L"1.) Left Width changed.");
{
const til::size s1{ 4, 10 };
const til::size s2{ 5, 10 };
VERIFY_IS_TRUE(s1 != s2);
}
Log::Comment(L"2.) Right Width changed.");
{
const til::size s1{ 5, 10 };
const til::size s2{ 6, 10 };
VERIFY_IS_TRUE(s1 != s2);
}
Log::Comment(L"3.) Left Height changed.");
{
const til::size s1{ 5, 9 };
const til::size s2{ 5, 10 };
VERIFY_IS_TRUE(s1 != s2);
}
Log::Comment(L"4.) Right Height changed.");
{
const til::size s1{ 5, 10 };
const til::size s2{ 5, 11 };
VERIFY_IS_TRUE(s1 != s2);
}
}
TEST_METHOD(Width)
{
const til::size sz{ 5,10 };
VERIFY_ARE_EQUAL(sz._width, sz.width());
}
TEST_METHOD(Height)
{
const til::size sz{ 5,10 };
VERIFY_ARE_EQUAL(sz._height, sz.height());
}
TEST_METHOD(Area)
{
Log::Comment(L"0.) Area of two things that should be in bounds.");
{
const til::size sz{ 5,10 };
VERIFY_ARE_EQUAL(sz._width * sz._height, sz.area());
}
Log::Comment(L"1.) Area is out of bounds on multiplication.");
{
constexpr ptrdiff_t bigSize = std::numeric_limits<ptrdiff_t>().max();
const til::size sz{ bigSize, bigSize};
auto fn = [&]()
{
sz.area();
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
}
}
TEST_METHOD(CastToCoord)
{
Log::Comment(L"0.) Typical situation.");
{
const til::size sz{ 5, 10 };
COORD val = sz;
VERIFY_ARE_EQUAL(5, val.X);
VERIFY_ARE_EQUAL(10, val.Y);
}
Log::Comment(L"1.) Overflow on width.");
{
constexpr ptrdiff_t width = std::numeric_limits<ptrdiff_t>().max();
const ptrdiff_t height = 10;
const til::size sz{ width, height };
auto fn = [&]()
{
COORD val = sz;
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
}
Log::Comment(L"2.) Overflow on height.");
{
constexpr ptrdiff_t height = std::numeric_limits<ptrdiff_t>().max();
const ptrdiff_t width = 10;
const til::size sz{ width, height };
auto fn = [&]()
{
COORD val = sz;
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
}
}
TEST_METHOD(CastToSize)
{
Log::Comment(L"0.) Typical situation.");
{
const til::size sz{ 5, 10 };
SIZE val = sz;
VERIFY_ARE_EQUAL(5, val.cx);
VERIFY_ARE_EQUAL(10, val.cy);
}
Log::Comment(L"1.) Overflow on width.");
{
constexpr ptrdiff_t width = std::numeric_limits<ptrdiff_t>().max();
const ptrdiff_t height = 10;
const til::size sz{ width, height };
auto fn = [&]()
{
SIZE val = sz;
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
}
Log::Comment(L"2.) Overflow on height.");
{
constexpr ptrdiff_t height = std::numeric_limits<ptrdiff_t>().max();
const ptrdiff_t width = 10;
const til::size sz{ width, height };
auto fn = [&]()
{
SIZE val = sz;
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
}
}
TEST_METHOD(CastToD2D1SizeF)
{
Log::Comment(L"0.) Typical situation.");
{
const til::size sz{ 5, 10 };
D2D1_SIZE_F val = sz;
VERIFY_ARE_EQUAL(5, val.width);
VERIFY_ARE_EQUAL(10, val.height);
}
// All ptrdiff_ts fit into a float, so there's no exception tests.
}
};

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;

View File

@@ -10,6 +10,11 @@
</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" />
<ClCompile Include="SomeTests.cpp" />
<ClCompile Include="..\precomp.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<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="OperatorTests.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\precomp.h" />
</ItemGroup>
</Project>

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;