mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-17 00:04:02 -04:00
Threading - WIP
This commit is contained in:
parent
247e6c762d
commit
b04f81a12b
@ -33,8 +33,6 @@ class QgsMapCanvasMap : QGraphicsRectItem
|
||||
|
||||
void enableAntiAliasing( bool flag );
|
||||
|
||||
void useImageToRender( bool flag );
|
||||
|
||||
//! renders map using QgsMapRenderer to mPixmap
|
||||
void render();
|
||||
|
||||
|
@ -462,6 +462,11 @@ ELSE (ANDROID)
|
||||
ADD_EXECUTABLE(${QGIS_APP_NAME} MACOSX_BUNDLE WIN32 main.cpp ${QGIS_APP_SRCS} ${QGIS_APP_MOC_SRCS} ${IMAGE_RCC_SRCS} ${TEST_RCC_SRCS})
|
||||
ENDIF (ANDROID)
|
||||
|
||||
|
||||
QT4_WRAP_CPP(tst_moc_srcs maprenderertest.h)
|
||||
ADD_EXECUTABLE(maprenderertest maprenderertest.cpp ${tst_moc_srcs})
|
||||
TARGET_LINK_LIBRARIES(maprenderertest qgis_core qgis_gui)
|
||||
|
||||
IF (WIN32)
|
||||
IF (MSVC)
|
||||
ADD_DEFINITIONS("-DAPP_EXPORT=${DLLEXPORT}")
|
||||
|
61
src/app/maprenderertest.cpp
Normal file
61
src/app/maprenderertest.cpp
Normal file
@ -0,0 +1,61 @@
|
||||
|
||||
#include "maprenderertest.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QPainter>
|
||||
#include <QPaintEvent>
|
||||
|
||||
#include "qgsapplication.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgsmaplayerregistry.h"
|
||||
|
||||
#include "qgsmapcanvas.h"
|
||||
#include "qgsmaptoolpan.h"
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
QApplication app(argc, argv);
|
||||
|
||||
QgsApplication::setPrefixPath("/home/martin/qgis/git-master/creator/output", true);
|
||||
QgsApplication::initQgis();
|
||||
|
||||
QString uri = "dbname='/data/gis/praha.osm.db' table=\"praha_polygons\" (geometry) sql=";
|
||||
QString uri2 = "dbname='/data/gis/praha.osm.db' table=\"praha_polylines\" (geometry) sql=";
|
||||
//QString uri = "/data/gis/cr-shp-wgs84/plochy/kraje_pseudo.shp";
|
||||
QgsVectorLayer* layer = new QgsVectorLayer(uri, "praha", "spatialite");
|
||||
if (!layer->isValid())
|
||||
{
|
||||
qDebug("invalid layer");
|
||||
return 1;
|
||||
}
|
||||
QgsMapLayerRegistry::instance()->addMapLayer(layer);
|
||||
|
||||
QgsVectorLayer* layer2 = new QgsVectorLayer(uri2, "praha", "spatialite");
|
||||
if (!layer2->isValid())
|
||||
{
|
||||
qDebug("invalid layer");
|
||||
return 1;
|
||||
}
|
||||
QgsMapLayerRegistry::instance()->addMapLayer(layer2);
|
||||
|
||||
// open a window and do the rendering!
|
||||
/*TestWidget l(layer);
|
||||
l.resize(360,360);
|
||||
l.show();*/
|
||||
|
||||
QgsMapCanvas canvas;
|
||||
canvas.setCanvasColor(Qt::white);
|
||||
canvas.setExtent(layer->extent());
|
||||
|
||||
QList<QgsMapCanvasLayer> layers;
|
||||
layers.append(QgsMapCanvasLayer(layer2));
|
||||
layers.append(QgsMapCanvasLayer(layer));
|
||||
canvas.setLayerSet(layers);
|
||||
|
||||
QgsMapTool* pan = new QgsMapToolPan(&canvas);
|
||||
canvas.setMapTool(pan);
|
||||
|
||||
canvas.show();
|
||||
|
||||
return app.exec();
|
||||
}
|
102
src/app/maprenderertest.h
Normal file
102
src/app/maprenderertest.h
Normal file
@ -0,0 +1,102 @@
|
||||
#ifndef MAPRENDERERTEST_H
|
||||
#define MAPRENDERERTEST_H
|
||||
|
||||
#include <QLabel>
|
||||
#include <QPainter>
|
||||
#include <QTimer>
|
||||
#include <QMouseEvent>
|
||||
|
||||
#include "qgsmaprendererv2.h"
|
||||
#include "qgsmaplayer.h"
|
||||
|
||||
class TestWidget : public QLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TestWidget(QgsMapLayer* layer)
|
||||
{
|
||||
//p = QPixmap(200,200);
|
||||
//p.fill(Qt::red);
|
||||
|
||||
i = QImage(size(), QImage::Format_ARGB32_Premultiplied);
|
||||
i.fill(Qt::gray);
|
||||
|
||||
// init renderer
|
||||
rend.setLayers(QStringList(layer->id()));
|
||||
rend.setExtent(layer->extent());
|
||||
rend.setOutputSize(i.size());
|
||||
rend.setOutputDpi(120);
|
||||
rend.updateDerived();
|
||||
|
||||
if (rend.hasValidSettings())
|
||||
qDebug("map renderer settings valid");
|
||||
|
||||
connect(&rend, SIGNAL(finished()), SLOT(f()));
|
||||
|
||||
setPixmap(QPixmap::fromImage(i));
|
||||
|
||||
connect(&timer, SIGNAL(timeout()), SLOT(onMapUpdateTimeout()));
|
||||
timer.setInterval(100);
|
||||
}
|
||||
|
||||
void mousePressEvent(QMouseEvent * event)
|
||||
{
|
||||
if (event->button() == Qt::RightButton)
|
||||
{
|
||||
qDebug("cancelling!");
|
||||
|
||||
rend.cancel();
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug("starting!");
|
||||
|
||||
if (rend.isRendering())
|
||||
{
|
||||
qDebug("need to cancel first!");
|
||||
rend.cancel();
|
||||
|
||||
// TODO: need to ensure that finished slot has been called
|
||||
}
|
||||
|
||||
i.fill(Qt::gray);
|
||||
|
||||
painter = new QPainter(&i);
|
||||
rend.startWithCustomPainter(painter);
|
||||
timer.start();
|
||||
}
|
||||
}
|
||||
|
||||
protected slots:
|
||||
void f()
|
||||
{
|
||||
qDebug("finished!");
|
||||
|
||||
painter->end();
|
||||
delete painter;
|
||||
|
||||
timer.stop();
|
||||
|
||||
//update();
|
||||
|
||||
setPixmap(QPixmap::fromImage(i));
|
||||
}
|
||||
|
||||
void onMapUpdateTimeout()
|
||||
{
|
||||
qDebug("update timer!");
|
||||
|
||||
setPixmap(QPixmap::fromImage(i));
|
||||
}
|
||||
|
||||
protected:
|
||||
//QPixmap p;
|
||||
QImage i;
|
||||
QPainter* painter;
|
||||
QgsMapRendererV2 rend;
|
||||
QTimer timer;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // MAPRENDERERTEST_H
|
@ -3,6 +3,9 @@
|
||||
|
||||
SET(QGIS_CORE_SRCS
|
||||
|
||||
qgsmaprendererv2.cpp
|
||||
qgsmaprendererjob.cpp
|
||||
|
||||
gps/qgsgpsconnection.cpp
|
||||
gps/qgsgpsconnectionregistry.cpp
|
||||
gps/qgsnmeaconnection.cpp
|
||||
@ -293,6 +296,10 @@ ADD_FLEX_FILES(QGIS_CORE_SRCS qgsexpressionlexer.ll)
|
||||
ADD_BISON_FILES(QGIS_CORE_SRCS qgsexpressionparser.yy)
|
||||
|
||||
SET(QGIS_CORE_MOC_HDRS
|
||||
|
||||
qgsmaprendererv2.h
|
||||
qgsmaprendererjob.h
|
||||
|
||||
qgsapplication.h
|
||||
qgsbrowsermodel.h
|
||||
qgscontexthelp.h
|
||||
|
@ -73,16 +73,11 @@ QgsMapLayer::QgsMapLayer( QgsMapLayer::LayerType type,
|
||||
mMinScale = 0;
|
||||
mMaxScale = 100000000;
|
||||
mScaleBasedVisibility = false;
|
||||
mpCacheImage = 0;
|
||||
}
|
||||
|
||||
QgsMapLayer::~QgsMapLayer()
|
||||
{
|
||||
delete mCRS;
|
||||
if ( mpCacheImage )
|
||||
{
|
||||
delete mpCacheImage;
|
||||
}
|
||||
}
|
||||
|
||||
QgsMapLayer::LayerType QgsMapLayer::type() const
|
||||
@ -1334,20 +1329,6 @@ void QgsMapLayer::writeCustomProperties( QDomNode & layerNode, QDomDocument & do
|
||||
layerNode.appendChild( propsElement );
|
||||
}
|
||||
|
||||
void QgsMapLayer::setCacheImage( QImage * thepImage )
|
||||
{
|
||||
QgsDebugMsg( "cache Image set!" );
|
||||
if ( mpCacheImage == thepImage )
|
||||
return;
|
||||
|
||||
if ( mpCacheImage )
|
||||
{
|
||||
onCacheImageDelete();
|
||||
delete mpCacheImage;
|
||||
}
|
||||
mpCacheImage = thepImage;
|
||||
}
|
||||
|
||||
bool QgsMapLayer::isEditable() const
|
||||
{
|
||||
return false;
|
||||
|
@ -360,17 +360,11 @@ class CORE_EXPORT QgsMapLayer : public QObject
|
||||
/** Return pointer to layer's undo stack */
|
||||
QUndoStack* undoStack();
|
||||
|
||||
/** Get the QImage used for caching render operations
|
||||
* @note This method was added in QGIS 1.4 **/
|
||||
QImage *cacheImage() { return mpCacheImage; }
|
||||
/** Set the QImage used for caching render operations
|
||||
* @note This method was added in QGIS 1.4 **/
|
||||
void setCacheImage( QImage * thepImage );
|
||||
|
||||
/**
|
||||
* @brief Is called when the cache image is being deleted. Overwrite and use to clean up.
|
||||
* @note added in 2.0
|
||||
*/
|
||||
/** \note Deprecated from 2.1 - returns NULL */
|
||||
QImage *cacheImage() { return 0; }
|
||||
/** \note Deprecated from 2.1 - does nothing */
|
||||
void setCacheImage( QImage * ) {}
|
||||
/** @note Deprecated from 2.1 - does nothing */
|
||||
virtual void onCacheImageDelete() {}
|
||||
|
||||
public slots:
|
||||
@ -421,8 +415,7 @@ class CORE_EXPORT QgsMapLayer : public QObject
|
||||
*/
|
||||
void repaintRequested();
|
||||
|
||||
/**The layer emits this signal when a screen update is requested.
|
||||
This signal should be connected with the slot QgsMapCanvas::updateMap()*/
|
||||
//! \note Deprecated in 2.1 and not emitted anymore
|
||||
void screenUpdateRequested();
|
||||
|
||||
/** This is used to send a request that any mapcanvas using this layer update its extents */
|
||||
@ -544,10 +537,6 @@ class CORE_EXPORT QgsMapLayer : public QObject
|
||||
|
||||
QMap<QString, QVariant> mCustomProperties;
|
||||
|
||||
/**QImage for caching of rendering operations
|
||||
* @note This property was added in QGIS 1.4 **/
|
||||
QImage * mpCacheImage;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
556
src/core/qgsmaprendererjob.cpp
Normal file
556
src/core/qgsmaprendererjob.cpp
Normal file
@ -0,0 +1,556 @@
|
||||
|
||||
#include "qgsmaprendererjob.h"
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
#include "qgslogger.h"
|
||||
#include "qgsrendercontext.h"
|
||||
#include "qgsmaplayer.h"
|
||||
#include "qgsmaplayerregistry.h"
|
||||
|
||||
QgsMapRendererSequentialJob::QgsMapRendererSequentialJob(const QgsMapRendererSettings& settings)
|
||||
: QgsMapRendererJob(SequentialJob, settings)
|
||||
, mInternalJob(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void QgsMapRendererSequentialJob::start()
|
||||
{
|
||||
mImage.fill(Qt::blue);
|
||||
|
||||
// 1. create an image where we will output all rendering
|
||||
mPainter = new QPainter(&mImage);
|
||||
|
||||
// 2. start rendering the layers in a thread (using custom painter)
|
||||
mInternalJob = new QgsMapRendererCustomPainterJob(mSettings, mPainter);
|
||||
mInternalJob->start();
|
||||
|
||||
connect(mInternalJob, SIGNAL(finished()), SLOT(internalFinished()));
|
||||
}
|
||||
|
||||
|
||||
void QgsMapRendererSequentialJob::cancel()
|
||||
{
|
||||
mInternalJob->cancel();
|
||||
}
|
||||
|
||||
|
||||
QImage QgsMapRendererSequentialJob::renderedImage()
|
||||
{
|
||||
return mImage;
|
||||
}
|
||||
|
||||
|
||||
void QgsMapRendererSequentialJob::internalFinished()
|
||||
{
|
||||
mPainter->end();
|
||||
delete mPainter;
|
||||
mPainter = 0;
|
||||
|
||||
delete mInternalJob;
|
||||
mInternalJob = 0;
|
||||
}
|
||||
|
||||
|
||||
////////
|
||||
|
||||
|
||||
|
||||
QgsMapRendererCustomPainterJob::QgsMapRendererCustomPainterJob(const QgsMapRendererSettings& settings, QPainter* painter)
|
||||
: QgsMapRendererJob(CustomPainterJob, settings)
|
||||
, mPainter(painter)
|
||||
{
|
||||
connect(&mFutureWatcher, SIGNAL(finished()), SLOT(futureFinished()));
|
||||
}
|
||||
|
||||
void QgsMapRendererCustomPainterJob::start()
|
||||
{
|
||||
qDebug("run!");
|
||||
mFuture = QtConcurrent::run(staticRender, this);
|
||||
mFutureWatcher.setFuture(mFuture);
|
||||
}
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
void QgsMapRendererCustomPainterJob::cancel()
|
||||
{
|
||||
mRenderContext.setRenderingStopped(true);
|
||||
|
||||
mFutureWatcher.waitForFinished();
|
||||
qApp->processEvents( QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers ); // TODO: necessary?
|
||||
}
|
||||
|
||||
|
||||
QImage QgsMapRendererCustomPainterJob::renderedImage()
|
||||
{
|
||||
return QImage();
|
||||
}
|
||||
|
||||
|
||||
void QgsMapRendererCustomPainterJob::futureFinished()
|
||||
{
|
||||
qDebug("futureFinished");
|
||||
emit finished();
|
||||
}
|
||||
|
||||
|
||||
void QgsMapRendererCustomPainterJob::staticRender(QgsMapRendererCustomPainterJob* self)
|
||||
{
|
||||
qDebug("staticRender");
|
||||
self->startRender();
|
||||
}
|
||||
|
||||
|
||||
void QgsMapRendererCustomPainterJob::startRender()
|
||||
{
|
||||
qDebug("startRender");
|
||||
/*
|
||||
for (int i = 0; i < 50; ++i)
|
||||
{
|
||||
if (mStopped)
|
||||
return;
|
||||
|
||||
mPainter->drawLine(rand() % 360, rand() % 360, rand() % 360, rand() % 360);
|
||||
//QThread::msleep(100);
|
||||
int x = 0;
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
for (int j = 0; j < 10000; j++)
|
||||
x *= x+5;
|
||||
}
|
||||
qDebug("drawn line");
|
||||
}*/
|
||||
|
||||
#ifdef QGISDEBUG
|
||||
QgsDebugMsg( "Starting to render layer stack." );
|
||||
QTime renderTime;
|
||||
renderTime.start();
|
||||
#endif
|
||||
|
||||
mRenderContext.setMapToPixel( QgsMapToPixel( mSettings.mapUnitsPerPixel, mSettings.size.height(), mSettings.visibleExtent.yMinimum(), mSettings.visibleExtent.xMinimum() ) );
|
||||
mRenderContext.setExtent( mSettings.visibleExtent );
|
||||
|
||||
mRenderContext.setDrawEditingInformation( false );
|
||||
mRenderContext.setPainter( mPainter );
|
||||
mRenderContext.setCoordinateTransform( 0 );
|
||||
//this flag is only for stopping during the current rendering progress,
|
||||
//so must be false at every new render operation
|
||||
mRenderContext.setRenderingStopped( false );
|
||||
|
||||
// set selection color
|
||||
/* TODO QgsProject* prj = QgsProject::instance();
|
||||
int myRed = prj->readNumEntry( "Gui", "/SelectionColorRedPart", 255 );
|
||||
int myGreen = prj->readNumEntry( "Gui", "/SelectionColorGreenPart", 255 );
|
||||
int myBlue = prj->readNumEntry( "Gui", "/SelectionColorBluePart", 0 );
|
||||
int myAlpha = prj->readNumEntry( "Gui", "/SelectionColorAlphaPart", 255 );*/
|
||||
mRenderContext.setSelectionColor( Qt::red ); // TODO QColor( myRed, myGreen, myBlue, myAlpha ) );
|
||||
|
||||
//calculate scale factor
|
||||
//use the specified dpi and not those from the paint device
|
||||
//because sometimes QPainter units are in a local coord sys (e.g. in case of QGraphicsScene)
|
||||
/* TODO double sceneDpi = mScaleCalculator->dpi();
|
||||
double scaleFactor = 1.0;
|
||||
if ( mOutputUnits == QgsMapRenderer::Millimeters )
|
||||
{
|
||||
if ( forceWidthScale )
|
||||
{
|
||||
scaleFactor = *forceWidthScale;
|
||||
}
|
||||
else
|
||||
{
|
||||
scaleFactor = sceneDpi / 25.4;
|
||||
}
|
||||
}
|
||||
double rasterScaleFactor = ( thePaintDevice->logicalDpiX() + thePaintDevice->logicalDpiY() ) / 2.0 / sceneDpi;
|
||||
if ( mRenderContext.rasterScaleFactor() != rasterScaleFactor )
|
||||
{
|
||||
mRenderContext.setRasterScaleFactor( rasterScaleFactor );
|
||||
mySameAsLastFlag = false;
|
||||
}
|
||||
if ( mRenderContext.scaleFactor() != scaleFactor )
|
||||
{
|
||||
mRenderContext.setScaleFactor( scaleFactor );
|
||||
mySameAsLastFlag = false;
|
||||
}
|
||||
if ( mRenderContext.rendererScale() != mScale )
|
||||
{
|
||||
//add map scale to render context
|
||||
mRenderContext.setRendererScale( mScale );
|
||||
mySameAsLastFlag = false;
|
||||
}
|
||||
if ( mLastExtent != mExtent )
|
||||
{
|
||||
mLastExtent = mExtent;
|
||||
mySameAsLastFlag = false;
|
||||
}
|
||||
|
||||
mRenderContext.setLabelingEngine( mLabelingEngine );
|
||||
if ( mLabelingEngine )
|
||||
mLabelingEngine->init( this );*/
|
||||
|
||||
// know we know if this render is just a repeat of the last time, we
|
||||
// can clear caches if it has changed
|
||||
/*if ( !mySameAsLastFlag )
|
||||
{
|
||||
//clear the cache pixmap if we changed resolution / extent
|
||||
QSettings mySettings;
|
||||
if ( mySettings.value( "/qgis/enable_render_caching", false ).toBool() )
|
||||
{
|
||||
QgsMapLayerRegistry::instance()->clearAllLayerCaches();
|
||||
}
|
||||
}*/
|
||||
|
||||
// render all layers in the stack, starting at the base
|
||||
QListIterator<QString> li( mSettings.layers );
|
||||
li.toBack();
|
||||
|
||||
QgsRectangle r1, r2;
|
||||
|
||||
while ( li.hasPrevious() )
|
||||
{
|
||||
if ( mRenderContext.renderingStopped() )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Store the painter in case we need to swap it out for the
|
||||
// cache painter
|
||||
QPainter * mypContextPainter = mRenderContext.painter();
|
||||
// Flattened image for drawing when a blending mode is set
|
||||
QImage * mypFlattenedImage = 0;
|
||||
|
||||
QString layerId = li.previous();
|
||||
|
||||
QgsDebugMsg( "Rendering at layer item " + layerId );
|
||||
|
||||
// This call is supposed to cause the progress bar to
|
||||
// advance. However, it seems that updating the progress bar is
|
||||
// incompatible with having a QPainter active (the one that is
|
||||
// passed into this function), as Qt produces a number of errors
|
||||
// when try to do so. I'm (Gavin) not sure how to fix this, but
|
||||
// added these comments and debug statement to help others...
|
||||
QgsDebugMsg( "If there is a QPaintEngine error here, it is caused by an emit call" );
|
||||
|
||||
//emit drawingProgress(myRenderCounter++, mLayerSet.size());
|
||||
QgsMapLayer *ml = QgsMapLayerRegistry::instance()->mapLayer( layerId );
|
||||
|
||||
if ( !ml )
|
||||
{
|
||||
QgsDebugMsg( "Layer not found in registry!" );
|
||||
continue;
|
||||
}
|
||||
|
||||
QgsDebugMsg( QString( "layer %1: minscale:%2 maxscale:%3 scaledepvis:%4 extent:%5 blendmode:%6" )
|
||||
.arg( ml->name() )
|
||||
.arg( ml->minimumScale() )
|
||||
.arg( ml->maximumScale() )
|
||||
.arg( ml->hasScaleBasedVisibility() )
|
||||
.arg( ml->extent().toString() )
|
||||
.arg( ml->blendMode() )
|
||||
);
|
||||
|
||||
if ( mRenderContext.useAdvancedEffects() )
|
||||
{
|
||||
// Set the QPainter composition mode so that this layer is rendered using
|
||||
// the desired blending mode
|
||||
mypContextPainter->setCompositionMode( ml->blendMode() );
|
||||
}
|
||||
|
||||
if ( !ml->hasScaleBasedVisibility() || ( ml->minimumScale() <= mSettings.scale && mSettings.scale < ml->maximumScale() ) ) //|| mOverview )
|
||||
{
|
||||
connect( ml, SIGNAL( drawingProgress( int, int ) ), this, SLOT( onDrawingProgress( int, int ) ) );
|
||||
|
||||
//
|
||||
// Now do the call to the layer that actually does
|
||||
// the rendering work!
|
||||
//
|
||||
|
||||
bool split = false;
|
||||
|
||||
/*if ( hasCrsTransformEnabled() )
|
||||
{
|
||||
r1 = mExtent;
|
||||
split = splitLayersExtent( ml, r1, r2 );
|
||||
ct = QgsCoordinateTransformCache::instance()->transform( ml->crs().authid(), mDestCRS->authid() );
|
||||
mRenderContext.setExtent( r1 );
|
||||
QgsDebugMsg( " extent 1: " + r1.toString() );
|
||||
QgsDebugMsg( " extent 2: " + r2.toString() );
|
||||
if ( !r1.isFinite() || !r2.isFinite() ) //there was a problem transforming the extent. Skip the layer
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ct = NULL;
|
||||
}*/
|
||||
|
||||
mRenderContext.setCoordinateTransform( 0 );
|
||||
|
||||
//decide if we have to scale the raster
|
||||
//this is necessary in case QGraphicsScene is used
|
||||
/*bool scaleRaster = false;
|
||||
QgsMapToPixel rasterMapToPixel;
|
||||
QgsMapToPixel bk_mapToPixel;
|
||||
|
||||
if ( ml->type() == QgsMapLayer::RasterLayer && qAbs( rasterScaleFactor - 1.0 ) > 0.000001 )
|
||||
{
|
||||
scaleRaster = true;
|
||||
}*/
|
||||
|
||||
// Force render of layers that are being edited
|
||||
// or if there's a labeling engine that needs the layer to register features
|
||||
/*if ( ml->type() == QgsMapLayer::VectorLayer )
|
||||
{
|
||||
QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml );
|
||||
if ( vl->isEditable() ||
|
||||
( mRenderContext.labelingEngine() && mRenderContext.labelingEngine()->willUseLayer( vl ) ) )
|
||||
{
|
||||
ml->setCacheImage( 0 );
|
||||
}
|
||||
}*/
|
||||
|
||||
/*QSettings mySettings;
|
||||
bool useRenderCaching = false;
|
||||
if ( ! split )//render caching does not yet cater for split extents
|
||||
{
|
||||
if ( mySettings.value( "/qgis/enable_render_caching", false ).toBool() )
|
||||
{
|
||||
useRenderCaching = true;
|
||||
if ( !mySameAsLastFlag || ml->cacheImage() == 0 )
|
||||
{
|
||||
QgsDebugMsg( "Caching enabled but layer redraw forced by extent change or empty cache" );
|
||||
QImage * mypImage = new QImage( mRenderContext.painter()->device()->width(),
|
||||
mRenderContext.painter()->device()->height(), QImage::Format_ARGB32 );
|
||||
if ( mypImage->isNull() )
|
||||
{
|
||||
QgsDebugMsg( "insufficient memory for image " + QString::number( mRenderContext.painter()->device()->width() ) + "x" + QString::number( mRenderContext.painter()->device()->height() ) );
|
||||
emit drawError( ml );
|
||||
painter->end(); // drawError is not caught by anyone, so we end painting to notify caller
|
||||
return;
|
||||
}
|
||||
mypImage->fill( 0 );
|
||||
ml->setCacheImage( mypImage ); //no need to delete the old one, maplayer does it for you
|
||||
QPainter * mypPainter = new QPainter( ml->cacheImage() );
|
||||
// Changed to enable anti aliasing by default in QGIS 1.7
|
||||
if ( mySettings.value( "/qgis/enable_anti_aliasing", true ).toBool() )
|
||||
{
|
||||
mypPainter->setRenderHint( QPainter::Antialiasing );
|
||||
}
|
||||
mRenderContext.setPainter( mypPainter );
|
||||
}
|
||||
else if ( mySameAsLastFlag )
|
||||
{
|
||||
//draw from cached image
|
||||
QgsDebugMsg( "Caching enabled --- drawing layer from cached image" );
|
||||
mypContextPainter->drawImage( 0, 0, *( ml->cacheImage() ) );
|
||||
disconnect( ml, SIGNAL( drawingProgress( int, int ) ), this, SLOT( onDrawingProgress( int, int ) ) );
|
||||
//short circuit as there is nothing else to do...
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
// If we are drawing with an alternative blending mode then we need to render to a separate image
|
||||
// before compositing this on the map. This effectively flattens the layer and prevents
|
||||
// blending occuring between objects on the layer
|
||||
// (this is not required for raster layers or when layer caching is enabled, since that has the same effect)
|
||||
/*bool flattenedLayer = false;
|
||||
if (( mRenderContext.useAdvancedEffects() ) && ( ml->type() == QgsMapLayer::VectorLayer ) )
|
||||
{
|
||||
QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml );
|
||||
if (( !useRenderCaching )
|
||||
&& (( vl->blendMode() != QPainter::CompositionMode_SourceOver )
|
||||
|| ( vl->featureBlendMode() != QPainter::CompositionMode_SourceOver )
|
||||
|| ( vl->layerTransparency() != 0 ) ) )
|
||||
{
|
||||
flattenedLayer = true;
|
||||
mypFlattenedImage = new QImage( mRenderContext.painter()->device()->width(),
|
||||
mRenderContext.painter()->device()->height(), QImage::Format_ARGB32 );
|
||||
if ( mypFlattenedImage->isNull() )
|
||||
{
|
||||
QgsDebugMsg( "insufficient memory for image " + QString::number( mRenderContext.painter()->device()->width() ) + "x" + QString::number( mRenderContext.painter()->device()->height() ) );
|
||||
emit drawError( ml );
|
||||
painter->end(); // drawError is not caught by anyone, so we end painting to notify caller
|
||||
return;
|
||||
}
|
||||
mypFlattenedImage->fill( 0 );
|
||||
QPainter * mypPainter = new QPainter( mypFlattenedImage );
|
||||
if ( mySettings.value( "/qgis/enable_anti_aliasing", true ).toBool() )
|
||||
{
|
||||
mypPainter->setRenderHint( QPainter::Antialiasing );
|
||||
}
|
||||
mypPainter->scale( rasterScaleFactor, rasterScaleFactor );
|
||||
mRenderContext.setPainter( mypPainter );
|
||||
}
|
||||
}*/
|
||||
|
||||
// Per feature blending mode
|
||||
/*if (( mRenderContext.useAdvancedEffects() ) && ( ml->type() == QgsMapLayer::VectorLayer ) )
|
||||
{
|
||||
QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml );
|
||||
if ( vl->featureBlendMode() != QPainter::CompositionMode_SourceOver )
|
||||
{
|
||||
// set the painter to the feature blend mode, so that features drawn
|
||||
// on this layer will interact and blend with each other
|
||||
mRenderContext.painter()->setCompositionMode( vl->featureBlendMode() );
|
||||
}
|
||||
}*/
|
||||
|
||||
/*if ( scaleRaster )
|
||||
{
|
||||
bk_mapToPixel = mRenderContext.mapToPixel();
|
||||
rasterMapToPixel = mRenderContext.mapToPixel();
|
||||
rasterMapToPixel.setMapUnitsPerPixel( mRenderContext.mapToPixel().mapUnitsPerPixel() / rasterScaleFactor );
|
||||
rasterMapToPixel.setYMaximum( mSize.height() * rasterScaleFactor );
|
||||
mRenderContext.setMapToPixel( rasterMapToPixel );
|
||||
mRenderContext.painter()->save();
|
||||
mRenderContext.painter()->scale( 1.0 / rasterScaleFactor, 1.0 / rasterScaleFactor );
|
||||
}*/
|
||||
|
||||
if ( !ml->draw( mRenderContext ) )
|
||||
{
|
||||
// TODO emit drawError( ml );
|
||||
}
|
||||
else
|
||||
{
|
||||
QgsDebugMsg( "Layer rendered without issues" );
|
||||
}
|
||||
|
||||
if ( split )
|
||||
{
|
||||
mRenderContext.setExtent( r2 );
|
||||
if ( !ml->draw( mRenderContext ) )
|
||||
{
|
||||
// TODO emit drawError( ml );
|
||||
}
|
||||
}
|
||||
|
||||
/*if ( scaleRaster )
|
||||
{
|
||||
mRenderContext.setMapToPixel( bk_mapToPixel );
|
||||
mRenderContext.painter()->restore();
|
||||
}*/
|
||||
|
||||
//apply layer transparency for vector layers
|
||||
/*if (( mRenderContext.useAdvancedEffects() ) && ( ml->type() == QgsMapLayer::VectorLayer ) )
|
||||
{
|
||||
QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml );
|
||||
if ( vl->layerTransparency() != 0 )
|
||||
{
|
||||
// a layer transparency has been set, so update the alpha for the flattened layer
|
||||
// by combining it with the layer transparency
|
||||
QColor transparentFillColor = QColor( 0, 0, 0, 255 - ( 255 * vl->layerTransparency() / 100 ) );
|
||||
// use destination in composition mode to merge source's alpha with destination
|
||||
mRenderContext.painter()->setCompositionMode( QPainter::CompositionMode_DestinationIn );
|
||||
mRenderContext.painter()->fillRect( 0, 0, mRenderContext.painter()->device()->width(),
|
||||
mRenderContext.painter()->device()->height(), transparentFillColor );
|
||||
}
|
||||
}*/
|
||||
|
||||
/*if ( useRenderCaching )
|
||||
{
|
||||
// composite the cached image into our view and then clean up from caching
|
||||
// by reinstating the painter as it was swapped out for caching renders
|
||||
delete mRenderContext.painter();
|
||||
mRenderContext.setPainter( mypContextPainter );
|
||||
//draw from cached image that we created further up
|
||||
if ( ml->cacheImage() )
|
||||
mypContextPainter->drawImage( 0, 0, *( ml->cacheImage() ) );
|
||||
}
|
||||
else if ( flattenedLayer )
|
||||
{
|
||||
// If we flattened this layer for alternate blend modes, composite it now
|
||||
delete mRenderContext.painter();
|
||||
mRenderContext.setPainter( mypContextPainter );
|
||||
mypContextPainter->save();
|
||||
mypContextPainter->scale( 1.0 / rasterScaleFactor, 1.0 / rasterScaleFactor );
|
||||
mypContextPainter->drawImage( 0, 0, *( mypFlattenedImage ) );
|
||||
mypContextPainter->restore();
|
||||
delete mypFlattenedImage;
|
||||
mypFlattenedImage = 0;
|
||||
}*/
|
||||
|
||||
disconnect( ml, SIGNAL( drawingProgress( int, int ) ), this, SLOT( onDrawingProgress( int, int ) ) );
|
||||
}
|
||||
else // layer not visible due to scale
|
||||
{
|
||||
QgsDebugMsg( "Layer not rendered because it is not within the defined "
|
||||
"visibility scale range" );
|
||||
}
|
||||
|
||||
} // while (li.hasPrevious())
|
||||
|
||||
QgsDebugMsg( "Done rendering map layers" );
|
||||
|
||||
// Reset the composition mode before rendering the labels
|
||||
mRenderContext.painter()->setCompositionMode( QPainter::CompositionMode_SourceOver );
|
||||
|
||||
/*if ( !mOverview )
|
||||
{
|
||||
// render all labels for vector layers in the stack, starting at the base
|
||||
li.toBack();
|
||||
while ( li.hasPrevious() )
|
||||
{
|
||||
if ( mRenderContext.renderingStopped() )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
QString layerId = li.previous();
|
||||
|
||||
// TODO: emit drawingProgress((myRenderCounter++),zOrder.size());
|
||||
QgsMapLayer *ml = QgsMapLayerRegistry::instance()->mapLayer( layerId );
|
||||
|
||||
if ( ml && ( ml->type() != QgsMapLayer::RasterLayer ) )
|
||||
{
|
||||
// only make labels if the layer is visible
|
||||
// after scale dep viewing settings are checked
|
||||
if ( !ml->hasScaleBasedVisibility() || ( ml->minimumScale() < mScale && mScale < ml->maximumScale() ) )
|
||||
{
|
||||
bool split = false;
|
||||
|
||||
if ( hasCrsTransformEnabled() )
|
||||
{
|
||||
QgsRectangle r1 = mExtent;
|
||||
split = splitLayersExtent( ml, r1, r2 );
|
||||
ct = new QgsCoordinateTransform( ml->crs(), *mDestCRS );
|
||||
mRenderContext.setExtent( r1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
ct = NULL;
|
||||
}
|
||||
|
||||
mRenderContext.setCoordinateTransform( ct );
|
||||
|
||||
ml->drawLabels( mRenderContext );
|
||||
if ( split )
|
||||
{
|
||||
mRenderContext.setExtent( r2 );
|
||||
ml->drawLabels( mRenderContext );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // if (!mOverview)*/
|
||||
|
||||
// make sure progress bar arrives at 100%!
|
||||
// TODO emit drawingProgress( 1, 1 );
|
||||
|
||||
/*if ( mLabelingEngine )
|
||||
{
|
||||
// set correct extent
|
||||
mRenderContext.setExtent( mExtent );
|
||||
mRenderContext.setCoordinateTransform( NULL );
|
||||
|
||||
mLabelingEngine->drawLabeling( mRenderContext );
|
||||
mLabelingEngine->exit();
|
||||
}*/
|
||||
|
||||
QgsDebugMsg( "Rendering completed in (seconds): " + QString( "%1" ).arg( renderTime.elapsed() / 1000.0 ) );
|
||||
|
||||
}
|
||||
|
||||
|
119
src/core/qgsmaprendererjob.h
Normal file
119
src/core/qgsmaprendererjob.h
Normal file
@ -0,0 +1,119 @@
|
||||
#ifndef QGSMAPRENDERERJOB_H
|
||||
#define QGSMAPRENDERERJOB_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QImage>
|
||||
|
||||
#include "qgsrendercontext.h"
|
||||
|
||||
#include "qgsmaprendererv2.h"
|
||||
|
||||
/** abstract base class renderer jobs that asynchronously start map rendering */
|
||||
class QgsMapRendererJob : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
enum Type
|
||||
{
|
||||
SequentialJob,
|
||||
CustomPainterJob
|
||||
//ParallelJob
|
||||
};
|
||||
|
||||
|
||||
QgsMapRendererJob(Type type, const QgsMapRendererSettings& settings) : mType(type), mSettings(settings) { }
|
||||
|
||||
virtual ~QgsMapRendererJob() {}
|
||||
|
||||
Type type() const { return mType; }
|
||||
|
||||
//! Start the rendering job and immediately return.
|
||||
virtual void start() = 0;
|
||||
|
||||
//! Stop the rendering job - does not return until the job has terminated.
|
||||
virtual void cancel() = 0;
|
||||
|
||||
//! Get a preview/resulting image - in case QPainter has not been provided.
|
||||
//! With QPainter specified, it will return invalid QImage (there's no way to provide it).
|
||||
virtual QImage renderedImage() = 0;
|
||||
|
||||
signals:
|
||||
|
||||
//! emitted when asynchronous rendering is finished (or canceled).
|
||||
void finished();
|
||||
|
||||
protected:
|
||||
|
||||
Type mType;
|
||||
|
||||
QgsMapRendererSettings mSettings;
|
||||
};
|
||||
|
||||
|
||||
class QgsMapRendererCustomPainterJob;
|
||||
|
||||
|
||||
/** job implementation that renders everything sequentially in one thread */
|
||||
class QgsMapRendererSequentialJob : public QgsMapRendererJob
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QgsMapRendererSequentialJob(const QgsMapRendererSettings& settings);
|
||||
|
||||
virtual void start();
|
||||
virtual void cancel();
|
||||
virtual QImage renderedImage();
|
||||
|
||||
public slots:
|
||||
|
||||
void internalFinished();
|
||||
|
||||
protected:
|
||||
|
||||
QgsMapRendererCustomPainterJob* mInternalJob;
|
||||
QImage mImage;
|
||||
QPainter* mPainter;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** job implementation that renders all layers in parallel - the implication is that rendering is done to QImage */
|
||||
//class QgsMapRendererParallelJob : public QgsMapRendererJob
|
||||
//{
|
||||
//};
|
||||
|
||||
|
||||
#include <QtConcurrentRun>
|
||||
#include <QFutureWatcher>
|
||||
|
||||
/** job implementation that renders everything sequentially using a custom painter.
|
||||
* The returned image is always invalid (because there is none available).
|
||||
*/
|
||||
class QgsMapRendererCustomPainterJob : public QgsMapRendererJob
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QgsMapRendererCustomPainterJob(const QgsMapRendererSettings& settings, QPainter* painter);
|
||||
|
||||
virtual void start();
|
||||
virtual void cancel();
|
||||
virtual QImage renderedImage();
|
||||
|
||||
protected slots:
|
||||
void futureFinished();
|
||||
|
||||
protected:
|
||||
static void staticRender(QgsMapRendererCustomPainterJob* self); // function to be used within the thread
|
||||
|
||||
void startRender();
|
||||
|
||||
private:
|
||||
QPainter* mPainter;
|
||||
QFuture<void> mFuture;
|
||||
QFutureWatcher<void> mFutureWatcher;
|
||||
QgsRenderContext mRenderContext;
|
||||
};
|
||||
|
||||
|
||||
#endif // QGSMAPRENDERERJOB_H
|
252
src/core/qgsmaprendererv2.cpp
Normal file
252
src/core/qgsmaprendererv2.cpp
Normal file
@ -0,0 +1,252 @@
|
||||
|
||||
#include "qgsmaprendererv2.h"
|
||||
|
||||
#include "qgsscalecalculator.h"
|
||||
#include "qgsmaprendererjob.h"
|
||||
#include "qgsmaptopixel.h"
|
||||
#include "qgslogger.h"
|
||||
|
||||
/*
|
||||
|
||||
usage in QgsMapCanvas - upon pan / zoom - in QgsMapCanvasMap:
|
||||
- stop rendering if active
|
||||
- update QgsMapRendererV2 settings
|
||||
- start rendering
|
||||
- start update timer
|
||||
- [on timeout/finished] show rendered image
|
||||
|
||||
usage in QgsComposer
|
||||
- create QgsMapRendererV2
|
||||
- setup, start with QPainter
|
||||
- wait until it finishes
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
QgsMapRendererV2::QgsMapRendererV2()
|
||||
: mActiveJob(0)
|
||||
, mScaleCalculator(new QgsScaleCalculator())
|
||||
{
|
||||
}
|
||||
|
||||
QgsMapRendererV2::~QgsMapRendererV2()
|
||||
{
|
||||
// make sure there is no job running
|
||||
cancel();
|
||||
|
||||
}
|
||||
|
||||
bool QgsMapRendererV2::start(bool parallel)
|
||||
{
|
||||
if (mActiveJob)
|
||||
return false;
|
||||
|
||||
if (parallel)
|
||||
{
|
||||
Q_ASSERT(false && "parallel job not implemented yet");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
mActiveJob = new QgsMapRendererSequentialJob(mSettings);
|
||||
}
|
||||
|
||||
connect(mActiveJob, SIGNAL(finished()), this, SLOT(onJobFinished()));
|
||||
|
||||
mActiveJob->start();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool QgsMapRendererV2::startWithCustomPainter(QPainter *painter)
|
||||
{
|
||||
if (mActiveJob)
|
||||
return false;
|
||||
|
||||
mActiveJob = new QgsMapRendererCustomPainterJob(mSettings, painter);
|
||||
|
||||
connect(mActiveJob, SIGNAL(finished()), this, SLOT(onJobFinished()));
|
||||
|
||||
mActiveJob->start();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QgsMapRendererV2::cancel()
|
||||
{
|
||||
if (!mActiveJob)
|
||||
return false;
|
||||
|
||||
mActiveJob->cancel();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void QgsMapRendererV2::onJobFinished()
|
||||
{
|
||||
qDebug("onJobFinished");
|
||||
Q_ASSERT(mActiveJob);
|
||||
|
||||
emit finished();
|
||||
|
||||
mActiveJob->deleteLater();
|
||||
mActiveJob = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
QgsRectangle QgsMapRendererV2::extent() const
|
||||
{
|
||||
return mSettings.extent;
|
||||
}
|
||||
|
||||
void QgsMapRendererV2::setExtent(const QgsRectangle& extent)
|
||||
{
|
||||
mSettings.extent = extent;
|
||||
}
|
||||
|
||||
|
||||
void QgsMapRendererV2::updateDerived()
|
||||
{
|
||||
QgsRectangle extent = mSettings.extent;
|
||||
|
||||
if ( extent.isEmpty() )
|
||||
{
|
||||
mSettings.valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't allow zooms where the current extent is so small that it
|
||||
// can't be accurately represented using a double (which is what
|
||||
// currentExtent uses). Excluding 0 avoids a divide by zero and an
|
||||
// infinite loop when rendering to a new canvas. Excluding extents
|
||||
// greater than 1 avoids doing unnecessary calculations.
|
||||
|
||||
// The scheme is to compare the width against the mean x coordinate
|
||||
// (and height against mean y coordinate) and only allow zooms where
|
||||
// the ratio indicates that there is more than about 12 significant
|
||||
// figures (there are about 16 significant figures in a double).
|
||||
|
||||
if ( extent.width() > 0 &&
|
||||
extent.height() > 0 &&
|
||||
extent.width() < 1 &&
|
||||
extent.height() < 1 )
|
||||
{
|
||||
// Use abs() on the extent to avoid the case where the extent is
|
||||
// symmetrical about 0.
|
||||
double xMean = ( qAbs( extent.xMinimum() ) + qAbs( extent.xMaximum() ) ) * 0.5;
|
||||
double yMean = ( qAbs( extent.yMinimum() ) + qAbs( extent.yMaximum() ) ) * 0.5;
|
||||
|
||||
double xRange = extent.width() / xMean;
|
||||
double yRange = extent.height() / yMean;
|
||||
|
||||
static const double minProportion = 1e-12;
|
||||
if ( xRange < minProportion || yRange < minProportion )
|
||||
{
|
||||
mSettings.valid = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
double myHeight = mSettings.size.height();
|
||||
double myWidth = mSettings.size.width();
|
||||
|
||||
if ( !myWidth || !myHeight )
|
||||
{
|
||||
mSettings.valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// calculate the translation and scaling parameters
|
||||
double mapUnitsPerPixelY = mSettings.extent.height() / myHeight;
|
||||
double mapUnitsPerPixelX = mSettings.extent.width() / myWidth;
|
||||
mSettings.mapUnitsPerPixel = mapUnitsPerPixelY > mapUnitsPerPixelX ? mapUnitsPerPixelY : mapUnitsPerPixelX;
|
||||
|
||||
// calculate the actual extent of the mapCanvas
|
||||
double dxmin = mSettings.extent.xMinimum(), dxmax = mSettings.extent.xMaximum(),
|
||||
dymin = mSettings.extent.yMinimum(), dymax = mSettings.extent.yMaximum(), whitespace;
|
||||
|
||||
if ( mapUnitsPerPixelY > mapUnitsPerPixelX )
|
||||
{
|
||||
whitespace = (( myWidth * mSettings.mapUnitsPerPixel ) - mSettings.extent.width() ) * 0.5;
|
||||
dxmin -= whitespace;
|
||||
dxmax += whitespace;
|
||||
}
|
||||
else
|
||||
{
|
||||
whitespace = (( myHeight * mSettings.mapUnitsPerPixel ) - mSettings.extent.height() ) * 0.5;
|
||||
dymin -= whitespace;
|
||||
dymax += whitespace;
|
||||
}
|
||||
|
||||
mSettings.visibleExtent.set( dxmin, dymin, dxmax, dymax );
|
||||
|
||||
// update the scale
|
||||
mSettings.scale = mScaleCalculator->calculate( mSettings.extent, mSettings.size.width() );
|
||||
|
||||
QgsDebugMsg( QString( "Map units per pixel (x,y) : %1, %2" ).arg( qgsDoubleToString( mapUnitsPerPixelX ) ).arg( qgsDoubleToString( mapUnitsPerPixelY ) ) );
|
||||
QgsDebugMsg( QString( "Pixmap dimensions (x,y) : %1, %2" ).arg( qgsDoubleToString( myWidth ) ).arg( qgsDoubleToString( myHeight ) ) );
|
||||
QgsDebugMsg( QString( "Extent dimensions (x,y) : %1, %2" ).arg( qgsDoubleToString( mSettings.extent.width() ) ).arg( qgsDoubleToString( mSettings.extent.height() ) ) );
|
||||
QgsDebugMsg( mSettings.extent.toString() );
|
||||
QgsDebugMsg( QString( "Adjusted map units per pixel (x,y) : %1, %2" ).arg( qgsDoubleToString( mSettings.visibleExtent.width() / myWidth ) ).arg( qgsDoubleToString( mSettings.visibleExtent.height() / myHeight ) ) );
|
||||
QgsDebugMsg( QString( "Recalced pixmap dimensions (x,y) : %1, %2" ).arg( qgsDoubleToString( mSettings.visibleExtent.width() / mSettings.mapUnitsPerPixel ) ).arg( qgsDoubleToString( mSettings.visibleExtent.height() / mSettings.mapUnitsPerPixel ) ) );
|
||||
QgsDebugMsg( QString( "Scale (assuming meters as map units) = 1:%1" ).arg( qgsDoubleToString( mSettings.scale ) ) );
|
||||
|
||||
}
|
||||
|
||||
|
||||
QSize QgsMapRendererV2::outputSize() const
|
||||
{
|
||||
return mSettings.size;
|
||||
}
|
||||
|
||||
void QgsMapRendererV2::setOutputSize(const QSize& size)
|
||||
{
|
||||
mSettings.size = size;
|
||||
}
|
||||
|
||||
double QgsMapRendererV2::outputDpi() const
|
||||
{
|
||||
return mSettings.dpi;
|
||||
}
|
||||
|
||||
void QgsMapRendererV2::setOutputDpi(double dpi)
|
||||
{
|
||||
mSettings.dpi = dpi;
|
||||
}
|
||||
|
||||
|
||||
QStringList QgsMapRendererV2::layers() const
|
||||
{
|
||||
return mSettings.layers;
|
||||
}
|
||||
|
||||
void QgsMapRendererV2::setLayers(const QStringList& layers)
|
||||
{
|
||||
mSettings.layers = layers;
|
||||
}
|
||||
|
||||
|
||||
bool QgsMapRendererV2::hasValidSettings() const
|
||||
{
|
||||
return mSettings.valid;
|
||||
}
|
||||
|
||||
QgsRectangle QgsMapRendererV2::visibleExtent() const
|
||||
{
|
||||
return mSettings.visibleExtent;
|
||||
}
|
||||
|
||||
double QgsMapRendererV2::mapUnitsPerPixel() const
|
||||
{
|
||||
return mSettings.mapUnitsPerPixel;
|
||||
}
|
||||
|
||||
double QgsMapRendererV2::scale() const
|
||||
{
|
||||
return mSettings.scale;
|
||||
}
|
121
src/core/qgsmaprendererv2.h
Normal file
121
src/core/qgsmaprendererv2.h
Normal file
@ -0,0 +1,121 @@
|
||||
#ifndef QGSMAPRENDERERV2_H
|
||||
#define QGSMAPRENDERERV2_H
|
||||
|
||||
#include <QSize>
|
||||
#include <QStringList>
|
||||
|
||||
#include "qgscoordinatereferencesystem.h"
|
||||
#include "qgsrectangle.h"
|
||||
|
||||
class QPainter;
|
||||
|
||||
class QgsScaleCalculator;
|
||||
class QgsMapRendererJob;
|
||||
|
||||
|
||||
|
||||
|
||||
struct QgsMapRendererSettings
|
||||
{
|
||||
// TODO
|
||||
|
||||
double dpi;
|
||||
|
||||
QSize size;
|
||||
|
||||
QgsRectangle extent;
|
||||
|
||||
QStringList layers;
|
||||
|
||||
bool projectionsEnabled;
|
||||
QgsCoordinateReferenceSystem destCRS;
|
||||
|
||||
// derived properties
|
||||
bool valid; //!< whether the actual settings are valid (set in updateDerived())
|
||||
QgsRectangle visibleExtent; //!< extent with some additional white space that matches the output aspect ratio
|
||||
double mapUnitsPerPixel;
|
||||
double scale;
|
||||
|
||||
|
||||
// TODO: utility functions
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
class QgsMapRendererV2 : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
QgsMapRendererV2();
|
||||
~QgsMapRendererV2();
|
||||
|
||||
//
|
||||
// getters/setters for rendering settings
|
||||
//
|
||||
|
||||
QgsRectangle extent() const;
|
||||
void setExtent(const QgsRectangle& rect);
|
||||
|
||||
QSize outputSize() const;
|
||||
void setOutputSize(const QSize& size);
|
||||
|
||||
double outputDpi() const;
|
||||
void setOutputDpi(double dpi);
|
||||
|
||||
QStringList layers() const;
|
||||
void setLayers(const QStringList& layers);
|
||||
|
||||
void updateDerived(); // TODO: should be protected, called automatically
|
||||
|
||||
bool hasValidSettings() const;
|
||||
QgsRectangle visibleExtent() const;
|
||||
double mapUnitsPerPixel() const;
|
||||
double scale() const;
|
||||
|
||||
//! Access all map renderer settings at once
|
||||
const QgsMapRendererSettings& settings() const { return mSettings; }
|
||||
|
||||
//
|
||||
// rendering control
|
||||
//
|
||||
|
||||
//! start rendering to a QImage with the current settings
|
||||
bool start(bool parallel = false);
|
||||
|
||||
//! start rendering with a custom painter (
|
||||
bool startWithCustomPainter(QPainter* painter);
|
||||
|
||||
//! cancel the rendering job and wait until it stops
|
||||
bool cancel();
|
||||
|
||||
//! block until the rendering is done
|
||||
void waitForFinished();
|
||||
|
||||
bool isRendering() const { return mActiveJob != 0; }
|
||||
|
||||
const QgsMapRendererJob* activeJob() const { return mActiveJob; }
|
||||
|
||||
signals:
|
||||
void finished();
|
||||
|
||||
protected slots:
|
||||
void onJobFinished();
|
||||
|
||||
protected:
|
||||
void updateScale();
|
||||
void adjustExtentToSize();
|
||||
|
||||
protected:
|
||||
|
||||
QgsMapRendererSettings mSettings;
|
||||
|
||||
QgsScaleCalculator* mScaleCalculator;
|
||||
|
||||
//! currently running renderer job (null if there is none)
|
||||
QgsMapRendererJob* mActiveJob;
|
||||
};
|
||||
|
||||
|
||||
#endif // QGSMAPRENDERERV2_H
|
@ -115,7 +115,6 @@ QgsVectorLayer::QgsVectorLayer( QString vectorLayerPath,
|
||||
QString providerKey,
|
||||
bool loadDefaultStyleFlag )
|
||||
: QgsMapLayer( VectorLayer, baseName, vectorLayerPath )
|
||||
, mUpdateThreshold( 0 ) // XXX better default value?
|
||||
, mDataProvider( NULL )
|
||||
, mProviderKey( providerKey )
|
||||
, mReadOnly( false )
|
||||
@ -379,11 +378,6 @@ void QgsVectorLayer::drawLabels( QgsRenderContext& rendererContext )
|
||||
}
|
||||
|
||||
QgsDebugMsg( QString( "Total features processed %1" ).arg( featureCount ) );
|
||||
|
||||
// XXX Something in our draw event is triggering an additional draw event when resizing [TE 01/26/06]
|
||||
// XXX Calling this will begin processing the next draw event causing image havoc and recursion crashes.
|
||||
//qApp->processEvents();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -399,10 +393,6 @@ void QgsVectorLayer::drawRendererV2( QgsFeatureIterator &fit, QgsRenderContext&
|
||||
QSettings settings;
|
||||
bool vertexMarkerOnlyForSelection = settings.value( "/qgis/digitizing/marker_only_for_selected", false ).toBool();
|
||||
|
||||
#ifndef Q_WS_MAC
|
||||
int featureCount = 0;
|
||||
#endif //Q_WS_MAC
|
||||
|
||||
QgsFeature fet;
|
||||
while ( fit.nextFeature( fet ) )
|
||||
{
|
||||
@ -411,29 +401,9 @@ void QgsVectorLayer::drawRendererV2( QgsFeatureIterator &fit, QgsRenderContext&
|
||||
if ( !fet.geometry() )
|
||||
continue; // skip features without geometry
|
||||
|
||||
#ifndef Q_WS_MAC //MH: disable this on Mac for now to avoid problems with resizing
|
||||
#ifdef Q_WS_X11
|
||||
if ( !mEnableBackbuffer ) // do not handle events, as we're already inside a paint event
|
||||
{
|
||||
#endif // Q_WS_X11
|
||||
if ( mUpdateThreshold > 0 && 0 == featureCount % mUpdateThreshold )
|
||||
{
|
||||
emit screenUpdateRequested();
|
||||
// emit drawingProgress( featureCount, totalFeatures );
|
||||
qApp->processEvents();
|
||||
}
|
||||
else if ( featureCount % 1000 == 0 )
|
||||
{
|
||||
// emit drawingProgress( featureCount, totalFeatures );
|
||||
qApp->processEvents();
|
||||
}
|
||||
#ifdef Q_WS_X11
|
||||
}
|
||||
#endif // Q_WS_X11
|
||||
#endif // Q_WS_MAC
|
||||
|
||||
if ( rendererContext.renderingStopped() )
|
||||
{
|
||||
qDebug("breaking!");
|
||||
break;
|
||||
}
|
||||
|
||||
@ -468,18 +438,11 @@ void QgsVectorLayer::drawRendererV2( QgsFeatureIterator &fit, QgsRenderContext&
|
||||
QgsDebugMsg( QString( "Failed to transform a point while drawing a feature with ID '%1'. Ignoring this feature. %2" )
|
||||
.arg( fet.id() ).arg( cse.what() ) );
|
||||
}
|
||||
#ifndef Q_WS_MAC
|
||||
++featureCount;
|
||||
#endif //Q_WS_MAC
|
||||
}
|
||||
|
||||
stopRendererV2( rendererContext, NULL );
|
||||
|
||||
mCurrentRendererContext = NULL;
|
||||
|
||||
#ifndef Q_WS_MAC
|
||||
QgsDebugMsg( QString( "Total features processed %1" ).arg( featureCount ) );
|
||||
#endif
|
||||
}
|
||||
|
||||
void QgsVectorLayer::drawRendererV2Levels( QgsFeatureIterator &fit, QgsRenderContext& rendererContext, bool labeling )
|
||||
@ -503,9 +466,6 @@ void QgsVectorLayer::drawRendererV2Levels( QgsFeatureIterator &fit, QgsRenderCon
|
||||
|
||||
// 1. fetch features
|
||||
QgsFeature fet;
|
||||
#ifndef Q_WS_MAC
|
||||
int featureCount = 0;
|
||||
#endif //Q_WS_MAC
|
||||
while ( fit.nextFeature( fet ) )
|
||||
{
|
||||
if ( !fet.geometry() )
|
||||
@ -513,15 +473,11 @@ void QgsVectorLayer::drawRendererV2Levels( QgsFeatureIterator &fit, QgsRenderCon
|
||||
|
||||
if ( rendererContext.renderingStopped() )
|
||||
{
|
||||
qDebug("rendering stop!");
|
||||
stopRendererV2( rendererContext, selRenderer );
|
||||
return;
|
||||
}
|
||||
#ifndef Q_WS_MAC
|
||||
if ( featureCount % 1000 == 0 )
|
||||
{
|
||||
qApp->processEvents();
|
||||
}
|
||||
#endif //Q_WS_MAC
|
||||
|
||||
QgsSymbolV2* sym = mRendererV2->symbolForFeature( fet );
|
||||
if ( !sym )
|
||||
{
|
||||
@ -551,10 +507,6 @@ void QgsVectorLayer::drawRendererV2Levels( QgsFeatureIterator &fit, QgsRenderCon
|
||||
rendererContext.labelingEngine()->registerDiagramFeature( this, fet, rendererContext );
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef Q_WS_MAC
|
||||
++featureCount;
|
||||
#endif //Q_WS_MAC
|
||||
}
|
||||
|
||||
// find out the order
|
||||
@ -590,9 +542,6 @@ void QgsVectorLayer::drawRendererV2Levels( QgsFeatureIterator &fit, QgsRenderCon
|
||||
int layer = item.layer();
|
||||
QList<QgsFeature>& lst = features[item.symbol()];
|
||||
QList<QgsFeature>::iterator fit;
|
||||
#ifndef Q_WS_MAC
|
||||
featureCount = 0;
|
||||
#endif //Q_WS_MAC
|
||||
for ( fit = lst.begin(); fit != lst.end(); ++fit )
|
||||
{
|
||||
if ( rendererContext.renderingStopped() )
|
||||
@ -600,12 +549,7 @@ void QgsVectorLayer::drawRendererV2Levels( QgsFeatureIterator &fit, QgsRenderCon
|
||||
stopRendererV2( rendererContext, selRenderer );
|
||||
return;
|
||||
}
|
||||
#ifndef Q_WS_MAC
|
||||
if ( featureCount % 1000 == 0 )
|
||||
{
|
||||
qApp->processEvents();
|
||||
}
|
||||
#endif //Q_WS_MAC
|
||||
|
||||
bool sel = mSelectedFeatureIds.contains( fit->id() );
|
||||
// maybe vertex markers should be drawn only during the last pass...
|
||||
bool drawMarker = ( mEditBuffer && ( !vertexMarkerOnlyForSelection || sel ) );
|
||||
@ -620,9 +564,6 @@ void QgsVectorLayer::drawRendererV2Levels( QgsFeatureIterator &fit, QgsRenderCon
|
||||
QgsDebugMsg( QString( "Failed to transform a point while drawing a feature with ID '%1'. Ignoring this feature. %2" )
|
||||
.arg( fet.id() ).arg( cse.what() ) );
|
||||
}
|
||||
#ifndef Q_WS_MAC
|
||||
++featureCount;
|
||||
#endif //Q_WS_MAC
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -643,20 +584,6 @@ bool QgsVectorLayer::draw( QgsRenderContext& rendererContext )
|
||||
if ( !hasGeometryType() )
|
||||
return true;
|
||||
|
||||
//set update threshold before each draw to make sure the current setting is picked up
|
||||
QSettings settings;
|
||||
mUpdateThreshold = settings.value( "Map/updateThreshold", 0 ).toInt();
|
||||
// users could accidently set updateThreshold threshold to a small value
|
||||
// and complain about bad performance -> force min 1000 here
|
||||
if ( mUpdateThreshold > 0 && mUpdateThreshold < 1000 )
|
||||
{
|
||||
mUpdateThreshold = 1000;
|
||||
}
|
||||
|
||||
#ifdef Q_WS_X11
|
||||
mEnableBackbuffer = settings.value( "/Map/enableBackbuffer", 1 ).toBool();
|
||||
#endif
|
||||
|
||||
if ( !mRendererV2 )
|
||||
return false;
|
||||
|
||||
|
@ -1572,17 +1572,6 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
|
||||
|
||||
private: // Private attributes
|
||||
|
||||
/** Update threshold for drawing features as they are read. A value of zero indicates
|
||||
* that no features will be drawn until all have been read
|
||||
*/
|
||||
int mUpdateThreshold;
|
||||
|
||||
/** Enables backbuffering for the map window. This improves graphics performance,
|
||||
* but the possibility to cancel rendering and incremental feature drawing will be lost.
|
||||
*
|
||||
*/
|
||||
bool mEnableBackbuffer;
|
||||
|
||||
/** Pointer to data provider derived from the abastract base class QgsDataProvider */
|
||||
QgsVectorDataProvider *mDataProvider;
|
||||
|
||||
|
@ -148,6 +148,7 @@ ENDIF (WITH_TOUCH)
|
||||
SET(QGIS_GUI_MOC_HDRS
|
||||
|
||||
qgscolorbutton.h
|
||||
qgsmapcanvasmap.h
|
||||
|
||||
raster/qgsrasterminmaxwidget.h
|
||||
raster/qgspalettedrendererwidget.h
|
||||
|
@ -81,8 +81,8 @@ class QgsMapCanvas::CanvasProperties
|
||||
QgsMapCanvas::QgsMapCanvas( QWidget * parent, const char *name )
|
||||
: QGraphicsView( parent )
|
||||
, mCanvasProperties( new CanvasProperties )
|
||||
, mNewSize( QSize() )
|
||||
, mPainting( false )
|
||||
//, mNewSize( QSize() )
|
||||
//, mPainting( false )
|
||||
, mAntiAliasing( false )
|
||||
{
|
||||
setObjectName( name );
|
||||
@ -96,7 +96,6 @@ QgsMapCanvas::QgsMapCanvas( QWidget * parent, const char *name )
|
||||
mMapTool = NULL;
|
||||
mLastNonZoomMapTool = NULL;
|
||||
|
||||
mBackbufferEnabled = true;
|
||||
mDrawing = false;
|
||||
mFrozen = false;
|
||||
mDirty = true;
|
||||
@ -178,8 +177,8 @@ void QgsMapCanvas::enableAntiAliasing( bool theFlag )
|
||||
|
||||
void QgsMapCanvas::useImageToRender( bool theFlag )
|
||||
{
|
||||
mMap->useImageToRender( theFlag );
|
||||
refresh(); // redraw the map on change - prevents black map view
|
||||
//mMap->useImageToRender( theFlag );
|
||||
//refresh(); // redraw the map on change - prevents black map view
|
||||
}
|
||||
|
||||
QgsMapCanvasMap* QgsMapCanvas::map()
|
||||
@ -187,10 +186,10 @@ QgsMapCanvasMap* QgsMapCanvas::map()
|
||||
return mMap;
|
||||
}
|
||||
|
||||
QgsMapRenderer* QgsMapCanvas::mapRenderer()
|
||||
/*QgsMapRenderer* QgsMapCanvas::mapRenderer()
|
||||
{
|
||||
return mMapRenderer;
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
QgsMapLayer* QgsMapCanvas::layer( int index )
|
||||
@ -235,7 +234,7 @@ bool QgsMapCanvas::isDrawing()
|
||||
// device size
|
||||
const QgsMapToPixel * QgsMapCanvas::getCoordinateTransform()
|
||||
{
|
||||
return mMapRenderer->coordinateTransform();
|
||||
return mMap->coordinateTransform();
|
||||
}
|
||||
|
||||
void QgsMapCanvas::setLayerSet( QList<QgsMapCanvasLayer> &layers )
|
||||
@ -284,7 +283,6 @@ void QgsMapCanvas::setLayerSet( QList<QgsMapCanvasLayer> &layers )
|
||||
// Ticket #811 - racicot
|
||||
QgsMapLayer *currentLayer = layer( i );
|
||||
disconnect( currentLayer, SIGNAL( repaintRequested() ), this, SLOT( refresh() ) );
|
||||
disconnect( currentLayer, SIGNAL( screenUpdateRequested() ), this, SLOT( updateMap() ) );
|
||||
QgsVectorLayer *isVectLyr = qobject_cast<QgsVectorLayer *>( currentLayer );
|
||||
if ( isVectLyr )
|
||||
{
|
||||
@ -300,7 +298,6 @@ void QgsMapCanvas::setLayerSet( QList<QgsMapCanvasLayer> &layers )
|
||||
// Ticket #811 - racicot
|
||||
QgsMapLayer *currentLayer = layer( i );
|
||||
connect( currentLayer, SIGNAL( repaintRequested() ), this, SLOT( refresh() ) );
|
||||
connect( currentLayer, SIGNAL( screenUpdateRequested() ), this, SLOT( updateMap() ) );
|
||||
QgsVectorLayer *isVectLyr = qobject_cast<QgsVectorLayer *>( currentLayer );
|
||||
if ( isVectLyr )
|
||||
{
|
||||
@ -353,6 +350,11 @@ void QgsMapCanvas::enableOverviewMode( QgsMapOverviewCanvas* overview )
|
||||
}
|
||||
}
|
||||
|
||||
const QgsMapRendererSettings &QgsMapCanvas::mapRendererSettings() const
|
||||
{
|
||||
return mMap->settings();
|
||||
}
|
||||
|
||||
|
||||
void QgsMapCanvas::updateOverview()
|
||||
{
|
||||
@ -372,6 +374,9 @@ QgsMapLayer* QgsMapCanvas::currentLayer()
|
||||
|
||||
void QgsMapCanvas::refresh()
|
||||
{
|
||||
mMap->refresh();
|
||||
|
||||
/*
|
||||
// we can't draw again if already drawing...
|
||||
if ( mDrawing )
|
||||
return;
|
||||
@ -384,32 +389,6 @@ void QgsMapCanvas::refresh()
|
||||
t.start();
|
||||
}
|
||||
|
||||
#ifdef Q_WS_X11
|
||||
bool enableBackbufferSetting = settings.value( "/Map/enableBackbuffer", 1 ).toBool();
|
||||
#endif
|
||||
|
||||
#ifdef Q_WS_X11
|
||||
#ifndef ANDROID
|
||||
// disable the update that leads to the resize crash on X11 systems
|
||||
if ( viewport() )
|
||||
{
|
||||
if ( enableBackbufferSetting != mBackbufferEnabled )
|
||||
{
|
||||
qDebug() << "Enable back buffering: " << enableBackbufferSetting;
|
||||
if ( enableBackbufferSetting )
|
||||
{
|
||||
viewport()->setAttribute( Qt::WA_PaintOnScreen, false );
|
||||
}
|
||||
else
|
||||
{
|
||||
viewport()->setAttribute( Qt::WA_PaintOnScreen, true );
|
||||
}
|
||||
mBackbufferEnabled = enableBackbufferSetting;
|
||||
}
|
||||
}
|
||||
#endif // ANDROID
|
||||
#endif // Q_WS_X11
|
||||
|
||||
mDrawing = true;
|
||||
|
||||
if ( mRenderFlag && !mFrozen )
|
||||
@ -454,15 +433,12 @@ void QgsMapCanvas::refresh()
|
||||
}
|
||||
QgsMessageLog::logMessage( logMsg, tr( "Rendering" ) );
|
||||
}
|
||||
*/
|
||||
|
||||
} // refresh
|
||||
|
||||
void QgsMapCanvas::updateMap()
|
||||
{
|
||||
if ( mMap )
|
||||
{
|
||||
mMap->updateContents();
|
||||
}
|
||||
}
|
||||
|
||||
//the format defaults to "PNG" if not specified
|
||||
@ -484,6 +460,7 @@ void QgsMapCanvas::saveAsImage( QString theFileName, QPixmap * theQPixmap, QStri
|
||||
}
|
||||
else //use the map view
|
||||
{
|
||||
// TODO[MD]: fix
|
||||
QPixmap *pixmap = dynamic_cast<QPixmap *>( &mMap->paintDevice() );
|
||||
if ( !pixmap )
|
||||
return;
|
||||
@ -1022,13 +999,28 @@ void QgsMapCanvas::mouseReleaseEvent( QMouseEvent * e )
|
||||
|
||||
void QgsMapCanvas::resizeEvent( QResizeEvent * e )
|
||||
{
|
||||
mNewSize = e->size();
|
||||
QGraphicsView::resizeEvent(e);
|
||||
|
||||
QSize lastSize = size();
|
||||
|
||||
//set map size before scene size helps keep scene indexes updated properly
|
||||
// this was the cause of rubberband artifacts
|
||||
mMap->resize( lastSize );
|
||||
mScene->setSceneRect( QRectF( 0, 0, lastSize.width(), lastSize.height() ) );
|
||||
|
||||
// notify canvas items of change
|
||||
updateCanvasItemPositions();
|
||||
|
||||
updateScale();
|
||||
|
||||
refresh();
|
||||
|
||||
emit extentsChanged();
|
||||
}
|
||||
|
||||
void QgsMapCanvas::paintEvent( QPaintEvent *e )
|
||||
{
|
||||
if ( mNewSize.isValid() )
|
||||
{
|
||||
/*
|
||||
if ( mPainting || mDrawing )
|
||||
{
|
||||
//cancel current render progress
|
||||
@ -1042,31 +1034,7 @@ void QgsMapCanvas::paintEvent( QPaintEvent *e )
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
mPainting = true;
|
||||
|
||||
while ( mNewSize.isValid() )
|
||||
{
|
||||
QSize lastSize = mNewSize;
|
||||
mNewSize = QSize();
|
||||
|
||||
//set map size before scene size helps keep scene indexes updated properly
|
||||
// this was the cause of rubberband artifacts
|
||||
mMap->resize( lastSize );
|
||||
mScene->setSceneRect( QRectF( 0, 0, lastSize.width(), lastSize.height() ) );
|
||||
|
||||
// notify canvas items of change
|
||||
updateCanvasItemPositions();
|
||||
|
||||
updateScale();
|
||||
|
||||
refresh();
|
||||
|
||||
emit extentsChanged();
|
||||
}
|
||||
|
||||
mPainting = false;
|
||||
}
|
||||
*/
|
||||
|
||||
QGraphicsView::paintEvent( e );
|
||||
} // paintEvent
|
||||
@ -1401,12 +1369,19 @@ void QgsMapCanvas::panActionEnd( QPoint releasePoint )
|
||||
QgsPoint start = getCoordinateTransform()->toMapCoordinates( mCanvasProperties->rubberStartPoint );
|
||||
QgsPoint end = getCoordinateTransform()->toMapCoordinates( releasePoint );
|
||||
|
||||
qDebug("start %f,%f", start.x(), start.y());
|
||||
qDebug("end %f,%f", end.x(), end.y());
|
||||
|
||||
double dx = qAbs( end.x() - start.x() );
|
||||
double dy = qAbs( end.y() - start.y() );
|
||||
|
||||
|
||||
// modify the extent
|
||||
QgsRectangle r = mMapRenderer->extent();
|
||||
|
||||
qDebug(" -------------XXX diff: %f,%f", dx, dy);
|
||||
qDebug(" ------------oldR: %f,%f", r.xMinimum(), r.yMinimum());
|
||||
|
||||
if ( end.x() < start.x() )
|
||||
{
|
||||
r.setXMinimum( r.xMinimum() + dx );
|
||||
@ -1432,6 +1407,10 @@ void QgsMapCanvas::panActionEnd( QPoint releasePoint )
|
||||
}
|
||||
|
||||
setExtent( r );
|
||||
|
||||
r = mMapRenderer->extent();
|
||||
qDebug(" ------------newR: %f,%f", r.xMinimum(), r.yMinimum());
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
|
@ -57,6 +57,7 @@ class QgsHighlight;
|
||||
class QgsVectorLayer;
|
||||
|
||||
class QgsMapRenderer;
|
||||
class QgsMapRendererSettings;
|
||||
class QgsMapCanvasMap;
|
||||
class QgsMapOverviewCanvas;
|
||||
class QgsMapTool;
|
||||
@ -118,11 +119,16 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView
|
||||
|
||||
void enableOverviewMode( QgsMapOverviewCanvas* overview );
|
||||
|
||||
const QgsMapRendererSettings& mapRendererSettings() const;
|
||||
|
||||
//! @deprecated since 2.1 - there could be more than just one "map" items
|
||||
QgsMapCanvasMap* map();
|
||||
|
||||
QgsMapRenderer* mapRenderer();
|
||||
//! @deprecated since 2.1 - use mapRendererSettings() for anything related to current renderer settings
|
||||
// temporarily disabled QgsMapRenderer* mapRenderer();
|
||||
|
||||
//! Accessor for the canvas paint device
|
||||
//! @deprecated since 2.1
|
||||
QPaintDevice &canvasPaintDevice();
|
||||
|
||||
//! Get the last reported scale of the canvas
|
||||
@ -291,7 +297,7 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView
|
||||
/** The map units may have changed, so cope with that */
|
||||
void mapUnitsChanged();
|
||||
|
||||
/** updates pixmap on render progress */
|
||||
//! \note Deprecated in 2.1 - does nothing - kept for API compatibility
|
||||
void updateMap();
|
||||
|
||||
//! show whatever error is exposed by the QgsMapLayer.
|
||||
@ -437,8 +443,6 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView
|
||||
//! map overview widget - it's controlled by QgsMapCanvas
|
||||
QgsMapOverviewCanvas* mMapOverview;
|
||||
|
||||
//! If backbuffering is currently enabled
|
||||
bool mBackbufferEnabled;
|
||||
//! Flag indicating a map refresh is in progress
|
||||
bool mDrawing;
|
||||
|
||||
@ -487,10 +491,10 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView
|
||||
WheelAction mWheelAction;
|
||||
|
||||
//! resize canvas size
|
||||
QSize mNewSize;
|
||||
//QSize mNewSize;
|
||||
|
||||
//! currently in paint event
|
||||
bool mPainting;
|
||||
//bool mPainting;
|
||||
|
||||
//! indicates whether antialiasing will be used for rendering
|
||||
bool mAntiAliasing;
|
||||
|
@ -17,39 +17,136 @@
|
||||
#include "qgsmapcanvas.h"
|
||||
#include "qgsmapcanvasmap.h"
|
||||
#include "qgsmaprenderer.h"
|
||||
#include "qgsmaprendererv2.h"
|
||||
#include "qgsmaplayer.h"
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
QgsMapCanvasMap::QgsMapCanvasMap( QgsMapCanvas* canvas )
|
||||
: mCanvas( canvas )
|
||||
, mDirty(true)
|
||||
{
|
||||
mRend = new QgsMapRendererV2();
|
||||
|
||||
setZValue( -10 );
|
||||
setPos( 0, 0 );
|
||||
resize( QSize( 1, 1 ) );
|
||||
mUseQImageToRender = true;
|
||||
|
||||
connect(mRend, SIGNAL(finished()), SLOT(finish()));
|
||||
|
||||
connect(&mTimer, SIGNAL(timeout()), SLOT(onMapUpdateTimeout()));
|
||||
mTimer.setInterval(400);
|
||||
|
||||
}
|
||||
|
||||
QgsMapCanvasMap::~QgsMapCanvasMap()
|
||||
{
|
||||
delete mRend;
|
||||
mRend = 0;
|
||||
}
|
||||
|
||||
void QgsMapCanvasMap::refresh()
|
||||
{
|
||||
if (mRend->isRendering())
|
||||
{
|
||||
qDebug("need to cancel first!");
|
||||
mRend->cancel();
|
||||
}
|
||||
|
||||
mDirty = true;
|
||||
update();
|
||||
}
|
||||
|
||||
void QgsMapCanvasMap::paint( QPainter* p, const QStyleOptionGraphicsItem*, QWidget* )
|
||||
{
|
||||
//refreshes the canvas map with the current offscreen image
|
||||
p->drawPixmap( 0, 0, mPixmap );
|
||||
qDebug("paint()");
|
||||
|
||||
if (mDirty)
|
||||
{
|
||||
if (mRend->isRendering())
|
||||
{
|
||||
qDebug("already rendering");
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug("need to render");
|
||||
|
||||
QStringList layerIds;
|
||||
foreach (QgsMapLayer* l, mCanvas->layers())
|
||||
layerIds.append(l->id());
|
||||
|
||||
mRend->setLayers(layerIds);
|
||||
mRend->setExtent(mCanvas->extent());
|
||||
mRend->setOutputSize(mImage.size());
|
||||
mRend->setOutputDpi(120);
|
||||
mRend->updateDerived();
|
||||
|
||||
const QgsMapRendererSettings& s = mRend->settings();
|
||||
mMapToPixel = QgsMapToPixel( s.mapUnitsPerPixel, s.size.height(), s.visibleExtent.yMinimum(), s.visibleExtent.xMinimum() );
|
||||
|
||||
qDebug("----------> EXTENT %f,%f", mRend->extent().xMinimum(), mRend->extent().yMinimum());
|
||||
|
||||
mImage.fill(mBgColor.rgb());
|
||||
|
||||
mPainter = new QPainter(&mImage);
|
||||
|
||||
// TODO[MD]: need to setup clipping?
|
||||
//paint.setClipRect( mImage.rect() );
|
||||
|
||||
// antialiasing
|
||||
if ( mAntiAliasing )
|
||||
mPainter->setRenderHint( QPainter::Antialiasing );
|
||||
|
||||
mRend->startWithCustomPainter(mPainter);
|
||||
|
||||
mTimer.start();
|
||||
|
||||
//p->drawImage(0,0, mLastImage);
|
||||
//return; // do not redraw the image
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef EGA_MODE
|
||||
QImage i2( mImage.size()/3, mImage.format() );
|
||||
QPainter p2(&i2);
|
||||
p2.drawImage( QRect(0,0, mImage.width()/3, mImage.height()/3), mImage ); //, 0,0, mImage.width()/3, mImage.height()/3);
|
||||
p2.end();
|
||||
p->drawImage( QRect( 0, 0, mImage.width(), mImage.height()), i2 ) ;//, 0, 0, i2.width()*3, i2.height()*3 );
|
||||
#else
|
||||
p->drawImage( 0, 0, mImage );
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
QRectF QgsMapCanvasMap::boundingRect() const
|
||||
{
|
||||
return QRectF( 0, 0, mPixmap.width(), mPixmap.height() );
|
||||
return QRectF( 0, 0, mImage.width(), mImage.height() );
|
||||
}
|
||||
|
||||
const QgsMapRendererSettings &QgsMapCanvasMap::settings() const
|
||||
{
|
||||
return mRend->settings();
|
||||
}
|
||||
|
||||
QgsMapToPixel *QgsMapCanvasMap::coordinateTransform()
|
||||
{
|
||||
return &mMapToPixel;
|
||||
}
|
||||
|
||||
|
||||
void QgsMapCanvasMap::resize( QSize size )
|
||||
{
|
||||
if (mRend->isRendering())
|
||||
{
|
||||
qDebug("need to cancel first!");
|
||||
mRend->cancel();
|
||||
}
|
||||
|
||||
QgsDebugMsg( QString( "resizing to %1x%2" ).arg( size.width() ).arg( size.height() ) );
|
||||
prepareGeometryChange(); // to keep QGraphicsScene indexes up to date on size change
|
||||
|
||||
mPixmap = QPixmap( size );
|
||||
mPixmap.fill( mBgColor.rgb() );
|
||||
mImage = QImage( size, QImage::Format_RGB32 ); // temporary image - build it here so it is available when switching from QPixmap to QImage rendering
|
||||
mCanvas->mapRenderer()->setOutputSize( size, mPixmap.logicalDpiX() );
|
||||
mImage = QImage( size, QImage::Format_ARGB32_Premultiplied );
|
||||
//mCanvas->mapRenderer()->setOutputSize( size, mImage.logicalDpiX() );
|
||||
}
|
||||
|
||||
void QgsMapCanvasMap::setPanningOffset( const QPoint& point )
|
||||
@ -58,64 +155,32 @@ void QgsMapCanvasMap::setPanningOffset( const QPoint& point )
|
||||
setPos( mOffset );
|
||||
}
|
||||
|
||||
void QgsMapCanvasMap::render()
|
||||
{
|
||||
QgsDebugMsg( QString( "mUseQImageToRender = %1" ).arg( mUseQImageToRender ) );
|
||||
if ( mUseQImageToRender )
|
||||
{
|
||||
// use temporary image for rendering
|
||||
mImage.fill( mBgColor.rgb() );
|
||||
|
||||
// clear the pixmap so that old map won't be displayed while rendering
|
||||
// TODO: do the canvas updates wisely -> this wouldn't be needed
|
||||
mPixmap = QPixmap( mImage.size() );
|
||||
mPixmap.fill( mBgColor.rgb() );
|
||||
|
||||
QPainter paint;
|
||||
paint.begin( &mImage );
|
||||
// Clip drawing to the QImage
|
||||
paint.setClipRect( mImage.rect() );
|
||||
|
||||
// antialiasing
|
||||
if ( mAntiAliasing )
|
||||
paint.setRenderHint( QPainter::Antialiasing );
|
||||
|
||||
mCanvas->mapRenderer()->render( &paint );
|
||||
|
||||
paint.end();
|
||||
|
||||
// convert QImage to QPixmap to achieve faster drawing on screen
|
||||
mPixmap = QPixmap::fromImage( mImage );
|
||||
}
|
||||
else
|
||||
{
|
||||
mPixmap.fill( mBgColor.rgb() );
|
||||
QPainter paint;
|
||||
paint.begin( &mPixmap );
|
||||
// Clip our drawing to the QPixmap
|
||||
paint.setClipRect( mPixmap.rect() );
|
||||
|
||||
// antialiasing
|
||||
if ( mAntiAliasing )
|
||||
paint.setRenderHint( QPainter::Antialiasing );
|
||||
|
||||
mCanvas->mapRenderer()->render( &paint );
|
||||
paint.end();
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
QPaintDevice& QgsMapCanvasMap::paintDevice()
|
||||
{
|
||||
return mPixmap;
|
||||
return mImage;
|
||||
}
|
||||
|
||||
void QgsMapCanvasMap::updateContents()
|
||||
{
|
||||
// make sure we're using current contents
|
||||
if ( mUseQImageToRender )
|
||||
mPixmap = QPixmap::fromImage( mImage );
|
||||
|
||||
// trigger update of this item
|
||||
void QgsMapCanvasMap::finish()
|
||||
{
|
||||
qDebug("finish!");
|
||||
|
||||
mTimer.stop();
|
||||
|
||||
mDirty = false;
|
||||
|
||||
delete mPainter;
|
||||
mPainter = 0;
|
||||
|
||||
//mLastImage = mImage;
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
void QgsMapCanvasMap::onMapUpdateTimeout()
|
||||
{
|
||||
qDebug("update timer!");
|
||||
|
||||
update();
|
||||
}
|
||||
|
@ -18,56 +18,68 @@
|
||||
|
||||
#include <QGraphicsRectItem>
|
||||
#include <QPixmap>
|
||||
#include <QTimer>
|
||||
|
||||
#include <qgis.h>
|
||||
#include <qgsmaptopixel.h>
|
||||
|
||||
class QgsMapRenderer;
|
||||
class QgsMapRendererSettings;
|
||||
class QgsMapRendererV2;
|
||||
class QgsMapCanvas;
|
||||
|
||||
/** \ingroup gui
|
||||
* A rectangular graphics item representing the map on the canvas.
|
||||
*/
|
||||
class GUI_EXPORT QgsMapCanvasMap : public QGraphicsRectItem
|
||||
class GUI_EXPORT QgsMapCanvasMap : public QObject, public QGraphicsRectItem
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
//! constructor
|
||||
QgsMapCanvasMap( QgsMapCanvas* canvas );
|
||||
|
||||
~QgsMapCanvasMap();
|
||||
|
||||
void refresh();
|
||||
|
||||
//! resize canvas item and pixmap
|
||||
void resize( QSize size );
|
||||
|
||||
void enableAntiAliasing( bool flag ) { mAntiAliasing = flag; }
|
||||
|
||||
void useImageToRender( bool flag ) { mUseQImageToRender = flag; }
|
||||
|
||||
//! renders map using QgsMapRenderer to mPixmap
|
||||
void render();
|
||||
//! @deprecated in 2.1 - does nothing. Kept for API compatibility
|
||||
void render() {}
|
||||
|
||||
void setBackgroundColor( const QColor& color ) { mBgColor = color; }
|
||||
|
||||
void setPanningOffset( const QPoint& point );
|
||||
|
||||
//! @deprecated in 2.1
|
||||
QPaintDevice& paintDevice();
|
||||
|
||||
void paint( QPainter* p, const QStyleOptionGraphicsItem*, QWidget* );
|
||||
|
||||
QRectF boundingRect() const;
|
||||
|
||||
//! Update contents - can be called while drawing to show the status.
|
||||
//! Added in version 1.2
|
||||
void updateContents();
|
||||
//! @deprecated in 2.1 - does nothing. Kept for API compatibility
|
||||
void updateContents() {}
|
||||
|
||||
const QgsMapRendererSettings& settings() const;
|
||||
|
||||
QgsMapToPixel* coordinateTransform(); // TODO: rename!
|
||||
|
||||
public slots:
|
||||
void finish();
|
||||
void onMapUpdateTimeout();
|
||||
|
||||
private:
|
||||
|
||||
//! indicates whether antialiasing will be used for rendering
|
||||
bool mAntiAliasing;
|
||||
|
||||
//! Whether to use a QPixmap or a QImage for the rendering
|
||||
bool mUseQImageToRender;
|
||||
|
||||
QPixmap mPixmap;
|
||||
QImage mImage;
|
||||
QImage mLastImage;
|
||||
|
||||
//QgsMapRenderer* mRender;
|
||||
QgsMapCanvas* mCanvas;
|
||||
@ -75,6 +87,16 @@ class GUI_EXPORT QgsMapCanvasMap : public QGraphicsRectItem
|
||||
QColor mBgColor;
|
||||
|
||||
QPoint mOffset;
|
||||
|
||||
bool mDirty; //!< whether a new rendering job should be started upon next paint() call
|
||||
|
||||
QgsMapRendererV2* mRend;
|
||||
|
||||
QPainter* mPainter;
|
||||
|
||||
QTimer mTimer;
|
||||
|
||||
QgsMapToPixel mMapToPixel;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user