https://bugs.webkit.org/show_bug.cgi?id=203089
https://trac.webkit.org/changeset/251993
+2019-11-06 Simon Fraser <simon.fraser@apple.com>
+
+ Box-shadow spread radius does not transition or animate correctly with CSS Transitions & Animations
+ https://bugs.webkit.org/show_bug.cgi?id=202489
+
+ Reviewed by Zalan Bujtas.
+
+ * fast/box-shadow/hidpi-box-shadow-expected.html: Added.
+ * fast/box-shadow/hidpi-box-shadow.html: Added.
+
2019-11-04 Chris Dumez <cdumez@apple.com>
MediaRecorder should not prevent entering the back/forward cache
--- /dev/null
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+ .container {
+ position: relative;
+ width: 30px;
+ height: 30px;
+ margin: 20px;
+ border: 1px solid silver;
+ }
+
+ .box {
+ position: absolute;
+ background-color: gray;
+ }
+
+ .shadow {
+ position: absolute;
+ background-color: blue;
+ }
+ </style>
+</head>
+<body>
+ <div class="container">
+ <div class="shadow" style="left: 0; top: 0; width: 12px; height: 12px;"></div>
+ <div class="box" style="left: 1px; top: 1px; width: 10px; height: 10px;"></div>
+ </div>
+
+ <div class="container">
+ <div class="shadow" style="left: 0.5px; top: 0.5px; width: 11px; height: 11px;"></div>
+ <div class="box" style="left: 1px; top: 1px; width: 10px; height: 10px;"></div>
+ </div>
+
+ <div class="container">
+ <div class="shadow" style="left: 0.5px; top: 0.5px; width: 11.5px; height: 11.5px;"></div>
+ <div class="box" style="left: 1px; top: 1px; width: 10.5px; height: 10.5px;"></div>
+ </div>
+
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+ .container {
+ position: relative;
+ width: 30px;
+ height: 30px;
+ margin: 20px;
+ border: 1px solid silver;
+ }
+
+ .box {
+ position: absolute;
+ background-color: gray;
+ }
+ </style>
+</head>
+<body>
+ <div class="container">
+ <div class="box" style="left: 1px; top: 1px; width: 10px; height: 10px; box-shadow: 0 0 0 1px blue;"></div>
+ </div>
+
+ <div class="container">
+ <div class="box" style="left: 1px; top: 1px; width: 10px; height: 10px; box-shadow: 0 0 0 0.5px blue"></div>
+ </div>
+
+ <div class="container">
+ <div class="box" style="left: 1px; top: 1px; width: 10.5px; height: 10.5px; box-shadow: 0 0 0 0.5px blue"></div>
+ </div>
+
+</body>
+</html>
(WebCore::WebAnimation::hasPendingActivity const):
* animation/WebAnimation.h:
+2019-11-06 Simon Fraser <simon.fraser@apple.com>
+
+ Box-shadow spread radius does not transition or animate correctly with CSS Transitions & Animations
+ https://bugs.webkit.org/show_bug.cgi?id=202489
+
+ Reviewed by Zalan Bujtas.
+
+ Fix box-shadow offset and spread to support subpixel values, and make the rendering subpixel-aware.
+ This also makes animation of shadow spread and offset be smoother on Retina displays.
+
+ Also make ShadowStyle an enum class.
+
+ Test: fast/box-shadow/hidpi-box-shadow.html
+
+ * css/CSSComputedStyleDeclaration.cpp:
+ (WebCore::ComputedStyleExtractor::valueForShadow):
+ (WebCore::ComputedStyleExtractor::valueForFilter):
+ * css/CSSPrimitiveValue.cpp:
+ (WebCore::CSSPrimitiveValue::computeLength const):
+ * page/animation/CSSPropertyAnimation.cpp:
+ (WebCore::blendFunc):
+ (WebCore::shadowForBlending):
+ * platform/animation/AnimationUtilities.h:
+ (WebCore::blend):
+ * platform/graphics/RoundedRect.h:
+ * rendering/InlineFlowBox.cpp:
+ (WebCore::InlineFlowBox::paintBoxDecorations):
+ * rendering/RenderBox.cpp:
+ (WebCore::RenderBox::paintBoxDecorations):
+ * rendering/RenderBoxModelObject.cpp:
+ (WebCore::applyBoxShadowForBackground):
+ (WebCore::RenderBoxModelObject::boxShadowShouldBeAppliedToBackground const):
+ (WebCore::areaCastingShadowInHole):
+ (WebCore::RenderBoxModelObject::paintBoxShadow):
+ Move the snapping until after we've adjusted rects for offset and spread. The "extraOffset"
+ computation makes the snapping a little more complex; we have to snap before and after applying
+ shadowOffset, and give to GraphicsContext the delta between the snapped values.
+ * rendering/RenderTable.cpp:
+ (WebCore::RenderTable::paintBoxDecorations):
+ * rendering/RenderTableCell.cpp:
+ (WebCore::RenderTableCell::paintBoxDecorations):
+ * rendering/style/RenderStyle.cpp:
+ (WebCore::RenderStyle::setTextShadow):
+ (WebCore::RenderStyle::getShadowExtent const):
+ (WebCore::RenderStyle::getShadowInsetExtent const):
+ (WebCore::RenderStyle::getShadowHorizontalExtent const):
+ (WebCore::RenderStyle::getShadowVerticalExtent const):
+ * rendering/style/ShadowData.cpp:
+ (WebCore::ShadowData::ShadowData):
+ (WebCore::calculateShadowExtent):
+ (WebCore::ShadowData::adjustRectForShadow const):
+ * rendering/style/ShadowData.h:
+ (WebCore::ShadowData::ShadowData):
+ (WebCore::ShadowData::x const):
+ (WebCore::ShadowData::y const):
+ (WebCore::ShadowData::location const):
+ (WebCore::ShadowData::paintingExtent const):
+ (WebCore::ShadowData::spread const):
+ * style/StyleBuilderCustom.h:
+ (WebCore::Style::BuilderCustom::applyTextOrBoxShadowValue):
+
2019-11-04 Chris Dumez <cdumez@apple.com>
MediaRecorder should not prevent entering the back/forward cache
auto y = adjustLengthForZoom(currShadowData->y(), style, adjust);
auto blur = adjustLengthForZoom(currShadowData->radius(), style, adjust);
auto spread = propertyID == CSSPropertyTextShadow ? RefPtr<CSSPrimitiveValue>() : adjustLengthForZoom(currShadowData->spread(), style, adjust);
- auto style = propertyID == CSSPropertyTextShadow || currShadowData->style() == Normal ? RefPtr<CSSPrimitiveValue>() : cssValuePool.createIdentifierValue(CSSValueInset);
+ auto style = propertyID == CSSPropertyTextShadow || currShadowData->style() == ShadowStyle::Normal ? RefPtr<CSSPrimitiveValue>() : cssValuePool.createIdentifierValue(CSSValueInset);
auto color = cssValuePool.createColorValue(currShadowData->color());
list->prepend(CSSShadowValue::create(WTFMove(x), WTFMove(y), WTFMove(blur), WTFMove(spread), WTFMove(style), WTFMove(color)));
}
DropShadowFilterOperation& dropShadowOperation = downcast<DropShadowFilterOperation>(filterOperation);
filterValue = CSSFunctionValue::create(CSSValueDropShadow);
// We want our computed style to look like that of a text shadow (has neither spread nor inset style).
- ShadowData shadowData = ShadowData(dropShadowOperation.location(), dropShadowOperation.stdDeviation(), 0, Normal, false, dropShadowOperation.color());
+ ShadowData shadowData = ShadowData(dropShadowOperation.location(), dropShadowOperation.stdDeviation(), 0, ShadowStyle::Normal, false, dropShadowOperation.color());
filterValue->append(valueForShadow(&shadowData, CSSPropertyTextShadow, style, adjust));
break;
}
return computeLengthDouble(conversionData);
}
+template<> LayoutUnit CSSPrimitiveValue::computeLength(const CSSToLengthConversionData& conversionData) const
+{
+ return LayoutUnit(computeLengthDouble(conversionData));
+}
+
double CSSPrimitiveValue::computeLengthDouble(const CSSToLengthConversionData& conversionData) const
{
if (m_primitiveUnitType == CSS_CALC)
if (from == to)
return to;
- double fromVal = from == Normal ? 1 : 0;
- double toVal = to == Normal ? 1 : 0;
+ double fromVal = from == ShadowStyle::Normal ? 1 : 0;
+ double toVal = to == ShadowStyle::Normal ? 1 : 0;
double result = blendFunc(anim, fromVal, toVal, progress);
- return result > 0 ? Normal : Inset;
+ return result > 0 ? ShadowStyle::Normal : ShadowStyle::Inset;
}
static inline std::unique_ptr<ShadowData> blendFunc(const CSSPropertyBlendingClient* anim, const ShadowData* from, const ShadowData* to, double progress)
static inline const ShadowData* shadowForBlending(const ShadowData* srcShadow, const ShadowData* otherShadow)
{
- static NeverDestroyed<ShadowData> defaultShadowData(IntPoint(), 0, 0, Normal, false, Color::transparent);
- static NeverDestroyed<ShadowData> defaultInsetShadowData(IntPoint(), 0, 0, Inset, false, Color::transparent);
- static NeverDestroyed<ShadowData> defaultWebKitBoxShadowData(IntPoint(), 0, 0, Normal, true, Color::transparent);
- static NeverDestroyed<ShadowData> defaultInsetWebKitBoxShadowData(IntPoint(), 0, 0, Inset, true, Color::transparent);
+ static NeverDestroyed<ShadowData> defaultShadowData(LayoutPoint(), 0, 0, ShadowStyle::Normal, false, Color::transparent);
+ static NeverDestroyed<ShadowData> defaultInsetShadowData(LayoutPoint(), 0, 0, ShadowStyle::Inset, false, Color::transparent);
+ static NeverDestroyed<ShadowData> defaultWebKitBoxShadowData(LayoutPoint(), 0, 0, ShadowStyle::Normal, true, Color::transparent);
+ static NeverDestroyed<ShadowData> defaultInsetWebKitBoxShadowData(LayoutPoint(), 0, 0, ShadowStyle::Inset, true, Color::transparent);
if (srcShadow)
return srcShadow;
- if (otherShadow->style() == Inset)
+ if (otherShadow->style() == ShadowStyle::Inset)
return otherShadow->isWebkitBoxShadow() ? &defaultInsetWebKitBoxShadowData.get() : &defaultInsetShadowData.get();
return otherShadow->isWebkitBoxShadow() ? &defaultWebKitBoxShadowData.get() : &defaultShadowData.get();
/*
- * Copyright (C) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2011-2019 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AnimationUtilities_h
-#define AnimationUtilities_h
+#pragma once
#include "IntPoint.h"
-#include "LayoutUnit.h"
+#include "LayoutPoint.h"
namespace WebCore {
inline IntPoint blend(const IntPoint& from, const IntPoint& to, double progress)
{
return IntPoint(blend(from.x(), to.x(), progress),
- blend(from.y(), to.y(), progress));
+ blend(from.y(), to.y(), progress));
}
-} // namespace WebCore
+inline LayoutPoint blend(const LayoutPoint& from, const LayoutPoint& to, double progress)
+{
+ return LayoutPoint(blend(from.x(), to.x(), progress),
+ blend(from.y(), to.y(), progress));
+}
-#endif // AnimationUtilities_h
+} // namespace WebCore
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef RoundedRect_h
-#define RoundedRect_h
+#pragma once
#include "FloatQuad.h"
#include "LayoutRect.h"
WEBCORE_EXPORT Region approximateAsRegion(const RoundedRect&, unsigned stepLength = 20);
} // namespace WebCore
-
-#endif // RoundedRect_h
LayoutRect paintRect = LayoutRect(adjustedPaintoffset, frameRect.size());
// Shadow comes first and is behind the background and border.
if (!renderer().boxShadowShouldBeAppliedToBackground(adjustedPaintoffset, BackgroundBleedNone, this))
- paintBoxShadow(paintInfo, lineStyle, Normal, paintRect);
+ paintBoxShadow(paintInfo, lineStyle, ShadowStyle::Normal, paintRect);
auto color = lineStyle.visitedDependentColor(CSSPropertyBackgroundColor);
auto compositeOp = renderer().document().compositeOperatorForBackgroundColor(color, renderer());
color = lineStyle.colorByApplyingColorFilter(color);
paintFillLayers(paintInfo, color, lineStyle.backgroundLayers(), paintRect, compositeOp);
- paintBoxShadow(paintInfo, lineStyle, Inset, paintRect);
+ paintBoxShadow(paintInfo, lineStyle, ShadowStyle::Inset, paintRect);
// :first-line cannot be used to put borders on a line. Always paint borders with our
// non-first-line style.
// FIXME: Should eventually give the theme control over whether the box shadow should paint, since controls could have
// custom shadows of their own.
if (!boxShadowShouldBeAppliedToBackground(paintRect.location(), bleedAvoidance))
- paintBoxShadow(paintInfo, paintRect, style(), Normal);
+ paintBoxShadow(paintInfo, paintRect, style(), ShadowStyle::Normal);
GraphicsContextStateSaver stateSaver(paintInfo.context(), false);
if (bleedAvoidance == BackgroundBleedUseTransparencyLayer) {
if (style().hasAppearance())
theme().paintDecorations(*this, paintInfo, paintRect);
}
- paintBoxShadow(paintInfo, paintRect, style(), Inset);
+ paintBoxShadow(paintInfo, paintRect, style(), ShadowStyle::Inset);
// The theme will tell us whether or not we should also paint the CSS border.
if (bleedAvoidance != BackgroundBleedBackgroundOverBorder && (!style().hasAppearance() || (borderOrBackgroundPaintingIsNeeded && theme().paintBorderOnly(*this, paintInfo, paintRect))) && style().hasVisibleBorderDecoration())
static void applyBoxShadowForBackground(GraphicsContext& context, const RenderStyle& style)
{
const ShadowData* boxShadow = style.boxShadow();
- while (boxShadow->style() != Normal)
+ while (boxShadow->style() != ShadowStyle::Normal)
boxShadow = boxShadow->next();
FloatSize shadowOffset(boxShadow->x(), boxShadow->y());
bool hasOneNormalBoxShadow = false;
for (const ShadowData* currentShadow = style().boxShadow(); currentShadow; currentShadow = currentShadow->next()) {
- if (currentShadow->style() != Normal)
+ if (currentShadow->style() != ShadowStyle::Normal)
continue;
if (hasOneNormalBoxShadow)
return true;
}
-static inline LayoutRect areaCastingShadowInHole(const LayoutRect& holeRect, int shadowExtent, int shadowSpread, const IntSize& shadowOffset)
+static inline LayoutRect areaCastingShadowInHole(const LayoutRect& holeRect, LayoutUnit shadowExtent, LayoutUnit shadowSpread, const LayoutSize& shadowOffset)
{
LayoutRect bounds(holeRect);
if (context.paintingDisabled() || !style.boxShadow())
return;
- RoundedRect border = (shadowStyle == Inset) ? style.getRoundedInnerBorderFor(paintRect, includeLogicalLeftEdge, includeLogicalRightEdge)
+ RoundedRect border = (shadowStyle == ShadowStyle::Inset) ? style.getRoundedInnerBorderFor(paintRect, includeLogicalLeftEdge, includeLogicalRightEdge)
: style.getRoundedBorderFor(paintRect, includeLogicalLeftEdge, includeLogicalRightEdge);
bool hasBorderRadius = style.hasBorderRadius();
if (shadow->style() != shadowStyle)
continue;
- // FIXME: Add subpixel support for the shadow values. Soon after the shadow offset becomes fractional,
- // all the early snappings here need to be pushed to the actual painting operations.
- IntSize shadowOffset(shadow->x(), shadow->y());
+ LayoutSize shadowOffset(shadow->x(), shadow->y());
+ LayoutUnit shadowPaintingExtent = shadow->paintingExtent();
+ LayoutUnit shadowSpread = shadow->spread();
int shadowRadius = shadow->radius();
- int shadowPaintingExtent = shadow->paintingExtent();
- int shadowSpread = shadow->spread();
-
+
if (shadowOffset.isZero() && !shadowRadius && !shadowSpread)
continue;
Color shadowColor = style.colorByApplyingColorFilter(shadow->color());
- if (shadow->style() == Normal) {
+ if (shadow->style() == ShadowStyle::Normal) {
RoundedRect fillRect = border;
fillRect.inflate(shadowSpread);
if (fillRect.isEmpty())
continue;
- FloatRect pixelSnappedShadowRect = snapRectToDevicePixels(border.rect(), deviceScaleFactor);
- pixelSnappedShadowRect.inflate(shadowPaintingExtent + shadowSpread);
- pixelSnappedShadowRect.move(shadowOffset);
+ LayoutRect shadowRect = border.rect();
+ shadowRect.inflate(shadowPaintingExtent + shadowSpread);
+ shadowRect.move(shadowOffset);
+ FloatRect pixelSnappedShadowRect = snapRectToDevicePixels(shadowRect, deviceScaleFactor);
GraphicsContextStateSaver stateSaver(context);
context.clip(pixelSnappedShadowRect);
- // Move the fill just outside the clip, adding 1 pixel separation so that the fill does not
+ // Move the fill just outside the clip, adding at least 1 pixel of separation so that the fill does not
// bleed in (due to antialiasing) if the context is transformed.
- IntSize extraOffset(roundToInt(paintRect.width()) + std::max(0, shadowOffset.width()) + shadowPaintingExtent + 2 * shadowSpread + 1, 0);
+ LayoutUnit xOffset = paintRect.width() + std::max<LayoutUnit>(0, shadowOffset.width()) + shadowPaintingExtent + 2 * shadowSpread + LayoutUnit(1);
+ LayoutSize extraOffset(xOffset.ceil(), 0);
shadowOffset -= extraOffset;
fillRect.move(extraOffset);
+ FloatRoundedRect pixelSnappedRectToClipOut = border.pixelSnappedRoundedRectForPainting(deviceScaleFactor);
+ FloatRoundedRect pixelSnappedFillRect = fillRect.pixelSnappedRoundedRectForPainting(deviceScaleFactor);
+
+ LayoutPoint shadowRectOrigin = fillRect.rect().location() + shadowOffset;
+ FloatPoint snappedShadowOrigin = FloatPoint(roundToDevicePixel(shadowRectOrigin.x(), deviceScaleFactor), roundToDevicePixel(shadowRectOrigin.y(), deviceScaleFactor));
+ FloatSize snappedShadowOffset = snappedShadowOrigin - pixelSnappedFillRect.rect().location();
+
if (shadow->isWebkitBoxShadow())
- context.setLegacyShadow(shadowOffset, shadowRadius, shadowColor);
+ context.setLegacyShadow(snappedShadowOffset, shadowRadius, shadowColor);
else
- context.setShadow(shadowOffset, shadowRadius, shadowColor);
+ context.setShadow(snappedShadowOffset, shadowRadius, shadowColor);
- FloatRoundedRect rectToClipOut = border.pixelSnappedRoundedRectForPainting(deviceScaleFactor);
- FloatRoundedRect pixelSnappedFillRect = fillRect.pixelSnappedRoundedRectForPainting(deviceScaleFactor);
if (hasBorderRadius) {
// If the box is opaque, it is unnecessary to clip it out. However, doing so saves time
// when painting the shadow. On the other hand, it introduces subpixel gaps along the
// corners. Those are avoided by insetting the clipping path by one pixel.
if (hasOpaqueBackground)
- rectToClipOut.inflateWithRadii(-1.0f);
+ pixelSnappedRectToClipOut.inflateWithRadii(-1.0f);
- if (!rectToClipOut.isEmpty())
- context.clipOutRoundedRect(rectToClipOut);
+ if (!pixelSnappedRectToClipOut.isEmpty())
+ context.clipOutRoundedRect(pixelSnappedRectToClipOut);
RoundedRect influenceRect(LayoutRect(pixelSnappedShadowRect), border.radii());
influenceRect.expandRadii(2 * shadowPaintingExtent + shadowSpread);
// FIXME: It's not clear if this check is right. What about integral scale factors?
AffineTransform transform = context.getCTM();
if (transform.a() != 1 || (transform.d() != 1 && transform.d() != -1) || transform.b() || transform.c())
- rectToClipOut.inflate(-1.0f);
+ pixelSnappedRectToClipOut.inflate(-1.0f);
}
- if (!rectToClipOut.isEmpty())
- context.clipOut(rectToClipOut.rect());
+ if (!pixelSnappedRectToClipOut.isEmpty())
+ context.clipOut(pixelSnappedRectToClipOut.rect());
+
context.fillRect(pixelSnappedFillRect.rect(), Color::black);
}
} else {
// Inset shadow.
+ LayoutRect holeRect = border.rect();
+ holeRect.inflate(-shadowSpread);
+ FloatRect pixelSnappedHoleRect = snapRectToDevicePixels(holeRect, deviceScaleFactor);
FloatRoundedRect pixelSnappedBorderRect = border.pixelSnappedRoundedRectForPainting(deviceScaleFactor);
- FloatRect pixelSnappedHoleRect = pixelSnappedBorderRect.rect();
- pixelSnappedHoleRect.inflate(-shadowSpread);
if (pixelSnappedHoleRect.isEmpty()) {
if (hasBorderRadius)
if (!includeLogicalLeftEdge) {
if (isHorizontal) {
- pixelSnappedHoleRect.move(-std::max(shadowOffset.width(), 0) - shadowPaintingExtent, 0);
- pixelSnappedHoleRect.setWidth(pixelSnappedHoleRect.width() + std::max(shadowOffset.width(), 0) + shadowPaintingExtent);
+ holeRect.move(-std::max<LayoutUnit>(shadowOffset.width(), 0) - shadowPaintingExtent, 0);
+ holeRect.setWidth(holeRect.width() + std::max<LayoutUnit>(shadowOffset.width(), 0) + shadowPaintingExtent);
} else {
- pixelSnappedHoleRect.move(0, -std::max(shadowOffset.height(), 0) - shadowPaintingExtent);
- pixelSnappedHoleRect.setHeight(pixelSnappedHoleRect.height() + std::max(shadowOffset.height(), 0) + shadowPaintingExtent);
+ holeRect.move(0, -std::max<LayoutUnit>(shadowOffset.height(), 0) - shadowPaintingExtent);
+ holeRect.setHeight(holeRect.height() + std::max<LayoutUnit>(shadowOffset.height(), 0) + shadowPaintingExtent);
}
}
+
if (!includeLogicalRightEdge) {
if (isHorizontal)
- pixelSnappedHoleRect.setWidth(pixelSnappedHoleRect.width() - std::min(shadowOffset.width(), 0) + shadowPaintingExtent);
+ holeRect.setWidth(holeRect.width() - std::min<LayoutUnit>(shadowOffset.width(), 0) + shadowPaintingExtent);
else
- pixelSnappedHoleRect.setHeight(pixelSnappedHoleRect.height() - std::min(shadowOffset.height(), 0) + shadowPaintingExtent);
+ holeRect.setHeight(holeRect.height() - std::min<LayoutUnit>(shadowOffset.height(), 0) + shadowPaintingExtent);
}
+ if (!includeLogicalLeftEdge || !includeLogicalRightEdge)
+ pixelSnappedHoleRect = snapRectToDevicePixels(holeRect, deviceScaleFactor);
+
Color fillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), 255);
- FloatRect pixelSnappedOuterRect = snapRectToDevicePixels(areaCastingShadowInHole(LayoutRect(pixelSnappedBorderRect.rect()), shadowPaintingExtent, shadowSpread, shadowOffset), deviceScaleFactor);
- FloatRoundedRect pixelSnappedRoundedHole = FloatRoundedRect(pixelSnappedHoleRect, pixelSnappedBorderRect.radii());
+ FloatRect pixelSnappedOuterRect = snapRectToDevicePixels(areaCastingShadowInHole(holeRect, shadowPaintingExtent, shadowSpread, shadowOffset), deviceScaleFactor);
+ RoundedRect roundedHoleRect(holeRect, border.radii());
+ FloatRoundedRect pixelSnappedRoundedHole = roundedHoleRect.pixelSnappedRoundedRectForPainting(deviceScaleFactor);
GraphicsContextStateSaver stateSaver(context);
if (hasBorderRadius) {
} else
context.clip(pixelSnappedBorderRect.rect());
- IntSize extraOffset(2 * roundToInt(paintRect.width()) + std::max(0, shadowOffset.width()) + shadowPaintingExtent - 2 * shadowSpread + 1, 0);
+ IntSize extraOffset(2 * roundToInt(paintRect.width()) + std::max<LayoutUnit>(0, shadowOffset.width()) + shadowPaintingExtent - 2 * shadowSpread + 1, 0);
context.translate(extraOffset);
shadowOffset -= extraOffset;
BackgroundBleedAvoidance bleedAvoidance = determineBackgroundBleedAvoidance(paintInfo.context());
if (!boxShadowShouldBeAppliedToBackground(rect.location(), bleedAvoidance))
- paintBoxShadow(paintInfo, rect, style(), Normal);
+ paintBoxShadow(paintInfo, rect, style(), ShadowStyle::Normal);
paintBackground(paintInfo, rect, bleedAvoidance);
- paintBoxShadow(paintInfo, rect, style(), Inset);
+ paintBoxShadow(paintInfo, rect, style(), ShadowStyle::Inset);
if (style().hasVisibleBorderDecoration() && !collapseBorders())
paintBorder(paintInfo, rect, style());
LayoutRect paintRect = LayoutRect(paintOffset, frameRect().size());
adjustBorderBoxRectForPainting(paintRect);
- paintBoxShadow(paintInfo, paintRect, style(), Normal);
+ paintBoxShadow(paintInfo, paintRect, style(), ShadowStyle::Normal);
// Paint our cell background.
paintBackgroundsBehindCell(paintInfo, paintOffset, this);
- paintBoxShadow(paintInfo, paintRect, style(), Inset);
+ paintBoxShadow(paintInfo, paintRect, style(), ShadowStyle::Inset);
if (!style().hasBorder() || table->collapseBorders())
return;
void RenderStyle::setTextShadow(std::unique_ptr<ShadowData> shadowData, bool add)
{
- ASSERT(!shadowData || (!shadowData->spread() && shadowData->style() == Normal));
+ ASSERT(!shadowData || (!shadowData->spread() && shadowData->style() == ShadowStyle::Normal));
auto& rareData = m_rareInheritedData.access();
if (!add) {
left = 0;
for ( ; shadow; shadow = shadow->next()) {
- if (shadow->style() == Inset)
+ if (shadow->style() == ShadowStyle::Inset)
continue;
- int extentAndSpread = shadow->paintingExtent() + shadow->spread();
+ auto extentAndSpread = shadow->paintingExtent() + shadow->spread();
top = std::min<LayoutUnit>(top, shadow->y() - extentAndSpread);
right = std::max<LayoutUnit>(right, shadow->x() + extentAndSpread);
bottom = std::max<LayoutUnit>(bottom, shadow->y() + extentAndSpread);
LayoutUnit left;
for ( ; shadow; shadow = shadow->next()) {
- if (shadow->style() == Normal)
+ if (shadow->style() == ShadowStyle::Normal)
continue;
- int extentAndSpread = shadow->paintingExtent() + shadow->spread();
+ auto extentAndSpread = shadow->paintingExtent() + shadow->spread();
top = std::max<LayoutUnit>(top, shadow->y() + extentAndSpread);
right = std::min<LayoutUnit>(right, shadow->x() - extentAndSpread);
bottom = std::min<LayoutUnit>(bottom, shadow->y() - extentAndSpread);
right = 0;
for ( ; shadow; shadow = shadow->next()) {
- if (shadow->style() == Inset)
+ if (shadow->style() == ShadowStyle::Inset)
continue;
- int extentAndSpread = shadow->paintingExtent() + shadow->spread();
+ auto extentAndSpread = shadow->paintingExtent() + shadow->spread();
left = std::min<LayoutUnit>(left, shadow->x() - extentAndSpread);
right = std::max<LayoutUnit>(right, shadow->x() + extentAndSpread);
}
bottom = 0;
for ( ; shadow; shadow = shadow->next()) {
- if (shadow->style() == Inset)
+ if (shadow->style() == ShadowStyle::Inset)
continue;
- int extentAndSpread = shadow->paintingExtent() + shadow->spread();
+ auto extentAndSpread = shadow->paintingExtent() + shadow->spread();
top = std::min<LayoutUnit>(top, shadow->y() - extentAndSpread);
bottom = std::max<LayoutUnit>(bottom, shadow->y() + extentAndSpread);
}
ShadowData::ShadowData(const ShadowData& o)
: m_location(o.m_location)
- , m_radius(o.m_radius)
, m_spread(o.m_spread)
+ , m_radius(o.m_radius)
, m_color(o.m_color)
, m_style(o.m_style)
, m_isWebkitBoxShadow(o.m_isWebkitBoxShadow)
&& m_isWebkitBoxShadow == o.m_isWebkitBoxShadow;
}
-static inline void calculateShadowExtent(const ShadowData* shadow, int additionalOutlineSize, int& shadowLeft, int& shadowRight, int& shadowTop, int& shadowBottom)
+static inline void calculateShadowExtent(const ShadowData* shadow, LayoutUnit additionalOutlineSize, LayoutUnit& shadowLeft, LayoutUnit& shadowRight, LayoutUnit& shadowTop, LayoutUnit& shadowBottom)
{
do {
- int extentAndSpread = shadow->paintingExtent() + shadow->spread() + additionalOutlineSize;
- if (shadow->style() == Normal) {
+ LayoutUnit extentAndSpread = shadow->paintingExtent() + shadow->spread() + additionalOutlineSize;
+ if (shadow->style() == ShadowStyle::Normal) {
shadowLeft = std::min(shadow->x() - extentAndSpread, shadowLeft);
shadowRight = std::max(shadow->x() + extentAndSpread, shadowRight);
shadowTop = std::min(shadow->y() - extentAndSpread, shadowTop);
void ShadowData::adjustRectForShadow(LayoutRect& rect, int additionalOutlineSize) const
{
- int shadowLeft = 0;
- int shadowRight = 0;
- int shadowTop = 0;
- int shadowBottom = 0;
+ LayoutUnit shadowLeft;
+ LayoutUnit shadowRight;
+ LayoutUnit shadowTop;
+ LayoutUnit shadowBottom;
calculateShadowExtent(this, additionalOutlineSize, shadowLeft, shadowRight, shadowTop, shadowBottom);
rect.move(shadowLeft, shadowTop);
void ShadowData::adjustRectForShadow(FloatRect& rect, int additionalOutlineSize) const
{
- int shadowLeft = 0;
- int shadowRight = 0;
- int shadowTop = 0;
- int shadowBottom = 0;
+ LayoutUnit shadowLeft = 0;
+ LayoutUnit shadowRight = 0;
+ LayoutUnit shadowTop = 0;
+ LayoutUnit shadowBottom = 0;
calculateShadowExtent(this, additionalOutlineSize, shadowLeft, shadowRight, shadowTop, shadowBottom);
rect.move(shadowLeft, shadowTop);
namespace WebCore {
-enum ShadowStyle { Normal, Inset };
+enum class ShadowStyle : uint8_t { Normal, Inset };
// This class holds information about shadows for the text-shadow and box-shadow properties.
class ShadowData {
WTF_MAKE_FAST_ALLOCATED;
public:
- ShadowData()
- : m_radius(0)
- , m_spread(0)
- , m_style(Normal)
- , m_isWebkitBoxShadow(false)
- {
- }
+ ShadowData() = default;
- ShadowData(const IntPoint& location, int radius, int spread, ShadowStyle style, bool isWebkitBoxShadow, const Color& color)
+ ShadowData(const LayoutPoint& location, int radius, LayoutUnit spread, ShadowStyle style, bool isWebkitBoxShadow, const Color& color)
: m_location(location)
- , m_radius(radius)
, m_spread(spread)
+ , m_radius(radius)
, m_color(color)
, m_style(style)
, m_isWebkitBoxShadow(isWebkitBoxShadow)
return !(*this == o);
}
- int x() const { return m_location.x(); }
- int y() const { return m_location.y(); }
- IntPoint location() const { return m_location; }
+ LayoutUnit x() const { return m_location.x(); }
+ LayoutUnit y() const { return m_location.y(); }
+ LayoutPoint location() const { return m_location; }
int radius() const { return m_radius; }
- int paintingExtent() const
+ LayoutUnit paintingExtent() const
{
// Blurring uses a Gaussian function whose std. deviation is m_radius/2, and which in theory
// extends to infinity. In 8-bit contexts, however, rounding causes the effect to become
// undetectable at around 1.4x the radius.
const float radiusExtentMultiplier = 1.4;
- return ceilf(m_radius * radiusExtentMultiplier);
+ return LayoutUnit(ceilf(m_radius * radiusExtentMultiplier));
}
- int spread() const { return m_spread; }
+ LayoutUnit spread() const { return m_spread; }
ShadowStyle style() const { return m_style; }
const Color& color() const { return m_color; }
bool isWebkitBoxShadow() const { return m_isWebkitBoxShadow; }
void adjustRectForShadow(FloatRect&, int additionalOutlineSize = 0) const;
private:
- IntPoint m_location;
- int m_radius; // This is the "blur radius", or twice the standard deviation of the Gaussian blur.
- int m_spread;
+ LayoutPoint m_location;
+ LayoutUnit m_spread;
+ int m_radius { 0 }; // This is the "blur radius", or twice the standard deviation of the Gaussian blur.
Color m_color;
- ShadowStyle m_style;
- bool m_isWebkitBoxShadow;
+ ShadowStyle m_style { ShadowStyle::Normal };
+ bool m_isWebkitBoxShadow { false };
std::unique_ptr<ShadowData> m_next;
};
for (auto& item : downcast<CSSValueList>(value)) {
auto& shadowValue = downcast<CSSShadowValue>(item.get());
auto conversionData = builderState.cssToLengthConversionData();
- int x = shadowValue.x->computeLength<int>(conversionData);
- int y = shadowValue.y->computeLength<int>(conversionData);
+ auto x = shadowValue.x->computeLength<LayoutUnit>(conversionData);
+ auto y = shadowValue.y->computeLength<LayoutUnit>(conversionData);
int blur = shadowValue.blur ? shadowValue.blur->computeLength<int>(conversionData) : 0;
- int spread = shadowValue.spread ? shadowValue.spread->computeLength<int>(conversionData) : 0;
- ShadowStyle shadowStyle = shadowValue.style && shadowValue.style->valueID() == CSSValueInset ? Inset : Normal;
+ auto spread = shadowValue.spread ? shadowValue.spread->computeLength<LayoutUnit>(conversionData) : LayoutUnit(0);
+ ShadowStyle shadowStyle = shadowValue.style && shadowValue.style->valueID() == CSSValueInset ? ShadowStyle::Inset : ShadowStyle::Normal;
Color color;
if (shadowValue.color)
color = builderState.colorFromPrimitiveValue(*shadowValue.color);
else
color = builderState.style().color();
- auto shadowData = makeUnique<ShadowData>(IntPoint(x, y), blur, spread, shadowStyle, property == CSSPropertyWebkitBoxShadow, color.isValid() ? color : Color::transparent);
+ auto shadowData = makeUnique<ShadowData>(LayoutPoint(x, y), blur, spread, shadowStyle, property == CSSPropertyWebkitBoxShadow, color.isValid() ? color : Color::transparent);
if (property == CSSPropertyTextShadow)
builderState.style().setTextShadow(WTFMove(shadowData), !isFirstEntry); // add to the list if this is not the first entry
else
fontPanel.shadowOpacity = 1;
[fontPanel toggleShadow];
EXPECT_WK_STREQ("baz", [webView selectedText]);
- EXPECT_WK_STREQ("rgb(0, 0, 0) 0px 1px 8px", textShadowAroundSelection());
+ EXPECT_WK_STREQ("rgb(0, 0, 0) 0px 1.25px 8px", textShadowAroundSelection());
{
NSShadow *shadow = [webView typingAttributes][NSShadowAttributeName];
EXPECT_EQ(shadow.shadowOffset.width, 0);
- EXPECT_EQ(shadow.shadowOffset.height, 1);
+ EXPECT_EQ(shadow.shadowOffset.height, 1.25);
EXPECT_EQ(shadow.shadowBlurRadius, 8);
EXPECT_TRUE([shadow.shadowColor isEqual:[NSColor colorWithRed:0 green:0 blue:0 alpha:1]]);
}
[fontPanel chooseUnderlineMenuItemWithTitle:@"single"];
[fontPanel chooseStrikeThroughMenuItemWithTitle:@"single"];
EXPECT_WK_STREQ("foo bar baz", [webView selectedText]);
- EXPECT_WK_STREQ("rgba(0, 0, 0, 0.2) 0px 1px 5px", textShadowAroundSelection());
+ EXPECT_WK_STREQ("rgba(0, 0, 0, 0.2) 0px 1.25px 5px", textShadowAroundSelection());
EXPECT_WK_STREQ("underline line-through", textDecorationsAroundSelection());
{
NSDictionary *typingAttributes = [webView typingAttributes];
NSShadow *shadow = typingAttributes[NSShadowAttributeName];
EXPECT_EQ(shadow.shadowOffset.width, 0);
- EXPECT_EQ(shadow.shadowOffset.height, 1);
+ EXPECT_EQ(shadow.shadowOffset.height, 1.25);
EXPECT_EQ(shadow.shadowBlurRadius, 5);
EXPECT_TRUE([shadow.shadowColor isEqual:[NSColor colorWithRed:0 green:0 blue:0 alpha:0.2]]);
}