video.cpp

Go to the documentation of this file.
00001 /* $Id: video.cpp 52533 2012-01-07 02:35:17Z shadowmaster $ */
00002 /*
00003    Copyright (C) 2003 - 2012 by David White <dave@whitevine.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 /**
00017  *  @file
00018  *  Video-testprogram, standalone
00019  */
00020 
00021 #include "global.hpp"
00022 
00023 #include "font.hpp"
00024 #include "foreach.hpp"
00025 #include "image.hpp"
00026 #include "log.hpp"
00027 #include "preferences.hpp"
00028 #include "preferences_display.hpp"
00029 #include "sdl_utils.hpp"
00030 #include "video.hpp"
00031 
00032 #include <vector>
00033 #include <map>
00034 #include <algorithm>
00035 
00036 static lg::log_domain log_display("display");
00037 #define LOG_DP LOG_STREAM(info, log_display)
00038 #define ERR_DP LOG_STREAM(err, log_display)
00039 
00040 namespace {
00041     bool fullScreen = false;
00042     int disallow_resize = 0;
00043 }
00044 void resize_monitor::process(events::pump_info &info) {
00045     if(info.resize_dimensions.first >= preferences::min_allowed_width()
00046     && info.resize_dimensions.second >= preferences::min_allowed_height()
00047     && disallow_resize == 0) {
00048         preferences::set_resolution(info.resize_dimensions);
00049     }
00050 }
00051 
00052 resize_lock::resize_lock()
00053 {
00054     ++disallow_resize;
00055 }
00056 
00057 resize_lock::~resize_lock()
00058 {
00059     --disallow_resize;
00060 }
00061 
00062 static unsigned int get_flags(unsigned int flags)
00063 {
00064     // SDL under Windows doesn't seem to like hardware surfaces
00065     // for some reason.
00066 #if !(defined(_WIN32) || defined(__APPLE__) || defined(__AMIGAOS4__))
00067         flags |= SDL_HWSURFACE;
00068 #endif
00069     if((flags&SDL_FULLSCREEN) == 0)
00070         flags |= SDL_RESIZABLE;
00071 
00072     return flags;
00073 }
00074 
00075 namespace {
00076 struct event {
00077     int x, y, w, h;
00078     bool in;
00079     event(const SDL_Rect& rect, bool i) : x(i ? rect.x : rect.x + rect.w), y(rect.y), w(rect.w), h(rect.h), in(i) { }
00080 };
00081 bool operator<(const event& a, const event& b) {
00082     if (a.x != b.x) return a.x < b.x;
00083     if (a.in != b.in) return a.in;
00084     if (a.y != b.y) return a.y < b.y;
00085     if (a.h != b.h) return a.h < b.h;
00086     if (a.w != b.w) return a.w < b.w;
00087     return false;
00088 }
00089 bool operator==(const event& a, const event& b) {
00090     return a.x == b.x && a.y == b.y && a.w == b.w && a.h == b.h && a.in == b.in;
00091 }
00092 
00093 struct segment {
00094     int x, count;
00095     segment() : x(0), count(0) { }
00096     segment(int x, int count) : x(x), count(count) { }
00097 };
00098 
00099 
00100 std::vector<SDL_Rect> update_rects;
00101 std::vector<event> events;
00102 std::map<int, segment> segments;
00103 
00104 static void calc_rects()
00105 {
00106     events.clear();
00107 
00108     foreach (SDL_Rect const &rect, update_rects) {
00109         events.push_back(event(rect, true));
00110         events.push_back(event(rect, false));
00111     }
00112 
00113     std::sort(events.begin(), events.end());
00114     std::vector<event>::iterator events_end = std::unique(events.begin(), events.end());
00115 
00116     segments.clear();
00117     update_rects.clear();
00118 
00119     for (std::vector<event>::iterator iter = events.begin(); iter != events_end; ++iter) {
00120         std::map<int, segment>::iterator lower = segments.find(iter->y);
00121         if (lower == segments.end()) {
00122             lower = segments.insert(std::make_pair(iter->y, segment())).first;
00123             if (lower != segments.begin()) {
00124                 std::map<int, segment>::iterator prev = lower;
00125                 --prev;
00126                 lower->second = prev->second;
00127             }
00128         }
00129 
00130         if (lower->second.count == 0) {
00131             lower->second.x = iter->x;
00132         }
00133 
00134         std::map<int, segment>::iterator upper = segments.find(iter->y + iter->h);
00135         if (upper == segments.end()) {
00136             upper = segments.insert(std::make_pair(iter->y + iter->h, segment())).first;
00137             std::map<int, segment>::iterator prev = upper;
00138             --prev;
00139             upper->second = prev->second;
00140         }
00141 
00142         if (iter->in) {
00143             while (lower != upper) {
00144                 ++lower->second.count;
00145                 ++lower;
00146             }
00147         } else {
00148             while (lower != upper) {
00149                 lower->second.count--;
00150                 if (lower->second.count == 0) {
00151                     std::map<int, segment>::iterator next = lower;
00152                     ++next;
00153 
00154                     int x = lower->second.x, y = lower->first;
00155                     unsigned w = iter->x - x;
00156                     unsigned h = next->first - y;
00157                     SDL_Rect a = create_rect(x, y, w, h);
00158 
00159                     if (update_rects.empty()) {
00160                         update_rects.push_back(a);
00161                     } else {
00162                         SDL_Rect& p = update_rects.back(), n;
00163                         int pa = p.w * p.h, aa = w * h, s = pa + aa;
00164                         int thresh = 51;
00165 
00166                         n.w = std::max<int>(x + w, p.x + p.w);
00167                         n.x = std::min<int>(p.x, x);
00168                         n.w -= n.x;
00169                         n.h = std::max<int>(y + h, p.y + p.h);
00170                         n.y = std::min<int>(p.y, y);
00171                         n.h -= n.y;
00172 
00173                         if (s * 100 < thresh * n.w * n.h) {
00174                             update_rects.push_back(a);
00175                         } else {
00176                             p = n;
00177                         }
00178                     }
00179 
00180                     if (lower == segments.begin()) {
00181                         segments.erase(lower);
00182                     } else {
00183                         std::map<int, segment>::iterator prev = lower;
00184                         --prev;
00185                         if (prev->second.count == 0) segments.erase(lower);
00186                     }
00187 
00188                     lower = next;
00189                 } else {
00190                     ++lower;
00191                 }
00192             }
00193         }
00194     }
00195 }
00196 
00197 
00198 bool update_all = false;
00199 }
00200 
00201 static void clear_updates()
00202 {
00203     update_all = false;
00204     update_rects.clear();
00205 }
00206 
00207 namespace {
00208 
00209 surface frameBuffer = NULL;
00210 bool fake_interactive = false;
00211 }
00212 
00213 bool non_interactive()
00214 {
00215     if (fake_interactive)
00216         return false;
00217     return SDL_GetVideoSurface() == NULL;
00218 }
00219 
00220 surface display_format_alpha(surface surf)
00221 {
00222     if(SDL_GetVideoSurface() != NULL)
00223         return SDL_DisplayFormatAlpha(surf);
00224     else if(frameBuffer != NULL)
00225         return SDL_ConvertSurface(surf,frameBuffer->format,0);
00226     else
00227         return NULL;
00228 }
00229 
00230 surface get_video_surface()
00231 {
00232     return frameBuffer;
00233 }
00234 
00235 SDL_Rect screen_area()
00236 {
00237     return create_rect(0, 0, frameBuffer->w, frameBuffer->h);
00238 }
00239 
00240 void update_rect(size_t x, size_t y, size_t w, size_t h)
00241 {
00242     update_rect(create_rect(x, y, w, h));
00243 }
00244 
00245 void update_rect(const SDL_Rect& rect_value)
00246 {
00247     if(update_all)
00248         return;
00249 
00250     SDL_Rect rect = rect_value;
00251 
00252     surface const fb = SDL_GetVideoSurface();
00253     if(fb != NULL) {
00254         if(rect.x < 0) {
00255             if(rect.x*-1 >= int(rect.w))
00256                 return;
00257 
00258             rect.w += rect.x;
00259             rect.x = 0;
00260         }
00261 
00262         if(rect.y < 0) {
00263             if(rect.y*-1 >= int(rect.h))
00264                 return;
00265 
00266             rect.h += rect.y;
00267             rect.y = 0;
00268         }
00269 
00270         if(rect.x + rect.w > fb->w) {
00271             rect.w = fb->w - rect.x;
00272         }
00273 
00274         if(rect.y + rect.h > fb->h) {
00275             rect.h = fb->h - rect.y;
00276         }
00277 
00278         if(rect.x >= fb->w) {
00279             return;
00280         }
00281 
00282         if(rect.y >= fb->h) {
00283             return;
00284         }
00285     }
00286 
00287     update_rects.push_back(rect);
00288 }
00289 
00290 void update_whole_screen()
00291 {
00292     update_all = true;
00293 }
00294 CVideo::CVideo(FAKE_TYPES type) : mode_changed_(false), bpp_(0), fake_screen_(false), help_string_(0), updatesLocked_(0)
00295 {
00296     initSDL();
00297     switch(type)
00298     {
00299         case NO_FAKE:
00300             break;
00301         case FAKE:
00302             make_fake();
00303             break;
00304         case FAKE_TEST:
00305             make_test_fake();
00306             break;
00307     }
00308 }
00309 
00310 void CVideo::initSDL()
00311 {
00312     const int res = SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE);
00313 
00314     if(res < 0) {
00315         ERR_DP << "Could not initialize SDL_video: " << SDL_GetError() << "\n";
00316         throw CVideo::error();
00317     }
00318 }
00319 
00320 CVideo::~CVideo()
00321 {
00322     LOG_DP << "calling SDL_Quit()\n";
00323     SDL_Quit();
00324     LOG_DP << "called SDL_Quit()\n";
00325 }
00326 
00327 void CVideo::blit_surface(int x, int y, surface surf, SDL_Rect* srcrect, SDL_Rect* clip_rect)
00328 {
00329     surface target(getSurface());
00330     SDL_Rect dst = create_rect(x, y, 0, 0);
00331 
00332     const clip_rect_setter clip_setter(target, clip_rect, clip_rect != NULL);
00333     sdl_blit(surf,srcrect,target,&dst);
00334 }
00335 
00336 void CVideo::make_fake()
00337 {
00338     fake_screen_ = true;
00339     frameBuffer = SDL_CreateRGBSurface(SDL_SWSURFACE,16,16,24,0xFF0000,0xFF00,0xFF,0);
00340     image::set_pixel_format(frameBuffer->format);
00341 }
00342 
00343 void CVideo::make_test_fake(const unsigned width,
00344             const unsigned height, const unsigned bpp)
00345 {
00346     frameBuffer = SDL_CreateRGBSurface(SDL_SWSURFACE,
00347             width, height, bpp, 0xFF0000, 0xFF00, 0xFF, 0);
00348     image::set_pixel_format(frameBuffer->format);
00349 
00350     fake_interactive = true;
00351 
00352 }
00353 
00354 int CVideo::bppForMode( int x, int y, int flags)
00355 {
00356     int test_values[3] = {getBpp(), 32, 16};
00357     foreach(int &bpp, test_values) {
00358         if(modePossible(x, y, bpp, flags) > 0) {
00359             return bpp;
00360         }
00361     }
00362 
00363     return 0;
00364 }
00365 
00366 int CVideo::modePossible( int x, int y, int bits_per_pixel, int flags, bool current_screen_optimal )
00367 {
00368 
00369     int bpp = SDL_VideoModeOK( x, y, bits_per_pixel, get_flags(flags) );
00370     if(current_screen_optimal)
00371     {
00372         const SDL_VideoInfo* const video_info = SDL_GetVideoInfo();
00373         /* if current video_info is smaller than the mode checking and the checked mode is supported
00374         (meaning that probably the video card supports higher resolutions than the monitor)
00375         that means that we just need to adjust the resolution and the bpp is ok
00376         */
00377         if(bpp==0 && video_info->current_h<y && video_info->current_w<x){
00378             return bits_per_pixel;
00379         }
00380     }
00381     return bpp;
00382 }
00383 
00384 int CVideo::setMode( int x, int y, int bits_per_pixel, int flags )
00385 {
00386     update_rects.clear();
00387     if (fake_screen_) return 0;
00388     mode_changed_ = true;
00389 
00390     flags = get_flags(flags);
00391     const int res = SDL_VideoModeOK( x, y, bits_per_pixel, flags );
00392 
00393     if( res == 0 )
00394         return 0;
00395 
00396     fullScreen = (flags & FULL_SCREEN) != 0;
00397     frameBuffer = SDL_SetVideoMode( x, y, bits_per_pixel, flags );
00398 
00399     if( frameBuffer != NULL ) {
00400         image::set_pixel_format(frameBuffer->format);
00401         return bits_per_pixel;
00402     } else  return 0;
00403 }
00404 
00405 bool CVideo::modeChanged()
00406 {
00407     bool ret = mode_changed_;
00408     mode_changed_ = false;
00409     return ret;
00410 }
00411 
00412 int CVideo::getx() const
00413 {
00414     return frameBuffer->w;
00415 }
00416 
00417 int CVideo::gety() const
00418 {
00419     return frameBuffer->h;
00420 }
00421 
00422 void CVideo::flip()
00423 {
00424     if(fake_screen_)
00425         return;
00426 
00427     if(update_all) {
00428         ::SDL_Flip(frameBuffer);
00429     } else if(update_rects.empty() == false) {
00430         calc_rects();
00431         if(!update_rects.empty()) {
00432             SDL_UpdateRects(frameBuffer, update_rects.size(), &update_rects[0]);
00433         }
00434     }
00435 
00436     clear_updates();
00437 }
00438 
00439 void CVideo::lock_updates(bool value)
00440 {
00441     if(value == true)
00442         ++updatesLocked_;
00443     else
00444         --updatesLocked_;
00445 }
00446 
00447 bool CVideo::update_locked() const
00448 {
00449     return updatesLocked_ > 0;
00450 }
00451 
00452 surface& CVideo::getSurface()
00453 {
00454     return frameBuffer;
00455 }
00456 
00457 bool CVideo::isFullScreen() const { return fullScreen; }
00458 
00459 void CVideo::setBpp( int bpp )
00460 {
00461     bpp_ = bpp;
00462 }
00463 
00464 int CVideo::getBpp()
00465 {
00466     return bpp_;
00467 }
00468 
00469 int CVideo::set_help_string(const std::string& str)
00470 {
00471     font::remove_floating_label(help_string_);
00472 
00473     const SDL_Color color = { 0, 0, 0, 0xbb };
00474 
00475     int size = font::SIZE_LARGE;
00476 
00477     while(size > 0) {
00478         if(font::line_width(str, size) > getx()) {
00479             size--;
00480         } else {
00481             break;
00482         }
00483     }
00484 
00485     const int border = 5;
00486 
00487     font::floating_label flabel(str);
00488     flabel.set_font_size(size);
00489     flabel.set_position(getx()/2, gety());
00490     flabel.set_bg_color(color);
00491     flabel.set_border_size(border);
00492 
00493     help_string_ = font::add_floating_label(flabel);
00494 
00495     const SDL_Rect& rect = font::get_floating_label_rect(help_string_);
00496     font::move_floating_label(help_string_,0.0,-double(rect.h));
00497     return help_string_;
00498 }
00499 
00500 void CVideo::clear_help_string(int handle)
00501 {
00502     if(handle == help_string_) {
00503         font::remove_floating_label(handle);
00504         help_string_ = 0;
00505     }
00506 }
00507 
00508 void CVideo::clear_all_help_strings()
00509 {
00510     clear_help_string(help_string_);
00511 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:03:14 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs