AffineTransform SVGSVGElement::localCoordinateSpaceTransform(SVGLocatable::CTMScope mode) const { AffineTransform viewBoxTransform; if (hasAttribute(SVGNames::viewBoxAttr)) { FloatSize size = currentViewportSize(); viewBoxTransform = viewBoxToViewTransform(size.width(), size.height()); } AffineTransform transform; if (!isOutermostSVGSVGElement()) { SVGLengthContext lengthContext(this); transform.translate(x().value(lengthContext), y().value(lengthContext)); } else if (mode == SVGLocatable::ScreenScope) { if (RenderObject* renderer = this->renderer()) { FloatPoint location; // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform // to map an element from SVG viewport coordinates to CSS box coordinates. // RenderSVGRoot's localToAbsolute method expects CSS box coordinates. if (renderer->isSVGRoot()) location = toRenderSVGRoot(renderer)->localToBorderBoxTransform().mapPoint(location); // Translate in our CSS parent coordinate space // FIXME: This doesn't work correctly with CSS transforms. location = renderer->localToAbsolute(location, false, true); // Be careful here! localToBorderBoxTransform() included the x/y offset coming from the viewBoxToViewTransform(), // so we have to subtract it here (original cause of bug #27183) transform.translate(location.x() - viewBoxTransform.e(), location.y() - viewBoxTransform.f()); // Respect scroll offset. if (FrameView* view = document()->view()) { LayoutSize scrollOffset = view->scrollOffset(); transform.translate(-scrollOffset.width(), -scrollOffset.height()); } } } return transform.multiply(viewBoxTransform); }
FloatRect SVGSVGElement::currentViewBoxRect() const { if (m_useCurrentView) return m_viewSpec ? m_viewSpec->viewBox() : FloatRect(); FloatRect useViewBox = viewBox(); if (!useViewBox.isEmpty()) return useViewBox; if (!renderer() || !renderer()->isSVGRoot()) return FloatRect(); if (!toRenderSVGRoot(renderer())->isEmbeddedThroughSVGImage()) return FloatRect(); Length intrinsicWidth = this->intrinsicWidth(); Length intrinsicHeight = this->intrinsicHeight(); if (!intrinsicWidth.isFixed() || !intrinsicHeight.isFixed()) return FloatRect(); // If no viewBox is specified but non-relative width/height values, then we // should always synthesize a viewBox if we're embedded through a SVGImage. return FloatRect(FloatPoint(), FloatSize(floatValueForLength(intrinsicWidth, 0), floatValueForLength(intrinsicHeight, 0))); }
void SVGSVGElement::svgAttributeChanged(const QualifiedName& attrName) { bool updateRelativeLengthsOrViewBox = false; bool widthChanged = attrName == SVGNames::widthAttr; if (widthChanged || attrName == SVGNames::heightAttr || attrName == SVGNames::xAttr || attrName == SVGNames::yAttr) { updateRelativeLengthsOrViewBox = true; updateRelativeLengthsInformation(); // At the SVG/HTML boundary (aka RenderSVGRoot), the width attribute can // affect the replaced size so we need to mark it for updating. if (widthChanged) { RenderObject* renderObject = renderer(); if (renderObject && renderObject->isSVGRoot()) toRenderSVGRoot(renderObject)->setNeedsLayoutAndPrefWidthsRecalc(); } } if (SVGFitToViewBox::isKnownAttribute(attrName)) { updateRelativeLengthsOrViewBox = true; if (RenderObject* object = renderer()) object->setNeedsTransformUpdate(); } SVGElementInstance::InvalidationGuard invalidationGuard(this); if (updateRelativeLengthsOrViewBox || SVGLangSpace::isKnownAttribute(attrName) || SVGExternalResourcesRequired::isKnownAttribute(attrName) || SVGZoomAndPan::isKnownAttribute(attrName)) { if (auto renderer = this->renderer()) RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer); return; } SVGGraphicsElement::svgAttributeChanged(attrName); }
void SVGRenderSupport::mapLocalToContainer(const RenderObject* object, const RenderLayerModelObject* paintInvalidationContainer, TransformState& transformState, bool* wasFixed, const PaintInvalidationState* paintInvalidationState) { transformState.applyTransform(object->localToParentTransform()); if (paintInvalidationState && paintInvalidationState->canMapToContainer(paintInvalidationContainer)) { // |svgTransform| contains localToBorderBoxTransform mentioned below. transformState.applyTransform(paintInvalidationState->svgTransform()); transformState.move(paintInvalidationState->paintOffset()); return; } RenderObject* parent = object->parent(); // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform // to map an element from SVG viewport coordinates to CSS box coordinates. // RenderSVGRoot's mapLocalToContainer method expects CSS box coordinates. if (parent->isSVGRoot()) transformState.applyTransform(toRenderSVGRoot(parent)->localToBorderBoxTransform()); MapCoordinatesFlags mode = UseTransforms; parent->mapLocalToContainer(paintInvalidationContainer, transformState, mode, wasFixed, paintInvalidationState); }
void write(TextStream& ts, const RenderObject& o, int indent, RenderAsTextBehavior behavior) { #if ENABLE(SVG) if (o.isSVGPath()) { write(ts, *toRenderSVGPath(&o), indent); return; } if (o.isSVGGradientStop()) { writeSVGGradientStop(ts, *toRenderSVGGradientStop(&o), indent); return; } if (o.isSVGResourceContainer()) { writeSVGResourceContainer(ts, o, indent); return; } if (o.isSVGContainer()) { writeSVGContainer(ts, o, indent); return; } if (o.isSVGRoot()) { write(ts, *toRenderSVGRoot(&o), indent); return; } if (o.isSVGText()) { writeSVGText(ts, *toRenderBlock(&o), indent); return; } if (o.isSVGInlineText()) { writeSVGInlineText(ts, *toRenderText(&o), indent); return; } if (o.isSVGImage()) { writeSVGImage(ts, *toRenderSVGImage(&o), indent); return; } #endif writeIndent(ts, indent); RenderTreeAsText::writeRenderObject(ts, o, behavior); ts << "\n"; if (o.isText() && !o.isBR()) { const RenderText& text = *toRenderText(&o); for (InlineTextBox* box = text.firstTextBox(); box; box = box->nextTextBox()) { writeIndent(ts, indent + 1); writeTextRun(ts, text, *box); } } for (RenderObject* child = o.firstChild(); child; child = child->nextSibling()) { if (child->hasLayer()) continue; write(ts, *child, indent + 1, behavior); } if (o.isWidget()) { Widget* widget = toRenderWidget(&o)->widget(); if (widget && widget->isFrameView()) { FrameView* view = static_cast<FrameView*>(widget); RenderView* root = view->frame()->contentRenderer(); if (root) { view->layout(); RenderLayer* l = root->layer(); if (l) writeLayers(ts, l, l, IntRect(l->x(), l->y(), l->width(), l->height()), indent + 1, behavior); } } } }
void SVGImage::drawSVGToImageBuffer(ImageBuffer* buffer, const IntSize& size, float zoom, float scale, ShouldClearBuffer shouldClear) { // FIXME: This doesn't work correctly with animations. If an image contains animations, that say run for 2 seconds, // and we currently have one <img> that displays us. If we open another document referencing the same SVGImage it // will display the document at a time where animations already ran - even though it has its own ImageBuffer. // We currently don't implement SVGSVGElement::setCurrentTime, and can NOT go back in time, once animations started. // There's no way to fix this besides avoiding style/attribute mutations from SVGAnimationElement. ASSERT(buffer); ASSERT(!size.isEmpty()); if (!m_page) return; Frame* frame = m_page->mainFrame(); SVGSVGElement* rootElement = static_cast<SVGDocument*>(frame->document())->rootElement(); if (!rootElement) return; RenderSVGRoot* renderer = toRenderSVGRoot(rootElement->renderer()); if (!renderer) return; // Draw image at requested size. ImageObserver* observer = imageObserver(); ASSERT(observer); // Temporarily reset image observer, we don't want to receive any changeInRect() calls due to this relayout. setImageObserver(0); // Disable repainting; we don't want deferred repaints to schedule any timers due to this relayout. frame->view()->beginDisableRepaints(); renderer->setContainerSize(size); frame->view()->resize(this->size()); if (zoom != 1) frame->setPageZoomFactor(zoom); // Eventually clear image buffer. IntRect rect(IntPoint(), size); FloatRect scaledRect(rect); scaledRect.scale(scale); if (shouldClear == ClearImageBuffer) buffer->context()->clearRect(enclosingIntRect(scaledRect)); // Draw SVG on top of ImageBuffer. draw(buffer->context(), enclosingIntRect(scaledRect), rect, ColorSpaceDeviceRGB, CompositeSourceOver); // Reset container size & zoom to initial state. Otherwhise the size() of this // image would return whatever last size was set by drawSVGToImageBuffer(). if (zoom != 1) frame->setPageZoomFactor(1); renderer->setContainerSize(IntSize()); frame->view()->resize(this->size()); if (frame->view()->needsLayout()) frame->view()->layout(); setImageObserver(observer); frame->view()->endDisableRepaints(); }