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/paint/SVGImagePainter.h" 31 #include "core/rendering/ImageQualityController.h" 32 #include "core/rendering/PointerEventsHitRules.h" 33 #include "core/rendering/RenderImageResource.h" 34 #include "core/rendering/svg/RenderSVGResource.h" 35 #include "core/rendering/svg/SVGRenderSupport.h" 36 #include "core/rendering/svg/SVGResources.h" 37 #include "core/rendering/svg/SVGResourcesCache.h" 38 #include "core/svg/SVGImageElement.h" 39 #include "platform/LengthFunctions.h" 40 41 namespace blink { 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 } 55 56 void RenderSVGImage::destroy() 57 { 58 ImageQualityController::remove(this); 59 m_imageResource->shutdown(); 60 RenderSVGModelObject::destroy(); 61 } 62 63 bool RenderSVGImage::forceNonUniformScaling(SVGImageElement* image) const 64 { 65 // Images with preserveAspectRatio=none should force non-uniform 66 // scaling. This can be achieved by setting the image's container size to 67 // its intrinsic size. If the image does not have an intrinsic size - or 68 // the intrinsic size is degenerate - set the container size to the bounds 69 // as in pAR!=none cases. 70 // See: http://www.w3.org/TR/SVG/single-page.html, 7.8 The preserveAspectRatio attribute. 71 if (image->preserveAspectRatio()->currentValue()->align() != SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE) 72 return false; 73 ImageResource* cachedImage = m_imageResource->cachedImage(); 74 if (!cachedImage) 75 return false; 76 Length intrinsicWidth; 77 Length intrinsicHeight; 78 FloatSize intrinsicRatio; 79 cachedImage->computeIntrinsicDimensions(intrinsicWidth, intrinsicHeight, intrinsicRatio); 80 if (!intrinsicWidth.isFixed() || !intrinsicHeight.isFixed()) 81 return false; 82 // If the viewport defined by the referenced image is zero in either 83 // dimension, then SVGImage will have computed an intrinsic size of 300x150. 84 if (!floatValueForLength(intrinsicWidth, 0) || !floatValueForLength(intrinsicHeight, 0)) 85 return false; 86 return true; 87 } 88 89 bool RenderSVGImage::updateImageViewport() 90 { 91 SVGImageElement* image = toSVGImageElement(element()); 92 FloatRect oldBoundaries = m_objectBoundingBox; 93 bool updatedViewport = false; 94 95 SVGLengthContext lengthContext(image); 96 m_objectBoundingBox = FloatRect(image->x()->currentValue()->value(lengthContext), image->y()->currentValue()->value(lengthContext), image->width()->currentValue()->value(lengthContext), image->height()->currentValue()->value(lengthContext)); 97 98 bool boundsChanged = oldBoundaries != m_objectBoundingBox; 99 100 IntSize newViewportSize; 101 if (forceNonUniformScaling(image)) { 102 LayoutSize intrinsicSize = m_imageResource->intrinsicSize(style()->effectiveZoom()); 103 if (intrinsicSize != m_imageResource->imageSize(style()->effectiveZoom())) { 104 newViewportSize = roundedIntSize(intrinsicSize); 105 updatedViewport = true; 106 } 107 } else if (boundsChanged) { 108 newViewportSize = enclosingIntRect(m_objectBoundingBox).size(); 109 updatedViewport = true; 110 } 111 if (updatedViewport) 112 m_imageResource->setContainerSizeForRenderer(newViewportSize); 113 m_needsBoundariesUpdate |= boundsChanged; 114 return updatedViewport; 115 } 116 117 void RenderSVGImage::layout() 118 { 119 ASSERT(needsLayout()); 120 121 updateImageViewport(); 122 123 bool transformOrBoundariesUpdate = m_needsTransformUpdate || m_needsBoundariesUpdate; 124 if (m_needsTransformUpdate) { 125 m_localTransform = toSVGImageElement(element())->animatedLocalTransform(); 126 m_needsTransformUpdate = false; 127 } 128 129 if (m_needsBoundariesUpdate) { 130 m_paintInvalidationBoundingBox = m_objectBoundingBox; 131 SVGRenderSupport::intersectPaintInvalidationRectWithResources(this, m_paintInvalidationBoundingBox); 132 133 m_needsBoundariesUpdate = false; 134 } 135 136 // Invalidate all resources of this client if our layout changed. 137 if (everHadLayout() && selfNeedsLayout()) 138 SVGResourcesCache::clientLayoutChanged(this); 139 140 // If our bounds changed, notify the parents. 141 if (transformOrBoundariesUpdate) 142 RenderSVGModelObject::setNeedsBoundariesUpdate(); 143 144 clearNeedsLayout(); 145 } 146 147 void RenderSVGImage::paint(PaintInfo& paintInfo, const LayoutPoint&) 148 { 149 SVGImagePainter(*this).paint(paintInfo); 150 } 151 152 void RenderSVGImage::invalidateBufferedForeground() 153 { 154 m_bufferedForeground.clear(); 155 } 156 157 bool RenderSVGImage::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction) 158 { 159 // We only draw in the forground phase, so we only hit-test then. 160 if (hitTestAction != HitTestForeground) 161 return false; 162 163 PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_IMAGE_HITTESTING, request, style()->pointerEvents()); 164 bool isVisible = (style()->visibility() == VISIBLE); 165 if (isVisible || !hitRules.requireVisible) { 166 FloatPoint localPoint; 167 if (!SVGRenderSupport::transformToUserSpaceAndCheckClipping(this, localToParentTransform(), pointInParent, localPoint)) 168 return false; 169 170 if (hitRules.canHitFill || hitRules.canHitBoundingBox) { 171 if (m_objectBoundingBox.contains(localPoint)) { 172 updateHitTestResult(result, roundedLayoutPoint(localPoint)); 173 return true; 174 } 175 } 176 } 177 178 return false; 179 } 180 181 void RenderSVGImage::imageChanged(WrappedImagePtr, const IntRect*) 182 { 183 // The image resource defaults to nullImage until the resource arrives. 184 // This empty image may be cached by SVG resources which must be invalidated. 185 if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this)) 186 resources->removeClientFromCache(this); 187 188 // Eventually notify parent resources, that we've changed. 189 RenderSVGResource::markForLayoutAndParentResourceInvalidation(this, false); 190 191 // Update the SVGImageCache sizeAndScales entry in case image loading finished after layout. 192 // (https://bugs.webkit.org/show_bug.cgi?id=99489) 193 m_objectBoundingBox = FloatRect(); 194 updateImageViewport(); 195 196 invalidateBufferedForeground(); 197 198 setShouldDoFullPaintInvalidation(true); 199 } 200 201 void RenderSVGImage::addFocusRingRects(Vector<LayoutRect>& rects, const LayoutPoint&, const RenderLayerModelObject*) const 202 { 203 // this is called from paint() after the localTransform has already been applied 204 LayoutRect contentRect = LayoutRect(paintInvalidationRectInLocalCoordinates()); 205 if (!contentRect.isEmpty()) 206 rects.append(contentRect); 207 } 208 209 } // namespace blink 210