18#include <QDomDocument>
22#include <klocalizedstring.h>
50 , m_stops(rhs.m_stops)
51 , m_start(rhs.m_start)
53 , m_focalPoint(rhs.m_focalPoint)
76 Q_UNUSED(resourcesInterface);
90 case QGradient::LinearGradient: {
94 case QGradient::RadialGradient: {
96 qreal radius = sqrt(diff.x() * diff.x() + diff.y() * diff.y());
100 case QGradient::ConicalGradient: {
104 gradient =
new QConicalGradient(
m_start, angle);
112 i->color.toQColor(&color);
113 gradient->setColorAt(i->position, color);
116 gradient->setCoordinateMode(QGradient::ObjectBoundingMode);
117 gradient->setSpread(this->
spread());
138 }
else if (t >=
m_stops.last().position) {
148 leftStop = *(it - 1);
157 if (!
stopsAt(leftStop, rightStop, t))
return;
165 const std::array<quint8 *, 2> colors = {{startDummy.
data(), endDummy.
data()}};
169 if (stopDistance < DBL_EPSILON) {
172 localT = (t - leftStop.
position) / stopDistance;
174 std::array<qint16, 2> colorWeights {};
175 colorWeights[0] = std::lround((1.0 - localT) *
qint16_MAX);
176 colorWeights[1] =
qint16_MAX - colorWeights[0];
189 newGradient->setType(gradient->type());
190 newGradient->setSpread(gradient->spread());
192 switch (gradient->type()) {
193 case QGradient::LinearGradient: {
194 const QLinearGradient* g =
static_cast<const QLinearGradient*
>(gradient);
195 newGradient->m_start = g->start();
196 newGradient->m_stop = g->finalStop();
197 newGradient->m_focalPoint = g->start();
200 case QGradient::RadialGradient: {
201 const QRadialGradient* g =
static_cast<const QRadialGradient*
>(gradient);
202 newGradient->m_start = g->center();
203 newGradient->m_stop = g->center() + QPointF(g->radius(), 0);
204 newGradient->m_focalPoint = g->focalPoint();
207 case QGradient::ConicalGradient: {
208 const QConicalGradient* g =
static_cast<const QConicalGradient*
>(gradient);
209 qreal radian = g->angle() *
M_PI / 180.0;
210 newGradient->m_start = g->center();
211 newGradient->m_stop = QPointF(100.0 * cos(radian), 100.0 * sin(radian));
212 newGradient->m_focalPoint = g->center();
219 Q_FOREACH(
const QGradientStop & stop, gradient->stops()) {
220 KoColor color(newGradient->colorSpace());
225 newGradient->setValid(
true);
303 if (!(doc.setContent(file))) {
306 QHash<QString, const KoColorProfile*> profiles;
307 for (QDomElement e = doc.documentElement().firstChildElement(
"defs"); !e.isNull(); e = e.nextSiblingElement(
"defs")) {
308 for (QDomElement profileEl = e.firstChildElement(
"color-profile"); !profileEl.isNull(); profileEl = profileEl.nextSiblingElement(
"color-profile")) {
309 const QString href = profileEl.attribute(
"xlink:href");
310 const QByteArray uniqueId = QByteArray::fromHex(profileEl.attribute(
"local").toLatin1());
311 const QString
name = profileEl.attribute(
"name");
317 if (file.size() > 0 && file.open(QIODevice::ReadOnly)) {
325 if (profile && !profiles.contains(
name)) {
326 profiles.insert(
name, profile);
330 for (QDomNode n = doc.documentElement().firstChild(); !n.isNull(); n = n.nextSibling()) {
331 QDomElement e = n.toElement();
333 if (e.isNull())
continue;
335 if (e.tagName() ==
"linearGradient" || e.tagName() ==
"radialGradient") {
340 if (e.tagName() ==
"defs") {
343 for (QDomNode defnode = e.firstChild(); !defnode.isNull(); defnode = defnode.nextSibling()) {
344 QDomElement defelement = defnode.toElement();
346 if (defelement.isNull())
continue;
348 if (defelement.tagName() ==
"linearGradient" || defelement.tagName() ==
"radialGradient") {
369 setName(element.attribute(
"id", i18n(
"SVG Gradient")));
371 bool bbox = element.attribute(
"gradientUnits") !=
"userSpaceOnUse";
373 if (element.tagName() ==
"linearGradient") {
378 s = element.attribute(
"x1",
"0%");
381 xOrigin = s.remove(
'%').toDouble();
383 xOrigin = s.toDouble() * 100.0;
385 s = element.attribute(
"y1",
"0%");
388 yOrigin = s.remove(
'%').toDouble();
390 yOrigin = s.toDouble() * 100.0;
392 s = element.attribute(
"x2",
"100%");
395 xVector = s.remove(
'%').toDouble();
397 xVector = s.toDouble() * 100.0;
399 s = element.attribute(
"y2",
"0%");
402 yVector = s.remove(
'%').toDouble();
404 yVector = s.toDouble() * 100.0;
406 m_start = QPointF(xOrigin, yOrigin);
407 m_stop = QPointF(xVector, yVector);
410 m_start = QPointF(element.attribute(
"x1").toDouble(), element.attribute(
"y1").toDouble());
411 m_stop = QPointF(element.attribute(
"x2").toDouble(), element.attribute(
"y2").toDouble());
413 setType(QGradient::LinearGradient);
419 s = element.attribute(
"cx",
"50%");
422 xOrigin = s.remove(
'%').toDouble();
424 xOrigin = s.toDouble() * 100.0;
426 s = element.attribute(
"cy",
"50%");
429 yOrigin = s.remove(
'%').toDouble();
431 yOrigin = s.toDouble() * 100.0;
433 s = element.attribute(
"cx",
"50%");
436 xVector = s.remove(
'%').toDouble();
438 xVector = s.toDouble() * 100.0;
440 s = element.attribute(
"r",
"50%");
442 xVector += s.remove(
'%').toDouble();
444 xVector += s.toDouble() * 100.0;
446 s = element.attribute(
"cy",
"50%");
449 yVector = s.remove(
'%').toDouble();
451 yVector = s.toDouble() * 100.0;
453 s = element.attribute(
"fx",
"50%");
456 xFocal = s.remove(
'%').toDouble();
458 xFocal = s.toDouble() * 100.0;
460 s = element.attribute(
"fy",
"50%");
463 yFocal = s.remove(
'%').toDouble();
465 yFocal = s.toDouble() * 100.0;
467 m_start = QPointF(xOrigin, yOrigin);
468 m_stop = QPointF(xVector, yVector);
472 m_start = QPointF(element.attribute(
"cx").toDouble(), element.attribute(
"cy").toDouble());
473 m_stop = QPointF(element.attribute(
"cx").toDouble() + element.attribute(
"r").toDouble(),
474 element.attribute(
"cy").toDouble());
475 m_focalPoint = QPointF(element.attribute(
"fx").toDouble(), element.attribute(
"fy").toDouble());
477 setType(QGradient::RadialGradient);
480 QString spreadMethod = element.attribute(
"spreadMethod");
481 if (!spreadMethod.isEmpty()) {
482 if (spreadMethod ==
"reflect")
484 else if (spreadMethod ==
"repeat")
488 for (QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) {
489 QDomElement colorstop = n.toElement();
490 if (colorstop.tagName() ==
"stop") {
494 QString temp = colorstop.attribute(
"offset");
495 if (temp.contains(
'%')) {
496 temp = temp.left(temp.length() - 1);
497 off = temp.toFloat() / 100.0;
500 off = temp.toFloat();
502 if (!colorstop.attribute(
"stop-color").isEmpty())
506 QString style = colorstop.attribute(
"style").simplified();
507 QStringList substyles = style.split(
';', Qt::SkipEmptyParts);
508 Q_FOREACH(
const QString & s, substyles) {
510 QString command = substyle[0].trimmed();
511 QString params = substyle[1].trimmed();
512 if (command ==
"stop-color")
514 if (command ==
"stop-opacity")
515 opacity = params.toDouble();
519 if (!colorstop.attribute(
"stop-opacity").isEmpty())
520 opacity = colorstop.attribute(
"stop-opacity").toDouble();
523 QString stopTypeStr = colorstop.attribute(
"krita:stop-type",
"color-stop");
545 return QString(
".svg");
550 gradientElt.setAttribute(
"type",
"stop");
551 for (
int s = 0; s <
m_stops.size(); s++) {
553 QDomElement stopElt = doc.createElement(
"stop");
559 gradientElt.appendChild(stopElt);
567 QDomElement stopElt = elt.firstChildElement(
"stop");
568 while (!stopElt.isNull()) {
575 stopElt = stopElt.nextSiblingElement(
"stop");
585 doc.setContent(QString(
"<svg xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:krita=\"%1\" > </svg>").arg(
KoXmlNS::krita));
587 const QString spreadMethod[3] = {
593 QDomElement gradient = doc.createElement(
"linearGradient");
594 gradient.setAttribute(
"id",
name());
595 gradient.setAttribute(
"gradientUnits",
"objectBoundingBox");
596 gradient.setAttribute(
"spreadMethod", spreadMethod[
spread()]);
598 QHash<QString, const KoColorProfile*> profiles;
600 QDomElement stopEl = doc.createElement(
"stop");
601 stopEl.setAttribute(
"stop-color", stop.color.toSVG11(&profiles));
602 stopEl.setAttribute(
"offset", QString().setNum(stop.position));
603 stopEl.setAttribute(
"stop-opacity", stop.color.opacityF());
604 stopEl.setAttribute(
"krita:stop-type", stop.typeString());
605 gradient.appendChild(stopEl);
608 if (profiles.size()>0) {
609 QDomElement defs = doc.createElement(
"defs");
610 for (QString key: profiles.keys()) {
613 QDomElement profileEl = doc.createElement(
"color-profile");
614 profileEl.setAttribute(
"name", key);
615 QString val = profile->
uniqueId().toHex();
616 profileEl.setAttribute(
"local", val);
617 profileEl.setAttribute(
"xlink:href", profile->
fileName());
618 defs.appendChild(profileEl);
620 doc.documentElement().appendChild(defs);
623 doc.documentElement().appendChild(gradient);
625 return doc.toString();
630 QTextStream stream(dev);
const KoID Integer8BitsColorDepthID("U8", ki18n("8-bit integer/channel"))
const quint8 OPACITY_OPAQUE_U8
static KoColorSpaceEngineRegistry * instance()
virtual KoID colorDepthId() const =0
KoMixColorsOp * mixColorsOp
static KoColor fromSVG11(const QString value, QHash< QString, const KoColorProfile * > profileList, KoColor current=KoColor())
fromSVG11 Parses a color attribute value and returns a KoColor. SVG defines the colorprofiles elsewhe...
static KoColor fromXML(const QDomElement &elt, const QString &channelDepthId)
void toXML(QDomDocument &doc, QDomElement &colorElt) const
void setOpacity(quint8 alpha)
void fromQColor(const QColor &c)
Convenient function for converting from a QColor.
const KoColorSpace * colorSpace() const
return the current colorSpace
T get(const QString &id) const
virtual void mixColors(const quint8 *const *colors, const qint16 *weights, int nColors, quint8 *dst, int weightSum=255) const =0
static QSharedPointer< KoStopGradient > fromQGradient(const QGradient *gradient)
Creates KoStopGradient from a QGradient.
void loadSvgGradient(QIODevice *file)
QString saveSvgGradient() const
QGradient * toQGradient() const override
reimplemented
QString defaultFileExtension() const override
reimplemented
bool stopsAt(KoGradientStop &leftStop, KoGradientStop &rightStop, qreal t) const
Find stops surrounding position, returns false if position outside gradient.
QList< KoGradientStop > m_stops
void colorAt(KoColor &, qreal t) const override
reimplemented
KoResourceSP clone() const override
bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override
QList< int > requiredCanvasResources() const override
void updateVariableColors(KoCanvasResourcesInterfaceSP canvasResourcesInterface) override
bool saveToDevice(QIODevice *dev) const override
void parseSvgGradient(const QDomElement &element, QHash< QString, const KoColorProfile * > profiles)
void setStops(QList< KoGradientStop > stops)
Sets the gradient stops.
void toXML(QDomDocument &doc, QDomElement &gradientElt) const
toXML Convert the gradient to an XML string.
bool operator==(const KoStopGradient &rhs) const
void bakeVariableColors(KoCanvasResourcesInterfaceSP canvasResourcesInterface) override
static KoStopGradient fromXML(const QDomElement &elt)
fromXML convert a gradient from xml.
KoStopGradient(const QString &filename=QString())
~KoStopGradient() override
QList< KoGradientStop > stops() const
static const QString krita
#define KIS_SAFE_ASSERT_RECOVER(cond)
QSharedPointer< KoResource > KoResourceSP
double toDouble(const QString &str, bool *ok=nullptr)
int toInt(const QString &str, bool *ok=nullptr)
QString toString(const QString &value)
void setUtf8OnStream(QTextStream &stream)
@ BackgroundColor
The active background color selected for this canvas.
@ ForegroundColor
The active foreground color selected for this canvas.
auto mem_equal_to(MemTypeNoRef Class::*ptr, MemType &&value)
mem_equal_to is an unary functor that compares a member of the object to a given value
auto mem_less(MemTypeNoRef Class::*ptr, MemType &&value)
mem_less is an unary functor that compares a member of the object to a given value
void setSpread(QGradient::Spread spreadMethod)
const KoColorSpace * colorSpace
void setType(QGradient::Type repeatType)
virtual QByteArray uniqueId() const =0
virtual const KoColorProfile * addProfile(const QString &filename)=0
static KoColorSpaceRegistry * instance()
const KoColorProfile * profileByUniqueId(const QByteArray &id) const
static KoGradientStopType typeFromString(QString typestring)
void setValid(bool valid)
void setName(const QString &name)