kis_kra_loader.cpp 38.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/* This file is part of the KDE project
 * Copyright (C) Boudewijn Rempt <boud@valdyas.org>, (C) 2007
 *
 * 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.
 */

20
#include "kra/kis_kra_loader.h"
21

22
#include <QApplication>
23
#include <QStringList>
Boudewijn Rempt's avatar
Boudewijn Rempt committed
24

Boudewijn Rempt's avatar
Boudewijn Rempt committed
25
#include <QMessageBox>
26

Boudewijn Rempt's avatar
Boudewijn Rempt committed
27
#include <QUrl>
28
#include <QBuffer>
29

30 31
#include <KoStore.h>
#include <KoColorSpaceRegistry.h>
32
#include <KoColorSpaceEngine.h>
33
#include <KoColorProfile.h>
34
#include <KoDocumentInfo.h>
35
#include <KoFileDialog.h>
36
#include <KisImportExportManager.h>
37
#include <KoXmlReader.h>
38

39 40
#include <filter/kis_filter.h>
#include <filter/kis_filter_registry.h>
41
#include <generator/kis_generator.h>
Boudewijn Rempt's avatar
Boudewijn Rempt committed
42
#include <generator/kis_generator_layer.h>
43
#include <generator/kis_generator_registry.h>
Boudewijn Rempt's avatar
Boudewijn Rempt committed
44 45 46 47 48
#include <kis_adjustment_layer.h>
#include <kis_annotation.h>
#include <kis_base_node.h>
#include <kis_clone_layer.h>
#include <kis_debug.h>
Boudewijn Rempt's avatar
Boudewijn Rempt committed
49
#include <kis_assert.h>
Boudewijn Rempt's avatar
Boudewijn Rempt committed
50 51
#include <kis_external_layer_iface.h>
#include <kis_filter_mask.h>
52
#include <kis_transform_mask.h>
53 54 55 56 57 58
#include <kis_group_layer.h>
#include <kis_image.h>
#include <kis_layer.h>
#include <kis_name_server.h>
#include <kis_paint_layer.h>
#include <kis_selection.h>
Boudewijn Rempt's avatar
Boudewijn Rempt committed
59 60 61
#include <kis_selection_mask.h>
#include <kis_shape_layer.h>
#include <kis_transparency_mask.h>
Sven Langkamp's avatar
Sven Langkamp committed
62
#include <kis_layer_composition.h>
63
#include <kis_file_layer.h>
64 65 66
#include <kis_psd_layer_style.h>
#include <kis_psd_layer_style_resource.h>
#include "kis_resource_server_provider.h"
Jouni Pentikäinen's avatar
Jouni Pentikäinen committed
67
#include "kis_keyframe_channel.h"
68

69
#include "KisDocument.h"
70 71 72 73
#include "kis_config.h"
#include "kis_kra_tags.h"
#include "kis_kra_utils.h"
#include "kis_kra_load_visitor.h"
74 75 76
#include "kis_dom_utils.h"
#include "kis_image_animation_interface.h"
#include "kis_time_range.h"
77
#include "kis_grid_config.h"
78
#include "kis_guides_config.h"
79
#include "kis_image_config.h"
80
#include "KisProofingConfiguration.h"
81

82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
/*

  Color model id comparison through the ages:

2.4        2.5          2.6         ideal

ALPHA      ALPHA        ALPHA       ALPHAU8

CMYK       CMYK         CMYK        CMYKAU8
           CMYKAF32     CMYKAF32
CMYKA16    CMYKAU16     CMYKAU16

GRAYA      GRAYA        GRAYA       GRAYAU8
GrayF32    GRAYAF32     GRAYAF32
GRAYA16    GRAYAU16     GRAYAU16

LABA       LABA         LABA        LABAU16
           LABAF32      LABAF32
           LABAU8       LABAU8

RGBA       RGBA         RGBA        RGBAU8
RGBA16     RGBA16       RGBA16      RGBAU16
RgbAF32    RGBAF32      RGBAF32
RgbAF16    RgbAF16      RGBAF16

XYZA16     XYZA16       XYZA16      XYZAU16
           XYZA8        XYZA8       XYZAU8
XyzAF16    XyzAF16      XYZAF16
XyzAF32    XYZAF32      XYZAF32

YCbCrA     YCBCRA8      YCBCRA8     YCBCRAU8
YCbCrAU16  YCBCRAU16    YCBCRAU16
           YCBCRF32     YCBCRF32
 */

117
using namespace KRA;
118

119
struct KisKraLoader::Private
120 121 122
{
public:

123
    KisDocument* document;
124 125
    QString imageName; // used to be stored in the image, is now in the documentInfo block
    QString imageComment; // used to be stored in the image, is now in the documentInfo block
Boudewijn Rempt's avatar
Boudewijn Rempt committed
126
    QMap<KisNode*, QString> layerFilenames; // temp storage during loading
127
    int syntaxVersion; // version of the fileformat we are loading
128
    vKisNodeSP selectedNodes; // the nodes that were active when saving the document.
129
    QMap<QString, QString> assistantsFilenames;
130
    QList<KisPaintingAssistantSP> assistants;
131
    QMap<KisNode*, QString> keyframeFilenames;
132
    QStringList errorMessages;
133
};
Boudewijn Rempt's avatar
Boudewijn Rempt committed
134

