File indexing completed on 2024-04-28 15:24:17

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)