Monday, September 28, 2009

Improvements

After reading Vincent's comment on my last post, I took a second look at the scrolling code and scuttled the InvalidateRect scrolling mechanism. The fact is that while on some older devices this did work in an acceptable fashion, it did not work that well on more recent models and you can actually see some "bumps" in the scrolling speed. The code now uses a timer (again) but instead of indirectly painting the window through the InvalidateRect / UpdateWindow calls, it now directly invokes the painting function. The result is a very smooth scroll on all devices I have tested the code with.

I also changed the way the ScrollLeft and ScrollRight view transitions work. Instead of painting the bitmap at full speed, I added a very small sleep period to each iteration so that each transition is performed in approximately 250 milliseconds. The first paint operation is timed and a per-iteration sleep period is calculated so that the whole process takes the quarter-second period to unfold. With this improvement, you will be able to see the view transition on all devices (especially the ones with a faster CPU and QVGA screen).

Finally, I decided to add a couple of new view transitions: Fade and Explode / Implode. The first transition uses the AlphaBlend API function to fade from the existing view to the new view (like in a Power Point presentation). I have to say that this is not a very compelling transition, but inspired me to write the second. The Explode / Implode view transition also tries to mimic one of the most famous iPhone view transitions (when you launch an application). The existing view is zoomed in while the new view fades in while also zooming in from half its original size. The Implode effect does the reverse. The result was a bit disappointing because AlphaBlend operation seems to be very greedy (you can replace it by a simple StretchBlt and compare the results). The GDI is not very good at this stuff, unfortunately...

Sample code: CrypSafe06.zip (323KB)

Sunday, September 20, 2009

View transitions

Now that I managed to get some decent finger feedback from the list, it's time to get back to the CrypSafe prototype and add some view transitions. The code I'm using is based on an article I published last year on CodeProject: Animating View Transitions on Windows Mobile. Please read it before continuing as what you will see here is an extension of what I wrote there.

Essentially this article explored the ability to do some fashionable (read iPhone-like) view transitions using child views implemented as regular Win32 controls (such as a tree view, a list view or even a dialog). The view transitions were implemented using a very simple mechanism:
  • Capture the existing view as a bitmap;
  • Remove the from the frame container;
  • Paint the view bitmap on the frame client area;
  • Create the new child view window off screen;
  • Enter a loop where the bitmap is scrolled to the requested direction a few pixels;
  • Every time the bitmap is scrolled, move the new child window to the contiguous position thus creating the illusion that both windows are moving in lockstep.
That was a bit crude but worked. One of the lessons I learned from writing this article is that, generally, you cannot force an arbitrary window to paint itself to a memory DC. This would be very useful for some advanced visual effects. By reading the docs you might even be tempted (as I was) to send a WM_PAINT message to the hidden window with your memory DC in the WPARAM and expect it to paint there, but that just doesn't work for all windows. That's why I had to use MoveWindow for the animation.

Now that we are rendering the touch windows and lists directly on a memory DC, there is a better chance to improve on the original algorithm and implement a smoother view transition algorithm. This is what you will find in the sample code (see below).

The major change I made to the code was to CChildViewBase where you can now see a GetBitmap virtual method. If this method returns NULL, then the child view is not able to render itself on a bitmap and the old brute-force method will be used. If, on the other hand, the child view is able to render itself to a bitmap it must do so here. Note how this is implemented in the CCrypSafeView class: the painting engine is called directly and a new HBITMAP is returned for the child view manager to paint.

The results are here for you to see: acceptable on some devices, way too fast on others and slower on older devices. What we need to do here is quite simple: use the same kinematics equations to control the view transition motion and leave nothing to chance.

Comments and suggestions are welcome!

Sample code: CrypSafe05.zip (349 KB)

Tuesday, September 15, 2009

Feedback

I just received a very nice email from one of you (you know who you are) reporting a bug in the OLE DB Client library, more specifically on the CDbValueRef class (file DbValue.cpp, line 758). The line must read:

*m_pLength = nStrLen * sizeof(wchar_t);

The original code did not multiply the string size with the Unicode character size, so you got a wrong byte count and a bug.

It's always very rewarding to receive emails with correction and suggestions from you (it means someone is reading this... - hehe). Thank you for all the feedback!

Sunday, September 13, 2009

Better touch

My professional life changed quite a bit recently and that's why I have not been posting as frequently as usual. I finally seem to have reached some balance and now's the time to resume my blog work.

One of the faults I found in the last version of the published code concerned finger sensitivity. This is a real issue in touch screens based on resistive technology (see a discussion about this topic on Marcus Perryman's blog). The bottom line is simple: resistive screens were made for a stylus, not for your finger. They are much more precise than capacitive screens (like the one found on the iPod Touch and iPhone), but require pressure. Capacitive screens are less precise but require only a very soft finger contact in order to operate. Resistive screens expect a very small pressure area (the tip of a stylus). An adult index finger uses up a very large area on the touch screen and the net result is noise and erratic motion messages.

You can see this in the last sample code I published: flick the list and then click it. It should stop the list but it doesn't (that's how it works on my HTC Touch Pro). What happens is that a single finger press is interpreted by the highly sensitive resistive screen as a sequence of one WM_LBUTTONDOWN message, a couple of WM_MOUSEMOVE messages and the final WM_LBUTTONUP. Inspecting the LPARAM parameter you will see that the POINT structure shows some fluctuations on the x and y coordinates. This means that a single finger press is actually equivalent to a small doodle drawn with your stylus. You will be lucky if the first and last POINT values are the same (they almost never are).

To mitigate this issue and provide better (more reliable) finger action feedback I made some adjustments to the code. The first thing I realized is that one of the sources of "position noise" was the first WM_MOUSEMOVE message. The offset between the WM_LBUTTONDOWN position and the first WM_MOUSEMOVE message seems to provide more variance than the offset to the second WM_MOUSEMOVE. To even things out a little bit I now discard the first offset and start calculating the gesture from the first WM_MOUSEMOVE message. Also, I brought back the concept of sensitivity to filter out smaller movements. Finally take a look at the code in CTouchGesture::Move method. Instead of calculating the move offset in a single if clause, I split the code into two, one for the vertical motion and the other for the horizontal motion.

The final result feels a bit better and more reliable on all the machines I have tested the code with, but the final word is yours.

Sample code: AppStart02.zip (164 KB)

Thursday, September 10, 2009

Shaken, not stirred...

The September 2009 issue of MSDN Magazine presents us with a very nice article on how to detect shaking with the HTC accelerometer. Note that the article is for managed developers, but that should be no problem for you...