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

Last change on this file since 494 was 494, checked in by Sam Hocevar, 12 years ago

style: remove trailing spaces, fix copyright statements.

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