WebCore/rendering/RenderSVGResourcePattern.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     1 /*
       
     2  * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@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 
       
    22 #include "config.h"
       
    23 
       
    24 #if ENABLE(SVG)
       
    25 #include "RenderSVGResourcePattern.h"
       
    26 
       
    27 #include "FrameView.h"
       
    28 #include "GraphicsContext.h"
       
    29 #include "PatternAttributes.h"
       
    30 #include "SVGRenderSupport.h"
       
    31 
       
    32 namespace WebCore {
       
    33 
       
    34 RenderSVGResourceType RenderSVGResourcePattern::s_resourceType = PatternResourceType;
       
    35 
       
    36 RenderSVGResourcePattern::RenderSVGResourcePattern(SVGPatternElement* node)
       
    37     : RenderSVGResourceContainer(node)
       
    38 {
       
    39 }
       
    40 
       
    41 RenderSVGResourcePattern::~RenderSVGResourcePattern()
       
    42 {
       
    43     deleteAllValues(m_pattern);
       
    44     m_pattern.clear();
       
    45 }
       
    46 
       
    47 void RenderSVGResourcePattern::invalidateClients()
       
    48 {
       
    49     const HashMap<RenderObject*, PatternData*>::const_iterator end = m_pattern.end();
       
    50     for (HashMap<RenderObject*, PatternData*>::const_iterator it = m_pattern.begin(); it != end; ++it)
       
    51         markForLayoutAndResourceInvalidation(it->first, false);
       
    52 
       
    53     deleteAllValues(m_pattern);
       
    54     m_pattern.clear();
       
    55 }
       
    56 
       
    57 void RenderSVGResourcePattern::invalidateClient(RenderObject* object)
       
    58 {
       
    59     ASSERT(object);
       
    60     if (!m_pattern.contains(object))
       
    61         return;
       
    62 
       
    63     delete m_pattern.take(object);
       
    64     markForLayoutAndResourceInvalidation(object, false);
       
    65 }
       
    66 
       
    67 bool RenderSVGResourcePattern::childElementReferencesResource(const SVGRenderStyle* style, const String& referenceId) const
       
    68 {
       
    69     if (style->hasFill()) {
       
    70         if (style->fillPaint()->matchesTargetURI(referenceId))
       
    71             return true;
       
    72     }
       
    73 
       
    74     if (style->hasStroke()) {
       
    75         if (style->strokePaint()->matchesTargetURI(referenceId))
       
    76             return true;
       
    77     }
       
    78 
       
    79     return false;
       
    80 }
       
    81 
       
    82 bool RenderSVGResourcePattern::applyResource(RenderObject* object, RenderStyle* style, GraphicsContext*& context, unsigned short resourceMode)
       
    83 {
       
    84     ASSERT(object);
       
    85     ASSERT(style);
       
    86     ASSERT(context);
       
    87     ASSERT(resourceMode != ApplyToDefaultMode);
       
    88 
       
    89     // Be sure to synchronize all SVG properties on the patternElement _before_ processing any further.
       
    90     // Otherwhise the call to collectPatternAttributes() in createTileImage(), may cause the SVG DOM property
       
    91     // synchronization to kick in, which causes invalidateClients() to be called, which in turn deletes our
       
    92     // PatternData object! Leaving out the line below will cause svg/dynamic-updates/SVGPatternElement-svgdom* to crash.
       
    93     SVGPatternElement* patternElement = static_cast<SVGPatternElement*>(node());
       
    94     if (!patternElement)
       
    95         return false;
       
    96 
       
    97     patternElement->updateAnimatedSVGAttribute(anyQName());
       
    98 
       
    99     if (!m_pattern.contains(object))
       
   100         m_pattern.set(object, new PatternData);
       
   101 
       
   102     PatternData* patternData = m_pattern.get(object);
       
   103     if (!patternData->pattern) {
       
   104         // Create tile image
       
   105         OwnPtr<ImageBuffer> tileImage = createTileImage(patternData, patternElement, object);
       
   106         if (!tileImage)
       
   107             return false;
       
   108 
       
   109         // Create pattern object
       
   110         buildPattern(patternData, tileImage.release());
       
   111 
       
   112         if (!patternData->pattern)
       
   113             return false;
       
   114 
       
   115         patternData->pattern->setPatternSpaceTransform(patternData->transform);
       
   116     }
       
   117 
       
   118     // Draw pattern
       
   119     context->save();
       
   120 
       
   121     const SVGRenderStyle* svgStyle = style->svgStyle();
       
   122     ASSERT(svgStyle);
       
   123 
       
   124     if (resourceMode & ApplyToFillMode) {
       
   125         context->setAlpha(svgStyle->fillOpacity());
       
   126         context->setFillPattern(patternData->pattern);
       
   127         context->setFillRule(svgStyle->fillRule());
       
   128     } else if (resourceMode & ApplyToStrokeMode) {
       
   129         if (svgStyle->vectorEffect() == VE_NON_SCALING_STROKE)
       
   130             patternData->pattern->setPatternSpaceTransform(transformOnNonScalingStroke(object, patternData->transform));
       
   131         context->setAlpha(svgStyle->strokeOpacity());
       
   132         context->setStrokePattern(patternData->pattern);
       
   133         SVGRenderSupport::applyStrokeStyleToContext(context, style, object);
       
   134     }
       
   135 
       
   136     if (resourceMode & ApplyToTextMode) {
       
   137         if (resourceMode & ApplyToFillMode) {
       
   138             context->setTextDrawingMode(cTextFill);
       
   139 
       
   140 #if PLATFORM(CG)
       
   141             context->applyFillPattern();
       
   142 #endif
       
   143         } else if (resourceMode & ApplyToStrokeMode) {
       
   144             context->setTextDrawingMode(cTextStroke);
       
   145 
       
   146 #if PLATFORM(CG)
       
   147             context->applyStrokePattern();
       
   148 #endif
       
   149         }
       
   150     }
       
   151 
       
   152     return true;
       
   153 }
       
   154 
       
   155 void RenderSVGResourcePattern::postApplyResource(RenderObject*, GraphicsContext*& context, unsigned short resourceMode)
       
   156 {
       
   157     ASSERT(context);
       
   158     ASSERT(resourceMode != ApplyToDefaultMode);
       
   159 
       
   160     if (!(resourceMode & ApplyToTextMode)) {
       
   161         if (resourceMode & ApplyToFillMode)
       
   162             context->fillPath();
       
   163         else if (resourceMode & ApplyToStrokeMode)
       
   164             context->strokePath();
       
   165     }
       
   166 
       
   167     context->restore();
       
   168 }
       
   169 
       
   170 static inline FloatRect calculatePatternBoundaries(PatternAttributes& attributes,
       
   171                                                    const FloatRect& objectBoundingBox,
       
   172                                                    const SVGPatternElement* patternElement)
       
   173 {
       
   174     if (attributes.boundingBoxMode())
       
   175         return FloatRect(attributes.x().valueAsPercentage() * objectBoundingBox.width(),
       
   176                          attributes.y().valueAsPercentage() * objectBoundingBox.height(),
       
   177                          attributes.width().valueAsPercentage() * objectBoundingBox.width(),
       
   178                          attributes.height().valueAsPercentage() * objectBoundingBox.height());
       
   179 
       
   180     return FloatRect(attributes.x().value(patternElement),
       
   181                      attributes.y().value(patternElement),
       
   182                      attributes.width().value(patternElement),
       
   183                      attributes.height().value(patternElement));
       
   184 }
       
   185 
       
   186 FloatRect RenderSVGResourcePattern::calculatePatternBoundariesIncludingOverflow(PatternAttributes& attributes,
       
   187                                                                                 const FloatRect& objectBoundingBox,
       
   188                                                                                 const AffineTransform& viewBoxCTM,
       
   189                                                                                 const FloatRect& patternBoundaries) const
       
   190 {
       
   191     // Eventually calculate the pattern content boundaries (only needed with overflow="visible").
       
   192     FloatRect patternContentBoundaries;
       
   193 
       
   194     const RenderStyle* style = this->style();
       
   195     if (style->overflowX() == OVISIBLE && style->overflowY() == OVISIBLE) {
       
   196         for (Node* node = attributes.patternContentElement()->firstChild(); node; node = node->nextSibling()) {
       
   197             if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyledTransformable() || !node->renderer())
       
   198                 continue;
       
   199             patternContentBoundaries.unite(node->renderer()->repaintRectInLocalCoordinates());
       
   200         }
       
   201     }
       
   202 
       
   203     if (patternContentBoundaries.isEmpty())
       
   204         return patternBoundaries;
       
   205 
       
   206     FloatRect patternBoundariesIncludingOverflow = patternBoundaries;
       
   207 
       
   208     // Respect objectBoundingBoxMode for patternContentUnits, if viewBox is not set.
       
   209     if (!viewBoxCTM.isIdentity())
       
   210         patternContentBoundaries = viewBoxCTM.mapRect(patternContentBoundaries);
       
   211     else if (attributes.boundingBoxModeContent())
       
   212         patternContentBoundaries = FloatRect(patternContentBoundaries.x() * objectBoundingBox.width(),
       
   213                                              patternContentBoundaries.y() * objectBoundingBox.height(),
       
   214                                              patternContentBoundaries.width() * objectBoundingBox.width(),
       
   215                                              patternContentBoundaries.height() * objectBoundingBox.height());
       
   216 
       
   217     patternBoundariesIncludingOverflow.unite(patternContentBoundaries);
       
   218     return patternBoundariesIncludingOverflow;
       
   219 }
       
   220 
       
   221 // FIXME: This method should be removed. RenderSVGResourcePatterns usage of it is just wrong.
       
   222 static inline void clampImageBufferSizeToViewport(FrameView* frameView, IntSize& size)
       
   223 {
       
   224     if (!frameView)
       
   225         return;
       
   226 
       
   227     int viewWidth = frameView->visibleWidth();
       
   228     int viewHeight = frameView->visibleHeight();
       
   229 
       
   230     if (size.width() > viewWidth)
       
   231         size.setWidth(viewWidth);
       
   232 
       
   233     if (size.height() > viewHeight)
       
   234         size.setHeight(viewHeight);
       
   235 }
       
   236 
       
   237 PassOwnPtr<ImageBuffer> RenderSVGResourcePattern::createTileImage(PatternData* patternData,
       
   238                                                                   const SVGPatternElement* patternElement,
       
   239                                                                   RenderObject* object) const
       
   240 {
       
   241     PatternAttributes attributes = patternElement->collectPatternProperties();
       
   242 
       
   243     // If we couldn't determine the pattern content element root, stop here.
       
   244     if (!attributes.patternContentElement())
       
   245         return 0;
       
   246 
       
   247     // Early exit, if this resource contains a child which references ourselves.
       
   248     if (containsCyclicReference(attributes.patternContentElement()))
       
   249         return 0;
       
   250 
       
   251     FloatRect objectBoundingBox = object->objectBoundingBox();    
       
   252     FloatRect patternBoundaries = calculatePatternBoundaries(attributes, objectBoundingBox, patternElement); 
       
   253     AffineTransform patternTransform = attributes.patternTransform();
       
   254 
       
   255     AffineTransform viewBoxCTM = patternElement->viewBoxToViewTransform(patternElement->viewBox(),
       
   256                                                                         patternElement->preserveAspectRatio(),
       
   257                                                                         patternBoundaries.width(),
       
   258                                                                         patternBoundaries.height());
       
   259 
       
   260     FloatRect patternBoundariesIncludingOverflow = calculatePatternBoundariesIncludingOverflow(attributes,
       
   261                                                                                                objectBoundingBox,
       
   262                                                                                                viewBoxCTM,
       
   263                                                                                                patternBoundaries);
       
   264 
       
   265     IntSize imageSize(lroundf(patternBoundariesIncludingOverflow.width()), lroundf(patternBoundariesIncludingOverflow.height()));
       
   266 
       
   267     // FIXME: We should be able to clip this more, needs investigation
       
   268     clampImageBufferSizeToViewport(object->document()->view(), imageSize);
       
   269 
       
   270     // Don't create ImageBuffers with image size of 0
       
   271     if (imageSize.isEmpty())
       
   272         return 0;
       
   273 
       
   274     OwnPtr<ImageBuffer> tileImage = ImageBuffer::create(imageSize);
       
   275 
       
   276     GraphicsContext* context = tileImage->context();
       
   277     ASSERT(context);
       
   278 
       
   279     context->save();
       
   280 
       
   281     // Translate to pattern start origin
       
   282     if (patternBoundariesIncludingOverflow.location() != patternBoundaries.location()) {
       
   283         context->translate(patternBoundaries.x() - patternBoundariesIncludingOverflow.x(),
       
   284                            patternBoundaries.y() - patternBoundariesIncludingOverflow.y());
       
   285 
       
   286         patternBoundaries.setLocation(patternBoundariesIncludingOverflow.location());
       
   287     }
       
   288 
       
   289     // Process viewBox or boundingBoxModeContent correction
       
   290     if (!viewBoxCTM.isIdentity())
       
   291         context->concatCTM(viewBoxCTM);
       
   292     else if (attributes.boundingBoxModeContent()) {
       
   293         context->translate(objectBoundingBox.x(), objectBoundingBox.y());
       
   294         context->scale(FloatSize(objectBoundingBox.width(), objectBoundingBox.height()));
       
   295     }
       
   296 
       
   297     // Render subtree into ImageBuffer
       
   298     for (Node* node = attributes.patternContentElement()->firstChild(); node; node = node->nextSibling()) {
       
   299         if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyled() || !node->renderer())
       
   300             continue;
       
   301         SVGRenderSupport::renderSubtreeToImage(tileImage.get(), node->renderer());
       
   302     }
       
   303 
       
   304     patternData->boundaries = patternBoundaries;
       
   305 
       
   306     // Compute pattern transformation
       
   307     patternData->transform.translate(patternBoundaries.x(), patternBoundaries.y());
       
   308     patternData->transform.multiply(patternTransform);
       
   309 
       
   310     context->restore();
       
   311     return tileImage.release();
       
   312 }
       
   313 
       
   314 void RenderSVGResourcePattern::buildPattern(PatternData* patternData, PassOwnPtr<ImageBuffer> tileImage) const
       
   315 {
       
   316     if (!tileImage->image()) {
       
   317         patternData->pattern = 0;
       
   318         return;
       
   319     }
       
   320 
       
   321     IntRect tileRect = tileImage->image()->rect();
       
   322     if (tileRect.width() <= patternData->boundaries.width() && tileRect.height() <= patternData->boundaries.height()) {
       
   323         patternData->pattern = Pattern::create(tileImage->image(), true, true);
       
   324         return;
       
   325     }
       
   326 
       
   327     // Draw the first cell of the pattern manually to support overflow="visible" on all platforms.
       
   328     int tileWidth = static_cast<int>(patternData->boundaries.width() + 0.5f);
       
   329     int tileHeight = static_cast<int>(patternData->boundaries.height() + 0.5f);
       
   330 
       
   331     // Don't create ImageBuffers with image size of 0
       
   332     if (!tileWidth || !tileHeight) {
       
   333         patternData->pattern = 0;
       
   334         return;
       
   335     }
       
   336 
       
   337     OwnPtr<ImageBuffer> newTileImage = ImageBuffer::create(IntSize(tileWidth, tileHeight));
       
   338     GraphicsContext* newTileImageContext = newTileImage->context();
       
   339 
       
   340     int numY = static_cast<int>(ceilf(tileRect.height() / tileHeight)) + 1;
       
   341     int numX = static_cast<int>(ceilf(tileRect.width() / tileWidth)) + 1;
       
   342 
       
   343     newTileImageContext->save();
       
   344     newTileImageContext->translate(-patternData->boundaries.width() * numX, -patternData->boundaries.height() * numY);
       
   345     for (int i = numY; i > 0; --i) {
       
   346         newTileImageContext->translate(0, patternData->boundaries.height());
       
   347         for (int j = numX; j > 0; --j) {
       
   348             newTileImageContext->translate(patternData->boundaries.width(), 0);
       
   349             newTileImageContext->drawImage(tileImage->image(), style()->colorSpace(), tileRect, tileRect);
       
   350         }
       
   351         newTileImageContext->translate(-patternData->boundaries.width() * numX, 0);
       
   352     }
       
   353     newTileImageContext->restore();
       
   354 
       
   355     patternData->pattern = Pattern::create(newTileImage->image(), true, true);
       
   356 }
       
   357 
       
   358 }
       
   359 
       
   360 #endif