Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support rendering of underline style and color #16097

Merged
merged 24 commits into from
Nov 10, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d2bea4c
initial commit for underline style and color
tusharsnx Oct 3, 2023
d341402
add new underline styles in UIA
tusharsnx Oct 9, 2023
a042401
draw curly line from the shader
tusharsnx Oct 12, 2023
e16055c
revert D2D antialias mode
tusharsnx Oct 12, 2023
1df5894
fix gridlines being drawn with underline color
tusharsnx Oct 14, 2023
0bdeb1a
add dotted underline to BackendD2D and Dx renderers
tusharsnx Oct 14, 2023
c179041
fix ScrollTest's MockScrollRenderEngine wasn't updated to receive und…
tusharsnx Oct 14, 2023
8684de8
make sine wave start with a crest
tusharsnx Oct 16, 2023
dc986c1
fix strikethrough being drawn with underline color
tusharsnx Oct 16, 2023
bea07a5
add support for underline styles in gdi renderer
tusharsnx Oct 24, 2023
4f13a3c
add spellings
tusharsnx Oct 24, 2023
1f3e535
fix signed/unsigned mismatch
tusharsnx Oct 25, 2023
2babe7b
make small improvements to Atlas curlyline rendering
tusharsnx Oct 25, 2023
e31d0fb
AtlasEngine: use cellBottomGap as the peak height for curly line
tusharsnx Oct 27, 2023
63ae6ce
GDIRenderer: use cellBottomGap as the peak height for curly line
tusharsnx Oct 27, 2023
b5574d0
initialize _curlyLineDrawPeakHeight
tusharsnx Oct 27, 2023
b8d3caa
send line rendition scale using texcoord
tusharsnx Oct 28, 2023
a2c4a36
underlines are mutually exclusive
tusharsnx Oct 28, 2023
6e4c65a
revert underline thickness scaling
tusharsnx Oct 28, 2023
b0be8b6
move curlyline peak height constants into the function
tusharsnx Nov 2, 2023
1ba1782
fix curlyline equation that was making a negative peak at the start
tusharsnx Nov 2, 2023
4f7ba10
BackendD2D: use `else if`
tusharsnx Nov 2, 2023
c7a8a55
shrink QuadInstance::ShadingType and add renditionScale member
tusharsnx Nov 3, 2023
3923fde
replace SelectObject with wil::SelectObject
tusharsnx Nov 4, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/renderer/gdi/gdirenderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ namespace Microsoft::Console::Render
int underlineWidth;
int strikethroughOffset;
int strikethroughWidth;
int curlylineHeight;
int curlylinePeakHeight;
};

LineMetrics _lineMetrics;
Expand Down
21 changes: 11 additions & 10 deletions src/renderer/gdi/paint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,7 @@ bool GdiEngine::FontHasWesternScript(HDC hdc)
wil::unique_hbrush hbr(CreateSolidBrush(gridlineColor));
RETURN_HR_IF_NULL(E_FAIL, hbr.get());

