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 }