Exemplo n.º 1
0
class MonospaceFontAtlas(object):
    def __init__(self,font_info,size,dpi):
        self.font_info=font_info
        self.size=size
        self.dpi=dpi
        self.id=self.getIdFromArgs(font_info,size,dpi)
        self._face=Face(font_info.path)
        self._face.set_char_size(height=self.size*64,vres=self.dpi)

        self.charcode2glyph=None
        self.charcode2unichr=None
        self.charcode2displaylist=None
        self.max_ascender = None
        self.max_descender = None
        self.max_tile_width = None
        self.max_tile_height = None
        self.max_bitmap_size = None
        self.total_bitmap_area=0
        self.atlas=None

    def getID(self):
        return self.id

    @staticmethod
    def getIdFromArgs(font_info,size,dpi):
        return "%s_%d_%d"%(font_info.getID(),size,dpi)

    def createFontAtlas(self):
        if self.atlas:
            self.atlas.free()
            self.atlas=None
        self.charcode2glyph={}
        self.charcode2unichr={}
        self.max_ascender = None
        self.max_descender = None
        self.max_tile_width = None
        self.max_tile_height = None
        self.max_bitmap_size = None
        self.total_bitmap_area=0
        # load font glyphs and calculate max. char size.
        # This is used when the altas is created to properly size the tex.
        # i.e. max glyph size * num glyphs
        #

        max_w,max_h=0,0
        max_ascender, max_descender, max_tile_width = 0, 0, 0
        face=self._face
        face.set_char_size(height=self.size*64,vres=self.dpi)

        # Create texAtlas for glyph set.
        x_ppem=face.size.x_ppem
        y_ppem=face.size.x_ppem
        units_ppem=self.font_info.units_per_em
        est_max_width=(face.bbox.xMax-face.bbox.xMin)/float(units_ppem)*x_ppem
        est_max_height=face.size.ascender/float(units_ppem)*y_ppem
        target_atlas_area=int(est_max_width*est_max_height)*face.num_glyphs
        # make sure it is big enough. ;)
        # height is trimmed before sending to video ram anyhow.
        target_atlas_area=target_atlas_area*3.0
        pow2_area=nextPow2(target_atlas_area)
        atlas_width=2048
        atlas_height=pow2_area/atlas_width
        self.atlas=TextureAtlas(atlas_width,atlas_height)
        charcode, gindex=face.get_first_char()

        while gindex:
            uchar = unichr(charcode)
            if ud.category(uchar) not in (u'Zl',u'Zp',u'Cc',u'Cf',u'Cs',u'Co',u'Cn'):
                self.charcode2unichr[charcode]=uchar
                face.load_char(uchar, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT )
                bitmap = face.glyph.bitmap

                self.total_bitmap_area+=bitmap.width*bitmap.rows
                max_ascender = max( max_ascender, face.glyph.bitmap_top)
                max_descender = max( max_descender, bitmap.rows - face.glyph.bitmap_top )
                max_tile_width = max( max_tile_width,bitmap.width)
                max_w=max(bitmap.width,max_w)
                max_h=max(bitmap.rows,max_h)

                x,y,w,h = self.atlas.get_region(bitmap.width+2, bitmap.rows+2)

                if x < 0:
                    raise Exception("MonospaceFontAtlas.get_region failed for: {0}, requested area: {1}. Atlas Full!".format(charcode,(bitmap.width+2, bitmap.rows+2)))
                x,y = x+1, y+1
                w,h = w-2, h-2
                data = np.array(bitmap._FT_Bitmap.buffer[:(bitmap.rows*bitmap.width)],dtype=np.ubyte).reshape(h,w,1)
                self.atlas.set_region((x,y,w,h), data)

                self.charcode2glyph[charcode]=dict(
                            offset=(face.glyph.bitmap_left, face.glyph.bitmap_top),
                            size=(w,h),
                            atlas_coords=(x,y,w,h),
                            texcoords = [x, y, x + w, y + h],
                            index=gindex,
                            unichar=uchar
                            )

            charcode, gindex = face.get_next_char(charcode, gindex)

        self.max_ascender = max_ascender
        self.max_descender = max_descender
        self.max_tile_width = max_tile_width
        self.max_tile_height = max_ascender+max_descender
        self.max_bitmap_size=max_w,max_h

        # resize atlas
        height=nextPow2(self.atlas.max_y+1)
        self.atlas.resize(height)
        self.atlas.upload()
        self.createDisplayLists()
        self._face=None
        #print 'w_max_glyth info:',w_max_glyph
        #print 'h_max_glyth info:',h_max_glyph

    def createDisplayLists(self):
        glyph_count=len(self.charcode2unichr)
        max_tile_width,max_tile_height=self.max_tile_width,self.max_tile_height
        display_lists_for_chars={}

        base = glGenLists(glyph_count)
        for i,(charcode,glyph) in enumerate( self.charcode2glyph.iteritems()):
            dl_index=base+i
            uchar=self.charcode2unichr[charcode]

            # update tex coords to reflect earlier resize of atlas height.
            gx1,gy1,gx2,gy2=glyph['texcoords']
            gx1=gx1/float(self.atlas.width)
            gy1=gy1/float(self.atlas.height)
            gx2=gx2/float(self.atlas.width)
            gy2=gy2/float(self.atlas.height)
            glyph['texcoords'] =[gx1,gy1,gx2,gy2]

            glNewList(dl_index, GL_COMPILE)
            if uchar not in [u'\t',u'\n']:
                glBegin( GL_QUADS )
                x1 = glyph['offset'][0]
                x2 = x1+glyph['size'][0]
                y1=(self.max_ascender-glyph['offset'][1])
                y2=y1+glyph['size'][1]

                glTexCoord2f( gx1, gy2 ),    glVertex2f( x1,-y2 )
                glTexCoord2f( gx1, gy1 ),    glVertex2f( x1,-y1 )
                glTexCoord2f( gx2, gy1 ),    glVertex2f( x2,-y1 )
                glTexCoord2f( gx2, gy2 ),    glVertex2f( x2,-y2 )
                glEnd( )
                glTranslatef( max_tile_width,0,0)
            glEndList( )

            display_lists_for_chars[charcode]=dl_index

        self.charcode2displaylist=display_lists_for_chars

    def saveGlyphBitmap(self,file_name=None):
        if file_name is None:
            import os
            #print 'CWD:',os.getcwd()
            file_name=os.path.join(os.getcwd(),self.getID().lower().replace(u' ',u'_')+'.png')
        from scipy import misc
        if self.atlas is None:
            self.loadAtlas()
        if self.atlas.depth==1:
            misc.imsave(file_name, self.atlas.data.reshape(self.atlas.data.shape[:2]))
        else:
            misc.imsave(file_name, self.atlas.data)


    def __del__(self):
        self._face=None
        if self.atlas.texid:
            glDeleteTextures(1, self.atlas.texid)
            self.atlas.texid=None
            self.atlas=None
        if self.charcode2displaylist:
            for dl in self.charcode2displaylist.values():
                glDeleteLists(dl, 1)
            self.charcode2displaylist.clear()
        self.charcode2displaylist=None
        if self.charcode2glyph:
            self.charcode2glyph.clear()
            self.charcode2glyph=None
        if self.charcode2unichr:
            self.charcode2unichr.clear()
            self.charcode2unichr=None
