00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include "qwt_graphic.h"
00011 #include "qwt_painter_command.h"
00012 #include <qvector.h>
00013 #include <qpainter.h>
00014 #include <qpaintengine.h>
00015 #include <qimage.h>
00016 #include <qpixmap.h>
00017 #include <qpainterpath.h>
00018 #include <qmath.h>
00019
00020 static bool qwtHasScalablePen( const QPainter *painter )
00021 {
00022 const QPen pen = painter->pen();
00023
00024 bool scalablePen = false;
00025
00026 if ( pen.style() != Qt::NoPen && pen.brush().style() != Qt::NoBrush )
00027 {
00028 scalablePen = !pen.isCosmetic();
00029 if ( !scalablePen && pen.widthF() == 0.0 )
00030 {
00031 const QPainter::RenderHints hints = painter->renderHints();
00032 if ( hints.testFlag( QPainter::NonCosmeticDefaultPen ) )
00033 scalablePen = true;
00034 }
00035 }
00036
00037 return scalablePen;
00038 }
00039
00040 static QRectF qwtStrokedPathRect(
00041 const QPainter *painter, const QPainterPath &path )
00042 {
00043 QPainterPathStroker stroker;
00044 stroker.setWidth( painter->pen().widthF() );
00045 stroker.setCapStyle( painter->pen().capStyle() );
00046 stroker.setJoinStyle( painter->pen().joinStyle() );
00047 stroker.setMiterLimit( painter->pen().miterLimit() );
00048
00049 QRectF rect;
00050 if ( qwtHasScalablePen( painter ) )
00051 {
00052 QPainterPath stroke = stroker.createStroke(path);
00053 rect = painter->transform().map(stroke).boundingRect();
00054 }
00055 else
00056 {
00057 QPainterPath mappedPath = painter->transform().map(path);
00058 mappedPath = stroker.createStroke( mappedPath );
00059
00060 rect = mappedPath.boundingRect();
00061 }
00062
00063 return rect;
00064 }
00065
00066 static inline void qwtExecCommand(
00067 QPainter *painter, const QwtPainterCommand &cmd,
00068 QwtGraphic::RenderHints renderHints,
00069 const QTransform &transform,
00070 const QTransform *initialTransform )
00071 {
00072 switch( cmd.type() )
00073 {
00074 case QwtPainterCommand::Path:
00075 {
00076 bool doMap = false;
00077
00078 if ( renderHints.testFlag( QwtGraphic::RenderPensUnscaled )
00079 && painter->transform().isScaling() )
00080 {
00081 bool isCosmetic = painter->pen().isCosmetic();
00082 if ( isCosmetic && painter->pen().widthF() == 0.0 )
00083 {
00084 QPainter::RenderHints hints = painter->renderHints();
00085 if ( hints.testFlag( QPainter::NonCosmeticDefaultPen ) )
00086 isCosmetic = false;
00087 }
00088
00089 doMap = !isCosmetic;
00090 }
00091
00092 if ( doMap )
00093 {
00094 const QTransform tr = painter->transform();
00095
00096 painter->resetTransform();
00097
00098 QPainterPath path = tr.map( *cmd.path() );
00099 if ( initialTransform )
00100 {
00101 painter->setTransform( *initialTransform );
00102 path = initialTransform->inverted().map( path );
00103 }
00104
00105 painter->drawPath( path );
00106
00107 painter->setTransform( tr );
00108 }
00109 else
00110 {
00111 painter->drawPath( *cmd.path() );
00112 }
00113 break;
00114 }
00115 case QwtPainterCommand::Pixmap:
00116 {
00117 const QwtPainterCommand::PixmapData *data = cmd.pixmapData();
00118 painter->drawPixmap( data->rect, data->pixmap, data->subRect );
00119 break;
00120 }
00121 case QwtPainterCommand::Image:
00122 {
00123 const QwtPainterCommand::ImageData *data = cmd.imageData();
00124 painter->drawImage( data->rect, data->image,
00125 data->subRect, data->flags );
00126 break;
00127 }
00128 case QwtPainterCommand::State:
00129 {
00130 const QwtPainterCommand::StateData *data = cmd.stateData();
00131
00132 if ( data->flags & QPaintEngine::DirtyPen )
00133 painter->setPen( data->pen );
00134
00135 if ( data->flags & QPaintEngine::DirtyBrush )
00136 painter->setBrush( data->brush );
00137
00138 if ( data->flags & QPaintEngine::DirtyBrushOrigin )
00139 painter->setBrushOrigin( data->brushOrigin );
00140
00141 if ( data->flags & QPaintEngine::DirtyFont )
00142 painter->setFont( data->font );
00143
00144 if ( data->flags & QPaintEngine::DirtyBackground )
00145 {
00146 painter->setBackgroundMode( data->backgroundMode );
00147 painter->setBackground( data->backgroundBrush );
00148 }
00149
00150 if ( data->flags & QPaintEngine::DirtyTransform )
00151 {
00152 painter->setTransform( data->transform * transform );
00153 }
00154
00155 if ( data->flags & QPaintEngine::DirtyClipEnabled )
00156 painter->setClipping( data->isClipEnabled );
00157
00158 if ( data->flags & QPaintEngine::DirtyClipRegion)
00159 {
00160 painter->setClipRegion( data->clipRegion,
00161 data->clipOperation );
00162 }
00163
00164 if ( data->flags & QPaintEngine::DirtyClipPath )
00165 {
00166 painter->setClipPath( data->clipPath, data->clipOperation );
00167 }
00168
00169 if ( data->flags & QPaintEngine::DirtyHints)
00170 {
00171 const QPainter::RenderHints hints = data->renderHints;
00172
00173 painter->setRenderHint( QPainter::Antialiasing,
00174 hints.testFlag( QPainter::Antialiasing ) );
00175
00176 painter->setRenderHint( QPainter::TextAntialiasing,
00177 hints.testFlag( QPainter::TextAntialiasing ) );
00178
00179 painter->setRenderHint( QPainter::SmoothPixmapTransform,
00180 hints.testFlag( QPainter::SmoothPixmapTransform ) );
00181
00182 painter->setRenderHint( QPainter::HighQualityAntialiasing,
00183 hints.testFlag( QPainter::HighQualityAntialiasing ) );
00184
00185 painter->setRenderHint( QPainter::NonCosmeticDefaultPen,
00186 hints.testFlag( QPainter::NonCosmeticDefaultPen ) );
00187 }
00188
00189 if ( data->flags & QPaintEngine::DirtyCompositionMode)
00190 painter->setCompositionMode( data->compositionMode );
00191
00192 if ( data->flags & QPaintEngine::DirtyOpacity)
00193 painter->setOpacity( data->opacity );
00194
00195 break;
00196 }
00197 default:
00198 break;
00199 }
00200
00201 }
00202
00203 class QwtGraphic::PathInfo
00204 {
00205 public:
00206 PathInfo():
00207 d_scalablePen( false )
00208 {
00209
00210 }
00211
00212 PathInfo( const QRectF &pointRect,
00213 const QRectF &boundingRect, bool scalablePen ):
00214 d_pointRect( pointRect ),
00215 d_boundingRect( boundingRect ),
00216 d_scalablePen( scalablePen )
00217 {
00218 }
00219
00220 inline QRectF scaledBoundingRect( double sx, double sy,
00221 bool scalePens ) const
00222 {
00223 if ( sx == 1.0 && sy == 1.0 )
00224 return d_boundingRect;
00225
00226 QTransform transform;
00227 transform.scale( sx, sy );
00228
00229 QRectF rect;
00230 if ( scalePens && d_scalablePen )
00231 {
00232 rect = transform.mapRect( d_boundingRect );
00233 }
00234 else
00235 {
00236 rect = transform.mapRect( d_pointRect );
00237
00238 const double l = qAbs( d_pointRect.left() - d_boundingRect.left() );
00239 const double r = qAbs( d_pointRect.right() - d_boundingRect.right() );
00240 const double t = qAbs( d_pointRect.top() - d_boundingRect.top() );
00241 const double b = qAbs( d_pointRect.bottom() - d_boundingRect.bottom() );
00242
00243 rect.adjust( -l, -t, r, b );
00244 }
00245
00246 return rect;
00247 }
00248
00249 inline double scaleFactorX( const QRectF& pathRect,
00250 const QRectF &targetRect, bool scalePens ) const
00251 {
00252 if ( pathRect.width() <= 0.0 )
00253 return 0.0;
00254
00255 const QPointF p0 = d_pointRect.center();
00256
00257 const double l = qAbs( pathRect.left() - p0.x() );
00258 const double r = qAbs( pathRect.right() - p0.x() );
00259
00260 const double w = 2.0 * qMin( l, r )
00261 * targetRect.width() / pathRect.width();
00262
00263 double sx;
00264 if ( scalePens && d_scalablePen )
00265 {
00266 sx = w / d_boundingRect.width();
00267 }
00268 else
00269 {
00270 const double pw = qMax(
00271 qAbs( d_boundingRect.left() - d_pointRect.left() ),
00272 qAbs( d_boundingRect.right() - d_pointRect.right() ) );
00273
00274 sx = ( w - 2 * pw ) / d_pointRect.width();
00275 }
00276
00277 return sx;
00278 }
00279
00280 inline double scaleFactorY( const QRectF& pathRect,
00281 const QRectF &targetRect, bool scalePens ) const
00282 {
00283 if ( pathRect.height() <= 0.0 )
00284 return 0.0;
00285
00286 const QPointF p0 = d_pointRect.center();
00287
00288 const double t = qAbs( pathRect.top() - p0.y() );
00289 const double b = qAbs( pathRect.bottom() - p0.y() );
00290
00291 const double h = 2.0 * qMin( t, b )
00292 * targetRect.height() / pathRect.height();
00293
00294 double sy;
00295 if ( scalePens && d_scalablePen )
00296 {
00297 sy = h / d_boundingRect.height();
00298 }
00299 else
00300 {
00301 const double pw =
00302 qMax( qAbs( d_boundingRect.top() - d_pointRect.top() ),
00303 qAbs( d_boundingRect.bottom() - d_pointRect.bottom() ) );
00304
00305 sy = ( h - 2 * pw ) / d_pointRect.height();
00306 }
00307
00308 return sy;
00309 }
00310
00311 private:
00312 QRectF d_pointRect;
00313 QRectF d_boundingRect;
00314 bool d_scalablePen;
00315 };
00316
00317 class QwtGraphic::PrivateData
00318 {
00319 public:
00320 PrivateData():
00321 boundingRect( 0.0, 0.0, -1.0, -1.0 ),
00322 pointRect( 0.0, 0.0, -1.0, -1.0 ),
00323 initialTransform( NULL )
00324 {
00325 }
00326
00327 QSizeF defaultSize;
00328 QVector<QwtPainterCommand> commands;
00329 QVector<QwtGraphic::PathInfo> pathInfos;
00330
00331 QRectF boundingRect;
00332 QRectF pointRect;
00333
00334 QwtGraphic::RenderHints renderHints;
00335 QTransform *initialTransform;
00336 };
00337
00344 QwtGraphic::QwtGraphic():
00345 QwtNullPaintDevice()
00346 {
00347 setMode( QwtNullPaintDevice::PathMode );
00348 d_data = new PrivateData;
00349 }
00350
00357 QwtGraphic::QwtGraphic( const QwtGraphic &other ):
00358 QwtNullPaintDevice()
00359 {
00360 setMode( other.mode() );
00361 d_data = new PrivateData( *other.d_data );
00362 }
00363
00365 QwtGraphic::~QwtGraphic()
00366 {
00367 delete d_data;
00368 }
00369
00376 QwtGraphic& QwtGraphic::operator=(const QwtGraphic &other)
00377 {
00378 setMode( other.mode() );
00379 *d_data = *other.d_data;
00380
00381 return *this;
00382 }
00383
00388 void QwtGraphic::reset()
00389 {
00390 d_data->commands.clear();
00391 d_data->pathInfos.clear();
00392
00393 d_data->boundingRect = QRectF( 0.0, 0.0, -1.0, -1.0 );
00394 d_data->pointRect = QRectF( 0.0, 0.0, -1.0, -1.0 );
00395 d_data->defaultSize = QSizeF();
00396
00397 }
00398
00403 bool QwtGraphic::isNull() const
00404 {
00405 return d_data->commands.isEmpty();
00406 }
00407
00412 bool QwtGraphic::isEmpty() const
00413 {
00414 return d_data->boundingRect.isEmpty();
00415 }
00416
00425 void QwtGraphic::setRenderHint( RenderHint hint, bool on )
00426 {
00427 if ( on )
00428 d_data->renderHints |= hint;
00429 else
00430 d_data->renderHints &= ~hint;
00431 }
00432
00440 bool QwtGraphic::testRenderHint( RenderHint hint ) const
00441 {
00442 return d_data->renderHints.testFlag( hint );
00443 }
00444
00453 QRectF QwtGraphic::boundingRect() const
00454 {
00455 if ( d_data->boundingRect.width() < 0 )
00456 return QRectF();
00457
00458 return d_data->boundingRect;
00459 }
00460
00469 QRectF QwtGraphic::controlPointRect() const
00470 {
00471 if ( d_data->pointRect.width() < 0 )
00472 return QRectF();
00473
00474 return d_data->pointRect;
00475 }
00476
00490 QRectF QwtGraphic::scaledBoundingRect( double sx, double sy ) const
00491 {
00492 if ( sx == 1.0 && sy == 1.0 )
00493 return d_data->boundingRect;
00494
00495 QTransform transform;
00496 transform.scale( sx, sy );
00497
00498 QRectF rect = transform.mapRect( d_data->pointRect );
00499
00500 for ( int i = 0; i < d_data->pathInfos.size(); i++ )
00501 {
00502 rect |= d_data->pathInfos[i].scaledBoundingRect( sx, sy,
00503 !d_data->renderHints.testFlag( RenderPensUnscaled ) );
00504 }
00505
00506 return rect;
00507 }
00508
00510 QSize QwtGraphic::sizeMetrics() const
00511 {
00512 const QSizeF sz = defaultSize();
00513 return QSize( qCeil( sz.width() ), qCeil( sz.height() ) );
00514 }
00515
00530 void QwtGraphic::setDefaultSize( const QSizeF &size )
00531 {
00532 const double w = qMax( qreal( 0.0 ), size.width() );
00533 const double h = qMax( qreal( 0.0 ), size.height() );
00534
00535 d_data->defaultSize = QSizeF( w, h );
00536 }
00537
00551 QSizeF QwtGraphic::defaultSize() const
00552 {
00553 if ( !d_data->defaultSize.isEmpty() )
00554 return d_data->defaultSize;
00555
00556 return boundingRect().size();
00557 }
00558
00563 void QwtGraphic::render( QPainter *painter ) const
00564 {
00565 if ( isNull() )
00566 return;
00567
00568 const int numCommands = d_data->commands.size();
00569 const QwtPainterCommand *commands = d_data->commands.constData();
00570
00571 const QTransform transform = painter->transform();
00572
00573 painter->save();
00574
00575 for ( int i = 0; i < numCommands; i++ )
00576 {
00577 qwtExecCommand( painter, commands[i],
00578 d_data->renderHints, transform, d_data->initialTransform );
00579 }
00580
00581 painter->restore();
00582 }
00583
00594 void QwtGraphic::render( QPainter *painter, const QSizeF &size,
00595 Qt::AspectRatioMode aspectRatioMode ) const
00596 {
00597 const QRectF r( 0.0, 0.0, size.width(), size.height() );
00598 render( painter, r, aspectRatioMode );
00599 }
00600
00610 void QwtGraphic::render( QPainter *painter, const QRectF &rect,
00611 Qt::AspectRatioMode aspectRatioMode ) const
00612 {
00613 if ( isEmpty() || rect.isEmpty() )
00614 return;
00615
00616 double sx = 1.0;
00617 double sy = 1.0;
00618
00619 if ( d_data->pointRect.width() > 0.0 )
00620 sx = rect.width() / d_data->pointRect.width();
00621
00622 if ( d_data->pointRect.height() > 0.0 )
00623 sy = rect.height() / d_data->pointRect.height();
00624
00625 const bool scalePens =
00626 !d_data->renderHints.testFlag( RenderPensUnscaled );
00627
00628 for ( int i = 0; i < d_data->pathInfos.size(); i++ )
00629 {
00630 const PathInfo info = d_data->pathInfos[i];
00631
00632 const double ssx = info.scaleFactorX(
00633 d_data->pointRect, rect, scalePens );
00634
00635 if ( ssx > 0.0 )
00636 sx = qMin( sx, ssx );
00637
00638 const double ssy = info.scaleFactorY(
00639 d_data->pointRect, rect, scalePens );
00640
00641 if ( ssy > 0.0 )
00642 sy = qMin( sy, ssy );
00643 }
00644
00645 if ( aspectRatioMode == Qt::KeepAspectRatio )
00646 {
00647 const double s = qMin( sx, sy );
00648 sx = s;
00649 sy = s;
00650 }
00651 else if ( aspectRatioMode == Qt::KeepAspectRatioByExpanding )
00652 {
00653 const double s = qMax( sx, sy );
00654 sx = s;
00655 sy = s;
00656 }
00657
00658 QTransform tr;
00659 tr.translate( rect.center().x() - 0.5 * sx * d_data->pointRect.width(),
00660 rect.center().y() - 0.5 * sy * d_data->pointRect.height() );
00661 tr.scale( sx, sy );
00662 tr.translate( -d_data->pointRect.x(), -d_data->pointRect.y() );
00663
00664 const QTransform transform = painter->transform();
00665 if ( !scalePens && transform.isScaling() )
00666 {
00667
00668
00669
00670
00671 d_data->initialTransform = new QTransform();
00672 d_data->initialTransform->scale( transform.m11(), transform.m22() );
00673 }
00674
00675 painter->setTransform( tr, true );
00676 render( painter );
00677
00678 painter->setTransform( transform );
00679
00680 delete d_data->initialTransform;
00681 d_data->initialTransform = NULL;
00682 }
00683
00695 void QwtGraphic::render( QPainter *painter,
00696 const QPointF &pos, Qt::Alignment alignment ) const
00697 {
00698 QRectF r( pos, defaultSize() );
00699
00700 if ( alignment & Qt::AlignLeft )
00701 {
00702 r.moveLeft( pos.x() );
00703 }
00704 else if ( alignment & Qt::AlignHCenter )
00705 {
00706 r.moveCenter( QPointF( pos.x(), r.center().y() ) );
00707 }
00708 else if ( alignment & Qt::AlignRight )
00709 {
00710 r.moveRight( pos.x() );
00711 }
00712
00713 if ( alignment & Qt::AlignTop )
00714 {
00715 r.moveTop( pos.y() );
00716 }
00717 else if ( alignment & Qt::AlignVCenter )
00718 {
00719 r.moveCenter( QPointF( r.center().x(), pos.y() ) );
00720 }
00721 else if ( alignment & Qt::AlignBottom )
00722 {
00723 r.moveBottom( pos.y() );
00724 }
00725
00726 render( painter, r );
00727 }
00728
00741 QPixmap QwtGraphic::toPixmap() const
00742 {
00743 if ( isNull() )
00744 return QPixmap();
00745
00746 const QSizeF sz = defaultSize();
00747
00748 const int w = qCeil( sz.width() );
00749 const int h = qCeil( sz.height() );
00750
00751 QPixmap pixmap( w, h );
00752 pixmap.fill( Qt::transparent );
00753
00754 const QRectF r( 0.0, 0.0, sz.width(), sz.height() );
00755
00756 QPainter painter( &pixmap );
00757 render( &painter, r, Qt::KeepAspectRatio );
00758 painter.end();
00759
00760 return pixmap;
00761 }
00762
00775 QPixmap QwtGraphic::toPixmap( const QSize &size,
00776 Qt::AspectRatioMode aspectRatioMode ) const
00777 {
00778 QPixmap pixmap( size );
00779 pixmap.fill( Qt::transparent );
00780
00781 const QRect r( 0, 0, size.width(), size.height() );
00782
00783 QPainter painter( &pixmap );
00784 render( &painter, r, aspectRatioMode );
00785 painter.end();
00786
00787 return pixmap;
00788 }
00789
00804 QImage QwtGraphic::toImage( const QSize &size,
00805 Qt::AspectRatioMode aspectRatioMode ) const
00806 {
00807 QImage image( size, QImage::Format_ARGB32_Premultiplied );
00808 image.fill( 0 );
00809
00810 const QRect r( 0, 0, size.width(), size.height() );
00811
00812 QPainter painter( &image );
00813 render( &painter, r, aspectRatioMode );
00814 painter.end();
00815
00816 return image;
00817 }
00818
00833 QImage QwtGraphic::toImage() const
00834 {
00835 if ( isNull() )
00836 return QImage();
00837
00838 const QSizeF sz = defaultSize();
00839
00840 const int w = qCeil( sz.width() );
00841 const int h = qCeil( sz.height() );
00842
00843 QImage image( w, h, QImage::Format_ARGB32 );
00844 image.fill( 0 );
00845
00846 const QRect r( 0, 0, sz.width(), sz.height() );
00847
00848 QPainter painter( &image );
00849 render( &painter, r, Qt::KeepAspectRatio );
00850 painter.end();
00851
00852 return image;
00853 }
00854
00861 void QwtGraphic::drawPath( const QPainterPath &path )
00862 {
00863 const QPainter *painter = paintEngine()->painter();
00864 if ( painter == NULL )
00865 return;
00866
00867 d_data->commands += QwtPainterCommand( path );
00868
00869 if ( !path.isEmpty() )
00870 {
00871 const QPainterPath scaledPath = painter->transform().map( path );
00872
00873 QRectF pointRect = scaledPath.boundingRect();
00874 QRectF boundingRect = pointRect;
00875
00876 if ( painter->pen().style() != Qt::NoPen
00877 && painter->pen().brush().style() != Qt::NoBrush )
00878 {
00879 boundingRect = qwtStrokedPathRect( painter, path );
00880 }
00881
00882 updateControlPointRect( pointRect );
00883 updateBoundingRect( boundingRect );
00884
00885 d_data->pathInfos += PathInfo( pointRect,
00886 boundingRect, qwtHasScalablePen( painter ) );
00887 }
00888 }
00889
00899 void QwtGraphic::drawPixmap( const QRectF &rect,
00900 const QPixmap &pixmap, const QRectF &subRect )
00901 {
00902 const QPainter *painter = paintEngine()->painter();
00903 if ( painter == NULL )
00904 return;
00905
00906 d_data->commands += QwtPainterCommand( rect, pixmap, subRect );
00907
00908 const QRectF r = painter->transform().mapRect( rect );
00909 updateControlPointRect( r );
00910 updateBoundingRect( r );
00911 }
00912
00923 void QwtGraphic::drawImage( const QRectF &rect, const QImage &image,
00924 const QRectF &subRect, Qt::ImageConversionFlags flags)
00925 {
00926 const QPainter *painter = paintEngine()->painter();
00927 if ( painter == NULL )
00928 return;
00929
00930 d_data->commands += QwtPainterCommand( rect, image, subRect, flags );
00931
00932 const QRectF r = painter->transform().mapRect( rect );
00933
00934 updateControlPointRect( r );
00935 updateBoundingRect( r );
00936 }
00937
00944 void QwtGraphic::updateState( const QPaintEngineState &state)
00945 {
00946 d_data->commands += QwtPainterCommand( state );
00947 }
00948
00949 void QwtGraphic::updateBoundingRect( const QRectF &rect )
00950 {
00951 QRectF br = rect;
00952
00953 const QPainter *painter = paintEngine()->painter();
00954 if ( painter && painter->hasClipping() )
00955 {
00956 QRectF cr = painter->clipRegion().boundingRect();
00957 cr = painter->transform().mapRect( cr );
00958
00959 br &= cr;
00960 }
00961
00962 if ( d_data->boundingRect.width() < 0 )
00963 d_data->boundingRect = br;
00964 else
00965 d_data->boundingRect |= br;
00966 }
00967
00968 void QwtGraphic::updateControlPointRect( const QRectF &rect )
00969 {
00970 if ( d_data->pointRect.width() < 0.0 )
00971 d_data->pointRect = rect;
00972 else
00973 d_data->pointRect |= rect;
00974 }
00975
00980 const QVector< QwtPainterCommand > &QwtGraphic::commands() const
00981 {
00982 return d_data->commands;
00983 }
00984
00991 void QwtGraphic::setCommands( QVector< QwtPainterCommand > &commands )
00992 {
00993 reset();
00994
00995 const int numCommands = commands.size();
00996 if ( numCommands <= 0 )
00997 return;
00998
00999
01000
01001
01002 const QwtPainterCommand *cmds = commands.constData();
01003
01004 QPainter painter( this );
01005 for ( int i = 0; i < numCommands; i++ )
01006 qwtExecCommand( &painter, cmds[i], RenderHints(), QTransform(), NULL );
01007
01008 painter.end();
01009 }