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