~mmach/netext73/webkit2gtk

« back to all changes in this revision

Viewing changes to Source/WebCore/layout/displaytree/DisplayPainter.cpp

  • Committer: mmach
  • Date: 2023-06-16 17:21:37 UTC
  • Revision ID: netbit73@gmail.com-20230616172137-2rqx6yr96ga9g3kp
1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2019 Apple Inc. All rights reserved.
 
3
 *
 
4
 * Redistribution and use in source and binary forms, with or without
 
5
 * modification, are permitted provided that the following conditions
 
6
 * are met:
 
7
 * 1. Redistributions of source code must retain the above copyright
 
8
 *    notice, this list of conditions and the following disclaimer.
 
9
 * 2. Redistributions in binary form must reproduce the above copyright
 
10
 *    notice, this list of conditions and the following disclaimer in the
 
11
 *    documentation and/or other materials provided with the distribution.
 
12
 *
 
13
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 
14
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 
15
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 
16
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 
17
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 
18
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 
19
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 
20
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 
21
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 
22
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 
23
 * THE POSSIBILITY OF SUCH DAMAGE.
 
24
 */
 
25
 
 
26
#include "config.h"
 
27
#include "DisplayPainter.h"
 
28
 
 
29
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
 
30
 
 
31
#include "CachedImage.h"
 
32
#include "Color.h"
 
33
#include "DisplayBox.h"
 
34
#include "GraphicsContext.h"
 
35
#include "InlineFormattingState.h"
 
36
#include "InlineTextItem.h"
 
37
#include "IntRect.h"
 
38
#include "LayoutContainer.h"
 
39
#include "LayoutDescendantIterator.h"
 
40
#include "LayoutState.h"
 
41
#include "RenderStyle.h"
 
42
#include "TextRun.h"
 
