Marble

MergedLayerDecorator.cpp
1// SPDX-FileCopyrightText: 2008 David Roberts <dvdr18@gmail.com>
2// SPDX-FileCopyrightText: 2009 Jens-Michael Hoffmann <jensmh@gmx.de>
3// SPDX-FileCopyrightText: 2011 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
4//
5// SPDX-License-Identifier: LGPL-2.1-or-later
6
7#include "MergedLayerDecorator.h"
8
9#include "GeoDataGroundOverlay.h"
10#include "GeoSceneTextureTileDataset.h"
11#include "ImageF.h"
12#include "MarbleDebug.h"
13#include "MarbleMath.h"
14#include "RenderState.h"
15#include "StackedTile.h"
16#include "SunLocator.h"
17#include "TextureTile.h"
18#include "TileLoader.h"
19#include "TileLoaderHelper.h"
20#include "blendings/Blending.h"
21#include "blendings/BlendingFactory.h"
22
23#include "GeoDataCoordinates.h"
24
25#include <QPainter>
26#include <QPainterPath>
27#include <QPointer>
28
29using namespace Marble;
30
31class Q_DECL_HIDDEN MergedLayerDecorator::Private
32{
33public:
34 Private(TileLoader *tileLoader, const SunLocator *sunLocator);
35
36 static int maxDivisor(int maximum, int fullLength);
37
38 StackedTile *createTile(const QList<QSharedPointer<TextureTile>> &tiles) const;
39
40 void renderGroundOverlays(QImage *tileImage, const QList<QSharedPointer<TextureTile>> &tiles) const;
41 void paintSunShading(QImage *tileImage, const TileId &id) const;
42 void paintTileId(QImage *tileImage, const TileId &id) const;
43
44 void detectMaxTileLevel();
45 QList<const GeoSceneTextureTileDataset *> findRelevantTextureLayers(const TileId &stackedTileId) const;
46
47 TileLoader *const m_tileLoader;
48 const SunLocator *const m_sunLocator;
49 BlendingFactory m_blendingFactory;
52 int m_maxTileLevel;
53 QString m_themeId;
54 int m_levelZeroColumns;
55 int m_levelZeroRows;
56 bool m_showSunShading;
57 bool m_showCityLights;
58 bool m_showTileId;
59};
60
61MergedLayerDecorator::Private::Private(TileLoader *tileLoader, const SunLocator *sunLocator)
62 : m_tileLoader(tileLoader)
63 , m_sunLocator(sunLocator)
64 , m_blendingFactory(sunLocator)
65 , m_textureLayers()
66 , m_maxTileLevel(0)
67 , m_themeId()
68 , m_levelZeroColumns(0)
69 , m_levelZeroRows(0)
70 , m_showSunShading(false)
71 , m_showCityLights(false)
72 , m_showTileId(false)
73{
74}
75
76MergedLayerDecorator::MergedLayerDecorator(TileLoader *const tileLoader, const SunLocator *sunLocator)
77 : d(new Private(tileLoader, sunLocator))
78{
79}
80
81MergedLayerDecorator::~MergedLayerDecorator()
82{
83 delete d;
84}
85
86void MergedLayerDecorator::setTextureLayers(const QList<const GeoSceneTextureTileDataset *> &textureLayers)
87{
88 if (textureLayers.count() > 0) {
89 const GeoSceneTileDataset *const firstTexture = textureLayers.at(0);
90 d->m_levelZeroColumns = firstTexture->levelZeroColumns();
91 d->m_levelZeroRows = firstTexture->levelZeroRows();
92 d->m_blendingFactory.setLevelZeroLayout(d->m_levelZeroColumns, d->m_levelZeroRows);
93 d->m_themeId = QLatin1StringView("maps/") + firstTexture->sourceDir();
94 }
95
96 d->m_textureLayers = textureLayers;
97
98 d->detectMaxTileLevel();
99}
100
101void MergedLayerDecorator::updateGroundOverlays(const QList<const GeoDataGroundOverlay *> &groundOverlays)
102{
103 d->m_groundOverlays = groundOverlays;
104}
105
106int MergedLayerDecorator::textureLayersSize() const
107{
108 return d->m_textureLayers.size();
109}
110
111int MergedLayerDecorator::maximumTileLevel() const
112{
113 return d->m_maxTileLevel;
114}
115
116int MergedLayerDecorator::tileColumnCount(int level) const
117{
118 Q_ASSERT(!d->m_textureLayers.isEmpty());
119
120 const int levelZeroColumns = d->m_textureLayers.at(0)->levelZeroColumns();
121
122 return TileLoaderHelper::levelToColumn(levelZeroColumns, level);
123}
124
125int MergedLayerDecorator::tileRowCount(int level) const
126{
127 Q_ASSERT(!d->m_textureLayers.isEmpty());
128
129 const int levelZeroRows = d->m_textureLayers.at(0)->levelZeroRows();
130
131 return TileLoaderHelper::levelToRow(levelZeroRows, level);
132}
133
134const GeoSceneAbstractTileProjection *MergedLayerDecorator::tileProjection() const
135{
136 Q_ASSERT(!d->m_textureLayers.isEmpty());
137
138 return d->m_textureLayers.at(0)->tileProjection();
139}
140
141QSize MergedLayerDecorator::tileSize() const
142{
143 Q_ASSERT(!d->m_textureLayers.isEmpty());
144
145 return d->m_textureLayers.at(0)->tileSize();
146}
147
148StackedTile *MergedLayerDecorator::Private::createTile(const QList<QSharedPointer<TextureTile>> &tiles) const
149{
150 Q_ASSERT(!tiles.isEmpty());
151
152 const TileId firstId = tiles.first()->id();
153 const TileId id(0, firstId.zoomLevel(), firstId.x(), firstId.y());
154
155 // Image for blending all the texture tiles on it
156 QImage resultImage;
157
158 // if there are more than one active texture layers, we have to convert the
159 // result tile into QImage::Format_ARGB32_Premultiplied to make blending possible
160 const bool withConversion = tiles.count() > 1 || m_showSunShading || m_showTileId || !m_groundOverlays.isEmpty();
161 for (const QSharedPointer<TextureTile> &tile : tiles) {
162 // Image blending. If there are several images in the same tile (like clouds
163 // or hillshading images over the map) blend them all into only one image
164
165 const Blending *const blending = tile->blending();
166 if (blending) {
167 mDebug() << "blending";
168
169 if (resultImage.isNull()) {
170 resultImage = QImage(tile->image()->size(), QImage::Format_ARGB32_Premultiplied);
171 }
172
173 blending->blend(&resultImage, tile.data());
174 } else {
175 mDebug() << "no blending defined => copying top over bottom image";
176 if (withConversion) {
177 resultImage = tile->image()->convertToFormat(QImage::Format_ARGB32_Premultiplied);
178 } else {
179 resultImage = tile->image()->copy();
180 }
181 }
182 }
183
184 renderGroundOverlays(&resultImage, tiles);
185
186 if (m_showSunShading && !m_showCityLights) {
187 paintSunShading(&resultImage, id);
188 }
189
190 if (m_showTileId) {
191 paintTileId(&resultImage, id);
192 }
193
194 return new StackedTile(id, resultImage, tiles);
195}
196
197void MergedLayerDecorator::Private::renderGroundOverlays(QImage *tileImage, const QList<QSharedPointer<TextureTile>> &tiles) const
198{
199 /* All tiles are covering the same area. Pick one. */
200 const TileId tileId = tiles.first()->id();
201
202 const GeoDataLatLonBox tileLatLonBox = findRelevantTextureLayers(tileId).first()->tileProjection()->geoCoordinates(tileId);
203
204 /* Map the ground overlay to the image. */
205 for (int i = 0; i < m_groundOverlays.size(); ++i) {
206 const GeoDataGroundOverlay *overlay = m_groundOverlays.at(i);
207 if (!overlay->isGloballyVisible()) {
208 continue;
209 }
210
211 const GeoDataLatLonBox overlayLatLonBox = overlay->latLonBox();
212
213 if (!tileLatLonBox.intersects(overlayLatLonBox.toCircumscribedRectangle())) {
214 continue;
215 }
216
217 const qreal pixelToLat = tileLatLonBox.height() / tileImage->height();
218 const qreal pixelToLon = tileLatLonBox.width() / tileImage->width();
219
220 const qreal latToPixel = overlay->icon().height() / overlayLatLonBox.height();
221 const qreal lonToPixel = overlay->icon().width() / overlayLatLonBox.width();
222
223 const qreal global_height = tileImage->height() * TileLoaderHelper::levelToRow(m_levelZeroRows, tileId.zoomLevel());
224 const qreal pixel2Rad = M_PI / global_height;
225 const qreal rad2Pixel = global_height / M_PI;
226
227 qreal latPixelPosition = rad2Pixel / 2 * gdInv(tileLatLonBox.north());
228 const bool isMercatorTileProjection = (m_textureLayers.at(0)->tileProjectionType() == GeoSceneAbstractTileProjection::Mercator);
229
230 for (int y = 0; y < tileImage->height(); ++y) {
231 QRgb *scanLine = (QRgb *)(tileImage->scanLine(y));
232
233 const qreal lat = isMercatorTileProjection ? gd(2 * (latPixelPosition - y) * pixel2Rad) : tileLatLonBox.north() - y * pixelToLat;
234
235 for (int x = 0; x < tileImage->width(); ++x, ++scanLine) {
236 qreal lon = GeoDataCoordinates::normalizeLon(tileLatLonBox.west() + x * pixelToLon);
237
238 GeoDataCoordinates coords(lon, lat);
239 GeoDataCoordinates rotatedCoords(coords);
240
241 if (overlay->latLonBox().rotation() != 0) {
242 // Possible TODO: Make this faster by creating the axisMatrix beforehand
243 // and just call Quaternion::rotateAroundAxis(const matrix &m) here.
244 rotatedCoords = coords.rotateAround(overlayLatLonBox.center(), -overlay->latLonBox().rotation());
245 }
246
247 // TODO: The rotated latLonBox is bigger. We need to take this into account.
248 // (Currently the GroundOverlay sometimes gets clipped because of that)
249 if (overlay->latLonBox().contains(rotatedCoords)) {
250 qreal px = GeoDataLatLonBox::width(rotatedCoords.longitude(), overlayLatLonBox.west()) * lonToPixel;
251 qreal py =
252 (qreal)(overlay->icon().height()) - (GeoDataLatLonBox::height(rotatedCoords.latitude(), overlayLatLonBox.south()) * latToPixel) - 1;
253
254 if (px >= 0 && px < overlay->icon().width() && py >= 0 && py < overlay->icon().height()) {
255 int alpha = qAlpha(overlay->icon().pixel(px, py));
256 if (alpha != 0) {
257 QRgb result = ImageF::pixelF(overlay->icon(), px, py);
258
259 if (alpha == 255) {
260 *scanLine = result;
261 } else {
262 *scanLine = qRgb((alpha * qRed(result) + (255 - alpha) * qRed(*scanLine)) / 255,
263 (alpha * qGreen(result) + (255 - alpha) * qGreen(*scanLine)) / 255,
264 (alpha * qBlue(result) + (255 - alpha) * qBlue(*scanLine)) / 255);
265 }
266 }
267 }
268 }
269 }
270 }
271 }
272}
273
274StackedTile *MergedLayerDecorator::loadTile(const TileId &stackedTileId)
275{
276 const QList<const GeoSceneTextureTileDataset *> textureLayers = d->findRelevantTextureLayers(stackedTileId);
278 tiles.reserve(textureLayers.size());
279
280 for (const GeoSceneTextureTileDataset *layer : textureLayers) {
281 const TileId tileId(layer->sourceDir(), stackedTileId.zoomLevel(), stackedTileId.x(), stackedTileId.y());
282
283 mDebug() << layer->sourceDir() << tileId << layer->tileSize() << layer->fileFormat();
284
285 // Blending (how to merge the images into an only image)
286 const Blending *blending = d->m_blendingFactory.findBlending(layer->blending());
287 if (blending == nullptr && !layer->blending().isEmpty()) {
288 mDebug() << "could not find blending" << layer->blending();
289 }
290
291 const GeoSceneTextureTileDataset *const textureLayer = static_cast<const GeoSceneTextureTileDataset *>(layer);
292 const QImage tileImage = d->m_tileLoader->loadTileImage(textureLayer, tileId, DownloadBrowse);
293
294 QSharedPointer<TextureTile> tile(new TextureTile(tileId, tileImage, blending));
295 tiles.append(tile);
296 }
297
298 Q_ASSERT(!tiles.isEmpty());
299
300 return d->createTile(tiles);
301}
302
303RenderState MergedLayerDecorator::renderState(const TileId &stackedTileId) const
304{
305 QString const nameTemplate = QStringLiteral("Tile %1/%2/%3");
306 RenderState state(nameTemplate.arg(stackedTileId.zoomLevel()).arg(stackedTileId.x()).arg(stackedTileId.y()));
307 const QList<const GeoSceneTextureTileDataset *> textureLayers = d->findRelevantTextureLayers(stackedTileId);
308 for (const GeoSceneTextureTileDataset *layer : textureLayers) {
309 const TileId tileId(layer->sourceDir(), stackedTileId.zoomLevel(), stackedTileId.x(), stackedTileId.y());
310 RenderStatus tileStatus = Complete;
311 switch (TileLoader::tileStatus(layer, tileId)) {
312 case TileLoader::Available:
313 tileStatus = Complete;
314 break;
315 case TileLoader::Expired:
316 tileStatus = WaitingForUpdate;
317 break;
318 case TileLoader::Missing:
319 tileStatus = WaitingForData;
320 break;
321 }
322
323 state.addChild(RenderState(layer->name(), tileStatus));
324 }
325
326 return state;
327}
328
329bool MergedLayerDecorator::hasTextureLayer() const
330{
331 return !d->m_textureLayers.isEmpty();
332}
333
334StackedTile *MergedLayerDecorator::updateTile(const StackedTile &stackedTile, const TileId &tileId, const QImage &tileImage)
335{
336 Q_ASSERT(!tileImage.isNull());
337
338 d->detectMaxTileLevel();
339
340 QList<QSharedPointer<TextureTile>> tiles = stackedTile.tiles();
341
342 for (int i = 0; i < tiles.count(); ++i) {
343 if (tiles[i]->id() == tileId) {
344 const Blending *blending = tiles[i]->blending();
345
346 tiles[i] = QSharedPointer<TextureTile>(new TextureTile(tileId, tileImage, blending));
347 }
348 }
349
350 return d->createTile(tiles);
351}
352
353void MergedLayerDecorator::downloadStackedTile(const TileId &id, DownloadUsage usage)
354{
355 const QList<const GeoSceneTextureTileDataset *> textureLayers = d->findRelevantTextureLayers(id);
356
357 for (const GeoSceneTextureTileDataset *textureLayer : textureLayers) {
358 if (textureLayer->tileLevels().isEmpty() || textureLayer->tileLevels().contains(id.zoomLevel())) {
359 if (TileLoader::tileStatus(textureLayer, id) != TileLoader::Available || usage == DownloadBrowse) {
360 d->m_tileLoader->downloadTile(textureLayer, id, usage);
361 }
362 }
363 }
364}
365
366void MergedLayerDecorator::setShowSunShading(bool show)
367{
368 d->m_showSunShading = show;
369}
370
371bool MergedLayerDecorator::showSunShading() const
372{
373 return d->m_showSunShading;
374}
375
376void MergedLayerDecorator::setShowCityLights(bool show)
377{
378 d->m_showCityLights = show;
379}
380
381bool MergedLayerDecorator::showCityLights() const
382{
383 return d->m_showCityLights;
384}
385
386void MergedLayerDecorator::setShowTileId(bool visible)
387{
388 d->m_showTileId = visible;
389}
390
391void MergedLayerDecorator::Private::paintSunShading(QImage *tileImage, const TileId &id) const
392{
393 if (tileImage->depth() != 32)
394 return;
395
396 // TODO add support for 8-bit maps?
397 // add sun shading
398 const qreal global_width = tileImage->width() * TileLoaderHelper::levelToColumn(m_levelZeroColumns, id.zoomLevel());
399 const qreal global_height = tileImage->height() * TileLoaderHelper::levelToRow(m_levelZeroRows, id.zoomLevel());
400 const qreal lon_scale = 2 * M_PI / global_width;
401 const qreal lat_scale = -M_PI / global_height;
402 const int tileHeight = tileImage->height();
403 const int tileWidth = tileImage->width();
404
405 // First we determine the supporting point interval for the interpolation.
406 const int n = maxDivisor(30, tileWidth);
407 const int ipRight = n * (int)(tileWidth / n);
408
409 for (int cur_y = 0; cur_y < tileHeight; ++cur_y) {
410 const qreal lat = lat_scale * (id.y() * tileHeight + cur_y) - 0.5 * M_PI;
411 const qreal a = sin((lat + DEG2RAD * m_sunLocator->getLat()) / 2.0);
412 const qreal c = cos(lat) * cos(-DEG2RAD * m_sunLocator->getLat());
413
414 QRgb *scanline = (QRgb *)tileImage->scanLine(cur_y);
415
416 qreal lastShade = -10.0;
417
418 int cur_x = 0;
419
420 while (cur_x < tileWidth) {
421 const bool interpolate = (cur_x != 0 && cur_x < ipRight && cur_x + n < tileWidth);
422
423 qreal shade = 0;
424
425 if (interpolate) {
426 const int check = cur_x + n;
427 const qreal checklon = lon_scale * (id.x() * tileWidth + check);
428 shade = m_sunLocator->shading(checklon, a, c);
429
430 // if the shading didn't change across the interpolation
431 // interval move on and don't change anything.
432 if (shade == lastShade && shade == 1.0) {
433 scanline += n;
434 cur_x += n;
435 continue;
436 }
437 if (shade == lastShade && shade == 0.0) {
438 for (int t = 0; t < n; ++t) {
439 SunLocator::shadePixel(*scanline, shade);
440 ++scanline;
441 }
442 cur_x += n;
443 continue;
444 }
445 for (int t = 0; t < n; ++t) {
446 const qreal lon = lon_scale * (id.x() * tileWidth + cur_x);
447 shade = m_sunLocator->shading(lon, a, c);
448 SunLocator::shadePixel(*scanline, shade);
449 ++scanline;
450 ++cur_x;
451 }
452 }
453
454 else {
455 // Make sure we don't exceed the image memory
456 if (cur_x < tileWidth) {
457 const qreal lon = lon_scale * (id.x() * tileWidth + cur_x);
458 shade = m_sunLocator->shading(lon, a, c);
459 SunLocator::shadePixel(*scanline, shade);
460 ++scanline;
461 ++cur_x;
462 }
463 }
464 lastShade = shade;
465 }
466 }
467}
468
469void MergedLayerDecorator::Private::paintTileId(QImage *tileImage, const TileId &id) const
470{
471 QString filename = QStringLiteral("%1_%2.jpg").arg(id.x(), tileDigits, 10, QLatin1Char('0')).arg(id.y(), tileDigits, 10, QLatin1Char('0'));
472
473 QPainter painter(tileImage);
474
475 QColor foreground;
476 QColor background;
477
478 if (((qreal)(id.x()) / 2 == id.x() / 2 && (qreal)(id.y()) / 2 == id.y() / 2) || ((qreal)(id.x()) / 2 != id.x() / 2 && (qreal)(id.y()) / 2 != id.y() / 2)) {
479 foreground = Qt::white;
480 background = Qt::black;
481 } else {
482 foreground = Qt::black;
483 background = Qt::white;
484 }
485
486 int strokeWidth = 10;
487 QPen testPen(foreground);
488 testPen.setWidth(strokeWidth);
489 testPen.setJoinStyle(Qt::MiterJoin);
490
491 painter.setPen(testPen);
492 painter.drawRect(strokeWidth / 2, strokeWidth / 2, tileImage->width() - strokeWidth, tileImage->height() - strokeWidth);
493 QFont testFont(QStringLiteral("Sans Serif"), 12);
494 QFontMetrics testFm(testFont);
495 painter.setFont(testFont);
496
497 QPen outlinepen(foreground);
498 outlinepen.setWidthF(6);
499
500 painter.setPen(outlinepen);
501 painter.setBrush(background);
502
503 QPainterPath outlinepath;
504
505 QPointF baseline1((tileImage->width() - testFm.boundingRect(filename).width()) / 2, (tileImage->height() * 0.25));
506 outlinepath.addText(baseline1, testFont, QStringLiteral("level: %1").arg(id.zoomLevel()));
507
508 QPointF baseline2((tileImage->width() - testFm.boundingRect(filename).width()) / 2, tileImage->height() * 0.50);
509 outlinepath.addText(baseline2, testFont, filename);
510
511 QPointF baseline3((tileImage->width() - testFm.boundingRect(filename).width()) / 2, tileImage->height() * 0.75);
512 outlinepath.addText(baseline3, testFont, m_themeId);
513
514 painter.drawPath(outlinepath);
515
516 painter.setPen(Qt::NoPen);
517 painter.drawPath(outlinepath);
518}
519
520void MergedLayerDecorator::Private::detectMaxTileLevel()
521{
522 if (m_textureLayers.isEmpty()) {
523 m_maxTileLevel = -1;
524 return;
525 }
526
527 m_maxTileLevel = TileLoader::maximumTileLevel(*m_textureLayers.at(0));
528}
529
530QList<const GeoSceneTextureTileDataset *> MergedLayerDecorator::Private::findRelevantTextureLayers(const TileId &stackedTileId) const
531{
533
534 for (const GeoSceneTextureTileDataset *candidate : m_textureLayers) {
535 Q_ASSERT(candidate);
536 // check, if layer provides tiles for the current level
537 if (!candidate->hasMaximumTileLevel() || candidate->maximumTileLevel() >= stackedTileId.zoomLevel()) {
538 // check if the tile intersects with texture bounds
539 if (candidate->latLonBox().isNull()) {
540 result.append(candidate);
541 } else {
542 const GeoDataLatLonBox bbox = candidate->tileProjection()->geoCoordinates(stackedTileId);
543
544 if (candidate->latLonBox().intersects(bbox)) {
545 result.append(candidate);
546 }
547 }
548 }
549 }
550
551 return result;
552}
553
554// TODO: This should likely go into a math class in the future ...
555
556int MergedLayerDecorator::Private::maxDivisor(int maximum, int fullLength)
557{
558 // Find the optimal interpolation interval n for the
559 // current image canvas width
560 int best = 2;
561
562 int nEvalMin = fullLength;
563 for (int it = 1; it <= maximum; ++it) {
564 // The optimum is the interval which results in the least amount
565 // supporting points taking into account the rest which can't
566 // get used for interpolation.
567 int nEval = fullLength / it + fullLength % it;
568 if (nEval < nEvalMin) {
569 nEvalMin = nEval;
570 best = it;
571 }
572 }
573 return best;
574}
A 3d point representation.
static qreal normalizeLon(qreal lon, GeoDataCoordinates::Unit=GeoDataCoordinates::Radian)
normalize the longitude to always be -M_PI <= lon <= +M_PI (Radian).
bool isGloballyVisible() const
Return whether this feature is visible or not in the context of its parenting.
A class that defines a 2D bounding box for geographic data.
qreal north(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the northern boundary of the bounding box.
qreal height(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the height of the latitude interval.
GeoDataLatLonBox toCircumscribedRectangle() const
qreal width(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the width of the longitude interval.
qreal west(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the western boundary of the bounding box.
qreal south(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the southern boundary of the bounding box.
qreal rotation(GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian) const
Get the rotation of the bounding box.
virtual GeoDataCoordinates center() const
returns the center of this box
A base class for projections between tile indizes and geo coordinates in Marble.
A single tile that consists of a stack of Tile layers.
Definition StackedTile.h:51
QList< QSharedPointer< TextureTile > > tiles() const
Returns the stack of Tiles.
A class that resembles an image tile (extends Tile).
Definition TextureTile.h:46
KGUIADDONS_EXPORT QColor shade(const QColor &, qreal lumaAmount, qreal chromaAmount=0.0)
Binds a QML item to a specific geodetic location in screen coordinates.
qreal gdInv(qreal x)
This method is a fast Mac Laurin power series approximation of the.
Definition MarbleMath.h:71
DownloadUsage
This enum is used to describe the type of download.
@ DownloadBrowse
Browsing mode, normal operation of Marble, like a web browser.
@ WaitingForData
Rendering is based on no or partial data, more data was requested (e.g. pending network queries)
@ Complete
All data is there and up to date.
@ WaitingForUpdate
Rendering is based on complete, but outdated data, data update was requested.
Format_ARGB32_Premultiplied
QImage convertToFormat(Format format, Qt::ImageConversionFlags flags) &&
QImage copy(const QRect &rectangle) const const
int depth() const const
int height() const const
bool isNull() const const
QRgb pixel(const QPoint &position) const const
uchar * scanLine(int i)
int width() const const
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
bool contains(const AT &value) const const
qsizetype count() const const
bool isEmpty() const const
void reserve(qsizetype size)
qsizetype size() const const
void addText(const QPointF &point, const QFont &font, const QString &text)
QString arg(Args &&... args) const const
MiterJoin
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:15:45 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.