While working on my new menu, I needed someway of improving the image quality of downscaled images. The easiest way for reducing the pixeling that occurs in downscaling is pixel smoothing, or averaging. However this can require a lot of power and end up being really slow if one decides to implement such feature the easy way i.e. unpacking color channels, averaging, then repacking.
I initially had implemented the easy method to my library and was appalled at the drastic performance hit. At 396mhz, I could not stream mp3, and redraw the album cover at full speed with the smoothing enabled. This new algorithm, is drastically faster, almost to the point where it's performance hit is negligible on the lowest CPU speed (60mhz), at least in comparison to looping through all the pixels to draw an image.
Here is an image to illustrate the results:
Far right, image with no smoothing: full speed on lowest CPU speed
Far left, image with easy smoothing solution, lags on 396mhz cpu speed
Middle image, new smoothing function, full speed on 60mhz cpu speed
Now for the code! It is relatively easy to add to any project:
And that is all there is too it. Hopefully this becomes of use to others as it has been for me.
PS. I have tried applying it to NDSSFC emulator, but the performance hit of looping through each pixel is too high to bother adding this smoothing functionality to.
I initially had implemented the easy method to my library and was appalled at the drastic performance hit. At 396mhz, I could not stream mp3, and redraw the album cover at full speed with the smoothing enabled. This new algorithm, is drastically faster, almost to the point where it's performance hit is negligible on the lowest CPU speed (60mhz), at least in comparison to looping through all the pixels to draw an image.
Here is an image to illustrate the results:
Far right, image with no smoothing: full speed on lowest CPU speed
Far left, image with easy smoothing solution, lags on 396mhz cpu speed
Middle image, new smoothing function, full speed on 60mhz cpu speed
Now for the code! It is relatively easy to add to any project:
Code:
typedef struct SmoothPixel{
u16 *buf;//pointer to image buffer
int x, y, wd, ht;//pixel you are drawing X, Y, image width, height
char corners;//set to non-zero for 8 pixel averaging, or 0 for 4 pixels
}SmoothPixel;
//these will average 15 bit colors, without unpacking
//method found from http://www.slack.net/~ant/info/rgb_mixing.html
#define PIXAVG_R5G5B5(a,b) ((a + b + ((a ^ b) & 0x0421)) >> 1)
//this is used for weighting the corner pixel averages
#define PIXAVG_R5G5B5_HALF(a,b) ((a + b + ((a ^ b) & 0x0421)) >> 2)
//only one branch statement used, could be split into two functions if necessary (one for 4 pixel avg, 8 pixel avg)
u16 BAG_Effects_PixSmoothing(const SmoothPixel *smooth){
register int curColor[9];
register int x = smooth->x, y = smooth->y, wd = smooth->wd, ht = smooth->ht;
//center pixel
curColor[0] = smooth->buf[x + y * wd];
//left pixel
x--;
x += (x < 0);
curColor[1] = smooth->buf[x + y * wd];
//right pixel
x+=2;
x -= (x > wd);
curColor[2] = smooth->buf[x + y * wd];
//reset x to main pix
x--;
//up pixel
y--;
y += (y < 0);
curColor[3] = smooth->buf[x + y * wd];
//down pixel
y+=2;
y -= (y > ht);
curColor[4] = smooth->buf[x + y * wd];
if(smooth->corners){
//reset pos to center pixel
y--;
//left corners
x--;
x += (x < 0);
//left top
y--;
y += (y < 0);
curColor[5] = smooth->buf[x + y * wd];
//left bottom
y+=2;
y -= (y > ht);
curColor[6] = smooth->buf[x + y * wd];
//right corners
x+=2;
x -= (x > wd);
//right bottom
curColor[7] = smooth->buf[x + y * wd];
//right yop
y-=2;
y += (y < 0);
curColor[8] = smooth->buf[x + y * wd];
//8 pixel average(lol)
return PIXAVG_R5G5B5(curColor[0], PIXAVG_R5G5B5(curColor[1], PIXAVG_R5G5B5(curColor[2], PIXAVG_R5G5B5(curColor[3],
//last normal pixel.......now the corner pixels
PIXAVG_R5G5B5(curColor[4], PIXAVG_R5G5B5_HALF(curColor[5], PIXAVG_R5G5B5_HALF(curColor[6],
PIXAVG_R5G5B5_HALF(curColor[7], curColor[8]))))))));
}
//4 pixel average
return PIXAVG_R5G5B5(curColor[0], PIXAVG_R5G5B5(curColor[1], PIXAVG_R5G5B5(curColor[2], PIXAVG_R5G5B5(curColor[3], curColor[4]))));
}
And that is all there is too it. Hopefully this becomes of use to others as it has been for me.
PS. I have tried applying it to NDSSFC emulator, but the performance hit of looping through each pixel is too high to bother adding this smoothing functionality to.