FloatQuad LayoutGeometryMap::mapToAncestor(const FloatRect& rect, const LayoutBoxModelObject* ancestor) const { FloatQuad result; if (!hasFixedPositionStep() && !hasTransformStep() && !hasNonUniformStep() && (!ancestor || (m_mapping.size() && ancestor == m_mapping[0].m_layoutObject))) { result = rect; result.move(m_accumulatedOffset); } else { TransformState transformState(TransformState::ApplyTransformDirection, rect.center(), rect); mapToAncestor(transformState, ancestor); result = transformState.lastPlanarQuad(); } #if ENABLE(ASSERT) if (m_mapping.size() > 0) { const LayoutObject* lastLayoutObject = m_mapping.last().m_layoutObject; FloatRect layoutObjectMappedResult = lastLayoutObject->localToAncestorQuad(rect, ancestor, m_mapCoordinatesFlags).boundingBox(); // Inspector creates layoutObjects with negative width <https://bugs.webkit.org/show_bug.cgi?id=87194>. // Taking FloatQuad bounds avoids spurious assertions because of that. ASSERT(enclosingIntRect(layoutObjectMappedResult) == enclosingIntRect(result.boundingBox()) || layoutObjectMappedResult.mayNotHaveExactIntRectRepresentation() || result.boundingBox().mayNotHaveExactIntRectRepresentation()); } #endif return result; }
void CCRenderSurface::drawLayer(LayerRendererChromium* layerRenderer, CCLayerImpl* maskLayer, const TransformationMatrix& drawTransform, int contentsTextureId) { TransformationMatrix deviceMatrix = computeDeviceTransform(layerRenderer, drawTransform); // Can only draw surface if device matrix is invertible. if (!deviceMatrix.isInvertible()) return; // Draw the background texture if there is one. if (m_backgroundTexture && m_backgroundTexture->isReserved()) copyTextureToFramebuffer(layerRenderer, m_backgroundTexture->textureId(), m_contentRect.size(), drawTransform); FloatQuad quad = deviceMatrix.mapQuad(layerRenderer->sharedGeometryQuad()); CCLayerQuad deviceRect = CCLayerQuad(FloatQuad(quad.boundingBox())); CCLayerQuad layerQuad = CCLayerQuad(quad); // Use anti-aliasing programs only when necessary. bool useAA = (!quad.isRectilinear() || !quad.boundingBox().isExpressibleAsIntRect()); if (useAA) { deviceRect.inflateAntiAliasingDistance(); layerQuad.inflateAntiAliasingDistance(); } bool useMask = false; if (maskLayer && maskLayer->drawsContent()) if (!maskLayer->bounds().isEmpty()) useMask = true; // FIXME: pass in backgroundTextureId and blend the background in with this draw instead of having a separate drawBackground() pass. if (useMask) { if (useAA) { const LayerRendererChromium::RenderSurfaceMaskProgramAA* program = layerRenderer->renderSurfaceMaskProgramAA(); drawSurface(layerRenderer, maskLayer, drawTransform, deviceMatrix, deviceRect, layerQuad, contentsTextureId, program, program->fragmentShader().maskSamplerLocation(), program->vertexShader().pointLocation(), program->fragmentShader().edgeLocation()); } else { const LayerRendererChromium::RenderSurfaceMaskProgram* program = layerRenderer->renderSurfaceMaskProgram(); drawSurface(layerRenderer, maskLayer, drawTransform, deviceMatrix, deviceRect, layerQuad, contentsTextureId, program, program->fragmentShader().maskSamplerLocation(), -1, -1); } } else { if (useAA) { const LayerRendererChromium::RenderSurfaceProgramAA* program = layerRenderer->renderSurfaceProgramAA(); drawSurface(layerRenderer, maskLayer, drawTransform, deviceMatrix, deviceRect, layerQuad, contentsTextureId, program, -1, program->vertexShader().pointLocation(), program->fragmentShader().edgeLocation()); } else { const LayerRendererChromium::RenderSurfaceProgram* program = layerRenderer->renderSurfaceProgram(); drawSurface(layerRenderer, maskLayer, drawTransform, deviceMatrix, deviceRect, layerQuad, contentsTextureId, program, -1, -1, -1); } } }
static inline Region computeOcclusionBehindLayer(const LayerType* layer, const TransformationMatrix& transform, const IntRect& scissorRect, bool usePaintTracking) { Region opaqueRegion; bool clipped; FloatQuad unoccludedQuad = CCMathUtil::mapQuad(transform, FloatQuad(layer->visibleLayerRect()), clipped); bool isPaintedAxisAligned = unoccludedQuad.isRectilinear(); // FIXME: Find a rect interior to each transformed quad. if (clipped || !isPaintedAxisAligned) return opaqueRegion; if (layer->opaque()) opaqueRegion = enclosedIntRect(unoccludedQuad.boundingBox()); else if (usePaintTracking && transform.isIdentity()) opaqueRegion = layer->visibleContentOpaqueRegion(); else if (usePaintTracking) { Region contentRegion = layer->visibleContentOpaqueRegion(); Vector<IntRect> contentRects = contentRegion.rects(); // We verify that the possible bounds of this region are not clipped above, so we can use mapRect() safely here. for (size_t i = 0; i < contentRects.size(); ++i) opaqueRegion.unite(enclosedIntRect(transform.mapRect(FloatRect(contentRects[i])))); } opaqueRegion.intersect(scissorRect); return opaqueRegion; }
void PainterOpenVG::intersectClipRect(const FloatRect& rect) { ASSERT(m_state); m_surface->makeCurrent(); if (m_state->surfaceTransformationMatrix.isIdentity()) { // No transformation required, skip all the complex stuff. intersectScissorRect(rect); return; } // Check if the actual destination rectangle is still rectilinear (can be // represented as FloatRect) so we could apply scissoring instead of // (potentially more expensive) path clipping. Note that scissoring is not // subject to transformations, so we need to do the transformation to // surface coordinates by ourselves. FloatQuad effectiveScissorQuad = m_state->surfaceTransformationMatrix.mapQuad(FloatQuad(rect)); if (effectiveScissorQuad.isRectilinear()) intersectScissorRect(effectiveScissorQuad.boundingBox()); else { // The transformed scissorRect cannot be represented as FloatRect // anymore, so we need to perform masking instead. Not yet implemented. notImplemented(); } }
void RenderLayerModelObject::addChildFocusRingRects(Vector<LayoutRect>& rects, const LayoutPoint& additionalOffset) const { for (RenderObject* current = slowFirstChild(); current; current = current->nextSibling()) { if (current->isText() || current->isListMarker()) continue; if (!current->isBox()) { current->addFocusRingRects(rects, additionalOffset); continue; } RenderBox* box = toRenderBox(current); if (!box->hasLayer()) { box->addFocusRingRects(rects, additionalOffset + box->locationOffset()); continue; } Vector<LayoutRect> layerFocusRingRects; box->addFocusRingRects(layerFocusRingRects, LayoutPoint()); for (size_t i = 0; i < layerFocusRingRects.size(); ++i) { FloatQuad quadInBox = box->localToContainerQuad(FloatQuad(layerFocusRingRects[i]), this); LayoutRect rect = LayoutRect(quadInBox.boundingBox()); if (!rect.isEmpty()) { rect.moveBy(additionalOffset); rects.append(rect); } } } }
FloatQuad LayoutGeometryMap::mapToContainer(const FloatRect& rect, const LayoutBoxModelObject* container) const { FloatQuad result; if (!hasFixedPositionStep() && !hasTransformStep() && !hasNonUniformStep() && (!container || (m_mapping.size() && container == m_mapping[0].m_layoutObject))) { result = rect; result.move(m_accumulatedOffset); } else { TransformState transformState(TransformState::ApplyTransformDirection, rect.center(), rect); mapToContainer(transformState, container); result = transformState.lastPlanarQuad(); } #if ENABLE(ASSERT) if (m_mapping.size() > 0) { const LayoutObject* lastLayoutObject = m_mapping.last().m_layoutObject; const DeprecatedPaintLayer* layer = lastLayoutObject->enclosingLayer(); // Bounds for invisible layers are intentionally not calculated, and are // therefore not necessarily expected to be correct here. This is ok, // because they will be recomputed if the layer becomes visible. if (!layer->subtreeIsInvisible() && lastLayoutObject->style()->visibility() == VISIBLE) { FloatRect layoutObjectMappedResult = lastLayoutObject->localToContainerQuad(rect, container, m_mapCoordinatesFlags).boundingBox(); // Inspector creates layoutObjects with negative width <https://bugs.webkit.org/show_bug.cgi?id=87194>. // Taking FloatQuad bounds avoids spurious assertions because of that. ASSERT(enclosingIntRect(layoutObjectMappedResult) == enclosingIntRect(result.boundingBox())); } } #endif return result; }
void CCRenderSurface::drawLayer(LayerRendererChromium* layerRenderer, CCLayerImpl* maskLayer, const TransformationMatrix& drawTransform) { TransformationMatrix renderMatrix = drawTransform; // Apply a scaling factor to size the quad from 1x1 to its intended size. renderMatrix.scale3d(m_contentRect.width(), m_contentRect.height(), 1); TransformationMatrix deviceMatrix = TransformationMatrix(layerRenderer->windowMatrix() * layerRenderer->projectionMatrix() * renderMatrix).to2dTransform(); // Can only draw surface if device matrix is invertible. if (!deviceMatrix.isInvertible()) return; FloatQuad quad = deviceMatrix.mapQuad(layerRenderer->sharedGeometryQuad()); CCLayerQuad deviceRect = CCLayerQuad(FloatQuad(quad.boundingBox())); CCLayerQuad layerQuad = CCLayerQuad(quad); // Use anti-aliasing programs only when necessary. bool useAA = (!quad.isRectilinear() || !quad.boundingBox().isExpressibleAsIntRect()); if (useAA) { deviceRect.inflateAntiAliasingDistance(); layerQuad.inflateAntiAliasingDistance(); } bool useMask = false; if (maskLayer && maskLayer->drawsContent()) if (!maskLayer->bounds().isEmpty()) useMask = true; if (useMask) { if (useAA) { const MaskProgramAA* program = layerRenderer->renderSurfaceMaskProgramAA(); drawSurface(layerRenderer, maskLayer, drawTransform, deviceMatrix, deviceRect, layerQuad, program, program->fragmentShader().maskSamplerLocation(), program->vertexShader().pointLocation(), program->fragmentShader().edgeLocation()); } else { const MaskProgram* program = layerRenderer->renderSurfaceMaskProgram(); drawSurface(layerRenderer, maskLayer, drawTransform, deviceMatrix, deviceRect, layerQuad, program, program->fragmentShader().maskSamplerLocation(), -1, -1); } } else { if (useAA) { const ProgramAA* program = layerRenderer->renderSurfaceProgramAA(); drawSurface(layerRenderer, maskLayer, drawTransform, deviceMatrix, deviceRect, layerQuad, program, -1, program->vertexShader().pointLocation(), program->fragmentShader().edgeLocation()); } else { const Program* program = layerRenderer->renderSurfaceProgram(); drawSurface(layerRenderer, maskLayer, drawTransform, deviceMatrix, deviceRect, layerQuad, program, -1, -1, -1); } } }
// Copied from RenderBox, this method likely requires further refactoring to work easily for both SVG and CSS Box Model content. // FIXME: This may also need to move into SVGRenderSupport as the RenderBox version depends // on borderBoundingBox() which SVG RenderBox subclases (like SVGRenderBlock) do not implement. LayoutRect RenderSVGModelObject::outlineBoundsForRepaint(const RenderLayerModelObject* repaintContainer, const RenderGeometryMap*) const { LayoutRect box = enclosingLayoutRect(repaintRectInLocalCoordinates()); adjustRectForOutlineAndShadow(box); FloatQuad containerRelativeQuad = localToContainerQuad(FloatRect(box), repaintContainer); return LayoutRect(snapRectToDevicePixels(LayoutRect(containerRelativeQuad.boundingBox()), document().deviceScaleFactor())); }
bool LinkHighlightImpl::computeHighlightLayerPathAndPosition(const LayoutBoxModelObject& paintInvalidationContainer) { if (!m_node || !m_node->layoutObject() || !m_currentGraphicsLayer) return false; // FIXME: This is defensive code to avoid crashes such as those described in // crbug.com/440887. This should be cleaned up once we fix the root cause of // of the paint invalidation container not being composited. if (!paintInvalidationContainer.layer()->compositedLayerMapping() && !paintInvalidationContainer.layer()->groupedMapping()) return false; // Get quads for node in absolute coordinates. Vector<FloatQuad> quads; computeQuads(*m_node, quads); DCHECK(quads.size()); Path newPath; for (size_t quadIndex = 0; quadIndex < quads.size(); ++quadIndex) { FloatQuad absoluteQuad = quads[quadIndex]; // Scrolling content layers have the same offset from layout object as the non-scrolling layers. Thus we need // to adjust for their scroll offset. if (m_isScrollingGraphicsLayer) { DoubleSize adjustedScrollOffset = paintInvalidationContainer.layer()->getScrollableArea()->adjustedScrollOffset(); absoluteQuad.move(adjustedScrollOffset.width(), adjustedScrollOffset.height()); } // Transform node quads in target absolute coords to local coordinates in the compositor layer. FloatQuad transformedQuad; convertTargetSpaceQuadToCompositedLayer(absoluteQuad, m_node->layoutObject(), paintInvalidationContainer, transformedQuad); // FIXME: for now, we'll only use rounded paths if we have a single node quad. The reason for this is that // we may sometimes get a chain of adjacent boxes (e.g. for text nodes) which end up looking like sausage // links: these should ideally be merged into a single rect before creating the path, but that's // another CL. if (quads.size() == 1 && transformedQuad.isRectilinear() && !m_owningWebViewImpl->settingsImpl()->mockGestureTapHighlightsEnabled()) { FloatSize rectRoundingRadii(3, 3); newPath.addRoundedRect(transformedQuad.boundingBox(), rectRoundingRadii); } else { addQuadToPath(transformedQuad, newPath); } } FloatRect boundingRect = newPath.boundingRect(); newPath.translate(-toFloatSize(boundingRect.location())); bool pathHasChanged = !(newPath == m_path); if (pathHasChanged) { m_path = newPath; m_contentLayer->layer()->setBounds(enclosingIntRect(boundingRect).size()); } m_contentLayer->layer()->setPosition(boundingRect.location()); return pathHasChanged; }
bool LinkHighlight::computeHighlightLayerPathAndPosition(const LayoutBoxModelObject* paintInvalidationContainer) { if (!m_node || !m_node->layoutObject() || !m_currentGraphicsLayer) return false; ASSERT(paintInvalidationContainer); // FIXME: This is defensive code to avoid crashes such as those described in // crbug.com/440887. This should be cleaned up once we fix the root cause of // of the paint invalidation container not being composited. if (!paintInvalidationContainer->layer()->compositedDeprecatedPaintLayerMapping() && !paintInvalidationContainer->layer()->groupedMapping()) return false; // Get quads for node in absolute coordinates. Vector<FloatQuad> quads; computeQuads(*m_node, quads); ASSERT(quads.size()); Path newPath; FloatPoint positionAdjustForCompositedScrolling = IntPoint(m_currentGraphicsLayer->offsetFromRenderer()); for (size_t quadIndex = 0; quadIndex < quads.size(); ++quadIndex) { FloatQuad absoluteQuad = quads[quadIndex]; // FIXME: this hack should not be necessary. It's a consequence of the fact that composited layers for scrolling are represented // differently in Blink than other composited layers. if (paintInvalidationContainer->layer()->needsCompositedScrolling() && m_node->layoutObject() != paintInvalidationContainer) absoluteQuad.move(-positionAdjustForCompositedScrolling.x(), -positionAdjustForCompositedScrolling.y()); // Transform node quads in target absolute coords to local coordinates in the compositor layer. FloatQuad transformedQuad; convertTargetSpaceQuadToCompositedLayer(absoluteQuad, m_node->layoutObject(), paintInvalidationContainer, transformedQuad); // FIXME: for now, we'll only use rounded paths if we have a single node quad. The reason for this is that // we may sometimes get a chain of adjacent boxes (e.g. for text nodes) which end up looking like sausage // links: these should ideally be merged into a single rect before creating the path, but that's // another CL. if (quads.size() == 1 && transformedQuad.isRectilinear() && !m_owningWebViewImpl->settingsImpl()->mockGestureTapHighlightsEnabled()) { FloatSize rectRoundingRadii(3, 3); newPath.addRoundedRect(transformedQuad.boundingBox(), rectRoundingRadii); } else addQuadToPath(transformedQuad, newPath); } FloatRect boundingRect = newPath.boundingRect(); newPath.translate(-toFloatSize(boundingRect.location())); bool pathHasChanged = !(newPath == m_path); if (pathHasChanged) { m_path = newPath; m_contentLayer->layer()->setBounds(enclosingIntRect(boundingRect).size()); } m_contentLayer->layer()->setPosition(boundingRect.location()); return pathHasChanged; }
HitTestLocation::HitTestLocation(const FloatPoint& point, const FloatQuad& quad) : m_transformedPoint(point) , m_transformedRect(quad) , m_isRectBased(true) { m_point = flooredLayoutPoint(point); m_boundingBox = enclosingIntRect(quad.boundingBox()); m_isRectilinear = quad.isRectilinear(); }
IntRect CCRenderSurface::computeDeviceBoundingBox(LayerRendererChromium* layerRenderer, const TransformationMatrix& drawTransform) const { TransformationMatrix contentsDeviceTransform = computeDeviceTransform(layerRenderer, drawTransform); // Can only draw surface if device matrix is invertible. if (!contentsDeviceTransform.isInvertible()) return IntRect(); FloatQuad deviceQuad = contentsDeviceTransform.mapQuad(layerRenderer->sharedGeometryQuad()); return enclosingIntRect(deviceQuad.boundingBox()); }
FloatRect TransformationMatrix::mapRect(const FloatRect& r) const { if (isIdentityOrTranslation()) { FloatRect mappedRect(r); mappedRect.move(static_cast<float>(m_matrix[3][0]), static_cast<float>(m_matrix[3][1])); return mappedRect; } FloatQuad resultQuad = mapQuad(FloatQuad(r)); return resultQuad.boundingBox(); }
FloatRect LayerRendererSurface::drawRect() const { float bx = m_size.width() / 2.0; float by = m_size.height() / 2.0; FloatQuad transformedBounds; transformedBounds.setP1(m_drawTransform.mapPoint(FloatPoint(-bx, -by))); transformedBounds.setP2(m_drawTransform.mapPoint(FloatPoint(-bx, by))); transformedBounds.setP3(m_drawTransform.mapPoint(FloatPoint(bx, by))); transformedBounds.setP4(m_drawTransform.mapPoint(FloatPoint(bx, -by))); FloatRect rect = transformedBounds.boundingBox(); if (m_ownerLayer->replicaLayer()) { FloatQuad bounds; bounds.setP1(m_replicaDrawTransform.mapPoint(FloatPoint(-bx, -by))); bounds.setP2(m_replicaDrawTransform.mapPoint(FloatPoint(-bx, by))); bounds.setP3(m_replicaDrawTransform.mapPoint(FloatPoint(bx, by))); bounds.setP4(m_replicaDrawTransform.mapPoint(FloatPoint(bx, -by))); rect.unite(bounds.boundingBox()); } return rect; }
FloatRect AffineTransform::mapRect(const FloatRect& rect) const { if (isIdentityOrTranslation()) { FloatRect mappedRect(rect); mappedRect.move(narrowPrecisionToFloat(m_transform[4]), narrowPrecisionToFloat(m_transform[5])); return mappedRect; } FloatQuad result; result.setP1(mapPoint(rect.location())); result.setP2(mapPoint(FloatPoint(rect.right(), rect.y()))); result.setP3(mapPoint(FloatPoint(rect.right(), rect.bottom()))); result.setP4(mapPoint(FloatPoint(rect.x(), rect.bottom()))); return result.boundingBox(); }
bool LinkHighlight::computeHighlightLayerPathAndPosition(RenderLayer* compositingLayer) { if (!m_node || !m_node->renderer() || !m_currentGraphicsLayer) return false; ASSERT(compositingLayer); // Get quads for node in absolute coordinates. Vector<FloatQuad> quads; computeQuads(m_node.get(), quads); ASSERT(quads.size()); // Adjust for offset between target graphics layer and the node's renderer. FloatPoint positionAdjust = IntPoint(m_currentGraphicsLayer->offsetFromRenderer()); Path newPath; for (size_t quadIndex = 0; quadIndex < quads.size(); ++quadIndex) { FloatQuad absoluteQuad = quads[quadIndex]; absoluteQuad.move(-positionAdjust.x(), -positionAdjust.y()); // Transform node quads in target absolute coords to local coordinates in the compositor layer. FloatQuad transformedQuad; convertTargetSpaceQuadToCompositedLayer(absoluteQuad, m_node->renderer(), compositingLayer->renderer(), transformedQuad); // FIXME: for now, we'll only use rounded paths if we have a single node quad. The reason for this is that // we may sometimes get a chain of adjacent boxes (e.g. for text nodes) which end up looking like sausage // links: these should ideally be merged into a single rect before creating the path, but that's // another CL. if (quads.size() == 1 && transformedQuad.isRectilinear()) { FloatSize rectRoundingRadii(3, 3); newPath.addRoundedRect(transformedQuad.boundingBox(), rectRoundingRadii); } else addQuadToPath(transformedQuad, newPath); } FloatRect boundingRect = newPath.boundingRect(); newPath.translate(-toFloatSize(boundingRect.location())); bool pathHasChanged = !(newPath == m_path); if (pathHasChanged) { m_path = newPath; m_contentLayer->layer()->setBounds(enclosingIntRect(boundingRect).size()); } m_contentLayer->layer()->setPosition(boundingRect.location()); return pathHasChanged; }
static inline Region computeOcclusionBehindLayer(const LayerType* layer, const TransformationMatrix& transform, bool usePaintTracking) { Region opaqueRegion; FloatQuad unoccludedQuad = transform.mapQuad(FloatQuad(layer->visibleLayerRect())); bool isPaintedAxisAligned = unoccludedQuad.isRectilinear(); if (!isPaintedAxisAligned) return opaqueRegion; if (layer->opaque()) opaqueRegion = enclosedIntRect(unoccludedQuad.boundingBox()); else if (usePaintTracking && transform.isIdentity()) opaqueRegion = layer->opaqueContentsRegion(); else if (usePaintTracking) { Region contentRegion = layer->opaqueContentsRegion(); Vector<IntRect> contentRects = contentRegion.rects(); for (size_t i = 0; i < contentRects.size(); ++i) opaqueRegion.unite(enclosedIntRect(transform.mapRect(FloatRect(contentRects[i])))); } return opaqueRegion; }
IntRect RenderSVGInlineText::computeRepaintRectForRange(RenderBoxModelObject* repaintContainer, int startPos, int endPos) { FloatQuad repaintQuad = computeRepaintQuadForRange(repaintContainer, startPos, endPos); return enclosingIntRect(repaintQuad.boundingBox()); }
bool LinkHighlight::computeHighlightLayerPathAndPosition(RenderLayer* compositingLayer) { if (!m_node || !m_node->renderer()) return false; ASSERT(compositingLayer); // Get quads for node in absolute coordinates. Vector<FloatQuad> quads; m_node->renderer()->absoluteQuads(quads); ASSERT(quads.size()); FloatRect positionAdjust; if (!m_usingNonCompositedContentHost) { const RenderStyle* style = m_node->renderer()->style(); // If we have a box shadow, and are non-relative, then must manually adjust // for its size. if (const ShadowData* shadow = style->boxShadow()) { int outlineSize = m_node->renderer()->outlineStyleForRepaint()->outlineSize(); shadow->adjustRectForShadow(positionAdjust, outlineSize); } // If absolute or fixed, need to subtract out our fixed positioning. // FIXME: should we use RenderLayer::staticBlockPosition() here instead? // Perhaps consider this if out-of-flow elements cause further problems. if (m_node->renderer()->isOutOfFlowPositioned()) { FloatPoint delta(style->left().getFloatValue(), style->top().getFloatValue()); positionAdjust.moveBy(delta); } } Path newPath; for (unsigned quadIndex = 0; quadIndex < quads.size(); ++quadIndex) { FloatQuad localQuad = m_node->renderer()->absoluteToLocalQuad(quads[quadIndex], UseTransforms); localQuad.move(-positionAdjust.location().x(), -positionAdjust.location().y()); FloatQuad absoluteQuad = m_node->renderer()->localToAbsoluteQuad(localQuad, UseTransforms); // Transform node quads in target absolute coords to local coordinates in the compositor layer. FloatQuad transformedQuad; convertTargetSpaceQuadToCompositedLayer(absoluteQuad, m_node->renderer(), compositingLayer->renderer(), transformedQuad); // FIXME: for now, we'll only use rounded paths if we have a single node quad. The reason for this is that // we may sometimes get a chain of adjacent boxes (e.g. for text nodes) which end up looking like sausage // links: these should ideally be merged into a single rect before creating the path, but that's // another CL. if (quads.size() == 1 && transformedQuad.isRectilinear()) { FloatSize rectRoundingRadii(3, 3); newPath.addRoundedRect(transformedQuad.boundingBox(), rectRoundingRadii); } else addQuadToPath(transformedQuad, newPath); } FloatRect boundingRect = newPath.boundingRect(); newPath.translate(-toFloatSize(boundingRect.location())); bool pathHasChanged = !(newPath == m_path); if (pathHasChanged) { m_path = newPath; m_contentLayer->layer()->setBounds(enclosingIntRect(boundingRect).size()); } m_contentLayer->layer()->setPosition(boundingRect.location()); return pathHasChanged; }