Reduced amount of overlapping rectangles sent to SDL. (Patch #1141.)

This commit is contained in:
Guillaume Melquiond 2009-12-06 20:10:31 +00:00
parent 4e7a4becc9
commit 58c7896b99

View File

@ -19,13 +19,17 @@
#include "global.hpp"
#include "font.hpp"
#include "foreach.hpp"
#include "image.hpp"
#include "log.hpp"
#include "preferences_display.hpp"
#include "video.hpp"
#include <vector>
#include <map>
#include <algorithm>
static lg::log_domain log_display("display");
#define LOG_DP LOG_STREAM(info, log_display)
#define ERR_DP LOG_STREAM(err, log_display)
@ -66,12 +70,129 @@ static unsigned int get_flags(unsigned int flags)
}
namespace {
std::vector<SDL_Rect> update_rects;
bool update_all = false;
struct event {
int x, y, w, h;
bool in;
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) { }
};
bool operator<(const event& a, const event& b) {
if (a.x != b.x) return a.x < b.x;
if (a.in != b.in) return a.in;
if (a.y != b.y) return a.y < b.y;
if (a.h != b.h) return a.h < b.h;
if (a.w != b.w) return a.w < b.w;
return false;
}
bool operator==(const event& a, const event& b) {
return a.x == b.x && a.y == b.y && a.w == b.w && a.h == b.h && a.in == b.in;
}
static bool rect_contains(const SDL_Rect& a, const SDL_Rect& b) {
return a.x <= b.x && a.y <= b.y && a.x+a.w >= b.x+b.w && a.y+a.h >= b.y+b.h;
struct segment {
int x, count;
segment() : x(0), count(0) { }
segment(int x, int count) : x(x), count(count) { }
};
std::vector<SDL_Rect> update_rects;
std::vector<event> events;
std::map<int, segment> segments;
static void calc_rects()
{
events.clear();
foreach (SDL_Rect const &rect, update_rects) {
events.push_back(event(rect, true));
events.push_back(event(rect, false));
}
std::sort(events.begin(), events.end());
std::vector<event>::iterator events_end = std::unique(events.begin(), events.end());
segments.clear();
update_rects.clear();
for (std::vector<event>::iterator iter = events.begin(); iter != events_end; ++iter) {
std::map<int, segment>::iterator lower = segments.find(iter->y);
if (lower == segments.end()) {
lower = segments.insert(std::make_pair(iter->y, segment())).first;
if (lower != segments.begin()) {
std::map<int, segment>::iterator prev = lower;
--prev;
lower->second = prev->second;
}
}
if (lower->second.count == 0) {
lower->second.x = iter->x;
}
std::map<int, segment>::iterator upper = segments.find(iter->y + iter->h);
if (upper == segments.end()) {
upper = segments.insert(std::make_pair(iter->y + iter->h, segment())).first;
std::map<int, segment>::iterator prev = upper;
--prev;
upper->second = prev->second;
}
if (iter->in) {
while (lower != upper) {
++lower->second.count;
++lower;
}
} else {
while (lower != upper) {
lower->second.count--;
if (lower->second.count == 0) {
std::map<int, segment>::iterator next = lower;
++next;
int x = lower->second.x, y = lower->first;
unsigned w = iter->x - x;
unsigned h = next->first - y;
SDL_Rect a = {x, y, w, h};
if (update_rects.size() == 0) {
update_rects.push_back(a);
} else {
SDL_Rect& p = update_rects.back(), n;
int pa = p.w * p.h, aa = w * h, s = pa + aa;
int thresh = 51;
n.w = std::max<int>(x + w, p.x + p.w);
n.x = std::min<int>(p.x, x);
n.w -= n.x;
n.h = std::max<int>(y + h, p.y + p.h);
n.y = std::min<int>(p.y, y);
n.h -= n.y;
if (s * 100 < thresh * n.w * n.h) {
update_rects.push_back(a);
} else {
p = n;
}
}
if (lower == segments.begin()) {
segments.erase(lower);
} else {
std::map<int, segment>::iterator prev = lower;
--prev;
if (prev->second.count == 0) segments.erase(lower);
}
lower = next;
} else {
++lower;
}
}
}
}
}
bool update_all = false;
}
static void clear_updates()
@ -162,24 +283,6 @@ void update_rect(const SDL_Rect& rect_value)
}
}
for(std::vector<SDL_Rect>::iterator i = update_rects.begin();
i != update_rects.end(); ++i) {
if(rect_contains(*i,rect)) {
return;
}
if(rect_contains(rect,*i)) {
*i = rect;
for(++i; i != update_rects.end(); ++i) {
if(rect_contains(rect,*i)) {
i->w = 0;
}
}
return;
}
}
update_rects.push_back(rect);
}
@ -302,17 +405,8 @@ void CVideo::flip()
if(update_all) {
::SDL_Flip(frameBuffer);
} else if(update_rects.empty() == false) {
size_t sum = 0;
for(size_t n = 0; n != update_rects.size(); ++n) {
sum += update_rects[n].w*update_rects[n].h;
}
const size_t redraw_whole_screen_threshold = 80;
if(sum > ((getx()*gety())*redraw_whole_screen_threshold)/100) {
::SDL_Flip(frameBuffer);
} else {
SDL_UpdateRects(frameBuffer,update_rects.size(),&update_rects[0]);
}
calc_rects();
SDL_UpdateRects(frameBuffer, update_rects.size(), &update_rects[0]);
}
clear_updates();