优化一直是我在程序中追求的东西之一,想想让自己的游戏在一个古董机器能流畅的运行或者说在当今的机器上,CPU占用率和内存占用率都很低的情况。(毕竟我非常讨厌一个游戏独占了我所有的CPU资源)。
如果从图形接口上作优化,常用的就是使用3D加速和CPU的特殊指令(虽然说DirectDraw能够使用2D硬件加速,但大部分机器支持的仅仅是简单的加速,比如带ColorKey的支持,连一些稍微高级一点的东西,比如Alpha混合,带Alpha通道的纹理(表面)都不支持,需要自己写,优化起来还是使用CPU的特殊指令)。虽然说使用3D加速非常简单,但是它的缺点也非常明显:对硬件有苛刻的要求,如果仅仅是做2D游戏不推荐使用(新手作为练习写DEMO而使用倒还可以,我也这样使用过,呵呵)。使用特殊的CPU指令最常见的就是使用MMX指令了,现在想找到一块装了Windows95以上但不支持MMX的CPU都有难度 ~自己花了大半年的时间用MMX高速实现了D3D对2D贴图的各种特效(带通道或者不带通道的纹理,带BlendColor, 带缩放旋转,做加减法的Alpha混合之类的)之后,虽然发现可以不使用D3D的东西,但是如果画面的东西很多的话,在一些内存带宽不高的机器上的速度还是不够理想,所以还是需要更多的优化。这时候我想起了DirtyRect。
什么是脏矩形?简单的说,就是游戏每次画面的刷新只更新需要更新的那一块区域。Windows本身就是最好的例子。或者说Flash控件,也正是利用了脏矩形技术,所以他的效率才如此的高。传统的游戏循环如下:
while( 游戏没有结束 )
{
if( 有Windows消息 )
{
处理Windows消息
}
else if( 需要渲染 )
{
清除游戏屏幕的缓冲区
把游戏中的物体画到缓冲区里面
把缓冲区更新到游戏窗口上
锁定游戏速度,Sleep一段时间,限制FPS
}
}
从上面的伪代码可以看出,每次游戏都要做清除缓冲区-〉渲染游戏的物体-〉更新到窗口,而基本上我们写游戏至少要保证最低每秒钟要刷新24帧以上(一般都在30)。所以上面的代码每秒钟要至少24次以上,画面东西越多,耗费的CPU越多。
不过我们也可以自然的想到,每次那么多东西不一定都需要更新的,比如一个动画,一般都有一个延迟,比如间隔200毫秒更新一次,那么在这段时间是不需要重新画的,只有更新了帧以后,表示这个动画所在的范围已经“脏”了,需要重新画,这个时候才需要画这个动画。而这段时间之内我们可以节约大量的CPU时间,很自然,积少成多,总体下来这个数值是非常可观的。再举一个例子,一个静止的游戏物体,(比如一棵树)是永远都不需要更新的,除非这个树的位置或者他的属性发生了变化。这样下来我们首先想到的是,每次我们都省略清除后台缓冲这个步骤,这个非常重要,因为上一次画下来的东西都在这个缓冲区里面,如果清除之后就什么都没有啦~~
搞明白了这个原理以后,下面来看看具体实现过程中遇到的问题:
游戏中的物体不会是相互没有遮挡的,所以如果遇到遮挡的问题怎么办?
如果游戏中有100个物体,里面的物体相互遮挡关系总有一个顺序,为了简化问题,只考虑两个物体遮挡的情况,多个物体的遮挡可以根据这个来衍生。
物体A |
物体B |
考虑上图,物体B遮挡了物体A, 也就是说渲染顺序是先画A再画B,这个顺序由各自定义,(我自己就喜欢用一棵渲染树来排序,当然如果你用连表或者其他数据结构来实现也没有问题。)如果物体A的整个区域都需要更新,那么对于B物体,需要更新的部分也就只有A与B的交集部分(图中的蓝色区域),在画B的时候,我们设置目标裁减区域(也就是屏幕缓冲的裁减区域)为这个交集部分,则B在渲染的时候,相当于整个缓冲区大小就只有蓝色区域那么大,那么裁减函数将会把B的数据区裁减到相应的位置(你实现的图形函数中不会没有做裁减的工作吧???如果没有实现,你就不用看了,直接return算了,不然下面的东西你肯定不明白我说什么)。怎么样,B物体相当于只画了蓝色区域这一部分的东西,比整个区域来说节约了不少时间吧?
不知道上面说的你明白了没有,如果没有明白请多看几遍,直到弄明白之后再往下看,不然千万不要往下看。
上面的例子大家肯定会问一个问题,我如何控制B只画蓝色区域的部分呢?这个问题我暂时不说,等到把所有的遮挡情况说完了再说。继续看另外的遮挡情况
X |
A |
C |
D |
E |
B |
上面6个物体A,B,C,D,E,X。X是我们的游戏背景颜色,假设画的顺序是EADCB,如果E需要重新画,那很显然,A,B,C,D不需要做什么
如果A,D都需要重新画,那显然A,D只需要各画一次。而B需要更新的,不是需要更新BD相交的区域,而是AB相交的大区域,也就是说小区域该忽略掉,如果B需要重新画,A,D,C需要重新画吗?也许有人会说,B画的次序是在最后的,所以前面的就不需要画了,对么?答案是错的,需要重新画,因为背景缓冲区我们一般情况下不去清除它,所以谈不上画的顺序了。也就是说,A与B相交的部分,A在下次画的时候也需要更新,D也同样(想通了吗?再举一个例子,如果B含有大量的透明色,如果B需要更新的话,那么B的区域首先要涂上X作为背景,不然B非透明色如果变成了透明色的话,那B在重新画的时候,由于透明色不需要画,那么B上一次留下来的颜色就残留在X上面,看起来当然不对啦,同理对于A,D也一样处理)。
上面的理论部分不知道听明白了没有,如果不明白的话自己花一点点时间去想象看。假如明白了的话,下面继续更加深入的问题。
从上面的理论解说部分可以看出,脏矩形的选取和优化是关键。怎样得到最优化的脏矩形表,就成为了这个技术优化的核心部分。
为了简单起见,这里使用的是一个链表来管理所有的渲染物体。
为了实现我们所设计的东西,我设计了一个非常简单的类:
class CRenderObject
{
public:
virtual ~CRenderObject(){}
virtual void OnRender( GraphicsDevice*pDevice ) = 0; //所有物体都在这里渲染
virtual void OnUpdate( float TimeStamp ) = 0;//物体更新,比如动画帧更新拉之类的,在这里面可以设置DirtyRect标志之类的
virtual bool IsDirty( ) = 0;//是否有脏矩形
virtual bool GetBoundsRect(RECT*pRect) =0;//得到该物体的范围
virtual int GetDirtyRects ( RECT*pRectBuffer ) = 0;//该物体的脏矩形个数,填充到pRectBuffer里面,返回填充了多少个
...其他函数
};
我们还需要一个简单的能管理脏矩形和渲染物体的类
class CRenderObjectManager
{
pulibc:
void RemoveRenderObject( CRenderObject*pObject );//删除一个渲染物体
void AddRenderObject( CRenderObject*pObject );//添加一个渲染物体
void Render( GraphicsDevice*pDevice );//渲染所有的物体
void Update( );//更新所有物体
.....其他函数
protected:
std::list< CRenderObject* > m_RenderObjects;
int m_nCurrentDirtyRectCount;//当前脏矩形数量
struct DirtyRect
{
RECT Range; //脏矩形范围
int AreaSize; //脏矩形大小,用来排序
};
BOOL m_bHoleDirty;//是否全部脏了
DirtyRect m_DirtyRects[128];//屏幕上最多的脏矩形数量,如果大于这个数量则认为屏幕所有范围都脏了
};
void CRenderObjectManager::Update()
{
m_bHoleDirty = false;
m_nCurrentDirtyRectCount = 0;
static RECT DirtyRectBuffer[128];
float TimeStamp = GetElapsedTime();
for(std::list< CRenderObject* >::iterator it = m_RenderObjects.begin();
it != m_RenderObjects.end(); it++)
{
CRenderObject*pObject = *it;
pObject->OnUpdate( TimeStamp );
if(m_bHoleDirty == false && pObject->IsDirty() )
{
int Count = pObject->GetDirtyRects(DirtyRectBuffer);
for( i =0; i { 对于该物体的每一个脏矩形DirtyRectBuffer[i] 如果DirtyRectBuffer[i] 没有在任何一个已有的脏矩形范围内 那么把这个脏矩形根据从大到小的顺序添加到脏矩形范围内,否则忽略这个脏矩形 如果脏矩形数量已经大于设定的最大脏矩形范围,设置所有所有屏幕都脏了的标志, } } } 如果屏幕所有都脏了,填充背景颜色 否则为每一个脏矩形填充背景颜色 } void CRenderObjectManager::Render( GraphicsDevice* pGraphics) { for(std::list< CRenderObject* >::iterator it = m_RenderObjects.begin(); it != m_RenderObjects.end(); it++) { CRenderObject*pObject = *it; { RECT rcBoundsRect = { 0, 0, 0, 0 }; if( pObject->GetBoundsRect( rcBoundsRect ) ) { //设置屏幕裁减区域 pGraphics->SetClipper( &rcBoundsRect ); } pObject->OnRender( pGraphics ); } else { RECT rcBoundsRect = { 0, 0, 0, 0 }; if( pObject->GetBoundsRect( rcBoundsRect ) ) { //如果该物体的范围与脏矩形缓冲区的任何一个脏矩形有交集的话 for( int i=0; i { RECT rcIntersect; if( ::IntersectRect( &rcIntersect, &m_DirtyRects[i].Range, &rcBoundsRect ) ) { //只画交集的部分 pGraphics-> SetClipper ( &m_DirtyRects[i].Range ); pObject->OnRender( pGraphics ); } } } } } 好了,核心代码的伪代码就在这里,不知道大家看明白没有,当然我在这里上面实现的这种方法有一个缺陷,最坏情况下一个也许会导致重新画很多次,如图的情况: A B C D E 假设A是渲染物体,B,C,D,E是由大到小的脏矩形范围,那么很显然,重叠的部分就被反复画。。。这是在分割脏矩形导致的问题,这样画下来,如果A物体是采用了叠加混合到背景的算法的话,问题就出来了,重画的部分会变得非常亮。所以脏矩形的分割就显得非常重要,也就是说把这些脏矩形还要分割为互相独立的互不相交的矩形,至于分割算法嘛,嘿嘿,各位还是动一下脑筋思考思考吧:)这个也是最值得优化的地方了,哈哈。实在想不出的话,最简单的方法就是把彼此相交的脏矩形都做一个合并,得到更大的脏矩形,虽然没有相交的区域了,但是也许这个脏矩形会变得比较大了哦:) 最后,大家一定关心的是我会不会提供源代码,很抱歉的说,不能。我在我的引擎中实现的不是以简单的链表去做的,用的是一棵比较复杂的渲染树,牵扯到的东西就比较多了,所以不方便提供代码,不过可以给一个演示吧:)再说大家如果真的明白了我所说的,那就可以自己动手写一下嘛,不要怕失败/P^_^, 好啦,关于脏矩形的技术就介绍到这里啦,用好这个技术你会发现你的游戏会在配置极低的机器上也能运行如飞的:)这种技术如果能用在现在市面上的那么多的游戏中的话,就不必为一个小游戏就强占了您100%的CPU资源而烦恼拉:) 如果您有更好的方法或者指出其中的不完善的地方还请您不吝赐教,大家多多交流:) 关于测试的Demo 该Demo渲染部分由Kylinx花了近半年的时间,全部采用MMX写成,已经成功实现d3d中对2d纹理的操作,速度非常快 关于Settings.ini EnableDirtyRect = 1 //是否允许脏矩形技术,0=关闭,1=开启 LockFPS = 1 //是否锁定FPS,0=关闭,1=开启 哈,这个Demo在不锁定FPS,脏技术开启的的情况下,我的Duron1.8G CPU,FPS达到 31500左右!(没错,是三万一千五百)这个数字吓人吧?如果脏技术未打开,只能在150左右,相差200倍阿!!! 如果LockFPS开启,在我机器上(512M DDR)跑30个DEMO,CPU占用还是为0,哈哈! 加一些C++源码:
if(如果屏幕都脏了的标志已经设定)
}
2 #ifndef _DIRTYRECT_CANVAS_H
3 #define _DIRTYRECT_CANVAS_H
4
5 #define CANVAS_BLOCK_WIDTH 64
6 #define CANVAS_BLOCK_HEIGHT 32
7 #define DIRTY (~0)
8
9 typedef unsigned short pixel;
10 typedef void (*draw_func)(pixel *ptr,int pitch,void *object,int x,int y);
11 struct canvas;
12
13 #define COMMON_HEADER \
14 unsigned dirty; \
15 int width; \
16 int height; \
17 int kx; \
18 int ky;
19
20 struct canvas* canvas_create(int w,int h,pixel *ptr,int pitch,draw_func flip,void *flip_object);
21 void canvas_release(struct canvas *c);
22 void canvas_draw(struct canvas *c,draw_func func,void *object,int x,int y);
23 void canvas_update(struct canvas *c);
24 void canvas_redraw(struct canvas *c);
25
26 #endif
27
2
#include
<
malloc.h
>
3
#include
<
assert.h
>
4
#include
<
string
.h
>
5
#include
"
canvas.h
"
6
7
#define
CANVAS_PIPELINE_EXPAND 32
8
9
struct
pipe_node {
10
draw_func func;
11
void
*
object
;
12
int
x;
13
int
y;
14
};
15
16
struct
canvas_block {
17
pixel
*
ptr;
18
struct
pipe_node
*
pipeline;
19
int
total_object;
20
int
total_last;
21
int
capability_pipeline;
22
};
23
24
struct
canvas {
25
int
width;
26
int
height;
27
int
pitch;
28
int
block_w;
29
int
block_h;
30
struct
canvas_block block[
1
];
31
};
32
33
static
void
drawfunc_clear(pixel
*
ptr,
int
pitch,
void
*
object
,
int
x,
int
y)
34
{
35
int
i;
36
for
(i
=
0
;i
<
CANVAS_BLOCK_HEIGHT;i
++
) {
37
memset(ptr,
0
,CANVAS_BLOCK_WIDTH
*
sizeof
(pixel));
38
ptr
=
(pixel
*
)((
char
*
)ptr
+
pitch);
39
}
40
}
41
42
static
void
canvas_block_resize(
struct
canvas_block
*
blk)
43
{
44
const
int
size
=
blk
->
capability_pipeline
+
CANVAS_PIPELINE_EXPAND;
45
const
int
newsize
=
size
*
sizeof
(
struct
pipe_node);
46
blk
->
capability_pipeline
=
size;
47
blk
->
pipeline
=
(
struct
pipe_node
*
)realloc(blk
->
pipeline,newsize);
48
}
49
50
static
void
canvas_init(
struct
canvas
*
c,pixel
*
ptr,draw_func flip,
void
*
flip_object)
51
{
52
int
pitch
=
c
->
pitch
*
CANVAS_BLOCK_HEIGHT;
53
int
i,j;
54
const
int
w
=
c
->
width;
55
const
int
h
=
c
->
height;
56
pixel
*
line
=
ptr;
57
struct
canvas_block
*
block
=
c
->
block;
58
for
(i
=
0
;i
<
h;i
+=
CANVAS_BLOCK_HEIGHT) {
59
for
(j
=
0
;j
<
w;j
+=
CANVAS_BLOCK_WIDTH) {
60
block
->
ptr
=
line
+
j;
61
block
->
pipeline
=
0
;
62
block
->
capability_pipeline
=
0
;
63
canvas_block_resize(block);
64
block
->
total_object
=
1
;
65
block
->
total_last
=
0
;
66
block
->
pipeline[
0
].func
=
flip;
67
block
->
pipeline[
0
].
object
=
flip_object;
68
block
->
pipeline[
0
].x
=
j;
69
block
->
pipeline[
0
].y
=
i;
70
++
block;
71
}
72
line
=
(pixel
*
)((
char
*
)line
+
pitch);
73
}
74
}
75
76
struct
canvas
*
canvas_create(
int
w,
int
h,pixel
*
ptr,
int
pitch,draw_func flip,
void
*
flip_object)
77
{
78
const
int
nw
=
(w
+
CANVAS_BLOCK_WIDTH
-
1
)
/
CANVAS_BLOCK_WIDTH;
79
const
int
nh
=
(h
+
CANVAS_BLOCK_HEIGHT
-
1
)
/
CANVAS_BLOCK_HEIGHT;
80
struct
canvas
*
c
=
(
struct
canvas
*
)malloc(
81
sizeof
(
struct
canvas)
-
sizeof
(
struct
canvas_block)
+
82
sizeof
(
struct
canvas_block)
*
(nw
*
nh)
83
);
84
85
c
->
width
=
w;
86
c
->
height
=
h;
87
c
->
block_w
=
nw;
88
c
->
block_h
=
nh;
89
c
->
pitch
=
pitch;
90
canvas_init(c,ptr,flip,flip_object);
91
return
c;
92
}
93
94
void
canvas_release(
struct
canvas
*
c)
95
{
96
int
i,j;
97
const
int
w
=
c
->
block_w;
98
const
int
h
=
c
->
block_h;
99
struct
canvas_block
*
block
=
c
->
block;
100
for
(i
=
0
;i
<
h;i
++
) {
101
for
(j
=
0
;j
<
w;j
++
) {
102
free(block
->
pipeline);
103
++
block;
104
}
105
}
106
free(c);
107
}
108
109
struct
object_2d { COMMON_HEADER } ;
110
111
void
canvas_draw(
struct
canvas
*
c,draw_func func,
void
*
object
,
int
x,
int
y)
112
{
113
struct
object_2d
*
obj
=
(
struct
object_2d
*
)
object
;
114
int
left,right,top,bottom;
115
unsigned dirty
=
obj
->
dirty;
116
if
(obj
->
width
<=
0
||
obj
->
height
<=
0
)
117
return
;
118
obj
->
dirty
=
0
;
119
left
=
(x
-=
obj
->
kx);
120
top
=
(y
-=
obj
->
ky);
121
right
=
left
+
obj
->
width
-
1
;
122
bottom
=
top
+
obj
->
height
-
1
;
123
124
//
clip to canvas
125
if
(left
<
0
) {
126
left
=
0
;
127
}
128
else
if
(left
>=
c
->
width) {
129
return
;
130
}
131
if
(top
<
0
) {
132
top
=
0
;
133
}
134
else
if
(top
>=
c
->
height) {
135
return
;
136
}
137
if
(right
>
c
->
width
-
1
) {
138
right
=
c
->
width
-
1
;
139
}
140
else
if
(right
<
0
) {
141
return
;
142
}
143
if
(bottom
>
c
->
height
-
1
) {
144
bottom
=
c
->
height
-
1
;
145
}
146
else
if
(bottom
<
0
) {
147
return
;
148
}
149
150
//
splite to blocks
151
do
{
152
int
i,j;
153
int
w
=
right
/
CANVAS_BLOCK_WIDTH;
154
int
h
=
bottom
/
CANVAS_BLOCK_HEIGHT;
155
int
from_x
=
left
/
CANVAS_BLOCK_WIDTH;
156
int
from_y
=
top
/
CANVAS_BLOCK_HEIGHT;
157
struct
canvas_block
*
block
=&
c
->
block[from_y
*
c
->
block_w];
158
for
(i
=
from_y;i
<=
h;i
++
) {
159
for
(j
=
from_x;j
<=
w;j
++
) {
160
struct
canvas_block
*
blk
=&
block[j];
161
struct
pipe_node
*
node;
162
if
(blk
->
total_object
==
blk
->
capability_pipeline) {
163
canvas_block_resize(blk);
164
}
165
node
=&
blk
->
pipeline[blk
->
total_object
++
];
166
node
->
func
=
func;
167
node
->
object
=
object
;
168
node
->
x
=
x
-
j
*
CANVAS_BLOCK_WIDTH;
169
node
->
y
=
y
-
i
*
CANVAS_BLOCK_HEIGHT;
170
blk
->
total_last
|=
dirty;
171
}
172
block
+=
c
->
block_w;
173
}
174
}
while
(
0
);
175
}
176
177
void
canvas_update(
struct
canvas
*
c)
178
{
179
int
i,j;
180
struct
canvas_block
*
blk
=
c
->
block;
181
int
pitch
=
c
->
pitch;
182
for
(i
=
c
->
block_w
*
c
->
block_h;i
>
0
;
--
i,
++
blk) {
183
if
(blk
->
total_object
!=
blk
->
total_last) {
184
pixel
*
ptr
=
blk
->
ptr;
185
drawfunc_clear(ptr,pitch,
0
,
0
,
0
);
186
for
(j
=
blk
->
total_object
-
1
;j
>=
0
;j
--
) {
187
struct
pipe_node
*
node
=&
blk
->
pipeline[j];
188
node
->
func(ptr,pitch,node
->
object
,node
->
x,node
->
y);
189
}
190
blk
->
total_last
=
blk
->
total_object;
191
}
192
blk
->
total_object
=
1
;
193
}
194
}
195
196
void
canvas_redraw(
struct
canvas
*
c)
197
{
198
int
i;
199
struct
canvas_block
*
blk
=
c
->
block;
200
int
pitch
=
c
->
pitch;
201
for
(i
=
c
->
block_w
*
c
->
block_h;i
>
0
;
--
i,
++
blk) {
202
blk
->
total_last
=
0
;
203
}
204
}
2
#ifndef _DIRTYRECT_BOX_H
3
#define
_DIRTYRECT_BOX_H
4
5
#include
"
canvas.h
"
6
7
struct
box {
8
COMMON_HEADER
9
pixel color;
10
};
11
12
void
box_draw(
struct
canvas
*
c,
struct
box
*
b,
int
x,
int
y);
13
14
#endif
15
2
#include
"
box.h
"
3
4
static
void
draw_box(pixel
*
ptr,
int
pitch,
void
*
object
,
int
x,
int
y)
5
{
6
struct
box
*
obj
=
(
struct
box
*
)
object
;
7
int
w
=
obj
->
width;
8
int
h
=
obj
->
height;
9
int
i,j;
10
pixel
*
buffer
=
ptr;
11
pixel color
=
obj
->
color;
12
if
(y
<
0
) {
13
h
+=
y;
14
y
=
0
;
15
}
16
else
{
17
buffer
=
(pixel
*
)((
char
*
)buffer
+
y
*
pitch);
18
}
19
if
(x
<
0
) {
20
w
+=
x;
21
x
=
0
;
22
}
23
else
{
24
buffer
+=
x;
25
}
26
if
(w
+
x
>
CANVAS_BLOCK_WIDTH) {
27
w
=
CANVAS_BLOCK_WIDTH
-
x;
28
}
29
if
(h
+
y
>
CANVAS_BLOCK_HEIGHT) {
30
h
=
CANVAS_BLOCK_HEIGHT
-
y;
31
}
32
for
(i
=
0
;i
<
h;i
++
) {
33
for
(j
=
0
;j
<
w;j
++
) {
34
buffer[j]
=
color;
35
}
36
buffer
=
(pixel
*
)((
char
*
)buffer
+
pitch);
37
}
38
}
39
40
void
box_draw(
struct
canvas
*
c,
struct
box
*
b,
int
x,
int
y)
41
{
42
canvas_draw(c,draw_box,b,x,y);
43
}
2
#ifndef _DIRTYRECT_FLIPGDI_H
3
#define
_DIRTYRECT_FLIPGDI_H
4
5
#include
"
canvas.h
"
6
7
struct
gdi {
8
draw_func flip;
9
void
*
object
;
10
pixel
*
buffer;
11
int
width;
12
int
height;
13
int
pitch;
14
};
15
16
void
gdi_create(
struct
gdi
*
g,
void
*
wnd,
int
w,
int
h);
17
void
gdi_release(
struct
gdi
*
g);
18
19
#endif
20
2
#include
"
gdi.h
"
3
#include
<
windows.h
>
4
#include
<
malloc.h
>
5
#include
<
string
.h
>
6
7
struct
gdi_object {
8
HWND wnd;
9
HDC dc;
10
BITMAPINFO bi;
11
RGBQUAD pal[
2
];
12
};
13
14
static
void
gdi_flip(pixel
*
ptr,
int
pitch,
void
*
object
,
int
x,
int
y)
15
{
16
struct
gdi_object
*
obj
=
(
struct
gdi_object
*
)
object
;
17
SetDIBitsToDevice(obj
->
dc,
18
x,y,CANVAS_BLOCK_WIDTH,CANVAS_BLOCK_HEIGHT,
19
x,
0
,
20
0
,CANVAS_BLOCK_HEIGHT,
21
ptr
-
x,
&
(obj
->
bi),DIB_RGB_COLORS
22
);
23
}
24
25
void
gdi_create(
struct
gdi
*
g,
void
*
wnd,
int
w,
int
h)
26
{
27
struct
gdi_object
*
obj;
28
BITMAPINFOHEADER
*
bh;
29
g
->
pitch
=
(w
*
sizeof
(pixel)
+
3
)
&
(
~
3
);
30
g
->
flip
=
gdi_flip;
31
g
->
buffer
=
(pixel
*
)malloc(h
*
g
->
pitch);
32
g
->
object
=
obj
=
(
struct
gdi_object
*
)malloc(
sizeof
(
struct
gdi_object));
33
g
->
width
=
w;
34
g
->
height
=
h;
35
obj
->
wnd
=
(HWND)wnd;
36
obj
->
dc
=
GetDC(obj
->
wnd);
37
bh
=&
(obj
->
bi.bmiHeader);
38
memset(bh,
0
,
sizeof
(
*
bh));
39
bh
->
biSize
=
sizeof
(
*
bh);
40
bh
->
biWidth
=
g
->
pitch
/
sizeof
(pixel);
41
bh
->
biHeight
=-
CANVAS_BLOCK_HEIGHT;
42
bh
->
biPlanes
=
1
;
43
bh
->
biBitCount
=
sizeof
(pixel)
*
8
;
44
bh
->
biCompression
=
BI_BITFIELDS;
45
*
(
int
*
)(obj
->
bi.bmiColors
+
0
)
=
0xf800
;
46
*
(
int
*
)(obj
->
bi.bmiColors
+
1
)
=
0x7e0
;
47
*
(
int
*
)(obj
->
bi.bmiColors
+
2
)
=
0x1f
;
48
}
49
50
void
gdi_release(
struct
gdi
*
g)
51
{
52
struct
gdi_object
*
obj
=
(
struct
gdi_object
*
)g
->
object
;
53
ReleaseDC(obj
->
wnd,obj
->
dc);
54
free(g
->
buffer);
55
free(g
->
object
);
56
}
2
#include
<
windows.h
>
3
#include
"
gdi.h
"
4
#include
"
canvas.h
"
5
#include
"
box.h
"
6
7
#define
WIDTH 640
8
#define
HEIGHT 480
9
10
struct
gdi g_gdi;
11
struct
canvas
*
g_canvas;
12
13
HINSTANCE hInst;
//
current instance
14
15
ATOM MyRegisterClass();
16
BOOL InitInstance();
17
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
18
19
int
main()
20
{
21
int
i;
22
MSG msg;
23
struct
box b;
24
hInst
=
GetModuleHandle(
0
);
//
Store instance handle in our global variable
25
MyRegisterClass();
26
27
if
(
!
InitInstance ()) {
28
return
FALSE;
29
}
30
b.kx
=
0
;
31
b.ky
=
0
;
32
b.width
=
100
;
33
b.height
=
100
;
34
b.color
=
0xffff
;
35
b.dirty
=
DIRTY;
36
37
//
Main message loop:
38
i
=
0
;
39
for
(;;) {
40
if
(PeekMessage(
&
msg,NULL,
0
,
0
,PM_NOREMOVE)) {
41
if
(
!
GetMessage(
&
msg,NULL,
0
,
0
)) {
42
break
;
43
}
44
TranslateMessage(
&
msg);
45
DispatchMessage(
&
msg);
46
}
47
b.dirty
=
DIRTY;
48
box_draw(g_canvas,
&
b,i,i);
49
canvas_update(g_canvas);
50
++
i;
51
Sleep(
5
);
52
}
53
54
55
gdi_release(
&
g_gdi);
56
canvas_release(g_canvas);
57
58
return
msg.wParam;
59
}
60
61
ATOM MyRegisterClass()
62
{
63
WNDCLASS wc;
64
65
wc.style
=
CS_HREDRAW
|
CS_VREDRAW;
66
wc.lpfnWndProc
=
(WNDPROC)WndProc;
67
wc.cbClsExtra
=
0
;
68
wc.cbWndExtra
=
0
;
69
wc.hInstance
=
hInst;
70
wc.hIcon
=
NULL;
71
wc.hCursor
=
LoadCursor(NULL, IDC_ARROW);
72
wc.hbrBackground
=
NULL;
73
wc.lpszMenuName
=
NULL;
74
wc.lpszClassName
=
"
TEST
"
;
75
76
return
RegisterClass(
&
wc);
77
}
78
79
BOOL InitInstance()
80
{
81
HWND hWnd;
82
DWORD style
=
WS_OVERLAPPEDWINDOW
&~
WS_SIZEBOX
&~
WS_MAXIMIZEBOX;
83
RECT rect
=
{
0
,
0
,WIDTH,HEIGHT};
84
AdjustWindowRect(
&
rect,style,FALSE);
85
86
hWnd
=
CreateWindow(
"
TEST
"
,
"
test
"
,
87
style, CW_USEDEFAULT,
0
,
88
rect.right
-
rect.left,rect.bottom
-
rect.top,
89
0
,
0
,hInst,
0
);
90
91
92
if
(
!
hWnd)
93
{
94
return
FALSE;
95
}
96
97
gdi_create(
&
g_gdi,hWnd,WIDTH,HEIGHT);
98
g_canvas
=
canvas_create(g_gdi.width,g_gdi.height,
99
g_gdi.buffer,g_gdi.pitch,g_gdi.flip,g_gdi.
object
);
100
101
ShowWindow(hWnd, SW_SHOWDEFAULT);
102
UpdateWindow(hWnd);
103
104
return
TRUE;
105
}
106
107
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
108
{
109
switch
(message)
110
{
111
case
WM_PAINT:
112
canvas_redraw(g_canvas);
113
break
;
114
case
WM_DESTROY:
115
PostQuitMessage(
0
);
116
break
;
117
default
:
118
return
DefWindowProc(hWnd, message, wParam, lParam);
119
}
120
return
0
;
121
}