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_AllocFormat函数的实现:

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;

这里需要关注的重点是hw_blit和sw_blit变量,它们是主要指向blit的操作的回调。GAL_blit的定义是:

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));
}

可以看到,它通过map的hw_blit或者sw_blit调用来实现的。

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函数的指针。

MiniGUI源码分析——GDI概览及Surface相关推荐

  1. Retrofit源码分析一 概览

    Retrofit源码分析一 概览 Retrofit的本质和与Okhttp的关系 ​ 说到Retrofit,免不了要提起Okhttp,因为二者通常是绑定到一起使用的.那么我们首先要明确一点Retrofi ...

  2. android 日历源码解析,Android 4.0日历(calendar)源码分析之概览

    Calendar 从4.0开始,谷歌android系统有了脱碳换骨的改变,相应的日历应用的代码架构也跟2.*完全不同.代码更规范,当然也更复杂,且涉及到了android开发的方方面面. 如果你熟悉了i ...

  3. miniGUI源码分析:消息机制

    miniGUI通过接收消息来和外界交互.消息由系统或应用程序产生,系统对输入事件产生消息,系统对应用程序的响应也会产生消息,应用程序可以通过产生消息来完成某个任务,或者与其它应用程序的窗口进行通讯.总 ...

  4. miniGUI源码分析:初始化

    前言 miniGUI是一个面向嵌入式系统的轻量级图形用户界面支持系统,其轻量小巧,占用资源少是主要优势.但由于没有更新,目前使用的仍是08年发布的版本,随着用户对操作界面的美观度及视觉效果的要求越来越 ...

  5. Dubbo服务注册源码分析

    本代码版本基于Dubbo2.7.8版本进行源码分析 注册概览 扫描所有@DubboService注解, 加载配置文件, 装载注解中的所有属性, 把每个服务都封装成一个ServiceBean, 注入到S ...

  6. xf86-video-intel源码分析1 —— 源码目录结构概览

    在<Spectacle/Flameshot/X11 Xlib截屏问题现象及解决方法>一文(链接如下)中提到, Spectacle/Flameshot/X11 Xlib截屏问题现象及解决方法 ...

  7. android.view.surface,Android SurfaceView 源码分析及使用

    概述 SurfaceView 是 Android 中一种比较特殊的视图(View),它跟平时时候的 TextView.Button 最大的区别是它跟它的视图容器并不是在同一个视图层上,它的 UI 显示 ...

  8. 【Spring源码分析】Bean加载流程概览

    代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...

  9. beaninfo详解源码解析 java_【Spring源码分析】Bean加载流程概览

    代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...

  10. minigui源码学习

    前言: 个人习惯学习源码先从编译结构了解代码模块,所以先从编译结构开始了解. 如果编译角度搞不定,就代码开始运行时,各个模块初始化的角度开始了解. 学习源码--编译角度 下载源码 git clone ...

最新文章

  1. POJ 3667 Hotel(线段树)
  2. Excel 统计IP
  3. ThinkPad安装deepin操作系统报错解决方法
  4. opencv 裁剪 java_如何在opencv java中裁剪检测到的面部图像
  5. Python运行异常 Original error was: DLL load failed:
  6. php如何做浏览量,php+ajax实现的点击浏览量加1
  7. 2021网络药理学研究的免费数据库、在线平台与软件工具汇总整理介绍
  8. Numpy实现酒鬼漫步问题【以及randint()、where()、cumsum()、argmax()的用法详解】
  9. Flash中与xml交互时不显示中文的解决办法
  10. Docker学习总结(27)——Dockerfile详解
  11. 2020年acwifi拆过的无线路由器配置汇总
  12. DBSCAN聚类算法
  13. DTCC 回顾:技术破局,分布式数据库创赢未来
  14. RGB图片和mask合在一起
  15. C#将PDF转为多种图像文件格式的方法(Png/Bmp/Emf/Tiff)
  16. 微处理器系统结构与嵌入式系统设计(一)
  17. Serval Project——Android
  18. 使用腾讯云开发者平台免费搭载静态云服务
  19. 计算机win7教案,win7操作系统教案_相关文章专题_写写帮文库
  20. 【学习笔记】大数据搜索与挖掘

热门文章

  1. 【网易微专业】算法原理与实践 2
  2. 104 自定义大头针
  3. 店内扫码点餐系统 计算机毕业设计 微信小程序开发
  4. plsql的注册激活
  5. IOS微信内置浏览器音频加载问题
  6. 基于FPGA的AD9854并行接口驱动(VerilogHDL语言)
  7. gentoo 下Local time zone must be set--see zic manual page解决办法
  8. TSE2019-The Art, Science, and Engineering of Fuzzing: A Survey
  9. android 平板刷机,小白必看,安卓平板电脑刷机教程之一键刷机
  10. andriod 和风天气SDK获取实时天气(保姆级教程)