Home | History | Annotate | Download | only in graphics
      1 /*
      2  * Copyright (C) 2006 Nikolas Zimmermann <zimmermann (at) kde.org>
      3  *               2008 Eric Seidel <eric (at) webkit.org>
      4  *               2008 Dirk Schulze <krit (at) webkit.org>
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
     16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     18  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     23  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include "config.h"
     29 
     30 #if ENABLE(SVG)
     31 #include "SVGPaintServerGradient.h"
     32 
     33 #include "FloatConversion.h"
     34 #include "GraphicsContext.h"
     35 #include "ImageBuffer.h"
     36 #include "RenderObject.h"
     37 #include "RenderView.h"
     38 #include "SVGGradientElement.h"
     39 #include "SVGPaintServerLinearGradient.h"
     40 #include "SVGPaintServerRadialGradient.h"
     41 #include "SVGRenderSupport.h"
     42 #include "SVGRenderTreeAsText.h"
     43 
     44 using namespace std;
     45 
     46 namespace WebCore {
     47 
     48 static TextStream& operator<<(TextStream& ts, GradientSpreadMethod m)
     49 {
     50     switch (m) {
     51         case SpreadMethodPad:
     52             ts << "PAD"; break;
     53         case SpreadMethodRepeat:
     54             ts << "REPEAT"; break;
     55         case SpreadMethodReflect:
     56             ts << "REFLECT"; break;
     57     }
     58 
     59     return ts;
     60 }
     61 
     62 static TextStream& operator<<(TextStream& ts, const Vector<SVGGradientStop>& l)
     63 {
     64     ts << "[";
     65     for (Vector<SVGGradientStop>::const_iterator it = l.begin(); it != l.end(); ++it) {
     66         ts << "(" << it->first << "," << it->second << ")";
     67         if (it + 1 != l.end())
     68             ts << ", ";
     69     }
     70     ts << "]";
     71     return ts;
     72 }
     73 
     74 SVGPaintServerGradient::SVGPaintServerGradient(const SVGGradientElement* owner)
     75     : m_boundingBoxMode(true)
     76     , m_ownerElement(owner)
     77 #if PLATFORM(CG)
     78     , m_savedContext(0)
     79     , m_imageBuffer(0)
     80 #endif
     81 {
     82     ASSERT(owner);
     83 }
     84 
     85 SVGPaintServerGradient::~SVGPaintServerGradient()
     86 {
     87 }
     88 
     89 Gradient* SVGPaintServerGradient::gradient() const
     90 {
     91     return m_gradient.get();
     92 }
     93 
     94 void SVGPaintServerGradient::setGradient(PassRefPtr<Gradient> gradient)
     95 {
     96     m_gradient = gradient;
     97 }
     98 
     99 bool SVGPaintServerGradient::boundingBoxMode() const
    100 {
    101     return m_boundingBoxMode;
    102 }
    103 
    104 void SVGPaintServerGradient::setBoundingBoxMode(bool mode)
    105 {
    106     m_boundingBoxMode = mode;
    107 }
    108 
    109 AffineTransform SVGPaintServerGradient::gradientTransform() const
    110 {
    111     return m_gradientTransform;
    112 }
    113 
    114 void SVGPaintServerGradient::setGradientTransform(const AffineTransform& transform)
    115 {
    116     m_gradientTransform = transform;
    117 }
    118 
    119 #if PLATFORM(CG)
    120 static inline const RenderObject* findTextRootObject(const RenderObject* start)
    121 {
    122     while (start && !start->isSVGText())
    123         start = start->parent();
    124     ASSERT(start);
    125     ASSERT(start->isSVGText());
    126 
    127     return start;
    128 }
    129 
    130 static inline AffineTransform absoluteTransformForRenderer(const RenderObject* object)
    131 {
    132     AffineTransform absoluteTransform;
    133 
    134     const RenderObject* currentObject = object;
    135     while (currentObject) {
    136         absoluteTransform = currentObject->localToParentTransform() * absoluteTransform;
    137         currentObject = currentObject->parent();
    138     }
    139 
    140     return absoluteTransform;
    141 }
    142 
    143 static inline bool createMaskAndSwapContextForTextGradient(
    144     GraphicsContext*& context, GraphicsContext*& savedContext,
    145     OwnPtr<ImageBuffer>& imageBuffer, const RenderObject* object)
    146 {
    147     const RenderObject* textRootBlock = findTextRootObject(object);
    148 
    149     AffineTransform transform = absoluteTransformForRenderer(textRootBlock);
    150     FloatRect maskAbsoluteBoundingBox = transform.mapRect(textRootBlock->repaintRectInLocalCoordinates());
    151 
    152     IntRect maskImageRect = enclosingIntRect(maskAbsoluteBoundingBox);
    153     if (maskImageRect.isEmpty())
    154         return false;
    155 
    156     // Allocate an image buffer as big as the absolute unclipped size of the object
    157     OwnPtr<ImageBuffer> maskImage = ImageBuffer::create(maskImageRect.size());
    158     if (!maskImage)
    159         return false;
    160 
    161     GraphicsContext* maskImageContext = maskImage->context();
    162 
    163     // Transform the mask image coordinate system to absolute screen coordinates
    164     maskImageContext->translate(-maskAbsoluteBoundingBox.x(), -maskAbsoluteBoundingBox.y());
    165     maskImageContext->concatCTM(transform);
    166 
    167     imageBuffer.set(maskImage.release());
    168     savedContext = context;
    169     context = maskImageContext;
    170 
    171     return true;
    172 }
    173 
    174 static inline AffineTransform clipToTextMask(GraphicsContext* context,
    175     OwnPtr<ImageBuffer>& imageBuffer, const RenderObject* object,
    176     const SVGPaintServerGradient* gradientServer)
    177 {
    178     const RenderObject* textRootBlock = findTextRootObject(object);
    179     context->clipToImageBuffer(textRootBlock->repaintRectInLocalCoordinates(), imageBuffer.get());
    180 
    181     AffineTransform matrix;
    182     if (gradientServer->boundingBoxMode()) {
    183         FloatRect maskBoundingBox = textRootBlock->objectBoundingBox();
    184         matrix.translate(maskBoundingBox.x(), maskBoundingBox.y());
    185         matrix.scaleNonUniform(maskBoundingBox.width(), maskBoundingBox.height());
    186     }
    187     matrix.multiply(gradientServer->gradientTransform());
    188     return matrix;
    189 }
    190 #endif
    191 
    192 bool SVGPaintServerGradient::setup(GraphicsContext*& context, const RenderObject* object, const RenderStyle*style, SVGPaintTargetType type, bool isPaintingText) const
    193 {
    194     m_ownerElement->buildGradient();
    195 
    196     const SVGRenderStyle* svgStyle = style->svgStyle();
    197     bool isFilled = (type & ApplyToFillTargetType) && svgStyle->hasFill();
    198     bool isStroked = (type & ApplyToStrokeTargetType) && svgStyle->hasStroke();
    199 
    200     ASSERT((isFilled && !isStroked) || (!isFilled && isStroked));
    201 
    202     context->save();
    203 
    204     if (isPaintingText) {
    205 #if PLATFORM(CG)
    206         if (!createMaskAndSwapContextForTextGradient(context, m_savedContext, m_imageBuffer, object)) {
    207             context->restore();
    208             return false;
    209         }
    210 #endif
    211         context->setTextDrawingMode(isFilled ? cTextFill : cTextStroke);
    212     }
    213 
    214     if (isFilled) {
    215         context->setAlpha(svgStyle->fillOpacity());
    216         context->setFillGradient(m_gradient);
    217         context->setFillRule(svgStyle->fillRule());
    218     }
    219     if (isStroked) {
    220         context->setAlpha(svgStyle->strokeOpacity());
    221         context->setStrokeGradient(m_gradient);
    222         applyStrokeStyleToContext(context, style, object);
    223     }
    224 
    225     AffineTransform matrix;
    226     // CG platforms will handle the gradient space transform for text in
    227     // teardown, so we don't apply it here.  For non-CG platforms, we
    228     // want the text bounding box applied to the gradient space transform now,
    229     // so the gradient shader can use it.
    230 #if PLATFORM(CG)
    231     if (boundingBoxMode() && !isPaintingText) {
    232 #else
    233     if (boundingBoxMode()) {
    234 #endif
    235         FloatRect bbox = object->objectBoundingBox();
    236         // Don't use gradients for 1d objects like horizontal/vertical
    237         // lines or rectangles without width or height.
    238         if (bbox.width() == 0 || bbox.height() == 0) {
    239             Color color(0, 0, 0);
    240             context->setStrokeColor(color, style->colorSpace());
    241             return true;
    242         }
    243         matrix.translate(bbox.x(), bbox.y());
    244         matrix.scaleNonUniform(bbox.width(), bbox.height());
    245     }
    246     matrix.multiply(gradientTransform());
    247     m_gradient->setGradientSpaceTransform(matrix);
    248 
    249     return true;
    250 }
    251 
    252 void SVGPaintServerGradient::teardown(GraphicsContext*& context, const RenderObject* object, SVGPaintTargetType, bool isPaintingText) const
    253 {
    254 #if PLATFORM(CG)
    255     // renderPath() is not used when painting text, so we paint the gradient during teardown()
    256     if (isPaintingText && m_savedContext) {
    257         // Restore on-screen drawing context
    258         context = m_savedContext;
    259         m_savedContext = 0;
    260 
    261         AffineTransform matrix = clipToTextMask(context, m_imageBuffer, object, this);
    262         m_gradient->setGradientSpaceTransform(matrix);
    263         context->setFillGradient(m_gradient);
    264 
    265         const RenderObject* textRootBlock = findTextRootObject(object);
    266         context->fillRect(textRootBlock->repaintRectInLocalCoordinates());
    267 
    268         m_imageBuffer.clear();
    269     }
    270 #endif
    271     context->restore();
    272 }
    273 
    274 TextStream& SVGPaintServerGradient::externalRepresentation(TextStream& ts) const
    275 {
    276     // Gradients/patterns aren't setup, until they are used for painting. Work around that fact.
    277     m_ownerElement->buildGradient();
    278 
    279     // abstract, don't stream type
    280     ts  << "[stops=" << gradientStops() << "]";
    281     if (m_gradient->spreadMethod() != SpreadMethodPad)
    282         ts << "[method=" << m_gradient->spreadMethod() << "]";
    283     if (!boundingBoxMode())
    284         ts << " [bounding box mode=" << boundingBoxMode() << "]";
    285     if (!gradientTransform().isIdentity())
    286         ts << " [transform=" << gradientTransform() << "]";
    287 
    288     return ts;
    289 }
    290 
    291 } // namespace WebCore
    292 
    293 #endif
    294