135 136 137
void convertColorSpaceNames(QString &colorspacename, QString &profileProductName) {
    if (colorspacename  == "Grayscale + Alpha") {
        colorspacename  = "GRAYA";
138
        profileProductName.clear();
139 140 141
    }
    else if (colorspacename == "RgbAF32") {
        colorspacename = "RGBAF32";
142
        profileProductName.clear();
143 144
    }
    else if (colorspacename == "RgbAF16") {
145
        colorspacename = "RGBAF16";
146
        profileProductName.clear();
147 148 149 150 151 152
    }
    else if (colorspacename == "CMYKA16") {
        colorspacename = "CMYKAU16";
    }
    else if (colorspacename == "GrayF32") {
        colorspacename =  "GRAYAF32";
153
        profileProductName.clear();
154 155 156 157 158 159
    }
    else if (colorspacename == "GRAYA16") {
        colorspacename  = "GRAYAU16";
    }
    else if (colorspacename == "XyzAF16") {
        colorspacename  = "XYZAF16";
160
        profileProductName.clear();
161 162 163
    }
    else if (colorspacename == "XyzAF32") {
        colorspacename  = "XYZAF32";
164
        profileProductName.clear();
165 166 167 168 169 170 171 172 173
    }
    else if (colorspacename == "YCbCrA") {
        colorspacename  = "YCBCRA8";
    }
    else if (colorspacename == "YCbCrAU16") {
        colorspacename  = "YCBCRAU16";
    }
}

174
KisKraLoader::KisKraLoader(KisDocument * document, int syntaxVersion)
Boudewijn Rempt's avatar
Boudewijn Rempt committed
175
        : m_d(new Private())
176 177
{
    m_d->document = document;
178
    m_d->syntaxVersion = syntaxVersion;
179 180 181 182 183
}


KisKraLoader::~KisKraLoader()
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
184
    delete m_d;
185 186 187
}


188
KisImageWSP KisKraLoader::loadXML(const KoXmlElement& element)
189 190
{
    QString attr;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
191
    KisImageWSP image = 0;
192 193 194 195
    QString name;
    qint32 width;
    qint32 height;
    QString profileProductName;
196 197
    double xres;
    double yres;
198 199 200
    QString colorspacename;
    const KoColorSpace * cs;

201
    if ((attr = element.attribute(MIME)) == NATIVE_MIMETYPE) {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
202

203 204
        if ((m_d->imageName = element.attribute(NAME)).isNull()) {
            m_d->errorMessages << i18n("Image does not have a name.");
205
            return KisImageWSP(0);
206
        }
Boudewijn Rempt's avatar
Boudewijn Rempt committed
207

Boudewijn Rempt's avatar
Boudewijn Rempt committed
208
        if ((attr = element.attribute(WIDTH)).isNull()) {
209
            m_d->errorMessages << i18n("Image does not specify a width.");
210
            return KisImageWSP(0);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
211
        }
212
        width = KisDomUtils::toInt(attr);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
213

Boudewijn Rempt's avatar
Boudewijn Rempt committed
214
        if ((attr = element.attribute(HEIGHT)).isNull()) {
215
            m_d->errorMessages << i18n("Image does not specify a height.");
216
            return KisImageWSP(0);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
217
        }
218

219
        height = KisDomUtils::toInt(attr);
220

Boudewijn Rempt's avatar
Boudewijn Rempt committed
221
        m_d->imageComment = element.attribute(DESCRIPTION);
222

223 224
        xres = 100.0 / 72.0;
        if (!(attr = element.attribute(X_RESOLUTION)).isNull()) {
225
            qreal value = KisDomUtils::toDouble(attr);
226 227 228

            if (value > 1.0) {
                xres = value / 72.0;
229
            }
230 231
        }

Boudewijn Rempt's avatar
Boudewijn Rempt committed
232
        yres = 100.0 / 72.0;
233
        if (!(attr = element.attribute(Y_RESOLUTION)).isNull()) {
234
            qreal value = KisDomUtils::toDouble(attr);
235 236
            if (value > 1.0) {
                yres = value / 72.0;
237
            }
238 239
        }

Boudewijn Rempt's avatar
Boudewijn Rempt committed
240
        if ((colorspacename = element.attribute(COLORSPACE_NAME)).isNull()) {
241 242 243 244 245 246
            // An old file: take a reasonable default.
            // Krita didn't support anything else in those
            // days anyway.
            colorspacename = "RGBA";
        }

247
        profileProductName = element.attribute(PROFILE);
248
        // A hack for an old colorspacename
249 250
        convertColorSpaceNames(colorspacename, profileProductName);

251
        QString colorspaceModel = KoColorSpaceRegistry::instance()->colorSpaceColorModelId(colorspacename).id();
252
        QString colorspaceDepth = KoColorSpaceRegistry::instance()->colorSpaceColorDepthId(colorspacename).id();
253

254
        if (profileProductName.isNull()) {
255
            // no mention of profile so get default profile";
256
            cs = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, "");
Boudewijn Rempt's avatar
Boudewijn Rempt committed
257
        } else {
258
            cs = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, profileProductName);
259 260 261
        }

        if (cs == 0) {
262 263 264
            // try once more without the profile
            cs = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, "");
            if (cs == 0) {
Yuri Chornoivan's avatar
Yuri Chornoivan committed
265
                m_d->errorMessages << i18n("Image specifies an unsupported color model: %1.", colorspacename);
266 267
                return KisImageWSP(0);
            }
