最近遇到一个framebuffer是RGB565(16bpp),minigui显示不正常的问题,显示现象如下
原始图片
屏幕上显示的效果
然后直接显示红绿蓝三色的图片
在屏幕上显示的效果
在RGB565中
颜色 | 值 |
---|---|
RED | 0xF800 |
GREEN | 0x07E0 |
BLUE | 0x001F |
分析原因发现是和LCD有关,在发送RGB数据的时候,驱动发送R分量是发送0x00F8,G分量0xE007,B分量0x1F00,也就是说大小端进行了交换,所以需要把源数据的像素值的大小端进行交换
主要是LoadBitmapFromFile加载图片函数,FillBoxWithBitmap填充图片函数
//libminigui-gpl-3.2/src/newgdi/readbmp.c
//图片加载函数
int GUIAPI LoadBitmapEx (HDC hdc, PBITMAP bmp, MG_RWops* area, const char* ext)
{
//...
ret = LoadMyBitmapSL (area, load_info, &my_bmp, cb_load_bitmap_sl, &info);
//判断是16bpp的并且图片不带alpha的时候才进行转换,因为带透明的图在alpha blend的时候会出现blend颜色不对的现象
if (!(bmp->bmType & BMP_TYPE_ALPHA) && bmp->bmBitsPerPixel == 16) {
int x, y;
Uint16 *srcrow = (Uint16 *) bmp->bmBits;
//循环遍历每一个像素点
for (y = bmp->bmHeight; y; --y) {
for (x = bmp->bmWidth; x; --x) {
//大小端转换
*srcrow = ((*srcrow & 0xff00) >> 8) | ((*srcrow & 0xff) << 8);
srcrow++;
}
}
}
CleanupMyBitmapSL (&my_bmp, load_info);
return ret;
}
//libminigui-gpl-3.2/src/newgal/surface.c
//源带透明度的叠加函数
static int _PutBoxAlpha (GAL_Surface* dst, BYTE* dstrow, BYTE* srcrow, Uint32 w, Uint32 h, BITMAP* box)
{
//...
//循环BLEND每一个像素值
while ( h-- ) {
dstpixels = dstrow;
srcpixels = srcrow;
alpha_mask_index = alpha_mask_row;
DUFFS_LOOP(
{
Uint32 pixel;
unsigned sR;
unsigned sG;
unsigned sB;
unsigned sA;
unsigned dR;
unsigned dG;
unsigned dB;
//SRC带透明度的大小端没有转换,所以不需还原
DISEMBLE_RGB_ALPHA (srcpixels, bpp, dstfmt, pixel, sR, sG, sB);
//DST是经过大小端转换的,所以需要再还原
DISEMBLE_RGB (dstpixels, bpp, dstfmt, pixel, dR, dG, dB);
sA = box->bmAlphaMask[alpha_mask_index];
//只有还原的像素值才能正常BLEND
ALPHA_BLEND (sR, sG, sB, sA, dR, dG, dB);
//BLEND之后再进行大小端交换才能显示正常
ASSEMBLE_RGBA (dstpixels, bpp, dstfmt, dR, dG, dB, alpha);
dstpixels += bpp;
srcpixels += bpp;
alpha_mask_index++;
},
w);
srcrow += box->bmPitch;
dstrow += dst->pitch;
alpha_mask_row += alpha_mask_pitch;
}
}
//SRC获取RGB分量
//libminigui-gpl-3.2/src/newgal/blit.h
#define DISEMBLE_RGB_ALPHA(buf, bpp, fmt, pixel, r, g, b) \
do { \
switch (bpp) { \
case 2: \
pixel = *((Uint16 *)(buf)); \
break; \
default: \
pixel = 0; /* prevent gcc from complaining */ \
break; \
} \
RGB_FROM_PIXEL(pixel, fmt, r, g, b); \
} while(0)
//DST获取RGB分量
//libminigui-gpl-3.2/src/newgal/blit.h
#define DISEMBLE_RGB(buf, bpp, fmt, pixel, r, g, b) \
do { \
switch (bpp) { \
case 2: \
pixel = *((Uint16 *)(buf)); \
//把交换的大小端再还原
pixel = ((pixel & 0xff00) >> 8) | ((pixel & 0xff) << 8); \
break; \
default: \
pixel = 0; /* prevent gcc from complaining */ \
break; \
} \
RGB_FROM_PIXEL(pixel, fmt, r, g, b); \
} while(0)
//libminigui-gpl-3.2/src/newgal/blit.h
//根据源alpha值混合两个像素的RGB值
#define ALPHA_BLEND(sR, sG, sB, A, dR, dG, dB) \
do { \
dR = (((sR-dR)*(A))>>8)+dR; \
dG = (((sG-dG)*(A))>>8)+dG; \
dB = (((sB-dB)*(A))>>8)+dB; \
} while(0)
//libminigui-gpl-3.2/src/newgal/blit.h
//把BLEND好的像素值写到buffer上
#define ASSEMBLE_RGBA(buf, bpp, fmt, r, g, b, a) \
{ \
switch (bpp) { \
case 2: { \
Uint16 pixel; \
PIXEL_FROM_RGBA(pixel, fmt, r, g, b, a); \
//把BLEND好的像素值大小端交换
pixel = ((pixel & 0xff00) >> 8) | ((pixel & 0xff) << 8); \
*((Uint16 *)(buf)) = pixel; \
} \
break; \
} \
}
还有一个填充矩形的函数FillBox,当然还有其他一些画线条的函数需要修改
//libminigui-gpl-3.2/src/newgal/surface.c
//填充颜色函数
int GAL_FillRect(GAL_Surface *dst, const GAL_Rect *dstrect, Uint32 color)
{
//...
switch (dst->format->BytesPerPixel) {
case 2:
//把颜色的大小端交换一下即可
color = ((color & 0xff00) >> 8) | ((color & 0xff) << 8);
for ( y=my_dstrect.h; y; --y ) {
Uint16 *pixels = (Uint16 *)row;
Uint16 c = color;
Uint32 cc = (Uint32)c << 16 | c;
int n = my_dstrect.w;
if((unsigned long)pixels & 3) {
*pixels++ = c;
n--;
}
if(n >> 1)
GAL_memset4(pixels, cc, n >> 1);
if(n & 1)
pixels[n - 1] = c;
row += dst->pitch;
}
break;
}
}
还有一个好的方法,以上的修改全都不需要,直接修改获取颜色的掩码,交换Rmask、Gmask、Bmask的大小端,但是这种方法显示的颜色有点失真
//libminigui-gpl-3.2/src/newgal/fbcon/fbvideo.c
//设置fbcon引擎的一些配置
static GAL_Surface *FB_SetVideoMode(_THIS, GAL_Surface *current,
int width, int height, int bpp, Uint32 flags)
{
//...
Rmask = 0;
for ( i=0; i<vinfo.red.length; ++i ) {
Rmask <<= 1;
Rmask |= (0x00000001<<vinfo.red.offset);
}
Gmask = 0;
for ( i=0; i<vinfo.green.length; ++i ) {
Gmask <<= 1;
Gmask |= (0x00000001<<vinfo.green.offset);
}
Bmask = 0;
for ( i=0; i<vinfo.blue.length; ++i ) {
Bmask <<= 1;
Bmask |= (0x00000001<<vinfo.blue.offset);
}
Amask = 0;
for ( i=0; i<vinfo.transp.length; ++i ) {
Amask <<= 1;
Amask |= (0x00000001<<vinfo.transp.offset);
}
//这里交换
Rmask = ((Rmask & 0xff00) >> 8) | ((Rmask & 0xff) << 8);
Gmask = ((Gmask & 0xff00) >> 8) | ((Gmask & 0xff) << 8);
Bmask = ((Bmask & 0xff00) >> 8) | ((Bmask & 0xff) << 8);
Amask = ((Amask & 0xff00) >> 8) | ((Amask & 0xff) << 8);
}
static int FB_VideoInit(_THIS, GAL_PixelFormat *vformat)
{
//...
for ( i=0; i<vinfo.red.length; ++i ) {
vformat->Rmask <<= 1;
vformat->Rmask |= (0x00000001<<vinfo.red.offset);
}
for ( i=0; i<vinfo.green.length; ++i ) {
vformat->Gmask <<= 1;
vformat->Gmask |= (0x00000001<<vinfo.green.offset);
}
for ( i=0; i<vinfo.blue.length; ++i ) {
vformat->Bmask <<= 1;
vformat->Bmask |= (0x00000001<<vinfo.blue.offset);
}
for ( i=0; i<vinfo.transp.length; ++i ) {
vformat->Amask <<= 1;
vformat->Amask |= (0x00000001<<vinfo.transp.offset);
}
//这里交换
vformat->Rmask = ((vformat->Rmask & 0xff00) >> 8) | ((vformat->Rmask & 0xff) << 8);
vformat->Gmask = ((vformat->Gmask & 0xff00) >> 8) | ((vformat->Gmask & 0xff) << 8);
vformat->Bmask = ((vformat->Bmask & 0xff00) >> 8) | ((vformat->Bmask & 0xff) << 8);
vformat->Amask = ((vformat->Amask & 0xff00) >> 8) | ((vformat->Amask & 0xff) << 8);
}