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 #include "core/rendering/svg/RenderSVGResourcePattern.h"
     24 
     25 #include "core/platform/graphics/GraphicsContext.h"
     26 #include "core/rendering/svg/SVGRenderSupport.h"
     27 #include "core/rendering/svg/SVGRenderingContext.h"
     28 #include "core/svg/PatternAttributes.h"
     29 #include "core/svg/SVGFitToViewBox.h"
     30 
     31 namespace WebCore {
     32 
     33 RenderSVGResourceType RenderSVGResourcePattern::s_resourceType = PatternResourceType;
     34 
     35 RenderSVGResourcePattern::RenderSVGResourcePattern(SVGPatternElement* node)
     36     : RenderSVGResourceContainer(node)
     37     , m_shouldCollectPatternAttributes(true)
     38 {
     39 }
     40 
     41 void RenderSVGResourcePattern::removeAllClientsFromCache(bool markForInvalidation)
     42 {
     43     m_patternMap.clear();
     44     m_shouldCollectPatternAttributes = true;
     45     markAllClientsForInvalidation(markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
     46 }
     47 
     48 void RenderSVGResourcePattern::removeClientFromCache(RenderObject* client, bool markForInvalidation)
     49 {
     50     ASSERT(client);
     51     m_patternMap.remove(client);
     52     markClientForInvalidation(client, markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
     53 }
     54 
     55 PatternData* RenderSVGResourcePattern::buildPattern(RenderObject* object, unsigned short resourceMode)
     56 {
     57     PatternData* currentData = m_patternMap.get(object);
     58     if (currentData && currentData->pattern)
     59         return currentData;
     60 
     61     SVGPatternElement* patternElement = toSVGPatternElement(node());
     62     if (!patternElement)
     63         return 0;
     64 
     65     if (m_shouldCollectPatternAttributes) {
     66         patternElement->synchronizeAnimatedSVGAttribute(anyQName());
     67 
     68         m_attributes = PatternAttributes();
     69         patternElement->collectPatternAttributes(m_attributes);
     70         m_shouldCollectPatternAttributes = false;
     71     }
     72 
     73     // If we couldn't determine the pattern content element root, stop here.
     74     if (!m_attributes.patternContentElement())
     75         return 0;
     76 
     77     // An empty viewBox disables rendering.
     78     if (m_attributes.hasViewBox() && m_attributes.viewBox().isEmpty())
     79         return 0;
     80 
     81     // Compute all necessary transformations to build the tile image & the pattern.
     82     FloatRect tileBoundaries;
     83     AffineTransform tileImageTransform;
     84     if (!buildTileImageTransform(object, m_attributes, patternElement, tileBoundaries, tileImageTransform))
     85         return 0;
     86 
     87     AffineTransform absoluteTransformIgnoringRotation;
     88     SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(object, absoluteTransformIgnoringRotation);
     89 
     90     // Ignore 2D rotation, as it doesn't affect the size of the tile.
     91     SVGRenderingContext::clear2DRotation(absoluteTransformIgnoringRotation);
     92     FloatRect absoluteTileBoundaries = absoluteTransformIgnoringRotation.mapRect(tileBoundaries);
     93     FloatRect clampedAbsoluteTileBoundaries;
     94 
     95     // Scale the tile size to match the scale level of the patternTransform.
     96     absoluteTileBoundaries.scale(static_cast<float>(m_attributes.patternTransform().xScale()),
     97         static_cast<float>(m_attributes.patternTransform().yScale()));
     98 
     99     // Build tile image.
    100     OwnPtr<ImageBuffer> tileImage = createTileImage(m_attributes, tileBoundaries, absoluteTileBoundaries, tileImageTransform, clampedAbsoluteTileBoundaries);
    101     if (!tileImage)
    102         return 0;
    103 
    104     RefPtr<Image> copiedImage = tileImage->copyImage(CopyBackingStore);
    105     if (!copiedImage)
    106         return 0;
    107 
    108     // Build pattern.
    109     OwnPtr<PatternData> patternData = adoptPtr(new PatternData);
    110     patternData->pattern = Pattern::create(copiedImage, true, true);
    111 
    112     // Compute pattern space transformation.
    113     const IntSize tileImageSize = tileImage->logicalSize();
    114     patternData->transform.translate(tileBoundaries.x(), tileBoundaries.y());
    115     patternData->transform.scale(tileBoundaries.width() / tileImageSize.width(), tileBoundaries.height() / tileImageSize.height());
    116 
    117     AffineTransform patternTransform = m_attributes.patternTransform();
    118     if (!patternTransform.isIdentity())
    119         patternData->transform = patternTransform * patternData->transform;
    120 
    121     // Account for text drawing resetting the context to non-scaled, see SVGInlineTextBox::paintTextWithShadows.
    122     if (resourceMode & ApplyToTextMode) {
    123         AffineTransform additionalTextTransformation;
    124         if (shouldTransformOnTextPainting(object, additionalTextTransformation))
    125             patternData->transform *= additionalTextTransformation;
    126     }
    127     patternData->pattern->setPatternSpaceTransform(patternData->transform);
    128 
    129     // Various calls above may trigger invalidations in some fringe cases (ImageBuffer allocation
    130     // failures in the SVG image cache for example). To avoid having our PatternData deleted by
    131     // removeAllClientsFromCache(), we only make it visible in the cache at the very end.
    132     return m_patternMap.set(object, patternData.release()).iterator->value.get();
    133 }
    134 
    135 bool RenderSVGResourcePattern::applyResource(RenderObject* object, RenderStyle* style, GraphicsContext*& context, unsigned short resourceMode)
    136 {
    137     ASSERT(object);
    138     ASSERT(style);
    139     ASSERT(context);
    140     ASSERT(resourceMode != ApplyToDefaultMode);
    141 
    142     // Spec: When the geometry of the applicable element has no width or height and objectBoundingBox is specified,
    143     // then the given effect (e.g. a gradient or a filter) will be ignored.
    144     FloatRect objectBoundingBox = object->objectBoundingBox();
    145     if (m_attributes.patternUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX && objectBoundingBox.isEmpty())
    146         return false;
    147 
    148     PatternData* patternData = buildPattern(object, resourceMode);
    149     if (!patternData)
    150         return false;
    151 
    152     // Draw pattern
    153     context->save();
    154 
    155     const SVGRenderStyle* svgStyle = style->svgStyle();
    156     ASSERT(svgStyle);
    157 
    158     if (resourceMode & ApplyToFillMode) {
    159         context->setAlpha(svgStyle->fillOpacity());
    160         context->setFillPattern(patternData->pattern);
    161         context->setFillRule(svgStyle->fillRule());
    162     } else if (resourceMode & ApplyToStrokeMode) {
    163         if (svgStyle->vectorEffect() == VE_NON_SCALING_STROKE)
    164             patternData->pattern->setPatternSpaceTransform(transformOnNonScalingStroke(object, patternData->transform));
    165         context->setAlpha(svgStyle->strokeOpacity());
    166         context->setStrokePattern(patternData->pattern);
    167         SVGRenderSupport::applyStrokeStyleToContext(context, style, object);
    168     }
    169 
    170     if (resourceMode & ApplyToTextMode) {
    171         if (resourceMode & ApplyToFillMode)
    172             context->setTextDrawingMode(TextModeFill);
    173         else if (resourceMode & ApplyToStrokeMode)
    174             context->setTextDrawingMode(TextModeStroke);
    175     }
    176 
    177     return true;
    178 }
    179 
    180 void RenderSVGResourcePattern::postApplyResource(RenderObject*, GraphicsContext*& context, unsigned short resourceMode, const Path* path, const RenderSVGShape* shape)
    181 {
    182     ASSERT(context);
    183     ASSERT(resourceMode != ApplyToDefaultMode);
    184 
    185     if (resourceMode & ApplyToFillMode) {
    186         if (path)
    187             context->fillPath(*path);
    188         else if (shape)
    189             shape->fillShape(context);
    190     }
    191     if (resourceMode & ApplyToStrokeMode) {
    192         if (path)
    193             context->strokePath(*path);
    194         else if (shape)
    195             shape->strokeShape(context);
    196     }
    197 
    198     context->restore();
    199 }
    200 
    201 static inline FloatRect calculatePatternBoundaries(const PatternAttributes& attributes,
    202                                                    const FloatRect& objectBoundingBox,
    203                                                    const SVGPatternElement* patternElement)
    204 {
    205     ASSERT(patternElement);
    206     return SVGLengthContext::resolveRectangle(patternElement, attributes.patternUnits(), objectBoundingBox, attributes.x(), attributes.y(), attributes.width(), attributes.height());
    207 }
    208 
    209 bool RenderSVGResourcePattern::buildTileImageTransform(RenderObject* renderer,
    210                                                        const PatternAttributes& attributes,
    211                                                        const SVGPatternElement* patternElement,
    212                                                        FloatRect& patternBoundaries,
    213                                                        AffineTransform& tileImageTransform) const
    214 {
    215     ASSERT(renderer);
    216     ASSERT(patternElement);
    217 
    218     FloatRect objectBoundingBox = renderer->objectBoundingBox();
    219     patternBoundaries = calculatePatternBoundaries(attributes, objectBoundingBox, patternElement);
    220     if (patternBoundaries.width() <= 0 || patternBoundaries.height() <= 0)
    221         return false;
    222 
    223     AffineTransform viewBoxCTM = SVGFitToViewBox::viewBoxToViewTransform(attributes.viewBox(), attributes.preserveAspectRatio(), patternBoundaries.width(), patternBoundaries.height());
    224 
    225     // Apply viewBox/objectBoundingBox transformations.
    226     if (!viewBoxCTM.isIdentity())
    227         tileImageTransform = viewBoxCTM;
    228     else if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
    229         tileImageTransform.scale(objectBoundingBox.width(), objectBoundingBox.height());
    230 
    231     return true;
    232 }
    233 
    234 PassOwnPtr<ImageBuffer> RenderSVGResourcePattern::createTileImage(const PatternAttributes& attributes,
    235                                                                   const FloatRect& tileBoundaries,
    236                                                                   const FloatRect& absoluteTileBoundaries,
    237                                                                   const AffineTransform& tileImageTransform,
    238                                                                   FloatRect& clampedAbsoluteTileBoundaries) const
    239 {
    240     clampedAbsoluteTileBoundaries = SVGRenderingContext::clampedAbsoluteTargetRect(absoluteTileBoundaries);
    241 
    242     OwnPtr<ImageBuffer> tileImage;
    243 
    244     if (!SVGRenderingContext::createImageBufferForPattern(absoluteTileBoundaries, clampedAbsoluteTileBoundaries, tileImage, Unaccelerated))
    245         return nullptr;
    246 
    247     GraphicsContext* tileImageContext = tileImage->context();
    248     ASSERT(tileImageContext);
    249 
    250     // The image buffer represents the final rendered size, so the content has to be scaled (to avoid pixelation).
    251     tileImageContext->scale(FloatSize(clampedAbsoluteTileBoundaries.width() / tileBoundaries.width(),
    252                                       clampedAbsoluteTileBoundaries.height() / tileBoundaries.height()));
    253 
    254     // Apply tile image transformations.
    255     if (!tileImageTransform.isIdentity())
    256         tileImageContext->concatCTM(tileImageTransform);
    257 
    258     AffineTransform contentTransformation;
    259     if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
    260         contentTransformation = tileImageTransform;
    261 
    262     // Draw the content into the ImageBuffer.
    263     for (Node* node = attributes.patternContentElement()->firstChild(); node; node = node->nextSibling()) {
    264         if (!node->isSVGElement() || !node->renderer())
    265             continue;
    266         if (node->renderer()->needsLayout())
    267             return nullptr;
    268         SVGRenderingContext::renderSubtreeToImageBuffer(tileImage.get(), node->renderer(), contentTransformation);
    269     }
    270 
    271     return tileImage.release();
    272 }
    273 
    274 }
    275