// TODO: hbrPrev shouldn't be managed.
wil::unique_hbrush hbrPrev(SelectBrush(_hdcMemoryContext, hbr.get()));
RETURN_HR_IF_NULL(E_FAIL, hbrPrev.get());
hbr.release(); // If SelectBrush was successful, GDI owns the brush. Release for now.
tusharsnx marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -560,8 +561,8 @@ bool GdiEngine::FontHasWesternScript(HDC hdc)
};
const auto DrawCurlyLine = [&](const auto x, const auto y, const auto cCurlyLines) {
const auto curlyLineWidth = fontWidth;
const auto curlyLineHalfWidth = gsl::narrow_cast<long>(std::floor(curlyLineWidth / 2));
const auto controlPointHeight = gsl::narrow_cast<long>(std::floor(3.5f * _lineMetrics.curlylineHeight));
const auto curlyLineHalfWidth = lrintf(curlyLineWidth / 2.0f);
const auto controlPointHeight = gsl::narrow_cast<long>(std::floor(3.5f * _lineMetrics.curlylinePeakHeight));
// Each curlyLine requires 3 `POINT`s
const auto cPoints = gsl::narrow<DWORD>(3 * cCurlyLines);
std::vector<POINT> points;
Expand Down Expand Up @@ -626,28 +627,28 @@ bool GdiEngine::FontHasWesternScript(HDC hdc)
hpen.release();
const auto restorePenOnExit = wil::scope_exit([&] { hpen.reset(SelectPen(_hdcMemoryContext, prevPen)); });

const auto underlineY = std::lround(ptTarget.y + _lineMetrics.underlineOffset + _lineMetrics.underlineWidth / 2.0f);
const auto underlineMidY = std::lround(ptTarget.y + _lineMetrics.underlineOffset + _lineMetrics.underlineWidth / 2.0f);
if (lines.test(GridLines::Underline))
{
return DrawStrokedLine(ptTarget.x, underlineY, widthOfAllCells);
return DrawStrokedLine(ptTarget.x, underlineMidY, widthOfAllCells);
}
else if (lines.test(GridLines::DoubleUnderline))
{
const auto doubleUnderlineBottomLineY = std::lround(ptTarget.y + _lineMetrics.underlineOffset2 + _lineMetrics.underlineWidth / 2.0f);
RETURN_IF_FAILED(DrawStrokedLine(ptTarget.x, underlineY, widthOfAllCells));
return DrawStrokedLine(ptTarget.x, doubleUnderlineBottomLineY, widthOfAllCells);
const auto doubleUnderlineBottomLineMidY = std::lround(ptTarget.y + _lineMetrics.underlineOffset2 + _lineMetrics.underlineWidth / 2.0f);
RETURN_IF_FAILED(DrawStrokedLine(ptTarget.x, underlineMidY, widthOfAllCells));
return DrawStrokedLine(ptTarget.x, doubleUnderlineBottomLineMidY, widthOfAllCells);
}
else if (lines.test(GridLines::CurlyUnderline))
{
return DrawCurlyLine(ptTarget.x, underlineY, cchLine);
return DrawCurlyLine(ptTarget.x, underlineMidY, cchLine);
}
else if (lines.test(GridLines::DottedUnderline))
{
return DrawStrokedLine(ptTarget.x, underlineY, widthOfAllCells);
return DrawStrokedLine(ptTarget.x, underlineMidY, widthOfAllCells);
}
else if (lines.test(GridLines::DashedUnderline))
{
return DrawStrokedLine(ptTarget.x, underlineY, widthOfAllCells);
return DrawStrokedLine(ptTarget.x, underlineMidY, widthOfAllCells);
}

return S_OK;
Expand Down
36 changes: 28 additions & 8 deletions src/renderer/gdi/state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@

using namespace Microsoft::Console::Render;

namespace
{
// The max height of Curly line peak in `em` units.
constexpr auto MaxCurlyLinePeakHeightEm = 0.075f;

// The min height of Curly line peak.
constexpr auto MinCurlyLinePeakHeight = 2.0f;
}

// Routine Description:
// - Creates a new GDI-based rendering engine
// - NOTE: Will throw if initialization failure. Caller must catch.
Expand Down Expand Up @@ -397,18 +406,29 @@ GdiEngine::~GdiEngine()
_lineMetrics.underlineOffset2 = _lineMetrics.underlineOffset - _lineMetrics.gridlineWidth;
}

_lineMetrics.curlylineHeight = gsl::narrow_cast<int>(std::lround(fontSize * 0.07f));
// We need to lower the curly line height incase the gap between the underline
// and cell bottom isn't enough. Curly-line is a stroked line so we account
// for that as well.
const auto maxCurlyLineHeight = gsl::narrow_cast<int>(std::floor(Font.GetSize().height - (_lineMetrics.underlineOffset + _lineMetrics.underlineWidth)));
if (maxCurlyLineHeight <= 1) // too small to be curly, make it a straight line instead.
// Curly line doesn't render properly below 1px stroke width. Make it a straight line.
if (_lineMetrics.underlineWidth < 1)
{
_lineMetrics.curlylineHeight = 0;
_lineMetrics.curlylinePeakHeight = 0;
}
else
{
_lineMetrics.curlylineHeight = std::min(maxCurlyLineHeight, _lineMetrics.curlylineHeight);
// Curlyline uses the gap between cell bottom and singly underline
// position as the height of the wave's peak. The baseline for curly
// line is at the middle of singly underline. The gap could be too big,
// so we also apply a limit on the peak height.
const auto strokeHalfWidth = _lineMetrics.underlineWidth / 2.0f;
const auto underlineMidY = _lineMetrics.underlineOffset + strokeHalfWidth;
const auto cellBottomGap = Font.GetSize().height - underlineMidY - strokeHalfWidth;
const auto maxCurlyLinePeakHeight = MaxCurlyLinePeakHeightEm * fontSize;
auto curlyLinePeakHeight = std::min(cellBottomGap, maxCurlyLinePeakHeight);

// When it's too small to be curly, make it a straight line.
if (curlyLinePeakHeight < MinCurlyLinePeakHeight)
{
curlyLinePeakHeight = 0.0f;
}
_lineMetrics.curlylinePeakHeight = gsl::narrow_cast<int>(std::floor(curlyLinePeakHeight));
}

// Now find the size of a 0 in this current font and save it for conversions done later.
Expand Down
Loading