上一篇:MiniGUI源码分析--Helloworld(3):消息概览
从本章开始,将逐步介绍MiniGUI部分GDI
GDI(Graphics Device Interface)是对绘图接口的描述,是MiniGUI的核心组成部分之一。
GDI虽然是GUI的基础,但是,它实际上不太适合作为GUI的组成部分之一的。因为GUI的核心功能在用户交互上,而不是如何绘制上。对MiniGUI来说,GDI是成也萧何败萧何:MiniGUI内置的GDI使得MiniGUI更紧凑和高效,但是由于其功能受限,使得MiniGUI在绘图精细度上,以及和其他优秀的界面库结合上,存在巨大的缺陷。由此也造成MiniGUI发展受限,无法赶上时代的潮流。
MiniGUI的GDI分成GAL、DC和Surface、位图管理、字体管理以及图元绘制(包括矩形、圆形的绘制和填充),可以用示意图表示:
GDI中涉及很多绘图的算法,这些可以通过查阅计算机图形学相关的书籍就可以了解,这不是我们的重点。我们的重点,在于了解他对显存的管理和操作。
所以,我们从Surface和DC说起。
Surface是管理显存的一个重要对象。它负责管理显存的大小、色深等信息。而且,可以把一块普通的内存看做一个显存,这样,我们就可以在内存中首先构造出该对象。
首先,看它的定义:(src/include/newgal.h)
typedef struct GAL_Surface { Uint32 flags; /* Read-only */ GAL_PixelFormat *format; /* Read-only */ void *video; /* Read-only */ int w, h; /* Read-only */ Uint32 pitch; /* Read-only */ void *pixels; /* Read-write */ int offset; /* Private */ /* Hardware-specific surface info */ struct private_hwdata *hwdata; /* clipping information */ GAL_Rect clip_rect; /* Read-only */ /* info for fast blit mapping to other surfaces */ struct GAL_BlitMap *map; /* Private */ /* format version, bumped at every change to invalidate blit maps */ unsigned int format_version; /* Private */ /* Reference count -- used when freeing surface */ int refcount; /* Read-mostly */ } GAL_Surface
format:表示像素的格式,是非常重要的成员
pxiels:是像素的地址指针
map:这是对像素做合并的时候,使用的合并方法。
其中,flags变量来标记surface的一些属性,例如,是硬件surface(surface的pixel指向的内存是显存)或者是软surface(pixel指向普通的内存)等。
由于硬surface和软surface没有太大的区别,所以我们可以先忽略这一部分。
Surface的重要的操作有:创建、删除、Surface的像素合并(BitBlt)。
Surface相关的操作都在src/newgal/surface.c中
首先,看Surface的创建过程。Surface的创建,主要是通过GAL_CreateRGBSurface函数创建的。它初始化一个Surface对象。先看下该函数的实现。
GAL_Surface * GAL_CreateRGBSurface (Uint32 flags, int width, int height, int depth, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask) { GAL_VideoDevice *video = current_video; GAL_VideoDevice *this = current_video; GAL_Surface *screen; GAL_Surface *surface; /* Check to see if we desire the surface in video memory */ if ( video ) { screen = GAL_PublicSurface; } else { screen = NULL; } if ( screen && ((screen->flags&GAL_HWSURFACE) == GAL_HWSURFACE) ) { if ( (flags&(GAL_SRCCOLORKEY|GAL_SRCALPHA)) != 0 ) { flags |= GAL_HWSURFACE; } if ( (flags & GAL_SRCCOLORKEY) == GAL_SRCCOLORKEY ) { if ( ! current_video->info.blit_hw_CC ) { flags &= ~GAL_HWSURFACE; } } if ( (flags & GAL_SRCALPHA) == GAL_SRCALPHA ) { if ( ! current_video->info.blit_hw_A ) { flags &= ~GAL_HWSURFACE; } } } else { flags &= ~GAL_HWSURFACE; } /* Allocate the surface */ surface = (GAL_Surface *)malloc(sizeof(*surface)); if ( surface == NULL ) { GAL_OutOfMemory(); return(NULL); } if ((flags & GAL_HWSURFACE) == GAL_HWSURFACE) surface->video = current_video; else surface->video = NULL; surface->flags = GAL_SWSURFACE; if ( (flags & GAL_HWSURFACE) == GAL_HWSURFACE ) { depth = screen->format->BitsPerPixel; Rmask = screen->format->Rmask; Gmask = screen->format->Gmask; Bmask = screen->format->Bmask; Amask = screen->format->Amask; } surface->format = GAL_AllocFormat(depth, Rmask, Gmask, Bmask, Amask); if ( surface->format == NULL ) { free(surface); return(NULL); } if ( Amask ) { surface->flags |= GAL_SRCALPHA; } surface->w = width; surface->h = height; surface->pitch = GAL_CalculatePitch(surface); surface->pixels = NULL; surface->offset = 0; surface->hwdata = NULL; surface->map = NULL; surface->format_version = 0; GAL_SetClipRect(surface, NULL); /* Get the pixels */ if ( ((flags&GAL_HWSURFACE) == GAL_SWSURFACE) || (video->AllocHWSurface(this, surface) < 0) ) { if ( surface->w && surface->h ) { surface->pixels = malloc(surface->h*surface->pitch); if ( surface->pixels == NULL ) { GAL_FreeSurface(surface); GAL_OutOfMemory(); return(NULL); } /* This is important for bitmaps */ memset(surface->pixels, 0, surface->h*surface->pitch); surface->flags &= ~GAL_HWSURFACE; } } /* Allocate an empty mapping */ surface->map = GAL_AllocBlitMap(); if ( surface->map == NULL ) { GAL_FreeSurface(surface); return(NULL); } /* The surface is ready to go */ surface->refcount = 1; #ifdef CHECK_LEAKS ++surfaces_allocated; #endif return(surface); }该函数创建一个GAL_Surface结构体对象,并初始化了其成员。这其中,我们需要重点注意对format成员和map成员的初始化。
对format成员,它调用了GAL_AllocFormat函数。首先,看下GAL_PixelFormat的定义:
typedef struct GAL_PixelFormat { GAL_Palette *palette; BOOL DitheredPalette; Uint8 BitsPerPixel; Uint8 BytesPerPixel; Uint8 Rloss; Uint8 Gloss; Uint8 Bloss; Uint8 Aloss; Uint8 Rshift; Uint8 Gshift; Uint8 Bshift; Uint8 Ashift; Uint32 Rmask; Uint32 Gmask; Uint32 Bmask; Uint32 Amask; /* RGB color key information */ gal_pixel colorkey; /* Alpha value information (per-surface alpha) */ gal_uint8 alpha; } GAL_PixelFormat;palette成员主要用在8位及以下的像素中,不是考虑的重点。
BitsPerPixel是一个像素占用的位数,可以选择的是1,2,4,8,16,24,32
BytesPerPixel是一个像素占用的字节数,可以是,1,2,4等
下面的成员,按照R、G、B、A分成loss, shift, mask三组,它是相对于一个分量8位来说的,分别表示RGBA分量所丢失的位数、在像素中的偏移位数和掩码值
例如,对于16位以565格式保存的RGB像素格式来说,其高6位被R分量占用,Rloss为3,Rshift为11,Rmask则为0xF800; 中间6位被G分量占用,Gloss为2,GShift为5,Gmask为0x07E0;B分量占用剩余的5位,其中Bloss为3,Bshift为0,Bmask为0x001F,表示如下:
RRRR RGGG GGGB BBBB
用这些信息,就可以通过RGB分量计算出一个像素的值,如:
Uint32 pixv = (r >> format->Rloss) << format->Rshift | (g >> format->Gloss) << format->Gshift | (b >> format->Bloss) << format->Bshift | ((a >> format->Aloss) << format->Ashift & format->Amask);
GAL_PixelFormat *GAL_AllocFormat(int bpp, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask) { GAL_PixelFormat *format; Uint32 mask; /* Allocate an empty pixel format structure */ format = malloc(sizeof(*format)); if ( format == NULL ) { GAL_OutOfMemory(); return(NULL); } memset(format, 0, sizeof(*format)); format->alpha = GAL_ALPHA_OPAQUE; /* Set up the format */ format->BitsPerPixel = bpp; format->BytesPerPixel = (bpp+7)/8; format->DitheredPalette = FALSE; switch (bpp) { case 1: /* Create the 2 color black-white palette */ format->palette = (GAL_Palette *)malloc( sizeof(GAL_Palette)); if ( format->palette == NULL ) { GAL_FreeFormat(format); GAL_OutOfMemory(); return(NULL); } (format->palette)->ncolors = 2; (format->palette)->colors = (GAL_Color *)malloc( (format->palette)->ncolors*sizeof(GAL_Color)); if ( (format->palette)->colors == NULL ) { GAL_FreeFormat(format); GAL_OutOfMemory(); return(NULL); } format->palette->colors[0].r = 0xFF; format->palette->colors[0].g = 0xFF; format->palette->colors[0].b = 0xFF; format->palette->colors[1].r = 0x00; format->palette->colors[1].g = 0x00; format->palette->colors[1].b = 0x00; format->Rloss = 8; format->Gloss = 8; format->Bloss = 8; format->Aloss = 8; format->Rshift = 0; format->Gshift = 0; format->Bshift = 0; format->Ashift = 0; format->Rmask = 0; format->Gmask = 0; format->Bmask = 0; format->Amask = 0; break; case 4: /* Create the 16 color VGA palette */ format->palette = (GAL_Palette *)malloc( sizeof(GAL_Palette)); if ( format->palette == NULL ) { GAL_FreeFormat(format); GAL_OutOfMemory(); return(NULL); } (format->palette)->ncolors = 16; (format->palette)->colors = (GAL_Color *)malloc( (format->palette)->ncolors*sizeof(GAL_Color)); if ( (format->palette)->colors == NULL ) { GAL_FreeFormat(format); GAL_OutOfMemory(); return(NULL); } /* Punt for now, will this ever be used? */ memset((format->palette)->colors, 0, (format->palette)->ncolors*sizeof(GAL_Color)); /* Palettized formats have no mask info */ format->Rloss = 8; format->Gloss = 8; format->Bloss = 8; format->Aloss = 8; format->Rshift = 0; format->Gshift = 0; format->Bshift = 0; format->Ashift = 0; format->Rmask = 0; format->Gmask = 0; format->Bmask = 0; format->Amask = 0; break; case 8: /* Create an empty 256 color palette */ format->palette = (GAL_Palette *)malloc( sizeof(GAL_Palette)); if ( format->palette == NULL ) { GAL_FreeFormat(format); GAL_OutOfMemory(); return(NULL); } (format->palette)->ncolors = 256; (format->palette)->colors = (GAL_Color *)malloc( (format->palette)->ncolors*sizeof(GAL_Color)); if ( (format->palette)->colors == NULL ) { GAL_FreeFormat(format); GAL_OutOfMemory(); return(NULL); } memset((format->palette)->colors, 0, (format->palette)->ncolors*sizeof(GAL_Color)); /* Palettized formats have no mask info */ format->Rloss = 8; format->Gloss = 8; format->Bloss = 8; format->Aloss = 8; format->Rshift = 0; format->Gshift = 0; format->Bshift = 0; format->Ashift = 0; format->Rmask = 0; format->Gmask = 0; format->Bmask = 0; format->Amask = 0; break; default: /* No palette, just packed pixel info */ format->palette = NULL; format->Rshift = 0; format->Rloss = 8; if ( Rmask ) { for ( mask = Rmask; !(mask&0x01); mask >>= 1 ) ++format->Rshift; for ( ; (mask&0x01); mask >>= 1 ) --format->Rloss; } format->Gshift = 0; format->Gloss = 8; if ( Gmask ) { for ( mask = Gmask; !(mask&0x01); mask >>= 1 ) ++format->Gshift; for ( ; (mask&0x01); mask >>= 1 ) --format->Gloss; } format->Bshift = 0; format->Bloss = 8; if ( Bmask ) { for ( mask = Bmask; !(mask&0x01); mask >>= 1 ) ++format->Bshift; for ( ; (mask&0x01); mask >>= 1 ) --format->Bloss; } format->Ashift = 0; format->Aloss = 8; if ( Amask ) { for ( mask = Amask; !(mask&0x01); mask >>= 1 ) ++format->Ashift; for ( ; (mask&0x01); mask >>= 1 ) --format->Aloss; } format->Rmask = Rmask; format->Gmask = Gmask; format->Bmask = Bmask; format->Amask = Amask; break; } /* Calculate some standard bitmasks, if necessary * Note: This could conflict with an alpha mask, if given. */ if ( (bpp > 8) && !format->Rmask && !format->Gmask && !format->Bmask ) { /* R-G-B */ if ( bpp > 24 ) bpp = 24; format->Rloss = 8-(bpp/3); format->Gloss = 8-(bpp/3)-(bpp%3); format->Bloss = 8-(bpp/3); format->Rshift = ((bpp/3)+(bpp%3))+(bpp/3); format->Gshift = (bpp/3); format->Bshift = 0; format->Rmask = ((0xFF>>format->Rloss)<<format->Rshift); format->Gmask = ((0xFF>>format->Gloss)<<format->Gshift); format->Bmask = ((0xFF>>format->Bloss)<<format->Bshift); } return(format); }代码很长,但是并不复杂。switch部分重点看default分支的代码。其余部分的代码,是当像素占用的字节数在一个字节以内的情况。这种情况下注意是通过调色板实现的,现在不常用了,不重点考虑。
default分支的代码,注意通过Rmask、Gmask、Bmask和Amask的值,计算得到loss和shift的,算法很简单。不再赘述。
另外一部分重要的代码是map成员变量的初始化。因为它关系到两个surface的混合。首先看下GAL_BitMap结构的定义
/* Blit mapping definition */ typedef struct GAL_BlitMap { GAL_Surface *dst; int identity; Uint8 *table; GAL_blit hw_blit; GAL_blit sw_blit; struct private_hwaccel *hw_data; struct private_swaccel *sw_data; /* the version count matches the destination; mismatch indicates an invalid mapping */ unsigned int format_version; } GAL_BlitMap;
typedef int (*GAL_blit)(struct GAL_Surface *src, GAL_Rect *srcrect, struct GAL_Surface *dst, GAL_Rect *dstrect);关于它的用法,后面会详细介绍。下面看下GAL_AllocBlitMap的实现
GAL_BlitMap *GAL_AllocBlitMap(void) { GAL_BlitMap *map; /* Allocate the empty map */ map = (GAL_BlitMap *)malloc(sizeof(*map)); if ( map == NULL ) { GAL_OutOfMemory(); return(NULL); } memset(map, 0, sizeof(*map)); /* Allocate the software blit data */ map->sw_data = (struct private_swaccel *)malloc(sizeof(*map->sw_data)); if ( map->sw_data == NULL ) { GAL_FreeBlitMap(map); GAL_OutOfMemory(); return(NULL); } memset(map->sw_data, 0, sizeof(*map->sw_data)); /* It's ready to go */ return(map); }该函数只是做了分配空间的操作。
Surface的另外一个重要操作,就是做层之间的混合。它是通过GAL_LowerBlit来实现的(lower表示更接近于设备底层的实现),其代码如下:
int GAL_LowerBlit (GAL_Surface *src, GAL_Rect *srcrect, GAL_Surface *dst, GAL_Rect *dstrect) { GAL_blit do_blit; /* Check to make sure the blit mapping is valid */ if ( (src->map->dst != dst) || (src->map->dst->format_version != src->map->format_version) ) { if ( GAL_MapSurface(src, dst) < 0 ) { return(-1); } } /* Figure out which blitter to use */ if ( (src->flags & GAL_HWACCEL) == GAL_HWACCEL ) { do_blit = src->map->hw_blit; } else { do_blit = src->map->sw_blit; } return(do_blit(src, srcrect, dst, dstrect)); }
hw_blit是用硬件加速实现的函数,所以,它是由GAL层实现和设置的。每种不同的开发板有字节的实现方法,所以,这不是我们讨论的重点。
sw_blit只软件实现的混合函数,由MiniGUI自己提供,它的确定,是在GAL_MapSurface函数中确定。
当第一次调用GAL_LowerBlit的时候,src->map->dst必定为NULL所以,就会调用到该函数。
GAL_MapSurface会调用到GAL_CalculateBlit函数,还选择一个正确的sw_blit,该函数会计算并填充hw_blit和sw_blit函数。这是一个比较复杂的过程。不过最终,sw_blit大部分情况下,会得到GAL_SoftBlit函数的指针。
关于这部分的详细情况,可以查看相关代码。