diff --git a/libs/flake/resources/KoGamutMask.cpp b/libs/flake/resources/KoGamutMask.cpp
index 2ea52ca51b..de09169e17 100644
--- a/libs/flake/resources/KoGamutMask.cpp
+++ b/libs/flake/resources/KoGamutMask.cpp
@@ -1,452 +1,437 @@
/*
* Copyright (c) 2018 Anna Medonosova
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "KoGamutMask.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
KoGamutMaskShape::KoGamutMaskShape(KoShape* shape)
: m_maskShape(shape)
, m_shapePaintingContext(KoShapePaintingContext())
{
}
KoGamutMaskShape::KoGamutMaskShape() {};
KoGamutMaskShape::~KoGamutMaskShape() {};
KoShape* KoGamutMaskShape::koShape()
{
return m_maskShape;
}
bool KoGamutMaskShape::coordIsClear(const QPointF& coord, const KoViewConverter& viewConverter, int maskRotation) const
{
// apply mask rotation to coord
const KisGamutMaskViewConverter& converter = dynamic_cast(viewConverter);
QPointF centerPoint(converter.viewSize().width()*0.5, converter.viewSize().height()*0.5);
QTransform rotationTransform;
rotationTransform.translate(centerPoint.x(), centerPoint.y());
rotationTransform.rotate(-maskRotation);
rotationTransform.translate(-centerPoint.x(), -centerPoint.y());
QPointF rotatedCoord = rotationTransform.map(coord);
QPointF translatedPoint = viewConverter.viewToDocument(rotatedCoord);
bool isClear = m_maskShape->hitTest(translatedPoint);
return isClear;
}
void KoGamutMaskShape::paint(QPainter &painter, const KoViewConverter& viewConverter, int maskRotation)
{
painter.save();
// apply mask rotation before drawing
QPointF centerPoint(painter.viewport().width()*0.5, painter.viewport().height()*0.5);
painter.translate(centerPoint);
painter.rotate(maskRotation);
painter.translate(-centerPoint);
+
painter.setTransform(m_maskShape->absoluteTransformation(&viewConverter) * painter.transform());
m_maskShape->paint(painter, viewConverter, m_shapePaintingContext);
painter.restore();
}
void KoGamutMaskShape::paintStroke(QPainter &painter, const KoViewConverter &viewConverter, int maskRotation)
{
painter.save();
// apply mask rotation before drawing
QPointF centerPoint(painter.viewport().width()*0.5, painter.viewport().height()*0.5);
painter.translate(centerPoint);
painter.rotate(maskRotation);
painter.translate(-centerPoint);
painter.setTransform(m_maskShape->absoluteTransformation(&viewConverter) * painter.transform());
m_maskShape->paintStroke(painter, viewConverter, m_shapePaintingContext);
painter.restore();
}
-struct Q_DECL_HIDDEN KoGamutMask::Private {
- QString name;
- QString title;
- QString description;
- QByteArray data;
- QVector maskShapes;
- QVector previewShapes;
- QSizeF maskSize;
- int rotation;
-};
-
KoGamutMask::KoGamutMask(const QString& filename)
: KoResource(filename)
- , d(new Private())
{
- d->maskSize = QSizeF(144.0,144.0);
+ m_maskSize = QSizeF(144.0,144.0);
setRotation(0);
+ setStoredInDocument(false);
}
KoGamutMask::KoGamutMask()
: KoResource(QString())
- , d(new Private())
{
- d->maskSize = QSizeF(144.0,144.0);
+ m_maskSize = QSizeF(144.0,144.0);
setRotation(0);
+ setStoredInDocument(false);
}
KoGamutMask::KoGamutMask(KoGamutMask* rhs)
: QObject(0)
, KoResource(QString())
- , d(new Private())
{
setFilename(rhs->filename());
- setTitle(rhs->title());
+ setName(rhs->name());
setDescription(rhs->description());
- d->maskSize = rhs->d->maskSize;
+ m_maskSize = rhs->maskSize();
+ setStoredInDocument(rhs->storedInDocument());
QList newShapes;
for(KoShape* sh: rhs->koShapes()) {
newShapes.append(sh->cloneShape());
}
setMaskShapes(newShapes);
setValid(true);
}
bool KoGamutMask::coordIsClear(const QPointF& coord, KoViewConverter &viewConverter, bool preview)
{
QVector* shapeVector;
- if (preview && !d->previewShapes.isEmpty()) {
- shapeVector = &d->previewShapes;
+ if (preview && !m_previewShapes.isEmpty()) {
+ shapeVector = &m_previewShapes;
} else {
- shapeVector = &d->maskShapes;
+ shapeVector = &m_maskShapes;
}
for(KoGamutMaskShape* shape: *shapeVector) {
if (shape->coordIsClear(coord, viewConverter, rotation()) == true) {
return true;
}
}
return false;
}
void KoGamutMask::paint(QPainter &painter, KoViewConverter& viewConverter, bool preview)
{
QVector* shapeVector;
- if (preview && !d->previewShapes.isEmpty()) {
- shapeVector = &d->previewShapes;
+ if (preview && !m_previewShapes.isEmpty()) {
+ shapeVector = &m_previewShapes;
} else {
- shapeVector = &d->maskShapes;
+ shapeVector = &m_maskShapes;
}
for(KoGamutMaskShape* shape: *shapeVector) {
shape->paint(painter, viewConverter, rotation());
}
}
void KoGamutMask::paintStroke(QPainter &painter, KoViewConverter &viewConverter, bool preview)
{
QVector* shapeVector;
- if (preview && !d->previewShapes.isEmpty()) {
- shapeVector = &d->previewShapes;
+ if (preview && !m_previewShapes.isEmpty()) {
+ shapeVector = &m_previewShapes;
} else {
- shapeVector = &d->maskShapes;
+ shapeVector = &m_maskShapes;
}
for(KoGamutMaskShape* shape: *shapeVector) {
shape->paintStroke(painter, viewConverter, rotation());
}
}
bool KoGamutMask::load()
{
QFile file(filename());
if (file.size() == 0) return false;
if (!file.open(QIODevice::ReadOnly)) {
warnFlake << "Can't open file " << filename();
return false;
}
bool res = loadFromDevice(&file);
file.close();
return res;
}
bool KoGamutMask::loadFromDevice(QIODevice *dev)
{
if (!dev->isOpen()) dev->open(QIODevice::ReadOnly);
- d->data = dev->readAll();
+ QByteArray data = dev->readAll();
- // TODO: test
- KIS_ASSERT_RECOVER_RETURN_VALUE(d->data.size() != 0, false);
+ KIS_ASSERT_RECOVER_RETURN_VALUE(data.size() != 0, false);
if (filename().isNull()) {
warnFlake << "Cannot load gamut mask" << name() << "there is no filename set";
return false;
}
- if (d->data.isNull()) {
+ if (data.isNull()) {
QFile file(filename());
if (file.size() == 0) {
warnFlake << "Cannot load gamut mask" << name() << "there is no data available";
return false;
}
file.open(QIODevice::ReadOnly);
- d->data = file.readAll();
+ data = file.readAll();
file.close();
}
- QBuffer buf(&d->data);
+ QBuffer buf(&data);
buf.open(QBuffer::ReadOnly);
KoStore* store(KoStore::createStore(&buf, KoStore::Read, "application/x-krita-gamutmask", KoStore::Zip));
if (!store || store->bad()) return false;
bool storeOpened = store->open("gamutmask.svg");
if (!storeOpened) { return false; }
- QByteArray data;
- data.resize(store->size());
QByteArray ba = store->read(store->size());
store->close();
QString errorMsg;
int errorLine = 0;
int errorColumn = 0;
KoXmlDocument xmlDocument = SvgParser::createDocumentFromSvg(ba, &errorMsg, &errorLine, &errorColumn);
if (xmlDocument.isNull()) {
errorFlake << "Parsing error in " << filename() << "! Aborting!" << endl
<< " In line: " << errorLine << ", column: " << errorColumn << endl
<< " Error message: " << errorMsg << endl;
errorFlake << "Parsing error in the main document at line" << errorLine
<< ", column" << errorColumn << endl
<< "Error message: " << errorMsg;
return false;
}
KoDocumentResourceManager manager;
SvgParser parser(&manager);
parser.setResolution(QRectF(0,0,100,100), 72); // initialize with default values
QSizeF fragmentSize;
QList shapes = parser.parseSvg(xmlDocument.documentElement(), &fragmentSize);
- d->maskSize = fragmentSize;
+ m_maskSize = fragmentSize;
- d->title = parser.documentTitle();
- setName(d->title);
- d->description = parser.documentDescription();
+ setName(parser.documentTitle());
+ m_description = parser.documentDescription();
setMaskShapes(shapes);
if (store->open("preview.png")) {
KoStoreDevice previewDev(store);
previewDev.open(QIODevice::ReadOnly);
QImage preview = QImage();
preview.load(&previewDev, "PNG");
setImage(preview);
(void)store->close();
}
buf.close();
setValid(true);
return true;
}
void KoGamutMask::setMaskShapes(QList shapes)
{
- setMaskShapesToVector(shapes, d->maskShapes);
+ setMaskShapesToVector(shapes, m_maskShapes);
}
bool KoGamutMask::save()
{
QFile file(filename());
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
return false;
}
saveToDevice(&file);
file.close();
return true;
}
QList KoGamutMask::koShapes() const
{
QList shapes;
- for(KoGamutMaskShape* maskShape: d->maskShapes) {
+ for(KoGamutMaskShape* maskShape: m_maskShapes) {
shapes.append(maskShape->koShape());
}
return shapes;
}
bool KoGamutMask::saveToDevice(QIODevice *dev) const
{
KoStore* store(KoStore::createStore(dev, KoStore::Write, "application/x-krita-gamutmask", KoStore::Zip));
if (!store || store->bad()) return false;
QList shapes = koShapes();
std::sort(shapes.begin(), shapes.end(), KoShape::compareShapeZIndex);
if (!store->open("gamutmask.svg")) {
return false;
}
KoStoreDevice storeDev(store);
storeDev.open(QIODevice::WriteOnly);
SvgWriter writer(shapes);
- writer.setDocumentTitle(d->title);
- writer.setDocumentDescription(d->description);
+ writer.setDocumentTitle(name());
+ writer.setDocumentDescription(m_description);
- writer.save(storeDev, d->maskSize);
+ writer.save(storeDev, m_maskSize);
if (!store->close()) { return false; }
if (!store->open("preview.png")) {
return false;
}
KoStoreDevice previewDev(store);
previewDev.open(QIODevice::WriteOnly);
image().save(&previewDev, "PNG");
if (!store->close()) { return false; }
return store->finalize();
}
bool KoGamutMask::loadFromByteArray(QByteArray data)
{
QBuffer buf(&data);
buf.open(QIODevice::ReadOnly);
return loadFromDevice(&buf);
}
QByteArray KoGamutMask::toByteArray()
{
QBuffer saveBuffer;
saveBuffer.open(QIODevice::WriteOnly);
if (!saveToDevice(&saveBuffer)) {
warnFlake << "KoGamutMask::toByteArray(): saving gamut mask failed:" << name();
return QByteArray();
}
saveBuffer.close();
saveBuffer.open(QIODevice::ReadOnly);
QByteArray data = saveBuffer.readAll();
saveBuffer.close();
return data;
}
-QString KoGamutMask::title()
+QString KoGamutMask::description()
{
- return d->title;
+ return m_description;
}
-void KoGamutMask::setTitle(QString title)
+void KoGamutMask::setDescription(QString description)
{
- d->title = title;
- setName(title);
+ m_description = description;
}
-QString KoGamutMask::description()
+int KoGamutMask::rotation()
{
- return d->description;
+ return m_rotation;
}
-void KoGamutMask::setDescription(QString description)
+void KoGamutMask::setRotation(int rotation)
{
- d->description = description;
+ m_rotation = rotation;
}
-int KoGamutMask::rotation()
+QSizeF KoGamutMask::maskSize()
{
- return d->rotation;
+ return m_maskSize;
}
-void KoGamutMask::setRotation(int rotation)
+bool KoGamutMask::storedInDocument()
{
- d->rotation = rotation;
+ return m_storedInDocument;
}
-QSizeF KoGamutMask::maskSize()
+void KoGamutMask::setStoredInDocument(bool state)
{
- return d->maskSize;
+ m_storedInDocument = state;
}
void KoGamutMask::setPreviewMaskShapes(QList shapes)
{
- setMaskShapesToVector(shapes, d->previewShapes);
+ setMaskShapesToVector(shapes, m_previewShapes);
}
void KoGamutMask::setMaskShapesToVector(QList shapes, QVector &targetVector)
{
targetVector.clear();
for(KoShape* sh: shapes) {
KoGamutMaskShape* maskShape = new KoGamutMaskShape(sh);
targetVector.append(maskShape);
}
}
// clean up when ending mask preview
void KoGamutMask::clearPreview()
{
- d->previewShapes.clear();
+ m_previewShapes.clear();
}
diff --git a/libs/flake/resources/KoGamutMask.h b/libs/flake/resources/KoGamutMask.h
index fad4bd7267..a88c440c1a 100644
--- a/libs/flake/resources/KoGamutMask.h
+++ b/libs/flake/resources/KoGamutMask.h
@@ -1,102 +1,107 @@
/*
* Copyright (c) 2018 Anna Medonosova
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KOGAMUTMASK_H
#define KOGAMUTMASK_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
class KoViewConverter;
class KoGamutMaskShape
{
public:
KoGamutMaskShape(KoShape* shape);
KoGamutMaskShape();
~KoGamutMaskShape();
bool coordIsClear(const QPointF& coord, const KoViewConverter& viewConverter, int maskRotation) const;
QPainterPath outline();
void paint(QPainter &painter, const KoViewConverter& viewConverter, int maskRotation);
void paintStroke(QPainter &painter, const KoViewConverter& viewConverter, int maskRotation);
KoShape* koShape();
private:
KoShape* m_maskShape;
KoShapePaintingContext m_shapePaintingContext;
};
/**
- * @brief The resource type for gamut masks used by the artistic color selector
+ * @brief The resource type for gamut masks used by color selectors
*/
class KRITAFLAKE_EXPORT KoGamutMask : public QObject, public KoResource
{
Q_OBJECT
public:
KoGamutMask(const QString &filename);
KoGamutMask();
KoGamutMask(KoGamutMask *rhs);
bool coordIsClear(const QPointF& coord, KoViewConverter& viewConverter, bool preview);
bool load() override __attribute__((optimize(0)));
bool loadFromDevice(QIODevice *dev) override;
bool save() override;
bool saveToDevice(QIODevice* dev) const override;
bool loadFromByteArray(QByteArray data);
QByteArray toByteArray();
void paint(QPainter &painter, KoViewConverter& viewConverter, bool preview);
void paintStroke(QPainter &painter, KoViewConverter& viewConverter, bool preview);
- QString title();
- void setTitle(QString title);
-
QString description();
void setDescription(QString description);
int rotation();
void setRotation(int rotation);
QSizeF maskSize();
+ bool storedInDocument();
+ void setStoredInDocument(bool state);
+
void setMaskShapes(QList shapes);
void setPreviewMaskShapes(QList shapes);
QList koShapes() const;
void clearPreview();
private:
void setMaskShapesToVector(QList shapes, QVector& targetVector);
- struct Private;
- Private* const d;
+ QString m_description;
+ QVector m_maskShapes;
+ QVector m_previewShapes;
+ QSizeF m_maskSize;
+ int m_rotation;
+ bool m_storedInDocument;
+
};
#endif // KOGAMUTMASK_H
diff --git a/plugins/dockers/gamutmask/KisGamutMaskChooser.cpp b/plugins/dockers/gamutmask/KisGamutMaskChooser.cpp
index 19cf385cc7..df5f874775 100644
--- a/plugins/dockers/gamutmask/KisGamutMaskChooser.cpp
+++ b/plugins/dockers/gamutmask/KisGamutMaskChooser.cpp
@@ -1,251 +1,251 @@
/*
* Copyright (c) 2018 Anna Medonosova
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "KisGamutMaskChooser.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/// The resource item delegate for rendering the resource preview
class KisGamutMaskDelegate: public QAbstractItemDelegate
{
public:
KisGamutMaskDelegate(QObject * parent = 0) : QAbstractItemDelegate(parent)
, m_mode(KisGamutMaskChooser::ViewMode::THUMBNAIL) {}
~KisGamutMaskDelegate() override {}
/// reimplemented
void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const override;
/// reimplemented
QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex &) const override {
return option.decorationSize;
}
void setViewMode(KisGamutMaskChooser::ViewMode mode) {
m_mode = mode;
}
private:
KisGamutMaskChooser::ViewMode m_mode;
};
void KisGamutMaskDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
painter->save();
painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
if (!index.isValid())
return;
KoResource* resource = static_cast(index.internalPointer());
KoGamutMask* mask = static_cast(resource);
if (!mask) {
return;
}
QImage preview = mask->image();
if(preview.isNull()) {
return;
}
QRect paintRect = option.rect.adjusted(1, 1, -1, -1);
if (m_mode == KisGamutMaskChooser::ViewMode::THUMBNAIL) {
painter->drawImage(paintRect.x(), paintRect.y(),
preview.scaled(paintRect.size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
if (option.state & QStyle::State_Selected) {
painter->setCompositionMode(QPainter::CompositionMode_Overlay);
painter->setOpacity(0.5);
painter->fillRect(paintRect, Qt::white);
painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
painter->setOpacity(1.0);
painter->setPen(QPen(option.palette.highlight(), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin));
QRect selectedBorder = option.rect.adjusted(1, 1, -1, -1);
painter->drawRect(selectedBorder);
}
} else {
QSize previewSize(paintRect.height(), paintRect.height());
painter->drawImage(paintRect.x(), paintRect.y(),
preview.scaled(previewSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
int leftMargin = 8;
int rightMargin = 7;
int vertMargin = 4;
int descOffset = 7;
QFont font = option.font;
font.setBold(true);
painter->setFont(font);
QRectF titleRect(QPointF(previewSize.width() + leftMargin, paintRect.y() + vertMargin),
QPointF(paintRect.width() - rightMargin, paintRect.y() + descOffset + painter->fontMetrics().lineSpacing()));
painter->drawText(titleRect, Qt::AlignLeft,
painter->fontMetrics().elidedText(
- mask->title(), Qt::ElideRight, titleRect.width()
+ mask->name(), Qt::ElideRight, titleRect.width()
)
);
if (!mask->description().isEmpty() && !mask->description().isNull()) {
font.setPointSize(font.pointSize()-1);
font.setBold(false);
font.setStyle(QFont::StyleItalic);
painter->setFont(font);
QRectF descRect(QPointF(previewSize.width() + leftMargin, paintRect.y() + descOffset + painter->fontMetrics().lineSpacing()),
QPointF(paintRect.right() - rightMargin, paintRect.bottom() - vertMargin));
int numLines = floor(((float)descRect.height() / (float)painter->fontMetrics().lineSpacing()));
if (numLines > 0) {
int elideWidth = 0;
QTextLayout textLayout(mask->description());
textLayout.beginLayout();
for (int i = 0; i < numLines; i++) {
QTextLine line = textLayout.createLine();
if (line.isValid()) {
line.setLineWidth(descRect.width());
elideWidth += line.naturalTextWidth();
}
}
QString elidedText = painter->fontMetrics().elidedText(mask->description(), Qt::ElideRight, elideWidth);
painter->drawText(descRect, Qt::AlignLeft|Qt::TextWordWrap, elidedText);
}
}
}
painter->restore();
}
KisGamutMaskChooser::KisGamutMaskChooser(QWidget *parent) : QWidget(parent)
{
m_delegate = new KisGamutMaskDelegate(this);
KoResourceServer* rServer = KoResourceServerProvider::instance()->gamutMaskServer();
QSharedPointer adapter(new KoResourceServerAdapter(rServer));
m_itemChooser = new KoResourceItemChooser(adapter, this);
m_itemChooser->setItemDelegate(m_delegate);
m_itemChooser->showTaggingBar(true);
m_itemChooser->showButtons(false);
m_itemChooser->setColumnCount(4);
m_itemChooser->setSynced(true);
QVBoxLayout* layout = new QVBoxLayout(this);
layout->setContentsMargins(0,0,0,0);
// TODO: menu for view mode change
QMenu* menu = new QMenu(this);
menu->setStyleSheet("margin: 6px");
menu->addSection(i18n("Display"));
QActionGroup *actionGroup = new QActionGroup(this);
KisConfig cfg(true);
m_mode = KisGamutMaskChooser::ViewMode(cfg.readEntry("GamutMasks.viewMode", KisGamutMaskChooser::THUMBNAIL));
QAction* action = menu->addAction(KisIconUtils::loadIcon("view-preview"),
i18n("Thumbnails"), this, SLOT(slotSetModeThumbnail()));
action->setCheckable(true);
action->setChecked(m_mode == KisGamutMaskChooser::THUMBNAIL);
action->setActionGroup(actionGroup);
action = menu->addAction(KisIconUtils::loadIcon("view-list-details"),
i18n("Details"), this, SLOT(slotSetModeDetail()));
action->setCheckable(true);
action->setChecked(m_mode == KisGamutMaskChooser::DETAIL);
action->setActionGroup(actionGroup);
// setting the view mode
setViewMode(m_mode);
m_itemChooser->setViewModeButtonVisible(true);
QToolButton* viewModeButton = m_itemChooser->viewModeButton();
viewModeButton->setMenu(menu);
layout->addWidget(m_itemChooser);
setLayout(layout);
connect(m_itemChooser, SIGNAL(resourceSelected(KoResource*)), this, SLOT(resourceSelected(KoResource*)));
}
KisGamutMaskChooser::~KisGamutMaskChooser()
{
}
void KisGamutMaskChooser::setCurrentResource(KoResource *resource)
{
m_itemChooser->setCurrentResource(resource);
}
void KisGamutMaskChooser::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
updateViewSettings();
}
void KisGamutMaskChooser::setViewMode(KisGamutMaskChooser::ViewMode mode)
{
m_mode = mode;
updateViewSettings();
}
void KisGamutMaskChooser::updateViewSettings()
{
KisConfig cfg(false);
cfg.writeEntry("GamutMasks.viewMode", qint32(m_mode));
if (m_mode == KisGamutMaskChooser::THUMBNAIL) {
m_itemChooser->setSynced(true);
m_delegate->setViewMode(m_mode);
} else if (m_mode == KisGamutMaskChooser::DETAIL) {
m_itemChooser->setSynced(false);
m_itemChooser->setColumnCount(1);
m_itemChooser->setRowHeight(this->fontMetrics().lineSpacing()*4);
m_itemChooser->setColumnWidth(m_itemChooser->width());
m_delegate->setViewMode(m_mode);
}
}
void KisGamutMaskChooser::resourceSelected(KoResource* resource)
{
emit sigGamutMaskSelected(static_cast(resource));
}
void KisGamutMaskChooser::slotSetModeThumbnail()
{
setViewMode(KisGamutMaskChooser::THUMBNAIL);
}
void KisGamutMaskChooser::slotSetModeDetail()
{
setViewMode(KisGamutMaskChooser::DETAIL);
}
diff --git a/plugins/dockers/gamutmask/gamutmask_dock.cpp b/plugins/dockers/gamutmask/gamutmask_dock.cpp
index bf64a8cecd..5842de0f51 100644
--- a/plugins/dockers/gamutmask/gamutmask_dock.cpp
+++ b/plugins/dockers/gamutmask/gamutmask_dock.cpp
@@ -1,662 +1,662 @@
/*
* Copyright (c) 2018 Anna Medonosova
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2.1 of the License.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "gamutmask_dock.h"
#include
#include
#include
#include
#include
#include
#include "ui_wdgGamutMaskChooser.h"
class KisMainWindow;
struct GamutMaskChooserUI: public QWidget, public Ui_wdgGamutMaskChooser
{
GamutMaskChooserUI() {
setupUi(this);
}
};
GamutMaskDock::GamutMaskDock()
: QDockWidget(i18n("Gamut Masks"))
, m_resourceProvider(0)
, m_selfClosingTemplate(false)
, m_externalTemplateClose(false)
, m_creatingNewMask(false)
, m_templatePrevSaved(false)
, m_selfSelectingMask(false)
, m_blockMaskStoring(false)
, m_selectedMask(nullptr)
, m_maskDocument(nullptr)
, m_templateView(nullptr)
{
m_dockerUI = new GamutMaskChooserUI();
m_dockerUI->bnMaskEditor->setIcon(KisIconUtils::loadIcon("dirty-preset"));
m_dockerUI->bnMaskDelete->setIcon(KisIconUtils::loadIcon("deletelayer"));
m_dockerUI->bnMaskNew->setIcon(KisIconUtils::loadIcon("list-add"));
m_dockerUI->bnMaskDuplicate->setIcon(KisIconUtils::loadIcon("duplicatelayer"));
m_dockerUI->maskPropertiesBox->setVisible(false);
m_dockerUI->bnSaveMask->setIcon(KisIconUtils::loadIcon("document-save"));
m_dockerUI->bnCancelMaskEdit->setIcon(KisIconUtils::loadIcon("dialog-cancel"));
m_dockerUI->bnPreviewMask->setIcon(KisIconUtils::loadIcon("visible"));
QRegularExpression maskTitleRegex("^[-_\\(\\)\\sA-Za-z0-9]+$");
QRegularExpressionValidator* m_maskTitleValidator = new QRegularExpressionValidator(maskTitleRegex, this);
m_dockerUI->maskTitleEdit->setValidator(m_maskTitleValidator);
KoResourceServer* rServer = KoResourceServerProvider::instance()->gamutMaskServer();
rServer->addObserver(this);
// gamut mask connections
connect(m_dockerUI->bnSaveMask , SIGNAL(clicked()) , SLOT(slotGamutMaskSave()));
connect(m_dockerUI->bnCancelMaskEdit , SIGNAL(clicked()) , SLOT(slotGamutMaskCancelEdit()));
connect(m_dockerUI->bnPreviewMask , SIGNAL(clicked()) , SLOT(slotGamutMaskPreview()));
connect(m_dockerUI->bnMaskEditor , SIGNAL(clicked()) , SLOT(slotGamutMaskEdit()));
connect(m_dockerUI->maskChooser, SIGNAL(sigGamutMaskSelected(KoGamutMask*)), SLOT(slotGamutMaskSelected(KoGamutMask*)));
connect(m_dockerUI->bnMaskNew , SIGNAL(clicked()) , SLOT(slotGamutMaskCreateNew()));
connect(m_dockerUI->bnMaskDelete , SIGNAL(clicked()) , SLOT(slotGamutMaskDelete()));
connect(m_dockerUI->bnMaskDuplicate , SIGNAL(clicked()) , SLOT(slotGamutMaskDuplicate()));
setWidget(m_dockerUI);
}
GamutMaskDock::~GamutMaskDock()
{
KoResourceServer* rServer = KoResourceServerProvider::instance()->gamutMaskServer();
rServer->removeObserver(this);
}
void GamutMaskDock::setViewManager(KisViewManager* kisview)
{
m_resourceProvider = kisview->resourceProvider();
selectMask(m_resourceProvider->currentGamutMask());
connect(this, SIGNAL(sigGamutMaskSet(KoGamutMask*)), m_resourceProvider, SLOT(slotGamutMaskActivated(KoGamutMask*)));
connect(this, SIGNAL(sigGamutMaskChanged(KoGamutMask*)), m_resourceProvider, SLOT(slotGamutMaskActivated(KoGamutMask*)));
connect(this, SIGNAL(sigGamutMaskUnset()), m_resourceProvider, SLOT(slotGamutMaskUnset()));
connect(this, SIGNAL(sigGamutMaskPreviewUpdate()), m_resourceProvider, SLOT(slotGamutMaskPreviewUpdate()));
connect(KisPart::instance(), SIGNAL(sigDocumentRemoved(QString)), this, SLOT(slotDocumentRemoved(QString)));
}
void GamutMaskDock::slotGamutMaskEdit()
{
if (!m_selectedMask) {
return;
}
openMaskEditor();
}
bool GamutMaskDock::openMaskEditor()
{
if (!m_selectedMask) {
return false;
}
// find the template resource first, so we can abort the action early on
QString maskTemplateFile = KoResourcePaths::findResource("ko_gamutmasks", "GamutMaskTemplate.kra");
if (maskTemplateFile.isEmpty() || maskTemplateFile.isNull() || !QFile::exists(maskTemplateFile)) {
dbgPlugins << "GamutMaskDock::openMaskEditor(): maskTemplateFile (" << maskTemplateFile << ") was not found on the system";
getUserFeedback(i18n("Could not open gamut mask for editing."),
i18n("The editor template was not found."),
QMessageBox::Ok, QMessageBox::Ok, QMessageBox::Critical);
return false;
}
m_dockerUI->maskPropertiesBox->setVisible(true);
m_dockerUI->maskPropertiesBox->setEnabled(true);
m_dockerUI->editControlsBox->setEnabled(false);
m_dockerUI->editControlsBox->setVisible(false);
- m_dockerUI->maskTitleEdit->setText(m_selectedMask->title());
+ m_dockerUI->maskTitleEdit->setText(m_selectedMask->name());
m_dockerUI->maskDescriptionEdit->setPlainText(m_selectedMask->description());
m_maskDocument = KisPart::instance()->createDocument();
KisPart::instance()->addDocument(m_maskDocument);
m_maskDocument->openUrl(QUrl::fromLocalFile(maskTemplateFile), KisDocument::DontAddToRecent);
// template document needs a proper autogenerated filename,
// to avoid collision with other documents,
// otherwise bugs happen when slotDocumentRemoved is called
// (e.g. user closes another view, the template stays open, but the edit operation is canceled)
m_maskDocument->setInfiniteAutoSaveInterval();
QString maskPath = QString("%1%2%3_%4.kra")
.arg(QDir::tempPath())
.arg(QDir::separator())
.arg("GamutMaskTemplate")
.arg(std::time(nullptr));
m_maskDocument->setUrl(QUrl::fromLocalFile(maskPath));
m_maskDocument->setLocalFilePath(maskPath);
KisShapeLayerSP shapeLayer = getShapeLayer();
// pass only copies of shapes to the layer,
// so the originals don't disappear from the mask later
for (KoShape *shape: m_selectedMask->koShapes()) {
KoShape* newShape = shape->cloneShape();
newShape->setStroke(KoShapeStrokeModelSP());
newShape->setBackground(QSharedPointer(new KoColorBackground(QColor(255,255,255))));
shapeLayer->addShape(newShape);
}
m_maskDocument->setPreActivatedNode(shapeLayer);
// set document as active
KisMainWindow* mainWindow = KisPart::instance()->currentMainwindow();
KIS_ASSERT(mainWindow);
m_templateView = mainWindow->addViewAndNotifyLoadingCompleted(m_maskDocument);
KIS_ASSERT(m_templateView);
for(KisView *view: KisPart::instance()->views()) {
if (view->document() == m_maskDocument) {
view->activateWindow();
break;
}
}
connect(m_templateView->viewManager(), SIGNAL(viewChanged()), this, SLOT(slotViewChanged()));
connect(m_maskDocument, SIGNAL(completed()), this, SLOT(slotDocumentSaved()));
return true;
}
void GamutMaskDock::cancelMaskEdit()
{
if (m_creatingNewMask) {
deleteMask();
}
if (m_selectedMask) {
m_selectedMask->clearPreview();
if (m_resourceProvider->currentGamutMask() == m_selectedMask) {
emit sigGamutMaskChanged(m_selectedMask);
}
}
closeMaskDocument();
}
void GamutMaskDock::selectMask(KoGamutMask *mask, bool notifyItemChooser)
{
if (!mask) {
return;
}
m_selectedMask = mask;
if (notifyItemChooser) {
m_selfSelectingMask = true;
m_dockerUI->maskChooser->setCurrentResource(m_selectedMask);
m_selfSelectingMask = false;
}
if (m_blockMaskStoring) {
if (m_canvas) {
m_canvas->imageView()->document()->setGamutMask(m_selectedMask);
}
}
emit sigGamutMaskSet(m_selectedMask);
}
bool GamutMaskDock::saveSelectedMaskResource()
{
if (!m_selectedMask || !m_maskDocument) {
return false;
}
bool maskSaved = false;
if (m_selectedMask) {
QList shapes = getShapesFromLayer();
if (shapes.count() > 0) {
m_selectedMask->setMaskShapes(shapes);
m_selectedMask->setImage(
m_maskDocument->image()->convertToQImage(m_maskDocument->image()->bounds()
, m_maskDocument->image()->profile()
)
);
m_selectedMask->setDescription(m_dockerUI->maskDescriptionEdit->toPlainText());
m_selectedMask->clearPreview();
m_selectedMask->save();
maskSaved = true;
} else {
- getUserFeedback(i18n("Saving of gamut mask '%1' was aborted.", m_selectedMask->title()),
+ getUserFeedback(i18n("Saving of gamut mask '%1' was aborted.", m_selectedMask->name()),
i18n("The mask template is invalid.
"
"Please check that:"
"
"
"- your template contains a vector layer named 'maskShapesLayer'
"
"- there are one or more vector shapes on the 'maskShapesLayer'
"
"
"
),
QMessageBox::Ok, QMessageBox::Ok);
}
}
return maskSaved;
}
void GamutMaskDock::deleteMask()
{
KoResourceServer* rServer = KoResourceServerProvider::instance()->gamutMaskServer();
rServer->removeResourceAndBlacklist(m_selectedMask);
m_selectedMask = nullptr;
}
int GamutMaskDock::getUserFeedback(QString text, QString informativeText,
QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton,
QMessageBox::Icon severity)
{
QMessageBox msgBox;
msgBox.setWindowTitle(i18nc("@title:window", "Krita"));
msgBox.setText(QString("%1
").arg(text));
msgBox.setInformativeText(informativeText);
msgBox.setStandardButtons(buttons);
msgBox.setDefaultButton(defaultButton);
msgBox.setIcon(severity);
int res = msgBox.exec();
return res;
}
int GamutMaskDock::saveOrCancel(QMessageBox::StandardButton defaultAction)
{
int response = 0;
if (m_maskDocument->isModified()) {
- response = getUserFeedback(i18n("Gamut mask '%1' has been modified.", m_selectedMask->title()),
+ response = getUserFeedback(i18n("Gamut mask '%1' has been modified.", m_selectedMask->name()),
i18n("Do you want to save it?"),
QMessageBox::Cancel | QMessageBox::Close | QMessageBox::Save, defaultAction);
} else if (m_templatePrevSaved && defaultAction != QMessageBox::Close) {
response = QMessageBox::Save;
} else if (!m_templatePrevSaved) {
response = QMessageBox::Close;
} else {
response = defaultAction;
}
switch (response) {
case QMessageBox::Save : {
slotGamutMaskSave();
break;
}
case QMessageBox::Close : {
cancelMaskEdit();
break;
}
}
return response;
}
KoGamutMask *GamutMaskDock::createMaskResource(KoGamutMask* sourceMask, QString newTitle)
{
m_creatingNewMask = true;
KoGamutMask* newMask = nullptr;
if (sourceMask) {
newMask = new KoGamutMask(sourceMask);
newMask->setImage(sourceMask->image());
} else {
newMask = new KoGamutMask();
QString defaultPreviewPath = KoResourcePaths::findResource("ko_gamutmasks", "empty_mask_preview.png");
KIS_SAFE_ASSERT_RECOVER_NOOP(!(defaultPreviewPath.isEmpty() || defaultPreviewPath.isNull() || !QFile::exists(defaultPreviewPath)));
newMask->setImage(QImage(defaultPreviewPath, "PNG"));
}
QPair maskFile = resolveMaskTitle(newTitle);
QString maskTitle = maskFile.first;
QFileInfo fileInfo = maskFile.second;
- newMask->setTitle(maskTitle);
+ newMask->setName(maskTitle);
newMask->setFilename(fileInfo.filePath());
newMask->setValid(true);
KoResourceServer* rServer = KoResourceServerProvider::instance()->gamutMaskServer();
rServer->removeFromBlacklist(newMask);
rServer->addResource(newMask, false);
return newMask;
}
QPair GamutMaskDock::resolveMaskTitle(QString suggestedTitle)
{
KoResourceServer* rServer = KoResourceServerProvider::instance()->gamutMaskServer();
QString saveLocation = rServer->saveLocation();
QString processedTitle = suggestedTitle.trimmed();
QString resourceName = processedTitle;
while (rServer->resourceByName(resourceName)) {
resourceName = resourceName + QString(" (Copy)");
}
QString maskTitle = resourceName;
QString maskFile = maskTitle + ".kgm";
QString path = saveLocation + maskFile.replace(QRegularExpression("\\s+"), "_");
QFileInfo fileInfo(path);
return QPair(maskTitle, fileInfo);
}
void GamutMaskDock::closeMaskDocument()
{
if (!m_externalTemplateClose) {
if (m_maskDocument) {
// set the document to not modified to bypass confirmation dialog
// the close is already confirmed
m_maskDocument->setModified(false);
m_maskDocument->closeUrl();
m_templateView->closeView();
m_templateView->deleteLater();
// set a flag that we are doing it ourselves, so the docker does not react to
// removing signal from KisPart
m_selfClosingTemplate = true;
KisPart::instance()->removeView(m_templateView);
KisPart::instance()->removeDocument(m_maskDocument);
m_selfClosingTemplate = false;
}
}
m_dockerUI->maskPropertiesBox->setVisible(false);
m_dockerUI->editControlsBox->setVisible(true);
m_dockerUI->editControlsBox->setEnabled(true);
disconnect(m_templateView->viewManager(), SIGNAL(viewChanged()), this, SLOT(slotViewChanged()));
disconnect(m_maskDocument, SIGNAL(completed()), this, SLOT(slotDocumentSaved()));
// the template file is meant as temporary, if the user saved it, delete now
if (QFile::exists(m_maskDocument->localFilePath())) {
QFile::remove(m_maskDocument->localFilePath());
}
m_maskDocument = nullptr;
m_templateView = nullptr;
m_creatingNewMask = false;
m_templatePrevSaved = false;
}
QList GamutMaskDock::getShapesFromLayer()
{
KisShapeLayerSP shapeLayer = getShapeLayer();
// create a deep copy of the shapes to save in the mask,
// otherwise they vanish when the template closes
QList newShapes;
if (shapeLayer) {
for (KoShape* sh: shapeLayer->shapes()) {
KoShape* newShape = sh->cloneShape();
KoShapeStrokeSP border(new KoShapeStroke(0.5f, Qt::white));
newShape->setStroke(border);
newShape->setBackground(QSharedPointer(new KoColorBackground(QColor(255,255,255,0))));
newShapes.append(newShape);
}
}
return newShapes;
}
KisShapeLayerSP GamutMaskDock::getShapeLayer()
{
KisNodeSP node = m_maskDocument->image()->rootLayer()->findChildByName("maskShapesLayer");
return KisShapeLayerSP(dynamic_cast(node.data()));
}
void GamutMaskDock::slotGamutMaskSave()
{
if (!m_selectedMask || !m_maskDocument) {
return;
}
QString newTitle = m_dockerUI->maskTitleEdit->text();
- if (m_selectedMask->title() != newTitle) {
+ if (m_selectedMask->name() != newTitle) {
// title has changed, rename
KoGamutMask* newMask = createMaskResource(m_selectedMask, newTitle);
// delete old mask and select new
deleteMask();
selectMask(newMask);
}
bool maskSaved = saveSelectedMaskResource();
if (maskSaved) {
emit sigGamutMaskSet(m_selectedMask);
closeMaskDocument();
}
}
void GamutMaskDock::slotGamutMaskCancelEdit()
{
if (!m_selectedMask) {
return;
}
saveOrCancel(QMessageBox::Close);
}
void GamutMaskDock::slotGamutMaskPreview()
{
if (!m_selectedMask) {
return;
}
m_selectedMask->setPreviewMaskShapes(getShapesFromLayer());
emit sigGamutMaskPreviewUpdate();
}
void GamutMaskDock::slotGamutMaskSelected(KoGamutMask *mask)
{
if (!m_selfSelectingMask) {
if (m_maskDocument) {
int res = saveOrCancel();
if (res == QMessageBox::Cancel) {
return;
}
}
selectMask(mask, false);
}
}
void GamutMaskDock::setCanvas(KoCanvasBase *canvas)
{
if (!canvas) {
return;
}
setEnabled(canvas != 0);
m_canvas = dynamic_cast(canvas);
if (!m_canvas && !m_canvas->imageView()) {
return;
}
KoGamutMask* storedMask = m_canvas->imageView()->document()->gamutMask();
if (storedMask) {
KoResourceServer* rServer = KoResourceServerProvider::instance()->gamutMaskServer();
if (!rServer->resourceByName(storedMask->title())) {
rServer->addResource(storedMask, false);
}
m_blockMaskStoring = true;
selectMask(storedMask);
m_blockMaskStoring = false;
}
}
void GamutMaskDock::unsetCanvas()
{
setEnabled(false);
m_canvas = nullptr;
}
void GamutMaskDock::unsetResourceServer()
{
KoResourceServer* rServer = KoResourceServerProvider::instance()->gamutMaskServer();
rServer->removeObserver(this);
}
void GamutMaskDock::removingResource(KoGamutMask *resource)
{
// if deleting previously set mask, notify selectors to unset their mask
if (resource == m_resourceProvider->currentGamutMask()) {
emit sigGamutMaskUnset();
m_selectedMask = nullptr;
}
}
void GamutMaskDock::resourceChanged(KoGamutMask *resource)
{
// if currently set mask has been changed, notify selectors
if (resource == m_resourceProvider->currentGamutMask()) {
selectMask(resource);
}
}
void GamutMaskDock::slotGamutMaskCreateNew()
{
KoGamutMask* newMask = createMaskResource(nullptr, "new mask");
selectMask(newMask);
bool editorOpened = openMaskEditor();
if (!editorOpened) {
deleteMask();
}
}
void GamutMaskDock::slotGamutMaskDuplicate()
{
if (!m_selectedMask) {
return;
}
- KoGamutMask* newMask = createMaskResource(m_selectedMask, m_selectedMask->title());
+ KoGamutMask* newMask = createMaskResource(m_selectedMask, m_selectedMask->name());
selectMask(newMask);
bool editorOpened = openMaskEditor();
if (!editorOpened) {
deleteMask();
}
}
void GamutMaskDock::slotGamutMaskDelete()
{
if (!m_selectedMask) {
return;
}
int res = getUserFeedback(i18n("Are you sure you want to delete mask '%1'?"
- , m_selectedMask->title()));
+ , m_selectedMask->name()));
if (res == QMessageBox::Yes) {
deleteMask();
}
}
void GamutMaskDock::slotDocumentRemoved(QString filename)
{
if (!m_maskDocument) {
return;
}
m_externalTemplateClose = true;
// we do not want to run this if it is we who close the file
if (!m_selfClosingTemplate) {
// KisPart called, that a document will be removed
// if it's ours, cancel the mask edit operation
if (m_maskDocument->url().toLocalFile() == filename) {
m_maskDocument->waitForSavingToComplete();
saveOrCancel();
}
}
m_externalTemplateClose = false;
}
void GamutMaskDock::slotViewChanged()
{
if (!m_maskDocument || !m_templateView) {
return;
}
if (m_templateView->viewManager()->document() == m_maskDocument) {
m_dockerUI->maskPropertiesBox->setEnabled(true);
} else {
m_dockerUI->maskPropertiesBox->setEnabled(false);
}
}
void GamutMaskDock::slotDocumentSaved()
{
m_templatePrevSaved = true;
}
diff --git a/plugins/impex/libkra/kis_kra_loader.cpp b/plugins/impex/libkra/kis_kra_loader.cpp
index ccf0d2665f..be9b76e74a 100644
--- a/plugins/impex/libkra/kis_kra_loader.cpp
+++ b/plugins/impex/libkra/kis_kra_loader.cpp
@@ -1,1293 +1,1295 @@
/* This file is part of the KDE project
* Copyright (C) Boudewijn Rempt , (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.
*/
#include "kis_kra_loader.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "lazybrush/kis_colorize_mask.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "KisResourceServerProvider.h"
#include "kis_keyframe_channel.h"
#include
#include "KisReferenceImagesLayer.h"
#include "KisReferenceImage.h"
#include
#include
#include "KisDocument.h"
#include "kis_config.h"
#include "kis_kra_tags.h"
#include "kis_kra_utils.h"
#include "kis_kra_load_visitor.h"
#include "kis_dom_utils.h"
#include "kis_image_animation_interface.h"
#include "kis_time_range.h"
#include "kis_grid_config.h"
#include "kis_guides_config.h"
#include "kis_image_config.h"
#include "KisProofingConfiguration.h"
#include "kis_layer_properties_icons.h"
#include "kis_node_view_color_scheme.h"
/*
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
*/
using namespace KRA;
struct KisKraLoader::Private
{
public:
KisDocument* document;
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
QMap layerFilenames; // temp storage during loading
int syntaxVersion; // version of the fileformat we are loading
vKisNodeSP selectedNodes; // the nodes that were active when saving the document.
QMap assistantsFilenames;
QList assistants;
QMap keyframeFilenames;
QVector paletteFilenames;
QString gamutMaskFilename;
int gamutMaskRotation;
QStringList errorMessages;
QStringList warningMessages;
};
void convertColorSpaceNames(QString &colorspacename, QString &profileProductName) {
if (colorspacename == "Grayscale + Alpha") {
colorspacename = "GRAYA";
profileProductName.clear();
}
else if (colorspacename == "RgbAF32") {
colorspacename = "RGBAF32";
profileProductName.clear();
}
else if (colorspacename == "RgbAF16") {
colorspacename = "RGBAF16";
profileProductName.clear();
}
else if (colorspacename == "CMYKA16") {
colorspacename = "CMYKAU16";
}
else if (colorspacename == "GrayF32") {
colorspacename = "GRAYAF32";
profileProductName.clear();
}
else if (colorspacename == "GRAYA16") {
colorspacename = "GRAYAU16";
}
else if (colorspacename == "XyzAF16") {
colorspacename = "XYZAF16";
profileProductName.clear();
}
else if (colorspacename == "XyzAF32") {
colorspacename = "XYZAF32";
profileProductName.clear();
}
else if (colorspacename == "YCbCrA") {
colorspacename = "YCBCRA8";
}
else if (colorspacename == "YCbCrAU16") {
colorspacename = "YCBCRAU16";
}
}
KisKraLoader::KisKraLoader(KisDocument * document, int syntaxVersion)
: m_d(new Private())
{
m_d->document = document;
m_d->syntaxVersion = syntaxVersion;
}
KisKraLoader::~KisKraLoader()
{
delete m_d;
}
KisImageSP KisKraLoader::loadXML(const KoXmlElement& element)
{
QString attr;
KisImageSP image = 0;
QString name;
qint32 width;
qint32 height;
QString profileProductName;
double xres;
double yres;
QString colorspacename;
const KoColorSpace * cs;
if ((attr = element.attribute(MIME)) == NATIVE_MIMETYPE) {
if ((m_d->imageName = element.attribute(NAME)).isNull()) {
m_d->errorMessages << i18n("Image does not have a name.");
return KisImageSP(0);
}
if ((attr = element.attribute(WIDTH)).isNull()) {
m_d->errorMessages << i18n("Image does not specify a width.");
return KisImageSP(0);
}
width = KisDomUtils::toInt(attr);
if ((attr = element.attribute(HEIGHT)).isNull()) {
m_d->errorMessages << i18n("Image does not specify a height.");
return KisImageSP(0);
}
height = KisDomUtils::toInt(attr);
m_d->imageComment = element.attribute(DESCRIPTION);
xres = 100.0 / 72.0;
if (!(attr = element.attribute(X_RESOLUTION)).isNull()) {
qreal value = KisDomUtils::toDouble(attr);
if (value > 1.0) {
xres = value / 72.0;
}
}
yres = 100.0 / 72.0;
if (!(attr = element.attribute(Y_RESOLUTION)).isNull()) {
qreal value = KisDomUtils::toDouble(attr);
if (value > 1.0) {
yres = value / 72.0;
}
}
if ((colorspacename = element.attribute(COLORSPACE_NAME)).isNull()) {
// An old file: take a reasonable default.
// Krita didn't support anything else in those
// days anyway.
colorspacename = "RGBA";
}
profileProductName = element.attribute(PROFILE);
// A hack for an old colorspacename
convertColorSpaceNames(colorspacename, profileProductName);
QString colorspaceModel = KoColorSpaceRegistry::instance()->colorSpaceColorModelId(colorspacename).id();
QString colorspaceDepth = KoColorSpaceRegistry::instance()->colorSpaceColorDepthId(colorspacename).id();
if (profileProductName.isNull()) {
// no mention of profile so get default profile";
cs = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, "");
} else {
cs = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, profileProductName);
}
if (cs == 0) {
// try once more without the profile
cs = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, "");
if (cs == 0) {
m_d->errorMessages << i18n("Image specifies an unsupported color model: %1.", colorspacename);
return KisImageSP(0);
}
}
KisProofingConfigurationSP proofingConfig = KisImageConfig(true).defaultProofingconfiguration();
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);
}
if (!(attr = element.attribute(PROOFINGADAPTATIONSTATE)).isNull()) {
proofingConfig->adaptationState = KisDomUtils::toDouble(attr);
}
if (m_d->document) {
image = new KisImage(m_d->document->createUndoStore(), width, height, cs, name);
}
else {
image = new KisImage(0, width, height, cs, name);
}
image->setResolution(xres, yres);
loadNodes(element, image, const_cast(image->rootLayer().data()));
KoXmlNode child;
for (child = element.lastChild(); !child.isNull(); child = child.previousSibling()) {
KoXmlElement e = child.toElement();
if(e.tagName() == CANVASPROJECTIONCOLOR) {
if (e.hasAttribute(COLORBYTEDATA)) {
QByteArray colorData = QByteArray::fromBase64(e.attribute(COLORBYTEDATA).toLatin1());
KoColor color((const quint8*)colorData.data(), image->colorSpace());
image->setDefaultProjectionColor(color);
}
}
if(e.tagName() == GLOBALASSISTANTSCOLOR) {
if (e.hasAttribute(SIMPLECOLORDATA)) {
QString colorData = e.attribute(SIMPLECOLORDATA);
m_d->document->setAssistantsGlobalColor(KisDomUtils::qStringToQColor(colorData));
}
}
if(e.tagName()== PROOFINGWARNINGCOLOR) {
QDomDocument dom;
KoXml::asQDomElement(dom, e);
QDomElement eq = dom.firstChildElement();
proofingConfig->warningColor = KoColor::fromXML(eq.firstChildElement(), Integer8BitsColorDepthID.id());
}
if (e.tagName().toLower() == "animation") {
loadAnimationMetadata(e, image);
}
if (e.tagName() == GAMUTMASK_ROOT) {
loadGamutMaskMetadata(e);
}
}
image->setProofingConfiguration(proofingConfig);
for (child = element.lastChild(); !child.isNull(); child = child.previousSibling()) {
KoXmlElement e = child.toElement();
if(e.tagName() == "compositions") {
loadCompositions(e, image);
}
}
}
KoXmlNode child;
for (child = element.lastChild(); !child.isNull(); child = child.previousSibling()) {
KoXmlElement e = child.toElement();
if (e.tagName() == "grid") {
loadGrid(e);
} else if (e.tagName() == "guides") {
loadGuides(e);
} else if (e.tagName() == "assistants") {
loadAssistantsList(e);
} else if (e.tagName() == "audio") {
loadAudio(e, image);
}
}
// reading palettes from XML
for (child = element.lastChild(); !child.isNull(); child = child.previousSibling()) {
QDomElement e = child.toElement();
if (e.tagName() == PALETTES) {
for (QDomElement paletteElement = e.lastChildElement();
!paletteElement.isNull();
paletteElement = paletteElement.previousSiblingElement()) {
QString paletteName = paletteElement.attribute("filename");
m_d->paletteFilenames.append(paletteName);
}
break;
}
}
return image;
}
void KisKraLoader::loadBinaryData(KoStore * store, KisImageSP image, const QString & uri, bool external)
{
// icc profile: if present, this overrides the profile product name loaded in loadXML.
QString location = external ? QString() : uri;
location += m_d->imageName + ICC_PATH;
if (store->hasFile(location)) {
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()) {
res = image->assignImageProfile(profile);
}
if (!res) {
const QString defaultProfileId = KoColorSpaceRegistry::instance()->defaultProfileForColorSpace(image->colorSpace()->id());
profile = KoColorSpaceRegistry::instance()->profileByName(defaultProfileId);
Q_ASSERT(profile && profile->valid());
image->assignImageProfile(profile);
}
}
}
}
//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();
KisProofingConfigurationSP proofingConfig = image->proofingConfiguration();
if (!proofingConfig) {
proofingConfig = KisImageConfig(true).defaultProofingconfiguration();
}
if (proofingProfileRes) {
const KoColorProfile *proofingProfile = KoColorSpaceRegistry::instance()->createColorProfile(proofingConfig->proofingModel, proofingConfig->proofingDepth, proofingData);
if (proofingProfile->valid()){
KoColorSpaceRegistry::instance()->addProfile(proofingProfile);
}
}
}
}
// Load the layers data: if there is a profile associated with a layer it will be set now.
KisKraLoadVisitor visitor(image, store, m_d->document->shapeController(), m_d->layerFilenames, m_d->keyframeFilenames, m_d->imageName, m_d->syntaxVersion);
if (external) {
visitor.setExternalUri(uri);
}
image->rootLayer()->accept(visitor);
if (!visitor.errorMessages().isEmpty()) {
m_d->errorMessages.append(visitor.errorMessages());
}
if (!visitor.warningMessages().isEmpty()) {
m_d->warningMessages.append(visitor.warningMessages());
}
// annotations
// exif
location = external ? QString() : uri;
location += m_d->imageName + EXIF_PATH;
if (store->hasFile(location)) {
QByteArray data;
store->open(location);
data = store->read(store->size());
store->close();
image->addAnnotation(KisAnnotationSP(new KisAnnotation("exif", "", data)));
}
// 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);
/**
* 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);
}
store->close();
if (collection->valid()) {
KoResourceServer *server = KisResourceServerProvider::instance()->layerStyleCollectionServer();
server->addResource(collection, false);
collection->assignAllLayerStyles(image->root());
} else {
warnKrita << "WARNING: Couldn't load layer styles library from .kra!";
delete collection;
}
}
if (m_d->document && m_d->document->documentInfo()->aboutInfo("title").isNull())
m_d->document->documentInfo()->setAboutInfo("title", m_d->imageName);
if (m_d->document && m_d->document->documentInfo()->aboutInfo("comment").isNull())
m_d->document->documentInfo()->setAboutInfo("comment", m_d->imageComment);
loadAssistants(store, uri, external);
loadGamutMask(store);
}
void KisKraLoader::loadPalettes(KoStore *store, KisDocument *doc)
{
QList list;
Q_FOREACH (const QString &filename, m_d->paletteFilenames) {
KoColorSet *newPalette = new KoColorSet(filename);
store->open(m_d->imageName + PALETTE_PATH + filename);
QByteArray data = store->read(store->size());
newPalette->fromByteArray(data);
newPalette->setIsGlobal(false);
newPalette->setIsEditable(true);
store->close();
list.append(newPalette);
}
doc->setPaletteList(list);
}
vKisNodeSP KisKraLoader::selectedNodes() const
{
return m_d->selectedNodes;
}
QList KisKraLoader::assistants() const
{
return m_d->assistants;
}
QStringList KisKraLoader::errorMessages() const
{
return m_d->errorMessages;
}
QStringList KisKraLoader::warningMessages() const
{
return m_d->warningMessages;
}
void KisKraLoader::loadAssistants(KoStore *store, const QString &uri, bool external)
{
QString file_path;
QString location;
QMap handleMap;
KisPaintingAssistant* assistant = 0;
const QColor globalColor = m_d->document->assistantsGlobalColor();
QMap::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();
location = external ? QString() : uri;
location += m_d->imageName + ASSISTANTS_PATH;
file_path = location + loadedAssistant.key();
assistant->loadXml(store, handleMap, file_path);
assistant->setAssistantGlobalColorCache(globalColor);
//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()){
m_d->assistants.append(toQShared(assistant));
}
}
loadedAssistant++;
}
}
void KisKraLoader::loadAnimationMetadata(const KoXmlElement &element, KisImageSP 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)) {
animation->setFullClipRange(range);
}
if (KisDomUtils::loadValue(qElement, "currentTime", ¤tTime)) {
animation->switchCurrentTimeAsync(currentTime);
}
}
KisNodeSP KisKraLoader::loadNodes(const KoXmlElement& element, KisImageSP image, KisNodeSP parent)
{
KoXmlNode node = element.firstChild();
KoXmlNode child;
if (!node.isNull()) {
if (node.isElement()) {
if (node.nodeName().toUpper() == LAYERS.toUpper() || node.nodeName().toUpper() == MASKS.toUpper()) {
for (child = node.lastChild(); !child.isNull(); child = child.previousSibling()) {
KisNodeSP node = loadNode(child.toElement(), image);
if (node) {
image->nextLayerName(); // Make sure the nameserver is current with the number of nodes.
image->addNode(node, parent);
if (node->inherits("KisLayer") && KoXml::childNodesCount(child) > 0) {
loadNodes(child.toElement(), image, node);
}
}
}
}
}
}
return parent;
}
KisNodeSP KisKraLoader::loadNode(const KoXmlElement& element, KisImageSP image)
{
// 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.
QString name = element.attribute(NAME, "No Name");
QUuid id = QUuid(element.attribute(UUID, QUuid().toString()));
qint32 x = element.attribute(X, "0").toInt();
qint32 y = element.attribute(Y, "0").toInt();
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;
const KoColorSpace* colorSpace = 0;
if ((element.attribute(COLORSPACE_NAME)).isNull()) {
dbgFile << "No attribute color space for layer: " << name;
colorSpace = image->colorSpace();
}
else {
QString colorspacename = element.attribute(COLORSPACE_NAME);
QString profileProductName;
convertColorSpaceNames(colorspacename, profileProductName);
QString colorspaceModel = KoColorSpaceRegistry::instance()->colorSpaceColorModelId(colorspacename).id();
QString colorspaceDepth = KoColorSpaceRegistry::instance()->colorSpaceColorDepthId(colorspacename).id();
dbgFile << "Searching color space: " << colorspacename << colorspaceModel << colorspaceDepth << " for layer: " << name;
// use default profile - it will be replaced later in completeLoading
colorSpace = KoColorSpaceRegistry::instance()->colorSpace(colorspaceModel, colorspaceDepth, "");
dbgFile << "found colorspace" << colorSpace;
if (!colorSpace) {
m_d->warningMessages << i18n("Layer %1 specifies an unsupported color model: %2.", name, colorspacename);
return 0;
}
}
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;
int colorLabelIndex = element.attribute(COLOR_LABEL, "0").toInt();
QVector labels = KisNodeViewColorScheme::instance()->allColorLabels();
if (colorLabelIndex >= labels.size()) {
colorLabelIndex = labels.size() - 1;
}
// Now find out the layer type and do specific handling
QString nodeType;
if (m_d->syntaxVersion == 1) {
nodeType = element.attribute("layertype");
if (nodeType.isEmpty()) {
nodeType = PAINT_LAYER;
}
}
else {
nodeType = element.attribute(NODE_TYPE);
}
if (nodeType.isEmpty()) {
m_d->warningMessages << i18n("Layer %1 has an unsupported type.", name);
return 0;
}
KisNodeSP node = 0;
if (nodeType == PAINT_LAYER)
node = loadPaintLayer(element, image, name, colorSpace, opacity);
else if (nodeType == GROUP_LAYER)
node = loadGroupLayer(element, image, name, colorSpace, opacity);
else if (nodeType == ADJUSTMENT_LAYER)
node = loadAdjustmentLayer(element, image, name, colorSpace, opacity);
else if (nodeType == SHAPE_LAYER)
node = loadShapeLayer(element, image, name, colorSpace, opacity);
else if (nodeType == GENERATOR_LAYER)
node = loadGeneratorLayer(element, image, name, colorSpace, opacity);
else if (nodeType == CLONE_LAYER)
node = loadCloneLayer(element, image, name, colorSpace, opacity);
else if (nodeType == FILTER_MASK)
node = loadFilterMask(element);
else if (nodeType == TRANSFORM_MASK)
node = loadTransformMask(element);
else if (nodeType == TRANSPARENCY_MASK)
node = loadTransparencyMask(element);
else if (nodeType == SELECTION_MASK)
node = loadSelectionMask(image, element);
else if (nodeType == COLORIZE_MASK)
node = loadColorizeMask(image, element, colorSpace);
else if (nodeType == FILE_LAYER)
node = loadFileLayer(element, image, name, opacity);
else if (nodeType == REFERENCE_IMAGES_LAYER)
node = loadReferenceImagesLayer(element, image);
else {
m_d->warningMessages << i18n("Layer %1 has an unsupported type: %2.", name, nodeType);
return 0;
}
// Loading the node went wrong. Return empty node and leave to
// upstream to complain to the user
if (!node) {
m_d->warningMessages << i18n("Failure loading layer %1 of type: %2.", name, nodeType);
return 0;
}
node->setVisible(visible, true);
node->setUserLocked(locked);
node->setCollapsed(collapsed);
node->setColorLabelIndex(colorLabelIndex);
node->setX(x);
node->setY(y);
node->setName(name);
if (! id.isNull()) // if no uuid in file, new one has been generated already
node->setUuid(id);
if (node->inherits("KisLayer") || node->inherits("KisColorizeMask")) {
QString compositeOpName = element.attribute(COMPOSITE_OP, "normal");
node->setCompositeOpId(compositeOpName);
}
if (node->inherits("KisLayer")) {
KisLayer* layer = qobject_cast(node.data());
QBitArray channelFlags = stringToFlags(element.attribute(CHANNEL_FLAGS, ""), colorSpace->channelCount());
layer->setChannelFlags(channelFlags);
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 {
warnKrita << "WARNING: Layer style for layer" << layer->name() << "contains invalid UUID" << uuidString;
}
}
}
if (node->inherits("KisGroupLayer")) {
if (element.hasAttribute(PASS_THROUGH_MODE)) {
bool value = element.attribute(PASS_THROUGH_MODE, "0") != "0";
KisGroupLayer *group = qobject_cast(node.data());
group->setPassThroughMode(value);
}
}
const bool timelineEnabled = element.attribute(VISIBLE_IN_TIMELINE, "0") == "0" ? false : true;
node->setUseInTimeline(timelineEnabled);
if (node->inherits("KisPaintLayer")) {
KisPaintLayer* layer = qobject_cast(node.data());
QBitArray channelLockFlags = stringToFlags(element.attribute(CHANNEL_LOCK_FLAGS, ""), colorSpace->channelCount());
layer->setChannelLockFlags(channelLockFlags);
bool onionEnabled = element.attribute(ONION_SKIN_ENABLED, "0") == "0" ? false : true;
layer->setOnionSkinEnabled(onionEnabled);
}
if (element.attribute(FILE_NAME).isNull()) {
m_d->layerFilenames[node.data()] = name;
}
else {
m_d->layerFilenames[node.data()] = element.attribute(FILE_NAME);
}
if (element.hasAttribute("selected") && element.attribute("selected") == "true") {
m_d->selectedNodes.append(node);
}
if (element.hasAttribute(KEYFRAME_FILE)) {
m_d->keyframeFilenames.insert(node.data(), element.attribute(KEYFRAME_FILE));
}
return node;
}
KisNodeSP KisKraLoader::loadPaintLayer(const KoXmlElement& element, KisImageSP image,
const QString& name, const KoColorSpace* cs, quint32 opacity)
{
Q_UNUSED(element);
KisPaintLayer* layer;
layer = new KisPaintLayer(image, name, opacity, cs);
Q_CHECK_PTR(layer);
return layer;
}
KisNodeSP KisKraLoader::loadFileLayer(const KoXmlElement& element, KisImageSP image, const QString& name, quint32 opacity)
{
QString filename = element.attribute("source", QString());
if (filename.isNull()) return 0;
bool scale = (element.attribute("scale", "true") == "true");
int scalingMethod = element.attribute("scalingmethod", "-1").toInt();
if (scalingMethod < 0) {
if (scale) {
scalingMethod = KisFileLayer::ToImagePPI;
}
else {
scalingMethod = KisFileLayer::None;
}
}
QString documentPath;
if (m_d->document) {
documentPath = m_d->document->url().toLocalFile();
}
QFileInfo info(documentPath);
QString basePath = info.absolutePath();
QString fullPath = QDir(basePath).filePath(QDir::cleanPath(filename));
if (!QFileInfo(fullPath).exists()) {
qApp->setOverrideCursor(Qt::ArrowCursor);
QString msg = i18nc(
"@info",
"The file associated to a file layer with the name \"%1\" is not found.\n\n"
"Expected path:\n"
"%2\n\n"
"Do you want to locate it manually?", name, fullPath);
int result = QMessageBox::warning(0, i18nc("@title:window", "File not found"), msg, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if (result == QMessageBox::Yes) {
KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument");
dialog.setMimeTypeFilters(KisImportExportManager::supportedMimeTypes(KisImportExportManager::Import));
dialog.setDefaultDir(basePath);
QString url = dialog.filename();
if (!QFileInfo(basePath).exists()) {
filename = url;
} else {
QDir d(basePath);
filename = d.relativeFilePath(url);
}
}
qApp->restoreOverrideCursor();
}
KisLayer *layer = new KisFileLayer(image, basePath, filename, (KisFileLayer::ScalingMethod)scalingMethod, name, opacity);
Q_CHECK_PTR(layer);
return layer;
}
KisNodeSP KisKraLoader::loadGroupLayer(const KoXmlElement& element, KisImageSP image,
const QString& name, const KoColorSpace* cs, quint32 opacity)
{
Q_UNUSED(element);
Q_UNUSED(cs);
QString attr;
KisGroupLayer* layer;
layer = new KisGroupLayer(image, name, opacity);
Q_CHECK_PTR(layer);
return layer;
}
KisNodeSP KisKraLoader::loadAdjustmentLayer(const KoXmlElement& element, KisImageSP image,
const QString& name, const KoColorSpace* cs, quint32 opacity)
{
// XXX: do something with filterversion?
Q_UNUSED(cs);
QString attr;
KisAdjustmentLayer* layer;
QString filtername;
QString legacy = filtername;
if ((filtername = element.attribute(FILTER_NAME)).isNull()) {
// XXX: Invalid adjustmentlayer! We should warn about it!
warnFile << "No filter in adjustment layer";
return 0;
}
//get deprecated filters.
if (filtername=="brightnesscontrast") {
legacy = filtername;
filtername = "perchannel";
}
if (filtername=="left edge detections"
|| filtername=="right edge detections"
|| filtername=="top edge detections"
|| filtername=="bottom edge detections") {
legacy = filtername;
filtername = "edge detection";
}
KisFilterSP f = KisFilterRegistry::instance()->value(filtername);
if (!f) {
warnFile << "No filter for filtername" << filtername << "";
return 0; // XXX: We don't have this filter. We should warn about it!
}
KisFilterConfigurationSP kfc = f->defaultConfiguration();
kfc->setProperty("legacy", legacy);
if (legacy=="brightnesscontrast") {
kfc->setProperty("colorModel", cs->colorModelId().id());
}
// We'll load the configuration and the selection later.
layer = new KisAdjustmentLayer(image, name, kfc, 0);
Q_CHECK_PTR(layer);
layer->setOpacity(opacity);
return layer;
}
KisNodeSP KisKraLoader::loadShapeLayer(const KoXmlElement& element, KisImageSP image,
const QString& name, const KoColorSpace* cs, quint32 opacity)
{
Q_UNUSED(element);
Q_UNUSED(cs);
QString attr;
KoShapeControllerBase * shapeController = 0;
if (m_d->document) {
shapeController = m_d->document->shapeController();
}
KisShapeLayer* layer = new KisShapeLayer(shapeController, image, name, opacity);
Q_CHECK_PTR(layer);
return layer;
}
KisNodeSP KisKraLoader::loadGeneratorLayer(const KoXmlElement& element, KisImageSP image,
const QString& name, const KoColorSpace* cs, quint32 opacity)
{
Q_UNUSED(cs);
// XXX: do something with generator version?
KisGeneratorLayer* layer;
QString generatorname = element.attribute(GENERATOR_NAME);
if (generatorname.isNull()) {
// XXX: Invalid generator layer! We should warn about it!
warnFile << "No generator in generator layer";
return 0;
}
KisGeneratorSP generator = KisGeneratorRegistry::instance()->value(generatorname);
if (!generator) {
warnFile << "No generator for generatorname" << generatorname << "";
return 0; // XXX: We don't have this generator. We should warn about it!
}
KisFilterConfigurationSP kgc = generator->defaultConfiguration();
// We'll load the configuration and the selection later.
layer = new KisGeneratorLayer(image, name, kgc, 0);
Q_CHECK_PTR(layer);
layer->setOpacity(opacity);
return layer;
}
KisNodeSP KisKraLoader::loadCloneLayer(const KoXmlElement& element, KisImageSP image,
const QString& name, const KoColorSpace* cs, quint32 opacity)
{
Q_UNUSED(cs);
KisCloneLayerSP layer = new KisCloneLayer(0, image, name, opacity);
KisNodeUuidInfo info;
if (! (element.attribute(CLONE_FROM_UUID)).isNull()) {
info = KisNodeUuidInfo(QUuid(element.attribute(CLONE_FROM_UUID)));
} else {
if ((element.attribute(CLONE_FROM)).isNull()) {
return 0;
} else {
info = KisNodeUuidInfo(element.attribute(CLONE_FROM));
}
}
layer->setCopyFromInfo(info);
if ((element.attribute(CLONE_TYPE)).isNull()) {
return 0;
} else {
layer->setCopyType((CopyLayerType) element.attribute(CLONE_TYPE).toInt());
}
return layer;
}
KisNodeSP KisKraLoader::loadFilterMask(const KoXmlElement& element)
{
QString attr;
KisFilterMask* mask;
QString filtername;
// XXX: should we check the version?
if ((filtername = element.attribute(FILTER_NAME)).isNull()) {
// XXX: Invalid filter layer! We should warn about it!
warnFile << "No filter in filter layer";
return 0;
}
KisFilterSP f = KisFilterRegistry::instance()->value(filtername);
if (!f) {
warnFile << "No filter for filtername" << filtername << "";
return 0; // XXX: We don't have this filter. We should warn about it!
}
KisFilterConfigurationSP kfc = f->defaultConfiguration();
// We'll load the configuration and the selection later.
mask = new KisFilterMask();
mask->setFilter(kfc);
Q_CHECK_PTR(mask);
return mask;
}
KisNodeSP KisKraLoader::loadTransformMask(const KoXmlElement& element)
{
Q_UNUSED(element);
KisTransformMask* mask;
/**
* We'll load the transform configuration later on a stage
* of binary data loading
*/
mask = new KisTransformMask();
Q_CHECK_PTR(mask);
return mask;
}
KisNodeSP KisKraLoader::loadTransparencyMask(const KoXmlElement& element)
{
Q_UNUSED(element);
KisTransparencyMask* mask = new KisTransparencyMask();
Q_CHECK_PTR(mask);
return mask;
}
KisNodeSP KisKraLoader::loadSelectionMask(KisImageSP image, const KoXmlElement& element)
{
KisSelectionMaskSP mask = new KisSelectionMask(image);
bool active = element.attribute(ACTIVE, "1") == "0" ? false : true;
mask->setActive(active);
Q_CHECK_PTR(mask);
return mask;
}
KisNodeSP KisKraLoader::loadColorizeMask(KisImageSP image, const KoXmlElement& element, const KoColorSpace *colorSpace)
{
KisColorizeMaskSP mask = new KisColorizeMask();
const bool editKeystrokes = element.attribute(COLORIZE_EDIT_KEYSTROKES, "1") == "0" ? false : true;
const bool showColoring = element.attribute(COLORIZE_SHOW_COLORING, "1") == "0" ? false : true;
KisLayerPropertiesIcons::setNodeProperty(mask, KisLayerPropertiesIcons::colorizeEditKeyStrokes, editKeystrokes, image);
KisLayerPropertiesIcons::setNodeProperty(mask, KisLayerPropertiesIcons::colorizeShowColoring, showColoring, image);
const bool useEdgeDetection = KisDomUtils::toInt(element.attribute(COLORIZE_USE_EDGE_DETECTION, "0"));
const qreal edgeDetectionSize = KisDomUtils::toDouble(element.attribute(COLORIZE_EDGE_DETECTION_SIZE, "4"));
const qreal radius = KisDomUtils::toDouble(element.attribute(COLORIZE_FUZZY_RADIUS, "0"));
const int cleanUp = KisDomUtils::toInt(element.attribute(COLORIZE_CLEANUP, "0"));
const bool limitToDevice = KisDomUtils::toInt(element.attribute(COLORIZE_LIMIT_TO_DEVICE, "0"));
mask->setUseEdgeDetection(useEdgeDetection);
mask->setEdgeDetectionSize(edgeDetectionSize);
mask->setFuzzyRadius(radius);
mask->setCleanUpAmount(qreal(cleanUp) / 100.0);
mask->setLimitToDeviceBounds(limitToDevice);
delete mask->setColorSpace(colorSpace);
mask->setImage(image);
return mask;
}
void KisKraLoader::loadCompositions(const KoXmlElement& elem, KisImageSP image)
{
KoXmlNode child;
for (child = elem.firstChild(); !child.isNull(); child = child.nextSibling()) {
KoXmlElement e = child.toElement();
QString name = e.attribute("name");
bool exportEnabled = e.attribute("exportEnabled", "1") == "0" ? false : true;
KisLayerCompositionSP composition(new KisLayerComposition(image, name));
composition->setExportEnabled(exportEnabled);
KoXmlNode value;
for (value = child.lastChild(); !value.isNull(); value = value.previousSibling()) {
KoXmlElement e = value.toElement();
QUuid uuid(e.attribute("uuid"));
bool visible = e.attribute("visible", "1") == "0" ? false : true;
composition->setVisible(uuid, visible);
bool collapsed = e.attribute("collapsed", "1") == "0" ? false : true;
composition->setCollapsed(uuid, collapsed);
}
image->addComposition(composition);
}
}
void KisKraLoader::loadAssistantsList(const KoXmlElement &elem)
{
KoXmlNode child;
int count = 0;
for (child = elem.firstChild(); !child.isNull(); child = child.nextSibling()) {
KoXmlElement e = child.toElement();
QString type = e.attribute("type");
QString file_name = e.attribute("filename");
m_d->assistantsFilenames.insert(file_name,type);
count++;
}
}
void KisKraLoader::loadGrid(const KoXmlElement& elem)
{
QDomDocument dom;
KoXml::asQDomElement(dom, elem);
QDomElement domElement = dom.firstChildElement();
KisGridConfig config;
config.loadDynamicDataFromXml(domElement);
config.loadStaticData();
m_d->document->setGridConfig(config);
}
void KisKraLoader::loadGuides(const KoXmlElement& elem)
{
QDomDocument dom;
KoXml::asQDomElement(dom, elem);
QDomElement domElement = dom.firstChildElement();
KisGuidesConfig guides;
guides.loadFromXml(domElement);
m_d->document->setGuidesConfig(guides);
}
void KisKraLoader::loadAudio(const KoXmlElement& elem, KisImageSP image)
{
QDomDocument dom;
KoXml::asQDomElement(dom, elem);
QDomElement qElement = dom.firstChildElement();
QString fileName;
if (KisDomUtils::loadValue(qElement, "masterChannelPath", &fileName)) {
fileName = QDir::toNativeSeparators(fileName);
QDir baseDirectory = QFileInfo(m_d->document->localFilePath()).absoluteDir();
fileName = baseDirectory.absoluteFilePath(fileName);
QFileInfo info(fileName);
if (!info.exists()) {
qApp->setOverrideCursor(Qt::ArrowCursor);
QString msg = i18nc(
"@info",
"Audio channel file \"%1\" doesn't exist!\n\n"
"Expected path:\n"
"%2\n\n"
"Do you want to locate it manually?", info.fileName(), info.absoluteFilePath());
int result = QMessageBox::warning(0, i18nc("@title:window", "File not found"), msg, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if (result == QMessageBox::Yes) {
info.setFile(KisImportExportManager::askForAudioFileName(info.absolutePath(), 0));
}
qApp->restoreOverrideCursor();
}
if (info.exists()) {
image->animationInterface()->setAudioChannelFileName(info.absoluteFilePath());
}
}
bool audioMuted = false;
if (KisDomUtils::loadValue(qElement, "audioMuted", &audioMuted)) {
image->animationInterface()->setAudioMuted(audioMuted);
}
qreal audioVolume = 0.5;
if (KisDomUtils::loadValue(qElement, "audioVolume", &audioVolume)) {
image->animationInterface()->setAudioVolume(audioVolume);
}
}
void KisKraLoader::loadGamutMaskMetadata(const KoXmlElement &elem)
{
KoXmlNode child;
for (child = elem.firstChild(); !child.isNull(); child = child.nextSibling()) {
KoXmlElement e = child.toElement();
if (e.tagName() == GAMUTMASK_RESOURCE) {
m_d->gamutMaskFilename = e.attribute("filename");
m_d->gamutMaskRotation = e.attribute("rotation").toInt();
}
// todo: selector settings
}
}
KisNodeSP KisKraLoader::loadReferenceImagesLayer(const KoXmlElement &elem, KisImageSP image)
{
KisSharedPtr layer =
new KisReferenceImagesLayer(m_d->document->shapeController(), image);
m_d->document->setReferenceImagesLayer(layer, false);
for (QDomElement child = elem.firstChildElement(); !child.isNull(); child = child.nextSiblingElement()) {
if (child.nodeName().toLower() == "referenceimage") {
auto* reference = KisReferenceImage::fromXml(child);
layer->addShape(reference);
}
}
return layer;
}
void KisKraLoader::loadGamutMask(KoStore* store)
{
if (m_d->gamutMaskFilename.isEmpty()) {
return;
}
QString gamutMaskPath = QString("%1%2%3")
.arg(m_d->imageName)
.arg(GAMUTMASK_PATH)
.arg(m_d->gamutMaskFilename);
KoGamutMask* mask = new KoGamutMask(m_d->gamutMaskFilename);
if (store->open(gamutMaskPath)) {
mask->loadFromByteArray(store->read(store->size()));
store->close();
mask->setRotation(m_d->gamutMaskRotation);
- mask->setTitle(QString("%1 %2").arg(mask->title()).arg("[document]"));
+ mask->setName(m_d->imageName);
+ mask->setFilename(QString("%1.kgm").arg(m_d->imageName));
+ mask->setStoredInDocument(true);
m_d->document->setGamutMask(mask);
}
}
diff --git a/plugins/impex/libkra/kis_kra_saver.cpp b/plugins/impex/libkra/kis_kra_saver.cpp
index efd4dcaceb..38ce5f5d94 100644
--- a/plugins/impex/libkra/kis_kra_saver.cpp
+++ b/plugins/impex/libkra/kis_kra_saver.cpp
@@ -1,564 +1,564 @@
/* This file is part of the KDE project
* Copyright 2008 (C) Boudewijn Rempt
*
* 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_kra_saver.h"
#include "kis_kra_tags.h"
#include "kis_kra_save_visitor.h"
#include "kis_kra_savexml_visitor.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "kis_png_converter.h"
#include "kis_keyframe_channel.h"
#include
#include "KisDocument.h"
#include
#include "kis_dom_utils.h"
#include "kis_grid_config.h"
#include "kis_guides_config.h"
#include "KisProofingConfiguration.h"
#include
#include
using namespace KRA;
struct KisKraSaver::Private
{
public:
KisDocument* doc;
QMap nodeFileNames;
QMap keyframeFilenames;
QString imageName;
QString filename;
QStringList errorMessages;
};
KisKraSaver::KisKraSaver(KisDocument* document, const QString &filename)
: m_d(new Private)
{
m_d->doc = document;
m_d->filename = filename;
m_d->imageName = m_d->doc->documentInfo()->aboutInfo("title");
if (m_d->imageName.isEmpty()) {
m_d->imageName = i18n("Unnamed");
}
}
KisKraSaver::~KisKraSaver()
{
delete m_d;
}
QDomElement KisKraSaver::saveXML(QDomDocument& doc, KisImageSP image)
{
QDomElement imageElement = doc.createElement("IMAGE");
Q_ASSERT(image);
imageElement.setAttribute(NAME, m_d->imageName);
imageElement.setAttribute(MIME, NATIVE_MIMETYPE);
imageElement.setAttribute(WIDTH, KisDomUtils::toString(image->width()));
imageElement.setAttribute(HEIGHT, KisDomUtils::toString(image->height()));
imageElement.setAttribute(COLORSPACE_NAME, image->colorSpace()->id());
imageElement.setAttribute(DESCRIPTION, m_d->doc->documentInfo()->aboutInfo("comment"));
// XXX: Save profile as blob inside the image, instead of the product name.
if (image->profile() && image->profile()-> valid()) {
imageElement.setAttribute(PROFILE, image->profile()->name());
}
imageElement.setAttribute(X_RESOLUTION, KisDomUtils::toString(image->xRes()*72.0));
imageElement.setAttribute(Y_RESOLUTION, KisDomUtils::toString(image->yRes()*72.0));
//now the proofing options:
if (image->proofingConfiguration()) {
imageElement.setAttribute(PROOFINGPROFILENAME, KisDomUtils::toString(image->proofingConfiguration()->proofingProfile));
imageElement.setAttribute(PROOFINGMODEL, KisDomUtils::toString(image->proofingConfiguration()->proofingModel));
imageElement.setAttribute(PROOFINGDEPTH, KisDomUtils::toString(image->proofingConfiguration()->proofingDepth));
imageElement.setAttribute(PROOFINGINTENT, KisDomUtils::toString(image->proofingConfiguration()->intent));
imageElement.setAttribute(PROOFINGADAPTATIONSTATE, KisDomUtils::toString(image->proofingConfiguration()->adaptationState));
}
quint32 count = 1; // We don't save the root layer, but it does count
KisSaveXmlVisitor visitor(doc, imageElement, count, m_d->doc->url().toLocalFile(), true);
visitor.setSelectedNodes({m_d->doc->preActivatedNode()});
image->rootLayer()->accept(visitor);
m_d->errorMessages.append(visitor.errorMessages());
m_d->nodeFileNames = visitor.nodeFileNames();
m_d->keyframeFilenames = visitor.keyframeFileNames();
saveBackgroundColor(doc, imageElement, image);
saveAssistantsGlobalColor(doc, imageElement);
saveWarningColor(doc, imageElement, image);
saveCompositions(doc, imageElement, image);
saveAssistantsList(doc, imageElement);
saveGrid(doc, imageElement);
saveGuides(doc, imageElement);
saveAudio(doc, imageElement);
savePalettesToXML(doc, imageElement);
saveGamutMaskMetadata(doc, imageElement);
QDomElement animationElement = doc.createElement("animation");
KisDomUtils::saveValue(&animationElement, "framerate", image->animationInterface()->framerate());
KisDomUtils::saveValue(&animationElement, "range", image->animationInterface()->fullClipRange());
KisDomUtils::saveValue(&animationElement, "currentTime", image->animationInterface()->currentUITime());
imageElement.appendChild(animationElement);
return imageElement;
}
bool KisKraSaver::savePalettes(KoStore *store, KisImageSP image, const QString &uri)
{
Q_UNUSED(image);
Q_UNUSED(uri);
bool res = false;
if (m_d->doc->paletteList().size() == 0) {
return true;
}
for (const KoColorSet *palette : m_d->doc->paletteList()) {
if (!palette->isGlobal()) {
if (!store->open(m_d->imageName + PALETTE_PATH + palette->filename())) {
m_d->errorMessages << i18n("could not save palettes");
return false;
}
QByteArray ba = palette->toByteArray();
if (!ba.isEmpty()) {
store->write(ba);
} else {
qWarning() << "Cannot save the palette to a byte array:" << palette->name();
}
store->close();
res = true;
}
}
return res;
}
bool KisKraSaver::saveGamutMask(KoStore* store)
{
// no mask in document -> automatic success
if (!m_d->doc->gamutMask()) {
return true;
}
QString gamutMaskPath = QString("%1%2%3")
.arg(m_d->imageName)
.arg(GAMUTMASK_PATH)
.arg(m_d->doc->gamutMask()->shortFilename());
if (!store->open(gamutMaskPath)) {
m_d->errorMessages << i18n("could not save gamut mask");
return false;
}
bool res = false;
QByteArray resourceData = m_d->doc->gamutMask()->toByteArray();
if (!resourceData.isEmpty()) {
// res = (store->write(resourceData) == -1) ? false : true;
store->write(resourceData);
res = true;
} else {
- qWarning() << "Cannot convert gamut mask to byte array:" << m_d->doc->gamutMask()->title();
+ qWarning() << "Cannot convert gamut mask to byte array:" << m_d->doc->gamutMask()->name();
res = false;
}
store->close();
return res;
}
void KisKraSaver::savePalettesToXML(QDomDocument &doc, QDomElement &element)
{
QDomElement ePalette = doc.createElement(PALETTES);
for (const KoColorSet *palette : m_d->doc->paletteList()) {
if (!palette->isGlobal()) {
QDomElement eFile = doc.createElement("palette");
eFile.setAttribute("filename", palette->filename());
ePalette.appendChild(eFile);
}
}
element.appendChild(ePalette);
}
void KisKraSaver::saveGamutMaskMetadata(QDomDocument& doc, QDomElement& element)
{
if (!m_d->doc->gamutMask()) {
return;
}
QDomElement gamutMaskRoot = doc.createElement(GAMUTMASK_ROOT);
QDomElement gamutMaskResource = doc.createElement(GAMUTMASK_RESOURCE);
gamutMaskResource.setAttribute("filename", m_d->doc->gamutMask()->shortFilename());
gamutMaskResource.setAttribute("rotation", m_d->doc->gamutMask()->rotation());
gamutMaskRoot.appendChild(gamutMaskResource);
// TODO: save color selector properties
element.appendChild(gamutMaskRoot);
}
bool KisKraSaver::saveKeyframes(KoStore *store, const QString &uri, bool external)
{
QMap::iterator it;
for (it = m_d->keyframeFilenames.begin(); it != m_d->keyframeFilenames.end(); it++) {
const KisNode *node = it.key();
QString filename = it.value();
QString location =
(external ? QString() : uri)
+ m_d->imageName + LAYER_PATH + filename;
if (!saveNodeKeyframes(store, location, node)) {
return false;
}
}
return true;
}
bool KisKraSaver::saveNodeKeyframes(KoStore *store, QString location, const KisNode *node)
{
QDomDocument doc = KisDocument::createDomDocument("krita-keyframes", "keyframes", "1.0");
QDomElement root = doc.documentElement();
KisKeyframeChannel *channel;
Q_FOREACH (channel, node->keyframeChannels()) {
QDomElement element = channel->toXML(doc, m_d->nodeFileNames[node]);
root.appendChild(element);
}
if (store->open(location)) {
QByteArray xml = doc.toByteArray();
store->write(xml);
store->close();
} else {
m_d->errorMessages << i18n("could not save keyframes");
return false;
}
return true;
}
bool KisKraSaver::saveBinaryData(KoStore* store, KisImageSP image, const QString &uri, bool external, bool autosave)
{
QString location;
// Save the layers data
KisKraSaveVisitor visitor(store, m_d->imageName, m_d->nodeFileNames);
if (external)
visitor.setExternalUri(uri);
image->rootLayer()->accept(visitor);
m_d->errorMessages.append(visitor.errorMessages());
if (!m_d->errorMessages.isEmpty()) {
return false;
}
// saving annotations
// XXX this only saves EXIF and ICC info. This would probably need
// a redesign of the dtd of the krita file to do this more generally correct
// e.g. have tags or so.
KisAnnotationSP annotation = image->annotation("exif");
if (annotation) {
location = external ? QString() : uri;
location += m_d->imageName + EXIF_PATH;
if (store->open(location)) {
store->write(annotation->annotation());
store->close();
}
}
if (image->profile()) {
const KoColorProfile *profile = image->profile();
KisAnnotationSP annotation;
if (profile) {
QByteArray profileRawData = profile->rawData();
if (!profileRawData.isEmpty()) {
if (profile->type() == "icc") {
annotation = new KisAnnotation(ICC, profile->name(), profile->rawData());
} else {
annotation = new KisAnnotation(PROFILE, profile->name(), profile->rawData());
}
}
}
if (annotation) {
location = external ? QString() : uri;
location += m_d->imageName + ICC_PATH;
if (store->open(location)) {
store->write(annotation->annotation());
store->close();
}
}
}
//This'll embed the profile used for proofing into the kra file.
if (image->proofingConfiguration()) {
const KoColorProfile *proofingProfile = KoColorSpaceRegistry::instance()->profileByName(image->proofingConfiguration()->proofingProfile);
if (proofingProfile && proofingProfile->valid()) {
QByteArray proofingProfileRaw = proofingProfile->rawData();
if (!proofingProfileRaw.isEmpty()) {
annotation = new KisAnnotation(ICCPROOFINGPROFILE, proofingProfile->name(), proofingProfile->rawData());
}
}
if (annotation) {
location = external ? QString() : uri;
location += m_d->imageName + ICC_PROOFING_PATH;
if (store->open(location)) {
store->write(annotation->annotation());
store->close();
}
}
}
{
KisPSDLayerStyleCollectionResource collection("not-nexists.asl");
KIS_ASSERT_RECOVER_NOOP(!collection.valid());
collection.collectAllLayerStyles(image->root());
if (collection.valid()) {
location = external ? QString() : uri;
location += m_d->imageName + LAYER_STYLES_PATH;
if (store->open(location)) {
QBuffer aslBuffer;
aslBuffer.open(QIODevice::WriteOnly);
collection.saveToDevice(&aslBuffer);
aslBuffer.close();
store->write(aslBuffer.buffer());
store->close();
}
}
}
if (!autosave) {
KisPaintDeviceSP dev = image->projection();
KisPNGConverter::saveDeviceToStore("mergedimage.png", image->bounds(), image->xRes(), image->yRes(), dev, store);
}
saveAssistants(store, uri,external);
return true;
}
QStringList KisKraSaver::errorMessages() const
{
return m_d->errorMessages;
}
void KisKraSaver::saveBackgroundColor(QDomDocument& doc, QDomElement& element, KisImageSP image)
{
QDomElement e = doc.createElement(CANVASPROJECTIONCOLOR);
KoColor color = image->defaultProjectionColor();
QByteArray colorData = QByteArray::fromRawData((const char*)color.data(), color.colorSpace()->pixelSize());
e.setAttribute(COLORBYTEDATA, QString(colorData.toBase64()));
element.appendChild(e);
}
void KisKraSaver::saveAssistantsGlobalColor(QDomDocument& doc, QDomElement& element)
{
QDomElement e = doc.createElement(GLOBALASSISTANTSCOLOR);
QString colorString = KisDomUtils::qColorToQString(m_d->doc->assistantsGlobalColor());
e.setAttribute(SIMPLECOLORDATA, QString(colorString));
element.appendChild(e);
}
void KisKraSaver::saveWarningColor(QDomDocument& doc, QDomElement& element, KisImageSP image)
{
if (image->proofingConfiguration()) {
QDomElement e = doc.createElement(PROOFINGWARNINGCOLOR);
KoColor color = image->proofingConfiguration()->warningColor;
color.toXML(doc, e);
element.appendChild(e);
}
}
void KisKraSaver::saveCompositions(QDomDocument& doc, QDomElement& element, KisImageSP image)
{
if (!image->compositions().isEmpty()) {
QDomElement e = doc.createElement("compositions");
Q_FOREACH (KisLayerCompositionSP composition, image->compositions()) {
composition->save(doc, e);
}
element.appendChild(e);
}
}
bool KisKraSaver::saveAssistants(KoStore* store, QString uri, bool external)
{
QString location;
QMap assistantcounters;
QByteArray data;
QList assistants = m_d->doc->assistants();
QMap handlemap;
if (!assistants.isEmpty()) {
Q_FOREACH (KisPaintingAssistantSP assist, assistants){
if (!assistantcounters.contains(assist->id())){
assistantcounters.insert(assist->id(),0);
}
location = external ? QString() : uri;
location += m_d->imageName + ASSISTANTS_PATH;
location += QString(assist->id()+"%1.assistant").arg(assistantcounters[assist->id()]);
data = assist->saveXml(handlemap);
store->open(location);
store->write(data);
store->close();
assistantcounters[assist->id()]++;
}
}
return true;
}
bool KisKraSaver::saveAssistantsList(QDomDocument& doc, QDomElement& element)
{
int count_ellipse = 0, count_perspective = 0, count_ruler = 0, count_vanishingpoint = 0,count_infiniteruler = 0, count_parallelruler = 0, count_concentricellipse = 0, count_fisheyepoint = 0, count_spline = 0;
QList assistants = m_d->doc->assistants();
if (!assistants.isEmpty()) {
QDomElement assistantsElement = doc.createElement("assistants");
Q_FOREACH (KisPaintingAssistantSP assist, assistants){
if (assist->id() == "ellipse"){
assist->saveXmlList(doc, assistantsElement, count_ellipse);
count_ellipse++;
}
else if (assist->id() == "spline"){
assist->saveXmlList(doc, assistantsElement, count_spline);
count_spline++;
}
else if (assist->id() == "perspective"){
assist->saveXmlList(doc, assistantsElement, count_perspective);
count_perspective++;
}
else if (assist->id() == "vanishing point"){
assist->saveXmlList(doc, assistantsElement, count_vanishingpoint);
count_vanishingpoint++;
}
else if (assist->id() == "infinite ruler"){
assist->saveXmlList(doc, assistantsElement, count_infiniteruler);
count_infiniteruler++;
}
else if (assist->id() == "parallel ruler"){
assist->saveXmlList(doc, assistantsElement, count_parallelruler);
count_parallelruler++;
}
else if (assist->id() == "concentric ellipse"){
assist->saveXmlList(doc, assistantsElement, count_concentricellipse);
count_concentricellipse++;
}
else if (assist->id() == "fisheye-point"){
assist->saveXmlList(doc, assistantsElement, count_fisheyepoint);
count_fisheyepoint++;
}
else if (assist->id() == "ruler"){
assist->saveXmlList(doc, assistantsElement, count_ruler);
count_ruler++;
}
}
element.appendChild(assistantsElement);
}
return true;
}
bool KisKraSaver::saveGrid(QDomDocument& doc, QDomElement& element)
{
KisGridConfig config = m_d->doc->gridConfig();
if (!config.isDefault()) {
QDomElement gridElement = config.saveDynamicDataToXml(doc, "grid");
element.appendChild(gridElement);
}
return true;
}
bool KisKraSaver::saveGuides(QDomDocument& doc, QDomElement& element)
{
KisGuidesConfig guides = m_d->doc->guidesConfig();
if (!guides.isDefault()) {
QDomElement guidesElement = guides.saveToXml(doc, "guides");
element.appendChild(guidesElement);
}
return true;
}
bool KisKraSaver::saveAudio(QDomDocument& doc, QDomElement& element)
{
const KisImageAnimationInterface *interface = m_d->doc->image()->animationInterface();
QString fileName = interface->audioChannelFileName();
if (fileName.isEmpty()) return true;
if (!QFileInfo::exists(fileName)) {
m_d->errorMessages << i18n("Audio channel file %1 doesn't exist!", fileName);
return false;
}
const QDir documentDir = QFileInfo(m_d->filename).absoluteDir();
KIS_ASSERT_RECOVER_RETURN_VALUE(documentDir.exists(), false);
fileName = documentDir.relativeFilePath(fileName);
fileName = QDir::fromNativeSeparators(fileName);
KIS_ASSERT_RECOVER_RETURN_VALUE(!fileName.isEmpty(), false);
QDomElement audioElement = doc.createElement("audio");
KisDomUtils::saveValue(&audioElement, "masterChannelPath", fileName);
KisDomUtils::saveValue(&audioElement, "audioMuted", interface->isAudioMuted());
KisDomUtils::saveValue(&audioElement, "audioVolume", interface->audioVolume());
element.appendChild(audioElement);
return true;
}