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/platform/graphics/GraphicsContextStateSaver.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/SVGRenderingContext.h"
     37 #include "core/rendering/svg/SVGResources.h"
     38 #include "core/rendering/svg/SVGResourcesCache.h"
     39 #include "core/svg/SVGImageElement.h"
     40 
     41 namespace WebCore {
     42 
     43 RenderSVGImage::RenderSVGImage(SVGImageElement* impl)
     44     : RenderSVGModelObject(impl)
     45     , m_needsBoundariesUpdate(true)
     46     , m_needsTransformUpdate(true)
     47     , m_imageResource(RenderImageResource::create())
     48 {
     49     m_imageResource->initialize(this);
     50 }
     51 
     52 RenderSVGImage::~RenderSVGImage()
     53 {
     54     ImageQualityController::remove(this);
     55     m_imageResource->shutdown();
     56 }
     57 
     58 bool RenderSVGImage::updateImageViewport()
     59 {
     60     SVGImageElement* image = toSVGImageElement(node());
     61     FloatRect oldBoundaries = m_objectBoundingBox;
     62     bool updatedViewport = false;
     63 
     64     SVGLengthContext lengthContext(image);
     65     m_objectBoundingBox = FloatRect(image->xCurrentValue().value(lengthContext), image->yCurrentValue().value(lengthContext), image->widthCurrentValue().value(lengthContext), image->heightCurrentValue().value(lengthContext));
     66 
     67     // Images with preserveAspectRatio=none should force non-uniform scaling. This can be achieved
     68     // by setting the image's container size to its intrinsic size.
     69     // See: http://www.w3.org/TR/SVG/single-page.html, 7.8 The preserveAspectRatio attribute.
     70     if (image->preserveAspectRatioCurrentValue().align() == SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE) {
     71         if (ImageResource* cachedImage = m_imageResource->cachedImage()) {
     72             LayoutSize intrinsicSize = cachedImage->imageSizeForRenderer(0, style()->effectiveZoom());
     73             if (intrinsicSize != m_imageResource->imageSize(style()->effectiveZoom())) {
     74                 m_imageResource->setContainerSizeForRenderer(roundedIntSize(intrinsicSize));
     75                 updatedViewport = true;
     76             }
     77         }
     78     }
     79 
     80     if (oldBoundaries != m_objectBoundingBox) {
     81         if (!updatedViewport)
     82             m_imageResource->setContainerSizeForRenderer(enclosingIntRect(m_objectBoundingBox).size());
     83         updatedViewport = true;
     84         m_needsBoundariesUpdate = true;
     85     }
     86 
     87     return updatedViewport;
     88 }
     89 
     90 void RenderSVGImage::layout()
     91 {
     92     StackStats::LayoutCheckPoint layoutCheckPoint;
     93     ASSERT(needsLayout());
     94 
     95     LayoutRepainter repainter(*this, SVGRenderSupport::checkForSVGRepaintDuringLayout(this) && selfNeedsLayout());
     96     updateImageViewport();
     97 
     98     bool transformOrBoundariesUpdate = m_needsTransformUpdate || m_needsBoundariesUpdate;
     99     if (m_needsTransformUpdate) {
    100         m_localTransform = toSVGImageElement(node())->animatedLocalTransform();
    101         m_needsTransformUpdate = false;
    102     }
    103 
    104     if (m_needsBoundariesUpdate) {
    105         m_repaintBoundingBox = m_objectBoundingBox;
    106         SVGRenderSupport::intersectRepaintRectWithResources(this, m_repaintBoundingBox);
    107 
    108         m_needsBoundariesUpdate = false;
    109     }
    110 
    111     // Invalidate all resources of this client if our layout changed.
    112     if (everHadLayout() && selfNeedsLayout())
    113         SVGResourcesCache::clientLayoutChanged(this);
    114 
    115     // If our bounds changed, notify the parents.
    116     if (transformOrBoundariesUpdate)
    117         RenderSVGModelObject::setNeedsBoundariesUpdate();
    118 
    119     repainter.repaintAfterLayout();
    120     clearNeedsLayout();
    121 }
    122 
    123 void RenderSVGImage::paint(PaintInfo& paintInfo, const LayoutPoint&)
    124 {
    125     ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
    126 
    127     if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN || !m_imageResource->hasImage())
    128         return;
    129 
    130     FloatRect boundingBox = repaintRectInLocalCoordinates();
    131     if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(boundingBox, m_localTransform, paintInfo))
    132         return;
    133 
    134     PaintInfo childPaintInfo(paintInfo);
    135     bool drawsOutline = style()->outlineWidth() && (childPaintInfo.phase == PaintPhaseOutline || childPaintInfo.phase == PaintPhaseSelfOutline);
    136     if (drawsOutline || childPaintInfo.phase == PaintPhaseForeground) {
    137         GraphicsContextStateSaver stateSaver(*childPaintInfo.context);
    138         childPaintInfo.applyTransform(m_localTransform);
    139 
    140         if (childPaintInfo.phase == PaintPhaseForeground) {
    141             SVGRenderingContext renderingContext(this, childPaintInfo);
    142 
    143             if (renderingContext.isRenderingPrepared()) {
    144                 if (style()->svgStyle()->bufferedRendering() == BR_STATIC  && renderingContext.bufferForeground(m_bufferedForeground))
    145                     return;
    146 
    147                 paintForeground(childPaintInfo);
    148             }
    149         }
    150 
    151         if (drawsOutline)
    152             paintOutline(childPaintInfo, IntRect(boundingBox));
    153     }
    154 }
    155 
    156 void RenderSVGImage::paintForeground(PaintInfo& paintInfo)
    157 {
    158     RefPtr<Image> image = m_imageResource->image();
    159     FloatRect destRect = m_objectBoundingBox;
    160     FloatRect srcRect(0, 0, image->width(), image->height());
    161 
    162     SVGImageElement* imageElement = toSVGImageElement(node());
    163     imageElement->preserveAspectRatioCurrentValue().transformRect(destRect, srcRect);
    164 
    165     bool useLowQualityScaling = false;
    166     if (style()->svgStyle()->bufferedRendering() != BR_STATIC)
    167         useLowQualityScaling = ImageQualityController::imageQualityController()->shouldPaintAtLowQuality(paintInfo.context, this, image.get(), image.get(), LayoutSize(destRect.size()));
    168 
    169     paintInfo.context->drawImage(image.get(), destRect, srcRect, CompositeSourceOver, DoNotRespectImageOrientation, useLowQualityScaling);
    170 }
    171 
    172 void RenderSVGImage::invalidateBufferedForeground()
    173 {
    174     m_bufferedForeground.clear();
    175 }
    176 
    177 bool RenderSVGImage::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
    178 {
    179     // We only draw in the forground phase, so we only hit-test then.
    180     if (hitTestAction != HitTestForeground)
    181         return false;
    182 
    183     PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_IMAGE_HITTESTING, request, style()->pointerEvents());
    184     bool isVisible = (style()->visibility() == VISIBLE);
    185     if (isVisible || !hitRules.requireVisible) {
    186         FloatPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent);
    187 
    188         if (!SVGRenderSupport::pointInClippingArea(this, localPoint))
    189             return false;
    190 
    191         if (hitRules.canHitFill) {
    192             if (m_objectBoundingBox.contains(localPoint)) {
    193                 updateHitTestResult(result, roundedLayoutPoint(localPoint));
    194                 return true;
    195             }
    196         }
    197     }
    198 
    199     return false;
    200 }
    201 
    202 void RenderSVGImage::imageChanged(WrappedImagePtr, const IntRect*)
    203 {
    204     // The image resource defaults to nullImage until the resource arrives.
    205     // This empty image may be cached by SVG resources which must be invalidated.
    206     if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this))
    207         resources->removeClientFromCache(this);
    208 
    209     // Eventually notify parent resources, that we've changed.
    210     RenderSVGResource::markForLayoutAndParentResourceInvalidation(this, false);
    211 
    212     // Update the SVGImageCache sizeAndScales entry in case image loading finished after layout.
    213     // (https://bugs.webkit.org/show_bug.cgi?id=99489)
    214     m_objectBoundingBox = FloatRect();
    215     updateImageViewport();
    216 
    217     invalidateBufferedForeground();
    218 
    219     repaint();
    220 }
    221 
    222 void RenderSVGImage::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint&, const RenderLayerModelObject*)
    223 {
    224     // this is called from paint() after the localTransform has already been applied
    225     IntRect contentRect = enclosingIntRect(repaintRectInLocalCoordinates());
    226     if (!contentRect.isEmpty())
    227         rects.append(contentRect);
    228 }
    229 
    230 } // namespace WebCore
    231