2 Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org>
3 2004, 2005 Rob Buis <buis@kde.org>
4 2005, 2007 Eric Seidel <eric@webkit.org>
6 This file is part of the KDE project
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later version.
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Library General Public License for more details.
18 You should have received a copy of the GNU Library General Public License
19 aint with this library; see the file COPYING.LIB. If not, write to
20 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 Boston, MA 02110-1301, USA.
25 #include "wtf/Platform.h"
29 #include "RenderPath.h"
33 #include "FloatPoint.h"
34 /*#include "GraphicsContext.h"
35 #include "PointerEventsHitRules.h"*/
36 #include "RenderSVGContainer.h"
37 #include "SVGPaintServer.h"
38 #include "SVGRenderSupport.h"
39 /*#include "SVGResourceFilter.h"
40 #include "SVGResourceMarker.h"
41 #include "SVGResourceMasker.h"*/
42 #include "SVGStyledTransformableElement.h"
43 #include "SVGTransformList.h"
44 #include "SVGURIReference.h"
46 #include <wtf/MathExtras.h>
51 RenderPath::RenderPath(RenderStyle
* style
, SVGStyledTransformableElement
* node
)
55 ASSERT(static_cast<SVGElement
*>(node
)->isStyledTransformable());
58 RenderPath::~RenderPath()
62 AffineTransform
RenderPath::localTransform() const
64 return m_localTransform
;
67 FloatPoint
RenderPath::mapAbsolutePointToLocal(const FloatPoint
& point
) const
69 // FIXME: does it make sense to map incoming points with the inverse of the
70 // absolute transform?
73 absoluteTransform().inverse().map(point
.x(), point
.y(), &localX
, &localY
);
74 return FloatPoint::narrowPrecision(localX
, localY
);
77 bool RenderPath::fillContains(const FloatPoint
& point
, bool requiresFill
) const
82 if (requiresFill
&& !SVGPaintServer::fillPaintServer(style(), this))
85 return m_path
.contains(point
, style()->svgStyle()->fillRule());
88 FloatRect
RenderPath::relativeBBox(bool includeStroke
) const
94 if (m_strokeBbox
.isEmpty())
95 /*m_strokeBbox = strokeBBox();*/
100 if (m_fillBBox
.isEmpty())
101 m_fillBBox
= m_path
.boundingRect();
106 void RenderPath::setPath(const Path
& newPath
)
109 m_strokeBbox
= FloatRect();
110 m_fillBBox
= FloatRect();
113 const Path
& RenderPath::path() const
118 bool RenderPath::calculateLocalTransform()
120 AffineTransform oldTransform
= m_localTransform
;
121 m_localTransform
= static_cast<SVGStyledTransformableElement
*>(element())->animatedLocalTransform();
122 return (m_localTransform
!= oldTransform
);
125 void RenderPath::layout()
128 IntRect oldOutlineBox
;
129 bool checkForRepaint
= /*checkForRepaintDuringLayout() && */selfNeedsLayout();
130 if (checkForRepaint
) {
131 oldBounds
= m_absoluteBounds
;
132 //oldOutlineBox = absoluteOutlineBox();
135 calculateLocalTransform();
137 setPath(static_cast<SVGStyledTransformableElement
*>(element())->toPathData());
139 m_absoluteBounds
= absoluteClippedOverflowRect();
141 setWidth(m_absoluteBounds
.width());
142 setHeight(m_absoluteBounds
.height());
144 /*if (checkForRepaint)
145 repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox);*/
147 setNeedsLayout(false);
150 IntRect
RenderPath::absoluteClippedOverflowRect()
152 return IntRect(0, 0, 100, 100);
153 /*FloatRect repaintRect = absoluteTransform().mapRect(relativeBBox(true));
155 // Markers can expand the bounding box
156 repaintRect.unite(m_markerBounds);
158 #if ENABLE(SVG_FILTERS)
159 // Filters can expand the bounding box
160 SVGResourceFilter* filter = getFilterById(document(), SVGURIReference::getTarget(style()->svgStyle()->filter()));
162 repaintRect.unite(filter->filterBBoxForItemBBox(repaintRect));
165 if (!repaintRect.isEmpty())
166 repaintRect.inflate(1); // inflate 1 pixel for antialiasing
168 return enclosingIntRect(repaintRect);*/
171 bool RenderPath::requiresLayer()
176 short RenderPath::lineHeight(bool b
, bool isRootLineBox
) const
178 return static_cast<short>(relativeBBox(true).height());
181 short RenderPath::baselinePosition(bool b
, bool isRootLineBox
) const
183 return static_cast<short>(relativeBBox(true).height());
186 static inline void fillAndStrokePath(const Path
& path
, QPainter
* painter
, RenderStyle
* style
, RenderPath
* object
)
188 /*context->beginPath();*/
190 SVGPaintServer
* fillPaintServer
= SVGPaintServer::fillPaintServer(style
, object
);
191 if (fillPaintServer
) {
192 /*context->addPath(path);*/
193 fillPaintServer
->draw(painter
, path
.platformPath(), object
, ApplyToFillTargetType
);
196 SVGPaintServer
* strokePaintServer
= SVGPaintServer::strokePaintServer(style
, object
);
197 if (strokePaintServer
) {
198 /*context->addPath(path); // path is cleared when filled.*/
199 strokePaintServer
->draw(painter
, path
.platformPath(), object
, ApplyToStrokeTargetType
);
203 void RenderPath::paint(PaintInfo
& paintInfo
, int, int)
206 paintInfo
.p
->setWorldMatrix(localTransform(), true);
207 SVGResourceFilter
* filter
= 0;
208 prepareToRenderSVGContent(this, paintInfo
, FloatRect(), filter
/*boundingBox, filter*/);
209 if (paintInfo
.phase
== PaintActionForeground
) {
210 fillAndStrokePath(m_path
, paintInfo
.p
, style(), this);
212 paintInfo
.p
->restore();
215 /*if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN || m_path.isEmpty())
218 //paintInfo.context->save();
219 /*paintInfo.context->concatCTM(localTransform());*/
221 //paintInfo.p->fillRect(0, 0, 50, 50, QBrush(Qt::yellow));
222 /*paintInfo.p->setPen(Qt::blue);
223 kDebug() << "path:" << *m_path.platformPath() << endl;
224 paintInfo.p->drawPath(*m_path.platformPath());*/
225 /*SVGResourceFilter* filter = 0;
227 FloatRect boundingBox = relativeBBox(true);
228 if (paintInfo.phase == PaintPhaseForeground) {
229 PaintInfo savedInfo(paintInfo);
231 prepareToRenderSVGContent(this, paintInfo, boundingBox, filter);
232 if (style()->svgStyle()->shapeRendering() == SR_CRISPEDGES)
233 paintInfo.context->setUseAntialiasing(false);
234 fillAndStrokePath(m_path, paintInfo.context, style(), this);
236 if (static_cast<SVGStyledElement*>(element())->supportsMarkers())
237 m_markerBounds = drawMarkersIfNeeded(paintInfo.context, paintInfo.rect, m_path);
239 finishRenderSVGContent(this, paintInfo, boundingBox, filter, savedInfo.context);
242 if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth())
243 paintOutline(paintInfo.context, static_cast<int>(boundingBox.x()), static_cast<int>(boundingBox.y()),
244 static_cast<int>(boundingBox.width()), static_cast<int>(boundingBox.height()), style());*/
246 //paintInfo.context->restore();
250 /*void RenderPath::addFocusRingRects(GraphicsContext* graphicsContext, int, int)
252 graphicsContext->addFocusRingRect(enclosingIntRect(relativeBBox(true)));
255 void RenderPath::absoluteRects(Vector
<IntRect
>& rects
, int, int, bool)
257 rects
.append(absoluteClippedOverflowRect());
260 /*bool RenderPath::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int _x, int _y, int, int, HitTestAction hitTestAction)
262 // We only draw in the forground phase, so we only hit-test then.
263 if (hitTestAction != HitTestForeground)
266 IntPoint absolutePoint(_x, _y);
268 PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_PATH_HITTESTING, style()->svgStyle()->pointerEvents());
270 bool isVisible = (style()->visibility() == VISIBLE);
271 if (isVisible || !hitRules.requireVisible) {
272 FloatPoint hitPoint = mapAbsolutePointToLocal(absolutePoint);
273 if ((hitRules.canHitStroke && (style()->svgStyle()->hasStroke() || !hitRules.requireStroke) && strokeContains(hitPoint, hitRules.requireStroke))
274 || (hitRules.canHitFill && (style()->svgStyle()->hasFill() || !hitRules.requireFill) && fillContains(hitPoint, hitRules.requireFill))) {
275 updateHitTestResult(result, absolutePoint);
291 FloatPoint subpathStart;
293 FloatPoint inslopePoints[2];
294 FloatPoint outslopePoints[2];
296 SVGResourceMarker* marker;
299 struct DrawMarkersData {
300 DrawMarkersData(GraphicsContext*, SVGResourceMarker* startMarker, SVGResourceMarker* midMarker, double strokeWidth);
301 GraphicsContext* context;
303 MarkerData previousMarkerData;
304 SVGResourceMarker* midMarker;
307 DrawMarkersData::DrawMarkersData(GraphicsContext* c, SVGResourceMarker *start, SVGResourceMarker *mid, double strokeWidth)
312 previousMarkerData.origin = FloatPoint();
313 previousMarkerData.subpathStart = FloatPoint();
314 previousMarkerData.strokeWidth = strokeWidth;
315 previousMarkerData.marker = start;
316 previousMarkerData.type = Start;
319 static void drawMarkerWithData(GraphicsContext* context, MarkerData &data)
324 FloatPoint inslopeChange = data.inslopePoints[1] - FloatSize(data.inslopePoints[0].x(), data.inslopePoints[0].y());
325 FloatPoint outslopeChange = data.outslopePoints[1] - FloatSize(data.outslopePoints[0].x(), data.outslopePoints[0].y());
327 double inslope = rad2deg(atan2(inslopeChange.y(), inslopeChange.x()));
328 double outslope = rad2deg(atan2(outslopeChange.y(), outslopeChange.x()));
336 angle = (inslope + outslope) / 2;
342 data.marker->draw(context, FloatRect(), data.origin.x(), data.origin.y(), data.strokeWidth, angle);
345 static inline void updateMarkerDataForElement(MarkerData& previousMarkerData, const PathElement* element)
347 FloatPoint* points = element->points;
349 switch (element->type) {
350 case PathElementAddQuadCurveToPoint:
352 previousMarkerData.origin = points[1];
354 case PathElementAddCurveToPoint:
355 previousMarkerData.inslopePoints[0] = points[1];
356 previousMarkerData.inslopePoints[1] = points[2];
357 previousMarkerData.origin = points[2];
359 case PathElementMoveToPoint:
360 previousMarkerData.subpathStart = points[0];
361 case PathElementAddLineToPoint:
362 previousMarkerData.inslopePoints[0] = previousMarkerData.origin;
363 previousMarkerData.inslopePoints[1] = points[0];
364 previousMarkerData.origin = points[0];
366 case PathElementCloseSubpath:
367 previousMarkerData.inslopePoints[0] = previousMarkerData.origin;
368 previousMarkerData.inslopePoints[1] = points[0];
369 previousMarkerData.origin = previousMarkerData.subpathStart;
370 previousMarkerData.subpathStart = FloatPoint();
374 static void drawStartAndMidMarkers(void* info, const PathElement* element)
376 DrawMarkersData& data = *reinterpret_cast<DrawMarkersData*>(info);
378 int elementIndex = data.elementIndex;
379 MarkerData& previousMarkerData = data.previousMarkerData;
381 FloatPoint* points = element->points;
383 // First update the outslope for the previous element
384 previousMarkerData.outslopePoints[0] = previousMarkerData.origin;
385 previousMarkerData.outslopePoints[1] = points[0];
387 // Draw the marker for the previous element
388 if (elementIndex != 0)
389 drawMarkerWithData(data.context, previousMarkerData);
391 // Update our marker data for this element
392 updateMarkerDataForElement(previousMarkerData, element);
394 if (elementIndex == 1) {
395 // After drawing the start marker, switch to drawing mid markers
396 previousMarkerData.marker = data.midMarker;
397 previousMarkerData.type = Mid;
403 FloatRect RenderPath::drawMarkersIfNeeded(GraphicsContext* context, const FloatRect& rect, const Path& path) const
405 Document* doc = document();
407 SVGElement* svgElement = static_cast<SVGElement*>(element());
408 ASSERT(svgElement && svgElement->document() && svgElement->isStyled());
410 SVGStyledElement* styledElement = static_cast<SVGStyledElement*>(svgElement);
411 const SVGRenderStyle* svgStyle = style()->svgStyle();
413 AtomicString startMarkerId(SVGURIReference::getTarget(svgStyle->startMarker()));
414 AtomicString midMarkerId(SVGURIReference::getTarget(svgStyle->midMarker()));
415 AtomicString endMarkerId(SVGURIReference::getTarget(svgStyle->endMarker()));
417 SVGResourceMarker* startMarker = getMarkerById(doc, startMarkerId);
418 SVGResourceMarker* midMarker = getMarkerById(doc, midMarkerId);
419 SVGResourceMarker* endMarker = getMarkerById(doc, endMarkerId);
421 if (!startMarker && !startMarkerId.isEmpty())
422 svgElement->document()->accessSVGExtensions()->addPendingResource(startMarkerId, styledElement);
423 else if (startMarker)
424 startMarker->addClient(styledElement);
426 if (!midMarker && !midMarkerId.isEmpty())
427 svgElement->document()->accessSVGExtensions()->addPendingResource(midMarkerId, styledElement);
429 midMarker->addClient(styledElement);
431 if (!endMarker && !endMarkerId.isEmpty())
432 svgElement->document()->accessSVGExtensions()->addPendingResource(endMarkerId, styledElement);
434 endMarker->addClient(styledElement);
436 if (!startMarker && !midMarker && !endMarker)
439 double strokeWidth = SVGRenderStyle::cssPrimitiveToLength(this, svgStyle->strokeWidth(), 1.0f);
440 DrawMarkersData data(context, startMarker, midMarker, strokeWidth);
442 path.apply(&data, drawStartAndMidMarkers);
444 data.previousMarkerData.marker = endMarker;
445 data.previousMarkerData.type = End;
446 drawMarkerWithData(context, data.previousMarkerData);
448 // We know the marker boundaries, only after they're drawn!
449 // Otherwhise we'd need to do all the marker calculation twice
450 // once here (through paint()) and once in absoluteClippedOverflowRect().
454 bounds.unite(startMarker->cachedBounds());
457 bounds.unite(midMarker->cachedBounds());
460 bounds.unite(endMarker->cachedBounds());
467 #endif // ENABLE(SVG)