text: /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
   * Qwt Widget Library
   * Copyright (C) 1997   Josef Wilgen
   * Copyright (C) 2002   Uwe Rathmann
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the Qwt License, Version 1.0
   *****************************************************************************/
  
  #include "qwt_painter.h"
  #include "qwt_math.h"
  #include "qwt_clipper.h"
  #include "qwt_color_map.h"
  #include "qwt_scale_map.h"
  #include <qwindowdefs.h>
  #include <qwidget.h>
  #include <qframe.h>
  #include <qrect.h>
  #include <qpainter.h>
  #include <qpalette.h>
  #include <qpaintdevice.h>
  #include <qpainterpath.h>
  #include <qpixmap.h>
  #include <qstyle.h>
  #include <qtextdocument.h>
  #include <qabstracttextdocumentlayout.h>
  #include <qstyleoption.h>
  #include <qpaintengine.h>
  #include <qapplication.h>
  #include <qdesktopwidget.h>
  
  #if QT_VERSION >= 0x050000
  #include <qwindow.h>
  #endif
  
  #if QT_VERSION < 0x050000
  
  #ifdef Q_WS_X11
  #include <qx11info_x11.h>
  #endif
  
  #endif
  
  bool QwtPainter::d_polylineSplitting = true;
  bool QwtPainter::d_roundingAlignment = true;
  
  static inline bool qwtIsClippingNeeded(
      const QPainter *painter, QRectF &clipRect )
  {
      bool doClipping = false;
      const QPaintEngine *pe = painter->paintEngine();
      if ( pe && pe->type() == QPaintEngine::SVG )
      {
          // The SVG paint engine ignores any clipping,
  
          if ( painter->hasClipping() )
          {
              doClipping = true;
              clipRect = painter->clipRegion().boundingRect();
          }
      }
  
      return doClipping;
  }
  
  template <class T>
  static inline void qwtDrawPolyline( QPainter *painter,
      const T *points, int pointCount, bool polylineSplitting )
  {
      bool doSplit = false;
      if ( polylineSplitting )
      {
          const QPaintEngine *pe = painter->paintEngine();
          if ( pe && pe->type() == QPaintEngine::Raster )
          {
              /*
                  The raster paint engine seems to use some algo with O(n*n).
                  ( Qt 4.3 is better than Qt 4.2, but remains unacceptable)
                  To work around this problem, we have to split the polygon into
                  smaller pieces.
               */
              doSplit = true;
          }
      }
  
      if ( doSplit )
      {
          const int splitSize = 6;
  
          for ( int i = 0; i < pointCount; i += splitSize )
          {
              const int n = qMin( splitSize + 1, pointCount - i );
              painter->drawPolyline( points + i, n );
          }
      }
      else
      {
          painter->drawPolyline( points, pointCount );
      }
  }
  
  static inline QSize qwtScreenResolution()
  {
      static QSize screenResolution;
      if ( !screenResolution.isValid() )
      {
          QDesktopWidget *desktop = QApplication::desktop();
          if ( desktop )
          {
              screenResolution.setWidth( desktop->logicalDpiX() );
              screenResolution.setHeight( desktop->logicalDpiY() );
          }
      }
  
      return screenResolution;
  }
  
  static inline void qwtUnscaleFont( QPainter *painter )
  {
      if ( painter->font().pixelSize() >= 0 )
          return;
  
      const QSize screenResolution = qwtScreenResolution();
  
      const QPaintDevice *pd = painter->device();
      if ( pd->logicalDpiX() != screenResolution.width() ||
          pd->logicalDpiY() != screenResolution.height() )
      {
          QFont pixelFont( painter->font(), QApplication::desktop() );
          pixelFont.setPixelSize( QFontInfo( pixelFont ).pixelSize() );
  
          painter->setFont( pixelFont );
      }
  }
  
  /*!
    Check is the application is running with the X11 graphics system
    that has some special capabilities that can be used for incremental
    painting to a widget.
  
    \return True, when the graphics system is X11
  */
  bool QwtPainter::isX11GraphicsSystem()
  {
      /*
          The X11 paint engine has been removed with Qt 5.0, but
          reintroduced with Qt 5.10. It can be enabled with
          "export QT_XCB_NATIVE_PAINTING=1".
       */
  
      static int onX11 = -1;
      if ( onX11 < 0 )
      {
          QPixmap pm( 1, 1 );
          QPainter painter( &pm );
  
          onX11 = ( painter.paintEngine()->type() == QPaintEngine::X11 ) ? 1 : 0;
      }
  
      return onX11 == 1;
  }
  
  /*!
    Check if the painter is using a paint engine, that aligns
    coordinates to integers. Today these are all paint engines
    beside QPaintEngine::Pdf and QPaintEngine::SVG.
  
    If we have an integer based paint engine it is also
    checked if the painter has a transformation matrix,
    that rotates or scales.
  
    \param  painter Painter
    \return true, when the painter is aligning
  
    \sa setRoundingAlignment()
  */
  bool QwtPainter::isAligning( QPainter *painter )
  {
      if ( painter && painter->isActive() )
      {
          switch ( painter->paintEngine()->type() )
          {
              case QPaintEngine::Pdf:
              case QPaintEngine::SVG:
                  return false;
  
              default:;
          }
  
          const QTransform &tr = painter->transform();
          if ( tr.isRotating() || tr.isScaling() )
          {
              // we might have to check translations too
              return false;
          }
      }
  
      return true;
  }
  
  /*!
    Enable whether coordinates should be rounded, before they are painted
    to a paint engine that floors to integer values. For other paint engines
    ( PDF, SVG ) this flag has no effect.
    QwtPainter stores this flag only, the rounding itself is done in
    the painting code ( f.e the plot items ).
  
    The default setting is true.
  
    \sa roundingAlignment(), isAligning()
  */
  void QwtPainter::setRoundingAlignment( bool enable )
  {
      d_roundingAlignment = enable;
  }
  
  /*!
    \brief En/Disable line splitting for the raster paint engine
  
    In some Qt versions the raster paint engine paints polylines of many points
    much faster when they are split in smaller chunks: f.e all supported Qt versions
    >= Qt 5.0 when drawing an antialiased polyline with a pen width >=2.
  
    Also the raster paint engine has a nasty bug in many versions ( Qt 4.8 - ... )
    for short lines ( https://codereview.qt-project.org/#/c/99456 ), that is worked
    around in this mode.
  
    The default setting is true.
  
    \sa polylineSplitting()
  */
  void QwtPainter::setPolylineSplitting( bool enable )
  {
      d_polylineSplitting = enable;
  }
  
  //! Wrapper for QPainter::drawPath()
  void QwtPainter::drawPath( QPainter *painter, const QPainterPath &path )
  {
      painter->drawPath( path );
  }
  
  //! Wrapper for QPainter::drawRect()
  void QwtPainter::drawRect( QPainter *painter, double x, double y, double w, double h )
  {
      drawRect( painter, QRectF( x, y, w, h ) );
  }
  
  //! Wrapper for QPainter::drawRect()
  void QwtPainter::drawRect( QPainter *painter, const QRectF &rect )
  {
      const QRectF r = rect;
  
      QRectF clipRect;
      const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
  
      if ( deviceClipping )
      {
          if ( !clipRect.intersects( r ) )
              return;
  
          if ( !clipRect.contains( r ) )
          {
              fillRect( painter, r & clipRect, painter->brush() );
  
              painter->save();
              painter->setBrush( Qt::NoBrush );
              drawPolyline( painter, QPolygonF( r ) );
              painter->restore();
  
              return;
          }
      }
  
      painter->drawRect( r );
  }
  
  //! Wrapper for QPainter::fillRect()
  void QwtPainter::fillRect( QPainter *painter,
      const QRectF &rect, const QBrush &brush )
  {
      if ( !rect.isValid() )
          return;
  
      QRectF clipRect;
      const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
  
      /*
        Performance of Qt4 is horrible for a non trivial brush. Without
        clipping expect minutes or hours for repainting large rectangles
        (might result from zooming)
      */
  
      if ( deviceClipping )
          clipRect &= painter->window();
      else
          clipRect = painter->window();
  
      if ( painter->hasClipping() )
          clipRect &= painter->clipRegion().boundingRect();
  
      QRectF r = rect;
      if ( deviceClipping )
          r = r.intersected( clipRect );
  
      if ( r.isValid() )
          painter->fillRect( r, brush );
  }
  
  //! Wrapper for QPainter::drawPie()
  void QwtPainter::drawPie( QPainter *painter, const QRectF &rect,
      int a, int alen )
  {
      QRectF clipRect;
      const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
      if ( deviceClipping && !clipRect.contains( rect ) )
          return;
  
      painter->drawPie( rect, a, alen );
  }
  
  //! Wrapper for QPainter::drawEllipse()
  void QwtPainter::drawEllipse( QPainter *painter, const QRectF &rect )
  {
      QRectF clipRect;
      const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
  
      if ( deviceClipping && !clipRect.contains( rect ) )
          return;
  
      painter->drawEllipse( rect );
  }
  
  //! Wrapper for QPainter::drawText()
  void QwtPainter::drawText( QPainter *painter, double x, double y,
          const QString &text )
  {
      drawText( painter, QPointF( x, y ), text );
  }
  
  //! Wrapper for QPainter::drawText()
  void QwtPainter::drawText( QPainter *painter, const QPointF &pos,
          const QString &text )
  {
      QRectF clipRect;
      const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
  
      if ( deviceClipping && !clipRect.contains( pos ) )
          return;
  
  
      painter->save();
      qwtUnscaleFont( painter );
      painter->drawText( pos, text );
      painter->restore();
  }
  
  //! Wrapper for QPainter::drawText()
  void QwtPainter::drawText( QPainter *painter,
      double x, double y, double w, double h,
      int flags, const QString &text )
  {
      drawText( painter, QRectF( x, y, w, h ), flags, text );
  }
  
  //! Wrapper for QPainter::drawText()
  void QwtPainter::drawText( QPainter *painter, const QRectF &rect,
          int flags, const QString &text )
  {
      painter->save();
      qwtUnscaleFont( painter );
      painter->drawText( rect, flags, text );
      painter->restore();
  }
  
  #ifndef QT_NO_RICHTEXT
  
  /*!
    Draw a text document into a rectangle
  
    \param painter Painter
    \param rect Traget rectangle
    \param flags Alignments/Text flags, see QPainter::drawText()
    \param text Text document
  */
  void QwtPainter::drawSimpleRichText( QPainter *painter, const QRectF &rect,
      int flags, const QTextDocument &text )
  {
      QTextDocument *txt = text.clone();
  
      painter->save();
  
      QRectF unscaledRect = rect;
  
      if ( painter->font().pixelSize() < 0 )
      {
          const QSize res = qwtScreenResolution();
  
          const QPaintDevice *pd = painter->device();
          if ( pd->logicalDpiX() != res.width() ||
              pd->logicalDpiY() != res.height() )
          {
              QTransform transform;
              transform.scale( res.width() / double( pd->logicalDpiX() ),
                  res.height() / double( pd->logicalDpiY() ));
  
              painter->setWorldTransform( transform, true );
              unscaledRect = transform.inverted().mapRect(rect);
          }
      }
  
      txt->setDefaultFont( painter->font() );
      txt->setPageSize( QSizeF( unscaledRect.width(), QWIDGETSIZE_MAX ) );
  
      QAbstractTextDocumentLayout* layout = txt->documentLayout();
  
      const double height = layout->documentSize().height();
      double y = unscaledRect.y();
      if ( flags & Qt::AlignBottom )
          y += ( unscaledRect.height() - height );
      else if ( flags & Qt::AlignVCenter )
          y += ( unscaledRect.height() - height ) / 2;
  
      QAbstractTextDocumentLayout::PaintContext context;
      context.palette.setColor( QPalette::Text, painter->pen().color() );
  
      painter->translate( unscaledRect.x(), y );
      layout->draw( painter, context );
  
      painter->restore();
      delete txt;
  }
  
  #endif // !QT_NO_RICHTEXT
  
  
  //! Wrapper for QPainter::drawLine()
  void QwtPainter::drawLine( QPainter *painter,
      const QPointF &p1, const QPointF &p2 )
  {
      QRectF clipRect;
      const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
  
      if ( deviceClipping &&
          !( clipRect.contains( p1 ) && clipRect.contains( p2 ) ) )
      {
          QPolygonF polygon;
          polygon += p1;
          polygon += p2;
          drawPolyline( painter, polygon );
          return;
      }
  
      painter->drawLine( p1, p2 );
  }
  
  //! Wrapper for QPainter::drawPolygon()
  void QwtPainter::drawPolygon( QPainter *painter, const QPolygonF &polygon )
  {
      QRectF clipRect;
      const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
  
      QPolygonF cpa = polygon;
      if ( deviceClipping )
          cpa = QwtClipper::clipPolygonF( clipRect, polygon, true );
  
      painter->drawPolygon( cpa );
  }
  
  //! Wrapper for QPainter::drawPolyline()
  void QwtPainter::drawPolyline( QPainter *painter, const QPolygonF &polygon )
  {
      QRectF clipRect;
      const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
  
      QPolygonF cpa = polygon;
      if ( deviceClipping )
          cpa = QwtClipper::clipPolygonF( clipRect, cpa );
  
      qwtDrawPolyline<QPointF>( painter,
          cpa.constData(), cpa.size(), d_polylineSplitting );
  }
  
  //! Wrapper for QPainter::drawPolyline()
  void QwtPainter::drawPolyline( QPainter *painter,
      const QPointF *points, int pointCount )
  {
      QRectF clipRect;
      const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
  
      if ( deviceClipping )
      {
          QPolygonF polygon( pointCount );
          ::memcpy( polygon.data(), points, pointCount * sizeof( QPointF ) );
  
          polygon = QwtClipper::clipPolygonF( clipRect, polygon );
          qwtDrawPolyline<QPointF>( painter,
              polygon.constData(), polygon.size(), d_polylineSplitting );
      }
      else
      {
          qwtDrawPolyline<QPointF>( painter, points, pointCount, d_polylineSplitting );
      }
  }
  
  //! Wrapper for QPainter::drawPolygon()
  void QwtPainter::drawPolygon( QPainter *painter, const QPolygon &polygon )
  {
      QRectF clipRect;
      const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
  
      QPolygon cpa = polygon;
      if ( deviceClipping )
          cpa = QwtClipper::clipPolygon( clipRect, polygon, true );
  
      painter->drawPolygon( cpa );
  }
  
  //! Wrapper for QPainter::drawPolyline()
  void QwtPainter::drawPolyline( QPainter *painter, const QPolygon &polygon )
  {
      QRectF clipRect;
      const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
  
      QPolygon cpa = polygon;
      if ( deviceClipping )
          cpa = QwtClipper::clipPolygon( clipRect, cpa );
  
      qwtDrawPolyline<QPoint>( painter,
          cpa.constData(), cpa.size(), d_polylineSplitting );
  }
  
  //! Wrapper for QPainter::drawPolyline()
  void QwtPainter::drawPolyline( QPainter *painter,
      const QPoint *points, int pointCount )
  {
      QRectF clipRect;
      const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
  
      if ( deviceClipping )
      {
          QPolygon polygon( pointCount );
          ::memcpy( polygon.data(), points, pointCount * sizeof( QPoint ) );
  
          polygon = QwtClipper::clipPolygon( clipRect, polygon );
          qwtDrawPolyline<QPoint>( painter,
              polygon.constData(), polygon.size(), d_polylineSplitting );
      }
      else
      {
          qwtDrawPolyline<QPoint>( painter, points, pointCount, d_polylineSplitting );
      }
  }
  
  //! Wrapper for QPainter::drawPoint()
  void QwtPainter::drawPoint( QPainter *painter, const QPointF &pos )
  {
      QRectF clipRect;
      const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
  
      if ( deviceClipping && !clipRect.contains( pos ) )
          return;
  
      painter->drawPoint( pos );
  }
  
  //! Wrapper for QPainter::drawPoint()
  void QwtPainter::drawPoint( QPainter *painter, const QPoint &pos )
  {
      QRectF clipRect;
      const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
  
      if ( deviceClipping )
      {
          const int minX = qCeil( clipRect.left() );
          const int maxX = qFloor( clipRect.right() );
          const int minY = qCeil( clipRect.top() );
          const int maxY = qFloor( clipRect.bottom() );
  
          if ( pos.x() < minX || pos.x() > maxX
              || pos.y() < minY || pos.y() > maxY )
          {
              return;
          }
      }
  
      painter->drawPoint( pos );
  }
  
  //! Wrapper for QPainter::drawPoints()
  void QwtPainter::drawPoints( QPainter *painter,
      const QPoint *points, int pointCount )
  {
      QRectF clipRect;
      const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
  
      if ( deviceClipping )
      {
          const int minX = qCeil( clipRect.left() );
          const int maxX = qFloor( clipRect.right() );
          const int minY = qCeil( clipRect.top() );
          const int maxY = qFloor( clipRect.bottom() );
  
          const QRect r( minX, minY, maxX - minX, maxY - minY );
  
          QPolygon clippedPolygon( pointCount );
          QPoint *clippedData = clippedPolygon.data();
  
          int numClippedPoints = 0;
          for ( int i = 0; i < pointCount; i++ )
          {
              if ( r.contains( points[i] ) )
                  clippedData[ numClippedPoints++ ] = points[i];
          }
          painter->drawPoints( clippedData, numClippedPoints );
      }
      else
      {
          painter->drawPoints( points, pointCount );
      }
  }
  
  //! Wrapper for QPainter::drawPoints()
  void QwtPainter::drawPoints( QPainter *painter,
      const QPointF *points, int pointCount )
  {
      QRectF clipRect;
      const bool deviceClipping = qwtIsClippingNeeded( painter, clipRect );
  
      if ( deviceClipping )
      {
          QPolygonF clippedPolygon( pointCount );
          QPointF *clippedData = clippedPolygon.data();
  
          int numClippedPoints = 0;
          for ( int i = 0; i < pointCount; i++ )
          {
              if ( clipRect.contains( points[i] ) )
                  clippedData[ numClippedPoints++ ] = points[i];
          }
          painter->drawPoints( clippedData, numClippedPoints );
      }
      else
      {
          painter->drawPoints( points, pointCount );
      }
  }
  
  //! Wrapper for QPainter::drawImage()
  void QwtPainter::drawImage( QPainter *painter,
      const QRectF &rect, const QImage &image )
  {
      const QRect alignedRect = rect.toAlignedRect();
  
      if ( alignedRect != rect )
      {
          const QRectF clipRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
  
          painter->save();
          painter->setClipRect( clipRect, Qt::IntersectClip );
          painter->drawImage( alignedRect, image );
          painter->restore();
      }
      else
      {
          painter->drawImage( alignedRect, image );
      }
  }
  
  //! Wrapper for QPainter::drawPixmap()
  void QwtPainter::drawPixmap( QPainter *painter,
      const QRectF &rect, const QPixmap &pixmap )
  {
      const QRect alignedRect = rect.toAlignedRect();
  
      if ( alignedRect != rect )
      {
          const QRectF clipRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
  
          painter->save();
          painter->setClipRect( clipRect, Qt::IntersectClip );
          painter->drawPixmap( alignedRect, pixmap );
          painter->restore();
      }
      else
      {
          painter->drawPixmap( alignedRect, pixmap );
      }
  }
  
  //! Draw a focus rectangle on a widget using its style.
  void QwtPainter::drawFocusRect( QPainter *painter, const QWidget *widget )
  {
      drawFocusRect( painter, widget, widget->rect() );
  }
  
  //! Draw a focus rectangle on a widget using its style.
  void QwtPainter::drawFocusRect( QPainter *painter, const QWidget *widget,
      const QRect &rect )
  {
      QStyleOptionFocusRect opt;
      opt.init( widget );
      opt.rect = rect;
      opt.state |= QStyle::State_HasFocus;
      opt.backgroundColor = widget->palette().color( widget->backgroundRole() );
  
      widget->style()->drawPrimitive(
          QStyle::PE_FrameFocusRect, &opt, painter, widget );
  }
  
  /*!
    Draw a round frame
  
    \param painter Painter
    \param rect Frame rectangle
    \param palette QPalette::WindowText is used for plain borders
                   QPalette::Dark and QPalette::Light for raised
                   or sunken borders
    \param lineWidth Line width
    \param frameStyle bitwise OR´ed value of QFrame::Shape and QFrame::Shadow
  */
  void QwtPainter::drawRoundFrame( QPainter *painter,
      const QRectF &rect, const QPalette &palette,
      int lineWidth, int frameStyle )
  {
      enum Style
      {
          Plain,
          Sunken,
          Raised
      };
  
      Style style = Plain;
      if ( (frameStyle & QFrame::Sunken) == QFrame::Sunken )
          style = Sunken;
      else if ( (frameStyle & QFrame::Raised) == QFrame::Raised )
          style = Raised;
  
      const double lw2 = 0.5 * lineWidth;
      QRectF r = rect.adjusted( lw2, lw2, -lw2, -lw2 );
  
      QBrush brush;
  
      if ( style != Plain )
      {
          QColor c1 = palette.color( QPalette::Light );
          QColor c2 = palette.color( QPalette::Dark );
  
          if ( style == Sunken )
              qSwap( c1, c2 );
  
          QLinearGradient gradient( r.topLeft(), r.bottomRight() );
          gradient.setColorAt( 0.0, c1 );
  #if 0
          gradient.setColorAt( 0.3, c1 );
          gradient.setColorAt( 0.7, c2 );
  #endif
          gradient.setColorAt( 1.0, c2 );
  
          brush = QBrush( gradient );
      }
      else // Plain
      {
          brush = palette.brush( QPalette::WindowText );
      }
  
      painter->save();
  
      painter->setPen( QPen( brush, lineWidth ) );
      painter->setBrush( Qt::NoBrush );
  
      painter->drawEllipse( r );
  
      painter->restore();
  }
  
  /*!
    Draw a rectangular frame
  
    \param painter Painter
    \param rect Frame rectangle
    \param palette Palette
    \param foregroundRole Foreground role used for QFrame::Plain
    \param frameWidth Frame width
    \param midLineWidth Used for QFrame::Box
    \param frameStyle bitwise OR´ed value of QFrame::Shape and QFrame::Shadow
  */
  void QwtPainter::drawFrame( QPainter *painter, const QRectF &rect,
      const QPalette &palette, QPalette::ColorRole foregroundRole,
      int frameWidth, int midLineWidth, int frameStyle )
  {
      if ( frameWidth <= 0 || rect.isEmpty() )
          return;
  
      const int shadow = frameStyle & QFrame::Shadow_Mask;
  
      painter->save();
  
      if ( shadow == QFrame::Plain )
      {
          const QRectF outerRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
          const QRectF innerRect = outerRect.adjusted(
              frameWidth, frameWidth, -frameWidth, -frameWidth );
  
          QPainterPath path;
          path.addRect( outerRect );
          path.addRect( innerRect );
  
          painter->setPen( Qt::NoPen );
          painter->setBrush( palette.color( foregroundRole ) );
  
          painter->drawPath( path );
      }
      else
      {
          const int shape = frameStyle & QFrame::Shape_Mask;
  
          if ( shape == QFrame::Box )
          {
              const QRectF outerRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
              const QRectF midRect1 = outerRect.adjusted(
                  frameWidth, frameWidth, -frameWidth, -frameWidth );
              const QRectF midRect2 = midRect1.adjusted(
                  midLineWidth, midLineWidth, -midLineWidth, -midLineWidth );
  
              const QRectF innerRect = midRect2.adjusted(
                  frameWidth, frameWidth, -frameWidth, -frameWidth );
  
              QPainterPath path1;
              path1.moveTo( outerRect.bottomLeft() );
              path1.lineTo( outerRect.topLeft() );
              path1.lineTo( outerRect.topRight() );
              path1.lineTo( midRect1.topRight() );
              path1.lineTo( midRect1.topLeft() );
              path1.lineTo( midRect1.bottomLeft() );
  
              QPainterPath path2;
              path2.moveTo( outerRect.bottomLeft() );
              path2.lineTo( outerRect.bottomRight() );
              path2.lineTo( outerRect.topRight() );
              path2.lineTo( midRect1.topRight() );
              path2.lineTo( midRect1.bottomRight() );
              path2.lineTo( midRect1.bottomLeft() );
  
              QPainterPath path3;
              path3.moveTo( midRect2.bottomLeft() );
              path3.lineTo( midRect2.topLeft() );
              path3.lineTo( midRect2.topRight() );
              path3.lineTo( innerRect.topRight() );
              path3.lineTo( innerRect.topLeft() );
              path3.lineTo( innerRect.bottomLeft() );
  
              QPainterPath path4;
              path4.moveTo( midRect2.bottomLeft() );
              path4.lineTo( midRect2.bottomRight() );
              path4.lineTo( midRect2.topRight() );
              path4.lineTo( innerRect.topRight() );
              path4.lineTo( innerRect.bottomRight() );
              path4.lineTo( innerRect.bottomLeft() );
  
              QPainterPath path5;
              path5.addRect( midRect1 );
              path5.addRect( midRect2 );
  
              painter->setPen( Qt::NoPen );
  
              QBrush brush1 = palette.dark().color();
              QBrush brush2 = palette.light().color();
  
              if ( shadow == QFrame::Raised )
                  qSwap( brush1, brush2 );
  
              painter->setBrush( brush1 );
              painter->drawPath( path1 );
              painter->drawPath( path4 );
  
              painter->setBrush( brush2 );
              painter->drawPath( path2 );
              painter->drawPath( path3 );
  
              painter->setBrush( palette.mid() );
              painter->drawPath( path5 );
          }
  #if 0
          // qDrawWinPanel doesn't result in something nice
          // on a scalable document like PDF. Better draw a
          // Panel.
  
          else if ( shape == QFrame::WinPanel )
          {
              painter->setRenderHint( QPainter::NonCosmeticDefaultPen, true );
              qDrawWinPanel ( painter, rect.toRect(), palette,
                  frameStyle & QFrame::Sunken );
          }
          else if ( shape == QFrame::StyledPanel )
          {
          }
  #endif
          else
          {
              const QRectF outerRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
              const QRectF innerRect = outerRect.adjusted(
                  frameWidth - 1.0, frameWidth - 1.0,
                  -( frameWidth - 1.0 ), -( frameWidth - 1.0 ) );
  
              QPainterPath path1;
              path1.moveTo( outerRect.bottomLeft() );
              path1.lineTo( outerRect.topLeft() );
              path1.lineTo( outerRect.topRight() );
              path1.lineTo( innerRect.topRight() );
              path1.lineTo( innerRect.topLeft() );
              path1.lineTo( innerRect.bottomLeft() );
  
  
              QPainterPath path2;
              path2.moveTo( outerRect.bottomLeft() );
              path2.lineTo( outerRect.bottomRight() );
              path2.lineTo( outerRect.topRight() );
              path2.lineTo( innerRect.topRight() );
              path2.lineTo( innerRect.bottomRight() );
              path2.lineTo( innerRect.bottomLeft() );
  
              painter->setPen( Qt::NoPen );
  
              QBrush brush1 = palette.dark().color();
              QBrush brush2 = palette.light().color();
  
              if ( shadow == QFrame::Raised )
                  qSwap( brush1, brush2 );
  
              painter->setBrush( brush1 );
              painter->drawPath( path1 );
  
              painter->setBrush( brush2 );
              painter->drawPath( path2 );
          }
  
      }
  
      painter->restore();
  }
  
  /*!
    Draw a rectangular frame with rounded borders
  
    \param painter Painter
    \param rect Frame rectangle
    \param xRadius x-radius of the ellipses defining the corners
    \param yRadius y-radius of the ellipses defining the corners
    \param palette QPalette::WindowText is used for plain borders
                   QPalette::Dark and QPalette::Light for raised
                   or sunken borders
    \param lineWidth Line width
    \param frameStyle bitwise OR´ed value of QFrame::Shape and QFrame::Shadow
  */
  
  void QwtPainter::drawRoundedFrame( QPainter *painter,
      const QRectF &rect, double xRadius, double yRadius,
      const QPalette &palette, int lineWidth, int frameStyle )
  {
      painter->save();
      painter->setRenderHint( QPainter::Antialiasing, true );
      painter->setBrush( Qt::NoBrush );
  
      double lw2 = lineWidth * 0.5;
      QRectF innerRect = rect.adjusted( lw2, lw2, -lw2, -lw2 );
  
      QPainterPath path;
      path.addRoundedRect( innerRect, xRadius, yRadius );
  
      enum Style
      {
          Plain,
          Sunken,
          Raised
      };
  
      Style style = Plain;
      if ( (frameStyle & QFrame::Sunken) == QFrame::Sunken )
          style = Sunken;
      else if ( (frameStyle & QFrame::Raised) == QFrame::Raised )
          style = Raised;
  
      if ( style != Plain && path.elementCount() == 17 )
      {
          // move + 4 * ( cubicTo + lineTo )
          QPainterPath pathList[8];
  
          for ( int i = 0; i < 4; i++ )
          {
              const int j = i * 4 + 1;
  
              pathList[ 2 * i ].moveTo(
                  path.elementAt(j - 1).x, path.elementAt( j - 1 ).y
              );
  
              pathList[ 2 * i ].cubicTo(
                  path.elementAt(j + 0).x, path.elementAt(j + 0).y,
                  path.elementAt(j + 1).x, path.elementAt(j + 1).y,
                  path.elementAt(j + 2).x, path.elementAt(j + 2).y );
  
              pathList[ 2 * i + 1 ].moveTo(
                  path.elementAt(j + 2).x, path.elementAt(j + 2).y
              );
              pathList[ 2 * i + 1 ].lineTo(
                  path.elementAt(j + 3).x, path.elementAt(j + 3).y
              );
          }
  
          QColor c1( palette.color( QPalette::Dark ) );
          QColor c2( palette.color( QPalette::Light ) );
  
          if ( style == Raised )
              qSwap( c1, c2 );
  
          for ( int i = 0; i < 4; i++ )
          {
              const QRectF r = pathList[2 * i].controlPointRect();
  
              QPen arcPen;
              arcPen.setCapStyle( Qt::FlatCap );
              arcPen.setWidth( lineWidth );
  
              QPen linePen;
              linePen.setCapStyle( Qt::FlatCap );
              linePen.setWidth( lineWidth );
  
              switch( i )
              {
                  case 0:
                  {
                      arcPen.setColor( c1 );
                      linePen.setColor( c1 );
                      break;
                  }
                  case 1:
                  {
                      QLinearGradient gradient;
                      gradient.setStart( r.topLeft() );
                      gradient.setFinalStop( r.bottomRight() );
                      gradient.setColorAt( 0.0, c1 );
                      gradient.setColorAt( 1.0, c2 );
  
                      arcPen.setBrush( gradient );
                      linePen.setColor( c2 );
                      break;
                  }
                  case 2:
                  {
                      arcPen.setColor( c2 );
                      linePen.setColor( c2 );
                      break;
                  }
                  case 3:
                  {
                      QLinearGradient gradient;
  
                      gradient.setStart( r.bottomRight() );
                      gradient.setFinalStop( r.topLeft() );
                      gradient.setColorAt( 0.0, c2 );
                      gradient.setColorAt( 1.0, c1 );
  
                      arcPen.setBrush( gradient );
                      linePen.setColor( c1 );
                      break;
                  }
              }
  
  
              painter->setPen( arcPen );
              painter->drawPath( pathList[ 2 * i] );
  
              painter->setPen( linePen );
              painter->drawPath( pathList[ 2 * i + 1] );
          }
      }
      else
      {
          QPen pen( palette.color( QPalette::WindowText ), lineWidth );
          painter->setPen( pen );
          painter->drawPath( path );
      }
  
      painter->restore();
  }
  
  /*!
    Draw a color bar into a rectangle
  
    \param painter Painter
    \param colorMap Color map
    \param interval Value range
    \param scaleMap Scale map
    \param orientation Orientation
    \param rect Traget rectangle
  */
  void QwtPainter::drawColorBar( QPainter *painter,
          const QwtColorMap &colorMap, const QwtInterval &interval,
          const QwtScaleMap &scaleMap, Qt::Orientation orientation,
          const QRectF &rect )
  {
      QVector<QRgb> colorTable;
      if ( colorMap.format() == QwtColorMap::Indexed )
          colorTable = colorMap.colorTable( interval );
  
      QColor c;
  
      const QRect devRect = rect.toAlignedRect();
  
      /*
        We paint to a pixmap first to have something scalable for printing
        ( f.e. in a Pdf document )
       */
  
      QPixmap pixmap( devRect.size() );
      pixmap.fill( Qt::transparent );
  
      QPainter pmPainter( &pixmap );
      pmPainter.translate( -devRect.x(), -devRect.y() );
  
      if ( orientation == Qt::Horizontal )
      {
          QwtScaleMap sMap = scaleMap;
          sMap.setPaintInterval( rect.left(), rect.right() );
  
          for ( int x = devRect.left(); x <= devRect.right(); x++ )
          {
              const double value = sMap.invTransform( x );
  
              if ( colorMap.format() == QwtColorMap::RGB )
                  c.setRgba( colorMap.rgb( interval, value ) );
              else
                  c = colorTable[colorMap.colorIndex( interval, value )];
  
              pmPainter.setPen( c );
              pmPainter.drawLine( x, devRect.top(), x, devRect.bottom() );
          }
      }
      else // Vertical
      {
          QwtScaleMap sMap = scaleMap;
          sMap.setPaintInterval( rect.bottom(), rect.top() );
  
          for ( int y = devRect.top(); y <= devRect.bottom(); y++ )
          {
              const double value = sMap.invTransform( y );
  
              if ( colorMap.format() == QwtColorMap::RGB )
                  c.setRgba( colorMap.rgb( interval, value ) );
              else
                  c = colorTable[colorMap.colorIndex( interval, value )];
  
              pmPainter.setPen( c );
              pmPainter.drawLine( devRect.left(), y, devRect.right(), y );
          }
      }
      pmPainter.end();
  
      drawPixmap( painter, rect, pixmap );
  }
  
  static inline void qwtFillRect( const QWidget *widget, QPainter *painter,
      const QRect &rect, const QBrush &brush)
  {
      if ( brush.style() == Qt::TexturePattern )
      {
          painter->save();
  
          painter->setClipRect( rect );
          painter->drawTiledPixmap(rect, brush.texture(), rect.topLeft());
  
          painter->restore();
      }
      else if ( brush.gradient() )
      {
          painter->save();
  
          painter->setClipRect( rect );
          painter->fillRect(0, 0, widget->width(),
              widget->height(), brush);
  
          painter->restore();
      }
      else
      {
          painter->fillRect(rect, brush);
      }
  }
  
  /*!
    Fill a pixmap with the content of a widget
  
    In Qt >= 5.0 QPixmap::fill() is a nop, in Qt 4.x it is buggy
    for backgrounds with gradients. Thus fillPixmap() offers
    an alternative implementation.
  
    \param widget Widget
    \param pixmap Pixmap to be filled
    \param offset Offset
  
    \sa QPixmap::fill()
   */
  void QwtPainter::fillPixmap( const QWidget *widget,
      QPixmap &pixmap, const QPoint &offset )
  {
      const QRect rect( offset, pixmap.size() );
  
      QPainter painter( &pixmap );
      painter.translate( -offset );
  
      const QBrush autoFillBrush =
          widget->palette().brush( widget->backgroundRole() );
  
      if ( !( widget->autoFillBackground() && autoFillBrush.isOpaque() ) )
      {
          const QBrush bg = widget->palette().brush( QPalette::Window );
          qwtFillRect( widget, &painter, rect, bg);
      }
  
      if ( widget->autoFillBackground() )
          qwtFillRect( widget, &painter, rect, autoFillBrush);
  
      if ( widget->testAttribute(Qt::WA_StyledBackground) )
      {
          painter.setClipRegion( rect );
  
          QStyleOption opt;
          opt.initFrom( widget );
          widget->style()->drawPrimitive( QStyle::PE_Widget,
              &opt, &painter, widget );
      }
  }
  
  /*!
    Fill rect with the background of a widget
  
    \param painter Painter
    \param rect Rectangle to be filled
    \param widget Widget
  
    \sa QStyle::PE_Widget, QWidget::backgroundRole()
   */
  void QwtPainter::drawBackgound( QPainter *painter,
      const QRectF &rect, const QWidget *widget )
  {
      if ( widget->testAttribute( Qt::WA_StyledBackground ) )
      {
          QStyleOption opt;
          opt.initFrom( widget );
          opt.rect = rect.toAlignedRect();
  
          widget->style()->drawPrimitive(
              QStyle::PE_Widget, &opt, painter, widget);
      }
      else
      {
          const QBrush brush =
              widget->palette().brush( widget->backgroundRole() );
  
          painter->fillRect( rect, brush );
      }
  }
  
  /*!
    \return A pixmap that can be used as backing store
  
    \param widget Widget, for which the backingstore is intended
    \param size Size of the pixmap
   */
  QPixmap QwtPainter::backingStore( QWidget *widget, const QSize &size )
  {
      QPixmap pm;
  
  #define QWT_HIGH_DPI 1
  
  #if QT_VERSION >= 0x050000 && QWT_HIGH_DPI
      qreal pixelRatio = 1.0;
  
      if ( widget && widget->windowHandle() )
      {
  #if QT_VERSION < 0x050100
          pixelRatio = widget->windowHandle()->devicePixelRatio();
  #else
          pixelRatio = widget->devicePixelRatio();
  #endif
      }
      else
      {
          if ( qApp )
              pixelRatio = qApp->devicePixelRatio();
      }
  
      pm = QPixmap( size * pixelRatio );
      pm.setDevicePixelRatio( pixelRatio );
  #else
      Q_UNUSED( widget )
      pm = QPixmap( size );
  #endif
  
  #if QT_VERSION < 0x050000
  #ifdef Q_WS_X11
      if ( widget && isX11GraphicsSystem() )
      {
          if ( pm.x11Info().screen() != widget->x11Info().screen() )
              pm.x11SetScreen( widget->x11Info().screen() );
      }
  #endif
  #endif
  
      return pm;
  }
  
  