Krita Source Code Documentation
Loading...
Searching...
No Matches
SvgParser.cpp File Reference
#include "SvgParser.h"
#include <cmath>
#include <FlakeDebug.h>
#include <QColor>
#include <QDir>
#include <QPainter>
#include <QPainterPath>
#include <QRandomGenerator>
#include <KoShape.h>
#include <KoShapeRegistry.h>
#include <KoShapeFactoryBase.h>
#include <KoShapeGroup.h>
#include <KoPathShape.h>
#include <KoDocumentResourceManager.h>
#include <KoPathShapeLoader.h>
#include <commands/KoShapeGroupCommand.h>
#include <commands/KoShapeUngroupCommand.h>
#include <KoColorBackground.h>
#include <KoGradientBackground.h>
#include <KoMeshGradientBackground.h>
#include <KoPatternBackground.h>
#include <KoClipPath.h>
#include <KoClipMask.h>
#include <KoXmlNS.h>
#include "SvgMeshGradient.h"
#include "SvgMeshPatch.h"
#include "SvgUtil.h"
#include "SvgShape.h"
#include "SvgGraphicContext.h"
#include "SvgGradientHelper.h"
#include "SvgClipPathHelper.h"
#include "parsers/SvgTransformParser.h"
#include "kis_pointer_utils.h"
#include <KoVectorPatternBackground.h>
#include <KoMarker.h>
#include <text/KoSvgTextShape.h>
#include <text/KoSvgTextLoader.h>
#include "kis_dom_utils.h"
#include "kis_algebra_2d.h"
#include "kis_debug.h"
#include "kis_global.h"
#include <QXmlStreamReader>
#include <algorithm>

Go to the source code of this file.

Classes

struct  SvgParser::DeferredUseStore
 
struct  SvgParser::DeferredUseStore::El
 

Macros

#define forEachElement(elem, parent)
 

Functions

void applyDashes (const KoShapeStrokeSP srcStroke, KoShapeStrokeSP dstStroke)
 
QPointF bakeShapeOffset (const QTransform &patternTransform, const QPointF &shapeOffset)
 
QPointF extraShapeOffset (const KoShape *shape, const QTransform coordinateSystemOnLoading)
 
QGradient * prepareGradientForShape (const SvgGradientHelper *gradient, const KoShape *shape, const SvgGraphicsContext *gc, QTransform *transform)
 
SvgMeshGradientprepareMeshGradientForShape (SvgGradientHelper *gradient, const KoShape *shape, const SvgGraphicsContext *gc)
 

Macro Definition Documentation

◆ forEachElement

#define forEachElement ( elem,
parent )
Value:
for ( QDomNode _node = parent.firstChild(); !_node.isNull(); _node = _node.nextSibling() ) \
if ( ( elem = _node.toElement() ).isNull() ) {} else

Definition at line 550 of file SvgParser.cpp.

550#define forEachElement( elem, parent ) \
551 for ( QDomNode _node = parent.firstChild(); !_node.isNull(); _node = _node.nextSibling() ) \
552 if ( ( elem = _node.toElement() ).isNull() ) {} else

Function Documentation

◆ applyDashes()

void applyDashes ( const KoShapeStrokeSP srcStroke,
KoShapeStrokeSP dstStroke )

Definition at line 1186 of file SvgParser.cpp.

1187{
1188 const double lineWidth = srcStroke->lineWidth();
1189 QVector<qreal> dashes = srcStroke->lineDashes();
1190
1191 // apply line width to dashes and dash offset
1192 if (dashes.count() && lineWidth > 0.0) {
1193 const double dashOffset = srcStroke->dashOffset();
1194 QVector<qreal> dashes = srcStroke->lineDashes();
1195
1196 for (int i = 0; i < dashes.count(); ++i) {
1197 dashes[i] /= lineWidth;
1198 }
1199
1200 dstStroke->setLineStyle(Qt::CustomDashLine, dashes);
1201 dstStroke->setDashOffset(dashOffset / lineWidth);
1202 } else {
1203 dstStroke->setLineStyle(Qt::SolidLine, QVector<qreal>());
1204 }
1205}

◆ bakeShapeOffset()

QPointF bakeShapeOffset ( const QTransform & patternTransform,
const QPointF & shapeOffset )
inline

Definition at line 578 of file SvgParser.cpp.

579{
580 QTransform result =
581 patternTransform *
582 QTransform::fromTranslate(-shapeOffset.x(), -shapeOffset.y()) *
583 patternTransform.inverted();
584 KIS_ASSERT_RECOVER_NOOP(result.type() <= QTransform::TxTranslate);
585
586 return QPointF(result.dx(), result.dy());
587}
#define KIS_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:97

References KIS_ASSERT_RECOVER_NOOP.

◆ extraShapeOffset()

QPointF extraShapeOffset ( const KoShape * shape,
const QTransform coordinateSystemOnLoading )
inline

Definition at line 1556 of file SvgParser.cpp.

1557{
1558 const QTransform shapeToOriginalUserCoordinates =
1559 shape->absoluteTransformation().inverted() *
1560 coordinateSystemOnLoading;
1561
1562 KIS_SAFE_ASSERT_RECOVER_NOOP(shapeToOriginalUserCoordinates.type() <= QTransform::TxTranslate);
1563 return QPointF(shapeToOriginalUserCoordinates.dx(), shapeToOriginalUserCoordinates.dy());
1564}
QTransform absoluteTransformation() const
Definition KoShape.cpp:330
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130

References KoShape::absoluteTransformation(), and KIS_SAFE_ASSERT_RECOVER_NOOP.

◆ prepareGradientForShape()

QGradient * prepareGradientForShape ( const SvgGradientHelper * gradient,
const KoShape * shape,
const SvgGraphicsContext * gc,
QTransform * transform )

Create a converted gradient that looks the same, but linked to the bounding rect of the shape, so it would be transformed with the shape

If shape outline rect is valid, convert the gradient into OBB mode by doing some magic conversions: we compensate non-uniform size of the shape by applying an additional pre-transform

Definition at line 1001 of file SvgParser.cpp.

1005{
1006 QGradient *resultGradient = 0;
1007 KIS_ASSERT(transform);
1008
1009 if (gradient->gradientUnits() == KoFlake::ObjectBoundingBox) {
1010 resultGradient = KoFlake::cloneGradient(gradient->gradient());
1011 *transform = gradient->transform();
1012 } else {
1013 if (gradient->gradient()->type() == QGradient::LinearGradient) {
1019 const QRectF boundingRect = shape->outline().boundingRect();
1020 const QTransform relativeToShape(boundingRect.width(), 0, 0, boundingRect.height(),
1021 boundingRect.x(), boundingRect.y());
1022
1023 const QTransform relativeToUser =
1024 relativeToShape * shape->transformation() * gc->matrix.inverted();
1025
1026 const QTransform userToRelative = relativeToUser.inverted();
1027
1028 const QLinearGradient *o = static_cast<const QLinearGradient*>(gradient->gradient());
1029 QLinearGradient *g = new QLinearGradient();
1030 g->setStart(userToRelative.map(o->start()));
1031 g->setFinalStop(userToRelative.map(o->finalStop()));
1032 g->setCoordinateMode(QGradient::ObjectBoundingMode);
1033 g->setStops(o->stops());
1034 g->setSpread(o->spread());
1035
1036 resultGradient = g;
1037 *transform = relativeToUser * gradient->transform() * userToRelative;
1038
1039 } else if (gradient->gradient()->type() == QGradient::RadialGradient) {
1040 // For radial and conical gradients such conversion is not possible
1041
1042 resultGradient = KoFlake::cloneGradient(gradient->gradient());
1043 *transform = gradient->transform() * gc->matrix * shape->transformation().inverted();
1044
1045 const QRectF outlineRect = shape->outlineRect();
1046 if (outlineRect.isEmpty()) return resultGradient;
1047
1054 QRadialGradient *rgradient = static_cast<QRadialGradient*>(resultGradient);
1055
1056 const qreal maxDimension = KisAlgebra2D::maxDimension(outlineRect);
1057 const QRectF uniformSize(outlineRect.topLeft(), QSizeF(maxDimension, maxDimension));
1058
1059 const QTransform uniformizeTransform =
1060 QTransform::fromTranslate(-outlineRect.x(), -outlineRect.y()) *
1061 QTransform::fromScale(maxDimension / shape->outlineRect().width(),
1062 maxDimension / shape->outlineRect().height()) *
1063 QTransform::fromTranslate(outlineRect.x(), outlineRect.y());
1064
1065 const QPointF centerLocal = transform->map(rgradient->center());
1066 const QPointF focalLocal = transform->map(rgradient->focalPoint());
1067
1068 const QPointF centerOBB = KisAlgebra2D::absoluteToRelative(centerLocal, uniformSize);
1069 const QPointF focalOBB = KisAlgebra2D::absoluteToRelative(focalLocal, uniformSize);
1070
1071 rgradient->setCenter(centerOBB);
1072 rgradient->setFocalPoint(focalOBB);
1073
1074 const qreal centerRadiusOBB = KisAlgebra2D::absoluteToRelative(rgradient->centerRadius(), uniformSize);
1075 const qreal focalRadiusOBB = KisAlgebra2D::absoluteToRelative(rgradient->focalRadius(), uniformSize);
1076
1077 rgradient->setCenterRadius(centerRadiusOBB);
1078 rgradient->setFocalRadius(focalRadiusOBB);
1079
1080 rgradient->setCoordinateMode(QGradient::ObjectBoundingMode);
1081
1082 // Warning: should it really be pre-multiplication?
1083 *transform = uniformizeTransform * gradient->transform();
1084 }
1085 }
1086
1087 // TODO: all gradients in Krita are rendered in a premultiplied-alpha
1088 // mode, which is against SVG standard. We need to fix that. Though
1089 // it requires deepeer changes, than just mere setting of the
1090 // QGradient's interpolation mode on loading.
1091 // resultGradient->setInterpolationMode(QGradient::ComponentInterpolation);
1092
1093 return resultGradient;
1094}
virtual QRectF outlineRect() const
Definition KoShape.cpp:561
virtual QPainterPath outline() const
Definition KoShape.cpp:554
QTransform transformation() const
Returns the shapes local transformation matrix.
Definition KoShape.cpp:378
QGradient * gradient() const
Returns the gradient.
KoFlake::CoordinateSystem gradientUnits() const
Returns gradient units type.
QTransform transform() const
Returns the gradient transformation.
QTransform matrix
the current transformation matrix
#define KIS_ASSERT(cond)
Definition kis_assert.h:33
auto maxDimension(Size size) -> decltype(size.width())
QPointF absoluteToRelative(const QPointF &pt, const QRectF &rc)
KRITAFLAKE_EXPORT QGradient * cloneGradient(const QGradient *gradient)
clones the given gradient
Definition KoFlake.cpp:17

References KisAlgebra2D::absoluteToRelative(), KoFlake::cloneGradient(), SvgGradientHelper::gradient(), SvgGradientHelper::gradientUnits(), KIS_ASSERT, SvgGraphicsContext::matrix, KisAlgebra2D::maxDimension(), KoFlake::ObjectBoundingBox, KoShape::outline(), KoShape::outlineRect(), SvgGradientHelper::transform(), and KoShape::transformation().

◆ prepareMeshGradientForShape()

SvgMeshGradient * prepareMeshGradientForShape ( SvgGradientHelper * gradient,
const KoShape * shape,
const SvgGraphicsContext * gc )

Definition at line 1096 of file SvgParser.cpp.

1098 {
1099
1100 SvgMeshGradient *resultGradient = nullptr;
1101
1102 if (gradient->gradientUnits() == KoFlake::ObjectBoundingBox) {
1103
1104 resultGradient = new SvgMeshGradient(*gradient->meshgradient());
1105
1106 const QRectF boundingRect = shape->outline().boundingRect();
1107 const QTransform relativeToShape(boundingRect.width(), 0, 0, boundingRect.height(),
1108 boundingRect.x(), boundingRect.y());
1109
1110 // NOTE: we apply translation right away, because caching hasn't been implemented for rendering, yet.
1111 // So, transform is called multiple times on the mesh and that's not nice
1112 resultGradient->setTransform(gradient->transform() * relativeToShape);
1113 } else {
1114 // NOTE: Krita's shapes use their own coordinate system. Where origin is at the top left
1115 // of the SHAPE. All the mesh patches will be rendered in the global 'user' coordinate system
1116 // where the origin is at the top left of the LAYER/DOCUMENT.
1117
1118 // Get the user coordinates of the shape
1119 const QTransform shapeglobal = shape->absoluteTransformation() * gc->matrix.inverted();
1120
1121 // Get the translation offset to shift the origin from "Shape" to "User"
1122 const QTransform translationOffset = QTransform::fromTranslate(-shapeglobal.dx(), -shapeglobal.dy());
1123
1124 resultGradient = new SvgMeshGradient(*gradient->meshgradient());
1125
1126 // NOTE: we apply translation right away, because caching hasn't been implemented for rendering, yet.
1127 // So, transform is called multiple times on the mesh and that's not nice
1128 resultGradient->setTransform(gradient->transform() * translationOffset);
1129 }
1130
1131 return resultGradient;
1132}
QScopedPointer< SvgMeshGradient > & meshgradient()
Returns the meshgradient.
void setTransform(const QTransform &matrix)

References KoShape::absoluteTransformation(), SvgGradientHelper::gradientUnits(), SvgGraphicsContext::matrix, SvgGradientHelper::meshgradient(), KoFlake::ObjectBoundingBox, KoShape::outline(), SvgMeshGradient::setTransform(), and SvgGradientHelper::transform().