fix logic
[personal-kdelibs.git] / khtml / rendering / RenderPath.cpp
blobb2ce2e4b8a18503c75037425c7db0f5914b28fb8
1 /*
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.
24 #include "config.h"
25 #include "wtf/Platform.h"
26 #include "Document.h"
28 #if ENABLE(SVG)
29 #include "RenderPath.h"
31 #include <math.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>
48 namespace WebCore {
50 // RenderPath
51 RenderPath::RenderPath(RenderStyle* style, SVGStyledTransformableElement* node)
52 : RenderObject(node)
54 ASSERT(style != 0);
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?
71 double localX;
72 double localY;
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
79 if (m_path.isEmpty())
80 return false;
82 if (requiresFill && !SVGPaintServer::fillPaintServer(style(), this))
83 return false;
85 return m_path.contains(point, style()->svgStyle()->fillRule());
88 FloatRect RenderPath::relativeBBox(bool includeStroke) const
90 if (m_path.isEmpty())
91 return FloatRect();
93 if (includeStroke) {
94 if (m_strokeBbox.isEmpty())
95 /*m_strokeBbox = strokeBBox();*/
97 return m_strokeBbox;
100 if (m_fillBBox.isEmpty())
101 m_fillBBox = m_path.boundingRect();
103 return m_fillBBox;
106 void RenderPath::setPath(const Path& newPath)
108 m_path = newPath;
109 m_strokeBbox = FloatRect();
110 m_fillBBox = FloatRect();
113 const Path& RenderPath::path() const
115 return m_path;
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()
127 IntRect oldBounds;
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()));
161 if (filter)
162 repaintRect.unite(filter->filterBBoxForItemBBox(repaintRect));
163 #endif
165 if (!repaintRect.isEmpty())
166 repaintRect.inflate(1); // inflate 1 pixel for antialiasing
168 return enclosingIntRect(repaintRect);*/
171 bool RenderPath::requiresLayer()
173 return false;
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)
205 paintInfo.p->save();
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();
214 #if 0
215 /*if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN || m_path.isEmpty())
216 return;*/
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();
247 #endif
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)
264 return false;
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);
276 return true;
280 return false;
283 /*enum MarkerType {
284 Start,
285 Mid,
289 struct MarkerData {
290 FloatPoint origin;
291 FloatPoint subpathStart;
292 double strokeWidth;
293 FloatPoint inslopePoints[2];
294 FloatPoint outslopePoints[2];
295 MarkerType type;
296 SVGResourceMarker* marker;
299 struct DrawMarkersData {
300 DrawMarkersData(GraphicsContext*, SVGResourceMarker* startMarker, SVGResourceMarker* midMarker, double strokeWidth);
301 GraphicsContext* context;
302 int elementIndex;
303 MarkerData previousMarkerData;
304 SVGResourceMarker* midMarker;
307 DrawMarkersData::DrawMarkersData(GraphicsContext* c, SVGResourceMarker *start, SVGResourceMarker *mid, double strokeWidth)
308 : context(c)
309 , elementIndex(0)
310 , midMarker(mid)
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)
321 if (!data.marker)
322 return;
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()));
330 double angle = 0.0;
331 switch (data.type) {
332 case Start:
333 angle = outslope;
334 break;
335 case Mid:
336 angle = (inslope + outslope) / 2;
337 break;
338 case End:
339 angle = inslope;
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:
351 // TODO
352 previousMarkerData.origin = points[1];
353 break;
354 case PathElementAddCurveToPoint:
355 previousMarkerData.inslopePoints[0] = points[1];
356 previousMarkerData.inslopePoints[1] = points[2];
357 previousMarkerData.origin = points[2];
358 break;
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];
365 break;
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;
400 data.elementIndex++;
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);
428 else if (midMarker)
429 midMarker->addClient(styledElement);
431 if (!endMarker && !endMarkerId.isEmpty())
432 svgElement->document()->accessSVGExtensions()->addPendingResource(endMarkerId, styledElement);
433 else if (endMarker)
434 endMarker->addClient(styledElement);
436 if (!startMarker && !midMarker && !endMarker)
437 return FloatRect();
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().
451 FloatRect bounds;
453 if (startMarker)
454 bounds.unite(startMarker->cachedBounds());
456 if (midMarker)
457 bounds.unite(midMarker->cachedBounds());
459 if (endMarker)
460 bounds.unite(endMarker->cachedBounds());
462 return bounds;
467 #endif // ENABLE(SVG)