File indexing completed on 2025-01-05 03:58:55
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2007 Murad Tagirov <tmurad@gmail.com> 0004 // 0005 0006 0007 #include "GeoDataIconStyle.h" 0008 0009 #include <QUrl> 0010 0011 #include "MarbleDirs.h" 0012 #include "digikam_debug.h" 0013 #include "RemoteIconLoader.h" 0014 0015 #include "GeoDataTypes.h" 0016 0017 #include <QImageReader> 0018 0019 namespace Marble 0020 { 0021 0022 class GeoDataIconStylePrivate 0023 { 0024 public: 0025 GeoDataIconStylePrivate() 0026 : m_scale( 1.0 ), 0027 m_size(0, 0), 0028 m_aspectRatioMode(Qt::KeepAspectRatio), 0029 m_iconPath(), 0030 m_heading( 0 ) 0031 { 0032 } 0033 0034 GeoDataIconStylePrivate( const QString& iconPath, const QPointF &hotSpot ) 0035 : m_scale( 1.0 ), 0036 m_size(0, 0), 0037 m_aspectRatioMode(Qt::KeepAspectRatio), 0038 m_iconPath( iconPath ), 0039 m_hotSpot( hotSpot ), 0040 m_heading( 0 ) 0041 { 0042 } 0043 0044 RemoteIconLoader* remoteIconLoader() const 0045 { 0046 static RemoteIconLoader *remoteIconLoader = new RemoteIconLoader(); 0047 return remoteIconLoader; 0048 } 0049 0050 QSize scaledSize(const QSize &size) const 0051 { 0052 QSize iconSize = size.isNull() ? m_icon.size() : size; 0053 // Scaling the placemark's icon based on its size, scale, and maximum icon size. 0054 if ( iconSize.width()*m_scale > s_maximumIconSize.width() 0055 || iconSize.height()*m_scale > s_maximumIconSize.height() ) { 0056 iconSize.scale( s_maximumIconSize, Qt::KeepAspectRatio ); 0057 } 0058 else if ( iconSize.width()*m_scale < s_minimumIconSize.width() 0059 || iconSize.height()*m_scale < s_minimumIconSize.width() ) { 0060 iconSize.scale( s_minimumIconSize, Qt::KeepAspectRatio ); 0061 } 0062 else { 0063 iconSize *= m_scale; 0064 } 0065 0066 return QSize(iconSize.width() - iconSize.width() % 2, 0067 iconSize.height() - iconSize.height() % 2); 0068 } 0069 0070 QImage loadIcon(const QString &path, const QSize &size) const 0071 { 0072 if (!path.isEmpty()) { 0073 // Icons from the local file system 0074 if (!size.isNull()) { 0075 QImageReader imageReader; 0076 imageReader.setFileName(path); 0077 auto const imageSize = imageReader.size(); 0078 auto const finalSize = imageSize.scaled(size, m_aspectRatioMode); 0079 imageReader.setScaledSize(finalSize); 0080 QImage icon = imageReader.read(); 0081 if (icon.isNull()) { 0082 qCDebug(DIGIKAM_MARBLE_LOG) << "GeoDataIconStyle: Failed to read image " << path << ": " << imageReader.errorString(); 0083 } 0084 return icon; 0085 } 0086 QImage icon = QImage(path); 0087 if (!icon.isNull()) { 0088 return icon; 0089 } 0090 } 0091 0092 if(QUrl(m_iconPath).isValid() ) { 0093 // if image is not found on disk, check whether the icon is 0094 // at remote location. If yes then go for remote icon loading 0095 return remoteIconLoader()->load(QUrl(m_iconPath)); 0096 } 0097 0098 qCDebug(DIGIKAM_MARBLE_LOG) << "Unable to open style icon at: " << path; 0099 return QImage(); 0100 } 0101 0102 float m_scale; 0103 0104 QImage m_icon; 0105 QSize m_size; 0106 Qt::AspectRatioMode m_aspectRatioMode; 0107 QImage m_scaledIcon; 0108 QString m_iconPath; 0109 GeoDataHotSpot m_hotSpot; 0110 int m_heading; 0111 }; 0112 0113 GeoDataIconStyle::GeoDataIconStyle() : 0114 d( new GeoDataIconStylePrivate() ) 0115 { 0116 } 0117 0118 GeoDataIconStyle::GeoDataIconStyle( const GeoDataIconStyle& other ) : 0119 GeoDataColorStyle( other ), d( new GeoDataIconStylePrivate( *other.d ) ) 0120 { 0121 } 0122 0123 GeoDataIconStyle::GeoDataIconStyle( const QString& iconPath, const QPointF &hotSpot ) : 0124 d( new GeoDataIconStylePrivate( iconPath, hotSpot ) ) 0125 { 0126 } 0127 0128 GeoDataIconStyle::~GeoDataIconStyle() 0129 { 0130 delete d; 0131 } 0132 0133 GeoDataIconStyle& GeoDataIconStyle::operator=( const GeoDataIconStyle& other ) 0134 { 0135 GeoDataColorStyle::operator=( other ); 0136 *d = *other.d; 0137 return *this; 0138 } 0139 0140 bool GeoDataIconStyle::operator==( const GeoDataIconStyle &other ) const 0141 { 0142 if ( GeoDataColorStyle::operator!=( other ) ) { 0143 return false; 0144 } 0145 0146 return d->m_scale == other.d->m_scale && 0147 d->m_icon == other.d->m_icon && 0148 d->m_size == other.d->m_size && 0149 d->m_iconPath == other.d->m_iconPath && 0150 d->m_hotSpot == other.d->m_hotSpot && 0151 d->m_heading == other.d->m_heading; 0152 } 0153 0154 bool GeoDataIconStyle::operator!=( const GeoDataIconStyle &other ) const 0155 { 0156 return !this->operator==( other ); 0157 } 0158 0159 const char* GeoDataIconStyle::nodeType() const 0160 { 0161 return GeoDataTypes::GeoDataIconStyleType; 0162 } 0163 0164 void GeoDataIconStyle::setIcon(const QImage &icon) 0165 { 0166 d->m_icon = icon; 0167 d->m_scaledIcon = QImage(); 0168 } 0169 0170 void GeoDataIconStyle::setIconPath( const QString& filename ) 0171 { 0172 d->m_iconPath = filename; 0173 0174 /** 0175 * Set the m_icon to be a default-constructed icon 0176 * so that m_icon is null and icon() doesn't return 0177 * previously loaded icon. 0178 */ 0179 d->m_icon = QImage(); 0180 d->m_scaledIcon = QImage(); 0181 } 0182 0183 QString GeoDataIconStyle::iconPath() const 0184 { 0185 return d->m_iconPath; 0186 } 0187 0188 QImage GeoDataIconStyle::icon() const 0189 { 0190 if ( !d->m_icon.isNull() ) { 0191 return d->m_icon; 0192 } 0193 else if ( !d->m_iconPath.isEmpty() ) { 0194 d->m_icon = d->loadIcon(resolvePath(d->m_iconPath), d->m_size); 0195 return d->m_icon; 0196 } 0197 else 0198 return QImage(); 0199 } 0200 0201 void GeoDataIconStyle::setHotSpot( const QPointF& hotSpot, 0202 GeoDataHotSpot::Units xunits, 0203 GeoDataHotSpot::Units yunits ) 0204 { 0205 d->m_hotSpot.setHotSpot( hotSpot, xunits, yunits ); 0206 } 0207 0208 QPointF GeoDataIconStyle::hotSpot( GeoDataHotSpot::Units &xunits, GeoDataHotSpot::Units &yunits ) const 0209 { 0210 return d->m_hotSpot.hotSpot( xunits, yunits ); 0211 } 0212 0213 void GeoDataIconStyle::setSize(const QSize &size, Qt::AspectRatioMode aspectRatioMode) 0214 { 0215 if (size == d->m_size && aspectRatioMode == d->m_aspectRatioMode) { 0216 return; 0217 } 0218 0219 d->m_aspectRatioMode = aspectRatioMode; 0220 d->m_size = QSize(size.width() - size.width() % 2, size.height() - size.height() % 2); 0221 if (!d->m_size.isNull() && !d->m_icon.isNull()) { 0222 // Resize existing icon that cannot be restored from an image path 0223 d->m_icon = d->m_icon.scaled(d->m_size); 0224 } else if (!d->m_iconPath.isEmpty()) { 0225 // Lazily reload the icons 0226 d->m_icon = QImage(); 0227 d->m_scaledIcon = QImage(); 0228 } 0229 } 0230 0231 QSize GeoDataIconStyle::size() const 0232 { 0233 return d->m_size; 0234 } 0235 0236 void GeoDataIconStyle::setScale(float scale) 0237 { 0238 d->m_scale = scale; 0239 d->m_scaledIcon = QImage(); 0240 } 0241 0242 float GeoDataIconStyle::scale() const 0243 { 0244 return d->m_scale; 0245 } 0246 0247 QImage GeoDataIconStyle::scaledIcon() const 0248 { 0249 if (!d->m_scaledIcon.isNull()) { 0250 return d->m_scaledIcon; 0251 } 0252 0253 // Invalid or trivial scale 0254 if (d->m_scale <= 0 || d->m_scale == 1.0) { 0255 return icon(); 0256 } 0257 0258 // Try to load it 0259 d->m_scaledIcon = d->loadIcon(resolvePath(d->m_iconPath), d->scaledSize(d->m_size)); 0260 0261 if (d->m_scaledIcon.isNull()) { 0262 // Direct loading failed, try to scale the icon as a last resort 0263 QImage const image = icon(); 0264 if (!image.isNull()) { 0265 QSize iconSize = d->scaledSize(image.size()); 0266 d->m_scaledIcon = image.scaled( iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation ) ; 0267 } 0268 } 0269 return d->m_scaledIcon; 0270 } 0271 0272 int GeoDataIconStyle::heading() const 0273 { 0274 return d->m_heading; 0275 } 0276 0277 void GeoDataIconStyle::setHeading( int heading ) 0278 { 0279 d->m_heading = heading; 0280 } 0281 0282 RemoteIconLoader *GeoDataIconStyle::remoteIconLoader() const 0283 { 0284 return d->remoteIconLoader(); 0285 } 0286 0287 void GeoDataIconStyle::pack( QDataStream& stream ) const 0288 { 0289 GeoDataColorStyle::pack( stream ); 0290 0291 stream << d->m_scale; 0292 stream << d->m_icon; 0293 d->m_hotSpot.pack( stream ); 0294 } 0295 0296 void GeoDataIconStyle::unpack( QDataStream& stream ) 0297 { 0298 GeoDataColorStyle::unpack( stream ); 0299 0300 stream >> d->m_scale; 0301 stream >> d->m_icon; 0302 d->m_hotSpot.unpack( stream ); 0303 } 0304 0305 }