void QgsMapRenderer::adjustExtentToSize() { double myHeight = mSize.height(); double myWidth = mSize.width(); QgsMapToPixel newCoordXForm; if ( !myWidth || !myHeight ) { mScale = 1; newCoordXForm.setParameters( 0, 0, 0, 0 ); return; } // calculate the translation and scaling parameters // mapUnitsPerPixel = map units per pixel double mapUnitsPerPixelY = mExtent.height() / myHeight; double mapUnitsPerPixelX = mExtent.width() / myWidth; mMapUnitsPerPixel = mapUnitsPerPixelY > mapUnitsPerPixelX ? mapUnitsPerPixelY : mapUnitsPerPixelX; // calculate the actual extent of the mapCanvas double dxmin, dxmax, dymin, dymax, whitespace; if ( mapUnitsPerPixelY > mapUnitsPerPixelX ) { dymin = mExtent.yMinimum(); dymax = mExtent.yMaximum(); whitespace = (( myWidth * mMapUnitsPerPixel ) - mExtent.width() ) * 0.5; dxmin = mExtent.xMinimum() - whitespace; dxmax = mExtent.xMaximum() + whitespace; } else { dxmin = mExtent.xMinimum(); dxmax = mExtent.xMaximum(); whitespace = (( myHeight * mMapUnitsPerPixel ) - mExtent.height() ) * 0.5; dymin = mExtent.yMinimum() - whitespace; dymax = mExtent.yMaximum() + whitespace; } QgsDebugMsg( QString( "Map units per pixel (x,y) : %1, %2\n" ).arg( mapUnitsPerPixelX ).arg( mapUnitsPerPixelY ) ); QgsDebugMsg( QString( "Pixmap dimensions (x,y) : %1, %2\n" ).arg( myWidth ).arg( myHeight ) ); QgsDebugMsg( QString( "Extent dimensions (x,y) : %1, %2\n" ).arg( mExtent.width() ).arg( mExtent.height() ) ); QgsDebugMsg( mExtent.toString() ); // update extent mExtent.setXMinimum( dxmin ); mExtent.setXMaximum( dxmax ); mExtent.setYMinimum( dymin ); mExtent.setYMaximum( dymax ); // update the scale updateScale(); QgsDebugMsg( QString( "Scale (assuming meters as map units) = 1:%1" ).arg( mScale ) ); newCoordXForm.setParameters( mMapUnitsPerPixel, dxmin, dymin, myHeight ); mRenderContext.setMapToPixel( newCoordXForm ); mRenderContext.setExtent( mExtent ); }
QgsLabelPreview::QgsLabelPreview( QWidget* parent ) : QLabel( parent ) { mTmpLyr = new QgsPalLayerSettings(); // construct a device-based render context QgsMapToPixel newCoordXForm; newCoordXForm.setParameters( 0, 0, 0, 0 ); mContext = new QgsRenderContext(); mContext->setMapToPixel( newCoordXForm ); }
void QgsVectorLayerDiagramProvider::drawLabel( QgsRenderContext &context, pal::LabelPosition *label ) const { #if 1 // XXX strk // features are pre-rotated but not scaled/translated, // so we only disable rotation here. Ideally, they'd be // also pre-scaled/translated, as suggested here: // https://issues.qgis.org/issues/11856 QgsMapToPixel xform = context.mapToPixel(); xform.setMapRotation( 0, 0, 0 ); #else const QgsMapToPixel &xform = context.mapToPixel(); #endif QgsDiagramLabelFeature *dlf = dynamic_cast<QgsDiagramLabelFeature *>( label->getFeaturePart()->feature() ); QgsFeature feature; feature.setFields( mFields ); feature.setValid( true ); feature.setId( label->getFeaturePart()->featureId() ); feature.setAttributes( dlf->attributes() ); context.expressionContext().setFeature( feature ); //calculate top-left point for diagram //first, calculate the centroid of the label (accounts for PAL creating //rotated labels when we do not want to draw the diagrams rotated) double centerX = 0; double centerY = 0; for ( int i = 0; i < 4; ++i ) { centerX += label->getX( i ); centerY += label->getY( i ); } QgsPointXY outPt( centerX / 4.0, centerY / 4.0 ); //then, calculate the top left point for the diagram with this center position QgsPointXY centerPt = xform.transform( outPt.x() - label->getWidth() / 2, outPt.y() - label->getHeight() / 2 ); mSettings.renderer()->renderDiagram( feature, context, centerPt.toQPointF(), mSettings.dataDefinedProperties() ); //insert into label search tree to manipulate position interactively mEngine->results()->mLabelSearchTree->insertLabel( label, label->getFeaturePart()->featureId(), mLayerId, QString(), QFont(), true, false ); }
void QgsMapRenderer::render( QPainter* painter ) { //flag to see if the render context has changed //since the last time we rendered. If it hasnt changed we can //take some shortcuts with rendering bool mySameAsLastFlag = true; QgsDebugMsg( "========== Rendering ==========" ); if ( mExtent.isEmpty() ) { QgsDebugMsg( "empty extent... not rendering" ); return; } if ( mSize.width() == 1 && mSize.height() == 1 ) { QgsDebugMsg( "size 1x1... not rendering" ); return; } QPaintDevice* thePaintDevice = painter->device(); if ( !thePaintDevice ) { return; } // wait if ( mDrawing ) { QgsDebugMsg( "already rendering" ); QCoreApplication::processEvents(); } if ( mDrawing ) { QgsDebugMsg( "still rendering - skipping" ); return; } mDrawing = true; QgsCoordinateTransform* ct; #ifdef QGISDEBUG QgsDebugMsg( "Starting to render layer stack." ); QTime renderTime; renderTime.start(); #endif if ( mOverview ) mRenderContext.setDrawEditingInformation( !mOverview ); mRenderContext.setPainter( painter ); 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 ); //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) double sceneDpi = mScaleCalculator->dpi(); double scaleFactor = 1.0; if ( mOutputUnits == QgsMapRenderer::Millimeters ) { 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(); } } QgsOverlayObjectPositionManager* overlayManager = overlayManagerFromSettings(); QList<QgsVectorOverlay*> allOverlayList; //list of all overlays, used to draw them after layers have been rendered // render all layers in the stack, starting at the base QListIterator<QString> li( mLayerSet ); 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(); 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( "Rendering layer " + ml->name() ); QgsDebugMsg( " Layer minscale " + QString( "%1" ).arg( ml->minimumScale() ) ); QgsDebugMsg( " Layer maxscale " + QString( "%1" ).arg( ml->maximumScale() ) ); QgsDebugMsg( " Scale dep. visibility enabled? " + QString( "%1" ).arg( ml->hasScaleBasedVisibility() ) ); QgsDebugMsg( " Input extent: " + ml->extent().toString() ); if ( !ml->hasScaleBasedVisibility() || ( ml->minimumScale() < mScale && mScale < 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 = new QgsCoordinateTransform( ml->crs(), *mDestCRS ); 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( ct ); //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; } //create overlay objects for features within the view extent if ( ml->type() == QgsMapLayer::VectorLayer && overlayManager ) { QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml ); if ( vl ) { QList<QgsVectorOverlay*> thisLayerOverlayList; vl->vectorOverlays( thisLayerOverlayList ); QList<QgsVectorOverlay*>::iterator overlayIt = thisLayerOverlayList.begin(); for ( ; overlayIt != thisLayerOverlayList.end(); ++overlayIt ) { if (( *overlayIt )->displayFlag() ) { ( *overlayIt )->createOverlayObjects( mRenderContext ); allOverlayList.push_back( *overlayIt ); } } overlayManager->addLayer( vl, thisLayerOverlayList ); } } // 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; if ( ! split )//render caching does not yet cater for split extents { if ( mySettings.value( "/qgis/enable_render_caching", false ).toBool() ) { if ( !mySameAsLastFlag || ml->cacheImage() == 0 ) { QgsDebugMsg( "\n\n\nCaching enabled but layer redraw forced by extent change or empty cache\n\n\n" ); QImage * mypImage = new QImage( mRenderContext.painter()->device()->width(), mRenderContext.painter()->device()->height(), QImage::Format_ARGB32 ); 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( "\n\n\nCaching enabled --- drawing layer from cached image\n\n\n" ); 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 ( 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 ) ) { emit drawError( ml ); } else { QgsDebugMsg( "Layer rendered without issues" ); } if ( split ) { mRenderContext.setExtent( r2 ); if ( !ml->draw( mRenderContext ) ) { emit drawError( ml ); } } if ( scaleRaster ) { mRenderContext.setMapToPixel( bk_mapToPixel ); mRenderContext.painter()->restore(); } if ( mySettings.value( "/qgis/enable_render_caching", false ).toBool() ) { if ( !split ) { // 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 mypContextPainter->drawImage( 0, 0, *( ml->cacheImage() ) ); } } disconnect( ml, SIGNAL( drawingProgress( int, int ) ), this, SLOT( onDrawingProgress( int, int ) ) ); } else // layer not visible due to scale {
QgsRasterLayerRenderer::QgsRasterLayerRenderer( QgsRasterLayer* layer, QgsRenderContext& rendererContext ) : QgsMapLayerRenderer( layer->id() ) , mRasterViewPort( nullptr ) , mPipe( nullptr ) { mPainter = rendererContext.painter(); const QgsMapToPixel& theQgsMapToPixel = rendererContext.mapToPixel(); mMapToPixel = &theQgsMapToPixel; QgsMapToPixel mapToPixel = theQgsMapToPixel; if ( mapToPixel.mapRotation() ) { // unset rotation for the sake of local computations. // Rotation will be handled by QPainter later // TODO: provide a method of QgsMapToPixel to fetch map center // in geographical units QgsPoint center = mapToPixel.toMapCoordinates( mapToPixel.mapWidth() / 2.0, mapToPixel.mapHeight() / 2.0 ); mapToPixel.setMapRotation( 0, center.x(), center.y() ); } QgsRectangle myProjectedViewExtent; QgsRectangle myProjectedLayerExtent; if ( rendererContext.coordinateTransform() ) { QgsDebugMsg( "coordinateTransform set -> project extents." ); try { myProjectedViewExtent = rendererContext.coordinateTransform()->transformBoundingBox( rendererContext.extent() ); } catch ( QgsCsException &cs ) { QgsMessageLog::logMessage( QObject::tr( "Could not reproject view extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) ); myProjectedViewExtent.setMinimal(); } try { myProjectedLayerExtent = rendererContext.coordinateTransform()->transformBoundingBox( layer->extent() ); } catch ( QgsCsException &cs ) { QgsMessageLog::logMessage( QObject::tr( "Could not reproject layer extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) ); myProjectedLayerExtent.setMinimal(); } } else { QgsDebugMsg( "coordinateTransform not set" ); myProjectedViewExtent = rendererContext.extent(); myProjectedLayerExtent = layer->extent(); } // clip raster extent to view extent QgsRectangle myRasterExtent = myProjectedViewExtent.intersect( &myProjectedLayerExtent ); if ( myRasterExtent.isEmpty() ) { QgsDebugMsg( "draw request outside view extent." ); // nothing to do return; } QgsDebugMsg( "theViewExtent is " + rendererContext.extent().toString() ); QgsDebugMsg( "myProjectedViewExtent is " + myProjectedViewExtent.toString() ); QgsDebugMsg( "myProjectedLayerExtent is " + myProjectedLayerExtent.toString() ); QgsDebugMsg( "myRasterExtent is " + myRasterExtent.toString() ); // // The first thing we do is set up the QgsRasterViewPort. This struct stores all the settings // relating to the size (in pixels and coordinate system units) of the raster part that is // in view in the map window. It also stores the origin. // //this is not a class level member because every time the user pans or zooms //the contents of the rasterViewPort will change mRasterViewPort = new QgsRasterViewPort(); mRasterViewPort->mDrawnExtent = myRasterExtent; if ( rendererContext.coordinateTransform() ) { mRasterViewPort->mSrcCRS = layer->crs(); mRasterViewPort->mDestCRS = rendererContext.coordinateTransform()->destCRS(); mRasterViewPort->mSrcDatumTransform = rendererContext.coordinateTransform()->sourceDatumTransform(); mRasterViewPort->mDestDatumTransform = rendererContext.coordinateTransform()->destinationDatumTransform(); } else { mRasterViewPort->mSrcCRS = QgsCoordinateReferenceSystem(); // will be invalid mRasterViewPort->mDestCRS = QgsCoordinateReferenceSystem(); // will be invalid mRasterViewPort->mSrcDatumTransform = -1; mRasterViewPort->mDestDatumTransform = -1; } // get dimensions of clipped raster image in device coordinate space (this is the size of the viewport) mRasterViewPort->mTopLeftPoint = mapToPixel.transform( myRasterExtent.xMinimum(), myRasterExtent.yMaximum() ); mRasterViewPort->mBottomRightPoint = mapToPixel.transform( myRasterExtent.xMaximum(), myRasterExtent.yMinimum() ); // align to output device grid, i.e. floor/ceil to integers // TODO: this should only be done if paint device is raster - screen, image // for other devices (pdf) it can have floating point origin // we could use floating point for raster devices as well, but respecting the // output device grid should make it more effective as the resampling is done in // the provider anyway mRasterViewPort->mTopLeftPoint.setX( floor( mRasterViewPort->mTopLeftPoint.x() ) ); mRasterViewPort->mTopLeftPoint.setY( floor( mRasterViewPort->mTopLeftPoint.y() ) ); mRasterViewPort->mBottomRightPoint.setX( ceil( mRasterViewPort->mBottomRightPoint.x() ) ); mRasterViewPort->mBottomRightPoint.setY( ceil( mRasterViewPort->mBottomRightPoint.y() ) ); // recalc myRasterExtent to aligned values myRasterExtent.set( mapToPixel.toMapCoordinatesF( mRasterViewPort->mTopLeftPoint.x(), mRasterViewPort->mBottomRightPoint.y() ), mapToPixel.toMapCoordinatesF( mRasterViewPort->mBottomRightPoint.x(), mRasterViewPort->mTopLeftPoint.y() ) ); //raster viewport top left / bottom right are already rounded to int mRasterViewPort->mWidth = static_cast<int>( mRasterViewPort->mBottomRightPoint.x() - mRasterViewPort->mTopLeftPoint.x() ); mRasterViewPort->mHeight = static_cast<int>( mRasterViewPort->mBottomRightPoint.y() - mRasterViewPort->mTopLeftPoint.y() ); //the drawable area can start to get very very large when you get down displaying 2x2 or smaller, this is becasue //mapToPixel.mapUnitsPerPixel() is less then 1, //so we will just get the pixel data and then render these special cases differently in paintImageToCanvas() QgsDebugMsgLevel( QString( "mapUnitsPerPixel = %1" ).arg( mapToPixel.mapUnitsPerPixel() ), 3 ); QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( layer->width() ), 3 ); QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( layer->height() ), 3 ); QgsDebugMsgLevel( QString( "myRasterExtent.xMinimum() = %1" ).arg( myRasterExtent.xMinimum() ), 3 ); QgsDebugMsgLevel( QString( "myRasterExtent.xMaximum() = %1" ).arg( myRasterExtent.xMaximum() ), 3 ); QgsDebugMsgLevel( QString( "myRasterExtent.yMinimum() = %1" ).arg( myRasterExtent.yMinimum() ), 3 ); QgsDebugMsgLevel( QString( "myRasterExtent.yMaximum() = %1" ).arg( myRasterExtent.yMaximum() ), 3 ); QgsDebugMsgLevel( QString( "mTopLeftPoint.x() = %1" ).arg( mRasterViewPort->mTopLeftPoint.x() ), 3 ); QgsDebugMsgLevel( QString( "mBottomRightPoint.x() = %1" ).arg( mRasterViewPort->mBottomRightPoint.x() ), 3 ); QgsDebugMsgLevel( QString( "mTopLeftPoint.y() = %1" ).arg( mRasterViewPort->mTopLeftPoint.y() ), 3 ); QgsDebugMsgLevel( QString( "mBottomRightPoint.y() = %1" ).arg( mRasterViewPort->mBottomRightPoint.y() ), 3 ); QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( mRasterViewPort->mWidth ), 3 ); QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( mRasterViewPort->mHeight ), 3 ); // /\/\/\ - added to handle zoomed-in rasters // TODO R->mLastViewPort = *mRasterViewPort; // TODO: is it necessary? Probably WMS only? layer->dataProvider()->setDpi( rendererContext.rasterScaleFactor() * 25.4 * rendererContext.scaleFactor() ); // copy the whole raster pipe! mPipe = new QgsRasterPipe( *layer->pipe() ); }
QPixmap QgsFontButton::createDragIcon( QSize size, const QgsTextFormat *tempFormat, const QFont *tempFont ) const { if ( !tempFormat ) tempFormat = &mFormat; if ( !tempFont ) tempFont = &mFont; //create an icon pixmap QPixmap pixmap( size.width(), size.height() ); pixmap.fill( Qt::transparent ); QPainter p; p.begin( &pixmap ); p.setRenderHint( QPainter::Antialiasing ); QRect rect( 0, 0, size.width(), size.height() ); if ( mMode == ModeQFont || tempFormat->color().lightnessF() < 0.7 ) { p.setBrush( QBrush( QColor( 255, 255, 255 ) ) ); p.setPen( QPen( QColor( 150, 150, 150 ), 0 ) ); } else { p.setBrush( QBrush( QColor( 0, 0, 0 ) ) ); p.setPen( QPen( QColor( 100, 100, 100 ), 0 ) ); } p.drawRect( rect ); p.setBrush( Qt::NoBrush ); p.setPen( Qt::NoPen ); switch ( mMode ) { case ModeTextRenderer: { QgsRenderContext context; QgsMapToPixel newCoordXForm; newCoordXForm.setParameters( 1, 0, 0, 0, 0, 0 ); context.setMapToPixel( newCoordXForm ); context.setScaleFactor( QgsApplication::desktop()->logicalDpiX() / 25.4 ); context.setUseAdvancedEffects( true ); context.setPainter( &p ); // slightly inset text to account for buffer/background double xtrans = 0; if ( tempFormat->buffer().enabled() ) xtrans = context.convertToPainterUnits( tempFormat->buffer().size(), tempFormat->buffer().sizeUnit(), tempFormat->buffer().sizeMapUnitScale() ); if ( tempFormat->background().enabled() && tempFormat->background().sizeType() != QgsTextBackgroundSettings::SizeFixed ) xtrans = std::max( xtrans, context.convertToPainterUnits( tempFormat->background().size().width(), tempFormat->background().sizeUnit(), tempFormat->background().sizeMapUnitScale() ) ); double ytrans = 0.0; if ( tempFormat->buffer().enabled() ) ytrans = std::max( ytrans, context.convertToPainterUnits( tempFormat->buffer().size(), tempFormat->buffer().sizeUnit(), tempFormat->buffer().sizeMapUnitScale() ) ); if ( tempFormat->background().enabled() ) ytrans = std::max( ytrans, context.convertToPainterUnits( tempFormat->background().size().height(), tempFormat->background().sizeUnit(), tempFormat->background().sizeMapUnitScale() ) ); QRectF textRect = rect; textRect.setLeft( xtrans ); textRect.setWidth( textRect.width() - xtrans ); textRect.setTop( ytrans ); if ( textRect.height() > 300 ) textRect.setHeight( 300 ); if ( textRect.width() > 2000 ) textRect.setWidth( 2000 ); QgsTextRenderer::drawText( textRect, 0, QgsTextRenderer::AlignCenter, QStringList() << tr( "Aa" ), context, *tempFormat ); break; } case ModeQFont: { p.setBrush( Qt::NoBrush ); p.setPen( QColor( 0, 0, 0 ) ); p.setFont( *tempFont ); QRectF textRect = rect; textRect.setLeft( 2 ); p.drawText( textRect, Qt::AlignVCenter, tr( "Aa" ) ); break; } } p.end(); return pixmap; }
void QgsMapRenderer::render( QPainter* painter, double* forceWidthScale ) { //Lock render method for concurrent threads (e.g. from globe) QMutexLocker renderLock( &mRenderMutex ); QgsDebugMsg( "========== Rendering ==========" ); if ( mExtent.isEmpty() ) { QgsDebugMsg( "empty extent... not rendering" ); return; } if ( mSize.width() == 1 && mSize.height() == 1 ) { QgsDebugMsg( "size 1x1... not rendering" ); return; } QPaintDevice* thePaintDevice = painter->device(); if ( !thePaintDevice ) { return; } // wait if ( mDrawing ) { QgsDebugMsg( "already rendering" ); QCoreApplication::processEvents(); } if ( mDrawing ) { QgsDebugMsg( "still rendering - skipping" ); return; } mDrawing = true; const QgsCoordinateTransform *ct; #ifdef QGISDEBUG QgsDebugMsg( "Starting to render layer stack." ); QTime renderTime; renderTime.start(); #endif if ( mOverview ) mRenderContext.setDrawEditingInformation( !mOverview ); mRenderContext.setPainter( painter ); 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 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( 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) 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 ); } if ( mRenderContext.scaleFactor() != scaleFactor ) { mRenderContext.setScaleFactor( scaleFactor ); } if ( mRenderContext.rendererScale() != mScale ) { //add map scale to render context mRenderContext.setRendererScale( mScale ); } if ( mLastExtent != mExtent ) { mLastExtent = mExtent; } mRenderContext.setLabelingEngine( mLabelingEngine ); if ( mLabelingEngine ) mLabelingEngine->init( mapSettings() ); // render all layers in the stack, starting at the base QListIterator<QString> li( mLayerSet ); 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() <= mScale && mScale < 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 = transformation( ml ); 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( ct ); //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; } QSettings mySettings; // 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 ((( 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 ) ) { emit drawError( ml ); } else { QgsDebugMsg( "Layer rendered without issues" ); } if ( split ) { mRenderContext.setExtent( r2 ); if ( !ml->draw( mRenderContext ) ) { 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 ( 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 {