QSizeF QgsRasterSymbolLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
{
  if ( ctx && ctx->painter )
  {
    QColor itemColor = mColor;
    if ( QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( layerNode()->layer() ) )
    {
      if ( QgsRasterRenderer *rasterRenderer = rasterLayer->renderer() )
        itemColor.setAlpha( rasterRenderer->opacity() * 255.0 );
    }
    ctx->painter->setBrush( itemColor );

    if ( settings.drawRasterStroke() )
    {
      QPen pen;
      pen.setColor( settings.rasterStrokeColor() );
      pen.setWidthF( settings.rasterStrokeWidth() );
      pen.setJoinStyle( Qt::MiterJoin );
      ctx->painter->setPen( pen );
    }
    else
    {
      ctx->painter->setPen( Qt::NoPen );
    }

    ctx->painter->drawRect( QRectF( ctx->point.x(), ctx->point.y() + ( itemHeight - settings.symbolSize().height() ) / 2,
                                    settings.symbolSize().width(), settings.symbolSize().height() ) );
  }
  return settings.symbolSize();
}
QSizeF QgsLayerTreeModelLegendNode::drawSymbol( const QgsLegendSettings& settings, ItemContext* ctx, double itemHeight ) const
{
    QIcon symbolIcon = data( Qt::DecorationRole ).value<QIcon>();
    if ( symbolIcon.isNull() )
        return QSizeF();

    if ( ctx )
        symbolIcon.paint( ctx->painter, ctx->point.x(), ctx->point.y() + ( itemHeight - settings.symbolSize().height() ) / 2,
                          settings.symbolSize().width(), settings.symbolSize().height() );
    return settings.symbolSize();
}
void QgsLayerTreeModelLegendNode::exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &, QJsonObject &json ) const
{
  const QIcon icon = data( Qt::DecorationRole ).value<QIcon>();
  if ( icon.isNull() )
    return;

  const QImage image( icon.pixmap( settings.symbolSize().width(), settings.symbolSize().height() ).toImage() );
  QByteArray byteArray;
  QBuffer buffer( &byteArray );
  image.save( &buffer, "PNG" );
  const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
  json[ "icon" ] = base64;
}
QSizeF QgsRasterSymbolLegendNode::drawSymbol( const QgsLegendSettings& settings, ItemContext* ctx, double itemHeight ) const
{
    if ( ctx )
    {
        QColor itemColor = mColor;
        if ( QgsRasterLayer* rasterLayer = dynamic_cast<QgsRasterLayer*>( layerNode()->layer() ) )
        {
            if ( QgsRasterRenderer* rasterRenderer = rasterLayer->renderer() )
                itemColor.setAlpha( rasterRenderer->opacity() * 255.0 );
        }

        ctx->painter->setBrush( itemColor );
        ctx->painter->drawRect( QRectF( ctx->point.x(), ctx->point.y() + ( itemHeight - settings.symbolSize().height() ) / 2,
                                        settings.symbolSize().width(), settings.symbolSize().height() ) );
    }
    return settings.symbolSize();
}
void QgsRasterSymbolLegendNode::exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &, QJsonObject &json ) const
{
  QImage img = QImage( settings.symbolSize().toSize(), QImage::Format_ARGB32 );
  img.fill( Qt::transparent );

  QPainter painter( &img );
  painter.setRenderHint( QPainter::Antialiasing );

  QColor itemColor = mColor;
  if ( QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( layerNode()->layer() ) )
  {
    if ( QgsRasterRenderer *rasterRenderer = rasterLayer->renderer() )
      itemColor.setAlpha( rasterRenderer->opacity() * 255.0 );
  }
  painter.setBrush( itemColor );

  if ( settings.drawRasterStroke() )
  {
    QPen pen;
    pen.setColor( settings.rasterStrokeColor() );
    pen.setWidthF( settings.rasterStrokeWidth() );
    pen.setJoinStyle( Qt::MiterJoin );
    painter.setPen( pen );
  }
  else
  {
    painter.setPen( Qt::NoPen );
  }

  painter.drawRect( QRectF( 0, 0, settings.symbolSize().width(), settings.symbolSize().height() ) );

  QByteArray byteArray;
  QBuffer buffer( &byteArray );
  img.save( &buffer, "PNG" );
  const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
  json[ "icon" ] = base64;
}
QgsLayerTreeModelLegendNode::ItemMetrics QgsLayerTreeModelLegendNode::draw( const QgsLegendSettings& settings, ItemContext* ctx )
{
    QFont symbolLabelFont = settings.style( QgsComposerLegendStyle::SymbolLabel ).font();

    double textHeight = settings.fontHeightCharacterMM( symbolLabelFont, QChar( '0' ) );
    // itemHeight here is not realy item height, it is only for symbol
    // vertical alignment purpose, i.e. ok take single line height
    // if there are more lines, thos run under the symbol
    double itemHeight = qMax(( double ) settings.symbolSize().height(), textHeight );

    ItemMetrics im;
    im.symbolSize = drawSymbol( settings, ctx, itemHeight );
    im.labelSize = drawSymbolText( settings, ctx, im.symbolSize );
    return im;
}
QSizeF QgsSymbolV2LegendNode::drawSymbol( const QgsLegendSettings& settings, ItemContext* ctx, double itemHeight ) const
{
    QgsSymbolV2* s = mItem.symbol();
    if ( !s )
    {
        return QSizeF();
    }

    // setup temporary render context
    QgsRenderContext context;
    context.setScaleFactor( settings.dpi() / 25.4 );
    context.setRendererScale( settings.mapScale() );
    context.setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * context.scaleFactor() ) ) );
    context.setForceVectorOutput( true );
    context.setPainter( ctx ? ctx->painter : 0 );

    //Consider symbol size for point markers
    double height = settings.symbolSize().height();
    double width = settings.symbolSize().width();
    double size = 0;
    //Center small marker symbols
    double widthOffset = 0;
    double heightOffset = 0;

    if ( QgsMarkerSymbolV2* markerSymbol = dynamic_cast<QgsMarkerSymbolV2*>( s ) )
    {
        // allow marker symbol to occupy bigger area if necessary
        size = markerSymbol->size() * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context, s->outputUnit(), s->mapUnitScale() ) / context.scaleFactor();
        height = size;
        width = size;
        if ( width < settings.symbolSize().width() )
        {
            widthOffset = ( settings.symbolSize().width() - width ) / 2.0;
        }
        if ( height < settings.symbolSize().height() )
        {
            heightOffset = ( settings.symbolSize().height() - height ) / 2.0;
        }
    }

    if ( ctx )
    {
        double currentXPosition = ctx->point.x();
        double currentYCoord = ctx->point.y() + ( itemHeight - settings.symbolSize().height() ) / 2;
        QPainter* p = ctx->painter;

        //setup painter scaling to dots so that raster symbology is drawn to scale
        double dotsPerMM = context.scaleFactor();

        int opacity = 255;
        if ( QgsVectorLayer* vectorLayer = dynamic_cast<QgsVectorLayer*>( layerNode()->layer() ) )
            opacity = 255 - ( 255 * vectorLayer->layerTransparency() / 100 );

        p->save();
        p->setRenderHint( QPainter::Antialiasing );
        p->translate( currentXPosition + widthOffset, currentYCoord + heightOffset );
        p->scale( 1.0 / dotsPerMM, 1.0 / dotsPerMM );
        if ( opacity != 255 && settings.useAdvancedEffects() )
        {
            //semi transparent layer, so need to draw symbol to an image (to flatten it first)
            //create image which is same size as legend rect, in case symbol bleeds outside its alloted space
            QSize tempImageSize( width * dotsPerMM, height * dotsPerMM );
            QImage tempImage = QImage( tempImageSize, QImage::Format_ARGB32 );
            tempImage.fill( Qt::transparent );
            QPainter imagePainter( &tempImage );
            context.setPainter( &imagePainter );
            s->drawPreviewIcon( &imagePainter, tempImageSize, &context );
            context.setPainter( ctx->painter );
            //reduce opacity of image
            imagePainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
            imagePainter.fillRect( tempImage.rect(), QColor( 0, 0, 0, opacity ) );
            imagePainter.end();
            //draw rendered symbol image
            p->drawImage( 0, 0, tempImage );
        }
        else
        {
            s->drawPreviewIcon( p, QSize( width * dotsPerMM, height * dotsPerMM ), &context );
        }
        p->restore();
    }

    return QSizeF( qMax( width + 2 * widthOffset, ( double ) settings.symbolSize().width() ),
                   qMax( height + 2 * heightOffset, ( double ) settings.symbolSize().height() ) );
}
QSizeF QgsSymbolLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
{
  QgsSymbol *s = mItem.symbol();
  if ( !s )
  {
    return QSizeF();
  }

  // setup temporary render context
  QgsRenderContext context;
  context.setScaleFactor( settings.dpi() / 25.4 );
  context.setRendererScale( settings.mapScale() );
  context.setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * context.scaleFactor() ) ) );
  context.setForceVectorOutput( true );
  context.setPainter( ctx ? ctx->painter : nullptr );

  if ( ctx && ctx->context )
  {
    context.setExpressionContext( ctx->context->expressionContext() );
  }
  else
  {
    // setup a minimal expression context
    QgsExpressionContext expContext;
    expContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( nullptr ) );
    context.setExpressionContext( expContext );
  }

  //Consider symbol size for point markers
  double height = settings.symbolSize().height();
  double width = settings.symbolSize().width();

  //Center small marker symbols
  double widthOffset = 0;
  double heightOffset = 0;

  if ( QgsMarkerSymbol *markerSymbol = dynamic_cast<QgsMarkerSymbol *>( s ) )
  {
    // allow marker symbol to occupy bigger area if necessary
    double size = markerSymbol->size( context ) / context.scaleFactor();
    height = size;
    width = size;
    if ( width < settings.symbolSize().width() )
    {
      widthOffset = ( settings.symbolSize().width() - width ) / 2.0;
    }
    if ( height < settings.symbolSize().height() )
    {
      heightOffset = ( settings.symbolSize().height() - height ) / 2.0;
    }
  }

  if ( ctx && ctx->painter )
  {
    double currentXPosition = ctx->point.x();
    double currentYCoord = ctx->point.y() + ( itemHeight - settings.symbolSize().height() ) / 2;
    QPainter *p = ctx->painter;

    //setup painter scaling to dots so that raster symbology is drawn to scale
    double dotsPerMM = context.scaleFactor();

    int opacity = 255;
    if ( QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( layerNode()->layer() ) )
      opacity = ( 255 * vectorLayer->opacity() );

    p->save();
    p->setRenderHint( QPainter::Antialiasing );
    p->translate( currentXPosition + widthOffset, currentYCoord + heightOffset );
    p->scale( 1.0 / dotsPerMM, 1.0 / dotsPerMM );
    if ( opacity != 255 && settings.useAdvancedEffects() )
    {
      //semi transparent layer, so need to draw symbol to an image (to flatten it first)
      //create image which is same size as legend rect, in case symbol bleeds outside its alloted space
      QSize tempImageSize( width * dotsPerMM, height * dotsPerMM );
      QImage tempImage = QImage( tempImageSize, QImage::Format_ARGB32 );
      tempImage.fill( Qt::transparent );
      QPainter imagePainter( &tempImage );
      context.setPainter( &imagePainter );
      s->drawPreviewIcon( &imagePainter, tempImageSize, &context );
      context.setPainter( ctx->painter );
      //reduce opacity of image
      imagePainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
      imagePainter.fillRect( tempImage.rect(), QColor( 0, 0, 0, opacity ) );
      imagePainter.end();
      //draw rendered symbol image
      p->drawImage( 0, 0, tempImage );
    }
    else
    {
      s->drawPreviewIcon( p, QSize( width * dotsPerMM, height * dotsPerMM ), &context );
    }

    if ( !mTextOnSymbolLabel.isEmpty() )
    {
      QFontMetricsF fm( mTextOnSymbolTextFormat.scaledFont( context ) );
      qreal yBaselineVCenter = ( height * dotsPerMM + fm.ascent() - fm.descent() ) / 2;
      QgsTextRenderer::drawText( QPointF( width * dotsPerMM / 2, yBaselineVCenter ), 0, QgsTextRenderer::AlignCenter,
                                 QStringList() << mTextOnSymbolLabel, context, mTextOnSymbolTextFormat );
    }

    p->restore();
  }

  return QSizeF( std::max( width + 2 * widthOffset, static_cast< double >( settings.symbolSize().width() ) ),
                 std::max( height + 2 * heightOffset, static_cast< double >( settings.symbolSize().height() ) ) );
}