[LFC][Painting] Add support for image/background image painting
https://bugs.webkit.org/show_bug.cgi?id=203054
<rdar://problem/56347733>
Reviewed by Antti Koivisto.
In addition to image rendering, this patch also addresses various run/decoration painting issues.
* layout/displaytree/DisplayPainter.cpp:
(WebCore::Display::paintBoxDecoration):
(WebCore::Display::paintInlineContent):
(WebCore::Display::absoluteDisplayBox):
(WebCore::Display::paintBoxDecorationAndChildren):
(WebCore::Display::Painter::paint):
* layout/displaytree/DisplayRun.h:
(WebCore::Display::Run::setImage):
(WebCore::Display::Run::image const):
* layout/inlineformatting/InlineLine.cpp:
(WebCore::Layout::Line::appendNonReplacedInlineBox):
(WebCore::Layout::Line::appendReplacedInlineBox):
* layout/layouttree/LayoutReplaced.h:
(WebCore::Layout::Replaced::setCachedImage):
(WebCore::Layout::Replaced::cachedImage const):
* layout/layouttree/LayoutTreeBuilder.cpp:
(WebCore::Layout::TreeBuilder::createLayoutBox):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@251239 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 42f50db..5fcff80 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,5 +1,33 @@
2019-10-17 Zalan Bujtas <zalan@apple.com>
+ [LFC][Painting] Add support for image/background image painting
+ https://bugs.webkit.org/show_bug.cgi?id=203054
+ <rdar://problem/56347733>
+
+ Reviewed by Antti Koivisto.
+
+ In addition to image rendering, this patch also addresses various run/decoration painting issues.
+
+ * layout/displaytree/DisplayPainter.cpp:
+ (WebCore::Display::paintBoxDecoration):
+ (WebCore::Display::paintInlineContent):
+ (WebCore::Display::absoluteDisplayBox):
+ (WebCore::Display::paintBoxDecorationAndChildren):
+ (WebCore::Display::Painter::paint):
+ * layout/displaytree/DisplayRun.h:
+ (WebCore::Display::Run::setImage):
+ (WebCore::Display::Run::image const):
+ * layout/inlineformatting/InlineLine.cpp:
+ (WebCore::Layout::Line::appendNonReplacedInlineBox):
+ (WebCore::Layout::Line::appendReplacedInlineBox):
+ * layout/layouttree/LayoutReplaced.h:
+ (WebCore::Layout::Replaced::setCachedImage):
+ (WebCore::Layout::Replaced::cachedImage const):
+ * layout/layouttree/LayoutTreeBuilder.cpp:
+ (WebCore::Layout::TreeBuilder::createLayoutBox):
+
+2019-10-17 Zalan Bujtas <zalan@apple.com>
+
[LFC][IFC] Add Display::Run/LineBox mapping
https://bugs.webkit.org/show_bug.cgi?id=203051
<rdar://problem/56342487>
diff --git a/Source/WebCore/layout/displaytree/DisplayPainter.cpp b/Source/WebCore/layout/displaytree/DisplayPainter.cpp
index 4ad95c6..d78a8f2 100644
--- a/Source/WebCore/layout/displaytree/DisplayPainter.cpp
+++ b/Source/WebCore/layout/displaytree/DisplayPainter.cpp
@@ -28,6 +28,7 @@
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+#include "CachedImage.h"
#include "Color.h"
#include "DisplayBox.h"
#include "GraphicsContext.h"
@@ -42,17 +43,28 @@
namespace WebCore {
namespace Display {
-static void paintBoxDecoration(GraphicsContext& context, const Box& absoluteDisplayBox, const RenderStyle& style)
+static void paintBoxDecoration(GraphicsContext& context, const Box& absoluteDisplayBox, const RenderStyle& style, bool needsMarginPainting)
{
- auto borderBoxAbsoluteTopLeft = absoluteDisplayBox.topLeft();
+ auto decorationBoxTopLeft = needsMarginPainting ? absoluteDisplayBox.rectWithMargin().topLeft() : absoluteDisplayBox.topLeft();
+ auto decorationBoxSize = needsMarginPainting ? absoluteDisplayBox.rectWithMargin().size() : LayoutSize(absoluteDisplayBox.borderBoxWidth(), absoluteDisplayBox.borderBoxHeight());
// Background color
- if (style.hasBackground())
- context.fillRect({ borderBoxAbsoluteTopLeft, FloatSize { absoluteDisplayBox.borderBoxWidth(), absoluteDisplayBox.borderBoxHeight() } }, style.backgroundColor());
+ if (style.hasBackground()) {
+ context.fillRect({ decorationBoxTopLeft, decorationBoxSize }, style.backgroundColor());
+ if (style.hasBackgroundImage()) {
+ auto& backgroundLayer = style.backgroundLayers();
+ if (backgroundLayer.image() && backgroundLayer.image()->cachedImage() && backgroundLayer.image()->cachedImage()->image())
+ context.drawImage(*backgroundLayer.image()->cachedImage()->image(), { decorationBoxTopLeft, backgroundLayer.image()->cachedImage()->image()->size() });
+ }
+ }
// Border
- if (style.hasBorder()) {
+ if (style.hasVisibleBorder()) {
auto drawBorderSide = [&](auto start, auto end, const auto& borderStyle) {
+ if (!borderStyle.width())
+ return;
+ if (borderStyle.style() == BorderStyle::None || borderStyle.style() == BorderStyle::Hidden)
+ return;
context.setStrokeColor(borderStyle.color());
context.setStrokeThickness(borderStyle.width());
context.drawLine(start, end);
@@ -60,38 +72,38 @@
context.setFillColor(Color::transparent);
- auto borderBoxWidth = absoluteDisplayBox.borderBoxWidth();
- auto borderBoxHeight = absoluteDisplayBox.borderBoxHeight();
+ auto decorationBoxWidth = decorationBoxSize.width();
+ auto decorationBoxHeight = decorationBoxSize.height();
// Top
{
auto borderWidth = style.borderTop().width();
- auto start = LayoutPoint { borderBoxAbsoluteTopLeft };
- auto end = LayoutPoint { start.x() + borderBoxWidth, start.y() + borderWidth };
+ auto start = LayoutPoint { decorationBoxTopLeft };
+ auto end = LayoutPoint { start.x() + decorationBoxWidth, start.y() + borderWidth };
drawBorderSide(start, end, style.borderTop());
}
// Right
{
auto borderWidth = style.borderRight().width();
- auto start = LayoutPoint { borderBoxAbsoluteTopLeft.x() + borderBoxWidth - borderWidth, borderBoxAbsoluteTopLeft.y() };
- auto end = LayoutPoint { start.x() + borderWidth, borderBoxAbsoluteTopLeft.y() + borderBoxHeight };
+ auto start = LayoutPoint { decorationBoxTopLeft.x() + decorationBoxWidth - borderWidth, decorationBoxTopLeft.y() };
+ auto end = LayoutPoint { start.x() + borderWidth, decorationBoxTopLeft.y() + decorationBoxHeight };
drawBorderSide(start, end, style.borderRight());
}
// Bottom
{
auto borderWidth = style.borderBottom().width();
- auto start = LayoutPoint { borderBoxAbsoluteTopLeft.x(), borderBoxAbsoluteTopLeft.y() + borderBoxHeight - borderWidth };
- auto end = LayoutPoint { start.x() + borderBoxWidth, start.y() + borderWidth };
+ auto start = LayoutPoint { decorationBoxTopLeft.x(), decorationBoxTopLeft.y() + decorationBoxHeight - borderWidth };
+ auto end = LayoutPoint { start.x() + decorationBoxWidth, start.y() + borderWidth };
drawBorderSide(start, end, style.borderBottom());
}
// Left
{
auto borderWidth = style.borderLeft().width();
- auto start = borderBoxAbsoluteTopLeft;
- auto end = LayoutPoint { start.x() + borderWidth, borderBoxAbsoluteTopLeft.y() + borderBoxHeight };
+ auto start = decorationBoxTopLeft;
+ auto end = LayoutPoint { start.x() + borderWidth, decorationBoxTopLeft.y() + decorationBoxHeight };
drawBorderSide(start, end, style.borderLeft());
}
}
@@ -102,64 +114,69 @@
auto& inlineRuns = formattingState.inlineRuns();
if (inlineRuns.isEmpty())
return;
- // FIXME: We should be able to paint runs independently from inline items.
- unsigned runIndex = 0;
- for (auto& inlineItem : formattingState.inlineItems()) {
- auto& style = inlineItem->style();
- context.setStrokeColor(style.color());
- context.setFillColor(style.color());
- if (inlineItem->isText()) {
- auto& inlineTextItem = downcast<Layout::InlineTextItem>(*inlineItem);
- auto inlineContent = inlineTextItem.layoutBox().textContent();
- while (true) {
- auto& run = *inlineRuns[runIndex++];
- auto textContext = run.textContext().value();
- auto runContent = inlineContent.substring(textContext.start(), textContext.length());
- auto logicalTopLeft = rootAbsoluteDisplayBox.topLeft() + run.logicalTopLeft();
- context.drawText(style.fontCascade(), TextRun { runContent }, { logicalTopLeft.x(), logicalTopLeft.y() + formattingState.lineBoxes()[0]->baselineOffset() });
- if (inlineTextItem.end() == textContext.end())
- break;
- if (runIndex == inlineRuns.size())
- return;
- }
- continue;
- }
+ for (auto& run : inlineRuns) {
+ if (run->textContext()) {
+ auto& style = run->style();
+ context.setStrokeColor(style.color());
+ context.setFillColor(style.color());
- if (inlineItem->isBox()) {
- auto& run = *inlineRuns[runIndex++];
- auto logicalTopLeft = rootAbsoluteDisplayBox.topLeft() + run.logicalTopLeft();
- context.fillRect({ logicalTopLeft, FloatSize { run.logicalWidth(), run.logicalHeight() } });
- continue;
+ auto logicalLeft = rootAbsoluteDisplayBox.left() + run->logicalLeft();
+ // FIXME: Add non-baseline align painting
+ auto& lineBox = formattingState.lineBoxForRun(*run);
+ auto baselineOffset = rootAbsoluteDisplayBox.top() + lineBox.logicalTop() + lineBox.baselineOffset();
+ context.drawText(style.fontCascade(), TextRun { run->textContext()->content() }, { logicalLeft, baselineOffset });
+ } else if (auto* cachedImage = run->image()) {
+ auto runAbsoluteRect = FloatRect { rootAbsoluteDisplayBox.left() + run->logicalLeft(), rootAbsoluteDisplayBox.top() + run->logicalTop(), run->logicalWidth(), run->logicalHeight() };
+ context.drawImage(*cachedImage->image(), runAbsoluteRect);
}
}
}
+static Box absoluteDisplayBox(const Layout::LayoutState& layoutState, const Layout::Box& layoutBox)
+{
+ // Should never really happen but table code is way too incomplete.
+ if (!layoutState.hasDisplayBox(layoutBox))
+ return { };
+
+ auto absoluteBox = Box { layoutState.displayBoxForLayoutBox(layoutBox) };
+ for (auto* container = layoutBox.containingBlock(); container != &layoutBox.initialContainingBlock(); container = container->containingBlock())
+ absoluteBox.moveBy(layoutState.displayBoxForLayoutBox(*container).topLeft());
+ return absoluteBox;
+}
+
+static void paintBoxDecorationAndChildren(GraphicsContext& context, const Layout::LayoutState& layoutState, const Layout::Box& layoutBox)
+{
+ if (!layoutBox.isAnonymous())
+ paintBoxDecoration(context, absoluteDisplayBox(layoutState, layoutBox), layoutBox.style(), layoutBox.isBodyBox());
+
+ if (!is<Layout::Container>(layoutBox))
+ return;
+ for (auto& childLayoutBox : Layout::childrenOfType<Layout::Box>(downcast<Layout::Container>(layoutBox))) {
+ if (childLayoutBox.style().visibility() != Visibility::Visible)
+ continue;
+ paintBoxDecorationAndChildren(context, layoutState, childLayoutBox);
+ }
+}
+
void Painter::paint(const Layout::LayoutState& layoutState, GraphicsContext& context)
{
- auto absoluteDisplayBox = [&](const auto& layoutBox) {
- auto absoluteBox = Box { layoutState.displayBoxForLayoutBox(layoutBox) };
- for (auto* container = layoutBox.containingBlock(); container != &layoutBox.initialContainingBlock(); container = container->containingBlock())
- absoluteBox.moveBy(layoutState.displayBoxForLayoutBox(*container).topLeft());
- return absoluteBox;
- };
-
auto& layoutRoot = layoutState.root();
auto& rootDisplayBox = layoutState.displayBoxForLayoutBox(layoutRoot);
context.fillRect({ FloatPoint { }, FloatSize { rootDisplayBox.borderBoxWidth(), rootDisplayBox.borderBoxHeight() } }, Color::white);
+ if (!layoutRoot.firstChild())
+ return;
// 1. Paint box decoration (both block and inline).
- for (auto& layoutBox : Layout::descendantsOfType<Layout::Box>(layoutRoot)) {
- if (layoutBox.isAnonymous())
- continue;
- paintBoxDecoration(context, absoluteDisplayBox(layoutBox), layoutBox.style());
- }
+ paintBoxDecorationAndChildren(context, layoutState, *layoutRoot.firstChild());
// 2. Paint content
for (auto& layoutBox : Layout::descendantsOfType<Layout::Box>(layoutRoot)) {
+ if (layoutBox.style().visibility() != Visibility::Visible)
+ continue;
if (layoutBox.establishesInlineFormattingContext()) {
auto& container = downcast<Layout::Container>(layoutBox);
- paintInlineContent(context, absoluteDisplayBox(container), downcast<Layout::InlineFormattingState>(layoutState.establishedFormattingState(container)));
+ paintInlineContent(context, absoluteDisplayBox(layoutState, container), downcast<Layout::InlineFormattingState>(layoutState.establishedFormattingState(container)));
continue;
}
}
diff --git a/Source/WebCore/layout/displaytree/DisplayRun.h b/Source/WebCore/layout/displaytree/DisplayRun.h
index 58a46af..e107c58 100644
--- a/Source/WebCore/layout/displaytree/DisplayRun.h
+++ b/Source/WebCore/layout/displaytree/DisplayRun.h
@@ -32,6 +32,9 @@
#include "RenderStyle.h"
namespace WebCore {
+
+class CachedImage;
+
namespace Display {
struct Run {
@@ -81,11 +84,15 @@
Optional<TextContext>& textContext() { return m_textContext; }
Optional<TextContext> textContext() const { return m_textContext; }
+ void setImage(CachedImage& image) { m_cachedImage = ℑ }
+ CachedImage* image() const { return m_cachedImage; }
+
const RenderStyle& style() const { return m_style; }
private:
// FIXME: Find out the Display::Run <-> paint style setup.
const RenderStyle& m_style;
+ CachedImage* m_cachedImage { nullptr };
Rect m_logicalRect;
Optional<TextContext> m_textContext;
};
diff --git a/Source/WebCore/layout/inlineformatting/InlineLine.cpp b/Source/WebCore/layout/inlineformatting/InlineLine.cpp
index 62c327d..ad89be9 100644
--- a/Source/WebCore/layout/inlineformatting/InlineLine.cpp
+++ b/Source/WebCore/layout/inlineformatting/InlineLine.cpp
@@ -378,7 +378,7 @@
void Line::appendNonReplacedInlineBox(const InlineItem& inlineItem, LayoutUnit logicalWidth)
{
auto& boxGeometry = formattingContext().geometryForBox(inlineItem.layoutBox());
- auto horizontalMargin = boxGeometry.horizontalMargin();
+ auto horizontalMargin = boxGeometry.horizontalMargin();
auto logicalRect = Display::Rect { };
logicalRect.setLeft(contentLogicalWidth() + horizontalMargin.start);
@@ -396,8 +396,11 @@
void Line::appendReplacedInlineBox(const InlineItem& inlineItem, LayoutUnit logicalWidth)
{
- // FIXME Surely replaced boxes behave differently.
+ ASSERT(inlineItem.layoutBox().isReplaced());
+ // FIXME: Surely replaced boxes behave differently.
appendNonReplacedInlineBox(inlineItem, logicalWidth);
+ if (auto* replaced = inlineItem.layoutBox().replaced(); replaced && replaced->cachedImage())
+ m_runList.last()->m_displayRun.setImage(*replaced->cachedImage());
}
void Line::appendHardLineBreak(const InlineItem& inlineItem)
diff --git a/Source/WebCore/layout/layouttree/LayoutReplaced.h b/Source/WebCore/layout/layouttree/LayoutReplaced.h
index a1311ad..13f93a6 100644
--- a/Source/WebCore/layout/layouttree/LayoutReplaced.h
+++ b/Source/WebCore/layout/layouttree/LayoutReplaced.h
@@ -27,6 +27,7 @@
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+#include "CachedImage.h"
#include "LayoutSize.h"
#include "LayoutUnit.h"
#include <wtf/IsoMalloc.h>
@@ -44,6 +45,9 @@
Replaced(const Box&);
~Replaced() = default;
+ void setCachedImage(CachedImage& cachedImage) { m_cachedImage = &cachedImage; }
+ CachedImage* cachedImage() const { return m_cachedImage; }
+
// FIXME: Temporary until after intrinsic size change is tracked internallys.
void setIntrinsicSize(LayoutSize size) { m_intrinsicSize = size; }
void setIntrinsicRatio(LayoutUnit ratio) { m_intrinsicRatio = ratio; };
@@ -61,6 +65,7 @@
WeakPtr<const Box> m_layoutBox;
Optional<LayoutSize> m_intrinsicSize;
Optional<LayoutUnit> m_intrinsicRatio;
+ CachedImage* m_cachedImage { nullptr };
};
}
diff --git a/Source/WebCore/layout/layouttree/LayoutTreeBuilder.cpp b/Source/WebCore/layout/layouttree/LayoutTreeBuilder.cpp
index e5b588b..1848bc7 100644
--- a/Source/WebCore/layout/layouttree/LayoutTreeBuilder.cpp
+++ b/Source/WebCore/layout/layouttree/LayoutTreeBuilder.cpp
@@ -28,6 +28,7 @@
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+#include "CachedImage.h"
#include "DisplayBox.h"
#include "DisplayRun.h"
#include "HTMLTableCellElement.h"
@@ -166,6 +167,8 @@
auto& imageRenderer = downcast<RenderImage>(renderer);
if (imageRenderer.shouldDisplayBrokenImageIcon())
childLayoutBox->replaced()->setIntrinsicRatio(1);
+ if (imageRenderer.cachedImage())
+ childLayoutBox->replaced()->setCachedImage(*imageRenderer.cachedImage());
}
} else {
if (displayType == DisplayType::Block) {