source: abuse/trunk/src/sdlport/video.cpp @ 130

Last change on this file since 130 was 129, checked in by Sam Hocevar, 14 years ago
  • Get rid of jmalloc and replace it with standard malloc. Modern operating systems certainly perform a lot better than this custom implementation, and we have superior tools (eg. valgrind) to debug and profile memory usage without interfering with the code itself.
File size: 13.3 KB
Line 
1/*
2 *  Abuse - dark 2D side-scrolling platform game
3 *  Copyright (c) 2001 Anthony Kruize <trandor@labyrinth.net.au>
4 *
5 *  This program is free software; you can redistribute it and/or modify
6 *  it under the terms of the GNU General Public License as published by
7 *  the Free Software Foundation; either version 2 of the License, or
8 *  (at your option) any later version.
9 *
10 *  This program is distributed in the hope that it will be useful,
11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 *  GNU General Public License for more details.
14 *
15 *  You should have received a copy of the GNU General Public License
16 *  along with this program; if not, write to the Free Software Foundation,
17 *  Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
18 */
19
20#include "config.h"
21
22#include <SDL.h>
23#ifdef HAVE_OPENGL
24#ifdef __APPLE__
25#include <OpenGL/gl.h>
26#include <OpenGL/glu.h>
27#else
28#include <GL/gl.h>
29#include <GL/glu.h>
30#endif    /* __APPLE__ */
31#endif    /* HAVE_OPENGL */
32#include "filter.hpp"
33#include "system.h"
34#include "video.hpp"
35#include "macs.hpp"
36#include "image.hpp"
37#include "setup.h"
38
39SDL_Surface *window = NULL, *surface = NULL;
40image *screen = NULL;
41unsigned char current_background;
42int win_xscale, win_yscale, mouse_xscale, mouse_yscale;
43extern unsigned int xres, yres;
44extern palette *lastl;
45extern flags_struct flags;
46#ifdef HAVE_OPENGL
47GLfloat texcoord[4];
48GLuint texid;
49SDL_Surface *texture = NULL;
50#endif
51
52// Forward declarations
53void update_window_part(SDL_Rect *rect);
54void update_window_done();
55
56//
57// power_of_two()
58// Get the nearest power of two
59//
60static int power_of_two(int input)
61{
62    int value;
63    for( value = 1 ; value < input ; value <<= 1);
64    return value;
65}
66
67//
68// set_mode()
69// Set the video mode
70//
71void set_mode( int mode, int argc, char **argv )
72{
73    const SDL_VideoInfo *vidInfo;
74    int vidFlags = SDL_HWPALETTE;
75
76    // Check for video capabilities
77    vidInfo = SDL_GetVideoInfo();
78    if( vidInfo->hw_available )
79    {
80        vidFlags |= SDL_HWSURFACE;
81    }
82    else
83    {
84        vidFlags |= SDL_SWSURFACE;
85    }
86    if( flags.fullscreen )
87    {
88        vidFlags |= SDL_FULLSCREEN;
89    }
90    if( flags.doublebuf )
91    {
92        vidFlags |= SDL_DOUBLEBUF;
93    }
94
95    // Calculate the window scale
96    win_xscale = mouse_xscale = (flags.xres << 16) / xres;
97    win_yscale = mouse_yscale = (flags.yres << 16) / yres;
98
99    // Try using opengl hw accell
100    if( flags.gl ) {
101#ifdef HAVE_OPENGL
102        printf( "Video : OpenGL enabled\n" );
103        // allow doublebuffering in with gl too
104        SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, flags.doublebuf );
105        // set video gl capability
106        vidFlags |= SDL_OPENGL;
107        // force no scaling, let the hw do it
108        win_xscale = win_yscale = 1 << 16;
109#else
110        // ignore the option if not available
111        printf( "Video : OpenGL disabled (Support missing in executable)\n");
112        flags.gl = 0;
113#endif
114    }
115
116    // Set the icon for this window.  Looks nice on taskbars etc.
117    SDL_WM_SetIcon( SDL_LoadBMP("abuse.bmp"), NULL );
118
119    // Create the window with a preference for 8-bit (palette animations!), but accept any depth */
120    window = SDL_SetVideoMode(flags.xres, flags.yres, 8, vidFlags | SDL_ANYFORMAT );
121    if( window == NULL )
122    {
123        printf( "Video : Unable to set video mode : %s\n", SDL_GetError() );
124        exit( 1 );
125    }
126
127    // Create the screen image
128    screen = new image( xres, yres, NULL, 2 );
129    if( screen == NULL )
130    {
131        // Our screen image is no good, we have to bail.
132        printf( "Video : Unable to create screen image.\n" );
133        exit( 1 );
134    }
135    screen->clear();
136
137    if (flags.gl)
138    {
139#ifdef HAVE_OPENGL
140        int w,h;
141
142        // texture width/height should be power of 2
143        w = power_of_two(xres);
144        h = power_of_two(yres);
145
146        // create texture surface
147        texture = SDL_CreateRGBSurface( SDL_SWSURFACE, w ,h ,32,
148#if SDL_BYTEORDER == SDL_LIL_ENDIAN
149                0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000 );
150#else
151                0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF );
152#endif
153
154        // setup 2D gl environment
155        glPushAttrib(GL_ENABLE_BIT);
156        glDisable(GL_DEPTH_TEST);
157        glDisable(GL_CULL_FACE);
158        glEnable(GL_TEXTURE_2D);
159
160        glViewport(0, 0, window->w, window->h);
161
162        glMatrixMode(GL_PROJECTION);
163        glPushMatrix();
164        glLoadIdentity();
165
166        glOrtho(0.0, (GLdouble)window->w, (GLdouble)window->h, 0.0, 0.0,1.0);
167
168        glMatrixMode(GL_MODELVIEW);
169        glPushMatrix();
170        glLoadIdentity();
171
172        // texture coordinates
173        texcoord[0] = 0.0f;
174        texcoord[1] = 0.0f;
175        texcoord[2] = (GLfloat)xres / texture->w;
176        texcoord[3] = (GLfloat)yres / texture->h;
177
178        // create an RGBA texture for the texture surface
179        glGenTextures(1, &texid);
180        glBindTexture(GL_TEXTURE_2D, texid);
181        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, flags.antialias);
182        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, flags.antialias);
183        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture->w, texture->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture->pixels );
184#endif
185    }
186
187    // Create our 8-bit surface
188    surface = SDL_CreateRGBSurface( SDL_SWSURFACE, window->w, window->h, 8, 0xff, 0xff, 0xff, 0xff );
189    if( surface == NULL )
190    {
191        // Our surface is no good, we have to bail.
192        printf( "Video : Unable to create 8-bit surface.\n" );
193        exit( 1 );
194    }
195
196    printf( "Video : %dx%d %dbpp\n", window->w, window->h, window->format->BitsPerPixel );
197
198    // Set the window caption
199    SDL_WM_SetCaption( "Abuse-SDL", "Abuse-SDL" );
200
201    // Grab and hide the mouse cursor
202    SDL_ShowCursor( 0 );
203    if( flags.grabmouse )
204    {
205        SDL_WM_GrabInput( SDL_GRAB_ON );
206    }
207
208    update_dirty( screen );
209}
210
211//
212// close_graphics()
213// Shutdown the video mode
214//
215void close_graphics()
216{
217    if( lastl )
218    {
219        delete lastl;
220    }
221    lastl = NULL;
222    // Free our 8-bit surface
223    if( surface )
224    {
225        SDL_FreeSurface( surface );
226    }
227#ifdef HAVE_OPENGL
228    if ( texture )
229    {
230        SDL_FreeSurface( texture );
231    }
232#endif
233    delete screen;
234}
235
236// put_part_image()
237// Draw only dirty parts of the image
238//
239void put_part_image( image *im, int x, int y, int x1, int y1, int x2, int y2 )
240{
241    int xe, ye;
242    SDL_Rect srcrect, dstrect;
243    int ii, jj;
244    int srcx, srcy, xstep, ystep;
245    Uint8 *dpixel;
246    Uint16 dinset;
247
248    if( (unsigned)y > yres || (unsigned)x > xres )
249    {
250        return;
251    }
252    CHECK( x1 >= 0 && x2 >= x1 && y1 >= 0 && y2 >= y1 );
253
254    // Adjust if we are trying to draw off the screen
255    if( x < 0 )
256    {
257        x1 += -x;
258        x = 0;
259    }
260    srcrect.x = x1;
261    if( (unsigned)(x + ( x2 - x1 )) >= xres )
262    {
263        xe = xres - x + x1 - 1;
264    }
265    else
266    {
267        xe = x2;
268    }
269    if( y < 0 )
270    {
271        y1 += -y;
272        y = 0;
273    }
274    srcrect.y = y1;
275    if( (unsigned)(y + ( y2 - y1 )) >= yres )
276    {
277        ye = yres - y + y1 - 1;
278    }
279    else
280    {
281        ye = y2;
282    }
283    if( srcrect.x >= xe || srcrect.y >= ye )
284        return;
285
286    // Scale the image onto the surface
287    srcrect.w = xe - srcrect.x;
288    srcrect.h = ye - srcrect.y;
289    dstrect.x = ((x * win_xscale) >> 16);
290    dstrect.y = ((y * win_yscale) >> 16);
291    dstrect.w = ((srcrect.w * win_xscale) >> 16);
292    dstrect.h = ((srcrect.h * win_yscale) >> 16);
293
294    xstep = (srcrect.w << 16) / dstrect.w;
295    ystep = (srcrect.h << 16) / dstrect.h;
296
297    srcy = ((srcrect.y) << 16);
298    dinset = ((surface->w - dstrect.w)) * surface->format->BytesPerPixel;
299
300    // Lock the surface if necessary
301    if( SDL_MUSTLOCK( surface ) )
302    {
303        SDL_LockSurface( surface );
304    }
305    dpixel = (Uint8 *)surface->pixels;
306    dpixel += (dstrect.x + ((dstrect.y) * surface->w)) * surface->format->BytesPerPixel;
307
308    // Update surface part
309    if ((win_xscale==1<<16) && (win_yscale==1<<16)) // no scaling or hw scaling
310    {
311        srcy = srcrect.y;
312        dpixel = ((Uint8 *)surface->pixels) + y * surface->w + x ;
313        for( ii=0 ; ii < srcrect.h; ii++ )
314        {
315            memcpy( dpixel, im->scan_line( srcy ) + srcrect.x , srcrect.w );
316            dpixel += surface->w;
317            srcy ++;
318        }
319    }
320    else    // sw scaling
321    {
322        xstep = (srcrect.w << 16) / dstrect.w;
323        ystep = (srcrect.h << 16) / dstrect.h;
324
325        srcy = ((srcrect.y) << 16);
326        dinset = ((surface->w - dstrect.w)) * surface->format->BytesPerPixel;
327
328        dpixel = (Uint8 *)surface->pixels + (dstrect.x + ((dstrect.y) * surface->w)) * surface->format->BytesPerPixel;
329
330        for( ii = 0; ii < dstrect.h; ii++ )
331        {
332            srcx = (srcrect.x << 16);
333            for( jj = 0; jj < dstrect.w; jj++ )
334            {
335                memcpy( dpixel, im->scan_line( (srcy >> 16) ) + ((srcx >> 16) * surface->format->BytesPerPixel), surface->format->BytesPerPixel );
336                dpixel += surface->format->BytesPerPixel;
337                srcx += xstep;
338            }
339            dpixel += dinset;
340            srcy += ystep;
341        }
342//        dpixel += dinset;
343//        srcy += ystep;
344    }
345
346    // Unlock the surface if we locked it.
347    if( SDL_MUSTLOCK( surface ) )
348    {
349        SDL_UnlockSurface( surface );
350    }
351
352    // Now blit the surface
353    update_window_part( &dstrect);
354}
355
356//
357// put_image()
358// Draw the entire image
359//
360void put_image( image * im, int x, int y )
361{
362    put_part_image( im, x, y, 0, 0, im->width() - 1, im->height() - 1 );
363}
364
365//
366// update_dirty_window()
367// Update the dirty parts of the window
368//
369void update_dirty_window( image *im, int xoff, int yoff )
370{
371    int count;
372    dirty_rect *dr, *q;
373    CHECK( im->special ); // make sure the image has the ability to contain dirty areas
374    if( im->special->keep_dirt == 0 )
375    {
376        put_image( im, xoff, yoff );
377    }
378    else
379    {
380        count = im->special->dirties.number_nodes();
381        if( !count )
382            return;  // if nothing to update, return
383        dr = (dirty_rect *)( im->special->dirties.first() );
384        while( count > 0 )
385        {
386            put_part_image( im, xoff + dr->dx1, yoff + dr->dy1, dr->dx1, dr->dy1, dr->dx2 + 1, dr->dy2 + 1 );
387            q = dr;
388            dr = (dirty_rect *)( dr->next() );
389            im->special->dirties.unlink( ( linked_node *)q );
390            delete q;
391            count--;
392        }
393    }
394}
395
396//
397// update_dirty()
398// Update the dirty parts of the image
399//
400void update_dirty( image *im, int xoff, int yoff )
401{
402    update_dirty_window( im, xoff, yoff );
403    update_window_done();
404}
405
406//
407// make_page()
408//
409void image::make_page( short width, short height, unsigned char *page_buffer )
410{
411    if( page_buffer )
412        data = page_buffer;
413    else
414        data = (unsigned char *)malloc( width * height );
415}
416
417//
418// delete_page()
419//
420void image::delete_page()
421{
422    if( !special || !special->static_mem )
423        free( data );
424}
425
426//
427// load()
428// Set the palette
429//
430void palette::load()
431{
432    if( lastl )
433    {
434        delete lastl;
435    }
436    lastl = copy();
437
438    // Force to only 256 colours.
439    // Shouldn't be needed, but best to be safe.
440    if( ncolors > 256 )
441    {
442        ncolors = 256;
443    }
444
445    SDL_Color colors[ncolors];
446    for( int ii = 0; ii < ncolors; ii++ )
447    {
448        colors[ii].r = red(ii);
449        colors[ii].g = green(ii);
450        colors[ii].b = blue(ii);
451    }
452    SDL_SetColors( surface, colors, 0, ncolors );
453    if( window->format->BitsPerPixel == 8 )
454    {
455        SDL_SetColors( window, colors, 0, ncolors );
456    }
457
458    // Now redraw the surface
459    update_window_part( NULL );
460    update_window_done();
461}
462
463//
464// load_nice()
465//
466void palette::load_nice()
467{
468    load();
469}
470
471// ---- support functions ----
472
473void update_window_done()
474{
475    // opengl blit complete surface to window
476    if (flags.gl)
477    {
478#ifdef HAVE_OPENGL
479        // convert color-indexed surface to RGB texture
480        SDL_BlitSurface( surface, NULL, texture, NULL );
481
482        // Texturemap complete texture to surface so we have free scaling
483        // and antialiasing
484        glTexSubImage2D( GL_TEXTURE_2D,0,
485             0,0,texture->w,texture->h,
486             GL_RGBA,GL_UNSIGNED_BYTE,texture->pixels);
487        glBegin(GL_TRIANGLE_STRIP);
488        glTexCoord2f(texcoord[0], texcoord[1]); glVertex3i(0, 0, 0);
489        glTexCoord2f(texcoord[2], texcoord[1]); glVertex3i(window->w, 0, 0);
490        glTexCoord2f(texcoord[0], texcoord[3]); glVertex3i(0, window->h, 0);
491        glTexCoord2f(texcoord[2], texcoord[3]); glVertex3i(window->w, window->h, 0);
492        glEnd();
493#endif
494    }
495
496    // swap buffers in case of double buffering
497    if (flags.doublebuf)
498    {
499        if (flags.gl)
500        {
501#ifdef HAVE_OPENGL
502            SDL_GL_SwapBuffers();
503#endif
504        }
505        else
506        {
507            SDL_Flip(window);
508        }
509    }
510
511    // do nothing in case of single buffering
512}
513
514void update_window_part( SDL_Rect *rect)
515{
516    // no partial blit's in case of opengl
517    // complete blit + scaling just before flip
518    if (flags.gl)
519    {
520        return;
521    }
522
523    SDL_BlitSurface( surface, rect, window, rect );
524
525    // no window update needed until end of run
526    if( flags.doublebuf)
527    {
528        return;
529    }
530
531    // update window part for single buffer
532    if( rect == NULL )
533    {
534        SDL_UpdateRect( window, 0, 0, 0, 0 );
535    }
536    else
537    {
538        SDL_UpdateRect( window, rect->x, rect->y, rect->w, rect->h );
539    }
540}
Note: See TracBrowser for help on using the repository browser.