268
        }
269 270
        KisImageConfig cfgImage;
        KisProofingConfiguration *proofingConfig = cfgImage.defaultProofingconfiguration();
271 272 273 274 275 276 277 278 279 280 281 282
        if (!(attr = element.attribute(PROOFINGPROFILENAME)).isNull()) {
            proofingConfig->proofingProfile = attr;
        }
        if (!(attr = element.attribute(PROOFINGMODEL)).isNull()) {
            proofingConfig->proofingModel = attr;
        }
        if (!(attr = element.attribute(PROOFINGDEPTH)).isNull()) {
            proofingConfig->proofingDepth = attr;
        }
        if (!(attr = element.attribute(PROOFINGINTENT)).isNull()) {
            proofingConfig->intent = (KoColorConversionTransformation::Intent) KisDomUtils::toInt(attr);
        }
283

284 285 286 287
        if (!(attr = element.attribute(PROOFINGADAPTATIONSTATE)).isNull()) {
            proofingConfig->adaptationState = KisDomUtils::toDouble(attr);
        }

288 289 290 291 292 293
        if (m_d->document) {
            image = new KisImage(m_d->document->createUndoStore(), width, height, cs, name);
        }
        else {
            image = new KisImage(0, width, height, cs, name);
        }
294
        image->setResolution(xres, yres);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
295
        loadNodes(element, image, const_cast<KisGroupLayer*>(image->rootLayer().data()));
296

297

Sven Langkamp's avatar
Sven Langkamp committed
298
        KoXmlNode child;
299 300
        for (child = element.lastChild(); !child.isNull(); child = child.previousSibling()) {
            KoXmlElement e = child.toElement();
301 302
            if(e.tagName() == CANVASPROJECTIONCOLOR) {
                if (e.hasAttribute(COLORBYTEDATA)) {
303 304 305 306 307
                    QByteArray colorData = QByteArray::fromBase64(e.attribute("ColorData").toLatin1());
                    KoColor color((const quint8*)colorData.data(), image->colorSpace());
                    image->setDefaultProjectionColor(color);
                }
            }
308

309
            if(e.tagName()== PROOFINGWARNINGCOLOR) {
310 311 312 313
                QDomDocument dom;
                KoXml::asQDomElement(dom, e);
                QDomElement eq = dom.firstChildElement();
                proofingConfig->warningColor = KoColor::fromXML(eq.firstChildElement(), Integer8BitsColorDepthID.id(), QHash<QString, QString>());
314 315
            }

316 317 318
            if (e.tagName().toLower() == "animation") {
                loadAnimationMetadata(e, image);
            }
319 320
        }

321 322
        image->setProofingConfiguration(proofingConfig);

Sven Langkamp's avatar
Sven Langkamp committed
323 324 325 326 327 328
        for (child = element.lastChild(); !child.isNull(); child = child.previousSibling()) {
            KoXmlElement e = child.toElement();
            if(e.tagName() == "compositions") {
                loadCompositions(e, image);
            }
        }
329
    }
330
    KoXmlNode child;
331 332 333 334 335

    for (child = element.lastChild(); !child.isNull(); child = child.previousSibling()) {
        KoXmlElement e = child.toElement();
        if (e.tagName() == "grid") {
            loadGrid(e);
336 337 338
        } else if (e.tagName() == "guides") {
            loadGuides(e);
        } else if (e.tagName() == "assistants") {
339 340 341
            loadAssistantsList(e);
        }
    }
342

Boudewijn Rempt's avatar
Boudewijn Rempt committed
343
    return image;
344 345
}

