Make the writers fun templates

This commit is contained in:
Dustin Howett 2021-07-29 16:54:26 -05:00
parent 271132edb4
commit d2f0f50651
7 changed files with 174 additions and 147 deletions

View file

@ -8,6 +8,7 @@
#include "../types/inc/utils.hpp"
#include "../types/inc/convert.hpp"
#include "../../types/inc/GlyphWidth.hpp"
#include "../types/inc/Utf16Parser.hpp"
#pragma hdrstop
@ -624,13 +625,11 @@ void TextBuffer::SetCurrentLineRendition(const LineRendition lineRendition)
// And if it's no longer single width, the right half of the row should be erased.
if (lineRendition != LineRendition::SingleWidth)
{
const auto fillChar = L' ';
auto fillAttrs = GetCurrentAttributes();
fillAttrs.SetStandardErase();
const size_t fillOffset = GetLineWidth(rowIndex);
const size_t fillLength = GetSize().Width() - fillOffset;
const auto fillData = OutputCellIterator{ fillChar, fillAttrs, fillLength };
row.WriteCells(fillData, fillOffset, false);
FillWithCharacterAndAttributeLinear(til::point{fillOffset, ::base::saturated_cast<size_t>(cursorPosition.Y)}, fillLength, UNICODE_SPACE, fillAttrs);
// We also need to make sure the cursor is clamped within the new width.
GetCursor().SetPosition(ClampPositionWithinLine(cursorPosition));
}
@ -2319,40 +2318,36 @@ PointTree TextBuffer::GetPatterns(const size_t firstRow, const size_t lastRow) c
return result;
}
void TextBuffer::FillWithAttribute(const Viewport& region, const TextAttribute& attribute)
void TextBuffer::FillWithAttributeRectangular(const til::rectangle& region, const TextAttribute& attribute)
{
for (auto y{ region.Top() }; y < region.BottomExclusive(); ++y)
{
auto& row = GetRowByOffset(y);
row.GetAttrRow().Replace(region.Left(), region.RightExclusive(), attribute);
}
// Rectangular region optimization: notify all at once
_NotifyPaint(region);
_WriteRectangular(
region,
[&](WriteResult&, auto&& at, auto&& size) {
auto& row = GetRowByOffset(at.y());
row.GetAttrRow().Replace(::base::saturated_cast<uint16_t>(at.x()), ::base::ClampAdd<uint16_t>(::base::saturated_cast<uint16_t>(at.x()), size), attribute);
});
}
size_t TextBuffer::FillWithAttributeLinear(til::point at, size_t count, const TextAttribute& attribute)
{
const auto initial{ count };
while (count && _size.IsInBounds(at))
{
// narrowing count to short here is fine; it will always be less than width
const auto w{ std::min<uint16_t>(_size.Width() - at.x<uint16_t>(), gsl::narrow_cast<uint16_t>(count)) };
auto out = _WriteLinear(
at,
[&](WriteResult& result, auto&& at, auto&& size) -> bool {
const auto w{ ::base::ClampMin(::base::saturated_cast<uint16_t>(size), count) };
auto& row = GetRowByOffset(at.y());
row.GetAttrRow().Replace(at.x<uint16_t>(), at.x<uint16_t>() + w + 1 /* exclusive */, attribute);
auto& row = GetRowByOffset(at.y());
row.GetAttrRow().Replace(::base::saturated_cast<uint16_t>(at.x()), ::base::ClampAdd<uint16_t>(::base::saturated_cast<uint16_t>(at.x()), size), attribute);
const Viewport paint = Viewport::FromDimensions(at, { gsl::narrow<SHORT>(w), 1 });
_NotifyPaint(paint);
result.colsConsumed += w.RawValue();
at = { 0, at.y() + 1 };
count -= w;
}
return initial - count;
const Viewport paint = Viewport::FromDimensions(at, { w.Cast<SHORT>(), 1 });
_NotifyPaint(paint);
return (count -= w.RawValue()) > 0; // keep going if data remains
});
return out.colsConsumed;
}
#include "../types/inc/Utf16Parser.hpp"
// TODO(DH): make this a member so that IsGlyphFullWidth can be a member ref
static std::tuple<til::small_rle<uint8_t, uint16_t, 3>, uint16_t> _MeasureStringAndCountColumns(std::wstring_view string) //const noexcept
{
@ -2374,135 +2369,148 @@ static std::tuple<til::small_rle<uint8_t, uint16_t, 3>, uint16_t> _MeasureString
return { std::move(measurements), colCount };
}
size_t TextBuffer::WriteMeasuredStringLinear(til::point at, std::wstring_view string, til::small_rle<uint8_t, uint16_t, 3> measurements)
struct no_attributes
{
};
template<typename T>
size_t TextBuffer::_WriteMeasuredStringLinearWithAttributes(const til::point& at, std::wstring_view string, til::small_rle<uint8_t, uint16_t, 3> measurements, [[maybe_unused]] const T& attributes)
{
THROW_HR_IF(E_BOUNDS, !_size.IsInBounds(at));
uint16_t colCount = std::accumulate(
measurements.runs().cbegin(),
measurements.runs().cend(),
uint16_t{},
[](auto&& v, auto&& r) -> uint16_t { return v + (r.value * r.length); });
size_t totalConsumedCols{};
while (!string.empty() && _size.IsInBounds(at))
{
auto& row = GetRowByOffset(at.y());
auto [consumedWchar, consumedDestCols, consumedSourceCols] = row.WriteStringAtMeasured(at.x<uint16_t>(), colCount, string, measurements);
row.GetAttrRow().Replace(at.x<uint16_t>(), at.x<uint16_t>() + consumedDestCols, _currentAttributes);
const Viewport paint = Viewport::FromDimensions(at, { gsl::narrow<SHORT>(consumedDestCols), 1 });
_NotifyPaint(paint);
at = til::point{ 0, at.y() + 1 };
colCount -= consumedSourceCols;
totalConsumedCols += consumedDestCols;
const auto out = _WriteLinear(
at,
[&](WriteResult& result, auto&& at, auto&& /* size */) {
auto& row = GetRowByOffset(at.y());
const auto [consumedWchar, consumedDestCols, consumedSourceCols] = row.WriteStringAtMeasured(at.x<uint16_t>(), colCount, string, measurements);
string = string.substr(consumedWchar);
measurements = measurements.slice(gsl::narrow_cast<uint16_t>(consumedWchar), measurements.size());
}
return totalConsumedCols;
if constexpr (std::is_same<T, TextAttribute>::value)
{
row.GetAttrRow().Replace(::base::saturated_cast<uint16_t>(at.x()), ::base::ClampAdd<uint16_t>(::base::saturated_cast<uint16_t>(at.x()), consumedDestCols), attributes);
}
const Viewport paint = Viewport::FromDimensions(at, { ::base::saturated_cast<SHORT>(consumedDestCols), 1 });
_NotifyPaint(paint);
colCount -= consumedSourceCols;
result.charsConsumed += consumedWchar;
result.colsConsumed += consumedDestCols;
string = string.substr(consumedWchar);
measurements = measurements.slice(gsl::narrow_cast<uint16_t>(consumedWchar), measurements.size());
return !string.empty();
});
return out.colsConsumed;
}
size_t TextBuffer::WriteStringLinearWithAttributes(til::point at, std::wstring_view string, const TextAttribute& attr)
size_t TextBuffer::WriteMeasuredStringLinear(const til::point& at, const std::wstring_view& string, til::small_rle<uint8_t, uint16_t, 3> measurements)
{
return _WriteMeasuredStringLinearWithAttributes(at, string, std::move(measurements), _currentAttributes);
}
size_t TextBuffer::WriteStringLinearWithAttributes(const til::point &at, const std::wstring_view& string, const TextAttribute& attr)
{
THROW_HR_IF(E_BOUNDS, !_size.IsInBounds(at));
auto [measurements, colCount] = _MeasureStringAndCountColumns(string);
size_t totalConsumedCols{};
while (!string.empty() && _size.IsInBounds(at))
{
auto& row = GetRowByOffset(at.y());
auto [consumedWchar, consumedDestCols, consumedSourceCols] = row.WriteStringAtMeasured(at.x<uint16_t>(), colCount, string, measurements);
const Viewport paint = Viewport::FromDimensions(at, { gsl::narrow<SHORT>(consumedDestCols), 1 });
row.GetAttrRow().Replace(at.x<uint16_t>(), at.x<uint16_t>() + consumedDestCols, attr);
_NotifyPaint(paint);
at = til::point{ 0, at.y() + 1 };
colCount -= consumedSourceCols;
totalConsumedCols += consumedDestCols;
string = string.substr(consumedWchar);
measurements = measurements.slice(gsl::narrow_cast<uint16_t>(consumedWchar), measurements.size());
}
return totalConsumedCols;
return _WriteMeasuredStringLinearWithAttributes(at, string, std::move(measurements), attr);
}
size_t TextBuffer::WriteStringLinearKeepAttributes(til::point at, std::wstring_view string)
size_t TextBuffer::WriteStringLinearKeepAttributes(const til::point &at, const std::wstring_view& string)
{
THROW_HR_IF(E_BOUNDS, !_size.IsInBounds(at));
auto [measurements, colCount] = _MeasureStringAndCountColumns(string);
size_t totalConsumedCols{};
while (!string.empty() && _size.IsInBounds(at))
{
auto& row = GetRowByOffset(at.y());
auto [consumedWchar, consumedDestCols, consumedSourceCols] = row.WriteStringAtMeasured(at.x<uint16_t>(), colCount, string, measurements);
const Viewport paint = Viewport::FromDimensions(at, { gsl::narrow<SHORT>(consumedDestCols), 1 });
_NotifyPaint(paint);
at = til::point{ 0, at.y() + 1 };
colCount -= consumedSourceCols;
totalConsumedCols += consumedDestCols;
string = string.substr(consumedWchar);
measurements = measurements.slice(gsl::narrow_cast<uint16_t>(consumedWchar), measurements.size());
}
return totalConsumedCols;
return _WriteMeasuredStringLinearWithAttributes(at, string, std::move(measurements), no_attributes{});
}
size_t TextBuffer::FillWithCharacter(const til::rectangle region, std::wstring_view character)
{
UNREFERENCED_PARAMETER(region);
UNREFERENCED_PARAMETER(character);
return region.size().area<size_t>();
}
size_t TextBuffer::FillWithCharacterLinear(til::point at, size_t count, const wchar_t character)
{
THROW_HR_IF(E_BOUNDS, !_size.IsInBounds(at));
const auto initial{ count };
const uint8_t width{ IsGlyphFullWidth(character) ? uint8_t{ 2u } : uint8_t{ 1u } };
while (count && _size.IsInBounds(at))
{
// narrowing count to short here is fine; it will always be less than width
const auto columns{ std::min<uint16_t>(_size.Width() - at.x<uint16_t>(), gsl::narrow_cast<uint16_t>(count * width)) };
auto& row = GetRowByOffset(at.y());
count -= row.Fill(at.x(), count, character, width);
const Viewport paint = Viewport::FromDimensions(at, { gsl::narrow<SHORT>(columns), 1 });
_NotifyPaint(paint);
at = { 0, at.y() + 1 };
}
return initial - count;
}
void TextBuffer::FillWithCharacterAndAttribute(const til::rectangle& region, const wchar_t character, const TextAttribute& attr)
template<typename T>
TextBuffer::WriteResult TextBuffer::_FillWithCharacterAndAttributeRectangular(const til::rectangle& region, const wchar_t character, [[maybe_unused]] const T& attr)
{
const uint8_t width{ IsGlyphFullWidth(character) ? uint8_t{ 2u } : uint8_t{ 1u } };
for (auto y{ region.top() }; y < region.bottom(); ++y)
{
auto& row = GetRowByOffset(y);
row.Fill(region.left(), region.width() / width, character, width);
row.GetAttrRow().Replace(region.left<uint16_t>(), gsl::narrow_cast<uint16_t>(region.right() + 1) /* exclusive */, attr);
}
auto out = _WriteRectangular(
region,
[&](WriteResult& /* result */, auto&& at, auto&& size) {
auto& row = GetRowByOffset(at.y());
row.Fill(region.left(), size / width, character, width);
if constexpr (std::is_same<T, TextAttribute>::value)
{
row.GetAttrRow().Replace(::base::saturated_cast<uint16_t>(at.x()), ::base::ClampAdd<uint16_t>(::base::saturated_cast<uint16_t>(at.x()), size), attr);
}
});
return out;
}
size_t TextBuffer::FillWithCharacterAndAttributeLinear(til::point at, size_t count, const wchar_t character, const TextAttribute& attr)
template<typename T>
TextBuffer::WriteResult TextBuffer::_FillWithCharacterAndAttributeLinear(const til::point &at, size_t count, const wchar_t character, [[maybe_unused]] const T& attr)
{
THROW_HR_IF(E_BOUNDS, !_size.IsInBounds(at));
const auto initial{ count };
const uint8_t width{ IsGlyphFullWidth(character) ? uint8_t{ 2u } : uint8_t{ 1u } };
while (count && _size.IsInBounds(at))
{
// narrowing count to short here is fine; it will always be less than width
const auto columns{ std::min<uint16_t>(_size.Width() - at.x<uint16_t>(), gsl::narrow_cast<uint16_t>(count * width)) };
return _WriteLinear(
at,
[&](WriteResult& result, auto&& at, auto&& size) -> bool {
// size is how many *columns* we have left to fill, k?
const auto w{ ::base::ClampMin(::base::saturated_cast<uint16_t>(size), count * width) };
auto& row = GetRowByOffset(at.y());
count -= row.Fill(at.x(), count, character, width);
row.GetAttrRow().Replace(at.x<uint16_t>(), at.x<uint16_t>() + columns + 1 /* exclusive */, attr);
auto& row = GetRowByOffset(at.y());
const auto consumedWchars = row.Fill(at.x(), count, character, width);
if constexpr (std::is_same<T, TextAttribute>::value)
{
row.GetAttrRow().Replace(::base::saturated_cast<uint16_t>(at.x()), ::base::ClampAdd<uint16_t>(::base::saturated_cast<uint16_t>(at.x()), w), attr);
}
const Viewport paint = Viewport::FromDimensions(at, { gsl::narrow<SHORT>(columns), 1 });
_NotifyPaint(paint);
result.charsConsumed += consumedWchars;
result.colsConsumed += w.RawValue();
at = { 0, at.y() + 1 };
}
return initial - count;
const Viewport paint = Viewport::FromDimensions(at, { w.Cast<SHORT>(), 1 });
_NotifyPaint(paint);
return (count -= consumedWchars) > 0; // keep going if data remains
});
}
size_t TextBuffer::FillWithCharacterRectangular(const til::rectangle& region, const wchar_t character)
{
return _FillWithCharacterAndAttributeRectangular(region, character, no_attributes{}).colsConsumed;
}
void TextBuffer::FillWithCharacterAndAttributeRectangular(const til::rectangle& region, const wchar_t character, const TextAttribute& attr)
{
_FillWithCharacterAndAttributeRectangular(region, character, attr);
}
size_t TextBuffer::FillWithCharacterLinear(const til::point& at, size_t count, const wchar_t character)
{
const auto out = _FillWithCharacterAndAttributeLinear(at, count, character, no_attributes{});
return out.charsConsumed;
}
size_t TextBuffer::FillWithCharacterAndAttributeLinear(const til::point &at, size_t count, const wchar_t character, const TextAttribute& attr)
{
const auto out = _FillWithCharacterAndAttributeLinear(at, count, character, attr);
return out.charsConsumed;
}
template<typename TLambda>
TextBuffer::WriteResult TextBuffer::_WriteRectangular(const til::rectangle& rect, TLambda&& thing)
{
WriteResult out{};
for (auto y{ rect.top() }; y < rect.bottom(); ++y)
{
thing(out, til::point{ rect.left(), y }, rect.width());
}
_NotifyPaint(Viewport::FromDimensions(rect.origin(), rect.size()));
return out;
}
template<typename TLambda>
TextBuffer::WriteResult TextBuffer::_WriteLinear(const til::point& start, TLambda&& thing)
{
auto point{ start };
WriteResult out{};
while (_size.IsInBounds(point) && thing(out, point, _size.Width() - point.x()))
{
point = { 0, point.y() };
};
return out;
}

