File indexing completed on 2025-01-26 04:11:31
0001 /* 0002 * SPDX-FileCopyrightText: 2020 Ashwin Dhakaita <ashwingpdhakaita@gmail.com> 0003 * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "MyPaintPaintOpPreset.h" 0009 0010 #include <QFile> 0011 #include <QFileInfo> 0012 #include <array> 0013 #include <libmypaint/mypaint-brush.h> 0014 #include <png.h> 0015 0016 #include <KisResourceLocator.h> 0017 #include <KisResourceServerProvider.h> 0018 #include <KoColorConversions.h> 0019 #include <KoColorModelStandardIds.h> 0020 #include <kis_debug.h> 0021 0022 #include "MyPaintPaintOpSettings.h" 0023 #include "MyPaintSensorPack.h" 0024 #include "MyPaintStandardOptionData.h" 0025 0026 class KisMyPaintPaintOpPreset::Private { 0027 0028 public: 0029 MyPaintBrush *brush; 0030 QImage icon; 0031 QByteArray json; 0032 }; 0033 0034 KisMyPaintPaintOpPreset::KisMyPaintPaintOpPreset(const QString &fileName) 0035 : KisPaintOpPreset(fileName) 0036 , d(new Private) 0037 { 0038 d->brush = mypaint_brush_new(); 0039 mypaint_brush_from_defaults(d->brush); 0040 } 0041 0042 KisMyPaintPaintOpPreset::KisMyPaintPaintOpPreset(const KisMyPaintPaintOpPreset &rhs) 0043 : KisPaintOpPreset(rhs) 0044 , d(new Private(*rhs.d)) 0045 { 0046 d->brush = mypaint_brush_new(); 0047 0048 if (d->json.isEmpty()) { 0049 mypaint_brush_from_defaults(d->brush); 0050 } else { 0051 mypaint_brush_from_string(d->brush, d->json); 0052 } 0053 } 0054 0055 KisMyPaintPaintOpPreset::~KisMyPaintPaintOpPreset() { 0056 0057 mypaint_brush_unref(d->brush); 0058 delete d; 0059 } 0060 0061 KoResourceSP KisMyPaintPaintOpPreset::clone() const 0062 { 0063 return toQShared(new KisMyPaintPaintOpPreset(*this)); 0064 } 0065 0066 void KisMyPaintPaintOpPreset::setColor(const KoColor color, const KoColorSpace *colorSpace) { 0067 0068 float hue, saturation, value; 0069 qreal r = 0, g = 0, b = 0; 0070 QColor dstColor; 0071 0072 if (colorSpace->colorModelId() == RGBAColorModelID) { 0073 colorSpace->toQColor(color.data(), &dstColor); 0074 dstColor.getRgbF(&r, &g, &b); 0075 } 0076 0077 RGBToHSV(r, g, b, &hue, &saturation, &value); 0078 0079 mypaint_brush_set_base_value(d->brush, MYPAINT_BRUSH_SETTING_COLOR_H, (hue)/360); 0080 mypaint_brush_set_base_value(d->brush, MYPAINT_BRUSH_SETTING_COLOR_S, (saturation)); 0081 mypaint_brush_set_base_value(d->brush, MYPAINT_BRUSH_SETTING_COLOR_V, (value)); 0082 } 0083 0084 void KisMyPaintPaintOpPreset::apply(KisPaintOpSettingsSP settings) { 0085 0086 if(settings->getProperty(MYPAINT_JSON).isNull()) { 0087 mypaint_brush_from_defaults(d->brush); 0088 } 0089 else { 0090 QByteArray ba = settings->getProperty(MYPAINT_JSON).toByteArray(); 0091 mypaint_brush_from_string(d->brush, ba); 0092 } 0093 0094 mypaint_brush_new_stroke(d->brush); 0095 } 0096 0097 MyPaintBrush* KisMyPaintPaintOpPreset::brush() { 0098 0099 return d->brush; 0100 } 0101 0102 bool KisMyPaintPaintOpPreset::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) 0103 { 0104 if (!dev->isSequential()) 0105 dev->seek(0); // ensure we do read *all* the bytes 0106 0107 std::array<png_byte, 8> signature; 0108 dev->peek(reinterpret_cast<char*>(signature.data()), 8); 0109 0110 #if PNG_LIBPNG_VER < 10400 0111 if (png_check_sig(signature, 8)) { 0112 #else 0113 if (png_sig_cmp(signature.data(), 0, 8) == 0) { 0114 #endif 0115 // this is a koresource 0116 if (KisPaintOpPreset::loadFromDevice(dev, resourcesInterface)) { 0117 apply(settings()); 0118 // correct filename 0119 const QString f = filename(); 0120 if (f.endsWith(".myb", Qt::CaseInsensitive)) { 0121 setFilename(QFileInfo(f).completeBaseName().append(KisPaintOpPreset::defaultFileExtension())); 0122 } 0123 return true; 0124 } else { 0125 warnPlugins << "Failed loading MyPaint preset from KoResource serialization"; 0126 return false; 0127 } 0128 } 0129 0130 const QByteArray ba(dev->readAll()); 0131 d->json = ba; 0132 // mypaint can handle invalid json files too, so this is the only way to find out if it was correct mypaint file or not... 0133 // if the json is incorrect, the brush will get the default mypaint brush settings 0134 // which looks like a round brush with low opacity and high spacing 0135 bool success = mypaint_brush_from_string(d->brush, ba); 0136 const float isEraser = mypaint_brush_get_base_value(d->brush, MYPAINT_BRUSH_SETTING_ERASER); 0137 0138 KisPaintOpSettingsSP s = new KisMyPaintOpSettings(resourcesInterface); 0139 s->setProperty("paintop", "mypaintbrush"); 0140 s->setProperty("filename", this->filename()); 0141 s->setProperty(MYPAINT_JSON, this->getJsonData()); 0142 s->setProperty("EraserMode", qRound(isEraser)); 0143 0144 0145 { 0146 /** 0147 * See a comment in `namespace deprecated_remove_after_krita6` in 0148 * MyPaintStandardOptionData.cpp 0149 */ 0150 0151 auto recoverDeprecatedProperty = [] (auto data, KisPaintOpSettingsSP settings) { 0152 /// we just round-trip the save operation to save the property 0153 /// out ot json object 0154 0155 data.read(settings.data()); 0156 data.write(settings.data()); 0157 }; 0158 0159 recoverDeprecatedProperty(MyPaintRadiusLogarithmicData(), s); 0160 recoverDeprecatedProperty(MyPaintOpacityData(), s); 0161 recoverDeprecatedProperty(MyPaintHardnessData(), s); 0162 } 0163 0164 0165 if (!metadata().contains("paintopid")) { 0166 addMetaData("paintopid", "mypaintbrush"); 0167 } 0168 0169 this->setSettings(s); 0170 setName(QFileInfo(filename()).baseName()); 0171 setValid(success); 0172 0173 return success; 0174 } 0175 0176 void KisMyPaintPaintOpPreset::updateThumbnail() 0177 { 0178 d->icon = thumbnail(); 0179 } 0180 0181 QString KisMyPaintPaintOpPreset::thumbnailPath() const 0182 { 0183 return QFileInfo(filename()).baseName() + "_prev.png"; 0184 } 0185 0186 QByteArray KisMyPaintPaintOpPreset::getJsonData() { 0187 0188 return d->json; 0189 }