File indexing completed on 2024-05-12 15:56:09

0001 /*
0002  *  SPDX-FileCopyrightText: 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_abr_translator.h"
0008 #include <kis_debug.h>
0009 #include <QStringList>
0010 #include <kis_dom_utils.h>
0011 
0012 KisAbrTranslator::KisAbrTranslator()
0013 {
0014     init();
0015 }
0016 
0017 KisAbrTranslator::~KisAbrTranslator()
0018 {
0019 
0020 }
0021 
0022 void KisAbrTranslator::init()
0023 {
0024     m_root = m_doc.createElement("Preset");
0025     m_root.setAttribute("paintopid", "paintbrush");
0026 }
0027 
0028 void KisAbrTranslator::addEntry(const QString& attributeName, const QString& type, const QString& value)
0029 {
0030     // setup object type
0031     // shape dynamics is not separated in the Objc so workaround by attribute name
0032     if (type == ABR_OBJECT ||
0033             attributeName == ABR_USE_TIP_DYNAMICS ||
0034             attributeName == ABR_USE_SCATTER) {
0035 
0036         if (m_currentObjectName == ABR_DUAL_BRUSH && attributeName == OBJECT_NAME_BRUSH) {
0037             m_currentObjectName = ABR_DUAL_BRUSH + '_' + attributeName;
0038         }
0039         else {
0040             m_currentObjectName = attributeName;
0041         }
0042 
0043         dbgKrita << "---- Object type changed to " << m_currentObjectName;
0044     }
0045 
0046     // behaviour according object's attribute name
0047     if (m_currentObjectName == ABR_PRESET_START) {
0048         if (attributeName == ABR_PRESET_START) {
0049             clean();
0050         }
0051         else if (attributeName == ABR_PRESET_NAME) {
0052             m_root.setAttribute("name", value);
0053         }
0054         else {
0055             //dbgKrita << "--Unknown attribute: " << attributeName;
0056         }
0057     }
0058     else if (m_currentObjectName == OBJECT_NAME_BRUSH) {
0059         m_abrBrushProperties.setupProperty(attributeName, type, value);
0060         // this is not object name but shape dynamics does not start with objc :/
0061         // those objects has been merged with shape attributes due to serialization
0062         // e.g. minumumDiameter belongs to ABR_SZVR and is ABR_USE_TIP_DYNAMICS object
0063     }
0064     else if (m_currentObjectName == ABR_USE_TIP_DYNAMICS ||
0065                m_currentObjectName == ABR_SZVR ||
0066                m_currentObjectName == ABR_ANGLE_DYNAMICS ||
0067                m_currentObjectName == ABR_ROUNDNESS_DYNAMICS) {
0068         m_abrTipDynamics.setupProperty(attributeName, type, value);
0069     }
0070     else if (m_currentObjectName == ABR_USE_SCATTER) {
0071         // TODO
0072     }
0073     else {
0074         dbgKrita << "Unknown attribute of " << m_currentObjectName << "| " << attributeName << type << value << " |";
0075     }
0076 
0077 
0078 
0079 }
0080 
0081 void KisAbrTranslator::finishPreset()
0082 {
0083     m_abrBrushProperties.toXML(m_doc, m_root);
0084     m_abrTipDynamics.toXML(m_doc, m_root);
0085     m_doc.appendChild(m_root);
0086 
0087     m_abrTipDynamics.reset();
0088     m_currentObjectName.clear();
0089 }
0090 
0091 
0092 QString KisAbrTranslator::toString()
0093 {
0094     return m_doc.toString();
0095 }
0096 
0097 void KisAbrTranslator::clean()
0098 {
0099     m_doc.clear();
0100     m_root.clear();
0101     init();
0102 }
0103 
0104 
0105 void AbrBrushProperties::setupProperty(const QString& attributeName, const QString& type, const QString& value)
0106 {
0107     Q_UNUSED(type);
0108     double valueDbl = 0.0;
0109     QStringList list;
0110     if (attributeName == ABR_BRUSH_DIAMETER ||
0111             attributeName == ABR_BRUSH_HARDNESS ||
0112             attributeName == ABR_BRUSH_ANGLE ||
0113             attributeName == ABR_BRUSH_ROUNDNESS ||
0114             attributeName == ABR_BRUSH_SPACING ||
0115             attributeName == OBJECT_NAME_BRUSH) {
0116         list = value.split(' ');
0117         //e.g. "#Pxl 10" -> ['#Pxl','10']
0118         Q_ASSERT(list.count() == 2);
0119         valueDbl = KisDomUtils::toDouble(list.at(1));
0120     }
0121 
0122     if (attributeName == OBJECT_NAME_BRUSH) {
0123         m_brushType = list.at(0);
0124     }
0125     else if (attributeName == ABR_BRUSH_DIAMETER) {
0126         m_diameter = valueDbl;
0127     }
0128     else if (attributeName == ABR_BRUSH_HARDNESS) {
0129         m_hardness = valueDbl;
0130     }
0131     else if (attributeName == ABR_BRUSH_ANGLE) {
0132         m_angle = valueDbl;
0133     }
0134     else if (attributeName == ABR_BRUSH_ROUNDNESS) {
0135         m_roundness = valueDbl;
0136     }
0137     else if (attributeName == ABR_BRUSH_SPACING) {
0138         m_spacing = valueDbl;
0139     }
0140     else if (attributeName == ABR_BRUSH_INTR) {
0141         m_intr = value.toInt();
0142     }
0143     else if (attributeName == ABR_FLIP_X) {
0144         m_flipX = value.toInt();
0145     }
0146     else if (attributeName == ABR_FLIP_Y) {
0147         m_flipY = value.toInt();
0148     }
0149     else {
0150         dbgKrita << "Unknown attribute " << attributeName;
0151     }
0152 }
0153 
0154 // <param name="brush_definition">
0155 //     <![CDATA[
0156 //     <Brush type="auto_brush" spacing="0.1" angle="0">
0157 //         <MaskGenerator radius="5" ratio="1" type="circle" vfade="0.5" spikes="2" hfade="0.5"/>
0158 //     </Brush> ]]>
0159 // </param>
0160 void AbrBrushProperties::toXML(QDomDocument& doc, QDomElement& root) const
0161 {
0162     if (m_brushType != BRUSH_TYPE_COMPUTED) {
0163         dbgKrita << m_brushType << "saved as computed brush...";
0164     }
0165 
0166     QDomDocument d;
0167     QDomElement e = d.createElement("Brush");
0168 
0169     QDomElement shapeElement = d.createElement("MaskGenerator");
0170     shapeElement.setAttribute("radius", KisDomUtils::toString(m_diameter * 0.5)); // radius == diameter / 2
0171     shapeElement.setAttribute("ratio", KisDomUtils::toString(m_roundness / 100.0)); // roundness in (0..100) to ratio in (0.0..1.0)
0172     shapeElement.setAttribute("hfade", KisDomUtils::toString(m_hardness / 100.0)); // same here
0173     shapeElement.setAttribute("vfade", KisDomUtils::toString(m_hardness / 100.0)); // and here too
0174     shapeElement.setAttribute("spikes", KisDomUtils::toString(2)); // just circle so far
0175     shapeElement.setAttribute("type", "circle");
0176     e.appendChild(shapeElement);
0177 
0178     e.setAttribute("type", "auto_brush");
0179     e.setAttribute("spacing", KisDomUtils::toString(m_spacing / 100.0)); // spacing from 0..1000 to
0180     e.setAttribute("angle", KisDomUtils::toString(m_angle < 0 ? m_angle + 360.0 : m_angle)); // angle from -180..180 to 0..360
0181     e.setAttribute("randomness", "0");  // default here
0182     d.appendChild(e);
0183 
0184     QDomElement elementParam = doc.createElement("param");
0185     elementParam.setAttribute("name", "brush_definition");
0186     QDomText text = doc.createCDATASection(d.toString());
0187     elementParam.appendChild(text);
0188     root.appendChild(elementParam);
0189 
0190 }
0191 
0192 AbrTipDynamicsProperties::AbrTipDynamicsProperties()
0193 {
0194     m_groups[ ABR_SZVR ] = &m_sizeVarianceProperties;
0195     m_groups[ ABR_ANGLE_DYNAMICS ] = &m_angleProperties;
0196     m_groups[ ABR_ROUNDNESS_DYNAMICS ] = &m_RoundnessProperties;
0197     Q_ASSERT(m_groupType.isNull());
0198 }
0199 
0200 
0201 void AbrTipDynamicsProperties::setupProperty(const QString& attributeName, const QString& type, const QString& value)
0202 {
0203     if (type == ABR_OBJECT) {
0204         if (!m_groups.contains(attributeName)) {
0205             dbgKrita << "Unknown " << type << " in Tip dynamics called " << attributeName << " : " << value;
0206         }
0207         else {
0208             m_groupType = attributeName;
0209         }
0210         return;
0211     }
0212 
0213     Q_UNUSED(type);
0214     double valueDbl = 0.0;
0215     QStringList list;
0216     if (attributeName == ABR_TIP_DYNAMICS_MINUMUM_DIAMETER ||
0217             attributeName == ABR_TIP_DYNAMICS_MINUMUM_ROUNDNESS ||
0218             attributeName == ABR_TIP_DYNAMICS_TILT_SCALE
0219        ) {
0220         list = value.split(' ');
0221         //e.g. "#Pxl 10" -> ['#Pxl','10']
0222         Q_ASSERT(list.count() == 2);
0223         valueDbl = KisDomUtils::toDouble(list.at(1));
0224     }
0225 
0226     if (m_groupType.isNull()) {
0227 
0228         if (attributeName == ABR_USE_TIP_DYNAMICS) {
0229             m_useTipDynamics = value.toInt();
0230         }
0231         else if (attributeName == ABR_FLIP_X) {
0232             m_flipX = value.toInt();
0233         }
0234         else if (attributeName == ABR_FLIP_Y) {
0235             m_flipY = value.toInt();
0236         }
0237         else if (attributeName == ABR_TIP_DYNAMICS_MINUMUM_DIAMETER) {
0238             m_minumumDiameter = valueDbl;
0239         }
0240         else if (attributeName == ABR_TIP_DYNAMICS_MINUMUM_ROUNDNESS) {
0241             m_minumumRoundness = valueDbl;
0242         }
0243         else if (attributeName == ABR_TIP_DYNAMICS_TILT_SCALE) {
0244             m_tiltScale = valueDbl;
0245         }
0246         else {
0247             dbgKrita << "Unknown attribute for tip dynamics" << attributeName;
0248         }
0249 
0250     } else {
0251         m_groups[ m_groupType ]->setupProperty(attributeName, type, value);
0252     }
0253 }
0254 
0255 void AbrTipDynamicsProperties::toXML(QDomDocument& doc, QDomElement& root) const
0256 {
0257     QDomElement el = doc.createElement("shape_dynamics");
0258     el.setAttribute("useTipDynamics", m_useTipDynamics);
0259     el.setAttribute("flipX", m_flipX);
0260     el.setAttribute("flipY", m_flipY);
0261 
0262     root.appendChild(el);
0263 
0264     el = doc.createElement("angleDynamics");
0265 
0266     el.setAttribute("angleJitter", KisDomUtils::toString(m_angleProperties.m_sizeJitter));
0267     el.setAttribute("angleController", KisDomUtils::toString(m_angleProperties.m_bVTy));
0268     el.setAttribute("angleFadeStep", KisDomUtils::toString(m_angleProperties.m_fadeStep));
0269     root.appendChild(el);
0270 
0271     el = doc.createElement("roundnessDynamics");
0272     el.setAttribute("minumumRoundness", KisDomUtils::toString(m_minumumRoundness));
0273     el.setAttribute("roundnessJitter", KisDomUtils::toString(m_RoundnessProperties.m_sizeJitter));
0274     el.setAttribute("roundnessController", KisDomUtils::toString(m_RoundnessProperties.m_bVTy));
0275     el.setAttribute("roundnessFadeStep", KisDomUtils::toString(m_RoundnessProperties.m_fadeStep));
0276     root.appendChild(el);
0277 
0278     el = doc.createElement("sizeDynamics");
0279     el.setAttribute("tiltScale", KisDomUtils::toString(m_tiltScale));
0280     el.setAttribute("minumumDiameter", KisDomUtils::toString(m_minumumDiameter));
0281     el.setAttribute("roundnessJitter", KisDomUtils::toString(m_RoundnessProperties.m_sizeJitter));
0282     el.setAttribute("roundnessController", KisDomUtils::toString(m_RoundnessProperties.m_bVTy));
0283     el.setAttribute("roundnessFadeStep", KisDomUtils::toString(m_RoundnessProperties.m_fadeStep));
0284     root.appendChild(el);
0285 
0286 }
0287 
0288 // <param name="CurveSize"><![CDATA[0,0.257028;1,0.493976;]]></param>
0289 //  0,m_minimumDiameter      1,m_sizeJitter
0290 // <param name="PressureSize">true</param>
0291 // <param name="SizeSensor"><![CDATA[<!DOCTYPE params>
0292 // <params id="fuzzy"/> ]]></param> // controller
0293 void AbrGroupProperties::setupProperty(const QString& attributeName, const QString& type, const QString& value)
0294 {
0295     Q_UNUSED(type);
0296     double valueDbl = 0.0;
0297     QStringList list;
0298     if (attributeName == ABR_DYNAMICS_JITTER) {
0299         list = value.split(' ');
0300         //e.g. "#Pxl 10" -> ['#Pxl','10']
0301         Q_ASSERT(list.count() == 2);
0302         valueDbl = KisDomUtils::toDouble(list.at(1));
0303     }
0304 
0305     if (attributeName == ABR_DYNAMICS_FADE_STEP) {
0306         m_fadeStep = value.toInt();
0307     }
0308     else if (attributeName == ABR_DYNAMICS_JITTER) {
0309         m_sizeJitter = valueDbl;
0310     }
0311     else if (attributeName == ABR_CONTROL) {
0312         m_bVTy = (enumAbrControllers)value.toInt();
0313     }
0314     else {
0315         dbgKrita << "Unknown attribute for Group!" << attributeName;
0316     }
0317 
0318 }
0319 
0320 
0321