TEST_F(PaintPropertyTreeBuilderTest, PositionAndScroll)
{
    loadTestData("position-and-scroll.html");

    Element* scroller = document().getElementById("scroller");
    scroller->scrollTo(0, 100);
    FrameView* frameView = document().view();
    frameView->updateAllLifecyclePhases();
    ObjectPaintProperties* scrollerProperties = scroller->layoutObject()->objectPaintProperties();
    EXPECT_EQ(TransformationMatrix().translate(0, -100), scrollerProperties->scrollTranslation()->matrix());
    EXPECT_EQ(frameView->scrollTranslation(), scrollerProperties->scrollTranslation()->parent());
    EXPECT_EQ(frameView->scrollTranslation(), scrollerProperties->overflowClip()->localTransformSpace());
    EXPECT_EQ(FloatRoundedRect(120, 340, 400, 300), scrollerProperties->overflowClip()->clipRect());
    EXPECT_EQ(frameView->contentClip(), scrollerProperties->overflowClip()->parent());

    // The relative-positioned element should have accumulated box offset (exclude scrolling),
    // and should be affected by ancestor scroll transforms.
    Element* relPos = document().getElementById("rel-pos");
    ObjectPaintProperties* relPosProperties = relPos->layoutObject()->objectPaintProperties();
    EXPECT_EQ(TransformationMatrix().translate(680, 1120), relPosProperties->paintOffsetTranslation()->matrix());
    EXPECT_EQ(scrollerProperties->scrollTranslation(), relPosProperties->paintOffsetTranslation()->parent());
    EXPECT_EQ(relPosProperties->transform(), relPosProperties->overflowClip()->localTransformSpace());
    EXPECT_EQ(FloatRoundedRect(0, 0, 400, 0), relPosProperties->overflowClip()->clipRect());
    EXPECT_EQ(scrollerProperties->overflowClip(), relPosProperties->overflowClip()->parent());

    // The absolute-positioned element should not be affected by non-positioned scroller at all.
    Element* absPos = document().getElementById("abs-pos");
    ObjectPaintProperties* absPosProperties = absPos->layoutObject()->objectPaintProperties();
    EXPECT_EQ(TransformationMatrix().translate(123, 456), absPosProperties->paintOffsetTranslation()->matrix());
    EXPECT_EQ(frameView->scrollTranslation(), absPosProperties->paintOffsetTranslation()->parent());
    EXPECT_EQ(absPosProperties->transform(), absPosProperties->overflowClip()->localTransformSpace());
    EXPECT_EQ(FloatRoundedRect(), absPosProperties->overflowClip()->clipRect());
    EXPECT_EQ(frameView->contentClip(), absPosProperties->overflowClip()->parent());
}
TEST_F(PaintPropertyTreeBuilderTest, FixedPosition)
{
    loadTestData("fixed-position.html");

    FrameView* frameView = document().view();

    // target1 is a fixed-position element inside an absolute-position scrolling element.
    // It should be attached under the viewport to skip scrolling and offset of the parent.
    Element* target1 = document().getElementById("target1");
    ObjectPaintProperties* target1Properties = target1->layoutObject()->objectPaintProperties();
    EXPECT_EQ(TransformationMatrix().translate(200, 150), target1Properties->paintOffsetTranslation()->matrix());
    EXPECT_EQ(frameView->preTranslation(), target1Properties->paintOffsetTranslation()->parent());
    EXPECT_EQ(target1Properties->paintOffsetTranslation(), target1Properties->overflowClip()->localTransformSpace());
    EXPECT_EQ(FloatRoundedRect(0, 0, 100, 100), target1Properties->overflowClip()->clipRect());
    // Likewise, it inherits clip from the viewport, skipping overflow clip of the scroller.
    EXPECT_EQ(frameView->contentClip(), target1Properties->overflowClip()->parent());

    // target2 is a fixed-position element inside a transformed scrolling element.
    // It should be attached under the scrolled box of the transformed element.
    Element* target2 = document().getElementById("target2");
    ObjectPaintProperties* target2Properties = target2->layoutObject()->objectPaintProperties();
    Element* scroller = document().getElementById("scroller");
    ObjectPaintProperties* scrollerProperties = scroller->layoutObject()->objectPaintProperties();
    EXPECT_EQ(TransformationMatrix().translate(200, 150), target2Properties->paintOffsetTranslation()->matrix());
    EXPECT_EQ(scrollerProperties->scrollTranslation(), target2Properties->paintOffsetTranslation()->parent());
    EXPECT_EQ(target2Properties->paintOffsetTranslation(), target2Properties->overflowClip()->localTransformSpace());
    EXPECT_EQ(FloatRoundedRect(0, 0, 100, 100), target2Properties->overflowClip()->clipRect());
    EXPECT_EQ(scrollerProperties->overflowClip(), target2Properties->overflowClip()->parent());
}
static void paintSliderRangeHighlight(const IntRect& rect, const ComputedStyle& style, GraphicsContext* context, int startPosition, int endPosition, Color startColor, Color endColor)
{
    // Calculate border radius; need to avoid being smaller than half the slider height
    // because of https://bugs.webkit.org/show_bug.cgi?id=30143.
    float borderRadius = rect.height() / 2.0f;
    FloatSize radii(borderRadius, borderRadius);

    // Calculate highlight rectangle and edge dimensions.
    int startOffset = startPosition;
    int endOffset = rect.width() - endPosition;
    int rangeWidth = endPosition - startPosition;

    if (rangeWidth <= 0)
        return;

    // Make sure the range width is bigger than border radius at the edges to retain rounded corners.
    if (startOffset < borderRadius && rangeWidth < borderRadius)
        rangeWidth = borderRadius;
    if (endOffset < borderRadius && rangeWidth < borderRadius)
        rangeWidth = borderRadius;

    // Set rectangle to highlight range.
    IntRect highlightRect = rect;
    highlightRect.move(startOffset, 0);
    highlightRect.setWidth(rangeWidth);

    // Don't bother drawing an empty area.
    if (highlightRect.isEmpty())
        return;

    // Calculate white-grey gradient.
    FloatPoint sliderTopLeft = highlightRect.location();
    FloatPoint sliderBottomLeft = sliderTopLeft;
    sliderBottomLeft.move(0, highlightRect.height());
    RefPtr<Gradient> gradient = Gradient::create(sliderTopLeft, sliderBottomLeft);
    gradient->addColorStop(0.0, startColor);
    gradient->addColorStop(1.0, endColor);

    // Fill highlight rectangle with gradient, potentially rounded if on left or right edge.
    context->save();
    context->setFillGradient(gradient);

    if (startOffset < borderRadius && endOffset < borderRadius)
        context->fillRoundedRect(FloatRoundedRect(highlightRect, radii, radii, radii, radii), startColor);
    else if (startOffset < borderRadius)
        context->fillRoundedRect(FloatRoundedRect(highlightRect, radii, FloatSize(0, 0), radii, FloatSize(0, 0)), startColor);
    else if (endOffset < borderRadius)
        context->fillRoundedRect(FloatRoundedRect(highlightRect, FloatSize(0, 0), radii, FloatSize(0, 0), radii), startColor);
    else
        context->fillRect(highlightRect);

    context->restore();
}
Exemple #4
0
void Path::addRoundedRect(const FloatRect& rect, const FloatSize& roundingRadii, RoundedRectStrategy strategy)
{
    if (rect.isEmpty())
        return;

    FloatSize radius(roundingRadii);
    FloatSize halfSize(rect.width() / 2, rect.height() / 2);

    // Apply the SVG corner radius constraints, per the rect section of the SVG shapes spec: if
    // one of rx,ry is negative, then the other corner radius value is used. If both values are
    // negative then rx = ry = 0. If rx is greater than half of the width of the rectangle
    // then set rx to half of the width; ry is handled similarly.

    if (radius.width() < 0)
        radius.setWidth((radius.height() < 0) ? 0 : radius.height());

    if (radius.height() < 0)
        radius.setHeight(radius.width());

    if (radius.width() > halfSize.width())
        radius.setWidth(halfSize.width());

    if (radius.height() > halfSize.height())
        radius.setHeight(halfSize.height());

    addRoundedRect(FloatRoundedRect(rect, radius, radius, radius, radius), strategy);
}
TEST(FloatRoundedRectTest, zeroRadii)
{
    FloatRoundedRect r = FloatRoundedRect(1, 2, 3, 4);

    EXPECT_EQ(FloatRect(1, 2, 3, 4), r.rect());
    EXPECT_EQ(FloatSize(), r.radii().topLeft());
    EXPECT_EQ(FloatSize(), r.radii().topRight());
    EXPECT_EQ(FloatSize(), r.radii().bottomLeft());
    EXPECT_EQ(FloatSize(), r.radii().bottomRight());
    EXPECT_TRUE(r.radii().isZero());
    EXPECT_FALSE(r.isRounded());
    EXPECT_FALSE(r.isEmpty());

    EXPECT_EQ(FloatRect(1, 2, 0, 0), r.topLeftCorner());
    EXPECT_EQ(FloatRect(4, 2, 0, 0), r.topRightCorner());
    EXPECT_EQ(FloatRect(4, 6, 0, 0), r.bottomRightCorner());
    EXPECT_EQ(FloatRect(1, 6, 0, 0), r.bottomLeftCorner());

    TEST_INTERCEPTS(r, 2, r.rect().x(), r.rect().maxX());
    TEST_INTERCEPTS(r, 4, r.rect().x(), r.rect().maxX());
    TEST_INTERCEPTS(r, 6, r.rect().x(), r.rect().maxX());

    float minXIntercept;
    float maxXIntercept;

    EXPECT_FALSE(r.xInterceptsAtY(1, minXIntercept, maxXIntercept));
    EXPECT_FALSE(r.xInterceptsAtY(7, minXIntercept, maxXIntercept));

    // The FloatRoundedRect::expandRadii() function doesn't change radii FloatSizes that
    // are <= zero. Same as RoundedRect::expandRadii().
    r.expandRadii(20);
    r.shrinkRadii(10);
    EXPECT_TRUE(r.radii().isZero());
}
/*
 * FloatRoundedRect geometry for this test. Corner radii are in parens, x and y intercepts
 * for the elliptical corners are noted. The rectangle itself is at 0,0 with width and height 100.
 *
 *         (10, 15)  x=10      x=90 (10, 20)
 *                (--+---------+--)
 *           y=15 +--|         |-+ y=20
 *                |               |
 *                |               |
 *           y=85 + -|         |- + y=70
 *                (--+---------+--)
 *       (25, 15)  x=25      x=80  (20, 30)
 */
