[8127] | 1 | /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
|
---|
| 2 | * Qwt Widget Library
|
---|
| 3 | * Copyright (C) 1997 Josef Wilgen
|
---|
| 4 | * Copyright (C) 2002 Uwe Rathmann
|
---|
| 5 | *
|
---|
| 6 | * This library is free software; you can redistribute it and/or
|
---|
| 7 | * modify it under the terms of the Qwt License, Version 1.0
|
---|
| 8 | *****************************************************************************/
|
---|
| 9 |
|
---|
| 10 | #include "qwt_graphic.h"
|
---|
| 11 | #include "qwt_painter_command.h"
|
---|
| 12 | #include <qvector.h>
|
---|
| 13 | #include <qpainter.h>
|
---|
| 14 | #include <qpaintengine.h>
|
---|
| 15 | #include <qimage.h>
|
---|
| 16 | #include <qpixmap.h>
|
---|
| 17 | #include <qpainterpath.h>
|
---|
| 18 | #include <qmath.h>
|
---|
| 19 |
|
---|
| 20 | static bool qwtHasScalablePen( const QPainter *painter )
|
---|
| 21 | {
|
---|
| 22 | const QPen pen = painter->pen();
|
---|
| 23 |
|
---|
| 24 | bool scalablePen = false;
|
---|
| 25 |
|
---|
| 26 | if ( pen.style() != Qt::NoPen && pen.brush().style() != Qt::NoBrush )
|
---|
| 27 | {
|
---|
| 28 | scalablePen = !pen.isCosmetic();
|
---|
| 29 | if ( !scalablePen && pen.widthF() == 0.0 )
|
---|
| 30 | {
|
---|
| 31 | const QPainter::RenderHints hints = painter->renderHints();
|
---|
| 32 | if ( hints.testFlag( QPainter::NonCosmeticDefaultPen ) )
|
---|
| 33 | scalablePen = true;
|
---|
| 34 | }
|
---|
| 35 | }
|
---|
| 36 |
|
---|
| 37 | return scalablePen;
|
---|
| 38 | }
|
---|
| 39 |
|
---|
| 40 | static QRectF qwtStrokedPathRect(
|
---|
| 41 | const QPainter *painter, const QPainterPath &path )
|
---|
| 42 | {
|
---|
| 43 | QPainterPathStroker stroker;
|
---|
| 44 | stroker.setWidth( painter->pen().widthF() );
|
---|
| 45 | stroker.setCapStyle( painter->pen().capStyle() );
|
---|
| 46 | stroker.setJoinStyle( painter->pen().joinStyle() );
|
---|
| 47 | stroker.setMiterLimit( painter->pen().miterLimit() );
|
---|
| 48 |
|
---|
| 49 | QRectF rect;
|
---|
| 50 | if ( qwtHasScalablePen( painter ) )
|
---|
| 51 | {
|
---|
| 52 | QPainterPath stroke = stroker.createStroke(path);
|
---|
| 53 | rect = painter->transform().map(stroke).boundingRect();
|
---|
| 54 | }
|
---|
| 55 | else
|
---|
| 56 | {
|
---|
| 57 | QPainterPath mappedPath = painter->transform().map(path);
|
---|
| 58 | mappedPath = stroker.createStroke( mappedPath );
|
---|
| 59 |
|
---|
| 60 | rect = mappedPath.boundingRect();
|
---|
| 61 | }
|
---|
| 62 |
|
---|
| 63 | return rect;
|
---|
| 64 | }
|
---|
| 65 |
|
---|
| 66 | static inline void qwtExecCommand(
|
---|
| 67 | QPainter *painter, const QwtPainterCommand &cmd,
|
---|
| 68 | QwtGraphic::RenderHints renderHints,
|
---|
| 69 | const QTransform &transform,
|
---|
| 70 | const QTransform *initialTransform )
|
---|
| 71 | {
|
---|
| 72 | switch( cmd.type() )
|
---|
| 73 | {
|
---|
| 74 | case QwtPainterCommand::Path:
|
---|
| 75 | {
|
---|
| 76 | bool doMap = false;
|
---|
| 77 |
|
---|
| 78 | if ( renderHints.testFlag( QwtGraphic::RenderPensUnscaled )
|
---|
| 79 | && painter->transform().isScaling() )
|
---|
| 80 | {
|
---|
| 81 | bool isCosmetic = painter->pen().isCosmetic();
|
---|
| 82 | if ( isCosmetic && painter->pen().widthF() == 0.0 )
|
---|
| 83 | {
|
---|
| 84 | QPainter::RenderHints hints = painter->renderHints();
|
---|
| 85 | if ( hints.testFlag( QPainter::NonCosmeticDefaultPen ) )
|
---|
| 86 | isCosmetic = false;
|
---|
| 87 | }
|
---|
| 88 |
|
---|
| 89 | doMap = !isCosmetic;
|
---|
| 90 | }
|
---|
| 91 |
|
---|
| 92 | if ( doMap )
|
---|
| 93 | {
|
---|
| 94 | const QTransform tr = painter->transform();
|
---|
| 95 |
|
---|
| 96 | painter->resetTransform();
|
---|
| 97 |
|
---|
| 98 | QPainterPath path = tr.map( *cmd.path() );
|
---|
| 99 | if ( initialTransform )
|
---|
| 100 | {
|
---|
| 101 | painter->setTransform( *initialTransform );
|
---|
| 102 | path = initialTransform->inverted().map( path );
|
---|
| 103 | }
|
---|
| 104 |
|
---|
| 105 | painter->drawPath( path );
|
---|
| 106 |
|
---|
| 107 | painter->setTransform( tr );
|
---|
| 108 | }
|
---|
| 109 | else
|
---|
| 110 | {
|
---|
| 111 | painter->drawPath( *cmd.path() );
|
---|
| 112 | }
|
---|
| 113 | break;
|
---|
| 114 | }
|
---|
| 115 | case QwtPainterCommand::Pixmap:
|
---|
| 116 | {
|
---|
| 117 | const QwtPainterCommand::PixmapData *data = cmd.pixmapData();
|
---|
| 118 | painter->drawPixmap( data->rect, data->pixmap, data->subRect );
|
---|
| 119 | break;
|
---|
| 120 | }
|
---|
| 121 | case QwtPainterCommand::Image:
|
---|
| 122 | {
|
---|
| 123 | const QwtPainterCommand::ImageData *data = cmd.imageData();
|
---|
| 124 | painter->drawImage( data->rect, data->image,
|
---|
| 125 | data->subRect, data->flags );
|
---|
| 126 | break;
|
---|
| 127 | }
|
---|
| 128 | case QwtPainterCommand::State:
|
---|
| 129 | {
|
---|
| 130 | const QwtPainterCommand::StateData *data = cmd.stateData();
|
---|
| 131 |
|
---|
| 132 | if ( data->flags & QPaintEngine::DirtyPen )
|
---|
| 133 | painter->setPen( data->pen );
|
---|
| 134 |
|
---|
| 135 | if ( data->flags & QPaintEngine::DirtyBrush )
|
---|
| 136 | painter->setBrush( data->brush );
|
---|
| 137 |
|
---|
| 138 | if ( data->flags & QPaintEngine::DirtyBrushOrigin )
|
---|
| 139 | painter->setBrushOrigin( data->brushOrigin );
|
---|
| 140 |
|
---|
| 141 | if ( data->flags & QPaintEngine::DirtyFont )
|
---|
| 142 | painter->setFont( data->font );
|
---|
| 143 |
|
---|
| 144 | if ( data->flags & QPaintEngine::DirtyBackground )
|
---|
| 145 | {
|
---|
| 146 | painter->setBackgroundMode( data->backgroundMode );
|
---|
| 147 | painter->setBackground( data->backgroundBrush );
|
---|
| 148 | }
|
---|
| 149 |
|
---|
| 150 | if ( data->flags & QPaintEngine::DirtyTransform )
|
---|
| 151 | {
|
---|
| 152 | painter->setTransform( data->transform * transform );
|
---|
| 153 | }
|
---|
| 154 |
|
---|
| 155 | if ( data->flags & QPaintEngine::DirtyClipEnabled )
|
---|
| 156 | painter->setClipping( data->isClipEnabled );
|
---|
| 157 |
|
---|
| 158 | if ( data->flags & QPaintEngine::DirtyClipRegion)
|
---|
| 159 | {
|
---|
| 160 | painter->setClipRegion( data->clipRegion,
|
---|
| 161 | data->clipOperation );
|
---|
| 162 | }
|
---|
| 163 |
|
---|
| 164 | if ( data->flags & QPaintEngine::DirtyClipPath )
|
---|
| 165 | {
|
---|
| 166 | painter->setClipPath( data->clipPath, data->clipOperation );
|
---|
| 167 | }
|
---|
| 168 |
|
---|
| 169 | if ( data->flags & QPaintEngine::DirtyHints)
|
---|
| 170 | {
|
---|
| 171 | const QPainter::RenderHints hints = data->renderHints;
|
---|
| 172 |
|
---|
| 173 | painter->setRenderHint( QPainter::Antialiasing,
|
---|
| 174 | hints.testFlag( QPainter::Antialiasing ) );
|
---|
| 175 |
|
---|
| 176 | painter->setRenderHint( QPainter::TextAntialiasing,
|
---|
| 177 | hints.testFlag( QPainter::TextAntialiasing ) );
|
---|
| 178 |
|
---|
| 179 | painter->setRenderHint( QPainter::SmoothPixmapTransform,
|
---|
| 180 | hints.testFlag( QPainter::SmoothPixmapTransform ) );
|
---|
| 181 |
|
---|
| 182 | painter->setRenderHint( QPainter::HighQualityAntialiasing,
|
---|
| 183 | hints.testFlag( QPainter::HighQualityAntialiasing ) );
|
---|
| 184 |
|
---|
| 185 | painter->setRenderHint( QPainter::NonCosmeticDefaultPen,
|
---|
| 186 | hints.testFlag( QPainter::NonCosmeticDefaultPen ) );
|
---|
| 187 | }
|
---|
| 188 |
|
---|
| 189 | if ( data->flags & QPaintEngine::DirtyCompositionMode)
|
---|
| 190 | painter->setCompositionMode( data->compositionMode );
|
---|
| 191 |
|
---|
| 192 | if ( data->flags & QPaintEngine::DirtyOpacity)
|
---|
| 193 | painter->setOpacity( data->opacity );
|
---|
| 194 |
|
---|
| 195 | break;
|
---|
| 196 | }
|
---|
| 197 | default:
|
---|
| 198 | break;
|
---|
| 199 | }
|
---|
| 200 |
|
---|
| 201 | }
|
---|
| 202 |
|
---|
| 203 | class QwtGraphic::PathInfo
|
---|
| 204 | {
|
---|
| 205 | public:
|
---|
| 206 | PathInfo():
|
---|
| 207 | d_scalablePen( false )
|
---|
| 208 | {
|
---|
| 209 | // QVector needs a default constructor
|
---|
| 210 | }
|
---|
| 211 |
|
---|
| 212 | PathInfo( const QRectF &pointRect,
|
---|
| 213 | const QRectF &boundingRect, bool scalablePen ):
|
---|
| 214 | d_pointRect( pointRect ),
|
---|
| 215 | d_boundingRect( boundingRect ),
|
---|
| 216 | d_scalablePen( scalablePen )
|
---|
| 217 | {
|
---|
| 218 | }
|
---|
| 219 |
|
---|
| 220 | inline QRectF scaledBoundingRect( double sx, double sy,
|
---|
| 221 | bool scalePens ) const
|
---|
| 222 | {
|
---|
| 223 | if ( sx == 1.0 && sy == 1.0 )
|
---|
| 224 | return d_boundingRect;
|
---|
| 225 |
|
---|
| 226 | QTransform transform;
|
---|
| 227 | transform.scale( sx, sy );
|
---|
| 228 |
|
---|
| 229 | QRectF rect;
|
---|
| 230 | if ( scalePens && d_scalablePen )
|
---|
| 231 | {
|
---|
| 232 | rect = transform.mapRect( d_boundingRect );
|
---|
| 233 | }
|
---|
| 234 | else
|
---|
| 235 | {
|
---|
| 236 | rect = transform.mapRect( d_pointRect );
|
---|
| 237 |
|
---|
| 238 | const double l = qAbs( d_pointRect.left() - d_boundingRect.left() );
|
---|
| 239 | const double r = qAbs( d_pointRect.right() - d_boundingRect.right() );
|
---|
| 240 | const double t = qAbs( d_pointRect.top() - d_boundingRect.top() );
|
---|
| 241 | const double b = qAbs( d_pointRect.bottom() - d_boundingRect.bottom() );
|
---|
| 242 |
|
---|
| 243 | rect.adjust( -l, -t, r, b );
|
---|
| 244 | }
|
---|
| 245 |
|
---|
| 246 | return rect;
|
---|
| 247 | }
|
---|
| 248 |
|
---|
| 249 | inline double scaleFactorX( const QRectF& pathRect,
|
---|
| 250 | const QRectF &targetRect, bool scalePens ) const
|
---|
| 251 | {
|
---|
| 252 | if ( pathRect.width() <= 0.0 )
|
---|
| 253 | return 0.0;
|
---|
| 254 |
|
---|
| 255 | const QPointF p0 = d_pointRect.center();
|
---|
| 256 |
|
---|
| 257 | const double l = qAbs( pathRect.left() - p0.x() );
|
---|
| 258 | const double r = qAbs( pathRect.right() - p0.x() );
|
---|
| 259 |
|
---|
| 260 | const double w = 2.0 * qMin( l, r )
|
---|
| 261 | * targetRect.width() / pathRect.width();
|
---|
| 262 |
|
---|
| 263 | double sx;
|
---|
| 264 | if ( scalePens && d_scalablePen )
|
---|
| 265 | {
|
---|
| 266 | sx = w / d_boundingRect.width();
|
---|
| 267 | }
|
---|
| 268 | else
|
---|
| 269 | {
|
---|
| 270 | const double pw = qMax(
|
---|
| 271 | qAbs( d_boundingRect.left() - d_pointRect.left() ),
|
---|
| 272 | qAbs( d_boundingRect.right() - d_pointRect.right() ) );
|
---|
| 273 |
|
---|
| 274 | sx = ( w - 2 * pw ) / d_pointRect.width();
|
---|
| 275 | }
|
---|
| 276 |
|
---|
| 277 | return sx;
|
---|
| 278 | }
|
---|
| 279 |
|
---|
| 280 | inline double scaleFactorY( const QRectF& pathRect,
|
---|
| 281 | const QRectF &targetRect, bool scalePens ) const
|
---|
| 282 | {
|
---|
| 283 | if ( pathRect.height() <= 0.0 )
|
---|
| 284 | return 0.0;
|
---|
| 285 |
|
---|
| 286 | const QPointF p0 = d_pointRect.center();
|
---|
| 287 |
|
---|
| 288 | const double t = qAbs( pathRect.top() - p0.y() );
|
---|
| 289 | const double b = qAbs( pathRect.bottom() - p0.y() );
|
---|
| 290 |
|
---|
| 291 | const double h = 2.0 * qMin( t, b )
|
---|
| 292 | * targetRect.height() / pathRect.height();
|
---|
| 293 |
|
---|
| 294 | double sy;
|
---|
| 295 | if ( scalePens && d_scalablePen )
|
---|
| 296 | {
|
---|
| 297 | sy = h / d_boundingRect.height();
|
---|
| 298 | }
|
---|
| 299 | else
|
---|
| 300 | {
|
---|
| 301 | const double pw =
|
---|
| 302 | qMax( qAbs( d_boundingRect.top() - d_pointRect.top() ),
|
---|
| 303 | qAbs( d_boundingRect.bottom() - d_pointRect.bottom() ) );
|
---|
| 304 |
|
---|
| 305 | sy = ( h - 2 * pw ) / d_pointRect.height();
|
---|
| 306 | }
|
---|
| 307 |
|
---|
| 308 | return sy;
|
---|
| 309 | }
|
---|
| 310 |
|
---|
| 311 | private:
|
---|
| 312 | QRectF d_pointRect;
|
---|
| 313 | QRectF d_boundingRect;
|
---|
| 314 | bool d_scalablePen;
|
---|
| 315 | };
|
---|
| 316 |
|
---|
| 317 | class QwtGraphic::PrivateData
|
---|
| 318 | {
|
---|
| 319 | public:
|
---|
| 320 | PrivateData():
|
---|
| 321 | boundingRect( 0.0, 0.0, -1.0, -1.0 ),
|
---|
| 322 | pointRect( 0.0, 0.0, -1.0, -1.0 ),
|
---|
| 323 | initialTransform( NULL )
|
---|
| 324 | {
|
---|
| 325 | }
|
---|
| 326 |
|
---|
| 327 | QSizeF defaultSize;
|
---|
| 328 | QVector<QwtPainterCommand> commands;
|
---|
| 329 | QVector<QwtGraphic::PathInfo> pathInfos;
|
---|
| 330 |
|
---|
| 331 | QRectF boundingRect;
|
---|
| 332 | QRectF pointRect;
|
---|
| 333 |
|
---|
| 334 | QwtGraphic::RenderHints renderHints;
|
---|
| 335 | QTransform *initialTransform;
|
---|
| 336 | };
|
---|
| 337 |
|
---|
| 338 | /*!
|
---|
| 339 | \brief Constructor
|
---|
| 340 |
|
---|
| 341 | Initializes a null graphic
|
---|
| 342 | \sa isNull()
|
---|
| 343 | */
|
---|
| 344 | QwtGraphic::QwtGraphic():
|
---|
| 345 | QwtNullPaintDevice()
|
---|
| 346 | {
|
---|
| 347 | setMode( QwtNullPaintDevice::PathMode );
|
---|
| 348 | d_data = new PrivateData;
|
---|
| 349 | }
|
---|
| 350 |
|
---|
| 351 | /*!
|
---|
| 352 | \brief Copy constructor
|
---|
| 353 |
|
---|
| 354 | \param other Source
|
---|
| 355 | \sa operator=()
|
---|
| 356 | */
|
---|
| 357 | QwtGraphic::QwtGraphic( const QwtGraphic &other ):
|
---|
| 358 | QwtNullPaintDevice()
|
---|
| 359 | {
|
---|
| 360 | setMode( other.mode() );
|
---|
| 361 | d_data = new PrivateData( *other.d_data );
|
---|
| 362 | }
|
---|
| 363 |
|
---|
| 364 | //! Destructor
|
---|
| 365 | QwtGraphic::~QwtGraphic()
|
---|
| 366 | {
|
---|
| 367 | delete d_data;
|
---|
| 368 | }
|
---|
| 369 |
|
---|
| 370 | /*!
|
---|
| 371 | \brief Assignment operator
|
---|
| 372 |
|
---|
| 373 | \param other Source
|
---|
| 374 | \return A reference of this object
|
---|
| 375 | */
|
---|
| 376 | QwtGraphic& QwtGraphic::operator=(const QwtGraphic &other)
|
---|
| 377 | {
|
---|
| 378 | setMode( other.mode() );
|
---|
| 379 | *d_data = *other.d_data;
|
---|
| 380 |
|
---|
| 381 | return *this;
|
---|
| 382 | }
|
---|
| 383 |
|
---|
| 384 | /*!
|
---|
| 385 | \brief Clear all stored commands
|
---|
| 386 | \sa isNull()
|
---|
| 387 | */
|
---|
| 388 | void QwtGraphic::reset()
|
---|
| 389 | {
|
---|
| 390 | d_data->commands.clear();
|
---|
| 391 | d_data->pathInfos.clear();
|
---|
| 392 |
|
---|
| 393 | d_data->boundingRect = QRectF( 0.0, 0.0, -1.0, -1.0 );
|
---|
| 394 | d_data->pointRect = QRectF( 0.0, 0.0, -1.0, -1.0 );
|
---|
| 395 | d_data->defaultSize = QSizeF();
|
---|
| 396 |
|
---|
| 397 | }
|
---|
| 398 |
|
---|
| 399 | /*!
|
---|
| 400 | \return True, when no painter commands have been stored
|
---|
| 401 | \sa isEmpty(), commands()
|
---|
| 402 | */
|
---|
| 403 | bool QwtGraphic::isNull() const
|
---|
| 404 | {
|
---|
| 405 | return d_data->commands.isEmpty();
|
---|
| 406 | }
|
---|
| 407 |
|
---|
| 408 | /*!
|
---|
| 409 | \return True, when the bounding rectangle is empty
|
---|
| 410 | \sa boundingRect(), isNull()
|
---|
| 411 | */
|
---|
| 412 | bool QwtGraphic::isEmpty() const
|
---|
| 413 | {
|
---|
| 414 | return d_data->boundingRect.isEmpty();
|
---|
| 415 | }
|
---|
| 416 |
|
---|
| 417 | /*!
|
---|
| 418 | Toggle an render hint
|
---|
| 419 |
|
---|
| 420 | \param hint Render hint
|
---|
| 421 | \param on true/false
|
---|
| 422 |
|
---|
| 423 | \sa testRenderHint(), RenderHint
|
---|
| 424 | */
|
---|
| 425 | void QwtGraphic::setRenderHint( RenderHint hint, bool on )
|
---|
| 426 | {
|
---|
| 427 | if ( on )
|
---|
| 428 | d_data->renderHints |= hint;
|
---|
| 429 | else
|
---|
| 430 | d_data->renderHints &= ~hint;
|
---|
| 431 | }
|
---|
| 432 |
|
---|
| 433 | /*!
|
---|
| 434 | Test a render hint
|
---|
| 435 |
|
---|
| 436 | \param hint Render hint
|
---|
| 437 | \return true/false
|
---|
| 438 | \sa setRenderHint(), RenderHint
|
---|
| 439 | */
|
---|
| 440 | bool QwtGraphic::testRenderHint( RenderHint hint ) const
|
---|
| 441 | {
|
---|
| 442 | return d_data->renderHints.testFlag( hint );
|
---|
| 443 | }
|
---|
| 444 |
|
---|
| 445 | /*!
|
---|
| 446 | The bounding rectangle is the controlPointRect()
|
---|
| 447 | extended by the areas needed for rendering the outlines
|
---|
| 448 | with unscaled pens.
|
---|
| 449 |
|
---|
| 450 | \return Bounding rectangle of the graphic
|
---|
| 451 | \sa controlPointRect(), scaledBoundingRect()
|
---|
| 452 | */
|
---|
| 453 | QRectF QwtGraphic::boundingRect() const
|
---|
| 454 | {
|
---|
| 455 | if ( d_data->boundingRect.width() < 0 )
|
---|
| 456 | return QRectF();
|
---|
| 457 |
|
---|
| 458 | return d_data->boundingRect;
|
---|
| 459 | }
|
---|
| 460 |
|
---|
| 461 | /*!
|
---|
| 462 | The control point rectangle is the bounding rectangle
|
---|
| 463 | of all control points of the paths and the target
|
---|
| 464 | rectangles of the images/pixmaps.
|
---|
| 465 |
|
---|
| 466 | \return Control point rectangle
|
---|
| 467 | \sa boundingRect(), scaledBoundingRect()
|
---|
| 468 | */
|
---|
| 469 | QRectF QwtGraphic::controlPointRect() const
|
---|
| 470 | {
|
---|
| 471 | if ( d_data->pointRect.width() < 0 )
|
---|
| 472 | return QRectF();
|
---|
| 473 |
|
---|
| 474 | return d_data->pointRect;
|
---|
| 475 | }
|
---|
| 476 |
|
---|
| 477 | /*!
|
---|
| 478 | \brief Calculate the target rectangle for scaling the graphic
|
---|
| 479 |
|
---|
| 480 | \param sx Horizontal scaling factor
|
---|
| 481 | \param sy Vertical scaling factor
|
---|
| 482 |
|
---|
| 483 | \note In case of paths that are painted with a cosmetic pen
|
---|
| 484 | ( see QPen::isCosmetic() ) the target rectangle is different to
|
---|
| 485 | multiplying the bounding rectangle.
|
---|
| 486 |
|
---|
| 487 | \return Scaled bounding rectangle
|
---|
| 488 | \sa boundingRect(), controlPointRect()
|
---|
| 489 | */
|
---|
| 490 | QRectF QwtGraphic::scaledBoundingRect( double sx, double sy ) const
|
---|
| 491 | {
|
---|
| 492 | if ( sx == 1.0 && sy == 1.0 )
|
---|
| 493 | return d_data->boundingRect;
|
---|
| 494 |
|
---|
| 495 | QTransform transform;
|
---|
| 496 | transform.scale( sx, sy );
|
---|
| 497 |
|
---|
| 498 | QRectF rect = transform.mapRect( d_data->pointRect );
|
---|
| 499 |
|
---|
| 500 | for ( int i = 0; i < d_data->pathInfos.size(); i++ )
|
---|
| 501 | {
|
---|
| 502 | rect |= d_data->pathInfos[i].scaledBoundingRect( sx, sy,
|
---|
| 503 | !d_data->renderHints.testFlag( RenderPensUnscaled ) );
|
---|
| 504 | }
|
---|
| 505 |
|
---|
| 506 | return rect;
|
---|
| 507 | }
|
---|
| 508 |
|
---|
| 509 | //! \return Ceiled defaultSize()
|
---|
| 510 | QSize QwtGraphic::sizeMetrics() const
|
---|
| 511 | {
|
---|
| 512 | const QSizeF sz = defaultSize();
|
---|
| 513 | return QSize( qCeil( sz.width() ), qCeil( sz.height() ) );
|
---|
| 514 | }
|
---|
| 515 |
|
---|
| 516 | /*!
|
---|
| 517 | \brief Set a default size
|
---|
| 518 |
|
---|
| 519 | The default size is used in all methods rendering the graphic,
|
---|
| 520 | where no size is explicitly specified. Assigning an empty size
|
---|
| 521 | means, that the default size will be calculated from the bounding
|
---|
| 522 | rectangle.
|
---|
| 523 |
|
---|
| 524 | The default setting is an empty size.
|
---|
| 525 |
|
---|
| 526 | \param size Default size
|
---|
| 527 |
|
---|
| 528 | \sa defaultSize(), boundingRect()
|
---|
| 529 | */
|
---|
| 530 | void QwtGraphic::setDefaultSize( const QSizeF &size )
|
---|
| 531 | {
|
---|
| 532 | const double w = qMax( qreal( 0.0 ), size.width() );
|
---|
| 533 | const double h = qMax( qreal( 0.0 ), size.height() );
|
---|
| 534 |
|
---|
| 535 | d_data->defaultSize = QSizeF( w, h );
|
---|
| 536 | }
|
---|
| 537 |
|
---|
| 538 | /*!
|
---|
| 539 | \brief Default size
|
---|
| 540 |
|
---|
| 541 | When a non empty size has been assigned by setDefaultSize() this
|
---|
| 542 | size will be returned. Otherwise the default size is the size
|
---|
| 543 | of the bounding rectangle.
|
---|
| 544 |
|
---|
| 545 | The default size is used in all methods rendering the graphic,
|
---|
| 546 | where no size is explicitly specified.
|
---|
| 547 |
|
---|
| 548 | \return Default size
|
---|
| 549 | \sa setDefaultSize(), boundingRect()
|
---|
| 550 | */
|
---|
| 551 | QSizeF QwtGraphic::defaultSize() const
|
---|
| 552 | {
|
---|
| 553 | if ( !d_data->defaultSize.isEmpty() )
|
---|
| 554 | return d_data->defaultSize;
|
---|
| 555 |
|
---|
| 556 | return boundingRect().size();
|
---|
| 557 | }
|
---|
| 558 |
|
---|
| 559 | /*!
|
---|
| 560 | \brief Replay all recorded painter commands
|
---|
| 561 | \param painter Qt painter
|
---|
| 562 | */
|
---|
| 563 | void QwtGraphic::render( QPainter *painter ) const
|
---|
| 564 | {
|
---|
| 565 | if ( isNull() )
|
---|
| 566 | return;
|
---|
| 567 |
|
---|
| 568 | const int numCommands = d_data->commands.size();
|
---|
| 569 | const QwtPainterCommand *commands = d_data->commands.constData();
|
---|
| 570 |
|
---|
| 571 | const QTransform transform = painter->transform();
|
---|
| 572 |
|
---|
| 573 | painter->save();
|
---|
| 574 |
|
---|
| 575 | for ( int i = 0; i < numCommands; i++ )
|
---|
| 576 | {
|
---|
| 577 | qwtExecCommand( painter, commands[i],
|
---|
| 578 | d_data->renderHints, transform, d_data->initialTransform );
|
---|
| 579 | }
|
---|
| 580 |
|
---|
| 581 | painter->restore();
|
---|
| 582 | }
|
---|
| 583 |
|
---|
| 584 | /*!
|
---|
| 585 | \brief Replay all recorded painter commands
|
---|
| 586 |
|
---|
| 587 | The graphic is scaled to fit into the rectangle
|
---|
| 588 | of the given size starting at ( 0, 0 ).
|
---|
| 589 |
|
---|
| 590 | \param painter Qt painter
|
---|
| 591 | \param size Size for the scaled graphic
|
---|
| 592 | \param aspectRatioMode Mode how to scale - See Qt::AspectRatioMode
|
---|
| 593 | */
|
---|
| 594 | void QwtGraphic::render( QPainter *painter, const QSizeF &size,
|
---|
| 595 | Qt::AspectRatioMode aspectRatioMode ) const
|
---|
| 596 | {
|
---|
| 597 | const QRectF r( 0.0, 0.0, size.width(), size.height() );
|
---|
| 598 | render( painter, r, aspectRatioMode );
|
---|
| 599 | }
|
---|
| 600 |
|
---|
| 601 | /*!
|
---|
| 602 | \brief Replay all recorded painter commands
|
---|
| 603 |
|
---|
| 604 | The graphic is scaled to fit into the given rectangle
|
---|
| 605 |
|
---|
| 606 | \param painter Qt painter
|
---|
| 607 | \param rect Rectangle for the scaled graphic
|
---|
| 608 | \param aspectRatioMode Mode how to scale - See Qt::AspectRatioMode
|
---|
| 609 | */
|
---|
| 610 | void QwtGraphic::render( QPainter *painter, const QRectF &rect,
|
---|
| 611 | Qt::AspectRatioMode aspectRatioMode ) const
|
---|
| 612 | {
|
---|
| 613 | if ( isEmpty() || rect.isEmpty() )
|
---|
| 614 | return;
|
---|
| 615 |
|
---|
| 616 | double sx = 1.0;
|
---|
| 617 | double sy = 1.0;
|
---|
| 618 |
|
---|
| 619 | if ( d_data->pointRect.width() > 0.0 )
|
---|
| 620 | sx = rect.width() / d_data->pointRect.width();
|
---|
| 621 |
|
---|
| 622 | if ( d_data->pointRect.height() > 0.0 )
|
---|
| 623 | sy = rect.height() / d_data->pointRect.height();
|
---|
| 624 |
|
---|
| 625 | const bool scalePens =
|
---|
| 626 | !d_data->renderHints.testFlag( RenderPensUnscaled );
|
---|
| 627 |
|
---|
| 628 | for ( int i = 0; i < d_data->pathInfos.size(); i++ )
|
---|
| 629 | {
|
---|
| 630 | const PathInfo info = d_data->pathInfos[i];
|
---|
| 631 |
|
---|
| 632 | const double ssx = info.scaleFactorX(
|
---|
| 633 | d_data->pointRect, rect, scalePens );
|
---|
| 634 |
|
---|
| 635 | if ( ssx > 0.0 )
|
---|
| 636 | sx = qMin( sx, ssx );
|
---|
| 637 |
|
---|
| 638 | const double ssy = info.scaleFactorY(
|
---|
| 639 | d_data->pointRect, rect, scalePens );
|
---|
| 640 |
|
---|
| 641 | if ( ssy > 0.0 )
|
---|
| 642 | sy = qMin( sy, ssy );
|
---|
| 643 | }
|
---|
| 644 |
|
---|
| 645 | if ( aspectRatioMode == Qt::KeepAspectRatio )
|
---|
| 646 | {
|
---|
| 647 | const double s = qMin( sx, sy );
|
---|
| 648 | sx = s;
|
---|
| 649 | sy = s;
|
---|
| 650 | }
|
---|
| 651 | else if ( aspectRatioMode == Qt::KeepAspectRatioByExpanding )
|
---|
| 652 | {
|
---|
| 653 | const double s = qMax( sx, sy );
|
---|
| 654 | sx = s;
|
---|
| 655 | sy = s;
|
---|
| 656 | }
|
---|
| 657 |
|
---|
| 658 | QTransform tr;
|
---|
| 659 | tr.translate( rect.center().x() - 0.5 * sx * d_data->pointRect.width(),
|
---|
| 660 | rect.center().y() - 0.5 * sy * d_data->pointRect.height() );
|
---|
| 661 | tr.scale( sx, sy );
|
---|
| 662 | tr.translate( -d_data->pointRect.x(), -d_data->pointRect.y() );
|
---|
| 663 |
|
---|
| 664 | const QTransform transform = painter->transform();
|
---|
| 665 | if ( !scalePens && transform.isScaling() )
|
---|
| 666 | {
|
---|
| 667 | // we don't want to scale pens according to sx/sy,
|
---|
| 668 | // but we want to apply the scaling from the
|
---|
| 669 | // painter transformation later
|
---|
| 670 |
|
---|
| 671 | d_data->initialTransform = new QTransform();
|
---|
| 672 | d_data->initialTransform->scale( transform.m11(), transform.m22() );
|
---|
| 673 | }
|
---|
| 674 |
|
---|
| 675 | painter->setTransform( tr, true );
|
---|
| 676 | render( painter );
|
---|
| 677 |
|
---|
| 678 | painter->setTransform( transform );
|
---|
| 679 |
|
---|
| 680 | delete d_data->initialTransform;
|
---|
| 681 | d_data->initialTransform = NULL;
|
---|
| 682 | }
|
---|
| 683 |
|
---|
| 684 | /*!
|
---|
| 685 | \brief Replay all recorded painter commands
|
---|
| 686 |
|
---|
| 687 | The graphic is scaled to the defaultSize() and aligned
|
---|
| 688 | to a position.
|
---|
| 689 |
|
---|
| 690 | \param painter Qt painter
|
---|
| 691 | \param pos Reference point, where to render
|
---|
| 692 | \param alignment Flags how to align the target rectangle
|
---|
| 693 | to pos.
|
---|
| 694 | */
|
---|
| 695 | void QwtGraphic::render( QPainter *painter,
|
---|
| 696 | const QPointF &pos, Qt::Alignment alignment ) const
|
---|
| 697 | {
|
---|
| 698 | QRectF r( pos, defaultSize() );
|
---|
| 699 |
|
---|
| 700 | if ( alignment & Qt::AlignLeft )
|
---|
| 701 | {
|
---|
| 702 | r.moveLeft( pos.x() );
|
---|
| 703 | }
|
---|
| 704 | else if ( alignment & Qt::AlignHCenter )
|
---|
| 705 | {
|
---|
| 706 | r.moveCenter( QPointF( pos.x(), r.center().y() ) );
|
---|
| 707 | }
|
---|
| 708 | else if ( alignment & Qt::AlignRight )
|
---|
| 709 | {
|
---|
| 710 | r.moveRight( pos.x() );
|
---|
| 711 | }
|
---|
| 712 |
|
---|
| 713 | if ( alignment & Qt::AlignTop )
|
---|
| 714 | {
|
---|
| 715 | r.moveTop( pos.y() );
|
---|
| 716 | }
|
---|
| 717 | else if ( alignment & Qt::AlignVCenter )
|
---|
| 718 | {
|
---|
| 719 | r.moveCenter( QPointF( r.center().x(), pos.y() ) );
|
---|
| 720 | }
|
---|
| 721 | else if ( alignment & Qt::AlignBottom )
|
---|
| 722 | {
|
---|
| 723 | r.moveBottom( pos.y() );
|
---|
| 724 | }
|
---|
| 725 |
|
---|
| 726 | render( painter, r );
|
---|
| 727 | }
|
---|
| 728 |
|
---|
| 729 | /*!
|
---|
| 730 | \brief Convert the graphic to a QPixmap
|
---|
| 731 |
|
---|
| 732 | All pixels of the pixmap get initialized by Qt::transparent
|
---|
| 733 | before the graphic is scaled and rendered on it.
|
---|
| 734 |
|
---|
| 735 | The size of the pixmap is the default size ( ceiled to integers )
|
---|
| 736 | of the graphic.
|
---|
| 737 |
|
---|
| 738 | \return The graphic as pixmap in default size
|
---|
| 739 | \sa defaultSize(), toImage(), render()
|
---|
| 740 | */
|
---|
| 741 | QPixmap QwtGraphic::toPixmap() const
|
---|
| 742 | {
|
---|
| 743 | if ( isNull() )
|
---|
| 744 | return QPixmap();
|
---|
| 745 |
|
---|
| 746 | const QSizeF sz = defaultSize();
|
---|
| 747 |
|
---|
| 748 | const int w = qCeil( sz.width() );
|
---|
| 749 | const int h = qCeil( sz.height() );
|
---|
| 750 |
|
---|
| 751 | QPixmap pixmap( w, h );
|
---|
| 752 | pixmap.fill( Qt::transparent );
|
---|
| 753 |
|
---|
| 754 | const QRectF r( 0.0, 0.0, sz.width(), sz.height() );
|
---|
| 755 |
|
---|
| 756 | QPainter painter( &pixmap );
|
---|
| 757 | render( &painter, r, Qt::KeepAspectRatio );
|
---|
| 758 | painter.end();
|
---|
| 759 |
|
---|
| 760 | return pixmap;
|
---|
| 761 | }
|
---|
| 762 |
|
---|
| 763 | /*!
|
---|
| 764 | \brief Convert the graphic to a QPixmap
|
---|
| 765 |
|
---|
| 766 | All pixels of the pixmap get initialized by Qt::transparent
|
---|
| 767 | before the graphic is scaled and rendered on it.
|
---|
| 768 |
|
---|
| 769 | \param size Size of the image
|
---|
| 770 | \param aspectRatioMode Aspect ratio how to scale the graphic
|
---|
| 771 |
|
---|
| 772 | \return The graphic as pixmap
|
---|
| 773 | \sa toImage(), render()
|
---|
| 774 | */
|
---|
| 775 | QPixmap QwtGraphic::toPixmap( const QSize &size,
|
---|
| 776 | Qt::AspectRatioMode aspectRatioMode ) const
|
---|
| 777 | {
|
---|
| 778 | QPixmap pixmap( size );
|
---|
| 779 | pixmap.fill( Qt::transparent );
|
---|
| 780 |
|
---|
| 781 | const QRect r( 0, 0, size.width(), size.height() );
|
---|
| 782 |
|
---|
| 783 | QPainter painter( &pixmap );
|
---|
| 784 | render( &painter, r, aspectRatioMode );
|
---|
| 785 | painter.end();
|
---|
| 786 |
|
---|
| 787 | return pixmap;
|
---|
| 788 | }
|
---|
| 789 |
|
---|
| 790 | /*!
|
---|
| 791 | \brief Convert the graphic to a QImage
|
---|
| 792 |
|
---|
| 793 | All pixels of the image get initialized by 0 ( transparent )
|
---|
| 794 | before the graphic is scaled and rendered on it.
|
---|
| 795 |
|
---|
| 796 | The format of the image is QImage::Format_ARGB32_Premultiplied.
|
---|
| 797 |
|
---|
| 798 | \param size Size of the image
|
---|
| 799 | \param aspectRatioMode Aspect ratio how to scale the graphic
|
---|
| 800 |
|
---|
| 801 | \return The graphic as image
|
---|
| 802 | \sa toPixmap(), render()
|
---|
| 803 | */
|
---|
| 804 | QImage QwtGraphic::toImage( const QSize &size,
|
---|
| 805 | Qt::AspectRatioMode aspectRatioMode ) const
|
---|
| 806 | {
|
---|
| 807 | QImage image( size, QImage::Format_ARGB32_Premultiplied );
|
---|
| 808 | image.fill( 0 );
|
---|
| 809 |
|
---|
| 810 | const QRect r( 0, 0, size.width(), size.height() );
|
---|
| 811 |
|
---|
| 812 | QPainter painter( &image );
|
---|
| 813 | render( &painter, r, aspectRatioMode );
|
---|
| 814 | painter.end();
|
---|
| 815 |
|
---|
| 816 | return image;
|
---|
| 817 | }
|
---|
| 818 |
|
---|
| 819 | /*!
|
---|
| 820 | \brief Convert the graphic to a QImage
|
---|
| 821 |
|
---|
| 822 | All pixels of the image get initialized by 0 ( transparent )
|
---|
| 823 | before the graphic is scaled and rendered on it.
|
---|
| 824 |
|
---|
| 825 | The format of the image is QImage::Format_ARGB32_Premultiplied.
|
---|
| 826 |
|
---|
| 827 | The size of the image is the default size ( ceiled to integers )
|
---|
| 828 | of the graphic.
|
---|
| 829 |
|
---|
| 830 | \return The graphic as image in default size
|
---|
| 831 | \sa defaultSize(), toPixmap(), render()
|
---|
| 832 | */
|
---|
| 833 | QImage QwtGraphic::toImage() const
|
---|
| 834 | {
|
---|
| 835 | if ( isNull() )
|
---|
| 836 | return QImage();
|
---|
| 837 |
|
---|
| 838 | const QSizeF sz = defaultSize();
|
---|
| 839 |
|
---|
| 840 | const int w = qCeil( sz.width() );
|
---|
| 841 | const int h = qCeil( sz.height() );
|
---|
| 842 |
|
---|
| 843 | QImage image( w, h, QImage::Format_ARGB32 );
|
---|
| 844 | image.fill( 0 );
|
---|
| 845 |
|
---|
| 846 | const QRect r( 0, 0, sz.width(), sz.height() );
|
---|
| 847 |
|
---|
| 848 | QPainter painter( &image );
|
---|
| 849 | render( &painter, r, Qt::KeepAspectRatio );
|
---|
| 850 | painter.end();
|
---|
| 851 |
|
---|
| 852 | return image;
|
---|
| 853 | }
|
---|
| 854 |
|
---|
| 855 | /*!
|
---|
| 856 | Store a path command in the command list
|
---|
| 857 |
|
---|
| 858 | \param path Painter path
|
---|
| 859 | \sa QPaintEngine::drawPath()
|
---|
| 860 | */
|
---|
| 861 | void QwtGraphic::drawPath( const QPainterPath &path )
|
---|
| 862 | {
|
---|
| 863 | const QPainter *painter = paintEngine()->painter();
|
---|
| 864 | if ( painter == NULL )
|
---|
| 865 | return;
|
---|
| 866 |
|
---|
| 867 | d_data->commands += QwtPainterCommand( path );
|
---|
| 868 |
|
---|
| 869 | if ( !path.isEmpty() )
|
---|
| 870 | {
|
---|
| 871 | const QPainterPath scaledPath = painter->transform().map( path );
|
---|
| 872 |
|
---|
| 873 | QRectF pointRect = scaledPath.boundingRect();
|
---|
| 874 | QRectF boundingRect = pointRect;
|
---|
| 875 |
|
---|
| 876 | if ( painter->pen().style() != Qt::NoPen
|
---|
| 877 | && painter->pen().brush().style() != Qt::NoBrush )
|
---|
| 878 | {
|
---|
| 879 | boundingRect = qwtStrokedPathRect( painter, path );
|
---|
| 880 | }
|
---|
| 881 |
|
---|
| 882 | updateControlPointRect( pointRect );
|
---|
| 883 | updateBoundingRect( boundingRect );
|
---|
| 884 |
|
---|
| 885 | d_data->pathInfos += PathInfo( pointRect,
|
---|
| 886 | boundingRect, qwtHasScalablePen( painter ) );
|
---|
| 887 | }
|
---|
| 888 | }
|
---|
| 889 |
|
---|
| 890 | /*!
|
---|
| 891 | \brief Store a pixmap command in the command list
|
---|
| 892 |
|
---|
| 893 | \param rect target rectangle
|
---|
| 894 | \param pixmap Pixmap to be painted
|
---|
| 895 | \param subRect Reactangle of the pixmap to be painted
|
---|
| 896 |
|
---|
| 897 | \sa QPaintEngine::drawPixmap()
|
---|
| 898 | */
|
---|
| 899 | void QwtGraphic::drawPixmap( const QRectF &rect,
|
---|
| 900 | const QPixmap &pixmap, const QRectF &subRect )
|
---|
| 901 | {
|
---|
| 902 | const QPainter *painter = paintEngine()->painter();
|
---|
| 903 | if ( painter == NULL )
|
---|
| 904 | return;
|
---|
| 905 |
|
---|
| 906 | d_data->commands += QwtPainterCommand( rect, pixmap, subRect );
|
---|
| 907 |
|
---|
| 908 | const QRectF r = painter->transform().mapRect( rect );
|
---|
| 909 | updateControlPointRect( r );
|
---|
| 910 | updateBoundingRect( r );
|
---|
| 911 | }
|
---|
| 912 |
|
---|
| 913 | /*!
|
---|
| 914 | \brief Store a image command in the command list
|
---|
| 915 |
|
---|
| 916 | \param rect traget rectangle
|
---|
| 917 | \param image Image to be painted
|
---|
| 918 | \param subRect Reactangle of the pixmap to be painted
|
---|
| 919 | \param flags Image conversion flags
|
---|
| 920 |
|
---|
| 921 | \sa QPaintEngine::drawImage()
|
---|
| 922 | */
|
---|
| 923 | void QwtGraphic::drawImage( const QRectF &rect, const QImage &image,
|
---|
| 924 | const QRectF &subRect, Qt::ImageConversionFlags flags)
|
---|
| 925 | {
|
---|
| 926 | const QPainter *painter = paintEngine()->painter();
|
---|
| 927 | if ( painter == NULL )
|
---|
| 928 | return;
|
---|
| 929 |
|
---|
| 930 | d_data->commands += QwtPainterCommand( rect, image, subRect, flags );
|
---|
| 931 |
|
---|
| 932 | const QRectF r = painter->transform().mapRect( rect );
|
---|
| 933 |
|
---|
| 934 | updateControlPointRect( r );
|
---|
| 935 | updateBoundingRect( r );
|
---|
| 936 | }
|
---|
| 937 |
|
---|
| 938 | /*!
|
---|
| 939 | \brief Store a state command in the command list
|
---|
| 940 |
|
---|
| 941 | \param state State to be stored
|
---|
| 942 | \sa QPaintEngine::updateState()
|
---|
| 943 | */
|
---|
| 944 | void QwtGraphic::updateState( const QPaintEngineState &state)
|
---|
| 945 | {
|
---|
| 946 | d_data->commands += QwtPainterCommand( state );
|
---|
| 947 | }
|
---|
| 948 |
|
---|
| 949 | void QwtGraphic::updateBoundingRect( const QRectF &rect )
|
---|
| 950 | {
|
---|
| 951 | QRectF br = rect;
|
---|
| 952 |
|
---|
| 953 | const QPainter *painter = paintEngine()->painter();
|
---|
| 954 | if ( painter && painter->hasClipping() )
|
---|
| 955 | {
|
---|
| 956 | QRectF cr = painter->clipRegion().boundingRect();
|
---|
| 957 | cr = painter->transform().mapRect( cr );
|
---|
| 958 |
|
---|
| 959 | br &= cr;
|
---|
| 960 | }
|
---|
| 961 |
|
---|
| 962 | if ( d_data->boundingRect.width() < 0 )
|
---|
| 963 | d_data->boundingRect = br;
|
---|
| 964 | else
|
---|
| 965 | d_data->boundingRect |= br;
|
---|
| 966 | }
|
---|
| 967 |
|
---|
| 968 | void QwtGraphic::updateControlPointRect( const QRectF &rect )
|
---|
| 969 | {
|
---|
| 970 | if ( d_data->pointRect.width() < 0.0 )
|
---|
| 971 | d_data->pointRect = rect;
|
---|
| 972 | else
|
---|
| 973 | d_data->pointRect |= rect;
|
---|
| 974 | }
|
---|
| 975 |
|
---|
| 976 | /*!
|
---|
| 977 | \return List of recorded paint commands
|
---|
| 978 | \sa setCommands()
|
---|
| 979 | */
|
---|
| 980 | const QVector< QwtPainterCommand > &QwtGraphic::commands() const
|
---|
| 981 | {
|
---|
| 982 | return d_data->commands;
|
---|
| 983 | }
|
---|
| 984 |
|
---|
| 985 | /*!
|
---|
| 986 | \brief Append paint commands
|
---|
| 987 |
|
---|
| 988 | \param commands Paint commands
|
---|
| 989 | \sa commands()
|
---|
| 990 | */
|
---|
| 991 | void QwtGraphic::setCommands( QVector< QwtPainterCommand > &commands )
|
---|
| 992 | {
|
---|
| 993 | reset();
|
---|
| 994 |
|
---|
| 995 | const int numCommands = commands.size();
|
---|
| 996 | if ( numCommands <= 0 )
|
---|
| 997 | return;
|
---|
| 998 |
|
---|
| 999 | // to calculate a proper bounding rectangle we don't simply copy
|
---|
| 1000 | // the commands.
|
---|
| 1001 |
|
---|
| 1002 | const QwtPainterCommand *cmds = commands.constData();
|
---|
| 1003 |
|
---|
| 1004 | QPainter painter( this );
|
---|
| 1005 | for ( int i = 0; i < numCommands; i++ )
|
---|
| 1006 | qwtExecCommand( &painter, cmds[i], RenderHints(), QTransform(), NULL );
|
---|
| 1007 |
|
---|
| 1008 | painter.end();
|
---|
| 1009 | }
|
---|