File indexing completed on 2025-01-05 03:59:37

0001 // SPDX-FileCopyrightText: 2006-2007 Torsten Rahn <tackat@kde.org>
0002 // SPDX-FileCopyrightText: 2007-2008 Inge Wallin <ingwa@kde.org>
0003 // SPDX-FileCopyrightText: 2011 Niko Sams <niko.sams@gmail.com>
0004 //
0005 // SPDX-License-Identifier: LGPL-2.1-or-later
0006 
0007 #include "TileCreator.h"
0008 
0009 #include <cmath>
0010 
0011 #include <QDir>
0012 #include <QRect>
0013 #include <QSize>
0014 #include <QVector>
0015 #include <QApplication>
0016 #include <QImage>
0017 #include <QPainter>
0018 
0019 #include "MarbleGlobal.h"
0020 #include "MarbleDirs.h"
0021 #include "TileLoaderHelper.h"
0022 
0023 #include "digikam_debug.h"
0024 
0025 namespace Marble
0026 {
0027 
0028 class TileCreatorPrivate
0029 {
0030  public:
0031     TileCreatorPrivate( TileCreatorSource *source,
0032                         const QString& dem, const QString& targetDir=QString() )
0033        : m_dem( dem ),
0034          m_targetDir( targetDir ),
0035          m_cancelled( false ),
0036          m_tileFormat( QString::fromUtf8("jpg") ),
0037          m_resume( false ),
0038          m_verify( false ),
0039          m_source( source )
0040      {
0041         if (m_dem == QLatin1String("true")) {
0042             m_tileQuality = 70;
0043         } else {
0044             m_tileQuality = 85;
0045         }
0046     }
0047 
0048     ~TileCreatorPrivate()
0049     {
0050         delete m_source;
0051     }
0052 
0053  public:
0054     QString  m_dem;
0055     QString  m_targetDir;
0056     bool     m_cancelled;
0057     QString  m_tileFormat;
0058     int      m_tileQuality;
0059     bool     m_resume;
0060     bool     m_verify;
0061 
0062     TileCreatorSource  *m_source;
0063 };
0064 
0065 class TileCreatorSourceImage : public TileCreatorSource
0066 {
0067 public:
0068     explicit TileCreatorSourceImage( const QString &sourcePath )
0069         : m_sourceImage( QImage( sourcePath ) ),
0070           m_cachedRowNum( -1 )
0071     {
0072     }
0073 
0074     QSize fullImageSize() const override
0075     {
0076         if ( m_sourceImage.size().width() > 21600 || m_sourceImage.height() > 10800 ) {
0077             qCDebug(DIGIKAM_MARBLE_LOG) << QString::fromUtf8("Install map too large!");
0078             return QSize();
0079         }
0080         return m_sourceImage.size();
0081     }
0082 
0083     QImage tile(int n, int m, int maxTileLevel) override
0084     {
0085         int  mmax = TileLoaderHelper::levelToColumn( defaultLevelZeroColumns, maxTileLevel );
0086         int  nmax = TileLoaderHelper::levelToRow( defaultLevelZeroRows, maxTileLevel );
0087 
0088         int imageHeight = m_sourceImage.height();
0089         int imageWidth = m_sourceImage.width();
0090 
0091         // If the image size of the image source does not match the expected
0092         // geometry we need to smooth-scale the image in advance to match
0093         // the required size
0094         bool needsScaling = ( imageWidth != 2 * nmax * (int)( c_defaultTileSize )
0095                             ||  imageHeight != nmax * (int)( c_defaultTileSize ) );
0096 
0097         if ( needsScaling )
0098             qCDebug(DIGIKAM_MARBLE_LOG) << "Image Size doesn't match 2*n*TILEWIDTH x n*TILEHEIGHT geometry. Scaling ...";
0099 
0100         int  stdImageWidth  = 2 * nmax * c_defaultTileSize;
0101         if ( stdImageWidth == 0 )
0102             stdImageWidth = 2 * c_defaultTileSize;
0103 
0104         int  stdImageHeight  = nmax * c_defaultTileSize;
0105         if ( stdImageWidth != imageWidth ) {
0106             qCDebug(DIGIKAM_MARBLE_LOG) <<
0107             QString::fromUtf8( "TileCreator::createTiles() The size of the final image will measure  %1 x %2 pixels").arg(stdImageWidth).arg(stdImageHeight);
0108         }
0109 
0110         QImage row;
0111 
0112         if ( m_cachedRowNum == n ) {
0113 
0114             row = m_rowCache;
0115 
0116         } else {
0117 
0118             QRect   sourceRowRect( 0, (int)( (qreal)( n * imageHeight ) / (qreal)( nmax )),
0119                                 imageWidth,(int)( (qreal)( imageHeight ) / (qreal)( nmax ) ) );
0120 
0121 
0122             row = m_sourceImage.copy( sourceRowRect );
0123 
0124             if ( needsScaling ) {
0125                 // Pick the current row and smooth scale it
0126                 // to make it match the expected size
0127                 QSize destSize( stdImageWidth, c_defaultTileSize );
0128                 row = row.scaled( destSize,
0129                                 Qt::IgnoreAspectRatio,
0130                                 Qt::SmoothTransformation );
0131             }
0132 
0133             m_cachedRowNum = n;
0134             m_rowCache = row;
0135         }
0136 
0137         if ( row.isNull() ) {
0138             qCDebug(DIGIKAM_MARBLE_LOG) << "Read-Error! Null QImage!";
0139             return QImage();
0140         }
0141 
0142         QImage  tile = row.copy( m * stdImageWidth / mmax, 0, c_defaultTileSize, c_defaultTileSize );
0143 
0144         return tile;
0145     }
0146 
0147 private:
0148     QImage m_sourceImage;
0149 
0150     QImage m_rowCache;
0151     int m_cachedRowNum;
0152 };
0153 
0154 
0155 TileCreator::TileCreator(const QString& sourceDir, const QString& installMap,
0156                          const QString& dem, const QString& targetDir)
0157     : QThread(nullptr),
0158       d( new TileCreatorPrivate( nullptr, dem, targetDir ) )
0159 
0160 {
0161     qCDebug(DIGIKAM_MARBLE_LOG) << "Prefix: " << sourceDir
0162         << "installmap:" << installMap;
0163 
0164     QString sourcePath;
0165 
0166     // If the sourceDir starts with a '/' assume an absolute path.
0167     // Otherwise assume a relative marble data path
0168     if ( QDir::isAbsolutePath( sourceDir ) ) {
0169         sourcePath = sourceDir + QLatin1Char('/') + installMap;
0170         qCDebug(DIGIKAM_MARBLE_LOG) << "Trying absolute path*:" << sourcePath;
0171     }
0172     else {
0173         sourcePath = MarbleDirs::path(QLatin1String("maps/") + sourceDir + QLatin1Char('/') + installMap);
0174         qCDebug(DIGIKAM_MARBLE_LOG) << "Trying relative path*:"
0175                 << QLatin1String("maps/") + sourceDir + QLatin1Char('/') + installMap;
0176     }
0177 
0178     qCDebug(DIGIKAM_MARBLE_LOG) << "Creating tiles from*: " << sourcePath;
0179 
0180     d->m_source = new TileCreatorSourceImage( sourcePath );
0181 
0182     if ( d->m_targetDir.isNull() )
0183         d->m_targetDir = MarbleDirs::localPath() + QLatin1String("/maps/")
0184             + sourcePath.section(QLatin1Char('/'), -3, -2) + QLatin1Char('/');
0185 
0186     setTerminationEnabled( true );
0187 }
0188 
0189 TileCreator::TileCreator( TileCreatorSource* source, const QString& dem, const QString& targetDir )
0190     : QThread(nullptr),
0191       d( new TileCreatorPrivate( source, dem, targetDir ) )
0192 {
0193     setTerminationEnabled( true );
0194 }
0195 
0196 TileCreator::~TileCreator()
0197 {
0198     delete d;
0199 }
0200 
0201 void TileCreator::cancelTileCreation()
0202 {
0203     d->m_cancelled = true;
0204 }
0205 
0206 void TileCreator::run()
0207 {
0208     if (d->m_resume && d->m_tileFormat == QLatin1String("jpg") && d->m_tileQuality != 100) {
0209         qCWarning(DIGIKAM_MARBLE_LOG) << "Resuming jpegs is only supported with tileQuality 100";
0210         return;
0211     }
0212 
0213     if (!d->m_targetDir.endsWith(QLatin1Char('/')))
0214         d->m_targetDir += QLatin1Char('/');
0215 
0216     qCDebug(DIGIKAM_MARBLE_LOG) << "Installing tiles to: " << d->m_targetDir;
0217 
0218     QVector<QRgb> grayScalePalette;
0219     for ( int cnt = 0; cnt <= 255; ++cnt ) {
0220         grayScalePalette.insert(cnt, qRgb(cnt, cnt, cnt));
0221     }
0222 
0223     QSize fullImageSize = d->m_source->fullImageSize();
0224     int  imageWidth  = fullImageSize.width();
0225     int  imageHeight = fullImageSize.height();
0226 
0227     qCDebug(DIGIKAM_MARBLE_LOG) << QString::fromUtf8( "TileCreator::createTiles() image dimensions %1 x %2").arg(imageWidth).arg(imageHeight);
0228 
0229     if ( imageWidth < 1 || imageHeight < 1 ) {
0230         qCDebug(DIGIKAM_MARBLE_LOG) << QString::fromUtf8("Invalid imagemap!");
0231         return;
0232     }
0233 
0234     // Calculating Maximum Tile Level
0235     float approxMaxTileLevel = std::log( imageWidth / ( 2.0 * c_defaultTileSize ) ) / std::log( 2.0 );
0236 
0237     int  maxTileLevel = 0;
0238     if ( approxMaxTileLevel == int( approxMaxTileLevel ) )
0239         maxTileLevel = static_cast<int>( approxMaxTileLevel );
0240     else
0241         maxTileLevel = static_cast<int>( approxMaxTileLevel + 1 );
0242 
0243     if ( maxTileLevel < 0 ) {
0244         qCDebug(DIGIKAM_MARBLE_LOG)
0245         << QString::fromUtf8( "TileCreator::createTiles(): Invalid Maximum Tile Level: %1" )
0246         .arg( maxTileLevel );
0247     }
0248     qCDebug(DIGIKAM_MARBLE_LOG) << "Maximum Tile Level: " << maxTileLevel;
0249 
0250 
0251     if ( !QDir( d->m_targetDir ).exists() )
0252         ( QDir::root() ).mkpath( d->m_targetDir );
0253 
0254     // Counting total amount of tiles to be generated for the progressbar
0255     // to prevent compiler warnings this var should
0256     // match the type of maxTileLevel
0257     int  tileLevel      = 0;
0258     int  totalTileCount = 0;
0259 
0260     while ( tileLevel <= maxTileLevel ) {
0261         totalTileCount += ( TileLoaderHelper::levelToRow( defaultLevelZeroRows, tileLevel )
0262                             * TileLoaderHelper::levelToColumn( defaultLevelZeroColumns, tileLevel ) );
0263         tileLevel++;
0264     }
0265 
0266     qCDebug(DIGIKAM_MARBLE_LOG) << totalTileCount << " tiles to be created in total.";
0267 
0268     int  mmax = TileLoaderHelper::levelToColumn( defaultLevelZeroColumns, maxTileLevel );
0269     int  nmax = TileLoaderHelper::levelToRow( defaultLevelZeroRows, maxTileLevel );
0270 
0271     // Loading each row at highest spatial resolution and cropping tiles
0272     int      percentCompleted = 0;
0273     int      createdTilesCount = 0;
0274     QString  tileName;
0275 
0276     // Creating directory structure for the highest level
0277     QString  dirName( d->m_targetDir
0278                       + QString::fromUtf8("%1").arg(maxTileLevel) );
0279     if ( !QDir( dirName ).exists() )
0280         ( QDir::root() ).mkpath( dirName );
0281 
0282     for ( int n = 0; n < nmax; ++n ) {
0283         QString dirName( d->m_targetDir
0284                          + QString::fromUtf8("%1/%2").arg(maxTileLevel).arg(n, tileDigits, 10, QLatin1Char('0')));
0285         if ( !QDir( dirName ).exists() )
0286             ( QDir::root() ).mkpath( dirName );
0287     }
0288 
0289     for ( int n = 0; n < nmax; ++n ) {
0290 
0291         for ( int m = 0; m < mmax; ++m ) {
0292 
0293             qCDebug(DIGIKAM_MARBLE_LOG) << QString::fromUtf8("** tile") << m << QString::fromUtf8("x") << n;
0294 
0295             if ( d->m_cancelled )
0296                 return;
0297 
0298             tileName = d->m_targetDir + QString::fromUtf8("%1/%2/%2_%3.%4")
0299                                        .arg( maxTileLevel )
0300                                        .arg(n, tileDigits, 10, QLatin1Char('0'))
0301                                        .arg(m, tileDigits, 10, QLatin1Char('0'))
0302                                        .arg( d->m_tileFormat );
0303 
0304             if ( QFile::exists( tileName ) && d->m_resume ) {
0305 
0306                 //qCDebug(DIGIKAM_MARBLE_LOG) << tileName << "exists already";
0307 
0308             } else {
0309 
0310                 QImage tile = d->m_source->tile( n, m, maxTileLevel );
0311 
0312                 if ( tile.isNull() ) {
0313                     qCDebug(DIGIKAM_MARBLE_LOG) << "Read-Error! Null QImage!";
0314                     return;
0315                 }
0316 
0317                 if (d->m_dem == QLatin1String("true")) {
0318                     tile = tile.convertToFormat(QImage::Format_Indexed8,
0319                                                 grayScalePalette,
0320                                                 Qt::ThresholdDither);
0321                 }
0322 
0323                 bool  ok = tile.save(tileName, d->m_tileFormat.toLatin1().data(), d->m_tileFormat == QLatin1String("jpg") ? 100 : d->m_tileQuality);
0324                 if ( !ok )
0325                     qCDebug(DIGIKAM_MARBLE_LOG) << "Error while writing Tile: " << tileName;
0326 
0327                 qCDebug(DIGIKAM_MARBLE_LOG) << tileName << "size" << QFile( tileName ).size();
0328 
0329                 if ( d->m_verify ) {
0330                     QImage writtenTile(tileName);
0331                     Q_ASSERT( writtenTile.size() == tile.size() );
0332                     for ( int i=0; i < writtenTile.size().width(); ++i) {
0333                         for ( int j=0; j < writtenTile.size().height(); ++j) {
0334                             if ( writtenTile.pixel( i, j ) != tile.pixel( i, j ) ) {
0335                                 unsigned int  pixel = tile.pixel( i, j);
0336                                 unsigned int  writtenPixel = writtenTile.pixel( i, j);
0337                                 qCWarning(DIGIKAM_MARBLE_LOG) << "***** pixel" << i << j << "is off by" << (pixel - writtenPixel) << "pixel" << pixel << "writtenPixel" << writtenPixel;
0338                                 QByteArray baPixel((char*)&pixel, sizeof(unsigned int));
0339                                 qCWarning(DIGIKAM_MARBLE_LOG) << "pixel" << baPixel.size() << "0x" << baPixel.toHex();
0340                                 QByteArray baWrittenPixel((char*)&writtenPixel, sizeof(unsigned int));
0341                                 qCWarning(DIGIKAM_MARBLE_LOG) << "writtenPixel" << baWrittenPixel.size() << "0x" << baWrittenPixel.toHex();
0342                                 Q_ASSERT(false);
0343                             }
0344                         }
0345                     }
0346                 }
0347 
0348             }
0349 
0350             percentCompleted =  (int) ( 90 * (qreal)(createdTilesCount)
0351                                         / (qreal)(totalTileCount) );
0352             createdTilesCount++;
0353 
0354             qCDebug(DIGIKAM_MARBLE_LOG) << "percentCompleted" << percentCompleted;
0355             Q_EMIT progress( percentCompleted );
0356         }
0357     }
0358 
0359     qCDebug(DIGIKAM_MARBLE_LOG) << "tileLevel: " << maxTileLevel << " successfully created.";
0360 
0361     tileLevel = maxTileLevel;
0362 
0363     // Now that we have the tiles at the highest resolution lets build
0364     // them together four by four.
0365 
0366     while( tileLevel > 0 ) {
0367         tileLevel--;
0368 
0369         int  nmaxit =  TileLoaderHelper::levelToRow( defaultLevelZeroRows, tileLevel );
0370 
0371         for ( int n = 0; n < nmaxit; ++n ) {
0372             QString  dirName( d->m_targetDir
0373                               + QString::fromUtf8("%1/%2")
0374                                   .arg(tileLevel)
0375                                   .arg(n, tileDigits, 10, QLatin1Char('0')));
0376 
0377             // qCDebug(DIGIKAM_MARBLE_LOG) << "dirName: " << dirName;
0378             if ( !QDir( dirName ).exists() )
0379                 ( QDir::root() ).mkpath( dirName );
0380 
0381             int   mmaxit = TileLoaderHelper::levelToColumn( defaultLevelZeroColumns, tileLevel );
0382             for ( int m = 0; m < mmaxit; ++m ) {
0383 
0384                 if ( d->m_cancelled )
0385                     return;
0386 
0387                 QString newTileName = d->m_targetDir + QString::fromUtf8("%1/%2/%2_%3.%4")
0388                                            .arg( tileLevel )
0389                                            .arg(n, tileDigits, 10, QLatin1Char('0'))
0390                                            .arg(m, tileDigits, 10, QLatin1Char('0'))
0391                                            .arg( d->m_tileFormat );
0392 
0393                 if ( QFile::exists( newTileName ) && d->m_resume ) {
0394                     //qCDebug(DIGIKAM_MARBLE_LOG) << newTileName << "exists already";
0395                 } else {
0396                     tileName = d->m_targetDir + QString::fromUtf8("%1/%2/%2_%3.%4")
0397                                             .arg( tileLevel + 1 )
0398                                             .arg(2*n, tileDigits, 10, QLatin1Char('0'))
0399                                             .arg(2*m, tileDigits, 10, QLatin1Char('0'))
0400                                             .arg( d->m_tileFormat );
0401                     QImage  img_topleft( tileName );
0402 
0403                     tileName = d->m_targetDir + QString::fromUtf8("%1/%2/%2_%3.%4")
0404                                             .arg( tileLevel + 1 )
0405                                             .arg(2*n, tileDigits, 10, QLatin1Char('0'))
0406                                             .arg(2*m+1, tileDigits, 10, QLatin1Char('0'))
0407                                             .arg( d->m_tileFormat );
0408                     QImage  img_topright( tileName );
0409 
0410                     tileName = d->m_targetDir + QString::fromUtf8("%1/%2/%2_%3.%4")
0411                                             .arg( tileLevel + 1 )
0412                                             .arg(2*n+1, tileDigits, 10, QLatin1Char('0'))
0413                                             .arg(2*m, tileDigits, 10, QLatin1Char('0'))
0414                                             .arg( d->m_tileFormat );
0415                     QImage  img_bottomleft( tileName );
0416 
0417                     tileName = d->m_targetDir + QString::fromUtf8("%1/%2/%2_%3.%4")
0418                                             .arg( tileLevel + 1 )
0419                                             .arg(2*n+1, tileDigits, 10, QLatin1Char('0'))
0420                                             .arg(2*m+1, tileDigits, 10, QLatin1Char('0'))
0421                                             .arg( d->m_tileFormat );
0422                     QImage  img_bottomright( tileName );
0423 
0424                     QSize const expectedSize( c_defaultTileSize, c_defaultTileSize );
0425                     if ( img_topleft.size() != expectedSize ||
0426                          img_topright.size() != expectedSize ||
0427                          img_bottomleft.size() != expectedSize ||
0428                          img_bottomright.size() != expectedSize ) {
0429                         qCDebug(DIGIKAM_MARBLE_LOG) << "Tile write failure. Missing write permissions?";
0430                         Q_EMIT progress( 100 );
0431                         return;
0432                     }
0433                     QImage  tile = img_topleft;
0434 
0435                     if (d->m_dem == QLatin1String("true")) {
0436 
0437                         tile.setColorTable( grayScalePalette );
0438                         uchar* destLine;
0439 
0440                         for ( uint y = 0; y < c_defaultTileSize / 2; ++y ) {
0441                             destLine = tile.scanLine( y );
0442                             const uchar* srcLine = img_topleft.scanLine( 2 * y );
0443                             for ( uint x = 0; x < c_defaultTileSize / 2; ++x )
0444                                 destLine[x] = srcLine[ 2*x ];
0445                         }
0446                         for ( uint y = 0; y < c_defaultTileSize / 2; ++y ) {
0447                             destLine = tile.scanLine( y );
0448                             const uchar* srcLine = img_topright.scanLine( 2 * y );
0449                             for ( uint x = c_defaultTileSize / 2; x < c_defaultTileSize; ++x )
0450                                 destLine[x] = srcLine[ 2 * ( x - c_defaultTileSize / 2 ) ];
0451                         }
0452                         for ( uint y = c_defaultTileSize / 2; y < c_defaultTileSize; ++y ) {
0453                             destLine = tile.scanLine( y );
0454                             const uchar* srcLine = img_bottomleft.scanLine( 2 * ( y - c_defaultTileSize / 2 ) );
0455                             for ( uint x = 0; x < c_defaultTileSize / 2; ++x )
0456                                 destLine[ x ] = srcLine[ 2 * x ];
0457                         }
0458                         for ( uint y = c_defaultTileSize / 2; y < c_defaultTileSize; ++y ) {
0459                             destLine = tile.scanLine( y );
0460                             const uchar* srcLine = img_bottomright.scanLine( 2 * ( y - c_defaultTileSize/2 ) );
0461                             for ( uint x = c_defaultTileSize / 2; x < c_defaultTileSize; ++x )
0462                                 destLine[x] = srcLine[ 2 * ( x - c_defaultTileSize / 2 ) ];
0463                         }
0464                     }
0465                     else {
0466 
0467                         // tile.depth() != 8
0468 
0469                         img_topleft = img_topleft.convertToFormat( QImage::Format_ARGB32 );
0470                         img_topright = img_topright.convertToFormat( QImage::Format_ARGB32 );
0471                         img_bottomleft = img_bottomleft.convertToFormat( QImage::Format_ARGB32 );
0472                         img_bottomright = img_bottomright.convertToFormat( QImage::Format_ARGB32 );
0473                         tile = img_topleft;
0474 
0475                         QRgb* destLine;
0476 
0477                         for ( uint y = 0; y < c_defaultTileSize / 2; ++y ) {
0478                             destLine = (QRgb*) tile.scanLine( y );
0479                             const QRgb* srcLine = (QRgb*) img_topleft.scanLine( 2 * y );
0480                             for ( uint x = 0; x < c_defaultTileSize / 2; ++x )
0481                                 destLine[x] = srcLine[ 2 * x ];
0482                         }
0483                         for ( uint y = 0; y < c_defaultTileSize / 2; ++y ) {
0484                             destLine = (QRgb*) tile.scanLine( y );
0485                             const QRgb* srcLine = (QRgb*) img_topright.scanLine( 2 * y );
0486                             for ( uint x = c_defaultTileSize / 2; x < c_defaultTileSize; ++x )
0487                                 destLine[x] = srcLine[ 2 * ( x - c_defaultTileSize / 2 ) ];
0488                         }
0489                         for ( uint y = c_defaultTileSize / 2; y < c_defaultTileSize; ++y ) {
0490                             destLine = (QRgb*) tile.scanLine( y );
0491                             const QRgb* srcLine = (QRgb*) img_bottomleft.scanLine( 2 * ( y-c_defaultTileSize/2 ) );
0492                             for ( uint x = 0; x < c_defaultTileSize / 2; ++x )
0493                                 destLine[x] = srcLine[ 2 * x ];
0494                         }
0495                         for ( uint y = c_defaultTileSize / 2; y < c_defaultTileSize; ++y ) {
0496                             destLine = (QRgb*) tile.scanLine( y );
0497                             const QRgb* srcLine = (QRgb*) img_bottomright.scanLine( 2 * ( y-c_defaultTileSize / 2 ) );
0498                             for ( uint x = c_defaultTileSize / 2; x < c_defaultTileSize; ++x )
0499                                 destLine[x] = srcLine[ 2*( x-c_defaultTileSize / 2 ) ];
0500                         }
0501                     }
0502 
0503                     qCDebug(DIGIKAM_MARBLE_LOG) << newTileName;
0504 
0505                     // Saving at 100% JPEG quality to have a high-quality
0506                     // version to create the remaining needed tiles from.
0507                     bool  ok = tile.save(newTileName, d->m_tileFormat.toLatin1().data(), d->m_tileFormat == QLatin1String("jpg") ? 100 : d->m_tileQuality);
0508                     if ( ! ok )
0509                         qCDebug(DIGIKAM_MARBLE_LOG) << "Error while writing Tile: " << newTileName;
0510                 }
0511 
0512                 percentCompleted =  (int) ( 90 * (qreal)(createdTilesCount)
0513                                             / (qreal)(totalTileCount) );
0514                 createdTilesCount++;
0515 
0516                 Q_EMIT progress( percentCompleted );
0517                 qCDebug(DIGIKAM_MARBLE_LOG) << "percentCompleted" << percentCompleted;
0518             }
0519         }
0520         qCDebug(DIGIKAM_MARBLE_LOG) << "tileLevel: " << tileLevel << " successfully created.";
0521     }
0522     qCDebug(DIGIKAM_MARBLE_LOG) << "Tile creation completed.";
0523 
0524     if (d->m_tileFormat == QLatin1String("jpg") && d->m_tileQuality != 100) {
0525 
0526         // Applying correct lower JPEG compression now that we created all tiles
0527         int savedTilesCount = 0;
0528 
0529         tileLevel = 0;
0530         while ( tileLevel <= maxTileLevel ) {
0531             int nmaxit =  TileLoaderHelper::levelToRow( defaultLevelZeroRows, tileLevel );
0532             for ( int n = 0; n < nmaxit; ++n) {
0533                 int mmaxit =  TileLoaderHelper::levelToColumn( defaultLevelZeroColumns, tileLevel );
0534                 for ( int m = 0; m < mmaxit; ++m) {
0535 
0536                     if ( d->m_cancelled )
0537                         return;
0538 
0539                     savedTilesCount++;
0540 
0541                     tileName = d->m_targetDir + QString::fromUtf8("%1/%2/%2_%3.%4")
0542                                             .arg( tileLevel )
0543                                             .arg(n, tileDigits, 10, QLatin1Char('0'))
0544                                             .arg(m, tileDigits, 10, QLatin1Char('0'))
0545                                             .arg( d->m_tileFormat );
0546                     QImage tile( tileName );
0547 
0548                     bool ok;
0549 
0550                     ok = tile.save( tileName, d->m_tileFormat.toLatin1().data(), d->m_tileQuality );
0551 
0552                     if ( !ok )
0553                         qCDebug(DIGIKAM_MARBLE_LOG) << "Error while writing Tile: " << tileName;
0554                     // Don't exceed 99% as this would cancel the thread unexpectedly
0555                     percentCompleted = 90 + (int)( 9 * (qreal)(savedTilesCount)
0556                                                 / (qreal)(totalTileCount) );
0557                     Q_EMIT progress( percentCompleted );
0558                     qCDebug(DIGIKAM_MARBLE_LOG) << "percentCompleted" << percentCompleted;
0559                     //qCDebug(DIGIKAM_MARBLE_LOG) << "Saving Tile #" << savedTilesCount
0560                     //         << " of " << totalTileCount
0561                     //         << " Percent: " << percentCompleted;
0562                 }
0563             }
0564             tileLevel++;
0565         }
0566     }
0567 
0568     percentCompleted = 100;
0569     Q_EMIT progress( percentCompleted );
0570 
0571     qCDebug(DIGIKAM_MARBLE_LOG) << "percentCompleted: " << percentCompleted;
0572 }
0573 
0574 void TileCreator::setTileFormat(const QString& format)
0575 {
0576     d->m_tileFormat = format;
0577 }
0578 
0579 QString TileCreator::tileFormat() const
0580 {
0581     return d->m_tileFormat;
0582 }
0583 
0584 void TileCreator::setTileQuality(int quality)
0585 {
0586     d->m_tileQuality = quality;
0587 }
0588 
0589 int TileCreator::tileQuality() const
0590 {
0591     return d->m_tileQuality;
0592 }
0593 
0594 void TileCreator::setResume(bool resume)
0595 {
0596     d->m_resume = resume;
0597 }
0598 
0599 bool TileCreator::resume() const
0600 {
0601     return d->m_resume;
0602 }
0603 
0604 void TileCreator::setVerifyExactResult(bool verify)
0605 {
0606     d->m_verify = verify;
0607 }
0608 
0609 bool TileCreator::verifyExactResult() const
0610 {
0611     return d->m_verify;
0612 }
0613 
0614 
0615 }
0616 
0617 #include "moc_TileCreator.cpp"