View file

@ -194,20 +194,24 @@ public:
void CopyPatterns(const TextBuffer& OtherBuffer);
interval_tree::IntervalTree<til::point, size_t> GetPatterns(const size_t firstRow, const size_t lastRow) const;
size_t WriteMeasuredStringLinear(til::point at, std::wstring_view string, til::small_rle<uint8_t, uint16_t, 3> measurements);
struct WriteResult
{
size_t charsConsumed;
size_t colsConsumed;
};
size_t WriteStringLinearWithAttributes(til::point at, std::wstring_view string, const TextAttribute& attr);
size_t WriteMeasuredStringLinear(const til::point& at, const std::wstring_view& string, til::small_rle<uint8_t, uint16_t, 3> measurements);
size_t WriteStringLinearKeepAttributes(til::point at, std::wstring_view string);
size_t WriteStringLinearWithAttributes(const til::point& at, const std::wstring_view& string, const TextAttribute& attr);
size_t WriteStringLinearKeepAttributes(const til::point& at, const std::wstring_view& string);
size_t FillWithCharacter(const til::rectangle region, std::wstring_view character);
size_t FillWithCharacterRectangular(const til::rectangle& region, const wchar_t character);
size_t FillWithCharacterLinear(const til::point& at, size_t count, const wchar_t character);
size_t FillWithCharacterLinear(til::point at, size_t count, const wchar_t character);
void FillWithCharacterAndAttribute(const til::rectangle& region, const wchar_t character, const TextAttribute& attr);
size_t FillWithCharacterAndAttributeLinear(til::point at, size_t count, const wchar_t character, const TextAttribute& attr);
void FillWithAttribute(const Microsoft::Console::Types::Viewport& region, const TextAttribute& attribute);
void FillWithCharacterAndAttributeRectangular(const til::rectangle& region, const wchar_t character, const TextAttribute& attr);
size_t FillWithCharacterAndAttributeLinear(const til::point& at, size_t count, const wchar_t character, const TextAttribute& attr);
void FillWithAttributeRectangular(const til::rectangle& region, const TextAttribute& attribute);
size_t FillWithAttributeLinear(const til::point at, size_t count, const TextAttribute& attribute);
private:
@ -249,6 +253,21 @@ private:
void _PruneHyperlinks();
template<typename TLambda>
WriteResult _WriteRectangular(const til::rectangle& rect, TLambda&& thing);
template<typename TLambda>
WriteResult _WriteLinear(const til::point& start, TLambda&& thing);
template<typename T>
WriteResult _FillWithCharacterAndAttributeRectangular(const til::rectangle& region, const wchar_t character, const T& attr);
template<typename T>
WriteResult _FillWithCharacterAndAttributeLinear(const til::point& at, size_t count, const wchar_t character, const T& attr);
template<typename T>
size_t _WriteMeasuredStringLinearWithAttributes(const til::point& at, std::wstring_view string, til::small_rle<uint8_t, uint16_t, 3> measurements, const T& attributes);
std::unordered_map<size_t, std::wstring> _idsAndPatterns;
size_t _currentPatternId;

