You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
373 lines
8.2 KiB
373 lines
8.2 KiB
/* -*- 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_widget_overlay.h" |
|
#include "qwt_painter.h" |
|
#include <qpainter.h> |
|
#include <qpaintengine.h> |
|
#include <qimage.h> |
|
#include <qevent.h> |
|
|
|
static QImage::Format qwtMaskImageFormat() |
|
{ |
|
if ( QwtPainter::isX11GraphicsSystem() ) |
|
return QImage::Format_ARGB32; |
|
|
|
return QImage::Format_ARGB32_Premultiplied; |
|
} |
|
|
|
static QRegion qwtAlphaMask( |
|
const QImage& image, const QVector<QRect> rects ) |
|
{ |
|
const int w = image.width(); |
|
const int h = image.height(); |
|
|
|
QRegion region; |
|
QRect rect; |
|
|
|
for ( int i = 0; i < rects.size(); i++ ) |
|
{ |
|
int x1, x2, y1, y2; |
|
rects[i].getCoords( &x1, &y1, &x2, &y2 ); |
|
|
|
x1 = qMax( x1, 0 ); |
|
x2 = qMin( x2, w - 1 ); |
|
y1 = qMax( y1, 0 ); |
|
y2 = qMin( y2, h - 1 ); |
|
|
|
for ( int y = y1; y <= y2; ++y ) |
|
{ |
|
bool inRect = false; |
|
int rx0 = -1; |
|
|
|
const uint *line = |
|
reinterpret_cast<const uint *> ( image.scanLine( y ) ) + x1; |
|
for ( int x = x1; x <= x2; x++ ) |
|
{ |
|
const bool on = ( ( *line++ >> 24 ) != 0 ); |
|
if ( on != inRect ) |
|
{ |
|
if ( inRect ) |
|
{ |
|
rect.setCoords( rx0, y, x - 1, y ); |
|
region += rect; |
|
} |
|
else |
|
{ |
|
rx0 = x; |
|
} |
|
|
|
inRect = on; |
|
} |
|
} |
|
|
|
if ( inRect ) |
|
{ |
|
rect.setCoords( rx0, y, x2, y ); |
|
region = region.united( rect ); |
|
} |
|
} |
|
} |
|
|
|
return region; |
|
} |
|
|
|
class QwtWidgetOverlay::PrivateData |
|
{ |
|
public: |
|
PrivateData(): |
|
maskMode( QwtWidgetOverlay::MaskHint ), |
|
renderMode( QwtWidgetOverlay::AutoRenderMode ), |
|
rgbaBuffer( NULL ) |
|
{ |
|
} |
|
|
|
~PrivateData() |
|
{ |
|
resetRgbaBuffer(); |
|
} |
|
|
|
void resetRgbaBuffer() |
|
{ |
|
if ( rgbaBuffer ) |
|
{ |
|
::free( rgbaBuffer ); |
|
rgbaBuffer = NULL; |
|
} |
|
} |
|
|
|
MaskMode maskMode; |
|
RenderMode renderMode; |
|
uchar *rgbaBuffer; |
|
}; |
|
|
|
/*! |
|
\brief Constructor |
|
\param widget Parent widget, where the overlay is aligned to |
|
*/ |
|
QwtWidgetOverlay::QwtWidgetOverlay( QWidget* widget ): |
|
QWidget( widget ) |
|
{ |
|
d_data = new PrivateData; |
|
|
|
setAttribute( Qt::WA_TransparentForMouseEvents ); |
|
setAttribute( Qt::WA_NoSystemBackground ); |
|
setFocusPolicy( Qt::NoFocus ); |
|
|
|
if ( widget ) |
|
{ |
|
resize( widget->size() ); |
|
widget->installEventFilter( this ); |
|
} |
|
} |
|
|
|
//! Destructor |
|
QwtWidgetOverlay::~QwtWidgetOverlay() |
|
{ |
|
delete d_data; |
|
} |
|
|
|
/*! |
|
\brief Specify how to find the mask for the overlay |
|
|
|
\param mode New mode |
|
\sa maskMode() |
|
*/ |
|
void QwtWidgetOverlay::setMaskMode( MaskMode mode ) |
|
{ |
|
if ( mode != d_data->maskMode ) |
|
{ |
|
d_data->maskMode = mode; |
|
d_data->resetRgbaBuffer(); |
|
} |
|
} |
|
|
|
/*! |
|
\return Mode how to find the mask for the overlay |
|
\sa setMaskMode() |
|
*/ |
|
QwtWidgetOverlay::MaskMode QwtWidgetOverlay::maskMode() const |
|
{ |
|
return d_data->maskMode; |
|
} |
|
|
|
/*! |
|
Set the render mode |
|
\param mode Render mode |
|
|
|
\sa RenderMode, renderMode() |
|
*/ |
|
void QwtWidgetOverlay::setRenderMode( RenderMode mode ) |
|
{ |
|
d_data->renderMode = mode; |
|
} |
|
|
|
/*! |
|
\return Render mode |
|
\sa RenderMode, setRenderMode() |
|
*/ |
|
QwtWidgetOverlay::RenderMode QwtWidgetOverlay::renderMode() const |
|
{ |
|
return d_data->renderMode; |
|
} |
|
|
|
/*! |
|
Recalculate the mask and repaint the overlay |
|
*/ |
|
void QwtWidgetOverlay::updateOverlay() |
|
{ |
|
updateMask(); |
|
update(); |
|
} |
|
|
|
void QwtWidgetOverlay::updateMask() |
|
{ |
|
d_data->resetRgbaBuffer(); |
|
|
|
QRegion mask; |
|
|
|
if ( d_data->maskMode == QwtWidgetOverlay::MaskHint ) |
|
{ |
|
mask = maskHint(); |
|
} |
|
else if ( d_data->maskMode == QwtWidgetOverlay::AlphaMask ) |
|
{ |
|
// TODO: the image doesn't need to be larger than |
|
// the bounding rectangle of the hint !! |
|
|
|
QRegion hint = maskHint(); |
|
if ( hint.isEmpty() ) |
|
hint += QRect( 0, 0, width(), height() ); |
|
|
|
// A fresh buffer from calloc() is usually faster |
|
// than reinitializing an existing one with |
|
// QImage::fill( 0 ) or memset() |
|
|
|
d_data->rgbaBuffer = ( uchar* )::calloc( width() * height(), 4 ); |
|
|
|
QImage image( d_data->rgbaBuffer, |
|
width(), height(), qwtMaskImageFormat() ); |
|
|
|
QPainter painter( &image ); |
|
draw( &painter ); |
|
painter.end(); |
|
|
|
mask = qwtAlphaMask( image, hint.rects() ); |
|
|
|
if ( d_data->renderMode == QwtWidgetOverlay::DrawOverlay ) |
|
{ |
|
// we don't need the buffer later |
|
d_data->resetRgbaBuffer(); |
|
} |
|
} |
|
|
|
// A bug in Qt initiates a full repaint of the widget |
|
// when we change the mask, while we are visible ! |
|
|
|
setVisible( false ); |
|
|
|
if ( mask.isEmpty() ) |
|
clearMask(); |
|
else |
|
setMask( mask ); |
|
|
|
setVisible( true ); |
|
} |
|
|
|
/*! |
|
Paint event |
|
\param event Paint event |
|
|
|
\sa drawOverlay() |
|
*/ |
|
void QwtWidgetOverlay::paintEvent( QPaintEvent* event ) |
|
{ |
|
const QRegion clipRegion = event->region(); |
|
|
|
QPainter painter( this ); |
|
|
|
bool useRgbaBuffer = false; |
|
if ( d_data->renderMode == QwtWidgetOverlay::CopyAlphaMask ) |
|
{ |
|
useRgbaBuffer = true; |
|
} |
|
else if ( d_data->renderMode == QwtWidgetOverlay::AutoRenderMode ) |
|
{ |
|
if ( painter.paintEngine()->type() == QPaintEngine::Raster ) |
|
useRgbaBuffer = true; |
|
} |
|
|
|
if ( d_data->rgbaBuffer && useRgbaBuffer ) |
|
{ |
|
const QImage image( d_data->rgbaBuffer, |
|
width(), height(), qwtMaskImageFormat() ); |
|
|
|
QVector<QRect> rects; |
|
if ( clipRegion.rects().size() > 2000 ) |
|
{ |
|
// the region is to complex |
|
painter.setClipRegion( clipRegion ); |
|
rects += clipRegion.boundingRect(); |
|
} |
|
else |
|
{ |
|
rects = clipRegion.rects(); |
|
} |
|
|
|
for ( int i = 0; i < rects.size(); i++ ) |
|
{ |
|
const QRect r = rects[i]; |
|
painter.drawImage( r.topLeft(), image, r ); |
|
} |
|
} |
|
else |
|
{ |
|
painter.setClipRegion( clipRegion ); |
|
draw( &painter ); |
|
} |
|
} |
|
|
|
/*! |
|
Resize event |
|
\param event Resize event |
|
*/ |
|
void QwtWidgetOverlay::resizeEvent( QResizeEvent* event ) |
|
{ |
|
Q_UNUSED( event ); |
|
|
|
d_data->resetRgbaBuffer(); |
|
} |
|
|
|
void QwtWidgetOverlay::draw( QPainter *painter ) const |
|
{ |
|
QWidget *widget = const_cast< QWidget *>( parentWidget() ); |
|
if ( widget ) |
|
{ |
|
painter->setClipRect( parentWidget()->contentsRect() ); |
|
|
|
// something special for the plot canvas |
|
QPainterPath clipPath; |
|
|
|
( void )QMetaObject::invokeMethod( |
|
widget, "borderPath", Qt::DirectConnection, |
|
Q_RETURN_ARG( QPainterPath, clipPath ), Q_ARG( QRect, rect() ) ); |
|
|
|
if (!clipPath.isEmpty()) |
|
{ |
|
painter->setClipPath( clipPath, Qt::IntersectClip ); |
|
} |
|
} |
|
|
|
drawOverlay( painter ); |
|
} |
|
|
|
/*! |
|
\brief Calculate an approximation for the mask |
|
|
|
- MaskHint |
|
The hint is used as mask. |
|
|
|
- AlphaMask |
|
The hint is used to speed up the algorithm |
|
for calculating a mask from non transparent pixels |
|
|
|
- NoMask |
|
The hint is unused. |
|
|
|
The default implementation returns an invalid region |
|
indicating no hint. |
|
|
|
\return Hint for the mask |
|
*/ |
|
QRegion QwtWidgetOverlay::maskHint() const |
|
{ |
|
return QRegion(); |
|
} |
|
|
|
/*! |
|
\brief Event filter |
|
|
|
Resize the overlay according to the size of the parent widget. |
|
|
|
\param object Object to be filtered |
|
\param event Event |
|
|
|
\return See QObject::eventFilter() |
|
*/ |
|
|
|
bool QwtWidgetOverlay::eventFilter( QObject* object, QEvent* event ) |
|
{ |
|
if ( object == parent() && event->type() == QEvent::Resize ) |
|
{ |
|
QResizeEvent *resizeEvent = static_cast<QResizeEvent *>( event ); |
|
resize( resizeEvent->size() ); |
|
} |
|
|
|
return QObject::eventFilter( object, event ); |
|
}
|
|
|