第二章 图像和视频
初始化SDL Video显示
视频是最常用的部分,也是SDL最完整的子系统。下面的初始化过程是每个SDL程序都要做的,即使可能有些不同。
例2-1 初始化视频
SDL_Surface *screen;
/* Initialize the SDL library */
if( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
fprintf(stderr,
"Couldn't initialize SDL: %s\n", SDL_GetError());
exit(1);
}
/* Clean up on exit */
atexit(SDL_Quit);
/*
* Initialize the display in a 640x480 8-bit palettized mode,
* requesting a software surface
*/
screen = SDL_SetVideoMode(640, 480, 8, SDL_SWSURFACE);
if ( screen == NULL ) {
fprintf(stderr, "Couldn't set 640x480x8 video mode: %s\n",
SDL_GetError());
exit(1);
}
初始化最佳视频模式
如果你希望某种色深(颜色数)但如果用户的显示器不支持也可以接受其他色深,使用加SDL_ANYFORMAT参数的SDL_SetVideoMode。您还可以用SDL_VideoModeOK来找到与请求模式最接近的模式。
例2-2 初始化最佳视频模式
/* Have a preference for 8-bit, but accept any depth */
screen = SDL_SetVideoMode(640, 480, 8, SDL_SWSURFACE|SDL_ANYFORMAT);
if ( screen == NULL ) {
fprintf(stderr, "Couldn't set 640x480x8 video mode: %s\n",
SDL_GetError());
exit(1);
}
printf("Set 640x480 at %d bits-per-pixel mode\n",
screen->format->BitsPerPixel);
读取并显示BMP文件
当SDL已经初始化,视频模式已经选择,下面的函数将读取并显示指定的BMP文件。
例2-3 读取并显示BMP文件
void display_bmp(char *file_name)
{
SDL_Surface *image;
/* Load the BMP file into a surface */
image = SDL_LoadBMP(file_name);
if (image == NULL) {
fprintf(stderr, "Couldn't load %s: %s\n", file_name, SDL_GetError());
return;
}
/*
* Palettized screen modes will have a default palette (a standard
* 8*8*4 colour cube), but if the image is palettized as well we can
* use that palette for a nicer colour matching
*/
if (image->format->palette && screen->format->palette) {
SDL_SetColors(screen, image->format->palette->colors, 0,
image->format->palette->ncolors);
}
/* Blit onto the screen surface */
if(SDL_BlitSurface(image, NULL, screen, NULL) < 0)
fprintf(stderr, "BlitSurface error: %s\n", SDL_GetError());
SDL_UpdateRect(screen, 0, 0, image->w, image->h);
/* Free the allocated BMP surface */
SDL_FreeSurface(image);
}
直接在显示上绘图
下面两个函数实现在图像平面的像素读写。它们被细心设计成可以用于所有色深。记住在使用前要先锁定图像平面,之后要解锁。
在像素值和其红、绿、蓝值间转换,使用SDL_GetRGB()和SDL_MapRGB()。
例2-4 getpixel()
/*
* Return the pixel value at (x, y)
* NOTE: The surface must be locked before calling this!
*/
Uint32 getpixel(SDL_Surface *surface, int x, int y)
{
int bpp = surface->format->BytesPerPixel;
/* Here p is the address to the pixel we want to retrieve */
Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
switch(bpp) {
case 1:
return *p;
case 2:
return *(Uint16 *)p;
case 3:
if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
return p[0] << 16 | p[1] << 8 | p[2];
else
return p[0] | p[1] << 8 | p[2] << 16;
case 4:
return *(Uint32 *)p;
default:
return 0; /* shouldn't happen, but avoids warnings */
}
}
例2-5 putpixel()
/*
* Set the pixel at (x, y) to the given value
* NOTE: The surface must be locked before calling this!
*/
void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
{
int bpp = surface->format->BytesPerPixel;
/* Here p is the address to the pixel we want to set */
Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
switch(bpp) {
case 1:
*p = pixel;
break;
case 2:
*(Uint16 *)p = pixel;
break;
case 3:
if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
p[0] = (pixel >> 16) & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = pixel & 0xff;
} else {
p[0] = pixel & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = (pixel >> 16) & 0xff;
}
break;
case 4:
*(Uint32 *)p = pixel;
break;
}
}
例2-6 使用上面的putpixel()在屏幕中心画一个黄点
/* Code to set a yellow pixel at the center of the screen */
int x, y;
Uint32 yellow;
/* Map the color yellow to this display (R=0xff, G=0xFF, B=0x00)
Note: If the display is palettized, you must set the palette first.
*/
yellow = SDL_MapRGB(screen->format, 0xff, 0xff, 0x00);
x = screen->w / 2;
y = screen->h / 2;
/* Lock the screen for direct access to the pixels */
if ( SDL_MUSTLOCK(screen) ) {
if ( SDL_LockSurface(screen) < 0 ) {
fprintf(stderr, "Can't lock screen: %s\n", SDL_GetError());
return;
}
}
putpixel(screen, x, y, yellow);
if ( SDL_MUSTLOCK(screen) ) {
SDL_UnlockSurface(screen);
}
/* Update just the part of the display that we've changed */
SDL_UpdateRect(screen, x, y, 1, 1);
return;
并用SDL和OpenGL
SDL可以在多种平台(Linux/X11, Win32, BeOS, MacOS Classic/Toolbox, MacOS X, FreeBSD/X11 and Solaris/X11)上创建和使用OpenGL上下文。这允许你在OpenGL程序中使用SDL的音频、事件、线程和记时器,而这些通常是GLUT的任务。
和普通的初始化类似,但有三点不同:必须传SDL_OPENGL参数给SDL_SetVideoMode;必须使用SDL_GL_SetAttribute指定一些GL属性(深度缓冲区位宽,帧缓冲位宽等);如果您想使用双缓冲,必须作为GL属性指定
例2-7 初始化SDL加OpenGL
/* Information about the current video settings. */
const SDL_VideoInfo* info = NULL;
/* Dimensions of our window. */
int width = 0;
int height = 0;
/* Color depth in bits of our window. */
int bpp = 0;
/* Flags we will pass into SDL_SetVideoMode. */
int flags = 0;
/* First, initialize SDL's video subsystem. */
if( SDL_Init( SDL_INIT_VIDEO ) < 0 ) {
/* Failed, exit. */
fprintf( stderr, "Video initialization failed: %s\n",
SDL_GetError( ) );
quit_tutorial( 1 );
}
/* Let's get some video information. */
info = SDL_GetVideoInfo( );
if( !info ) {
/* This should probably never happen. */
fprintf( stderr, "Video query failed: %s\n",
SDL_GetError( ) );
quit_tutorial( 1 );
}
/*
* Set our width/height to 640/480 (you would
* of course let the user decide this in a normal
* app). We get the bpp we will request from
* the display. On X11, VidMode can't change
* resolution, so this is probably being overly
* safe. Under Win32, ChangeDisplaySettings
* can change the bpp.
*/
width = 640;
height = 480;
bpp = info->vfmt->BitsPerPixel;
/*
* Now, we want to setup our requested
* window attributes for our OpenGL window.
* We want *at least* 5 bits of red, green
* and blue. We also want at least a 16-bit
* depth buffer.
*
* The last thing we do is request a double
* buffered window. '1' turns on double
* buffering, '0' turns it off.
*
* Note that we do not use SDL_DOUBLEBUF in
* the flags to SDL_SetVideoMode. That does
* not affect the GL attribute state, only
* the standard 2D blitting setup.
*/
SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );
SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 );
SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 );
SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
/*
* We want to request that SDL provide us
* with an OpenGL window, in a fullscreen
* video mode.
*
* EXERCISE:
* Make starting windowed an option, and
* handle the resize events properly with
* glViewport.
*/
flags = SDL_OPENGL | SDL_FULLSCREEN;
/*
* Set the video mode
*/
if( SDL_SetVideoMode( width, height, bpp, flags ) == 0 ) {
/*
* This could happen for a variety of reasons,
* including DISPLAY not being set, the specified
* resolution not being available, etc.
*/
fprintf( stderr, "Video mode set failed: %s\n",
SDL_GetError( ) );
quit_tutorial( 1 );
}
OpenGL绘图
除了初始化,在SDL程序中使用OpenGL和其他情况基本相同,是同样函数和数据类型。但是如果您使用双缓冲,则必须用SDL_GL_SwapBuffers()来交换前后缓冲,而不是glxSwapBuffers()(GLX)或SwapBuffers()(Windows)。
例2-8 SDL和OpenGL
/*
* SDL OpenGL Tutorial.
* (c) Michael Vance, 2000
* [email protected]
*
* Distributed under terms of the LGPL.
*/
#include
1<sdl sdl.h="">
2 #include <gl gl.h="">
3 #include <gl glu.h="">
4
5 #include <stdio.h>
6 #include <stdlib.h>
7
8 static GLboolean should_rotate = GL_TRUE;
9
10 static void quit_tutorial( int code )
11 {
12 /*
13 * Quit SDL so we can release the fullscreen
14 * mode and restore the previous video settings,
15 * etc.
16 */
17 SDL_Quit( );
18
19 /* Exit program. */
20 exit( code );
21 }
22
23 static void handle_key_down( SDL_keysym* keysym )
24 {
25
26 /*
27 * We're only interested if 'Esc' has
28 * been presssed.
29 *
30 * EXERCISE:
31 * Handle the arrow keys and have that change the
32 * viewing position/angle.
33 */
34 switch( keysym->sym ) {
35 case SDLK_ESCAPE:
36 quit_tutorial( 0 );
37 break;
38 case SDLK_SPACE:
39 should_rotate = !should_rotate;
40 break;
41 default:
42 break;
43 }
44
45 }
46
47 static void process_events( void )
48 {
49 /* Our SDL event placeholder. */
50 SDL_Event event;
51
52 /* Grab all the events off the queue. */
53 while( SDL_PollEvent( &event ) ) {
54
55 switch( event.type ) {
56 case SDL_KEYDOWN:
57 /* Handle key presses. */
58 handle_key_down( &event.key.keysym );
59 break;
60 case SDL_QUIT:
61 /* Handle quit requests (like Ctrl-c). */
62 quit_tutorial( 0 );
63 break;
64 }
65
66 }
67
68 }
69
70 static void draw_screen( void )
71 {
72 /* Our angle of rotation. */
73 static float angle = 0.0f;
74
75 /*
76 * EXERCISE:
77 * Replace this awful mess with vertex
78 * arrays and a call to glDrawElements.
79 *
80 * EXERCISE:
81 * After completing the above, change
82 * it to use compiled vertex arrays.
83 *
84 * EXERCISE:
85 * Verify my windings are correct here ;).
86 */
87 static GLfloat v0[] = { -1.0f, -1.0f, 1.0f };
88 static GLfloat v1[] = { 1.0f, -1.0f, 1.0f };
89 static GLfloat v2[] = { 1.0f, 1.0f, 1.0f };
90 static GLfloat v3[] = { -1.0f, 1.0f, 1.0f };
91 static GLfloat v4[] = { -1.0f, -1.0f, -1.0f };
92 static GLfloat v5[] = { 1.0f, -1.0f, -1.0f };
93 static GLfloat v6[] = { 1.0f, 1.0f, -1.0f };
94 static GLfloat v7[] = { -1.0f, 1.0f, -1.0f };
95 static GLubyte red[] = { 255, 0, 0, 255 };
96 static GLubyte green[] = { 0, 255, 0, 255 };
97 static GLubyte blue[] = { 0, 0, 255, 255 };
98 static GLubyte white[] = { 255, 255, 255, 255 };
99 static GLubyte yellow[] = { 0, 255, 255, 255 };
100 static GLubyte black[] = { 0, 0, 0, 255 };
101 static GLubyte orange[] = { 255, 255, 0, 255 };
102 static GLubyte purple[] = { 255, 0, 255, 0 };
103
104 /* Clear the color and depth buffers. */
105 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
106
107 /* We don't want to modify the projection matrix. */
108 glMatrixMode( GL_MODELVIEW );
109 glLoadIdentity( );
110
111 /* Move down the z-axis. */
112 glTranslatef( 0.0, 0.0, -5.0 );
113
114 /* Rotate. */
115 glRotatef( angle, 0.0, 1.0, 0.0 );
116
117 if( should_rotate ) {
118
119 if( ++angle > 360.0f ) {
120 angle = 0.0f;
121 }
122
123 }
124
125 /* Send our triangle data to the pipeline. */
126 glBegin( GL_TRIANGLES );
127
128 glColor4ubv( red );
129 glVertex3fv( v0 );
130 glColor4ubv( green );
131 glVertex3fv( v1 );
132 glColor4ubv( blue );
133 glVertex3fv( v2 );
134
135 glColor4ubv( red );
136 glVertex3fv( v0 );
137 glColor4ubv( blue );
138 glVertex3fv( v2 );
139 glColor4ubv( white );
140 glVertex3fv( v3 );
141
142 glColor4ubv( green );
143 glVertex3fv( v1 );
144 glColor4ubv( black );
145 glVertex3fv( v5 );
146 glColor4ubv( orange );
147 glVertex3fv( v6 );
148
149 glColor4ubv( green );
150 glVertex3fv( v1 );
151 glColor4ubv( orange );
152 glVertex3fv( v6 );
153 glColor4ubv( blue );
154 glVertex3fv( v2 );
155
156 glColor4ubv( black );
157 glVertex3fv( v5 );
158 glColor4ubv( yellow );
159 glVertex3fv( v4 );
160 glColor4ubv( purple );
161 glVertex3fv( v7 );
162
163 glColor4ubv( black );
164 glVertex3fv( v5 );
165 glColor4ubv( purple );
166 glVertex3fv( v7 );
167 glColor4ubv( orange );
168 glVertex3fv( v6 );
169
170 glColor4ubv( yellow );
171 glVertex3fv( v4 );
172 glColor4ubv( red );
173 glVertex3fv( v0 );
174 glColor4ubv( white );
175 glVertex3fv( v3 );
176
177 glColor4ubv( yellow );
178 glVertex3fv( v4 );
179 glColor4ubv( white );
180 glVertex3fv( v3 );
181 glColor4ubv( purple );
182 glVertex3fv( v7 );
183
184 glColor4ubv( white );
185 glVertex3fv( v3 );
186 glColor4ubv( blue );
187 glVertex3fv( v2 );
188 glColor4ubv( orange );
189 glVertex3fv( v6 );
190
191 glColor4ubv( white );
192 glVertex3fv( v3 );
193 glColor4ubv( orange );
194 glVertex3fv( v6 );
195 glColor4ubv( purple );
196 glVertex3fv( v7 );
197
198 glColor4ubv( green );
199 glVertex3fv( v1 );
200 glColor4ubv( red );
201 glVertex3fv( v0 );
202 glColor4ubv( yellow );
203 glVertex3fv( v4 );
204
205 glColor4ubv( green );
206 glVertex3fv( v1 );
207 glColor4ubv( yellow );
208 glVertex3fv( v4 );
209 glColor4ubv( black );
210 glVertex3fv( v5 );
211
212 glEnd( );
213
214 /*
215 * EXERCISE:
216 * Draw text telling the user that 'Spc'
217 * pauses the rotation and 'Esc' quits.
218 * Do it using vetors and textured quads.
219 */
220
221 /*
222 * Swap the buffers. This this tells the driver to
223 * render the next frame from the contents of the
224 * back-buffer, and to set all rendering operations
225 * to occur on what was the front-buffer.
226 *
227 * Double buffering prevents nasty visual tearing
228 * from the application drawing on areas of the
229 * screen that are being updated at the same time.
230 */
231 SDL_GL_SwapBuffers( );
232 }
233
234 static void setup_opengl( int width, int height )
235 {
236 float ratio = (float) width / (float) height;
237
238 /* Our shading model--Gouraud (smooth). */
239 glShadeModel( GL_SMOOTH );
240
241 /* Culling. */
242 glCullFace( GL_BACK );
243 glFrontFace( GL_CCW );
244 glEnable( GL_CULL_FACE );
245
246 /* Set the clear color. */
247 glClearColor( 0, 0, 0, 0 );
248
249 /* Setup our viewport. */
250 glViewport( 0, 0, width, height );
251
252 /*
253 * Change to the projection matrix and set
254 * our viewing volume.
255 */
256 glMatrixMode( GL_PROJECTION );
257 glLoadIdentity( );
258 /*
259 * EXERCISE:
260 * Replace this with a call to glFrustum.
261 */
262 gluPerspective( 60.0, ratio, 1.0, 1024.0 );
263 }
264
265 int main( int argc, char* argv[] )
266 {
267 /* Information about the current video settings. */
268 const SDL_VideoInfo* info = NULL;
269 /* Dimensions of our window. */
270 int width = 0;
271 int height = 0;
272 /* Color depth in bits of our window. */
273 int bpp = 0;
274 /* Flags we will pass into SDL_SetVideoMode. */
275 int flags = 0;
276
277 /* First, initialize SDL's video subsystem. */
278 if( SDL_Init( SDL_INIT_VIDEO ) < 0 ) {
279 /* Failed, exit. */
280 fprintf( stderr, "Video initialization failed: %s\n",
281 SDL_GetError( ) );
282 quit_tutorial( 1 );
283 }
284
285 /* Let's get some video information. */
286 info = SDL_GetVideoInfo( );
287
288 if( !info ) {
289 /* This should probably never happen. */
290 fprintf( stderr, "Video query failed: %s\n",
291 SDL_GetError( ) );
292 quit_tutorial( 1 );
293 }
294
295 /*
296 * Set our width/height to 640/480 (you would
297 * of course let the user decide this in a normal
298 * app). We get the bpp we will request from
299 * the display. On X11, VidMode can't change
300 * resolution, so this is probably being overly
301 * safe. Under Win32, ChangeDisplaySettings
302 * can change the bpp.
303 */
304 width = 640;
305 height = 480;
306 bpp = info->vfmt->BitsPerPixel;
307
308 /*
309 * Now, we want to setup our requested
310 * window attributes for our OpenGL window.
311 * We want *at least* 5 bits of red, green
312 * and blue. We also want at least a 16-bit
313 * depth buffer.
314 *
315 * The last thing we do is request a double
316 * buffered window. '1' turns on double
317 * buffering, '0' turns it off.
318 *
319 * Note that we do not use SDL_DOUBLEBUF in
320 * the flags to SDL_SetVideoMode. That does
321 * not affect the GL attribute state, only
322 * the standard 2D blitting setup.
323 */
324 SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );
325 SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 );
326 SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 );
327 SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
328 SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
329
330 /*
331 * We want to request that SDL provide us
332 * with an OpenGL window, in a fullscreen
333 * video mode.
334 *
335 * EXERCISE:
336 * Make starting windowed an option, and
337 * handle the resize events properly with
338 * glViewport.
339 */
340 flags = SDL_OPENGL | SDL_FULLSCREEN;
341
342 /*
343 * Set the video mode
344 */
345 if( SDL_SetVideoMode( width, height, bpp, flags ) == 0 ) {
346 /*
347 * This could happen for a variety of reasons,
348 * including DISPLAY not being set, the specified
349 * resolution not being available, etc.
350 */
351 fprintf( stderr, "Video mode set failed: %s\n",
352 SDL_GetError( ) );
353 quit_tutorial( 1 );
354 }
355
356 /*
357 * At this point, we should have a properly setup
358 * double-buffered window for use with OpenGL.
359 */
360 setup_opengl( width, height );
361
362 /*
363 * Now we want to begin our normal app process--
364 * an event loop with a lot of redrawing.
365 */
366 while( 1 ) {
367 /* Process incoming events. */
368 process_events( );
369 /* Draw the screen. */
370 draw_screen( );
371 }
372
373 /*
374 * EXERCISE:
375 * Record timings using SDL_GetTicks() and
376 * and print out frames per second at program
377 * end.
378 */
379
380 /* Never reached. */
381 return 0;
382 }</stdlib.h></stdio.h></gl></gl></sdl>