View file

@ -429,7 +429,7 @@ void CommandListPopup::_drawList()
TextAttribute inverted = _attributes;
inverted.Invert();
_screenInfo.GetTextBuffer().FillWithAttribute(Microsoft::Console::Types::Viewport::FromDimensions(WriteCoord, { Width(), 1 }), inverted);
_screenInfo.GetTextBuffer().FillWithAttributeRectangular(til::rectangle{WriteCoord, til::size{ Width(), 1 }}, inverted);
}
WriteCoord.Y += 1;
@ -526,11 +526,11 @@ void CommandListPopup::_updateHighlight(const SHORT OldCurrentCommand, const SHO
WriteCoord.X = _region.Left + 1i16;
WriteCoord.Y = _region.Top + 1i16 + OldCurrentCommand - TopIndex;
_screenInfo.GetTextBuffer().FillWithAttribute(Microsoft::Console::Types::Viewport::FromDimensions(WriteCoord, { Width(), 1 }), _attributes);
_screenInfo.GetTextBuffer().FillWithAttributeRectangular(til::rectangle{WriteCoord, til::size{ Width(), 1 }}, _attributes);
// highlight new command
WriteCoord.Y = _region.Top + 1i16 + NewCurrentCommand - TopIndex;
TextAttribute inverted = _attributes;
inverted.Invert();
_screenInfo.GetTextBuffer().FillWithAttribute(Microsoft::Console::Types::Viewport::FromDimensions(WriteCoord, { Width(), 1 }), inverted);
_screenInfo.GetTextBuffer().FillWithAttributeRectangular(til::rectangle{WriteCoord, til::size{ Width(), 1 }}, inverted);
}