Exemplo n.º 2
0
class MonospaceFontAtlas(object):
    def __init__(self, font_info, size, dpi):
        self.font_info = font_info
        self.size = size
        self.dpi = dpi
        self.id = self.getIdFromArgs(font_info, size, dpi)
        self._face = Face(font_info.path)
        self._face.set_char_size(height=self.size * 64, vres=self.dpi)

        self.charcode2glyph = None
        self.charcode2unichr = None
        self.charcode2displaylist = None
        self.max_ascender = None
        self.max_descender = None
        self.max_tile_width = None
        self.max_tile_height = None
        self.max_bitmap_size = None
        self.total_bitmap_area = 0
        self.atlas = None

    def getID(self):
        return self.id

    @staticmethod
    def getIdFromArgs(font_info, size, dpi):
        return "%s_%d_%d" % (font_info.getID(), size, dpi)

    def createFontAtlas(self):
        if self.atlas:
            self.atlas.free()
            self.atlas = None
        self.charcode2glyph = {}
        self.charcode2unichr = {}
        self.max_ascender = None
        self.max_descender = None
        self.max_tile_width = None
        self.max_tile_height = None
        self.max_bitmap_size = None
        self.total_bitmap_area = 0
        # load font glyphs and calculate max. char size.
        # This is used when the altas is created to properly size the tex.
        # i.e. max glyph size * num glyphs

        max_w, max_h = 0, 0
        max_ascender, max_descender, max_tile_width = 0, 0, 0
        face = self._face
        face.set_char_size(height=self.size * 64, vres=self.dpi)

        # Create texAtlas for glyph set.
        x_ppem = face.size.x_ppem
        y_ppem = face.size.x_ppem
        units_ppem = self.font_info.units_per_em
        est_max_width = ((face.bbox.xMax - face.bbox.xMin) /
                         float(units_ppem) * x_ppem)
        est_max_height = face.size.ascender / float(units_ppem) * y_ppem
        target_atlas_area = int(
            est_max_width * est_max_height) * face.num_glyphs
        # make sure it is big enough. ;)
        # height is trimmed before sending to video ram anyhow.
        target_atlas_area = target_atlas_area * 3.0
        pow2_area = nextPow2(target_atlas_area)
        atlas_width = 2048
        atlas_height = pow2_area / atlas_width
        self.atlas = TextureAtlas(atlas_width, atlas_height * 2)
        charcode, gindex = face.get_first_char()

        while gindex:
            uchar = chr(charcode)
            if ud.category(uchar) not in (u'Zl', u'Zp', u'Cc', u'Cf', u'Cs',
                                          u'Co', u'Cn'):
                self.charcode2unichr[charcode] = uchar
                face.load_char(uchar, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT)
                bitmap = face.glyph.bitmap

                self.total_bitmap_area += bitmap.width * bitmap.rows
                max_ascender = max(max_ascender, face.glyph.bitmap_top)
                max_descender = max(max_descender,
                                    bitmap.rows - face.glyph.bitmap_top)
                max_tile_width = max(max_tile_width, bitmap.width)
                max_w = max(bitmap.width, max_w)
                max_h = max(bitmap.rows, max_h)

                x, y, w, h = self.atlas.get_region(bitmap.width + 2,
                                                   bitmap.rows + 2)

                if x < 0:
                    msg = ("MonospaceFontAtlas.get_region failed "
                           "for: {0}, requested area: {1}. Atlas Full!")
                    vals = charcode, (bitmap.width + 2, bitmap.rows + 2)
                    raise Exception(msg.format(vals))
                x, y = x + 1, y + 1
                w, h = w - 2, h - 2
                data = np.array(bitmap._FT_Bitmap.buffer[:(bitmap.rows *
                                                           bitmap.width)],
                                dtype=np.ubyte).reshape(h, w, 1)
                self.atlas.set_region((x, y, w, h), data)

                self.charcode2glyph[charcode] = dict(
                    offset=(face.glyph.bitmap_left, face.glyph.bitmap_top),
                    size=(w, h),
                    atlas_coords=(x, y, w, h),
                    texcoords=[x, y, x + w, y + h],
                    index=gindex,
                    unichar=uchar)

            charcode, gindex = face.get_next_char(charcode, gindex)

        self.max_ascender = max_ascender
        self.max_descender = max_descender
        self.max_tile_width = max_tile_width
        self.max_tile_height = max_ascender + max_descender
        self.max_bitmap_size = max_w, max_h

        # resize atlas
        height = nextPow2(self.atlas.max_y + 1)
        self.atlas.resize(height)
        self.atlas.upload()
        self.createDisplayLists()
        self._face = None

    def createDisplayLists(self):
        glyph_count = len(self.charcode2unichr)
        max_tile_width = self.max_tile_width
        max_tile_height = self.max_tile_height
        display_lists_for_chars = {}

        base = glGenLists(glyph_count)
        for i, (charcode, glyph) in enumerate(self.charcode2glyph.items()):
            dl_index = base + i
            uchar = self.charcode2unichr[charcode]

            # update tex coords to reflect earlier resize of atlas height.
            gx1, gy1, gx2, gy2 = glyph['texcoords']
            gx1 = gx1 / float(self.atlas.width)
            gy1 = gy1 / float(self.atlas.height)
            gx2 = gx2 / float(self.atlas.width)
            gy2 = gy2 / float(self.atlas.height)
            glyph['texcoords'] = [gx1, gy1, gx2, gy2]

            glNewList(dl_index, GL_COMPILE)
            if uchar not in [u'\t', u'\n']:
                glBegin(GL_QUADS)
                x1 = glyph['offset'][0]
                x2 = x1 + glyph['size'][0]
                y1 = (self.max_ascender - glyph['offset'][1])
                y2 = y1 + glyph['size'][1]

                glTexCoord2f(gx1, gy2), glVertex2f(x1, -y2)
                glTexCoord2f(gx1, gy1), glVertex2f(x1, -y1)
                glTexCoord2f(gx2, gy1), glVertex2f(x2, -y1)
                glTexCoord2f(gx2, gy2), glVertex2f(x2, -y2)
                glEnd()
                glTranslatef(max_tile_width, 0, 0)
            glEndList()

            display_lists_for_chars[charcode] = dl_index

        self.charcode2displaylist = display_lists_for_chars

    def saveGlyphBitmap(self, file_name=None):
        if file_name is None:
            import os
            file_name = os.path.join(
                os.getcwd(),
                self.getID().lower().replace(u' ', u'_') + '.png')
        from scipy import misc
        if self.atlas is None:
            self.loadAtlas()
        if self.atlas.depth == 1:
            misc.imsave(file_name,
                        self.atlas.data.reshape(self.atlas.data.shape[:2]))
        else:
            misc.imsave(file_name, self.atlas.data)

    def __del__(self):
        self._face = None
        if self.atlas.texid is not None:
            #glDeleteTextures(1, self.atlas.texid)
            self.atlas.texid = None
            self.atlas = None
        if self.charcode2displaylist is not None:
            # for dl in self.charcode2displaylist.values():
            #    glDeleteLists(dl, 1)
            self.charcode2displaylist.clear()
            self.charcode2displaylist = None
        if self.charcode2glyph is not None:
            self.charcode2glyph.clear()
            self.charcode2glyph = None
        if self.charcode2unichr is not None:
            self.charcode2unichr.clear()
            self.charcode2unichr = None