Boudewijn Rempt's avatar
Boudewijn Rempt committed
346
void KisKraLoader::loadBinaryData(KoStore * store, KisImageWSP image, const QString & uri, bool external)
347
{
348
    // icc profile: if present, this overrides the profile product name loaded in loadXML.
349
    QString location = external ? QString() : uri;
350 351
    location += m_d->imageName + ICC_PATH;
    if (store->hasFile(location)) {
352 353 354 355 356 357 358
        if (store->open(location)) {
            QByteArray data; data.resize(store->size());
            bool res = (store->read(data.data(), store->size()) > -1);
            store->close();
            if (res) {
                const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(image->colorSpace()->colorModelId().id(), image->colorSpace()->colorDepthId().id(), data);
                if (profile && profile->valid()) {
359
                    res = image->assignImageProfile(profile);
360
                }
361
                if (!res) {
362 363 364 365 366
                    profile = KoColorSpaceRegistry::instance()->profileByName(KoColorSpaceRegistry::instance()->colorSpaceFactory(image->colorSpace()->id())->defaultProfile());
                    Q_ASSERT(profile && profile->valid());
                    image->assignImageProfile(profile);
                }
            }
367
        }
368
    }
369 370 371 372 373 374 375 376 377 378 379 380 381
    //load the embed proofing profile, it only needs to be loaded into Krita, not assigned.
    location = external ? QString() : uri;
    location += m_d->imageName + ICC_PROOFING_PATH;
    if (store->hasFile(location)) {
        if (store->open(location)) {
            QByteArray proofingData;
            proofingData.resize(store->size());
            bool proofingProfileRes = (store->read(proofingData.data(), store->size())>-1);
            store->close();
            if (proofingProfileRes)
            {
                const KoColorProfile *proofingProfile = KoColorSpaceRegistry::instance()->createColorProfile(image->proofingConfiguration()->proofingModel, image->proofingConfiguration()->proofingDepth, proofingData);
                if (proofingProfile->valid()){
382 383 384 385

                    //if (KoColorSpaceEngineRegistry::instance()->get("icc")) {
                    //    KoColorSpaceEngineRegistry::instance()->get("icc")->addProfile(proofingProfile->fileName());
                    //}
386 387 388 389 390 391
                    KoColorSpaceRegistry::instance()->addProfile(proofingProfile);
                }
            }
        }
    }

392 393

    // Load the layers data: if there is a profile associated with a layer it will be set now.
Boudewijn Rempt's avatar
Boudewijn Rempt committed
394
    KisKraLoadVisitor visitor(image, store, m_d->layerFilenames, m_d->imageName, m_d->syntaxVersion);
395

396
    if (external) {
397
        visitor.setExternalUri(uri);
398
    }
399

Boudewijn Rempt's avatar
Boudewijn Rempt committed
400
    image->rootLayer()->accept(visitor);
401 402 403
    if (!visitor.errorMessages().isEmpty()) {
        m_d->errorMessages.append(visitor.errorMessages());
    }
404

405 406
    // annotations
    // exif
407
    location = external ? QString() : uri;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
408
    location += m_d->imageName + EXIF_PATH;
409 410 411 412 413
    if (store->hasFile(location)) {
        QByteArray data;
        store->open(location);
        data = store->read(store->size());
        store->close();
Boudewijn Rempt's avatar
Boudewijn Rempt committed
414
        image->addAnnotation(KisAnnotationSP(new KisAnnotation("exif", "", data)));
415
    }
416

417

418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
    // layer styles
    location = external ? QString() : uri;
    location += m_d->imageName + LAYER_STYLES_PATH;
    if (store->hasFile(location)) {
        KisPSDLayerStyleCollectionResource *collection =
            new KisPSDLayerStyleCollectionResource("Embedded Styles.asl");

        collection->setName(i18nc("Auto-generated layer style collection name for embedded styles (collection)", "<%1> (embedded)", m_d->imageName));

        KIS_ASSERT_RECOVER_NOOP(!collection->valid());

        store->open(location);
        {
            KoStoreDevice device(store);
            device.open(QIODevice::ReadOnly);
433 434 435 436 437 438 439 440 441

            /**
             * ASL loading code cannot work with non-sequential IO devices,
             * so convert the device beforehand!
             */
            QByteArray buf = device.readAll();
            QBuffer raDevice(&buf);
            raDevice.open(QIODevice::ReadOnly);
            collection->loadFromDevice(&raDevice);
442 443 444 445 446 447 448 449 450
        }
        store->close();

        if (collection->valid()) {
            KoResourceServer<KisPSDLayerStyleCollectionResource> *server = KisResourceServerProvider::instance()->layerStyleCollectionServer();
            server->addResource(collection, false);

            collection->assignAllLayerStyles(image->root());
        } else {
451
            warnKrita << "WARNING: Couldn't load layer styles library from .kra!";
452 453 454 455
            delete collection;
        }
    }

456
    if (m_d->document && m_d->document->documentInfo()->aboutInfo("title").isNull())
457
        m_d->document->documentInfo()->setAboutInfo("title", m_d->imageName);
458
    if (m_d->document && m_d->document->documentInfo()->aboutInfo("comment").isNull())
459
        m_d->document->documentInfo()->setAboutInfo("comment", m_d->imageComment);
460

461
    loadAssistants(store, uri, external);
462 463
}

464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518
void KisKraLoader::loadKeyframes(KoStore *store, const QString uri, bool external)
{
    QMap<KisNode*, QString>::iterator it;

    for (it = m_d->keyframeFilenames.begin(); it != m_d->keyframeFilenames.end(); it++) {
        KisNodeSP node = it.key();
        QString filename = it.value();

        QString location =
                (external ? QString() : uri)
                + m_d->imageName + LAYER_PATH + filename;

        loadNodeKeyframes(store, location, node);
    }
}

void KisKraLoader::loadNodeKeyframes(KoStore *store, const QString &location, KisNodeSP node)
{
    if (!store->open(location)) {
        m_d->errorMessages << i18n("Could not load keyframes from %1.", location);
        return;
    }

    QString errorMsg;
    int errorLine;
    int errorColumn;
    KoXmlDocument doc = KoXmlDocument(true);

    bool ok = doc.setContent(store->device(), &errorMsg, &errorLine, &errorColumn);
    store->close();

    if (!ok) {
        m_d->errorMessages << i18n("parsing error in the keyframe file %1 at line %2, column %3\nError message: %4", location, errorLine, errorColumn, i18n(errorMsg.toUtf8()));
        return;
    }

    QDomDocument dom;
    KoXml::asQDomElement(dom, doc.documentElement());
    QDomElement root = dom.firstChildElement();

    for (QDomElement child = root.firstChildElement(); !child.isNull(); child = child.nextSiblingElement()) {
        if (child.nodeName().toUpper() == "CHANNEL") {
            QString id = child.attribute("name");
            KisKeyframeChannel *channel = node->getKeyframeChannel(id);

            if (!channel) {
                m_d->errorMessages << i18n("unknown keyframe channel type: %1 in %2", id, location);
                continue;
            }

            channel->loadXML(child);
        }
    }
}

