File indexing completed on 2024-11-03 09:54:37
0001 /* 0002 Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org> 0003 2004, 2005 Rob Buis <buis@kde.org> 0004 2005, 2007 Eric Seidel <eric@webkit.org> 0005 0006 This file is part of the KDE project 0007 0008 This library is free software; you can redistribute it and/or 0009 modify it under the terms of the GNU Library General Public 0010 License as published by the Free Software Foundation; either 0011 version 2 of the License, or (at your option) any later version. 0012 0013 This library is distributed in the hope that it will be useful, 0014 but WITHOUT ANY WARRANTY; without even the implied warranty of 0015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0016 Library General Public License for more details. 0017 0018 You should have received a copy of the GNU Library General Public License 0019 aint with this library; see the file COPYING.LIB. If not, write to 0020 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0021 Boston, MA 02110-1301, USA. 0022 */ 0023 0024 #include "wtf/Platform.h" 0025 #include "Document.h" 0026 0027 #if ENABLE(SVG) 0028 #include "RenderPath.h" 0029 0030 #include <math.h> 0031 0032 /*#include "GraphicsContext.h" 0033 #include "PointerEventsHitRules.h"*/ 0034 #include "RenderSVGContainer.h" 0035 #include "SVGPaintServer.h" 0036 #include "SVGRenderSupport.h" 0037 /*#include "SVGResourceFilter.h" 0038 #include "SVGResourceMarker.h" 0039 #include "SVGResourceMasker.h"*/ 0040 #include "SVGStyledTransformableElement.h" 0041 #include "SVGTransformList.h" 0042 #include "SVGURIReference.h" 0043 0044 #include <wtf/MathExtras.h> 0045 0046 namespace WebCore 0047 { 0048 0049 // RenderPath 0050 RenderPath::RenderPath(RenderStyle *style, SVGStyledTransformableElement *node) 0051 : RenderObject(node) 0052 { 0053 ASSERT(style != nullptr); Q_UNUSED(style); 0054 ASSERT(static_cast<SVGElement *>(node)->isStyledTransformable()); 0055 } 0056 0057 RenderPath::~RenderPath() 0058 { 0059 } 0060 0061 AffineTransform RenderPath::localTransform() const 0062 { 0063 return m_localTransform; 0064 } 0065 0066 FloatPoint RenderPath::mapAbsolutePointToLocal(const FloatPoint &point) const 0067 { 0068 // FIXME: does it make sense to map incoming points with the inverse of the 0069 // absolute transform? 0070 double localX; 0071 double localY; 0072 absoluteTransform().inverse().map(point.x(), point.y(), &localX, &localY); 0073 return FloatPoint::narrowPrecision(localX, localY); 0074 } 0075 0076 bool RenderPath::fillContains(const FloatPoint &point, bool requiresFill) const 0077 { 0078 if (m_path.isEmpty()) { 0079 return false; 0080 } 0081 0082 if (requiresFill && !SVGPaintServer::fillPaintServer(style(), this)) { 0083 return false; 0084 } 0085 0086 return m_path.contains(point, style()->svgStyle()->fillRule()); 0087 } 0088 0089 FloatRect RenderPath::relativeBBox(bool includeStroke) const 0090 { 0091 if (m_path.isEmpty()) { 0092 return FloatRect(); 0093 } 0094 0095 if (includeStroke) { 0096 if (m_strokeBbox.isEmpty()) 0097 /*m_strokeBbox = strokeBBox();*/ 0098 0099 { 0100 return m_strokeBbox; 0101 } 0102 } 0103 0104 if (m_fillBBox.isEmpty()) { 0105 m_fillBBox = m_path.boundingRect(); 0106 } 0107 0108 return m_fillBBox; 0109 } 0110 0111 void RenderPath::setPath(const Path &newPath) 0112 { 0113 m_path = newPath; 0114 m_strokeBbox = FloatRect(); 0115 m_fillBBox = FloatRect(); 0116 } 0117 0118 const Path &RenderPath::path() const 0119 { 0120 return m_path; 0121 } 0122 0123 bool RenderPath::calculateLocalTransform() 0124 { 0125 AffineTransform oldTransform = m_localTransform; 0126 m_localTransform = static_cast<SVGStyledTransformableElement *>(element())->animatedLocalTransform(); 0127 return (m_localTransform != oldTransform); 0128 } 0129 0130 void RenderPath::layout() 0131 { 0132 IntRect oldBounds; 0133 IntRect oldOutlineBox; 0134 bool checkForRepaint = /*checkForRepaintDuringLayout() && */selfNeedsLayout(); 0135 if (checkForRepaint) { 0136 oldBounds = m_absoluteBounds; 0137 //oldOutlineBox = absoluteOutlineBox(); 0138 } 0139 0140 calculateLocalTransform(); 0141 0142 setPath(static_cast<SVGStyledTransformableElement *>(element())->toPathData()); 0143 0144 m_absoluteBounds = absoluteClippedOverflowRect(); 0145 0146 setWidth(m_absoluteBounds.width()); 0147 setHeight(m_absoluteBounds.height()); 0148 0149 /*if (checkForRepaint) 0150 repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox);*/ 0151 0152 setNeedsLayout(false); 0153 } 0154 0155 IntRect RenderPath::absoluteClippedOverflowRect() 0156 { 0157 return IntRect(0, 0, 100, 100); 0158 /*FloatRect repaintRect = absoluteTransform().mapRect(relativeBBox(true)); 0159 0160 // Markers can expand the bounding box 0161 repaintRect.unite(m_markerBounds); 0162 0163 #if ENABLE(SVG_FILTERS) 0164 // Filters can expand the bounding box 0165 SVGResourceFilter* filter = getFilterById(document(), SVGURIReference::getTarget(style()->svgStyle()->filter())); 0166 if (filter) 0167 repaintRect.unite(filter->filterBBoxForItemBBox(repaintRect)); 0168 #endif 0169 0170 if (!repaintRect.isEmpty()) 0171 repaintRect.inflate(1); // inflate 1 pixel for antialiasing 0172 0173 return enclosingIntRect(repaintRect);*/ 0174 } 0175 0176 bool RenderPath::requiresLayer() const 0177 { 0178 return false; 0179 } 0180 0181 short RenderPath::lineHeight(bool b) const 0182 { 0183 Q_UNUSED(b); 0184 return static_cast<short>(relativeBBox(true).height()); 0185 } 0186 0187 short RenderPath::baselinePosition(bool b) const 0188 { 0189 Q_UNUSED(b); 0190 return static_cast<short>(relativeBBox(true).height()); 0191 } 0192 0193 static inline void fillAndStrokePath(const Path &path, QPainter *painter, RenderStyle *style, RenderPath *object) 0194 { 0195 /*context->beginPath();*/ 0196 0197 SVGPaintServer *fillPaintServer = SVGPaintServer::fillPaintServer(style, object); 0198 if (fillPaintServer) { 0199 /*context->addPath(path);*/ 0200 fillPaintServer->draw(painter, path.platformPath(), object, ApplyToFillTargetType); 0201 } 0202 0203 SVGPaintServer *strokePaintServer = SVGPaintServer::strokePaintServer(style, object); 0204 if (strokePaintServer) { 0205 /*context->addPath(path); // path is cleared when filled.*/ 0206 strokePaintServer->draw(painter, path.platformPath(), object, ApplyToStrokeTargetType); 0207 } 0208 } 0209 0210 void RenderPath::paint(PaintInfo &paintInfo, int, int) 0211 { 0212 paintInfo.p->save(); 0213 paintInfo.p->setWorldMatrix(localTransform(), true); 0214 SVGResourceFilter *filter = nullptr; 0215 prepareToRenderSVGContent(this, paintInfo, FloatRect(), filter/*boundingBox, filter*/); 0216 if (paintInfo.phase == PaintActionForeground) { 0217 fillAndStrokePath(m_path, paintInfo.p, style(), this); 0218 } 0219 paintInfo.p->restore(); 0220 0221 #if 0 0222 /*if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN || m_path.isEmpty()) 0223 return;*/ 0224 0225 //paintInfo.context->save(); 0226 /*paintInfo.context->concatCTM(localTransform());*/ 0227 0228 //paintInfo.p->fillRect(0, 0, 50, 50, QBrush(Qt::yellow)); 0229 /*paintInfo.p->setPen(Qt::blue); 0230 // qCDebug(KHTML_LOG) << "path:" << *m_path.platformPath(); 0231 paintInfo.p->drawPath(*m_path.platformPath());*/ 0232 /*SVGResourceFilter* filter = 0; 0233 0234 FloatRect boundingBox = relativeBBox(true); 0235 if (paintInfo.phase == PaintPhaseForeground) { 0236 PaintInfo savedInfo(paintInfo); 0237 0238 prepareToRenderSVGContent(this, paintInfo, boundingBox, filter); 0239 if (style()->svgStyle()->shapeRendering() == SR_CRISPEDGES) 0240 paintInfo.context->setUseAntialiasing(false); 0241 fillAndStrokePath(m_path, paintInfo.context, style(), this); 0242 0243 if (static_cast<SVGStyledElement*>(element())->supportsMarkers()) 0244 m_markerBounds = drawMarkersIfNeeded(paintInfo.context, paintInfo.rect, m_path); 0245 0246 finishRenderSVGContent(this, paintInfo, boundingBox, filter, savedInfo.context); 0247 } 0248 0249 if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth()) 0250 paintOutline(paintInfo.context, static_cast<int>(boundingBox.x()), static_cast<int>(boundingBox.y()), 0251 static_cast<int>(boundingBox.width()), static_cast<int>(boundingBox.height()), style());*/ 0252 0253 //paintInfo.context->restore(); 0254 #endif 0255 } 0256 0257 /*void RenderPath::addFocusRingRects(GraphicsContext* graphicsContext, int, int) 0258 { 0259 graphicsContext->addFocusRingRect(enclosingIntRect(relativeBBox(true))); 0260 }*/ 0261 0262 void RenderPath::absoluteRects(Vector<IntRect> &rects, int, int, bool) 0263 { 0264 rects.append(absoluteClippedOverflowRect()); 0265 } 0266 0267 /*bool RenderPath::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int _x, int _y, int, int, HitTestAction hitTestAction) 0268 { 0269 // We only draw in the forground phase, so we only hit-test then. 0270 if (hitTestAction != HitTestForeground) 0271 return false; 0272 0273 IntPoint absolutePoint(_x, _y); 0274 0275 PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_PATH_HITTESTING, style()->svgStyle()->pointerEvents()); 0276 0277 bool isVisible = (style()->visibility() == VISIBLE); 0278 if (isVisible || !hitRules.requireVisible) { 0279 FloatPoint hitPoint = mapAbsolutePointToLocal(absolutePoint); 0280 if ((hitRules.canHitStroke && (style()->svgStyle()->hasStroke() || !hitRules.requireStroke) && strokeContains(hitPoint, hitRules.requireStroke)) 0281 || (hitRules.canHitFill && (style()->svgStyle()->hasFill() || !hitRules.requireFill) && fillContains(hitPoint, hitRules.requireFill))) { 0282 updateHitTestResult(result, absolutePoint); 0283 return true; 0284 } 0285 } 0286 0287 return false; 0288 }*/ 0289 0290 /*enum MarkerType { 0291 Start, 0292 Mid, 0293 End 0294 }; 0295 0296 struct MarkerData { 0297 FloatPoint origin; 0298 FloatPoint subpathStart; 0299 double strokeWidth; 0300 FloatPoint inslopePoints[2]; 0301 FloatPoint outslopePoints[2]; 0302 MarkerType type; 0303 SVGResourceMarker* marker; 0304 }; 0305 0306 struct DrawMarkersData { 0307 DrawMarkersData(GraphicsContext*, SVGResourceMarker* startMarker, SVGResourceMarker* midMarker, double strokeWidth); 0308 GraphicsContext* context; 0309 int elementIndex; 0310 MarkerData previousMarkerData; 0311 SVGResourceMarker* midMarker; 0312 }; 0313 0314 DrawMarkersData::DrawMarkersData(GraphicsContext* c, SVGResourceMarker *start, SVGResourceMarker *mid, double strokeWidth) 0315 : context(c) 0316 , elementIndex(0) 0317 , midMarker(mid) 0318 { 0319 previousMarkerData.origin = FloatPoint(); 0320 previousMarkerData.subpathStart = FloatPoint(); 0321 previousMarkerData.strokeWidth = strokeWidth; 0322 previousMarkerData.marker = start; 0323 previousMarkerData.type = Start; 0324 } 0325 0326 static void drawMarkerWithData(GraphicsContext* context, MarkerData &data) 0327 { 0328 if (!data.marker) 0329 return; 0330 0331 FloatPoint inslopeChange = data.inslopePoints[1] - FloatSize(data.inslopePoints[0].x(), data.inslopePoints[0].y()); 0332 FloatPoint outslopeChange = data.outslopePoints[1] - FloatSize(data.outslopePoints[0].x(), data.outslopePoints[0].y()); 0333 0334 double inslope = rad2deg(atan2(inslopeChange.y(), inslopeChange.x())); 0335 double outslope = rad2deg(atan2(outslopeChange.y(), outslopeChange.x())); 0336 0337 double angle = 0.0; 0338 switch (data.type) { 0339 case Start: 0340 angle = outslope; 0341 break; 0342 case Mid: 0343 angle = (inslope + outslope) / 2; 0344 break; 0345 case End: 0346 angle = inslope; 0347 } 0348 0349 data.marker->draw(context, FloatRect(), data.origin.x(), data.origin.y(), data.strokeWidth, angle); 0350 } 0351 0352 static inline void updateMarkerDataForElement(MarkerData& previousMarkerData, const PathElement* element) 0353 { 0354 FloatPoint* points = element->points; 0355 0356 switch (element->type) { 0357 case PathElementAddQuadCurveToPoint: 0358 // TODO 0359 previousMarkerData.origin = points[1]; 0360 break; 0361 case PathElementAddCurveToPoint: 0362 previousMarkerData.inslopePoints[0] = points[1]; 0363 previousMarkerData.inslopePoints[1] = points[2]; 0364 previousMarkerData.origin = points[2]; 0365 break; 0366 case PathElementMoveToPoint: 0367 previousMarkerData.subpathStart = points[0]; 0368 case PathElementAddLineToPoint: 0369 previousMarkerData.inslopePoints[0] = previousMarkerData.origin; 0370 previousMarkerData.inslopePoints[1] = points[0]; 0371 previousMarkerData.origin = points[0]; 0372 break; 0373 case PathElementCloseSubpath: 0374 previousMarkerData.inslopePoints[0] = previousMarkerData.origin; 0375 previousMarkerData.inslopePoints[1] = points[0]; 0376 previousMarkerData.origin = previousMarkerData.subpathStart; 0377 previousMarkerData.subpathStart = FloatPoint(); 0378 } 0379 } 0380 0381 static void drawStartAndMidMarkers(void* info, const PathElement* element) 0382 { 0383 DrawMarkersData& data = *reinterpret_cast<DrawMarkersData*>(info); 0384 0385 int elementIndex = data.elementIndex; 0386 MarkerData& previousMarkerData = data.previousMarkerData; 0387 0388 FloatPoint* points = element->points; 0389 0390 // First update the outslope for the previous element 0391 previousMarkerData.outslopePoints[0] = previousMarkerData.origin; 0392 previousMarkerData.outslopePoints[1] = points[0]; 0393 0394 // Draw the marker for the previous element 0395 if (elementIndex != 0) 0396 drawMarkerWithData(data.context, previousMarkerData); 0397 0398 // Update our marker data for this element 0399 updateMarkerDataForElement(previousMarkerData, element); 0400 0401 if (elementIndex == 1) { 0402 // After drawing the start marker, switch to drawing mid markers 0403 previousMarkerData.marker = data.midMarker; 0404 previousMarkerData.type = Mid; 0405 } 0406 0407 data.elementIndex++; 0408 } 0409 0410 FloatRect RenderPath::drawMarkersIfNeeded(GraphicsContext* context, const FloatRect& rect, const Path& path) const 0411 { 0412 Document* doc = document(); 0413 0414 SVGElement* svgElement = static_cast<SVGElement*>(element()); 0415 ASSERT(svgElement && svgElement->document() && svgElement->isStyled()); 0416 0417 SVGStyledElement* styledElement = static_cast<SVGStyledElement*>(svgElement); 0418 const SVGRenderStyle* svgStyle = style()->svgStyle(); 0419 0420 AtomicString startMarkerId(SVGURIReference::getTarget(svgStyle->startMarker())); 0421 AtomicString midMarkerId(SVGURIReference::getTarget(svgStyle->midMarker())); 0422 AtomicString endMarkerId(SVGURIReference::getTarget(svgStyle->endMarker())); 0423 0424 SVGResourceMarker* startMarker = getMarkerById(doc, startMarkerId); 0425 SVGResourceMarker* midMarker = getMarkerById(doc, midMarkerId); 0426 SVGResourceMarker* endMarker = getMarkerById(doc, endMarkerId); 0427 0428 if (!startMarker && !startMarkerId.isEmpty()) 0429 svgElement->document()->accessSVGExtensions()->addPendingResource(startMarkerId, styledElement); 0430 else if (startMarker) 0431 startMarker->addClient(styledElement); 0432 0433 if (!midMarker && !midMarkerId.isEmpty()) 0434 svgElement->document()->accessSVGExtensions()->addPendingResource(midMarkerId, styledElement); 0435 else if (midMarker) 0436 midMarker->addClient(styledElement); 0437 0438 if (!endMarker && !endMarkerId.isEmpty()) 0439 svgElement->document()->accessSVGExtensions()->addPendingResource(endMarkerId, styledElement); 0440 else if (endMarker) 0441 endMarker->addClient(styledElement); 0442 0443 if (!startMarker && !midMarker && !endMarker) 0444 return FloatRect(); 0445 0446 double strokeWidth = SVGRenderStyle::cssPrimitiveToLength(this, svgStyle->strokeWidth(), 1.0f); 0447 DrawMarkersData data(context, startMarker, midMarker, strokeWidth); 0448 0449 path.apply(&data, drawStartAndMidMarkers); 0450 0451 data.previousMarkerData.marker = endMarker; 0452 data.previousMarkerData.type = End; 0453 drawMarkerWithData(context, data.previousMarkerData); 0454 0455 // We know the marker boundaries, only after they're drawn! 0456 // Otherwhise we'd need to do all the marker calculation twice 0457 // once here (through paint()) and once in absoluteClippedOverflowRect(). 0458 FloatRect bounds; 0459 0460 if (startMarker) 0461 bounds.unite(startMarker->cachedBounds()); 0462 0463 if (midMarker) 0464 bounds.unite(midMarker->cachedBounds()); 0465 0466 if (endMarker) 0467 bounds.unite(endMarker->cachedBounds()); 0468 0469 return bounds; 0470 }*/ 0471 0472 } 0473 0474 #endif // ENABLE(SVG)