NativeImagePtr SVGImage::nativeImageForCurrentFrame() { // FIXME: In order to support dynamic SVGs we need to have a way to invalidate this // frame cache, or better yet, not use a cache for tiled drawing at all, instead // having a tiled drawing callback (hopefully non-virtual). if (!m_frameCache) { m_frameCache.set(ImageBuffer::create(size(), false).release()); if (!m_frameCache) // failed to allocate image return 0; renderSubtreeToImage(m_frameCache.get(), m_frame->contentRenderer()); } return m_frameCache->image()->nativeImageForCurrentFrame(); }
void RenderSVGResourceMasker::createMaskImage(MaskerData* maskerData, const SVGMaskElement* maskElement, RenderObject* object) { FloatRect objectBoundingBox = object->objectBoundingBox(); // Mask rect clipped with clippingBoundingBox and filterBoundingBox as long as they are present. maskerData->maskRect = object->repaintRectInLocalCoordinates(); if (maskerData->maskRect.isEmpty()) { maskerData->emptyMask = true; return; } if (m_maskBoundaries.isEmpty()) calculateMaskContentRepaintRect(); FloatRect repaintRect = m_maskBoundaries; AffineTransform contextTransform; // We need to scale repaintRect for objectBoundingBox to get the drawing area. if (maskElement->maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { contextTransform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height()); FloatPoint contextAdjustment = repaintRect.location(); repaintRect = contextTransform.mapRect(repaintRect); repaintRect.move(objectBoundingBox.x(), objectBoundingBox.y()); contextTransform.translate(-contextAdjustment.x(), -contextAdjustment.y()); } repaintRect.intersect(maskerData->maskRect); maskerData->maskRect = repaintRect; IntRect maskImageRect = enclosingIntRect(maskerData->maskRect); maskImageRect.setLocation(IntPoint()); // Don't create ImageBuffers with image size of 0 if (maskImageRect.isEmpty()) { maskerData->emptyMask = true; return; } // FIXME: This changes color space to linearRGB, the default color space // for masking operations in SVG. We need a switch for the other color-space // attribute values sRGB, inherit and auto. maskerData->maskImage = ImageBuffer::create(maskImageRect.size(), LinearRGB); if (!maskerData->maskImage) return; GraphicsContext* maskImageContext = maskerData->maskImage->context(); ASSERT(maskImageContext); maskImageContext->save(); if (maskElement->maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) maskImageContext->translate(-maskerData->maskRect.x(), -maskerData->maskRect.y()); maskImageContext->concatCTM(contextTransform); // draw the content into the ImageBuffer for (Node* node = maskElement->firstChild(); node; node = node->nextSibling()) { RenderObject* renderer = node->renderer(); if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyled() || !renderer) continue; RenderStyle* style = renderer->style(); if (!style || style->display() == NONE || style->visibility() != VISIBLE) continue; renderSubtreeToImage(maskerData->maskImage.get(), renderer); } maskImageContext->restore(); // create the luminance mask RefPtr<ImageData> imageData(maskerData->maskImage->getUnmultipliedImageData(maskImageRect)); CanvasPixelArray* srcPixelArray(imageData->data()); for (unsigned pixelOffset = 0; pixelOffset < srcPixelArray->length(); pixelOffset += 4) { unsigned char a = srcPixelArray->get(pixelOffset + 3); if (!a) continue; unsigned char r = srcPixelArray->get(pixelOffset); unsigned char g = srcPixelArray->get(pixelOffset + 1); unsigned char b = srcPixelArray->get(pixelOffset + 2); double luma = (r * 0.2125 + g * 0.7154 + b * 0.0721) * ((double)a / 255.0); srcPixelArray->set(pixelOffset + 3, luma); } maskerData->maskImage->putUnmultipliedImageData(imageData.get(), maskImageRect, IntPoint()); }
PassOwnPtr<ImageBuffer> RenderSVGResourcePattern::createTileImage(PatternData* patternData, const SVGPatternElement* patternElement, RenderObject* object) const { PatternAttributes attributes = patternElement->collectPatternProperties(); // If we couldn't determine the pattern content element root, stop here. if (!attributes.patternContentElement()) return 0; FloatRect objectBoundingBox = object->objectBoundingBox(); FloatRect patternBoundaries = calculatePatternBoundaries(attributes, objectBoundingBox, patternElement); AffineTransform patternTransform = attributes.patternTransform(); AffineTransform viewBoxCTM = patternElement->viewBoxToViewTransform(patternElement->viewBox(), patternElement->preserveAspectRatio(), patternBoundaries.width(), patternBoundaries.height()); FloatRect patternBoundariesIncludingOverflow = calculatePatternBoundariesIncludingOverflow(attributes, objectBoundingBox, viewBoxCTM, patternBoundaries); IntSize imageSize(lroundf(patternBoundariesIncludingOverflow.width()), lroundf(patternBoundariesIncludingOverflow.height())); // FIXME: We should be able to clip this more, needs investigation clampImageBufferSizeToViewport(object->document()->view(), imageSize); // Don't create ImageBuffers with image size of 0 if (imageSize.isEmpty()) return 0; OwnPtr<ImageBuffer> tileImage = ImageBuffer::create(imageSize); GraphicsContext* context = tileImage->context(); ASSERT(context); context->save(); // Translate to pattern start origin if (patternBoundariesIncludingOverflow.location() != patternBoundaries.location()) { context->translate(patternBoundaries.x() - patternBoundariesIncludingOverflow.x(), patternBoundaries.y() - patternBoundariesIncludingOverflow.y()); patternBoundaries.setLocation(patternBoundariesIncludingOverflow.location()); } // Process viewBox or boundingBoxModeContent correction if (!viewBoxCTM.isIdentity()) context->concatCTM(viewBoxCTM); else if (attributes.boundingBoxModeContent()) { context->translate(objectBoundingBox.x(), objectBoundingBox.y()); context->scale(FloatSize(objectBoundingBox.width(), objectBoundingBox.height())); } // Render subtree into ImageBuffer for (Node* node = attributes.patternContentElement()->firstChild(); node; node = node->nextSibling()) { if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyled() || !node->renderer()) continue; renderSubtreeToImage(tileImage.get(), node->renderer()); } patternData->boundaries = patternBoundaries; // Compute pattern transformation patternData->transform.translate(patternBoundaries.x(), patternBoundaries.y()); patternData->transform.multiply(patternTransform); context->restore(); return tileImage.release(); }
PassOwnPtr<ImageBuffer> SVGMaskElement::drawMaskerContent(const FloatRect& targetRect, FloatRect& maskDestRect) const { // Determine specified mask size float xValue; float yValue; float widthValue; float heightValue; if (maskUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { xValue = x().valueAsPercentage() * targetRect.width(); yValue = y().valueAsPercentage() * targetRect.height(); widthValue = width().valueAsPercentage() * targetRect.width(); heightValue = height().valueAsPercentage() * targetRect.height(); } else { xValue = x().value(this); yValue = y().value(this); widthValue = width().value(this); heightValue = height().value(this); } IntSize imageSize(lroundf(widthValue), lroundf(heightValue)); clampImageBufferSizeToViewport(document()->view(), imageSize); if (imageSize.width() < static_cast<int>(widthValue)) widthValue = imageSize.width(); if (imageSize.height() < static_cast<int>(heightValue)) heightValue = imageSize.height(); OwnPtr<ImageBuffer> maskImage = ImageBuffer::create(imageSize, false); if (!maskImage) return 0; maskDestRect = FloatRect(xValue, yValue, widthValue, heightValue); if (maskUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) maskDestRect.move(targetRect.x(), targetRect.y()); GraphicsContext* maskImageContext = maskImage->context(); ASSERT(maskImageContext); maskImageContext->save(); maskImageContext->translate(-xValue, -yValue); if (maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { maskImageContext->save(); maskImageContext->scale(FloatSize(targetRect.width(), targetRect.height())); } // Render subtree into ImageBuffer for (Node* n = firstChild(); n; n = n->nextSibling()) { SVGElement* elem = 0; if (n->isSVGElement()) elem = static_cast<SVGElement*>(n); if (!elem || !elem->isStyled()) continue; SVGStyledElement* e = static_cast<SVGStyledElement*>(elem); RenderObject* item = e->renderer(); if (!item) continue; renderSubtreeToImage(maskImage.get(), item); } if (maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) maskImageContext->restore(); maskImageContext->restore(); return maskImage.release(); }
PassOwnPtr<ImageBuffer> SVGMaskElement::drawMaskerContent(const FloatRect& targetRect, FloatRect& maskDestRect) const { // Determine specified mask size if (maskUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) maskDestRect = FloatRect(x().valueAsPercentage() * targetRect.width(), y().valueAsPercentage() * targetRect.height(), width().valueAsPercentage() * targetRect.width(), height().valueAsPercentage() * targetRect.height()); else maskDestRect = FloatRect(x().value(this), y().value(this), width().value(this), height().value(this)); IntSize imageSize(lroundf(maskDestRect.width()), lroundf(maskDestRect.height())); clampImageBufferSizeToViewport(document()->view(), imageSize); if (imageSize.width() < static_cast<int>(maskDestRect.width())) maskDestRect.setWidth(imageSize.width()); if (imageSize.height() < static_cast<int>(maskDestRect.height())) maskDestRect.setHeight(imageSize.height()); // FIXME: This changes color space to linearRGB, the default color space // for masking operations in SVG. We need a switch for the other color-space // attribute values sRGB, inherit and auto. OwnPtr<ImageBuffer> maskImage = ImageBuffer::create(imageSize, LinearRGB); if (!maskImage) return 0; FloatPoint maskContextLocation = maskDestRect.location(); if (maskUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) maskDestRect.move(targetRect.x(), targetRect.y()); if (maskContentUnits() != SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) maskContextLocation.move(targetRect.x(), targetRect.y()); GraphicsContext* maskImageContext = maskImage->context(); ASSERT(maskImageContext); maskImageContext->save(); maskImageContext->translate(-maskContextLocation.x(), -maskContextLocation.y()); if (maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { maskImageContext->save(); maskImageContext->scale(FloatSize(targetRect.width(), targetRect.height())); } // Render subtree into ImageBuffer for (Node* n = firstChild(); n; n = n->nextSibling()) { SVGElement* elem = 0; if (n->isSVGElement()) elem = static_cast<SVGElement*>(n); if (!elem || !elem->isStyled()) continue; SVGStyledElement* e = static_cast<SVGStyledElement*>(elem); RenderObject* item = e->renderer(); if (!item) continue; renderSubtreeToImage(maskImage.get(), item); } if (maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) maskImageContext->restore(); maskImageContext->restore(); return maskImage.release(); }