519 520 521 522 523
vKisNodeSP KisKraLoader::selectedNodes() const
{
    return m_d->selectedNodes;
}

524
QList<KisPaintingAssistantSP> KisKraLoader::assistants() const
525 526 527 528
{
    return m_d->assistants;
}

529 530 531 532 533
QStringList KisKraLoader::errorMessages() const
{
    return m_d->errorMessages;
}

534 535 536 537 538 539 540 541 542 543 544
void KisKraLoader::loadAssistants(KoStore *store, const QString &uri, bool external)
{
    QString file_path;
    QString location;
    QMap<int ,KisPaintingAssistantHandleSP> handleMap;
    KisPaintingAssistant* assistant = 0;
    QMap<QString,QString>::const_iterator loadedAssistant = m_d->assistantsFilenames.constBegin();
    while (loadedAssistant != m_d->assistantsFilenames.constEnd()){
        const KisPaintingAssistantFactory* factory = KisPaintingAssistantFactoryRegistry::instance()->get(loadedAssistant.value());
        if (factory) {
            assistant = factory->createPaintingAssistant();
545
            location = external ? QString() : uri;
546 547 548
            location += m_d->imageName + ASSISTANTS_PATH;
            file_path = location + loadedAssistant.key();
            assistant->loadXml(store, handleMap, file_path);
549 550
            //If an assistant has too few handles than it should according to it's own setup, just don't load it//
            if (assistant->handles().size()==assistant->numHandles()){
551
                m_d->assistants.append(toQShared(assistant));
552
            }
553 554 555 556 557
        }
        loadedAssistant++;
    }
}

558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574
void KisKraLoader::loadAnimationMetadata(const KoXmlElement &element, KisImageWSP image)
{
    QDomDocument qDom;
    KoXml::asQDomElement(qDom, element);
    QDomElement qElement = qDom.firstChildElement();

    float framerate;
    KisTimeRange range;
    int currentTime;

    KisImageAnimationInterface *animation = image->animationInterface();

    if (KisDomUtils::loadValue(qElement, "framerate", &framerate)) {
        animation->setFramerate(framerate);
    }

    if (KisDomUtils::loadValue(qElement, "range", &range)) {
575
        animation->setFullClipRange(range);
576 577 578 579 580 581 582
    }

    if (KisDomUtils::loadValue(qElement, "currentTime", &currentTime)) {
        animation->switchCurrentTimeAsync(currentTime);
    }
}

583
KisNodeSP KisKraLoader::loadNodes(const KoXmlElement& element, KisImageWSP image, KisNodeSP parent)
584
{
585

Sven Langkamp's avatar
Sven Langkamp committed
586
    KoXmlNode node = element.firstChild();
587
    KoXmlNode child;
588

Boudewijn Rempt's avatar
Boudewijn Rempt committed
589
    if (!node.isNull()) {
590

Boudewijn Rempt's avatar
Boudewijn Rempt committed
591
        if (node.isElement()) {
592

Boudewijn Rempt's avatar
Boudewijn Rempt committed
593 594
            if (node.nodeName().toUpper() == LAYERS.toUpper() || node.nodeName().toUpper() == MASKS.toUpper()) {
                for (child = node.lastChild(); !child.isNull(); child = child.previousSibling()) {
595
                    KisNodeSP node = loadNode(child.toElement(), image, parent);
596
                    if (node) {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
597 598
                        image->nextLayerName(); // Make sure the nameserver is current with the number of nodes.
                        image->addNode(node, parent);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
599
                        if (node->inherits("KisLayer") && child.childNodesCount() > 0) {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
600
                            loadNodes(child.toElement(), image, node);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
601
                        }
602 603 604 605 606
                    }
                }
            }
        }
    }
Boudewijn Rempt's avatar
Boudewijn Rempt committed
607 608

    return parent;
609 610
}

