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

Last change on this file since 519 was 519, checked in by Sam Hocevar, 11 years ago

core: fix a few useless casts.

File size: 12.6 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 "common.h"
36
37#include "filter.h"
38#include "system.h"
39#include "video.h"
40#include "macs.h"
41#include "image.h"
42#include "setup.h"
43
44SDL_Surface *window = NULL, *surface = NULL;
45image *screen = NULL;
46unsigned char current_background;
47int win_xscale, win_yscale, mouse_xscale, mouse_yscale;
48int xres, yres;
49
50extern palette *lastl;
51extern flags_struct flags;
52#ifdef HAVE_OPENGL
53GLfloat texcoord[4];
54GLuint texid;
55SDL_Surface *texture = NULL;
56#endif
57
58// Forward declarations
59void update_window_part(SDL_Rect *rect);
60void update_window_done();
61
62//
63// power_of_two()
64// Get the nearest power of two
65//
66static int power_of_two(int input)
67{
68    int value;
69    for(value = 1 ; value < input ; value <<= 1);
70    return value;
71}
72
73//
74// set_mode()
75// Set the video mode
76//
77void set_mode(int mode, int argc, char **argv)
78{
79    const SDL_VideoInfo *vidInfo;
80    int vidFlags = SDL_HWPALETTE;
81
82    // Check for video capabilities
83    vidInfo = SDL_GetVideoInfo();
84    if(vidInfo->hw_available)
85        vidFlags |= SDL_HWSURFACE;
86    else
87        vidFlags |= SDL_SWSURFACE;
88
89    if(flags.fullscreen)
90        vidFlags |= SDL_FULLSCREEN;
91
92    if(flags.doublebuf)
93        vidFlags |= SDL_DOUBLEBUF;
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(vec2i(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        // FIXME: we can use GL_ARB_texture_non_power_of_two or
144        // GL_ARB_texture_rectangle to avoid the extra memory allocation
145        w = power_of_two(xres);
146        h = power_of_two(yres);
147
148        // create texture surface
149        texture = SDL_CreateRGBSurface(SDL_SWSURFACE, w , h , 32,
150#if SDL_BYTEORDER == SDL_LIL_ENDIAN
151                0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
152#else
153                0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
154#endif
155
156        // setup 2D gl environment
157        glPushAttrib(GL_ENABLE_BIT);
158        glDisable(GL_DEPTH_TEST);
159        glDisable(GL_CULL_FACE);
160        glEnable(GL_TEXTURE_2D);
161
162        glViewport(0, 0, window->w, window->h);
163
164        glMatrixMode(GL_PROJECTION);
165        glPushMatrix();
166        glLoadIdentity();
167
168        glOrtho(0.0, (GLdouble)window->w, (GLdouble)window->h, 0.0, 0.0, 1.0);
169
170        glMatrixMode(GL_MODELVIEW);
171        glPushMatrix();
172        glLoadIdentity();
173
174        // texture coordinates
175        texcoord[0] = 0.0f;
176        texcoord[1] = 0.0f;
177        texcoord[2] = (GLfloat)xres / texture->w;
178        texcoord[3] = (GLfloat)yres / texture->h;
179
180        // create an RGBA texture for the texture surface
181        glGenTextures(1, &texid);
182        glBindTexture(GL_TEXTURE_2D, texid);
183        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, flags.antialias);
184        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, flags.antialias);
185        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture->w, texture->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture->pixels);
186#endif
187    }
188
189    // Create our 8-bit surface
190    surface = SDL_CreateRGBSurface(SDL_SWSURFACE, window->w, window->h, 8, 0xff, 0xff, 0xff, 0xff);
191    if(surface == NULL)
192    {
193        // Our surface is no good, we have to bail.
194        printf("Video : Unable to create 8-bit surface.\n");
195        exit(1);
196    }
197
198    printf("Video : %dx%d %dbpp\n", window->w, window->h, window->format->BitsPerPixel);
199
200    // Set the window caption
201    SDL_WM_SetCaption("Abuse", "Abuse");
202
203    // Grab and hide the mouse cursor
204    SDL_ShowCursor(0);
205    if(flags.grabmouse)
206        SDL_WM_GrabInput(SDL_GRAB_ON);
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        delete lastl;
219    lastl = NULL;
220    // Free our 8-bit surface
221    if(surface)
222        SDL_FreeSurface(surface);
223
224#ifdef HAVE_OPENGL
225    if (texture)
226        SDL_FreeSurface(texture);
227#endif
228    delete screen;
229}
230
231// put_part_image()
232// Draw only dirty parts of the image
233//
234void put_part_image(image *im, int x, int y, int x1, int y1, int x2, int y2)
235{
236    int xe, ye;
237    SDL_Rect srcrect, dstrect;
238    int ii, jj;
239    int srcx, srcy, xstep, ystep;
240    Uint8 *dpixel;
241    Uint16 dinset;
242
243    if(y > yres || x > xres)
244        return;
245
246    CHECK(x1 >= 0 && x2 >= x1 && y1 >= 0 && y2 >= y1);
247
248    // Adjust if we are trying to draw off the screen
249    if(x < 0)
250    {
251        x1 += -x;
252        x = 0;
253    }
254    srcrect.x = x1;
255    if(x + (x2 - x1) >= xres)
256        xe = xres - x + x1 - 1;
257    else
258        xe = x2;
259
260    if(y < 0)
261    {
262        y1 += -y;
263        y = 0;
264    }
265    srcrect.y = y1;
266    if(y + (y2 - y1) >= yres)
267        ye = yres - y + y1 - 1;
268    else
269        ye = y2;
270
271    if(srcrect.x >= xe || srcrect.y >= ye)
272        return;
273
274    // Scale the image onto the surface
275    srcrect.w = xe - srcrect.x;
276    srcrect.h = ye - srcrect.y;
277    dstrect.x = ((x * win_xscale) >> 16);
278    dstrect.y = ((y * win_yscale) >> 16);
279    dstrect.w = ((srcrect.w * win_xscale) >> 16);
280    dstrect.h = ((srcrect.h * win_yscale) >> 16);
281
282    xstep = (srcrect.w << 16) / dstrect.w;
283    ystep = (srcrect.h << 16) / dstrect.h;
284
285    srcy = ((srcrect.y) << 16);
286    dinset = ((surface->w - dstrect.w)) * surface->format->BytesPerPixel;
287
288    // Lock the surface if necessary
289    if(SDL_MUSTLOCK(surface))
290        SDL_LockSurface(surface);
291
292    dpixel = (Uint8 *)surface->pixels;
293    dpixel += (dstrect.x + ((dstrect.y) * surface->w)) * surface->format->BytesPerPixel;
294
295    // Update surface part
296    if ((win_xscale==1<<16) && (win_yscale==1<<16)) // no scaling or hw scaling
297    {
298        srcy = srcrect.y;
299        dpixel = ((Uint8 *)surface->pixels) + y * surface->w + x ;
300        for(ii=0 ; ii < srcrect.h; ii++)
301        {
302            memcpy(dpixel, im->scan_line(srcy) + srcrect.x , srcrect.w);
303            dpixel += surface->w;
304            srcy ++;
305        }
306    }
307    else    // sw scaling
308    {
309        xstep = (srcrect.w << 16) / dstrect.w;
310        ystep = (srcrect.h << 16) / dstrect.h;
311
312        srcy = ((srcrect.y) << 16);
313        dinset = ((surface->w - dstrect.w)) * surface->format->BytesPerPixel;
314
315        dpixel = (Uint8 *)surface->pixels + (dstrect.x + ((dstrect.y) * surface->w)) * surface->format->BytesPerPixel;
316
317        for(ii = 0; ii < dstrect.h; ii++)
318        {
319            srcx = (srcrect.x << 16);
320            for(jj = 0; jj < dstrect.w; jj++)
321            {
322                memcpy(dpixel, im->scan_line((srcy >> 16)) + ((srcx >> 16) * surface->format->BytesPerPixel), surface->format->BytesPerPixel);
323                dpixel += surface->format->BytesPerPixel;
324                srcx += xstep;
325            }
326            dpixel += dinset;
327            srcy += ystep;
328        }
329//        dpixel += dinset;
330//        srcy += ystep;
331    }
332
333    // Unlock the surface if we locked it.
334    if(SDL_MUSTLOCK(surface))
335        SDL_UnlockSurface(surface);
336
337    // Now blit the surface
338    update_window_part(&dstrect);
339}
340
341//
342// put_image()
343// Draw the entire image
344//
345void put_image(image * im, int x, int y)
346{
347    put_part_image(im, x, y, 0, 0, im->Size().x - 1, im->Size().y - 1);
348}
349
350//
351// update_dirty_window()
352// Update the dirty parts of the window
353//
354void update_dirty_window(image *im, int xoff, int yoff)
355{
356    int count;
357    dirty_rect *dr, *q;
358    CHECK(im->m_special); // make sure the image has the ability to contain dirty areas
359    if(im->m_special->keep_dirt == 0)
360    {
361        put_image(im, xoff, yoff);
362    }
363    else
364    {
365        count = im->m_special->dirties.number_nodes();
366        if(!count)
367            return;  // if nothing to update, return
368        dr = (dirty_rect *)(im->m_special->dirties.first());
369        while(count > 0)
370        {
371            put_part_image(im, xoff + dr->dx1, yoff + dr->dy1, dr->dx1, dr->dy1, dr->dx2 + 1, dr->dy2 + 1);
372            q = dr;
373            dr = (dirty_rect *)(dr->next());
374            im->m_special->dirties.unlink(q);
375            delete q;
376            count--;
377        }
378    }
379}
380
381//
382// update_dirty()
383// Update the dirty parts of the image
384//
385void update_dirty(image *im, int xoff, int yoff)
386{
387    update_dirty_window(im, xoff, yoff);
388    update_window_done();
389}
390
391//
392// load()
393// Set the palette
394//
395void palette::load()
396{
397    if(lastl)
398        delete lastl;
399    lastl = copy();
400
401    // Force to only 256 colours.
402    // Shouldn't be needed, but best to be safe.
403    if(ncolors > 256)
404        ncolors = 256;
405
406    SDL_Color colors[ncolors];
407    for(int ii = 0; ii < ncolors; ii++)
408    {
409        colors[ii].r = red(ii);
410        colors[ii].g = green(ii);
411        colors[ii].b = blue(ii);
412    }
413    SDL_SetColors(surface, colors, 0, ncolors);
414    if(window->format->BitsPerPixel == 8)
415        SDL_SetColors(window, colors, 0, ncolors);
416
417    // Now redraw the surface
418    update_window_part(NULL);
419    update_window_done();
420}
421
422//
423// load_nice()
424//
425void palette::load_nice()
426{
427    load();
428}
429
430// ---- support functions ----
431
432void update_window_done()
433{
434#ifdef HAVE_OPENGL
435    // opengl blit complete surface to window
436    if(flags.gl)
437    {
438        // convert color-indexed surface to RGB texture
439        SDL_BlitSurface(surface, NULL, texture, NULL);
440
441        // Texturemap complete texture to surface so we have free scaling
442        // and antialiasing
443        glTexSubImage2D(GL_TEXTURE_2D, 0,
444                        0, 0, texture->w, texture->h,
445                        GL_RGBA, GL_UNSIGNED_BYTE, texture->pixels);
446        glBegin(GL_TRIANGLE_STRIP);
447        glTexCoord2f(texcoord[0], texcoord[1]); glVertex3i(0, 0, 0);
448        glTexCoord2f(texcoord[2], texcoord[1]); glVertex3i(window->w, 0, 0);
449        glTexCoord2f(texcoord[0], texcoord[3]); glVertex3i(0, window->h, 0);
450        glTexCoord2f(texcoord[2], texcoord[3]); glVertex3i(window->w, window->h, 0);
451        glEnd();
452
453        if(flags.doublebuf)
454            SDL_GL_SwapBuffers();
455    }
456#else
457    // swap buffers in case of double buffering
458    // do nothing in case of single buffering
459    if(flags.doublebuf)
460        SDL_Flip(window);
461#endif
462}
463
464void update_window_part(SDL_Rect *rect)
465{
466    // no partial blit's in case of opengl
467    // complete blit + scaling just before flip
468    if (flags.gl)
469        return;
470
471    SDL_BlitSurface(surface, rect, window, rect);
472
473    // no window update needed until end of run
474    if(flags.doublebuf)
475        return;
476
477    // update window part for single buffer
478    if(rect == NULL)
479        SDL_UpdateRect(window, 0, 0, 0, 0);
480    else
481        SDL_UpdateRect(window, rect->x, rect->y, rect->w, rect->h);
482}
Note: See TracBrowser for help on using the repository browser.