Introduction
This article intends to explain the principles of coding an image viewer for Pocket PCs. The sample application is simple. There are no advanced features like zoom in/out or image rotate. In fact, there are only 3 main points that I want to explain in the sample:
- How to toggle a window to full screen mode.
- How to load a JPEG image and draw it on a window.
- How to scroll the image when it is bigger than the screen area (by using a stylus pen).
A Quick View of the Image Viewer Sample
wmimgvwr
is a C++/MFC application coded using Visual Studio 2005. You can build the final executable either to PPC 2003 or to Windows Mobile 5. There are two forms of opening an image with vmimgvwr
:
- Starting it with no arguments, an Open File Dialog (CFileDialog) is going to be shown (Figure 1). The main problem with CFileDialog is that it allows access only to files and folders in the \My Documents folder.
- Starting it by passing a full image path, for instance, an application can start
wmimgvwr
by calling the CreateProcess API function:
PROCESS_INFORMATION procInfo;
CreateProcess( L"\\Apps\\wmimgvwr.exe",
L"\\Images\\fig1.bmp",
NULL,
NULL,
FALSE,
NULL,
NULL,
NULL,
NULL,
&procInfo );
Figure 1
At the upper-left corner of the window, there is a close button. In fact, it is not a button but a picture control (Figure 2). While you are scrolling the image, that button is not visible. Thus, you can see all image details (Figure 3).
Figure 2
|
Figure 3
|
Understanding the Source Code
Now it is time to explain the main points of the source code.
1. How to Toggle the Window to Full Screen Mode
Pocket PCs have low resolution screens (320x240 or 240x240). So it is good to use all that resolution to display an image, right? wmimgvwr
is a dialog application. It is a dialog with no title bar or system menu. However, that is not enough. We need some coding to toggle the dialog to full screen mode. Take a look at wmimgvwrDlg.cpp (line 91):
SHINITDLGINFO shidi;
(void) ::ZeroMemory(&shidi, sizeof(shidi));
shidi.dwMask = SHIDIM_FLAGS;
shidi.dwFlags = SHIDIF_FULLSCREENNOMENUBAR;
shidi.hDlg = this->m_hWnd;
::SHInitDialog(&shidi);
this->uf_full_screen();
SHInitDialog is used to set the dialog to full screen using SHIDIF_FULLSCREENNOMENUBAR, which removes the command bar from the dialog. Surprisingly, the result is not a full-screen window, but it is still necessary. Member that function uf_full_screen()
got the rest of the necessary code (wmimgvwrDlg.h, line 60):
void uf_full_screen(BOOL _bMove = TRUE)
{
HWND hBar = ::SHFindMenuBar(this->m_hWnd);
::CommandBar_Show( hBar, FALSE );
this->SetForegroundWindow();
::SHFullScreen( this->m_hWnd,
SHFS_HIDETASKBAR | SHFS_HIDESIPBUTTON | SHFS_HIDESTARTICON );
RECT cliRect;
this->GetClientRect(&cliRect);
if ( _bMove )
{
cliRect.top -= vg_wintask_h;
cliRect.bottom += vg_wintask_h;
this->MoveWindow(&cliRect);
}
}
The first two lines of the function contain the code to hide the command bar (SHFindMenuBar and CommandBar_Show). Next, SHFullScreen gives privileges to the dialog window to cover the task bar and SIP button. That is the best way I found to describe what SHFullScreen really does, as a call to it does not produce a full screen window. Besides, you must call SetForegroundWindow before calling it.
Finally, you must resize the window to cover all the screen. I mean that the window must cover the task bar area, as well. So, what is the height of the task bar? Usually 26 pixels, but we can never be sure. Take a look at the variable vg_wintask_h
(wmimgvwrDlg.h, line 73). Its value is the task bar height. wmimgvwr.cpp, line 52 shows how it gets that value.
2. How to Load a JPEG Image File and Draw It on a Window
Bitmaps are drawn on a window's client area. So, all you need to do is load the bitmap file, get the handle and use the appropriated functions to draw it. MFC gives us the CBitmap and CDC classes. Both are enough to load and display a bitmap file, but how about JPEG files?
You might think that is a problem because there is no CJPEG class to load a JPEG file and convert it to a bitmap. In fact, even basic functions like LoadImage only accept bitmaps, icons or cursor files to load. Don't worry; you will not have to search the Internet looking for a JPEG library for Windows CE. Take a look at the uf_load_bitmap
member function defined in wmimgvwrDlg.cpp (line 183). There is such a cool function called SHLoadImageFile that allows an application to load several types of images and convert all of them to bitmaps! Then the application can attach the CBitmap class to the bitmap handle returned by SHLoadImageFile and easily deal with it. Notice that it can load GIF and PNG files.
3. How to Scroll the Image When It is Bigger than the Screen Area (by Using a Stylus Pen)
Drawing a bitmap on a window's client area is a simple matter of calling the BitBlt function and passing the correct arguments. How to control those argument values might be a problem. Member function OnPaint
defined at wmimgvwrDlg.cpp (line 125) is called every time a window needs to be redrawn. The following code is always executed when OnPaint
is called:
dc.BitBlt( this->m_img_pos.dc_x,
this->m_img_pos.dc_y,
this->m_img_pos.dc_crop_cx,
this->m_img_pos.dc_crop_cy,
&this->m_dcMem,
this->m_img_pos.bmp_x,
this->m_img_pos.bmp_y,
SRCCOPY );
Member variable m_img_pos
is a structure defined at globals.h. Some fields are initialized in uf_load_bitmap
and others in the uf_calc_bmp_pos
member function (wmimgvwrDlg.cpp, line 148). uf_calc_bmp_pos
does the following: if the image is smaller than the window's client area, then the image is centered in the window. Otherwise, the central part of the image is drawn on the window's client area.
Image scrolling is controlled in the PreTranslateMessage
member function (wmimgvwrDlg.cpp, line 227). Notice how the visible state of the close button is controlled (lines 239 and 301). The WM_MOUSEMOVE
message is handled to deal with scrolling and scroll only happens if the image does not fit in the window. Two Boolean fields in the m_img_pos
structure control image scrolling: bmp_scroll_x
and bmp_scroll_y
.
The bmp_x
and bmp_y
fields are the fields that have their values changed depending on pen action. Finally, if a scroll really happens, a call to Invalidate is made to force the window's client area to be redrawn (line 295).
Enjoy. I hope this helps.
History
23 November, 2007 -- First version