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/LayoutRectRecorder.h"
     33 #include "core/rendering/LayoutRepainter.h"
     34 #include "core/rendering/PointerEventsHitRules.h"
     35 #include "core/rendering/RenderImageResource.h"
     36 #include "core/rendering/svg/RenderSVGResource.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->xCurrentValue().value(lengthContext), image->yCurrentValue().value(lengthContext), image->widthCurrentValue().value(lengthContext), image->heightCurrentValue().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->preserveAspectRatioCurrentValue().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     LayoutRectRecorder recorder(*this);
     97     LayoutRepainter repainter(*this, SVGRenderSupport::checkForSVGRepaintDuringLayout(this) && selfNeedsLayout());
     98     updateImageViewport();
     99 
    100     bool transformOrBoundariesUpdate = m_needsTransformUpdate || m_needsBoundariesUpdate;
    101     if (m_needsTransformUpdate) {
    102         m_localTransform = toSVGImageElement(element())->animatedLocalTransform();
    103         m_needsTransformUpdate = false;
    104     }
    105 
    106     if (m_needsBoundariesUpdate) {
    107         m_repaintBoundingBox = m_objectBoundingBox;
    108         SVGRenderSupport::intersectRepaintRectWithResources(this, m_repaintBoundingBox);
    109 
    110         m_needsBoundariesUpdate = false;
    111     }
    112 
    113     // Invalidate all resources of this client if our layout changed.
    114     if (everHadLayout() && selfNeedsLayout())
    115         SVGResourcesCache::clientLayoutChanged(this);
    116 
    117     // If our bounds changed, notify the parents.
    118     if (transformOrBoundariesUpdate)
    119         RenderSVGModelObject::setNeedsBoundariesUpdate();
    120 
    121     repainter.repaintAfterLayout();
    122     clearNeedsLayout();
    123 }
    124 
    125 void RenderSVGImage::paint(PaintInfo& paintInfo, const LayoutPoint&)
    126 {
    127     ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
    128 
    129     if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN || !m_imageResource->hasImage())
    130         return;
    131 
    132     FloatRect boundingBox = repaintRectInLocalCoordinates();
    133     if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(boundingBox, m_localTransform, paintInfo))
    134         return;
    135 
    136     PaintInfo childPaintInfo(paintInfo);
    137     bool drawsOutline = style()->outlineWidth() && (childPaintInfo.phase == PaintPhaseOutline || childPaintInfo.phase == PaintPhaseSelfOutline);
    138     if (drawsOutline || childPaintInfo.phase == PaintPhaseForeground) {
    139         GraphicsContextStateSaver stateSaver(*childPaintInfo.context);
    140         childPaintInfo.applyTransform(m_localTransform);
    141 
    142         if (childPaintInfo.phase == PaintPhaseForeground && !m_objectBoundingBox.isEmpty()) {
    143             SVGRenderingContext renderingContext(this, childPaintInfo);
    144 
    145             if (renderingContext.isRenderingPrepared()) {
    146                 if (style()->svgStyle()->bufferedRendering() == BR_STATIC && renderingContext.bufferForeground(m_bufferedForeground))
    147                     return;
    148 
    149                 paintForeground(childPaintInfo);
    150             }
    151         }
    152 
    153         if (drawsOutline)
    154             paintOutline(childPaintInfo, IntRect(boundingBox));
    155     }
    156 }
    157 
    158 void RenderSVGImage::paintForeground(PaintInfo& paintInfo)
    159 {
    160     RefPtr<Image> image = m_imageResource->image();
    161     FloatRect destRect = m_objectBoundingBox;
    162     FloatRect srcRect(0, 0, image->width(), image->height());
    163 
    164     SVGImageElement* imageElement = toSVGImageElement(element());
    165     imageElement->preserveAspectRatioCurrentValue().transformRect(destRect, srcRect);
    166 
    167     bool useLowQualityScaling = false;
    168     if (style()->svgStyle()->bufferedRendering() != BR_STATIC)
    169         useLowQualityScaling = ImageQualityController::imageQualityController()->shouldPaintAtLowQuality(paintInfo.context, this, image.get(), image.get(), LayoutSize(destRect.size()));
    170 
    171     paintInfo.context->drawImage(image.get(), destRect, srcRect, CompositeSourceOver, DoNotRespectImageOrientation, useLowQualityScaling);
    172 }
    173 
    174 void RenderSVGImage::invalidateBufferedForeground()
    175 {
    176     m_bufferedForeground.clear();
    177 }
    178 
    179 bool RenderSVGImage::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
    180 {
    181     // We only draw in the forground phase, so we only hit-test then.
    182     if (hitTestAction != HitTestForeground)
    183         return false;
    184 
    185     PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_IMAGE_HITTESTING, request, style()->pointerEvents());
    186     bool isVisible = (style()->visibility() == VISIBLE);
    187     if (isVisible || !hitRules.requireVisible) {
    188         FloatPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent);
    189 
    190         if (!SVGRenderSupport::pointInClippingArea(this, localPoint))
    191             return false;
    192 
    193         if (hitRules.canHitFill || hitRules.canHitBoundingBox) {
    194             if (m_objectBoundingBox.contains(localPoint)) {
    195                 updateHitTestResult(result, roundedLayoutPoint(localPoint));
    196                 return true;
    197             }
    198         }
    199     }
    200 
    201     return false;
    202 }
    203 
    204 void RenderSVGImage::imageChanged(WrappedImagePtr, const IntRect*)
    205 {
    206     // The image resource defaults to nullImage until the resource arrives.
    207     // This empty image may be cached by SVG resources which must be invalidated.
    208     if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this))
    209         resources->removeClientFromCache(this);
    210 
    211     // Eventually notify parent resources, that we've changed.
    212     RenderSVGResource::markForLayoutAndParentResourceInvalidation(this, false);
    213 
    214     // Update the SVGImageCache sizeAndScales entry in case image loading finished after layout.
    215     // (https://bugs.webkit.org/show_bug.cgi?id=99489)
    216     m_objectBoundingBox = FloatRect();
    217     updateImageViewport();
    218 
    219     invalidateBufferedForeground();
    220 
    221     repaint();
    222 }
    223 
    224 void RenderSVGImage::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint&, const RenderLayerModelObject*)
    225 {
    226     // this is called from paint() after the localTransform has already been applied
    227     IntRect contentRect = enclosingIntRect(repaintRectInLocalCoordinates());
    228     if (!contentRect.isEmpty())
    229         rects.append(contentRect);
    230 }
    231 
    232 } // namespace WebCore
    233