From a02ae3a4607c3bbaf4ab97a93b003f71b2daab5e Mon Sep 17 00:00:00 2001 From: HW Date: Thu, 1 Dec 2011 16:11:43 +0100 Subject: [PATCH] Added kerning for rendering text --- einkfb.c | 105 ++++++++++++++++++++++++++++++++++++++++- ft.c | 25 ++++++++++ rendertext.lua | 20 +++++--- rendertext_example.lua | 10 +++- 4 files changed, 151 insertions(+), 9 deletions(-) diff --git a/einkfb.c b/einkfb.c index 4227df6d1..181fb3024 100644 --- a/einkfb.c +++ b/einkfb.c @@ -167,7 +167,6 @@ static int blitToFrameBuffer(lua_State *L) { uint8_t *fbptr; uint8_t *bbptr; - uint8_t smask; if(xdest & 1) { /* this will render the leftmost column */ @@ -230,6 +229,109 @@ static int blitToFrameBuffer(lua_State *L) { return 0; } +static int addblitToFrameBuffer(lua_State *L) { + FBInfo *fb = (FBInfo*) luaL_checkudata(L, 1, "einkfb"); + BlitBuffer *bb = (BlitBuffer*) luaL_checkudata(L, 2, "blitbuffer"); + int xdest = luaL_checkint(L, 3); + int ydest = luaL_checkint(L, 4); + int xoffs = luaL_checkint(L, 5); + int yoffs = luaL_checkint(L, 6); + int w = luaL_checkint(L, 7); + int h = luaL_checkint(L, 8); + int x, y; + + // check bounds + if(yoffs >= bb->h) { + return 0; + } else if(yoffs + h > bb->h) { + h = bb->h - yoffs; + } + if(ydest >= fb->vinfo.yres) { + return 0; + } else if(ydest + h > fb->vinfo.yres) { + h = fb->vinfo.yres - ydest; + } + if(xoffs >= bb->w) { + return 0; + } else if(xoffs + w > bb->w) { + w = bb->w - xoffs; + } + if(xdest >= fb->vinfo.xres) { + return 0; + } else if(xdest + w > fb->vinfo.xres) { + w = fb->vinfo.xres - xdest; + } + + uint8_t *fbptr; + uint8_t *bbptr; + + if(xdest & 1) { + /* this will render the leftmost column */ + fbptr = (uint8_t*)(fb->data + + ydest * fb->finfo.line_length + + xdest / 2); + bbptr = (uint8_t*)(bb->data + + yoffs * bb->w / 2 + + xoffs / 2 ); + if(xoffs & 1) { + for(y = 0; y < h; y++) { + uint8_t v = (*fbptr & 0x0F) + (*bbptr & 0x0F); + *fbptr = (*fbptr & 0xF0) | (v < 0x0F ? v : 0x0F); + fbptr += fb->finfo.line_length; + bbptr += (bb->w / 2); + } + } else { + for(y = 0; y < h; y++) { + uint8_t v = (*fbptr & 0x0F) + (*bbptr >> 4); + *fbptr = (*fbptr & 0xF0) | (v < 0x0F ? v : 0x0F); + fbptr += fb->finfo.line_length; + bbptr += (bb->w / 2); + } + } + xdest++; + xoffs++; + w--; + } + + fbptr = (uint8_t*)(fb->data + + ydest * fb->finfo.line_length + + xdest / 2); + bbptr = (uint8_t*)(bb->data + + yoffs * bb->w / 2 + + xoffs / 2 ); + + if(xoffs & 1) { + for(y = 0; y < h; y++) { + for(x = 0; x < (w / 2); x++) { + uint16_t v1 = (fbptr[x] & 0xF0) + ((bbptr[x] & 0x0F) << 4); + uint8_t v2 = (fbptr[x] & 0x0F) + (bbptr[x+1] >> 4); + fbptr[x] = (v1 < 0xF0 ? v1 : 0xF0) | (v2 < 0x0F ? v2 : 0x0F); + } + if(w & 1) { + uint16_t v1 = (fbptr[x] & 0xF0) + ((bbptr[x] & 0x0F) << 4); + fbptr[x] = (fbptr[x] & 0x0F) | (v1 < 0xF0 ? v1 : 0xF0); + } + fbptr += fb->finfo.line_length; + bbptr += (bb->w / 2); + } + } else { + for(y = 0; y < h; y++) { + for(x = 0; x < (w / 2); x++) { + uint16_t v1 = (fbptr[x] & 0xF0) + (bbptr[x] & 0xF0); + uint8_t v2 = (fbptr[x] & 0x0F) + (bbptr[x] & 0x0F); + fbptr[x] = (v1 < 0xF0 ? v1 : 0xF0) | (v2 < 0x0F ? v2 : 0x0F); + } + if(w & 1) { + uint16_t v1 = (fbptr[x] & 0xF0) + (bbptr[x] & 0xF0); + fbptr[x] = (fbptr[x] & 0x0F) | (v1 < 0xF0 ? v1 : 0xF0); + } + fbptr += fb->finfo.line_length; + bbptr += (bb->w / 2); + } + } + return 0; +} + static int paintRect(lua_State *L) { FBInfo *fb = (FBInfo*) luaL_checkudata(L, 1, "einkfb"); } @@ -286,6 +388,7 @@ static const struct luaL_reg einkfb_meth[] = { {"refresh", einkUpdate}, {"getSize", getSize}, {"blitFrom", blitToFrameBuffer}, + {"addblitFrom", addblitToFrameBuffer}, {"blitFullFrom", blitFullToFrameBuffer}, {NULL, NULL} }; diff --git a/ft.c b/ft.c index 952d2bb71..6943498bd 100644 --- a/ft.c +++ b/ft.c @@ -138,6 +138,29 @@ static int renderGlyph(lua_State *L) { return 1; } +static int hasKerning(lua_State *L) { + FT_Face *face = (FT_Face*) luaL_checkudata(L, 1, "ft_face"); + if(FT_HAS_KERNING((*face))) { + lua_pushinteger(L, 1); + } else { + lua_pushinteger(L, 0); + } + return 1; +} + +static int getKerning(lua_State *L) { + FT_Face *face = (FT_Face*) luaL_checkudata(L, 1, "ft_face"); + int left = FT_Get_Char_Index(*face, luaL_checkint(L, 2)); + int right = FT_Get_Char_Index(*face, luaL_checkint(L, 3)); + FT_Vector kerning; + FT_Error error = FT_Get_Kerning(*face, left, right, FT_KERNING_DEFAULT, &kerning); + if(error) { + return luaL_error(L, "freetype error when getting kerning (l=%d, r=%d)", left, right); + } + lua_pushinteger(L, kerning.x >> 6); + return 1; +} + static int doneFace(lua_State *L) { FT_Face *face = (FT_Face*) luaL_checkudata(L, 1, "ft_face"); if(*face != NULL) { @@ -152,6 +175,8 @@ static int doneFace(lua_State *L) { static const struct luaL_reg ft_face_meth[] = { {"renderGlyph", renderGlyph}, + {"hasKerning", hasKerning}, + {"getKerning", getKerning}, {"done", doneFace}, {"__gc", doneFace}, {NULL, NULL} diff --git a/rendertext.lua b/rendertext.lua index 13514fc9a..9a5b62a3f 100644 --- a/rendertext.lua +++ b/rendertext.lua @@ -23,10 +23,8 @@ end function getglyph(face, facehash, charcode) local hash = glyphcachehash(facehash, charcode) if glyphcache[hash] == nil then - print("render glyph") local glyph = face:renderGlyph(charcode) local size = glyph.bb:getWidth() * glyph.bb:getHeight() / 2 + 32 - print("cache claim") glyphcacheclaim(size); glyphcache[hash] = { age = glyphcache_max_age, @@ -45,13 +43,23 @@ function clearglyphcache() glyphcache = {} end -function renderUtf8Text(x, y, face, facehash, text) +function renderUtf8Text(x, y, face, facehash, text, kerning) + -- may still need more adaptive pen placement when kerning, + -- see: http://freetype.org/freetype2/docs/glyphs/glyphs-4.html local pen_x = 0 + local prevcharcode = 0 for uchar in string.gfind(text, "([%z\1-\127\194-\244][\128-\191]*)") do - local glyph = getglyph(face, facehash, util.utf8charcode(uchar)) - fb:blitFrom(glyph.bb, x + pen_x + glyph.l, y - glyph.t, 0, 0, glyph.bb:getWidth(), glyph.bb:getHeight()) - print(uchar, x + pen_x + glyph.l, y - glyph.t, glyph.bb:getWidth(), glyph.bb:getHeight()) + local charcode = util.utf8charcode(uchar) + local glyph = getglyph(face, facehash, charcode) + if kerning and prevcharcode then + local kern = face:getKerning(prevcharcode, charcode) + pen_x = pen_x + kern + fb:addblitFrom(glyph.bb, x + pen_x + glyph.l, y - glyph.t, 0, 0, glyph.bb:getWidth(), glyph.bb:getHeight()) + else + fb:blitFrom(glyph.bb, x + pen_x + glyph.l, y - glyph.t, 0, 0, glyph.bb:getWidth(), glyph.bb:getHeight()) + end pen_x = pen_x + glyph.ax + prevcharcode = charcode end end diff --git a/rendertext_example.lua b/rendertext_example.lua index 2d7733844..b8b7d1c5e 100644 --- a/rendertext_example.lua +++ b/rendertext_example.lua @@ -5,10 +5,16 @@ width, height = fb:getSize() print("open") -face = freetype.newBuiltinFace("Helvetica", 64) +-- face = freetype.newBuiltinFace("Helvetica", 64) +face = freetype.newFace("test.ttf", 64) print("got face") -renderUtf8Text(100,100,face,"h","Hello World! äöü") +if face:hasKerning() then + print("has kerning") +end + +renderUtf8Text(100,100,face,"h","AV T.T: gxyt!",true) +renderUtf8Text(100,200,face,"h","AV T.T: gxyt!",false) fb:refresh()