Tablet example: smoother, rotation stylus, better airbrush
The drawing is antialiased and with rounded caps and joins. Rotation stylus acts like a felt marker. Airbrush sprays a radial gradient of color, and its alpha can depend on the tangential pressure if so chosen. Task-number: QTBUG-46694 Change-Id: I4fb16eb621707e3c56a75aa302dd0d3bf418a745 Reviewed-by: Laszlo Agocs <laszlo.agocs@theqtcompany.com> Reviewed-by: Topi Reiniö <topi.reinio@digia.com>
This commit is contained in:
parent
ad4d25589f
commit
6a991f9cce
@ -259,24 +259,18 @@
|
|||||||
|
|
||||||
\snippet widgets/tablet/tabletcanvas.cpp 5
|
\snippet widgets/tablet/tabletcanvas.cpp 5
|
||||||
|
|
||||||
In this function we draw on the pixmap based on the movement of the
|
In this function we draw on the pixmap based on the movement of the device.
|
||||||
device. If the device used on the tablet is a stylus we want to draw a
|
If the device used on the tablet is a stylus, we want to draw a line from
|
||||||
line between the positions of the stylus recorded in \c polyLine. We
|
the last-known position to the current position. We also assume that this
|
||||||
also assume that this is a reasonable handling of any unknown device,
|
is a reasonable handling of any unknown device, but update the status bar
|
||||||
but update the statusbar with a warning so that the user can see that
|
with a warning. If it is an airbrush, we want to draw a circle filled with
|
||||||
for his tablet he might have to implement special handling.
|
a soft gradient, whose density can depend on various event parameters.
|
||||||
If it is an airbrush we want to draw a circle of points with a
|
By default it depends on the tangential pressure, which is the position of
|
||||||
point density based on the tangential pressure, which is the position
|
the finger wheel on the airbrush. If it is a rotation stylus, we simulate
|
||||||
of the finger wheel on the airbrush. We use the Qt::BrushStyle to
|
a felt marker by drawing trapezoidal strokes.
|
||||||
draw the points as it has styles that draw points with different
|
|
||||||
density; we select the style based on the tangential pressure in
|
|
||||||
\c brushPattern().
|
|
||||||
|
|
||||||
\snippet widgets/tablet/tabletcanvas.cpp 6
|
\snippet widgets/tablet/tabletcanvas.cpp 6
|
||||||
|
|
||||||
We return a brush style with a point density that increases with
|
|
||||||
the tangential pressure.
|
|
||||||
|
|
||||||
In \c updateBrush() we set the pen and brush used for drawing
|
In \c updateBrush() we set the pen and brush used for drawing
|
||||||
to match \c alphaChannelType, \c lineWidthType, \c
|
to match \c alphaChannelType, \c lineWidthType, \c
|
||||||
colorSaturationType, and \c myColor. We will examine the code to
|
colorSaturationType, and \c myColor. We will examine the code to
|
||||||
|
@ -52,7 +52,7 @@ MainWindow::MainWindow(TabletCanvas *canvas)
|
|||||||
|
|
||||||
myCanvas->setColor(Qt::red);
|
myCanvas->setColor(Qt::red);
|
||||||
myCanvas->setLineWidthType(TabletCanvas::LineWidthPressure);
|
myCanvas->setLineWidthType(TabletCanvas::LineWidthPressure);
|
||||||
myCanvas->setAlphaChannelType(TabletCanvas::NoAlpha);
|
myCanvas->setAlphaChannelType(TabletCanvas::AlphaTangentialPressure);
|
||||||
myCanvas->setColorSaturationType(TabletCanvas::NoSaturation);
|
myCanvas->setColorSaturationType(TabletCanvas::NoSaturation);
|
||||||
|
|
||||||
setWindowTitle(tr("Tablet Example"));
|
setWindowTitle(tr("Tablet Example"));
|
||||||
@ -75,6 +75,8 @@ void MainWindow::alphaActionTriggered(QAction *action)
|
|||||||
{
|
{
|
||||||
if (action == alphaChannelPressureAction) {
|
if (action == alphaChannelPressureAction) {
|
||||||
myCanvas->setAlphaChannelType(TabletCanvas::AlphaPressure);
|
myCanvas->setAlphaChannelType(TabletCanvas::AlphaPressure);
|
||||||
|
} else if (action == alphaChannelTangentialPressureAction) {
|
||||||
|
myCanvas->setAlphaChannelType(TabletCanvas::AlphaTangentialPressure);
|
||||||
} else if (action == alphaChannelTiltAction) {
|
} else if (action == alphaChannelTiltAction) {
|
||||||
myCanvas->setAlphaChannelType(TabletCanvas::AlphaTilt);
|
myCanvas->setAlphaChannelType(TabletCanvas::AlphaTilt);
|
||||||
} else {
|
} else {
|
||||||
@ -157,15 +159,19 @@ void MainWindow::createActions()
|
|||||||
alphaChannelPressureAction = new QAction(tr("&Pressure"), this);
|
alphaChannelPressureAction = new QAction(tr("&Pressure"), this);
|
||||||
alphaChannelPressureAction->setCheckable(true);
|
alphaChannelPressureAction->setCheckable(true);
|
||||||
|
|
||||||
|
alphaChannelTangentialPressureAction = new QAction(tr("T&angential Pressure"), this);
|
||||||
|
alphaChannelTangentialPressureAction->setCheckable(true);
|
||||||
|
alphaChannelTangentialPressureAction->setChecked(true);
|
||||||
|
|
||||||
alphaChannelTiltAction = new QAction(tr("&Tilt"), this);
|
alphaChannelTiltAction = new QAction(tr("&Tilt"), this);
|
||||||
alphaChannelTiltAction->setCheckable(true);
|
alphaChannelTiltAction->setCheckable(true);
|
||||||
|
|
||||||
noAlphaChannelAction = new QAction(tr("No Alpha Channel"), this);
|
noAlphaChannelAction = new QAction(tr("No Alpha Channel"), this);
|
||||||
noAlphaChannelAction->setCheckable(true);
|
noAlphaChannelAction->setCheckable(true);
|
||||||
noAlphaChannelAction->setChecked(true);
|
|
||||||
|
|
||||||
alphaChannelGroup = new QActionGroup(this);
|
alphaChannelGroup = new QActionGroup(this);
|
||||||
alphaChannelGroup->addAction(alphaChannelPressureAction);
|
alphaChannelGroup->addAction(alphaChannelPressureAction);
|
||||||
|
alphaChannelGroup->addAction(alphaChannelTangentialPressureAction);
|
||||||
alphaChannelGroup->addAction(alphaChannelTiltAction);
|
alphaChannelGroup->addAction(alphaChannelTiltAction);
|
||||||
alphaChannelGroup->addAction(noAlphaChannelAction);
|
alphaChannelGroup->addAction(noAlphaChannelAction);
|
||||||
connect(alphaChannelGroup, SIGNAL(triggered(QAction*)),
|
connect(alphaChannelGroup, SIGNAL(triggered(QAction*)),
|
||||||
@ -259,6 +265,7 @@ void MainWindow::createMenus()
|
|||||||
|
|
||||||
alphaChannelMenu = tabletMenu->addMenu(tr("&Alpha Channel"));
|
alphaChannelMenu = tabletMenu->addMenu(tr("&Alpha Channel"));
|
||||||
alphaChannelMenu->addAction(alphaChannelPressureAction);
|
alphaChannelMenu->addAction(alphaChannelPressureAction);
|
||||||
|
alphaChannelMenu->addAction(alphaChannelTangentialPressureAction);
|
||||||
alphaChannelMenu->addAction(alphaChannelTiltAction);
|
alphaChannelMenu->addAction(alphaChannelTiltAction);
|
||||||
alphaChannelMenu->addAction(noAlphaChannelAction);
|
alphaChannelMenu->addAction(noAlphaChannelAction);
|
||||||
|
|
||||||
|
@ -79,6 +79,7 @@ private:
|
|||||||
|
|
||||||
QActionGroup *alphaChannelGroup;
|
QActionGroup *alphaChannelGroup;
|
||||||
QAction *alphaChannelPressureAction;
|
QAction *alphaChannelPressureAction;
|
||||||
|
QAction *alphaChannelTangentialPressureAction;
|
||||||
QAction *alphaChannelTiltAction;
|
QAction *alphaChannelTiltAction;
|
||||||
QAction *noAlphaChannelAction;
|
QAction *noAlphaChannelAction;
|
||||||
|
|
||||||
|
@ -47,14 +47,13 @@
|
|||||||
TabletCanvas::TabletCanvas()
|
TabletCanvas::TabletCanvas()
|
||||||
{
|
{
|
||||||
resize(500, 500);
|
resize(500, 500);
|
||||||
myBrush = QBrush();
|
myColor = Qt::red;
|
||||||
myPen = QPen();
|
myBrush = QBrush(myColor);
|
||||||
|
myPen = QPen(myBrush, 1.0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
|
||||||
initPixmap();
|
initPixmap();
|
||||||
setAutoFillBackground(true);
|
setAutoFillBackground(true);
|
||||||
deviceDown = false;
|
deviceDown = false;
|
||||||
myColor = Qt::red;
|
alphaChannelType = AlphaTangentialPressure;
|
||||||
myTabletDevice = QTabletEvent::Stylus;
|
|
||||||
alphaChannelType = NoAlpha;
|
|
||||||
colorSaturationType = NoSaturation;
|
colorSaturationType = NoSaturation;
|
||||||
lineWidthType = LineWidthPressure;
|
lineWidthType = LineWidthPressure;
|
||||||
}
|
}
|
||||||
@ -99,24 +98,23 @@ void TabletCanvas::tabletEvent(QTabletEvent *event)
|
|||||||
case QEvent::TabletPress:
|
case QEvent::TabletPress:
|
||||||
if (!deviceDown) {
|
if (!deviceDown) {
|
||||||
deviceDown = true;
|
deviceDown = true;
|
||||||
polyLine[0] = polyLine[1] = polyLine[2] = event->pos();
|
lastPoint.pos = event->posF();
|
||||||
|
lastPoint.rotation = event->rotation();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case QEvent::TabletRelease:
|
|
||||||
if (deviceDown)
|
|
||||||
deviceDown = false;
|
|
||||||
break;
|
|
||||||
case QEvent::TabletMove:
|
case QEvent::TabletMove:
|
||||||
polyLine[2] = polyLine[1];
|
|
||||||
polyLine[1] = polyLine[0];
|
|
||||||
polyLine[0] = event->pos();
|
|
||||||
|
|
||||||
if (deviceDown) {
|
if (deviceDown) {
|
||||||
updateBrush(event);
|
updateBrush(event);
|
||||||
QPainter painter(&pixmap);
|
QPainter painter(&pixmap);
|
||||||
paintPixmap(painter, event);
|
paintPixmap(painter, event);
|
||||||
|
lastPoint.pos = event->posF();
|
||||||
|
lastPoint.rotation = event->rotation();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case QEvent::TabletRelease:
|
||||||
|
if (deviceDown && event->buttons() == Qt::NoButton)
|
||||||
|
deviceDown = false;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -135,23 +133,44 @@ void TabletCanvas::paintEvent(QPaintEvent *)
|
|||||||
//! [5]
|
//! [5]
|
||||||
void TabletCanvas::paintPixmap(QPainter &painter, QTabletEvent *event)
|
void TabletCanvas::paintPixmap(QPainter &painter, QTabletEvent *event)
|
||||||
{
|
{
|
||||||
QPoint brushAdjust(10, 10);
|
painter.setRenderHint(QPainter::Antialiasing);
|
||||||
|
|
||||||
switch (myTabletDevice) {
|
switch (event->device()) {
|
||||||
|
//! [6]
|
||||||
case QTabletEvent::Airbrush:
|
case QTabletEvent::Airbrush:
|
||||||
myBrush.setColor(myColor);
|
{
|
||||||
myBrush.setStyle(brushPattern(event->pressure()));
|
|
||||||
painter.setPen(Qt::NoPen);
|
painter.setPen(Qt::NoPen);
|
||||||
painter.setBrush(myBrush);
|
QRadialGradient grad(lastPoint.pos, myPen.widthF() * 10.0);
|
||||||
|
QColor color = myBrush.color();
|
||||||
for (int i = 0; i < 3; ++i) {
|
color.setAlphaF(color.alphaF() * 0.25);
|
||||||
painter.drawEllipse(QRect(polyLine[i] - brushAdjust,
|
grad.setColorAt(0, myBrush.color());
|
||||||
polyLine[i] + brushAdjust));
|
grad.setColorAt(0.5, Qt::transparent);
|
||||||
|
painter.setBrush(grad);
|
||||||
|
qreal radius = grad.radius();
|
||||||
|
painter.drawEllipse(event->posF(), radius, radius);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case QTabletEvent::RotationStylus:
|
||||||
|
{
|
||||||
|
myBrush.setStyle(Qt::SolidPattern);
|
||||||
|
painter.setPen(Qt::NoPen);
|
||||||
|
painter.setBrush(myBrush);
|
||||||
|
QPolygonF poly;
|
||||||
|
qreal halfWidth = myPen.widthF();
|
||||||
|
QPointF brushAdjust(qSin(qDegreesToRadians(lastPoint.rotation)) * halfWidth,
|
||||||
|
qCos(qDegreesToRadians(lastPoint.rotation)) * halfWidth);
|
||||||
|
poly << lastPoint.pos + brushAdjust;
|
||||||
|
poly << lastPoint.pos - brushAdjust;
|
||||||
|
brushAdjust = QPointF(qSin(qDegreesToRadians(event->rotation())) * halfWidth,
|
||||||
|
qCos(qDegreesToRadians(event->rotation())) * halfWidth);
|
||||||
|
poly << event->posF() - brushAdjust;
|
||||||
|
poly << event->posF() + brushAdjust;
|
||||||
|
painter.drawConvexPolygon(poly);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
//! [6]
|
||||||
case QTabletEvent::Puck:
|
case QTabletEvent::Puck:
|
||||||
case QTabletEvent::FourDMouse:
|
case QTabletEvent::FourDMouse:
|
||||||
case QTabletEvent::RotationStylus:
|
|
||||||
{
|
{
|
||||||
const QString error(tr("This input device is not supported by the example."));
|
const QString error(tr("This input device is not supported by the example."));
|
||||||
#ifndef QT_NO_STATUSTIP
|
#ifndef QT_NO_STATUSTIP
|
||||||
@ -174,40 +193,13 @@ void TabletCanvas::paintPixmap(QPainter &painter, QTabletEvent *event)
|
|||||||
}
|
}
|
||||||
// FALL-THROUGH
|
// FALL-THROUGH
|
||||||
case QTabletEvent::Stylus:
|
case QTabletEvent::Stylus:
|
||||||
painter.setBrush(myBrush);
|
|
||||||
painter.setPen(myPen);
|
painter.setPen(myPen);
|
||||||
painter.drawLine(polyLine[1], event->pos());
|
painter.drawLine(lastPoint.pos, event->posF());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//! [5]
|
//! [5]
|
||||||
|
|
||||||
//! [6]
|
|
||||||
Qt::BrushStyle TabletCanvas::brushPattern(qreal value)
|
|
||||||
{
|
|
||||||
int pattern = int((value) * 100.0) % 7;
|
|
||||||
|
|
||||||
switch (pattern) {
|
|
||||||
case 0:
|
|
||||||
return Qt::SolidPattern;
|
|
||||||
case 1:
|
|
||||||
return Qt::Dense1Pattern;
|
|
||||||
case 2:
|
|
||||||
return Qt::Dense2Pattern;
|
|
||||||
case 3:
|
|
||||||
return Qt::Dense3Pattern;
|
|
||||||
case 4:
|
|
||||||
return Qt::Dense4Pattern;
|
|
||||||
case 5:
|
|
||||||
return Qt::Dense5Pattern;
|
|
||||||
case 6:
|
|
||||||
return Qt::Dense6Pattern;
|
|
||||||
default:
|
|
||||||
return Qt::Dense7Pattern;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//! [6]
|
|
||||||
|
|
||||||
//! [7]
|
//! [7]
|
||||||
void TabletCanvas::updateBrush(QTabletEvent *event)
|
void TabletCanvas::updateBrush(QTabletEvent *event)
|
||||||
{
|
{
|
||||||
@ -220,7 +212,13 @@ void TabletCanvas::updateBrush(QTabletEvent *event)
|
|||||||
|
|
||||||
switch (alphaChannelType) {
|
switch (alphaChannelType) {
|
||||||
case AlphaPressure:
|
case AlphaPressure:
|
||||||
myColor.setAlpha(int(event->pressure() * 255.0));
|
myColor.setAlphaF(event->pressure());
|
||||||
|
break;
|
||||||
|
case AlphaTangentialPressure:
|
||||||
|
if (event->device() == QTabletEvent::Airbrush)
|
||||||
|
myColor.setAlphaF(qMax(0.01, (event->tangentialPressure() + 1.0) / 2.0));
|
||||||
|
else
|
||||||
|
myColor.setAlpha(255);
|
||||||
break;
|
break;
|
||||||
case AlphaTilt:
|
case AlphaTilt:
|
||||||
myColor.setAlpha(maximum(abs(vValue - 127), abs(hValue - 127)));
|
myColor.setAlpha(maximum(abs(vValue - 127), abs(hValue - 127)));
|
||||||
@ -271,5 +269,4 @@ void TabletCanvas::updateBrush(QTabletEvent *event)
|
|||||||
void TabletCanvas::resizeEvent(QResizeEvent *)
|
void TabletCanvas::resizeEvent(QResizeEvent *)
|
||||||
{
|
{
|
||||||
initPixmap();
|
initPixmap();
|
||||||
polyLine[0] = polyLine[1] = polyLine[2] = QPoint();
|
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ class TabletCanvas : public QWidget
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum AlphaChannelType { AlphaPressure, AlphaTilt, NoAlpha };
|
enum AlphaChannelType { AlphaPressure, AlphaTangentialPressure, AlphaTilt, NoAlpha };
|
||||||
enum ColorSaturationType { SaturationVTilt, SaturationHTilt,
|
enum ColorSaturationType { SaturationVTilt, SaturationHTilt,
|
||||||
SaturationPressure, NoSaturation };
|
SaturationPressure, NoSaturation };
|
||||||
enum LineWidthType { LineWidthPressure, LineWidthTilt, NoLineWidth };
|
enum LineWidthType { LineWidthPressure, LineWidthTilt, NoLineWidth };
|
||||||
@ -107,7 +107,11 @@ private:
|
|||||||
QBrush myBrush;
|
QBrush myBrush;
|
||||||
QPen myPen;
|
QPen myPen;
|
||||||
bool deviceDown;
|
bool deviceDown;
|
||||||
QPoint polyLine[3];
|
|
||||||
|
struct Point {
|
||||||
|
QPointF pos;
|
||||||
|
qreal rotation;
|
||||||
|
} lastPoint;
|
||||||
};
|
};
|
||||||
//! [0]
|
//! [0]
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user