Chapter 9: Introducing Hilo Annotator
Hilo is a collection of sample applications that allow you to browse, annotate, and share photographs and images. The previous articles in this series described the design and implementation of the Hilo Browser application, which allows you to browse and select images using a touch-enabled user interface. This article describes the Hilo Annotator application, which allows you to crop, rotate, and draw on the photographs you have selected. Hilo Annotator uses the Windows Ribbon Control to provide easy access to the various annotation functions, and the Windows Imaging Component to load and manipulate the images and their metadata.
Integrating the Browser and the Annotator
The Hilo Annotator is a separate application that you can launch either directly from the desktop, the command line, or from within the Hilo Browser application itself. The Browser application was updated to support the integration of the Annotator. The user can launch the Annotator from within the Browser by double tapping on a photo with their finger or double-clicking with the mouse. This action generates a WM_LBUTTONDBLCLK message which is handled by the media pane through the MediaPaneMessageHandler::LaunchAnnotator method, by passing the name of the selected photo. Listing 1 shows the code for the LaunchAnnotator method.
Listing 1 Hilo Browser code to launch the Annotator process
void MediaPaneMessageHandler::LaunchAnnotator(std::wstring fileName)
{
// Get the path of target exe first
wchar_t currentFileName[FILENAME_MAX];
if ( !GetModuleFileName(nullptr, currentFileName, FILENAME_MAX) )
{
return;
}
// Annotator should be found in the same directory as this binary
std::wstring currentDirectory = std::wstring(currentFileName);
std::wstring externalFileName = currentDirectory.substr(
0, currentDirectory.find_last_of(L"\\") + 1);
externalFileName += L"annotator.exe";
STARTUPINFO startInfo;
PROCESS_INFORMATION processInfo;
// Initialize startup and process info structures
ZeroMemory(&startInfo, sizeof(startInfo));
startInfo.cb = sizeof(startInfo);
ZeroMemory(&processInfo, sizeof(processInfo));
// Create command line parameter list
wchar_t buffer[FILENAME_MAX];
swprintf_s(buffer, FILENAME_MAX, L"\"%s\" \"%s\"", externalFileName.c_str(),
fileName.c_str());
::CreateProcess(nullptr, buffer, nullptr, nullptr, false, 0, nullptr, nullptr,
&startInfo, &processInfo);
// Release memory
::CloseHandle(processInfo.hProcess);
::CloseHandle(processInfo.hThread);
return;
}
The Browser assumes that the Annotator executable is in the same folder as the Browser. The first part of the LaunchAnnotator method gets the full path to the Browser application by calling the GetModuleFileName function and then extracts the folder path. This folder path is used as the path to the Annotator application.
The Browser then launches the Annotator by using the CreateProcess function, and specifies the name of the photo to edit via the command line. The Annotator process accesses the command line in the AnnotatorApplication::Initialize method when the application first starts. Listing 2 shows the code to do this. First the code calls the GetCommandLineW method and then splits this into an array of pointers to the individual command line arguments by calling the CommandLineToArgvW function. The command line is part of the process environment, so the process does not need to provide storage for the string, nor provide code to de-allocate the string buffer. The GetCommandLineW function is used because the lpCmdLine parameter of the process entry point function, WinMain, can only provide the command line as an ANSI string even if, as in the case of Hilo, the process is compiled for Unicode.
Listing 2 Annotator code to access the command line parameters
int argumentCount = 0;
ComPtr<IShellItem> currentBrowseLocationItem;
if (SUCCEEDED(hr))
{
// Process command line
wchar_t ** commandArgumentList = CommandLineToArgvW(
GetCommandLineW(), &argumentCount);
if (argumentCount > 1)
{
hr = ::SHCreateItemFromParsingName(
commandArgumentList[1],
nullptr,
IID_PPV_ARGS(¤tBrowseLocationItem));
}
else
{
// Default to pictures library
hr = ::SHCreateItemInKnownFolder(
FOLDERID_PicturesLibrary, 0, nullptr,
IID_PPV_ARGS(¤tBrowseLocationItem));
if (FAILED(hr))
{
// Set to top-level computer folder
hr = ::SHGetKnownFolderItem(
FOLDERID_ComputerFolder, static_cast<KNOWN_FOLDER_FLAG>(0),
nullptr, IID_PPV_ARGS(¤tBrowseLocationItem));
}
}
}
Listing 2 also shows that if the process is started with a command line parameter then it is used to create a shell item object by calling the SHCreateItemFromParsingName function. If the Annotator is called without a command line parameter, the Annotator obtains as the starting point the Pictures library, or failing that, the Computer folder. It is important to note that the shell item object can either be a file (the photo passed by the Browser) or a folder (Pictures library or Computer folder). Annotator uses this shell item to populate the editor pane (the equivalent of the Browser’s media pane) with all the photos in the specified folder, or if the shell item object is a file, the selected photo.
Other than the command line there is no other communication between Browser and Annotator. The Annotator is a separate process so the user can task switch to the Browser and continue to use it. The user can also create another instance of the Annotator. Note that since there is no direct communication with the Browser application if you change a photo in Annotator the cached image of the photo in Browser is not automatically updated to show the changes made in Annotator.
Debugging the Annotator
The multi-process architecture of the Browser and Annotator applications has implications for how you debug the overall solution. Since the Annotator is a separate process, if you are debugging the Browser and launch the Annotator, you cannot step into the Annotator code. Similarly, if you are debugging the Browser you cannot set breakpoints in the Annotator. Instead, if you wish to debug the Annotator you have three options.
First, you may specify that the Annotator process is started for debugging by clicking the Set as StartUp Project menu item in Solution Explorer (Figure 1). This option is useful since it allows you to place breakpoints anywhere in the process, including the WinMain function.
Figure 1 Setting the Annotator as the StartUp Project
The Annotator process will be started by, and run under, the debugger. This means that because it is not started by the Browser it will not have the file path passed to it by the Browser. If you want to test how the Annotator handles command line parameters then you have to give the full path to a photo as the Command Arguments property on the Annotator Debugging property page, as shown in Figure 2.
Figure 2 Specifying an image on the Annotator command line
Second, you may allow the Browser to start the Annotator process and then attach to the Annotator process with the Visual Studio debugger. To do this, select the Attach to Process menu item on the Debug menu. The Attach to Process dialog lists all the running processes on the computer. To attach the debugger double-click on the line for Annotator.exe as shown in Figure 3. This option is useful for attaching to any existing process, but any debugging can only be done from the point that you attach, which usually means that you cannot debug the WinMain function nor the code that creates and initializes the window.
Figure 3 Attaching the debugger to the Annotator process
The third option is not available for Visual C++ Express but is available for Visual Studio Professional and above: use Just In Time (JIT) debugging. To do this you put a call to the DebugBreak function (or the __debugbreak intrinsic) at the point in your program where you want debugging to start. However, before you can use these functions you have to tell Windows 7 to allow the function call to start the debugger. These functions cause a software exception (an interrupt, int 3) and by default, Windows 7 security will treat all exceptions as faults in the program and will handle this by searching for a solution online, so you must disable this action for the Annotator process. If the assert C runtime library (CRT) function is called with a false condition the __debugbreak intrinsic is called, so if you have asserts in your code and you want them handled through JIT debugging you must disable Windows 7 problem solving as explained below.
To disable problem solving open the Action Center in the Control Panel, expand the Maintenance section (Figure 4), and click on the Settings link.
Figure 4 Using the Action Center
This shows the Problem Reporting Settings page which lists the settings that will be used for all processes running on the computer (Figure 3). This dialog box also allows you to list the programs that will be excluded from problem reporting. Click on the Select programs to exclude from reporting link and then use the Add button to locate and select the debug build of the Annotator process (Figures 5 and 6).
Figure 5 Using the Problem Reporting Settings dialog
Figure 6 Excluding the Annotator process from problem reporting
Now whenever Annotator is run and there is a call to the DebugBreak function, Windows 7 will give you one or more dialog boxes similar to Figure 7. Then it will call the Visual Studio JIT Debugger, which will give you the option to start a new instance of Visual Studio or attach the debugger from a running instance.
Figure 7 Windows 7 problem reporting allowing you to debug a process
The advantage of JIT debugging is that you can debug any code where you can put a call to the DebugBreak function, however, you must make sure that you remove this code when you have finished testing the application.
Examining the Annotator UI
The Annotator process is started by double tapping a photo in the media pane of the Browser with your finger, or by double-clicking with the mouse. The Hilo Annotator user interface is shown in Figure 8. There are three main areas to consider: the image editor, the Ribbon, and the title bar.
Figure 8 The Hilo Annotator user interface
The image editor area takes up most of the application’s window. It behaves in a similar way to the media pane in the Browser. You can scroll left or right by using the mouse, the left and right arrow keys, or by dragging a photo with your finger on a touch screen. The default zoom level shows one complete photo and a preview of the photos to the left and right. The Annotator fades the left and right photos by first rendering the images and then drawing over them with a white linear gradient brush (where the gradient is the alpha channel changing from fully opaque to transparent).
Above the image editor is a Windows Ribbon control. This ribbon has two tabs, a menu, and a quick access toolbar. The Home tab has two groups and these have controls that allow you to crop, rotate, and draw on the photo in the image list. When you click either the Pencil or Crop buttons it selects the appropriate action that either allows you to draw with a pencil or crop the image. When you click the Rotate button the Annotator rotates the photo clockwise. The Rotate control also includes a dropdown menu giving additional transformation options: rotate counter-clockwise, mirror horizontally, or mirror vertically. The Color control is a standard control called a Dropdown Color Picker. When you click on this control a color swatch is displayed. The Size control is a drop down list that displays the four different pencil widths that are available. The View tab has three push button controls: zoom in, zoom out, and reset to 100% zoom.
The ribbon has two menus. The main menu is a dropdown menu control that is to the left of the Home ribbon tab. This menu has the following items: Open, Save, Save A Copy As, and Exit. The other menu is the Quick Access Toolbar and by default this is shown on the title bar. The Quick Access Toolbar has four controls. The first three are buttons that generate commands to save the photo, to undo an action, or redo an action. The fourth control is a dropdown menu that allows you to customize the toolbar: show or hide the other buttons, change the location of the toolbar, and minimize the ribbon.
The default position of the Quick Access Toolbar is on the title bar, but the customize menu has a menu item called Show below the Ribbon, when you click this item the toolbar moves to beneath the ribbon control, and the image list is resized accordingly. Clearly moving the toolbar to beneath the ribbon means that the area occupied by the photos is reduced. To give the image editor additional space you can select the Minimize the Ribbon item on the quick access toolbar. When the ribbon is minimized only the tab headers are shown. If you click on one of these headers the tab appears, but in front of the photo rather than above it.
Using the Annotator
When you start Annotator from the Start menu as a standalone process, you can use the Open menu item to select the photo to edit. When you double tap on a photo in the Browser it will start Annotator and the photo selected in the Browser will be opened for editing. You can draw on the photo with the pencil tool and use the Color and Size controls to select the type of pencil to use. When you select the pencil button the cursor changes to a pencil and you can then draw on the photo, as shown in Figure 9.
Figure 9 Drawing on a photo with the pencil tool
When you have changed a photo, you’ll see that the Undo button is enabled (the third icon from the left on the title bar in Figure 9). Annotator keeps a list of every change that you make to a photo and the Undo button allows you to undo one of these steps (the Redo button allows you to redo a step that you have undone).
When the Pencil button is selected the cursor becomes a pencil and when you use the stylus on a touch screen or move the mouse with the left button down the effect is to draw on the photo. If the Pencil button is deselected the cursor changes to an arrow and when you use the stylus or the mouse with the left button down the effect is to move the photo.This is useful if you have zoomed in so that the editor only shows part of the photo and you want to move to a different part.
In some cases you’ll want to crop a photo and to do this you use the crop tool. When you click the Crop button the cursor changes to cross-hairs and the entire photo is grayed out. You can now use the stylus or the mouse to draw the new boundaries of the photo, Figure 10.
Figure 10 Cropping a photo
The Annotator also allows you to rotate and mirror the photo. You do this through the items on the Rotate drop down menu. Figure 11, shows a rotation of 90° clockwise. When you perform one of these operations Annotator animates the operation so that you see the rotation or mirror occurring rather than simply the final result. In addition, the image is zoomed so that it completely fills the space available, so in Figure 11, since the cropped image is taller than it is wide after the rotation, it is zoomed out (and appears smaller) so that the height of the photo fits the height of the editor pane. If you rotate the photo by another 90° the image will now be wider than it is high so it will be zoomed so that the new height fills the height of the editor.
Figure 11 Rotating a photo
if you want to examine the details of a photo, there are various ways to zoom in or out. First you can hold down the CTRL key and use the mouse wheel, second you can use the Plus Sign and Minus Sign keys and third, you can use the Zoom In and Zoom Out buttons on the View tab shown in Figure 12. To zoom to 100% you can press the ESC key or click the 100% button.
Figure 12 Zooming a photo
When you exit the Annotator any changes that you have made are saved automatically, but you may also save the changes at any time by using the Save or Save A Copy As menu items. When you save a photo the Annotator makes a backup copy of the original photo in a subfolder called AnnotatorBackup.
Conclusion
The Hilo Annotator is the second application in the Hilo suite of applications and is used to edit photos in various ways. The Annotator provides tools to draw, crop, and rotate a photo and access to these tools is given through an instance of the Windows Ribbon control. Programming the Windows Ribbon control is the subject of the next chapter in the series.