Home | History | Annotate | Download | only in svg
      1 /*
      2  * Copyright (C) 2006 Nikolas Zimmermann <zimmermann (at) kde.org>
      3  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
      4  *
      5  * This library is free software; you can redistribute it and/or
      6  * modify it under the terms of the GNU Library General Public
      7  * License as published by the Free Software Foundation; either
      8  * version 2 of the License, or (at your option) any later version.
      9  *
     10  * This library is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13  * Library General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU Library General Public License
     16  * along with this library; see the file COPYING.LIB.  If not, write to
     17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18  * Boston, MA 02110-1301, USA.
     19  */
     20 
     21 #include "config.h"
     22 
     23 #if ENABLE(SVG)
     24 #include "RenderSVGResourcePattern.h"
     25 
     26 #include "FrameView.h"
     27 #include "GraphicsContext.h"
     28 #include "PatternAttributes.h"
     29 #include "RenderSVGRoot.h"
     30 #include "SVGImageBufferTools.h"
     31 #include "SVGRenderSupport.h"
     32 
     33 namespace WebCore {
     34 
     35 RenderSVGResourceType RenderSVGResourcePattern::s_resourceType = PatternResourceType;
     36 
     37 RenderSVGResourcePattern::RenderSVGResourcePattern(SVGPatternElement* node)
     38     : RenderSVGResourceContainer(node)
     39     , m_shouldCollectPatternAttributes(true)
     40 {
     41 }
     42 
     43 RenderSVGResourcePattern::~RenderSVGResourcePattern()
     44 {
     45     if (m_pattern.isEmpty())
     46         return;
     47 
     48     deleteAllValues(m_pattern);
     49     m_pattern.clear();
     50 }
     51 
     52 void RenderSVGResourcePattern::removeAllClientsFromCache(bool markForInvalidation)
     53 {
     54     if (!m_pattern.isEmpty()) {
     55         deleteAllValues(m_pattern);
     56         m_pattern.clear();
     57     }
     58 
     59     m_shouldCollectPatternAttributes = true;
     60     markAllClientsForInvalidation(markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
     61 }
     62 
     63 void RenderSVGResourcePattern::removeClientFromCache(RenderObject* client, bool markForInvalidation)
     64 {
     65     ASSERT(client);
     66 
     67     if (m_pattern.contains(client))
     68         delete m_pattern.take(client);
     69 
     70     markClientForInvalidation(client, markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
     71 }
     72 
     73 bool RenderSVGResourcePattern::applyResource(RenderObject* object, RenderStyle* style, GraphicsContext*& context, unsigned short resourceMode)
     74 {
     75     ASSERT(object);
     76     ASSERT(style);
     77     ASSERT(context);
     78     ASSERT(resourceMode != ApplyToDefaultMode);
     79 
     80     // Be sure to synchronize all SVG properties on the patternElement _before_ processing any further.
     81     // Otherwhise the call to collectPatternAttributes() below, may cause the SVG DOM property
     82     // synchronization to kick in, which causes removeAllClientsFromCache() to be called, which in turn deletes our
     83     // PatternData object! Leaving out the line below will cause svg/dynamic-updates/SVGPatternElement-svgdom* to crash.
     84     SVGPatternElement* patternElement = static_cast<SVGPatternElement*>(node());
     85     if (!patternElement)
     86         return false;
     87 
     88     if (m_shouldCollectPatternAttributes) {
     89         patternElement->updateAnimatedSVGAttribute(anyQName());
     90 
     91         m_attributes = PatternAttributes();
     92         patternElement->collectPatternAttributes(m_attributes);
     93         m_shouldCollectPatternAttributes = false;
     94     }
     95 
     96     // Spec: When the geometry of the applicable element has no width or height and objectBoundingBox is specified,
     97     // then the given effect (e.g. a gradient or a filter) will be ignored.
     98     FloatRect objectBoundingBox = object->objectBoundingBox();
     99     if (m_attributes.boundingBoxMode() && objectBoundingBox.isEmpty())
    100         return false;
    101 
    102     if (!m_pattern.contains(object))
    103         m_pattern.set(object, new PatternData);
    104 
    105     PatternData* patternData = m_pattern.get(object);
    106     if (!patternData->pattern) {
    107         // If we couldn't determine the pattern content element root, stop here.
    108         if (!m_attributes.patternContentElement())
    109             return false;
    110 
    111         // Compute all necessary transformations to build the tile image & the pattern.
    112         FloatRect tileBoundaries;
    113         AffineTransform tileImageTransform;
    114         if (!buildTileImageTransform(object, m_attributes, patternElement, tileBoundaries, tileImageTransform))
    115             return false;
    116 
    117         AffineTransform absoluteTransform;
    118         SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransform);
    119 
    120         FloatRect absoluteTileBoundaries = absoluteTransform.mapRect(tileBoundaries);
    121 
    122         // Build tile image.
    123         OwnPtr<ImageBuffer> tileImage = createTileImage(object, m_attributes, tileBoundaries, absoluteTileBoundaries, tileImageTransform);
    124         if (!tileImage)
    125             return false;
    126 
    127         RefPtr<Image> copiedImage = tileImage->copyImage();
    128         if (!copiedImage)
    129             return false;
    130 
    131         // Build pattern.
    132         patternData->pattern = Pattern::create(copiedImage, true, true);
    133         if (!patternData->pattern)
    134             return false;
    135 
    136         // Compute pattern space transformation.
    137         patternData->transform.translate(tileBoundaries.x(), tileBoundaries.y());
    138         patternData->transform.scale(tileBoundaries.width() / absoluteTileBoundaries.width(), tileBoundaries.height() / absoluteTileBoundaries.height());
    139 
    140         AffineTransform patternTransform = m_attributes.patternTransform();
    141         if (!patternTransform.isIdentity())
    142             patternData->transform = patternTransform * patternData->transform;
    143 
    144         patternData->pattern->setPatternSpaceTransform(patternData->transform);
    145     }
    146 
    147     // Draw pattern
    148     context->save();
    149 
    150     const SVGRenderStyle* svgStyle = style->svgStyle();
    151     ASSERT(svgStyle);
    152 
    153     if (resourceMode & ApplyToFillMode) {
    154         context->setAlpha(svgStyle->fillOpacity());
    155         context->setFillPattern(patternData->pattern);
    156         context->setFillRule(svgStyle->fillRule());
    157     } else if (resourceMode & ApplyToStrokeMode) {
    158         if (svgStyle->vectorEffect() == VE_NON_SCALING_STROKE)
    159             patternData->pattern->setPatternSpaceTransform(transformOnNonScalingStroke(object, patternData->transform));
    160         context->setAlpha(svgStyle->strokeOpacity());
    161         context->setStrokePattern(patternData->pattern);
    162         SVGRenderSupport::applyStrokeStyleToContext(context, style, object);
    163     }
    164 
    165     if (resourceMode & ApplyToTextMode) {
    166         if (resourceMode & ApplyToFillMode) {
    167             context->setTextDrawingMode(TextModeFill);
    168 
    169 #if USE(CG)
    170             context->applyFillPattern();
    171 #endif
    172         } else if (resourceMode & ApplyToStrokeMode) {
    173             context->setTextDrawingMode(TextModeStroke);
    174 
    175 #if USE(CG)
    176             context->applyStrokePattern();
    177 #endif
    178         }
    179     }
    180 
    181     return true;
    182 }
    183 
    184 void RenderSVGResourcePattern::postApplyResource(RenderObject*, GraphicsContext*& context, unsigned short resourceMode, const Path* path)
    185 {
    186     ASSERT(context);
    187     ASSERT(resourceMode != ApplyToDefaultMode);
    188 
    189     if (path && !(resourceMode & ApplyToTextMode)) {
    190         if (resourceMode & ApplyToFillMode)
    191             context->fillPath(*path);
    192         else if (resourceMode & ApplyToStrokeMode)
    193             context->strokePath(*path);
    194     }
    195 
    196     context->restore();
    197 }
    198 
    199 static inline FloatRect calculatePatternBoundaries(const PatternAttributes& attributes,
    200                                                    const FloatRect& objectBoundingBox,
    201                                                    const SVGPatternElement* patternElement)
    202 {
    203     ASSERT(patternElement);
    204 
    205     if (attributes.boundingBoxMode())
    206         return FloatRect(attributes.x().valueAsPercentage() * objectBoundingBox.width() + objectBoundingBox.x(),
    207                          attributes.y().valueAsPercentage() * objectBoundingBox.height() + objectBoundingBox.y(),
    208                          attributes.width().valueAsPercentage() * objectBoundingBox.width(),
    209                          attributes.height().valueAsPercentage() * objectBoundingBox.height());
    210 
    211     return FloatRect(attributes.x().value(patternElement),
    212                      attributes.y().value(patternElement),
    213                      attributes.width().value(patternElement),
    214                      attributes.height().value(patternElement));
    215 }
    216 
    217 bool RenderSVGResourcePattern::buildTileImageTransform(RenderObject* renderer,
    218                                                        const PatternAttributes& attributes,
    219                                                        const SVGPatternElement* patternElement,
    220                                                        FloatRect& patternBoundaries,
    221                                                        AffineTransform& tileImageTransform) const
    222 {
    223     ASSERT(renderer);
    224     ASSERT(patternElement);
    225 
    226     FloatRect objectBoundingBox = renderer->objectBoundingBox();
    227     patternBoundaries = calculatePatternBoundaries(attributes, objectBoundingBox, patternElement);
    228     if (patternBoundaries.width() <= 0 || patternBoundaries.height() <= 0)
    229         return false;
    230 
    231     AffineTransform viewBoxCTM = patternElement->viewBoxToViewTransform(attributes.viewBox(), attributes.preserveAspectRatio(), patternBoundaries.width(), patternBoundaries.height());
    232 
    233     // Apply viewBox/objectBoundingBox transformations.
    234     if (!viewBoxCTM.isIdentity())
    235         tileImageTransform = viewBoxCTM;
    236     else if (attributes.boundingBoxModeContent())
    237         tileImageTransform.scale(objectBoundingBox.width(), objectBoundingBox.height());
    238 
    239     return true;
    240 }
    241 
    242 PassOwnPtr<ImageBuffer> RenderSVGResourcePattern::createTileImage(RenderObject* object,
    243                                                                   const PatternAttributes& attributes,
    244                                                                   const FloatRect& tileBoundaries,
    245                                                                   const FloatRect& absoluteTileBoundaries,
    246                                                                   const AffineTransform& tileImageTransform) const
    247 {
    248     ASSERT(object);
    249 
    250     // Clamp tile image size against SVG viewport size, as last resort, to avoid allocating huge image buffers.
    251     FloatRect contentBoxRect = SVGRenderSupport::findTreeRootObject(object)->contentBoxRect();
    252 
    253     FloatRect clampedAbsoluteTileBoundaries = absoluteTileBoundaries;
    254     if (clampedAbsoluteTileBoundaries.width() > contentBoxRect.width())
    255         clampedAbsoluteTileBoundaries.setWidth(contentBoxRect.width());
    256 
    257     if (clampedAbsoluteTileBoundaries.height() > contentBoxRect.height())
    258         clampedAbsoluteTileBoundaries.setHeight(contentBoxRect.height());
    259 
    260     OwnPtr<ImageBuffer> tileImage;
    261 
    262     if (!SVGImageBufferTools::createImageBuffer(absoluteTileBoundaries, clampedAbsoluteTileBoundaries, tileImage, ColorSpaceDeviceRGB))
    263         return PassOwnPtr<ImageBuffer>();
    264 
    265     GraphicsContext* tileImageContext = tileImage->context();
    266     ASSERT(tileImageContext);
    267 
    268     // The image buffer represents the final rendered size, so the content has to be scaled (to avoid pixelation).
    269     tileImageContext->scale(FloatSize(absoluteTileBoundaries.width() / tileBoundaries.width(),
    270                                       absoluteTileBoundaries.height() / tileBoundaries.height()));
    271 
    272     // Apply tile image transformations.
    273     if (!tileImageTransform.isIdentity())
    274         tileImageContext->concatCTM(tileImageTransform);
    275 
    276     AffineTransform contentTransformation;
    277     if (attributes.boundingBoxModeContent())
    278         contentTransformation = tileImageTransform;
    279 
    280     // Draw the content into the ImageBuffer.
    281     for (Node* node = attributes.patternContentElement()->firstChild(); node; node = node->nextSibling()) {
    282         if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyled() || !node->renderer())
    283             continue;
    284         SVGImageBufferTools::renderSubtreeToImageBuffer(tileImage.get(), node->renderer(), contentTransformation);
    285     }
    286 
    287     return tileImage.release();
    288 }
    289 
    290 }
    291 
    292 #endif
    293