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 (!isOutermostSVG()) { SVGLengthContext lengthContext(this); transform.translate(x().value(lengthContext), y().value(lengthContext)); } else if (mode == SVGLocatable::ScreenScope) { if (RenderObject* renderer = this->renderer()) { // Translate in our CSS parent coordinate space // FIXME: This doesn't work correctly with CSS transforms. FloatPoint location = renderer->localToAbsolute(FloatPoint(), false, true); // Be careful here! localToAbsolute() includes the x/y offset coming from the viewBoxToViewTransform(), because // RenderSVGRoot::localToBorderBoxTransform() (called through mapLocalToContainer(), called from localToAbsolute()) // also takes the viewBoxToViewTransform() into account, 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); }
static void drawDeferredFilter(GraphicsContext* context, FilterData* filterData, SVGFilterElement* filterElement) { SkiaImageFilterBuilder builder(context); SourceGraphic* sourceGraphic = static_cast<SourceGraphic*>(filterData->builder->getEffectById(SourceGraphic::effectName())); ASSERT(sourceGraphic); builder.setSourceGraphic(sourceGraphic); RefPtr<ImageFilter> imageFilter = builder.build(filterData->builder->lastEffect(), ColorSpaceDeviceRGB); FloatRect boundaries = filterData->boundaries; context->save(); FloatSize deviceSize = context->getCTM().mapSize(boundaries.size()); float scaledArea = deviceSize.width() * deviceSize.height(); // If area of scaled size is bigger than the upper limit, adjust the scale // to fit. Note that this only really matters in the non-impl-side painting // case, since the impl-side case never allocates a full-sized backing // store, only tile-sized. // FIXME: remove this once all platforms are using impl-side painting. // crbug.com/169282. if (scaledArea > FilterEffect::maxFilterArea()) { float scale = sqrtf(FilterEffect::maxFilterArea() / scaledArea); context->scale(scale, scale); } // Clip drawing of filtered image to the minimum required paint rect. FilterEffect* lastEffect = filterData->builder->lastEffect(); context->clipRect(lastEffect->determineAbsolutePaintRect(lastEffect->maxEffectRect())); if (filterElement->hasAttribute(SVGNames::filterResAttr)) { // Get boundaries in device coords. // FIXME: See crbug.com/382491. Is the use of getCTM OK here, given it does not include device // zoom or High DPI adjustments? FloatSize size = context->getCTM().mapSize(boundaries.size()); // Compute the scale amount required so that the resulting offscreen is exactly filterResX by filterResY pixels. float filterResScaleX = filterElement->filterResX()->currentValue()->value() / size.width(); float filterResScaleY = filterElement->filterResY()->currentValue()->value() / size.height(); // Scale the CTM so the primitive is drawn to filterRes. context->scale(filterResScaleX, filterResScaleY); // Create a resize filter with the inverse scale. AffineTransform resizeMatrix; resizeMatrix.scale(1 / filterResScaleX, 1 / filterResScaleY); imageFilter = builder.buildTransform(resizeMatrix, imageFilter.get()); } // If the CTM contains rotation or shearing, apply the filter to // the unsheared/unrotated matrix, and do the shearing/rotation // as a final pass. AffineTransform ctm = context->getCTM(); if (ctm.b() || ctm.c()) { AffineTransform scaleAndTranslate; scaleAndTranslate.translate(ctm.e(), ctm.f()); scaleAndTranslate.scale(ctm.xScale(), ctm.yScale()); ASSERT(scaleAndTranslate.isInvertible()); AffineTransform shearAndRotate = scaleAndTranslate.inverse(); shearAndRotate.multiply(ctm); context->setCTM(scaleAndTranslate); imageFilter = builder.buildTransform(shearAndRotate, imageFilter.get()); } context->beginLayer(1, CompositeSourceOver, &boundaries, ColorFilterNone, imageFilter.get()); context->endLayer(); context->restore(); }
AffineTransform CanvasSurface::baseTransform() const { ASSERT(m_hasCreatedImageBuffer); FloatSize unscaledSize(width(), height()); IntSize size = convertLogicalToDevice(unscaledSize); AffineTransform transform; if (size.width() && size.height()) transform.scaleNonUniform(size.width() / unscaledSize.width(), size.height() / unscaledSize.height()); transform.multiply(m_imageBuffer->baseTransform()); return transform; }
AffineTransform SVGSVGElement::localCoordinateSpaceTransform( SVGElement::CTMScope mode) const { AffineTransform viewBoxTransform; if (!hasEmptyViewBox()) { FloatSize size = currentViewportSize(); viewBoxTransform = viewBoxToViewTransform(size.width(), size.height()); } AffineTransform transform; if (!isOutermostSVGSVGElement()) { SVGLengthContext lengthContext(this); transform.translate(m_x->currentValue()->value(lengthContext), m_y->currentValue()->value(lengthContext)); } else if (mode == SVGElement::ScreenScope) { if (LayoutObject* layoutObject = this->layoutObject()) { FloatPoint location; float zoomFactor = 1; // At the SVG/HTML boundary (aka LayoutSVGRoot), we apply the // localToBorderBoxTransform to map an element from SVG viewport // coordinates to CSS box coordinates. LayoutSVGRoot's localToAbsolute // method expects CSS box coordinates. We also need to adjust for the // zoom level factored into CSS coordinates (bug #96361). if (layoutObject->isSVGRoot()) { location = toLayoutSVGRoot(layoutObject) ->localToBorderBoxTransform() .mapPoint(location); zoomFactor = 1 / layoutObject->style()->effectiveZoom(); } // Translate in our CSS parent coordinate space // FIXME: This doesn't work correctly with CSS transforms. location = layoutObject->localToAbsolute(location, UseTransforms); location.scale(zoomFactor, zoomFactor); // 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->getScrollOffset()); scrollOffset.scale(zoomFactor); transform.translate(-scrollOffset.width(), -scrollOffset.height()); } } } return transform.multiply(viewBoxTransform); }
void SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(const RenderObject* renderer, AffineTransform& absoluteTransform) { const RenderObject* current = renderer; ASSERT(current); absoluteTransform = currentContentTransformation(); while (current) { absoluteTransform.multiply(current->localToParentTransform()); if (current->isSVGRoot()) break; current = current->parent(); } }
AffineTransform SVGRootPainter::transformToPixelSnappedBorderBox( const LayoutPoint& paintOffset) const { const IntRect snappedSize = pixelSnappedSize(paintOffset); AffineTransform paintOffsetToBorderBox = AffineTransform::translation(snappedSize.x(), snappedSize.y()); LayoutSize size = m_layoutSVGRoot.size(); if (!size.isEmpty()) { paintOffsetToBorderBox.scale( snappedSize.width() / size.width().toFloat(), snappedSize.height() / size.height().toFloat()); } paintOffsetToBorderBox.multiply(m_layoutSVGRoot.localToBorderBoxTransform()); return paintOffsetToBorderBox; }
AffineTransform SVGSVGElement::localCoordinateSpaceTransform(SVGLocatable::CTMScope mode) const { AffineTransform viewBoxTransform; if (!hasEmptyViewBox()) { 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 (auto* renderer = this->renderer()) { FloatPoint location; float zoomFactor = 1; // 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. // We also need to adjust for the zoom level factored into CSS coordinates (bug #96361). if (is<RenderSVGRoot>(*renderer)) { location = downcast<RenderSVGRoot>(*renderer).localToBorderBoxTransform().mapPoint(location); zoomFactor = 1 / renderer->style().effectiveZoom(); } // Translate in our CSS parent coordinate space // FIXME: This doesn't work correctly with CSS transforms. location = renderer->localToAbsolute(location, UseTransforms); location.scale(zoomFactor); // 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()) { LayoutPoint scrollPosition = view->scrollPosition(); scrollPosition.scale(zoomFactor); transform.translate(-scrollPosition.x(), -scrollPosition.y()); } } } return transform.multiply(viewBoxTransform); }
static inline AffineTransform clipToTextMask(GraphicsContext* context, OwnPtr<ImageBuffer>& imageBuffer, const RenderObject* object, GradientData* gradientData) { const RenderObject* textRootBlock = findTextRootObject(object); context->clipToImageBuffer(textRootBlock->repaintRectInLocalCoordinates(), imageBuffer.get()); AffineTransform matrix; if (gradientData->boundingBoxMode) { FloatRect maskBoundingBox = textRootBlock->objectBoundingBox(); matrix.translate(maskBoundingBox.x(), maskBoundingBox.y()); matrix.scaleNonUniform(maskBoundingBox.width(), maskBoundingBox.height()); } matrix.multiply(gradientData->transform); return matrix; }
static void paintFilteredContent(const LayoutObject& object, GraphicsContext& context, FilterData* filterData) { ASSERT(filterData->m_state == FilterData::ReadyToPaint); ASSERT(filterData->filter->sourceGraphic()); filterData->m_state = FilterData::PaintingFilter; SkiaImageFilterBuilder builder; RefPtr<SkImageFilter> imageFilter = builder.build(filterData->filter->lastEffect(), ColorSpaceDeviceRGB); FloatRect boundaries = filterData->filter->filterRegion(); context.save(); // Clip drawing of filtered image to the minimum required paint rect. FilterEffect* lastEffect = filterData->filter->lastEffect(); context.clipRect(lastEffect->determineAbsolutePaintRect(lastEffect->maxEffectRect())); #ifdef CHECK_CTM_FOR_TRANSFORMED_IMAGEFILTER // TODO: Remove this workaround once skew/rotation support is added in Skia // (https://code.google.com/p/skia/issues/detail?id=3288, crbug.com/446935). // If the CTM contains rotation or shearing, apply the filter to // the unsheared/unrotated matrix, and do the shearing/rotation // as a final pass. AffineTransform ctm = SVGLayoutSupport::deprecatedCalculateTransformToLayer(&object); if (ctm.b() || ctm.c()) { AffineTransform scaleAndTranslate; scaleAndTranslate.translate(ctm.e(), ctm.f()); scaleAndTranslate.scale(ctm.xScale(), ctm.yScale()); ASSERT(scaleAndTranslate.isInvertible()); AffineTransform shearAndRotate = scaleAndTranslate.inverse(); shearAndRotate.multiply(ctm); context.concatCTM(shearAndRotate.inverse()); imageFilter = builder.buildTransform(shearAndRotate, imageFilter.get()); } #endif context.beginLayer(1, SkXfermode::kSrcOver_Mode, &boundaries, ColorFilterNone, imageFilter.get()); context.endLayer(); context.restore(); filterData->m_state = FilterData::ReadyToPaint; }
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); }
static void getElementCTM(SVGElement* element, AffineTransform& transform) { ASSERT(element); element->document().updateLayoutIgnorePendingStylesheets(); SVGElement* stopAtElement = SVGLocatable::nearestViewportElement(element); ASSERT(stopAtElement); AffineTransform localTransform; Node* current = element; while (current && current->isSVGElement()) { SVGElement* currentElement = toSVGElement(current); localTransform = currentElement->renderer()->localToParentTransform(); transform = localTransform.multiply(transform); // For getCTM() computation, stop at the nearest viewport element if (currentElement == stopAtElement) break; current = current->parentOrShadowHostNode(); } }
bool RenderSVGResourceFilter::applyResource(RenderElement& renderer, const RenderStyle&, GraphicsContext*& context, unsigned short resourceMode) { ASSERT(context); ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode); if (m_filter.contains(&renderer)) { FilterData* filterData = m_filter.get(&renderer); if (filterData->state == FilterData::PaintingSource || filterData->state == FilterData::Applying) filterData->state = FilterData::CycleDetected; return false; // Already built, or we're in a cycle, or we're marked for removal. Regardless, just do nothing more now. } auto filterData = std::make_unique<FilterData>(); FloatRect targetBoundingBox = renderer.objectBoundingBox(); filterData->boundaries = SVGLengthContext::resolveRectangle<SVGFilterElement>(&filterElement(), filterElement().filterUnits(), targetBoundingBox); if (filterData->boundaries.isEmpty()) return false; // Determine absolute transformation matrix for filter. AffineTransform absoluteTransform; SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(renderer, absoluteTransform); if (!absoluteTransform.isInvertible()) return false; // Eliminate shear of the absolute transformation matrix, to be able to produce unsheared tile images for feTile. filterData->shearFreeAbsoluteTransform = AffineTransform(absoluteTransform.xScale(), 0, 0, absoluteTransform.yScale(), 0, 0); // Determine absolute boundaries of the filter and the drawing region. FloatRect absoluteFilterBoundaries = filterData->shearFreeAbsoluteTransform.mapRect(filterData->boundaries); filterData->drawingRegion = renderer.strokeBoundingBox(); filterData->drawingRegion.intersect(filterData->boundaries); FloatRect absoluteDrawingRegion = filterData->shearFreeAbsoluteTransform.mapRect(filterData->drawingRegion); // Create the SVGFilter object. bool primitiveBoundingBoxMode = filterElement().primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX; filterData->filter = SVGFilter::create(filterData->shearFreeAbsoluteTransform, absoluteDrawingRegion, targetBoundingBox, filterData->boundaries, primitiveBoundingBoxMode); // Create all relevant filter primitives. filterData->builder = buildPrimitives(filterData->filter.get()); if (!filterData->builder) return false; // Calculate the scale factor for the use of filterRes. // Also see http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion FloatSize scale(1, 1); if (filterElement().hasAttribute(SVGNames::filterResAttr)) { scale.setWidth(filterElement().filterResX() / absoluteFilterBoundaries.width()); scale.setHeight(filterElement().filterResY() / absoluteFilterBoundaries.height()); } if (scale.isEmpty()) return false; // Determine scale factor for filter. The size of intermediate ImageBuffers shouldn't be bigger than kMaxFilterSize. FloatRect tempSourceRect = absoluteDrawingRegion; tempSourceRect.scale(scale.width(), scale.height()); fitsInMaximumImageSize(tempSourceRect.size(), scale); // Set the scale level in SVGFilter. filterData->filter->setFilterResolution(scale); static const unsigned maxTotalOfEffectInputs = 100; FilterEffect* lastEffect = filterData->builder->lastEffect(); if (!lastEffect || lastEffect->totalNumberOfEffectInputs() > maxTotalOfEffectInputs) return false; RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(*lastEffect); FloatRect subRegion = lastEffect->maxEffectRect(); // At least one FilterEffect has a too big image size, // recalculate the effect sizes with new scale factors. if (!fitsInMaximumImageSize(subRegion.size(), scale)) { filterData->filter->setFilterResolution(scale); RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(*lastEffect); } // If the drawingRegion is empty, we have something like <g filter=".."/>. // Even if the target objectBoundingBox() is empty, we still have to draw the last effect result image in postApplyResource. if (filterData->drawingRegion.isEmpty()) { ASSERT(!m_filter.contains(&renderer)); filterData->savedContext = context; m_filter.set(&renderer, WTF::move(filterData)); return false; } // Change the coordinate transformation applied to the filtered element to reflect the resolution of the filter. AffineTransform effectiveTransform; effectiveTransform.scale(scale.width(), scale.height()); effectiveTransform.multiply(filterData->shearFreeAbsoluteTransform); std::unique_ptr<ImageBuffer> sourceGraphic; RenderingMode renderingMode = renderer.frame().settings().acceleratedFiltersEnabled() ? Accelerated : Unaccelerated; if (!SVGRenderingContext::createImageBuffer(filterData->drawingRegion, effectiveTransform, sourceGraphic, ColorSpaceLinearRGB, renderingMode)) { ASSERT(!m_filter.contains(&renderer)); filterData->savedContext = context; m_filter.set(&renderer, WTF::move(filterData)); return false; } // Set the rendering mode from the page's settings. filterData->filter->setRenderingMode(renderingMode); GraphicsContext* sourceGraphicContext = sourceGraphic->context(); ASSERT(sourceGraphicContext); filterData->sourceGraphicBuffer = WTF::move(sourceGraphic); filterData->savedContext = context; context = sourceGraphicContext; ASSERT(!m_filter.contains(&renderer)); m_filter.set(&renderer, WTF::move(filterData)); return true; }
bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode) { ASSERT(object); ASSERT(context); ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode); // Returning false here, to avoid drawings onto the context. We just want to // draw the stored filter output, not the unfiltered object as well. if (m_filter.contains(object)) { FilterData* filterData = m_filter.get(object); if (filterData->builded) return false; delete m_filter.take(object); // Oops, have to rebuild, go through normal code path } OwnPtr<FilterData> filterData(adoptPtr(new FilterData)); FloatRect targetBoundingBox = object->objectBoundingBox(); SVGFilterElement* filterElement = static_cast<SVGFilterElement*>(node()); filterData->boundaries = SVGLengthContext::resolveRectangle<SVGFilterElement>(filterElement, filterElement->filterUnits(), targetBoundingBox); if (filterData->boundaries.isEmpty()) return false; // Determine absolute transformation matrix for filter. AffineTransform absoluteTransform; SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransform); if (!absoluteTransform.isInvertible()) return false; // Eliminate shear of the absolute transformation matrix, to be able to produce unsheared tile images for feTile. filterData->shearFreeAbsoluteTransform = AffineTransform(absoluteTransform.xScale(), 0, 0, absoluteTransform.yScale(), 0, 0); // Determine absolute boundaries of the filter and the drawing region. FloatRect absoluteFilterBoundaries = filterData->shearFreeAbsoluteTransform.mapRect(filterData->boundaries); FloatRect drawingRegion = object->strokeBoundingBox(); drawingRegion.intersect(filterData->boundaries); FloatRect absoluteDrawingRegion = filterData->shearFreeAbsoluteTransform.mapRect(drawingRegion); // Create the SVGFilter object. bool primitiveBoundingBoxMode = filterElement->primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX; filterData->filter = SVGFilter::create(filterData->shearFreeAbsoluteTransform, absoluteDrawingRegion, targetBoundingBox, filterData->boundaries, primitiveBoundingBoxMode); // Create all relevant filter primitives. filterData->builder = buildPrimitives(filterData->filter.get()); if (!filterData->builder) return false; // Calculate the scale factor for the use of filterRes. // Also see http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion FloatSize scale(1, 1); if (filterElement->hasAttribute(SVGNames::filterResAttr)) { scale.setWidth(filterElement->filterResX() / absoluteFilterBoundaries.width()); scale.setHeight(filterElement->filterResY() / absoluteFilterBoundaries.height()); } if (scale.isEmpty()) return false; // Determine scale factor for filter. The size of intermediate ImageBuffers shouldn't be bigger than kMaxFilterSize. FloatRect tempSourceRect = absoluteDrawingRegion; tempSourceRect.scale(scale.width(), scale.height()); fitsInMaximumImageSize(tempSourceRect.size(), scale); // Set the scale level in SVGFilter. filterData->filter->setFilterResolution(scale); FilterEffect* lastEffect = filterData->builder->lastEffect(); if (!lastEffect) return false; RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(lastEffect); FloatRect subRegion = lastEffect->maxEffectRect(); // At least one FilterEffect has a too big image size, // recalculate the effect sizes with new scale factors. if (!fitsInMaximumImageSize(subRegion.size(), scale)) { filterData->filter->setFilterResolution(scale); RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(lastEffect); } // If the drawingRegion is empty, we have something like <g filter=".."/>. // Even if the target objectBoundingBox() is empty, we still have to draw the last effect result image in postApplyResource. if (drawingRegion.isEmpty()) { ASSERT(!m_filter.contains(object)); filterData->savedContext = context; m_filter.set(object, filterData.leakPtr()); return false; } // Change the coordinate transformation applied to the filtered element to reflect the resolution of the filter. AffineTransform effectiveTransform; effectiveTransform.scale(scale.width(), scale.height()); effectiveTransform.multiply(filterData->shearFreeAbsoluteTransform); OwnPtr<ImageBuffer> sourceGraphic; RenderingMode renderingMode = object->document()->page()->settings()->acceleratedFiltersEnabled() ? Accelerated : Unaccelerated; if (!SVGImageBufferTools::createImageBuffer(drawingRegion, effectiveTransform, sourceGraphic, ColorSpaceLinearRGB, renderingMode)) { ASSERT(!m_filter.contains(object)); filterData->savedContext = context; m_filter.set(object, filterData.leakPtr()); return false; } // Set the rendering mode from the page's settings. filterData->filter->setRenderingMode(renderingMode); GraphicsContext* sourceGraphicContext = sourceGraphic->context(); ASSERT(sourceGraphicContext); filterData->sourceGraphicBuffer = sourceGraphic.release(); filterData->savedContext = context; context = sourceGraphicContext; ASSERT(!m_filter.contains(object)); m_filter.set(object, filterData.leakPtr()); return true; }
bool RenderSVGResourceGradient::applyResource(RenderObject* object, RenderStyle* style, GraphicsContext*& context, unsigned short resourceMode) { ASSERT(object); ASSERT(style); ASSERT(context); ASSERT(resourceMode != ApplyToDefaultMode); // Be sure to synchronize all SVG properties on the gradientElement _before_ processing any further. // Otherwhise the call to collectGradientAttributes() in createTileImage(), may cause the SVG DOM property // synchronization to kick in, which causes invalidateClients() to be called, which in turn deletes our // GradientData object! Leaving out the line below will cause svg/dynamic-updates/SVG*GradientElement-svgdom* to crash. SVGGradientElement* gradientElement = static_cast<SVGGradientElement*>(node()); if (!gradientElement) return false; gradientElement->updateAnimatedSVGAttribute(anyQName()); if (!m_gradient.contains(object)) m_gradient.set(object, new GradientData); GradientData* gradientData = m_gradient.get(object); // Create gradient object if (!gradientData->gradient) buildGradient(gradientData, gradientElement); if (!gradientData->gradient) return false; // Draw gradient context->save(); bool isPaintingText = resourceMode & ApplyToTextMode; if (isPaintingText) { #if PLATFORM(CG) if (!createMaskAndSwapContextForTextGradient(context, m_savedContext, m_imageBuffer, object)) { context->restore(); return false; } #endif context->setTextDrawingMode(resourceMode & ApplyToFillMode ? cTextFill : cTextStroke); } AffineTransform transform; // CG platforms will handle the gradient space transform for text after applying the // resource, so don't apply it here. For non-CG platforms, we want the text bounding // box applied to the gradient space transform now, so the gradient shader can use it. #if PLATFORM(CG) if (gradientData->boundingBoxMode && !isPaintingText) { #else if (gradientData->boundingBoxMode) { #endif FloatRect objectBoundingBox = object->objectBoundingBox(); transform.translate(objectBoundingBox.x(), objectBoundingBox.y()); transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height()); } transform.multiply(gradientData->transform); gradientData->gradient->setGradientSpaceTransform(transform); const SVGRenderStyle* svgStyle = style->svgStyle(); ASSERT(svgStyle); if (resourceMode & ApplyToFillMode) { context->setAlpha(svgStyle->fillOpacity()); context->setFillGradient(gradientData->gradient); context->setFillRule(svgStyle->fillRule()); } else if (resourceMode & ApplyToStrokeMode) { context->setAlpha(svgStyle->strokeOpacity()); context->setStrokeGradient(gradientData->gradient); applyStrokeStyleToContext(context, style, object); } return true; } void RenderSVGResourceGradient::postApplyResource(RenderObject* object, GraphicsContext*& context, unsigned short resourceMode) { ASSERT(context); ASSERT(resourceMode != ApplyToDefaultMode); if (resourceMode & ApplyToTextMode) { #if PLATFORM(CG) // CG requires special handling for gradient on text if (m_savedContext && m_gradient.contains(object)) { GradientData* gradientData = m_gradient.get(object); // Restore on-screen drawing context context = m_savedContext; m_savedContext = 0; gradientData->gradient->setGradientSpaceTransform(clipToTextMask(context, m_imageBuffer, object, gradientData)); context->setFillGradient(gradientData->gradient); const RenderObject* textRootBlock = findTextRootObject(object); context->fillRect(textRootBlock->repaintRectInLocalCoordinates()); m_imageBuffer.clear(); } #else UNUSED_PARAM(object); #endif } else { if (resourceMode & ApplyToFillMode) context->fillPath(); else if (resourceMode & ApplyToStrokeMode) context->strokePath(); } context->restore(); }