Copy Your Favorite Nokia AppsAriya Hidayat 09/25/09
Agenda
• Mobile platform challenges:– differences vs desktop– things to keep in mind
• Examples which demonstrate:– Low-level painting– Graphics View– Network and web stack
2
Spread the Love
3
All examples are available from...
labs.qt.nokia.com
bit.ly/graphicsdojo
Desktop vs Mobile Platforms
• Hardware power
• Screen size and resolutions
• Input method
• Network access
4
Nokia N95
• CPU: 332 MHz ARM11
• RAM: 128 MB
• Screen: 240 x 320 (QVGA)
• 9-Button keypad
5
Nokia E71
• CPU: 369 MHz ARM11
• RAM: 128 MB
• Screen: 320x240 (QVGA)
• Full QWERTY Keypad
6
Nokia 5800 XpressMusic
• CPU: 434 MHz ARM11
• RAM: 128 MB
• Screen: 360 x 640 (nHD)
• Touch interface
7
Processing Power
• ARM architecture– Different than x86
• Limited memory
8
Graphics Power
• Lower resolution
• Software rendering– hardware acceleration with OpenGL ES
• Font metrics
9
Input Methods
• Keypad, not keyboard– often not full QWERTY
• No mouse– don't bother with mouse move event
• Touch interface– single vs multitouch– stylus vs finger
• Sensors: accelerometer, GPS, compass, ...
10
Network Access
• Wireless LAN– sometimes not available
• Other data access– slower data rate
11
Development Flow
• Device emulator
• Remote debugging
• Performance test
• Profiling tools
12
Example #1
Digital Clock
Digital (Vintage) Clock
14
Framed Digits
15
plain digits with frame
gradient
Gradient is Expensive, so...
16
QLinearGradient gr(QPoint(0, 0), QPoint(0, height));gr.setColorAt(0.00, QColor(128, 128, 128));gr.setColorAt(0.49, QColor(64, 64, 64));gr.setColorAt(0.51, QColor(128, 128, 128));gr.setColorAt(1.00, QColor(16, 16, 16));
QPainter p;p.begin(&pixmap);p.setFont(font);QPen pen;pen.setBrush(QBrush(gr));p.setPen(pen);p.drawText(pixmap.rect(), Qt::AlignCenter, str);p.end();
Cache to a pixmap
Sliding Effect
17
translating both pixmaps
Sliding Effect: The Code
18
QPainter p(this);int y = height() * currentFrame / 100;p.drawPixmap(0, y, m_lastPixmap);p.drawPixmap(0, y - height(), m_pixmap);p.end();
Rotation
19
transform.rotate(30, Qt::ZAxis)
Perspective Transformation
20
transform.rotate(60, Qt::YAxis)
transform.rotate(60, Qt::XAxis)
Rotating Effect
21
rotating pixmaps
Rotating Effect: The Code
22
QTransform transform;transform.translate(width() / 2, height() / 2);transform.rotate(angle, Qt::XAxis);
QPainter p(this);p.setTransform(transform);p.drawPixmap(-width() / 2, -height() / 2, pixmap);p.end();
Flipping Effect
23
rotating and interleaving
two pixmaps
A Trick with QBasicTimer
24
QBasicTimer ticker;ticker.start(1000, this);
void timerEvent(QTimerEvent*){ // update the clock}
No need for QTimer, a slot, and
periodic triggering of signal/slot !
QTimeLine for Frame-based Animation
25
QTimeLine animator;animator.setFrameRange(0, 100);animator.setDuration(600);animator.setCurveShape(QTimeLine::EaseInOutCurve);
int progress = animator.currentFrame();
EaseInOutLinear
acceleration
deacceleration
Sacrifice Quality for Speed
26
Beauty is in the eye of the beholder.
Aliasing during Animation
27
QPainter p(this);p.setRenderHint(QPainter::SmoothPixmapTransform, false);p.setRenderHint(QPainter::Antialiasing, false);
Example #2
Weather Info
Weather Info
29
Screen Orientation
30
requires different layout strategy
Lock the Orientation
31
#include <eikenv.h>#include <eikappui.h>#include <aknenv.h>#include <aknappui.h>
CAknAppUi* appUi = dynamic_cast<CAknAppUi*> (CEikonEnv::Static()->AppUi());appUi->SetOrientationL (CAknAppUi::EAppUiOrientationPortrait);
S60-specific, usually you do NOT need to do this!
Animation with Graphics View
32
item movement &
opacity
Using Web Service
33
<weather><forecast_information> <city data="Oslo, Oslo"/> <postal_code data="Oslo"/> <latitude_e6 data=""/><longitude_e6 data=""/> <forecast_date data="2009-09-15"/> <current_date_time data="2009-09-15 13:40:00 +0000"/> <unit_system data="US"/></forecast_information><current_conditions> <condition data="Clear"/> <temp_f data="61"/><temp_c data="16"/> <humidity data="Humidity: 55%"/> <icon data="/ig/images/weather/sunny.gif"/> <wind_condition data="Wind: S at 12 mph"/></current_conditions><forecast_conditions>...
Create the Network Request
34
http://www.google.com/ig/api?hl=en&weather=oslo
QUrl url("http://www.google.com/ig/api");url.addEncodedQueryItem("hl", "en");url.addEncodedQueryItem("weather", QUrl::toPercentEncoding(location));
QNetworkAccessManager *manager;manager = new QNetworkAccessManager(this);connect(manager, SIGNAL(finished(QNetworkReply*)), this,SLOT(handleNetworkData(QNetworkReply*)));
manager->get(QNetworkRequest(url));
Handle the Network Reply
35
http://www.google.com/ig/api?hl=en&weather=oslo
QUrl url = networkReply->url();data = QString::fromUtf8(networkReply->readAll());networkReply->deleteLater();networkReply->manager()->deleteLater();
XML Stream Reader for Parsing
36
QXmlStreamReader xml(data);while (!xml.atEnd()) { xml.readNext(); if (xml.tokenType() == QXmlStreamReader::StartElement) if (xml.name() == "city") city = xml.attributes().value("data").toString()}
faster and requires less memory than
using QDom!
Scalable Icons with SVG
37
bitmap vs vector
Optimize Your SVG
38
Because one element in a group often
does not make sense!
<g> <ellipse cx="10" cy="10" rx=2" ry="2"/></g>
<ellipse cx="10" cy="10" rx=2" ry="2"/>
SVG Loading Time Comparison
39
Use tools like:
codedread.com/scour
svgmin.googlecode.com
Example #3
Flight Tracking
Flight Tracking
41
No Web Service?
42
Use “mobile” version of the web site.
Web Scraping
• Grab the HTML contents
• Scrap it– remove unnecessary clutters– parse and extract the interesting bits
• Legal aspect– some sites explicitly prohibit the use other than
in a web browser
43
Parsing HTML with XML Stream Reader
• HTML != XML
• Potentially provoke the parser (→ errors)– Solution: “scrub it”, i.e. clean up the raw HTML
44
// remove all inline frameswhile (true) { i = data.indexOf("<iframe"); if (i < 0) break; data.remove(i, data.indexOf("</iframe>") - i + 8);}
The Usual Tricks
• Use network request and reply
• Contents– Construct the request– Get the HTML data– Clean it up and then parse it
• Flight map– Construct the request– Get data and feed it to a QPixmap– Display the pixmap, e.g. with QLabel
45
Beware of Different Font Metrics
46
Example #4
Magnifying Glass
Image Zoom
48
Shadow with Radial Gradient
49
magnifier
Shadow with Radial Gradient
50
QRadialGradient g;g.setCenter(radius, radius);g.setFocalPoint(radius, radius);g.setRadius(radius);g.setColorAt(1.0, QColor(255, 255, 255, 0));g.setColorAt(0.5, QColor(128, 128, 128, 255));
Prepare the gradient brush
p.setCompositionMode (QPainter::CompositionMode_Source);p.setBrush(g);p.drawRect(maskPixmap.rect());
p.setBrush(QColor(Qt::transparent));p.drawEllipse(g.center(), radius-15, radius-15);
Draw the shadow
Carve the “hole”
Panning with Mouse/Finger/Stylus
51
void mousePressEvent(QMouseEvent *event) { if (event->buttons() != Qt::LeftButton) return; pressed = true; pressPos = dragPos = event->pos();}
Record the tap position
void mouseMoveEvent(QMouseEvent *event) { if (!event->buttons()) return; QPoint delta = event->pos() - pressPos; pressPos = event->pos(); pan(delta);}
Pan the map based on the movement
Avoid Accidental Panning
52
panning is not started past the threshold
Turbo Downscaling (We're Cheating!)
53
Fast, no filtering
With filtering
Up to 200x faster,
see http://bit.ly/cheatscale for details!
Example #5
Maps
Maps (with OpenStreetMap)
55
OpenStreetMap vs [Google,Yahoo]Maps
• Free content– Creative Commons Attribution-ShareAlike 2.0
• API does not require the license key
• Available in– rendered images– vector data
More info at www.openstreetmap.org !
56
Getting the Rendered Tiles
• Each tile is 256 x 256 pixels
• Zoom level of 0 → the whole world
• Zoom level of 17 → most detailed
57
QPointF tileForCoordinate(qreal lat, qreal lng, int zoom){ qreal zn = static_cast<qreal>(1 << zoom); qreal tx = (lng + 180.0) / 360.0; qreal ty = (1.0 - log(tan(lat * M_PI / 180.0) + 1.0 / cos(lat * M_PI / 180.0)) / M_PI) / 2.0; return QPointF(tx * zn, ty * zn);}
Rendering and Caching Strategy
58
application windowcached tiles
Avoid Accidental Panning
59
panning is not started past the threshold
Night Mode
60
Night Mode: The Code
61
QPainter p(this);p.setCompositionMode (QPainter::CompositionMode_Difference);p.fillRect(event->rect(), Qt::white);p.end();
Color channel inversion
red = 255 – red
green = 255 – green
blue = 255 - blue
Example #6
Kinetic Scrolling
Kinetic Scrolling
63
State Machine for Scrolling
64
Steady
Pressed
Manual Scroll
Auto Scroll
Stop
Mouse press
Mouse release
Mouse move
Mouse release
Mouse release Mouse press
Mousemove
Timer tick
Example #7
Parallax Sliding
Parallax Sliding
66
Slow-moving Background
67
1 542
3
Example #8
Web Technologies
Web Browser
• QtWebKit:– Standard compliant, fast rendering engine– Used in Apple Safari, Google Chrome, …– S60 Browser, Apple iPhone, Google Android,
Palm WebOS, …
• Use QWebView and you are done!
• Add flick support for kinetic scrolling
69
Google Chat Client
70
We Cheat (Again)!
71
• Use QWebView from QtWebKit
• Show the “mobile” version
• Add custom login page
Example #9
Unfinished Mini-game
Ray Casting
73
Made Popular in (DOS) Wolfenstein 3-D
• A single ray is traced for every column
• Grid-based world, uniform height → fast
74
Shading Trick
75
Direct Screen Blit
76
setAttribute(Qt::WA_OpaquePaintEvent, true);
Don't bother “clearing” the widget!
Faster direct screen access using
CDirectScreenBitmap or
Anti-Tearing API
Bypass Alpha-Blending
77
QPainter p(this);p.setCompositionMode(QPainter::Composition_Source)p.drawImage(0, 0, buffer);
Since we handle each and every pixel, ...
Storing Pixels in Image
78
Memory Access Optimization
79
Minimize location (in memory) betweentwo vertically separated pixels
Jump several hundreds bytes
Jump fewbytes only
Inner Loop Approach
80
while (y1 >= 0 && y2 < bufh && p1 >= 0) { *pixel1 = tex[p1 >> 12]; *pixel2 = tex[p2 >> 12]; p1 -= dy; p2 += dy; --y1; ++y2; pixel1 -= stride; pixel2 += stride;}
Use only simple, 12-bits fixed-point arithmetics
Conclusion
81
Mobile application developmentwith Qt is FUN
That's all, folks...
82
Thank You!
Bleeding-Edge
83
labs.qt.nokia.com
bit.ly/graphicsdojo