From 546f8c436b08b957bb0e687b2bdb761f3ae88c99 Mon Sep 17 00:00:00 2001 From: Stefanos Harhalakis Date: Tue, 3 Aug 2010 18:45:36 +0000 Subject: [PATCH] Faster icon rotation using various cachings --- src/icon.py | 179 ++++++++++++++++++++++++++++++++++++++----------------- src/icongrid.py | 26 +++++--- 2 files changed, 142 insertions(+), 63 deletions(-) diff --git a/src/icon.py b/src/icon.py index df559cc..aa8d5a8 100755 --- a/src/icon.py +++ b/src/icon.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # coding=UTF-8 -# +# # Copyright (C) 2010 Stefanos Harhalakis # # This file is part of wifieye. @@ -39,6 +39,9 @@ from xdg.IconTheme import getIconPath #import config import apps +# Background surface for icons +iconbg=None + # Load an icon # Fall-back to default/blue if not found or name==None def getIcon(name, iconsize): @@ -70,6 +73,7 @@ class Icon(gobject.GObject): self.name=None self.icon=None + self.sicon=None self.lastpress=0 self.ispressed=False @@ -84,6 +88,8 @@ class Icon(gobject.GObject): self.angle=0 + self.cached_icons={} + def timePressed(self): """ return how much time a button is pressed """ dt=time.time() - self.lastpress @@ -94,48 +100,66 @@ class Icon(gobject.GObject): if dt==None: self.name=None self.icon=None + self.sicon=None else: self.name=dt['id'] self.icon=dt['icon2'] + self.sicon=None + self.cached_icons={} self.invalidate() def getSize(self): return(self.config.iconsize+self.config.iconspace) def setAngle(self, angle): - if self.angle==angle: - print "Same angle" - return + """ Set the angle. Return True if the angle changed or False if it + didn't. The caller should invalidate the icon """ + + # The step in degrees + step=15 + + angle2=int(angle/step)*step - self.angle=angle + if angle2==self.angle: + return(False) + + self.angle=angle2 # The caller should be responsible for redrawing. # If we call invalidate() here there is the risk of having # icons rotate individually using different angles # self.invalidate() - def draw(self, cr, x, y): - self.draw_queued=False - self.x=x - self.y=y + return(True) - if self.icon==None and not self.isconfig: - return + def mkbg(self, t_pressed): + """ Create the background of the icon and cache it as a global + variable among all icons and widget instances """ + global iconbg - cr.save() + if iconbg!=None and t_pressed<=0.001: + return(iconbg) + + w=self.config.iconsize + self.config.iconspace + s=cairo.ImageSurface(cairo.FORMAT_ARGB32, w, w) + cr0=cairo.Context(s) + cr=gtk.gdk.CairoContext(cr0) - cr.set_source_rgba(0.1, 0.1, 0.1, 1) + cr.set_source_rgba(0.1, 0.1, 0.1, 1) cr.set_line_width(5) #if self.ispressed: - if self.timePressed() <= self.presstime or self.ispressed: - t=1.0 * min(self.timePressed(), self.presstime) / self.presstime + if t_pressed>0.001 and \ + (t_pressed <= self.presstime or self.ispressed): + t=1.0 * min(t_pressed, self.presstime) / self.presstime g=0.3+0.5*t b=0.3+0.7*t cr.set_source_rgba(0, g, b, 0.7) else: cr.set_source_rgba(0.3, 0.3, 0.3, 0.7) + x=0 + y=0 x3=x + (self.config.iconspace/6) y3=y + (self.config.iconspace/6) @@ -152,62 +176,105 @@ class Icon(gobject.GObject): cr.fill() cr.clip() cr.paint() - cr.restore() +# cr.restore() - if self.icon==None: - return + if t_pressed<0.001: + iconbg=s - icon=self.icon + return(s) - icon2=icon + def get_sicon(self): + """ Return the icon as a surface. Cache it. """ + if self.sicon!=None: + return(self.sicon) -# Old method. Faster rotation but without support for rotation -# animation + w=self.config.iconsize + s=cairo.ImageSurface(cairo.FORMAT_ARGB32, w, w) + cr0=cairo.Context(s) + cr=gtk.gdk.CairoContext(cr0) -# if mode=='l': -# icon2=icon -# else: -# icon2=icon.rotate_simple(gdk.PIXBUF_ROTATE_COUNTERCLOCKWISE) + cr.set_source_pixbuf(self.icon, 0, 0) + cr.paint() -# cr.save() -# x3=x + (self.config.iconspace/2) -# y3=y + (self.config.iconspace/2) -# cr.set_source_pixbuf(icon2, x3, y3) -# -# cr.paint() -# cr.restore() + self.sicon=s + return(s) - # Width is the iconsize plus the empty border around the icon - w=self.config.iconsize + self.config.iconspace + def get_paint_icon(self): + """ Return the icon to paint as a surface. The icon is rotated + as needed. The result is cached. """ + angle=self.angle - # This is used to locate the center of the surface - dx=int(w/2) + if self.timePressed() <= self.presstime or self.ispressed: + t=self.timePressed() + pressed=True + else: + t=0 + pressed=False + + if not pressed and self.cached_icons.has_key(angle): + return(self.cached_icons[angle]) + + w=self.config.iconsize + self.config.iconspace + s=cairo.ImageSurface(cairo.FORMAT_ARGB32, w, w) + cr0=cairo.Context(s) + cr=gtk.gdk.CairoContext(cr0) + + # Paint the background + s2=self.mkbg(t) + cr.save() + cr.set_source_surface(s2, 0, 0) + cr.paint() + cr.restore() + + # If there is no icon then don't do anything more + if self.icon!=None: + # Get the icon as a surface (get_sicon() will cache the surface) + sicon=self.get_sicon() + + # Width is the iconsize plus the empty border around the icon + #w=self.config.iconsize + self.config.iconspace + + # This is used to locate the center of the surface + dx=int(w/2) + + # This is the delta from the center where icons are drawn + dx2=int(self.config.iconsize/2) + +# cr.save() - # This is the delta from the center where icons are drawn - dx2=int(self.config.iconsize/2) + # A transformation matrix with dx/dy set to point to the center + m=cairo.Matrix(1, 0, 0, 1, dx, dx) + cr.set_matrix(m) + # Transform degrees to rads + rot=-1 * pi * 2 * self.angle / 360 + cr.rotate(rot) + # Draw the icon + cr.set_source_surface(sicon, -dx2, -dx2) # Faster than pixbuf +# cr.set_source_pixbuf(icon2, -dx2, -dx2) + cr.paint() - # A surface to draw on - t_s=cairo.ImageSurface(cairo.FORMAT_ARGB32, w, w) +# cr.restore() - # And a context to draw - t_cr0=cairo.Context(t_s) - t_cr=gtk.gdk.CairoContext(t_cr0) + if not pressed: + self.cached_icons[angle]=s - # A transformation matrix with dx/dy set to point to the center - m=cairo.Matrix(1, 0, 0, 1, dx, dx) - t_cr.set_matrix(m) - # Transform degrees to rads - rot=-1 * pi * 2 * self.angle / 360 - t_cr.rotate(rot) - # Draw the icon - t_cr.set_source_pixbuf(icon2, -dx2, -dx2) - t_cr.paint() + return(s) + + + def draw(self, cr, x, y): + self.draw_queued=False + self.x=x + self.y=y + + if self.icon==None and not self.isconfig: + return - # Draw the rotated icon on the main cairo context cr.save() - cr.set_source_surface(t_s, x, y) + s=self.get_paint_icon() + cr.set_source_surface(s, x, y) cr.paint() + cr.restore() return(False) @@ -261,7 +328,7 @@ class Icon(gobject.GObject): window=self.window else: self.window=window - + if window==None: return diff --git a/src/icongrid.py b/src/icongrid.py index 0278dde..3571736 100755 --- a/src/icongrid.py +++ b/src/icongrid.py @@ -110,16 +110,22 @@ class IconGrid(object): #(gobject.GObject): #self.queue_draw() self.angle_timer_start=time.time() gobject.timeout_add(20, self.timerAngle) - + else: + if self.mode=='l': + self.setAngle(0) + else: + self.setAngle(90) + def timerAngle(self): - self.queue_draw() - if self.angle_timer_start==0: - self.angle_timer_start=time.time() + self.angle_timer_start=time.time()-0.05 dt=time.time()-self.angle_timer_start - da=90.0*dt/0.5 + # Duration of the rotation effect + rotation_time=0.5 + + da=90.0*dt/rotation_time if self.mode=='l': angle=90-da @@ -135,7 +141,8 @@ class IconGrid(object): #(gobject.GObject): else: ret=True - self.setAngle(angle) + if self.setAngle(angle): + self.queue_draw() if ret==False: self.angle_timer_start=0 @@ -186,9 +193,14 @@ class IconGrid(object): #(gobject.GObject): ico.draw(cr, x2, y2) def setAngle(self, angle): + """ Return True/False indicating that angle has changed """ + ret=False for x,y in self.icons: ic=self.icons.get(x,y) - ic.setAngle(angle) + if ic.setAngle(angle): + ret=True + + return(ret) def do_expose_event(self, event): cr=self.window.cairo_create() -- 1.7.9.5