sdl.c

Go to the documentation of this file.
00001 #include <assert.h>
00002 #include <stdio.h>
00003 #include <stdlib.h>
00004 #include <unistd.h>
00005 #include <time.h>
00006 #include <sys/time.h>
00007 #include <math.h>
00008 #include <signal.h>
00009 #include <memory.h>
00010 #include <SDL.h>
00011 #include "Buzz_inc.h"
00012 #include "macros.h"
00013 #include "options.h"
00014 #include "utils.h"
00015 #include "logging.h"
00016 #include "av.h"
00017 #define MAX_X   320
00018 #define MAX_Y   200
00019 
00020 LOG_DEFAULT_CATEGORY(sdl);
00021 
00022 #define KEYBUF_SIZE 256
00023 static int keybuf[KEYBUF_SIZE];
00024 static int keybuf_in_idx, keybuf_out_idx;
00025 
00026 int av_mouse_cur_x, av_mouse_cur_y;
00027 int av_mouse_pressed_x, av_mouse_pressed_y;
00028 int av_mouse_pressed_cur;
00029 int av_mouse_pressed_latched;
00030 
00031 unsigned char *screen;
00032 
00033 SDL_Surface *display;
00034 SDL_Overlay *video_overlay;
00035 SDL_Overlay *news_overlay;
00036 SDL_Rect video_rect;
00037 SDL_Rect news_rect;
00038 static SDL_Surface *screen_surf;
00039 static SDL_Surface *screen_surf2x;
00040 
00041 static SDL_Color pal_colors[256];
00042 
00043 static struct audio_channel Channels[AV_NUM_CHANNELS];
00044 
00045 /** RGB color palette. */
00046 unsigned char pal[3 * 256];
00047 
00048 /* information about current fading operation */
00049 static struct fade_information {
00050     unsigned from;
00051     unsigned to;
00052     unsigned step;
00053     unsigned steps;
00054     unsigned force_black;
00055     int inc;
00056     unsigned end;
00057 } fade_info;
00058 
00059 /** Indicates that screen redraw is required. */
00060 int screen_dirty;
00061 
00062 static int have_audio;
00063 
00064 static int do_fading;
00065 
00066 static SDL_AudioSpec audio_desired;
00067 
00068 static unsigned char *dirty_tree;
00069 static unsigned dirty_tree_length;
00070 static SDL_Rect *dirty_rect_list;
00071 
00072 static void
00073 alloc_dirty_tree(void)
00074 {
00075     int depth = AV_DTREE_DEPTH + 1;
00076     int ratio = 1;
00077     int bytes = 0;
00078 
00079     /* use power series formula, S = (1 - q**(n+1))/(1 - q) */
00080     while (depth--)
00081         ratio *= 4;
00082     bytes = (1 - ratio) / (1 - 4);
00083     dirty_tree = xcalloc(bytes, 1);
00084     dirty_tree_length = bytes;
00085 
00086     ratio /= 4;
00087     dirty_rect_list = xcalloc(ratio, sizeof(SDL_Rect));
00088 }
00089 
00090 static int get_dirty_rect_list();
00091 
00092 static void
00093 audio_callback(void *userdata, Uint8 * stream, int len)
00094 {
00095     int ch = 0;
00096 
00097     for (ch = 0; ch < AV_NUM_CHANNELS; ++ch)
00098     {
00099         int pos = 0;
00100         struct audio_channel *chp = &Channels[ch];
00101 
00102         if (!chp->mute && chp->volume)
00103         {
00104             struct audio_chunk *ac = chp->chunk;
00105 
00106             while (ac)
00107             {
00108                 int bytes =
00109                     min(len - pos, (int) ac->size - (int) chp->offset);
00110 
00111                 /*
00112                  * SDL docs say that this should not be used to mix more than
00113                  * 2 channels, BUT SDL_mixer library just does that for each
00114                  * stream! Anyway, we have 2 channels so no worries ;)
00115                  */
00116                 SDL_MixAudio(stream + pos, ac->data + chp->offset,
00117                         bytes, chp->volume);
00118 
00119                 pos += bytes;
00120                 chp->offset += bytes;
00121 
00122                 if (chp->offset == ac->size)
00123                 {
00124                     chp->offset = 0;
00125                     if (!ac->loop)
00126                     {
00127                         ac = chp->chunk = chp->chunk->next;
00128                         if (!chp->chunk)
00129                             chp->chunk_tailp = &chp->chunk;
00130                         /* why this tailp?? */
00131                     }
00132                 }
00133 
00134                 if (pos == len)
00135                     break;
00136 
00137             }
00138         }
00139     }
00140 }
00141 
00142 /** Check if animation sound playback is in progress.
00143  * Currently #AV_SOUND_CHANNEL is used only for animation sounds.
00144  * \return 0 means busy playing audio; 1 means idle
00145  */
00146 char
00147 AnimSoundCheck(void)
00148 {
00149     /* assume sound channel */
00150     av_step();
00151     if (Channels[AV_SOUND_CHANNEL].chunk)
00152         return (0);
00153     return (1);
00154 }
00155 
00156 int
00157 IsChannelMute(int channel)
00158 {
00159     assert(channel >= 0 && channel < AV_NUM_CHANNELS);
00160     if (!have_audio)
00161         return 1;
00162     return Channels[channel].mute;
00163 }
00164 
00165 void
00166 play(struct audio_chunk *new_chunk, int channel)
00167 {
00168     struct audio_chunk *cp;
00169     struct audio_channel *chp;
00170 
00171     assert(channel >= 0 && channel < AV_NUM_CHANNELS);
00172 
00173     chp = &Channels[channel];
00174 
00175     if (!have_audio)
00176         return;
00177 
00178     SDL_LockAudio();
00179     for (cp = chp->chunk; cp; cp = cp->next)
00180     {
00181         if (cp == new_chunk)
00182         {
00183             DEBUG1("attempt to do add duplicate chunk");
00184             av_silence(channel);
00185             break;
00186         }
00187     }
00188 
00189     new_chunk->next = NULL;
00190     *chp->chunk_tailp = new_chunk;
00191     SDL_UnlockAudio();
00192 }
00193 
00194 void
00195 av_silence(int channel)
00196 {
00197     int i = channel;
00198 
00199     if (channel == AV_ALL_CHANNELS)
00200     {
00201         for (i = 0; i < AV_NUM_CHANNELS; ++i)
00202             av_silence(i);
00203     }
00204     else
00205     {
00206         assert(channel >= 0 && channel < AV_NUM_CHANNELS);
00207         if (Channels[channel].chunk)
00208         {
00209             SDL_LockAudio();
00210             Channels[channel].chunk = NULL;
00211             Channels[channel].chunk_tailp = &Channels[channel].chunk;
00212             Channels[channel].offset = 0;
00213             SDL_UnlockAudio();
00214         }
00215     }
00216 }
00217 
00218 Uint32
00219 sdl_timer_callback(Uint32 interval, void *param)
00220 {
00221     static SDL_Event tick;
00222 
00223     tick.type = SDL_USEREVENT;
00224     SDL_PushEvent(&tick);
00225     return (interval);
00226 }
00227 
00228 /**
00229  * Setup SDL audio, video and window subsystems.
00230  */
00231 void
00232 av_setup(void)
00233 {
00234     unsigned video_flags = SDL_SWSURFACE;
00235 
00236 #ifndef CONFIG_MACOSX
00237     char *icon_path = NULL;
00238 #endif
00239 
00240     if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0)
00241     {
00242         CRITICAL2("SDL_Init error: %s", SDL_GetError());
00243         exit(EXIT_FAILURE);
00244     }
00245 
00246     atexit(SDL_Quit);
00247 
00248     if (options.want_audio)
00249     {
00250 #ifdef CONFIG_WIN32
00251         /*
00252          * default direct-audio-something has got unreasonably long audio buffers,
00253          * but if user knows what he's doing then no problemo...
00254          */
00255         if (!SDL_getenv("SDL_AUDIODRIVER"))
00256         {
00257             INFO1("fixing WIN32 audio driver setup");
00258             SDL_putenv("SDL_AUDIODRIVER=waveout");
00259         }
00260         /*
00261          * also some sources mention that on win audio needs to be initialised
00262          * together with video. Maybe, it works for me as it is now.
00263          */
00264 #endif
00265         if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
00266         {
00267             ERROR2("audio initialization failed: %s", SDL_GetError());
00268         }
00269         else
00270         {
00271             NOTICE1("audio subsystem initialized");
00272             have_audio = 1;
00273         }
00274     }
00275     else
00276         NOTICE1("no audio");
00277 
00278     if (options.want_fullscreen)
00279     {
00280         video_flags |= SDL_FULLSCREEN;
00281         NOTICE1("fullscreen mode enabled");
00282     }
00283 
00284 #ifndef CONFIG_MACOSX
00285     if ((icon_path = locate_file("moon_32x32.bmp", FT_IMAGE)))
00286     {
00287         SDL_Surface *icon = SDL_LoadBMP(icon_path);
00288 
00289         if (icon != NULL)
00290             SDL_WM_SetIcon(icon, NULL);
00291         else
00292             INFO2("setting icon failed: %s\n", SDL_GetError());
00293         free(icon_path);
00294     }
00295 #endif
00296 
00297 #ifdef PACKAGE_BUILD
00298     SDL_WM_SetCaption(PACKAGE_NAME " " PACKAGE_VERSION " build "
00299         PACKAGE_BUILD, NULL);
00300 #else
00301     SDL_WM_SetCaption(PACKAGE_STRING, NULL);
00302 #endif
00303 
00304     if ((display =
00305             SDL_SetVideoMode(MAX_X * 2, MAX_Y * 2, 24, video_flags)) == NULL)
00306     {
00307         CRITICAL2("SDL_SetVideoMode failed: %s", SDL_GetError());
00308         exit(EXIT_FAILURE);
00309     }
00310 
00311     screen = xcalloc(MAX_X * MAX_Y, 1);
00312     screen_surf = SDL_CreateRGBSurfaceFrom(screen, MAX_X, MAX_Y, 8,
00313         MAX_X, 0, 0, 0, 0);
00314     if (!screen_surf)
00315     {
00316         CRITICAL2("can't create screen surface: %s", SDL_GetError());
00317         exit(EXIT_FAILURE);
00318     }
00319     screen_surf2x =
00320         SDL_CreateRGBSurface(SDL_SWSURFACE, MAX_X * 2, MAX_Y * 2, 8, ~0, ~0,
00321         ~0, 0);
00322     if (!screen_surf2x)
00323     {
00324         CRITICAL2("can't create screen_2x surface: %s", SDL_GetError());
00325         exit(EXIT_FAILURE);
00326     }
00327 
00328     /* XXX: Hardcoded video width & height */
00329     video_overlay =
00330         SDL_CreateYUVOverlay(160, 100, SDL_YV12_OVERLAY, display);
00331     if (!video_overlay)
00332     {
00333         CRITICAL2("can't create video_overlay: %s", SDL_GetError());
00334         exit(EXIT_FAILURE);
00335     }
00336     news_overlay = SDL_CreateYUVOverlay(312, 106, SDL_YV12_OVERLAY, display);
00337     /* XXX: Hardcoded video width & height */
00338     if (!news_overlay)
00339     {
00340         CRITICAL2("can't create news_overlay: %s", SDL_GetError());
00341         exit(EXIT_FAILURE);
00342     }
00343 
00344     fade_info.step = 1;
00345     fade_info.steps = 1;
00346     do_fading = 1;
00347 
00348     alloc_dirty_tree();
00349 
00350     SDL_EnableUNICODE(1);
00351     SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,
00352         SDL_DEFAULT_REPEAT_INTERVAL);
00353 
00354     if (have_audio)
00355     {
00356         int i = 0;
00357 
00358         audio_desired.freq = 11025;
00359         audio_desired.format = AUDIO_U8;
00360         audio_desired.channels = 1;
00361         /* audio was unresponsive on win32 so let's use shorter buffer */
00362         audio_desired.samples = 2048;   /* was 8192 */
00363         audio_desired.callback = audio_callback;
00364 
00365         /* initialize audio channels */
00366         for (i = 0; i < AV_NUM_CHANNELS; ++i)
00367         {
00368             Channels[i].volume = AV_MAX_VOLUME;
00369             Channels[i].mute = 0;
00370             Channels[i].chunk = NULL;
00371             Channels[i].chunk_tailp = &Channels[i].chunk;
00372             Channels[i].offset = 0;
00373         }
00374 
00375         /* we don't care what we got, library will convert for us */
00376         if (SDL_OpenAudio(&audio_desired, NULL) < 0)
00377         {
00378             ERROR2("SDL_OpenAudio error: %s", SDL_GetError());
00379             NOTICE1("disabling audio");
00380             have_audio = 0;
00381         }
00382         else
00383             SDL_PauseAudio(0);
00384     }
00385 
00386     SDL_AddTimer(30, sdl_timer_callback, NULL);
00387 }
00388 
00389 static void
00390 av_process_event(SDL_Event * evp)
00391 {
00392     int c;
00393 
00394     switch (evp->type)
00395     {
00396         case SDL_QUIT:
00397             exit(0);
00398             break;
00399 
00400         case SDL_USEREVENT:
00401             break;
00402 
00403         case SDL_KEYDOWN:
00404             switch (evp->key.keysym.sym)
00405             {
00406                 case SDLK_UP:
00407                     c = 0x4800;
00408                     break;
00409                 case SDLK_DOWN:
00410                     c = 0x5000;
00411                     break;
00412                 case SDLK_RIGHT:
00413                     c = 0x4D00;
00414                     break;
00415                 case SDLK_LEFT:
00416                     c = 0x4B00;
00417                     break;
00418                 case SDLK_F1:
00419                     c = 0x3B00;
00420                     break;
00421                 case SDLK_F2:
00422                     c = 0x3C00;
00423                     break;
00424                 case SDLK_F3:
00425                     c = 0x3D00;
00426                     break;
00427                 default:
00428                     c = evp->key.keysym.unicode;
00429                     break;
00430             }
00431             if (c)
00432             {
00433                 keybuf[keybuf_in_idx] = c;
00434                 keybuf_in_idx = (keybuf_in_idx + 1) % KEYBUF_SIZE;
00435             }
00436             break;
00437 
00438         case SDL_MOUSEBUTTONDOWN:
00439             av_mouse_pressed_cur = 1;
00440             av_mouse_pressed_latched = 1;
00441             av_mouse_pressed_x = evp->button.x;
00442             av_mouse_pressed_y = evp->button.y;
00443             TRACE4("mouseclick(%d, %d) b = %d", av_mouse_pressed_x,
00444                     av_mouse_pressed_y, evp->button.button);
00445             break;
00446 
00447         case SDL_MOUSEBUTTONUP:
00448             av_mouse_pressed_cur = 0;
00449 
00450             /* if we get a mouse wheel event then translate it to arrow keypress */
00451             if (evp->button.button == SDL_BUTTON_WHEELUP
00452                     || evp->button.button == SDL_BUTTON_WHEELDOWN)
00453             {
00454                 SDL_Event ev;
00455                 int up = evp->button.button == SDL_BUTTON_WHEELUP;
00456                 SDLMod mod = SDL_GetModState();
00457                 SDLKey key;
00458                 
00459                 if (mod & KMOD_SHIFT)
00460                     key = up ? SDLK_LEFT : SDLK_RIGHT;
00461                 else
00462                     key = up ? SDLK_UP : SDLK_DOWN;
00463 
00464                 ev.type = SDL_KEYDOWN;
00465                 ev.key.type = SDL_KEYDOWN;
00466                 ev.key.state = SDL_RELEASED;
00467                 ev.key.keysym.scancode = 0;
00468                 ev.key.keysym.mod = mod;
00469                 ev.key.keysym.unicode = 0;
00470                 ev.key.keysym.sym = key;
00471                 av_process_event(&ev);
00472             }
00473             break;
00474 
00475         case SDL_MOUSEMOTION:
00476             av_mouse_cur_x = evp->motion.x;
00477             av_mouse_cur_y = evp->motion.y;
00478             break;
00479 
00480             /* ignore these events */
00481         case SDL_KEYUP:
00482         case SDL_ACTIVEEVENT:
00483             break;
00484         default:
00485             DEBUG2("got unknown event %d", evp->type);
00486             break;
00487     }
00488 }
00489 
00490 /* non-blocking */
00491 void
00492 av_step(void)
00493 {
00494     SDL_Event ev;
00495     
00496     /* Have the music system update itself as required */
00497     music_pump();
00498 
00499     while (SDL_PollEvent(&ev))
00500         av_process_event(&ev);
00501 }
00502 
00503 /**
00504  * Block until an SDL event comes in.
00505  *
00506  * We have a 30ms timer going, so that is the
00507  * maximum wait time.
00508  */
00509 void
00510 av_block(void)
00511 {
00512     SDL_Event ev;
00513 
00514     if (SDL_WaitEvent(&ev))
00515     {
00516         av_process_event(&ev);
00517         av_step();                 /* soak up any other currently available events */
00518     }
00519 }
00520 
00521 int
00522 bioskey(int peek)
00523 {
00524     int c;
00525 
00526     av_step();
00527 
00528     if (peek)
00529     {
00530         if (keybuf_in_idx != keybuf_out_idx)
00531             return (1);
00532         return (0);
00533     }
00534 
00535     if (keybuf_in_idx == keybuf_out_idx)
00536         return (0);
00537 
00538     c = keybuf[keybuf_out_idx];
00539     keybuf_out_idx = (keybuf_out_idx + 1) % KEYBUF_SIZE;
00540 
00541     return (c);
00542 }
00543 
00544 void
00545 UpdateAudio(void)
00546 {
00547 //  av_step ();
00548 }
00549 
00550 void
00551 NUpdateVoice(void)
00552 {
00553     av_step();
00554 }
00555 
00556 static SDL_Surface *
00557 SDL_Scale2x(SDL_Surface * src, SDL_Surface * dst)
00558 {
00559     int x, y, bpp;
00560     uint8_t *from, *to;
00561     SDL_Rect clp;
00562     SDL_PixelFormat *pf;
00563 
00564     assert(src);
00565     assert(src != dst);
00566 
00567     pf = src->format;
00568 
00569     if (!dst)
00570         dst = SDL_CreateRGBSurface(SDL_SWSURFACE,
00571             2 * src->w, 2 * src->h,
00572             pf->BitsPerPixel, pf->Rmask, pf->Gmask, pf->Bmask, pf->Amask);
00573 
00574     if (!dst)
00575         return NULL;
00576 
00577     bpp = pf->BytesPerPixel;
00578 
00579     if (2 * src->h != dst->h
00580         || 2 * src->w != dst->w || bpp != dst->format->BytesPerPixel)
00581     {
00582         SDL_SetError("dst surface size or bpp mismatch (%d vs %d)",
00583             bpp, dst->format->BytesPerPixel);
00584         return NULL;
00585     }
00586 
00587     if (bpp == 1)
00588         SDL_SetColors(dst, pf->palette->colors, 0, pf->palette->ncolors);
00589 
00590     if (SDL_MUSTLOCK(src))
00591         SDL_LockSurface(src);
00592     if (SDL_MUSTLOCK(dst))
00593         SDL_LockSurface(dst);
00594 
00595     SDL_GetClipRect(dst, &clp);
00596 
00597     for (y = clp.y / 2; y < clp.y / 2 + clp.h / 2; ++y)
00598     {
00599         for (x = clp.x / 2; x < clp.x / 2 + clp.w / 2; ++x)
00600         {
00601             from = ((uint8_t *) src->pixels) + y * src->pitch + x * bpp;
00602             to = ((uint8_t *) dst->pixels) + 2 * y * dst->pitch +
00603                 2 * x * bpp;
00604             switch (bpp)
00605             {
00606 #define ASSIGN do { \
00607                     *(TYPE (to)) = *(TYPE from); \
00608                     *(TYPE (to+bpp)) = *(TYPE from); \
00609                     *(TYPE (to+dst->pitch)) = *(TYPE from); \
00610                     *(TYPE (to+dst->pitch+bpp)) = *(TYPE from); \
00611                 } while (0)
00612 
00613                 case 1:
00614 #define TYPE (uint8_t *)
00615                     ASSIGN;
00616                     break;
00617 #undef TYPE
00618                 case 2:
00619 #define TYPE (uint16_t *)
00620                     ASSIGN;
00621                     break;
00622 #undef TYPE
00623                 case 3:
00624 #define TYPE (uint8_t *)
00625                     ASSIGN;
00626                     to++;
00627                     from++;
00628                     ASSIGN;
00629                     to++;
00630                     from++;
00631                     ASSIGN;
00632                     to++;
00633                     from++;
00634                     break;
00635 #undef TYPE
00636                 case 4:
00637 #define TYPE (uint32_t *)
00638                     ASSIGN;
00639                     break;
00640 #undef TYPE
00641 #undef ASSIGN
00642             }
00643         }
00644     }
00645 
00646     if (SDL_MUSTLOCK(dst))
00647         SDL_UnlockSurface(dst);
00648     if (SDL_MUSTLOCK(src))
00649         SDL_UnlockSurface(src);
00650 
00651     return dst;
00652 }
00653 
00654 static void
00655 transform_palette(void)
00656 {
00657     unsigned i, j, step, steps;
00658     struct range {
00659         unsigned start, end;
00660     } ranges[] = {{0, fade_info.from}, {fade_info.to, 256}};
00661 
00662     for (j = 0; j < ARRAY_LENGTH(ranges); ++j)
00663         for (i = ranges[j].start; i < ranges[j].end; ++i)
00664         {
00665             if (!fade_info.force_black)
00666             {
00667                 pal_colors[i].r = pal[3 * i] * 4;
00668                 pal_colors[i].g = pal[3 * i + 1] * 4;
00669                 pal_colors[i].b = pal[3 * i + 2] * 4;
00670             }
00671             else
00672             {
00673                 pal_colors[i].r = 0;
00674                 pal_colors[i].g = 0;
00675                 pal_colors[i].b = 0;
00676             }
00677         }
00678     step = fade_info.step;
00679     steps = fade_info.steps;
00680     /* sanity checks */
00681     assert(steps != 0 && step <= steps);
00682     for (i = fade_info.from; i < fade_info.to; ++i)
00683     {
00684         /* 
00685          * This should be done this way, but unfortunately some image files
00686          * have palettes for which pal * 4 overflows single byte. They display
00687          * correctly in game, but not when multiplication factor varies.
00688         pal_colors[i].r = pal[3 * i] * 4 * step / steps;
00689         pal_colors[i].g = pal[3 * i + 1] * 4 * step / steps;
00690         pal_colors[i].b = pal[3 * i + 2] * 4 * step / steps;
00691          */
00692 
00693         pal_colors[i].r = pal[3 * i] * 4;
00694         pal_colors[i].r = pal_colors[i].r * step / steps;
00695         pal_colors[i].g = pal[3 * i + 1] * 4;
00696         pal_colors[i].g = pal_colors[i].g * step / steps;
00697         pal_colors[i].b = pal[3 * i + 2] * 4;
00698         pal_colors[i].b = pal_colors[i].b * step / steps;
00699     }
00700 }
00701 
00702 void
00703 av_sync(void)
00704 {
00705     int num_rect = 0;
00706     SDL_Rect r;
00707 
00708 #ifdef PROFILE_GRAPHICS
00709     float tot_area = 0;
00710     int i = 0;
00711     Uint32 ticks = SDL_GetTicks();
00712 #endif
00713 
00714     SDL_Scale2x(screen_surf, screen_surf2x);
00715     /* copy palette and handle fading! */
00716     transform_palette();
00717     SDL_SetColors(screen_surf2x, pal_colors, 0, 256);
00718     SDL_BlitSurface(screen_surf2x, NULL, display, NULL);
00719     if (video_rect.h && video_rect.w)
00720     {
00721         av_need_update(&video_rect);
00722         r.h = 2 * video_rect.h;
00723         r.w = 2 * video_rect.w;
00724         r.x = 2 * video_rect.x;
00725         r.y = 2 * video_rect.y;
00726         SDL_DisplayYUVOverlay(video_overlay, &r);
00727     }
00728     if (news_rect.h && news_rect.w)
00729     {
00730         av_need_update(&news_rect);
00731         r.h = 2 * news_rect.h;
00732         r.w = 2 * news_rect.w;
00733         r.x = 2 * news_rect.x;
00734         r.y = 2 * news_rect.y;
00735         SDL_DisplayYUVOverlay(news_overlay, &r);
00736     }
00737     num_rect = get_dirty_rect_list();
00738     SDL_UpdateRects(display, num_rect, dirty_rect_list);
00739 #ifdef PROFILE_GRAPHICS
00740     for (i = 0; i < num_rect; ++i)
00741         tot_area += dirty_rect_list[i].w * dirty_rect_list[i].h;
00742     tot_area = tot_area * 100 / (2 * MAX_X) / (2 * MAX_Y);
00743     TRACE4("%3d rects (%6.2f%%) updated in ~%3ums\n",
00744         num_rect, tot_area, SDL_GetTicks() - ticks);
00745 #endif
00746     screen_dirty = 0;
00747 }
00748 
00749 void
00750 MuteChannel(int channel, int mute)
00751 {
00752     int i;
00753 
00754     if (channel == AV_ALL_CHANNELS)
00755     {
00756         for (i = 0; i < AV_NUM_CHANNELS; ++i)
00757             MuteChannel(i, mute);
00758     }
00759     else
00760     {
00761         assert(channel >= 0 && channel < AV_NUM_CHANNELS);
00762         Channels[channel].mute = mute;
00763     }
00764 }
00765 
00766 /**
00767  * Set up screen fade effect.  Fading applies only to a range of palette
00768  * color indexes. Rest of colors in the palette can be preserved or
00769  * forced to black.
00770  *
00771  * \param type AV_FADE_IN or AV_FADE_OUT
00772  * \param from index of first affected color
00773  * \param to index of last affected color
00774  * \param steps how many color change steps to perform
00775  * \param preserve whether preserve rest of palette colors or not
00776  */
00777 /* 
00778  * NOTES: A hack, but hey, it works :)
00779  * Adding periodic timer won't work, because we can't call av_sync from timer.
00780  * The only thing allowed is SDL_PushEvent, and we don't have event-driven
00781  * setup. So for now either this or nothing.
00782  */
00783 void
00784 av_set_fading(int type, int from, int to, int steps, int preserve)
00785 {
00786     int dir = (type == AV_FADE_IN) ? 1 : -1;
00787     unsigned st;  
00788     unsigned st_end;
00789     SDL_Rect r = {0, 0, MAX_X, MAX_Y};
00790 
00791     if (!do_fading)
00792         return;
00793 
00794     if (!steps > 0)
00795         steps = 5;
00796     st = (type == AV_FADE_IN) ? 0 : steps;
00797     st_end = steps - st;
00798 
00799     fade_info.from = from;
00800     fade_info.to = to;
00801     fade_info.steps = steps;
00802     fade_info.step = st;
00803     fade_info.force_black = !preserve;
00804     fade_info.inc = dir;
00805     fade_info.end = st_end;
00806 
00807     for (;fade_info.step != fade_info.end; fade_info.step += fade_info.inc)
00808     {
00809         av_need_update(&r);
00810         av_sync();
00811         SDL_Delay(10);
00812     }
00813     av_need_update(&r);
00814     av_sync();
00815 }
00816 
00817 /** compute area of intersection of rectangles */
00818 inline static int
00819 intersect_area(SDL_Rect *first, SDL_Rect *second)
00820 {
00821     int isect_h = 0, isect_w = 0;
00822     SDL_Rect *t;
00823     /* 
00824      * Treat dimensions separately. Sort accroding to start point,
00825      * then compute amount of overlap.
00826      */
00827     if (first->x > second->x)
00828     {
00829         t = first; first = second; second = t;
00830     }
00831     if (first->x + first->w < second->x)
00832         return 0;
00833     else
00834         isect_w = min(second->w, first->x + first->w - second->x);
00835 
00836     if (first->y > second->y)
00837     {
00838         t = first; first = second; second = t;
00839     }
00840     if (first->y + first->h < second->y)
00841         return 0;
00842     else
00843         isect_h = min(second->h, first->y + first->h - second->y);
00844 
00845     return isect_h * isect_w;
00846 }
00847 
00848 static void
00849 update_rect(SDL_Rect *fill, int x, int y, int w, int h, int idx, int level)
00850 {
00851     SDL_Rect r = {x, y, w, h};
00852     int nw = w / 2;
00853     int nh = h / 2;
00854     int area = 0;
00855 
00856     assert((unsigned)idx < dirty_tree_length);
00857 
00858     /* PRUNING: see if already dirty */
00859     if (dirty_tree[idx])
00860         return;
00861 
00862     /* PRUNING: check if covered area > AV_DTREE_FILL_RATIO */
00863     area = intersect_area(fill, &r);
00864     if (area == 0)
00865         return;
00866     else if (level == AV_DTREE_DEPTH
00867             || area > AV_DTREE_FILL_RATIO * h * w)
00868     {
00869         dirty_tree[idx] = 1;
00870         return;
00871     }
00872 
00873     idx *= 4;
00874     level += 1;
00875 
00876     update_rect(fill, x,      y,      nw,     nh,     idx + 1, level);
00877     update_rect(fill, x + nw, y,      w - nw, nh,     idx + 2, level);
00878     update_rect(fill, x,      y + nh, nw,     h - nh, idx + 3, level);
00879     update_rect(fill, x + nw, y + nh, w - nw, h - nh, idx + 4, level);
00880 }
00881 
00882 static void
00883 fill_rect_list(SDL_Rect *arr, int *len, int x, int y, int w, int h,
00884         int idx, int level)
00885 {
00886     int nw = w / 2;
00887     int nh = h / 2;
00888 
00889     if (level > AV_DTREE_DEPTH)
00890         return;
00891 
00892     assert((unsigned)idx < dirty_tree_length);
00893 
00894     if (dirty_tree[idx])
00895     {
00896         /* XXX multiply by 2 because of scaling */
00897         SDL_Rect r = {2*x, 2*y, 2*w, 2*h};
00898         memcpy(&arr[(*len)++], &r, sizeof(r));
00899         return;
00900     }
00901 
00902     idx *= 4;
00903     level += 1;
00904 
00905     fill_rect_list(arr, len, x,      y,      nw,     nh,     idx + 1, level);
00906     fill_rect_list(arr, len, x + nw, y,      w - nw, nh,     idx + 2, level);
00907     fill_rect_list(arr, len, x,      y + nh, nw,     h - nh, idx + 3, level);
00908     fill_rect_list(arr, len, x + nw, y + nh, w - nw, h - nh, idx + 4, level);
00909 }
00910 
00911 static int
00912 get_dirty_rect_list(void)
00913 {
00914     int len = 0;
00915     fill_rect_list(dirty_rect_list, &len, 0, 0, MAX_X, MAX_Y, 0, 0);
00916     memset(dirty_tree, 0, dirty_tree_length);
00917     return len;
00918 }
00919 
00920 /**
00921  * Notify graphic subsystem that rectangle has to be redrawn.
00922  * \param r rectangle coordinates
00923  */
00924 void
00925 av_need_update(SDL_Rect *r)
00926 {
00927     update_rect(r, 0, 0, MAX_X, MAX_Y, 0, 0);
00928     screen_dirty = 1;
00929 }
00930 
00931 /**
00932  * Notify graphic subsystem that rectangle has to be redrawn.
00933  * \param x1 \a x screen coord. of upper left corner
00934  * \param y1 \a y screen coord. of upper left corner
00935  * \param x2 \a x screen coord. of bottom right corner
00936  * \param y2 \a y screen coord. of bottom right corner
00937  */
00938 void
00939 av_need_update_xy(int x1, int y1, int x2, int y2)
00940 {
00941     SDL_Rect r = {x1, y1, x2-x1+1, y2-y1+1};
00942     av_need_update(&r);
00943 }
00944 
00945 /* vim: set noet ts=4 sw=4 tw=77: */

Generated on Fri Sep 28 00:35:46 2007 for raceintospace by  doxygen 1.5.3