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