2
* Copyright (C) 2019 Apple Inc. All rights reserved.
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions
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.
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.
27
#include "DisplayPainter.h"
29
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
31
#include "CachedImage.h"
33
#include "DisplayBox.h"
34
#include "GraphicsContext.h"
35
#include "InlineFormattingState.h"
36
#include "InlineTextItem.h"
38
#include "LayoutContainer.h"
39
#include "LayoutDescendantIterator.h"
40
#include "LayoutState.h"
41
#include "RenderStyle.h"
47
static void paintBoxDecoration(GraphicsContext& context, const Box& absoluteDisplayBox, const RenderStyle& style, bool needsMarginPainting)
49
auto decorationBoxTopLeft = needsMarginPainting ? absoluteDisplayBox.rectWithMargin().topLeft() : absoluteDisplayBox.topLeft();
50
auto decorationBoxSize = needsMarginPainting ? absoluteDisplayBox.rectWithMargin().size() : LayoutSize(absoluteDisplayBox.borderBoxWidth(), absoluteDisplayBox.borderBoxHeight());
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() });
62
if (style.hasVisibleBorder()) {
64
auto drawBorderSide = [&](auto start, auto end, const auto& borderStyle) {
65
if (!borderStyle.width())
67
if (borderStyle.style() == BorderStyle::None || borderStyle.style() == BorderStyle::Hidden)
69
context.setStrokeColor(borderStyle.color());
70
context.setStrokeThickness(borderStyle.width());
71
context.drawLine(start, end);
74
context.setFillColor(Color::transparent);
76
auto decorationBoxWidth = decorationBoxSize.width();
77
auto decorationBoxHeight = decorationBoxSize.height();
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());
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());
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());
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());
113
static void paintInlineContent(GraphicsContext& context, LayoutPoint absoluteOffset, const Layout::InlineFormattingState& formattingState)
115
auto* displayInlineContent = formattingState.displayInlineContent();
116
if (!displayInlineContent)
119
auto& displayRuns = displayInlineContent->runs;
120
if (displayRuns.isEmpty())
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());
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);
146
static Box absoluteDisplayBox(const Layout::LayoutState& layoutState, const Layout::Box& layoutBox)
148
// Should never really happen but table code is way too incomplete.
149
if (!layoutState.hasDisplayBox(layoutBox))
151
if (layoutBox.isInitialContainingBlock())
152
return layoutState.displayBoxForLayoutBox(layoutBox);
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());
160
static bool isPaintRootCandidate(const Layout::Box& layoutBox)
162
return layoutBox.isPositioned();
165
using LayoutBoxList = Vector<const Layout::Box*>;
167
enum PaintPhase { Decoration, Content };
168
static void paintSubtree(GraphicsContext& context, const Layout::LayoutState& layoutState, const Layout::Box& paintRootBox, const IntRect& dirtyRect, PaintPhase paintPhase)
170
auto paint = [&] (auto& layoutBox) {
171
if (layoutBox.style().visibility() != Visibility::Visible)
173
auto absoluteDisplayBox = Display::absoluteDisplayBox(layoutState, layoutBox);
174
if (!dirtyRect.intersects(snappedIntRect(absoluteDisplayBox.rect())))
177
if (paintPhase == PaintPhase::Decoration) {
178
if (layoutBox.isAnonymous())
180
paintBoxDecoration(context, absoluteDisplayBox, layoutBox.style(), layoutBox.isBodyBox());
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));
191
if (!is<Layout::Container>(paintRootBox) || !downcast<Layout::Container>(paintRootBox).hasChild())
194
LayoutBoxList layoutBoxList;
195
layoutBoxList.append(downcast<Layout::Container>(paintRootBox).firstChild());
196
while (!layoutBoxList.isEmpty()) {
198
auto& layoutBox = *layoutBoxList.last();
199
if (isPaintRootCandidate(layoutBox))
202
if (!is<Layout::Container>(layoutBox) || !downcast<Layout::Container>(layoutBox).hasChild())
204
layoutBoxList.append(downcast<Layout::Container>(layoutBox).firstChild());
206
while (!layoutBoxList.isEmpty()) {
207
auto& layoutBox = *layoutBoxList.takeLast();
209
if (&layoutBox == &paintRootBox)
211
if (auto* nextSibling = layoutBox.nextSibling()) {
212
layoutBoxList.append(nextSibling);
219
static LayoutRect collectPaintRootsAndContentRect(const Layout::LayoutState& layoutState, const Layout::Box& rootLayoutBox, LayoutBoxList& positiveZOrderList, LayoutBoxList& negativeZOrderList)
221
auto appendPaintRoot = [&] (const auto& layoutBox) {
222
if (layoutBox.style().usedZIndex() < 0) {
223
negativeZOrderList.append(&layoutBox);
226
positiveZOrderList.append(&layoutBox);
229
auto contentRect = LayoutRect { layoutState.displayBoxForLayoutBox(rootLayoutBox).rect() };
231
// Initial BFC is always a paint root.
232
appendPaintRoot(rootLayoutBox);
233
LayoutBoxList layoutBoxList;
234
layoutBoxList.append(&rootLayoutBox);
235
while (!layoutBoxList.isEmpty()) {
237
auto& layoutBox = *layoutBoxList.last();
238
if (layoutBox.style().visibility() != Visibility::Visible)
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())
245
layoutBoxList.append(downcast<Layout::Container>(layoutBox).firstChild());
247
while (!layoutBoxList.isEmpty()) {
248
auto& layoutBox = *layoutBoxList.takeLast();
249
if (auto* nextSibling = layoutBox.nextSibling()) {
250
layoutBoxList.append(nextSibling);
256
auto compareZIndex = [] (const Layout::Box* a, const Layout::Box* b) {
257
return a->style().usedZIndex() < b->style().usedZIndex();
260
std::stable_sort(positiveZOrderList.begin(), positiveZOrderList.end(), compareZIndex);
261
std::stable_sort(negativeZOrderList.begin(), negativeZOrderList.end(), compareZIndex);
265
void Painter::paint(const Layout::LayoutState& layoutState, GraphicsContext& context, const IntRect& dirtyRect)
267
auto& rootLayoutBox = layoutState.root();
268
if (!rootLayoutBox.firstChild())
271
Vector<const Layout::Box*> negativeZOrderList;
272
Vector<const Layout::Box*> positiveZOrderList;
273
auto contentRect = collectPaintRootsAndContentRect(layoutState, rootLayoutBox, positiveZOrderList, negativeZOrderList);
275
// Fill the entire content area.
276
context.fillRect(contentRect, Color::white);
278
for (auto& paintRootBox : negativeZOrderList) {
279
paintSubtree(context, layoutState, *paintRootBox, dirtyRect, PaintPhase::Decoration);
280
paintSubtree(context, layoutState, *paintRootBox, dirtyRect, PaintPhase::Content);
283
for (auto& paintRootBox : positiveZOrderList) {
284
paintSubtree(context, layoutState, *paintRootBox, dirtyRect, PaintPhase::Decoration);
285
paintSubtree(context, layoutState, *paintRootBox, dirtyRect, PaintPhase::Content);
289
void Painter::paintInlineFlow(const Layout::LayoutState& layoutState, GraphicsContext& context)
291
auto& layoutRoot = layoutState.root();
293
ASSERT(layoutRoot.establishesInlineFormattingContext());
295
paintInlineContent(context, { }, layoutState.establishedInlineFormattingState(layoutRoot));