def initialize_renderers(self): ''' We will initialize the draw objects for each individual/solution as to save computations. Instead of reinitializing them each iteration we will wipe the images by drawing a opaque rectangle in white or black. Then render the shapes on top. This method creates those draw objects and stores them in a list. ''' self.draws = [] self.bg_brush = Brush((0, 0, 0) if self.bg_color == 'black' else (255, 255, 255), 255) self.bg_coords = (0, 0, *self.img_size) if self.shape_type in {'circle', 'square'}: self.render_method = self.render_uni_verts else: self.render_method = self.render_multi_verts if self.shape_type == 'circle': self.draw_method = 'ellipse' elif self.shape_type == 'square': self.draw_method = 'rectangle' else: self.draw_method = self.shape_type for _ in self.pop_range: draw = Draw('RGBA', self.img_size, self.bg_color) draw.setantialias(False) self.draws.append([draw, getattr(draw, self.draw_method)])
def render_img_oaat_exis_mode(self, i: int): ''' One At A Time Renders the last image that was initialized ''' img = self.bgs[i] draw = Draw(img) draw.setantialias(False) draw_method = getattr(draw, self.draw_method) self.render_method(draw_method, self.mapped_population_b10[i, self.b10_idx[-1]]) return np.frombuffer(draw.tobytes(), dtype='uint8').reshape(-1, 4, order='C')[:, :3]
def _fill_annotations(a: Annotation, label: str, color=0xFF, out=None): if out is None: out = Image.new(mode='L', size=a.image.size) brush = Brush(color) d = Draw(out) d.setantialias(False) for o in a.iter_objects(label): xy = a.points(o) d.polygon(xy.flatten(), brush) return d.flush()
def _stroke_annotations(a: Annotation, label: str, color=0xFF, width=5, out=None): if out is None: out = Image.new(mode='L', size=a.image.size) pen = Pen(color, width) d = Draw(out) d.setantialias(False) # Should not antialias masks for o in a.iter_objects(label): xy = a.points(o) d.polygon(xy.flatten(), pen) return d.flush()
class Canvas(CanvasBase): # fonts appear smaller in aggdraw than with cairo # fix that here: fontScale = 1.2 def __init__(self, img=None, imageType=None, # determines file type fileName=None, # if set determines output file name size=None, ): if img is None: try: import Image except ImportError: from PIL import Image if size is None: raise ValueError('please provide either an image or a size') img = Image.new('RGBA', size, "white") self.image = img self.draw = Draw(img) self.draw.setantialias(True) if size is None: self.size = self.draw.size else: self.size = size if imageType and imageType not in ('png', 'jpg'): raise ValueError('unsupported image type for agg canvas') self.drawType = imageType self.fileName = fileName def _doLine(self, p1, p2, pen, **kwargs): if kwargs.get('dash', (0, 0)) == (0, 0): self.draw.line((p1[0], p1[1], p2[0], p2[1]), pen) else: dash = kwargs['dash'] pts = self._getLinePoints(p1, p2, dash) currDash = 0 dashOn = True while currDash < (len(pts) - 1): if dashOn: p1 = pts[currDash] p2 = pts[currDash + 1] self.draw.line((p1[0], p1[1], p2[0], p2[1]), pen) currDash += 1 dashOn = not dashOn def addCanvasLine(self, p1, p2, color=(0, 0, 0), color2=None, **kwargs): if color2 and color2 != color: mp = (p1[0] + p2[0]) / 2., (p1[1] + p2[1]) / 2. color = convertColor(color) self._doLine(p1, mp, Pen(color, kwargs.get('linewidth', 1)), **kwargs) color2 = convertColor(color2) self._doLine(mp, p2, Pen(color2, kwargs.get('linewidth', 1)), **kwargs) else: color = convertColor(color) self._doLine(p1, p2, Pen(color, kwargs.get('linewidth', 1)), **kwargs) def addCanvasText(self, text, pos, font, color=(0, 0, 0), **kwargs): orientation = kwargs.get('orientation', 'E') color = convertColor(color) aggFont = Font(color, faceMap[font.face], size=font.size * self.fontScale) blocks = list(re.finditer(r'\<(.+?)\>(.+?)\</\1\>', text)) w, h = 0, 0 supH = 0 subH = 0 if not len(blocks): w, h = self.draw.textsize(text, aggFont) tw, th = w, h offset = w * pos[2] dPos = pos[0] - w / 2. + offset, pos[1] - h / 2. self.draw.text(dPos, text, aggFont) else: dblocks = [] idx = 0 for block in blocks: blockStart, blockEnd = block.span(0) if blockStart != idx: # untagged text: tblock = text[idx:blockStart] tw, th = self.draw.textsize(tblock, aggFont) w += tw h = max(h, th) dblocks.append((tblock, '', tw, th)) fmt = block.groups()[0] tblock = block.groups()[1] if fmt in ('sub', 'sup'): lFont = Font(color, faceMap[font.face], size=0.8 * font.size * self.fontScale) else: lFont = aggFont tw, th = self.draw.textsize(tblock, lFont) w += tw if fmt == 'sub': subH = max(subH, th) elif fmt == 'sup': supH = max(supH, th) else: h = max(h, th) dblocks.append((tblock, fmt, tw, th)) idx = blockEnd if idx != len(text): # untagged text: tblock = text[idx:] tw, th = self.draw.textsize(tblock, aggFont) w += tw h = max(h, th) dblocks.append((tblock, '', tw, th)) supH *= 0.5 subH *= 0.5 h += supH + subH offset = w * pos[2] if orientation == 'W': dPos = [pos[0] - w + offset, pos[1] - h / 2.] elif orientation == 'E': dPos = [pos[0] + offset, pos[1] - h / 2.] else: dPos = [pos[0] - w / 2. + offset, pos[1] - h / 2.] if supH: dPos[1] += supH for txt, fmt, tw, th in dblocks: tPos = dPos.copy() if fmt == 'sub': tPos[1] += subH elif fmt == 'sup': tPos[1] -= supH if fmt in ('sub', 'sup'): lFont = Font(color, faceMap[font.face], size=0.8 * font.size * self.fontScale) else: lFont = aggFont self.draw.text(tPos, txt, lFont) dPos[0] += tw return (tw + th * .4, th + th * .4, offset) def addCanvasPolygon(self, ps, color=(0, 0, 0), fill=True, stroke=False, **kwargs): if not fill and not stroke: return dps = [] for p in ps: dps.extend(p) color = convertColor(color) brush = None pen = None if fill: brush = Brush(color) if stroke: pen = Pen(color) self.draw.polygon(dps, pen, brush) def addCanvasDashedWedge(self, p1, p2, p3, dash=(2, 2), color=(0, 0, 0), color2=None, **kwargs): pen = Pen(color, kwargs.get('linewidth', 1)) dash = (3, 3) pts1 = self._getLinePoints(p1, p2, dash) pts2 = self._getLinePoints(p1, p3, dash) if len(pts2) < len(pts1): pts2, pts1 = pts1, pts2 for i in range(len(pts1)): self.draw.line((pts1[i][0], pts1[i][1], pts2[i][0], pts2[i][1]), pen) def flush(self): self.draw.flush() if self.fileName: self.image.save(self.fileName)
class Canvas(CanvasBase): # fonts appear smaller in aggdraw than with cairo # fix that here: fontScale=1.2 def __init__(self, img=None, imageType=None, # determines file type fileName=None, # if set determines output file name size=None, ): if img is None: import Image if size is None: raise ValueError,'please provide either an image or a size' img = Image.new('RGBA',size,"white") self.image = img self.draw = Draw(img) self.draw.setantialias(True) if size is None: self.size = self.draw.size else: self.size = size if imageType and imageType not in ('png','jpg'): raise ValueError,'unsupported image type for agg canvas' self.drawType=imageType self.fileName=fileName def _doLine(self, p1, p2, pen, **kwargs): if kwargs.get('dash',(0,0)) == (0,0): self.draw.line((p1[0],p1[1],p2[0],p2[1]),pen) else: dash = kwargs['dash'] pts = self._getLinePoints(p1,p2,dash) currDash = 0 dashOn = True while currDash<(len(pts)-1): if dashOn: p1 = pts[currDash] p2 = pts[currDash+1] self.draw.line((p1[0],p1[1],p2[0],p2[1]),pen) currDash+=1 dashOn = not dashOn def addCanvasLine(self, p1, p2, color=(0,0,0), color2=None, **kwargs): if color2 and color2!=color: mp = (p1[0]+p2[0])/2.,(p1[1]+p2[1])/2. color = convertColor(color) self._doLine(p1,mp,Pen(color,kwargs.get('linewidth',1)),**kwargs) color2 = convertColor(color2) self._doLine(mp,p2,Pen(color2,kwargs.get('linewidth',1)),**kwargs) else: color = convertColor(color) self._doLine(p1,p2,Pen(color,kwargs.get('linewidth',1)),**kwargs) def addCanvasText(self,text,pos,font,color=(0,0,0),**kwargs): orientation=kwargs.get('orientation','E') color = convertColor(color) aggFont = Font(color,faceMap[font.face],size=font.size*self.fontScale) blocks = list(re.finditer(r'\<(.+?)\>(.+?)\</\1\>',text)) w,h = 0,0 supH=0 subH=0 if not len(blocks): w,h=self.draw.textsize(text,aggFont) bw,bh=w*1.1,h*1.1 dPos = pos[0]-bw/2.,pos[1]-bh/2. bgColor=kwargs.get('bgColor',(1,1,1)) bgColor = convertColor(bgColor) self.draw.rectangle((dPos[0],dPos[1],dPos[0]+bw,dPos[1]+bh), None,Brush(bgColor)) dPos = pos[0]-w/2.,pos[1]-h/2. self.draw.text(dPos,text,aggFont) else: dblocks=[] idx=0 for block in blocks: blockStart,blockEnd=block.span(0) if blockStart != idx: # untagged text: tblock = text[idx:blockStart] tw,th=self.draw.textsize(tblock,aggFont) w+=tw h = max(h,th) dblocks.append((tblock,'',tw,th)) fmt = block.groups()[0] tblock = block.groups()[1] if fmt in ('sub','sup'): lFont = Font(color,faceMap[font.face],size=0.8*font.size*self.fontScale) else: lFont = aggFont tw,th=self.draw.textsize(tblock,lFont) w+=tw if fmt == 'sub': subH = max(subH,th) elif fmt=='sup': supH = max(supH,th) else: h = max(h,th) dblocks.append((tblock,fmt,tw,th)) idx = blockEnd if idx!=len(text): # untagged text: tblock = text[idx:] tw,th=self.draw.textsize(tblock,aggFont) w+=tw h = max(h,th) dblocks.append((tblock,'',tw,th)) supH *= 0.25 subH *= 0.25 h += supH + subH bw,bh=w*1.1,h #dPos = pos[0]-bw/2.,pos[1]-bh/2. dPos = [pos[0]-w/2.,pos[1]-h/2.] if orientation=='W': dPos = [pos[0]-w,pos[1]-h/2.] elif orientation=='E': dPos = [pos[0],pos[1]-h/2.] else: dPos = [pos[0]-w/2,pos[1]-h/2.] bgColor=kwargs.get('bgColor',(1,1,1)) bgColor = convertColor(bgColor) self.draw.rectangle((dPos[0],dPos[1],dPos[0]+bw,dPos[1]+bh), None,Brush(bgColor)) if supH: dPos[1]+=supH for txt,fmt,tw,th in dblocks: tPos = dPos[:] if fmt=='sub': tPos[1]+=subH elif fmt=='sup': tPos[1]-=supH if fmt in ('sub','sup'): lFont = Font(color,faceMap[font.face],size=0.8*font.size*self.fontScale) else: lFont = aggFont self.draw.text(tPos,txt,lFont) dPos[0]+=tw def addCanvasPolygon(self,ps,color=(0,0,0),fill=True,stroke=False,**kwargs): if not fill and not stroke: return dps = [] for p in ps: dps.extend(p) color = convertColor(color) brush=None pen=None if fill: brush = Brush(color) if stroke: pen = Pen(color) self.draw.polygon(dps,pen,brush) def addCanvasDashedWedge(self,p1,p2,p3,dash=(2,2),color=(0,0,0), color2=None,**kwargs): pen = Pen(color,kwargs.get('linewidth',1)) dash = (3,3) pts1 = self._getLinePoints(p1,p2,dash) pts2 = self._getLinePoints(p1,p3,dash) if len(pts2)<len(pts1): pts2,pts1=pts1,pts2 for i in range(len(pts1)): self.draw.line((pts1[i][0],pts1[i][1],pts2[i][0],pts2[i][1]),pen) def flush(self): self.draw.flush() if self.fileName: self.image.save(self.fileName)
class Drawbject(object): """An abstract class that serves as the foundation for all KLDraw shapes. All Drawbjects are drawn on an internal surface using the aggdraw drawing library, which can then be drawn to the display buffer using blit() and displayed on the screen using flip(). For more infomration on drawing in KLibs, please refer to the guide in the documentation. Args: width (int): The width of the shape in pixels. height (int): The height of the shape in pixels. stroke (List[width, Tuple[color], alignment]): The stroke of the shape, indicating the width, color, and alignment (inner, center, or outer) of the stroke. fill (Tuple[color]): The fill color for the shape expressed as an iterable of integer values from 0 to 255 representing an RGB or RGBA color (e.g. (255,0,0,128) for bright red with 50% transparency.) rotation (int|float, optional): The degrees by which to rotate the Drawbject during rendering. Defaults to 0. Attributes: stroke_color (None or Tuple[color]): The stroke color for the shape, expressed as an iterable of integer values from 0 to 255 representing an RGB or RGBA color. Defaults to 'None' if the shape has no stroke. stroke_width (int): The stroke width for the in pixels. Defaults to '0' if the shape has no stroke. stroke_alignment (int): The stroke alignment for the shape (inner, center, or outer). Defaults to '1' (STROKE_INNER) if the shape has no stroke. fill_color (None or Tuple[color]): The fill color for the shape, expressed as an iterable of integer values from 0 to 255 representing an RGB or RGBA color. Defaults to 'None' if the shape has no fill. opacity (int): The opacity of the shape, expressed as an integer from 0 (fully transparent) to 255 (fully opaque). object_width (int): The width of the shape in pixels. object_height (int): The height of the shape in pixels. surface_width (int): The width of the draw surface in pixels. At minimum two pixels wider than the object_width (if no stroke or stroke is inner aligned), at maximum (2 + 2*stroke_width) pixels wider than object width (if stroke is outer aligned). surface_height (int): The height of the draw surface in pixels. At minimum two pixels wider than the object_height (if no stroke or stroke is inner aligned), at maximum (2 + 2*stroke_height) pixels wider than object height (if stroke is outer aligned). surface (:obj:`aggdraw.Draw`): The aggdraw context on which the shape is drawn. When a shape is drawn to the surface, it is immediately applied to the canvas. canvas (:obj:`PIL.Image.Image`): The Image object that contains the shape of the Drawbject before opacity has been applied. Initialized upon creation with a size of (surface_width x surface_height). rendered (None or :obj:`numpy.array`): The rendered surface containing the shape, which is created using the render() method. If the Drawbject has not yet been rendered, this attribute will be 'None'. rotation (int): The rotation of the shape in degrees. Will be equal to 0 if no rotation is set. """ transparent_brush = Brush((255, 0, 0), 0) def __init__(self, width, height, stroke, fill, rotation=0): super(Drawbject, self).__init__() self.surface = None self.canvas = None self.rendered = None self.__stroke = None self.stroke_width = 0 self.stroke_color = None self.stroke_alignment = STROKE_OUTER self.stroke = stroke self.__fill = None self.fill_color = None self.fill = fill self.__dimensions = None self.object_width = width self.object_height = height self.rotation = rotation self._init_surface() def __str__(self): properties = [ self.__name__, self.surface_width, self.surface_height, hex(id(self)) ] return "klibs.Drawbject.{0} ({1} x {2}) at {3}".format(*properties) def _init_surface(self): self._update_dimensions() self.rendered = None # Clear any existing rendered texture if self.fill_color: if self.stroke_color and self.fill_color[3] == 255: col = self.stroke_color else: col = self.fill_color elif self.stroke_color: col = self.stroke_color else: col = (0, 0, 0) self.canvas = Image.new("RGBA", self.dimensions, (col[0], col[1], col[2], 0)) self.surface = Draw(self.canvas) self.surface.setantialias(True) def render(self): """Pre-renders the shape so it can be drawn to the screen using :func:`~klibs.KLGraphics.blit`. Although it is not necessary to pre-render shapes before drawing them to the screen, it will make the initial blit faster and is recommended wherever possible. Once a Drawbject has been rendered, it will not need to be rendered again unless any of its properties (e.g. stroke, fill, rotation) are changed. Returns: :obj:`~numpy.ndarray`: A numpy array of the rendered shape. """ self._init_surface() self.draw() self.rendered = asarray(self.canvas) return self.rendered def _update_dimensions(self): pts = self._draw_points(outline=True) if pts != None: self.__dimensions = canvas_size_from_points(pts, flat=True) else: if self.stroke_alignment == STROKE_OUTER: stroke_w = self.stroke_width * 2 elif self.stroke_alignment == STROKE_CENTER: stroke_w = self.stroke_width else: stroke_w = 0 w, h = [self.object_width, self.object_height] self.__dimensions = [ int(ceil(w + stroke_w)) + 2, int(ceil(h + stroke_w)) + 2 ] @property def dimensions(self): """List[int, int]: The height and width of the internal surface on which the shape is drawn. """ return self.__dimensions @property def surface_width(self): return self.__dimensions[0] @property def surface_height(self): return self.__dimensions[1] @property def stroke(self): """None or :obj:`aggdraw.Pen`: An aggdraw Pen object set to the specified stroke width and color, or None if the Drawbject has no stroke. Raises: ValueError: If an invalid stroke alignment value is passed to the stroke setter. Valid values are 1 (STROKE_INNER), 2 (STROKE_CENTER), or 3 (STROKE_OUTER). For the sake of clarity, it is recommended that you define stroke alignment using the variable names provided in KLConstants (in brackets above). """ return self.__stroke @stroke.setter def stroke(self, style): if not style: self.stroke_width = 0 self.stroke_color = None self.stroke_alignment = STROKE_OUTER return self try: width, color, alignment = style except ValueError: width, color = style alignment = STROKE_OUTER if alignment in [STROKE_INNER, STROKE_CENTER, STROKE_OUTER]: self.stroke_alignment = alignment else: raise ValueError( "Invalid stroke alignment, see KLConstants for accepted values" ) color = list(color) if len(color) == 3: color += [255] self.stroke_color = color self.stroke_width = width self.__stroke = Pen(tuple(color[:3]), width, color[3]) if self.surface: # don't call this when initializing the Drawbject for the first time self._init_surface() return self @property def stroke_offset(self): if self.stroke_alignment == STROKE_OUTER: return self.stroke_width * 0.5 if self.stroke_alignment == STROKE_INNER: return self.stroke_width * -0.5 else: return 0 @property def fill(self): """None or :obj:`aggdraw.Brush`: An aggdraw Brush object set to the specified fill color, or None if the Drawbject has no fill. """ return self.__fill @fill.setter def fill(self, color): if not color: self.fill_color = None return self color = list(color) if len(color) == 3: color += [255] self.fill_color = color self.__fill = Brush(tuple(color[:3]), color[3]) if self.surface: # don't call this when initializing the Drawbject for the first time self._init_surface() return self @abc.abstractmethod def _draw_points(self, outline=False): return None @abc.abstractmethod def draw(self): pts = self._draw_points() dx = self.surface_width / 2.0 dy = self.surface_height / 2.0 pts = translate_points(pts, delta=(dx, dy), flat=True) self.surface.polygon(pts, self.stroke, self.fill) self.surface.flush() return self.canvas @abc.abstractproperty def __name__(self): pass
class ELCustomDisplay(pylink.EyeLinkCustomDisplay, EnvAgent): #TODO: add scaling support for images without ruining performance (OpenGL scale?) def __init__(self): EnvAgent.__init__(self) self.size = (0, 0) self.imagebuffer = [] self.palette = [] self.img = None # PIL.Image self.drawer = None # aggdraw Draw with self.img as context self.title = None self.txtm.add_style("el_setup", "20px", P.default_color, font_label="Hind-Medium") self.dc_target = drift_correct_target() pylink.EyeLinkCustomDisplay.__init__(self) # If using an EyeLink 1000 or newer, these commands need to be sent # to the tracker for everything to work correctly if self.el.getTrackerVersion() >= EYELINK_1000: self.el.sendCommand("enable_search_limits=YES") self.el.sendCommand("track_search_limits=YES") self.el.sendCommand("autothreshold_click=YES") self.el.sendCommand("autothreshold_repeat=YES") self.el.sendCommand("enable_camera_position_detect=YES") # Define dict mapping sdl2 keycodes to pylink keycodes self.pylink_keycodes = dict([(sdl2.SDLK_F1, pylink.F1_KEY), (sdl2.SDLK_F2, pylink.F2_KEY), (sdl2.SDLK_F3, pylink.F3_KEY), (sdl2.SDLK_F4, pylink.F4_KEY), (sdl2.SDLK_F5, pylink.F5_KEY), (sdl2.SDLK_F6, pylink.F6_KEY), (sdl2.SDLK_F7, pylink.F7_KEY), (sdl2.SDLK_F8, pylink.F8_KEY), (sdl2.SDLK_F9, pylink.F9_KEY), (sdl2.SDLK_F10, pylink.F10_KEY), (sdl2.SDLK_PAGEUP, pylink.PAGE_UP), (sdl2.SDLK_PAGEDOWN, pylink.PAGE_DOWN), (sdl2.SDLK_UP, pylink.CURS_UP), (sdl2.SDLK_DOWN, pylink.CURS_DOWN), (sdl2.SDLK_LEFT, pylink.CURS_LEFT), (sdl2.SDLK_RIGHT, pylink.CURS_RIGHT), (sdl2.SDLK_RETURN, pylink.ENTER_KEY), (sdl2.SDLK_ESCAPE, pylink.ESC_KEY), (sdl2.SDLK_BACKSPACE, ord('\b')), (sdl2.SDLK_TAB, ord('\t'))]) # Define dict mapping pylink colour constants to RGB colours self.pylink_colors = [ (0, 0, 0), # 0 = placeholder (transparent) (255, 255, 255), # 1 = pylink.CR_HAIR_COLOR (white) (255, 255, 255), # 2 = pylink.PUPIL_HAIR_COLOR (white) (0, 255, 0), # 3 = pylink.PUPIL_BOX_COLOR (green) (255, 0, 0), # 4 = pylink.SEARCH_LIMIT_BOX_COLOR (red) (255, 0, 0) # 5 = pylink.MOUSE_CURSOR_COLOR (red) ] try: self.__target_beep__ = AudioClip("target_beep.wav") self.__target_beep__done__ = AudioClip("target_beep_done.wav") self.__target_beep__error__ = AudioClip("target_beep_error.wav") except: self.__target_beep__ = None self.__target_beep__done__ = None self.__target_beep__error__ = None def record_abort_hide(self): pass def clear_cal_display(self): fill() flip() fill() def setup_cal_display(self): self.clear_cal_display() def exit_cal_display(self): self.clear_cal_display() def draw_cal_target(self, x, y=None, pump_events=True): fill() if pump_events: pump() if y is None: y = x[1] x = x[0] blit(self.dc_target, 5, (int(x), int(y))) flip() def erase_cal_target(self): self.clear_cal_display() def play_beep(self, clip): try: if clip in [pylink.DC_TARG_BEEP, pylink.CAL_TARG_BEEP]: self.__target_beep__.play() elif clip in [pylink.CAL_ERR_BEEP, pylink.DC_ERR_BEEP]: self.__target_beep__error__.play() else: self.__target_beep__done__.play() except: pass def get_input_key(self): keys = [] for event in pump(True): if event.type == sdl2.SDL_KEYDOWN: keysym = event.key.keysym if not self.el._quitting: # don't process quit requests while already quitting ui_request(keysym) try: key = self.pylink_keycodes[keysym.sym] except KeyError: key = keysym.sym # don't allow escape to control tracker unless calibrating if key == pylink.ESC_KEY and not self.el.in_setup: key = pylink.JUNK_KEY keys.append(pylink.KeyInput(key, keysym.mod)) return keys def get_mouse_state(self): x, y, b = mouse_pos(pump_event_queue=False, return_button_state=True) x = int(x) - (P.screen_c[0] - self.size[0] / 2) y = int(y) - (P.screen_c[1] - self.size[1] / 2) # Restrict mouse coords to within bounds of camera image x = clip(x, minimum=0, maximum=self.size[0]) y = clip(y, minimum=0, maximum=self.size[1]) if b != 1: # Register left clicks only b = 0 return ((x, y), b) def alert_printf(self, message): print("EyeLink Alert: {0}".format(message)) def setup_image_display(self, width, height): '''Sets camera image to the provided size, returns 1 on success.''' self.size = (width, height) self.clear_cal_display() return 1 def exit_image_display(self): self.clear_cal_display() def image_title(self, text): self.title = message(text, "el_setup", blit_txt=False) def set_image_palette(self, r, g, b): ''' Sets the palette to use for the camera image and clears the image buffer. Converts r,g,b (lists containing the RGB palette) to a list of colours ([R,G,B,R,G,B,...]) that can be used by PIL.Image. ''' self.imagebuffer = [] self.palette = list(sum(zip(r, g, b), ())) def draw_image_line(self, width, line, totlines, buff): ''' Reads in the buffer from the EyeLink camera image line by line and writes it into a buffer of size (width * totlines). Once the last line of the image has been read into the buffer, the image buffer is placed in a PIL.Image with the palette set by set_image_palette, converted to RGBA, resized, and then rendered to the middle of the screen. After rendering, the image buffer is cleared. ''' if len(self.imagebuffer) > (width * totlines): self.imagebuffer = [] self.imagebuffer += buff if int(line) == int(totlines): # Render complete camera image and resize to self.size img = Image.new("P", (width, totlines), 0) img.putpalette(self.palette) img.putdata(self.imagebuffer) self.img = img.convert('RGBA').resize(self.size, Image.BILINEAR) # Set up aggdraw to draw crosshair/bounds/etc. on image surface self.drawer = Draw(self.img) self.drawer.setantialias(True) self.draw_cross_hair() self.drawer.flush() # Draw complete image to screen fill() blit(asarray(self.img), 5, P.screen_c) if self.title: loc_x = (P.screen_c[0]) loc_y = (P.screen_c[1] + self.size[1] / 2 + 20) blit(self.title, 8, (loc_x, loc_y)) flip() # Clear image buffer self.imagebuffer = [] def draw_lozenge(self, x, y, width, height, colorindex): lozenge_pen = Pen(self.pylink_colors[colorindex], 3, 255) if width > height: gap = width - height middle = x + width / 2.0 arc_left = (x, y, x + height, y + height) arc_right = (x + gap, y, x + width, y + height) line_top = (floor(middle - gap / 2.0), y, ceil(middle + gap / 2.0), y) line_bottom = (floor(middle - gap / 2.0), y + height, ceil(middle + gap / 2.0), y + height) self.drawer.arc(arc_left, 90, 270, lozenge_pen) self.drawer.arc(arc_right, -90, 90, lozenge_pen) self.drawer.line(line_top, lozenge_pen) self.drawer.line(line_bottom, lozenge_pen) elif height > width: gap = height - width middle = y + height / 2.0 arc_top = (x, y, x + width, y + width) arc_bottom = (x, y + gap, x + width, y + height) line_left = (x, floor(middle - gap / 2.0), x, ceil(middle + gap / 2.0)) line_right = (x + width, floor(middle - gap / 2.0), x + width, ceil(middle + gap / 2.0)) self.drawer.arc(arc_top, 0, 180, lozenge_pen) self.drawer.arc(arc_bottom, 180, 360, lozenge_pen) self.drawer.line(line_left, lozenge_pen) self.drawer.line(line_right, lozenge_pen) else: self.drawer.ellipse((x, y, x + width, y + height), lozenge_pen) def draw_line(self, x1, y1, x2, y2, colorindex): line_pen = Pen(self.pylink_colors[colorindex], 3, 255) self.drawer.line((x1, y1, x2, y2), line_pen)