RenderSVGImage.cpp revision 635860845790a19bf50bbc51ba8fb66a96dde068
1/* 2 Copyright (C) 2006 Alexander Kellett <lypanov@kde.org> 3 Copyright (C) 2006 Apple Computer, Inc. 4 Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> 5 Copyright (C) 2007, 2008 Rob Buis <buis@kde.org> 6 7 This file is part of the WebKit project 8 9 This library is free software; you can redistribute it and/or 10 modify it under the terms of the GNU Library General Public 11 License as published by the Free Software Foundation; either 12 version 2 of the License, or (at your option) any later version. 13 14 This library is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 Library General Public License for more details. 18 19 You should have received a copy of the GNU Library General Public License 20 along with this library; see the file COPYING.LIB. If not, write to 21 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 Boston, MA 02110-1301, USA. 23*/ 24 25#include "config.h" 26 27#if ENABLE(SVG) 28#include "RenderSVGImage.h" 29 30#include "Attr.h" 31#include "FloatConversion.h" 32#include "FloatQuad.h" 33#include "GraphicsContext.h" 34#include "PointerEventsHitRules.h" 35#include "SVGImageElement.h" 36#include "SVGLength.h" 37#include "SVGPreserveAspectRatio.h" 38#include "SVGRenderSupport.h" 39#include "SVGResourceClipper.h" 40#include "SVGResourceFilter.h" 41#include "SVGResourceMasker.h" 42 43namespace WebCore { 44 45RenderSVGImage::RenderSVGImage(SVGImageElement* impl) 46 : RenderImage(impl) 47{ 48} 49 50RenderSVGImage::~RenderSVGImage() 51{ 52} 53 54void RenderSVGImage::adjustRectsForAspectRatio(FloatRect& destRect, FloatRect& srcRect, SVGPreserveAspectRatio* aspectRatio) 55{ 56 float origDestWidth = destRect.width(); 57 float origDestHeight = destRect.height(); 58 if (aspectRatio->meetOrSlice() == SVGPreserveAspectRatio::SVG_MEETORSLICE_MEET) { 59 float widthToHeightMultiplier = srcRect.height() / srcRect.width(); 60 if (origDestHeight > (origDestWidth * widthToHeightMultiplier)) { 61 destRect.setHeight(origDestWidth * widthToHeightMultiplier); 62 switch(aspectRatio->align()) { 63 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID: 64 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: 65 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: 66 destRect.setY(destRect.y() + origDestHeight / 2.0f - destRect.height() / 2.0f); 67 break; 68 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX: 69 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: 70 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: 71 destRect.setY(destRect.y() + origDestHeight - destRect.height()); 72 break; 73 } 74 } 75 if (origDestWidth > (origDestHeight / widthToHeightMultiplier)) { 76 destRect.setWidth(origDestHeight / widthToHeightMultiplier); 77 switch(aspectRatio->align()) { 78 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN: 79 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: 80 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: 81 destRect.setX(destRect.x() + origDestWidth / 2.0f - destRect.width() / 2.0f); 82 break; 83 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN: 84 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: 85 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: 86 destRect.setX(destRect.x() + origDestWidth - destRect.width()); 87 break; 88 } 89 } 90 } else if (aspectRatio->meetOrSlice() == SVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE) { 91 float widthToHeightMultiplier = srcRect.height() / srcRect.width(); 92 // if the destination height is less than the height of the image we'll be drawing 93 if (origDestHeight < (origDestWidth * widthToHeightMultiplier)) { 94 float destToSrcMultiplier = srcRect.width() / destRect.width(); 95 srcRect.setHeight(destRect.height() * destToSrcMultiplier); 96 switch(aspectRatio->align()) { 97 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID: 98 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: 99 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: 100 srcRect.setY(destRect.y() + image()->height() / 2.0f - srcRect.height() / 2.0f); 101 break; 102 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX: 103 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: 104 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: 105 srcRect.setY(destRect.y() + image()->height() - srcRect.height()); 106 break; 107 } 108 } 109 // if the destination width is less than the width of the image we'll be drawing 110 if (origDestWidth < (origDestHeight / widthToHeightMultiplier)) { 111 float destToSrcMultiplier = srcRect.height() / destRect.height(); 112 srcRect.setWidth(destRect.width() * destToSrcMultiplier); 113 switch(aspectRatio->align()) { 114 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN: 115 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: 116 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: 117 srcRect.setX(destRect.x() + image()->width() / 2.0f - srcRect.width() / 2.0f); 118 break; 119 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN: 120 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: 121 case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: 122 srcRect.setX(destRect.x() + image()->width() - srcRect.width()); 123 break; 124 } 125 } 126 } 127} 128 129bool RenderSVGImage::calculateLocalTransform() 130{ 131 TransformationMatrix oldTransform = m_localTransform; 132 m_localTransform = static_cast<SVGStyledTransformableElement*>(element())->animatedLocalTransform(); 133 return (m_localTransform != oldTransform); 134} 135 136void RenderSVGImage::layout() 137{ 138 ASSERT(needsLayout()); 139 140 IntRect oldBounds; 141 IntRect oldOutlineBox; 142 bool checkForRepaint = checkForRepaintDuringLayout(); 143 if (checkForRepaint) { 144 oldBounds = absoluteClippedOverflowRect(); 145 oldOutlineBox = absoluteOutlineBounds(); 146 } 147 148 calculateLocalTransform(); 149 150 // minimum height 151 setHeight(errorOccurred() ? intrinsicSize().height() : 0); 152 153 calcWidth(); 154 calcHeight(); 155 156 SVGImageElement* image = static_cast<SVGImageElement*>(node()); 157 m_localBounds = FloatRect(image->x().value(image), image->y().value(image), image->width().value(image), image->height().value(image)); 158 159 calculateAbsoluteBounds(); 160 161 if (checkForRepaint) 162 repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox); 163 164 setNeedsLayout(false); 165} 166 167void RenderSVGImage::paint(PaintInfo& paintInfo, int, int) 168{ 169 if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN) 170 return; 171 172 paintInfo.context->save(); 173 paintInfo.context->concatCTM(localTransform()); 174 175 if (paintInfo.phase == PaintPhaseForeground) { 176 SVGResourceFilter* filter = 0; 177 178 PaintInfo savedInfo(paintInfo); 179 180 prepareToRenderSVGContent(this, paintInfo, m_localBounds, filter); 181 182 FloatRect destRect = m_localBounds; 183 FloatRect srcRect(0, 0, image()->width(), image()->height()); 184 185 SVGImageElement* imageElt = static_cast<SVGImageElement*>(node()); 186 if (imageElt->preserveAspectRatio()->align() != SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE) 187 adjustRectsForAspectRatio(destRect, srcRect, imageElt->preserveAspectRatio()); 188 189 paintInfo.context->drawImage(image(), destRect, srcRect); 190 191 finishRenderSVGContent(this, paintInfo, m_localBounds, filter, savedInfo.context); 192 } 193 194 paintInfo.context->restore(); 195} 196 197bool RenderSVGImage::nodeAtPoint(const HitTestRequest&, HitTestResult& result, int _x, int _y, int, int, HitTestAction hitTestAction) 198{ 199 // We only draw in the forground phase, so we only hit-test then. 200 if (hitTestAction != HitTestForeground) 201 return false; 202 203 PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_IMAGE_HITTESTING, style()->pointerEvents()); 204 205 bool isVisible = (style()->visibility() == VISIBLE); 206 if (isVisible || !hitRules.requireVisible) { 207 double localX, localY; 208 absoluteTransform().inverse().map(_x, _y, &localX, &localY); 209 210 if (hitRules.canHitFill) { 211 if (m_localBounds.contains(narrowPrecisionToFloat(localX), narrowPrecisionToFloat(localY))) { 212 updateHitTestResult(result, IntPoint(_x, _y)); 213 return true; 214 } 215 } 216 } 217 218 return false; 219} 220 221FloatRect RenderSVGImage::relativeBBox(bool) const 222{ 223 return m_localBounds; 224} 225 226void RenderSVGImage::imageChanged(WrappedImagePtr image, const IntRect* rect) 227{ 228 RenderImage::imageChanged(image, rect); 229 230 // We override to invalidate a larger rect, since SVG images can draw outside their "bounds" 231 repaintRectangle(absoluteClippedOverflowRect()); 232} 233 234void RenderSVGImage::calculateAbsoluteBounds() 235{ 236 // FIXME: broken with CSS transforms 237 FloatRect absoluteRect = absoluteTransform().mapRect(relativeBBox(true)); 238 239#if ENABLE(SVG_FILTERS) 240 // Filters can expand the bounding box 241 SVGResourceFilter* filter = getFilterById(document(), style()->svgStyle()->filter()); 242 if (filter) 243 absoluteRect.unite(filter->filterBBoxForItemBBox(absoluteRect)); 244#endif 245 246 if (!absoluteRect.isEmpty()) 247 absoluteRect.inflate(1); // inflate 1 pixel for antialiasing 248 249 m_absoluteBounds = enclosingIntRect(absoluteRect); 250} 251 252IntRect RenderSVGImage::clippedOverflowRectForRepaint(RenderBox* /*repaintContainer*/) 253{ 254 // FIXME: handle non-root repaintContainer 255 return m_absoluteBounds; 256} 257 258void RenderSVGImage::addFocusRingRects(GraphicsContext* graphicsContext, int, int) 259{ 260 // this is called from paint() after the localTransform has already been applied 261 IntRect contentRect = enclosingIntRect(relativeBBox()); 262 graphicsContext->addFocusRingRect(contentRect); 263} 264 265void RenderSVGImage::absoluteRects(Vector<IntRect>& rects, int, int, bool) 266{ 267 rects.append(absoluteClippedOverflowRect()); 268} 269 270void RenderSVGImage::absoluteQuads(Vector<FloatQuad>& quads, bool) 271{ 272 quads.append(FloatRect(absoluteClippedOverflowRect())); 273} 274 275} 276 277#endif // ENABLE(SVG) 278