flatsh = Shader("shaders/uv_flat") defocus = Defocus() #Create textures shapeimg = Texture("textures/straw1.jpg") shapebump = Texture("textures/floor_nm.jpg", True) shapeshine = Texture("textures/pong3.png") #Create shape myshape = MergeShape(camera=persp_cam) #specify perspective view asphere = Sphere(sides=16, slices=16) myshape.radialCopy(asphere, step=72) myshape.position(0.0, 0.0, 5.0) myshape.set_draw_details(shader, [shapeimg, shapebump, shapeshine], 8.0, 0.1) mysprite = Sprite(w=10.0, h=10.0, camera=persp_cam) mysprite.position(0.0, 0.0, 15.0) mysprite.set_draw_details(flatsh, [shapebump]) tick=0 next_time = time.time()+2.0 #load ttf font and set the font colour to 'raspberry' arialFont = Ttffont("fonts/FreeMonoBoldOblique.ttf", "#dd00aa") mystring = String(font=arialFont, string="blurring with distance!", camera=ortho_cam, z=1.0, is_3d=False) # orthographic view mystring.set_shader(flatsh) # Fetch key presses. mykeys = Keyboard()
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 __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 __init__(self, shader="uv_flat", mipmap=False, separation=0.4, interlace=0): """ calls Texture.__init__ but doesn't need to set file name as texture generated from the framebuffer. Keyword Arguments: *shader* to use when drawing sprite, defaults to uv_flat. *mipmap* can be set to True with slight cost to speed, or use fxaa shader *separation* distance between the two camera positions - how wide apart the eye views are. *interlace* if interlace > 0 then the images are not taken with glScissor and must be drawn with a special interlacing shader. """ # load shader if interlace <= 0: # i.e. default side by side behaviour self.shader = Shader.create(shader) else: self.shader = Shader(vshader_source = """ precision mediump float; attribute vec3 vertex; attribute vec2 texcoord; uniform mat4 modelviewmatrix[2]; varying vec2 texcoordout; void main(void) { texcoordout = texcoord; gl_Position = modelviewmatrix[1] * vec4(vertex,1.0); } """, fshader_source = """ precision mediump float; uniform sampler2D tex0; uniform sampler2D tex1; varying vec2 texcoordout; void main(void) {{ vec4 texc0 = texture2D(tex0, texcoordout); vec4 texc1 = texture2D(tex1, texcoordout); vec2 coord = vec2(gl_FragCoord); gl_FragColor = mix(texc0, texc1, step(0.5, fract(coord.x / {:f}))); }} """.format(interlace * 2.0)) self.camera_3d = Camera() # create 3d cam first so it becomes default instance self.forMtrx = np.identity(4, dtype='float32') # initially not rotated self.position = [0.0, 0.0, 0.0] self.camera_2d = Camera(is_3d=False) self.offs = separation / 2.0 self.interlace = interlace self.textures = [] self.sprites = [] self.tex_list = [] for i in range(2): self.textures.append(OffScreenTexture(name="stereo")) ix, iy = self.textures[i].ix, self.textures[i].iy #two sprites full width but moved so that they are centred on the #left and right edges. The offset values then move the uv mapping #so the image is on the right of the left sprite and left of the #right sprite self.sprites.append(Sprite(z=20.0, w=ix, h=iy, flip=True)) if interlace <= 0: self.sprites[i].positionX(-ix/2.0 + i*ix) self.sprites[i].set_offset((i * 0.5 - 0.25, 0.0)) else: self.sprites[i].set_2d_size(w=ix, h=iy) self.textures[i].blend = True self.textures[i].mipmap = mipmap self.tex_list.append(self.textures[i]) opengles.glColorMask(1, 1, 1, 1)
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): """
flatsh = Shader("shaders/uv_flat") defocus = Defocus() #Create textures shapeimg = Texture("textures/straw1.jpg") shapebump = Texture("textures/floor_nm.jpg", True) shapeshine = Texture("textures/pong3.png") #Create shape myshape = MergeShape(camera=persp_cam) #specify perspective view asphere = Sphere(sides=16, slices=16) myshape.radialCopy(asphere, step=72) myshape.position(0.0, 0.0, 5.0) myshape.set_draw_details(shader, [shapeimg, shapebump, shapeshine], 8.0, 0.1) mysprite = Sprite(w=10.0, h=10.0, camera=persp_cam) mysprite.position(0.0, 0.0, 15.0) mysprite.set_draw_details(flatsh, [shapebump]) tick = 0 next_time = time.time() + 2.0 #load ttf font and set the font colour to 'raspberry' arialFont = Ttffont("fonts/FreeMonoBoldOblique.ttf", "#dd00aa") mystring = String(font=arialFont, string="blurring with distance!", camera=ortho_cam, z=1.0, is_3d=False) # orthographic view mystring.set_shader(flatsh)
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)
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): """
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)
cloudTex = [] cloudTex.append(Texture("textures/cloud2.png", True)) cloudTex.append(Texture("textures/cloud3.png", True)) cloudTex.append(Texture("textures/cloud4.png", True)) cloudTex.append(Texture("textures/cloud5.png", True)) cloudTex.append(Texture("textures/cloud6.png", True)) # Setup cloud positions and cloud image refs cz = 0.0 clouds = [] # an array for the clouds for b in range(0, cloudno): size = 0.5 + random.random() / 2.0 cloud = Sprite(w=size * widex, h=size * widey, x=150.0 * (random.random() - 0.5), y=0.0, z=cloud_depth - cz) cloud.set_draw_details(shader, [cloudTex[int(random.random() * 4.99999)]], 0.0, 0.0) clouds.append(cloud) cz = cz + zd CAMERA = Camera.instance() CAMERA.position((0.0, 0.0, 50.0)) CAMERA.was_moved = False # Fetch key presses mykeys = Keyboard() while DISPLAY.loop_running(): # the z position of each cloud is held in clouds[i].unif[2] returned by cloud.z()
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 == '': pass 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_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 _load_disk(self): """
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)
shader = Shader("shaders/uv_flat") ############################# cloudTex = [] cloudTex.append(Texture("textures/cloud2.png",True)) cloudTex.append(Texture("textures/cloud3.png",True)) cloudTex.append(Texture("textures/cloud4.png",True)) cloudTex.append(Texture("textures/cloud5.png",True)) cloudTex.append(Texture("textures/cloud6.png",True)) # Setup cloud positions and cloud image refs cz = 0.0 clouds = [] # an array for the clouds for b in range (0, cloudno): size = 0.5 + random.random()/2.0 cloud = Sprite(w=size * widex, h=size * widey, x=150.0 * (random.random() - 0.5), y=0.0, z=cloud_depth - cz) cloud.set_draw_details(shader, [cloudTex[int(random.random() * 4.99999)]], 0.0, 0.0) clouds.append(cloud) cz = cz + zd CAMERA = Camera.instance() CAMERA.position((0.0, 0.0, 50.0)) CAMERA.was_moved = False # Fetch key presses mykeys = Keyboard() while DISPLAY.loop_running(): # the z position of each cloud is held in clouds[i].unif[2] returned by cloud.z() # it stops the clouds being drawn behind nearer clouds and pixels being # discarded by the shader! that should be blended. # first go through the clouds to find index of furthest away
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, width=None): """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') new_lines = [] maxwid = 0 for l in lines: line_wid = imgfont.getsize(l)[0] if width is not None and line_wid > width: new_line = "" space = "" words = l.split(" ") for word in words: check_line = "{}{}{}".format(new_line, space, word) if imgfont.getsize(check_line)[0] <= width: new_line = check_line else: # wrap before this word TODO cope with lines with no spaces if "-" in word: # TODO make this a function to split first on " " then "-" then on split_word = word.split("-") pre = split_word[0] post = "-".join(split_word[1:]) check_line = "{} {}-".format(new_line, pre) if imgfont.getsize(check_line)[0] <= width: new_line = check_line word = post new_lines.append(new_line) new_line = word space = " " new_lines.append(new_line) else: new_lines.append(l) lines = new_lines for l in lines: line_wid = imgfont.getsize(l)[0] if line_wid > maxwid: maxwid = line_wid maxwid += 2.0 * margin if Display.INSTANCE is not None: max_size = Display.INSTANCE.opengl.max_texture_size.value else: max_size = MAX_SIZE texture_wid = min(int(maxwid / 4) * 4, max_size) nlines = len(lines) 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): """