1 /***************************************************************************
2                          qgsrasterdrawer.cpp
3                          ---------------------
4     begin                : June 2012
5     copyright            : (C) 2012 by Radim Blazek
6     email                : radim dot blazek at gmail.com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include "qgslogger.h"
19 #include "qgsrasterblock.h"
20 #include "qgsrasterdrawer.h"
21 #include "qgsrasterinterface.h"
22 #include "qgsrasteriterator.h"
23 #include "qgsrasterviewport.h"
24 #include "qgsmaptopixel.h"
25 #include "qgsrendercontext.h"
26 #include <QImage>
27 #include <QPainter>
28 #ifndef QT_NO_PRINTER
29 #include <QPrinter>
30 #endif
31 
QgsRasterDrawer(QgsRasterIterator * iterator)32 QgsRasterDrawer::QgsRasterDrawer( QgsRasterIterator *iterator ): mIterator( iterator )
33 {
34 }
35 
draw(QPainter * p,QgsRasterViewPort * viewPort,const QgsMapToPixel * qgsMapToPixel,QgsRasterBlockFeedback * feedback)36 void QgsRasterDrawer::draw( QPainter *p, QgsRasterViewPort *viewPort, const QgsMapToPixel *qgsMapToPixel, QgsRasterBlockFeedback *feedback )
37 {
38   QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
39   if ( !p || !mIterator || !viewPort || !qgsMapToPixel )
40   {
41     return;
42   }
43 
44   // last pipe filter has only 1 band
45   int bandNumber = 1;
46   mIterator->startRasterRead( bandNumber, viewPort->mWidth, viewPort->mHeight, viewPort->mDrawnExtent, feedback );
47 
48   //number of cols/rows in output pixels
49   int nCols = 0;
50   int nRows = 0;
51   //shift to top left point for the raster part
52   int topLeftCol = 0;
53   int topLeftRow = 0;
54 
55   // We know that the output data type of last pipe filter is QImage data
56 
57   std::unique_ptr< QgsRasterBlock > block;
58 
59   // readNextRasterPart calcs and resets  nCols, nRows, topLeftCol, topLeftRow
60   while ( mIterator->readNextRasterPart( bandNumber, nCols, nRows,
61                                          block, topLeftCol, topLeftRow ) )
62   {
63     if ( !block )
64     {
65       QgsDebugMsg( QStringLiteral( "Cannot get block" ) );
66       continue;
67     }
68 
69     QImage img = block->image();
70 
71 #ifndef QT_NO_PRINTER
72     // Because of bug in Acrobat Reader we must use "white" transparent color instead
73     // of "black" for PDF. See #9101.
74     QPrinter *printer = dynamic_cast<QPrinter *>( p->device() );
75     if ( printer && printer->outputFormat() == QPrinter::PdfFormat )
76     {
77       QgsDebugMsgLevel( QStringLiteral( "PdfFormat" ), 4 );
78 
79       img = img.convertToFormat( QImage::Format_ARGB32 );
80       QRgb transparentBlack = qRgba( 0, 0, 0, 0 );
81       QRgb transparentWhite = qRgba( 255, 255, 255, 0 );
82       for ( int x = 0; x < img.width(); x++ )
83       {
84         for ( int y = 0; y < img.height(); y++ )
85         {
86           if ( img.pixel( x, y ) == transparentBlack )
87           {
88             img.setPixel( x, y, transparentWhite );
89           }
90         }
91       }
92     }
93 #endif
94 
95     if ( feedback && feedback->renderPartialOutput() )
96     {
97       // there could have been partial preview written before
98       // so overwrite anything with the resulting image.
99       // (we are guaranteed to have a temporary image for this layer, see QgsMapRendererJob::needTemporaryImage)
100       p->setCompositionMode( QPainter::CompositionMode_Source );
101     }
102 
103     drawImage( p, viewPort, img, topLeftCol, topLeftRow, qgsMapToPixel );
104 
105     if ( feedback && feedback->renderPartialOutput() )
106     {
107       // go back to the default composition mode
108       p->setCompositionMode( QPainter::CompositionMode_SourceOver );
109     }
110 
111     // OK this does not matter much anyway as the tile size quite big so most of the time
112     // there would be just one tile for the whole display area, but it won't hurt...
113     if ( feedback && feedback->isCanceled() )
114       break;
115   }
116 }
117 
drawImage(QPainter * p,QgsRasterViewPort * viewPort,const QImage & img,int topLeftCol,int topLeftRow,const QgsMapToPixel * qgsMapToPixel) const118 void QgsRasterDrawer::drawImage( QPainter *p, QgsRasterViewPort *viewPort, const QImage &img, int topLeftCol, int topLeftRow, const QgsMapToPixel *qgsMapToPixel ) const
119 {
120   if ( !p || !viewPort )
121   {
122     return;
123   }
124 
125   //top left position in device coords
126   QPoint tlPoint = QPoint( viewPort->mTopLeftPoint.x() + topLeftCol, viewPort->mTopLeftPoint.y() + topLeftRow );
127   QgsScopedQPainterState painterState( p );
128   p->setRenderHint( QPainter::Antialiasing, false );
129 
130   // Blending problem was reported with PDF output if background color has alpha < 255
131   // in #7766, it seems to be a bug in Qt, setting a brush with alpha 255 is a workaround
132   // which should not harm anything
133   p->setBrush( QBrush( QColor( Qt::white ), Qt::NoBrush ) );
134 
135   if ( qgsMapToPixel )
136   {
137     int w = qgsMapToPixel->mapWidth();
138     int h = qgsMapToPixel->mapHeight();
139 
140     double rotation = qgsMapToPixel->mapRotation();
141     if ( rotation )
142     {
143       // both viewPort and image sizes are dependent on scale
144       double cx = w / 2.0;
145       double cy = h / 2.0;
146       p->translate( cx, cy );
147       p->rotate( rotation );
148       p->translate( -cx, -cy );
149     }
150   }
151 
152   p->drawImage( tlPoint, img );
153 
154 #if 0
155   // For debugging:
156   QRectF br = QRectF( tlPoint, img.size() );
157   QPointF c = br.center();
158   double rad = std::max( br.width(), br.height() ) / 10;
159   p->drawRoundedRect( br, rad, rad );
160   p->drawLine( QLineF( br.x(), br.y(), br.x() + br.width(), br.y() + br.height() ) );
161   p->drawLine( QLineF( br.x() + br.width(), br.y(), br.x(), br.y() + br.height() ) );
162 
163   double nw = br.width() * 0.5;
164   double nh = br.height() * 0.5;
165   br = QRectF( c - QPointF( nw / 2, nh / 2 ), QSize( nw, nh ) );
166   p->drawRoundedRect( br, rad, rad );
167 
168   nw = br.width() * 0.5;
169   nh = br.height() * 0.5;
170   br = QRectF( c - QPointF( nw / 2, nh / 2 ), QSize( nw, nh ) );
171   p->drawRoundedRect( br, rad, rad );
172 #endif
173 }
174 
175