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