View file

@ -446,7 +446,7 @@ void ScrollRegion(SCREEN_INFORMATION& screenInfo,
// Apply the fill data to each of the viewports we're given here.
for (const auto& view : remaining)
{
screenInfo.GetTextBuffer().FillWithCharacterAndAttribute(til::rectangle{ view.Origin(), til::size{ view.Dimensions() } }, fillChar, fillAttrs);
screenInfo.GetTextBuffer().FillWithCharacterAndAttributeRectangular(til::rectangle{ view.Origin(), til::size{ view.Dimensions() } }, fillChar, fillAttrs);
// If we're scrolling an area that encompasses the full buffer width,
// then the filled rows should also have their line rendition reset.

View file

@ -90,7 +90,7 @@ void Popup::_DrawBorder()
COORD WriteCoord;
WriteCoord.X = _region.Left;
WriteCoord.Y = _region.Top;
_screenInfo.GetTextBuffer().FillWithAttribute(Viewport::FromInclusive(_region), _attributes);
_screenInfo.GetTextBuffer().FillWithAttributeRectangular(_region, _attributes);
// draw upper left corner
_screenInfo.GetTextBuffer().FillWithCharacterLinear(WriteCoord, 1, UNICODE_BOX_DRAW_LIGHT_DOWN_AND_RIGHT);

View file

@ -2251,7 +2251,7 @@ void SCREEN_INFORMATION::SetViewport(const Viewport& newViewport,
auto fillAttributes = GetAttributes();
fillAttributes.SetStandardErase();
// TODO(DH): The original code moved Left to 0 and Right to Width
_textBuffer->FillWithAttribute(_viewport, fillAttributes);
_textBuffer->FillWithAttributeRectangular(til::rectangle{ _viewport.Origin(), til::size{ _viewport.Dimensions() } }, fillAttributes);
// TODO(DH): THIS ALSO CLEARED THE WRAP ON ALL ROWS (!)
//Write(fillData, fillPosition, false);

View file

@ -388,7 +388,7 @@ void Selection::ColorSelection(const SMALL_RECT& srRect, const TextAttribute att
// Read selection rectangle, assumed already clipped to buffer.
SCREEN_INFORMATION& screenInfo = gci.GetActiveOutputBuffer();
screenInfo.GetTextBuffer().FillWithAttribute(Viewport::FromInclusive(srRect), attr);
screenInfo.GetTextBuffer().FillWithAttributeRectangular(srRect, attr);
}
// Routine Description: