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 551 of file SvgParser.cpp.

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

Function Documentation

◆ applyDashes()

void applyDashes ( const KoShapeStrokeSP srcStroke,
KoShapeStrokeSP dstStroke )

Definition at line 1187 of file SvgParser.cpp.

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

◆ bakeShapeOffset()

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

Definition at line 579 of file SvgParser.cpp.

580{
581 QTransform result =
582 patternTransform *
583 QTransform::fromTranslate(-shapeOffset.x(), -shapeOffset.y()) *
584 patternTransform.inverted();
585 KIS_ASSERT_RECOVER_NOOP(result.type() <= QTransform::TxTranslate);
586
587 return QPointF(result.dx(), result.dy());
588}
#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 1557 of file SvgParser.cpp.

1558{
1559 const QTransform shapeToOriginalUserCoordinates =
1560 shape->absoluteTransformation().inverted() *
1561 coordinateSystemOnLoading;
1562
1563 KIS_SAFE_ASSERT_RECOVER_NOOP(shapeToOriginalUserCoordinates.type() <= QTransform::TxTranslate);
1564 return QPointF(shapeToOriginalUserCoordinates.dx(), shapeToOriginalUserCoordinates.dy());
1565}
QTransform absoluteTransformation() const
Definition KoShape.cpp:335
#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 1002 of file SvgParser.cpp.

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

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