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");
320 file.open(QIODevice::ReadOnly);
321 const QByteArray profileData = file.readAll();
322 if (!profileData.isEmpty()) {
329 if (profile && !profiles.contains(
name)) {
330 profiles.insert(
name, profile);
334 for (QDomNode n = doc.documentElement().firstChild(); !n.isNull(); n = n.nextSibling()) {
335 QDomElement e = n.toElement();
337 if (e.isNull())
continue;
339 if (e.tagName() ==
"linearGradient" || e.tagName() ==
"radialGradient") {
344 if (e.tagName() ==
"defs") {
347 for (QDomNode defnode = e.firstChild(); !defnode.isNull(); defnode = defnode.nextSibling()) {
348 QDomElement defelement = defnode.toElement();
350 if (defelement.isNull())
continue;
352 if (defelement.tagName() ==
"linearGradient" || defelement.tagName() ==
"radialGradient") {
373 setName(element.attribute(
"id", i18n(
"SVG Gradient")));
375 bool bbox = element.attribute(
"gradientUnits") !=
"userSpaceOnUse";
377 if (element.tagName() ==
"linearGradient") {
382 s = element.attribute(
"x1",
"0%");
385 xOrigin = s.remove(
'%').toDouble();
387 xOrigin = s.toDouble() * 100.0;
389 s = element.attribute(
"y1",
"0%");
392 yOrigin = s.remove(
'%').toDouble();
394 yOrigin = s.toDouble() * 100.0;
396 s = element.attribute(
"x2",
"100%");
399 xVector = s.remove(
'%').toDouble();
401 xVector = s.toDouble() * 100.0;
403 s = element.attribute(
"y2",
"0%");
406 yVector = s.remove(
'%').toDouble();
408 yVector = s.toDouble() * 100.0;
410 m_start = QPointF(xOrigin, yOrigin);
411 m_stop = QPointF(xVector, yVector);
414 m_start = QPointF(element.attribute(
"x1").toDouble(), element.attribute(
"y1").toDouble());
415 m_stop = QPointF(element.attribute(
"x2").toDouble(), element.attribute(
"y2").toDouble());
417 setType(QGradient::LinearGradient);
423 s = element.attribute(
"cx",
"50%");
426 xOrigin = s.remove(
'%').toDouble();
428 xOrigin = s.toDouble() * 100.0;
430 s = element.attribute(
"cy",
"50%");
433 yOrigin = s.remove(
'%').toDouble();
435 yOrigin = s.toDouble() * 100.0;
437 s = element.attribute(
"cx",
"50%");
440 xVector = s.remove(
'%').toDouble();
442 xVector = s.toDouble() * 100.0;
444 s = element.attribute(
"r",
"50%");
446 xVector += s.remove(
'%').toDouble();
448 xVector += s.toDouble() * 100.0;
450 s = element.attribute(
"cy",
"50%");
453 yVector = s.remove(
'%').toDouble();
455 yVector = s.toDouble() * 100.0;
457 s = element.attribute(
"fx",
"50%");
460 xFocal = s.remove(
'%').toDouble();
462 xFocal = s.toDouble() * 100.0;
464 s = element.attribute(
"fy",
"50%");
467 yFocal = s.remove(
'%').toDouble();
469 yFocal = s.toDouble() * 100.0;
471 m_start = QPointF(xOrigin, yOrigin);
472 m_stop = QPointF(xVector, yVector);
476 m_start = QPointF(element.attribute(
"cx").toDouble(), element.attribute(
"cy").toDouble());
477 m_stop = QPointF(element.attribute(
"cx").toDouble() + element.attribute(
"r").toDouble(),
478 element.attribute(
"cy").toDouble());
479 m_focalPoint = QPointF(element.attribute(
"fx").toDouble(), element.attribute(
"fy").toDouble());
481 setType(QGradient::RadialGradient);
484 QString spreadMethod = element.attribute(
"spreadMethod");
485 if (!spreadMethod.isEmpty()) {
486 if (spreadMethod ==
"reflect")
488 else if (spreadMethod ==
"repeat")
492 for (QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) {
493 QDomElement colorstop = n.toElement();
494 if (colorstop.tagName() ==
"stop") {
498 QString temp = colorstop.attribute(
"offset");
499 if (temp.contains(
'%')) {
500 temp = temp.left(temp.length() - 1);
501 off = temp.toFloat() / 100.0;
504 off = temp.toFloat();
506 if (!colorstop.attribute(
"stop-color").isEmpty())
510 QString style = colorstop.attribute(
"style").simplified();
511 QStringList substyles = style.split(
';', Qt::SkipEmptyParts);
512 Q_FOREACH(
const QString & s, substyles) {
514 QString command = substyle[0].trimmed();
515 QString params = substyle[1].trimmed();
516 if (command ==
"stop-color")
518 if (command ==
"stop-opacity")
519 opacity = params.toDouble();
523 if (!colorstop.attribute(
"stop-opacity").isEmpty())
524 opacity = colorstop.attribute(
"stop-opacity").toDouble();
527 QString stopTypeStr = colorstop.attribute(
"krita:stop-type",
"color-stop");
549 return QString(
".svg");
554 gradientElt.setAttribute(
"type",
"stop");
555 for (
int s = 0; s <
m_stops.size(); s++) {
557 QDomElement stopElt = doc.createElement(
"stop");
563 gradientElt.appendChild(stopElt);
571 QDomElement stopElt = elt.firstChildElement(
"stop");
572 while (!stopElt.isNull()) {
579 stopElt = stopElt.nextSiblingElement(
"stop");
589 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));
591 const QString spreadMethod[3] = {
597 QDomElement gradient = doc.createElement(
"linearGradient");
598 gradient.setAttribute(
"id",
name());
599 gradient.setAttribute(
"gradientUnits",
"objectBoundingBox");
600 gradient.setAttribute(
"spreadMethod", spreadMethod[
spread()]);
602 QHash<QString, const KoColorProfile*> profiles;
604 QDomElement stopEl = doc.createElement(
"stop");
605 stopEl.setAttribute(
"stop-color", stop.color.toSVG11(&profiles));
606 stopEl.setAttribute(
"offset", QString().setNum(stop.position));
607 stopEl.setAttribute(
"stop-opacity", stop.color.opacityF());
608 stopEl.setAttribute(
"krita:stop-type", stop.typeString());
609 gradient.appendChild(stopEl);
612 if (profiles.size()>0) {
613 QDomElement defs = doc.createElement(
"defs");
614 for (QString key: profiles.keys()) {
617 QDomElement profileEl = doc.createElement(
"color-profile");
618 profileEl.setAttribute(
"name", key);
619 QString val = profile->
uniqueId().toHex();
620 profileEl.setAttribute(
"local", val);
621 profileEl.setAttribute(
"xlink:href", profile->
fileName());
622 defs.appendChild(profileEl);
624 doc.documentElement().appendChild(defs);
627 doc.documentElement().appendChild(gradient);
629 return doc.toString();
634 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)