Marble

TileCreator.cpp
1// SPDX-FileCopyrightText: 2006-2007 Torsten Rahn <tackat@kde.org>
2// SPDX-FileCopyrightText: 2007-2008 Inge Wallin <ingwa@kde.org>
3// SPDX-FileCopyrightText: 2011 Niko Sams <niko.sams@gmail.com>
4//
5// SPDX-License-Identifier: LGPL-2.1-or-later
6
7#include "TileCreator.h"
8
9#include <cmath>
10
11#include <QDir>
12#include <QRect>
13#include <QSize>
14#include <QVector>
15#include <QApplication>
16#include <QImage>
17#include <QPainter>
18
19#include "MarbleGlobal.h"
20#include "MarbleDirs.h"
21#include "MarbleDebug.h"
22#include "TileLoaderHelper.h"
23
24namespace Marble
25{
26
27class TileCreatorPrivate
28{
29 public:
30 TileCreatorPrivate( TileCreatorSource *source,
31 const QString& dem, const QString& targetDir=QString() )
32 : m_dem( dem ),
33 m_targetDir( targetDir ),
34 m_cancelled( false ),
35 m_tileFormat( "jpg" ),
36 m_resume( false ),
37 m_verify( false ),
38 m_source( source )
39 {
40 if (m_dem == QLatin1String("true")) {
41 m_tileQuality = 70;
42 } else {
43 m_tileQuality = 85;
44 }
45 }
46
47 ~TileCreatorPrivate()
48 {
49 delete m_source;
50 }
51
52 public:
53 QString m_dem;
54 QString m_targetDir;
55 bool m_cancelled;
56 QString m_tileFormat;
57 int m_tileQuality;
58 bool m_resume;
59 bool m_verify;
60
61 TileCreatorSource *m_source;
62};
63
64class TileCreatorSourceImage : public TileCreatorSource
65{
66public:
67 explicit TileCreatorSourceImage( const QString &sourcePath )
68 : m_sourceImage( QImage( sourcePath ) ),
69 m_cachedRowNum( -1 )
70 {
71 }
72
73 QSize fullImageSize() const override
74 {
75 if ( m_sourceImage.size().width() > 21600 || m_sourceImage.height() > 10800 ) {
76 qDebug("Install map too large!");
77 return QSize();
78 }
79 return m_sourceImage.size();
80 }
81
82 QImage tile(int n, int m, int maxTileLevel) override
83 {
84 int mmax = TileLoaderHelper::levelToColumn( defaultLevelZeroColumns, maxTileLevel );
85 int nmax = TileLoaderHelper::levelToRow( defaultLevelZeroRows, maxTileLevel );
86
87 int imageHeight = m_sourceImage.height();
88 int imageWidth = m_sourceImage.width();
89
90 // If the image size of the image source does not match the expected
91 // geometry we need to smooth-scale the image in advance to match
92 // the required size
93 bool needsScaling = ( imageWidth != 2 * nmax * (int)( c_defaultTileSize )
94 || imageHeight != nmax * (int)( c_defaultTileSize ) );
95
96 if ( needsScaling )
97 mDebug() << "Image Size doesn't match 2*n*TILEWIDTH x n*TILEHEIGHT geometry. Scaling ...";
98
99 int stdImageWidth = 2 * nmax * c_defaultTileSize;
100 if ( stdImageWidth == 0 )
101 stdImageWidth = 2 * c_defaultTileSize;
102
103 int stdImageHeight = nmax * c_defaultTileSize;
104 if ( stdImageWidth != imageWidth ) {
105 mDebug() <<
106 QString( "TileCreator::createTiles() The size of the final image will measure %1 x %2 pixels").arg(stdImageWidth).arg(stdImageHeight);
107 }
108
109 QImage row;
110
111 if ( m_cachedRowNum == n ) {
112
113 row = m_rowCache;
114
115 } else {
116
117 QRect sourceRowRect( 0, (int)( (qreal)( n * imageHeight ) / (qreal)( nmax )),
118 imageWidth,(int)( (qreal)( imageHeight ) / (qreal)( nmax ) ) );
119
120
121 row = m_sourceImage.copy( sourceRowRect );
122
123 if ( needsScaling ) {
124 // Pick the current row and smooth scale it
125 // to make it match the expected size
126 QSize destSize( stdImageWidth, c_defaultTileSize );
127 row = row.scaled( destSize,
130 }
131
132 m_cachedRowNum = n;
133 m_rowCache = row;
134 }
135
136 if ( row.isNull() ) {
137 mDebug() << "Read-Error! Null QImage!";
138 return QImage();
139 }
140
141 QImage tile = row.copy( m * stdImageWidth / mmax, 0, c_defaultTileSize, c_defaultTileSize );
142
143 return tile;
144 }
145
146private:
147 QImage m_sourceImage;
148
149 QImage m_rowCache;
150 int m_cachedRowNum;
151};
152
153
154TileCreator::TileCreator(const QString& sourceDir, const QString& installMap,
155 const QString& dem, const QString& targetDir)
156 : QThread(nullptr),
157 d( new TileCreatorPrivate( nullptr, dem, targetDir ) )
158
159{
160 mDebug() << "Prefix: " << sourceDir
161 << "installmap:" << installMap;
162
163 QString sourcePath;
164
165 // If the sourceDir starts with a '/' assume an absolute path.
166 // Otherwise assume a relative marble data path
167 if ( QDir::isAbsolutePath( sourceDir ) ) {
168 sourcePath = sourceDir + QLatin1Char('/') + installMap;
169 mDebug() << "Trying absolute path*:" << sourcePath;
170 }
171 else {
172 sourcePath = MarbleDirs::path(QLatin1String("maps/") + sourceDir + QLatin1Char('/') + installMap);
173 mDebug() << "Trying relative path*:"
174 << QLatin1String("maps/") + sourceDir + QLatin1Char('/') + installMap;
175 }
176
177 mDebug() << "Creating tiles from*: " << sourcePath;
178
179 d->m_source = new TileCreatorSourceImage( sourcePath );
180
181 if ( d->m_targetDir.isNull() )
182 d->m_targetDir = MarbleDirs::localPath() + QLatin1String("/maps/")
183 + sourcePath.section(QLatin1Char('/'), -3, -2) + QLatin1Char('/');
184
185 setTerminationEnabled( true );
186}
187
188TileCreator::TileCreator( TileCreatorSource* source, const QString& dem, const QString& targetDir )
189 : QThread(nullptr),
190 d( new TileCreatorPrivate( source, dem, targetDir ) )
191{
192 setTerminationEnabled( true );
193}
194
195TileCreator::~TileCreator()
196{
197 delete d;
198}
199
200void TileCreator::cancelTileCreation()
201{
202 d->m_cancelled = true;
203}
204
205void TileCreator::run()
206{
207 if (d->m_resume && d->m_tileFormat == QLatin1String("jpg") && d->m_tileQuality != 100) {
208 qWarning() << "Resuming jpegs is only supported with tileQuality 100";
209 return;
210 }
211
212 if (!d->m_targetDir.endsWith(QLatin1Char('/')))
213 d->m_targetDir += QLatin1Char('/');
214
215 mDebug() << "Installing tiles to: " << d->m_targetDir;
216
217 QVector<QRgb> grayScalePalette;
218 for ( int cnt = 0; cnt <= 255; ++cnt ) {
219 grayScalePalette.insert(cnt, qRgb(cnt, cnt, cnt));
220 }
221
222 QSize fullImageSize = d->m_source->fullImageSize();
223 int imageWidth = fullImageSize.width();
224 int imageHeight = fullImageSize.height();
225
226 mDebug() << QString( "TileCreator::createTiles() image dimensions %1 x %2").arg(imageWidth).arg(imageHeight);
227
228 if ( imageWidth < 1 || imageHeight < 1 ) {
229 qDebug("Invalid imagemap!");
230 return;
231 }
232
233 // Calculating Maximum Tile Level
234 float approxMaxTileLevel = std::log( imageWidth / ( 2.0 * c_defaultTileSize ) ) / std::log( 2.0 );
235
236 int maxTileLevel = 0;
237 if ( approxMaxTileLevel == int( approxMaxTileLevel ) )
238 maxTileLevel = static_cast<int>( approxMaxTileLevel );
239 else
240 maxTileLevel = static_cast<int>( approxMaxTileLevel + 1 );
241
242 if ( maxTileLevel < 0 ) {
243 mDebug()
244 << QString( "TileCreator::createTiles(): Invalid Maximum Tile Level: %1" )
245 .arg( maxTileLevel );
246 }
247 mDebug() << "Maximum Tile Level: " << maxTileLevel;
248
249
250 if ( !QDir( d->m_targetDir ).exists() )
251 ( QDir::root() ).mkpath( d->m_targetDir );
252
253 // Counting total amount of tiles to be generated for the progressbar
254 // to prevent compiler warnings this var should
255 // match the type of maxTileLevel
256 int tileLevel = 0;
257 int totalTileCount = 0;
258
259 while ( tileLevel <= maxTileLevel ) {
260 totalTileCount += ( TileLoaderHelper::levelToRow( defaultLevelZeroRows, tileLevel )
261 * TileLoaderHelper::levelToColumn( defaultLevelZeroColumns, tileLevel ) );
262 tileLevel++;
263 }
264
265 mDebug() << totalTileCount << " tiles to be created in total.";
266
267 int mmax = TileLoaderHelper::levelToColumn( defaultLevelZeroColumns, maxTileLevel );
268 int nmax = TileLoaderHelper::levelToRow( defaultLevelZeroRows, maxTileLevel );
269
270 // Loading each row at highest spatial resolution and cropping tiles
271 int percentCompleted = 0;
272 int createdTilesCount = 0;
273 QString tileName;
274
275 // Creating directory structure for the highest level
276 QString dirName( d->m_targetDir
277 + QString("%1").arg(maxTileLevel) );
278 if ( !QDir( dirName ).exists() )
279 ( QDir::root() ).mkpath( dirName );
280
281 for ( int n = 0; n < nmax; ++n ) {
282 QString dirName( d->m_targetDir
283 + QString("%1/%2").arg(maxTileLevel).arg(n, tileDigits, 10, QLatin1Char('0')));
284 if ( !QDir( dirName ).exists() )
285 ( QDir::root() ).mkpath( dirName );
286 }
287
288 for ( int n = 0; n < nmax; ++n ) {
289
290 for ( int m = 0; m < mmax; ++m ) {
291
292 mDebug() << "** tile" << m << "x" << n;
293
294 if ( d->m_cancelled )
295 return;
296
297 tileName = d->m_targetDir + QString("%1/%2/%2_%3.%4")
298 .arg( maxTileLevel )
299 .arg(n, tileDigits, 10, QLatin1Char('0'))
300 .arg(m, tileDigits, 10, QLatin1Char('0'))
301 .arg( d->m_tileFormat );
302
303 if ( QFile::exists( tileName ) && d->m_resume ) {
304
305 //mDebug() << tileName << "exists already";
306
307 } else {
308
309 QImage tile = d->m_source->tile( n, m, maxTileLevel );
310
311 if ( tile.isNull() ) {
312 mDebug() << "Read-Error! Null QImage!";
313 return;
314 }
315
316 if (d->m_dem == QLatin1String("true")) {
318 grayScalePalette,
320 }
321
322 bool ok = tile.save(tileName, d->m_tileFormat.toLatin1().data(), d->m_tileFormat == QLatin1String("jpg") ? 100 : d->m_tileQuality);
323 if ( !ok )
324 mDebug() << "Error while writing Tile: " << tileName;
325
326 mDebug() << tileName << "size" << QFile( tileName ).size();
327
328 if ( d->m_verify ) {
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();
341 Q_ASSERT(false);
342 }
343 }
344 }
345 }
346
347 }
348
349 percentCompleted = (int) ( 90 * (qreal)(createdTilesCount)
350 / (qreal)(totalTileCount) );
351 createdTilesCount++;
352
353 mDebug() << "percentCompleted" << percentCompleted;
354 emit progress( percentCompleted );
355 }
356 }
357
358 mDebug() << "tileLevel: " << maxTileLevel << " successfully created.";
359
360 tileLevel = maxTileLevel;
361
362 // Now that we have the tiles at the highest resolution lets build
363 // them together four by four.
364
365 while( tileLevel > 0 ) {
366 tileLevel--;
367
368 int nmaxit = TileLoaderHelper::levelToRow( defaultLevelZeroRows, tileLevel );
369
370 for ( int n = 0; n < nmaxit; ++n ) {
371 QString dirName( d->m_targetDir
372 + QString("%1/%2")
373 .arg(tileLevel)
374 .arg(n, tileDigits, 10, QLatin1Char('0')));
375
376 // mDebug() << "dirName: " << dirName;
377 if ( !QDir( dirName ).exists() )
378 ( QDir::root() ).mkpath( dirName );
379
380 int mmaxit = TileLoaderHelper::levelToColumn( defaultLevelZeroColumns, tileLevel );
381 for ( int m = 0; m < mmaxit; ++m ) {
382
383 if ( d->m_cancelled )
384 return;
385
386 QString newTileName = d->m_targetDir + QString("%1/%2/%2_%3.%4")
387 .arg( tileLevel )
388 .arg(n, tileDigits, 10, QLatin1Char('0'))
389 .arg(m, tileDigits, 10, QLatin1Char('0'))
390 .arg( d->m_tileFormat );
391
392 if ( QFile::exists( newTileName ) && d->m_resume ) {
393 //mDebug() << newTileName << "exists already";
394 } else {
395 tileName = d->m_targetDir + QString("%1/%2/%2_%3.%4")
396 .arg( tileLevel + 1 )
397 .arg(2*n, tileDigits, 10, QLatin1Char('0'))
398 .arg(2*m, tileDigits, 10, QLatin1Char('0'))
399 .arg( d->m_tileFormat );
400 QImage img_topleft( tileName );
401
402 tileName = d->m_targetDir + QString("%1/%2/%2_%3.%4")
403 .arg( tileLevel + 1 )
404 .arg(2*n, tileDigits, 10, QLatin1Char('0'))
405 .arg(2*m+1, tileDigits, 10, QLatin1Char('0'))
406 .arg( d->m_tileFormat );
407 QImage img_topright( tileName );
408
409 tileName = d->m_targetDir + QString("%1/%2/%2_%3.%4")
410 .arg( tileLevel + 1 )
411 .arg(2*n+1, tileDigits, 10, QLatin1Char('0'))
412 .arg(2*m, tileDigits, 10, QLatin1Char('0'))
413 .arg( d->m_tileFormat );
414 QImage img_bottomleft( tileName );
415
416 tileName = d->m_targetDir + QString("%1/%2/%2_%3.%4")
417 .arg( tileLevel + 1 )
418 .arg(2*n+1, tileDigits, 10, QLatin1Char('0'))
419 .arg(2*m+1, tileDigits, 10, QLatin1Char('0'))
420 .arg( d->m_tileFormat );
421 QImage img_bottomright( tileName );
422
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 );
430 return;
431 }
432 QImage tile = img_topleft;
433
434 if (d->m_dem == QLatin1String("true")) {
435
436 tile.setColorTable( grayScalePalette );
437 uchar* destLine;
438
439 for ( uint y = 0; y < c_defaultTileSize / 2; ++y ) {
440 destLine = tile.scanLine( 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 ];
444 }
445 for ( uint y = 0; y < c_defaultTileSize / 2; ++y ) {
446 destLine = tile.scanLine( 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 ) ];
450 }
451 for ( uint y = c_defaultTileSize / 2; y < c_defaultTileSize; ++y ) {
452 destLine = tile.scanLine( 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 ];
456 }
457 for ( uint y = c_defaultTileSize / 2; y < c_defaultTileSize; ++y ) {
458 destLine = tile.scanLine( 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 ) ];
462 }
463 }
464 else {
465
466 // tile.depth() != 8
467
468 img_topleft = img_topleft.convertToFormat( QImage::Format_ARGB32 );
469 img_topright = img_topright.convertToFormat( QImage::Format_ARGB32 );
470 img_bottomleft = img_bottomleft.convertToFormat( QImage::Format_ARGB32 );
471 img_bottomright = img_bottomright.convertToFormat( QImage::Format_ARGB32 );
472 tile = img_topleft;
473
474 QRgb* destLine;
475
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 ];
481 }
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 ) ];
487 }
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 ];
493 }
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 ) ];
499 }
500 }
501
502 mDebug() << newTileName;
503
504 // Saving at 100% JPEG quality to have a high-quality
505 // version to create the remaining needed tiles from.
506 bool ok = tile.save(newTileName, d->m_tileFormat.toLatin1().data(), d->m_tileFormat == QLatin1String("jpg") ? 100 : d->m_tileQuality);
507 if ( ! ok )
508 mDebug() << "Error while writing Tile: " << newTileName;
509 }
510
511 percentCompleted = (int) ( 90 * (qreal)(createdTilesCount)
512 / (qreal)(totalTileCount) );
513 createdTilesCount++;
514
515 emit progress( percentCompleted );
516 mDebug() << "percentCompleted" << percentCompleted;
517 }
518 }
519 mDebug() << "tileLevel: " << tileLevel << " successfully created.";
520 }
521 mDebug() << "Tile creation completed.";
522
523 if (d->m_tileFormat == QLatin1String("jpg") && d->m_tileQuality != 100) {
524
525 // Applying correct lower JPEG compression now that we created all tiles
526 int savedTilesCount = 0;
527
528 tileLevel = 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) {
534
535 if ( d->m_cancelled )
536 return;
537
538 savedTilesCount++;
539
540 tileName = d->m_targetDir + QString("%1/%2/%2_%3.%4")
541 .arg( tileLevel )
542 .arg(n, tileDigits, 10, QLatin1Char('0'))
543 .arg(m, tileDigits, 10, QLatin1Char('0'))
544 .arg( d->m_tileFormat );
545 QImage tile( tileName );
546
547 bool ok;
548
549 ok = tile.save( tileName, d->m_tileFormat.toLatin1().data(), d->m_tileQuality );
550
551 if ( !ok )
552 mDebug() << "Error while writing Tile: " << tileName;
553 // Don't exceed 99% as this would cancel the thread unexpectedly
554 percentCompleted = 90 + (int)( 9 * (qreal)(savedTilesCount)
555 / (qreal)(totalTileCount) );
556 emit progress( percentCompleted );
557 mDebug() << "percentCompleted" << percentCompleted;
558 //mDebug() << "Saving Tile #" << savedTilesCount
559 // << " of " << totalTileCount
560 // << " Percent: " << percentCompleted;
561 }
562 }
563 tileLevel++;
564 }
565 }
566
567 percentCompleted = 100;
568 emit progress( percentCompleted );
569
570 mDebug() << "percentCompleted: " << percentCompleted;
571}
572
573void TileCreator::setTileFormat(const QString& format)
574{
575 d->m_tileFormat = format;
576}
577
578QString TileCreator::tileFormat() const
579{
580 return d->m_tileFormat;
581}
582
583void TileCreator::setTileQuality(int quality)
584{
585 d->m_tileQuality = quality;
586}
587
588int TileCreator::tileQuality() const
589{
590 return d->m_tileQuality;
591}
592
593void TileCreator::setResume(bool resume)
594{
595 d->m_resume = resume;
596}
597
598bool TileCreator::resume() const
599{
600 return d->m_resume;
601}
602
603void TileCreator::setVerifyExactResult(bool verify)
604{
605 d->m_verify = verify;
606}
607
608bool TileCreator::verifyExactResult() const
609{
610 return d->m_verify;
611}
612
613
614}
615
616#include "moc_TileCreator.cpp"
KIOCORE_EXPORT MkpathJob * mkpath(const QUrl &url, const QUrl &baseUrl=QUrl(), JobFlags flags=DefaultFlags)
Binds a QML item to a specific geodetic location in screen coordinates.
bool exists() const const
bool isAbsolutePath(const QString &path)
QDir root()
bool exists() const const
virtual qint64 size() const const override
QImage convertToFormat(Format format, Qt::ImageConversionFlags flags) &&
QImage copy(const QRect &rectangle) const const
int height() const const
bool isNull() const const
QRgb pixel(const QPoint &position) const const
bool save(QIODevice *device, const char *format, int quality) const const
QImage scaled(const QSize &size, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const const
uchar * scanLine(int i)
void setColorTable(const QList< QRgb > &colors)
QSize size() const const
int width() const const
iterator insert(const_iterator before, parameter_type value)
int height() const const
int width() const const
QString arg(Args &&... args) const const
QString section(QChar sep, qsizetype start, qsizetype end, SectionFlags flags) const const
IgnoreAspectRatio
ThresholdDither
SmoothTransformation
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jun 21 2024 12:00:07 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.