611
KisNodeSP KisKraLoader::loadNode(const KoXmlElement& element, KisImageWSP image, KisNodeSP parent)
612 613 614 615 616
{
    // Nota bene: If you add new properties to layers, you should
    // ALWAYS define a default value in case the property is not
    // present in the layer definition: this helps a LOT with backward
    // compatibility.
Boudewijn Rempt's avatar
Boudewijn Rempt committed
617
    QString name = element.attribute(NAME, "No Name");
Boudewijn Rempt's avatar
Boudewijn Rempt committed
618

619 620
    QUuid id = QUuid(element.attribute(UUID, QUuid().toString()));

Boudewijn Rempt's avatar
Boudewijn Rempt committed
621 622
    qint32 x = element.attribute(X, "0").toInt();
    qint32 y = element.attribute(Y, "0").toInt();
623

624 625 626
    qint32 opacity = element.attribute(OPACITY, QString::number(OPACITY_OPAQUE_U8)).toInt();
    if (opacity < OPACITY_TRANSPARENT_U8) opacity = OPACITY_TRANSPARENT_U8;
    if (opacity > OPACITY_OPAQUE_U8) opacity = OPACITY_OPAQUE_U8;
627

Boudewijn Rempt's avatar
Boudewijn Rempt committed
628
    const KoColorSpace* colorSpace = 0;
629 630
    if ((element.attribute(COLORSPACE_NAME)).isNull()) {
        dbgFile << "No attribute color space for layer: " << name;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
631
        colorSpace = image->colorSpace();
632 633
    }
    else {
634
        QString colorspacename = element.attribute(COLORSPACE_NAME);
635 636 637 638
        QString profileProductName;

        convertColorSpaceNames(colorspacename, profileProductName);

639
        QString colorspaceModel = KoColorSpaceRegistry::instance()->colorSpaceColorModelId(colorspacename).id();
640
        QString colorspaceDepth = KoColorSpaceRegistry::instance()->colorSpaceColorDepthId(colorspacename).id();
641
        dbgFile << "Searching color space: " << colorspacename << colorspaceModel << colorspaceDepth << " for layer: " << name;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
642
        // use default profile - it will be replaced later in completeLoading
643

644
        colorSpace = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, "");
645
        dbgFile << "found colorspace" << colorSpace;
646
        if (!colorSpace) {
647
            m_d->errorMessages << i18n("Layer %1 specifies an unsupported color model: %2.", name, colorspacename);
648 649
            return 0;
        }
650
    }
Boudewijn Rempt's avatar
Boudewijn Rempt committed
651

652 653 654 655
    const bool visible = element.attribute(VISIBLE, "1") == "0" ? false : true;
    const bool locked = element.attribute(LOCKED, "0") == "0" ? false : true;
    const bool collapsed = element.attribute(COLLAPSED, "0") == "0" ? false : true;
    const int colorLabelIndex = element.attribute(COLOR_LABEL, "0").toInt();
656 657

    // Now find out the layer type and do specific handling
658 659
    QString nodeType;

Boudewijn Rempt's avatar
Boudewijn Rempt committed
660
    if (m_d->syntaxVersion == 1) {
661
        nodeType = element.attribute("layertype");
Boudewijn Rempt's avatar
Boudewijn Rempt committed
662
        if (nodeType.isEmpty()) {
663 664
            nodeType = PAINT_LAYER;
        }
665 666
    }
    else {
667 668 669
        nodeType = element.attribute(NODE_TYPE);
    }

670
    if (nodeType.isEmpty()) {
Yuri Chornoivan's avatar
Yuri Chornoivan committed
671
        m_d->errorMessages << i18n("Layer %1 has an unsupported type.", name);
672 673
        return 0;
    }
674 675


676
    KisNodeSP node = 0;
677

Boudewijn Rempt's avatar
Boudewijn Rempt committed
678
    if (nodeType == PAINT_LAYER)
Boudewijn Rempt's avatar
Boudewijn Rempt committed
679
        node = loadPaintLayer(element, image, name, colorSpace, opacity);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
680
    else if (nodeType == GROUP_LAYER)
Boudewijn Rempt's avatar
Boudewijn Rempt committed
681
        node = loadGroupLayer(element, image, name, colorSpace, opacity);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
682
    else if (nodeType == ADJUSTMENT_LAYER)
Boudewijn Rempt's avatar
Boudewijn Rempt committed
683
        node = loadAdjustmentLayer(element, image, name, colorSpace, opacity);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
684
    else if (nodeType == SHAPE_LAYER)
Boudewijn Rempt's avatar
Boudewijn Rempt committed
685
        node = loadShapeLayer(element, image, name, colorSpace, opacity);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
686
    else if (nodeType == GENERATOR_LAYER)
Boudewijn Rempt's avatar
Boudewijn Rempt committed
687
        node = loadGeneratorLayer(element, image, name, colorSpace, opacity);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
688
    else if (nodeType == CLONE_LAYER)
Boudewijn Rempt's avatar
Boudewijn Rempt committed
689
        node = loadCloneLayer(element, image, name, colorSpace, opacity);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
690
    else if (nodeType == FILTER_MASK)
691
        node = loadFilterMask(element, parent);
692 693
    else if (nodeType == TRANSFORM_MASK)
        node = loadTransformMask(element, parent);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
694
    else if (nodeType == TRANSPARENCY_MASK)
695
        node = loadTransparencyMask(element, parent);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
696
    else if (nodeType == SELECTION_MASK)
697
        node = loadSelectionMask(image, element, parent);
698 699 700
    else if (nodeType == FILE_LAYER) {
        node = loadFileLayer(element, image, name, opacity);
    }
701
    else {
Yuri Chornoivan's avatar
Yuri Chornoivan committed
702
        m_d->errorMessages << i18n("Layer %1 has an unsupported type: %2.", name, nodeType);
703 704
        return 0;
    }
Boudewijn Rempt's avatar
Boudewijn Rempt committed
705

706 707
    // Loading the node went wrong. Return empty node and leave to
    // upstream to complain to the user
