Commit 6a991f9c authored by Shawn Rutledge's avatar Shawn Rutledge Committed by Shawn Rutledge
Browse files

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: default avatarLaszlo Agocs <laszlo.agocs@theqtcompany.com>
Reviewed-by: default avatarTopi Reiniö <topi.reinio@digia.com>
Showing with 77 additions and 74 deletions
......@@ -259,24 +259,18 @@
\snippet widgets/tablet/tabletcanvas.cpp 5
In this function we draw on the pixmap based on the movement of the
device. If the device used on the tablet is a stylus we want to draw a
line between the positions of the stylus recorded in \c polyLine. We
also assume that this is a reasonable handling of any unknown device,
but update the statusbar with a warning so that the user can see that
for his tablet he might have to implement special handling.
If it is an airbrush we want to draw a circle of points with a
point density based on the tangential pressure, which is the position
of the finger wheel on the airbrush. We use the Qt::BrushStyle to
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().
In this function we draw on the pixmap based on the movement of the device.
If the device used on the tablet is a stylus, we want to draw a line from
the last-known position to the current position. We also assume that this
is a reasonable handling of any unknown device, but update the status bar
with a warning. If it is an airbrush, we want to draw a circle filled with
a soft gradient, whose density can depend on various event parameters.
By default it depends on the tangential pressure, which is the position of
the finger wheel on the airbrush. If it is a rotation stylus, we simulate
a felt marker by drawing trapezoidal strokes.
\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
to match \c alphaChannelType, \c lineWidthType, \c
colorSaturationType, and \c myColor. We will examine the code to
......
......@@ -52,7 +52,7 @@ MainWindow::MainWindow(TabletCanvas *canvas)
myCanvas->setColor(Qt::red);
myCanvas->setLineWidthType(TabletCanvas::LineWidthPressure);
myCanvas->setAlphaChannelType(TabletCanvas::NoAlpha);
myCanvas->setAlphaChannelType(TabletCanvas::AlphaTangentialPressure);
myCanvas->setColorSaturationType(TabletCanvas::NoSaturation);
setWindowTitle(tr("Tablet Example"));
......@@ -75,6 +75,8 @@ void MainWindow::alphaActionTriggered(QAction *action)
{
if (action == alphaChannelPressureAction) {
myCanvas->setAlphaChannelType(TabletCanvas::AlphaPressure);
} else if (action == alphaChannelTangentialPressureAction) {
myCanvas->setAlphaChannelType(TabletCanvas::AlphaTangentialPressure);
} else if (action == alphaChannelTiltAction) {
myCanvas->setAlphaChannelType(TabletCanvas::AlphaTilt);
} else {
......@@ -157,15 +159,19 @@ void MainWindow::createActions()
alphaChannelPressureAction = new QAction(tr("&Pressure"), this);
alphaChannelPressureAction->setCheckable(true);
alphaChannelTangentialPressureAction = new QAction(tr("T&angential Pressure"), this);
alphaChannelTangentialPressureAction->setCheckable(true);
alphaChannelTangentialPressureAction->setChecked(true);
alphaChannelTiltAction = new QAction(tr("&Tilt"), this);
alphaChannelTiltAction->setCheckable(true);
noAlphaChannelAction = new QAction(tr("No Alpha Channel"), this);
noAlphaChannelAction->setCheckable(true);
noAlphaChannelAction->setChecked(true);
alphaChannelGroup = new QActionGroup(this);
alphaChannelGroup->addAction(alphaChannelPressureAction);
alphaChannelGroup->addAction(alphaChannelTangentialPressureAction);
alphaChannelGroup->addAction(alphaChannelTiltAction);
alphaChannelGroup->addAction(noAlphaChannelAction);
connect(alphaChannelGroup, SIGNAL(triggered(QAction*)),
......@@ -259,6 +265,7 @@ void MainWindow::createMenus()
alphaChannelMenu = tabletMenu->addMenu(tr("&Alpha Channel"));
alphaChannelMenu->addAction(alphaChannelPressureAction);
alphaChannelMenu->addAction(alphaChannelTangentialPressureAction);
alphaChannelMenu->addAction(alphaChannelTiltAction);
alphaChannelMenu->addAction(noAlphaChannelAction);
......
......@@ -79,6 +79,7 @@ private:
QActionGroup *alphaChannelGroup;
QAction *alphaChannelPressureAction;
QAction *alphaChannelTangentialPressureAction;
QAction *alphaChannelTiltAction;
QAction *noAlphaChannelAction;
......
......@@ -47,14 +47,13 @@
TabletCanvas::TabletCanvas()
{
resize(500, 500);
myBrush = QBrush();
myPen = QPen();
myColor = Qt::red;
myBrush = QBrush(myColor);
myPen = QPen(myBrush, 1.0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
initPixmap();
setAutoFillBackground(true);
deviceDown = false;
myColor = Qt::red;
myTabletDevice = QTabletEvent::Stylus;
alphaChannelType = NoAlpha;
alphaChannelType = AlphaTangentialPressure;
colorSaturationType = NoSaturation;
lineWidthType = LineWidthPressure;
}
......@@ -99,24 +98,23 @@ void TabletCanvas::tabletEvent(QTabletEvent *event)
case QEvent::TabletPress:
if (!deviceDown) {
deviceDown = true;
polyLine[0] = polyLine[1] = polyLine[2] = event->pos();
lastPoint.pos = event->posF();
lastPoint.rotation = event->rotation();
}
break;
case QEvent::TabletRelease:
if (deviceDown)
deviceDown = false;
break;
case QEvent::TabletMove:
polyLine[2] = polyLine[1];
polyLine[1] = polyLine[0];
polyLine[0] = event->pos();
if (deviceDown) {
updateBrush(event);
QPainter painter(&pixmap);
paintPixmap(painter, event);
lastPoint.pos = event->posF();
lastPoint.rotation = event->rotation();
}
break;
case QEvent::TabletRelease:
if (deviceDown && event->buttons() == Qt::NoButton)
deviceDown = false;
break;
default:
break;
}
......@@ -135,23 +133,44 @@ void TabletCanvas::paintEvent(QPaintEvent *)
//! [5]
void TabletCanvas::paintPixmap(QPainter &painter, QTabletEvent *event)
{
QPoint brushAdjust(10, 10);
painter.setRenderHint(QPainter::Antialiasing);
switch (myTabletDevice) {
switch (event->device()) {
//! [6]
case QTabletEvent::Airbrush:
myBrush.setColor(myColor);
myBrush.setStyle(brushPattern(event->pressure()));
painter.setPen(Qt::NoPen);
painter.setBrush(myBrush);
for (int i = 0; i < 3; ++i) {
painter.drawEllipse(QRect(polyLine[i] - brushAdjust,
polyLine[i] + brushAdjust));
{
painter.setPen(Qt::NoPen);
QRadialGradient grad(lastPoint.pos, myPen.widthF() * 10.0);
QColor color = myBrush.color();
color.setAlphaF(color.alphaF() * 0.25);
grad.setColorAt(0, myBrush.color());
grad.setColorAt(0.5, Qt::transparent);
painter.setBrush(grad);
qreal radius = grad.radius();
painter.drawEllipse(event->posF(), radius, radius);
}
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::FourDMouse:
case QTabletEvent::RotationStylus:
{
const QString error(tr("This input device is not supported by the example."));
#ifndef QT_NO_STATUSTIP
......@@ -174,40 +193,13 @@ void TabletCanvas::paintPixmap(QPainter &painter, QTabletEvent *event)
}
// FALL-THROUGH
case QTabletEvent::Stylus:
painter.setBrush(myBrush);
painter.setPen(myPen);
painter.drawLine(polyLine[1], event->pos());
painter.drawLine(lastPoint.pos, event->posF());
break;
}
}
//! [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]
void TabletCanvas::updateBrush(QTabletEvent *event)
{
......@@ -220,7 +212,13 @@ void TabletCanvas::updateBrush(QTabletEvent *event)
switch (alphaChannelType) {
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;
case AlphaTilt:
myColor.setAlpha(maximum(abs(vValue - 127), abs(hValue - 127)));
......@@ -271,5 +269,4 @@ void TabletCanvas::updateBrush(QTabletEvent *event)
void TabletCanvas::resizeEvent(QResizeEvent *)
{
initPixmap();
polyLine[0] = polyLine[1] = polyLine[2] = QPoint();
}
......@@ -61,7 +61,7 @@ class TabletCanvas : public QWidget
Q_OBJECT
public:
enum AlphaChannelType { AlphaPressure, AlphaTilt, NoAlpha };
enum AlphaChannelType { AlphaPressure, AlphaTangentialPressure, AlphaTilt, NoAlpha };
enum ColorSaturationType { SaturationVTilt, SaturationHTilt,
SaturationPressure, NoSaturation };
enum LineWidthType { LineWidthPressure, LineWidthTilt, NoLineWidth };
......@@ -107,7 +107,11 @@ private:
QBrush myBrush;
QPen myPen;
bool deviceDown;
QPoint polyLine[3];
struct Point {
QPointF pos;
qreal rotation;
} lastPoint;
};
//! [0]
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment