7 #include "TileCreator.h"
15 #include <QApplication>
19 #include "MarbleGlobal.h"
20 #include "MarbleDirs.h"
21 #include "MarbleDebug.h"
22 #include "TileLoaderHelper.h"
27 class TileCreatorPrivate
30 TileCreatorPrivate( TileCreatorSource *source,
33 m_targetDir( targetDir ),
35 m_tileFormat(
"jpg" ),
61 TileCreatorSource *m_source;
64 class TileCreatorSourceImage :
public TileCreatorSource
67 explicit TileCreatorSourceImage(
const QString &sourcePath )
68 : m_sourceImage(
QImage( sourcePath ) ),
73 QSize fullImageSize()
const override
75 if ( m_sourceImage.size().width() > 21600 || m_sourceImage.height() > 10800 ) {
76 qDebug(
"Install map too large!");
79 return m_sourceImage.size();
82 QImage tile(
int n,
int m,
int maxTileLevel)
override
84 int mmax = TileLoaderHelper::levelToColumn( defaultLevelZeroColumns, maxTileLevel );
85 int nmax = TileLoaderHelper::levelToRow( defaultLevelZeroRows, maxTileLevel );
87 int imageHeight = m_sourceImage.height();
88 int imageWidth = m_sourceImage.width();
93 bool needsScaling = ( imageWidth != 2 * nmax * (int)( c_defaultTileSize )
94 || imageHeight != nmax * (int)( c_defaultTileSize ) );
97 mDebug() <<
"Image Size doesn't match 2*n*TILEWIDTH x n*TILEHEIGHT geometry. Scaling ...";
99 int stdImageWidth = 2 * nmax * c_defaultTileSize;
100 if ( stdImageWidth == 0 )
101 stdImageWidth = 2 * c_defaultTileSize;
103 int stdImageHeight = nmax * c_defaultTileSize;
104 if ( stdImageWidth != imageWidth ) {
106 QString(
"TileCreator::createTiles() The size of the final image will measure %1 x %2 pixels").
arg(stdImageWidth).
arg(stdImageHeight);
111 if ( m_cachedRowNum == n ) {
117 QRect sourceRowRect( 0, (
int)( (qreal)( n * imageHeight ) / (qreal)( nmax )),
118 imageWidth,(
int)( (qreal)( imageHeight ) / (qreal)( nmax ) ) );
121 row = m_sourceImage.
copy( sourceRowRect );
123 if ( needsScaling ) {
126 QSize destSize( stdImageWidth, c_defaultTileSize );
127 row = row.
scaled( destSize,
137 mDebug() <<
"Read-Error! Null QImage!";
141 QImage tile = row.
copy( m * stdImageWidth / mmax, 0, c_defaultTileSize, c_defaultTileSize );
154 TileCreator::TileCreator(
const QString& sourceDir,
const QString& installMap,
157 d( new TileCreatorPrivate( nullptr, dem, targetDir ) )
160 mDebug() <<
"Prefix: " << sourceDir
161 <<
"installmap:" << installMap;
168 sourcePath = sourceDir +
QLatin1Char(
'/') + installMap;
169 mDebug() <<
"Trying absolute path*:" << sourcePath;
173 mDebug() <<
"Trying relative path*:"
177 mDebug() <<
"Creating tiles from*: " << sourcePath;
179 d->m_source =
new TileCreatorSourceImage( sourcePath );
181 if ( d->m_targetDir.isNull() )
182 d->m_targetDir = MarbleDirs::localPath() +
QLatin1String(
"/maps/")
185 setTerminationEnabled(
true );
188 TileCreator::TileCreator( TileCreatorSource* source,
const QString& dem,
const QString& targetDir )
190 d( new TileCreatorPrivate( source, dem, targetDir ) )
192 setTerminationEnabled(
true );
195 TileCreator::~TileCreator()
200 void TileCreator::cancelTileCreation()
202 d->m_cancelled =
true;
205 void TileCreator::run()
207 if (d->m_resume && d->m_tileFormat ==
QLatin1String(
"jpg") && d->m_tileQuality != 100) {
208 qWarning() <<
"Resuming jpegs is only supported with tileQuality 100";
215 mDebug() <<
"Installing tiles to: " << d->m_targetDir;
218 for (
int cnt = 0; cnt <= 255; ++cnt ) {
219 grayScalePalette.
insert(cnt, qRgb(cnt, cnt, cnt));
222 QSize fullImageSize = d->m_source->fullImageSize();
223 int imageWidth = fullImageSize.
width();
224 int imageHeight = fullImageSize.
height();
226 mDebug() <<
QString(
"TileCreator::createTiles() image dimensions %1 x %2").
arg(imageWidth).
arg(imageHeight);
228 if ( imageWidth < 1 || imageHeight < 1 ) {
229 qDebug(
"Invalid imagemap!");
234 float approxMaxTileLevel = std::log( imageWidth / ( 2.0 * c_defaultTileSize ) ) / std::log( 2.0 );
236 int maxTileLevel = 0;
237 if ( approxMaxTileLevel ==
int( approxMaxTileLevel ) )
238 maxTileLevel =
static_cast<int>( approxMaxTileLevel );
240 maxTileLevel =
static_cast<int>( approxMaxTileLevel + 1 );
242 if ( maxTileLevel < 0 ) {
244 <<
QString(
"TileCreator::createTiles(): Invalid Maximum Tile Level: %1" )
245 .
arg( maxTileLevel );
247 mDebug() <<
"Maximum Tile Level: " << maxTileLevel;
257 int totalTileCount = 0;
259 while ( tileLevel <= maxTileLevel ) {
260 totalTileCount += ( TileLoaderHelper::levelToRow( defaultLevelZeroRows, tileLevel )
261 * TileLoaderHelper::levelToColumn( defaultLevelZeroColumns, tileLevel ) );
265 mDebug() << totalTileCount <<
" tiles to be created in total.";
267 int mmax = TileLoaderHelper::levelToColumn( defaultLevelZeroColumns, maxTileLevel );
268 int nmax = TileLoaderHelper::levelToRow( defaultLevelZeroRows, maxTileLevel );
271 int percentCompleted = 0;
272 int createdTilesCount = 0;
276 QString dirName( d->m_targetDir
277 +
QString(
"%1").arg(maxTileLevel) );
278 if ( !
QDir( dirName ).exists() )
281 for (
int n = 0; n < nmax; ++n ) {
282 QString dirName( d->m_targetDir
284 if ( !
QDir( dirName ).exists() )
288 for (
int n = 0; n < nmax; ++n ) {
290 for (
int m = 0; m < mmax; ++m ) {
292 mDebug() <<
"** tile" << m <<
"x" << n;
294 if ( d->m_cancelled )
297 tileName = d->m_targetDir +
QString(
"%1/%2/%2_%3.%4")
301 .
arg( d->m_tileFormat );
309 QImage tile = d->m_source->tile( n, m, maxTileLevel );
312 mDebug() <<
"Read-Error! Null QImage!";
322 bool ok = tile.
save(tileName, d->m_tileFormat.toLatin1().data(), d->m_tileFormat ==
QLatin1String(
"jpg") ? 100 : d->m_tileQuality);
324 mDebug() <<
"Error while writing Tile: " << tileName;
329 QImage writtenTile(tileName);
330 Q_ASSERT( writtenTile.size() == tile.
size() );
331 for (
int i=0; i < writtenTile.size().width(); ++i) {
332 for (
int j=0; j < writtenTile.size().height(); ++j) {
333 if ( writtenTile.pixel( i, j ) != tile.
pixel( i, j ) ) {
334 unsigned int pixel = tile.
pixel( i, j);
335 unsigned int writtenPixel = writtenTile.pixel( i, j);
336 qWarning() <<
"***** pixel" << i << j <<
"is off by" << (pixel - writtenPixel) <<
"pixel" << pixel <<
"writtenPixel" << writtenPixel;
337 QByteArray baPixel((
char*)&pixel,
sizeof(
unsigned int));
338 qWarning() <<
"pixel" << baPixel.size() <<
"0x" << baPixel.toHex();
339 QByteArray baWrittenPixel((
char*)&writtenPixel,
sizeof(
unsigned int));
340 qWarning() <<
"writtenPixel" << baWrittenPixel.size() <<
"0x" << baWrittenPixel.toHex();
349 percentCompleted = (int) ( 90 * (qreal)(createdTilesCount)
350 / (qreal)(totalTileCount) );
353 mDebug() <<
"percentCompleted" << percentCompleted;
354 emit progress( percentCompleted );
358 mDebug() <<
"tileLevel: " << maxTileLevel <<
" successfully created.";
360 tileLevel = maxTileLevel;
365 while( tileLevel > 0 ) {
368 int nmaxit = TileLoaderHelper::levelToRow( defaultLevelZeroRows, tileLevel );
370 for (
int n = 0; n < nmaxit; ++n ) {
371 QString dirName( d->m_targetDir
377 if ( !
QDir( dirName ).exists() )
380 int mmaxit = TileLoaderHelper::levelToColumn( defaultLevelZeroColumns, tileLevel );
381 for (
int m = 0; m < mmaxit; ++m ) {
383 if ( d->m_cancelled )
390 .
arg( d->m_tileFormat );
395 tileName = d->m_targetDir +
QString(
"%1/%2/%2_%3.%4")
396 .
arg( tileLevel + 1 )
399 .
arg( d->m_tileFormat );
400 QImage img_topleft( tileName );
402 tileName = d->m_targetDir +
QString(
"%1/%2/%2_%3.%4")
403 .
arg( tileLevel + 1 )
406 .
arg( d->m_tileFormat );
407 QImage img_topright( tileName );
409 tileName = d->m_targetDir +
QString(
"%1/%2/%2_%3.%4")
410 .
arg( tileLevel + 1 )
413 .
arg( d->m_tileFormat );
414 QImage img_bottomleft( tileName );
416 tileName = d->m_targetDir +
QString(
"%1/%2/%2_%3.%4")
417 .
arg( tileLevel + 1 )
420 .
arg( d->m_tileFormat );
421 QImage img_bottomright( tileName );
423 QSize const expectedSize( c_defaultTileSize, c_defaultTileSize );
424 if ( img_topleft.size() != expectedSize ||
425 img_topright.size() != expectedSize ||
426 img_bottomleft.size() != expectedSize ||
427 img_bottomright.size() != expectedSize ) {
428 mDebug() <<
"Tile write failure. Missing write permissions?";
429 emit progress( 100 );
432 QImage tile = img_topleft;
439 for ( uint y = 0; y < c_defaultTileSize / 2; ++y ) {
441 const uchar* srcLine = img_topleft.scanLine( 2 * y );
442 for ( uint x = 0; x < c_defaultTileSize / 2; ++x )
443 destLine[x] = srcLine[ 2*x ];
445 for ( uint y = 0; y < c_defaultTileSize / 2; ++y ) {
447 const uchar* srcLine = img_topright.scanLine( 2 * y );
448 for ( uint x = c_defaultTileSize / 2; x < c_defaultTileSize; ++x )
449 destLine[x] = srcLine[ 2 * ( x - c_defaultTileSize / 2 ) ];
451 for ( uint y = c_defaultTileSize / 2; y < c_defaultTileSize; ++y ) {
453 const uchar* srcLine = img_bottomleft.scanLine( 2 * ( y - c_defaultTileSize / 2 ) );
454 for ( uint x = 0; x < c_defaultTileSize / 2; ++x )
455 destLine[ x ] = srcLine[ 2 * x ];
457 for ( uint y = c_defaultTileSize / 2; y < c_defaultTileSize; ++y ) {
459 const uchar* srcLine = img_bottomright.scanLine( 2 * ( y - c_defaultTileSize/2 ) );
460 for ( uint x = c_defaultTileSize / 2; x < c_defaultTileSize; ++x )
461 destLine[x] = srcLine[ 2 * ( x - c_defaultTileSize / 2 ) ];
476 for ( uint y = 0; y < c_defaultTileSize / 2; ++y ) {
477 destLine = (QRgb*) tile.
scanLine( y );
478 const QRgb* srcLine = (QRgb*) img_topleft.scanLine( 2 * y );
479 for ( uint x = 0; x < c_defaultTileSize / 2; ++x )
480 destLine[x] = srcLine[ 2 * x ];
482 for ( uint y = 0; y < c_defaultTileSize / 2; ++y ) {
483 destLine = (QRgb*) tile.
scanLine( y );
484 const QRgb* srcLine = (QRgb*) img_topright.scanLine( 2 * y );
485 for ( uint x = c_defaultTileSize / 2; x < c_defaultTileSize; ++x )
486 destLine[x] = srcLine[ 2 * ( x - c_defaultTileSize / 2 ) ];
488 for ( uint y = c_defaultTileSize / 2; y < c_defaultTileSize; ++y ) {
489 destLine = (QRgb*) tile.
scanLine( y );
490 const QRgb* srcLine = (QRgb*) img_bottomleft.scanLine( 2 * ( y-c_defaultTileSize/2 ) );
491 for ( uint x = 0; x < c_defaultTileSize / 2; ++x )
492 destLine[x] = srcLine[ 2 * x ];
494 for ( uint y = c_defaultTileSize / 2; y < c_defaultTileSize; ++y ) {
495 destLine = (QRgb*) tile.
scanLine( y );
496 const QRgb* srcLine = (QRgb*) img_bottomright.scanLine( 2 * ( y-c_defaultTileSize / 2 ) );
497 for ( uint x = c_defaultTileSize / 2; x < c_defaultTileSize; ++x )
498 destLine[x] = srcLine[ 2*( x-c_defaultTileSize / 2 ) ];
506 bool ok = tile.
save(newTileName, d->m_tileFormat.toLatin1().data(), d->m_tileFormat ==
QLatin1String(
"jpg") ? 100 : d->m_tileQuality);
508 mDebug() <<
"Error while writing Tile: " << newTileName;
511 percentCompleted = (int) ( 90 * (qreal)(createdTilesCount)
512 / (qreal)(totalTileCount) );
515 emit progress( percentCompleted );
516 mDebug() <<
"percentCompleted" << percentCompleted;
519 mDebug() <<
"tileLevel: " << tileLevel <<
" successfully created.";
521 mDebug() <<
"Tile creation completed.";
523 if (d->m_tileFormat ==
QLatin1String(
"jpg") && d->m_tileQuality != 100) {
526 int savedTilesCount = 0;
529 while ( tileLevel <= maxTileLevel ) {
530 int nmaxit = TileLoaderHelper::levelToRow( defaultLevelZeroRows, tileLevel );
531 for (
int n = 0; n < nmaxit; ++n) {
532 int mmaxit = TileLoaderHelper::levelToColumn( defaultLevelZeroColumns, tileLevel );
533 for (
int m = 0; m < mmaxit; ++m) {
535 if ( d->m_cancelled )
540 tileName = d->m_targetDir +
QString(
"%1/%2/%2_%3.%4")
544 .
arg( d->m_tileFormat );
549 ok = tile.
save( tileName, d->m_tileFormat.toLatin1().data(), d->m_tileQuality );
552 mDebug() <<
"Error while writing Tile: " << tileName;
554 percentCompleted = 90 + (int)( 9 * (qreal)(savedTilesCount)
555 / (qreal)(totalTileCount) );
556 emit progress( percentCompleted );
557 mDebug() <<
"percentCompleted" << percentCompleted;
567 percentCompleted = 100;
568 emit progress( percentCompleted );
570 mDebug() <<
"percentCompleted: " << percentCompleted;
573 void TileCreator::setTileFormat(
const QString& format)
575 d->m_tileFormat = format;
578 QString TileCreator::tileFormat()
const
580 return d->m_tileFormat;
583 void TileCreator::setTileQuality(
int quality)
585 d->m_tileQuality = quality;
588 int TileCreator::tileQuality()
const
590 return d->m_tileQuality;
593 void TileCreator::setResume(
bool resume)
595 d->m_resume = resume;
598 bool TileCreator::resume()
const
603 void TileCreator::setVerifyExactResult(
bool verify)
608 bool TileCreator::verifyExactResult()
const
616 #include "moc_TileCreator.cpp"