void ScreenPainter::drawGlyphOutline(const GlyphLayout gl, bool fill) { if (fill) drawGlyph(gl); m_painter->save(); bool fr = m_painter->fillRule(); m_painter->setFillRule(false); setupState(false); m_painter->translate(0, -(fontSize() * gl.scaleV)); FPointArray outline = font().glyphOutline(gl.glyph); double scaleHv = gl.scaleH * fontSize() / 10.0; double scaleVv = gl.scaleV * fontSize() / 10.0; QTransform trans; trans.scale(scaleHv, scaleVv); outline.map(trans); m_painter->setupPolygon(&outline, true); if (outline.size() > 3) { m_painter->setLineWidth(strokeWidth()); m_painter->strokePath(); } m_painter->setFillRule(fr); m_painter->restore(); }
QRectF QSvgEllipse::bounds(QPainter *p, QSvgExtraStates &) const { QPainterPath path; path.addEllipse(m_bounds); qreal sw = strokeWidth(p); return qFuzzyIsNull(sw) ? p->transform().map(path).boundingRect() : boundsOnStroke(p, path, sw); }
void LayoutSVGRect::updateStrokeAndFillBoundingBoxes() { SVGLengthContext lengthContext(toSVGRectElement(element())); FloatSize boundingBoxSize( lengthContext.valueForLength(styleRef().width(), styleRef(), SVGLengthMode::Width), lengthContext.valueForLength(styleRef().height(), styleRef(), SVGLengthMode::Height)); // Spec: "A negative value is an error." if (boundingBoxSize.width() < 0 || boundingBoxSize.height() < 0) { m_fillBoundingBox = FloatRect(); m_strokeBoundingBox = FloatRect(); return; } if (m_usePathFallback) { // Spec: "A value of zero disables rendering of the element." so we can skip // the path fallback and rely on the existing bounding box calculation. if (!boundingBoxSize.isEmpty()) { LayoutSVGShape::updateStrokeAndFillBoundingBoxes(); return; } m_usePathFallback = false; clearPath(); } m_fillBoundingBox = FloatRect( FloatPoint( lengthContext.valueForLength(styleRef().svgStyle().x(), styleRef(), SVGLengthMode::Width), lengthContext.valueForLength(styleRef().svgStyle().y(), styleRef(), SVGLengthMode::Height)), boundingBoxSize); m_strokeBoundingBox = m_fillBoundingBox; if (style()->svgStyle().hasStroke()) m_strokeBoundingBox.inflate(strokeWidth() / 2); }
void RenderSVGEllipse::updateShapeFromElement() { // Before creating a new object we need to clear the cached bounding box // to avoid using garbage. m_fillBoundingBox = FloatRect(); m_strokeBoundingBox = FloatRect(); m_center = FloatPoint(); m_radii = FloatSize(); calculateRadiiAndCenter(); // Spec: "A negative value is an error. A value of zero disables rendering of the element." if (m_radii.width() < 0 || m_radii.height() < 0) return; if (!m_radii.isEmpty()) { // Fallback to RenderSVGShape if shape has a non-scaling stroke. if (hasNonScalingStroke()) { RenderSVGShape::updateShapeFromElement(); m_usePathFallback = true; return; } m_usePathFallback = false; } m_fillBoundingBox = FloatRect(m_center.x() - m_radii.width(), m_center.y() - m_radii.height(), 2 * m_radii.width(), 2 * m_radii.height()); m_strokeBoundingBox = m_fillBoundingBox; if (style()->svgStyle()->hasStroke()) m_strokeBoundingBox.inflate(strokeWidth() / 2); }
FloatRect RenderSVGShape::calculateStrokeBoundingBox() const { ASSERT(m_path); FloatRect strokeBoundingBox = m_fillBoundingBox; const SVGRenderStyle& svgStyle = style().svgStyle(); if (svgStyle.hasStroke()) { BoundingRectStrokeStyleApplier strokeStyle(*this); if (hasNonScalingStroke()) { AffineTransform nonScalingTransform = nonScalingStrokeTransform(); if (Optional<AffineTransform> inverse = nonScalingTransform.inverse()) { Path* usePath = nonScalingStrokePath(m_path.get(), nonScalingTransform); FloatRect strokeBoundingRect = usePath->strokeBoundingRect(&strokeStyle); strokeBoundingRect = inverse.value().mapRect(strokeBoundingRect); strokeBoundingBox.unite(strokeBoundingRect); } } else strokeBoundingBox.unite(path().strokeBoundingRect(&strokeStyle)); } if (!m_markerPositions.isEmpty()) strokeBoundingBox.unite(markerRect(strokeWidth())); return strokeBoundingBox; }
void RenderSVGRect::strokeShape(GraphicsContext* context) const { if (!isPaintingFallback()) { context->strokeRect(m_boundingBox, strokeWidth()); return; } RenderSVGShape::strokeShape(context); }
LayoutRect LayoutSVGShape::clippedOverflowRectForPaintInvalidation( const LayoutBoxModelObject* paintInvalidationContainer, const PaintInvalidationState* paintInvalidationState) const { const float strokeWidthForHairlinePadding = style()->svgStyle().hasStroke() ? strokeWidth() : 0; return SVGLayoutSupport::clippedOverflowRectForPaintInvalidation(*this, paintInvalidationContainer, paintInvalidationState, strokeWidthForHairlinePadding); }
QRectF QSvgArc::bounds() const { qreal sw = strokeWidth(); if (qFuzzyCompare(sw + 1, 1)) return m_cachedBounds; else { return boundsOnStroke(cubic, sw); } }
void ScreenPainter::setupState(bool rect) { if (selected() && rect) { // we are drawing a selection rect QColor color = qApp->palette().color(QPalette::Active, QPalette::Highlight); m_painter->setBrush(color); m_painter->setPen(color, strokeWidth(), Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin); } else if (selected()) { // we are drawing selected text QColor color = qApp->palette().color(QPalette::Active, QPalette::HighlightedText); m_painter->setBrush(color); m_painter->setPen(color, strokeWidth(), Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin); } else { QColor tmp; if (m_fillColor != fillColor()) { m_item->SetQColor(&tmp, fillColor().color, fillColor().shade); m_fillQColor = tmp; m_fillColor = fillColor(); } if (m_strokeColor != strokeColor()) { m_item->SetQColor(&tmp, strokeColor().color, strokeColor().shade); m_fillStrokeQColor = tmp; m_strokeColor = strokeColor(); } m_painter->setBrush(m_fillQColor); m_painter->setPen(m_fillStrokeQColor, strokeWidth(), Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin); } m_painter->translate(x(), y()); if (scaleH() != 1.0 || scaleV() != 1.0) m_painter->scale(scaleH(), scaleV()); if (matrix() != QTransform()) { m_painter->setWorldMatrix(matrix() * m_painter->worldMatrix()); } }
QRectF QSvgCircle::bounds() const { qreal sw = strokeWidth(); if (qFuzzyCompare(sw + 1, 1)) return m_bounds; else { QPainterPath path; path.addRect(m_bounds); return boundsOnStroke(path, sw); } }
QRectF QSvgPolyline::bounds(QPainter *p, QSvgExtraStates &) const { qreal sw = strokeWidth(p); if (qFuzzyIsNull(sw)) { return p->transform().map(m_poly).boundingRect(); } else { QPainterPath path; path.addPolygon(m_poly); return boundsOnStroke(p, path, sw); } }
QRectF QSvgPolyline::bounds() const { qreal sw = strokeWidth(); if (qFuzzyCompare(sw + 1, 1)) return m_poly.boundingRect(); else { QPainterPath path; path.addPolygon(m_poly); return boundsOnStroke(path, sw); } }
QRectF QSvgRect::bounds(QPainter *p, QSvgExtraStates &) const { qreal sw = strokeWidth(p); if (qFuzzyIsNull(sw)) { return p->transform().mapRect(m_rect); } else { QPainterPath path; path.addRect(m_rect); return boundsOnStroke(p, path, sw); } }
void RenderSVGRect::strokeShape(GraphicsContext* context) const { if (!style()->svgStyle()->hasVisibleStroke()) return; if (m_usePathFallback) { RenderSVGShape::strokeShape(context); return; } context->strokeRect(m_fillBoundingBox, strokeWidth()); }
QRectF QSvgLine::bounds() const { qreal sw = strokeWidth(); if (qFuzzyCompare(sw + 1, 1)) { qreal minX = qMin(m_bounds.x1(), m_bounds.x2()); qreal minY = qMin(m_bounds.y1(), m_bounds.y2()); qreal maxX = qMax(m_bounds.x1(), m_bounds.x2()); qreal maxY = qMax(m_bounds.y1(), m_bounds.y2()); return QRectF(minX, minY, maxX-minX, maxY-minY); } else { QPainterPath path; path.moveTo(m_bounds.x1(), m_bounds.y1()); path.lineTo(m_bounds.x2(), m_bounds.y2()); return boundsOnStroke(path, sw); } }
bool LayoutSVGEllipse::shapeDependentStrokeContains(const FloatPoint& point) { // The optimized check below for circles does not support non-scaling or // discontinuous strokes. if (m_usePathFallback || !hasContinuousStroke() || m_radii.width() != m_radii.height()) { if (!hasPath()) createPath(); return LayoutSVGShape::shapeDependentStrokeContains(point); } const FloatPoint center = FloatPoint(m_center.x() - point.x(), m_center.y() - point.y()); const float halfStrokeWidth = strokeWidth() / 2; const float r = m_radii.width(); return std::abs(center.length() - r) <= halfStrokeWidth; }
QRectF QSvgLine::bounds(QPainter *p, QSvgExtraStates &) const { qreal sw = strokeWidth(p); if (qFuzzyIsNull(sw)) { QPointF p1 = p->transform().map(m_line.p1()); QPointF p2 = p->transform().map(m_line.p2()); qreal minX = qMin(p1.x(), p2.x()); qreal minY = qMin(p1.y(), p2.y()); qreal maxX = qMax(p1.x(), p2.x()); qreal maxY = qMax(p1.y(), p2.y()); return QRectF(minX, minY, maxX - minX, maxY - minY); } else { QPainterPath path; path.moveTo(m_line.p1()); path.lineTo(m_line.p2()); return boundsOnStroke(p, path, sw); } }
void LayoutSVGRect::updateShapeFromElement() { // Before creating a new object we need to clear the cached bounding box // to avoid using garbage. m_fillBoundingBox = FloatRect(); m_strokeBoundingBox = FloatRect(); m_usePathFallback = false; SVGRectElement* rect = toSVGRectElement(element()); ASSERT(rect); SVGLengthContext lengthContext(rect); FloatSize boundingBoxSize( lengthContext.valueForLength(styleRef().width(), styleRef(), SVGLengthMode::Width), lengthContext.valueForLength(styleRef().height(), styleRef(), SVGLengthMode::Height)); // Spec: "A negative value is an error." if (boundingBoxSize.width() < 0 || boundingBoxSize.height() < 0) return; // Spec: "A value of zero disables rendering of the element." if (!boundingBoxSize.isEmpty()) { // Fallback to LayoutSVGShape and path-based hit detection if the rect // has rounded corners or a non-scaling or non-simple stroke. if (lengthContext.valueForLength(styleRef().svgStyle().rx(), styleRef(), SVGLengthMode::Width) > 0 || lengthContext.valueForLength(styleRef().svgStyle().ry(), styleRef(), SVGLengthMode::Height) > 0 || hasNonScalingStroke() || !definitelyHasSimpleStroke()) { LayoutSVGShape::updateShapeFromElement(); m_usePathFallback = true; return; } } m_fillBoundingBox = FloatRect( FloatPoint( lengthContext.valueForLength(styleRef().svgStyle().x(), styleRef(), SVGLengthMode::Width), lengthContext.valueForLength(styleRef().svgStyle().y(), styleRef(), SVGLengthMode::Height)), boundingBoxSize); m_strokeBoundingBox = m_fillBoundingBox; if (style()->svgStyle().hasStroke()) m_strokeBoundingBox.inflate(strokeWidth() / 2); }
bool RenderSVGEllipse::shapeDependentStrokeContains(const FloatPoint& point) { // The optimized contains code below does not support non-smooth strokes so we need // to fall back to RenderSVGShape::shapeDependentStrokeContains in these cases. if (m_usePathFallback || !hasSmoothStroke()) { if (!hasPath()) RenderSVGShape::updateShapeFromElement(); return RenderSVGShape::shapeDependentStrokeContains(point); } float halfStrokeWidth = strokeWidth() / 2; FloatPoint center = FloatPoint(m_center.x() - point.x(), m_center.y() - point.y()); // This works by checking if the point satisfies the ellipse equation, // (x/rX)^2 + (y/rY)^2 <= 1, for the outer but not the inner stroke. float xrXOuter = center.x() / (m_radii.width() + halfStrokeWidth); float yrYOuter = center.y() / (m_radii.height() + halfStrokeWidth); if (xrXOuter * xrXOuter + yrYOuter * yrYOuter > 1.0) return false; float xrXInner = center.x() / (m_radii.width() - halfStrokeWidth); float yrYInner = center.y() / (m_radii.height() - halfStrokeWidth); return xrXInner * xrXInner + yrYInner * yrYInner >= 1.0; }
bool LayoutSVGRect::shapeDependentStrokeContains(const FloatPoint& point) { // The optimized code below does not support non-simple strokes so we need // to fall back to LayoutSVGShape::shapeDependentStrokeContains in these cases. if (m_usePathFallback || !definitelyHasSimpleStroke()) { if (!hasPath()) LayoutSVGShape::updateShapeFromElement(); return LayoutSVGShape::shapeDependentStrokeContains(point); } const float halfStrokeWidth = strokeWidth() / 2; const float halfWidth = m_fillBoundingBox.width() / 2; const float halfHeight = m_fillBoundingBox.height() / 2; const FloatPoint fillBoundingBoxCenter = FloatPoint(m_fillBoundingBox.x() + halfWidth, m_fillBoundingBox.y() + halfHeight); const float absDeltaX = std::abs(point.x() - fillBoundingBoxCenter.x()); const float absDeltaY = std::abs(point.y() - fillBoundingBoxCenter.y()); if (!(absDeltaX <= halfWidth + halfStrokeWidth && absDeltaY <= halfHeight + halfStrokeWidth)) return false; return (halfWidth - halfStrokeWidth <= absDeltaX) || (halfHeight - halfStrokeWidth <= absDeltaY); }
QRectF QSvgArc::bounds(QPainter *p, QSvgExtraStates &) const { qreal sw = strokeWidth(p); return qFuzzyIsNull(sw) ? p->transform().map(m_path).boundingRect() : boundsOnStroke(p, m_path, sw); }