708 709 710 711
    if (!node) {
        m_d->errorMessages << i18n("Failure loading layer %1 of type: %2.", name, nodeType);
        return 0;
    }
Boudewijn Rempt's avatar
Boudewijn Rempt committed
712

Boudewijn Rempt's avatar
Boudewijn Rempt committed
713
    node->setVisible(visible, true);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
714
    node->setUserLocked(locked);
715
    node->setCollapsed(collapsed);
716
    node->setColorLabelIndex(colorLabelIndex);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
717 718 719 720
    node->setX(x);
    node->setY(y);
    node->setName(name);

721 722 723
    if (! id.isNull())          // if no uuid in file, new one has been generated already
        node->setUuid(id);

Boudewijn Rempt's avatar
Boudewijn Rempt committed
724
    if (node->inherits("KisLayer")) {
725
        KisLayer* layer           = qobject_cast<KisLayer*>(node.data());
726 727
        QBitArray channelFlags    = stringToFlags(element.attribute(CHANNEL_FLAGS, ""), colorSpace->channelCount());
        QString   compositeOpName = element.attribute(COMPOSITE_OP, "normal");
Boudewijn Rempt's avatar
Boudewijn Rempt committed
728

729
        layer->setChannelFlags(channelFlags);
730
        layer->setCompositeOpId(compositeOpName);
731 732 733 734 735 736 737 738 739

        if (element.hasAttribute(LAYER_STYLE_UUID)) {
            QString uuidString = element.attribute(LAYER_STYLE_UUID);
            QUuid uuid(uuidString);
            if (!uuid.isNull()) {
                KisPSDLayerStyleSP dumbLayerStyle(new KisPSDLayerStyle());
                dumbLayerStyle->setUuid(uuid);
                layer->setLayerStyle(dumbLayerStyle);
            } else {
740
                warnKrita << "WARNING: Layer style for layer" << layer->name() << "contains invalid UUID" << uuidString;
741 742
            }
        }
Boudewijn Rempt's avatar
Boudewijn Rempt committed
743
    }
744

745 746 747 748 749 750 751 752 753
    if (node->inherits("KisGroupLayer")) {
        if (element.hasAttribute(PASS_THROUGH_MODE)) {
            bool value = element.attribute(PASS_THROUGH_MODE, "0") != "0";

            KisGroupLayer *group = qobject_cast<KisGroupLayer*>(node.data());
            group->setPassThroughMode(value);
        }
    }

Boudewijn Rempt's avatar
Boudewijn Rempt committed
754
    if (node->inherits("KisPaintLayer")) {
755
        KisPaintLayer* layer            = qobject_cast<KisPaintLayer*>(node.data());
756 757
        QBitArray      channelLockFlags = stringToFlags(element.attribute(CHANNEL_LOCK_FLAGS, ""), colorSpace->channelCount());
        layer->setChannelLockFlags(channelLockFlags);
758 759 760

        bool onionEnabled = element.attribute(ONION_SKIN_ENABLED, "0") == "0" ? false : true;
        layer->setOnionSkinEnabled(onionEnabled);
761 762 763

        bool timelineEnabled = element.attribute(VISIBLE_IN_TIMELINE, "0") == "0" ? false : true;
        layer->setUseInTimeline(timelineEnabled);
764
    }
Boudewijn Rempt's avatar
Boudewijn Rempt committed
765

Boudewijn Rempt's avatar
Boudewijn Rempt committed
766
    if (element.attribute(FILE_NAME).isNull()) {
767
        m_d->layerFilenames[node.data()] = name;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
768 769
    }
    else {
770
        m_d->layerFilenames[node.data()] = element.attribute(FILE_NAME);
Boudewijn Rempt's avatar
Boudewijn Rempt committed
771
    }
772 773 774 775

    if (element.hasAttribute("selected") && element.attribute("selected") == "true")  {
        m_d->selectedNodes.append(node);
    }
Boudewijn Rempt's avatar
Boudewijn Rempt committed
776

777
    if (element.hasAttribute(KEYFRAME_FILE)) {
778
        node->enableAnimation();
779
        m_d->keyframeFilenames.insert(node.data(), element.attribute(KEYFRAME_FILE));
780 781
    }

Boudewijn Rempt's avatar
Boudewijn Rempt committed
782
    return node;
783 784
}

Boudewijn Rempt's avatar
Boudewijn Rempt committed
785

786
KisNodeSP KisKraLoader::loadPaintLayer(const KoXmlElement& element, KisImageWSP image,
Boudewijn Rempt's avatar
Boudewijn Rempt committed
787
                                      const QString& name, const KoColorSpace* cs, quint32 opacity)
788
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
789
    Q_UNUSED(element);
790
    KisPaintLayer* layer;
791

Boudewijn Rempt's avatar
Boudewijn Rempt committed
792
    layer = new KisPaintLayer(image, name, opacity, cs);
793 794 795
    Q_CHECK_PTR(layer);

    // Load exif info
Boudewijn Rempt's avatar
Boudewijn Rempt committed
796
    /*TODO: write and use the legacy stuff to load that exif tag
Boudewijn Rempt's avatar
Boudewijn Rempt committed
797 798 799 800 801 802 803 804
      for( KoXmlNode node = element.firstChild(); !node.isNull(); node = node.nextSibling() )
      {
      KoXmlElement e = node.toElement();
      if ( !e.isNull() && e.tagName() == "ExifInfo" )
      {
      layer->paintDevice()->exifInfo()->load(e);
      }
      }*/
