void BackgroundImageGeometry::setRepeatY(const FillLayer& fillLayer, LayoutUnit unsnappedTileHeight, LayoutUnit snappedAvailableHeight, LayoutUnit unsnappedAvailableHeight, LayoutUnit extraOffset) { // We would like to identify the phase as a fraction of the image size in the // absence of snapping, then re-apply it to the snapped values. This is to // handle large positions. if (unsnappedTileHeight) { LayoutUnit computedYPosition = roundedMinimumValueForLength( fillLayer.yPosition(), unsnappedAvailableHeight); if (fillLayer.backgroundYOrigin() == BottomEdge) { float numberOfTilesInPosition = (snappedAvailableHeight - computedYPosition + extraOffset).toFloat() / unsnappedTileHeight.toFloat(); float fractionalPositionWithinTile = numberOfTilesInPosition - truncf(numberOfTilesInPosition); setPhaseY(LayoutUnit( roundf(fractionalPositionWithinTile * tileSize().height()))); } else { float numberOfTilesInPosition = (computedYPosition + extraOffset).toFloat() / unsnappedTileHeight.toFloat(); float fractionalPositionWithinTile = 1.0f - (numberOfTilesInPosition - truncf(numberOfTilesInPosition)); setPhaseY(LayoutUnit( roundf(fractionalPositionWithinTile * tileSize().height()))); } } else { setPhaseY(LayoutUnit()); } setSpaceSize(LayoutSize(spaceSize().width(), LayoutUnit())); }
void CSSToStyleMap::mapFillSize(CSSPropertyID, FillLayer& layer, const CSSValue& value) { if (value.isInitialValue()) { layer.setSize(FillLayer::initialFillSize(layer.type())); return; } if (!is<CSSPrimitiveValue>(value)) return; auto& primitiveValue = downcast<CSSPrimitiveValue>(value); FillSize fillSize; switch (primitiveValue.getValueID()) { case CSSValueContain: fillSize.type = Contain; break; case CSSValueCover: fillSize.type = Cover; break; default: ASSERT(fillSize.type == SizeLength); if (!convertToLengthSize(primitiveValue, m_resolver->state().cssToLengthConversionData(), fillSize.size)) return; break; } layer.setSize(fillSize); }
FixedBackgroundImageLayerAndroid::FixedBackgroundImageLayerAndroid(PassRefPtr<RenderStyle> aStyle, int w, int h) : LayerAndroid((RenderLayer*)0) , m_width(w) , m_height(h) { RefPtr<RenderStyle> style = aStyle; FillLayer* layers = style->accessBackgroundLayers(); StyleImage* styleImage = layers->image(); CachedImage* cachedImage = static_cast<StyleCachedImage*>(styleImage)->cachedImage(); WebCore::Image* image = cachedImage->image(); setContentsImage(image->nativeImageForCurrentFrame()); setSize(image->width(), image->height()); setIntrinsicallyComposited(true); SkLength left, top; left = SkLength::convertLength(style->backgroundXPosition()); top = SkLength::convertLength(style->backgroundYPosition()); BackgroundImagePositioning* position = new BackgroundImagePositioning(this); position->setRepeatX(style->backgroundRepeatX() != WebCore::NoRepeatFill); position->setRepeatY(style->backgroundRepeatY() != WebCore::NoRepeatFill); setFixedPosition(position); position->setPosition(left, top); #ifdef DEBUG_COUNT ClassTracker::instance()->increment("FixedBackgroundImageLayerAndroid"); #endif }
virtual void applyInitialValue(CSSStyleSelector* selector) const { FillLayer* currChild = (selector->style()->*m_accessLayers)(); (currChild->*m_set)((*m_initial)(m_fillLayerType)); for (currChild = currChild->next(); currChild; currChild = currChild->next()) (currChild->*m_clear)(); }
void CSSToStyleMap::mapFillYPosition(CSSPropertyID propertyID, FillLayer& layer, const CSSValue& value) { if (value.isInitialValue()) { layer.setYPosition(FillLayer::initialFillYPosition(layer.type())); return; } if (!is<CSSPrimitiveValue>(value)) return; auto* primitiveValue = &downcast<CSSPrimitiveValue>(value); Pair* pair = primitiveValue->getPairValue(); if (pair) { ASSERT_UNUSED(propertyID, propertyID == CSSPropertyBackgroundPositionY || propertyID == CSSPropertyWebkitMaskPositionY); primitiveValue = pair->second(); } Length length; if (primitiveValue->isLength()) length = primitiveValue->computeLength<Length>(m_resolver->state().cssToLengthConversionData()); else if (primitiveValue->isPercentage()) length = Length(primitiveValue->getDoubleValue(), Percent); else if (primitiveValue->isCalculatedPercentageWithLength()) length = Length(primitiveValue->cssCalcValue()->createCalculationValue(m_resolver->state().cssToLengthConversionData())); else return; layer.setYPosition(length); if (pair) layer.setBackgroundYOrigin(*pair->first()); }
void CSSToStyleMap::mapFillMaskSourceType(CSSPropertyID, FillLayer& layer, const CSSValue& value) { EMaskSourceType type = FillLayer::initialFillMaskSourceType(layer.type()); if (value.isInitialValue()) { layer.setMaskSourceType(type); return; } if (!is<CSSPrimitiveValue>(value)) return; switch (downcast<CSSPrimitiveValue>(value).getValueID()) { case CSSValueAlpha: type = EMaskSourceType::MaskAlpha; break; case CSSValueLuminance: type = EMaskSourceType::MaskLuminance; break; case CSSValueAuto: break; default: ASSERT_NOT_REACHED(); } layer.setMaskSourceType(type); }
void InlineFlowBoxPainter::paintFillLayers(const PaintInfo& paintInfo, const Color& c, const FillLayer& fillLayer, const LayoutRect& rect, SkXfermode::Mode op) { // FIXME: This should be a for loop or similar. It's a little non-trivial to do so, however, since the layers need to be // painted in reverse order. if (fillLayer.next()) paintFillLayers(paintInfo, c, *fillLayer.next(), rect, op); paintFillLayer(paintInfo, c, fillLayer, rect, op); }
void StyleResourceLoader::loadPendingImages(RenderStyle* style, ElementStyleResources& elementStyleResources) { if (elementStyleResources.pendingImageProperties().isEmpty()) return; PendingImagePropertyMap::const_iterator::Keys end = elementStyleResources.pendingImageProperties().end().keys(); for (PendingImagePropertyMap::const_iterator::Keys it = elementStyleResources.pendingImageProperties().begin().keys(); it != end; ++it) { CSSPropertyID currentProperty = *it; switch (currentProperty) { case CSSPropertyBackgroundImage: { for (FillLayer* backgroundLayer = &style->accessBackgroundLayers(); backgroundLayer; backgroundLayer = backgroundLayer->next()) { if (backgroundLayer->image() && backgroundLayer->image()->isPendingImage()) backgroundLayer->setImage(loadPendingImage(toStylePendingImage(backgroundLayer->image()), elementStyleResources.deviceScaleFactor())); } break; } case CSSPropertyCursor: { if (CursorList* cursorList = style->cursors()) { for (size_t i = 0; i < cursorList->size(); ++i) { CursorData& currentCursor = cursorList->at(i); if (StyleImage* image = currentCursor.image()) { if (image->isPendingImage()) currentCursor.setImage(loadPendingImage(toStylePendingImage(image), elementStyleResources.deviceScaleFactor())); } } } break; } case CSSPropertyListStyleImage: { if (style->listStyleImage() && style->listStyleImage()->isPendingImage()) style->setListStyleImage(loadPendingImage(toStylePendingImage(style->listStyleImage()), elementStyleResources.deviceScaleFactor())); break; } case CSSPropertyBorderImageSource: { if (style->borderImageSource() && style->borderImageSource()->isPendingImage()) style->setBorderImageSource(loadPendingImage(toStylePendingImage(style->borderImageSource()), elementStyleResources.deviceScaleFactor())); break; } case CSSPropertyWebkitMaskBoxImageSource: { if (style->maskBoxImageSource() && style->maskBoxImageSource()->isPendingImage()) style->setMaskBoxImageSource(loadPendingImage(toStylePendingImage(style->maskBoxImageSource()), elementStyleResources.deviceScaleFactor())); break; } case CSSPropertyWebkitMaskImage: { for (FillLayer* maskLayer = &style->accessMaskLayers(); maskLayer; maskLayer = maskLayer->next()) { if (maskLayer->image() && maskLayer->image()->isPendingImage()) maskLayer->setImage(loadPendingImage(toStylePendingImage(maskLayer->image()), elementStyleResources.deviceScaleFactor())); } break; } default: ASSERT_NOT_REACHED(); } } elementStyleResources.clearPendingImageProperties(); }
void CSSToStyleMap::mapFillImage(CSSPropertyID property, FillLayer& layer, CSSValue& value) { if (value.isInitialValue()) { layer.setImage(FillLayer::initialFillImage(layer.type())); return; } layer.setImage(styleImage(property, value)); }
void CSSToStyleMap::mapFillRepeatY(CSSPropertyID propertyID, FillLayer& layer, const CSSValue& value) { if (value.treatAsInitialValue(propertyID)) { layer.setRepeatY(FillLayer::initialFillRepeatY(layer.type())); return; } if (!is<CSSPrimitiveValue>(value)) return; layer.setRepeatY(downcast<CSSPrimitiveValue>(value)); }
void CSSToStyleMap::mapFillClip(CSSPropertyID, FillLayer& layer, const CSSValue& value) { if (value.isInitialValue()) { layer.setClip(FillLayer::initialFillClip(layer.type())); return; } if (!is<CSSPrimitiveValue>(value)) return; layer.setClip(downcast<CSSPrimitiveValue>(value)); }
void FillLayer::cullEmptyLayers() { FillLayer* next; for (FillLayer* p = this; p; p = next) { next = p->m_next; if (next && !next->isImageSet()) { delete next; p->m_next = 0; break; } } }
void FillAnnotationImpl::updateStyle(Style& style) const { Layer* layer = style.getLayer(layerID); if (!layer) { auto newLayer = std::make_unique<FillLayer>(layerID, AnnotationManager::SourceID); newLayer->setSourceLayer(layerID); layer = style.addLayer(std::move(newLayer), AnnotationManager::PointLayerID); } FillLayer* fillLayer = layer->as<FillLayer>(); fillLayer->setFillOpacity(annotation.opacity); fillLayer->setFillColor(annotation.color); fillLayer->setFillOutlineColor(annotation.outlineColor); }
virtual void applyValue(CSSStyleSelector* selector, CSSValue* value) const { FillLayer* currChild = (selector->style()->*m_accessLayers)(); FillLayer* prevChild = 0; if (value->isValueList()) { /* Walk each value and put it into a layer, creating new layers as needed. */ CSSValueList* valueList = static_cast<CSSValueList*>(value); for (unsigned int i = 0; i < valueList->length(); i++) { if (!currChild) { /* Need to make a new layer to hold this value */ currChild = new FillLayer(m_fillLayerType); prevChild->setNext(currChild); } (selector->*m_mapFill)(m_propertyId, currChild, valueList->itemWithoutBoundsCheck(i)); prevChild = currChild; currChild = currChild->next(); } } else { (selector->*m_mapFill)(m_propertyId, currChild, value); currChild = currChild->next(); } while (currChild) { /* Reset all remaining layers to not have the property set. */ (currChild->*m_clear)(); currChild = currChild->next(); } }
virtual void applyInheritValue(CSSStyleSelector* selector) const { FillLayer* currChild = (selector->style()->*m_accessLayers)(); FillLayer* prevChild = 0; const FillLayer* currParent = (selector->parentStyle()->*m_layers)(); while (currParent && (currParent->*m_test)()) { if (!currChild) { /* Need to make a new layer.*/ currChild = new FillLayer(m_fillLayerType); prevChild->setNext(currChild); } (currChild->*m_set)((currParent->*m_get)()); prevChild = currChild; currChild = prevChild->next(); currParent = currParent->next(); } while (currChild) { /* Reset any remaining layers to not have the property set. */ (currChild->*m_clear)(); currChild = currChild->next(); } }
Image* FixedBackgroundImageLayerAndroid::GetCachedImage(PassRefPtr<RenderStyle> aStyle) { RefPtr<RenderStyle> style = aStyle; if (!style) return 0; if (!style->hasFixedBackgroundImage()) return 0; FillLayer* layers = style->accessBackgroundLayers(); StyleImage* styleImage = layers->image(); if (!styleImage) return 0; if (!styleImage->isLoaded()) return 0; if (!styleImage->isCachedImage()) return 0; CachedImage* cachedImage = static_cast<StyleCachedImage*>(styleImage)->cachedImage(); Image* image = cachedImage->image(); bool willPaintBrokenImage = cachedImage->willPaintBrokenImage(); //SAMSUNG_CHANGES -- MPSG6322 & MPSG6356 if (image && !image->nativeImageForCurrentFrame()) return 0; //SAMSUNG_CHANGES -- MPSG6322 & MPSG6356 //WAS: if (image == Image::nullImage()) if (image == Image::nullImage() || willPaintBrokenImage) return 0; return image; }
void CSSToStyleMap::mapFillAttachment(CSSPropertyID, FillLayer& layer, const CSSValue& value) { if (value.isInitialValue()) { layer.setAttachment(FillLayer::initialFillAttachment(layer.type())); return; } if (!is<CSSPrimitiveValue>(value)) return; switch (downcast<CSSPrimitiveValue>(value).getValueID()) { case CSSValueFixed: layer.setAttachment(FixedBackgroundAttachment); break; case CSSValueScroll: layer.setAttachment(ScrollBackgroundAttachment); break; case CSSValueLocal: layer.setAttachment(LocalBackgroundAttachment); break; default: return; } }
void SizeListPropertyFunctions::setSizeList(CSSPropertyID property, ComputedStyle& style, const SizeList& sizeList) { FillLayer* fillLayer = accessFillLayer(property, style); FillLayer* prev = nullptr; for (const FillSize& size : sizeList) { if (!fillLayer) fillLayer = prev->ensureNext(); fillLayer->setSize(size); prev = fillLayer; fillLayer = fillLayer->next(); } while (fillLayer) { fillLayer->clearSize(); fillLayer = fillLayer->next(); } }
static bool isBackgroundOrBorderStyled(const RenderStyle& style, const CachedUAStyle& uaStyle) { // Code below excludes the background-repeat from comparison by resetting it FillLayer backgroundCopy = uaStyle.backgroundLayers; FillLayer backgroundLayersCopy = *style.backgroundLayers(); backgroundCopy.setRepeatX(NoRepeatFill); backgroundCopy.setRepeatY(NoRepeatFill); backgroundLayersCopy.setRepeatX(NoRepeatFill); backgroundLayersCopy.setRepeatY(NoRepeatFill); // Test the style to see if the UA border and background match. return style.border() != uaStyle.border || backgroundLayersCopy != backgroundCopy || style.visitedDependentColor(CSSPropertyBackgroundColor) != uaStyle.backgroundColor; }
void InlineFlowBoxPainter::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const FillLayer& fillLayer, const LayoutRect& rect, SkXfermode::Mode op) { StyleImage* img = fillLayer.image(); bool hasFillImage = img && img->canRender(m_inlineFlowBox.layoutObject(), m_inlineFlowBox.layoutObject().style()->effectiveZoom()); if ((!hasFillImage && !m_inlineFlowBox.layoutObject().style()->hasBorderRadius()) || (!m_inlineFlowBox.prevLineBox() && !m_inlineFlowBox.nextLineBox()) || !m_inlineFlowBox.parent()) { BoxPainter::paintFillLayerExtended(*m_inlineFlowBox.boxModelObject(), paintInfo, c, fillLayer, rect, BackgroundBleedNone, &m_inlineFlowBox, rect.size(), op); } else if (m_inlineFlowBox.layoutObject().style()->boxDecorationBreak() == DCLONE) { GraphicsContextStateSaver stateSaver(*paintInfo.context); paintInfo.context->clip(LayoutRect(rect.x(), rect.y(), m_inlineFlowBox.width(), m_inlineFlowBox.height())); BoxPainter::paintFillLayerExtended(*m_inlineFlowBox.boxModelObject(), paintInfo, c, fillLayer, rect, BackgroundBleedNone, &m_inlineFlowBox, rect.size(), op); } else { // We have a fill image that spans multiple lines. // FIXME: frameSize ought to be the same as rect.size(). LayoutSize frameSize(m_inlineFlowBox.width().toLayoutUnit(), m_inlineFlowBox.height().toLayoutUnit()); LayoutRect imageStripPaintRect = paintRectForImageStrip(rect.location(), frameSize, m_inlineFlowBox.layoutObject().style()->direction()); GraphicsContextStateSaver stateSaver(*paintInfo.context); paintInfo.context->clip(LayoutRect(rect.x(), rect.y(), m_inlineFlowBox.width(), m_inlineFlowBox.height())); BoxPainter::paintFillLayerExtended(*m_inlineFlowBox.boxModelObject(), paintInfo, c, fillLayer, imageStripPaintRect, BackgroundBleedNone, &m_inlineFlowBox, rect.size(), op); } }
void InlineFlowBoxPainter::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const FillLayer& fillLayer, const LayoutRect& rect, SkXfermode::Mode op) { LayoutBoxModelObject* boxModel = toLayoutBoxModelObject(LineLayoutAPIShim::layoutObjectFrom(m_inlineFlowBox.boxModelObject())); StyleImage* img = fillLayer.image(); bool hasFillImage = img && img->canRender(); if ((!hasFillImage && !m_inlineFlowBox.lineLayoutItem().style()->hasBorderRadius()) || (!m_inlineFlowBox.prevLineBox() && !m_inlineFlowBox.nextLineBox()) || !m_inlineFlowBox.parent()) { BoxPainter::paintFillLayer(*boxModel, paintInfo, c, fillLayer, rect, BackgroundBleedNone, &m_inlineFlowBox, rect.size(), op); } else if (m_inlineFlowBox.lineLayoutItem().style()->boxDecorationBreak() == DCLONE) { GraphicsContextStateSaver stateSaver(paintInfo.context); paintInfo.context.clip(pixelSnappedIntRect(rect)); BoxPainter::paintFillLayer(*boxModel, paintInfo, c, fillLayer, rect, BackgroundBleedNone, &m_inlineFlowBox, rect.size(), op); } else { // We have a fill image that spans multiple lines. // FIXME: frameSize ought to be the same as rect.size(). LayoutSize frameSize(m_inlineFlowBox.width(), m_inlineFlowBox.height()); LayoutRect imageStripPaintRect = paintRectForImageStrip(rect.location(), frameSize, m_inlineFlowBox.lineLayoutItem().style()->direction()); GraphicsContextStateSaver stateSaver(paintInfo.context); // TODO(chrishtr): this should likely be pixel-snapped. paintInfo.context.clip(pixelSnappedIntRect(rect)); BoxPainter::paintFillLayer(*boxModel, paintInfo, c, fillLayer, imageStripPaintRect, BackgroundBleedNone, &m_inlineFlowBox, rect.size(), op); } }
static inline bool layerImagesIdentical(const FillLayer& layer1, const FillLayer& layer2) { // We just care about pointer equivalency. return layer1.image() == layer2.image(); }
void StyleResourceLoader::loadPendingImages(RenderStyle* style, const ElementStyleResources& elementStyleResources) { if (elementStyleResources.pendingImageProperties().isEmpty()) return; PendingImagePropertyMap::const_iterator::Keys end = elementStyleResources.pendingImageProperties().end().keys(); for (PendingImagePropertyMap::const_iterator::Keys it = elementStyleResources.pendingImageProperties().begin().keys(); it != end; ++it) { CSSPropertyID currentProperty = *it; switch (currentProperty) { case CSSPropertyBackgroundImage: { for (FillLayer* backgroundLayer = style->accessBackgroundLayers(); backgroundLayer; backgroundLayer = backgroundLayer->next()) { if (backgroundLayer->image() && backgroundLayer->image()->isPendingImage()) backgroundLayer->setImage(loadPendingImage(static_cast<StylePendingImage*>(backgroundLayer->image()), elementStyleResources.deviceScaleFactor())); } break; } case CSSPropertyContent: { for (ContentData* contentData = const_cast<ContentData*>(style->contentData()); contentData; contentData = contentData->next()) { if (contentData->isImage()) { StyleImage* image = static_cast<ImageContentData*>(contentData)->image(); if (image->isPendingImage()) { RefPtr<StyleImage> loadedImage = loadPendingImage(static_cast<StylePendingImage*>(image), elementStyleResources.deviceScaleFactor()); if (loadedImage) static_cast<ImageContentData*>(contentData)->setImage(loadedImage.release()); } } } break; } case CSSPropertyCursor: { if (CursorList* cursorList = style->cursors()) { for (size_t i = 0; i < cursorList->size(); ++i) { CursorData& currentCursor = cursorList->at(i); if (StyleImage* image = currentCursor.image()) { if (image->isPendingImage()) currentCursor.setImage(loadPendingImage(static_cast<StylePendingImage*>(image), elementStyleResources.deviceScaleFactor())); } } } break; } case CSSPropertyListStyleImage: { if (style->listStyleImage() && style->listStyleImage()->isPendingImage()) style->setListStyleImage(loadPendingImage(static_cast<StylePendingImage*>(style->listStyleImage()), elementStyleResources.deviceScaleFactor())); break; } case CSSPropertyBorderImageSource: { if (style->borderImageSource() && style->borderImageSource()->isPendingImage()) style->setBorderImageSource(loadPendingImage(static_cast<StylePendingImage*>(style->borderImageSource()), elementStyleResources.deviceScaleFactor())); break; } case CSSPropertyWebkitBoxReflect: { if (StyleReflection* reflection = style->boxReflect()) { const NinePieceImage& maskImage = reflection->mask(); if (maskImage.image() && maskImage.image()->isPendingImage()) { RefPtr<StyleImage> loadedImage = loadPendingImage(static_cast<StylePendingImage*>(maskImage.image()), elementStyleResources.deviceScaleFactor()); reflection->setMask(NinePieceImage(loadedImage.release(), maskImage.imageSlices(), maskImage.fill(), maskImage.borderSlices(), maskImage.outset(), maskImage.horizontalRule(), maskImage.verticalRule())); } } break; } case CSSPropertyWebkitMaskBoxImageSource: { if (style->maskBoxImageSource() && style->maskBoxImageSource()->isPendingImage()) style->setMaskBoxImageSource(loadPendingImage(static_cast<StylePendingImage*>(style->maskBoxImageSource()), elementStyleResources.deviceScaleFactor())); break; } case CSSPropertyWebkitMaskImage: { for (FillLayer* maskLayer = style->accessMaskLayers(); maskLayer; maskLayer = maskLayer->next()) { if (maskLayer->image() && maskLayer->image()->isPendingImage()) maskLayer->setImage(loadPendingImage(static_cast<StylePendingImage*>(maskLayer->image()), elementStyleResources.deviceScaleFactor())); } break; } case CSSPropertyWebkitShapeInside: loadPendingShapeImage(style, style->shapeInside()); break; case CSSPropertyWebkitShapeOutside: loadPendingShapeImage(style, style->shapeOutside()); break; default: ASSERT_NOT_REACHED(); } } }
void FillLayer::fillUnsetProperties() { FillLayer* curr; for (curr = this; curr && curr->isXPositionSet(); curr = curr->next()) { } if (curr && curr != this) { // We need to fill in the remaining values with the pattern specified. for (FillLayer* pattern = this; curr; curr = curr->next()) { curr->m_xPosition = pattern->m_xPosition; if (pattern->isBackgroundOriginSet()) { curr->m_backgroundXOrigin = pattern->m_backgroundXOrigin; curr->m_backgroundYOrigin = pattern->m_backgroundYOrigin; } pattern = pattern->next(); if (pattern == curr || !pattern) pattern = this; } } for (curr = this; curr && curr->isYPositionSet(); curr = curr->next()) { } if (curr && curr != this) { // We need to fill in the remaining values with the pattern specified. for (FillLayer* pattern = this; curr; curr = curr->next()) { curr->m_yPosition = pattern->m_yPosition; if (pattern->isBackgroundOriginSet()) { curr->m_backgroundXOrigin = pattern->m_backgroundXOrigin; curr->m_backgroundYOrigin = pattern->m_backgroundYOrigin; } pattern = pattern->next(); if (pattern == curr || !pattern) pattern = this; } } for (curr = this; curr && curr->isAttachmentSet(); curr = curr->next()) { } if (curr && curr != this) { // We need to fill in the remaining values with the pattern specified. for (FillLayer* pattern = this; curr; curr = curr->next()) { curr->m_attachment = pattern->m_attachment; pattern = pattern->next(); if (pattern == curr || !pattern) pattern = this; } } for (curr = this; curr && curr->isClipSet(); curr = curr->next()) { } if (curr && curr != this) { // We need to fill in the remaining values with the pattern specified. for (FillLayer* pattern = this; curr; curr = curr->next()) { curr->m_clip = pattern->m_clip; pattern = pattern->next(); if (pattern == curr || !pattern) pattern = this; } } for (curr = this; curr && curr->isCompositeSet(); curr = curr->next()) { } if (curr && curr != this) { // We need to fill in the remaining values with the pattern specified. for (FillLayer* pattern = this; curr; curr = curr->next()) { curr->m_composite = pattern->m_composite; pattern = pattern->next(); if (pattern == curr || !pattern) pattern = this; } } for (curr = this; curr && curr->isBlendModeSet(); curr = curr->next()) { } if (curr && curr != this) { // We need to fill in the remaining values with the pattern specified. for (FillLayer* pattern = this; curr; curr = curr->next()) { curr->m_blendMode = pattern->m_blendMode; pattern = pattern->next(); if (pattern == curr || !pattern) pattern = this; } } for (curr = this; curr && curr->isOriginSet(); curr = curr->next()) { } if (curr && curr != this) { // We need to fill in the remaining values with the pattern specified. for (FillLayer* pattern = this; curr; curr = curr->next()) { curr->m_origin = pattern->m_origin; pattern = pattern->next(); if (pattern == curr || !pattern) pattern = this; } } for (curr = this; curr && curr->isRepeatXSet(); curr = curr->next()) { } if (curr && curr != this) { // We need to fill in the remaining values with the pattern specified. for (FillLayer* pattern = this; curr; curr = curr->next()) { curr->m_repeatX = pattern->m_repeatX; pattern = pattern->next(); if (pattern == curr || !pattern) pattern = this; } } for (curr = this; curr && curr->isRepeatYSet(); curr = curr->next()) { } if (curr && curr != this) { // We need to fill in the remaining values with the pattern specified. for (FillLayer* pattern = this; curr; curr = curr->next()) { curr->m_repeatY = pattern->m_repeatY; pattern = pattern->next(); if (pattern == curr || !pattern) pattern = this; } } for (curr = this; curr && curr->isSizeSet(); curr = curr->next()) { } if (curr && curr != this) { // We need to fill in the remaining values with the pattern specified. for (FillLayer* pattern = this; curr; curr = curr->next()) { curr->m_sizeType = pattern->m_sizeType; curr->m_sizeLength = pattern->m_sizeLength; pattern = pattern->next(); if (pattern == curr || !pattern) pattern = this; } } }
void BackgroundImageGeometry::calculate( const LayoutBoxModelObject& obj, const LayoutBoxModelObject* paintContainer, const GlobalPaintFlags globalPaintFlags, const FillLayer& fillLayer, const LayoutRect& paintRect) { LayoutUnit left; LayoutUnit top; LayoutSize positioningAreaSize; bool isLayoutView = obj.isLayoutView(); const LayoutBox* rootBox = nullptr; if (isLayoutView) { // It is only possible reach here when root element has a box. Element* documentElement = obj.document().documentElement(); DCHECK(documentElement); DCHECK(documentElement->layoutObject()); DCHECK(documentElement->layoutObject()->isBox()); rootBox = toLayoutBox(documentElement->layoutObject()); } const LayoutBoxModelObject& positioningBox = isLayoutView ? static_cast<const LayoutBoxModelObject&>(*rootBox) : obj; // Determine the background positioning area and set destRect to the // background painting area. destRect will be adjusted later if the // background is non-repeating. // FIXME: transforms spec says that fixed backgrounds behave like scroll // inside transforms. bool fixedAttachment = fillLayer.attachment() == FixedBackgroundAttachment; if (RuntimeEnabledFeatures::fastMobileScrollingEnabled()) { // As a side effect of an optimization to blit on scroll, we do not honor // the CSS property "background-attachment: fixed" because it may result in // rendering artifacts. Note, these artifacts only appear if we are blitting // on scroll of a page that has fixed background images. fixedAttachment = false; } if (!fixedAttachment) { setDestRect(paintRect); LayoutUnit right; LayoutUnit bottom; // Scroll and Local. if (fillLayer.origin() != BorderFillBox) { left = LayoutUnit(positioningBox.borderLeft()); right = LayoutUnit(positioningBox.borderRight()); top = LayoutUnit(positioningBox.borderTop()); bottom = LayoutUnit(positioningBox.borderBottom()); if (fillLayer.origin() == ContentFillBox) { left += positioningBox.paddingLeft(); right += positioningBox.paddingRight(); top += positioningBox.paddingTop(); bottom += positioningBox.paddingBottom(); } } if (isLayoutView) { // The background of the box generated by the root element covers the // entire canvas and will be painted by the view object, but the we should // still use the root element box for positioning. positioningAreaSize = rootBox->size() - LayoutSize(left + right, top + bottom), rootBox->location(); // The input paint rect is specified in root element local coordinate // (i.e. a transform is applied on the context for painting), and is // expanded to cover the whole canvas. Since left/top is relative to the // paint rect, we need to offset them back. left -= paintRect.x(); top -= paintRect.y(); } else { positioningAreaSize = paintRect.size() - LayoutSize(left + right, top + bottom); } } else { setHasNonLocalGeometry(); LayoutRect viewportRect = obj.viewRect(); if (fixedBackgroundPaintsInLocalCoordinates(obj, globalPaintFlags)) { viewportRect.setLocation(LayoutPoint()); } else { if (FrameView* frameView = obj.view()->frameView()) viewportRect.setLocation(IntPoint(frameView->scrollOffsetInt())); // Compensate the translations created by ScrollRecorders. // TODO(trchen): Fix this for SP phase 2. crbug.com/529963. viewportRect.moveBy( accumulatedScrollOffsetForFixedBackground(obj, paintContainer)); } if (paintContainer) viewportRect.moveBy( LayoutPoint(-paintContainer->localToAbsolute(FloatPoint()))); setDestRect(viewportRect); positioningAreaSize = destRect().size(); } LayoutSize fillTileSize( calculateFillTileSize(positioningBox, fillLayer, positioningAreaSize)); // It's necessary to apply the heuristic here prior to any further // calculations to avoid incorrectly using sub-pixel values that won't be // present in the painted tile. setTileSize(applySubPixelHeuristicToImageSize(fillTileSize, m_destRect)); EFillRepeat backgroundRepeatX = fillLayer.repeatX(); EFillRepeat backgroundRepeatY = fillLayer.repeatY(); LayoutUnit unsnappedAvailableWidth = positioningAreaSize.width() - fillTileSize.width(); LayoutUnit unsnappedAvailableHeight = positioningAreaSize.height() - fillTileSize.height(); positioningAreaSize = LayoutSize(snapSizeToPixel(positioningAreaSize.width(), m_destRect.x()), snapSizeToPixel(positioningAreaSize.height(), m_destRect.y())); LayoutUnit availableWidth = positioningAreaSize.width() - tileSize().width(); LayoutUnit availableHeight = positioningAreaSize.height() - tileSize().height(); LayoutUnit computedXPosition = roundedMinimumValueForLength(fillLayer.xPosition(), availableWidth); if (backgroundRepeatX == RoundFill && positioningAreaSize.width() > LayoutUnit() && fillTileSize.width() > LayoutUnit()) { int nrTiles = std::max( 1, roundToInt(positioningAreaSize.width() / fillTileSize.width())); LayoutUnit roundedWidth = positioningAreaSize.width() / nrTiles; // Maintain aspect ratio if background-size: auto is set if (fillLayer.size().size.height().isAuto() && backgroundRepeatY != RoundFill) { fillTileSize.setHeight(fillTileSize.height() * roundedWidth / fillTileSize.width()); } fillTileSize.setWidth(roundedWidth); setTileSize(applySubPixelHeuristicToImageSize(fillTileSize, m_destRect)); setPhaseX(tileSize().width() ? LayoutUnit(roundf( tileSize().width() - fmodf((computedXPosition + left), tileSize().width()))) : LayoutUnit()); setSpaceSize(LayoutSize()); } LayoutUnit computedYPosition = roundedMinimumValueForLength(fillLayer.yPosition(), availableHeight); if (backgroundRepeatY == RoundFill && positioningAreaSize.height() > LayoutUnit() && fillTileSize.height() > LayoutUnit()) { int nrTiles = std::max( 1, roundToInt(positioningAreaSize.height() / fillTileSize.height())); LayoutUnit roundedHeight = positioningAreaSize.height() / nrTiles; // Maintain aspect ratio if background-size: auto is set if (fillLayer.size().size.width().isAuto() && backgroundRepeatX != RoundFill) { fillTileSize.setWidth(fillTileSize.width() * roundedHeight / fillTileSize.height()); } fillTileSize.setHeight(roundedHeight); setTileSize(applySubPixelHeuristicToImageSize(fillTileSize, m_destRect)); setPhaseY(tileSize().height() ? LayoutUnit(roundf( tileSize().height() - fmodf((computedYPosition + top), tileSize().height()))) : LayoutUnit()); setSpaceSize(LayoutSize()); } if (backgroundRepeatX == RepeatFill) { setRepeatX(fillLayer, fillTileSize.width(), availableWidth, unsnappedAvailableWidth, left); } else if (backgroundRepeatX == SpaceFill && tileSize().width() > LayoutUnit()) { LayoutUnit space = getSpaceBetweenImageTiles(positioningAreaSize.width(), tileSize().width()); if (space >= LayoutUnit()) setSpaceX(space, availableWidth, left); else backgroundRepeatX = NoRepeatFill; } if (backgroundRepeatX == NoRepeatFill) { LayoutUnit xOffset = fillLayer.backgroundXOrigin() == RightEdge ? availableWidth - computedXPosition : computedXPosition; setNoRepeatX(left + xOffset); } if (backgroundRepeatY == RepeatFill) { setRepeatY(fillLayer, fillTileSize.height(), availableHeight, unsnappedAvailableHeight, top); } else if (backgroundRepeatY == SpaceFill && tileSize().height() > LayoutUnit()) { LayoutUnit space = getSpaceBetweenImageTiles(positioningAreaSize.height(), tileSize().height()); if (space >= LayoutUnit()) setSpaceY(space, availableHeight, top); else backgroundRepeatY = NoRepeatFill; } if (backgroundRepeatY == NoRepeatFill) { LayoutUnit yOffset = fillLayer.backgroundYOrigin() == BottomEdge ? availableHeight - computedYPosition : computedYPosition; setNoRepeatY(top + yOffset); } if (fixedAttachment) useFixedAttachment(paintRect.location()); // Clip the final output rect to the paint rect m_destRect.intersect(paintRect); // Snap as-yet unsnapped values. setDestRect(LayoutRect(pixelSnappedIntRect(m_destRect))); }
void ElementStyleResources::loadPendingImages(ComputedStyle* style) { // We must loop over the properties and then look at the style to see if // a pending image exists, and only load that image. For example: // // <style> // div { background-image: url(a.png); } // div { background-image: url(b.png); } // div { background-image: none; } // </style> // <div></div> // // We call styleImage() for both a.png and b.png adding the // CSSPropertyBackgroundImage property to the m_pendingImageProperties set, // then we null out the background image because of the "none". // // If we eagerly loaded the images we'd fetch a.png, even though it's not // used. If we didn't null check below we'd crash since the none actually // removed all background images. for (CSSPropertyID property : m_pendingImageProperties) { switch (property) { case CSSPropertyBackgroundImage: { for (FillLayer* backgroundLayer = &style->accessBackgroundLayers(); backgroundLayer; backgroundLayer = backgroundLayer->next()) { if (backgroundLayer->image() && backgroundLayer->image()->isPendingImage()) backgroundLayer->setImage(loadPendingImage(toStylePendingImage(backgroundLayer->image()))); } break; } case CSSPropertyContent: { for (ContentData* contentData = const_cast<ContentData*>(style->contentData()); contentData; contentData = contentData->next()) { if (contentData->isImage()) { StyleImage* image = toImageContentData(contentData)->image(); if (image->isPendingImage()) toImageContentData(contentData)->setImage(loadPendingImage(toStylePendingImage(image))); } } break; } case CSSPropertyCursor: { if (CursorList* cursorList = style->cursors()) { for (size_t i = 0; i < cursorList->size(); ++i) { CursorData& currentCursor = cursorList->at(i); if (StyleImage* image = currentCursor.image()) { if (image->isPendingImage()) currentCursor.setImage(loadPendingImage(toStylePendingImage(image))); } } } break; } case CSSPropertyListStyleImage: { if (style->listStyleImage() && style->listStyleImage()->isPendingImage()) style->setListStyleImage(loadPendingImage(toStylePendingImage(style->listStyleImage()))); break; } case CSSPropertyBorderImageSource: { if (style->borderImageSource() && style->borderImageSource()->isPendingImage()) style->setBorderImageSource(loadPendingImage(toStylePendingImage(style->borderImageSource()))); break; } case CSSPropertyWebkitBoxReflect: { if (StyleReflection* reflection = style->boxReflect()) { const NinePieceImage& maskImage = reflection->mask(); if (maskImage.image() && maskImage.image()->isPendingImage()) { RefPtrWillBeRawPtr<StyleImage> loadedImage = loadPendingImage(toStylePendingImage(maskImage.image())); reflection->setMask(NinePieceImage(loadedImage.release(), maskImage.imageSlices(), maskImage.fill(), maskImage.borderSlices(), maskImage.outset(), maskImage.horizontalRule(), maskImage.verticalRule())); } } break; } case CSSPropertyWebkitMaskBoxImageSource: { if (style->maskBoxImageSource() && style->maskBoxImageSource()->isPendingImage()) style->setMaskBoxImageSource(loadPendingImage(toStylePendingImage(style->maskBoxImageSource()))); break; } case CSSPropertyWebkitMaskImage: { for (FillLayer* maskLayer = &style->accessMaskLayers(); maskLayer; maskLayer = maskLayer->next()) { if (maskLayer->image() && maskLayer->image()->isPendingImage()) maskLayer->setImage(loadPendingImage(toStylePendingImage(maskLayer->image()))); } break; } case CSSPropertyShapeOutside: if (style->shapeOutside() && style->shapeOutside()->image() && style->shapeOutside()->image()->isPendingImage()) style->shapeOutside()->setImage(loadPendingImage(toStylePendingImage(style->shapeOutside()->image()), CrossOriginAttributeAnonymous)); break; default: ASSERT_NOT_REACHED(); } } }
static void loadPendingImages(const PendingResources& pendingResources, Document& document, RenderStyle& style, const Element* element) { for (auto currentProperty : pendingResources.pendingImages.keys()) { switch (currentProperty) { case CSSPropertyBackgroundImage: { for (FillLayer* backgroundLayer = &style.ensureBackgroundLayers(); backgroundLayer; backgroundLayer = backgroundLayer->next()) { auto* styleImage = backgroundLayer->image(); if (is<StylePendingImage>(styleImage)) backgroundLayer->setImage(loadPendingImage(document, *styleImage, element)); } break; } case CSSPropertyContent: { for (ContentData* contentData = const_cast<ContentData*>(style.contentData()); contentData; contentData = contentData->next()) { if (is<ImageContentData>(*contentData)) { auto& styleImage = downcast<ImageContentData>(*contentData).image(); if (is<StylePendingImage>(styleImage)) { if (auto loadedImage = loadPendingImage(document, styleImage, element)) downcast<ImageContentData>(*contentData).setImage(WTFMove(loadedImage)); } } } break; } case CSSPropertyCursor: { if (CursorList* cursorList = style.cursors()) { for (size_t i = 0; i < cursorList->size(); ++i) { CursorData& currentCursor = cursorList->at(i); auto* styleImage = currentCursor.image(); if (is<StylePendingImage>(styleImage)) currentCursor.setImage(loadPendingImage(document, *styleImage, element)); } } break; } case CSSPropertyListStyleImage: { auto* styleImage = style.listStyleImage(); if (is<StylePendingImage>(styleImage)) style.setListStyleImage(loadPendingImage(document, *styleImage, element)); break; } case CSSPropertyBorderImageSource: { auto* styleImage = style.borderImageSource(); if (is<StylePendingImage>(styleImage)) style.setBorderImageSource(loadPendingImage(document, *styleImage, element)); break; } case CSSPropertyWebkitBoxReflect: { if (StyleReflection* reflection = style.boxReflect()) { const NinePieceImage& maskImage = reflection->mask(); auto* styleImage = maskImage.image(); if (is<StylePendingImage>(styleImage)) { auto loadedImage = loadPendingImage(document, *styleImage, element); reflection->setMask(NinePieceImage(WTFMove(loadedImage), maskImage.imageSlices(), maskImage.fill(), maskImage.borderSlices(), maskImage.outset(), maskImage.horizontalRule(), maskImage.verticalRule())); } } break; } case CSSPropertyWebkitMaskBoxImageSource: { auto* styleImage = style.maskBoxImageSource(); if (is<StylePendingImage>(styleImage)) style.setMaskBoxImageSource(loadPendingImage(document, *styleImage, element)); break; } case CSSPropertyWebkitMaskImage: { for (FillLayer* maskLayer = &style.ensureMaskLayers(); maskLayer; maskLayer = maskLayer->next()) { auto* styleImage = maskLayer->image(); if (is<StylePendingImage>(styleImage)) maskLayer->setImage(loadPendingImage(document, *styleImage, element)); } break; } #if ENABLE(CSS_SHAPES) case CSSPropertyWebkitShapeOutside: { if (!style.shapeOutside()) return; StyleImage* image = style.shapeOutside()->image(); if (is<StylePendingImage>(image)) style.shapeOutside()->setImage(loadPendingImage(document, *image, element, LoadPolicy::ShapeOutside)); break; } #endif default: ASSERT_NOT_REACHED(); } } }
void FillLayer::cullEmptyLayers() { FillLayer* next; for (FillLayer* p = this; p; p = next) { next = p->m_next; if (next && !next->isImageSet() && !next->isXPositionSet() && !next->isYPositionSet() && !next->isAttachmentSet() && !next->isClipSet() && !next->isCompositeSet() && !next->isOriginSet() && !next->isRepeatXSet() && !next->isRepeatYSet() && !next->isSizeSet()) { delete next; p->m_next = 0; break; } } }