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