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