Tuesday, March 31, 2009

The Touch Pro menu

This is not really a menu but a window instead, and it's also not what I want to write about. What really interested me was the effect of showing a single menu item at the right side of the menu bar. This is a very cool effect because you use the same toolbar button to both show and hide the menu list. Achieving this effect is actually quite easy: you must add an empty button on the left. Here's the script I used to mimic this effect:


IDM_MENU_EMPTY_CANCEL RCDATA
BEGIN
IDM_MENU_EMPTY_CANCEL, 2,
I_IMAGENONE, ID_MENU_EMPTY, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE,
IDS_EMPTY, 0, NOMENU,
I_IMAGENONE, ID_MENU_CANCEL, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE,
ID_MENU_CANCEL, 0, NOMENU,
END

The IDS_EMPTY string resource contains one space character only and the ID_MENU_EMPTY command is not handled. This seems to be the same approach that HTC used when writing the user interface: if you click the left soft button or the left menu area, you will see the visual feedback just like it had a real button.

Monday, March 16, 2009

IImage::Draw

Drawing PNG images on the screen has become quite easy since Microsoft released the imaging library for Windows Mobile devices. Christopher Fairbairn illustrated the technique on one of his excellent blog posts where he illustrates the process, from acquiring the bitmap data to rendering it on the screen. As he points out, if you load a PNG file with an alpha layer, the transparency will be correctly painted to the image creatin all sorts of nice visual effects. But...

... this is a very expensive operation! In fact, IImage::Draw is slower than painting a non transparency bitmap using the regular BitBlt API. If you need raw performance (like when you are repeatedly painting lots of these to the screen, even with a double buffer) yo will get a terrible performance hit. I found out about this in my map rendering scenario, while adding KML-encoded layers. As the number of bitmaps grows, the rendering speed degrades very rapidly to the point where it becomes unusable. What can you do?

My solution was to pre-render the PNG file into a regular GDI bitmap (adding a white background) and then using the BitBlt API to render it. The performance was back at the expense of some unsightly white backgrounds on my map icons... Seems like this is going to be the solution if I want to stick with the GDI.

P.S: The white box issue was neatly solved by rendering the PNG against a Magenta (RGB(0xff, 0, 0xff)) background and using the TransparentImage API.

Wednesday, March 04, 2009

Seek and you shall find

I recently found myself involved in a prototype Windows Mobile mapping application using the Virtual Earth tile system. Downloading the 256 x 256 bitmaps to a mobile device through a GPRS connection is slow and a bit painful, so I thought about creating an external cache mechanism. This would temporarily store the bitmaps for future use and, instead of writing the bitmaps to the file system I thought about using SQL Compact. There are a few advantages for using a database instead of the file system: you store all the bitmaps in a single file and you get the added benefit of easier maintenance (especially when deleting outdated bitmaps to clear the cache). So I looked back to the OLE DB library I have been working with for some help but I found a major whole (yes, there are more, I know!): there was no support for IRowsetIndex::Seek. Why do I need to use this method when I can equally retrieve individual records using a plain SELECT command? The answer is speed. When you go through the SQL Compact Query Processor your application pays a performance penalty that may be avoided (with varying degrees of effort). In this case I wanted to retrieve a single row from a table using an index (no fancy JOIN or GROUP BY clauses) so using a base table is very appropriate, performance-wise.

The idea is very simple: open a base table cursor and specify the index to use; seek using the required key values and retrieve the row data (if it's there). As I said, you can do this using SQL but there is performance penalty that I did not want to pay, especially when fast painting is required. My major problem was that there was no support for this in the previous version of the OLD DB library, so I had to implement it.

Implementing the IRowsetIndex::Seek supporting code implies adding an extra column binding code (similar to the one I wrote for the columns). In fact, you need an extra IAccessor object to bind the key columns as well as all the other supporting data structures. After a few hours worth of work, I managed to develop a working version of the code that I'm publishing here. If you look at the CRowset class implementation you will see how its complexity increased to a point where some new abstractions are just crying out to be created...

When using this code, remember to:
  • Add the DBPROP_IRowsetIndex property (set to true) to the rowset property set;
  • Call CRowset::SetKeyValue for each one of the index key values: ordinals start at one;
  • To seek to the requested row, call CRowset::Seek specifying the number of valid key columns (the number of valid values set with CRowset::SetKeyValue).
If you get an S_OK as return value, the row was found and the data loaded into the row and ready to use.

Sample code: OleDbClientLib3.zip

EDIT: The link has been fixed.