Repository: microsoft/terminal
File: src/renderer/base/renderer.cpp (C++)

Code before:
// - This portion primarily handles figuring the current viewport, comparing it/trimming it versus the invalid portion of the frame, and queuing up, row by row, which pieces of text need to be further processed.
// - See also: Helper functions that separate out each complexity of text rendering.
// Arguments:
// - <none>
// Return Value:
// - <none>
void Renderer::_PaintBufferOutput(_In_ IRenderEngine* const pEngine)
{
    // This is the subsection of the entire screen buffer that is currently being presented.
    // It can move left/right or top/bottom depending on how the viewport is scrolled
    // relative to the entire buffer.
    const auto view = _pData->GetViewport();

    // This is effectively the number of cells on the visible screen that need to be redrawn.
    // The origin is always 0, 0 because it represents the screen itself, not the underlying buffer.
    gsl::span<const til::rect> dirtyAreas;
    LOG_IF_FAILED(pEngine->GetDirtyArea(dirtyAreas));

    // This is to make sure any transforms are reset when this paint is finished.
    auto resetLineTransform = wil::scope_exit([&]() {
        LOG_IF_FAILED(pEngine->ResetLineTransform());
    });

    for (const auto& dirtyRect : dirtyAreas)
    {
        // Shortcut: don't bother redrawing if the width is 0.
        if (dirtyRect.left == dirtyRect.right)
        {
            continue;
        }

        auto dirty = Viewport::FromInclusive(dirtyRect.to_small_rect());

        // Shift the origin of the dirty region to match the underlying buffer so we can
        // compare the two regions directly for intersection.
        dirty = Viewport::Offset(dirty, view.Origin());

        // The intersection between what is dirty on the screen (in need of repaint)
        // and what is supposed to be visible on the screen (the viewport) is what
        // we need to walk through line-by-line and repaint onto the screen.
        const auto redraw = Viewport::Intersect(dirty, view);

        // Retrieve the text buffer so we can read information out of it.
        const auto& buffer = _pData->GetTextBuffer();

        // Now walk through each row of text that we need to redraw.
        for (auto row = redraw.Top(); row < redraw.BottomExclusive(); row++)
        {
            // Calculate the boundaries of a single line. This is from the left to right edge of the dirty
            // area in width and exactly 1 tall.
            const auto screenLine = SMALL_RECT{ redraw.Left(), row, redraw.RightInclusive(), row };

Review comment (bug):
BTW This is a bug. Some engines return dirty rects where `top == bottom`, and I don't think any engine returns `left == right` actually. This causes an invalid `Viewport` to be created a few lines below. In either case though, this now checks both dimensions.

Code after:
// - This portion primarily handles figuring the current viewport, comparing it/trimming it versus the invalid portion of the frame, and queuing up, row by row, which pieces of text need to be further processed.
// - See also: Helper functions that separate out each complexity of text rendering.
// Arguments:
// - <none>
// Return Value:
// - <none>
void Renderer::_PaintBufferOutput(_In_ IRenderEngine* const pEngine)
{
    // This is the subsection of the entire screen buffer that is currently being presented.
    // It can move left/right or top/bottom depending on how the viewport is scrolled
    // relative to the entire buffer.
    const auto view = _pData->GetViewport();

    // This is effectively the number of cells on the visible screen that need to be redrawn.
    // The origin is always 0, 0 because it represents the screen itself, not the underlying buffer.
    gsl::span<const til::rect> dirtyAreas;
    LOG_IF_FAILED(pEngine->GetDirtyArea(dirtyAreas));

    // This is to make sure any transforms are reset when this paint is finished.
    auto resetLineTransform = wil::scope_exit([&]() {
        LOG_IF_FAILED(pEngine->ResetLineTransform());
    });

    for (const auto& dirtyRect : dirtyAreas)
    {
        if (!dirtyRect)
        {
            continue;
        }

        auto dirty = Viewport::FromInclusive(dirtyRect.to_small_rect());

        // Shift the origin of the dirty region to match the underlying buffer so we can
        // compare the two regions directly for intersection.
        dirty = Viewport::Offset(dirty, view.Origin());

        // The intersection between what is dirty on the screen (in need of repaint)
        // and what is supposed to be visible on the screen (the viewport) is what
        // we need to walk through line-by-line and repaint onto the screen.
        const auto redraw = Viewport::Intersect(dirty, view);

        // Retrieve the text buffer so we can read information out of it.
        const auto& buffer = _pData->GetTextBuffer();

        // Now walk through each row of text that we need to redraw.
        for (auto row = redraw.Top(); row < redraw.BottomExclusive(); row++)
        {
            // Calculate the boundaries of a single line. This is from the left to right edge of the dirty
            // area in width and exactly 1 tall.
            const auto screenLine = SMALL_RECT{ redraw.Left(), row, redraw.RightInclusive(), row };