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/dom/ElementTraversal.h"
     26 #include "core/rendering/svg/SVGRenderSupport.h"
     27 #include "core/rendering/svg/SVGRenderingContext.h"
     28 #include "core/svg/SVGFitToViewBox.h"
     29 #include "platform/graphics/GraphicsContext.h"
     30 
     31 namespace blink {
     32 
     33 const 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 ? PaintInvalidation : ParentOnlyInvalidation);
     46 }
     47 
     48 void RenderSVGResourcePattern::removeClientFromCache(RenderObject* client, bool markForInvalidation)
     49 {
     50     ASSERT(client);
     51     m_patternMap.remove(client);
     52     markClientForInvalidation(client, markForInvalidation ? PaintInvalidation : ParentOnlyInvalidation);
     53 }
     54 
     55 PatternData* RenderSVGResourcePattern::buildPattern(RenderObject* object, const SVGPatternElement* patternElement)
     56 {
     57     ASSERT(object);
     58     PatternData* currentData = m_patternMap.get(object);
     59     if (currentData && currentData->pattern)
     60         return currentData;
     61 
     62     // If we couldn't determine the pattern content element root, stop here.
     63     if (!m_attributes.patternContentElement())
     64         return 0;
     65 
     66     // An empty viewBox disables rendering.
     67     if (m_attributes.hasViewBox() && m_attributes.viewBox().isEmpty())
     68         return 0;
     69 
     70     // Compute all necessary transformations to build the tile image & the pattern.
     71     FloatRect tileBoundaries;
     72     AffineTransform tileImageTransform;
     73     if (!buildTileImageTransform(object, m_attributes, patternElement, tileBoundaries, tileImageTransform))
     74         return 0;
     75 
     76     AffineTransform absoluteTransformIgnoringRotation;
     77     SVGRenderingContext::calculateDeviceSpaceTransformation(object, absoluteTransformIgnoringRotation);
     78 
     79     // Ignore 2D rotation, as it doesn't affect the size of the tile.
     80     SVGRenderingContext::clear2DRotation(absoluteTransformIgnoringRotation);
     81     FloatRect absoluteTileBoundaries = absoluteTransformIgnoringRotation.mapRect(tileBoundaries);
     82 
     83     // Scale the tile size to match the scale level of the patternTransform.
     84     absoluteTileBoundaries.scale(static_cast<float>(m_attributes.patternTransform().xScale()),
     85         static_cast<float>(m_attributes.patternTransform().yScale()));
     86 
     87     // Build tile image.
     88     OwnPtr<ImageBuffer> tileImage = createTileImage(m_attributes, tileBoundaries, absoluteTileBoundaries, tileImageTransform);
     89     if (!tileImage)
     90         return 0;
     91 
     92     RefPtr<Image> copiedImage = tileImage->copyImage(CopyBackingStore);
     93     if (!copiedImage)
     94         return 0;
     95 
     96     // Build pattern.
     97     OwnPtr<PatternData> patternData = adoptPtr(new PatternData);
     98     patternData->pattern = Pattern::createBitmapPattern(copiedImage);
     99 
    100     // Compute pattern space transformation.
    101     const IntSize tileImageSize = tileImage->size();
    102     patternData->transform.translate(tileBoundaries.x(), tileBoundaries.y());
    103     patternData->transform.scale(tileBoundaries.width() / tileImageSize.width(), tileBoundaries.height() / tileImageSize.height());
    104 
    105     AffineTransform patternTransform = m_attributes.patternTransform();
    106     if (!patternTransform.isIdentity())
    107         patternData->transform = patternTransform * patternData->transform;
    108 
    109     // Various calls above may trigger invalidations in some fringe cases (ImageBuffer allocation
    110     // failures in the SVG image cache for example). To avoid having our PatternData deleted by
    111     // removeAllClientsFromCache(), we only make it visible in the cache at the very end.
    112     return m_patternMap.set(object, patternData.release()).storedValue->value.get();
    113 }
    114 
    115 bool RenderSVGResourcePattern::applyResource(RenderObject* object, RenderStyle* style, GraphicsContext*& context, unsigned short resourceMode)
    116 {
    117     ASSERT(object);
    118     ASSERT(style);
    119     ASSERT(context);
    120     ASSERT(resourceMode != ApplyToDefaultMode);
    121 
    122     clearInvalidationMask();
    123 
    124     SVGPatternElement* patternElement = toSVGPatternElement(element());
    125     if (!patternElement)
    126         return false;
    127 
    128     if (m_shouldCollectPatternAttributes) {
    129         patternElement->synchronizeAnimatedSVGAttribute(anyQName());
    130 
    131         m_attributes = PatternAttributes();
    132         patternElement->collectPatternAttributes(m_attributes);
    133         m_shouldCollectPatternAttributes = false;
    134     }
    135 
    136     // Spec: When the geometry of the applicable element has no width or height and objectBoundingBox is specified,
    137     // then the given effect (e.g. a gradient or a filter) will be ignored.
    138     FloatRect objectBoundingBox = object->objectBoundingBox();
    139     if (m_attributes.patternUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX && objectBoundingBox.isEmpty())
    140         return false;
    141 
    142     PatternData* patternData = buildPattern(object, patternElement);
    143     if (!patternData)
    144         return false;
    145 
    146     const SVGRenderStyle& svgStyle = style->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)
    175 {
    176     ASSERT(context);
    177     context->restore();
    178 }
    179 
    180 static inline FloatRect calculatePatternBoundaries(const PatternAttributes& attributes,
    181                                                    const FloatRect& objectBoundingBox,
    182                                                    const SVGPatternElement* patternElement)
    183 {
    184     ASSERT(patternElement);
    185     return SVGLengthContext::resolveRectangle(patternElement, attributes.patternUnits(), objectBoundingBox, attributes.x(), attributes.y(), attributes.width(), attributes.height());
    186 }
    187 
    188 bool RenderSVGResourcePattern::buildTileImageTransform(RenderObject* renderer,
    189                                                        const PatternAttributes& attributes,
    190                                                        const SVGPatternElement* patternElement,
    191                                                        FloatRect& patternBoundaries,
    192                                                        AffineTransform& tileImageTransform) const
    193 {
    194     ASSERT(renderer);
    195     ASSERT(patternElement);
    196 
    197     FloatRect objectBoundingBox = renderer->objectBoundingBox();
    198     patternBoundaries = calculatePatternBoundaries(attributes, objectBoundingBox, patternElement);
    199     if (patternBoundaries.width() <= 0 || patternBoundaries.height() <= 0)
    200         return false;
    201 
    202     AffineTransform viewBoxCTM = SVGFitToViewBox::viewBoxToViewTransform(attributes.viewBox(), attributes.preserveAspectRatio(), patternBoundaries.width(), patternBoundaries.height());
    203 
    204     // Apply viewBox/objectBoundingBox transformations.
    205     if (!viewBoxCTM.isIdentity())
    206         tileImageTransform = viewBoxCTM;
    207     else if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
    208         tileImageTransform.scale(objectBoundingBox.width(), objectBoundingBox.height());
    209 
    210     return true;
    211 }
    212 
    213 PassOwnPtr<ImageBuffer> RenderSVGResourcePattern::createTileImage(const PatternAttributes& attributes,
    214                                                                   const FloatRect& tileBoundaries,
    215                                                                   const FloatRect& absoluteTileBoundaries,
    216                                                                   const AffineTransform& tileImageTransform) const
    217 {
    218     FloatRect clampedAbsoluteTileBoundaries = SVGRenderingContext::clampedAbsoluteTargetRect(absoluteTileBoundaries);
    219 
    220     IntSize imageSize(roundedIntSize(clampedAbsoluteTileBoundaries.size()));
    221     if (imageSize.isEmpty())
    222         return nullptr;
    223     OwnPtr<ImageBuffer> tileImage = ImageBuffer::create(imageSize);
    224     if (!tileImage)
    225         return nullptr;
    226 
    227     GraphicsContext* tileImageContext = tileImage->context();
    228     ASSERT(tileImageContext);
    229     IntSize unclampedImageSize(roundedIntSize(absoluteTileBoundaries.size()));
    230     tileImageContext->scale(unclampedImageSize.width() / absoluteTileBoundaries.width(), unclampedImageSize.height() / absoluteTileBoundaries.height());
    231 
    232     // The image buffer represents the final rendered size, so the content has to be scaled (to avoid pixelation).
    233     tileImageContext->scale(
    234         clampedAbsoluteTileBoundaries.width() / tileBoundaries.width(),
    235         clampedAbsoluteTileBoundaries.height() / tileBoundaries.height());
    236 
    237     // Apply tile image transformations.
    238     if (!tileImageTransform.isIdentity())
    239         tileImageContext->concatCTM(tileImageTransform);
    240 
    241     AffineTransform contentTransformation;
    242     if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
    243         contentTransformation = tileImageTransform;
    244 
    245     // Draw the content into the ImageBuffer.
    246     for (SVGElement* element = Traversal<SVGElement>::firstChild(*attributes.patternContentElement()); element; element = Traversal<SVGElement>::nextSibling(*element)) {
    247         if (!element->renderer())
    248             continue;
    249         if (element->renderer()->needsLayout())
    250             return nullptr;
    251         SVGRenderingContext::renderSubtree(tileImage->context(), element->renderer(), contentTransformation);
    252     }
    253 
    254     return tileImage.release();
    255 }
    256 
    257 }
    258