Home | History | Annotate | Download | only in svg
      1 /*
      2  * Copyright (C) 2006 Alexander Kellett <lypanov (at) kde.org>
      3  * Copyright (C) 2006 Apple Computer, Inc.
      4  * Copyright (C) 2007 Nikolas Zimmermann <zimmermann (at) kde.org>
      5  * Copyright (C) 2007, 2008, 2009 Rob Buis <buis (at) kde.org>
      6  * Copyright (C) 2009 Google, Inc.
      7  * Copyright (C) 2009 Dirk Schulze <krit (at) webkit.org>
      8  * Copyright (C) 2010 Patrick Gansterer <paroga (at) paroga.com>
      9  *
     10  * This library is free software; you can redistribute it and/or
     11  * modify it under the terms of the GNU Library General Public
     12  * License as published by the Free Software Foundation; either
     13  * version 2 of the License, or (at your option) any later version.
     14  *
     15  * This library is distributed in the hope that it will be useful,
     16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     18  * Library General Public License for more details.
     19  *
     20  * You should have received a copy of the GNU Library General Public License
     21  * along with this library; see the file COPYING.LIB.  If not, write to
     22  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     23  * Boston, MA 02110-1301, USA.
     24  */
     25 
     26 #include "config.h"
     27 
     28 #include "core/rendering/svg/RenderSVGImage.h"
     29 
     30 #include "core/rendering/GraphicsContextAnnotator.h"
     31 #include "core/rendering/ImageQualityController.h"
     32 #include "core/rendering/LayoutRepainter.h"
     33 #include "core/rendering/PointerEventsHitRules.h"
     34 #include "core/rendering/RenderImageResource.h"
     35 #include "core/rendering/svg/RenderSVGResource.h"
     36 #include "core/rendering/svg/SVGRenderSupport.h"
     37 #include "core/rendering/svg/SVGRenderingContext.h"
     38 #include "core/rendering/svg/SVGResources.h"
     39 #include "core/rendering/svg/SVGResourcesCache.h"
     40 #include "core/svg/SVGImageElement.h"
     41 #include "platform/graphics/GraphicsContextStateSaver.h"
     42 
     43 namespace WebCore {
     44 
     45 RenderSVGImage::RenderSVGImage(SVGImageElement* impl)
     46     : RenderSVGModelObject(impl)
     47     , m_needsBoundariesUpdate(true)
     48     , m_needsTransformUpdate(true)
     49     , m_imageResource(RenderImageResource::create())
     50 {
     51     m_imageResource->initialize(this);
     52 }
     53 
     54 RenderSVGImage::~RenderSVGImage()
     55 {
     56     ImageQualityController::remove(this);
     57     m_imageResource->shutdown();
     58 }
     59 
     60 bool RenderSVGImage::updateImageViewport()
     61 {
     62     SVGImageElement* image = toSVGImageElement(element());
     63     FloatRect oldBoundaries = m_objectBoundingBox;
     64     bool updatedViewport = false;
     65 
     66     SVGLengthContext lengthContext(image);
     67     m_objectBoundingBox = FloatRect(image->x()->currentValue()->value(lengthContext), image->y()->currentValue()->value(lengthContext), image->width()->currentValue()->value(lengthContext), image->height()->currentValue()->value(lengthContext));
     68 
     69     // Images with preserveAspectRatio=none should force non-uniform scaling. This can be achieved
     70     // by setting the image's container size to its intrinsic size.
     71     // See: http://www.w3.org/TR/SVG/single-page.html, 7.8 The preserveAspectRatio attribute.
     72     if (image->preserveAspectRatio()->currentValue()->align() == SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE) {
     73         if (ImageResource* cachedImage = m_imageResource->cachedImage()) {
     74             LayoutSize intrinsicSize = cachedImage->imageSizeForRenderer(0, style()->effectiveZoom());
     75             if (intrinsicSize != m_imageResource->imageSize(style()->effectiveZoom())) {
     76                 m_imageResource->setContainerSizeForRenderer(roundedIntSize(intrinsicSize));
     77                 updatedViewport = true;
     78             }
     79         }
     80     }
     81 
     82     if (oldBoundaries != m_objectBoundingBox) {
     83         if (!updatedViewport)
     84             m_imageResource->setContainerSizeForRenderer(enclosingIntRect(m_objectBoundingBox).size());
     85         updatedViewport = true;
     86         m_needsBoundariesUpdate = true;
     87     }
     88 
     89     return updatedViewport;
     90 }
     91 
     92 void RenderSVGImage::layout()
     93 {
     94     ASSERT(needsLayout());
     95 
     96     LayoutRepainter repainter(*this, SVGRenderSupport::checkForSVGRepaintDuringLayout(this) && selfNeedsLayout());
     97     updateImageViewport();
     98 
     99     bool transformOrBoundariesUpdate = m_needsTransformUpdate || m_needsBoundariesUpdate;
    100     if (m_needsTransformUpdate) {
    101         m_localTransform = toSVGImageElement(element())->animatedLocalTransform();
    102         m_needsTransformUpdate = false;
    103     }
    104 
    105     if (m_needsBoundariesUpdate) {
    106         m_repaintBoundingBox = m_objectBoundingBox;
    107         SVGRenderSupport::intersectRepaintRectWithResources(this, m_repaintBoundingBox);
    108 
    109         m_needsBoundariesUpdate = false;
    110     }
    111 
    112     // Invalidate all resources of this client if our layout changed.
    113     if (everHadLayout() && selfNeedsLayout())
    114         SVGResourcesCache::clientLayoutChanged(this);
    115 
    116     // If our bounds changed, notify the parents.
    117     if (transformOrBoundariesUpdate)
    118         RenderSVGModelObject::setNeedsBoundariesUpdate();
    119 
    120     repainter.repaintAfterLayout();
    121     clearNeedsLayout();
    122 }
    123 
    124 void RenderSVGImage::paint(PaintInfo& paintInfo, const LayoutPoint&)
    125 {
    126     ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
    127 
    128     if (paintInfo.context->paintingDisabled()
    129         || paintInfo.phase != PaintPhaseForeground
    130         || style()->visibility() == HIDDEN
    131         || !m_imageResource->hasImage())
    132         return;
    133 
    134     FloatRect boundingBox = paintInvalidationRectInLocalCoordinates();
    135     if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(boundingBox, m_localTransform, paintInfo))
    136         return;
    137 
    138     PaintInfo childPaintInfo(paintInfo);
    139     GraphicsContextStateSaver stateSaver(*childPaintInfo.context, false);
    140 
    141     if (!m_localTransform.isIdentity()) {
    142         stateSaver.save();
    143         childPaintInfo.applyTransform(m_localTransform, false);
    144     }
    145     if (!m_objectBoundingBox.isEmpty()) {
    146         // SVGRenderingContext may taint the state - make sure we're always saving.
    147         SVGRenderingContext renderingContext(this, childPaintInfo, stateSaver.saved() ?
    148             SVGRenderingContext::DontSaveGraphicsContext : SVGRenderingContext::SaveGraphicsContext);
    149 
    150         if (renderingContext.isRenderingPrepared()) {
    151             if (style()->svgStyle()->bufferedRendering() == BR_STATIC && renderingContext.bufferForeground(m_bufferedForeground))
    152                 return;
    153 
    154             paintForeground(childPaintInfo);
    155         }
    156     }
    157 
    158     if (style()->outlineWidth())
    159         paintOutline(childPaintInfo, IntRect(boundingBox));
    160 }
    161 
    162 void RenderSVGImage::paintForeground(PaintInfo& paintInfo)
    163 {
    164     RefPtr<Image> image = m_imageResource->image();
    165     FloatRect destRect = m_objectBoundingBox;
    166     FloatRect srcRect(0, 0, image->width(), image->height());
    167 
    168     SVGImageElement* imageElement = toSVGImageElement(element());
    169     imageElement->preserveAspectRatio()->currentValue()->transformRect(destRect, srcRect);
    170 
    171     InterpolationQuality interpolationQuality = InterpolationDefault;
    172     if (style()->svgStyle()->bufferedRendering() != BR_STATIC)
    173         interpolationQuality = ImageQualityController::imageQualityController()->chooseInterpolationQuality(paintInfo.context, this, image.get(), image.get(), LayoutSize(destRect.size()));
    174 
    175     InterpolationQuality previousInterpolationQuality = paintInfo.context->imageInterpolationQuality();
    176     paintInfo.context->setImageInterpolationQuality(interpolationQuality);
    177     paintInfo.context->drawImage(image.get(), destRect, srcRect, CompositeSourceOver);
    178     paintInfo.context->setImageInterpolationQuality(previousInterpolationQuality);
    179 }
    180 
    181 void RenderSVGImage::invalidateBufferedForeground()
    182 {
    183     m_bufferedForeground.clear();
    184 }
    185 
    186 bool RenderSVGImage::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
    187 {
    188     // We only draw in the forground phase, so we only hit-test then.
    189     if (hitTestAction != HitTestForeground)
    190         return false;
    191 
    192     PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_IMAGE_HITTESTING, request, style()->pointerEvents());
    193     bool isVisible = (style()->visibility() == VISIBLE);
    194     if (isVisible || !hitRules.requireVisible) {
    195         FloatPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent);
    196 
    197         if (!SVGRenderSupport::pointInClippingArea(this, localPoint))
    198             return false;
    199 
    200         if (hitRules.canHitFill || hitRules.canHitBoundingBox) {
    201             if (m_objectBoundingBox.contains(localPoint)) {
    202                 updateHitTestResult(result, roundedLayoutPoint(localPoint));
    203                 return true;
    204             }
    205         }
    206     }
    207 
    208     return false;
    209 }
    210 
    211 void RenderSVGImage::imageChanged(WrappedImagePtr, const IntRect*)
    212 {
    213     // The image resource defaults to nullImage until the resource arrives.
    214     // This empty image may be cached by SVG resources which must be invalidated.
    215     if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this))
    216         resources->removeClientFromCache(this);
    217 
    218     // Eventually notify parent resources, that we've changed.
    219     RenderSVGResource::markForLayoutAndParentResourceInvalidation(this, false);
    220 
    221     // Update the SVGImageCache sizeAndScales entry in case image loading finished after layout.
    222     // (https://bugs.webkit.org/show_bug.cgi?id=99489)
    223     m_objectBoundingBox = FloatRect();
    224     updateImageViewport();
    225 
    226     invalidateBufferedForeground();
    227 
    228     paintInvalidationForWholeRenderer();
    229 }
    230 
    231 void RenderSVGImage::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint&, const RenderLayerModelObject*)
    232 {
    233     // this is called from paint() after the localTransform has already been applied
    234     IntRect contentRect = enclosingIntRect(paintInvalidationRectInLocalCoordinates());
    235     if (!contentRect.isEmpty())
    236         rects.append(contentRect);
    237 }
    238 
    239 } // namespace WebCore
    240