2 Copyright (C) 2004, 2005 Nikolas Zimmermann <wildfox@kde.org>
3 2004, 2005 Rob Buis <buis@kde.org>
4 2005 Eric Seidel <eric.seidel@kdemail.net>
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., 59 Temple Place - Suite 330,
21 Boston, MA 02111-1307, USA.
26 #include "RenderPath.h"
30 #include "GraphicsContext.h"
31 #include "RenderSVGContainer.h"
32 #include "KCanvasClipper.h"
33 #include "KCanvasMasker.h"
34 #include "KCanvasMarker.h"
35 #include "KRenderingDevice.h"
36 #include "KRenderingFillPainter.h"
37 #include "KRenderingStrokePainter.h"
38 #include "SVGStyledElement.h"
39 #include <wtf/OwnPtr.h>
44 RenderPath::RenderPath(RenderStyle* style, SVGStyledElement* node)
50 RenderPath::~RenderPath()
54 AffineTransform RenderPath::localTransform() const
59 void RenderPath::setLocalTransform(const AffineTransform &matrix)
65 FloatPoint RenderPath::mapAbsolutePointToLocal(const FloatPoint& point) const
67 // FIXME: does it make sense to map incoming points with the inverse of the
68 // absolute transform?
71 absoluteTransform().invert().map(point.x(), point.y(), &localX, &localY);
72 return FloatPoint(localX, localY);
75 bool RenderPath::fillContains(const FloatPoint& point, bool requiresFill) const
80 if (requiresFill && !KSVGPainterFactory::fillPaintServer(style(), this))
83 return path().contains(mapAbsolutePointToLocal(point),
84 KSVGPainterFactory::fillPainter(style(), this).fillRule());
87 FloatRect RenderPath::relativeBBox(bool includeStroke) const
93 if (m_strokeBbox.isEmpty())
94 m_strokeBbox = strokeBBox();
98 if (m_fillBBox.isEmpty())
99 m_fillBBox = path().boundingRect();
103 void RenderPath::setPath(const Path& newPath)
106 m_strokeBbox = FloatRect();
107 m_fillBBox = FloatRect();
110 const Path& RenderPath::path() const
115 void RenderPath::layout()
117 // FIXME: Currently the DOM does all of the % length calculations, so we
118 // pretend that one of the attributes of the element has changed on the DOM
119 // to force the DOM object to update this render object with new aboslute position values.
122 bool checkForRepaint = checkForRepaintDuringLayout();
123 if (selfNeedsLayout() && checkForRepaint)
124 oldBounds = m_absoluteBounds;
126 static_cast<SVGStyledElement*>(element())->notifyAttributeChange();
128 m_absoluteBounds = getAbsoluteRepaintRect();
130 setWidth(m_absoluteBounds.width());
131 setHeight(m_absoluteBounds.height());
133 if (selfNeedsLayout() && checkForRepaint)
134 repaintAfterLayoutIfNeeded(oldBounds, oldBounds);
136 setNeedsLayout(false);
139 IntRect RenderPath::getAbsoluteRepaintRect()
141 FloatRect repaintRect = absoluteTransform().mapRect(relativeBBox(true));
143 // Filters can expand the bounding box
144 KCanvasFilter* filter = getFilterById(document(), style()->svgStyle()->filter().substring(1));
146 repaintRect.unite(filter->filterBBoxForItemBBox(repaintRect));
148 if (!repaintRect.isEmpty())
149 repaintRect.inflate(1); // inflate 1 pixel for antialiasing
150 return enclosingIntRect(repaintRect);
153 bool RenderPath::requiresLayer()
158 short RenderPath::lineHeight(bool b, bool isRootLineBox) const
160 return static_cast<short>(relativeBBox(true).height());
163 short RenderPath::baselinePosition(bool b, bool isRootLineBox) const
165 return static_cast<short>(relativeBBox(true).height());
168 void RenderPath::paint(PaintInfo &paintInfo, int parentX, int parentY)
170 // No one should be transforming us via these.
171 //ASSERT(parentX == 0);
172 //ASSERT(parentY == 0);
174 if (paintInfo.p->paintingDisabled() || (paintInfo.phase != PaintPhaseForeground) || style()->visibility() == HIDDEN || path().isEmpty())
177 KRenderingDevice* device = renderingDevice();
178 KRenderingDeviceContext* context = device->currentContext();
179 bool shouldPopContext = false;
183 // Need to set up KCanvas rendering if it hasn't already been done.
184 context = paintInfo.p->createRenderingDeviceContext();
185 device->pushContext(context);
186 shouldPopContext = true;
189 context->concatCTM(localTransform());
191 // setup to apply filters
192 KCanvasFilter* filter = getFilterById(document(), style()->svgStyle()->filter().substring(1));
194 filter->prepareFilter(relativeBBox(true));
195 context = device->currentContext();
198 if (KCanvasClipper* clipper = getClipperById(document(), style()->svgStyle()->clipPath().substring(1)))
199 clipper->applyClip(relativeBBox(true));
201 if (KCanvasMasker* masker = getMaskerById(document(), style()->svgStyle()->maskElement().substring(1)))
202 masker->applyMask(relativeBBox(true));
204 context->clearPath();
206 KRenderingPaintServer *fillPaintServer = KSVGPainterFactory::fillPaintServer(style(), this);
207 if (fillPaintServer) {
208 context->addPath(path());
209 fillPaintServer->setActiveClient(this);
210 fillPaintServer->draw(context, this, APPLY_TO_FILL);
212 KRenderingPaintServer *strokePaintServer = KSVGPainterFactory::strokePaintServer(style(), this);
213 if (strokePaintServer) {
214 context->addPath(path()); // path is cleared when filled.
215 strokePaintServer->setActiveClient(this);
216 strokePaintServer->draw(context, this, APPLY_TO_STROKE);
219 drawMarkersIfNeeded(paintInfo.p, paintInfo.r, path());
221 // actually apply the filter
223 filter->applyFilter(relativeBBox(true));
225 // restore drawing state
226 if (!shouldPopContext)
227 paintInfo.p->restore();
229 device->popContext();
234 void RenderPath::absoluteRects(Vector<IntRect>& rects, int _tx, int _ty)
236 rects.append(getAbsoluteRepaintRect());
239 RenderPath::PointerEventsHitRules RenderPath::pointerEventsHitRules()
241 PointerEventsHitRules hitRules;
243 switch (style()->svgStyle()->pointerEvents())
245 case PE_VISIBLE_PAINTED:
246 hitRules.requireVisible = true;
247 hitRules.requireFill = true;
248 hitRules.requireStroke = true;
249 hitRules.canHitFill = true;
250 hitRules.canHitStroke = true;
252 case PE_VISIBLE_FILL:
253 hitRules.requireVisible = true;
254 hitRules.canHitFill = true;
256 case PE_VISIBLE_STROKE:
257 hitRules.requireVisible = true;
258 hitRules.canHitStroke = true;
261 hitRules.requireVisible = true;
262 hitRules.canHitFill = true;
263 hitRules.canHitStroke = true;
266 hitRules.requireFill = true;
267 hitRules.requireStroke = true;
268 hitRules.canHitFill = true;
269 hitRules.canHitStroke = true;
272 hitRules.canHitFill = true;
275 hitRules.canHitStroke = true;
278 hitRules.canHitFill = true;
279 hitRules.canHitStroke = true;
282 // nothing to do here, defaults are all false.
288 bool RenderPath::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction)
290 // We only draw in the forground phase, so we only hit-test then.
291 if (hitTestAction != HitTestForeground)
293 PointerEventsHitRules hitRules = pointerEventsHitRules();
295 bool isVisible = (style()->visibility() == VISIBLE);
296 if (isVisible || !hitRules.requireVisible) {
297 FloatPoint hitPoint(_x,_y);
298 if ((hitRules.canHitStroke && (KSVGPainterFactory::isStroked(style()) || !hitRules.requireStroke) && strokeContains(hitPoint, hitRules.requireStroke))
299 || (hitRules.canHitFill && (KSVGPainterFactory::isFilled(style()) || !hitRules.requireFill) && fillContains(hitPoint, hitRules.requireFill))) {
315 FloatPoint subpathStart;
317 FloatPoint inslopePoints[2];
318 FloatPoint outslopePoints[2];
320 KCanvasMarker *marker;
323 struct DrawMarkersData {
324 DrawMarkersData(GraphicsContext*, KCanvasMarker* startMarker, KCanvasMarker* midMarker, double strokeWidth);
325 GraphicsContext* context;
327 MarkerData previousMarkerData;
328 KCanvasMarker* midMarker;
331 DrawMarkersData::DrawMarkersData(GraphicsContext* c, KCanvasMarker *start, KCanvasMarker *mid, double strokeWidth)
336 previousMarkerData.origin = FloatPoint();
337 previousMarkerData.subpathStart = FloatPoint();
338 previousMarkerData.strokeWidth = strokeWidth;
339 previousMarkerData.marker = start;
340 previousMarkerData.type = Start;
343 static void drawMarkerWithData(GraphicsContext* context, MarkerData &data)
348 FloatPoint inslopeChange = data.inslopePoints[1] - FloatSize(data.inslopePoints[0].x(), data.inslopePoints[0].y());
349 FloatPoint outslopeChange = data.outslopePoints[1] - FloatSize(data.outslopePoints[0].x(), data.outslopePoints[0].y());
351 static const double deg2rad = M_PI/180.0;
352 double inslope = atan2(inslopeChange.y(), inslopeChange.x()) / deg2rad;
353 double outslope = atan2(outslopeChange.y(), outslopeChange.x()) / deg2rad;
361 angle = (inslope + outslope) / 2;
367 data.marker->draw(context, FloatRect(), data.origin.x(), data.origin.y(), data.strokeWidth, angle);
370 static inline void updateMarkerDataForElement(MarkerData &previousMarkerData, const PathElement *element)
372 FloatPoint *points = element->points;
374 switch (element->type) {
375 case PathElementAddQuadCurveToPoint:
377 previousMarkerData.origin = points[1];
379 case PathElementAddCurveToPoint:
380 previousMarkerData.inslopePoints[0] = points[1];
381 previousMarkerData.inslopePoints[1] = points[2];
382 previousMarkerData.origin = points[2];
384 case PathElementMoveToPoint:
385 previousMarkerData.subpathStart = points[0];
386 case PathElementAddLineToPoint:
387 previousMarkerData.inslopePoints[0] = previousMarkerData.origin;
388 previousMarkerData.inslopePoints[1] = points[0];
389 previousMarkerData.origin = points[0];
391 case PathElementCloseSubpath:
392 previousMarkerData.inslopePoints[0] = previousMarkerData.origin;
393 previousMarkerData.inslopePoints[1] = points[0];
394 previousMarkerData.origin = previousMarkerData.subpathStart;
395 previousMarkerData.subpathStart = FloatPoint();
399 static void drawStartAndMidMarkers(void *info, const PathElement *element)
401 DrawMarkersData& data = *reinterpret_cast<DrawMarkersData*>(info);
403 int elementIndex = data.elementIndex;
404 MarkerData &previousMarkerData = data.previousMarkerData;
406 FloatPoint *points = element->points;
408 // First update the outslope for the previous element
409 previousMarkerData.outslopePoints[0] = previousMarkerData.origin;
410 previousMarkerData.outslopePoints[1] = points[0];
412 // Draw the marker for the previous element
413 if (elementIndex != 0)
414 drawMarkerWithData(data.context, previousMarkerData);
416 // Update our marker data for this element
417 updateMarkerDataForElement(previousMarkerData, element);
419 if (elementIndex == 1) {
420 // After drawing the start marker, switch to drawing mid markers
421 previousMarkerData.marker = data.midMarker;
422 previousMarkerData.type = Mid;
428 void RenderPath::drawMarkersIfNeeded(GraphicsContext* context, const FloatRect& rect, const Path& path) const
430 Document *doc = document();
431 const SVGRenderStyle* svgStyle = style()->svgStyle();
433 KCanvasMarker* startMarker = getMarkerById(doc, svgStyle->startMarker().substring(1));
434 KCanvasMarker* midMarker = getMarkerById(doc, svgStyle->midMarker().substring(1));
435 KCanvasMarker* endMarker = getMarkerById(doc, svgStyle->endMarker().substring(1));
437 if (!startMarker && !midMarker && !endMarker)
440 double strokeWidth = KSVGPainterFactory::cssPrimitiveToLength(this, style()->svgStyle()->strokeWidth(), 1.0);
442 DrawMarkersData data(context, startMarker, midMarker, strokeWidth);
444 path.apply(&data, drawStartAndMidMarkers);
446 data.previousMarkerData.marker = endMarker;
447 data.previousMarkerData.type = End;
448 drawMarkerWithData(context, data.previousMarkerData);
451 bool RenderPath::hasPercentageValues() const
453 return static_cast<SVGStyledElement*>(element())->hasPercentageValues();
458 #endif // SVG_SUPPORT