--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/WebCore/rendering/RenderSVGResourcePattern.cpp Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) Research In Motion Limited 2010. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+
+#if ENABLE(SVG)
+#include "RenderSVGResourcePattern.h"
+
+#include "FrameView.h"
+#include "GraphicsContext.h"
+#include "PatternAttributes.h"
+#include "SVGRenderSupport.h"
+
+namespace WebCore {
+
+RenderSVGResourceType RenderSVGResourcePattern::s_resourceType = PatternResourceType;
+
+RenderSVGResourcePattern::RenderSVGResourcePattern(SVGPatternElement* node)
+ : RenderSVGResourceContainer(node)
+{
+}
+
+RenderSVGResourcePattern::~RenderSVGResourcePattern()
+{
+ deleteAllValues(m_pattern);
+ m_pattern.clear();
+}
+
+void RenderSVGResourcePattern::invalidateClients()
+{
+ const HashMap<RenderObject*, PatternData*>::const_iterator end = m_pattern.end();
+ for (HashMap<RenderObject*, PatternData*>::const_iterator it = m_pattern.begin(); it != end; ++it)
+ markForLayoutAndResourceInvalidation(it->first, false);
+
+ deleteAllValues(m_pattern);
+ m_pattern.clear();
+}
+
+void RenderSVGResourcePattern::invalidateClient(RenderObject* object)
+{
+ ASSERT(object);
+ if (!m_pattern.contains(object))
+ return;
+
+ delete m_pattern.take(object);
+ markForLayoutAndResourceInvalidation(object, false);
+}
+
+bool RenderSVGResourcePattern::childElementReferencesResource(const SVGRenderStyle* style, const String& referenceId) const
+{
+ if (style->hasFill()) {
+ if (style->fillPaint()->matchesTargetURI(referenceId))
+ return true;
+ }
+
+ if (style->hasStroke()) {
+ if (style->strokePaint()->matchesTargetURI(referenceId))
+ return true;
+ }
+
+ return false;
+}
+
+bool RenderSVGResourcePattern::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 patternElement _before_ processing any further.
+ // Otherwhise the call to collectPatternAttributes() in createTileImage(), may cause the SVG DOM property
+ // synchronization to kick in, which causes invalidateClients() to be called, which in turn deletes our
+ // PatternData object! Leaving out the line below will cause svg/dynamic-updates/SVGPatternElement-svgdom* to crash.
+ SVGPatternElement* patternElement = static_cast<SVGPatternElement*>(node());
+ if (!patternElement)
+ return false;
+
+ patternElement->updateAnimatedSVGAttribute(anyQName());
+
+ if (!m_pattern.contains(object))
+ m_pattern.set(object, new PatternData);
+
+ PatternData* patternData = m_pattern.get(object);
+ if (!patternData->pattern) {
+ // Create tile image
+ OwnPtr<ImageBuffer> tileImage = createTileImage(patternData, patternElement, object);
+ if (!tileImage)
+ return false;
+
+ // Create pattern object
+ buildPattern(patternData, tileImage.release());
+
+ if (!patternData->pattern)
+ return false;
+
+ patternData->pattern->setPatternSpaceTransform(patternData->transform);
+ }
+
+ // Draw pattern
+ context->save();
+
+ const SVGRenderStyle* svgStyle = style->svgStyle();
+ ASSERT(svgStyle);
+
+ if (resourceMode & ApplyToFillMode) {
+ context->setAlpha(svgStyle->fillOpacity());
+ context->setFillPattern(patternData->pattern);
+ context->setFillRule(svgStyle->fillRule());
+ } else if (resourceMode & ApplyToStrokeMode) {
+ if (svgStyle->vectorEffect() == VE_NON_SCALING_STROKE)
+ patternData->pattern->setPatternSpaceTransform(transformOnNonScalingStroke(object, patternData->transform));
+ context->setAlpha(svgStyle->strokeOpacity());
+ context->setStrokePattern(patternData->pattern);
+ SVGRenderSupport::applyStrokeStyleToContext(context, style, object);
+ }
+
+ if (resourceMode & ApplyToTextMode) {
+ if (resourceMode & ApplyToFillMode) {
+ context->setTextDrawingMode(cTextFill);
+
+#if PLATFORM(CG)
+ context->applyFillPattern();
+#endif
+ } else if (resourceMode & ApplyToStrokeMode) {
+ context->setTextDrawingMode(cTextStroke);
+
+#if PLATFORM(CG)
+ context->applyStrokePattern();
+#endif
+ }
+ }
+
+ return true;
+}
+
+void RenderSVGResourcePattern::postApplyResource(RenderObject*, GraphicsContext*& context, unsigned short resourceMode)
+{
+ ASSERT(context);
+ ASSERT(resourceMode != ApplyToDefaultMode);
+
+ if (!(resourceMode & ApplyToTextMode)) {
+ if (resourceMode & ApplyToFillMode)
+ context->fillPath();
+ else if (resourceMode & ApplyToStrokeMode)
+ context->strokePath();
+ }
+
+ context->restore();
+}
+
+static inline FloatRect calculatePatternBoundaries(PatternAttributes& attributes,
+ const FloatRect& objectBoundingBox,
+ const SVGPatternElement* patternElement)
+{
+ if (attributes.boundingBoxMode())
+ return FloatRect(attributes.x().valueAsPercentage() * objectBoundingBox.width(),
+ attributes.y().valueAsPercentage() * objectBoundingBox.height(),
+ attributes.width().valueAsPercentage() * objectBoundingBox.width(),
+ attributes.height().valueAsPercentage() * objectBoundingBox.height());
+
+ return FloatRect(attributes.x().value(patternElement),
+ attributes.y().value(patternElement),
+ attributes.width().value(patternElement),
+ attributes.height().value(patternElement));
+}
+
+FloatRect RenderSVGResourcePattern::calculatePatternBoundariesIncludingOverflow(PatternAttributes& attributes,
+ const FloatRect& objectBoundingBox,
+ const AffineTransform& viewBoxCTM,
+ const FloatRect& patternBoundaries) const
+{
+ // Eventually calculate the pattern content boundaries (only needed with overflow="visible").
+ FloatRect patternContentBoundaries;
+
+ const RenderStyle* style = this->style();
+ if (style->overflowX() == OVISIBLE && style->overflowY() == OVISIBLE) {
+ for (Node* node = attributes.patternContentElement()->firstChild(); node; node = node->nextSibling()) {
+ if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyledTransformable() || !node->renderer())
+ continue;
+ patternContentBoundaries.unite(node->renderer()->repaintRectInLocalCoordinates());
+ }
+ }
+
+ if (patternContentBoundaries.isEmpty())
+ return patternBoundaries;
+
+ FloatRect patternBoundariesIncludingOverflow = patternBoundaries;
+
+ // Respect objectBoundingBoxMode for patternContentUnits, if viewBox is not set.
+ if (!viewBoxCTM.isIdentity())
+ patternContentBoundaries = viewBoxCTM.mapRect(patternContentBoundaries);
+ else if (attributes.boundingBoxModeContent())
+ patternContentBoundaries = FloatRect(patternContentBoundaries.x() * objectBoundingBox.width(),
+ patternContentBoundaries.y() * objectBoundingBox.height(),
+ patternContentBoundaries.width() * objectBoundingBox.width(),
+ patternContentBoundaries.height() * objectBoundingBox.height());
+
+ patternBoundariesIncludingOverflow.unite(patternContentBoundaries);
+ return patternBoundariesIncludingOverflow;
+}
+
+// FIXME: This method should be removed. RenderSVGResourcePatterns usage of it is just wrong.
+static inline void clampImageBufferSizeToViewport(FrameView* frameView, IntSize& size)
+{
+ if (!frameView)
+ return;
+
+ int viewWidth = frameView->visibleWidth();
+ int viewHeight = frameView->visibleHeight();
+
+ if (size.width() > viewWidth)
+ size.setWidth(viewWidth);
+
+ if (size.height() > viewHeight)
+ size.setHeight(viewHeight);
+}
+
+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;
+
+ // Early exit, if this resource contains a child which references ourselves.
+ if (containsCyclicReference(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;
+ SVGRenderSupport::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();
+}
+
+void RenderSVGResourcePattern::buildPattern(PatternData* patternData, PassOwnPtr<ImageBuffer> tileImage) const
+{
+ if (!tileImage->image()) {
+ patternData->pattern = 0;
+ return;
+ }
+
+ IntRect tileRect = tileImage->image()->rect();
+ if (tileRect.width() <= patternData->boundaries.width() && tileRect.height() <= patternData->boundaries.height()) {
+ patternData->pattern = Pattern::create(tileImage->image(), true, true);
+ return;
+ }
+
+ // Draw the first cell of the pattern manually to support overflow="visible" on all platforms.
+ int tileWidth = static_cast<int>(patternData->boundaries.width() + 0.5f);
+ int tileHeight = static_cast<int>(patternData->boundaries.height() + 0.5f);
+
+ // Don't create ImageBuffers with image size of 0
+ if (!tileWidth || !tileHeight) {
+ patternData->pattern = 0;
+ return;
+ }
+
+ OwnPtr<ImageBuffer> newTileImage = ImageBuffer::create(IntSize(tileWidth, tileHeight));
+ GraphicsContext* newTileImageContext = newTileImage->context();
+
+ int numY = static_cast<int>(ceilf(tileRect.height() / tileHeight)) + 1;
+ int numX = static_cast<int>(ceilf(tileRect.width() / tileWidth)) + 1;
+
+ newTileImageContext->save();
+ newTileImageContext->translate(-patternData->boundaries.width() * numX, -patternData->boundaries.height() * numY);
+ for (int i = numY; i > 0; --i) {
+ newTileImageContext->translate(0, patternData->boundaries.height());
+ for (int j = numX; j > 0; --j) {
+ newTileImageContext->translate(patternData->boundaries.width(), 0);
+ newTileImageContext->drawImage(tileImage->image(), style()->colorSpace(), tileRect, tileRect);
+ }
+ newTileImageContext->translate(-patternData->boundaries.width() * numX, 0);
+ }
+ newTileImageContext->restore();
+
+ patternData->pattern = Pattern::create(newTileImage->image(), true, true);
+}
+
+}
+
+#endif