summaryrefslogtreecommitdiff
path: root/Source/WebCore/rendering/SimpleLineLayoutFunctions.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/rendering/SimpleLineLayoutFunctions.cpp')
-rw-r--r--Source/WebCore/rendering/SimpleLineLayoutFunctions.cpp250
1 files changed, 176 insertions, 74 deletions
diff --git a/Source/WebCore/rendering/SimpleLineLayoutFunctions.cpp b/Source/WebCore/rendering/SimpleLineLayoutFunctions.cpp
index 346879569..12a903647 100644
--- a/Source/WebCore/rendering/SimpleLineLayoutFunctions.cpp
+++ b/Source/WebCore/rendering/SimpleLineLayoutFunctions.cpp
@@ -35,28 +35,48 @@
#include "InlineTextBox.h"
#include "PaintInfo.h"
#include "RenderBlockFlow.h"
+#include "RenderIterator.h"
#include "RenderStyle.h"
#include "RenderText.h"
#include "RenderView.h"
#include "Settings.h"
#include "SimpleLineLayoutResolver.h"
#include "Text.h"
+#include "TextDecorationPainter.h"
#include "TextPaintStyle.h"
-#include <wtf/unicode/Unicode.h>
+#include "TextPainter.h"
+
+#if ENABLE(TREE_DEBUGGING)
+#include <stdio.h>
+#endif
namespace WebCore {
namespace SimpleLineLayout {
-static void paintDebugBorders(GraphicsContext& context, const LayoutRect& borderRect, const LayoutPoint& paintOffset)
+static void paintDebugBorders(GraphicsContext& context, LayoutRect borderRect, const LayoutPoint& paintOffset)
{
- if (borderRect.isEmpty())
+ borderRect.moveBy(paintOffset);
+ IntRect snappedRect = snappedIntRect(borderRect);
+ if (snappedRect.isEmpty())
return;
GraphicsContextStateSaver stateSaver(context);
- context.setStrokeColor(Color(0, 255, 0), ColorSpaceDeviceRGB);
- context.setFillColor(Color::transparent, ColorSpaceDeviceRGB);
- LayoutRect rect(borderRect);
- rect.moveBy(paintOffset);
- context.drawRect(pixelSnappedIntRect(rect));
+ context.setStrokeColor(Color(0, 255, 0));
+ context.setFillColor(Color::transparent);
+ context.drawRect(snappedRect);
+}
+
+static FloatRect computeOverflow(const RenderBlockFlow& flow, const FloatRect& layoutRect)
+{
+ auto overflowRect = layoutRect;
+ auto strokeOverflow = std::ceil(flow.style().textStrokeWidth());
+ overflowRect.inflate(strokeOverflow);
+
+ auto letterSpacing = flow.style().fontCascade().letterSpacing();
+ if (letterSpacing >= 0)
+ return overflowRect;
+ // Last letter's negative spacing shrinks layout rect. Push it to visual overflow.
+ overflowRect.expand(-letterSpacing, 0);
+ return overflowRect;
}
void paintFlow(const RenderBlockFlow& flow, const Layout& layout, PaintInfo& paintInfo, const LayoutPoint& paintOffset)
@@ -64,38 +84,54 @@ void paintFlow(const RenderBlockFlow& flow, const Layout& layout, PaintInfo& pai
if (paintInfo.phase != PaintPhaseForeground)
return;
- RenderStyle& style = flow.style();
+ auto& style = flow.style();
if (style.visibility() != VISIBLE)
return;
- RenderText& textRenderer = toRenderText(*flow.firstChild());
- ASSERT(!textRenderer.firstTextBox());
+ bool debugBordersEnabled = flow.settings().simpleLineLayoutDebugBordersEnabled();
- bool debugBordersEnabled = flow.frame().settings().simpleLineLayoutDebugBordersEnabled();
+ TextPainter textPainter(paintInfo.context());
+ textPainter.setFont(style.fontCascade());
+ textPainter.setTextPaintStyle(computeTextPaintStyle(flow.frame(), style, paintInfo));
- GraphicsContext& context = *paintInfo.context;
-
- const Font& font = style.font();
- TextPaintStyle textPaintStyle = computeTextPaintStyle(textRenderer, style, paintInfo);
- GraphicsContextStateSaver stateSaver(context, textPaintStyle.strokeWidth > 0);
-
- updateGraphicsContext(context, textPaintStyle);
- LayoutPoint adjustedPaintOffset = roundedIntPoint(paintOffset);
+ std::optional<TextDecorationPainter> textDecorationPainter;
+ if (style.textDecorationsInEffect() != TextDecorationNone) {
+ const RenderText* textRenderer = childrenOfType<RenderText>(flow).first();
+ if (textRenderer) {
+ textDecorationPainter.emplace(paintInfo.context(), style.textDecorationsInEffect(), *textRenderer, false);
+ textDecorationPainter->setFont(style.fontCascade());
+ textDecorationPainter->setBaseline(style.fontMetrics().ascent());
+ }
+ }
LayoutRect paintRect = paintInfo.rect;
- paintRect.moveBy(-adjustedPaintOffset);
+ paintRect.moveBy(-paintOffset);
auto resolver = runResolver(flow, layout);
- auto range = resolver.rangeForRect(paintRect);
- for (auto it = range.begin(), end = range.end(); it != end; ++it) {
- const auto& run = *it;
- if (!run.rect().intersects(paintRect))
+ float deviceScaleFactor = flow.document().deviceScaleFactor();
+ for (auto run : resolver.rangeForRect(paintRect)) {
+ if (run.start() == run.end())
+ continue;
+
+ FloatRect rect = run.rect();
+ FloatRect visualOverflowRect = computeOverflow(flow, rect);
+ if (paintRect.y() > visualOverflowRect.maxY() || paintRect.maxY() < visualOverflowRect.y())
continue;
- TextRun textRun(run.text());
+
+ String textWithHyphen;
+ if (run.hasHyphen())
+ textWithHyphen = run.textWithHyphen();
+ // x position indicates the line offset from the rootbox. It's always 0 in case of simple line layout.
+ TextRun textRun(run.hasHyphen() ? textWithHyphen : run.text(), 0, run.expansion(), run.expansionBehavior());
textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
- context.drawText(font, textRun, run.baseline() + adjustedPaintOffset);
+ FloatPoint textOrigin = FloatPoint(rect.x() + paintOffset.x(), roundToDevicePixel(run.baselinePosition() + paintOffset.y(), deviceScaleFactor));
+ textPainter.paintText(textRun, textRun.length(), rect, textOrigin);
+ if (textDecorationPainter) {
+ textDecorationPainter->setWidth(rect.width());
+ textDecorationPainter->paintTextDecoration(textRun, textOrigin, rect.location() + paintOffset);
+ }
if (debugBordersEnabled)
- paintDebugBorders(context, run.rect(), adjustedPaintOffset);
+ paintDebugBorders(paintInfo.context(), LayoutRect(run.rect()), paintOffset);
}
}
@@ -107,90 +143,156 @@ bool hitTestFlow(const RenderBlockFlow& flow, const Layout& layout, const HitTes
if (!layout.runCount())
return false;
- RenderStyle& style = flow.style();
+ auto& style = flow.style();
if (style.visibility() != VISIBLE || style.pointerEvents() == PE_NONE)
return false;
- RenderText& textRenderer = toRenderText(*flow.firstChild());
-
LayoutRect rangeRect = locationInContainer.boundingBox();
rangeRect.moveBy(-accumulatedOffset);
-
auto resolver = lineResolver(flow, layout);
auto range = resolver.rangeForRect(rangeRect);
for (auto it = range.begin(), end = range.end(); it != end; ++it) {
auto lineRect = *it;
lineRect.moveBy(accumulatedOffset);
+ auto& renderer = const_cast<RenderObject&>(it.renderer());
if (!locationInContainer.intersects(lineRect))
continue;
- textRenderer.updateHitTestResult(result, locationInContainer.point() - toLayoutSize(accumulatedOffset));
- if (!result.addNodeToRectBasedTestResult(textRenderer.textNode(), request, locationInContainer, lineRect))
+ renderer.updateHitTestResult(result, locationInContainer.point() - toLayoutSize(accumulatedOffset));
+ if (!result.addNodeToRectBasedTestResult(renderer.node(), request, locationInContainer, lineRect))
return true;
}
-
return false;
}
void collectFlowOverflow(RenderBlockFlow& flow, const Layout& layout)
{
- auto resolver = lineResolver(flow, layout);
- for (auto it = resolver.begin(), end = resolver.end(); it != end; ++it) {
- auto rect = *it;
- flow.addLayoutOverflow(rect);
- flow.addVisualOverflow(rect);
+ for (auto lineRect : lineResolver(flow, layout)) {
+ LayoutRect visualOverflowRect = LayoutRect(computeOverflow(flow, lineRect));
+ flow.addLayoutOverflow(LayoutRect(lineRect));
+ flow.addVisualOverflow(visualOverflowRect);
}
}
-IntRect computeTextBoundingBox(const RenderText& textRenderer, const Layout& layout)
+IntRect computeBoundingBox(const RenderObject& renderer, const Layout& layout)
{
- auto resolver = lineResolver(toRenderBlockFlow(*textRenderer.parent()), layout);
- auto it = resolver.begin();
- auto end = resolver.end();
- if (it == end)
- return IntRect();
- auto firstLineRect = *it;
- float left = firstLineRect.x();
- float right = firstLineRect.maxX();
- float bottom = firstLineRect.maxY();
- for (++it; it != end; ++it) {
- auto rect = *it;
- if (rect.x() < left)
- left = rect.x();
- if (rect.maxX() > right)
- right = rect.maxX();
- if (rect.maxY() > bottom)
- bottom = rect.maxY();
+ auto resolver = runResolver(downcast<RenderBlockFlow>(*renderer.parent()), layout);
+ FloatRect boundingBoxRect;
+ for (auto run : resolver.rangeForRenderer(renderer)) {
+ FloatRect rect = run.rect();
+ if (boundingBoxRect == FloatRect())
+ boundingBoxRect = rect;
+ else
+ boundingBoxRect.uniteEvenIfEmpty(rect);
}
- float x = firstLineRect.x();
- float y = firstLineRect.y();
- float width = right - left;
- float height = bottom - y;
- return enclosingIntRect(FloatRect(x, y, width, height));
+ return enclosingIntRect(boundingBoxRect);
}
-Vector<IntRect> collectTextAbsoluteRects(const RenderText& textRenderer, const Layout& layout, const LayoutPoint& accumulatedOffset)
+IntPoint computeFirstRunLocation(const RenderObject& renderer, const Layout& layout)
+{
+ auto resolver = runResolver(downcast<RenderBlockFlow>(*renderer.parent()), layout);
+ auto range = resolver.rangeForRenderer(renderer);
+ auto begin = range.begin();
+ if (begin == range.end())
+ return IntPoint(0, 0);
+ return flooredIntPoint((*begin).rect().location());
+}
+
+Vector<IntRect> collectAbsoluteRects(const RenderObject& renderer, const Layout& layout, const LayoutPoint& accumulatedOffset)
{
Vector<IntRect> rects;
- auto resolver = runResolver(toRenderBlockFlow(*textRenderer.parent()), layout);
- for (auto it = resolver.begin(), end = resolver.end(); it != end; ++it) {
- const auto& run = *it;
- auto rect = run.rect();
+ auto resolver = runResolver(downcast<RenderBlockFlow>(*renderer.parent()), layout);
+ for (auto run : resolver.rangeForRenderer(renderer)) {
+ FloatRect rect = run.rect();
rects.append(enclosingIntRect(FloatRect(accumulatedOffset + rect.location(), rect.size())));
}
return rects;
}
-Vector<FloatQuad> collectTextAbsoluteQuads(const RenderText& textRenderer, const Layout& layout, bool* wasFixed)
+Vector<FloatQuad> collectAbsoluteQuads(const RenderObject& renderer, const Layout& layout, bool* wasFixed)
+{
+ Vector<FloatQuad> quads;
+ auto resolver = runResolver(downcast<RenderBlockFlow>(*renderer.parent()), layout);
+ for (auto run : resolver.rangeForRenderer(renderer))
+ quads.append(renderer.localToAbsoluteQuad(FloatQuad(run.rect()), UseTransforms, wasFixed));
+ return quads;
+}
+
+unsigned textOffsetForPoint(const LayoutPoint& point, const RenderText& renderer, const Layout& layout)
+{
+ auto& flow = downcast<RenderBlockFlow>(*renderer.parent());
+ ASSERT(flow.firstChild() == flow.lastChild());
+ auto resolver = runResolver(flow, layout);
+ auto it = resolver.runForPoint(point);
+ if (it == resolver.end())
+ return renderer.textLength();
+ auto run = *it;
+ auto& style = flow.style();
+ TextRun textRun(run.text(), run.logicalLeft(), run.expansion(), run.expansionBehavior());
+ textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
+ return run.start() + style.fontCascade().offsetForPosition(textRun, point.x() - run.logicalLeft(), true);
+}
+
+Vector<FloatQuad> collectAbsoluteQuadsForRange(const RenderObject& renderer, unsigned start, unsigned end, const Layout& layout, bool* wasFixed)
{
+ auto& style = downcast<RenderBlockFlow>(*renderer.parent()).style();
Vector<FloatQuad> quads;
- auto resolver = runResolver(toRenderBlockFlow(*textRenderer.parent()), layout);
- for (auto it = resolver.begin(), end = resolver.end(); it != end; ++it) {
- const auto& run = *it;
- auto rect = run.rect();
- quads.append(textRenderer.localToAbsoluteQuad(FloatQuad(rect), 0, wasFixed));
+ auto resolver = runResolver(downcast<RenderBlockFlow>(*renderer.parent()), layout);
+ for (auto run : resolver.rangeForRendererWithOffsets(renderer, start, end)) {
+ // This run is fully contained.
+ if (start <= run.start() && end >= run.end()) {
+ quads.append(renderer.localToAbsoluteQuad(FloatQuad(run.rect()), UseTransforms, wasFixed));
+ continue;
+ }
+ // Partially contained run.
+ TextRun textRun(run.text(), run.logicalLeft(), run.expansion(), run.expansionBehavior());
+ textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
+ LayoutRect runRect(run.rect());
+ // Special case empty ranges.
+ if (start == end) {
+ runRect.setWidth(0);
+ quads.append(renderer.localToAbsoluteQuad(FloatQuad(runRect), UseTransforms, wasFixed));
+ continue;
+ }
+ ASSERT(start < run.end());
+ ASSERT(end > run.start());
+ auto localStart = std::max(run.start(), start) - run.start();
+ auto localEnd = std::min(run.end(), end) - run.start();
+ style.fontCascade().adjustSelectionRectForText(textRun, runRect, localStart, localEnd);
+ quads.append(renderer.localToAbsoluteQuad(FloatQuad(runRect), UseTransforms, wasFixed));
}
return quads;
}
+#if ENABLE(TREE_DEBUGGING)
+static void printPrefix(int& printedCharacters, int depth)
+{
+ fprintf(stderr, "-------- --");
+ printedCharacters = 0;
+ while (++printedCharacters <= depth * 2)
+ fputc(' ', stderr);
+}
+
+void showLineLayoutForFlow(const RenderBlockFlow& flow, const Layout& layout, int depth)
+{
+ int printedCharacters = 0;
+ printPrefix(printedCharacters, depth);
+
+ fprintf(stderr, "SimpleLineLayout (%u lines, %u runs) (%p)\n", layout.lineCount(), layout.runCount(), &layout);
+ ++depth;
+
+ for (auto run : runResolver(flow, layout)) {
+ FloatRect rect = run.rect();
+ printPrefix(printedCharacters, depth);
+ if (run.start() < run.end()) {
+ fprintf(stderr, "line %u run(%u, %u) (%.2f, %.2f) (%.2f, %.2f) \"%s\"\n", run.lineIndex(), run.start(), run.end(),
+ rect.x(), rect.y(), rect.width(), rect.height(), run.text().toStringWithoutCopying().utf8().data());
+ } else {
+ ASSERT(run.start() == run.end());
+ fprintf(stderr, "line break %u run(%u, %u) (%.2f, %.2f) (%.2f, %.2f)\n", run.lineIndex(), run.start(), run.end(), rect.x(), rect.y(), rect.width(), rect.height());
+ }
+ }
+}
+#endif
+
}
}