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