class FixedString(Texture): """ A texture containing a simple string drawn using ImageDraw. The advantage over a standard String is that it only requires a simple Sprite shape for drawing so the gpu has to only draw two triangles rather than two triangles for each letter.""" def __init__(self, font, string, camera=None, color=(255, 255, 255, 255), shadow=(0, 0, 0, 255), shadow_radius=0, font_size=24, margin=5.0, justify='C', background_color=None, shader=None, f_type='', mipmap=True): """Arguments: *font*: File path/name to a TrueType font file. *string*: String to write. *camera*: Camera object passed on to constructor of sprite *color*: Color in format '#RRGGBB', (255,0,0,255), 'orange' etc (as accepted by PIL.ImageDraw) default (255, 255, 255, 255) i.e. white 100% alpha *shadow*: Color of shadow, default black. *shadow_radius*: Gaussian blur radius applied to shadow layer, default 0 (no shadow) *font_size*: Point size for drawing the letters on the internal Texture. default 24 *margin*: Offsets from the top left corner for the text and space on right and bottom. default 5.0 *justify*: L(eft), C(entre), R(ight) default C *background_color*: filled background in ImageDraw format as above. default None i.e. transparent. *shader*: can be passed to init otherwise needs to be set in set_shader or draw. default None *f_type*: filter type. BUMP will generate a normal map (indented by default, +BUMP or BUMP+ will make it stick out), EMBOSS, CONTOUR, BLUR and SMOOTH do what they sound like they will do. """ super(FixedString, self).__init__(font, mipmap=mipmap) self.font = font try: imgfont = ImageFont.truetype(font, font_size) except IOError: abspath = os.path.abspath(font) msg = "Couldn't find font file '%s'" % font if font != abspath: msg = "%s - absolute path is '%s'" % (msg, abspath) raise Exception(msg) justify = justify.upper() f_type = f_type.upper() ascent, descent = imgfont.getmetrics() height = ascent + descent lines = string.split('\n') nlines = len(lines) maxwid = 0 for l in lines: line_wid = imgfont.getsize(l)[0] if line_wid > maxwid: maxwid = line_wid maxwid += 2.0 * margin texture_wid = WIDTHS[0] for l in WIDTHS: if l > maxwid: texture_wid = l break texture_wid = l texture_hgt = int(nlines * height + 2 * margin) self.im = Image.new("RGBA", (texture_wid, texture_hgt), background_color) self.ix, self.iy = texture_wid, texture_hgt draw = ImageDraw.Draw(self.im) if shadow_radius > 0: from PIL import ImageFilter self._render_text(lines, justify, margin, imgfont, maxwid, height, shadow, draw) self.im = self.im.filter( ImageFilter.GaussianBlur(radius=shadow_radius)) if background_color == None: im_arr = self._force_color(np.array(self.im), shadow) try: # numpy not quite working fully in pypy so have to convert tobytes self.im = Image.fromarray(im_arr) except: h, w, c = im_arr.shape rgb = 'RGB' if c == 3 else 'RGBA' self.im = Image.frombytes(rgb, (w, h), im_arr.tobytes()) draw = ImageDraw.Draw(self.im) self._render_text(lines, justify, margin, imgfont, maxwid, height, color, draw) force_color = background_color is None and shadow_radius == 0 if f_type == '': self.image = np.array(self.im) elif 'BUMP' in f_type: amount = -1.0 if '+' in f_type else 1.0 self.image = self._normal_map(np.array(self.im, dtype=np.uint8), amount) force_color = False else: from PIL import ImageFilter if f_type == 'EMBOSS': self.im = self.im.filter(ImageFilter.EMBOSS) elif f_type == 'CONTOUR': self.im = self.im.filter(ImageFilter.CONTOUR) elif f_type == 'BLUR': self.im = self.im.filter(ImageFilter.BLUR) elif f_type == 'SMOOTH': self.im = self.im.filter(ImageFilter.SMOOTH_MORE) self.image = np.array(self.im) if force_color: self.image = self._force_color(self.image, color) self._tex = ctypes.c_uint() bmedge = nlines * height + 2.0 * margin self.sprite = Sprite(camera=camera, w=maxwid, h=bmedge) buf = self.sprite.buf[0] #convenience alias buf.textures = [self] if shader != None: self.sprite.shader = shader buf.shader = shader buf.unib[6] = float(maxwid / texture_wid) #scale to fit buf.unib[7] = float(bmedge / texture_hgt) def _render_text(self, lines, justify, margin, imgfont, maxwid, height, color, draw): for i, line in enumerate(lines): line_len = imgfont.getsize(line)[0] if justify == "C": xoff = (maxwid - line_len) / 2.0 elif justify == "L": xoff = margin else: xoff = maxwid - line_len draw.text((xoff, margin + i * height), line, font=imgfont, fill=color) def set_shader(self, shader): ''' wrapper for Shape.set_shader''' self.sprite.set_shader(shader) def draw(self, shader=None, txtrs=None, ntl=None, shny=None, camera=None, mlist=[]): '''wrapper for Shape.draw()''' self.sprite.draw(shader, txtrs, ntl, shny, camera, mlist) def _force_color(self, im_array, color): """ Overwrite color of all pixels as PIL renders text incorrectly when drawing on transparent backgrounds http://nedbatchelder.com/blog/200801/truly_transparent_text_with_pil.html """ if isinstance(color, str): from PIL import ImageColor color = ImageColor.getrgb(color) im_array[:, :, :3] = color[:3] return im_array def _load_disk(self): """
class FixedOutlineString(Texture): """ A texture containing a simple string drawn using ImageDraw. The advantage over a standard String is that it only requires a simple Sprite shape for drawing so the gpu has to only draw two triangles rather than two triangles for each letter.""" def __init__(self, font, string, camera=None, color=(255, 255, 255, 255), outline=(0, 0, 0, 255), outline_size=0, font_size=24, margin=5.0, justify='C', background_color=None, shader=None, f_type='', mipmap=True): """Arguments: *font*: File path/name to a TrueType font file. *string*: String to write. *camera*: Camera object passed on to constructor of sprite *color*: Color in format #RRGGBB, (255,0,0,255) etc (as accepted by PIL.ImageDraw) default (255, 255, 255, 255) i.e. white 100% alpha *font_size*: Point size for drawing the letters on the internal Texture. default 24 *margin*: Offsets from the top left corner for the text and space on right and bottom. default 5.0 *justify*: L(eft), C(entre), R(ight) default C *background_color*: filled background in ImageDraw format as above. default None i.e. transparent. *shader*: can be passed to init otherwise needs to be set in set_shader or draw. default None *f_type*: filter type. BUMP will generate a normal map (indented), EMBOSS, CONTOUR, BLUR and SMOOTH do what they sound like they will do. """ super(FixedOutlineString, self).__init__(font, mipmap=mipmap) self.font = font try: imgfont = ImageFont.truetype(font, font_size) except IOError: abspath = os.path.abspath(font) msg = "Couldn't find font file '%s'" % font if font != abspath: msg = "%s - absolute path is '%s'" % (msg, abspath) raise Exception(msg) justify = justify.upper() f_type = f_type.upper() ascent, descent = imgfont.getmetrics() height = ascent + descent lines = string.split('\n') nlines = len(lines) maxwid = 0 for l in lines: line_wid = imgfont.getsize(l)[0] if line_wid > maxwid: maxwid = line_wid maxwid += 2.0 * margin texture_wid = WIDTHS[0] for l in WIDTHS: if l > maxwid: texture_wid = l break texture_wid = l texture_hgt = int(nlines * height + 2 * margin) self.im = Image.new("RGBA", (texture_wid, texture_hgt), background_color) self.alpha = True self.ix, self.iy = texture_wid, texture_hgt draw = ImageDraw.Draw(self.im) for i, line in enumerate(lines): line_len = imgfont.getsize(line)[0] if justify == "C": xoff = (maxwid - line_len) / 2.0 elif justify == "L": xoff = margin else: xoff = maxwid - line_len # draw the outline by drawing the displaced text repeatedly if outline_size > 0: for xi in range(-outline_size, outline_size + 1): for yi in range(-outline_size, outline_size + 1): if xi != 0 and yi != 0: draw.text((xoff + xi, margin + i * height + yi), line, font=imgfont, fill=outline) draw.text((xoff, margin + i * height), line, font=imgfont, fill=color) if f_type == '': pass elif f_type == 'BUMP': self.im = self.make_bump_map() else: from PIL import ImageFilter if f_type == 'EMBOSS': self.im = self.im.filter(ImageFilter.EMBOSS) elif f_type == 'CONTOUR': self.im = self.im.filter(ImageFilter.CONTOUR) elif f_type == 'BLUR': self.im = self.im.filter(ImageFilter.BLUR) elif f_type == 'SMOOTH': self.im = self.im.filter(ImageFilter.SMOOTH_MORE) #self.image = self.im.convert('RGBA').tostring('raw', 'RGBA') self.im = self.im.convert('RGBA') self.image = np.array(self.im) self._tex = ctypes.c_int() bmedge = nlines * height + 2.0 * margin self.sprite = Sprite(camera=camera, w=maxwid, h=bmedge) buf = self.sprite.buf[0] #convenience alias buf.textures = [self] if shader != None: self.sprite.shader = shader buf.shader = shader buf.unib[6] = float(maxwid / texture_wid) #scale to fit buf.unib[7] = float(bmedge / texture_hgt) def set_shader(self, shader): ''' wrapper for Shape.set_shader''' self.sprite.set_shader(shader) def draw(self, shader=None, txtrs=None, ntl=None, shny=None, camera=None, mlist=[]): '''wrapper for Shape.draw()''' self.sprite.draw(shader, txtrs, ntl, shny, camera, mlist) def make_bump_map(self): """ essentially just blurs the image then allocates R or G values according to the rate of change of grayscale in x and y directions """ import numpy as np a = np.array(self.im, dtype=np.uint8) a = np.average(a, axis=2, weights=[1.0, 1.0, 1.0, 0.0]).astype(int) b = [[0.01, 0.025, 0.05, 0.025, 0.01], [0.025, 0.05, 0.065, 0.05, 0.025], [0.05, 0.065, 0.1, 0.065, 0.05], [0.025, 0.05, 0.065, 0.05, 0.025], [0.01, 0.025, 0.05, 0.025, 0.01]] c = np.zeros(a.shape, dtype=np.uint8) steps = [i - 2 for i in range(5)] for i, istep in enumerate(steps): for j, jstep in enumerate(steps): c += (np.roll(np.roll(a, istep, 0), jstep, 1) * b[i][j]).astype(np.uint8) cx = np.roll(c, 1, 0) cy = np.roll(c, 1, 1) d = np.zeros((a.shape[0], a.shape[1], 4), dtype=np.uint8) d[:, :, 0] = ((c - cy) + 127).astype(int) d[:, :, 1] = ((c - cx) + 127).astype(int) d[:, :, 2] = (np.clip( (65025 - (127 - d[:, :, 0])**2 - (127 - d[:, :, 1])**2)**0.5, 0, 255)).astype(int) d[:, :, 3] = 255 return Image.fromarray(d) def _load_disk(self): """
class FixedOutlineString(Texture): """ A texture containing a simple string drawn using ImageDraw. The advantage over a standard String is that it only requires a simple Sprite shape for drawing so the gpu has to only draw two triangles rather than two triangles for each letter.""" def __init__(self, font, string, camera=None, color=(255,255,255,255), outline=(0,0,0,255), outline_size=0, font_size=24, margin=5.0, justify='C', background_color=None, shader=None, f_type='', mipmap=True): """Arguments: *font*: File path/name to a TrueType font file. *string*: String to write. *camera*: Camera object passed on to constructor of sprite *color*: Color in format #RRGGBB, (255,0,0,255) etc (as accepted by PIL.ImageDraw) default (255, 255, 255, 255) i.e. white 100% alpha *font_size*: Point size for drawing the letters on the internal Texture. default 24 *margin*: Offsets from the top left corner for the text and space on right and bottom. default 5.0 *justify*: L(eft), C(entre), R(ight) default C *background_color*: filled background in ImageDraw format as above. default None i.e. transparent. *shader*: can be passed to init otherwise needs to be set in set_shader or draw. default None *f_type*: filter type. BUMP will generate a normal map (indented), EMBOSS, CONTOUR, BLUR and SMOOTH do what they sound like they will do. """ super(FixedOutlineString, self).__init__(font, mipmap=mipmap) self.font = font try: imgfont = ImageFont.truetype(font, font_size) except IOError: abspath = os.path.abspath(font) msg = "Couldn't find font file '%s'" % font if font != abspath: msg = "%s - absolute path is '%s'" % (msg, abspath) raise Exception(msg) justify = justify.upper() f_type = f_type.upper() ascent, descent = imgfont.getmetrics() height = ascent + descent lines = string.split('\n') nlines = len(lines) maxwid = 0 for l in lines: line_wid = imgfont.getsize(l)[0] if line_wid > maxwid: maxwid = line_wid maxwid += 2.0 * margin texture_wid = WIDTHS[0] for l in WIDTHS: if l > maxwid: texture_wid = l break texture_wid = l texture_hgt = int(nlines * height + 2 * margin) self.im = Image.new("RGBA", (texture_wid, texture_hgt), background_color) self.alpha = True self.ix, self.iy = texture_wid, texture_hgt draw = ImageDraw.Draw(self.im) for i, line in enumerate(lines): line_len = imgfont.getsize(line)[0] if justify == "C": xoff = (maxwid - line_len) / 2.0 elif justify == "L": xoff = margin else: xoff = maxwid - line_len # draw the outline by drawing the displaced text repeatedly if outline_size > 0: for xi in range(-outline_size, outline_size + 1): for yi in range(-outline_size, outline_size + 1): if xi != 0 and yi != 0: draw.text((xoff + xi, margin + i * height + yi), line, font=imgfont, fill=outline) draw.text((xoff, margin + i * height), line, font=imgfont, fill=color) if f_type == '': pass elif f_type == 'BUMP': self.im = self.make_bump_map() else: from PIL import ImageFilter if f_type == 'EMBOSS': self.im = self.im.filter(ImageFilter.EMBOSS) elif f_type == 'CONTOUR': self.im = self.im.filter(ImageFilter.CONTOUR) elif f_type == 'BLUR': self.im = self.im.filter(ImageFilter.BLUR) elif f_type == 'SMOOTH': self.im = self.im.filter(ImageFilter.SMOOTH_MORE) #self.image = self.im.convert('RGBA').tostring('raw', 'RGBA') self.im = self.im.convert('RGBA') self.image = np.array(self.im) self._tex = ctypes.c_int() bmedge = nlines * height + 2.0 * margin self.sprite = Sprite(camera=camera, w=maxwid, h=bmedge) buf = self.sprite.buf[0] #convenience alias buf.textures = [self] if shader != None: self.sprite.shader = shader buf.shader = shader buf.unib[6] = float(maxwid / texture_wid) #scale to fit buf.unib[7] = float(bmedge / texture_hgt) def set_shader(self, shader): ''' wrapper for Shape.set_shader''' self.sprite.set_shader(shader) def draw(self, shader=None, txtrs=None, ntl=None, shny=None, camera=None, mlist=[]): '''wrapper for Shape.draw()''' self.sprite.draw(shader, txtrs, ntl, shny, camera, mlist) def make_bump_map(self): """ essentially just blurs the image then allocates R or G values according to the rate of change of grayscale in x and y directions """ import numpy as np a = np.array(self.im, dtype=np.uint8) a = np.average(a, axis=2, weights=[1.0, 1.0, 1.0, 0.0]).astype(int) b = [[0.01, 0.025, 0.05, 0.025, 0.01], [0.025,0.05, 0.065,0.05, 0.025], [0.05, 0.065, 0.1, 0.065, 0.05], [0.025,0.05, 0.065,0.05, 0.025], [0.01, 0.025, 0.05, 0.025, 0.01]] c = np.zeros(a.shape, dtype=np.uint8) steps = [i - 2 for i in range(5)] for i, istep in enumerate(steps): for j, jstep in enumerate(steps): c += (np.roll(np.roll(a, istep, 0), jstep, 1) * b[i][j]).astype(np.uint8) cx = np.roll(c, 1, 0) cy = np.roll(c, 1, 1) d = np.zeros((a.shape[0], a.shape[1], 4), dtype=np.uint8) d[:, :, 0] = ((c - cy) + 127).astype(int) d[:, :, 1] = ((c - cx) + 127).astype(int) d[:, :, 2] = (np.clip((65025 - (127 - d[:, :, 0]) ** 2 - (127 - d[:, :, 1]) ** 2) ** 0.5, 0, 255)).astype(int) d[:, :, 3] = 255 return Image.fromarray(d) def _load_disk(self): """
class FixedString(Texture): """ A texture containing a simple string drawn using ImageDraw. The advantage over a standard String is that it only requires a simple Sprite shape for drawing so the gpu has to only draw two triangles rather than two triangles for each letter.""" def __init__( self, font, string, camera=None, color=(255, 255, 255, 255), font_size=24, margin=5.0, justify="C", background_color=None, shader=None, f_type="", mipmap=True, ): """Arguments: *font*: File path/name to a TrueType font file. *string*: String to write. *camera*: Camera object passed on to constructor of sprite *color*: Color in format '#RRGGBB', (255,0,0,255), 'orange' etc (as accepted by PIL.ImageDraw) default (255, 255, 255, 255) i.e. white 100% alpha *font_size*: Point size for drawing the letters on the internal Texture. default 24 *margin*: Offsets from the top left corner for the text and space on right and bottom. default 5.0 *justify*: L(eft), C(entre), R(ight) default C *background_color*: filled background in ImageDraw format as above. default None i.e. transparent. *shader*: can be passed to init otherwise needs to be set in set_shader or draw. default None *f_type*: filter type. BUMP will generate a normal map (indented by default, +BUMP or BUMP+ will make it stick out), EMBOSS, CONTOUR, BLUR and SMOOTH do what they sound like they will do. """ super(FixedString, self).__init__(font, mipmap=mipmap) self.font = font try: imgfont = ImageFont.truetype(font, font_size) except IOError: abspath = os.path.abspath(font) msg = "Couldn't find font file '%s'" % font if font != abspath: msg = "%s - absolute path is '%s'" % (msg, abspath) raise Exception(msg) justify = justify.upper() f_type = f_type.upper() ascent, descent = imgfont.getmetrics() height = ascent + descent lines = string.split("\n") nlines = len(lines) maxwid = 0 for l in lines: line_wid = imgfont.getsize(l)[0] if line_wid > maxwid: maxwid = line_wid maxwid += 2.0 * margin texture_wid = WIDTHS[0] for l in WIDTHS: if l > maxwid: texture_wid = l break texture_wid = l texture_hgt = int(nlines * height + 2 * margin) self.im = Image.new("RGBA", (texture_wid, texture_hgt), background_color) self.ix, self.iy = texture_wid, texture_hgt draw = ImageDraw.Draw(self.im) for i, line in enumerate(lines): line_len = imgfont.getsize(line)[0] if justify == "C": xoff = (maxwid - line_len) / 2.0 elif justify == "L": xoff = margin else: xoff = maxwid - line_len draw.text((xoff, margin + i * height), line, font=imgfont, fill=color) if f_type == "": self.image = np.array(self.im) elif "BUMP" in f_type: amount = -1.0 if "+" in f_type else 1.0 self.image = self._normal_map(np.array(self.im, dtype=np.uint8), amount) else: from PIL import ImageFilter if f_type == "EMBOSS": self.im = self.im.filter(ImageFilter.EMBOSS) elif f_type == "CONTOUR": self.im = self.im.filter(ImageFilter.CONTOUR) elif f_type == "BLUR": self.im = self.im.filter(ImageFilter.BLUR) elif f_type == "SMOOTH": self.im = self.im.filter(ImageFilter.SMOOTH_MORE) self.image = np.array(self.im) if background_color is None: if isinstance(color, str): from PIL import ImageColor color = ImageColor.getrgb(color) self.image[:, :, :3] = color[:3] self._tex = ctypes.c_uint() bmedge = nlines * height + 2.0 * margin self.sprite = Sprite(camera=camera, w=maxwid, h=bmedge) buf = self.sprite.buf[0] # convenience alias buf.textures = [self] if shader != None: self.sprite.shader = shader buf.shader = shader buf.unib[6] = float(maxwid / texture_wid) # scale to fit buf.unib[7] = float(bmedge / texture_hgt) def set_shader(self, shader): """ wrapper for Shape.set_shader""" self.sprite.set_shader(shader) def draw(self, shader=None, txtrs=None, ntl=None, shny=None, camera=None, mlist=[]): """wrapper for Shape.draw()""" self.sprite.draw(shader, txtrs, ntl, shny, camera, mlist) def _load_disk(self): """
class FixedString(Texture): """ A texture containing a simple string drawn using ImageDraw. The advantage over a standard String is that it only requires a simple Sprite shape for drawing so the gpu has to only draw two triangles rather than two triangles for each letter.""" def __init__(self, font, string, camera=None, color=(255,255,255,255), shadow=(0,0,0,255), shadow_radius=0, font_size=24, margin=5.0, justify='C', background_color=None, shader=None, f_type='', mipmap=True): """Arguments: *font*: File path/name to a TrueType font file. *string*: String to write. *camera*: Camera object passed on to constructor of sprite *color*: Color in format '#RRGGBB', (255,0,0,255), 'orange' etc (as accepted by PIL.ImageDraw) default (255, 255, 255, 255) i.e. white 100% alpha *shadow*: Color of shadow, default black. *shadow_radius*: Gaussian blur radius applied to shadow layer, default 0 (no shadow) *font_size*: Point size for drawing the letters on the internal Texture. default 24 *margin*: Offsets from the top left corner for the text and space on right and bottom. default 5.0 *justify*: L(eft), C(entre), R(ight) default C *background_color*: filled background in ImageDraw format as above. default None i.e. transparent. *shader*: can be passed to init otherwise needs to be set in set_shader or draw. default None *f_type*: filter type. BUMP will generate a normal map (indented by default, +BUMP or BUMP+ will make it stick out), EMBOSS, CONTOUR, BLUR and SMOOTH do what they sound like they will do. """ super(FixedString, self).__init__(font, mipmap=mipmap) self.font = font try: imgfont = ImageFont.truetype(font, font_size) except IOError: abspath = os.path.abspath(font) msg = "Couldn't find font file '%s'" % font if font != abspath: msg = "%s - absolute path is '%s'" % (msg, abspath) raise Exception(msg) justify = justify.upper() f_type = f_type.upper() ascent, descent = imgfont.getmetrics() height = ascent + descent lines = string.split('\n') nlines = len(lines) maxwid = 0 for l in lines: line_wid = imgfont.getsize(l)[0] if line_wid > maxwid: maxwid = line_wid maxwid += 2.0 * margin texture_wid = WIDTHS[0] for l in WIDTHS: if l > maxwid: texture_wid = l break texture_wid = l texture_hgt = int(nlines * height + 2 * margin) self.im = Image.new("RGBA", (texture_wid, texture_hgt), background_color) self.ix, self.iy = texture_wid, texture_hgt draw = ImageDraw.Draw(self.im) if shadow_radius > 0: from PIL import ImageFilter self._render_text(lines, justify, margin, imgfont, maxwid, height, shadow, draw) self.im = self.im.filter(ImageFilter.GaussianBlur(radius=shadow_radius)) if background_color == None: im_arr = self._force_color(np.array(self.im), shadow) try: # numpy not quite working fully in pypy so have to convert tobytes self.im = Image.fromarray(im_arr) except: h, w, c = im_arr.shape rgb = 'RGB' if c == 3 else 'RGBA' self.im = Image.frombytes(rgb, (w, h), im_arr.tobytes()) draw = ImageDraw.Draw(self.im) self._render_text(lines, justify, margin, imgfont, maxwid, height, color, draw) force_color = background_color is None and shadow_radius == 0 if f_type == '': self.image = np.array(self.im) elif 'BUMP' in f_type: amount = -1.0 if '+' in f_type else 1.0 self.image = self._normal_map(np.array(self.im, dtype=np.uint8), amount) force_color = False else: from PIL import ImageFilter if f_type == 'EMBOSS': self.im = self.im.filter(ImageFilter.EMBOSS) elif f_type == 'CONTOUR': self.im = self.im.filter(ImageFilter.CONTOUR) elif f_type == 'BLUR': self.im = self.im.filter(ImageFilter.BLUR) elif f_type == 'SMOOTH': self.im = self.im.filter(ImageFilter.SMOOTH_MORE) self.image = np.array(self.im) if force_color: self.image = self._force_color(self.image, color) self._tex = ctypes.c_uint() bmedge = nlines * height + 2.0 * margin self.sprite = Sprite(camera=camera, w=maxwid, h=bmedge) buf = self.sprite.buf[0] #convenience alias buf.textures = [self] if shader != None: self.sprite.shader = shader buf.shader = shader buf.unib[6] = float(maxwid / texture_wid) #scale to fit buf.unib[7] = float(bmedge / texture_hgt) def _render_text(self, lines, justify, margin, imgfont, maxwid, height, color, draw): for i, line in enumerate(lines): line_len = imgfont.getsize(line)[0] if justify == "C": xoff = (maxwid - line_len) / 2.0 elif justify == "L": xoff = margin else: xoff = maxwid - line_len draw.text((xoff, margin + i * height), line, font=imgfont, fill=color) def set_shader(self, shader): ''' wrapper for Shape.set_shader''' self.sprite.set_shader(shader) def draw(self, shader=None, txtrs=None, ntl=None, shny=None, camera=None): '''wrapper for Shape.draw()''' self.sprite.draw(shader, txtrs, ntl, shny, camera) def _force_color(self, im_array, color): """ Overwrite color of all pixels as PIL renders text incorrectly when drawing on transparent backgrounds http://nedbatchelder.com/blog/200801/truly_transparent_text_with_pil.html """ if isinstance(color, str): from PIL import ImageColor color = ImageColor.getrgb(color) im_array[:,:,:3] = color[:3] return im_array def _load_disk(self): """