kis_paintop_preset.cpp 8.13 KB
Newer Older
1 2
/* This file is part of the KDE project
 * Copyright (C) Boudewijn Rempt <boud@valdyas.org>, (C) 2008
Sven Langkamp's avatar
 
Sven Langkamp committed
3
 * Copyright (C) Sven Langkamp <sven.langkamp@gmail.com>, (C) 2009
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */
#include "kis_paintop_preset.h"

#include <QFile>
23
#include <QSize>
24
#include <QImage>
25 26
#include <QImageWriter>
#include <QImageReader>
Sven Langkamp's avatar
 
Sven Langkamp committed
27
#include <QDomDocument>
28
#include <QBuffer>
29

Sven Langkamp's avatar
 
Sven Langkamp committed
30
#include <KoInputDevice.h>
31 32 33 34 35 36 37 38

#include "kis_paintop_settings.h"
#include "kis_paintop_registry.h"
#include "kis_painter.h"
#include "kis_paint_information.h"
#include "kis_paint_device.h"
#include "kis_image.h"

39
#include <KoStore.h>
40

41
struct Q_DECL_HIDDEN KisPaintOpPreset::Private {
42
    Private()
43 44 45
        : settings(0),
          dirtyPreset(false)
    {
46
    }
47

Boudewijn Rempt's avatar
Boudewijn Rempt committed
48
    KisPaintOpSettingsSP settings;
49 50
    bool dirtyPreset;

51 52 53 54
};


KisPaintOpPreset::KisPaintOpPreset()
Boudewijn Rempt's avatar
Boudewijn Rempt committed
55 56
    : KoResource(QString())
    , m_d(new Private)
57 58 59 60
{
}

KisPaintOpPreset::KisPaintOpPreset(const QString & fileName)
Boudewijn Rempt's avatar
Boudewijn Rempt committed
61 62
    : KoResource(fileName)
    , m_d(new Private)
63 64 65 66 67 68 69 70
{
}

KisPaintOpPreset::~KisPaintOpPreset()
{
    delete m_d;
}

71
KisPaintOpPresetSP KisPaintOpPreset::clone() const
72
{
73
    KisPaintOpPresetSP preset = new KisPaintOpPreset();
74

Boudewijn Rempt's avatar
Boudewijn Rempt committed
75 76
    if (settings()) {
        preset->setSettings(settings()->clone());
77
    }
78
    preset->setPresetDirty(isPresetDirty());
79 80 81
    // only valid if we could clone the settings
    preset->setValid(settings());

Boudewijn Rempt's avatar
Boudewijn Rempt committed
82 83
    preset->setPaintOp(paintOp());
    preset->setName(name());
84
    preset->setImage(image());
85 86
    preset->settings()->setPreset(KisPaintOpPresetWSP(preset));

87
    Q_ASSERT(preset->valid());
88 89 90

    return preset;
}
91 92
void KisPaintOpPreset::setPresetDirty(bool value)
{
93
    m_d->dirtyPreset = value;
94 95 96 97 98
}
bool KisPaintOpPreset::isPresetDirty() const
{
    return m_d->dirtyPreset;
}
99

100 101
void KisPaintOpPreset::setPaintOp(const KoID & paintOp)
{
102
    Q_ASSERT(m_d->settings);
103 104 105 106
    m_d->settings->setProperty("paintop", paintOp.id());
}

KoID KisPaintOpPreset::paintOp() const
Boudewijn Rempt's avatar
Boudewijn Rempt committed
107
{
108
    Q_ASSERT(m_d->settings);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
109 110 111
    return KoID(m_d->settings->getString("paintop"), name());
}

112 113
void KisPaintOpPreset::setSettings(KisPaintOpSettingsSP settings)
{
114
    Q_ASSERT(settings);
115
    Q_ASSERT(!settings->getString("paintop", "").isEmpty());
116

117 118
    DirtyStateSaver dirtyStateSaver(this);

119 120
    if (settings) {
        m_d->settings = settings->clone();
121 122
        m_d->settings->setPreset(KisPaintOpPresetWSP(this));
    } else {
123
        m_d->settings = 0;
124
        m_d->settings->setPreset(0);
125 126
    }
    setValid(m_d->settings);
127 128 129
}

KisPaintOpSettingsSP KisPaintOpPreset::settings() const
Boudewijn Rempt's avatar
Boudewijn Rempt committed
130
{
131
    Q_ASSERT(m_d->settings);
132
    Q_ASSERT(!m_d->settings->getString("paintop", "").isEmpty());
133

Boudewijn Rempt's avatar
Boudewijn Rempt committed
134 135 136
    return m_d->settings;
}

137 138
bool KisPaintOpPreset::load()
{
139
    dbgImage << "Load preset " << filename();
140
    setValid(false);
141

142
    if (filename().isEmpty()) {
143
        return false;
144
    }
145

146 147
    QIODevice *dev = 0;
    QByteArray ba;
148

149
    if (filename().startsWith("bundle://")) {
150
        dbgKrita << "bundle";
151
        QString bn = filename().mid(9);
152 153 154
        int pos = bn.lastIndexOf(":");
        QString fn = bn.right(bn.size() - pos - 1);
        bn = bn.left(pos);
155 156 157

        QScopedPointer<KoStore> resourceStore(KoStore::createStore(bn, KoStore::Read, "application/x-krita-resourcebundle", KoStore::Zip));
        if (!resourceStore || resourceStore->bad()) {
158
            warnKrita << "Could not open store on bundle" << bn;
159 160 161 162
            return false;
        }

        if (resourceStore->isOpen()) resourceStore->close();
163

164
        if (!resourceStore->open(fn)) {
165
            warnKrita << "Could not open preset" << fn << "in bundle" << bn;
166 167
            return false;
        }
168

169 170 171 172 173 174
        ba = resourceStore->device()->readAll();
        dev = new QBuffer(&ba);

        resourceStore->close();
    }
    else {
175

176
        dev = new QFile(filename());
177 178 179 180 181
        if (dev->size() == 0)
        {
            delete dev;
            return false;
        }
182 183 184 185 186 187 188

        if (!dev->open(QIODevice::ReadOnly)) {
            warnKrita << "Can't open file " << filename();
            delete dev;
            return false;
        }
    }
189

190
    bool res = loadFromDevice(dev);
191 192
    delete dev;

193 194
    setValid(res);
    setPresetDirty(false);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
195
    return res;
196

197 198 199 200
}

bool KisPaintOpPreset::loadFromDevice(QIODevice *dev)
{
201

202
    QImageReader reader(dev, "PNG");
Sven Langkamp's avatar
 
Sven Langkamp committed
203

204 205
    QString version = reader.text("version");
    QString preset = reader.text("preset");
206

207
    dbgImage << version;
208

209
    if (version != "2.2") {
Sven Langkamp's avatar
 
Sven Langkamp committed
210 211
        return false;
    }
212

213
    QImage img;
214
    if (!reader.read(&img)) {
215 216
        dbgImage << "Fail to decode PNG";
        return false;
217
    }
218 219 220 221 222

    //Workaround for broken presets
    //Presets was saved with nested cdata section
    preset.replace("<curve><![CDATA[", "<curve>");
    preset.replace("]]></curve>", "</curve>");
223

224 225 226 227
    QDomDocument doc;
    if (!doc.setContent(preset)) {
        return false;
    }
228

229
    fromXML(doc.documentElement());
230

231
    if (!m_d->settings) {
Sven Langkamp's avatar
 
Sven Langkamp committed
232
        return false;
233
    }
234

235
    setValid(true);
236
    setImage(img);
237

Sven Langkamp's avatar
 
Sven Langkamp committed
238
    return true;
239 240 241 242
}

bool KisPaintOpPreset::save()
{
243

244 245
    if (filename().isEmpty())
        return false;
246

Sven Langkamp's avatar
 
Sven Langkamp committed
247
    QString paintopid = m_d->settings->getString("paintop", "");
248

Boudewijn Rempt's avatar
Boudewijn Rempt committed
249
    if (paintopid.isEmpty())
Sven Langkamp's avatar
 
Sven Langkamp committed
250
        return false;
251

252 253
    QFile f(filename());
    f.open(QFile::WriteOnly);
254

255
    return saveToDevice(&f);
256 257
}

258 259 260 261
void KisPaintOpPreset::toXML(QDomDocument& doc, QDomElement& elt) const
{
    QString paintopid = m_d->settings->getString("paintop", "");

262 263
    elt.setAttribute("paintopid", paintopid);
    elt.setAttribute("name", name());
264

265 266 267
    // sanitize the settings
    bool hasTexture = m_d->settings->getBool("Texture/Pattern/Enabled");
    if (!hasTexture) {
268
        Q_FOREACH (const QString & key, m_d->settings->getProperties().keys()) {
269 270 271 272 273 274
            if (key.startsWith("Texture") && key != "Texture/Pattern/Enabled") {
                m_d->settings->removeProperty(key);
            }
        }
    }

275
    m_d->settings->toXML(doc, elt);
276 277
}

278
void KisPaintOpPreset::fromXML(const QDomElement& presetElt)
279 280 281 282
{
    setName(presetElt.attribute("name"));
    QString paintopid = presetElt.attribute("paintopid");

283
    if (paintopid.isEmpty()) {
284
        dbgImage << "No paintopid attribute";
285
        setValid(false);
286
        return;
287
    }
288

289
    if (KisPaintOpRegistry::instance()->get(paintopid) == 0) {
290
        dbgImage << "No paintop " << paintopid;
291
        setValid(false);
292 293
        return;
    }
294 295 296

    KoID id(paintopid, "");

Boudewijn Rempt's avatar
Boudewijn Rempt committed
297
    KisPaintOpSettingsSP settings = KisPaintOpRegistry::instance()->settings(id);
298
    if (!settings) {
299
        setValid(false);
300
        warnKrita << "Could not load settings for preset" << paintopid;
301
        return;
302
    }
303

304
    settings->fromXML(presetElt);
305 306 307
    // sanitize the settings
    bool hasTexture = settings->getBool("Texture/Pattern/Enabled");
    if (!hasTexture) {
308
        Q_FOREACH (const QString & key, settings->getProperties().keys()) {
309 310 311 312 313
            if (key.startsWith("Texture") && key != "Texture/Pattern/Enabled") {
                settings->removeProperty(key);
            }
        }
    }
314
    setSettings(settings);
315

316 317
}

318
bool KisPaintOpPreset::saveToDevice(QIODevice *dev) const
319
{
320
    QImageWriter writer(dev, "PNG");
321 322 323 324 325 326 327 328 329 330 331

    QDomDocument doc;
    QDomElement root = doc.createElement("Preset");
    toXML(doc, root);
    doc.appendChild(root);

    writer.setText("version", "2.2");
    writer.setText("preset", doc.toString());

    QImage img;

332
    if (image().isNull()) {
333 334
        img = QImage(1, 1, QImage::Format_RGB32);
    } else {
335
        img = image();
336 337
    }

338 339
    m_d->dirtyPreset = false;

340 341
    KoResource::saveToDevice(dev);

342 343 344 345
    return writer.write(img);

}