Monday, January 11, 2010

BREW Display Rendering - IBitmap Faster way?


I was trying to build a user interface which works faster on Touch devices. Unlike the Non touch devices, the display need to be comparatively faster to have a good user experience.

Following is the interface i was planning to be tested for Various options. The application is a Quiz application which presents user with a set of images and user can guess the identity of the image such as name. On Tapping the screen would show the identity of the image with a semi transparency layer background on top of the current image. When the user swipe his finger through the screen, the application would start moving the next image to the display while the current image goes off the display. This transition needed to be smooth.

Given below is the screen shots of this effect. The Quiz app presents a set of country flags.


Following are the items i thought to try.

  • Using IBitmap
  • Directly paint on the screen without using IBitmap
Using IBitmap the following was the one in plan.
- Create a IBitmap of double the screen width and same Screen height.
- When displaying image 1, draw its contents to the first half of the IBitmap
- When the user start swiping the screen, draw the second image to the second half of the IBitmap.
- When the moving happens, Bitblt the IBitmap to the Device bitmap so that it get rendered to the screen.

The above is converted to the following.
if(IDisplay_GetDeviceBitmap(pMe->a.m_pIDisplay,(IBitmap**)&pMe->m_pIBitMap) == SUCCESS)
{
//Got the Device bitmap successfully
IBitmap_GetInfo(pMe->m_pIBitMap,&bmInfo,sizeof(AEEBitmapInfo));
if(IBitmap_CreateCompatibleBitmap(pMe->m_pIBitMap,(IBitmap**)&pMe->m_pIBitMap2,bmInfo.cx *2,bmInfo.cy) == SUCCESS)
{
//Compatible Bitmap2 is created
}
else
{
//Compatible Bitmap Creation failed.
}
}
else
{
//Getting the Device bit map failed
}

The following function fills the first half of the IBitmap

static void QuizApp_PaintScreen(QuizAppStruct *pMe)
{
DBGPRINTF("[QuizApp_PaintScreen] Entering");
// check the app mode here and draw the screen accordingly.
if(pMe->m_pIBitMap != NULL && pMe->m_pIBitMap2 != NULL )
{
DBGPRINTF("[QuizApp_PaintScreen] Destination is set to NULL");
IDisplay_SetDestination(pMe->a.m_pIDisplay,pMe->m_pIBitMap2);
}
QuizApp_PaintQuizImage(pMe);
if(pMe->m_pIBitMap != NULL && pMe->m_pIBitMap2 != NULL )
{
IDisplay_SetDestination(pMe->a.m_pIDisplay,NULL);
IBitmap_BltIn(pMe->m_pIBitMap,0,0,pMe->m_DeviceInfo.cxScreen,pMe->m_DeviceInfo.cyScreen,pMe->m_pIBitMap2,0,0,AEE_RO_TRANSPARENT);
}
IDISPLAY_Update(pMe->m_pIDisplay);
}

The following function is where the second half of the Bitmap buffer is filled. This happens when there is a swipe detected. In this case, Moving left.

boolean QuizApp_OnMovingLeft(void *pi, AEEEvent eCode, uint16 wParam, uint32 dwParam)
{
IImage* pImg;
AEEImageInfo imgInfo;
QuizAppStruct *pMe = (QuizAppStruct*)pi;
DBGPRINTF("[QuizApp_OnMovingLeft] == On Moving Right == ");
if(pMe->m_MovingFirstTime == TRUE)
{
DBGPRINTF("[QuizApp_OnMovingLeft] == On Moving Right FIRST time == ");
pMe->m_MovingFirstTime = FALSE;
pMe->m_ImgIdx ++ ;
if(pMe->m_ImgIdx > pMe->m_TotalImages)
{
pMe->m_ImgIdx = 0;
}
pImg = ISHELL_LoadImage(pMe->a.m_pIShell,pMe->m_ImgPaths[pMe->m_ImgIdx]);
DBGPRINTF("[QuizApp_OnMovingLeft] Img path is %s",pMe->m_ImgPaths[pMe->m_ImgIdx]);
if(pImg != NULL)
{
IImage_GetInfo(pImg,&imgInfo);
pMe->m_ImgDrawRect.x = (pMe->m_DeviceInfo.cxScreen/2)- (imgInfo.cx/2);
pMe->m_ImgDrawRect.y = (pMe->m_DeviceInfo.cyScreen/2)- (imgInfo.cy/2);
IDisplay_SetDestination(pMe->a.m_pIDisplay,pMe->m_pIBitMap2);
IImage_Draw(pImg,pMe->m_DeviceInfo.cxScreen + pMe->m_ImgDrawRect.x,pMe->m_ImgDrawRect.y);
IImage_Release(pImg);
}
pMe->m_dx = 0;

}
else
{
DBGPRINTF("[QuizApp_OnMovingLeft] == On Moving Right NOT FIRST time == ");
pMe->m_dx+= 10;
}
QuizApp_PaintScreenAnimated(pMe);
}

PaintScreenAnimated function paints the contents on to the buffer to the screen without having worries to repaint with all the logic.

static void QuizApp_PaintScreenAnimated(QuizAppStruct *pMe)
{
DBGPRINTF("[QuizApp_PaintScreenAnimated] Entering");
if(pMe->m_pIBitMap != NULL && pMe->m_pIBitMap2 != NULL )
{
DBGPRINTF("[QuizApp_PaintScreenAnimated] Destination is set to NULL");
IDisplay_SetDestination(pMe->a.m_pIDisplay,pMe->m_pIBitMap2);
}
if(pMe->m_pIBitMap != NULL && pMe->m_pIBitMap2 != NULL )
{
IDisplay_SetDestination(pMe->a.m_pIDisplay,NULL);
DBGPRINTF("[QuizApp_PaintScreenAnimated] = dx val is %d",pMe->m_dx);
IBitmap_BltIn(pMe->m_pIBitMap,0,0,pMe->m_DeviceInfo.cxScreen,pMe->m_DeviceInfo.cyScreen,pMe->m_pIBitMap2,pMe->m_dx,0,AEE_RO_TRANSPARENT);
}
IDISPLAY_Update(pMe->m_pIDisplay);
}

This I felt to be not having problems and was working well without much flickering or no flickering.