TEST(FloatRoundedRectTest, ellipticalCorners)
{
    FloatSize cornerSize(10, 20);
    FloatRoundedRect::Radii cornerRadii;
    cornerRadii.setTopLeft(FloatSize(10, 15));
    cornerRadii.setTopRight(FloatSize(10, 20));
    cornerRadii.setBottomLeft(FloatSize(25, 15));
    cornerRadii.setBottomRight(FloatSize(20, 30));

    FloatRoundedRect r(FloatRect(0, 0, 100, 100), cornerRadii);

    EXPECT_EQ(r.radii(), FloatRoundedRect::Radii(FloatSize(10, 15), FloatSize(10, 20), FloatSize(25, 15), FloatSize(20, 30)));
    EXPECT_EQ(r, FloatRoundedRect(FloatRect(0, 0, 100, 100), cornerRadii));

    EXPECT_EQ(FloatRect(0, 0, 10, 15), r.topLeftCorner());
    EXPECT_EQ(FloatRect(90, 0, 10, 20), r.topRightCorner());
    EXPECT_EQ(FloatRect(0, 85, 25, 15), r.bottomLeftCorner());
    EXPECT_EQ(FloatRect(80, 70, 20, 30), r.bottomRightCorner());

    TEST_INTERCEPTS(r, 5, 2.5464401, 96.61438);
    TEST_INTERCEPTS(r, 15, 0, 99.682457);
    TEST_INTERCEPTS(r, 20, 0, 100);
    TEST_INTERCEPTS(r, 50, 0, 100);
    TEST_INTERCEPTS(r, 70, 0, 100);
    TEST_INTERCEPTS(r, 85, 0, 97.320511);
    TEST_INTERCEPTS(r, 95, 6.3661003, 91.05542);

    float minXIntercept;
    float maxXIntercept;

    EXPECT_FALSE(r.xInterceptsAtY(-1, minXIntercept, maxXIntercept));
    EXPECT_FALSE(r.xInterceptsAtY(101, minXIntercept, maxXIntercept));
}
bool RenderThemeGtk::paintMediaSliderTrack(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
{
    HTMLMediaElement* mediaElement = parentMediaElement(o);
    if (!mediaElement)
        return false;

    GraphicsContext* context = paintInfo.context;
    context->save();
    context->setStrokeStyle(NoStroke);

    float mediaDuration = mediaElement->duration();
    float totalTrackWidth = r.width();
    RenderStyle* style = &o.style();
    RefPtr<TimeRanges> timeRanges = mediaElement->buffered();
    for (unsigned index = 0; index < timeRanges->length(); ++index) {
        float start = timeRanges->start(index, IGNORE_EXCEPTION);
        float end = timeRanges->end(index, IGNORE_EXCEPTION);
        float startRatio = start / mediaDuration;
        float lengthRatio = (end - start) / mediaDuration;
        if (!lengthRatio)
            continue;

        IntRect rangeRect(r);
        rangeRect.setWidth(lengthRatio * totalTrackWidth);
        if (index)
            rangeRect.move(startRatio * totalTrackWidth, 0);
        context->fillRoundedRect(FloatRoundedRect(rangeRect, borderRadiiFromStyle(style)), style->visitedDependentColor(CSSPropertyColor), style->colorSpace());
    }

    context->restore();
    return false;
}
bool RenderThemeGtk::paintMediaVolumeSliderTrack(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
{
    HTMLMediaElement* mediaElement = parentMediaElement(renderObject);
    if (!mediaElement)
        return true;

    float volume = mediaElement->muted() ? 0.0f : mediaElement->volume();
    if (!volume)
        return true;

    GraphicsContext* context = paintInfo.context;
    context->save();
    context->setStrokeStyle(NoStroke);

    int rectHeight = rect.height();
    float trackHeight = rectHeight * volume;
    RenderStyle* style = &renderObject.style();
    IntRect volumeRect(rect);
    volumeRect.move(0, rectHeight - trackHeight);
    volumeRect.setHeight(ceil(trackHeight));

    context->fillRoundedRect(FloatRoundedRect(volumeRect, borderRadiiFromStyle(style)),
        style->visitedDependentColor(CSSPropertyColor), style->colorSpace());
    context->restore();

    return false;
}
void PaintPropertyTreeBuilder::updateOverflowClip(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
{
    if (!object.isBox())
        return;
    const LayoutBox& box = toLayoutBox(object);

    // The <input> elements can't have contents thus CSS overflow property doesn't apply.
    // However for layout purposes we do generate child layout objects for them, e.g. button label.
    // We should clip the overflow from those children. This is called control clip and we
    // technically treat them like overflow clip.
    LayoutRect clipRect;
    if (box.hasControlClip())
        clipRect = box.controlClipRect(context.paintOffset);
    else if (box.hasOverflowClip())
        clipRect = box.overflowClipRect(context.paintOffset);
    else
        return;

    RefPtr<ClipPaintPropertyNode> borderRadiusClip;
    if (box.styleRef().hasBorderRadius()) {
        auto innerBorder = box.styleRef().getRoundedInnerBorderFor(
            LayoutRect(context.paintOffset, box.size()));
        borderRadiusClip = ClipPaintPropertyNode::create(
            context.currentTransform, innerBorder, context.currentClip);
    }

    RefPtr<ClipPaintPropertyNode> overflowClip = ClipPaintPropertyNode::create(
        context.currentTransform,
        FloatRoundedRect(FloatRect(clipRect)),
        borderRadiusClip ? borderRadiusClip.release() : context.currentClip);
    context.currentClip = overflowClip.get();
    object.getMutableForPainting().ensureObjectPaintProperties().setOverflowClip(overflowClip.release());
}
static PassRefPtr<ClipPaintPropertyNode> createOverflowClipIfNeeded(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
{
    if (!object.isBox())
        return nullptr;
    const LayoutBox& box = toLayoutBox(object);

    // The <input> elements can't have contents thus CSS overflow property doesn't apply.
    // However for layout purposes we do generate child layout objects for them, e.g. button label.
    // We should clip the overflow from those children. This is called control clip and we
    // technically treat them like overflow clip.
    LayoutRect clipRect;
    if (box.hasControlClip())
        clipRect = box.controlClipRect(context.paintOffset);
    else if (box.hasOverflowClip())
        clipRect = box.overflowClipRect(context.paintOffset);
    else
        return nullptr;

    RefPtr<ClipPaintPropertyNode> newClipNodeForBorderRadiusClip;
    const ComputedStyle& style = box.styleRef();
    if (style.hasBorderRadius()) {
        newClipNodeForBorderRadiusClip = ClipPaintPropertyNode::create(
            context.currentTransform,
            style.getRoundedInnerBorderFor(LayoutRect(context.paintOffset, box.size())),
            context.currentClip);
    }

    RefPtr<ClipPaintPropertyNode> newClipNodeForOverflowClip = ClipPaintPropertyNode::create(
        context.currentTransform,
        FloatRoundedRect(FloatRect(clipRect)),
        newClipNodeForBorderRadiusClip ? newClipNodeForBorderRadiusClip.release() : context.currentClip);
    context.currentClip = newClipNodeForOverflowClip.get();
    return newClipNodeForOverflowClip.release();
}
static void paintRoundedSliderBackground(const IntRect& rect, const ComputedStyle& style, GraphicsContext* context, Color sliderBackgroundColor )
{
    float borderRadius = rect.height() / 2;
    FloatSize radii(borderRadius, borderRadius);

    context->fillRoundedRect(FloatRoundedRect(rect, radii, radii, radii, radii), sliderBackgroundColor);
}
RoundedInnerRectClipper::RoundedInnerRectClipper(LayoutObject& layoutObject, const PaintInfo& paintInfo, const LayoutRect& rect, const FloatRoundedRect& clipRect, RoundedInnerRectClipperBehavior behavior)
    : m_layoutObject(layoutObject)
    , m_paintInfo(paintInfo)
    , m_useDisplayItemList(RuntimeEnabledFeatures::slimmingPaintEnabled() && behavior == ApplyToDisplayListIfEnabled)
    , m_clipType(m_useDisplayItemList ? m_paintInfo.displayItemTypeForClipping() : DisplayItem::ClipBoxPaintPhaseFirst)
{
    Vector<FloatRoundedRect> roundedRectClips;
    if (clipRect.isRenderable()) {
        roundedRectClips.append(clipRect);
    } else {
        // We create a rounded rect for each of the corners and clip it, while making sure we clip opposing corners together.
        if (!clipRect.radii().topLeft().isEmpty() || !clipRect.radii().bottomRight().isEmpty()) {
            FloatRect topCorner(clipRect.rect().x(), clipRect.rect().y(), rect.maxX() - clipRect.rect().x(), rect.maxY() - clipRect.rect().y());
            FloatRoundedRect::Radii topCornerRadii;
            topCornerRadii.setTopLeft(clipRect.radii().topLeft());
            roundedRectClips.append(FloatRoundedRect(topCorner, topCornerRadii));

            FloatRect bottomCorner(rect.x().toFloat(), rect.y().toFloat(), clipRect.rect().maxX() - rect.x().toFloat(), clipRect.rect().maxY() - rect.y().toFloat());
            FloatRoundedRect::Radii bottomCornerRadii;
            bottomCornerRadii.setBottomRight(clipRect.radii().bottomRight());
            roundedRectClips.append(FloatRoundedRect(bottomCorner, bottomCornerRadii));
        }

        if (!clipRect.radii().topRight().isEmpty() || !clipRect.radii().bottomLeft().isEmpty()) {
            FloatRect topCorner(rect.x().toFloat(), clipRect.rect().y(), clipRect.rect().maxX() - rect.x().toFloat(), rect.maxY() - clipRect.rect().y());
            FloatRoundedRect::Radii topCornerRadii;
            topCornerRadii.setTopRight(clipRect.radii().topRight());
            roundedRectClips.append(FloatRoundedRect(topCorner, topCornerRadii));

            FloatRect bottomCorner(clipRect.rect().x(), rect.y().toFloat(), rect.maxX() - clipRect.rect().x(), clipRect.rect().maxY() - rect.y().toFloat());
            FloatRoundedRect::Radii bottomCornerRadii;
            bottomCornerRadii.setBottomLeft(clipRect.radii().bottomLeft());
            roundedRectClips.append(FloatRoundedRect(bottomCorner, bottomCornerRadii));
        }
    }

    if (m_useDisplayItemList) {
        ASSERT(m_paintInfo.context->displayItemList());
        if (m_paintInfo.context->displayItemList()->displayItemConstructionIsDisabled())
            return;
        m_paintInfo.context->displayItemList()->createAndAppend<ClipDisplayItem>(layoutObject, m_clipType, LayoutRect::infiniteIntRect(), roundedRectClips);
    } else {
        ClipDisplayItem clipDisplayItem(layoutObject, m_clipType, LayoutRect::infiniteIntRect(), roundedRectClips);
        clipDisplayItem.replay(*paintInfo.context);
    }
}
TEST_F(PaintPropertyTreeBuilderTest, BorderRadiusClip)
{
    setBodyInnerHTML(
        "<style>"
        " body {"
        "   margin: 0px;"
        " }"
        " #div {"
        "   border-radius: 12px 34px 56px 78px;"
        "   border-top: 45px solid;"
        "   border-right: 50px solid;"
        "   border-bottom: 55px solid;"
        "   border-left: 60px solid;"
        "   width: 500px;"
        "   height: 400px;"
        "   overflow: scroll;"
        " }"
        "</style>"
        "<div id='div'></div>");

    FrameView* frameView = document().view();
    LayoutObject& div = *document().getElementById("div")->layoutObject();
    ObjectPaintProperties* divProperties = div.objectPaintProperties();
    EXPECT_EQ(frameView->scrollTranslation(), divProperties->overflowClip()->localTransformSpace());
    // The overflow clip rect includes only the padding box.
    // padding box = border box(500+60+50, 400+45+55) - border outset(60+50, 45+55) - scrollbars(15, 15)
    EXPECT_EQ(FloatRoundedRect(60, 45, 500, 400), divProperties->overflowClip()->clipRect());
    const ClipPaintPropertyNode* borderRadiusClip = divProperties->overflowClip()->parent();
    EXPECT_EQ(frameView->scrollTranslation(), borderRadiusClip->localTransformSpace());
    // The border radius clip is the area enclosed by inner border edge, including the scrollbars.
    // As the border-radius is specified in outer radius, the inner radius is calculated by:
    // inner radius = max(outer radius - border width, 0)
    // In the case that two adjacent borders have different width, the inner radius of the corner
    // may transition from one value to the other. i.e. being an ellipse.
    EXPECT_EQ(
        FloatRoundedRect(
            FloatRect(60, 45, 500, 400), // = border box(610, 500) - border outset(110, 100)
            FloatSize(0, 0), //   (top left)     = max((12, 12) - (60, 45), (0, 0))
            FloatSize(0, 0), //   (top right)    = max((34, 34) - (50, 45), (0, 0))
            FloatSize(18, 23), // (bottom left)  = max((78, 78) - (60, 55), (0, 0))
            FloatSize(6, 1)), //  (bottom right) = max((56, 56) - (50, 55), (0, 0))
        borderRadiusClip->clipRect());
    EXPECT_EQ(frameView->contentClip(), borderRadiusClip->parent());
}
void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace)
{
    if (paintingDisabled())
        return;

    if (hasShadow())
        platformContext()->shadowBlur().drawRectShadow(this, FloatRoundedRect(rect));

    fillRectWithColor(platformContext()->cr(), rect, color);
}
Exemple #15
0
void Path::addPathForRoundedRect(const FloatRect& rect,
                                 const FloatSize& topLeftRadius,
                                 const FloatSize& topRightRadius,
                                 const FloatSize& bottomLeftRadius,
                                 const FloatSize& bottomRightRadius) {
  // Start at upper-left (after corner radii), add clock-wise.
  m_path.addRRect(FloatRoundedRect(rect, topLeftRadius, topRightRadius,
                                   bottomLeftRadius, bottomRightRadius),
                  SkPath::kCW_Direction, 0);
}
FloatRoundedRect FloatRoundedRect::paddingBounds(float padding) const
{
    ASSERT(padding >= 0);
    if (!padding || isEmpty())
        return *this;

    float boundsX = x() + std::min(width() / 2, padding);
    float boundsY = y() + std::min(height() / 2, padding);
    float boundsWidth = std::max(0.0f, width() - padding * 2);
    float boundsHeight = std::max(0.0f, height() - padding * 2);
    float boundsRadiusX = std::max(0.0f, rx() - padding);
    float boundsRadiusY = std::max(0.0f, ry() - padding);
    return FloatRoundedRect(FloatRect(boundsX, boundsY, boundsWidth, boundsHeight), FloatSize(boundsRadiusX, boundsRadiusY));
}
FloatRoundedRect FloatRoundedRect::marginBounds(float margin) const
{
    ASSERT(margin >= 0);
    if (!margin)
        return *this;

    float boundsX = x() - margin;
    float boundsY = y() - margin;
    float boundsWidth = width() + margin * 2;
    float boundsHeight = height() + margin * 2;
    float boundsRadiusX = rx() + margin;
    float boundsRadiusY = ry() + margin;
    return FloatRoundedRect(FloatRect(boundsX, boundsY, boundsWidth, boundsHeight), FloatSize(boundsRadiusX, boundsRadiusY));
}
void PaintPropertyTreeBuilder::updateOverflowClip(
    const LayoutObject& object,
    PaintPropertyTreeBuilderContext& context) {
  if (!object.isBox())
    return;
  const LayoutBox& box = toLayoutBox(object);
  // The <input> elements can't have contents thus CSS overflow property
  // doesn't apply.  However for layout purposes we do generate child layout
  // objects for them, e.g. button label.  We should clip the overflow from
  // those children. This is called control clip and we technically treat them
  // like overflow clip.
  LayoutRect clipRect;
  if (box.hasControlClip()) {
    clipRect = box.controlClipRect(context.current.paintOffset);
  } else if (box.hasOverflowClip() || box.styleRef().containsPaint() ||
             (box.isSVGRoot() &&
              toLayoutSVGRoot(box).shouldApplyViewportClip())) {
    clipRect = LayoutRect(
        pixelSnappedIntRect(box.overflowClipRect(context.current.paintOffset)));
  } else {
    if (auto* properties = object.getMutableForPainting().paintProperties()) {
      properties->clearInnerBorderRadiusClip();
      properties->clearOverflowClip();
    }
    return;
  }

  const auto* currentClip = context.current.clip;
  if (box.styleRef().hasBorderRadius()) {
    auto innerBorder = box.styleRef().getRoundedInnerBorderFor(
        LayoutRect(context.current.paintOffset, box.size()));
    object.getMutableForPainting()
        .ensurePaintProperties()
        .updateInnerBorderRadiusClip(context.current.clip,
                                     context.current.transform, innerBorder);
    currentClip = object.paintProperties()->innerBorderRadiusClip();
  } else if (auto* properties =
                 object.getMutableForPainting().paintProperties()) {
    properties->clearInnerBorderRadiusClip();
  }

  object.getMutableForPainting().ensurePaintProperties().updateOverflowClip(
      currentClip, context.current.transform,
      FloatRoundedRect(FloatRect(clipRect)));

  const auto* properties = object.paintProperties();
  if (properties && properties->overflowClip())
    context.current.clip = properties->overflowClip();
}
Exemple #19
0
void RenderWidget::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    if (!shouldPaint(paintInfo, paintOffset))
        return;

    LayoutPoint adjustedPaintOffset = paintOffset + location();

    if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection))
        paintBoxDecorations(paintInfo, adjustedPaintOffset);

    if (paintInfo.phase == PaintPhaseMask) {
        paintMask(paintInfo, adjustedPaintOffset);
        return;
    }

    if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && hasOutline())
        paintOutline(paintInfo, LayoutRect(adjustedPaintOffset, size()));

    if (paintInfo.phase != PaintPhaseForeground)
        return;

    if (style().hasBorderRadius()) {
        LayoutRect borderRect = LayoutRect(adjustedPaintOffset, size());

        if (borderRect.isEmpty())
            return;

        // Push a clip if we have a border radius, since we want to round the foreground content that gets painted.
        paintInfo.context().save();
        FloatRoundedRect roundedInnerRect = FloatRoundedRect(style().getRoundedInnerBorderFor(borderRect,
            paddingTop() + borderTop(), paddingBottom() + borderBottom(), paddingLeft() + borderLeft(), paddingRight() + borderRight(), true, true));
        clipRoundedInnerRect(paintInfo.context(), borderRect, roundedInnerRect);
    }

    if (m_widget)
        paintContents(paintInfo, paintOffset);

    if (style().hasBorderRadius())
        paintInfo.context().restore();

    // Paint a partially transparent wash over selected widgets.
    if (isSelected() && !document().printing()) {
        // FIXME: selectionRect() is in absolute, not painting coordinates.
        paintInfo.context().fillRect(snappedIntRect(selectionRect()), selectionBackgroundColor(), style().colorSpace());
    }

    if (hasLayer() && layer()->canResize())
        layer()->paintResizer(paintInfo.context(), roundedIntPoint(adjustedPaintOffset), paintInfo.rect);
}
Exemple #20
0
const Path& BasicShapeInset::path(const FloatRect& boundingBox)
{
    float left = floatValueForLength(m_left, boundingBox.width());
    float top = floatValueForLength(m_top, boundingBox.height());
    auto rect = FloatRect(left + boundingBox.x(), top + boundingBox.y(),
        std::max<float>(boundingBox.width() - left - floatValueForLength(m_right, boundingBox.width()), 0),
        std::max<float>(boundingBox.height() - top - floatValueForLength(m_bottom, boundingBox.height()), 0));
    auto radii = FloatRoundedRect::Radii(floatSizeForLengthSize(m_topLeftRadius, boundingBox),
        floatSizeForLengthSize(m_topRightRadius, boundingBox),
        floatSizeForLengthSize(m_bottomLeftRadius, boundingBox),
        floatSizeForLengthSize(m_bottomRightRadius, boundingBox));
    radii.scale(calcBorderRadiiConstraintScaleFor(rect, radii));

    return cachedRoundedRectPath(FloatRoundedRect(rect, radii));
}
void PaintPropertyTreeBuilder::buildTreeRootNodes(FrameView& rootFrame, PaintPropertyTreeBuilderContext& context)
{
    // Only create extra root clip and transform nodes when RLS is enabled, because the main frame
    // unconditionally create frame translation / clip nodes otherwise.
    if (rootFrame.frame().settings() && rootFrame.frame().settings()->rootLayerScrolls()) {
        transformRoot = TransformPaintPropertyNode::create(TransformationMatrix(), FloatPoint3D(), nullptr);
        context.currentTransform = context.transformForAbsolutePosition = context.transformForFixedPosition = transformRoot.get();

        clipRoot = ClipPaintPropertyNode::create(transformRoot, FloatRoundedRect(LayoutRect::infiniteIntRect()), nullptr);
        context.currentClip = context.clipForAbsolutePosition = context.clipForFixedPosition = clipRoot.get();
    }

    // The root frame never creates effect node so we unconditionally create a root node here.
    effectRoot = EffectPaintPropertyNode::create(1.0, nullptr);
    context.currentEffect = effectRoot.get();
}
void PaintPropertyTreeBuilder::updateCssClip(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
{
    if (!object.hasClip())
        return;
    ASSERT(object.canContainAbsolutePositionObjects());

    // Create clip node for descendants that are not fixed position.
    // We don't have to setup context.clipForAbsolutePosition here because this object must be
    // a container for absolute position descendants, and will copy from in-flow context later
    // at updateOutOfFlowContext() step.
    LayoutRect clipRect = toLayoutBox(object).clipRect(context.paintOffset);
    RefPtr<ClipPaintPropertyNode> clipNode = ClipPaintPropertyNode::create(
        context.currentTransform,
        FloatRoundedRect(FloatRect(clipRect)),
        context.currentClip);
    context.currentClip = clipNode.get();
    object.getMutableForPainting().ensureObjectPaintProperties().setCssClip(clipNode.release());
}
Exemple #23
0
void BasicShapeInset::path(Path& path, const FloatRect& boundingBox)
{
    ASSERT(path.isEmpty());
    float left = floatValueForLength(m_left, boundingBox.width());
    float top = floatValueForLength(m_top, boundingBox.height());
    FloatRoundedRect r = FloatRoundedRect(
        FloatRect(
            left + boundingBox.x(),
            top + boundingBox.y(),
            std::max<float>(boundingBox.width() - left - floatValueForLength(m_right, boundingBox.width()), 0),
            std::max<float>(boundingBox.height() - top - floatValueForLength(m_bottom, boundingBox.height()), 0)
        ),
        floatSizeForLengthSize(m_topLeftRadius, boundingBox),
        floatSizeForLengthSize(m_topRightRadius, boundingBox),
        floatSizeForLengthSize(m_bottomLeftRadius, boundingBox),
        floatSizeForLengthSize(m_bottomRightRadius, boundingBox)
    );
    path.addRoundedRect(r);
}
TEST_F(PaintPropertyTreeBuilderTest, FrameScrollingTraditional)
{
    setBodyInnerHTML("<style> body { height: 10000px; } </style>");

    document().domWindow()->scrollTo(0, 100);

    FrameView* frameView = document().view();
    frameView->updateAllLifecyclePhases();
    EXPECT_EQ(TransformationMatrix(), frameView->preTranslation()->matrix());
    EXPECT_EQ(nullptr, frameView->preTranslation()->parent());
    EXPECT_EQ(TransformationMatrix().translate(0, -100), frameView->scrollTranslation()->matrix());
    EXPECT_EQ(frameView->preTranslation(), frameView->scrollTranslation()->parent());
    EXPECT_EQ(frameView->preTranslation(), frameView->contentClip()->localTransformSpace());
    EXPECT_EQ(FloatRoundedRect(0, 0, 800, 600), frameView->contentClip()->clipRect());
    EXPECT_EQ(nullptr, frameView->contentClip()->parent());

    LayoutView* layoutView = document().layoutView();
    ObjectPaintProperties* layoutViewProperties = layoutView->objectPaintProperties();
    EXPECT_EQ(nullptr, layoutViewProperties);
}
void PaintPropertyTreeBuilder::updateCssClip(
    const LayoutObject& object,
    PaintPropertyTreeBuilderContext& context) {
  if (object.hasClip()) {
    // Create clip node for descendants that are not fixed position.
    // We don't have to setup context.absolutePosition.clip here because this
    // object must be a container for absolute position descendants, and will
    // copy from in-flow context later at updateOutOfFlowContext() step.
    DCHECK(object.canContainAbsolutePositionObjects());
    LayoutRect clipRect =
        toLayoutBox(object).clipRect(context.current.paintOffset);
    context.current.clip =
        object.getMutableForPainting().ensurePaintProperties().updateCssClip(
            context.current.clip, context.current.transform,
            FloatRoundedRect(FloatRect(clipRect)));
    return;
  }

  if (auto* properties = object.getMutableForPainting().paintProperties())
    properties->clearCssClip();
}
TEST_F(PaintPropertyTreeBuilderTest, ControlClip)
{
    setBodyInnerHTML(
        "<style>"
        "  body {"
        "    margin: 0;"
        "  }"
        "  input {"
        "    border-width: 5px;"
        "    padding: 0;"
        "  }"
        "</style>"
        "<input id='button' type='button' style='width:345px; height:123px' value='some text'/>");

    FrameView* frameView = document().view();
    LayoutObject& button = *document().getElementById("button")->layoutObject();
    ObjectPaintProperties* buttonProperties = button.objectPaintProperties();
    EXPECT_EQ(frameView->scrollTranslation(), buttonProperties->overflowClip()->localTransformSpace());
    EXPECT_EQ(FloatRoundedRect(5, 5, 335, 113), buttonProperties->overflowClip()->clipRect());
    EXPECT_EQ(frameView->contentClip(), buttonProperties->overflowClip()->parent());
}
Exemple #27
0
void RenderReplaced::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    if (!shouldPaint(paintInfo, paintOffset))
        return;

#ifndef NDEBUG
    SetLayoutNeededForbiddenScope scope(this);
#endif
    LayoutPoint adjustedPaintOffset = paintOffset + location();
    
    if (hasVisibleBoxDecorations() && paintInfo.phase == PaintPhaseForeground)
        paintBoxDecorations(paintInfo, adjustedPaintOffset);
    
    if (paintInfo.phase == PaintPhaseMask) {
        paintMask(paintInfo, adjustedPaintOffset);
        return;
    }

    LayoutRect paintRect = LayoutRect(adjustedPaintOffset, size());
    if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style().outlineWidth())
        paintOutline(paintInfo, paintRect);
    
    if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection && !canHaveChildren())
        return;
    
    if (!paintInfo.shouldPaintWithinRoot(*this))
        return;
    
    bool drawSelectionTint = shouldDrawSelectionTint();
    if (paintInfo.phase == PaintPhaseSelection) {
        if (selectionState() == SelectionNone)
            return;
        drawSelectionTint = false;
    }

    bool completelyClippedOut = false;
    if (style().hasBorderRadius()) {
        LayoutRect borderRect = LayoutRect(adjustedPaintOffset, size());

        if (borderRect.isEmpty())
            completelyClippedOut = true;
        else {
            // Push a clip if we have a border radius, since we want to round the foreground content that gets painted.
            paintInfo.context().save();
            FloatRoundedRect roundedInnerRect = FloatRoundedRect(style().getRoundedInnerBorderFor(paintRect,
                paddingTop() + borderTop(), paddingBottom() + borderBottom(), paddingLeft() + borderLeft(), paddingRight() + borderRight(), true, true));
            clipRoundedInnerRect(paintInfo.context(), paintRect, roundedInnerRect);
        }
    }

    if (!completelyClippedOut) {
        paintReplaced(paintInfo, adjustedPaintOffset);

        if (style().hasBorderRadius())
            paintInfo.context().restore();
    }
        
    // The selection tint never gets clipped by border-radius rounding, since we want it to run right up to the edges of
    // surrounding content.
    if (drawSelectionTint) {
        LayoutRect selectionPaintingRect = localSelectionRect();
        selectionPaintingRect.moveBy(adjustedPaintOffset);
        paintInfo.context().fillRect(snappedIntRect(selectionPaintingRect), selectionBackgroundColor());
    }
}
Exemple #28
0
DragImageRef createDragImageForLink(URL& url, const String& inLabel, FontRenderingMode fontRenderingMode)
{
    // This is more or less an exact match for the Mac OS X code.

    const Font* labelFont;
    const Font* urlFont;
    FontCachePurgePreventer fontCachePurgePreventer;

    if (fontRenderingMode == AlternateRenderingMode) {
        static const Font alternateRenderingModeLabelFont = dragLabelFont(DragLinkLabelFontsize, true, AlternateRenderingMode);
        static const Font alternateRenderingModeURLFont = dragLabelFont(DragLinkUrlFontSize, false, AlternateRenderingMode);
        labelFont = &alternateRenderingModeLabelFont;
        urlFont = &alternateRenderingModeURLFont;
    } else {
        static const Font normalRenderingModeLabelFont = dragLabelFont(DragLinkLabelFontsize, true, NormalRenderingMode);
        static const Font normalRenderingModeURLFont = dragLabelFont(DragLinkUrlFontSize, false, NormalRenderingMode);
        labelFont = &normalRenderingModeLabelFont;
        urlFont = &normalRenderingModeURLFont;
    }

    bool drawURLString = true;
    bool clipURLString = false;
    bool clipLabelString = false;

    String urlString = url.string(); 
    String label = inLabel;
    if (label.isEmpty()) {
        drawURLString = false;
        label = urlString;
    }

    // First step in drawing the link drag image width.
    TextRun labelRun(label.impl());
    TextRun urlRun(urlString.impl());
    IntSize labelSize(labelFont->width(labelRun), labelFont->fontMetrics().ascent() + labelFont->fontMetrics().descent());

    if (labelSize.width() > MaxDragLabelStringWidth) {
        labelSize.setWidth(MaxDragLabelStringWidth);
        clipLabelString = true;
    }
    
    IntSize urlStringSize;
    IntSize imageSize(labelSize.width() + DragLabelBorderX * 2, labelSize.height() + DragLabelBorderY * 2);

    if (drawURLString) {
        urlStringSize.setWidth(urlFont->width(urlRun));
        urlStringSize.setHeight(urlFont->fontMetrics().ascent() + urlFont->fontMetrics().descent()); 
        imageSize.setHeight(imageSize.height() + urlStringSize.height());
        if (urlStringSize.width() > MaxDragLabelStringWidth) {
            imageSize.setWidth(MaxDragLabelWidth);
            clipURLString = true;
        } else
            imageSize.setWidth(std::max(labelSize.width(), urlStringSize.width()) + DragLabelBorderX * 2);
    }

    // We now know how big the image needs to be, so we create and
    // fill the background
    HWndDC dc(0);
    auto workingDC = adoptGDIObject(::CreateCompatibleDC(dc));
    if (!workingDC)
        return 0;

    PlatformGraphicsContext* contextRef;
    auto image = allocImage(workingDC.get(), imageSize, &contextRef);
    if (!image)
        return 0;
        
    ::SelectObject(workingDC.get(), image.get());
    GraphicsContext context(contextRef);
    // On Mac alpha is {0.7, 0.7, 0.7, 0.8}, however we can't control alpha
    // for drag images on win, so we use 1
    static const Color backgroundColor(140, 140, 140);
    static const IntSize radii(DragLabelRadius, DragLabelRadius);
    IntRect rect(0, 0, imageSize.width(), imageSize.height());
    context.fillRoundedRect(FloatRoundedRect(rect, radii, radii, radii, radii), backgroundColor, ColorSpaceDeviceRGB);
 
    // Draw the text
    static const Color topColor(0, 0, 0, 255); // original alpha = 0.75
    static const Color bottomColor(255, 255, 255, 127); // original alpha = 0.5
    if (drawURLString) {
        if (clipURLString)
            urlString = StringTruncator::rightTruncate(urlString, imageSize.width() - (DragLabelBorderX * 2.0f), *urlFont, StringTruncator::EnableRoundingHacks);
        IntPoint textPos(DragLabelBorderX, imageSize.height() - (LabelBorderYOffset + urlFont->fontMetrics().descent()));
        WebCoreDrawDoubledTextAtPoint(context, urlString, textPos, *urlFont, topColor, bottomColor);
    }
    
    if (clipLabelString)
        label = StringTruncator::rightTruncate(label, imageSize.width() - (DragLabelBorderX * 2.0f), *labelFont, StringTruncator::EnableRoundingHacks);

    IntPoint textPos(DragLabelBorderX, DragLabelBorderY + labelFont->pixelSize());
    WebCoreDrawDoubledTextAtPoint(context, label, textPos, *labelFont, topColor, bottomColor);

    deallocContext(contextRef);
    return image.leak();
}
Exemple #29
0
std::unique_ptr<Shape> Shape::createShape(const BasicShape* basicShape, const LayoutSize& logicalBoxSize, WritingMode writingMode, float margin)
{
    ASSERT(basicShape);

    bool horizontalWritingMode = isHorizontalWritingMode(writingMode);
    float boxWidth = horizontalWritingMode ? logicalBoxSize.width() : logicalBoxSize.height();
    float boxHeight = horizontalWritingMode ? logicalBoxSize.height() : logicalBoxSize.width();
    std::unique_ptr<Shape> shape;

    switch (basicShape->type()) {

    case BasicShape::BasicShapeCircleType: {
        const BasicShapeCircle* circle = static_cast<const BasicShapeCircle*>(basicShape);
        float centerX = floatValueForCenterCoordinate(circle->centerX(), boxWidth);
        float centerY = floatValueForCenterCoordinate(circle->centerY(), boxHeight);
        float radius = circle->floatValueForRadiusInBox(boxWidth, boxHeight);
        FloatPoint logicalCenter = physicalPointToLogical(FloatPoint(centerX, centerY), logicalBoxSize.height(), writingMode);

        shape = createCircleShape(logicalCenter, radius);
        break;
    }

    case BasicShape::BasicShapeEllipseType: {
        const BasicShapeEllipse* ellipse = static_cast<const BasicShapeEllipse*>(basicShape);
        float centerX = floatValueForCenterCoordinate(ellipse->centerX(), boxWidth);
        float centerY = floatValueForCenterCoordinate(ellipse->centerY(), boxHeight);
        float radiusX = ellipse->floatValueForRadiusInBox(ellipse->radiusX(), centerX, boxWidth);
        float radiusY = ellipse->floatValueForRadiusInBox(ellipse->radiusY(), centerY, boxHeight);
        FloatPoint logicalCenter = physicalPointToLogical(FloatPoint(centerX, centerY), logicalBoxSize.height(), writingMode);

        shape = createEllipseShape(logicalCenter, FloatSize(radiusX, radiusY));
        break;
    }

    case BasicShape::BasicShapePolygonType: {
        const BasicShapePolygon& polygon = *static_cast<const BasicShapePolygon*>(basicShape);
        const Vector<Length>& values = polygon.values();
        size_t valuesSize = values.size();
        ASSERT(!(valuesSize % 2));
        std::unique_ptr<Vector<FloatPoint>> vertices = std::make_unique<Vector<FloatPoint>>(valuesSize / 2);
        for (unsigned i = 0; i < valuesSize; i += 2) {
            FloatPoint vertex(
                floatValueForLength(values.at(i), boxWidth),
                floatValueForLength(values.at(i + 1), boxHeight));
            (*vertices)[i / 2] = physicalPointToLogical(vertex, logicalBoxSize.height(), writingMode);
        }

        shape = createPolygonShape(WTF::move(vertices), polygon.windRule());
        break;
    }

    case BasicShape::BasicShapeInsetType: {
        const BasicShapeInset& inset = *static_cast<const BasicShapeInset*>(basicShape);
        float left = floatValueForLength(inset.left(), boxWidth);
        float top = floatValueForLength(inset.top(), boxHeight);
        FloatRect rect(left,
            top,
            std::max<float>(boxWidth - left - floatValueForLength(inset.right(), boxWidth), 0),
            std::max<float>(boxHeight - top - floatValueForLength(inset.bottom(), boxHeight), 0));
        FloatRect logicalRect = physicalRectToLogical(rect, logicalBoxSize.height(), writingMode);

        FloatSize boxSize(boxWidth, boxHeight);
        FloatSize topLeftRadius = physicalSizeToLogical(floatSizeForLengthSize(inset.topLeftRadius(), boxSize), writingMode);
        FloatSize topRightRadius = physicalSizeToLogical(floatSizeForLengthSize(inset.topRightRadius(), boxSize), writingMode);
        FloatSize bottomLeftRadius = physicalSizeToLogical(floatSizeForLengthSize(inset.bottomLeftRadius(), boxSize), writingMode);
        FloatSize bottomRightRadius = physicalSizeToLogical(floatSizeForLengthSize(inset.bottomRightRadius(), boxSize), writingMode);
        FloatRoundedRect::Radii cornerRadii(topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);

        cornerRadii.scale(calcBorderRadiiConstraintScaleFor(logicalRect, cornerRadii));

        shape = createInsetShape(FloatRoundedRect(logicalRect, cornerRadii));
        break;
    }

    default:
        ASSERT_NOT_REACHED();
    }

    shape->m_writingMode = writingMode;
    shape->m_margin = margin;

    return shape;
}
Exemple #30
0
bool RenderThemeGtk::paintMediaSliderThumb(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
{
    RenderStyle* style = &o.style();
    paintInfo.context->fillRoundedRect(FloatRoundedRect(r, borderRadiiFromStyle(style)), style->visitedDependentColor(CSSPropertyColor), style->colorSpace());
    return false;
}