File indexing completed on 2024-05-19 04:28:57

0001 /*
0002  *  SPDX-FileCopyrightText: 2013 Boudewijn Rempt <boud@valdyas.org>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 #include "kis_file_layer.h"
0007 
0008 #include <QFile>
0009 #include <QFileInfo>
0010 
0011 #include "kis_transform_worker.h"
0012 #include "kis_filter_strategy.h"
0013 #include "kis_node_progress_proxy.h"
0014 #include "kis_node_visitor.h"
0015 #include "kis_image.h"
0016 #include "kis_types.h"
0017 #include "commands_new/kis_node_move_command2.h"
0018 #include "kis_default_bounds.h"
0019 #include "kis_layer_properties_icons.h"
0020 #include <KisPart.h>
0021 #include <KisDocument.h>
0022 #include <QDir>
0023 
0024 
0025 KisFileLayer::KisFileLayer(KisImageWSP image, const QString &name, quint8 opacity)
0026     : KisExternalLayer(image, name, opacity)
0027 {
0028     /**
0029      * Set default paint device for a layer. It will be used in case
0030      * the file does not exist anymore. Or course, this can happen only
0031      * in the failing execution path.
0032      */
0033     m_paintDevice = new KisPaintDevice(image->colorSpace());
0034     m_paintDevice->setDefaultBounds(new KisDefaultBounds(image));
0035 
0036     connect(&m_loader, SIGNAL(loadingFinished(KisPaintDeviceSP,qreal,qreal,QSize)), SLOT(slotLoadingFinished(KisPaintDeviceSP,qreal,qreal,QSize)));
0037     connect(&m_loader, SIGNAL(loadingFailed()), SLOT(slotLoadingFailed()));
0038     connect(&m_loader, SIGNAL(fileExistsStateChanged(bool)), SLOT(slotFileExistsStateChanged(bool)));
0039     connect(this, SIGNAL(sigRequestOpenFile()), SLOT(openFile()));
0040 }
0041 
0042 KisFileLayer::KisFileLayer(KisImageWSP image, const QString &basePath, const QString &filename, ScalingMethod scaleToImageResolution, QString scalingFilter, const QString &name, quint8 opacity, const KoColorSpace *fallbackColorSpace)
0043     : KisExternalLayer(image, name, opacity)
0044     , m_basePath(basePath)
0045     , m_filename(filename)
0046     , m_scalingMethod(scaleToImageResolution)
0047     , m_scalingFilter(scalingFilter)
0048 {
0049     /**
0050      * Set default paint device for a layer. It will be used in case
0051      * the file does not exist anymore. Or course, this can happen only
0052      * in the failing execution path.
0053      */
0054     m_paintDevice = new KisPaintDevice(fallbackColorSpace ? fallbackColorSpace : image->colorSpace());
0055     m_paintDevice->setDefaultBounds(new KisDefaultBounds(image));
0056 
0057     connect(&m_loader, SIGNAL(loadingFinished(KisPaintDeviceSP,qreal,qreal,QSize)), SLOT(slotLoadingFinished(KisPaintDeviceSP,qreal,qreal,QSize)));
0058     connect(&m_loader, SIGNAL(loadingFailed()), SLOT(slotLoadingFailed()));
0059     connect(&m_loader, SIGNAL(fileExistsStateChanged(bool)), SLOT(slotFileExistsStateChanged(bool)));
0060     connect(this, SIGNAL(sigRequestOpenFile()), SLOT(openFile()));
0061 
0062     QFileInfo fi(path());
0063     if (fi.exists()) {
0064         m_loader.setPath(path());
0065         m_loader.reloadImage();
0066     }
0067 }
0068 
0069 KisFileLayer::~KisFileLayer()
0070 {
0071 }
0072 
0073 KisFileLayer::KisFileLayer(const KisFileLayer &rhs)
0074     : KisExternalLayer(rhs)
0075 {
0076     m_basePath = rhs.m_basePath;
0077     m_filename = rhs.m_filename;
0078     m_scalingMethod = rhs.m_scalingMethod;
0079     m_scalingFilter = rhs.m_scalingFilter;
0080 
0081     m_generatedForImageSize = rhs.m_generatedForImageSize;
0082     m_generatedForXRes = rhs.m_generatedForXRes;
0083     m_generatedForYRes = rhs.m_generatedForYRes;
0084     m_state = rhs.m_state;
0085 
0086     m_paintDevice = new KisPaintDevice(*rhs.m_paintDevice);
0087 
0088     connect(&m_loader, SIGNAL(loadingFinished(KisPaintDeviceSP,qreal,qreal,QSize)), SLOT(slotLoadingFinished(KisPaintDeviceSP,qreal,qreal,QSize)));
0089     connect(this, SIGNAL(sigRequestOpenFile()), SLOT(openFile()));
0090     m_loader.setPath(path());
0091 }
0092 
0093 QIcon KisFileLayer::icon() const
0094 {
0095     return KisIconUtils::loadIcon("fileLayer");
0096 }
0097 
0098 void KisFileLayer::resetCache()
0099 {
0100     m_loader.reloadImage();
0101 }
0102 
0103 KisPaintDeviceSP KisFileLayer::original() const
0104 {
0105     return m_paintDevice;
0106 }
0107 
0108 KisPaintDeviceSP KisFileLayer::paintDevice() const
0109 {
0110     return 0;
0111 }
0112 
0113 void KisFileLayer::setSectionModelProperties(const KisBaseNode::PropertyList &properties)
0114 {
0115     KisBaseNode::setSectionModelProperties(properties);
0116     Q_FOREACH (const KisBaseNode::Property &property, properties) {
0117         if (property.id== KisLayerPropertiesIcons::openFileLayerFile.id()) {
0118             if (property.state.toBool() == false) {
0119                 Q_EMIT sigRequestOpenFile();
0120             }
0121         }
0122     }
0123 }
0124 
0125 KisBaseNode::PropertyList KisFileLayer::sectionModelProperties() const
0126 {
0127     KisBaseNode::PropertyList l = KisLayer::sectionModelProperties();
0128     l << KisBaseNode::Property(KoID("sourcefile", i18n("File")), m_filename);
0129     l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::openFileLayerFile, true);
0130 
0131     auto fileNameOrPlaceholder =
0132     [this] () {
0133         return !m_filename.isEmpty() ? m_filename : i18nc("placeholder test for a warning when not file is set in the file layer", "<No file name is set>");
0134     };
0135 
0136     if (m_state == FileNotFound) {
0137         l << KisLayerPropertiesIcons::getErrorProperty(i18nc("a tooltip shown when a file layer cannot find its linked file",
0138                                                              "Linked file not found: %1", fileNameOrPlaceholder()));
0139     } else if (m_state == FileLoadingFailed) {
0140         l << KisLayerPropertiesIcons::getErrorProperty(i18nc("a tooltip shown when a file layer cannot load its linked file",
0141                                                              "Failed to load linked file: %1", fileNameOrPlaceholder()));
0142     }
0143     return l;
0144 }
0145 
0146 void KisFileLayer::setFileName(const QString &basePath, const QString &filename)
0147 {
0148     m_basePath = basePath;
0149     m_filename = filename;
0150     QFileInfo fi(path());
0151     if (fi.exists()) {
0152         m_loader.setPath(path());
0153         m_loader.reloadImage();
0154     }
0155 }
0156 
0157 QString KisFileLayer::fileName() const
0158 {
0159     return m_filename;
0160 }
0161 
0162 QString KisFileLayer::path() const
0163 {
0164     if (m_basePath.isEmpty()) {
0165         return m_filename;
0166     }
0167     else {
0168 #ifndef Q_OS_ANDROID
0169         return QDir(m_basePath).filePath(QDir::cleanPath(m_filename));
0170 #else
0171         return m_filename;
0172 #endif
0173     }
0174 }
0175 
0176 void KisFileLayer::openFile() const
0177 {
0178     bool fileAlreadyOpen = false;
0179     Q_FOREACH (KisDocument *doc, KisPart::instance()->documents()) {
0180         if (doc->path()==path()){
0181             fileAlreadyOpen = true;
0182         }
0183     }
0184     if (!fileAlreadyOpen && QFile::exists(QFileInfo(path()).absoluteFilePath())) {
0185         KisPart::instance()->openExistingFile(QFileInfo(path()).absoluteFilePath());
0186     }
0187 }
0188 
0189 void KisFileLayer::changeState(State newState)
0190 {
0191     const State oldState = m_state;
0192     m_state = newState;
0193     if (oldState != newState) {
0194         baseNodeChangedCallback();
0195     }
0196 }
0197 
0198 KisFileLayer::ScalingMethod KisFileLayer::scalingMethod() const
0199 {
0200     return m_scalingMethod;
0201 }
0202 
0203 void KisFileLayer::setScalingMethod(ScalingMethod method)
0204 {
0205     m_scalingMethod = method;
0206 }
0207 
0208 QString KisFileLayer::scalingFilter() const
0209 {
0210     return m_scalingFilter;
0211 }
0212 
0213 void KisFileLayer::setScalingFilter(QString filter)
0214 {
0215     m_scalingFilter = filter;
0216 }
0217 
0218 void KisFileLayer::slotLoadingFinished(KisPaintDeviceSP projection,
0219                                        qreal xRes, qreal yRes,
0220                                        const QSize &size)
0221 {
0222     qint32 oldX = x();
0223     qint32 oldY = y();
0224     const QRect oldLayerExtent = m_paintDevice->extent();
0225 
0226 
0227     m_paintDevice->makeCloneFrom(projection, projection->extent());
0228     m_paintDevice->setDefaultBounds(new KisDefaultBounds(image()));
0229 
0230     /**
0231      * This method can be transitively called from KisFileLayer::setImage(),
0232      * which, in turn, can be called from the KisImage's copy-ctor. The shared
0233      * pointer is, obviously, not initialized during construction, therefore
0234      * upgrading our constructor to a strong pointer will cause a crash.
0235      *
0236      * Therefore, we use a weak pointer here. It is extremely dangerous, but
0237      * since this method is usually called from the GUI thread synchronously
0238      * it should be "somewhat safe".
0239      */
0240     KisImageWSP image = this->image();
0241     if (image) {
0242         if (m_scalingMethod == ToImagePPI &&
0243                 (!qFuzzyCompare(image->xRes(), xRes) ||
0244                  !qFuzzyCompare(image->yRes(), yRes))) {
0245 
0246             qreal xscale = image->xRes() / xRes;
0247             qreal yscale = image->yRes() / yRes;
0248 
0249             KisTransformWorker worker(m_paintDevice, xscale, yscale, 0.0, 0.0, 0.0, 0, 0, 0, 0, 0, KisFilterStrategyRegistry::instance()->get(m_scalingFilter));
0250             worker.run();
0251         }
0252         else if (m_scalingMethod == ToImageSize && size != image->size()) {
0253             QSize sz = size;
0254             sz.scale(image->size(), Qt::KeepAspectRatio);
0255             qreal xscale =  (qreal)sz.width() / (qreal)size.width();
0256             qreal yscale = (qreal)sz.height() / (qreal)size.height();
0257 
0258             KisTransformWorker worker(m_paintDevice, xscale, yscale, 0.0, 0.0, 0.0, 0, 0, 0, 0, 0, KisFilterStrategyRegistry::instance()->get(m_scalingFilter));
0259             worker.run();
0260         }
0261 
0262         m_generatedForImageSize = image->size();
0263         m_generatedForXRes = image->xRes();
0264         m_generatedForYRes = image->yRes();
0265     }
0266 
0267     m_paintDevice->setX(oldX);
0268     m_paintDevice->setY(oldY);
0269 
0270     changeState(FileLoaded);
0271     setDirty(m_paintDevice->extent() | oldLayerExtent);
0272 }
0273 
0274 void KisFileLayer::slotLoadingFailed()
0275 {
0276     changeState(FileLoadingFailed);
0277 }
0278 
0279 void KisFileLayer::slotFileExistsStateChanged(bool exists)
0280 {
0281     changeState(exists ? FileLoaded : FileNotFound);
0282 }
0283 
0284 KisNodeSP KisFileLayer::clone() const
0285 {
0286     return KisNodeSP(new KisFileLayer(*this));
0287 }
0288 
0289 bool KisFileLayer::allowAsChild(KisNodeSP node) const
0290 {
0291     return node->inherits("KisMask");
0292 }
0293 
0294 bool KisFileLayer::accept(KisNodeVisitor& visitor)
0295 {
0296     return visitor.visit(this);
0297 }
0298 
0299 void KisFileLayer::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter)
0300 {
0301     return visitor.visit(this, undoAdapter);
0302 }
0303 
0304 KUndo2Command* KisFileLayer::crop(const QRect & rect)
0305 {
0306     QPoint oldPos(x(), y());
0307     QPoint newPos = oldPos - rect.topLeft();
0308 
0309     return new KisNodeMoveCommand2(this, oldPos, newPos);
0310 }
0311 
0312 KUndo2Command* KisFileLayer::transform(const QTransform &/*transform*/)
0313 {
0314     warnKrita << "WARNING: File Layer does not support transformations!" << name();
0315     return 0;
0316 }
0317 
0318 void KisFileLayer::setImage(KisImageWSP image)
0319 {
0320     KisImageWSP oldImage = this->image();
0321 
0322     m_paintDevice->setDefaultBounds(new KisDefaultBounds(image));
0323     KisExternalLayer::setImage(image);
0324 
0325     if (m_scalingMethod != None && image && oldImage != image) {
0326         bool canSkipReloading = false;
0327 
0328         if (m_scalingMethod == ToImageSize && image && image->size() == m_generatedForImageSize) {
0329             canSkipReloading = true;
0330         }
0331 
0332         if (m_scalingMethod == ToImagePPI && image &&
0333                 qFuzzyCompare(image->xRes(), m_generatedForXRes) &&
0334                 qFuzzyCompare(image->yRes(), m_generatedForYRes)) {
0335 
0336             canSkipReloading = true;
0337         }
0338 
0339         if (!canSkipReloading) {
0340             m_loader.reloadImage();
0341         }
0342     }
0343 }
0344