diff options
Diffstat (limited to 'third_party/freetype-py/examples')
22 files changed, 2478 insertions, 0 deletions
diff --git a/third_party/freetype-py/examples/Vera.ttf b/third_party/freetype-py/examples/Vera.ttf Binary files differnew file mode 100644 index 0000000..58cd6b5 --- /dev/null +++ b/third_party/freetype-py/examples/Vera.ttf diff --git a/third_party/freetype-py/examples/VeraMono.ttf b/third_party/freetype-py/examples/VeraMono.ttf Binary files differnew file mode 100644 index 0000000..139f0b4 --- /dev/null +++ b/third_party/freetype-py/examples/VeraMono.ttf diff --git a/third_party/freetype-py/examples/agg-trick.py b/third_party/freetype-py/examples/agg-trick.py new file mode 100644 index 0000000..c0bc134 --- /dev/null +++ b/third_party/freetype-py/examples/agg-trick.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# +# FreeType high-level python API - Copyright 2011 Nicolas P. Rougier +# Distributed under the terms of the new BSD license. +# +# ----------------------------------------------------------------------------- +from freetype import * +import numpy as np +import Image + + +def render(filename = "Vera.ttf", hinting = (False,False), gamma = 1.5, lcd=False): + text = "A Quick Brown Fox Jumps Over The Lazy Dog 0123456789" + + W,H,D = 680, 280, 1 + Z = np.zeros( (H,W), dtype=np.ubyte ) + face = Face(filename) + pen = Vector(5*64, (H-10)*64) + + flags = FT_LOAD_RENDER + if hinting[1]: flags |= FT_LOAD_FORCE_AUTOHINT + else: flags |= FT_LOAD_NO_HINTING + if hinting[0]: hres, hscale = 72, 1.0 + else: hres, hscale = 72*10, 0.1 + if lcd: + flags |= FT_LOAD_TARGET_LCD + Z = np.zeros( (H,W,3), dtype=np.ubyte ) + set_lcd_filter( FT_LCD_FILTER_DEFAULT ) + + + for size in range(9,23): + face.set_char_size( size * 64, 0, hres, 72 ) + matrix = Matrix( int((hscale) * 0x10000L), int((0.0) * 0x10000L), + int((0.0) * 0x10000L), int((1.0) * 0x10000L) ) + previous = 0 + pen.x = 5*64 + for current in text: + face.set_transform( matrix, pen ) + face.load_char( current, flags) + kerning = face.get_kerning( previous, current, FT_KERNING_UNSCALED ) + pen.x += kerning.x + glyph = face.glyph + bitmap = glyph.bitmap + x, y = glyph.bitmap_left, glyph.bitmap_top + w, h, p = bitmap.width, bitmap.rows, bitmap.pitch + buff = np.array(bitmap.buffer, dtype=np.ubyte).reshape((h,p)) + if lcd: + Z[H-y:H-y+h,x:x+w/3].flat |= buff[:,:w].flatten() + else: + Z[H-y:H-y+h,x:x+w].flat |= buff[:,:w].flatten() + pen.x += glyph.advance.x + previous = current + pen.y -= (size+4)*64 + + # Gamma correction + Z = (Z/255.0)**(gamma) + Z = ((1-Z)*255).astype(np.ubyte) + if lcd: + I = Image.fromarray(Z, mode='RGB') + else: + I = Image.fromarray(Z, mode='L') + + name = filename.split('.')[0] + filename = '%s-gamma(%.1f)-hinting(%d,%d)-lcd(%d).png' % (name,gamma,hinting[0],hinting[1],lcd) + I.save(filename) + + + +if __name__ == '__main__': + render('Vera.ttf', (0,1), 1.25, True) + diff --git a/third_party/freetype-py/examples/ascii.py b/third_party/freetype-py/examples/ascii.py new file mode 100644 index 0000000..ce718d3 --- /dev/null +++ b/third_party/freetype-py/examples/ascii.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# +# FreeType high-level python API - Copyright 2011 Nicolas P. Rougier +# Distributed under the terms of the new BSD license. +# +# ----------------------------------------------------------------------------- +import numpy +import os, sys +from freetype import * + + +class ColorMap: + ''' A colormap is used to map scalar values to colors. It is build by + adding couples of (value,color) where value must be between 0 and 1. + The 'scale' method allows to specify the range of the colormap and + the 'color' method then returns a color for any value. ''' + + def __init__ (self, colors): + self.colors = colors + self.min = 0 + self.max = 1 + + def scale (self, min, max): + self.min, self.max = min,max + + def color (self, value): + ''' Return the color corresponding to value. ''' + if not len(self.colors): + return (0,0,0) + elif len(self.colors) == 1: + return self.colors[0][1] + elif value < self.min: + return self.colors[0][1] + elif value > self.max: + return self.colors[-1][1] + value = (value-self.min)/(self.max-self.min) + sup_color = self.colors[0] + inf_color = self.colors[-1] + for i in range (len(self.colors)-1): + if value < self.colors[i+1][0]: + inf_color = self.colors[i] + sup_color = self.colors[i+1] + break + r = (value-inf_color[0]) / (sup_color[0] - inf_color[0]) + if r < 0: r = -r + color = [sup_color[1][0]*r + inf_color[1][0]*(1-r), + sup_color[1][1]*r + inf_color[1][1]*(1-r), + sup_color[1][2]*r + inf_color[1][2]*(1-r)] + return color + +# Some colormaps +CM_IceAndFire = ColorMap([(0.00, (0.0, 0.0, 1.0)), + (0.25, (0.0, 0.5, 1.0)), + (0.50, (1.0, 1.0, 1.0)), + (0.75, (1.0, 1.0, 0.0)), + (1.00, (1.0, 0.0, 0.0))]) +CM_Ice = ColorMap([(0.00, (0.0, 0.0, 1.0)), + (0.50, (0.5, 0.5, 1.0)), + (1.00, (1.0, 1.0, 1.0))]) +CM_Fire = ColorMap([(0.00, (1.0, 1.0, 1.0)), + (0.50, (1.0, 1.0, 0.0)), + (1.00, (1.0, 0.0, 0.0))]) +CM_Hot = ColorMap([(0.00, (0.0, 0.0, 0.0)), + (0.33, (1.0, 0.0, 0.0)), + (0.66, (1.0, 1.0, 0.0)), + (1.00, (1.0, 1.0, 1.0))]) +CM_Grey = ColorMap([(0.00, (0.0, 0.0, 0.0)), + (1.00, (1.0, 1.0, 1.0))]) + + + +def imshow (Z, vmin=None, vmax=None, cmap=CM_Hot, show_cmap=False): + ''' Show a 2D numpy array using terminal colors ''' + + if len(Z.shape) != 2: + print "Cannot display non 2D array" + return + + vmin = vmin or Z.min() + vmax = vmax or Z.max() + cmap.scale (vmin, vmax) + + # Build initialization string that setup terminal colors + init = '' + for i in range(240): + v = cmap.min + (i/240.0)* (cmap.max - cmap.min) + r,g,b = cmap.color (v) + init += "\x1b]4;%d;rgb:%02x/%02x/%02x\x1b\\" % (16+i, int(r*255),int(g*255),int(b*255)) + + # Build array data string + data = '' + for i in range(Z.shape[0]): + for j in range(Z.shape[1]): + c = 16 + int( ((Z[Z.shape[0]-i-1,j]-cmap.min) / (cmap.max-cmap.min))*239) + if (c < 16): + c=16 + elif (c > 255): + c=255 + data += "\x1b[48;5;%dm " % c + u = cmap.max - (i/float(Z.shape[0]-1)) * ((cmap.max-cmap.min)) + if show_cmap: + data += "\x1b[0m " + data += "\x1b[48;5;%dm " % (16 + (1-i/float(Z.shape[0]))*239) + data += "\x1b[0m %+.2f" % u + data += "\x1b[0m\n" + print init+data[:-1]+'\x1b[0m' + + +if __name__ == '__main__': + face = Face('./Vera.ttf') + face.set_char_size( 32*64 ) + face.load_glyph(face.get_char_index('S')) + slot = face.glyph + bitmap = slot.bitmap + data, rows, width = bitmap.buffer, bitmap.rows, bitmap.width + Z = numpy.array(data,dtype=float).reshape(rows,width) + Z = Z[::-1,:] + imshow (Z, cmap=CM_Grey) + diff --git a/third_party/freetype-py/examples/example_1.py b/third_party/freetype-py/examples/example_1.py new file mode 100644 index 0000000..a21512b --- /dev/null +++ b/third_party/freetype-py/examples/example_1.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# +# FreeType high-level python API - Copyright 2011-2014 Nicolas P. Rougier +# Distributed under the terms of the new BSD license. +# +# ----------------------------------------------------------------------------- +# +# Direct translation of example 1 from the freetype tutorial: +# http://www.freetype.org/freetype2/docs/tutorial/step1.html +# +import math +from freetype import * + + +if __name__ == '__main__': + from PIL import Image + from freetype import * + + WIDTH, HEIGHT = 640, 480 + image = Image.new('L', (WIDTH,HEIGHT)) + def draw_bitmap( bitmap, x, y): + x_max = x + bitmap.width + y_max = y + bitmap.rows + p = 0 + for p,i in enumerate(range(x,x_max)): + for q,j in enumerate(range(y,y_max)): + if i < 0 or j < 0 or i >= WIDTH or j >= HEIGHT: + continue; + pixel = image.getpixel((i,j)) + pixel |= int(bitmap.buffer[q * bitmap.width + p]); + image.putpixel((i,j), pixel) + + library = FT_Library() + matrix = FT_Matrix() + face = FT_Face() + pen = FT_Vector() + filename= './Vera.ttf' + text = 'Hello World !' + num_chars = len(text) + angle = ( 25.0 / 360 ) * 3.14159 * 2 + + # initialize library, error handling omitted + error = FT_Init_FreeType( byref(library) ) + + # create face object, error handling omitted + error = FT_New_Face( library, filename, 0, byref(face) ) + + # set character size: 50pt at 100dpi, error handling omitted + error = FT_Set_Char_Size( face, 50 * 64, 0, 100, 0 ) + slot = face.contents.glyph + + # set up matrix + matrix.xx = (int)( math.cos( angle ) * 0x10000L ) + matrix.xy = (int)(-math.sin( angle ) * 0x10000L ) + matrix.yx = (int)( math.sin( angle ) * 0x10000L ) + matrix.yy = (int)( math.cos( angle ) * 0x10000L ) + + # the pen position in 26.6 cartesian space coordinates; */ + # start at (300,200) relative to the upper left corner */ + pen.x = 200 * 64; + pen.y = ( HEIGHT - 300 ) * 64 + + for n in range(num_chars): + # set transformation + FT_Set_Transform( face, byref(matrix), byref(pen) ) + + # load glyph image into the slot (erase previous one) + charcode = ord(text[n]) + index = FT_Get_Char_Index( face, charcode ) + FT_Load_Glyph( face, index, FT_LOAD_RENDER ) + + # now, draw to our target surface (convert position) + draw_bitmap( slot.contents.bitmap, + slot.contents.bitmap_left, + HEIGHT - slot.contents.bitmap_top ) + + # increment pen position + pen.x += slot.contents.advance.x + pen.y += slot.contents.advance.y + + FT_Done_Face(face) + FT_Done_FreeType(library) + + import matplotlib.pyplot as plt + plt.imshow(image, origin='lower', + interpolation='nearest', cmap=plt.cm.gray) + plt.show() diff --git a/third_party/freetype-py/examples/font-info.py b/third_party/freetype-py/examples/font-info.py new file mode 100644 index 0000000..015ee94 --- /dev/null +++ b/third_party/freetype-py/examples/font-info.py @@ -0,0 +1,42 @@ +# ----------------------------------------------------------------------------- +# +# FreeType high-level python API - Copyright 2011 Nicolas P. Rougier +# Distributed under the terms of the new BSD license. +# +# ----------------------------------------------------------------------------- +from freetype import * + +if __name__ == '__main__': + import sys + + if len(sys.argv) < 2: + print("Usage: %s font_filename" % sys.argv[0]) + sys.exit() + + face = Face(sys.argv[1]) + + print 'Family name: ', face.family_name + print 'Style name: ', face.style_name + print 'Charmaps: ', [charmap.encoding_name for charmap in face.charmaps] + print + print 'Face number: ', face.num_faces + print 'Glyph number: ', face.num_glyphs + print 'Available sizes: ', face.available_sizes + print + print 'units per em: ', face.units_per_EM + print 'ascender: ', face.ascender + print 'descender: ', face.descender + print 'height: ', face.height + print + print 'max_advance_width: ', face.max_advance_width + print 'max_advance_height: ', face.max_advance_height + print + print 'underline_position: ', face.underline_position + print 'underline_thickness:', face.underline_thickness + print + print 'Has horizontal: ', face.has_horizontal + print 'Has vertical: ', face.has_vertical + print 'Has kerning: ', face.has_kerning + print 'Is fixed width: ', face.is_fixed_width + print 'Is scalable: ', face.is_scalable + print diff --git a/third_party/freetype-py/examples/ftdump.py b/third_party/freetype-py/examples/ftdump.py new file mode 100644 index 0000000..13662aa --- /dev/null +++ b/third_party/freetype-py/examples/ftdump.py @@ -0,0 +1,295 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# FreeType high-level python API - Copyright 2011 Nicolas P. Rougier +# Distributed under the terms of the new BSD license. +# ----------------------------------------------------------------------------- +from __future__ import print_function +from __future__ import division +import sys +from freetype import * + +verbose = 0 +debug = 0 +name_tables = 0 + +def usage( execname ): + print( ) + print( "ftdump: simple font dumper -- part of the FreeType project" ) + print( "----------------------------------------------------------" ) + print( "Usage: %s [options] fontname", execname ) + print( ) + print( " -n print SFNT name tables" ) + print( " -v be verbose" ) + print( ) + sys.exit() + + +def Print_Name( face ): + print( "font name entries" ); + print( " family: %s" % face.family_name ) + print( " style: %s" % face.style_name ) + ps_name = face.postscript_name or "UNAVAILABLE" + print( " postscript: %s" % ps_name ) + + +def Print_Type( face ): + + print( "font type entries" ) + + # module = &face->driver->root; + # printf( " FreeType driver: %s\n", module->clazz->module_name ); + + # Is it better to dump all sfnt tag names? + print( " sfnt wrapped: ",end="") + if face.is_sfnt: print( "yes") + else: print( "no") + + # is scalable ? + print( " type: ", end="") + if face.is_scalable: + print( "scalable, ", end="") + if face.has_multiple_masters: + print( "multiple_masters, ", end="") + if face.has_fixed_sizes: + print( "fixed size",end="") + print() + + # Direction + print( " direction: ", end="" ) + if face.has_horizontal: + print( "horizontal, ", end="") + if face.has_vertical: + print( "vertical", end="") + print( ) + + # Fixed width + print( " fixed width: ", end="") + if face.is_fixed_width: print( "yes") + else: print( "no") + + # Glyph names + print( " glyph names: ", end="") + if face.has_glyph_names: print( "yes") + else: print( "no") + + if face.is_scalable: + print( " EM size: %d" % face.units_per_EM ) + print( " global BBox: (%ld,%ld):(%ld,%ld)" % + (face.bbox.xMin, face.bbox.yMin, + face.bbox.xMax, face.bbox.yMax )) + print( " ascent: %d" % face.ascender ) + print( " descent: %d" % face.descender ) + print( " text height: %d" % face.height ) + + +def get_platform_id( platform_id ): + if platform_id == TT_PLATFORM_APPLE_UNICODE: + return "Apple (Unicode)" + elif platform_id == TT_PLATFORM_MACINTOSH: + return "Macintosh" + elif platform_id == TT_PLATFORM_ISO: + return "ISO (deprecated)" + elif platform_id == TT_PLATFORM_MICROSOFT: + return "Microsoft" + elif platform_id == TT_PLATFORM_CUSTOM: + return "custom" + elif platform_id == TT_PLATFORM_ADOBE: + return "Adobe" + else: + return "UNKNOWN" + +def get_name_id( name_id ): + if name_id == TT_NAME_ID_COPYRIGHT: + return "copyright" + elif name_id == TT_NAME_ID_FONT_FAMILY: + return "font family" + elif name_id == TT_NAME_ID_FONT_SUBFAMILY: + return "font subfamily" + elif name_id == TT_NAME_ID_UNIQUE_ID: + return "unique ID" + elif name_id == TT_NAME_ID_FULL_NAME: + return "full name" + elif name_id == TT_NAME_ID_VERSION_STRING: + return "version string" + elif name_id == TT_NAME_ID_PS_NAME: + return "PostScript name" + elif name_id == TT_NAME_ID_TRADEMARK: + return "trademark" + + # the following values are from the OpenType spec + elif name_id == TT_NAME_ID_MANUFACTURER: + return "manufacturer" + elif name_id == TT_NAME_ID_DESIGNER: + return "designer" + elif name_id == TT_NAME_ID_DESCRIPTION: + return "description" + elif name_id == TT_NAME_ID_VENDOR_URL: + return "vendor URL" + elif name_id == TT_NAME_ID_DESIGNER_URL: + return "designer URL" + elif name_id == TT_NAME_ID_LICENSE: + return "license" + elif name_id == TT_NAME_ID_LICENSE_URL: + return "license URL" + # number 15 is reserved + elif name_id == TT_NAME_ID_PREFERRED_FAMILY: + return "preferred family" + elif name_id == TT_NAME_ID_PREFERRED_SUBFAMILY: + return "preferred subfamily" + elif name_id == TT_NAME_ID_MAC_FULL_NAME: + return "Mac full name" + + # The following code is new as of 2000-01-21 + elif name_id == TT_NAME_ID_SAMPLE_TEXT: + return "sample text" + + # This is new in OpenType 1.3 + elif name_id == TT_NAME_ID_CID_FINDFONT_NAME: + return "CID 'findfont' name" + else: + return "UNKNOWN"; + + +def Print_Sfnt_Names( face ): + print( "font string entries" ); + + for i in range(face.sfnt_name_count): + + name = face.get_sfnt_name(i) + print( " %-15s [%s]" % ( get_name_id( name.name_id ), + get_platform_id( name.platform_id )),end="") + + if name.platform_id == TT_PLATFORM_APPLE_UNICODE: + if name.encoding_id in [TT_APPLE_ID_DEFAULT, + TT_APPLE_ID_UNICODE_1_1, + TT_APPLE_ID_ISO_10646, + TT_APPLE_ID_UNICODE_2_0]: + print(name.string.decode('utf-16be', 'ignore')) + else: + print( "{unsupported encoding %d}" % name.encoding_id ) + + elif name.platform_id == TT_PLATFORM_MACINTOSH: + if name.language_id != TT_MAC_LANGID_ENGLISH: + print( " (language=%d)" % name.language_id ) + print ( " : " ) + if name.encoding_id == TT_MAC_ID_ROMAN: + # FIXME: convert from MacRoman to ASCII/ISO8895-1/whatever + # (MacRoman is mostly like ISO8895-1 but there are differences) + print(name.string) + else: + print( "{unsupported encoding %d}" % name.encoding_id ) + + elif name.platform_id == TT_PLATFORM_ISO: + if name.encoding_id in [ TT_ISO_ID_7BIT_ASCII, + TT_ISO_ID_8859_1]: + print(name.string) + print ( " : " ) + if name.encoding_id == TT_ISO_ID_10646: + print(name.string.decode('utf-16be', 'ignore')) + else: + print( "{unsupported encoding %d}" % name.encoding_id ) + + elif name.platform_id == TT_PLATFORM_MICROSOFT: + if name.language_id != TT_MS_LANGID_ENGLISH_UNITED_STATES: + print( " (language=0x%04x)" % name.language_id ); + print( " : " ) + if name.encoding_id in [TT_MS_ID_SYMBOL_CS, + TT_MS_ID_UNICODE_CS]: + print(name.string.decode('utf-16be', 'ignore')) + else: + print( "{unsupported encoding %d}" % name.encoding_id ) + else: + print( "{unsupported platform}" ) + + print( ) + + +def Print_Fixed( face ): + + # num_fixed_size + print( "fixed size\n" ) + + # available size + for i,bsize in enumerate(face.available_sizes): + print( " %3d: height %d, width %d\n", + i, bsize.height, bsize.width ) + print( " size %.3f, x_ppem %.3f, y_ppem %.3f\n", + bsize.size / 64.0, + bsize.x_ppem / 64.0, bsize.y_ppem / 64.0 ) + + +def Print_Charmaps( face ): + global verbose + active = -1 + if face.charmap: + active = face.charmap.index + + # CharMaps + print( "charmaps" ) + for i,charmap in enumerate(face.charmaps): + print( " %d: platform %d, encoding %d, language %d" % + (i, charmap.platform_id, charmap.encoding_id, + int(charmap.cmap_language_id)), end="" ) + if i == active: + print( " (active)", end="" ) + print ( ) + if verbose: + face.set_charmap( charmap ) + charcode, gindex = face.get_first_char() + while ( gindex ): + print( " 0x%04lx => %d" % (charcode, gindex) ) + charcode, gindex = face.get_next_char( charcode, gindex ) + + + +# ----------------------------------------------------------------------------- +if __name__ == '__main__': + import getopt + execname = sys.argv[0] + + if len(sys.argv) < 2: + usage( execname ) + + try: + opts, args = getopt.getopt(sys.argv[1:], ':nv') + except getopt.GetoptError, err: + usage( execname ) + + verbose = False + name_tables = False + + for o, a in opts: + if o == "-v": verbose = True + elif o == "-n": name_tables = True + else: usage( execname ) + + + face = Face(args[0]) + num_faces = face.num_faces + + if num_faces > 1: + print( "There are %d faces in this file." % num_faces) + else: + print( "There is 1 face in this file.") + + for i in range(num_faces): + face = Face(args[0], i) + + print( "\n----- Face number: %d -----\n" % i ) + Print_Name( face ) + print( "" ) + Print_Type( face ) + print( " glyph count: %d" % face.num_glyphs ) + + if name_tables and face.is_sfnt: + print( ) + Print_Sfnt_Names( face ) + + if face.num_fixed_sizes: + print( ) + Print_Fixed( face ) + + if face.num_charmaps: + print( ) + Print_Charmaps( face ) diff --git a/third_party/freetype-py/examples/glyph-alpha.py b/third_party/freetype-py/examples/glyph-alpha.py new file mode 100644 index 0000000..2477450 --- /dev/null +++ b/third_party/freetype-py/examples/glyph-alpha.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# +# FreeType high-level python API - Copyright 2011 Nicolas P. Rougier +# Distributed under the terms of the new BSD license. +# +# ----------------------------------------------------------------------------- +''' +Glyph bitmap monochrome rendering +''' +from freetype import * + +if __name__ == '__main__': + import numpy + import matplotlib.pyplot as plt + + face = Face('./Vera.ttf') + face.set_char_size( 48*64 ) + face.load_char('S', FT_LOAD_RENDER ) + bitmap = face.glyph.bitmap + width = face.glyph.bitmap.width + rows = face.glyph.bitmap.rows + pitch = face.glyph.bitmap.pitch + + data = [] + for i in range(rows): + data.extend(bitmap.buffer[i*pitch:i*pitch+width]) + Z = numpy.array(data,dtype=numpy.ubyte).reshape(rows, width) + plt.imshow(Z, interpolation='nearest', cmap=plt.cm.gray, origin='lower') + plt.show() diff --git a/third_party/freetype-py/examples/glyph-color.py b/third_party/freetype-py/examples/glyph-color.py new file mode 100644 index 0000000..7f3b28e --- /dev/null +++ b/third_party/freetype-py/examples/glyph-color.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# +# FreeType high-level python API - Copyright 2011 Nicolas P. Rougier +# Distributed under the terms of the new BSD license. +# +# ----------------------------------------------------------------------------- +''' +Glyph colored rendering (with outline) +''' +from freetype import * + +if __name__ == '__main__': + import numpy as np + import matplotlib.pyplot as plt + + face = Face('./Vera.ttf') + face.set_char_size( 96*64 ) + RGBA = [('R',float), ('G',float), ('B',float), ('A',float)] + + # Outline + flags = FT_LOAD_DEFAULT | FT_LOAD_NO_BITMAP + face.load_char('S', flags ) + slot = face.glyph + glyph = slot.get_glyph() + stroker = Stroker( ) + stroker.set(64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0 ) + glyph.stroke( stroker ) + blyph = glyph.to_bitmap(FT_RENDER_MODE_NORMAL, Vector(0,0)) + bitmap = blyph.bitmap + width, rows, pitch = bitmap.width, bitmap.rows, bitmap.pitch + top, left = blyph.top, blyph.left + data = [] + for i in range(rows): + data.extend(bitmap.buffer[i*pitch:i*pitch+width]) + Z = np.array(data).reshape(rows, width)/255.0 + O = np.zeros((rows,width), dtype=RGBA) + O['A'] = Z + O['R'] = 1 + O['G'] = 0 + O['B'] = 0 + + # Plain + flags = FT_LOAD_RENDER + face.load_char('S', flags) + F = np.zeros((rows,width), dtype=RGBA) + Z = np.zeros((rows, width)) + bitmap = face.glyph.bitmap + width, rows, pitch = bitmap.width, bitmap.rows, bitmap.pitch + top, left = face.glyph.bitmap_top, face.glyph.bitmap_left + dy = blyph.top - face.glyph.bitmap_top + dx = face.glyph.bitmap_left - blyph.left + data = [] + for i in range(rows): + data.extend(bitmap.buffer[i*pitch:i*pitch+width]) + Z[dx:dx+rows,dy:dy+width] = np.array(data).reshape(rows, width)/255. + F['R'] = 1 + F['G'] = 1 + F['B'] = 0 + F['A'] = Z + + # Combine outline and plain + R1,G1,B1,A1 = O['R'],O['G'],O['B'],O['A'] + R2,G2,B2,A2 = F['R'],F['G'],F['B'],F['A'] + Z = np.zeros(O.shape, dtype=RGBA) + Z['R'] = (A1 * R1 + A2 * (1 - A1) * R2) + Z['G'] = (A1 * G1 + A2 * (1 - A1) * G2) + Z['B'] = (A1 * B1 + A2 * (1 - A1) * B2) + Z['A'] = (A1 + A2 * (1 - A1)) + + + # Draw + plt.figure(figsize=(12,5)) + + plt.subplot(1,3,1) + plt.title('Plain') + plt.xticks([]), plt.yticks([]) + I = F.view(dtype=float).reshape(O.shape[0],O.shape[1],4) + plt.imshow(I, interpolation='nearest', origin='lower') + + plt.subplot(1,3,2) + plt.title('Outline') + plt.xticks([]), plt.yticks([]) + I = O.view(dtype=float).reshape(O.shape[0],O.shape[1],4) + plt.imshow(I, interpolation='nearest', origin='lower') + + plt.subplot(1,3,3) + plt.title('Outline + Plain') + plt.xticks([]), plt.yticks([]) + I = Z.view(dtype=float).reshape(O.shape[0],O.shape[1],4) + plt.imshow(I, interpolation='nearest', origin='lower') + + plt.show() diff --git a/third_party/freetype-py/examples/glyph-lcd.py b/third_party/freetype-py/examples/glyph-lcd.py new file mode 100644 index 0000000..5992c12 --- /dev/null +++ b/third_party/freetype-py/examples/glyph-lcd.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# +# FreeType high-level python API - Copyright 2011 Nicolas P. Rougier +# Distributed under the terms of the new BSD license. +# +# ----------------------------------------------------------------------------- +''' +Glyph bitmap monochrome rendring +''' +from freetype import * + +if __name__ == '__main__': + import numpy + import matplotlib.pyplot as plt + + face = Face('./Vera.ttf') + face.set_char_size( 48*64 ) + face.load_char('S', FT_LOAD_RENDER | + FT_LOAD_TARGET_LCD ) + bitmap = face.glyph.bitmap + width = face.glyph.bitmap.width + rows = face.glyph.bitmap.rows + pitch = face.glyph.bitmap.pitch + + data = [] + for i in range(rows): + data.extend(bitmap.buffer[i*pitch:i*pitch+width]) + Z = numpy.array(data,dtype=numpy.ubyte).reshape(rows, width/3, 3) + plt.imshow(Z, interpolation='nearest', origin='lower') + plt.show() diff --git a/third_party/freetype-py/examples/glyph-metrics.py b/third_party/freetype-py/examples/glyph-metrics.py new file mode 100644 index 0000000..f0bb21a --- /dev/null +++ b/third_party/freetype-py/examples/glyph-metrics.py @@ -0,0 +1,233 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# +# FreeType high-level python API - Copyright 2011 Nicolas P. Rougier +# Distributed under the terms of the new BSD license. +# +# ----------------------------------------------------------------------------- +''' +Show glyph metrics in horizontal and vertical layout +''' +from freetype import * + +def arrow( x,y, dx, dy, **kwargs): + kwargs['shape'] = 'full' + kwargs['head_width'] = 30 + kwargs['head_length'] = 40 + kwargs['length_includes_head'] =True + kwargs['facecolor'] = 'k' + kwargs['edgecolor'] ='k' + kwargs['linewidth'] =.5 + plt.arrow(x,y,dx,dy,**kwargs) + +def double_arrow(x, y, dx, dy, **kwargs): + cx,cy = x+dx/2., y+dy/2. + dx /= 2.0 + dy /= 2.0 + arrow(cx,cy,+dx,+dy,**kwargs) + arrow(cx,cy,-dx,-dy,**kwargs) + +def line(x, y, dx, dy, **kwargs): + kwargs['color'] = 'k' + kwargs['linewidth'] =.5 + plt.plot([x,x+dx],[y,y+dy],**kwargs) + +def point(x, y, r, **kwargs): + kwargs['color'] = 'k' + plt.scatter([x],[y],r,**kwargs) + +def text( x,y,text, **kwargs): + kwargs['fontsize'] = 18 + plt.text(x, y, text, **kwargs) + + + +if __name__ == '__main__': + import numpy as np + import matplotlib.pyplot as plt + from matplotlib.path import Path + import matplotlib.patches as patches + + + face = Face('./Vera.ttf') + face.set_char_size( 32*64 ) + face.load_char('g') + slot = face.glyph + bitmap = slot.bitmap + width = slot.bitmap.width + rows = slot.bitmap.rows + pitch = slot.bitmap.pitch + outline= slot.outline + + start, end = 0, 0 + VERTS, CODES = [], [] + # Iterate over each contour + for i in range(len(outline.contours)): + end = outline.contours[i] + points = outline.points[start:end+1] + points.append(points[0]) + tags = outline.tags[start:end+1] + tags.append(tags[0]) + segments = [ [points[0],], ] + for j in range(1, len(points) ): + segments[-1].append(points[j]) + if tags[j] & (1 << 0) and j < (len(points)-1): + segments.append( [points[j],] ) + verts = [points[0], ] + codes = [Path.MOVETO,] + for segment in segments: + if len(segment) == 2: + verts.extend(segment[1:]) + codes.extend([Path.LINETO]) + elif len(segment) == 3: + verts.extend(segment[1:]) + codes.extend([Path.CURVE3, Path.CURVE3]) + else: + verts.append(segment[1]) + codes.append(Path.CURVE3) + for i in range(1,len(segment)-2): + A,B = segment[i], segment[i+1] + C = ((A[0]+B[0])/2.0, (A[1]+B[1])/2.0) + verts.extend([ C, B ]) + codes.extend([ Path.CURVE3, Path.CURVE3]) + verts.append(segment[-1]) + codes.append(Path.CURVE3) + VERTS.extend(verts) + CODES.extend(codes) + start = end+1 + VERTS = np.array(VERTS) + x,y = VERTS[:,0], VERTS[:,1] + VERTS[:,0], VERTS[:,1] = x, y + + + path = Path(VERTS, CODES) + xmin, xmax = x.min(), x.max() + ymin, ymax = y.min(), y.max() + width,height = xmax-xmin, ymax-ymin + dw, dh = 0.2*width, 0.1*height + bearing = xmin - slot.metrics.horiBearingX, ymin - slot.metrics.horiBearingY + advance = slot.advance + origin = bearing + + + figure = plt.figure(figsize=(16,10), frameon=False, facecolor="white") + + axes = plt.subplot(121, frameon=False, aspect=1) + glyph = patches.PathPatch(path, fill = True, facecolor='k', lw=0) + plt.xlim(xmin - .25*width, xmax + .75*width) + plt.ylim(ymin - .5*height, xmax + .75*height) + plt.xticks([]), plt.yticks([]) + axes.add_patch(glyph) + + # Y axis + arrow(origin[0], ymin-dh, 0, height+3*dh) + + # X axis + arrow(origin[0]-dw, 0, width+3*dw, 0) + + # origin + point(0,0,50) + text( -20, -20, "$origin$", va='top', ha='right') + + # Bounding box + bbox = patches.Rectangle( (xmin,ymin), width, height, fill = False, lw=.5) + axes.add_patch(bbox) + + # Width + line(xmin, ymax, 0, 3*dh, linestyle="dotted") + text( xmin, ymax+3.25*dh, "$x_{min}$", va='bottom', ha='center') + line(xmax, ymax, 0, 3*dh, linestyle="dotted") + text( xmax, ymax+3.25*dh, "$x_{max}$", va='bottom', ha='center') + double_arrow(xmin, ymax+2.5*dh, width, 0) + text(xmin+width/2., ymax+1.75*dh, "$width$", va='bottom', ha='center') + + # Height + line(xmax, ymin, 3*dw, 0, linestyle="dotted") + text(xmax+3.25*dw, ymin, "$y_{min}$", va='baseline', ha='left') + line(xmax, ymax, 3*dw, 0, linestyle="dotted") + text(xmax+3.25*dw, ymax, "$y_{max}$", va='baseline', ha='left') + double_arrow(xmax+2.5*dw, ymin, 0, height) + text(xmax+2.75*dw, ymin+height/2., "$height$", va='center', ha='left') + + # Advance + point(advance.x,0,50) + line(advance.x, 0, 0, ymin-dh, linestyle="dotted") + arrow(0, ymin-.5*dh, advance.x, 0) + text(advance.x/2., ymin-1.25*dh, "$advance$", va='bottom', ha='center') + + # Bearing Y + arrow(xmax+.25*dw, 0, 0, ymax) + text(xmax+.5*dw, ymax/2, "$Y_{bearing}$", va='center', ha='left') + + # Bearing X + arrow(0, ymax/2., xmin, 0) + text(-10, ymax/2, "$X_{bearing}$", va='baseline', ha='right') + + + # ------------------------------------------------------------------------- + + axes = plt.subplot(122, frameon=False, aspect=1) + glyph = patches.PathPatch(path, fill = True, facecolor='k', lw=0) + axes.add_patch(glyph) + + plt.xlim(xmin - .25*width, xmax + .75*width) + plt.ylim(ymin - .5*height, xmax + .75*height) + plt.xticks([]), plt.yticks([]) + + + advance = slot.metrics.vertAdvance + x_bearing = slot.metrics.vertBearingX + y_bearing = slot.metrics.vertBearingY + + # Y axis + arrow(xmin-x_bearing, ymax+y_bearing+2*dh, 0, -advance-3*dh) + + # X axis + arrow(xmin-2*dw, ymax+y_bearing, width+4*dw, 0) + + # origin + point( xmin-x_bearing, ymax+y_bearing, 50) + text( xmin-x_bearing-30, ymax+y_bearing+10, "$origin$", va='bottom', ha='right') + + # Bounding box + bbox = patches.Rectangle( (xmin,ymin), width, height, fill = False, lw=.5) + axes.add_patch(bbox) + + + # # Advance + point(xmin-x_bearing, ymax+y_bearing-advance, 50) + line(xmin-x_bearing, ymax+y_bearing-advance, xmax-dw, 0, linestyle="dotted") + arrow(xmax+dw, ymax+y_bearing, 0, -advance) + text(xmax+1.25*dw, ymax+y_bearing-advance/2., "$advance$", va='baseline', ha='left') + + + # Width + line(xmin, ymin, 0, -4*dh, linestyle="dotted") + text( xmin, ymin-4.25*dh, "$x_{min}$", va='top', ha='center') + line(xmax, ymin, 0, -4*dh, linestyle="dotted") + text( xmax, ymin-4.25*dh, "$x_{max}$", va='top', ha='center') + double_arrow(xmin, ymin-3.5*dh, width, 0) + text(xmin+width/2., ymin-3.75*dh, "$width$", va='top', ha='center') + + # Height + line(xmin, ymin, -3*dw, 0, linestyle="dotted") + text(xmin-1.5*dw, ymin, "$y_{min}$", va='baseline', ha='right') + line(xmin, ymax, -3*dw, 0, linestyle="dotted") + text(xmin-1.5*dw, ymax, "$y_{max}$", va='baseline', ha='right') + double_arrow(xmin-.5*dw, ymin, 0, height) + text(xmin-.75*dw, ymin+height/2., "$height$", va='center', ha='right') + + + #point(xmin-x_bearing, ymax+y_bearing, 50) + # Bearing Y + arrow(xmax-.5*dw, ymax+y_bearing, 0, -y_bearing) + text(xmax-.5*dw, ymax+y_bearing+.25*dh, "$Y_{bearing}$", va='bottom', ha='center') + + # # Bearing X + line(xmin, ymax, 0, 3*dh, linestyle="dotted") + arrow(xmin-x_bearing, ymax+y_bearing+dh, x_bearing, 0) + text(xmin-.25*dw, ymax+y_bearing+dh, "$X_{bearing}$", va='baseline', ha='right') + + plt.savefig('glyph-metrics.pdf') + plt.show() diff --git a/third_party/freetype-py/examples/glyph-monochrome.py b/third_party/freetype-py/examples/glyph-monochrome.py new file mode 100644 index 0000000..3714ceb --- /dev/null +++ b/third_party/freetype-py/examples/glyph-monochrome.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# +# FreeType high-level python API - Copyright 2011 Nicolas P. Rougier +# Distributed under the terms of the new BSD license. +# +# ----------------------------------------------------------------------------- +''' +Glyph bitmap monochrome rendring +''' +from freetype import * + +def bits(x): + data = [] + for i in range(8): + data.insert(0, int((x & 1) == 1)) + x = x >> 1 + return data + +if __name__ == '__main__': + import numpy + import matplotlib.pyplot as plt + + face = Face('./Vera.ttf') + face.set_char_size( 48*64 ) + face.load_char('S', FT_LOAD_RENDER | + FT_LOAD_TARGET_MONO ) + + bitmap = face.glyph.bitmap + width = face.glyph.bitmap.width + rows = face.glyph.bitmap.rows + pitch = face.glyph.bitmap.pitch + + data = [] + for i in range(bitmap.rows): + row = [] + for j in range(bitmap.pitch): + row.extend(bits(bitmap.buffer[i*bitmap.pitch+j])) + data.extend(row[:bitmap.width]) + Z = numpy.array(data).reshape(bitmap.rows, bitmap.width) + plt.imshow(Z, interpolation='nearest', cmap=plt.cm.gray, origin='lower') + plt.show() diff --git a/third_party/freetype-py/examples/glyph-outline.py b/third_party/freetype-py/examples/glyph-outline.py new file mode 100644 index 0000000..6588e21 --- /dev/null +++ b/third_party/freetype-py/examples/glyph-outline.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# +# FreeType high-level python API - Copyright 2011 Nicolas P. Rougier +# Distributed under the terms of the new BSD license. +# +# ----------------------------------------------------------------------------- +''' +Glyph outline rendering +''' +from freetype import * + +if __name__ == '__main__': + import numpy + import matplotlib.pyplot as plt + + face = Face('./Vera.ttf') + face.set_char_size( 4*48*64 ) + flags = FT_LOAD_DEFAULT | FT_LOAD_NO_BITMAP + face.load_char('S', flags ) + slot = face.glyph + glyph = slot.get_glyph() + stroker = Stroker( ) + stroker.set(64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0 ) + glyph.stroke( stroker ) + blyph = glyph.to_bitmap(FT_RENDER_MODE_NORMAL, Vector(0,0)) + bitmap = blyph.bitmap + width, rows, pitch = bitmap.width, bitmap.rows, bitmap.pitch + top, left = blyph.top, blyph.left + data = [] + for i in range(rows): + data.extend(bitmap.buffer[i*pitch:i*pitch+width]) + Z = numpy.array(data,dtype=numpy.ubyte).reshape(rows, width) + plt.figure(figsize=(6,8)) + plt.imshow(Z, interpolation='nearest', cmap=plt.cm.gray_r, origin='lower') + plt.show() diff --git a/third_party/freetype-py/examples/glyph-vector-2.py b/third_party/freetype-py/examples/glyph-vector-2.py new file mode 100644 index 0000000..fc2c7da --- /dev/null +++ b/third_party/freetype-py/examples/glyph-vector-2.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# +# FreeType high-level python API - Copyright 2011 Nicolas P. Rougier +# Distributed under the terms of the new BSD license. +# +# ----------------------------------------------------------------------------- +''' +Show how to access glyph outline description. +''' +from freetype import * + +if __name__ == '__main__': + import numpy + import matplotlib.pyplot as plt + from matplotlib.path import Path + import matplotlib.patches as patches + + face = Face('./Vera.ttf') + face.set_char_size( 32*64 ) + face.load_char('g') + slot = face.glyph + + bitmap = face.glyph.bitmap + width = face.glyph.bitmap.width + rows = face.glyph.bitmap.rows + pitch = face.glyph.bitmap.pitch + + data = [] + for i in range(rows): + data.extend(bitmap.buffer[i*pitch:i*pitch+width]) + Z = numpy.array(data,dtype=numpy.ubyte).reshape(rows, width) + + outline = slot.outline + points = numpy.array(outline.points, dtype=[('x',float), ('y',float)]) + x, y = points['x'], points['y'] + + figure = plt.figure(figsize=(8,10)) + axis = figure.add_subplot(111) + #axis.scatter(points['x'], points['y'], alpha=.25) + start, end = 0, 0 + + VERTS, CODES = [], [] + # Iterate over each contour + for i in range(len(outline.contours)): + end = outline.contours[i] + points = outline.points[start:end+1] + points.append(points[0]) + tags = outline.tags[start:end+1] + tags.append(tags[0]) + + segments = [ [points[0],], ] + for j in range(1, len(points) ): + segments[-1].append(points[j]) + if tags[j] & (1 << 0) and j < (len(points)-1): + segments.append( [points[j],] ) + verts = [points[0], ] + codes = [Path.MOVETO,] + for segment in segments: + if len(segment) == 2: + verts.extend(segment[1:]) + codes.extend([Path.LINETO]) + elif len(segment) == 3: + verts.extend(segment[1:]) + codes.extend([Path.CURVE3, Path.CURVE3]) + else: + verts.append(segment[1]) + codes.append(Path.CURVE3) + for i in range(1,len(segment)-2): + A,B = segment[i], segment[i+1] + C = ((A[0]+B[0])/2.0, (A[1]+B[1])/2.0) + verts.extend([ C, B ]) + codes.extend([ Path.CURVE3, Path.CURVE3]) + verts.append(segment[-1]) + codes.append(Path.CURVE3) + VERTS.extend(verts) + CODES.extend(codes) + start = end+1 + + + # Draw glyph + path = Path(VERTS, CODES) + glyph = patches.PathPatch(path, fill = True, facecolor=(0.8,0.5,0.8), alpha=.25, lw=0) + glyph_outline = patches.PathPatch(path, fill = False, edgecolor='black', lw=3) + + plt.imshow(Z, extent=[x.min(), x.max(),y.min(), y.max()], origin='lower', + interpolation='nearest', cmap = plt.cm.gray_r, vmin=0, vmax=400) + plt.xticks(numpy.linspace(x.min(), x.max(), Z.shape[1]+1), ()) + plt.yticks(numpy.linspace(y.min(), y.max(), Z.shape[0]+1), ()) + plt.grid(color='k', linewidth=1, linestyle='-') + axis.add_patch(glyph) + axis.add_patch(glyph_outline) + axis.set_xlim(x.min(), x.max()) + axis.set_ylim(y.min(), y.max()) + + plt.savefig('test.svg') + plt.show() diff --git a/third_party/freetype-py/examples/glyph-vector.py b/third_party/freetype-py/examples/glyph-vector.py new file mode 100644 index 0000000..8cc34bf --- /dev/null +++ b/third_party/freetype-py/examples/glyph-vector.py @@ -0,0 +1,91 @@ +# ----------------------------------------------------------------------------- +# +# FreeType high-level python API - Copyright 2011 Nicolas P. Rougier +# Distributed under the terms of the new BSD license. +# +# ----------------------------------------------------------------------------- +''' +Show how to access glyph outline description. +''' +from freetype import * + +if __name__ == '__main__': + import numpy + import matplotlib.pyplot as plt + from matplotlib.path import Path + import matplotlib.patches as patches + + face = Face('./Vera.ttf') + face.set_char_size( 48*64 ) + face.load_char('S') + slot = face.glyph + + outline = slot.outline + points = numpy.array(outline.points, dtype=[('x',float), ('y',float)]) + x, y = points['x'], points['y'] + + figure = plt.figure(figsize=(8,10)) + axis = figure.add_subplot(111) + #axis.scatter(points['x'], points['y'], alpha=.25) + start, end = 0, 0 + + VERTS, CODES = [], [] + # Iterate over each contour + for i in range(len(outline.contours)): + end = outline.contours[i] + points = outline.points[start:end+1] + points.append(points[0]) + tags = outline.tags[start:end+1] + tags.append(tags[0]) + + segments = [ [points[0],], ] + for j in range(1, len(points) ): + segments[-1].append(points[j]) + if tags[j] & (1 << 0) and j < (len(points)-1): + segments.append( [points[j],] ) + verts = [points[0], ] + codes = [Path.MOVETO,] + for segment in segments: + if len(segment) == 2: + verts.extend(segment[1:]) + codes.extend([Path.LINETO]) + elif len(segment) == 3: + verts.extend(segment[1:]) + codes.extend([Path.CURVE3, Path.CURVE3]) + else: + verts.append(segment[1]) + codes.append(Path.CURVE3) + for i in range(1,len(segment)-2): + A,B = segment[i], segment[i+1] + C = ((A[0]+B[0])/2.0, (A[1]+B[1])/2.0) + verts.extend([ C, B ]) + codes.extend([ Path.CURVE3, Path.CURVE3]) + verts.append(segment[-1]) + codes.append(Path.CURVE3) + VERTS.extend(verts) + CODES.extend(codes) + start = end+1 + + + # Draw glyph lines + path = Path(VERTS, CODES) + glyph = patches.PathPatch(path, facecolor='.75', lw=1) + + # Draw "control" lines + for i, code in enumerate(CODES): + if code == Path.CURVE3: + CODES[i] = Path.LINETO + path = Path(VERTS, CODES) + patch = patches.PathPatch(path, ec='.5', fill=False, ls='dashed', lw=1 ) + + axis.add_patch(patch) + axis.add_patch(glyph) + + axis.set_xlim(x.min()-100, x.max()+100) + plt.xticks([]) + axis.set_ylim(y.min()-100, y.max()+100) + plt.yticks([]) + plt.show() + + + diff --git a/third_party/freetype-py/examples/hello-world.py b/third_party/freetype-py/examples/hello-world.py new file mode 100644 index 0000000..72126ae --- /dev/null +++ b/third_party/freetype-py/examples/hello-world.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# +# FreeType high-level python API - Copyright 2011 Nicolas P. Rougier +# Distributed under the terms of the new BSD license. +# +# ----------------------------------------------------------------------------- +from freetype import * + +if __name__ == '__main__': + import numpy + import matplotlib.pyplot as plt + + face = Face('./Vera.ttf') + text = 'Hello World !' + face.set_char_size( 48*64 ) + slot = face.glyph + + # First pass to compute bbox + width, height, baseline = 0, 0, 0 + previous = 0 + for i,c in enumerate(text): + face.load_char(c) + bitmap = slot.bitmap + height = max(height, + bitmap.rows + max(0,-(slot.bitmap_top-bitmap.rows))) + baseline = max(baseline, max(0,-(slot.bitmap_top-bitmap.rows))) + kerning = face.get_kerning(previous, c) + width += (slot.advance.x >> 6) + (kerning.x >> 6) + previous = c + + Z = numpy.zeros((height,width), dtype=numpy.ubyte) + + # Second pass for actual rendering + x, y = 0, 0 + previous = 0 + for c in text: + face.load_char(c) + bitmap = slot.bitmap + top = slot.bitmap_top + left = slot.bitmap_left + w,h = bitmap.width, bitmap.rows + y = height-baseline-top + kerning = face.get_kerning(previous, c) + x += (kerning.x >> 6) + Z[y:y+h,x:x+w] += numpy.array(bitmap.buffer).reshape(h,w) + x += (slot.advance.x >> 6) + previous = c + + plt.figure(figsize=(10, 10*Z.shape[0]/float(Z.shape[1]))) + plt.imshow(Z, interpolation='nearest', origin='upper', cmap=plt.cm.gray) + plt.xticks([]), plt.yticks([]) + plt.show() diff --git a/third_party/freetype-py/examples/opengl.py b/third_party/freetype-py/examples/opengl.py new file mode 100644 index 0000000..19309be --- /dev/null +++ b/third_party/freetype-py/examples/opengl.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# +# FreeType high-level python API - Copyright 2011 Nicolas P. Rougier +# Distributed under the terms of the new BSD license. +# +# ----------------------------------------------------------------------------- +import numpy +from freetype import * +import OpenGL.GL as gl +import OpenGL.GLUT as glut + +base, texid = 0, 0 +text = '''Hello World !''' + +def on_display( ): + global texid + gl.glClearColor(1,1,1,1) + gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) + gl.glBindTexture( gl.GL_TEXTURE_2D, texid ) + gl.glColor(0,0,0,1) + gl.glPushMatrix( ) + gl.glTranslate( 10, 100, 0 ) + gl.glPushMatrix( ) + gl.glListBase( base+1 ) + gl.glCallLists( [ord(c) for c in text] ) + gl.glPopMatrix( ) + gl.glPopMatrix( ) + glut.glutSwapBuffers( ) + +def on_reshape( width, height ): + gl.glViewport( 0, 0, width, height ) + gl.glMatrixMode( gl.GL_PROJECTION ) + gl.glLoadIdentity( ) + gl.glOrtho( 0, width, 0, height, -1, 1 ) + gl.glMatrixMode( gl.GL_MODELVIEW ) + gl.glLoadIdentity( ) + +def on_keyboard( key, x, y ): + if key == '\033': sys.exit( ) + +def makefont(filename, size): + global texid + + # Load font and check it is monotype + face = Face(filename) + face.set_char_size( size*64 ) + if not face.is_fixed_width: + raise 'Font is not monotype' + + # Determine largest glyph size + width, height, ascender, descender = 0, 0, 0, 0 + for c in range(32,128): + face.load_char( chr(c), FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT ) + bitmap = face.glyph.bitmap + width = max( width, bitmap.width ) + ascender = max( ascender, face.glyph.bitmap_top ) + descender = max( descender, bitmap.rows-face.glyph.bitmap_top ) + height = ascender+descender + + # Generate texture data + Z = numpy.zeros((height*6, width*16), dtype=numpy.ubyte) + for j in range(6): + for i in range(16): + face.load_char(chr(32+j*16+i), FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT ) + bitmap = face.glyph.bitmap + x = i*width + face.glyph.bitmap_left + y = j*height + ascender - face.glyph.bitmap_top + Z[y:y+bitmap.rows,x:x+bitmap.width].flat = bitmap.buffer + + # Bound texture + texid = gl.glGenTextures(1) + gl.glBindTexture( gl.GL_TEXTURE_2D, texid ) + gl.glTexParameterf( gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR ) + gl.glTexParameterf( gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR ) + gl.glTexImage2D( gl.GL_TEXTURE_2D, 0, gl.GL_ALPHA, Z.shape[1], Z.shape[0], 0, + gl.GL_ALPHA, gl.GL_UNSIGNED_BYTE, Z ) + + # Generate display lists + dx, dy = width/float(Z.shape[1]), height/float(Z.shape[0]) + base = gl.glGenLists(8*16) + for i in range(8*16): + c = chr(i) + x = i%16 + y = i//16-2 + gl.glNewList(base+i, gl.GL_COMPILE) + if (c == '\n'): + gl.glPopMatrix( ) + gl.glTranslatef( 0, -height, 0 ) + gl.glPushMatrix( ) + elif (c == '\t'): + gl.glTranslatef( 4*width, 0, 0 ) + elif (i >= 32): + gl.glBegin( gl.GL_QUADS ) + gl.glTexCoord2f( (x )*dx, (y+1)*dy ), gl.glVertex( 0, -height ) + gl.glTexCoord2f( (x )*dx, (y )*dy ), gl.glVertex( 0, 0 ) + gl.glTexCoord2f( (x+1)*dx, (y )*dy ), gl.glVertex( width, 0 ) + gl.glTexCoord2f( (x+1)*dx, (y+1)*dy ), gl.glVertex( width, -height ) + gl.glEnd( ) + gl.glTranslatef( width, 0, 0 ) + gl.glEndList( ) + + +if __name__ == '__main__': + import sys + glut.glutInit( sys.argv ) + glut.glutInitDisplayMode( glut.GLUT_DOUBLE | glut.GLUT_RGB | glut.GLUT_DEPTH ) + glut.glutCreateWindow( "Freetype OpenGL" ) + glut.glutReshapeWindow( 600, 100 ) + glut.glutDisplayFunc( on_display ) + glut.glutReshapeFunc( on_reshape ) + glut.glutKeyboardFunc( on_keyboard ) + gl.glTexEnvf( gl.GL_TEXTURE_ENV, gl.GL_TEXTURE_ENV_MODE, gl.GL_MODULATE ) + gl.glEnable( gl.GL_DEPTH_TEST ) + gl.glEnable( gl.GL_BLEND ) + gl.glEnable( gl.GL_COLOR_MATERIAL ) + gl.glColorMaterial( gl.GL_FRONT_AND_BACK, gl.GL_AMBIENT_AND_DIFFUSE ) + gl.glBlendFunc( gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA ) + gl.glEnable( gl.GL_TEXTURE_2D ) + makefont( './VeraMono.ttf', 64 ) + glut.glutMainLoop( ) diff --git a/third_party/freetype-py/examples/sfnt-names.py b/third_party/freetype-py/examples/sfnt-names.py new file mode 100644 index 0000000..ffebc00 --- /dev/null +++ b/third_party/freetype-py/examples/sfnt-names.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# FreeType high-level python API - Copyright 2011 Nicolas P. Rougier +# Distributed under the terms of the new BSD license. +# ----------------------------------------------------------------------------- +from __future__ import print_function +from __future__ import division +from freetype import * + +def platform_name(platform_id): + for key, value in TT_PLATFORMS.items(): + if value == platform_id: + return key + return 'Unknown platform' + +def encoding_name(platform_id, encoding_id): + if platform_id == TT_PLATFORM_APPLE_UNICODE: + encodings = TT_APPLE_IDS + elif platform_id == TT_PLATFORM_MACINTOSH: + encodings = TT_MAC_IDS + elif platform_id == TT_PLATFORM_MICROSOFT: + encodings = TT_MS_IDS + elif platform_id == TT_PLATFORM_ADOBE: + encodings = TT_ADOBE_IDS + else: + return 'Unknown encoding' + for key, value in encodings.items(): + if value == encoding_id: + return key + return 'Unknown encoding' + +def language_name(platform_id, language_id): + if platform_id == TT_PLATFORM_MACINTOSH: + languages = TT_MAC_LANGIDS + elif platform_id == TT_PLATFORM_MICROSOFT: + languages = TT_MS_LANGIDS + else: + return 'Unknown language' + for key, value in languages.items(): + if value == language_id: + return key + return 'Unknown language' + + +if __name__ == '__main__': + import os, sys + + if len(sys.argv) < 2: + print("Usage: %s font_filename" % sys.argv[0]) + sys.exit() + face = Face(sys.argv[1]) + + name = face.get_sfnt_name(0) + print( 'platform_id:', platform_name(name.platform_id) ) + print( 'encoding_id:', encoding_name(name.platform_id, + name.encoding_id) ) + print( 'language_id:', language_name(name.platform_id, + name.language_id) ) + for i in range(face.sfnt_name_count): + name = face.get_sfnt_name(i).string + print(i, name.decode('utf-8', 'ignore')) + + + + diff --git a/third_party/freetype-py/examples/shader.py b/third_party/freetype-py/examples/shader.py new file mode 100644 index 0000000..998dd8b --- /dev/null +++ b/third_party/freetype-py/examples/shader.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright (C) 2009-2010 Nicolas P. Rougier +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +# ----------------------------------------------------------------------------- +# +# Copyright Tristam Macdonald 2008. +# +# Distributed under the Boost Software License, Version 1.0 +# (see http://www.boost.org/LICENSE_1_0.txt) +# +''' Base shader class. + + Example: + -------- + shader = Shader() + + shader.bind() + glActiveTexture(GL_TEXTURE1) + glBindTexture(lut.target, lut.id) + shader.uniformi('lut', 1) + + glActiveTexture(GL_TEXTURE0) + glBindTexture(texture.target, texture.id) + shader.uniformi('texture', 0) + shader.uniformf('pixel', 1.0/texture.width, 1.0/texture.height) + + texture.blit(x,y,w,h) + shader.unbind() +''' +import os +import OpenGL.GL as gl +import ctypes + +class Shader: + ''' Base shader class. ''' + + def __init__(self, vert = None, frag = None, name=''): + ''' vert, frag and geom take arrays of source strings + the arrays will be concatenated into one string by OpenGL.''' + + self.uniforms = {} + self.name = name + # create the program handle + self.handle = gl.glCreateProgram() + # we are not linked yet + self.linked = False + # create the vertex shader + self._build_shader(vert, gl.GL_VERTEX_SHADER) + # create the fragment shader + self._build_shader(frag, gl.GL_FRAGMENT_SHADER) + # the geometry shader will be the same, once pyglet supports the + # extension self.createShader(frag, GL_GEOMETRY_SHADER_EXT) attempt to + # link the program + self._link() + + def _build_shader(self, strings, stype): + ''' Actual building of the shader ''' + + count = len(strings) + # if we have no source code, ignore this shader + if count < 1: + return + + # create the shader handle + shader = gl.glCreateShader(stype) + + # Upload shader code + gl.glShaderSource(shader, strings) + + # compile the shader + gl.glCompileShader(shader) + + # retrieve the compile status + status = gl.glGetShaderiv(shader, gl.GL_COMPILE_STATUS) + + # if compilation failed, print the log + if not status: + # display the log + print gl.glGetShaderInfoLog(shader) + else: + # all is well, so attach the shader to the program + gl.glAttachShader(self.handle, shader) + + def _link(self): + ''' Link the program ''' + + gl.glLinkProgram(self.handle) + # retrieve the link status + temp = ctypes.c_int(0) + gl.glGetProgramiv(self.handle, gl.GL_LINK_STATUS, ctypes.byref(temp)) + + # if linking failed, print the log + if not temp: + # retrieve the log length + gl.glGetProgramiv(self.handle, + gl.GL_INFO_LOG_LENGTH, ctypes.byref(temp)) + # create a buffer for the log + #buffer = ctypes.create_string_buffer(temp.value) + # retrieve the log text + log = gl.glGetProgramInfoLog(self.handle) #, temp, None, buffer) + # print the log to the console + print log + else: + # all is well, so we are linked + self.linked = True + + def bind(self): + ''' Bind the program, i.e. use it. ''' + gl.glUseProgram(self.handle) + + def unbind(self): + ''' Unbind whatever program is currently bound - not necessarily this + program, so this should probably be a class method instead. ''' + gl.glUseProgram(0) + + def uniformf(self, name, *vals): + ''' Uploads float uniform(s), program must be currently bound. ''' + + loc = self.uniforms.get(name, + gl.glGetUniformLocation(self.handle,name)) + self.uniforms[name] = loc + + # Check there are 1-4 values + if len(vals) in range(1, 5): + # Select the correct function + { 1 : gl.glUniform1f, + 2 : gl.glUniform2f, + 3 : gl.glUniform3f, + 4 : gl.glUniform4f + # Retrieve uniform location, and set it + }[len(vals)](loc, *vals) + + def uniformi(self, name, *vals): + ''' Upload integer uniform(s), program must be currently bound. ''' + + loc = self.uniforms.get(name, + gl.glGetUniformLocation(self.handle,name)) + self.uniforms[name] = loc + + # Checks there are 1-4 values + if len(vals) in range(1, 5): + # Selects the correct function + { 1 : gl.glUniform1i, + 2 : gl.glUniform2i, + 3 : gl.glUniform3i, + 4 : gl.glUniform4i + # Retrieves uniform location, and set it + }[len(vals)](loc, *vals) + + + def uniform_matrixf(self, name, mat): + ''' Upload uniform matrix, program must be currently bound. ''' + + loc = self.uniforms.get(name, + gl.glGetUniformLocation(self.handle,name)) + self.uniforms[name] = loc + + # Upload the 4x4 floating point matrix + gl.glUniformMatrix4fv(loc, 1, False, (ctypes.c_float * 16)(*mat)) diff --git a/third_party/freetype-py/examples/subpixel-positioning.py b/third_party/freetype-py/examples/subpixel-positioning.py new file mode 100644 index 0000000..6483517 --- /dev/null +++ b/third_party/freetype-py/examples/subpixel-positioning.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# +# FreeType high-level python API - Copyright 2011 Nicolas P. Rougier +# Distributed under the terms of the new BSD license. +# +# ----------------------------------------------------------------------------- +''' +Subpixel rendering AND positioning using OpenGL and shaders. + +''' +import numpy as np +import OpenGL.GL as gl +import OpenGL.GLUT as glut +from texture_font import TextureFont, TextureAtlas +from shader import Shader + + +vert=''' +uniform sampler2D texture; +uniform vec2 pixel; +attribute float modulo; +varying float m; +void main() { + gl_FrontColor = gl_Color; + gl_TexCoord[0].xy = gl_MultiTexCoord0.xy; + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + m = modulo; +} +''' + +frag=''' +uniform sampler2D texture; +uniform vec2 pixel; +varying float m; +void main() { + float gamma = 1.0; + + vec2 uv = gl_TexCoord[0].xy; + vec4 current = texture2D(texture, uv); + vec4 previous= texture2D(texture, uv+vec2(-1,0)*pixel); + + current = pow(current, vec4(1.0/gamma)); + previous = pow(previous, vec4(1.0/gamma)); + + float r = current.r; + float g = current.g; + float b = current.b; + float a = current.a; + if( m <= 0.333 ) + { + float z = m/0.333; + r = mix(current.r, previous.b, z); + g = mix(current.g, current.r, z); + b = mix(current.b, current.g, z); + } + else if( m <= 0.666 ) + { + float z = (m-0.33)/0.333; + r = mix(previous.b, previous.g, z); + g = mix(current.r, previous.b, z); + b = mix(current.g, current.r, z); + } + else if( m < 1.0 ) + { + float z = (m-0.66)/0.334; + r = mix(previous.g, previous.r, z); + g = mix(previous.b, previous.g, z); + b = mix(current.r, previous.b, z); + } + + float t = max(max(r,g),b); + vec4 color = vec4(0.,0.,0., (r+g+b)/2.); + color = t*color + (1.-t)*vec4(r,g,b, min(min(r,g),b)); + gl_FragColor = vec4( color.rgb, color.a); +} +''' + + + + + +class Label: + def __init__(self, text, font, color=(1.0, 1.0, 1.0, 0.0), x=0, y=0, + width=None, height=None, anchor_x='left', anchor_y='baseline'): + self.text = text + self.vertices = np.zeros((len(text)*4,3), dtype=np.float32) + self.indices = np.zeros((len(text)*6, ), dtype=np.uint) + self.colors = np.zeros((len(text)*4,4), dtype=np.float32) + self.texcoords= np.zeros((len(text)*4,2), dtype=np.float32) + self.attrib = np.zeros((len(text)*4,1), dtype=np.float32) + pen = [x,y] + prev = None + + for i,charcode in enumerate(text): + glyph = font[charcode] + kerning = glyph.get_kerning(prev) + x0 = pen[0] + glyph.offset[0] + kerning + dx = x0-int(x0) + x0 = int(x0) + y0 = pen[1] + glyph.offset[1] + x1 = x0 + glyph.size[0] + y1 = y0 - glyph.size[1] + u0 = glyph.texcoords[0] + v0 = glyph.texcoords[1] + u1 = glyph.texcoords[2] + v1 = glyph.texcoords[3] + + index = i*4 + indices = [index, index+1, index+2, index, index+2, index+3] + vertices = [[x0,y0,1],[x0,y1,1],[x1,y1,1], [x1,y0,1]] + texcoords = [[u0,v0],[u0,v1],[u1,v1], [u1,v0]] + colors = [color,]*4 + + self.vertices[i*4:i*4+4] = vertices + self.indices[i*6:i*6+6] = indices + self.texcoords[i*4:i*4+4] = texcoords + self.colors[i*4:i*4+4] = colors + self.attrib[i*4:i*4+4] = dx + pen[0] = pen[0]+glyph.advance[0]/64.0 + kerning + pen[1] = pen[1]+glyph.advance[1]/64.0 + prev = charcode + + width = pen[0]-glyph.advance[0]/64.0+glyph.size[0] + + if anchor_y == 'top': + dy = -round(font.ascender) + elif anchor_y == 'center': + dy = +round(-font.height/2-font.descender) + elif anchor_y == 'bottom': + dy = -round(font.descender) + else: + dy = 0 + + if anchor_x == 'right': + dx = -width/1.0 + elif anchor_x == 'center': + dx = -width/2.0 + else: + dx = 0 + self.vertices += (round(dx), round(dy), 0) + + + def draw(self): + gl.glEnable( gl.GL_TEXTURE_2D ) + gl.glDisable( gl.GL_DEPTH_TEST ) + + gl.glEnableClientState(gl.GL_VERTEX_ARRAY) + gl.glEnableClientState(gl.GL_COLOR_ARRAY) + gl.glEnableClientState(gl.GL_TEXTURE_COORD_ARRAY) + gl.glEnableClientState(gl.GL_VERTEX_ARRAY) + + gl.glVertexPointer(3, gl.GL_FLOAT, 0, self.vertices) + gl.glColorPointer(4, gl.GL_FLOAT, 0, self.colors) + gl.glTexCoordPointer(2, gl.GL_FLOAT, 0, self.texcoords) + + r,g,b = 0,0,0 + gl.glColor( 1, 1, 1, 1 ) + gl.glEnable( gl.GL_BLEND ) + #gl.glBlendFunc( gl.GL_CONSTANT_COLOR_EXT, gl.GL_ONE_MINUS_SRC_COLOR ) + #gl.glBlendColor(r,g,b,1) + gl.glBlendFunc( gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA ) + gl.glBlendColor( 1, 1, 1, 1 ) + + gl.glEnableVertexAttribArray( 1 ); + gl.glVertexAttribPointer( 1, 1, gl.GL_FLOAT, gl.GL_FALSE, 0, self.attrib) + shader.bind() + shader.uniformi('texture', 0) + shader.uniformf('pixel', 1.0/512, 1.0/512) + gl.glDrawElements(gl.GL_TRIANGLES, len(self.indices), + gl.GL_UNSIGNED_INT, self.indices) + shader.unbind() + gl.glDisableVertexAttribArray( 1 ); + gl.glDisableClientState(gl.GL_VERTEX_ARRAY) + gl.glDisableClientState(gl.GL_COLOR_ARRAY) + gl.glDisableClientState(gl.GL_TEXTURE_COORD_ARRAY) + gl.glDisable( gl.GL_TEXTURE_2D ) + gl.glDisable( gl.GL_BLEND ) + + + + +if __name__ == '__main__': + import sys + + atlas = TextureAtlas(512,512,3) + + def on_display( ): + #gl.glClearColor(0,0,0,1) + gl.glClearColor(1,1,1,1) + gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) + gl.glBindTexture( gl.GL_TEXTURE_2D, atlas.texid ) + for label in labels: + label.draw() + + gl.glColor(0,0,0,1) + gl.glBegin(gl.GL_LINES) + gl.glVertex2i(15,0) + gl.glVertex2i(15, 330) + gl.glVertex2i(225, 0) + gl.glVertex2i(225, 330) + gl.glEnd() + glut.glutSwapBuffers( ) + + def on_reshape( width, height ): + gl.glViewport( 0, 0, width, height ) + gl.glMatrixMode( gl.GL_PROJECTION ) + gl.glLoadIdentity( ) + gl.glOrtho( 0, width, 0, height, -1, 1 ) + gl.glMatrixMode( gl.GL_MODELVIEW ) + gl.glLoadIdentity( ) + + def on_keyboard( key, x, y ): + if key == '\033': + sys.exit( ) + + glut.glutInit( sys.argv ) + glut.glutInitDisplayMode( glut.GLUT_DOUBLE | glut.GLUT_RGBA | glut.GLUT_DEPTH ) + glut.glutCreateWindow( "Freetype OpenGL" ) + glut.glutReshapeWindow( 240, 330 ) + glut.glutDisplayFunc( on_display ) + glut.glutReshapeFunc( on_reshape ) + glut.glutKeyboardFunc( on_keyboard ) + + font = TextureFont(atlas, './Arial.ttf', 9) + text = "|... A Quick Brown Fox Jumps Over The Lazy Dog" + labels = [] + x,y = 20,310 + for i in range(30): + labels.append(Label(text=text, font=font, x=x, y=y)) + x += 0.1000000000001 + y -= 10 + atlas.upload() + shader = Shader(vert,frag) + glut.glutMainLoop( ) diff --git a/third_party/freetype-py/examples/texture_font.py b/third_party/freetype-py/examples/texture_font.py new file mode 100644 index 0000000..8abdd2e --- /dev/null +++ b/third_party/freetype-py/examples/texture_font.py @@ -0,0 +1,421 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# +# FreeType high-level python API - Copyright 2011 Nicolas P. Rougier +# Distributed under the terms of the new BSD license. +# +# ----------------------------------------------------------------------------- +''' +Texture font class + +''' +import sys +import math +import numpy as np +import OpenGL.GL as gl +from freetype import * + + +class TextureAtlas: + ''' + Group multiple small data regions into a larger texture. + + The algorithm is based on the article by Jukka Jylänki : "A Thousand Ways + to Pack the Bin - A Practical Approach to Two-Dimensional Rectangle Bin + Packing", February 27, 2010. More precisely, this is an implementation of + the Skyline Bottom-Left algorithm based on C++ sources provided by Jukka + Jylänki at: http://clb.demon.fi/files/RectangleBinPack/ + + Example usage: + -------------- + + atlas = TextureAtlas(512,512,3) + region = atlas.get_region(20,20) + ... + atlas.set_region(region, data) + ''' + + def __init__(self, width=1024, height=1024, depth=1): + ''' + Initialize a new atlas of given size. + + Parameters + ---------- + + width : int + Width of the underlying texture + + height : int + Height of the underlying texture + + depth : 1 or 3 + Depth of the underlying texture + ''' + self.width = int(math.pow(2, int(math.log(width, 2) + 0.5))) + self.height = int(math.pow(2, int(math.log(height, 2) + 0.5))) + self.depth = depth + self.nodes = [ (0,0,self.width), ] + self.data = np.zeros((self.height, self.width, self.depth), + dtype=np.ubyte) + self.texid = 0 + self.used = 0 + + + + def upload(self): + ''' + Upload atlas data into video memory. + ''' + + if not self.texid: + self.texid = gl.glGenTextures(1) + + gl.glBindTexture( gl.GL_TEXTURE_2D, self.texid ) + gl.glTexParameteri( gl.GL_TEXTURE_2D, + gl.GL_TEXTURE_WRAP_S, gl.GL_CLAMP ) + gl.glTexParameteri( gl.GL_TEXTURE_2D, + gl.GL_TEXTURE_WRAP_T, gl.GL_CLAMP ) + gl.glTexParameteri( gl.GL_TEXTURE_2D, + gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR ) + gl.glTexParameteri( gl.GL_TEXTURE_2D, + gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR ) + if self.depth == 1: + gl.glTexImage2D( gl.GL_TEXTURE_2D, 0, gl.GL_ALPHA, + self.width, self.height, 0, + gl.GL_ALPHA, gl.GL_UNSIGNED_BYTE, self.data ) + else: + gl.glTexImage2D( gl.GL_TEXTURE_2D, 0, gl.GL_RGB, + self.width, self.height, 0, + gl.GL_RGB, gl.GL_UNSIGNED_BYTE, self.data ) + + + + def set_region(self, region, data): + ''' + Set a given region width provided data. + + Parameters + ---------- + + region : (int,int,int,int) + an allocated region (x,y,width,height) + + data : numpy array + data to be copied into given region + ''' + + x, y, width, height = region + self.data[y:y+height,x:x+width, :] = data + + + + def get_region(self, width, height): + ''' + Get a free region of given size and allocate it + + Parameters + ---------- + + width : int + Width of region to allocate + + height : int + Height of region to allocate + + Return + ------ + A newly allocated region as (x,y,width,height) or (-1,-1,0,0) + ''' + + best_height = sys.maxint + best_index = -1 + best_width = sys.maxint + region = 0, 0, width, height + + for i in range(len(self.nodes)): + y = self.fit(i, width, height) + if y >= 0: + node = self.nodes[i] + if (y+height < best_height or + (y+height == best_height and node[2] < best_width)): + best_height = y+height + best_index = i + best_width = node[2] + region = node[0], y, width, height + + if best_index == -1: + return -1,-1,0,0 + + node = region[0], region[1]+height, width + self.nodes.insert(best_index, node) + + i = best_index+1 + while i < len(self.nodes): + node = self.nodes[i] + prev_node = self.nodes[i-1] + if node[0] < prev_node[0]+prev_node[2]: + shrink = prev_node[0]+prev_node[2] - node[0] + x,y,w = self.nodes[i] + self.nodes[i] = x+shrink, y, w-shrink + if self.nodes[i][2] <= 0: + del self.nodes[i] + i -= 1 + else: + break + else: + break + i += 1 + + self.merge() + self.used += width*height + return region + + + + def fit(self, index, width, height): + ''' + Test if region (width,height) fit into self.nodes[index] + + Parameters + ---------- + + index : int + Index of the internal node to be tested + + width : int + Width or the region to be tested + + height : int + Height or the region to be tested + + ''' + + node = self.nodes[index] + x,y = node[0], node[1] + width_left = width + + if x+width > self.width: + return -1 + + i = index + while width_left > 0: + node = self.nodes[i] + y = max(y, node[1]) + if y+height > self.height: + return -1 + width_left -= node[2] + i += 1 + return y + + + + def merge(self): + ''' + Merge nodes + ''' + + i = 0 + while i < len(self.nodes)-1: + node = self.nodes[i] + next_node = self.nodes[i+1] + if node[1] == next_node[1]: + self.nodes[i] = node[0], node[1], node[2]+next_node[2] + del self.nodes[i+1] + else: + i += 1 + + +class TextureFont: + ''' + A texture font gathers a set of glyph relatively to a given font filename + and size. + ''' + + def __init__(self, atlas, filename, size): + ''' + Initialize font + + Parameters: + ----------- + + atlas: TextureAtlas + Texture atlas where glyph texture will be stored + + filename: str + Font filename + + size : float + Font size + ''' + self.atlas = atlas + self.filename = filename + self.size = size + self.glyphs = {} + face = Face( self.filename ) + face.set_char_size( int(self.size*64)) + self._dirty = False + metrics = face.size + self.ascender = metrics.ascender/64.0 + self.descender = metrics.descender/64.0 + self.height = metrics.height/64.0 + self.linegap = self.height - self.ascender + self.descender + self.depth = atlas.depth + set_lcd_filter(FT_LCD_FILTER_LIGHT) + + + def __getitem__(self, charcode): + ''' + x.__getitem__(y) <==> x[y] + ''' + if charcode not in self.glyphs.keys(): + self.load('%c' % charcode) + return self.glyphs[charcode] + + + + def get_texid(self): + ''' + Get underlying texture identity . + ''' + + if self._dirty: + self.atlas.upload() + self._dirty = False + return self.atlas.texid + + texid = property(get_texid, + doc='''Underlying texture identity.''') + + + + def load(self, charcodes = ''): + ''' + Build glyphs corresponding to individual characters in charcodes. + + Parameters: + ----------- + + charcodes: [str | unicode] + Set of characters to be represented + ''' + face = Face( self.filename ) + pen = Vector(0,0) + hres = 16*72 + hscale = 1.0/16 + + for charcode in charcodes: + face.set_char_size( int(self.size * 64), 0, hres, 72 ) + matrix = Matrix( int((hscale) * 0x10000L), int((0.0) * 0x10000L), + int((0.0) * 0x10000L), int((1.0) * 0x10000L) ) + face.set_transform( matrix, pen ) + if charcode in self.glyphs.keys(): + continue + + self.dirty = True + flags = FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT + flags |= FT_LOAD_TARGET_LCD + + face.load_char( charcode, flags ) + bitmap = face.glyph.bitmap + left = face.glyph.bitmap_left + top = face.glyph.bitmap_top + width = face.glyph.bitmap.width + rows = face.glyph.bitmap.rows + pitch = face.glyph.bitmap.pitch + + x,y,w,h = self.atlas.get_region(width/self.depth+2, rows+2) + if x < 0: + print 'Missed !' + continue + x,y = x+1, y+1 + w,h = w-2, h-2 + data = [] + for i in range(rows): + data.extend(bitmap.buffer[i*pitch:i*pitch+width]) + data = np.array(data,dtype=np.ubyte).reshape(h,w,3) + gamma = 1.5 + Z = ((data/255.0)**(gamma)) + data = (Z*255).astype(np.ubyte) + self.atlas.set_region((x,y,w,h), data) + + # Build glyph + size = w,h + offset = left, top + advance= face.glyph.advance.x, face.glyph.advance.y + + u0 = (x + 0.0)/float(self.atlas.width) + v0 = (y + 0.0)/float(self.atlas.height) + u1 = (x + w - 0.0)/float(self.atlas.width) + v1 = (y + h - 0.0)/float(self.atlas.height) + texcoords = (u0,v0,u1,v1) + glyph = TextureGlyph(charcode, size, offset, advance, texcoords) + self.glyphs[charcode] = glyph + + # Generate kerning + for g in self.glyphs.values(): + # 64 * 64 because of 26.6 encoding AND the transform matrix used + # in texture_font_load_face (hres = 64) + kerning = face.get_kerning(g.charcode, charcode, mode=FT_KERNING_UNFITTED) + if kerning.x != 0: + glyph.kerning[g.charcode] = kerning.x/(64.0*64.0) + kerning = face.get_kerning(charcode, g.charcode, mode=FT_KERNING_UNFITTED) + if kerning.x != 0: + g.kerning[charcode] = kerning.x/(64.0*64.0) + + # High resolution advance.x calculation + # gindex = face.get_char_index( charcode ) + # a = face.get_advance(gindex, FT_LOAD_RENDER | FT_LOAD_TARGET_LCD)/(64*72) + # glyph.advance = a, glyph.advance[1] + + +class TextureGlyph: + ''' + A texture glyph gathers information relative to the size/offset/advance and + texture coordinates of a single character. It is generally built + automatically by a TextureFont. + ''' + + def __init__(self, charcode, size, offset, advance, texcoords): + ''' + Build a new texture glyph + + Parameter: + ---------- + + charcode : char + Represented character + + size: tuple of 2 ints + Glyph size in pixels + + offset: tuple of 2 floats + Glyph offset relatively to anchor point + + advance: tuple of 2 floats + Glyph advance + + texcoords: tuple of 4 floats + Texture coordinates of bottom-left and top-right corner + ''' + self.charcode = charcode + self.size = size + self.offset = offset + self.advance = advance + self.texcoords = texcoords + self.kerning = {} + + + def get_kerning(self, charcode): + ''' Get kerning information + + Parameters: + ----------- + + charcode: char + Character preceding this glyph + ''' + if charcode in self.kerning.keys(): + return self.kerning[charcode] + else: + return 0 diff --git a/third_party/freetype-py/examples/wordle.py b/third_party/freetype-py/examples/wordle.py new file mode 100644 index 0000000..249f86d --- /dev/null +++ b/third_party/freetype-py/examples/wordle.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# +# FreeType high-level python API - Copyright 2011 Nicolas P. Rougier +# Distributed under the terms of the new BSD license. +# +# ----------------------------------------------------------------------------- +import math +import numpy as np +from freetype import * +import matplotlib.pyplot as plt + + +def make_label(text, filename, size=12, angle=0): + ''' + Parameters: + ----------- + text : string + Text to be displayed + filename : string + Path to a font + size : int + Font size in 1/64th points + angle : float + Text angle in degrees + ''' + face = Face(filename) + face.set_char_size( size*64 ) + angle = (angle/180.0)*math.pi + matrix = FT_Matrix( (int)( math.cos( angle ) * 0x10000L ), + (int)(-math.sin( angle ) * 0x10000L ), + (int)( math.sin( angle ) * 0x10000L ), + (int)( math.cos( angle ) * 0x10000L )) + flags = FT_LOAD_RENDER + pen = FT_Vector(0,0) + FT_Set_Transform( face._FT_Face, byref(matrix), byref(pen) ) + previous = 0 + xmin, xmax = 0, 0 + ymin, ymax = 0, 0 + for c in text: + face.load_char(c, flags) + kerning = face.get_kerning(previous, c) + previous = c + bitmap = face.glyph.bitmap + pitch = face.glyph.bitmap.pitch + width = face.glyph.bitmap.width + rows = face.glyph.bitmap.rows + top = face.glyph.bitmap_top + left = face.glyph.bitmap_left + pen.x += kerning.x + x0 = (pen.x >> 6) + left + x1 = x0 + width + y0 = (pen.y >> 6) - (rows - top) + y1 = y0 + rows + xmin, xmax = min(xmin, x0), max(xmax, x1) + ymin, ymax = min(ymin, y0), max(ymax, y1) + pen.x += face.glyph.advance.x + pen.y += face.glyph.advance.y + + L = np.zeros((ymax-ymin, xmax-xmin),dtype=np.ubyte) + previous = 0 + pen.x, pen.y = 0, 0 + for c in text: + face.load_char(c, flags) + kerning = face.get_kerning(previous, c) + previous = c + bitmap = face.glyph.bitmap + pitch = face.glyph.bitmap.pitch + width = face.glyph.bitmap.width + rows = face.glyph.bitmap.rows + top = face.glyph.bitmap_top + left = face.glyph.bitmap_left + pen.x += kerning.x + x = (pen.x >> 6) - xmin + left + y = (pen.y >> 6) - ymin - (rows - top) + data = [] + for j in range(rows): + data.extend(bitmap.buffer[j*pitch:j*pitch+width]) + if len(data): + Z = np.array(data,dtype=np.ubyte).reshape(rows, width) + L[y:y+rows,x:x+width] |= Z[::-1,::1] + pen.x += face.glyph.advance.x + pen.y += face.glyph.advance.y + + return L + + +if __name__ == '__main__': + from PIL import Image + + n_words = 100 + H, W, dpi = 600, 800, 72.0 + I = np.zeros((H, W, 3), dtype=np.ubyte) + S = np.random.normal(0,1,n_words) + S = (S-S.min())/(S.max()-S.min()) + S = np.sort(1-np.sqrt(S))[::-1] + sizes = (12 + S*48).astype(int).tolist() + + def spiral(): + eccentricity = 1.5 + radius = 8 + step = 0.1 + t = 0 + while True: + t += step + yield eccentricity*radius*t*math.cos(t), radius*t*math.sin(t) + + fails = 0 + for size in sizes: + angle = np.random.randint(-25,25) + L = make_label('Hello', './Vera.ttf', size, angle=angle) + h,w = L.shape + if h < H and w < W: + x0 = W//2 + (np.random.uniform()-.1)*50 + y0 = H//2 + (np.random.uniform()-.1)*50 + for dx,dy in spiral(): + c = .25+.75*np.random.random() + x = int(x0+dx) + y = int(y0+dy) + if x <= w//2 or y <= h//2 or x >= (W-w//2) or y >= (H-h//2): + fails += 1 + break + if (I[y-h//2:y-h//2+h, x-w//2:x-w//2+w,0] * L).sum() == 0: + I[y-h//2:y-h//2+h, x-w//2:x-w//2+w,0] |= (c * L).astype(int) + I[y-h//2:y-h//2+h, x-w//2:x-w//2+w,1] |= (c * L).astype(int) + I[y-h//2:y-h//2+h, x-w//2:x-w//2+w,2] |= (c * L).astype(int) + break + + print "Number of fails:", fails + fig = plt.figure(figsize=(W/dpi,H/dpi), dpi=dpi) + ax = fig.add_axes([0,0,1,1], frameon=False) + ax.imshow(I, interpolation='nearest', cmap=plt.cm.gray, origin='upper') + #plt.axis('off') + plt.show() + I = Image.fromarray(I[::-1,::1,::1], mode='RGB') + I.save('wordle.png') |