43
 
 
44
namespace WebCore {
 
45
namespace Display {
 
46
 
 
47
static void paintBoxDecoration(GraphicsContext& context, const Box& absoluteDisplayBox, const RenderStyle& style, bool needsMarginPainting)
 
48
{
 
49
    auto decorationBoxTopLeft = needsMarginPainting ? absoluteDisplayBox.rectWithMargin().topLeft() : absoluteDisplayBox.topLeft();
 
50
    auto decorationBoxSize = needsMarginPainting ? absoluteDisplayBox.rectWithMargin().size() : LayoutSize(absoluteDisplayBox.borderBoxWidth(), absoluteDisplayBox.borderBoxHeight());
 
51
    // Background color
 
52
    if (style.hasBackground()) {
 
53
        context.fillRect({ decorationBoxTopLeft, decorationBoxSize }, style.backgroundColor());
 
54
        if (style.hasBackgroundImage()) {
 
55
            auto& backgroundLayer = style.backgroundLayers();
 
56
            if (backgroundLayer.image() && backgroundLayer.image()->cachedImage() && backgroundLayer.image()->cachedImage()->image())
 
57
                context.drawImage(*backgroundLayer.image()->cachedImage()->image(), { decorationBoxTopLeft, backgroundLayer.image()->cachedImage()->image()->size() });
 
58
        }
 
59
    }
 
60
 
 
61
    // Border
 
62
    if (style.hasVisibleBorder()) {
 
63
 
 
64
        auto drawBorderSide = [&](auto start, auto end, const auto& borderStyle) {
 
65
            if (!borderStyle.width())
 
66
                return;
 
67
            if (borderStyle.style() == BorderStyle::None || borderStyle.style() == BorderStyle::Hidden)
 
68
                return;
 
69
            context.setStrokeColor(borderStyle.color());
 
70
            context.setStrokeThickness(borderStyle.width());
 
71
            context.drawLine(start, end);
 
72
        };
 
73
 
 
74
        context.setFillColor(Color::transparent);
 
75
 
 
76
        auto decorationBoxWidth = decorationBoxSize.width();
 
77
        auto decorationBoxHeight = decorationBoxSize.height();
 
78
 
 
79
        // Top
 
80
        {
 
81
            auto borderWidth = style.borderTop().width();
 
82
            auto start = LayoutPoint { decorationBoxTopLeft };
 
83
            auto end = LayoutPoint { start.x() + decorationBoxWidth, start.y() + borderWidth };
 
84
            drawBorderSide(start, end, style.borderTop());
 
85
        }
 
86
 
 
87
        // Right
 
88
        {
 
89
            auto borderWidth = style.borderRight().width();
 
90
            auto start = LayoutPoint { decorationBoxTopLeft.x() + decorationBoxWidth - borderWidth, decorationBoxTopLeft.y() };
 
91
            auto end = LayoutPoint { start.x() + borderWidth, decorationBoxTopLeft.y() + decorationBoxHeight };
 
92
            drawBorderSide(start, end, style.borderRight());
 
93
        }
 
94
 
 
95
        // Bottom
 
96
        {
 
97
            auto borderWidth = style.borderBottom().width();
 
98
            auto start = LayoutPoint { decorationBoxTopLeft.x(), decorationBoxTopLeft.y() + decorationBoxHeight - borderWidth };
 
99
            auto end = LayoutPoint { start.x() + decorationBoxWidth, start.y() + borderWidth };
 
100
            drawBorderSide(start, end, style.borderBottom());
 
101
        }
 
102
 
 
103
        // Left
 
104
        {
 
105
            auto borderWidth = style.borderLeft().width();
 
106
            auto start = decorationBoxTopLeft;
 
107
            auto end = LayoutPoint { start.x() + borderWidth, decorationBoxTopLeft.y() + decorationBoxHeight };
 
108
            drawBorderSide(start, end, style.borderLeft());
 
109
        }
 
110
    }
 
111
}
 
112
 
 
113
static void paintInlineContent(GraphicsContext& context, LayoutPoint absoluteOffset, const Layout::InlineFormattingState& formattingState)
 
114
{
 
115
    auto* displayInlineContent = formattingState.displayInlineContent();
 
116
    if (!displayInlineContent)
 
117
        return;
 
118
 
 
119
    auto& displayRuns = displayInlineContent->runs;
 
120
    if (displayRuns.isEmpty())
 
121
        return;
 
122
 
 
123
    for (auto& run : displayRuns) {
 
124
        if (auto& textContext = run.textContext()) {
 
125
            auto& style = run.style();
 
126
            context.setStrokeColor(style.color());
 
127
            context.setFillColor(style.color());
 
128
 
 
129
            auto absoluteLeft = absoluteOffset.x() + run.left();
 
130
            // FIXME: Add non-baseline align painting
 
131
            auto& lineBox = displayInlineContent->lineBoxForRun(run);
 
132
            auto baselineOffset = absoluteOffset.y() + lineBox.top() + lineBox.baselineOffset();
 
133
            auto expansionContext = textContext->expansion();
 
134
            auto textRun = TextRun { textContext->content(), run.left() - lineBox.left(),
 
135
                expansionContext ? expansionContext->horizontalExpansion : 0,
 
136
                expansionContext ? expansionContext->behavior : DefaultExpansion };
 
137
            textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
 
138
            context.drawText(style.fontCascade(), textRun, { absoluteLeft, baselineOffset });
 
139
        } else if (auto* cachedImage = run.image()) {
 
140
            auto runAbsoluteRect = FloatRect { absoluteOffset.x() + run.left(), absoluteOffset.y() + run.top(), run.width(), run.height() };
 
141
            context.drawImage(*cachedImage->image(), runAbsoluteRect);
 
142
        }
 
143
    }
 
144
}
 
145
 
 
146
static Box absoluteDisplayBox(const Layout::LayoutState& layoutState, const Layout::Box& layoutBox)
 
147
{
 
148
    // Should never really happen but table code is way too incomplete.
 
149
    if (!layoutState.hasDisplayBox(layoutBox))
 
150
        return { };
 
151
    if (layoutBox.isInitialContainingBlock())
 
152
        return layoutState.displayBoxForLayoutBox(layoutBox);
 
153
 
 
154
    auto absoluteBox = Box { layoutState.displayBoxForLayoutBox(layoutBox) };
 
155
    for (auto* container = layoutBox.containingBlock(); container != &layoutBox.initialContainingBlock(); container = container->containingBlock())
 
156
        absoluteBox.moveBy(layoutState.displayBoxForLayoutBox(*container).topLeft());
 
157
    return absoluteBox;
 
158
}
 
159
 
 
160
static bool isPaintRootCandidate(const Layout::Box& layoutBox)
 
161
{
 
162
    return layoutBox.isPositioned();
 
163
}
 
164
 
 
165
using LayoutBoxList = Vector<const Layout::Box*>;
 
166
 
 
167
enum PaintPhase { Decoration, Content };
 
168
static void paintSubtree(GraphicsContext& context, const Layout::LayoutState& layoutState, const Layout::Box& paintRootBox, const IntRect& dirtyRect, PaintPhase paintPhase)
 
169
{
 
170
    auto paint = [&] (auto& layoutBox) {
 
171
        if (layoutBox.style().visibility() != Visibility::Visible)
 
172
            return;
 
173
        auto absoluteDisplayBox = Display::absoluteDisplayBox(layoutState, layoutBox);
 
174
        if (!dirtyRect.intersects(snappedIntRect(absoluteDisplayBox.rect())))
 
175
            return;
 
176
 
 
177
        if (paintPhase == PaintPhase::Decoration) {
 
178
            if (layoutBox.isAnonymous())
 
179
                return;
 
180
            paintBoxDecoration(context, absoluteDisplayBox, layoutBox.style(), layoutBox.isBodyBox());
 
181
            return;
 
182
        }
 
183
        // Only inline content for now.
 
184
        if (layoutBox.establishesInlineFormattingContext()) {
 
185
            auto& container = downcast<Layout::Container>(layoutBox);
 
186
            paintInlineContent(context, absoluteDisplayBox.topLeft(), layoutState.establishedInlineFormattingState(container));
 
187
        }
 
188
    };
 
189
 
 
190
    paint(paintRootBox);
 
191
    if (!is<Layout::Container>(paintRootBox) || !downcast<Layout::Container>(paintRootBox).hasChild())
 
192
        return;
 
193
 
 
194
    LayoutBoxList layoutBoxList;
 
195
    layoutBoxList.append(downcast<Layout::Container>(paintRootBox).firstChild());
 
196
    while (!layoutBoxList.isEmpty()) {
 
197
        while (true) {
 
198
            auto& layoutBox = *layoutBoxList.last();
 
199
            if (isPaintRootCandidate(layoutBox))
 
200
                break;
 
201
            paint(layoutBox);
 
202
            if (!is<Layout::Container>(layoutBox) || !downcast<Layout::Container>(layoutBox).hasChild())
 
203
                break;
 
204
            layoutBoxList.append(downcast<Layout::Container>(layoutBox).firstChild());
 
205
        }
 
206
        while (!layoutBoxList.isEmpty()) {
 
207
            auto& layoutBox = *layoutBoxList.takeLast();
 
208
            // Stay within.
 
209
            if (&layoutBox == &paintRootBox)
 
210
                return;
 
211
            if (auto* nextSibling = layoutBox.nextSibling()) {
 
212
                layoutBoxList.append(nextSibling);
 
213
                break;
 
214
            }
 
215
        }
 
216
    }
 
217
}
 
218
 
 
219
static LayoutRect collectPaintRootsAndContentRect(const Layout::LayoutState& layoutState, const Layout::Box& rootLayoutBox, LayoutBoxList& positiveZOrderList, LayoutBoxList& negativeZOrderList)
 
220
{
 
221
    auto appendPaintRoot = [&] (const auto& layoutBox) {
 
222
        if (layoutBox.style().usedZIndex() < 0) {
 
223
            negativeZOrderList.append(&layoutBox);
 
224
            return;
 
225
        }
 
226
        positiveZOrderList.append(&layoutBox);
 
227
    };
 
228
 
 
229
    auto contentRect = LayoutRect { layoutState.displayBoxForLayoutBox(rootLayoutBox).rect() };
 
230
 
 
231
    // Initial BFC is always a paint root.
 
232
    appendPaintRoot(rootLayoutBox);
 
233
    LayoutBoxList layoutBoxList;
 
234
    layoutBoxList.append(&rootLayoutBox);
 
235
    while (!layoutBoxList.isEmpty()) {
 
236
        while (true) {
 
237
            auto& layoutBox = *layoutBoxList.last();
 
238
            if (layoutBox.style().visibility() != Visibility::Visible)
 
239
                break;
 
240
            if (isPaintRootCandidate(layoutBox))
 
241
                appendPaintRoot(layoutBox);
 
242
            contentRect.uniteIfNonZero(Display::absoluteDisplayBox(layoutState, layoutBox).rect());
 
243
            if (!is<Layout::Container>(layoutBox) || !downcast<Layout::Container>(layoutBox).hasChild())
 
244
                break;
 
245
            layoutBoxList.append(downcast<Layout::Container>(layoutBox).firstChild());
 
246
        }
 
247
        while (!layoutBoxList.isEmpty()) {
 
248
            auto& layoutBox = *layoutBoxList.takeLast();
 
249
            if (auto* nextSibling = layoutBox.nextSibling()) {
 
250
                layoutBoxList.append(nextSibling);
 
251
                break;
 
252
            }
 
253
        }
 
254
    }
 
255
 
 
256
    auto compareZIndex = [] (const Layout::Box* a, const Layout::Box* b) {
 
257
        return a->style().usedZIndex() < b->style().usedZIndex();
 
258
    };
 
259
 
 
260
    std::stable_sort(positiveZOrderList.begin(), positiveZOrderList.end(), compareZIndex);
 
261
    std::stable_sort(negativeZOrderList.begin(), negativeZOrderList.end(), compareZIndex);
 
262
    return contentRect;
 
263
}
 
264
 
 
265
void Painter::paint(const Layout::LayoutState& layoutState, GraphicsContext& context, const IntRect& dirtyRect)
 
266
{
 
267
    auto& rootLayoutBox = layoutState.root();
 
268
    if (!rootLayoutBox.firstChild())
 
269
        return;
 
270
 
 
271
    Vector<const Layout::Box*> negativeZOrderList;
 
272
    Vector<const Layout::Box*> positiveZOrderList;
 
273
    auto contentRect = collectPaintRootsAndContentRect(layoutState, rootLayoutBox, positiveZOrderList, negativeZOrderList);
 
274
 
 
275
    // Fill the entire content area.
 
276
    context.fillRect(contentRect, Color::white);
 
277
 
 
278
    for (auto& paintRootBox : negativeZOrderList) {
 
279
        paintSubtree(context, layoutState, *paintRootBox, dirtyRect, PaintPhase::Decoration);
 
280
        paintSubtree(context, layoutState, *paintRootBox, dirtyRect, PaintPhase::Content);
 
281
    }
 
282
 
 
283
    for (auto& paintRootBox : positiveZOrderList) {
 
284
        paintSubtree(context, layoutState, *paintRootBox, dirtyRect, PaintPhase::Decoration);
 
285
        paintSubtree(context, layoutState, *paintRootBox, dirtyRect, PaintPhase::Content);
 
286
    }
 
287
}
 
288
 
 
289
void Painter::paintInlineFlow(const Layout::LayoutState& layoutState, GraphicsContext& context)
 
290
{
 
291
    auto& layoutRoot = layoutState.root();
 
292
 
 
293
    ASSERT(layoutRoot.establishesInlineFormattingContext());
 
294
 
 
295
    paintInlineContent(context, { }, layoutState.establishedInlineFormattingState(layoutRoot));
 
296
}
 
297
 
 
298
}
 
299
}
 
300
 
 
301
#endif