How to Scale Bitmap Faster
| Tue, 2007-12-11 22:28 | |
|
Hi All, I want to constantly scale large bitmaps down to a displayable size. I have tried drawRect() in CFbsBitGc and it turns out it's taking too long. I also wrote my own scaling function and it's no better than drawRect(). My scale function is below. Can anyone please suggest a better algorithm to improve my scaling function or give me some hints on other ways to do it fast and right? Another question is, is it gonna be any better regarding to scale processing time if I play with jpg rather than bitmap? void CMyEngine::ScaleBitmap(CFbsBitmap& aSrcBmp, CFbsBitmap& aDstBmp) srcBmpUtil.SetPos(TPoint(srcXIndex, srcYIndex)); |
|






Forum posts: 1134
Why do you need to constantly rescale?
Is it possible for you to cache the scaled result and re-use it to save on scaling?
Scaling is always a heavy operation and those questions need answer first.
Then, onto making it faster...
1. Don't use TBitmapUtils,
It is very slow, because you will do a lot of unnecessary pixel conversions, and also a lot of unnecessary heap locking.
2. Generally, avoid any symbian draw calls
Unfortunately, high performance graphics programming is not symbians strong side.
3. Go low level
Use LockHeap, DataAddress and UnlockHeap to get raw access to the pixel buffer in memory.
This will need more knowledge on pixel formats, and some skill in modifying buffers efficiently.
The first is available on this forum by searching (or ask me) and the second is available in lots of example code out there, look for games and "democoder" examples from the 90s.
4. Optimize
For example, try avoid multiplications in your inner loops, calculate scanline offsets outside it (eg: y*data_stride)
Your algoritm looks pretty good though on the first look, though I don't have time to analyse it in detail
I think your main stumblestone might be pixel conversions going on inside TBitmapUtils when you read out and write back pixels. If the color depth is the same on dst and src, this is ofcourse unnecessary, but TBitmapUtils can't know this by its design.
Forum posts: 12
Hi Alh,
Thanks for you detailed reply. To answer your concerns. I am doing a zoom in/out on a bitmap. Instead of keeping scaled bitmaps at different zoom levels, I am scaling a portion of the original bitmap per zoom in/out call. That's where it requires constantly scale.
I will look into your suggestions and see what I can do to improve my app. Many thanks.
Forum posts: 1134
In that case, If I were you, I'd take a look at some DDA zooming routines, and zoom directly to the screen.
You can even kick in rotation in it with almost no extra overhead.
In a DDA you only have add:s in your inner loop, you calculate all deltas outside it, so it is almost as fast as a straight blit (except that you will always trash the cache when you start rotating, decreasing performance because of this)
I wouldn't be suprised if you could get it faster then the (non-scaling) symbian CFbsBitGc:BitBlt if you make sure to match color formats on src and dst
Forum posts: 17
There is also another option almost as good as the ones suggested if you have a HW-accelerated device.
You can use openGL to render the image to the screen (if the image changes often, you can feed a texture).
Forum posts: 1134
I would say using openGL is even better
Specially if the image is not changeing.
Then you should be able to add some bilinear filtering too, without losing too much frame rate, increasing the image quality a lot.
Though, very few devices have real HW support still, most just use Hybrids SW OpenGL that S60 has licensed.
Hybrids engine has very good performance for being a SW only implementation, but its not sure it will be faster for this specific usecase.
Its ofcourse possible to add bilinear filtering to a software DDA routine too, but it slows it down with a factor of at least 10 times.
And it don't add that much quality as long as you are constantly moving the image, then you hide a lot of the aliasing, at least if you calculate your roundings right
bi-linear is mostly needed if you are going to scale an image and show it in that size for an extended time. (and then you can pre-calc it and redisplay it)
Forum posts: 12
Thank you both, alh and miguelbl. Alh, your comments really open my eyes on ways to re-scale bitmaps. And miguelbl, if time allows, i will definitely give it a try to use openGL if the device supports 3d hardware acceleration.
I dont' really have much experience with dealing with pixels. so I am trying my best to get there. I have avoided using TBitmapUtil and instead use lockheap() and dataAddress(). It give me a improved processing time. But there's still something I have to figure out. my scaled bitmap is declining... I guess was wrong with the stride... or pixel size. I think i know where... I will post my findings later.
void CMyEngine::ScaleBitmap(CFbsBitmap& aSrcBmp, CFbsBitmap& aDstBmp)
{
TSize srcBmpSize = aSrcBmp.SizeInPixels();
TSize dstBmpSize = aDstBmp.SizeInPixels();
TInt intPart2 = srcBmpSize.iHeight / dstBmpSize.iHeight;
TInt fractPart2 = srcBmpSize.iHeight % dstBmpSize.iHeight;
TInt e2 = 0;
TInt intPart = srcBmpSize.iWidth / dstBmpSize.iWidth;
TInt fractPart = srcBmpSize.iWidth % dstBmpSize.iWidth;
TInt e = 0;
aSrcBmp.LockHeap(ETrue);
aDstBmp.LockHeap(ETrue);
TUint16* src = (TUint16*) aSrcBmp.DataAddress();
TUint16* dst = (TUint16*) aDstBmp.DataAddress();
TUint16* origin = src;
TInt srcYIndex = 0;
for(TInt i = 0; i < dstBmpSize.iHeight; i++)
{
for(TInt j = 0; j < dstBmpSize.iWidth; j++)
{
Mem::Copy(dst, src, 2);
src += intPart;
dst++;
e += fractPart;
if( e >= dstBmpSize.iWidth)
{
e -= dstBmpSize.iWidth;
src++;
}
}
srcYIndex += intPart2;
// dst = (TUint16*) aDstBmp.DataAddress() + i * dstBmpSize.iWidth;
e2 += fractPart2;
if(e2 >= dstBmpSize.iHeight)
{
e2 -= dstBmpSize.iHeight;
srcYIndex++;
}
src = origin + srcYIndex * srcBmpSize.iWidth;
}
aSrcBmp.UnlockHeap();
aDstBmp.UnlockHeap();
}
Forum posts: 12
I found my problem. As discussed in this topic, http://newlc.com/topic-17576, I didn't call scanLineLength() to find out the number of bytes a scan line in the picture is as I jumped around to pick the pixel I want to copy. Now the scaled bmp looks ok.