MiniGUI RGB565大小端显示问题

问题描述

最近遇到一个framebuffer是RGB565(16bpp),minigui显示不正常的问题,显示现象如下
原始图片
MiniGUI RGB565大小端显示问题_第1张图片
屏幕上显示的效果
MiniGUI RGB565大小端显示问题_第2张图片
然后直接显示红绿蓝三色的图片
MiniGUI RGB565大小端显示问题_第3张图片
在屏幕上显示的效果
MiniGUI RGB565大小端显示问题_第4张图片
在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);
}

你可能感兴趣的:(Linux,问题锦集,MiniGUI)