diff --git a/libs/psd/asl/kis_asl_xml_parser.cpp b/libs/psd/asl/kis_asl_xml_parser.cpp index 9b208b3672..6aa0b4a65b 100644 --- a/libs/psd/asl/kis_asl_xml_parser.cpp +++ b/libs/psd/asl/kis_asl_xml_parser.cpp @@ -1,556 +1,578 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_asl_xml_parser.h" #include #include #include #include #include #include #include #include #include #include #include "kis_dom_utils.h" #include "kis_debug.h" #include "psd_utils.h" #include "psd.h" #include "compression.h" #include "kis_asl_object_catcher.h" namespace Private { void parseElement(const QDomElement &el, const QString &parentPath, KisAslObjectCatcher &catcher); class CurveObjectCatcher : public KisAslObjectCatcher { public: void addText(const QString &path, const QString &value) override { if (path == "/Nm ") { m_name = value; } else { warnKrita << "XML (ASL): failed to parse curve object" << path << value; } } void addPoint(const QString &path, const QPointF &value) override { if (!m_arrayMode) { warnKrita << "XML (ASL): failed to parse curve object (array fault)" << path << value << ppVar(m_arrayMode); } m_points.append(value); } public: QVector m_points; QString m_name; }; QColor parseRGBColorObject(QDomElement parent) { QColor color(Qt::black); QDomNode child = parent.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); if (type != "Double") { warnKrita << "Unknown color component type:" << ppVar(type) << ppVar(key); return Qt::red; } double value = KisDomUtils::toDouble(childEl.attribute("value", "0")); if (key == "Rd ") { color.setRed(value); } else if (key == "Grn ") { color.setGreen(value); } else if (key == "Bl ") { color.setBlue(value); } else { warnKrita << "Unknown color key value:" << ppVar(key); return Qt::red; } child = child.nextSibling(); } return color; } void parseColorStopsList(QDomElement parent, QVector &startLocations, QVector &middleOffsets, QVector &colors) { QDomNode child = parent.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); QString classId = childEl.attribute("classId", ""); if (type == "Descriptor" && classId == "Clrt") { // sorry for naming... QDomNode child = childEl.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); QString classId = childEl.attribute("classId", ""); if (type == "Integer" && key == "Lctn") { int value = KisDomUtils::toInt(childEl.attribute("value", "0")); startLocations.append(qreal(value) / 4096.0); } else if (type == "Integer" && key == "Mdpn") { int value = KisDomUtils::toInt(childEl.attribute("value", "0")); middleOffsets.append(qreal(value) / 100.0); } else if (type == "Descriptor" && key == "Clr ") { colors.append(parseRGBColorObject(childEl)); } else if (type == "Enum" && key == "Type") { QString typeId = childEl.attribute("typeId", ""); if (typeId != "Clry") { warnKrita << "WARNING: Invalid typeId of a gradient stop type" << typeId; } QString value = childEl.attribute("value", ""); if (value == "BckC" || value == "FrgC") { warnKrita << "WARNING: Using foreground/background colors in ASL gradients is not yet supported"; } } child = child.nextSibling(); } } else { warnKrita << "WARNING: Unrecognized object in color stops list" << ppVar(type) << ppVar(key) << ppVar(classId); } child = child.nextSibling(); } } void parseTransparencyStopsList(QDomElement parent, QVector &startLocations, QVector &middleOffsets, QVector &transparencies) { QDomNode child = parent.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); QString classId = childEl.attribute("classId", ""); if (type == "Descriptor" && classId == "TrnS") { // sorry for naming again... QDomNode child = childEl.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); if (type == "Integer" && key == "Lctn") { int value = KisDomUtils::toInt(childEl.attribute("value", "0")); startLocations.append(qreal(value) / 4096.0); } else if (type == "Integer" && key == "Mdpn") { int value = KisDomUtils::toInt(childEl.attribute("value", "0")); middleOffsets.append(qreal(value) / 100.0); } else if (type == "UnitFloat" && key == "Opct") { QString unit = childEl.attribute("unit", ""); if (unit != "#Prc") { warnKrita << "WARNING: Invalid unit of a gradient stop transparency" << unit; } qreal value = KisDomUtils::toDouble(childEl.attribute("value", "100")); transparencies.append(value / 100.0); } child = child.nextSibling(); } } else { warnKrita << "WARNING: Unrecognized object in transparency stops list" << ppVar(type) << ppVar(key) << ppVar(classId); } child = child.nextSibling(); } } inline QString buildPath(const QString &parent, const QString &key) { return parent + "/" + key; } bool tryParseDescriptor(const QDomElement &el, const QString &path, const QString &classId, KisAslObjectCatcher &catcher) { bool retval = true; if (classId == "null") { catcher.newStyleStarted(); // here we just notify that a new style is started, we haven't // processed the whole block yet, so return false. retval = false; } else if (classId == "RGBC") { catcher.addColor(path, parseRGBColorObject(el)); } else if (classId == "ShpC") { CurveObjectCatcher curveCatcher; QDomNode child = el.firstChild(); while (!child.isNull()) { parseElement(child.toElement(), "", curveCatcher); child = child.nextSibling(); } catcher.addCurve(path, curveCatcher.m_name, curveCatcher.m_points); } else if (classId == "CrPt") { QPointF point; QDomNode child = el.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); if (type == "Boolean" && key == "Cnty") { warnKrita << "WARNING: tryParseDescriptor: The points of the curve object contain \'Cnty\' flag which is unsupported by Krita"; warnKrita << " " << ppVar(type) << ppVar(key) << ppVar(path); child = child.nextSibling(); continue; } if (type != "Double") { warnKrita << "Unknown point component type:" << ppVar(type) << ppVar(key) << ppVar(path); return false; } double value = KisDomUtils::toDouble(childEl.attribute("value", "0")); if (key == "Hrzn") { point.setX(value); } else if (key == "Vrtc") { point.setY(value); } else { warnKrita << "Unknown point key value:" << ppVar(key) << ppVar(path); return false; } child = child.nextSibling(); } catcher.addPoint(path, point); } else if (classId == "Pnt ") { QPointF point; QDomNode child = el.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); QString unit = childEl.attribute("unit", ""); if (type != "Double" && !(type == "UnitFloat" && unit == "#Prc")) { warnKrita << "Unknown point component type:" << ppVar(unit) << ppVar(type) << ppVar(key) << ppVar(path); return false; } double value = KisDomUtils::toDouble(childEl.attribute("value", "0")); if (key == "Hrzn") { point.setX(value); } else if (key == "Vrtc") { point.setY(value); } else { warnKrita << "Unknown point key value:" << ppVar(key) << ppVar(path); return false; } child = child.nextSibling(); } catcher.addPoint(path, point); } else if (classId == "KisPattern") { QByteArray patternData; QString patternUuid; QDomNode child = el.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); if (type == "Text" && key == "Idnt") { patternUuid = childEl.attribute("value", ""); } if (type == "KisPatternData" && key == "Data") { QDomNode dataNode = child.firstChild(); if (!dataNode.isCDATASection()) { warnKrita << "WARNING: failed to parse KisPatternData XML section!"; continue; } QDomCDATASection dataSection = dataNode.toCDATASection(); QByteArray data = dataSection.data().toLatin1(); data = QByteArray::fromBase64(data); data = qUncompress(data); if (data.isEmpty()) { warnKrita << "WARNING: failed to parse KisPatternData XML section!"; continue; } patternData = data; } child = child.nextSibling(); } if (!patternUuid.isEmpty() && !patternData.isEmpty()) { QString fileName = QString("%1.pat").arg(patternUuid); QScopedPointer pattern(new KoPattern(fileName)); QBuffer buffer(&patternData); buffer.open(QIODevice::ReadOnly); pattern->loadPatFromDevice(&buffer); catcher.addPattern(path, pattern.data()); } else { warnKrita << "WARNING: failed to load KisPattern XML section!" << ppVar(patternUuid); } } else if (classId == "Ptrn") { // reference to an existing pattern QString patternUuid; QString patternName; QDomNode child = el.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); if (type == "Text" && key == "Idnt") { patternUuid = childEl.attribute("value", ""); } else if (type == "Text" && key == "Nm ") { patternName = childEl.attribute("value", ""); } else { warnKrita << "WARNING: unrecognized pattern-ref section key:" << ppVar(type) << ppVar(key); } child = child.nextSibling(); } catcher.addPatternRef(path, patternUuid, patternName); } else if (classId == "Grdn") { QString gradientName; qreal gradientSmoothness = 100.0; QVector startLocations; QVector middleOffsets; QVector colors; QVector transpStartLocations; QVector transpMiddleOffsets; QVector transparencies; QDomNode child = el.firstChild(); while (!child.isNull()) { QDomElement childEl = child.toElement(); QString type = childEl.attribute("type", ""); QString key = childEl.attribute("key", ""); if (type == "Text" && key == "Nm ") { gradientName = childEl.attribute("value", ""); } else if (type == "Enum" && key == "GrdF") { QString typeId = childEl.attribute("typeId", ""); QString value = childEl.attribute("value", ""); if (typeId != "GrdF" || value != "CstS") { warnKrita << "WARNING: Unsupported gradient type (porbably, noise-based):" << value; return true; } } else if (type == "Double" && key == "Intr") { double value = KisDomUtils::toDouble(childEl.attribute("value", "4096")); gradientSmoothness = 100.0 * value / 4096.0; } else if (type == "List" && key == "Clrs") { parseColorStopsList(childEl, startLocations, middleOffsets, colors); } else if (type == "List" && key == "Trns") { parseTransparencyStopsList(childEl, transpStartLocations, transpMiddleOffsets, transparencies); } child = child.nextSibling(); } - if (colors.size() < 2) { - warnKrita << "WARNING: ASL gradient has too few stops" << ppVar(colors.size()); + + if (colors.size() < transparencies.size()) { + const QColor lastColor = !colors.isEmpty() ? colors.last() : QColor(Qt::black); + while (colors.size() != transparencies.size()) { + const int index = colors.size(); + colors.append(lastColor); + startLocations.append(transpStartLocations[index]); + middleOffsets.append(transpMiddleOffsets[index]); + } } - if (colors.size() != transparencies.size()) { - warnKrita << "WARNING: ASL gradient has inconsistent number of transparency stops. Dropping transparency..." << ppVar(colors.size()) << ppVar(transparencies.size()); - transparencies.resize(colors.size()); - for (int i = 0; i < colors.size(); i++) { - transparencies[i] = 1.0; + if (colors.size() > transparencies.size()) { + const qreal lastTransparency = !transparencies.isEmpty() ? transparencies.last() : 1.0; + while (colors.size() != transparencies.size()) { + const int index = transparencies.size(); + transparencies.append(lastTransparency); + transpStartLocations.append(startLocations[index]); + transpMiddleOffsets.append(middleOffsets[index]); } } + if (colors.size() == 1) { + colors.append(colors.last()); + startLocations.append(1.0); + middleOffsets.append(0.5); + + transparencies.append(transparencies.last()); + transpStartLocations.append(1.0); + transpMiddleOffsets.append(0.5); + } + QString fileName = gradientName + ".ggr"; QSharedPointer gradient(new KoSegmentGradient(fileName)); Q_UNUSED(gradientSmoothness); gradient->setName(gradientName); - for (int i = 1; i < colors.size(); i++) { - QColor startColor = colors[i-1]; - QColor endColor = colors[i]; - startColor.setAlphaF(transparencies[i-1]); - endColor.setAlphaF(transparencies[i]); - - qreal start = startLocations[i-1]; - qreal end = startLocations[i]; - qreal middle = start + middleOffsets[i-1] * (end - start); - - gradient->createSegment(INTERP_LINEAR, COLOR_INTERP_RGB, - start, end, middle, - startColor, - endColor); + if (colors.size() >= 2) { + for (int i = 1; i < colors.size(); i++) { + QColor startColor = colors[i-1]; + QColor endColor = colors[i]; + startColor.setAlphaF(transparencies[i-1]); + endColor.setAlphaF(transparencies[i]); + + qreal start = startLocations[i-1]; + qreal end = startLocations[i]; + qreal middle = start + middleOffsets[i-1] * (end - start); + + gradient->createSegment(INTERP_LINEAR, COLOR_INTERP_RGB, + start, end, middle, + startColor, + endColor); + } + gradient->setValid(true); + } else { + gradient->setValid(false); } - gradient->setValid(true); - catcher.addGradient(path, gradient); } else { retval = false; } return retval; } void parseElement(const QDomElement &el, const QString &parentPath, KisAslObjectCatcher &catcher) { KIS_ASSERT_RECOVER_RETURN(el.tagName() == "node"); QString type = el.attribute("type", ""); QString key = el.attribute("key", ""); if (type == "Descriptor") { QString classId = el.attribute("classId", ""); QString containerName = key.isEmpty() ? classId : key; QString containerPath = buildPath(parentPath, containerName); if (!tryParseDescriptor(el, containerPath, classId, catcher)) { QDomNode child = el.firstChild(); while (!child.isNull()) { parseElement(child.toElement(), containerPath, catcher); child = child.nextSibling(); } } } else if (type == "List") { catcher.setArrayMode(true); QString containerName = key; QString containerPath = buildPath(parentPath, containerName); QDomNode child = el.firstChild(); while (!child.isNull()) { parseElement(child.toElement(), containerPath, catcher); child = child.nextSibling(); } catcher.setArrayMode(false); } else if (type == "Double") { double v = KisDomUtils::toDouble(el.attribute("value", "0")); catcher.addDouble(buildPath(parentPath, key), v); } else if (type == "UnitFloat") { QString unit = el.attribute("unit", ""); double v = KisDomUtils::toDouble(el.attribute("value", "0")); catcher.addUnitFloat(buildPath(parentPath, key), unit, v); } else if (type == "Text") { QString v = el.attribute("value", ""); catcher.addText(buildPath(parentPath, key), v); } else if (type == "Enum") { QString v = el.attribute("value", ""); QString typeId = el.attribute("typeId", ""); catcher.addEnum(buildPath(parentPath, key), typeId, v); } else if (type == "Integer") { int v = KisDomUtils::toInt(el.attribute("value", "0")); catcher.addInteger(buildPath(parentPath, key), v); } else if (type == "Boolean") { int v = KisDomUtils::toInt(el.attribute("value", "0")); catcher.addBoolean(buildPath(parentPath, key), v); } else { warnKrita << "WARNING: XML (ASL) Unknown element type:" << type << ppVar(parentPath) << ppVar(key); } } } // namespace void KisAslXmlParser::parseXML(const QDomDocument &doc, KisAslObjectCatcher &catcher) { QDomElement root = doc.documentElement(); if (root.tagName() != "asl") { return; } QDomNode child = root.firstChild(); while (!child.isNull()) { Private::parseElement(child.toElement(), "", catcher); child = child.nextSibling(); } } diff --git a/libs/psd/asl/kis_asl_xml_writer.cpp b/libs/psd/asl/kis_asl_xml_writer.cpp index 4bb21592ef..a902e76769 100644 --- a/libs/psd/asl/kis_asl_xml_writer.cpp +++ b/libs/psd/asl/kis_asl_xml_writer.cpp @@ -1,398 +1,399 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_asl_xml_writer.h" #include #include #include #include #include #include #include #include #include #include "kis_dom_utils.h" #include "kis_asl_writer_utils.h" struct KisAslXmlWriter::Private { QDomDocument document; QDomElement currentElement; }; KisAslXmlWriter::KisAslXmlWriter() : m_d(new Private) { QDomElement el = m_d->document.createElement("asl"); m_d->document.appendChild(el); m_d->currentElement = el; } KisAslXmlWriter::~KisAslXmlWriter() { } QDomDocument KisAslXmlWriter::document() const { if (m_d->document.documentElement() != m_d->currentElement) { warnKrita << "KisAslXmlWriter::document(): unbalanced enter/leave descriptor/array"; } return m_d->document; } void KisAslXmlWriter::enterDescriptor(const QString &key, const QString &name, const QString &classId) { QDomElement el = m_d->document.createElement("node"); if (!key.isEmpty()) { el.setAttribute("key", key); } el.setAttribute("type", "Descriptor"); el.setAttribute("name", name); el.setAttribute("classId", classId); m_d->currentElement.appendChild(el); m_d->currentElement = el; } void KisAslXmlWriter::leaveDescriptor() { if (!m_d->currentElement.parentNode().toElement().isNull()) { m_d->currentElement = m_d->currentElement.parentNode().toElement(); } else { warnKrita << "KisAslXmlWriter::leaveDescriptor(): unbalanced enter/leave descriptor"; } } void KisAslXmlWriter::enterList(const QString &key) { QDomElement el = m_d->document.createElement("node"); if (!key.isEmpty()) { el.setAttribute("key", key); } el.setAttribute("type", "List"); m_d->currentElement.appendChild(el); m_d->currentElement = el; } void KisAslXmlWriter::leaveList() { if (!m_d->currentElement.parentNode().toElement().isNull()) { m_d->currentElement = m_d->currentElement.parentNode().toElement(); } else { warnKrita << "KisAslXmlWriter::leaveList(): unbalanced enter/leave list"; } } void KisAslXmlWriter::writeDouble(const QString &key, double value) { QDomElement el = m_d->document.createElement("node"); if (!key.isEmpty()) { el.setAttribute("key", key); } el.setAttribute("type", "Double"); el.setAttribute("value", KisDomUtils::toString(value)); m_d->currentElement.appendChild(el); } void KisAslXmlWriter::writeInteger(const QString &key, int value) { QDomElement el = m_d->document.createElement("node"); if (!key.isEmpty()) { el.setAttribute("key", key); } el.setAttribute("type", "Integer"); el.setAttribute("value", KisDomUtils::toString(value)); m_d->currentElement.appendChild(el); } void KisAslXmlWriter::writeEnum(const QString &key, const QString &typeId, const QString &value) { QDomElement el = m_d->document.createElement("node"); if (!key.isEmpty()) { el.setAttribute("key", key); } el.setAttribute("type", "Enum"); el.setAttribute("typeId", typeId); el.setAttribute("value", value); m_d->currentElement.appendChild(el); } void KisAslXmlWriter::writeUnitFloat(const QString &key, const QString &unit, double value) { QDomElement el = m_d->document.createElement("node"); if (!key.isEmpty()) { el.setAttribute("key", key); } el.setAttribute("type", "UnitFloat"); el.setAttribute("unit", unit); el.setAttribute("value", KisDomUtils::toString(value)); m_d->currentElement.appendChild(el); } void KisAslXmlWriter::writeText(const QString &key, const QString &value) { QDomElement el = m_d->document.createElement("node"); if (!key.isEmpty()) { el.setAttribute("key", key); } el.setAttribute("type", "Text"); el.setAttribute("value", value); m_d->currentElement.appendChild(el); } void KisAslXmlWriter::writeBoolean(const QString &key, bool value) { QDomElement el = m_d->document.createElement("node"); if (!key.isEmpty()) { el.setAttribute("key", key); } el.setAttribute("type", "Boolean"); el.setAttribute("value", KisDomUtils::toString(value)); m_d->currentElement.appendChild(el); } void KisAslXmlWriter::writeColor(const QString &key, const QColor &value) { enterDescriptor(key, "", "RGBC"); writeDouble("Rd ", value.red()); writeDouble("Grn ", value.green()); writeDouble("Bl ", value.blue()); leaveDescriptor(); } void KisAslXmlWriter::writePoint(const QString &key, const QPointF &value) { enterDescriptor(key, "", "CrPt"); writeDouble("Hrzn", value.x()); writeDouble("Vrtc", value.y()); leaveDescriptor(); } void KisAslXmlWriter::writePhasePoint(const QString &key, const QPointF &value) { enterDescriptor(key, "", "Pnt "); writeDouble("Hrzn", value.x()); writeDouble("Vrtc", value.y()); leaveDescriptor(); } void KisAslXmlWriter::writeOffsetPoint(const QString &key, const QPointF &value) { enterDescriptor(key, "", "Pnt "); writeUnitFloat("Hrzn", "#Prc", value.x()); writeUnitFloat("Vrtc", "#Prc", value.y()); leaveDescriptor(); } void KisAslXmlWriter::writeCurve(const QString &key, const QString &name, const QVector &points) { enterDescriptor(key, "", "ShpC"); writeText("Nm ", name); enterList("Crv "); Q_FOREACH (const QPointF &pt, points) { writePoint("", pt); } leaveList(); leaveDescriptor(); } QString KisAslXmlWriter::writePattern(const QString &key, const KoPattern *pattern) { enterDescriptor(key, "", "KisPattern"); writeText("Nm ", pattern->name()); QString uuid = KisAslWriterUtils::getPatternUuidLazy(pattern); writeText("Idnt", uuid); // Write pattern data QBuffer buffer; buffer.open(QIODevice::WriteOnly); pattern->savePatToDevice(&buffer); QDomCDATASection dataSection = m_d->document.createCDATASection(qCompress(buffer.buffer()).toBase64()); QDomElement dataElement = m_d->document.createElement("node"); dataElement.setAttribute("type", "KisPatternData"); dataElement.setAttribute("key", "Data"); dataElement.appendChild(dataSection); m_d->currentElement.appendChild(dataElement); leaveDescriptor(); return uuid; } void KisAslXmlWriter::writePatternRef(const QString &key, const KoPattern *pattern, const QString &uuid) { enterDescriptor(key, "", "Ptrn"); writeText("Nm ", pattern->name()); writeText("Idnt", uuid); leaveDescriptor(); } void KisAslXmlWriter::writeGradientImpl(const QString &key, const QString &name, QVector colors, QVector transparencies, QVector positions, QVector middleOffsets) { enterDescriptor(key, "Gradient", "Grdn"); writeText("Nm ", name); writeEnum("GrdF", "GrdF", "CstS"); writeDouble("Intr", 4096); enterList("Clrs"); for (int i = 0; i < colors.size(); i++) { enterDescriptor("", "", "Clrt"); writeColor("Clr ", colors[i]); writeEnum("Type", "Clry", "UsrS"); // NOTE: we do not support BG/FG color tags writeInteger("Lctn", positions[i] * 4096.0); writeInteger("Mdpn", middleOffsets[i] * 100.0); leaveDescriptor(); }; leaveList(); enterList("Trns"); for (int i = 0; i < colors.size(); i++) { enterDescriptor("", "", "TrnS"); writeUnitFloat("Opct", "#Prc", transparencies[i] * 100.0); writeInteger("Lctn", positions[i] * 4096.0); writeInteger("Mdpn", middleOffsets[i] * 100.0); leaveDescriptor(); }; leaveList(); leaveDescriptor(); } void KisAslXmlWriter::writeSegmentGradient(const QString &key, const KoSegmentGradient *gradient) { const QList&segments = gradient->segments(); + KIS_SAFE_ASSERT_RECOVER_RETURN(!segments.isEmpty()); QVector colors; QVector transparencies; QVector positions; QVector middleOffsets; Q_FOREACH (const KoGradientSegment *seg, segments) { const qreal start = seg->startOffset(); const qreal end = seg->endOffset(); const qreal mid = (end - start) > DBL_EPSILON ? (seg->middleOffset() - start) / (end - start) : 0.5; QColor color = seg->startColor().toQColor(); qreal transparency = color.alphaF(); color.setAlphaF(1.0); colors << color; transparencies << transparency; positions << start; middleOffsets << mid; } // last segment if (!segments.isEmpty()) { const KoGradientSegment *lastSeg = segments.last(); QColor color = lastSeg->endColor().toQColor(); qreal transparency = color.alphaF(); color.setAlphaF(1.0); colors << color; transparencies << transparency; positions << lastSeg->endOffset(); middleOffsets << 0.5; } writeGradientImpl(key, gradient->name(), colors, transparencies, positions, middleOffsets); } void KisAslXmlWriter::writeStopGradient(const QString &key, const KoStopGradient *gradient) { QVector colors; QVector transparencies; QVector positions; QVector middleOffsets; Q_FOREACH (const KoGradientStop &stop, gradient->stops()) { QColor color = stop.second.toQColor(); qreal transparency = color.alphaF(); color.setAlphaF(1.0); colors << color; transparencies << transparency; positions << stop.first; middleOffsets << 0.5; } writeGradientImpl(key, gradient->name(), colors, transparencies, positions, middleOffsets); } diff --git a/libs/ui/kis_asl_layer_style_serializer.cpp b/libs/ui/kis_asl_layer_style_serializer.cpp index bbcd287628..e4014130ef 100644 --- a/libs/ui/kis_asl_layer_style_serializer.cpp +++ b/libs/ui/kis_asl_layer_style_serializer.cpp @@ -1,1234 +1,1234 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_asl_layer_style_serializer.h" #include #include #include #include #include #include #include "kis_dom_utils.h" #include "psd.h" #include "kis_global.h" #include "asl/kis_asl_reader.h" #include "asl/kis_asl_xml_parser.h" #include "asl/kis_asl_writer_utils.h" #include "asl/kis_asl_xml_writer.h" #include "asl/kis_asl_writer.h" #include using namespace std::placeholders; KisAslLayerStyleSerializer::KisAslLayerStyleSerializer() { } KisAslLayerStyleSerializer::~KisAslLayerStyleSerializer() { } QVector KisAslLayerStyleSerializer::styles() const { return m_stylesVector; } void KisAslLayerStyleSerializer::setStyles(const QVector &styles) { m_stylesVector = styles; } QString compositeOpToBlendMode(const QString &compositeOp) { QString mode = "Nrml"; if (compositeOp == COMPOSITE_OVER) { mode = "Nrml"; } else if (compositeOp == COMPOSITE_DISSOLVE) { mode = "Dslv"; } else if (compositeOp == COMPOSITE_DARKEN) { mode = "Drkn"; } else if (compositeOp == COMPOSITE_MULT) { mode = "Mltp"; } else if (compositeOp == COMPOSITE_BURN) { mode = "CBrn"; } else if (compositeOp == COMPOSITE_LINEAR_BURN) { mode = "linearBurn"; } else if (compositeOp == COMPOSITE_DARKER_COLOR) { mode = "darkerColor"; } else if (compositeOp == COMPOSITE_LIGHTEN) { mode = "Lghn"; } else if (compositeOp == COMPOSITE_SCREEN) { mode = "Scrn"; } else if (compositeOp == COMPOSITE_DODGE) { mode = "CDdg"; } else if (compositeOp == COMPOSITE_LINEAR_DODGE) { mode = "linearDodge"; } else if (compositeOp == COMPOSITE_LIGHTER_COLOR) { mode = "lighterColor"; } else if (compositeOp == COMPOSITE_OVERLAY) { mode = "Ovrl"; } else if (compositeOp == COMPOSITE_SOFT_LIGHT_PHOTOSHOP) { mode = "SftL"; } else if (compositeOp == COMPOSITE_HARD_LIGHT) { mode = "HrdL"; } else if (compositeOp == COMPOSITE_VIVID_LIGHT) { mode = "vividLight"; } else if (compositeOp == COMPOSITE_LINEAR_LIGHT) { mode = "linearLight"; } else if (compositeOp == COMPOSITE_PIN_LIGHT) { mode = "pinLight"; } else if (compositeOp == COMPOSITE_HARD_MIX_PHOTOSHOP) { mode = "hardMix"; } else if (compositeOp == COMPOSITE_DIFF) { mode = "Dfrn"; } else if (compositeOp == COMPOSITE_EXCLUSION) { mode = "Xclu"; } else if (compositeOp == COMPOSITE_SUBTRACT) { mode = "Sbtr"; } else if (compositeOp == COMPOSITE_DIVIDE) { mode = "divide"; } else if (compositeOp == COMPOSITE_HUE) { mode = "H "; } else if (compositeOp == COMPOSITE_SATURATION) { mode = "Strt"; } else if (compositeOp == COMPOSITE_COLOR) { mode = "Clr "; } else if (compositeOp == COMPOSITE_LUMINIZE) { mode = "Lmns"; } else { dbgKrita << "Unknown composite op:" << mode << "Returning \"Nrml\"!"; } return mode; } QString techniqueToString(psd_technique_type technique, const QString &typeId) { QString result = "SfBL"; switch (technique) { case psd_technique_softer: result = "SfBL"; break; case psd_technique_precise: result = "PrBL"; break; case psd_technique_slope_limit: result = "Slmt"; break; } if (typeId == "BETE" && technique == psd_technique_slope_limit) { warnKrita << "WARNING: techniqueToString: invalid technique type!" << ppVar(technique) << ppVar(typeId); } return result; } QString bevelStyleToString(psd_bevel_style style) { QString result = "OtrB"; switch (style) { case psd_bevel_outer_bevel: result = "OtrB"; break; case psd_bevel_inner_bevel: result = "InrB"; break; case psd_bevel_emboss: result = "Embs"; break; case psd_bevel_pillow_emboss: result = "PlEb"; break; case psd_bevel_stroke_emboss: result = "strokeEmboss"; break; } return result; } QString gradientTypeToString(psd_gradient_style style) { QString result = "Lnr "; switch (style) { case psd_gradient_style_linear: result = "Lnr "; break; case psd_gradient_style_radial: result = "Rdl "; break; case psd_gradient_style_angle: result = "Angl"; break; case psd_gradient_style_reflected: result = "Rflc"; break; case psd_gradient_style_diamond: result = "Dmnd"; break; } return result; } QString strokePositionToString(psd_stroke_position position) { QString result = "OutF"; switch (position) { case psd_stroke_outside: result = "OutF"; break; case psd_stroke_inside: result = "InsF"; break; case psd_stroke_center: result = "CtrF"; break; } return result; } QString strokeFillTypeToString(psd_fill_type position) { QString result = "SClr"; switch (position) { case psd_fill_solid_color: result = "SClr"; break; case psd_fill_gradient: result = "GrFl"; break; case psd_fill_pattern: result = "Ptrn"; break; } return result; } QVector KisAslLayerStyleSerializer::fetchAllPatterns(KisPSDLayerStyle *style) const { QVector allPatterns; if (style->patternOverlay()->effectEnabled()) { allPatterns << style->patternOverlay()->pattern(); } if (style->stroke()->effectEnabled() && style->stroke()->fillType() == psd_fill_pattern) { allPatterns << style->stroke()->pattern(); } if(style->bevelAndEmboss()->effectEnabled() && style->bevelAndEmboss()->textureEnabled()) { allPatterns << style->bevelAndEmboss()->texturePattern(); } return allPatterns; } QString fetchPatternUuidSafe(KoPattern *pattern, QHash patternToUuid) { if (patternToUuid.contains(pattern)) { return patternToUuid[pattern]; } else { warnKrita << "WARNING: the pattern is not present in the Uuid map!"; return "invalid-uuid"; } } QDomDocument KisAslLayerStyleSerializer::formXmlDocument() const { KIS_ASSERT_RECOVER(!m_stylesVector.isEmpty()) { return QDomDocument(); } QVector allPatterns; Q_FOREACH (KisPSDLayerStyleSP style, m_stylesVector) { allPatterns += fetchAllPatterns(style.data()); } QHash patternToUuidMap; KisAslXmlWriter w; if (!allPatterns.isEmpty()) { w.enterList("Patterns"); Q_FOREACH (KoPattern *pattern, allPatterns) { if (pattern) { if (!patternToUuidMap.contains(pattern)) { QString uuid = w.writePattern("", pattern); patternToUuidMap.insert(pattern, uuid); } } else { warnKrita << "WARNING: KisAslLayerStyleSerializer::saveToDevice: saved pattern is null!"; } } w.leaveList(); } Q_FOREACH (KisPSDLayerStyleSP style, m_stylesVector) { w.enterDescriptor("", "", "null"); w.writeText("Nm ", style->name()); w.writeText("Idnt", style->psdUuid()); w.leaveDescriptor(); w.enterDescriptor("", "", "Styl"); w.enterDescriptor("documentMode", "", "documentMode"); w.leaveDescriptor(); w.enterDescriptor("Lefx", "", "Lefx"); w.writeUnitFloat("Scl ", "#Prc", 100); w.writeBoolean("masterFXSwitch", style->isEnabled()); // Drop Shadow const psd_layer_effects_drop_shadow *dropShadow = style->dropShadow(); if (dropShadow->effectEnabled()) { w.enterDescriptor("DrSh", "", "DrSh"); w.writeBoolean("enab", dropShadow->effectEnabled()); w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(dropShadow->blendMode())); w.writeColor("Clr ", dropShadow->color()); w.writeUnitFloat("Opct", "#Prc", dropShadow->opacity()); w.writeBoolean("uglg", dropShadow->useGlobalLight()); w.writeUnitFloat("lagl", "#Ang", dropShadow->angle()); w.writeUnitFloat("Dstn", "#Pxl", dropShadow->distance()); w.writeUnitFloat("Ckmt", "#Pxl", dropShadow->spread()); w.writeUnitFloat("blur", "#Pxl", dropShadow->size()); w.writeUnitFloat("Nose", "#Prc", dropShadow->noise()); w.writeBoolean("AntA", dropShadow->antiAliased()); // FIXME: save curves w.writeCurve("TrnS", "Linear", QVector() << QPointF() << QPointF(255, 255)); w.writeBoolean("layerConceals", dropShadow->knocksOut()); w.leaveDescriptor(); } // Inner Shadow const psd_layer_effects_inner_shadow *innerShadow = style->innerShadow(); if (innerShadow->effectEnabled()) { w.enterDescriptor("IrSh", "", "IrSh"); w.writeBoolean("enab", innerShadow->effectEnabled()); w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(innerShadow->blendMode())); w.writeColor("Clr ", innerShadow->color()); w.writeUnitFloat("Opct", "#Prc", innerShadow->opacity()); w.writeBoolean("uglg", innerShadow->useGlobalLight()); w.writeUnitFloat("lagl", "#Ang", innerShadow->angle()); w.writeUnitFloat("Dstn", "#Pxl", innerShadow->distance()); w.writeUnitFloat("Ckmt", "#Pxl", innerShadow->spread()); w.writeUnitFloat("blur", "#Pxl", innerShadow->size()); w.writeUnitFloat("Nose", "#Prc", innerShadow->noise()); w.writeBoolean("AntA", innerShadow->antiAliased()); // FIXME: save curves w.writeCurve("TrnS", "Linear", QVector() << QPointF() << QPointF(255, 255)); w.leaveDescriptor(); } // Outer Glow const psd_layer_effects_outer_glow *outerGlow = style->outerGlow(); if (outerGlow->effectEnabled()) { w.enterDescriptor("OrGl", "", "OrGl"); w.writeBoolean("enab", outerGlow->effectEnabled()); w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(outerGlow->blendMode())); if (outerGlow->fillType() == psd_fill_gradient && outerGlow->gradient()) { KoSegmentGradient *segmentGradient = dynamic_cast(outerGlow->gradient().data()); KoStopGradient *stopGradient = dynamic_cast(outerGlow->gradient().data()); if (segmentGradient && segmentGradient->valid()) { w.writeSegmentGradient("Grad", segmentGradient); } else if (stopGradient && stopGradient->valid()) { w.writeStopGradient("Grad", stopGradient); } else { warnKrita << "WARNING: OG: Unknown gradient type!"; w.writeColor("Clr ", outerGlow->color()); } } else { w.writeColor("Clr ", outerGlow->color()); } w.writeUnitFloat("Opct", "#Prc", outerGlow->opacity()); w.writeEnum("GlwT", "BETE", techniqueToString(outerGlow->technique(), "BETE")); w.writeUnitFloat("Ckmt", "#Pxl", outerGlow->spread()); w.writeUnitFloat("blur", "#Pxl", outerGlow->size()); w.writeUnitFloat("Nose", "#Prc", outerGlow->noise()); w.writeUnitFloat("ShdN", "#Prc", outerGlow->jitter()); w.writeBoolean("AntA", outerGlow->antiAliased()); // FIXME: save curves w.writeCurve("TrnS", "Linear", QVector() << QPointF() << QPointF(255, 255)); w.writeUnitFloat("Inpr", "#Prc", outerGlow->range()); w.leaveDescriptor(); } // Inner Glow const psd_layer_effects_inner_glow *innerGlow = style->innerGlow(); if (innerGlow->effectEnabled()) { w.enterDescriptor("IrGl", "", "IrGl"); w.writeBoolean("enab", innerGlow->effectEnabled()); w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(innerGlow->blendMode())); if (innerGlow->fillType() == psd_fill_gradient && innerGlow->gradient()) { KoSegmentGradient *segmentGradient = dynamic_cast(innerGlow->gradient().data()); KoStopGradient *stopGradient = dynamic_cast(innerGlow->gradient().data()); if (segmentGradient && innerGlow->gradient()->valid()) { w.writeSegmentGradient("Grad", segmentGradient); } else if (stopGradient && innerGlow->gradient()->valid()) { w.writeStopGradient("Grad", stopGradient); } else { warnKrita << "WARNING: IG: Unknown gradient type!"; w.writeColor("Clr ", innerGlow->color()); } } else { w.writeColor("Clr ", innerGlow->color()); } w.writeUnitFloat("Opct", "#Prc", innerGlow->opacity()); w.writeEnum("GlwT", "BETE", techniqueToString(innerGlow->technique(), "BETE")); w.writeUnitFloat("Ckmt", "#Pxl", innerGlow->spread()); w.writeUnitFloat("blur", "#Pxl", innerGlow->size()); // NOTE: order is swapped in ASL! w.writeUnitFloat("ShdN", "#Prc", innerGlow->jitter()); w.writeUnitFloat("Nose", "#Prc", innerGlow->noise()); w.writeBoolean("AntA", innerGlow->antiAliased()); w.writeEnum("glwS", "IGSr", innerGlow->source() == psd_glow_center ? "SrcC" : "SrcE"); // FIXME: save curves w.writeCurve("TrnS", "Linear", QVector() << QPointF() << QPointF(255, 255)); w.writeUnitFloat("Inpr", "#Prc", innerGlow->range()); w.leaveDescriptor(); } // Bevel and Emboss const psd_layer_effects_bevel_emboss *bevelAndEmboss = style->bevelAndEmboss(); if (bevelAndEmboss->effectEnabled()) { w.enterDescriptor("ebbl", "", "ebbl"); w.writeBoolean("enab", bevelAndEmboss->effectEnabled()); w.writeEnum("hglM", "BlnM", compositeOpToBlendMode(bevelAndEmboss->highlightBlendMode())); w.writeColor("hglC", bevelAndEmboss->highlightColor()); w.writeUnitFloat("hglO", "#Prc", bevelAndEmboss->highlightOpacity()); w.writeEnum("sdwM", "BlnM", compositeOpToBlendMode(bevelAndEmboss->shadowBlendMode())); w.writeColor("sdwC", bevelAndEmboss->shadowColor()); w.writeUnitFloat("sdwO", "#Prc", bevelAndEmboss->shadowOpacity()); w.writeEnum("bvlT", "bvlT", techniqueToString(bevelAndEmboss->technique(), "bvlT")); w.writeEnum("bvlS", "BESl", bevelStyleToString(bevelAndEmboss->style())); w.writeBoolean("uglg", bevelAndEmboss->useGlobalLight()); w.writeUnitFloat("lagl", "#Ang", bevelAndEmboss->angle()); w.writeUnitFloat("Lald", "#Ang", bevelAndEmboss->altitude()); w.writeUnitFloat("srgR", "#Prc", bevelAndEmboss->depth()); w.writeUnitFloat("blur", "#Pxl", bevelAndEmboss->size()); w.writeEnum("bvlD", "BESs", bevelAndEmboss->direction() == psd_direction_up ? "In " : "Out "); // FIXME: save curves w.writeCurve("TrnS", "Linear", QVector() << QPointF() << QPointF(255, 255)); w.writeBoolean("antialiasGloss", bevelAndEmboss->glossAntiAliased()); w.writeUnitFloat("Sftn", "#Pxl", bevelAndEmboss->soften()); if (bevelAndEmboss->contourEnabled()) { w.writeBoolean("useShape", bevelAndEmboss->contourEnabled()); // FIXME: save curves w.writeCurve("MpgS", "Linear", QVector() << QPointF() << QPointF(255, 255)); w.writeBoolean("AntA", bevelAndEmboss->antiAliased()); w.writeUnitFloat("Inpr", "#Prc", bevelAndEmboss->contourRange()); } w.writeBoolean("useTexture", bevelAndEmboss->textureEnabled()); if (bevelAndEmboss->textureEnabled()) { w.writeBoolean("InvT", bevelAndEmboss->textureInvert()); w.writeBoolean("Algn", bevelAndEmboss->textureAlignWithLayer()); w.writeUnitFloat("Scl ", "#Prc", bevelAndEmboss->textureScale()); w.writeUnitFloat("textureDepth ", "#Prc", bevelAndEmboss->textureDepth()); w.writePatternRef("Ptrn", bevelAndEmboss->texturePattern(), fetchPatternUuidSafe(bevelAndEmboss->texturePattern(), patternToUuidMap)); w.writePhasePoint("phase", bevelAndEmboss->texturePhase()); } w.leaveDescriptor(); } // Satin const psd_layer_effects_satin *satin = style->satin(); if (satin->effectEnabled()) { w.enterDescriptor("ChFX", "", "ChFX"); w.writeBoolean("enab", satin->effectEnabled()); w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(satin->blendMode())); w.writeColor("Clr ", satin->color()); w.writeBoolean("AntA", satin->antiAliased()); w.writeBoolean("Invr", satin->invert()); w.writeUnitFloat("Opct", "#Prc", satin->opacity()); w.writeUnitFloat("lagl", "#Ang", satin->angle()); w.writeUnitFloat("Dstn", "#Pxl", satin->distance()); w.writeUnitFloat("blur", "#Pxl", satin->size()); // FIXME: save curves w.writeCurve("MpgS", "Linear", QVector() << QPointF() << QPointF(255, 255)); w.leaveDescriptor(); } const psd_layer_effects_color_overlay *colorOverlay = style->colorOverlay(); if (colorOverlay->effectEnabled()) { w.enterDescriptor("SoFi", "", "SoFi"); w.writeBoolean("enab", colorOverlay->effectEnabled()); w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(colorOverlay->blendMode())); w.writeUnitFloat("Opct", "#Prc", colorOverlay->opacity()); w.writeColor("Clr ", colorOverlay->color()); w.leaveDescriptor(); } // Gradient Overlay const psd_layer_effects_gradient_overlay *gradientOverlay = style->gradientOverlay(); KoSegmentGradient *segmentGradient = dynamic_cast(gradientOverlay->gradient().data()); KoStopGradient *stopGradient = dynamic_cast(gradientOverlay->gradient().data()); if (gradientOverlay->effectEnabled() && ((segmentGradient && segmentGradient->valid()) || (stopGradient && stopGradient->valid()))) { w.enterDescriptor("GrFl", "", "GrFl"); w.writeBoolean("enab", gradientOverlay->effectEnabled()); w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(gradientOverlay->blendMode())); w.writeUnitFloat("Opct", "#Prc", gradientOverlay->opacity()); - if (segmentGradient) { + if (segmentGradient && segmentGradient->valid()) { w.writeSegmentGradient("Grad", segmentGradient); - } else if (stopGradient) { + } else if (stopGradient && stopGradient->valid()) { w.writeStopGradient("Grad", stopGradient); } w.writeUnitFloat("Angl", "#Ang", gradientOverlay->angle()); w.writeEnum("Type", "GrdT", gradientTypeToString(gradientOverlay->style())); w.writeBoolean("Rvrs", gradientOverlay->reverse()); w.writeBoolean("Algn", gradientOverlay->alignWithLayer()); w.writeUnitFloat("Scl ", "#Prc", gradientOverlay->scale()); w.writeOffsetPoint("Ofst", gradientOverlay->gradientOffset()); // FIXME: Krita doesn't support dithering w.writeBoolean("Dthr", true/*gradientOverlay->dither()*/); w.leaveDescriptor(); } // Pattern Overlay const psd_layer_effects_pattern_overlay *patternOverlay = style->patternOverlay(); if (patternOverlay->effectEnabled()) { w.enterDescriptor("patternFill", "", "patternFill"); w.writeBoolean("enab", patternOverlay->effectEnabled()); w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(patternOverlay->blendMode())); w.writeUnitFloat("Opct", "#Prc", patternOverlay->opacity()); w.writePatternRef("Ptrn", patternOverlay->pattern(), fetchPatternUuidSafe(patternOverlay->pattern(), patternToUuidMap)); w.writeUnitFloat("Scl ", "#Prc", patternOverlay->scale()); w.writeBoolean("Algn", patternOverlay->alignWithLayer()); w.writePhasePoint("phase", patternOverlay->patternPhase()); w.leaveDescriptor(); } const psd_layer_effects_stroke *stroke = style->stroke(); if (stroke->effectEnabled()) { w.enterDescriptor("FrFX", "", "FrFX"); w.writeBoolean("enab", stroke->effectEnabled()); w.writeEnum("Styl", "FStl", strokePositionToString(stroke->position())); w.writeEnum("PntT", "FrFl", strokeFillTypeToString(stroke->fillType())); w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(stroke->blendMode())); w.writeUnitFloat("Opct", "#Prc", stroke->opacity()); w.writeUnitFloat("Sz ", "#Pxl", stroke->size()); if (stroke->fillType() == psd_fill_solid_color) { w.writeColor("Clr ", stroke->color()); } else if (stroke->fillType() == psd_fill_gradient) { KoSegmentGradient *segmentGradient = dynamic_cast(stroke->gradient().data()); KoStopGradient *stopGradient = dynamic_cast(stroke->gradient().data()); if (segmentGradient && segmentGradient->valid()) { w.writeSegmentGradient("Grad", segmentGradient); } else if (stopGradient && stopGradient->valid()) { w.writeStopGradient("Grad", stopGradient); } else { warnKrita << "WARNING: Stroke: Unknown gradient type!"; w.writeColor("Clr ", stroke->color()); } w.writeUnitFloat("Angl", "#Ang", stroke->angle()); w.writeEnum("Type", "GrdT", gradientTypeToString(stroke->style())); w.writeBoolean("Rvrs", stroke->reverse()); w.writeUnitFloat("Scl ", "#Prc", stroke->scale()); w.writeBoolean("Algn", stroke->alignWithLayer()); w.writeOffsetPoint("Ofst", stroke->gradientOffset()); // FIXME: Krita doesn't support dithering w.writeBoolean("Dthr", true/*stroke->dither()*/); } else if (stroke->fillType() == psd_fill_pattern) { w.writePatternRef("Ptrn", stroke->pattern(), fetchPatternUuidSafe(stroke->pattern(), patternToUuidMap)); w.writeUnitFloat("Scl ", "#Prc", stroke->scale()); w.writeBoolean("Lnkd", stroke->alignWithLayer()); w.writePhasePoint("phase", stroke->patternPhase()); } w.leaveDescriptor(); } w.leaveDescriptor(); w.leaveDescriptor(); } return w.document(); } inline QDomNode findNodeByClassId(const QString &classId, QDomNode parent) { return KisDomUtils::findElementByAttibute(parent, "node", "classId", classId); } void replaceAllChildren(QDomNode src, QDomNode dst) { QDomNode node; do { node = dst.lastChild(); dst.removeChild(node); } while(!node.isNull()); node = src.firstChild(); while(!node.isNull()) { dst.appendChild(node); node = src.firstChild(); } src.parentNode().removeChild(src); } QDomDocument KisAslLayerStyleSerializer::formPsdXmlDocument() const { QDomDocument doc = formXmlDocument(); QDomNode nullNode = findNodeByClassId("null", doc.documentElement()); QDomNode stylNode = findNodeByClassId("Styl", doc.documentElement()); QDomNode lefxNode = findNodeByClassId("Lefx", stylNode); replaceAllChildren(lefxNode, nullNode); return doc; } void KisAslLayerStyleSerializer::saveToDevice(QIODevice *device) { QDomDocument doc = formXmlDocument(); if (doc.isNull()) return ; KisAslWriter writer; writer.writeFile(device, doc); } void convertAndSetBlendMode(const QString &mode, boost::function setBlendMode) { QString compositeOp = COMPOSITE_OVER; if (mode == "Nrml") { compositeOp = COMPOSITE_OVER; } else if (mode == "Dslv") { compositeOp = COMPOSITE_DISSOLVE; } else if (mode == "Drkn") { compositeOp = COMPOSITE_DARKEN; } else if (mode == "Mltp") { compositeOp = COMPOSITE_MULT; } else if (mode == "CBrn") { compositeOp = COMPOSITE_BURN; } else if (mode == "linearBurn") { compositeOp = COMPOSITE_LINEAR_BURN; } else if (mode == "darkerColor") { compositeOp = COMPOSITE_DARKER_COLOR; } else if (mode == "Lghn") { compositeOp = COMPOSITE_LIGHTEN; } else if (mode == "Scrn") { compositeOp = COMPOSITE_SCREEN; } else if (mode == "CDdg") { compositeOp = COMPOSITE_DODGE; } else if (mode == "linearDodge") { compositeOp = COMPOSITE_LINEAR_DODGE; } else if (mode == "lighterColor") { compositeOp = COMPOSITE_LIGHTER_COLOR; } else if (mode == "Ovrl") { compositeOp = COMPOSITE_OVERLAY; } else if (mode == "SftL") { compositeOp = COMPOSITE_SOFT_LIGHT_PHOTOSHOP; } else if (mode == "HrdL") { compositeOp = COMPOSITE_HARD_LIGHT; } else if (mode == "vividLight") { compositeOp = COMPOSITE_VIVID_LIGHT; } else if (mode == "linearLight") { compositeOp = COMPOSITE_LINEAR_LIGHT; } else if (mode == "pinLight") { compositeOp = COMPOSITE_PIN_LIGHT; } else if (mode == "hardMix") { compositeOp = COMPOSITE_HARD_MIX_PHOTOSHOP; } else if (mode == "Dfrn") { compositeOp = COMPOSITE_DIFF; } else if (mode == "Xclu") { compositeOp = COMPOSITE_EXCLUSION; } else if (mode == "Sbtr") { compositeOp = COMPOSITE_SUBTRACT; } else if (mode == "divide") { compositeOp = COMPOSITE_DIVIDE; } else if (mode == "H ") { compositeOp = COMPOSITE_HUE; } else if (mode == "Strt") { compositeOp = COMPOSITE_SATURATION; } else if (mode == "Clr ") { compositeOp = COMPOSITE_COLOR; } else if (mode == "Lmns") { compositeOp = COMPOSITE_LUMINIZE; } else { dbgKrita << "Unknown blending mode:" << mode << "Returning COMPOSITE_OVER!"; } setBlendMode(compositeOp); } void convertAndSetCurve(const QString &name, const QVector &points, boost::function setCurveLookupTable) { Q_UNUSED(name); Q_UNUSED(points); Q_UNUSED(setCurveLookupTable); warnKrita << "convertAndSetBlendMode:" << "Curve conversion is not implemented yet"; } template void convertAndSetEnum(const QString &value, const QMap map, boost::function setMappedValue) { setMappedValue(map[value]); } inline QString _prepaddr(const QString &pref, const QString &addr) { return pref + addr; } #define CONN_TEXT_RADDR(addr, method, object, type) m_catcher.subscribeText(addr, std::bind(&type::method, object, _1)) #define CONN_COLOR(addr, method, object, type, prefix) m_catcher.subscribeColor(_prepaddr(prefix, addr), std::bind(&type::method, object, _1)) #define CONN_UNITF(addr, unit, method, object, type, prefix) m_catcher.subscribeUnitFloat(_prepaddr(prefix, addr), unit, std::bind(&type::method, object, _1)) #define CONN_BOOL(addr, method, object, type, prefix) m_catcher.subscribeBoolean(_prepaddr(prefix, addr), std::bind(&type::method, object, _1)) #define CONN_POINT(addr, method, object, type, prefix) m_catcher.subscribePoint(_prepaddr(prefix, addr), std::bind(&type::method, object, _1)) #define CONN_COMPOSITE_OP(addr, method, object, type, prefix) \ { \ boost::function setter = \ std::bind(&type::method, object, _1); \ m_catcher.subscribeEnum(_prepaddr(prefix, addr), "BlnM", std::bind(convertAndSetBlendMode, _1, setter)); \ } #define CONN_CURVE(addr, method, object, type, prefix) \ { \ boost::function setter = \ std::bind(&type::method, object, _1); \ m_catcher.subscribeCurve(_prepaddr(prefix, addr), std::bind(convertAndSetCurve, _1, _2, setter)); \ } #define CONN_ENUM(addr, tag, method, map, mapped_type, object, type, prefix) \ { \ boost::function setter = \ std::bind(&type::method, object, _1); \ m_catcher.subscribeEnum(_prepaddr(prefix, addr), tag, std::bind(convertAndSetEnum, _1, map, setter)); \ } #define CONN_GRADIENT(addr, method, object, type, prefix) \ { \ m_catcher.subscribeGradient(_prepaddr(prefix, addr), std::bind(&type::method, object, _1)); \ } #define CONN_PATTERN(addr, method, object, type, prefix) \ { \ boost::function setter = \ std::bind(&type::method, object, _1); \ m_catcher.subscribePatternRef(_prepaddr(prefix, addr), std::bind(&KisAslLayerStyleSerializer::assignPatternObject, this, _1, _2, setter)); \ } void KisAslLayerStyleSerializer::registerPatternObject(const KoPattern *pattern) { QString uuid = KisAslWriterUtils::getPatternUuidLazy(pattern); if (m_patternsStore.contains(uuid)) { warnKrita << "WARNING: ASL style contains a duplicated pattern!" << ppVar(pattern->name()) << ppVar(m_patternsStore[uuid]->name()); } else { KoResourceServer *server = KoResourceServerProvider::instance()->patternServer(); KoPattern *patternToAdd = server->resourceByMD5(pattern->md5()); if (!patternToAdd) { patternToAdd = pattern->clone(); server->addResource(patternToAdd, false); } m_patternsStore.insert(uuid, patternToAdd); } } void KisAslLayerStyleSerializer::assignPatternObject(const QString &patternUuid, const QString &patternName, boost::function setPattern) { Q_UNUSED(patternName); KoPattern *pattern = m_patternsStore[patternUuid]; if (!pattern) { warnKrita << "WARNING: ASL style contains inexistent pattern reference!"; QImage dumbImage(32, 32, QImage::Format_ARGB32); dumbImage.fill(Qt::red); KoPattern *dumbPattern = new KoPattern(dumbImage, "invalid", ""); registerPatternObject(dumbPattern); pattern = dumbPattern; } setPattern(pattern); } class FillStylesCorrector { public: static void correct(KisPSDLayerStyle *style) { correctWithoutPattern(style->outerGlow()); correctWithoutPattern(style->innerGlow()); correctWithPattern(style->stroke()); } private: template static void correctWithPattern(T *config) { if (config->pattern()) { config->setFillType(psd_fill_pattern); } else if (config->gradient()) { config->setFillType(psd_fill_gradient); } else { config->setFillType(psd_fill_solid_color); } } template static void correctWithoutPattern(T *config) { if (config->gradient()) { config->setFillType(psd_fill_gradient); } else { config->setFillType(psd_fill_solid_color); } } }; void KisAslLayerStyleSerializer::connectCatcherToStyle(KisPSDLayerStyle *style, const QString &prefix) { CONN_TEXT_RADDR("/null/Nm ", setName, style, KisPSDLayerStyle); CONN_TEXT_RADDR("/null/Idnt", setPsdUuid, style, KisPSDLayerStyle); CONN_BOOL("/masterFXSwitch", setEnabled, style, KisPSDLayerStyle, prefix); psd_layer_effects_drop_shadow *dropShadow = style->dropShadow(); CONN_COMPOSITE_OP("/DrSh/Md ", setBlendMode, dropShadow, psd_layer_effects_drop_shadow, prefix); CONN_COLOR("/DrSh/Clr ", setColor, dropShadow, psd_layer_effects_drop_shadow, prefix); CONN_UNITF("/DrSh/Opct", "#Prc", setOpacity, dropShadow, psd_layer_effects_drop_shadow, prefix); CONN_UNITF("/DrSh/lagl", "#Ang", setAngle, dropShadow, psd_layer_effects_drop_shadow, prefix); CONN_UNITF("/DrSh/Dstn", "#Pxl", setDistance, dropShadow, psd_layer_effects_drop_shadow, prefix); CONN_UNITF("/DrSh/Ckmt", "#Pxl", setSpread, dropShadow, psd_layer_effects_drop_shadow, prefix); CONN_UNITF("/DrSh/blur", "#Pxl", setSize, dropShadow, psd_layer_effects_drop_shadow, prefix); CONN_UNITF("/DrSh/Nose", "#Prc", setNoise, dropShadow, psd_layer_effects_drop_shadow, prefix); CONN_BOOL("/DrSh/enab", setEffectEnabled, dropShadow, psd_layer_effects_drop_shadow, prefix); CONN_BOOL("/DrSh/uglg", setUseGlobalLight, dropShadow, psd_layer_effects_drop_shadow, prefix); CONN_BOOL("/DrSh/AntA", setAntiAliased, dropShadow, psd_layer_effects_drop_shadow, prefix); CONN_BOOL("/DrSh/layerConceals", setKnocksOut, dropShadow, psd_layer_effects_drop_shadow, prefix); CONN_CURVE("/DrSh/TrnS", setContourLookupTable, dropShadow, psd_layer_effects_drop_shadow, prefix); psd_layer_effects_inner_shadow *innerShadow = style->innerShadow(); CONN_COMPOSITE_OP("/IrSh/Md ", setBlendMode, innerShadow, psd_layer_effects_inner_shadow, prefix); CONN_COLOR("/IrSh/Clr ", setColor, innerShadow, psd_layer_effects_inner_shadow, prefix); CONN_UNITF("/IrSh/Opct", "#Prc", setOpacity, innerShadow, psd_layer_effects_inner_shadow, prefix); CONN_UNITF("/IrSh/lagl", "#Ang", setAngle, innerShadow, psd_layer_effects_inner_shadow, prefix); CONN_UNITF("/IrSh/Dstn", "#Pxl", setDistance, innerShadow, psd_layer_effects_inner_shadow, prefix); CONN_UNITF("/IrSh/Ckmt", "#Pxl", setSpread, innerShadow, psd_layer_effects_inner_shadow, prefix); CONN_UNITF("/IrSh/blur", "#Pxl", setSize, innerShadow, psd_layer_effects_inner_shadow, prefix); CONN_UNITF("/IrSh/Nose", "#Prc", setNoise, innerShadow, psd_layer_effects_inner_shadow, prefix); CONN_BOOL("/IrSh/enab", setEffectEnabled, innerShadow, psd_layer_effects_inner_shadow, prefix); CONN_BOOL("/IrSh/uglg", setUseGlobalLight, innerShadow, psd_layer_effects_inner_shadow, prefix); CONN_BOOL("/IrSh/AntA", setAntiAliased, innerShadow, psd_layer_effects_inner_shadow, prefix); CONN_CURVE("/IrSh/TrnS", setContourLookupTable, innerShadow, psd_layer_effects_inner_shadow, prefix); psd_layer_effects_outer_glow *outerGlow = style->outerGlow(); CONN_COMPOSITE_OP("/OrGl/Md ", setBlendMode, outerGlow, psd_layer_effects_outer_glow, prefix); CONN_COLOR("/OrGl/Clr ", setColor, outerGlow, psd_layer_effects_outer_glow, prefix); CONN_UNITF("/OrGl/Opct", "#Prc", setOpacity, outerGlow, psd_layer_effects_outer_glow, prefix); CONN_UNITF("/OrGl/Ckmt", "#Pxl", setSpread, outerGlow, psd_layer_effects_outer_glow, prefix); CONN_UNITF("/OrGl/blur", "#Pxl", setSize, outerGlow, psd_layer_effects_outer_glow, prefix); CONN_UNITF("/OrGl/Nose", "#Prc", setNoise, outerGlow, psd_layer_effects_outer_glow, prefix); CONN_BOOL("/OrGl/enab", setEffectEnabled, outerGlow, psd_layer_effects_outer_glow, prefix); CONN_BOOL("/OrGl/AntA", setAntiAliased, outerGlow, psd_layer_effects_outer_glow, prefix); CONN_CURVE("/OrGl/TrnS", setContourLookupTable, outerGlow, psd_layer_effects_outer_glow, prefix); QMap fillTechniqueMap; fillTechniqueMap.insert("PrBL", psd_technique_precise); fillTechniqueMap.insert("SfBL", psd_technique_softer); CONN_ENUM("/OrGl/GlwT", "BETE", setTechnique, fillTechniqueMap, psd_technique_type, outerGlow, psd_layer_effects_outer_glow, prefix); CONN_GRADIENT("/OrGl/Grad", setGradient, outerGlow, psd_layer_effects_outer_glow, prefix); CONN_UNITF("/OrGl/Inpr", "#Prc", setRange, outerGlow, psd_layer_effects_outer_glow, prefix); CONN_UNITF("/OrGl/ShdN", "#Prc", setJitter, outerGlow, psd_layer_effects_outer_glow, prefix); psd_layer_effects_inner_glow *innerGlow = style->innerGlow(); CONN_COMPOSITE_OP("/IrGl/Md ", setBlendMode, innerGlow, psd_layer_effects_inner_glow, prefix); CONN_COLOR("/IrGl/Clr ", setColor, innerGlow, psd_layer_effects_inner_glow, prefix); CONN_UNITF("/IrGl/Opct", "#Prc", setOpacity, innerGlow, psd_layer_effects_inner_glow, prefix); CONN_UNITF("/IrGl/Ckmt", "#Pxl", setSpread, innerGlow, psd_layer_effects_inner_glow, prefix); CONN_UNITF("/IrGl/blur", "#Pxl", setSize, innerGlow, psd_layer_effects_inner_glow, prefix); CONN_UNITF("/IrGl/Nose", "#Prc", setNoise, innerGlow, psd_layer_effects_inner_glow, prefix); CONN_BOOL("/IrGl/enab", setEffectEnabled, innerGlow, psd_layer_effects_inner_glow, prefix); CONN_BOOL("/IrGl/AntA", setAntiAliased, innerGlow, psd_layer_effects_inner_glow, prefix); CONN_CURVE("/IrGl/TrnS", setContourLookupTable, innerGlow, psd_layer_effects_inner_glow, prefix); CONN_ENUM("/IrGl/GlwT", "BETE", setTechnique, fillTechniqueMap, psd_technique_type, innerGlow, psd_layer_effects_inner_glow, prefix); CONN_GRADIENT("/IrGl/Grad", setGradient, innerGlow, psd_layer_effects_inner_glow, prefix); CONN_UNITF("/IrGl/Inpr", "#Prc", setRange, innerGlow, psd_layer_effects_inner_glow, prefix); CONN_UNITF("/IrGl/ShdN", "#Prc", setJitter, innerGlow, psd_layer_effects_inner_glow, prefix); QMap glowSourceMap; glowSourceMap.insert("SrcC", psd_glow_center); glowSourceMap.insert("SrcE", psd_glow_edge); CONN_ENUM("/IrGl/glwS", "IGSr", setSource, glowSourceMap, psd_glow_source, innerGlow, psd_layer_effects_inner_glow, prefix); psd_layer_effects_satin *satin = style->satin(); CONN_COMPOSITE_OP("/ChFX/Md ", setBlendMode, satin, psd_layer_effects_satin, prefix); CONN_COLOR("/ChFX/Clr ", setColor, satin, psd_layer_effects_satin, prefix); CONN_UNITF("/ChFX/Opct", "#Prc", setOpacity, satin, psd_layer_effects_satin, prefix); CONN_UNITF("/ChFX/lagl", "#Ang", setAngle, satin, psd_layer_effects_satin, prefix); CONN_UNITF("/ChFX/Dstn", "#Pxl", setDistance, satin, psd_layer_effects_satin, prefix); CONN_UNITF("/ChFX/blur", "#Pxl", setSize, satin, psd_layer_effects_satin, prefix); CONN_BOOL("/ChFX/enab", setEffectEnabled, satin, psd_layer_effects_satin, prefix); CONN_BOOL("/ChFX/AntA", setAntiAliased, satin, psd_layer_effects_satin, prefix); CONN_BOOL("/ChFX/Invr", setInvert, satin, psd_layer_effects_satin, prefix); CONN_CURVE("/ChFX/MpgS", setContourLookupTable, satin, psd_layer_effects_satin, prefix); psd_layer_effects_color_overlay *colorOverlay = style->colorOverlay(); CONN_COMPOSITE_OP("/SoFi/Md ", setBlendMode, colorOverlay, psd_layer_effects_color_overlay, prefix); CONN_COLOR("/SoFi/Clr ", setColor, colorOverlay, psd_layer_effects_color_overlay, prefix); CONN_UNITF("/SoFi/Opct", "#Prc", setOpacity, colorOverlay, psd_layer_effects_color_overlay, prefix); CONN_BOOL("/SoFi/enab", setEffectEnabled, colorOverlay, psd_layer_effects_color_overlay, prefix); psd_layer_effects_gradient_overlay *gradientOverlay = style->gradientOverlay(); CONN_COMPOSITE_OP("/GrFl/Md ", setBlendMode, gradientOverlay, psd_layer_effects_gradient_overlay, prefix); CONN_UNITF("/GrFl/Opct", "#Prc", setOpacity, gradientOverlay, psd_layer_effects_gradient_overlay, prefix); CONN_UNITF("/GrFl/Scl ", "#Prc", setScale, gradientOverlay, psd_layer_effects_gradient_overlay, prefix); CONN_UNITF("/GrFl/Angl", "#Ang", setAngle, gradientOverlay, psd_layer_effects_gradient_overlay, prefix); CONN_BOOL("/GrFl/enab", setEffectEnabled, gradientOverlay, psd_layer_effects_gradient_overlay, prefix); // CONN_BOOL("/GrFl/Dthr", setDitherNotImplemented, gradientOverlay, psd_layer_effects_gradient_overlay, prefix); CONN_BOOL("/GrFl/Rvrs", setReverse, gradientOverlay, psd_layer_effects_gradient_overlay, prefix); CONN_BOOL("/GrFl/Algn", setAlignWithLayer, gradientOverlay, psd_layer_effects_gradient_overlay, prefix); CONN_POINT("/GrFl/Ofst", setGradientOffset, gradientOverlay, psd_layer_effects_gradient_overlay, prefix); CONN_GRADIENT("/GrFl/Grad", setGradient, gradientOverlay, psd_layer_effects_gradient_overlay, prefix); QMap gradientStyleMap; gradientStyleMap.insert("Lnr ", psd_gradient_style_linear); gradientStyleMap.insert("Rdl ", psd_gradient_style_radial); gradientStyleMap.insert("Angl", psd_gradient_style_angle); gradientStyleMap.insert("Rflc", psd_gradient_style_reflected); gradientStyleMap.insert("Dmnd", psd_gradient_style_diamond); CONN_ENUM("/GrFl/Type", "GrdT", setStyle, gradientStyleMap, psd_gradient_style, gradientOverlay, psd_layer_effects_gradient_overlay, prefix); psd_layer_effects_pattern_overlay *patternOverlay = style->patternOverlay(); CONN_BOOL("/patternFill/enab", setEffectEnabled, patternOverlay, psd_layer_effects_pattern_overlay, prefix); CONN_COMPOSITE_OP("/patternFill/Md ", setBlendMode, patternOverlay, psd_layer_effects_pattern_overlay, prefix); CONN_UNITF("/patternFill/Opct", "#Prc", setOpacity, patternOverlay, psd_layer_effects_pattern_overlay, prefix); CONN_PATTERN("/patternFill/Ptrn", setPattern, patternOverlay, psd_layer_effects_pattern_overlay, prefix); CONN_UNITF("/patternFill/Scl ", "#Prc", setScale, patternOverlay, psd_layer_effects_pattern_overlay, prefix); CONN_BOOL("/patternFill/Algn", setAlignWithLayer, patternOverlay, psd_layer_effects_pattern_overlay, prefix); CONN_POINT("/patternFill/phase", setPatternPhase, patternOverlay, psd_layer_effects_pattern_overlay, prefix); psd_layer_effects_stroke *stroke = style->stroke(); CONN_COMPOSITE_OP("/FrFX/Md ", setBlendMode, stroke, psd_layer_effects_stroke, prefix); CONN_BOOL("/FrFX/enab", setEffectEnabled, stroke, psd_layer_effects_stroke, prefix); CONN_UNITF("/FrFX/Opct", "#Prc", setOpacity, stroke, psd_layer_effects_stroke, prefix); CONN_UNITF("/FrFX/Sz ", "#Pxl", setSize, stroke, psd_layer_effects_stroke, prefix); QMap strokeStyleMap; strokeStyleMap.insert("OutF", psd_stroke_outside); strokeStyleMap.insert("InsF", psd_stroke_inside); strokeStyleMap.insert("CtrF", psd_stroke_center); CONN_ENUM("/FrFX/Styl", "FStl", setPosition, strokeStyleMap, psd_stroke_position, stroke, psd_layer_effects_stroke, prefix); QMap strokeFillType; strokeFillType.insert("SClr", psd_fill_solid_color); strokeFillType.insert("GrFl", psd_fill_gradient); strokeFillType.insert("Ptrn", psd_fill_pattern); CONN_ENUM("/FrFX/PntT", "FrFl", setFillType, strokeFillType, psd_fill_type, stroke, psd_layer_effects_stroke, prefix); // Color type CONN_COLOR("/FrFX/Clr ", setColor, stroke, psd_layer_effects_stroke, prefix); // Gradient Type CONN_GRADIENT("/FrFX/Grad", setGradient, stroke, psd_layer_effects_stroke, prefix); CONN_UNITF("/FrFX/Angl", "#Ang", setAngle, stroke, psd_layer_effects_stroke, prefix); CONN_UNITF("/FrFX/Scl ", "#Prc", setScale, stroke, psd_layer_effects_stroke, prefix); CONN_ENUM("/FrFX/Type", "GrdT", setStyle, gradientStyleMap, psd_gradient_style, stroke, psd_layer_effects_stroke, prefix); CONN_BOOL("/FrFX/Rvrs", setReverse, stroke, psd_layer_effects_stroke, prefix); CONN_BOOL("/FrFX/Algn", setAlignWithLayer, stroke, psd_layer_effects_stroke, prefix); CONN_POINT("/FrFX/Ofst", setGradientOffset, stroke, psd_layer_effects_stroke, prefix); // CONN_BOOL("/FrFX/Dthr", setDitherNotImplemented, stroke, psd_layer_effects_stroke, prefix); // Pattern type CONN_PATTERN("/FrFX/Ptrn", setPattern, stroke, psd_layer_effects_stroke, prefix); CONN_BOOL("/FrFX/Lnkd", setAlignWithLayer, stroke, psd_layer_effects_stroke, prefix); // yes, we share the params... CONN_POINT("/FrFX/phase", setPatternPhase, stroke, psd_layer_effects_stroke, prefix); psd_layer_effects_bevel_emboss *bevelAndEmboss = style->bevelAndEmboss(); CONN_BOOL("/ebbl/enab", setEffectEnabled, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_COMPOSITE_OP("/ebbl/hglM", setHighlightBlendMode, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_COLOR("/ebbl/hglC", setHighlightColor, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_UNITF("/ebbl/hglO", "#Prc", setHighlightOpacity, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_COMPOSITE_OP("/ebbl/sdwM", setShadowBlendMode, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_COLOR("/ebbl/sdwC", setShadowColor, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_UNITF("/ebbl/sdwO", "#Prc", setShadowOpacity, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); QMap bevelTechniqueMap; bevelTechniqueMap.insert("PrBL", psd_technique_precise); bevelTechniqueMap.insert("SfBL", psd_technique_softer); bevelTechniqueMap.insert("Slmt", psd_technique_slope_limit); CONN_ENUM("/ebbl/bvlT", "bvlT", setTechnique, bevelTechniqueMap, psd_technique_type, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); QMap bevelStyleMap; bevelStyleMap.insert("OtrB", psd_bevel_outer_bevel); bevelStyleMap.insert("InrB", psd_bevel_inner_bevel); bevelStyleMap.insert("Embs", psd_bevel_emboss); bevelStyleMap.insert("PlEb", psd_bevel_pillow_emboss); bevelStyleMap.insert("strokeEmboss", psd_bevel_stroke_emboss); CONN_ENUM("/ebbl/bvlS", "BESl", setStyle, bevelStyleMap, psd_bevel_style, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_BOOL("/ebbl/uglg", setUseGlobalLight, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_UNITF("/ebbl/lagl", "#Ang", setAngle, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_UNITF("/ebbl/Lald", "#Ang", setAltitude, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_UNITF("/ebbl/srgR", "#Prc", setDepth, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_UNITF("/ebbl/blur", "#Pxl", setSize, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); QMap bevelDirectionMap; bevelDirectionMap.insert("In ", psd_direction_up); bevelDirectionMap.insert("Out ", psd_direction_down); CONN_ENUM("/ebbl/bvlD", "BESs", setDirection, bevelDirectionMap, psd_direction, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_CURVE("/ebbl/TrnS", setContourLookupTable, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_BOOL("/ebbl/antialiasGloss", setGlossAntiAliased, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_UNITF("/ebbl/Sftn", "#Pxl", setSoften, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); // Use shape mode CONN_BOOL("/ebbl/useShape", setContourEnabled, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_CURVE("/ebbl/MpgS", setGlossContourLookupTable, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_BOOL("/ebbl/AntA", setAntiAliased, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_UNITF("/ebbl/Inpr", "#Prc", setContourRange, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); // Use texture mode CONN_BOOL("/ebbl/useTexture", setTextureEnabled, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_BOOL("/ebbl/InvT", setTextureInvert, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_BOOL("/ebbl/Algn", setTextureAlignWithLayer, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_UNITF("/ebbl/Scl ", "#Prc", setTextureScale, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_UNITF("/ebbl/textureDepth", "#Prc", setTextureDepth, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_PATTERN("/ebbl/Ptrn", setTexturePattern, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_POINT("/ebbl/phase", setTexturePhase, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); } void KisAslLayerStyleSerializer::newStyleStarted(bool isPsdStructure) { m_stylesVector.append(toQShared(new KisPSDLayerStyle())); KisPSDLayerStyle *currentStyle = m_stylesVector.last().data(); psd_layer_effects_context *context = currentStyle->context(); context->keep_original = 0; QString prefix = isPsdStructure ? "/null" : "/Styl/Lefx"; connectCatcherToStyle(currentStyle, prefix); } void KisAslLayerStyleSerializer::readFromDevice(QIODevice *device) { m_stylesVector.clear(); m_catcher.subscribePattern("/Patterns/KisPattern", std::bind(&KisAslLayerStyleSerializer::registerPatternObject, this, _1)); m_catcher.subscribeNewStyleStarted(std::bind(&KisAslLayerStyleSerializer::newStyleStarted, this, false)); KisAslReader reader; QDomDocument doc = reader.readFile(device); //dbgKrita << ppVar(doc.toString()); //KisAslObjectCatcher c2; KisAslXmlParser parser; parser.parseXML(doc, m_catcher); // correct all the layer styles Q_FOREACH (KisPSDLayerStyleSP style, m_stylesVector) { FillStylesCorrector::correct(style.data()); } } void KisAslLayerStyleSerializer::registerPSDPattern(const QDomDocument &doc) { KisAslCallbackObjectCatcher catcher; catcher.subscribePattern("/Patterns/KisPattern", std::bind(&KisAslLayerStyleSerializer::registerPatternObject, this, _1)); //KisAslObjectCatcher c2; KisAslXmlParser parser; parser.parseXML(doc, catcher); } void KisAslLayerStyleSerializer::readFromPSDXML(const QDomDocument &doc) { // The caller prepares the document using th efollowing code // // KisAslReader reader; // QDomDocument doc = reader.readLfx2PsdSection(device); m_stylesVector.clear(); //m_catcher.subscribePattern("/Patterns/KisPattern", std::bind(&KisAslLayerStyleSerializer::registerPatternObject, this, _1)); m_catcher.subscribeNewStyleStarted(std::bind(&KisAslLayerStyleSerializer::newStyleStarted, this, true)); //KisAslObjectCatcher c2; KisAslXmlParser parser; parser.parseXML(doc, m_catcher); // correct all the layer styles Q_FOREACH (KisPSDLayerStyleSP style, m_stylesVector) { FillStylesCorrector::correct(style.data()); } }