From: Jan D. Behrens Date: Thu, 30 Aug 2012 10:53:34 +0000 (+0200) Subject: More design work on the two badge themes; also improved skin support X-Git-Url: https://git.xonotic.org/?a=commitdiff_plain;h=21e1f476d3aae503be905936ea130a2193d9b4f8;p=xonotic%2Fxonstat.git More design work on the two badge themes; also improved skin support - More parameters that can be changed by skins - Minimal skin: fixed gametype alignment, retuned coloring to be more modest, some redesign - Classic skin: gametypes are now aligned to the center if less than max. gameteypes are shown, re-introduced the "no stats" stamp, also some redesign - Some code cleanup --- diff --git a/xonstat/batch/badges/gen_badges.py b/xonstat/batch/badges/gen_badges.py index 0cd17a3..82620b2 100644 --- a/xonstat/batch/badges/gen_badges.py +++ b/xonstat/batch/badges/gen_badges.py @@ -21,7 +21,7 @@ DELTA = 6 # classic skin WITHOUT NAME - writes PNGs into "output//###.png" skin_classic = Skin( "", - bg = "asfalt", + bg = "broken_noise", overlay = "overlay_classic", ) @@ -47,11 +47,11 @@ skin_minimal = Skin( "minimal", gametype_text = "%s:", gametype_width = 96, gametype_fontsize = 10, - gametype_center = False, + gametype_align = -1, elo_pos = (82,30), elo_text = "Elo %.0f", elo_color = (0.7, 0.7, 0.7), - elo_center = False, + elo_align = 1, rank_pos = None, nostats_pos = (82,30), nostats_fontsize = 10, @@ -61,16 +61,16 @@ skin_minimal = Skin( "minimal", nostats_center = False, kdr_pos = (392,15), kdr_fontsize = 10, - kdr_colortop = (0.1, 0.8, 0.1), - kdr_colormid = (0.6, 0.6, 0.1), - kdr_colorbot = (0.8, 0.1, 0.1), + kdr_colortop = (0.6, 0.8, 0.6), + kdr_colormid = (0.6, 0.6, 0.6), + kdr_colorbot = (0.8, 0.6, 0.6), kills_pos = None, deaths_pos = None, winp_pos = (508,15), winp_fontsize = 10, - winp_colortop = (0.1, 0.6, 0.6), - winp_colormid = (0.2, 0.6, 0.2), - winp_colorbot = (0.6, 0.6, 0.1), + winp_colortop = (0.6, 0.8, 0.8), + winp_colormid = (0.6, 0.6, 0.6), + winp_colorbot = (0.8, 0.8, 0.6), wins_pos = None, loss_pos = None, ptime_pos = (451,30), diff --git a/xonstat/batch/badges/img/overlay_classic.png b/xonstat/batch/badges/img/overlay_classic.png index 9578e90..c7a2347 100644 Binary files a/xonstat/batch/badges/img/overlay_classic.png and b/xonstat/batch/badges/img/overlay_classic.png differ diff --git a/xonstat/batch/badges/img/overlay_classic.xcf b/xonstat/batch/badges/img/overlay_classic.xcf index ab01944..aafc152 100644 Binary files a/xonstat/batch/badges/img/overlay_classic.xcf and b/xonstat/batch/badges/img/overlay_classic.xcf differ diff --git a/xonstat/batch/badges/img/overlay_minimal.png b/xonstat/batch/badges/img/overlay_minimal.png index fa8eb8b..184af23 100644 Binary files a/xonstat/batch/badges/img/overlay_minimal.png and b/xonstat/batch/badges/img/overlay_minimal.png differ diff --git a/xonstat/batch/badges/img/overlay_minimal.xcf b/xonstat/batch/badges/img/overlay_minimal.xcf index fe74fd6..047278e 100644 Binary files a/xonstat/batch/badges/img/overlay_minimal.xcf and b/xonstat/batch/badges/img/overlay_minimal.xcf differ diff --git a/xonstat/batch/badges/skin.py b/xonstat/batch/badges/skin.py index a2bb0ed..5083446 100644 --- a/xonstat/batch/badges/skin.py +++ b/xonstat/batch/badges/skin.py @@ -8,6 +8,7 @@ from xonstat.util import strip_colors, qfont_decode, _all_colors # similar to html_colors() from util.py _contrast_threshold = 0.5 +# standard colorset (^0 ... ^9) _dec_colors = [ (0.5,0.5,0.5), (1.0,0.0,0.0), (0.2,1.0,0.0), @@ -21,14 +22,13 @@ _dec_colors = [ (0.5,0.5,0.5), ] -def writepng(filename, buf, width, height): +# function to write compressed PNG (using zlib) +def write_png(filename, buf, width, height): width_byte_4 = width * 4 - # fix color ordering (BGR -> RGB) + # fix color ordering (BGRA -> RGBA) for byte in xrange(width*height): pos = byte * 4 buf[pos:pos+4] = buf[pos+2] + buf[pos+1] + buf[pos+0] + buf[pos+3] - # merge lines - #raw_data = b"".join(b'\x00' + buf[span:span + width_byte_4] for span in xrange((height - 1) * width * 4, -1, - width_byte_4)) raw_data = b"".join(b'\x00' + buf[span:span + width_byte_4] for span in range(0, (height-1) * width * 4 + 1, width_byte_4)) def png_pack(png_tag, data): chunk_head = png_tag + data @@ -53,6 +53,9 @@ class Skin: # skin name name = "" + # render context + ctx = None + def __init__(self, name, **params): # default parameters self.name = name @@ -69,62 +72,72 @@ class Skin: 'gametype_fontsize':10, 'gametype_pos': (101,33), 'gametype_width': 94, + 'gametype_height': 0, 'gametype_color': (0.9, 0.9, 0.9), - 'gametype_text': "[ %s ]", - 'gametype_center': True, + 'gametype_text': "%s", + 'gametype_align': 0, 'num_gametypes': 3, 'nostats_fontsize': 12, 'nostats_pos': (101,59), 'nostats_color': (0.8, 0.2, 0.1), 'nostats_angle': -10, 'nostats_text': "no stats yet!", - 'nostats_center': True, + 'nostats_align': 0, 'elo_pos': (101,47), 'elo_fontsize': 10, 'elo_color': (1.0, 1.0, 0.5), 'elo_text': "Elo %.0f", - 'elo_center': True, + 'elo_align': 0, 'rank_fontsize': 8, - 'rank_pos': (101,57), + 'rank_pos': (101,58), 'rank_color': (0.8, 0.8, 1.0), 'rank_text': "Rank %d of %d", - 'rank_center': True, + 'rank_align': 0, 'wintext_fontsize': 10, 'wintext_pos': (508,3), 'wintext_color': (0.8, 0.8, 0.8), 'wintext_text': "Win Percentage", + 'wintext_align': 0, 'winp_fontsize': 12, 'winp_pos': (508,19), 'winp_colortop': (0.2, 1.0, 1.0), 'winp_colormid': (0.4, 0.8, 0.4), 'winp_colorbot': (1.0, 1.0, 0.2), + 'winp_align': 0, 'wins_fontsize': 8, 'wins_pos': (508,33), 'wins_color': (0.6, 0.8, 0.8), + 'wins_align': 0, 'loss_fontsize': 8, 'loss_pos': (508,43), 'loss_color': (0.8, 0.8, 0.6), + 'loss_align': 0, 'kdtext_fontsize': 10, 'kdtext_pos': (390,3), 'kdtext_width': 102, 'kdtext_color': (0.8, 0.8, 0.8), 'kdtext_bg': (0.8, 0.8, 0.8, 0.1), 'kdtext_text': "Kill Ratio", + 'kdtext_align': 0, 'kdr_fontsize': 12, 'kdr_pos': (392,19), 'kdr_colortop': (0.2, 1.0, 0.2), 'kdr_colormid': (0.8, 0.8, 0.4), 'kdr_colorbot': (1.0, 0.2, 0.2), + 'kdr_align': 0, 'kills_fontsize': 8, - 'kills_pos': (392,46), + 'kills_pos': (392,33), 'kills_color': (0.6, 0.8, 0.6), + 'kills_align': 0, 'deaths_fontsize': 8, - 'deaths_pos': (392,56), + 'deaths_pos': (392,43), 'deaths_color': (0.8, 0.6, 0.6), + 'deaths_align': 0, 'ptime_fontsize': 10, 'ptime_pos': (451,60), 'ptime_color': (0.1, 0.1, 0.1), 'ptime_text': "Playing Time: %s", + 'ptime_align': 0, } for k,v in params.items(): @@ -139,6 +152,39 @@ class Skin: return self.params[key] return None + def show_text(self, txt, pos, align=0, angle=None, offset=(0,0)): + ctx = self.ctx + + xoff, yoff, tw, th = ctx.text_extents(txt)[:4] + if align > 0: + ctx.move_to(pos[0]+offset[0]-xoff, pos[1]+offset[1]-yoff) + elif align < 0: + ctx.move_to(pos[0]+offset[0]-xoff-tw, pos[1]+offset[1]-yoff) + else: + ctx.move_to(pos[0]+offset[0]-xoff-tw/2, pos[1]+offset[1]-yoff) + ctx.save() + if angle: + ctx.rotate(math.radians(angle)) + ctx.show_text(txt) + ctx.restore() + + def set_font(self, fontsize, color, bold=False, italic=False): + ctx = self.ctx + font = self.font + slant = C.FONT_SLANT_ITALIC if italic else C.FONT_SLANT_NORMAL + weight = C.FONT_WEIGHT_BOLD if bold else C.FONT_WEIGHT_NORMAL + + ctx.select_font_face(font, slant, weight) + ctx.set_font_size(fontsize) + if len(color) == 1: + ctx.set_source_rgb(color[0], color[0], color[0]) + elif len(color) == 3: + ctx.set_source_rgb(color[0], color[1], color[2]) + elif len(color) == 4: + ctx.set_source_rgba(color[0], color[1], color[2], color[3]) + else: + ctx.set_source_rgb(1, 1, 1) + def render_image(self, data, output_filename): """Render an image for the given player id.""" @@ -154,15 +200,11 @@ class Skin: alivetime = data.total_stats['alivetime'] - font = "Xolonium" - if self.font == 1: - font = "DejaVu Sans" - - # build image surf = C.ImageSurface(C.FORMAT_ARGB32, self.width, self.height) ctx = C.Context(surf) + self.ctx = ctx ctx.set_antialias(C.ANTIALIAS_GRAY) # draw background @@ -221,7 +263,7 @@ class Skin: stripped_nick = strip_colors(qstr.replace(' ', '_')) # fontsize is reduced if width gets too large - ctx.select_font_face(font, C.FONT_SLANT_NORMAL, C.FONT_WEIGHT_NORMAL) + ctx.select_font_face(self.font, C.FONT_SLANT_NORMAL, C.FONT_WEIGHT_NORMAL) shrinknick = 0 while shrinknick < 10: ctx.set_font_size(self.nick_fontsize - shrinknick) @@ -278,77 +320,63 @@ class Skin: ## print elos and ranks - # show up to three gametypes the player has participated in - xoffset = 0 + xoffset, yoffset = 0, 0 + count = 0 for gt in data.total_stats['gametypes'][:self.num_gametypes]: if not elos.has_key(gt) or not ranks.has_key(gt): continue - - if self.gametype_pos: - ctx.select_font_face(font, C.FONT_SLANT_NORMAL, C.FONT_WEIGHT_BOLD) - ctx.set_font_size(self.gametype_fontsize) - ctx.set_source_rgb(self.gametype_color[0],self.gametype_color[1],self.gametype_color[2]) - txt = self.gametype_text % gt.upper() - xoff, yoff, tw, th = ctx.text_extents(txt)[:4] - if self.gametype_center: - ctx.move_to(self.gametype_pos[0]+xoffset-xoff-tw/2, self.gametype_pos[1]-yoff) + count += 1 + + # re-align segments if less than max. gametypes are shown + if count > 0: + if count < self.num_gametypes: + diff = self.num_gametypes - count + if diff % 2 == 0: + xoffset += (diff-1) * self.gametype_width + yoffset += (diff-1) * self.gametype_height else: - ctx.move_to(self.gametype_pos[0]+xoffset-xoff-tw, self.gametype_pos[1]-yoff) - ctx.show_text(txt) + xoffset += 0.5 * diff * self.gametype_width + yoffset += 0.5 * diff * self.gametype_height + + # show a number gametypes the player has participated in + for gt in data.total_stats['gametypes'][:self.num_gametypes]: + if not elos.has_key(gt) or not ranks.has_key(gt): + continue + + offset = (xoffset, yoffset) + if self.gametype_pos: + txt = self.gametype_text % gt.upper() + self.set_font(self.gametype_fontsize, self.gametype_color, bold=True) + self.show_text(txt, self.gametype_pos, self.gametype_align, offset=offset) - if not elos.has_key(gt) or not ranks.has_key(gt): - pass - if self.nostats_pos: - ctx.select_font_face(font, C.FONT_SLANT_NORMAL, C.FONT_WEIGHT_BOLD) - ctx.set_font_size(self.nostats_fontsize) - ctx.set_source_rgb(self.nostats_color[0],self.nostats_color[1],self.nostats_color[2]) - txt = self.nostats_text - xoff, yoff, tw, th = ctx.text_extents(txt)[:4] - if self.nostats_center: - ctx.move_to(self.nostats_pos[0]+xoffset-xoff-tw/2, self.nostats_pos[1]-yoff) - else: - ctx.move_to(self.nostats_pos[0]+xoffset-xoff, self.nostats_pos[1]-yoff) - ctx.save() - ctx.rotate(math.radians(self.nostats_angle)) - ctx.show_text(txt) - ctx.restore() - else: if self.elo_pos: - ctx.select_font_face(font, C.FONT_SLANT_NORMAL, C.FONT_WEIGHT_NORMAL) - ctx.set_font_size(self.elo_fontsize) - ctx.set_source_rgb(self.elo_color[0], self.elo_color[1], self.elo_color[2]) txt = self.elo_text % round(elos[gt], 0) - xoff, yoff, tw, th = ctx.text_extents(txt)[:4] - if self.elo_center: - ctx.move_to(self.elo_pos[0]+xoffset-xoff-tw/2, self.elo_pos[1]-yoff) - else: - ctx.move_to(self.elo_pos[0]+xoffset-xoff, self.elo_pos[1]-yoff) - ctx.show_text(txt) + self.set_font(self.elo_fontsize, self.elo_color) + self.show_text(txt, self.elo_pos, self.elo_align, offset=offset) if self.rank_pos: - ctx.select_font_face(font, C.FONT_SLANT_NORMAL, C.FONT_WEIGHT_NORMAL) - ctx.set_font_size(self.rank_fontsize) - ctx.set_source_rgb(self.rank_color[0], self.rank_color[1], self.rank_color[2]) txt = self.rank_text % ranks[gt] - xoff, yoff, tw, th = ctx.text_extents(txt)[:4] - if self.rank_center: - ctx.move_to(self.rank_pos[0]+xoffset-xoff-tw/2, self.rank_pos[1]-yoff) - else: - ctx.move_to(self.rank_pos[0]+xoffset-xoff, self.rank_pos[1]-yoff) - ctx.show_text(txt) - - xoffset += self.gametype_width + self.set_font(self.rank_fontsize, self.rank_color) + self.show_text(txt, self.rank_pos, self.rank_align, offset=offset) + + xoffset += self.gametype_width + yoffset += self.gametype_height + else: + if self.nostats_pos: + xoffset += (self.num_gametypes-2) * self.gametype_width + yoffset += (self.num_gametypes-2) * self.gametype_height + offset = (xoffset, yoffset) + + txt = self.nostats_text + self.set_font(self.nostats_fontsize, self.nostats_color, bold=True) + self.show_text(txt, self.nostats_pos, self.nostats_align, angle=self.nostats_angle, offset=offset) # print win percentage if self.wintext_pos: - ctx.select_font_face(font, C.FONT_SLANT_NORMAL, C.FONT_WEIGHT_NORMAL) - ctx.set_font_size(self.wintext_fontsize) - ctx.set_source_rgb(self.wintext_color[0], self.wintext_color[1], self.wintext_color[2]) txt = self.wintext_text - xoff, yoff, tw, th = ctx.text_extents(txt)[:4] - ctx.move_to(self.wintext_pos[0]-xoff-tw/2, self.wintext_pos[1]-yoff) - ctx.show_text(txt) + self.set_font(self.wintext_fontsize, self.wintext_color) + self.show_text(txt, self.wintext_pos, self.wintext_align) txt = "???" try: @@ -358,8 +386,6 @@ class Skin: ratio = 0 if self.winp_pos: - ctx.select_font_face(font, C.FONT_SLANT_NORMAL, C.FONT_WEIGHT_BOLD) - ctx.set_font_size(self.winp_fontsize) if ratio >= 0.5: nr = 2*(ratio-0.5) r = nr*self.winp_colortop[0] + (1-nr)*self.winp_colormid[0] @@ -370,43 +396,30 @@ class Skin: r = nr*self.winp_colormid[0] + (1-nr)*self.winp_colorbot[0] g = nr*self.winp_colormid[1] + (1-nr)*self.winp_colorbot[1] b = nr*self.winp_colormid[2] + (1-nr)*self.winp_colorbot[2] - ctx.set_source_rgb(r, g, b) - xoff, yoff, tw, th = ctx.text_extents(txt)[:4] - ctx.move_to(self.winp_pos[0]-xoff-tw/2, self.winp_pos[1]-yoff) - ctx.show_text(txt) + self.set_font(self.winp_fontsize, (r,g,b), bold=True) + self.show_text(txt, self.winp_pos, self.winp_align) if self.wins_pos: - ctx.select_font_face(font, C.FONT_SLANT_NORMAL, C.FONT_WEIGHT_NORMAL) - ctx.set_font_size(self.wins_fontsize) - ctx.set_source_rgb(self.wins_color[0], self.wins_color[1], self.wins_color[2]) txt = "%d win" % wins if wins != 1: txt += "s" - xoff, yoff, tw, th = ctx.text_extents(txt)[:4] - ctx.move_to(self.wins_pos[0]-xoff-tw/2, self.wins_pos[1]-yoff) - ctx.show_text(txt) + self.set_font(self.wins_fontsize, self.wins_color) + self.show_text(txt, self.wins_pos, self.wins_align) if self.loss_pos: - ctx.set_font_size(self.loss_fontsize) - ctx.set_source_rgb(self.loss_color[0], self.loss_color[1], self.loss_color[2]) txt = "%d loss" % losses if losses != 1: txt += "es" - xoff, yoff, tw, th = ctx.text_extents(txt)[:4] - ctx.move_to(self.loss_pos[0]-xoff-tw/2, self.loss_pos[1]-yoff) - ctx.show_text(txt) + self.set_font(self.loss_fontsize, self.loss_color) + self.show_text(txt, self.loss_pos, self.loss_align) # print kill/death ratio if self.kdtext_pos: - ctx.select_font_face(font, C.FONT_SLANT_NORMAL, C.FONT_WEIGHT_NORMAL) - ctx.set_font_size(self.kdtext_fontsize) - ctx.set_source_rgb(self.kdtext_color[0], self.kdtext_color[1], self.kdtext_color[2]) txt = self.kdtext_text - xoff, yoff, tw, th = ctx.text_extents(txt)[:4] - ctx.move_to(self.kdtext_pos[0]-xoff-tw/2, self.kdtext_pos[1]-yoff) - ctx.show_text(txt) + self.set_font(self.kdtext_fontsize, self.kdtext_color) + self.show_text(txt, self.kdtext_pos, self.kdtext_align) txt = "???" try: @@ -416,8 +429,6 @@ class Skin: ratio = 0 if self.kdr_pos: - ctx.select_font_face(font, C.FONT_SLANT_NORMAL, C.FONT_WEIGHT_BOLD) - ctx.set_font_size(self.kdr_fontsize) if ratio >= 1.0: nr = ratio-1.0 if nr > 1: @@ -430,52 +441,37 @@ class Skin: r = nr*self.kdr_colormid[0] + (1-nr)*self.kdr_colorbot[0] g = nr*self.kdr_colormid[1] + (1-nr)*self.kdr_colorbot[1] b = nr*self.kdr_colormid[2] + (1-nr)*self.kdr_colorbot[2] - ctx.set_source_rgb(r, g, b) - xoff, yoff, tw, th = ctx.text_extents(txt)[:4] - ctx.move_to(self.kdr_pos[0]-xoff-tw/2, self.kdr_pos[1]-yoff) - ctx.show_text(txt) + self.set_font(self.kdr_fontsize, (r,g,b), bold=True) + self.show_text(txt, self.kdr_pos, self.kdr_align) if self.kills_pos: - ctx.select_font_face(font, C.FONT_SLANT_NORMAL, C.FONT_WEIGHT_NORMAL) - ctx.set_font_size(self.kills_fontsize) - ctx.set_source_rgb(self.kills_color[0], self.kills_color[1], self.kills_color[2]) txt = "%d kill" % kills if kills != 1: txt += "s" - xoff, yoff, tw, th = ctx.text_extents(txt)[:4] - ctx.move_to(self.kills_pos[0]-xoff-tw/2, self.kills_pos[1]+yoff) - ctx.show_text(txt) + self.set_font(self.kills_fontsize, self.kills_color) + self.show_text(txt, self.kills_pos, self.kills_align) if self.deaths_pos: - ctx.select_font_face(font, C.FONT_SLANT_NORMAL, C.FONT_WEIGHT_NORMAL) - ctx.set_font_size(self.deaths_fontsize) - ctx.set_source_rgb(self.deaths_color[0], self.deaths_color[1], self.deaths_color[2]) + txt = "" if deaths is not None: txt = "%d death" % deaths if deaths != 1: txt += "s" - else: - txt = "" - xoff, yoff, tw, th = ctx.text_extents(txt)[:4] - ctx.move_to(self.deaths_pos[0]-xoff-tw/2, self.deaths_pos[1]+yoff) - ctx.show_text(txt) + self.set_font(self.deaths_fontsize, self.deaths_color) + self.show_text(txt, self.deaths_pos, self.deaths_align) # print playing time if self.ptime_pos: - ctx.select_font_face(font, C.FONT_SLANT_NORMAL, C.FONT_WEIGHT_NORMAL) - ctx.set_font_size(self.ptime_fontsize) - ctx.set_source_rgb(self.ptime_color[0], self.ptime_color[1], self.ptime_color[2]) txt = self.ptime_text % str(alivetime) - xoff, yoff, tw, th = ctx.text_extents(txt)[:4] - ctx.move_to(self.ptime_pos[0]-xoff-tw/2, self.ptime_pos[1]-yoff) - ctx.show_text(txt) + self.set_font(self.ptime_fontsize, self.ptime_color) + self.show_text(txt, self.ptime_pos, self.ptime_align) # save to PNG #surf.write_to_png(output_filename) surf.flush() imgdata = surf.get_data() - writepng(output_filename, imgdata, self.width, self.height) + write_png(output_filename, imgdata, self.width, self.height)