805
    // TODO load metadata
Boudewijn Rempt's avatar
Boudewijn Rempt committed
806

Boudewijn Rempt's avatar
Boudewijn Rempt committed
807
    return layer;
Boudewijn Rempt's avatar
Boudewijn Rempt committed
808

809 810
}

811 812 813 814 815
KisNodeSP KisKraLoader::loadFileLayer(const KoXmlElement& element, KisImageWSP image, const QString& name, quint32 opacity)
{
    QString filename = element.attribute("source", QString());
    if (filename.isNull()) return 0;
    bool scale = (element.attribute("scale", "true")  == "true");
816 817 818 819 820 821 822 823 824
    int scalingMethod = element.attribute("scalingmethod", "-1").toInt();
    if (scalingMethod < 0) {
        if (scale) {
            scalingMethod = KisFileLayer::ToImagePPI;
        }
        else {
            scalingMethod = KisFileLayer::None;
        }
    }
825

826 827 828 829
    QString documentPath;
    if (m_d->document) {
        documentPath = m_d->document->url().toLocalFile();
    }
830
    QFileInfo info(documentPath);
831 832 833
    QString basePath = info.absolutePath();

    QString fullPath = basePath + QDir::separator() + filename;
834 835
    // Entering the event loop to show the messagebox will delete the image, so up the ref by one
    image->ref();
836
    if (!QFileInfo(fullPath).exists()) {
837 838

        qApp->setOverrideCursor(Qt::ArrowCursor);
839 840 841 842 843 844 845
        QString msg = i18nc(
            "@info",
            "The file associated to a file layer with the name \"%1\" is not found.<nl/><nl/>"
            "Expected path:<nl/>"
            "%2<nl/><nl/>"
            "Do you want to locate it manually?", name, fullPath);

Boudewijn Rempt's avatar
Boudewijn Rempt committed
846
        int result = QMessageBox::warning(0, i18nc("@title:window", "File not found"), msg, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
847

Boudewijn Rempt's avatar
Boudewijn Rempt committed
848
        if (result == QMessageBox::Yes) {
849 850

            KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument");
851
            dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KisImportExportManager::Import));
852
            dialog.setDefaultDir(basePath);
853
            QString url = dialog.filename();
854 855 856 857 858 859 860 861

            if (!QFileInfo(basePath).exists()) {
                filename = url;
            } else {
                QDir d(basePath);
                filename = d.relativeFilePath(url);
            }
        }
862 863

        qApp->restoreOverrideCursor();
864 865 866
    }

    KisLayer *layer = new KisFileLayer(image, basePath, filename, (KisFileLayer::ScalingMethod)scalingMethod, name, opacity);
867 868 869 870 871
    Q_CHECK_PTR(layer);

    return layer;
}

872
KisNodeSP KisKraLoader::loadGroupLayer(const KoXmlElement& element, KisImageWSP image,
Boudewijn Rempt's avatar
Boudewijn Rempt committed
873
                                      const QString& name, const KoColorSpace* cs, quint32 opacity)
874
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
875 876
    Q_UNUSED(element);
    Q_UNUSED(cs);
877
    QString attr;
878
    KisGroupLayer* layer;
879

Boudewijn Rempt's avatar
Boudewijn Rempt committed
880
    layer = new KisGroupLayer(image, name, opacity);
881 882
    Q_CHECK_PTR(layer);

Boudewijn Rempt's avatar
Boudewijn Rempt committed
883
    return layer;
884 885 886

}

887
KisNodeSP KisKraLoader::loadAdjustmentLayer(const KoXmlElement& element, KisImageWSP image,
Boudewijn Rempt's avatar
Boudewijn Rempt committed
888
        const QString& name, const KoColorSpace* cs, quint32 opacity)
889
{
890
    // XXX: do something with filterversion?
Boudewijn Rempt's avatar
Boudewijn Rempt committed
891
    Q_UNUSED(cs);
892
    QString attr;
893
    KisAdjustmentLayer* layer;
894 895
    QString filtername;

Boudewijn Rempt's avatar
Boudewijn Rempt committed
896
    if ((filtername = element.attribute(FILTER_NAME)).isNull()) {
897
        // XXX: Invalid adjustmentlayer! We should warn about it!
898
        warnFile << "No filter in adjustment layer";
899
        return 0;
900 901 902 903
    }

    KisFilterSP f = KisFilterRegistry::instance()->value(filtername);
    if (!f) {
904
        warnFile << "No filter for filtername" << filtername << "";
905
        return 0; // XXX: We don't have this filter. We should warn about it!
906 907 908 909 910
    }

    KisFilterConfiguration * kfc = f->defaultConfiguration(0);

    // We'll load the configuration and the selection later.
Boudewijn Rempt's avatar
Boudewijn Rempt committed
911
    layer = new KisAdjustmentLayer(image, name, kfc, 0);
912 913
    Q_CHECK_PTR(layer);

Boudewijn Rempt's avatar
Boudewijn Rempt committed
914
    layer->setOpacity(opacity);
Boudewijn Rempt's avatar