Home | History | Annotate | Download | only in svg
      1 /*
      2  * Copyright (C) 2006 Nikolas Zimmermann <zimmermann (at) kde.org>
      3  * Copyright (C) 2008 Eric Seidel <eric (at) webkit.org>
      4  * Copyright (C) 2008 Dirk Schulze <krit (at) webkit.org>
      5  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
      6  *
      7  * This library is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU Library General Public
      9  * License as published by the Free Software Foundation; either
     10  * version 2 of the License, or (at your option) any later version.
     11  *
     12  * This library is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  * Library General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU Library General Public License
     18  * along with this library; see the file COPYING.LIB.  If not, write to
     19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     20  * Boston, MA 02110-1301, USA.
     21  */
     22 
     23 #include "config.h"
     24 
     25 #if ENABLE(SVG)
     26 #include "RenderSVGResourceGradient.h"
     27 
     28 #include "GradientAttributes.h"
     29 #include "GraphicsContext.h"
     30 #include "RenderSVGText.h"
     31 #include "SVGImageBufferTools.h"
     32 #include "SVGRenderSupport.h"
     33 #include <wtf/UnusedParam.h>
     34 
     35 namespace WebCore {
     36 
     37 RenderSVGResourceGradient::RenderSVGResourceGradient(SVGGradientElement* node)
     38     : RenderSVGResourceContainer(node)
     39     , m_shouldCollectGradientAttributes(true)
     40 #if USE(CG)
     41     , m_savedContext(0)
     42 #endif
     43 {
     44 }
     45 
     46 RenderSVGResourceGradient::~RenderSVGResourceGradient()
     47 {
     48     if (m_gradient.isEmpty())
     49         return;
     50 
     51     deleteAllValues(m_gradient);
     52     m_gradient.clear();
     53 }
     54 
     55 void RenderSVGResourceGradient::removeAllClientsFromCache(bool markForInvalidation)
     56 {
     57     if (!m_gradient.isEmpty()) {
     58         deleteAllValues(m_gradient);
     59         m_gradient.clear();
     60     }
     61 
     62     m_shouldCollectGradientAttributes = true;
     63     markAllClientsForInvalidation(markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
     64 }
     65 
     66 void RenderSVGResourceGradient::removeClientFromCache(RenderObject* client, bool markForInvalidation)
     67 {
     68     ASSERT(client);
     69 
     70     if (m_gradient.contains(client))
     71         delete m_gradient.take(client);
     72 
     73     markClientForInvalidation(client, markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
     74 }
     75 
     76 #if USE(CG)
     77 static inline bool createMaskAndSwapContextForTextGradient(GraphicsContext*& context,
     78                                                            GraphicsContext*& savedContext,
     79                                                            OwnPtr<ImageBuffer>& imageBuffer,
     80                                                            RenderObject* object)
     81 {
     82     RenderObject* textRootBlock = RenderSVGText::locateRenderSVGTextAncestor(object);
     83     ASSERT(textRootBlock);
     84 
     85     AffineTransform absoluteTransform;
     86     SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(textRootBlock, absoluteTransform);
     87 
     88     FloatRect absoluteTargetRect = absoluteTransform.mapRect(textRootBlock->repaintRectInLocalCoordinates());
     89     FloatRect clampedAbsoluteTargetRect = SVGImageBufferTools::clampedAbsoluteTargetRectForRenderer(textRootBlock, absoluteTargetRect);
     90     if (clampedAbsoluteTargetRect.isEmpty())
     91         return false;
     92 
     93     OwnPtr<ImageBuffer> maskImage;
     94     if (!SVGImageBufferTools::createImageBuffer(absoluteTargetRect, clampedAbsoluteTargetRect, maskImage, ColorSpaceDeviceRGB))
     95         return false;
     96 
     97     GraphicsContext* maskImageContext = maskImage->context();
     98     ASSERT(maskImageContext);
     99 
    100     maskImageContext->translate(-clampedAbsoluteTargetRect.x(), -clampedAbsoluteTargetRect.y());
    101     maskImageContext->concatCTM(absoluteTransform);
    102 
    103     ASSERT(maskImage);
    104     savedContext = context;
    105     context = maskImageContext;
    106     imageBuffer = maskImage.release();
    107     return true;
    108 }
    109 
    110 static inline AffineTransform clipToTextMask(GraphicsContext* context,
    111                                              OwnPtr<ImageBuffer>& imageBuffer,
    112                                              FloatRect& targetRect,
    113                                              RenderObject* object,
    114                                              bool boundingBoxMode,
    115                                              const AffineTransform& gradientTransform)
    116 {
    117     RenderObject* textRootBlock = RenderSVGText::locateRenderSVGTextAncestor(object);
    118     ASSERT(textRootBlock);
    119 
    120     targetRect = textRootBlock->repaintRectInLocalCoordinates();
    121 
    122     AffineTransform absoluteTransform;
    123     SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(textRootBlock, absoluteTransform);
    124 
    125     FloatRect absoluteTargetRect = absoluteTransform.mapRect(targetRect);
    126     FloatRect clampedAbsoluteTargetRect = SVGImageBufferTools::clampedAbsoluteTargetRectForRenderer(textRootBlock, absoluteTargetRect);
    127 
    128     SVGImageBufferTools::clipToImageBuffer(context, absoluteTransform, clampedAbsoluteTargetRect, imageBuffer);
    129 
    130     AffineTransform matrix;
    131     if (boundingBoxMode) {
    132         FloatRect maskBoundingBox = textRootBlock->objectBoundingBox();
    133         matrix.translate(maskBoundingBox.x(), maskBoundingBox.y());
    134         matrix.scaleNonUniform(maskBoundingBox.width(), maskBoundingBox.height());
    135     }
    136     matrix *= gradientTransform;
    137     return matrix;
    138 }
    139 #endif
    140 
    141 bool RenderSVGResourceGradient::applyResource(RenderObject* object, RenderStyle* style, GraphicsContext*& context, unsigned short resourceMode)
    142 {
    143     ASSERT(object);
    144     ASSERT(style);
    145     ASSERT(context);
    146     ASSERT(resourceMode != ApplyToDefaultMode);
    147 
    148     // Be sure to synchronize all SVG properties on the gradientElement _before_ processing any further.
    149     // Otherwhise the call to collectGradientAttributes() in createTileImage(), may cause the SVG DOM property
    150     // synchronization to kick in, which causes removeAllClientsFromCache() to be called, which in turn deletes our
    151     // GradientData object! Leaving out the line below will cause svg/dynamic-updates/SVG*GradientElement-svgdom* to crash.
    152     SVGGradientElement* gradientElement = static_cast<SVGGradientElement*>(node());
    153     if (!gradientElement)
    154         return false;
    155 
    156     if (m_shouldCollectGradientAttributes) {
    157         gradientElement->updateAnimatedSVGAttribute(anyQName());
    158         collectGradientAttributes(gradientElement);
    159         m_shouldCollectGradientAttributes = false;
    160     }
    161 
    162     // Spec: When the geometry of the applicable element has no width or height and objectBoundingBox is specified,
    163     // then the given effect (e.g. a gradient or a filter) will be ignored.
    164     FloatRect objectBoundingBox = object->objectBoundingBox();
    165     if (boundingBoxMode() && objectBoundingBox.isEmpty())
    166         return false;
    167 
    168     if (!m_gradient.contains(object))
    169         m_gradient.set(object, new GradientData);
    170 
    171     GradientData* gradientData = m_gradient.get(object);
    172     bool isPaintingText = resourceMode & ApplyToTextMode;
    173 
    174     // Create gradient object
    175     if (!gradientData->gradient) {
    176         buildGradient(gradientData, gradientElement);
    177 
    178         // CG platforms will handle the gradient space transform for text after applying the
    179         // resource, so don't apply it here. For non-CG platforms, we want the text bounding
    180         // box applied to the gradient space transform now, so the gradient shader can use it.
    181 #if USE(CG)
    182         if (boundingBoxMode() && !objectBoundingBox.isEmpty() && !isPaintingText) {
    183 #else
    184         if (boundingBoxMode() && !objectBoundingBox.isEmpty()) {
    185 #endif
    186             gradientData->userspaceTransform.translate(objectBoundingBox.x(), objectBoundingBox.y());
    187             gradientData->userspaceTransform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
    188         }
    189 
    190         AffineTransform gradientTransform;
    191         calculateGradientTransform(gradientTransform);
    192 
    193         gradientData->userspaceTransform *= gradientTransform;
    194         gradientData->gradient->setGradientSpaceTransform(gradientData->userspaceTransform);
    195     }
    196 
    197     if (!gradientData->gradient)
    198         return false;
    199 
    200     // Draw gradient
    201     context->save();
    202 
    203     if (isPaintingText) {
    204 #if USE(CG)
    205         if (!createMaskAndSwapContextForTextGradient(context, m_savedContext, m_imageBuffer, object)) {
    206             context->restore();
    207             return false;
    208         }
    209 #endif
    210 
    211         context->setTextDrawingMode(resourceMode & ApplyToFillMode ? TextModeFill : TextModeStroke);
    212     }
    213 
    214     const SVGRenderStyle* svgStyle = style->svgStyle();
    215     ASSERT(svgStyle);
    216 
    217     if (resourceMode & ApplyToFillMode) {
    218         context->setAlpha(svgStyle->fillOpacity());
    219         context->setFillGradient(gradientData->gradient);
    220         context->setFillRule(svgStyle->fillRule());
    221     } else if (resourceMode & ApplyToStrokeMode) {
    222         if (svgStyle->vectorEffect() == VE_NON_SCALING_STROKE)
    223             gradientData->gradient->setGradientSpaceTransform(transformOnNonScalingStroke(object, gradientData->userspaceTransform));
    224         context->setAlpha(svgStyle->strokeOpacity());
    225         context->setStrokeGradient(gradientData->gradient);
    226         SVGRenderSupport::applyStrokeStyleToContext(context, style, object);
    227     }
    228 
    229     return true;
    230 }
    231 
    232 void RenderSVGResourceGradient::postApplyResource(RenderObject* object, GraphicsContext*& context, unsigned short resourceMode, const Path* path)
    233 {
    234     ASSERT(context);
    235     ASSERT(resourceMode != ApplyToDefaultMode);
    236 
    237     if (resourceMode & ApplyToTextMode) {
    238 #if USE(CG)
    239         // CG requires special handling for gradient on text
    240         if (m_savedContext && m_gradient.contains(object)) {
    241             GradientData* gradientData = m_gradient.get(object);
    242 
    243             // Restore on-screen drawing context
    244             context = m_savedContext;
    245             m_savedContext = 0;
    246 
    247             AffineTransform gradientTransform;
    248             calculateGradientTransform(gradientTransform);
    249 
    250             FloatRect targetRect;
    251             gradientData->gradient->setGradientSpaceTransform(clipToTextMask(context, m_imageBuffer, targetRect, object, boundingBoxMode(), gradientTransform));
    252             context->setFillGradient(gradientData->gradient);
    253 
    254             context->fillRect(targetRect);
    255             m_imageBuffer.clear();
    256         }
    257 #else
    258         UNUSED_PARAM(object);
    259 #endif
    260     } else if (path) {
    261         if (resourceMode & ApplyToFillMode)
    262             context->fillPath(*path);
    263         else if (resourceMode & ApplyToStrokeMode)
    264             context->strokePath(*path);
    265     }
    266 
    267     context->restore();
    268 }
    269 
    270 void RenderSVGResourceGradient::addStops(GradientData* gradientData, const Vector<Gradient::ColorStop>& stops) const
    271 {
    272     ASSERT(gradientData->gradient);
    273 
    274     const Vector<Gradient::ColorStop>::const_iterator end = stops.end();
    275     for (Vector<Gradient::ColorStop>::const_iterator it = stops.begin(); it != end; ++it)
    276         gradientData->gradient->addColorStop(*it);
    277 }
    278 
    279 }
    280 
    281 #endif
    282