blob: 92a9afbc4fd8d33472bc0eeb03351418de1dcdd8 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "config.h"
#include "core/paint/InlinePainter.h"
#include "core/layout/LayoutBlock.h"
#include "core/layout/LayoutInline.h"
#include "core/layout/LayoutTheme.h"
#include "core/layout/line/RootInlineBox.h"
#include "core/paint/BoxPainter.h"
#include "core/paint/LayoutObjectDrawingRecorder.h"
#include "core/paint/LineBoxListPainter.h"
#include "core/paint/ObjectPainter.h"
#include "core/paint/PaintInfo.h"
#include "platform/geometry/LayoutPoint.h"
#include <limits>
namespace blink {
void InlinePainter::paint(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
// FIXME: When Skia supports annotation rect covering (https://code.google.com/p/skia/issues/detail?id=3872),
// this rect may be covered by foreground and descendant drawings. Then we may need a dedicated paint phase.
if (paintInfo.phase == PaintPhaseForeground && paintInfo.context->printing())
ObjectPainter(m_layoutInline).addPDFURLRectIfNeeded(paintInfo, paintOffset);
LineBoxListPainter(*m_layoutInline.lineBoxes()).paint(&m_layoutInline, paintInfo, paintOffset);
}
LayoutRect InlinePainter::outlinePaintRect(const Vector<LayoutRect>& outlineRects, const LayoutPoint& paintOffset) const
{
int outlineOutset = m_layoutInline.styleRef().outlineOutset();
LayoutRect outlineRect;
for (const LayoutRect& rect : outlineRects) {
LayoutRect inflatedRect(rect);
// Inflate the individual rects instead of the union, to avoid losing
// rects which have degenerate width/height (== isEmpty() true.)
inflatedRect.inflate(outlineOutset);
outlineRect.unite(inflatedRect);
}
outlineRect.moveBy(paintOffset);
return outlineRect;
}
void InlinePainter::paintOutline(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
const ComputedStyle& styleToUse = m_layoutInline.styleRef();
if (!styleToUse.hasOutline())
return;
if (styleToUse.outlineStyleIsAuto()) {
if (LayoutTheme::theme().shouldDrawDefaultFocusRing(&m_layoutInline)) {
Vector<LayoutRect> focusRingRects;
m_layoutInline.addFocusRingRects(focusRingRects, paintOffset);
LayoutObjectDrawingRecorder recorder(*paintInfo.context, m_layoutInline, paintInfo.phase, outlinePaintRect(focusRingRects, LayoutPoint()));
if (recorder.canUseCachedDrawing())
return;
// Only paint the focus ring by hand if the theme isn't able to draw the focus ring.
ObjectPainter(m_layoutInline).paintFocusRing(paintInfo, styleToUse, focusRingRects);
}
return;
}
if (styleToUse.outlineStyle() == BNONE)
return;
Vector<LayoutRect> rects;
rects.append(LayoutRect());
for (InlineFlowBox* curr = m_layoutInline.firstLineBox(); curr; curr = curr->nextLineBox()) {
RootInlineBox& root = curr->root();
LayoutUnit top = std::max<LayoutUnit>(root.lineTop(), curr->logicalTop());
LayoutUnit bottom = std::min<LayoutUnit>(root.lineBottom(), curr->logicalBottom());
rects.append(LayoutRect(curr->x(), top, curr->logicalWidth(), bottom - top));
}
rects.append(LayoutRect());
Color outlineColor = m_layoutInline.resolveColor(styleToUse, CSSPropertyOutlineColor);
bool useTransparencyLayer = outlineColor.hasAlpha();
LayoutObjectDrawingRecorder recorder(*paintInfo.context, m_layoutInline, paintInfo.phase, outlinePaintRect(rects, paintOffset));
if (recorder.canUseCachedDrawing())
return;
GraphicsContext* graphicsContext = paintInfo.context;
if (useTransparencyLayer) {
graphicsContext->beginLayer(static_cast<float>(outlineColor.alpha()) / 255);
outlineColor = Color(outlineColor.red(), outlineColor.green(), outlineColor.blue());
}
for (unsigned i = 1; i < rects.size() - 1; i++)
paintOutlineForLine(graphicsContext, paintOffset, rects.at(i - 1), rects.at(i), rects.at(i + 1), outlineColor);
if (useTransparencyLayer)
graphicsContext->endLayer();
}
void InlinePainter::paintOutlineForLine(GraphicsContext* graphicsContext, const LayoutPoint& paintOffset,
const LayoutRect& lastline, const LayoutRect& thisline, const LayoutRect& nextline, const Color outlineColor)
{
const ComputedStyle& styleToUse = m_layoutInline.styleRef();
int outlineWidth = styleToUse.outlineWidth();
EBorderStyle outlineStyle = styleToUse.outlineStyle();
bool antialias = BoxPainter::shouldAntialiasLines(graphicsContext);
int offset = m_layoutInline.style()->outlineOffset();
LayoutRect box(LayoutPoint(paintOffset.x() + thisline.x() - offset, paintOffset.y() + thisline.y() - offset),
LayoutSize(thisline.width() + offset, thisline.height() + offset));
IntRect pixelSnappedBox = pixelSnappedIntRect(box);
if (pixelSnappedBox.width() < 0 || pixelSnappedBox.height() < 0)
return;
// Note that we use IntRect below for working with solely x/width values, simplifying logic at cost of a bit of memory.
IntRect pixelSnappedLastLine = pixelSnappedIntRect(paintOffset.x() + lastline.x() - offset, 0, lastline.width() + offset, 0);
IntRect pixelSnappedNextLine = pixelSnappedIntRect(paintOffset.x() + nextline.x() - offset, 0, nextline.width() + offset, 0);
const int fallbackMaxOutlineX = std::numeric_limits<int>::max();
const int fallbackMinOutlineX = std::numeric_limits<int>::min();
// left edge
ObjectPainter::drawLineForBoxSide(graphicsContext,
pixelSnappedBox.x() - outlineWidth,
pixelSnappedBox.y() - (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.maxX() - 1) <= thisline.x() ? outlineWidth : 0),
pixelSnappedBox.x(),
pixelSnappedBox.maxY() + (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.maxX() - 1) <= thisline.x() ? outlineWidth : 0),
BSLeft,
outlineColor, outlineStyle,
(lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.maxX() - 1) <= thisline.x() ? outlineWidth : -outlineWidth),
(nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.maxX() - 1) <= thisline.x() ? outlineWidth : -outlineWidth),
antialias);
// right edge
ObjectPainter::drawLineForBoxSide(graphicsContext,
pixelSnappedBox.maxX(),
pixelSnappedBox.y() - (lastline.isEmpty() || lastline.maxX() < thisline.maxX() || (thisline.maxX() - 1) <= lastline.x() ? outlineWidth : 0),
pixelSnappedBox.maxX() + outlineWidth,
pixelSnappedBox.maxY() + (nextline.isEmpty() || nextline.maxX() <= thisline.maxX() || (thisline.maxX() - 1) <= nextline.x() ? outlineWidth : 0),
BSRight,
outlineColor, outlineStyle,
(lastline.isEmpty() || lastline.maxX() < thisline.maxX() || (thisline.maxX() - 1) <= lastline.x() ? outlineWidth : -outlineWidth),
(nextline.isEmpty() || nextline.maxX() <= thisline.maxX() || (thisline.maxX() - 1) <= nextline.x() ? outlineWidth : -outlineWidth),
antialias);
// upper edge
if (thisline.x() < lastline.x()) {
ObjectPainter::drawLineForBoxSide(graphicsContext,
pixelSnappedBox.x() - outlineWidth,
pixelSnappedBox.y() - outlineWidth,
std::min(pixelSnappedBox.maxX() + outlineWidth, (lastline.isEmpty() ? fallbackMaxOutlineX : pixelSnappedLastLine.x())),
pixelSnappedBox.y(),
BSTop, outlineColor, outlineStyle,
outlineWidth,
(!lastline.isEmpty() && paintOffset.x() + lastline.x() + 1 < pixelSnappedBox.maxX() + outlineWidth) ? -outlineWidth : outlineWidth,
antialias);
}
if (lastline.maxX() < thisline.maxX()) {
ObjectPainter::drawLineForBoxSide(graphicsContext,
std::max(lastline.isEmpty() ? fallbackMinOutlineX : pixelSnappedLastLine.maxX(), pixelSnappedBox.x() - outlineWidth),
pixelSnappedBox.y() - outlineWidth,
pixelSnappedBox.maxX() + outlineWidth,
pixelSnappedBox.y(),
BSTop, outlineColor, outlineStyle,
(!lastline.isEmpty() && pixelSnappedBox.x() - outlineWidth < paintOffset.x() + lastline.maxX()) ? -outlineWidth : outlineWidth,
outlineWidth, antialias);
}
if (thisline.x() == thisline.maxX()) {
ObjectPainter::drawLineForBoxSide(graphicsContext,
pixelSnappedBox.x() - outlineWidth,
pixelSnappedBox.y() - outlineWidth,
pixelSnappedBox.maxX() + outlineWidth,
pixelSnappedBox.y(),
BSTop, outlineColor, outlineStyle,
outlineWidth,
outlineWidth,
antialias);
}
// lower edge
if (thisline.x() < nextline.x()) {
ObjectPainter::drawLineForBoxSide(graphicsContext,
pixelSnappedBox.x() - outlineWidth,
pixelSnappedBox.maxY(),
std::min(pixelSnappedBox.maxX() + outlineWidth, !nextline.isEmpty() ? pixelSnappedNextLine.x() + 1 : fallbackMaxOutlineX),
pixelSnappedBox.maxY() + outlineWidth,
BSBottom, outlineColor, outlineStyle,
outlineWidth,
(!nextline.isEmpty() && paintOffset.x() + nextline.x() + 1 < pixelSnappedBox.maxX() + outlineWidth) ? -outlineWidth : outlineWidth,
antialias);
}
if (nextline.maxX() < thisline.maxX()) {
ObjectPainter::drawLineForBoxSide(graphicsContext,
std::max(!nextline.isEmpty() ? pixelSnappedNextLine.maxX() : fallbackMinOutlineX, pixelSnappedBox.x() - outlineWidth),
pixelSnappedBox.maxY(),
pixelSnappedBox.maxX() + outlineWidth,
pixelSnappedBox.maxY() + outlineWidth,
BSBottom, outlineColor, outlineStyle,
(!nextline.isEmpty() && pixelSnappedBox.x() - outlineWidth < paintOffset.x() + nextline.maxX()) ? -outlineWidth : outlineWidth,
outlineWidth, antialias);
}
if (thisline.x() == thisline.maxX()) {
ObjectPainter::drawLineForBoxSide(graphicsContext,
pixelSnappedBox.x() - outlineWidth,
pixelSnappedBox.maxY(),
pixelSnappedBox.maxX() + outlineWidth,
pixelSnappedBox.maxY() + outlineWidth,
BSBottom, outlineColor, outlineStyle,
outlineWidth,
outlineWidth,
antialias);
}
}
} // namespace blink