1001 lines
26 KiB
C++
1001 lines
26 KiB
C++
/* This file is a part of plaYUVer project
|
||
* Copyright (C) 2014-2015 by Luis Lucas (luisfrlucas@gmail.com)
|
||
* Joao Carreira (jfmcarreira@gmail.com)
|
||
*
|
||
* This program is free software; you can redistribute it and/or modify
|
||
* it under the terms of the GNU General Public License as published by
|
||
* the Free Software Foundation; either version 2 of the License, or
|
||
* (at your option) any later version.
|
||
*
|
||
* This program is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
* GNU General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU General Public License along
|
||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||
*/
|
||
|
||
/**
|
||
* \file ViewArea.cpp
|
||
* \brief Function to manage image display
|
||
* Based on the work of Ricardo N. Rocha Sardo in SCode project
|
||
*/
|
||
|
||
#include <cmath>
|
||
|
||
#include <QCoreApplication>
|
||
#include <QWidget>
|
||
#include <QPainter>
|
||
#include <QPainterPath>
|
||
#include <QMouseEvent>
|
||
#include <QImage>
|
||
#include <QColor>
|
||
#include <QString>
|
||
#include <QPixmapCache>
|
||
#include <QRectF>
|
||
#include <QDebug>
|
||
#include "ViewArea.h"
|
||
#include "GridManager.h"
|
||
|
||
namespace plaYUVer
|
||
{
|
||
|
||
static const QColor selectionColor = Qt::cyan;
|
||
static const QColor imageMaskColor = Qt::green;
|
||
static const QColor eraserColor = Qt::red;
|
||
|
||
ViewArea::ViewArea( QWidget *parent ) :
|
||
QWidget( parent )
|
||
{
|
||
setMouseTracking( true );
|
||
|
||
m_pcCurrFrame = NULL;
|
||
m_pixmap = QPixmap();
|
||
m_grid = GridManager();
|
||
m_mask = QBitmap();
|
||
m_selectedArea = QRect();
|
||
m_zoomFactor = 1;
|
||
m_xOffset = 0;
|
||
m_yOffset = 0;
|
||
m_mode = NormalMode;
|
||
m_tool = NavigationTool;
|
||
m_gridVisible = false;
|
||
m_snapToGrid = false;
|
||
m_blockTrackEnable = false;
|
||
m_pStream = NULL;
|
||
|
||
}
|
||
|
||
void ViewArea::setImage( PlaYUVerFrame* pcFrame )
|
||
{
|
||
m_pcCurrFrame = pcFrame;
|
||
m_uiPixelHalfScale = 1 << ( m_pcCurrFrame->getBitsPel() - 1 );
|
||
m_pcCurrFrame->fillRGBBuffer();
|
||
QImage qimg = QImage( m_pcCurrFrame->getRGBBuffer(), m_pcCurrFrame->getWidth(), m_pcCurrFrame->getHeight(), QImage::Format_RGB32 );
|
||
setImage( QPixmap::fromImage( qimg ) );
|
||
}
|
||
|
||
void ViewArea::setImage( const QPixmap &pixmap )
|
||
{
|
||
m_pixmap = pixmap;
|
||
m_mask = QBitmap( pixmap.width(), pixmap.height() );
|
||
m_mask.clear();
|
||
updateSize();
|
||
update();
|
||
updateGeometry();
|
||
}
|
||
|
||
void ViewArea::clear()
|
||
{
|
||
m_pixmap = QPixmap();
|
||
m_mask = QBitmap();
|
||
m_mode = NormalMode;
|
||
updateSize();
|
||
update();
|
||
updateGeometry();
|
||
}
|
||
|
||
void ViewArea::clearMask()
|
||
{
|
||
m_mask.clear();
|
||
}
|
||
|
||
Void ViewArea::setZoomFactor( Double f )
|
||
{
|
||
m_zoomFactor = f;
|
||
|
||
updateSize();
|
||
update();
|
||
}
|
||
|
||
Double ViewArea::scaleZoomFactor( Double scale, QPoint center, QSize minimumSize )
|
||
{
|
||
Double maxZoom = 100.0;
|
||
Double minZoom = 0.01; //( 1.0 / m_pixmap.width() );
|
||
Double new_scale = 1.0;
|
||
|
||
if( ( m_zoomFactor == minZoom ) && ( scale < 1 ) )
|
||
return new_scale;
|
||
|
||
if( ( m_zoomFactor == maxZoom ) && ( scale > 1 ) )
|
||
return new_scale;
|
||
|
||
Double zoomFactor = m_zoomFactor * scale * 100.0;
|
||
zoomFactor = round( zoomFactor );
|
||
zoomFactor = zoomFactor / 100.0;
|
||
scale = zoomFactor / m_zoomFactor;
|
||
|
||
if( !minimumSize.isNull() )
|
||
{
|
||
Double cw = m_pixmap.width() * m_zoomFactor;
|
||
Double ch = m_pixmap.height() * m_zoomFactor;
|
||
Double fw = m_pixmap.width() * zoomFactor;
|
||
Double fh = m_pixmap.height() * zoomFactor;
|
||
Double mw = minimumSize.width();
|
||
Double mh = minimumSize.height();
|
||
|
||
if( ( cw < mw ) && ( ch < mh ) && ( scale < 1 ) )
|
||
{
|
||
return new_scale;
|
||
}
|
||
|
||
if( ( fw < mw ) && ( fh < mh ) && ( scale < 1 ) )
|
||
{
|
||
Double wfactor = mw / fw;
|
||
Double hfactor = mh / fh;
|
||
|
||
if( wfactor < hfactor )
|
||
scale = wfactor;
|
||
else
|
||
scale = hfactor;
|
||
|
||
zoomFactor = zoomFactor * scale * 100.0;
|
||
zoomFactor = round( zoomFactor );
|
||
zoomFactor = zoomFactor / 100.0;
|
||
scale = zoomFactor / m_zoomFactor;
|
||
}
|
||
}
|
||
|
||
new_scale = scale;
|
||
|
||
if( zoomFactor < minZoom )
|
||
{
|
||
zoomFactor = minZoom;
|
||
new_scale = zoomFactor / m_zoomFactor;
|
||
}
|
||
else
|
||
{
|
||
if( zoomFactor > maxZoom )
|
||
{
|
||
zoomFactor = maxZoom;
|
||
new_scale = zoomFactor / m_zoomFactor;
|
||
}
|
||
}
|
||
|
||
setZoomFactor( zoomFactor );
|
||
|
||
return new_scale;
|
||
}
|
||
|
||
void ViewArea::setMode( ViewMode mode )
|
||
{
|
||
if( m_mode == mode )
|
||
return;
|
||
|
||
m_mode = mode;
|
||
update();
|
||
}
|
||
|
||
void ViewArea::setNormalMode()
|
||
{
|
||
setMode( NormalMode );
|
||
}
|
||
|
||
void ViewArea::setMaskMode()
|
||
{
|
||
setMode( MaskMode );
|
||
}
|
||
|
||
void ViewArea::setMaskColor( const QColor &color )
|
||
{
|
||
m_maskColor = color;
|
||
update();
|
||
}
|
||
|
||
void ViewArea::setTool( eTool tool )
|
||
{
|
||
m_tool = tool;
|
||
}
|
||
|
||
void ViewArea::setMaskTool()
|
||
{
|
||
setMode( MaskMode );
|
||
setTool( MaskTool );
|
||
m_blockTrackEnable = true;
|
||
}
|
||
|
||
void ViewArea::setEraserTool()
|
||
{
|
||
setMode( MaskMode );
|
||
setTool( EraserTool );
|
||
m_blockTrackEnable = true;
|
||
}
|
||
|
||
void ViewArea::setNormalSelectionTool()
|
||
{
|
||
setMode( NormalMode );
|
||
setTool( NormalSelectionTool );
|
||
m_blockTrackEnable = false;
|
||
}
|
||
|
||
void ViewArea::setBlockSelectionTool()
|
||
{
|
||
setMode( NormalMode );
|
||
setTool( BlockSelectionTool );
|
||
m_blockTrackEnable = true;
|
||
}
|
||
|
||
void ViewArea::setGridVisible( bool enable )
|
||
{
|
||
m_gridVisible = enable;
|
||
update();
|
||
}
|
||
|
||
void ViewArea::setSnapToGrid( bool enable )
|
||
{
|
||
m_snapToGrid = enable;
|
||
}
|
||
|
||
void ViewArea::setSelectedArea( QRect &rect )
|
||
{
|
||
if( rect.isNull() )
|
||
{
|
||
update();
|
||
return;
|
||
}
|
||
|
||
QRect updateRect;
|
||
setNormalMode();
|
||
|
||
if( m_selectedArea.isNull() )
|
||
{
|
||
m_selectedArea = rect.normalized();
|
||
updateRect = m_selectedArea;
|
||
}
|
||
else
|
||
{
|
||
updateRect = m_selectedArea;
|
||
m_selectedArea = rect.normalized();
|
||
updateRect = updateRect.united( m_selectedArea );
|
||
}
|
||
updateRect.adjust( 0, 0, 1, 1 );
|
||
update( updateRect.normalized() );
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
// Geometry Updates
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
void ViewArea::updateSize()
|
||
{
|
||
int w = m_pixmap.width() * m_zoomFactor;
|
||
int h = m_pixmap.height() * m_zoomFactor;
|
||
setMinimumSize( w, h );
|
||
|
||
QWidget *p = parentWidget();
|
||
if( p )
|
||
{
|
||
// If the parent size is bigger than the minimum area to view the
|
||
// image, resize() will call resizeEvent(); otherwise, we need to
|
||
// perform the necessary updates (updateOffset).
|
||
resize( p->width(), p->height() );
|
||
}
|
||
|
||
if( w <= width() && h <= height() )
|
||
updateOffset();
|
||
}
|
||
|
||
void ViewArea::updateOffset()
|
||
{
|
||
if( width() > m_pixmap.width() * m_zoomFactor )
|
||
{
|
||
m_xOffset = ( width() - m_pixmap.width() * m_zoomFactor ) / 2;
|
||
}
|
||
else
|
||
{
|
||
m_xOffset = 0;
|
||
}
|
||
if( height() > m_pixmap.height() * m_zoomFactor )
|
||
{
|
||
m_yOffset = ( height() - m_pixmap.height() * m_zoomFactor ) / 2;
|
||
}
|
||
else
|
||
{
|
||
m_yOffset = 0;
|
||
}
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
// Resize Event
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
void ViewArea::resizeEvent( QResizeEvent *event )
|
||
{
|
||
if( size().isEmpty() || m_pixmap.isNull() )
|
||
return;
|
||
|
||
updateOffset();
|
||
update();
|
||
}
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
// Paint Event
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
void ViewArea::paintEvent( QPaintEvent *event )
|
||
{
|
||
QRect winRect = event->rect();
|
||
|
||
if( visibleRegion().isEmpty() )
|
||
return;
|
||
|
||
if( size().isEmpty() || m_pixmap.isNull() )
|
||
return;
|
||
|
||
QPainter painter( this );
|
||
|
||
// Save the actual painter properties and scales the coordinate system.
|
||
painter.save();
|
||
painter.translate( m_xOffset, m_yOffset );
|
||
painter.scale( m_zoomFactor, m_zoomFactor );
|
||
|
||
// This line is for fast paiting. Only visible area of the image is painted.
|
||
// We take the exposed rect from the event (that gives us scroll/expose optimizations for free – no need
|
||
// to draw the whole pixmap if your widget is only partially exposed), and reverse map it with the painter matrix.
|
||
// That gives us the part of the pixmap that has actually been exposed.
|
||
// See: http://blog.qt.digia.com/blog/2006/05/13/fast-transformed-pixmapimage-drawing/
|
||
QRect exposedRect = painter.worldTransform().inverted().mapRect( event->rect() ).adjusted( -1, -1, 1, 1 );
|
||
// Draw the pixmap.
|
||
painter.drawPixmap( exposedRect, m_pixmap, exposedRect );
|
||
|
||
// Draw the Grid if it's visible.
|
||
if( m_gridVisible )
|
||
{
|
||
// Do we need to draw the whole grid?
|
||
// To know that, we need to perform a transformation of the rectangle
|
||
// area that the painter needs to update - transform the windows
|
||
// coordinates (origin at the top-left corner of the widget), to the
|
||
// relatives coordinates of the image at it's original size (origin at
|
||
// the top-left corner of the image).
|
||
QRect vr = windowToView( winRect );
|
||
|
||
// Now we have the (to update) rectangle area on a coordinates system
|
||
// that has it's origin at the top-left corner of the image. That
|
||
// is, is referenced to the not scaled image.
|
||
// To know what image area we need to update, just intersects the
|
||
// rectangle area with the image area.
|
||
vr &= QRect( 0, 0, m_pixmap.width(), m_pixmap.height() );
|
||
|
||
// Set up for the grid drawer.
|
||
painter.setRenderHint( QPainter::Antialiasing );
|
||
|
||
// Draw grid.
|
||
m_grid.drawGrid( m_pixmap, vr, &painter );
|
||
}
|
||
|
||
painter.restore();
|
||
|
||
// Draw a border around the image.
|
||
/* if( m_xOffset || m_yOffset )
|
||
{
|
||
painter.setPen( Qt::black );
|
||
painter.drawRect( m_xOffset - 1, m_yOffset - 1, m_pixmap.width() * m_zoomFactor + 1, m_pixmap.height() * m_zoomFactor + 1 );
|
||
}*/
|
||
|
||
// Draw pixel values in grid
|
||
if( m_zoomFactor >= 50.0 )
|
||
{
|
||
Int imageWidth = m_pixmap.width();
|
||
Int imageHeight = m_pixmap.height();
|
||
PlaYUVerFrame::Pixel sPixelValue;
|
||
|
||
QFont font( "Helvetica" );
|
||
font.setPixelSize( 12 );
|
||
painter.setFont( font );
|
||
|
||
QRect vr = windowToView( winRect );
|
||
vr &= QRect( 0, 0, imageWidth, imageHeight );
|
||
|
||
for( Int i = vr.x(); i <= vr.right(); i++ )
|
||
{
|
||
for( Int j = vr.y(); j <= vr.bottom(); j++ )
|
||
{
|
||
QPoint pixelTopLeft( i, j );
|
||
|
||
QRect pixelRect( viewToWindow( pixelTopLeft ), QSize( m_zoomFactor, m_zoomFactor ) );
|
||
|
||
Int frFormat = m_pcCurrFrame->getColorSpace();
|
||
|
||
if( frFormat == PlaYUVerFrame::COLOR_YUV )
|
||
{
|
||
sPixelValue = m_pcCurrFrame->getPixelValue( pixelTopLeft.x(), pixelTopLeft.y() );
|
||
|
||
if( sPixelValue.Y() < m_uiPixelHalfScale )
|
||
painter.setPen( QColor( Qt::white ) );
|
||
else
|
||
painter.setPen( QColor( Qt::black ) );
|
||
|
||
painter.drawText( pixelRect, Qt::AlignCenter,
|
||
"Y: " + QString::number( sPixelValue.Y() ) + "\n" + "U: " + QString::number( sPixelValue.Cb() ) + "\n" + "V: "
|
||
+ QString::number( sPixelValue.Cr() ) );
|
||
}
|
||
if( frFormat == PlaYUVerFrame::COLOR_GRAY )
|
||
{
|
||
sPixelValue = m_pcCurrFrame->getPixelValue( pixelTopLeft.x(), pixelTopLeft.y() );
|
||
|
||
if( sPixelValue.Y() < m_uiPixelHalfScale )
|
||
painter.setPen( QColor( Qt::white ) );
|
||
else
|
||
painter.setPen( QColor( Qt::black ) );
|
||
|
||
painter.drawText( pixelRect, Qt::AlignCenter, "Y: " + QString::number( sPixelValue.Y() ) );
|
||
}
|
||
|
||
if( ( frFormat == PlaYUVerFrame::COLOR_RGB ) )
|
||
{
|
||
sPixelValue = m_pcCurrFrame->getPixelValue( pixelTopLeft.x(), pixelTopLeft.y() );
|
||
|
||
if( ( sPixelValue.R() + sPixelValue.G() + sPixelValue.B() ) < ( m_uiPixelHalfScale * 3 ) )
|
||
painter.setPen( QColor( Qt::white ) );
|
||
else
|
||
painter.setPen( QColor( Qt::black ) );
|
||
|
||
painter.drawText( pixelRect, Qt::AlignCenter,
|
||
"R: " + QString::number( sPixelValue.R() ) + "\n" + "G: " + QString::number( sPixelValue.G() ) + "\n" + "B: "
|
||
+ QString::number( sPixelValue.B() ) );
|
||
}
|
||
}
|
||
}
|
||
|
||
QColor color( Qt::white );
|
||
QPen mainPen = QPen( color, 1, Qt::SolidLine );
|
||
painter.setPen( mainPen );
|
||
|
||
// Draw vertical line
|
||
for( Int x = vr.x(); x <= ( vr.right() + 1 ); x++ )
|
||
{
|
||
// Always draw the full line otherwise the line stippling
|
||
// varies with the location of view area and we get glitchy
|
||
// patterns.
|
||
painter.drawLine( viewToWindow( QPoint( x, 0 ) ), viewToWindow( QPoint( x, imageHeight ) ) );
|
||
}
|
||
// Draw horizontal line
|
||
for( Int y = vr.y(); y <= ( vr.bottom() + 1 ); y++ )
|
||
{
|
||
painter.drawLine( viewToWindow( QPoint( 0, y ) ), viewToWindow( QPoint( imageWidth, y ) ) );
|
||
}
|
||
}
|
||
|
||
QRect zoomWinRect = QRect(0, 0, m_pixmap.width()/10, m_pixmap.height()/10 );
|
||
QPoint zWinPosition = QPoint(winRect.bottomRight()) -
|
||
QPoint(m_pixmap.width()/10,m_pixmap.height()/10) - QPoint(10,10);
|
||
|
||
zoomWinRect.moveTopLeft(zWinPosition);
|
||
painter.fillRect(zoomWinRect, QBrush(QColor(128, 128, 255, 128)));
|
||
painter.drawRect(zoomWinRect);
|
||
|
||
|
||
|
||
QRect vr = windowToView( winRect );
|
||
zoomWinRect.moveTopLeft(QPoint(0,0));
|
||
QRect zoomVisibleRect = QRect(0,0,vr.width()/10,vr.height()/10) & zoomWinRect;
|
||
zoomVisibleRect.moveTopLeft(vr.topLeft()/10);
|
||
|
||
qDebug() << winRect << vr << zoomVisibleRect << m_pixmap.size() << zoomWinRect;
|
||
|
||
if(zoomVisibleRect.left()<0)
|
||
zoomVisibleRect.moveLeft(0);
|
||
if(zoomVisibleRect.top()<0)
|
||
zoomVisibleRect.moveTop(0);
|
||
|
||
zoomVisibleRect.moveTopLeft(zoomVisibleRect.topLeft() + zWinPosition);
|
||
|
||
painter.fillRect(zoomVisibleRect, QBrush(QColor(128, 200, 255, 128)));
|
||
painter.drawRect(zoomVisibleRect);
|
||
|
||
QRect sr = viewToWindow( m_selectedArea );
|
||
QRect ir = sr & winRect;
|
||
|
||
if( ( mode() == NormalMode ) && !selectedArea().isNull() )
|
||
{
|
||
if( m_newShape || m_blockTrackEnable )
|
||
{
|
||
// Set the tool color
|
||
QColor selectColor = selectionColor;
|
||
|
||
selectColor.setAlpha( 120 );
|
||
|
||
QBrush brush( selectColor );
|
||
|
||
if( m_blockTrackEnable )
|
||
painter.setBrush( Qt::NoBrush );
|
||
else
|
||
painter.setBrush( brush );
|
||
|
||
painter.setPen( Qt::darkCyan );
|
||
|
||
// painter.drawRect( sr/*m_selectedArea*/ );
|
||
}
|
||
else
|
||
{
|
||
// 1) Cover the image with a light gray except the selected area
|
||
QColor fill( Qt::lightGray );
|
||
fill.setAlpha( 150 );
|
||
|
||
QBrush brush( fill );
|
||
painter.setBrush( fill );
|
||
painter.setPen( Qt::NoPen );
|
||
QPainterPath myPath;
|
||
QRect imgr = viewToWindow( QRect( 0, 0, m_pixmap.width(), m_pixmap.height() ) );
|
||
|
||
myPath.addRect( imgr );
|
||
myPath.addRect( sr ); //m_selectedArea
|
||
painter.drawPath( myPath );
|
||
|
||
// 2) Draw the selection rectangle
|
||
painter.setBrush( Qt::NoBrush );
|
||
painter.setPen( Qt::darkCyan );
|
||
// painter.drawRect( sr/*m_selectedArea*/ );
|
||
}
|
||
|
||
if( !ir.isNull() )
|
||
painter.drawRect( sr/*m_selectedArea*/);
|
||
|
||
}
|
||
|
||
// Mask Mode ----------------------------------------------------------
|
||
|
||
if( mode() != MaskMode )
|
||
return;
|
||
|
||
QColor color;
|
||
QBrush brush;
|
||
|
||
// Draw Mask
|
||
// Set the tool color
|
||
if( m_maskColor.isValid() )
|
||
color = m_maskColor;
|
||
else
|
||
color = imageMaskColor;
|
||
|
||
color.setAlpha( 120 );
|
||
painter.setPen( color );
|
||
painter.save();
|
||
painter.translate( m_xOffset, m_yOffset );
|
||
painter.scale( m_zoomFactor, m_zoomFactor );
|
||
painter.drawPixmap( QPoint( 0, 0 ), m_mask );
|
||
painter.restore();
|
||
|
||
if( !m_selectedArea.isNull() )
|
||
{
|
||
if( m_newShape || m_blockTrackEnable )
|
||
{
|
||
switch( tool() )
|
||
{
|
||
case MaskTool:
|
||
{
|
||
if( m_maskColor.isValid() )
|
||
color = m_maskColor;
|
||
else
|
||
color = imageMaskColor;
|
||
|
||
break;
|
||
}
|
||
case EraserTool:
|
||
{
|
||
color = eraserColor;
|
||
break;
|
||
}
|
||
default:
|
||
color = selectionColor; // ?Problems!
|
||
}
|
||
|
||
color.setAlpha( 120 );
|
||
brush = QBrush( color );
|
||
|
||
if( m_blockTrackEnable )
|
||
painter.setBrush( Qt::NoBrush );
|
||
else
|
||
painter.setBrush( brush );
|
||
|
||
painter.setPen( color );
|
||
if( !ir.isNull() )
|
||
painter.drawRect( sr );
|
||
}
|
||
}
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
// Mouse Events
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
void ViewArea::wheelEvent( QWheelEvent *event )
|
||
{
|
||
Double scale;
|
||
Double usedScale;
|
||
|
||
if( event->modifiers() & Qt::ControlModifier )
|
||
{
|
||
scale = 0.001 * event->delta();
|
||
if( scale > 0 )
|
||
scale = 1.25;
|
||
else
|
||
scale = 0.8;
|
||
|
||
QWidget *p = parentWidget();
|
||
QSize minimumSize = QSize( p->size().width() - 5, p->size().height() - 5 );
|
||
usedScale = scaleZoomFactor( scale, event->pos(), minimumSize );
|
||
if( usedScale != 1.0 )
|
||
{
|
||
emit zoomFactorChanged_byWheel( usedScale, event->pos() );
|
||
}
|
||
}
|
||
}
|
||
|
||
void ViewArea::mousePressEvent( QMouseEvent *event )
|
||
{
|
||
event->accept();
|
||
|
||
QPoint vpos = windowToView( event->pos() );
|
||
|
||
if( event->button() == Qt::MidButton )
|
||
{
|
||
|
||
}
|
||
if( event->button() == Qt::LeftButton )
|
||
{
|
||
if( tool() == NavigationTool )
|
||
{
|
||
if( !( m_xOffset && m_yOffset ) )
|
||
setCursor( Qt::ClosedHandCursor );
|
||
|
||
m_lastWindowPos = event->pos();
|
||
}
|
||
// Is the mouse over the image? If yes, save the mouse position;
|
||
// otherwise, QPoint( -1, -1 ) indicates an invalid position.
|
||
if( isPosValid( vpos ) )
|
||
{
|
||
m_lastPos = vpos;
|
||
}
|
||
else
|
||
{
|
||
m_lastPos = QPoint( -1, -1 );
|
||
return;
|
||
}
|
||
|
||
// If grid tracking
|
||
if( m_snapToGrid )
|
||
{
|
||
// Find if the cursor is near a grid intersection
|
||
bool isNear = m_grid.isNear( m_lastPos );
|
||
|
||
if( isNear )
|
||
{
|
||
// The grid 'near intersection' found when used isNear()
|
||
m_lastPos = m_grid.nearPos();
|
||
}
|
||
}
|
||
|
||
m_newShape = true;
|
||
|
||
if( tool() == NormalSelectionTool )
|
||
{
|
||
m_blockTrackEnable = false;
|
||
return;
|
||
}
|
||
|
||
if( mode() == MaskMode )
|
||
{
|
||
// Disable the block track to enable fill the block
|
||
m_blockTrackEnable = false;
|
||
}
|
||
|
||
// Block selection
|
||
// m_selectedArea = m_grid.rectContains( m_lastPos );
|
||
|
||
QRect updateRect = viewToWindow( m_selectedArea );
|
||
updateRect.adjust( 0, 0, 1, 1 );
|
||
update( updateRect.normalized() );
|
||
}
|
||
}
|
||
|
||
void ViewArea::mouseMoveEvent( QMouseEvent *event )
|
||
{
|
||
event->accept();
|
||
|
||
#ifndef _MSC_VER
|
||
// Add this code line to avoid slow navigation with some specific mouses
|
||
// This seems to always appear on windows
|
||
if( qApp->hasPendingEvents())
|
||
return;
|
||
#endif
|
||
|
||
QPoint actualPos = windowToView( event->pos() );
|
||
QRect updateRect;
|
||
|
||
emit positionChanged( actualPos );
|
||
|
||
// If mouse left button pressed
|
||
if( event->buttons() == Qt::LeftButton && m_lastPos != QPoint( -1, -1 ) )
|
||
{
|
||
// Grid tracking
|
||
if( m_snapToGrid )
|
||
{
|
||
// Find if the cursor is near a grid intersection
|
||
bool isNear = m_grid.isNear( actualPos );
|
||
|
||
if( isNear )
|
||
{
|
||
// Return the last grid near intersection found
|
||
actualPos = m_grid.nearPos();
|
||
}
|
||
}
|
||
|
||
if( tool() == NavigationTool )
|
||
{
|
||
QPoint offset = m_lastWindowPos - event->pos();
|
||
emit scrollBarMoved( offset );
|
||
}
|
||
|
||
updateRect = viewToWindow( m_selectedArea );
|
||
|
||
if( tool() == NormalSelectionTool )
|
||
{
|
||
// If the selection is only vertical or horizontal then we have one
|
||
// of the dimentions null.
|
||
if( actualPos.x() == m_lastPos.x() && actualPos.y() == m_lastPos.y() )
|
||
{
|
||
m_selectedArea = QRect();
|
||
}
|
||
// Selection from top to bottom
|
||
else if( m_lastPos.y() < actualPos.y() )
|
||
{
|
||
// From left to right
|
||
if( m_lastPos.x() < actualPos.x() )
|
||
{
|
||
QPoint bottomR = actualPos; // - QPoint( 1, 1 );
|
||
m_selectedArea = QRect( m_lastPos, bottomR );
|
||
}
|
||
// From right to left
|
||
else
|
||
{
|
||
QPoint topL( actualPos.x(), m_lastPos.y() );
|
||
QPoint bottomR( m_lastPos.x(), actualPos.y() );
|
||
|
||
m_selectedArea = QRect( topL, bottomR );
|
||
}
|
||
}
|
||
// Selection from bottom to top
|
||
else
|
||
{
|
||
// From left to right
|
||
if( m_lastPos.x() < actualPos.x() )
|
||
{
|
||
QPoint topL( m_lastPos.x(), actualPos.y() );
|
||
QPoint bottomR( actualPos.x(), m_lastPos.y() );
|
||
m_selectedArea = QRect( topL, bottomR );
|
||
}
|
||
// From right to left
|
||
else
|
||
{
|
||
QPoint bottomR = m_lastPos; // - QPoint( 1, 1 );
|
||
m_selectedArea = QRect( actualPos, bottomR );
|
||
}
|
||
}
|
||
}
|
||
/* else // if tool() == BlockSelectionTool || MaskTool || EraserTool
|
||
{
|
||
m_blockTrackEnable = false;
|
||
// If cursor is inside the selected area, we most redraw
|
||
// the selection rect because it may be smaller.
|
||
if( m_selectedArea.contains( actualPos ) )
|
||
{
|
||
m_selectedArea = m_grid.rectContains( m_lastPos );
|
||
}
|
||
|
||
m_selectedArea = m_selectedArea.united( m_grid.rectContains( actualPos ) );
|
||
}
|
||
*/
|
||
if( tool() == NormalSelectionTool )
|
||
{
|
||
// Intercept the selected area with the image area to limit the
|
||
// selection only to the image area, preventing it to come outside
|
||
// the image.
|
||
m_selectedArea &= QRect( 0, 0, m_pixmap.width(), m_pixmap.height() );
|
||
|
||
// Update only the united area
|
||
// updateRect = updateRect.united( viewToWindow( m_selectedArea ) );
|
||
|
||
// "When rendering with a one pixel wide pen the QRect's
|
||
// boundary line will be rendered to the right and below the
|
||
// mathematical rectangle's boundary line.", in QT4 doc.
|
||
// Our selection pen width is 1, let's adjust the rendering area.
|
||
// updateRect.adjust( 0, 0, 1, 1 );
|
||
|
||
// update( updateRect.normalized() );
|
||
update();
|
||
}
|
||
return;
|
||
}
|
||
|
||
// If no mouse button is pressed and bloks selection tool is set, then
|
||
// track the bloks.
|
||
if( m_blockTrackEnable )
|
||
{
|
||
updateRect = viewToWindow( m_selectedArea );
|
||
|
||
if( isPosValid( actualPos ) )
|
||
{
|
||
m_selectedArea = m_grid.rectContains( actualPos );
|
||
}
|
||
else
|
||
{
|
||
m_selectedArea = QRect();
|
||
}
|
||
updateRect = updateRect.united( viewToWindow( m_selectedArea ) );
|
||
updateRect.adjust( 0, 0, 1, 1 );
|
||
update( updateRect );
|
||
return;
|
||
}
|
||
}
|
||
|
||
void ViewArea::mouseReleaseEvent( QMouseEvent *event )
|
||
{
|
||
event->accept();
|
||
|
||
QPoint vpos = windowToView( event->pos() );
|
||
|
||
if( event->button() == Qt::LeftButton && m_lastPos != QPoint( -1, -1 ) )
|
||
{
|
||
if( tool() == NavigationTool )
|
||
{
|
||
unsetCursor();
|
||
}
|
||
else if( tool() == NormalSelectionTool )
|
||
{
|
||
// Normal Mode ------------------------------------------------------
|
||
if( mode() == NormalMode )
|
||
{
|
||
if( vpos == m_lastPos )
|
||
{
|
||
m_selectedArea = QRect();
|
||
|
||
if( tool() == BlockSelectionTool )
|
||
m_blockTrackEnable = true;
|
||
}
|
||
emit selectionChanged( m_selectedArea );
|
||
|
||
}
|
||
|
||
// Mask Mode -----------------------------------------------------------
|
||
else if( mode() == MaskMode )
|
||
{
|
||
if( !m_selectedArea.isNull() )
|
||
{
|
||
updateMask( m_selectedArea );
|
||
}
|
||
|
||
m_selectedArea = QRect();
|
||
m_blockTrackEnable = true;
|
||
}
|
||
m_newShape = false;
|
||
update();
|
||
m_lastPos = QPoint( -1, -1 );
|
||
}
|
||
}
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
bool ViewArea::isPosValid( const QPoint &pos ) const
|
||
{
|
||
|
||
if( pos.x() < 0 || pos.y() < 0 || pos.x() >= m_pixmap.width() || pos.y() >= m_pixmap.height() )
|
||
return false;
|
||
else
|
||
return true;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
QPoint ViewArea::windowToView( const QPoint& pt ) const
|
||
{
|
||
QPoint p;
|
||
p.setX( static_cast<int>( ( pt.x() - m_xOffset ) / m_zoomFactor ) );
|
||
p.setY( static_cast<int>( ( pt.y() - m_yOffset ) / m_zoomFactor ) );
|
||
|
||
return p;
|
||
}
|
||
|
||
QRect ViewArea::windowToView( const QRect& rc ) const
|
||
{
|
||
QRect r;
|
||
|
||
r.setTopLeft( windowToView( rc.topLeft() ) );
|
||
// r.setRight ( (int)( ceil(( rc.right() - m_xOffset)/m_zoomFactor )));
|
||
// r.setBottom( (int)( ceil(( rc.bottom()- m_yOffset)/m_zoomFactor )));
|
||
// r.setRight ( static_cast<int>(( rc.right() - m_xOffset ) / m_zoomFactor +1));
|
||
// r.setBottom( static_cast<int>(( rc.bottom() - m_xOffset ) / m_zoomFactor+1));
|
||
r.setBottomRight( windowToView( rc.bottomRight() ) );
|
||
return r;
|
||
}
|
||
|
||
QPoint ViewArea::viewToWindow( const QPoint& pt ) const
|
||
{
|
||
QPoint p;
|
||
|
||
p.setX( static_cast<int>( pt.x() * m_zoomFactor + m_xOffset ) );
|
||
p.setY( static_cast<int>( pt.y() * m_zoomFactor + m_yOffset ) );
|
||
|
||
return p;
|
||
}
|
||
|
||
QRect ViewArea::viewToWindow( const QRect& rc ) const
|
||
{
|
||
QRect r;
|
||
|
||
r.setTopLeft( viewToWindow( rc.topLeft() ) );
|
||
// r.setRight ( (int)( ceil(( rc.right() +1+m_xOffset )*m_zoomFactor ) - 1 ));
|
||
// r.setBottom( (int)( ceil(( rc.bottom()+1+m_yOffset )*m_zoomFactor ) - 1 ));
|
||
// r.setRight ( (int)( ceil(( rc.right()+0.5)*m_zoomFactor )+ m_xOffset )-1);
|
||
// r.setBottom( (int)( ceil(( rc.bottom()+0.5)*m_zoomFactor ) +m_yOffset )-1);
|
||
// qDebug()<<"Right = "<< r.right();
|
||
// r.setRight ( static_cast<int>(( rc.right()+1) * m_zoomFactor + m_xOffset -1) );
|
||
// r.setBottom( static_cast<int>(( rc.bottom()+1) * m_zoomFactor + m_yOffset -1));
|
||
r.setBottomRight( viewToWindow( rc.bottomRight() ) );
|
||
|
||
return r;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
// Masks Management
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
void ViewArea::updateMask( const QRect &rect )
|
||
{
|
||
switch( tool() )
|
||
{
|
||
case MaskTool:
|
||
{
|
||
// Add rect to the mask
|
||
QPainter painter( &m_mask );
|
||
painter.setBrush( Qt::color1 );
|
||
painter.setPen( Qt::NoPen );
|
||
painter.drawRect( rect );
|
||
painter.end();
|
||
break;
|
||
}
|
||
case EraserTool:
|
||
{
|
||
// Clears rect area in the mask
|
||
QPainter painter( &m_mask );
|
||
painter.setBrush( Qt::color0 );
|
||
painter.setPen( Qt::NoPen );
|
||
painter.drawRect( rect );
|
||
painter.end();
|
||
break;
|
||
}
|
||
default: /* Do Nothing */
|
||
;
|
||
}
|
||
}
|
||
|
||
void ViewArea::setInputStream( PlaYUVerStream *stream )
|
||
{
|
||
m_pStream = stream;
|
||
}
|
||
|
||
PlaYUVerStream* ViewArea::getInputStream()
|
||
{
|
||
return m_pStream;
|
||
}
|
||
|
||
} // NameSpace plaYUVer
|