This is quite ugly at places, but it seems to work well. This isn't really done for the speed
boost (although fades etc. are quite a lot smoother when the split screen can do all its work
in the native resolution instead of fading and _then_ downscaling), but primarily for the
legibility (ie. let FreeType _understand_ that we are on a smaller screen, so get hinting etc.
right), which seems to have improved by quite a lot (the overall "polish" look is a bit down,
though, probably the autohinter is evil).
19:30-04:00
}
if (mainscreen && mainscreen->check_invalidated()) {
- mainscreen->draw(framebuf);
+ mainscreen->draw(framebuf, SCREEN_WIDTH, SCREEN_HEIGHT);
ptc_update(framebuf);
conn.await_notification(0, 10000);
} else {
return needs_update;
}
-void GroupScreen::draw(unsigned char *buf)
+void GroupScreen::draw(unsigned char *buf, unsigned width, unsigned height)
{
std::vector<TextDefer> td;
scores_changed.reset_flag();
+ set_screen_size(width, height);
/*
* We'll probably need some values from here later on (although not all), just fetch them
conn.perform(FetchGroup(tournament, round, parallel, &group));
gettimeofday(&last_updated, NULL);
- memset(buf, 0, SCREEN_WIDTH * SCREEN_HEIGHT * 4);
+ memset(buf, 0, width * height * 4);
// main heading
char heading[64];
// Find out how wide each column has to be. First try unlimited width (ie.
// long titles for everything); if that gets too long, try again with short
// titles for chosen songs.
- unsigned width[16], num_scores;
+ unsigned colwidth[16], num_scores;
unsigned max_num_width = my_draw_text("8888", NULL, 22.0);
- unsigned sumwidth;
+ unsigned sumcolwidth;
for (unsigned mode = 0; mode < 2; ++mode) {
for (unsigned i = 0; i < 16; ++i)
- width[i] = 0;
+ colwidth[i] = 0;
for (std::vector<Player>::const_iterator i = group.players.begin(); i != group.players.end(); ++i) {
unsigned col = 1;
- width[0] = std::max(width[0], my_draw_text(i->nick, NULL, 18.0));
+ colwidth[0] = std::max(colwidth[0], my_draw_text(i->nick, NULL, 18.0));
for (std::vector<Score>::const_iterator j = i->scores.begin(); j != i->scores.end(); ++j, ++col) {
if (j->chosen) {
- width[col] = std::max(width[col], my_draw_text((mode == 0) ? j->song.title : j->song.short_title, NULL, 12.0) +
+ colwidth[col] = std::max(colwidth[col], my_draw_text((mode == 0) ? j->song.title : j->song.short_title, NULL, 12.0) +
max_num_width + 10);
} else {
- width[col] = std::max(width[col], my_draw_text(j->song.short_title, NULL, 12.0));
- width[col] = std::max(width[col], max_num_width);
+ colwidth[col] = std::max(colwidth[col], my_draw_text(j->song.short_title, NULL, 12.0));
+ colwidth[col] = std::max(colwidth[col], max_num_width);
}
}
}
num_scores = group.players[0].scores.size();
- width[num_scores + 1] = std::max(my_draw_text("Total", NULL, 12.0), max_num_width);
- width[num_scores + 2] = my_draw_text("Rank", NULL, 12.0);
+ colwidth[num_scores + 1] = std::max(my_draw_text("Total", NULL, 12.0), max_num_width);
+ colwidth[num_scores + 2] = my_draw_text("Rank", NULL, 12.0);
// if we're at long titles and that works, don't try the short ones
- sumwidth = 0;
+ sumcolwidth = 0;
for (unsigned i = 0; i <= num_scores + 2; ++i)
- sumwidth += width[i] + 20;
+ sumcolwidth += colwidth[i] + 20;
- if (sumwidth < 780)
+ if (sumcolwidth < 780)
break;
}
* If we have space to go, distribute as much as we can to the chosen song column, so we won't have
* total and rank jumping around.
*/
- if (sumwidth < 780) {
+ if (sumcolwidth < 780) {
int first_chosen_col = -1;
unsigned col = 1;
}
if (first_chosen_col != -1) {
- width[first_chosen_col] += 780 - sumwidth;
+ colwidth[first_chosen_col] += 780 - sumcolwidth;
}
}
// make column headings from the first player's songs
unsigned col = 1;
- unsigned x = 40 + width[0];
+ unsigned x = 40 + colwidth[0];
for (std::vector<Score>::const_iterator i = group.players[0].scores.begin(); i != group.players[0].scores.end(); ++i, ++col) {
if (!i->chosen) {
unsigned this_width = my_draw_text(i->song.short_title, NULL, 12.0);
- my_draw_text_deferred(td, i->song.short_title, 12.0, x + width[col] / 2 - this_width / 2, 100);
+ my_draw_text_deferred(td, i->song.short_title, 12.0, x + colwidth[col] / 2 - this_width / 2, 100);
}
- x += width[col] + 20;
+ x += colwidth[col] + 20;
}
- my_draw_text_deferred(td, "Total", 12.0, x + width[num_scores + 1] / 2 - my_draw_text("Total", NULL, 12.0) / 2, 100);
- x += width[num_scores + 1] + 20;
- my_draw_text_deferred(td, "Rank", 12.0, x + width[num_scores + 2] / 2 - my_draw_text("Rank", NULL, 12.0) / 2, 100);
+ my_draw_text_deferred(td, "Total", 12.0, x + colwidth[num_scores + 1] / 2 - my_draw_text("Total", NULL, 12.0) / 2, 100);
+ x += colwidth[num_scores + 1] + 20;
+ my_draw_text_deferred(td, "Rank", 12.0, x + colwidth[num_scores + 2] / 2 - my_draw_text("Rank", NULL, 12.0) / 2, 100);
// show all the players and the scores
unsigned num_players_this_machine = (group.players.size() + num_machines - machine - 1) / num_machines;
my_draw_text_deferred(td, i->nick, 18.0, 20, y);
- unsigned x = 40 + width[0];
+ unsigned x = 40 + colwidth[0];
unsigned col = 1;
for (std::vector<Score>::const_iterator j = i->scores.begin(); j != i->scores.end(); ++j, ++col) {
my_draw_text_deferred(td, text, 22.0, x + max_num_width - this_width, y);
// draw the long name if we can, otherwise use the short one
- if (my_draw_text(j->song.title, NULL, 12.0) > (width[col] - 10 - max_num_width)) {
+ if (my_draw_text(j->song.title, NULL, 12.0) > (colwidth[col] - 10 - max_num_width)) {
my_draw_text_deferred(td, j->song.short_title, 12.0, x + max_num_width + 10, y);
} else {
my_draw_text_deferred(td, j->song.title, 12.0, x + max_num_width + 10, y);
}
} else {
- my_draw_text_deferred(td, text, 22.0, x + width[col] / 2 - this_width / 2, y);
+ my_draw_text_deferred(td, text, 22.0, x + colwidth[col] / 2 - this_width / 2, y);
}
- x += width[col] + 20;
+ x += colwidth[col] + 20;
}
// draw total
std::sprintf(text, "%u", i->total);
unsigned this_width = my_draw_text(text, NULL, 22.0);
- my_draw_text_deferred(td, text, 22.0, x + width[num_scores + 1] / 2 - this_width / 2, y);
- x += width[num_scores + 1] + 20;
+ my_draw_text_deferred(td, text, 22.0, x + colwidth[num_scores + 1] / 2 - this_width / 2, y);
+ x += colwidth[num_scores + 1] + 20;
}
if (show_players > 7)
continue;
unsigned this_width = my_draw_text(text, NULL, 22.0);
- my_draw_text_deferred(td, text, 22.0, x + width[num_scores + 2] / 2 - this_width / 2, y);
+ my_draw_text_deferred(td, text, 22.0, x + colwidth[num_scores + 2] / 2 - this_width / 2, y);
if (show_players > 7)
y += 40 - (show_players - 7) * 4;
virtual ~GroupScreen();
bool check_invalidated();
- void draw(unsigned char *buf);
+ void draw(unsigned char *buf, unsigned width, unsigned height);
int get_priority();
};
+/* NOTE: this class will _NOT_ handle resolution changes cleanly. You have been warned. :-) */
+
+#include <cstdio>
#include <cstring>
#include "rotatescreen.h"
RotateScreen::RotateScreen()
- : valid(false), current_screen(0), in_fade(false)
+ : fadefrom_buf(NULL), valid(false), current_screen(0), in_fade(false)
{
- fadefrom_buf = new unsigned char[SCREEN_WIDTH * SCREEN_HEIGHT * 4];
}
RotateScreen::~RotateScreen()
return false;
for (unsigned i = 0; i < subscreens.size(); ++i) {
- if (subscreens[i].screen->check_invalidated())
+ if (subscreens[i].buf == NULL || subscreens[i].screen->check_invalidated())
return true;
}
return false;
}
-void RotateScreen::draw(unsigned char *buf)
+void RotateScreen::draw(unsigned char *buf, unsigned width, unsigned height)
{
+ // see line 1 for all of this
+ if (fadefrom_buf == NULL) {
+ fadefrom_buf = new unsigned char[width * height * 4];
+ }
+ for (std::vector<Subscreen>::iterator i = subscreens.begin(); i != subscreens.end(); ++i) {
+ if (i->buf == NULL) {
+ i->buf = new unsigned char[width * height * 4];
+ i->screen->draw(i->buf, width, height);
+ }
+ }
+ // end of "line 1"-code :-)
+
bool force = false;
if (subscreens.size() == 0) {
in_fade = false;
// ugly hack here? :-)
- subscreens[current_screen].screen->draw(subscreens[current_screen].buf);
+ subscreens[current_screen].screen->draw(subscreens[current_screen].buf, width, height);
- memcpy(buf, subscreens[current_screen].buf, SCREEN_WIDTH * SCREEN_HEIGHT * 4);
+ memcpy(buf, subscreens[current_screen].buf, width * height * 4);
} else {
// find the fade factors
int fr, fg, fb, fa;
if (fade_to_new_info && elapsed_fade >= 0.5) {
// fade G&B to be = R
- for (unsigned i = 0; i < SCREEN_WIDTH * SCREEN_HEIGHT; ++i) {
+ for (unsigned i = 0; i < width * height; ++i) {
dptr[0] = sptr2[0] + (((int(sptr2[2]) - int(sptr2[0])) * fb) >> 8);
dptr[1] = sptr2[1] + (((int(sptr2[2]) - int(sptr2[1])) * fg) >> 8);
dptr[2] = sptr2[2];
sptr1 += 4, sptr2 += 4, dptr += 4;
}
} else {
- for (unsigned i = 0; i < SCREEN_WIDTH * SCREEN_HEIGHT; ++i) {
+ for (unsigned i = 0; i < width * height; ++i) {
dptr[0] = sptr1[0] + (((int(sptr2[0]) - int(sptr1[0])) * fb) >> 8);
dptr[1] = sptr1[1] + (((int(sptr2[1]) - int(sptr1[1])) * fg) >> 8);
dptr[2] = sptr1[2] + (((int(sptr2[2]) - int(sptr1[2])) * fr) >> 8);
fade_found_start_time = false;
fade_to_new_info = force;
- memcpy(fadefrom_buf, subscreens[old_current_screen].buf, SCREEN_WIDTH * SCREEN_HEIGHT * 4);
+ memcpy(fadefrom_buf, subscreens[old_current_screen].buf, width * height * 4);
- if (subscreens[current_screen].screen->check_invalidated())
- subscreens[current_screen].screen->draw(subscreens[current_screen].buf);
+ if (subscreens[current_screen].screen->check_invalidated()) {
+ subscreens[current_screen].screen->draw(subscreens[current_screen].buf, width, height);
+ }
}
}
void RotateScreen::add_screen(GenericScreen *screen)
{
Subscreen ss;
- ss.buf = new unsigned char[SCREEN_WIDTH * SCREEN_HEIGHT * 4];
+ ss.buf = NULL;
ss.screen = screen;
- screen->draw(ss.buf);
-
subscreens.push_back(ss);
}
virtual ~RotateScreen();
bool check_invalidated();
- void draw(unsigned char *buf);
+ void draw(unsigned char *buf, unsigned width, unsigned height);
void add_screen(GenericScreen *screen);
// void remove_screen(GenericScreen *screen);
public:
virtual ~GenericScreen();
virtual bool check_invalidated() = 0;
- virtual void draw(unsigned char *buf) = 0;
+ virtual void draw(unsigned char *buf, unsigned width, unsigned height) = 0;
virtual int get_priority();
};
+/* NOTE: this class will _NOT_ handle resolution changes cleanly. You have been warned. :-) */
+
#include <cstring>
#include "splitscreen.h"
subscreens[2] = s3;
subscreens[3] = s4;
- memset(subbufs[0], 0, SCREEN_WIDTH*SCREEN_HEIGHT*4);
- memset(subbufs[1], 0, SCREEN_WIDTH*SCREEN_HEIGHT*4);
- memset(subbufs[2], 0, SCREEN_WIDTH*SCREEN_HEIGHT*4);
- memset(subbufs[3], 0, SCREEN_WIDTH*SCREEN_HEIGHT*4);
+ for (unsigned i = 0; i < 4; ++i)
+ subbufs[i] = NULL;
}
SplitScreen::~SplitScreen()
return false;
}
-void SplitScreen::draw(unsigned char *buf)
+void SplitScreen::draw(unsigned char *buf, unsigned width, unsigned height)
{
for (unsigned i = 0; i < 4; ++i) {
+ if (subbufs[i] == NULL) { // see line 1
+ subbufs[i] = new unsigned char[width/2 * height/2 * 4];
+ memset(subbufs[i], 0, width/2 * height/2 * 4);
+ }
if (subscreens[i] && subscreens[i]->check_invalidated()) {
- subscreens[i]->draw(subbufs[i]);
+ subscreens[i]->draw(subbufs[i], width/2, height/2);
}
}
- downscale_2x2(buf, subbufs[0]);
- downscale_2x2(buf + (SCREEN_WIDTH/2) * 4, subbufs[1]);
- downscale_2x2(buf + SCREEN_WIDTH * (SCREEN_HEIGHT/2) * 4, subbufs[2]);
- downscale_2x2(buf + SCREEN_WIDTH * (SCREEN_HEIGHT/2) * 4 + (SCREEN_WIDTH/2) * 4, subbufs[3]);
-
+ copy_subscreen(buf, subbufs[0], width, height);
+ copy_subscreen(buf + (width/2) * 4, subbufs[1], width, height);
+ copy_subscreen(buf + width * (height/2) * 4, subbufs[2], width, height);
+ copy_subscreen(buf + width * (height/2) * 4 + (width/2) * 4, subbufs[3], width, height);
+
// make divider lines
- unsigned char *ptr = buf + (SCREEN_HEIGHT/2) * SCREEN_WIDTH * 4;
- for (unsigned x = 0; x < SCREEN_WIDTH; ++x) {
+ unsigned char *ptr = buf + (height/2) * width * 4;
+ for (unsigned x = 0; x < width; ++x) {
*ptr++ = 255;
*ptr++ = 255;
*ptr++ = 255;
*ptr++ = 0;
}
- ptr = buf + (SCREEN_WIDTH/2) * 4;
- for (unsigned y = 0; y < SCREEN_HEIGHT; ++y) {
+ ptr = buf + (width/2) * 4;
+ for (unsigned y = 0; y < height; ++y) {
ptr[0] = 255;
ptr[1] = 255;
ptr[2] = 255;
ptr[3] = 0;
- ptr += SCREEN_WIDTH * 4;
+ ptr += width * 4;
}
valid = true;
}
-
-// simple box filter (blah)
-void SplitScreen::downscale_2x2(unsigned char *dst, unsigned char *src)
+
+void SplitScreen::copy_subscreen(unsigned char *dst, unsigned char *src, unsigned width, unsigned height)
{
- for (unsigned y = 0; y < (SCREEN_HEIGHT/2); ++y) {
- unsigned char *sptr1 = src + (y*2) * SCREEN_WIDTH * 4;
- unsigned char *sptr2 = src + (y*2+1) * SCREEN_WIDTH * 4;
- unsigned char *dptr = dst + y * SCREEN_WIDTH * 4;
-
- for (unsigned x = 0; x < (SCREEN_WIDTH/2); ++x) {
- *dptr++ = (sptr1[0] + sptr1[4] + sptr2[0] + sptr2[4]) >> 2; // red
- *dptr++ = (sptr1[1] + sptr1[5] + sptr2[1] + sptr2[5]) >> 2; // green
- *dptr++ = (sptr1[2] + sptr1[6] + sptr2[2] + sptr2[6]) >> 2; // blue
- *dptr++ = (sptr1[3] + sptr1[7] + sptr2[3] + sptr2[7]) >> 2; // alpha
+ for (unsigned y = 0; y < height/2; ++y) {
+ unsigned char *sptr = src + y * width/2 * 4;
+ unsigned char *dptr = dst + y * width * 4;
- sptr1 += 8;
- sptr2 += 8;
- }
+ memcpy(dptr, sptr, width/2 * 4);
}
}
-
+
/* A 4x4 split class */
class SplitScreen : public GenericScreen {
private:
- unsigned char subbufs[4][SCREEN_WIDTH * SCREEN_HEIGHT * 4];
+ unsigned char *subbufs[4];
GenericScreen *subscreens[4];
bool valid;
- void downscale_2x2(unsigned char *dst, unsigned char *src);
+ void copy_subscreen(unsigned char *dst, unsigned char *src, unsigned width, unsigned height);
public:
SplitScreen(GenericScreen *s1, GenericScreen *s2, GenericScreen *s3, GenericScreen *s4);
virtual ~SplitScreen();
bool check_invalidated();
- void draw(unsigned char *buf);
+ void draw(unsigned char *buf, unsigned width, unsigned height);
};
#endif /* !defined(_SPLITSCREEN_H) */
return false;
}
-void Top10ScoreScreen::draw(unsigned char *buf)
+void Top10ScoreScreen::draw(unsigned char *buf, unsigned width, unsigned height)
{
scores_changed.reset_flag();
- memset(buf, 0, SCREEN_WIDTH * SCREEN_HEIGHT * 4);
+ memset(buf, 0, width * height * 4);
+ set_screen_size(width, height);
// fetch the top 10 scores
std::vector<TopScore> scores;
virtual ~Top10ScoreScreen();
bool check_invalidated();
- void draw(unsigned char *buf);
+ void draw(unsigned char *buf, unsigned width, unsigned height);
int get_priority();
};
return false;
}
-void Top5ChosenScreen::draw(unsigned char *buf)
+void Top5ChosenScreen::draw(unsigned char *buf, unsigned width, unsigned height)
{
scores_changed.reset_flag();
- memset(buf, 0, SCREEN_WIDTH * SCREEN_HEIGHT * 4);
+ memset(buf, 0, width * height * 4);
+ set_screen_size(width, height);
// fetch the top 5 chosen songs
std::vector<TopChosen> scores;
virtual ~Top5ChosenScreen();
bool check_invalidated();
- void draw(unsigned char *buf);
+ void draw(unsigned char *buf, unsigned width, unsigned height);
};
#endif /* !defined(_TOP5CHOSENSCREEN_H) */