From 9edd6d4ee912273690b2600e8c52183dfa058eb9 Mon Sep 17 00:00:00 2001 From: Thomas Martitz Date: Sat, 5 Mar 2011 18:36:51 +0000 Subject: [PATCH] Anti-Aliased Fonts support. This enables Rockbox to render anti-aliased fonts using an alpha blending method. The input font bitmaps are 4bit, i.e. 4x larger, but the metadata size stays the same. A tool, convttf, for converting ttf fonts directly to the Rockbox fnt format is provided. It has a useful help output, but the parameter that works best is -c1 or -c2 (2 for larger font sizes). Flyspray: FS#8961 Author: Initial work by Jonas Hurrelmann, further work by Fred Bauer, Andrew Mahone, Teruaki Kawashima and myself. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@29523 a1c6a512-1295-4272-9138-f99709370657 --- apps/plugins/mpegplayer/mpegplayer.c | 174 +++- apps/plugins/rockpaint.c | 182 +++- firmware/drivers/lcd-16bit.c | 252 +++++ firmware/drivers/lcd-bitmap-common.c | 10 +- firmware/export/font.h | 3 +- firmware/font.c | 31 +- tools/Makefile | 9 +- tools/convbdf.c | 15 +- tools/convttf.c | 1283 ++++++++++++++++++++++++++ tools/root.make | 2 +- tools/tools.make | 5 + 11 files changed, 1939 insertions(+), 27 deletions(-) create mode 100644 tools/convttf.c diff --git a/apps/plugins/mpegplayer/mpegplayer.c b/apps/plugins/mpegplayer/mpegplayer.c index 2361ba0fbd..22136d8393 100644 --- a/apps/plugins/mpegplayer/mpegplayer.c +++ b/apps/plugins/mpegplayer/mpegplayer.c @@ -663,6 +663,172 @@ static void draw_oriented_mono_bitmap_part(const unsigned char *src, while (src < src_end); } +/* draw alpha bitmap for anti-alias font */ +#define ALPHA_COLOR_FONT_DEPTH 2 +#define ALPHA_COLOR_LOOKUP_SHIFT (1 << ALPHA_COLOR_FONT_DEPTH) +#define ALPHA_COLOR_LOOKUP_SIZE ((1 << ALPHA_COLOR_LOOKUP_SHIFT) - 1) +#define ALPHA_COLOR_PIXEL_PER_BYTE (8 >> ALPHA_COLOR_FONT_DEPTH) +#define ALPHA_COLOR_PIXEL_PER_WORD (32 >> ALPHA_COLOR_FONT_DEPTH) +#ifdef CPU_ARM +#define BLEND_INIT do {} while (0) +#define BLEND_START(acc, color, alpha) \ + asm volatile("mul %0, %1, %2" : "=&r" (acc) : "r" (color), "r" (alpha)) +#define BLEND_CONT(acc, color, alpha) \ + asm volatile("mla %0, %1, %2, %0" : "+&r" (acc) : "r" (color), "r" (alpha)) +#define BLEND_OUT(acc) do {} while (0) +#elif defined(CPU_COLDFIRE) +#define ALPHA_BITMAP_READ_WORDS +#define BLEND_INIT coldfire_set_macsr(EMAC_UNSIGNED) +#define BLEND_START(acc, color, alpha) \ + asm volatile("mac.l %0, %1, %%acc0" :: "%d" (color), "d" (alpha)) +#define BLEND_CONT BLEND_START +#define BLEND_OUT(acc) asm volatile("movclr.l %%acc0, %0" : "=d" (acc)) +#else +#define BLEND_INIT do {} while (0) +#define BLEND_START(acc, color, alpha) ((acc) = (color) * (alpha)) +#define BLEND_CONT(acc, color, alpha) ((acc) += (color) * (alpha)) +#define BLEND_OUT(acc) do {} while (0) +#endif + +/* Blend the given two colors */ +static inline unsigned blend_two_colors(unsigned c1, unsigned c2, unsigned a) +{ + a += a >> (ALPHA_COLOR_LOOKUP_SHIFT - 1); +#if (LCD_PIXELFORMAT == RGB565SWAPPED) + c1 = swap16(c1); + c2 = swap16(c2); +#endif + unsigned c1l = (c1 | (c1 << 16)) & 0x07e0f81f; + unsigned c2l = (c2 | (c2 << 16)) & 0x07e0f81f; + unsigned p; + BLEND_START(p, c1l, a); + BLEND_CONT(p, c2l, ALPHA_COLOR_LOOKUP_SIZE + 1 - a); + BLEND_OUT(p); + p = (p >> ALPHA_COLOR_LOOKUP_SHIFT) & 0x07e0f81f; + p |= (p >> 16); +#if (LCD_PIXELFORMAT == RGB565SWAPPED) + return swap16(p); +#else + return p; +#endif +} + +static void draw_oriented_alpha_bitmap_part(const unsigned char *src, + int src_x, int src_y, + int stride, int x, int y, + int width, int height) +{ + fb_data *dst, *dst_start; + unsigned fg_pattern, bg_pattern; + + if (x + width > SCREEN_WIDTH) + width = SCREEN_WIDTH - x; /* Clip right */ + if (x < 0) + width += x, x = 0; /* Clip left */ + if (width <= 0) + return; /* nothing left to do */ + + if (y + height > SCREEN_HEIGHT) + height = SCREEN_HEIGHT - y; /* Clip bottom */ + if (y < 0) + height += y, y = 0; /* Clip top */ + if (height <= 0) + return; /* nothing left to do */ + + /* initialize blending */ + BLEND_INIT; + + fg_pattern = rb->lcd_get_foreground(); + bg_pattern = rb->lcd_get_background(); + + dst_start = rb->lcd_framebuffer + (LCD_WIDTH - y - 1) + x*LCD_WIDTH; + int col, row = height; + unsigned data, pixels; + unsigned skip_end = (stride - width); + unsigned skip_start = src_y * stride + src_x; + +#ifdef ALPHA_BITMAP_READ_WORDS + uint32_t *src_w = (uint32_t *)((uintptr_t)src & ~3); + skip_start += ALPHA_COLOR_PIXEL_PER_BYTE * ((uintptr_t)src & 3); + src_w += skip_start / ALPHA_COLOR_PIXEL_PER_WORD; + data = letoh32(*src_w++); +#else + src += skip_start / ALPHA_COLOR_PIXEL_PER_BYTE; + data = *src; +#endif + pixels = skip_start % ALPHA_COLOR_PIXEL_PER_WORD; + data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT; +#ifdef ALPHA_BITMAP_READ_WORDS + pixels = 8 - pixels; +#endif + + do + { + col = width; + dst = dst_start--; +#ifdef ALPHA_BITMAP_READ_WORDS +#define UPDATE_SRC_ALPHA do { \ + if (--pixels) \ + data >>= ALPHA_COLOR_LOOKUP_SHIFT; \ + else \ + { \ + data = letoh32(*src_w++); \ + pixels = ALPHA_COLOR_PIXEL_PER_WORD; \ + } \ + } while (0) +#elif ALPHA_COLOR_PIXEL_PER_BYTE == 2 +#define UPDATE_SRC_ALPHA do { \ + if (pixels ^= 1) \ + data >>= ALPHA_COLOR_LOOKUP_SHIFT; \ + else \ + data = *(++src); \ + } while (0) +#else +#define UPDATE_SRC_ALPHA do { \ + if (pixels = (++pixels % ALPHA_COLOR_PIXEL_PER_BYTE)) \ + data >>= ALPHA_COLOR_LOOKUP_SHIFT; \ + else \ + data = *(++src); \ + } while (0) +#endif + do + { + *dst=blend_two_colors(*dst, fg_pattern, + data & ALPHA_COLOR_LOOKUP_SIZE ); + dst += LCD_WIDTH; + UPDATE_SRC_ALPHA; + } + while (--col); +#ifdef ALPHA_BITMAP_READ_WORDS + if (skip_end < pixels) + { + pixels -= skip_end; + data >>= skip_end * ALPHA_COLOR_LOOKUP_SHIFT; + } else { + pixels = skip_end - pixels; + src_w += pixels / ALPHA_COLOR_PIXEL_PER_WORD; + pixels %= ALPHA_COLOR_PIXEL_PER_WORD; + data = letoh32(*src_w++); + data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT; + pixels = 8 - pixels; + } +#else + if (skip_end) + { + pixels += skip_end; + if (pixels >= ALPHA_COLOR_PIXEL_PER_BYTE) + { + src += pixels / ALPHA_COLOR_PIXEL_PER_BYTE; + pixels %= ALPHA_COLOR_PIXEL_PER_BYTE; + data = *src; + data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT; + } else + data >>= skip_end * ALPHA_COLOR_LOOKUP_SHIFT; + } +#endif + } while (--row); +} + static void draw_putsxy_oriented(int x, int y, const char *str) { unsigned short ch; @@ -690,8 +856,12 @@ static void draw_putsxy_oriented(int x, int y, const char *str) bits = rb->font_get_bits(pf, ch); - draw_oriented_mono_bitmap_part(bits, ofs, 0, width, x, y, - width - ofs, pf->height); + if (pf->depth) + draw_oriented_alpha_bitmap_part(bits, ofs, 0, width, x, y, + width - ofs, pf->height); + else + draw_oriented_mono_bitmap_part(bits, ofs, 0, width, x, y, + width - ofs, pf->height); x += width - ofs; ofs = 0; diff --git a/apps/plugins/rockpaint.c b/apps/plugins/rockpaint.c index 0acbe41aaf..1fef0e9f5f 100644 --- a/apps/plugins/rockpaint.c +++ b/apps/plugins/rockpaint.c @@ -562,6 +562,180 @@ static void buffer_mono_bitmap_part( } while( src < src_end ); } +/* draw alpha bitmap for anti-alias font */ +#define ALPHA_COLOR_FONT_DEPTH 2 +#define ALPHA_COLOR_LOOKUP_SHIFT (1 << ALPHA_COLOR_FONT_DEPTH) +#define ALPHA_COLOR_LOOKUP_SIZE ((1 << ALPHA_COLOR_LOOKUP_SHIFT) - 1) +#define ALPHA_COLOR_PIXEL_PER_BYTE (8 >> ALPHA_COLOR_FONT_DEPTH) +#define ALPHA_COLOR_PIXEL_PER_WORD (32 >> ALPHA_COLOR_FONT_DEPTH) +#ifdef CPU_ARM +#define BLEND_INIT do {} while (0) +#define BLEND_START(acc, color, alpha) \ + asm volatile("mul %0, %1, %2" : "=&r" (acc) : "r" (color), "r" (alpha)) +#define BLEND_CONT(acc, color, alpha) \ + asm volatile("mla %0, %1, %2, %0" : "+&r" (acc) : "r" (color), "r" (alpha)) +#define BLEND_OUT(acc) do {} while (0) +#elif defined(CPU_COLDFIRE) +#define ALPHA_BITMAP_READ_WORDS +#define BLEND_INIT coldfire_set_macsr(EMAC_UNSIGNED) +#define BLEND_START(acc, color, alpha) \ + asm volatile("mac.l %0, %1, %%acc0" :: "%d" (color), "d" (alpha)) +#define BLEND_CONT BLEND_START +#define BLEND_OUT(acc) asm volatile("movclr.l %%acc0, %0" : "=d" (acc)) +#else +#define BLEND_INIT do {} while (0) +#define BLEND_START(acc, color, alpha) ((acc) = (color) * (alpha)) +#define BLEND_CONT(acc, color, alpha) ((acc) += (color) * (alpha)) +#define BLEND_OUT(acc) do {} while (0) +#endif + +/* Blend the given two colors */ +static inline unsigned blend_two_colors(unsigned c1, unsigned c2, unsigned a) +{ + a += a >> (ALPHA_COLOR_LOOKUP_SHIFT - 1); +#if (LCD_PIXELFORMAT == RGB565SWAPPED) + c1 = swap16(c1); + c2 = swap16(c2); +#endif + unsigned c1l = (c1 | (c1 << 16)) & 0x07e0f81f; + unsigned c2l = (c2 | (c2 << 16)) & 0x07e0f81f; + unsigned p; + BLEND_START(p, c1l, a); + BLEND_CONT(p, c2l, ALPHA_COLOR_LOOKUP_SIZE + 1 - a); + BLEND_OUT(p); + p = (p >> ALPHA_COLOR_LOOKUP_SHIFT) & 0x07e0f81f; + p |= (p >> 16); +#if (LCD_PIXELFORMAT == RGB565SWAPPED) + return swap16(p); +#else + return p; +#endif +} + +static void buffer_alpha_bitmap_part( + fb_data *buf, int buf_width, int buf_height, + const unsigned char *src, int src_x, int src_y, + int stride, int x, int y, int width, int height ) +{ + fb_data *dst; + unsigned fg_pattern = rb->lcd_get_foreground(); + + /* nothing to draw? */ + if ((width <= 0) || (height <= 0) || (x >= buf_width) || + (y >= buf_height) || (x + width <= 0) || (y + height <= 0)) + return; + + /* initialize blending */ + BLEND_INIT; + + /* clipping */ + if (x < 0) + { + width += x; + src_x -= x; + x = 0; + } + if (y < 0) + { + height += y; + src_y -= y; + y = 0; + } + if (x + width > buf_width) + width = buf_width - x; + if (y + height > buf_height) + height = buf_height - y; + + dst = buf + y*buf_width + x; + + int col, row = height; + unsigned data, pixels; + unsigned skip_end = (stride - width); + unsigned skip_start = src_y * stride + src_x; + +#ifdef ALPHA_BITMAP_READ_WORDS + uint32_t *src_w = (uint32_t *)((uintptr_t)src & ~3); + skip_start += ALPHA_COLOR_PIXEL_PER_BYTE * ((uintptr_t)src & 3); + src_w += skip_start / ALPHA_COLOR_PIXEL_PER_WORD; + data = letoh32(*src_w++); +#else + src += skip_start / ALPHA_COLOR_PIXEL_PER_BYTE; + data = *src; +#endif + pixels = skip_start % ALPHA_COLOR_PIXEL_PER_WORD; + data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT; +#ifdef ALPHA_BITMAP_READ_WORDS + pixels = 8 - pixels; +#endif + + do + { + col = width; +#ifdef ALPHA_BITMAP_READ_WORDS +#define UPDATE_SRC_ALPHA do { \ + if (--pixels) \ + data >>= ALPHA_COLOR_LOOKUP_SHIFT; \ + else \ + { \ + data = letoh32(*src_w++); \ + pixels = ALPHA_COLOR_PIXEL_PER_WORD; \ + } \ + } while (0) +#elif ALPHA_COLOR_PIXEL_PER_BYTE == 2 +#define UPDATE_SRC_ALPHA do { \ + if (pixels ^= 1) \ + data >>= ALPHA_COLOR_LOOKUP_SHIFT; \ + else \ + data = *(++src); \ + } while (0) +#else +#define UPDATE_SRC_ALPHA do { \ + if (pixels = (++pixels % ALPHA_COLOR_PIXEL_PER_BYTE)) \ + data >>= ALPHA_COLOR_LOOKUP_SHIFT; \ + else \ + data = *(++src); \ + } while (0) +#endif + + do + { + *dst=blend_two_colors(*dst, fg_pattern, + data & ALPHA_COLOR_LOOKUP_SIZE ); + dst++; + UPDATE_SRC_ALPHA; + } + while (--col); +#ifdef ALPHA_BITMAP_READ_WORDS + if (skip_end < pixels) + { + pixels -= skip_end; + data >>= skip_end * ALPHA_COLOR_LOOKUP_SHIFT; + } else { + pixels = skip_end - pixels; + src_w += pixels / ALPHA_COLOR_PIXEL_PER_WORD; + pixels %= ALPHA_COLOR_PIXEL_PER_WORD; + data = letoh32(*src_w++); + data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT; + pixels = 8 - pixels; + } +#else + if (skip_end) + { + pixels += skip_end; + if (pixels >= ALPHA_COLOR_PIXEL_PER_BYTE) + { + src += pixels / ALPHA_COLOR_PIXEL_PER_BYTE; + pixels %= ALPHA_COLOR_PIXEL_PER_BYTE; + data = *src; + data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT; + } else + data >>= skip_end * ALPHA_COLOR_LOOKUP_SHIFT; + } +#endif + dst += LCD_WIDTH - width; + } while (--row); +} + static void buffer_putsxyofs( fb_data *buf, int buf_width, int buf_height, int x, int y, int ofs, const unsigned char *str ) { @@ -589,8 +763,12 @@ static void buffer_putsxyofs( fb_data *buf, int buf_width, int buf_height, bits = rb->font_get_bits( pf, ch ); - buffer_mono_bitmap_part( buf, buf_width, buf_height, bits, ofs, 0, - width, x, y, width - ofs, pf->height); + if (pf->depth) + buffer_alpha_bitmap_part( buf, buf_width, buf_height, bits, ofs, 0, + width, x, y, width - ofs, pf->height); + else + buffer_mono_bitmap_part( buf, buf_width, buf_height, bits, ofs, 0, + width, x, y, width - ofs, pf->height); x += width - ofs; ofs = 0; diff --git a/firmware/drivers/lcd-16bit.c b/firmware/drivers/lcd-16bit.c index be4f21f412..a2eb3e630c 100644 --- a/firmware/drivers/lcd-16bit.c +++ b/firmware/drivers/lcd-16bit.c @@ -901,6 +901,258 @@ void lcd_mono_bitmap(const unsigned char *src, int x, int y, int width, int heig lcd_mono_bitmap_part(src, 0, 0, width, x, y, width, height); } +/* draw alpha bitmap for anti-alias font */ +#define ALPHA_COLOR_FONT_DEPTH 2 +#define ALPHA_COLOR_LOOKUP_SHIFT (1 << ALPHA_COLOR_FONT_DEPTH) +#define ALPHA_COLOR_LOOKUP_SIZE ((1 << ALPHA_COLOR_LOOKUP_SHIFT) - 1) +#define ALPHA_COLOR_PIXEL_PER_BYTE (8 >> ALPHA_COLOR_FONT_DEPTH) +#define ALPHA_COLOR_PIXEL_PER_WORD (32 >> ALPHA_COLOR_FONT_DEPTH) +#ifdef CPU_ARM +#define BLEND_INIT do {} while (0) +#define BLEND_START(acc, color, alpha) \ + asm volatile("mul %0, %1, %2" : "=&r" (acc) : "r" (color), "r" (alpha)) +#define BLEND_CONT(acc, color, alpha) \ + asm volatile("mla %0, %1, %2, %0" : "+&r" (acc) : "r" (color), "r" (alpha)) +#define BLEND_OUT(acc) do {} while (0) +#elif defined(CPU_COLDFIRE) +#define ALPHA_BITMAP_READ_WORDS +#define BLEND_INIT coldfire_set_macsr(EMAC_UNSIGNED) +#define BLEND_START(acc, color, alpha) \ + asm volatile("mac.l %0, %1, %%acc0" :: "%d" (color), "d" (alpha)) +#define BLEND_CONT BLEND_START +#define BLEND_OUT(acc) asm volatile("movclr.l %%acc0, %0" : "=d" (acc)) +#else +#define BLEND_INIT do {} while (0) +#define BLEND_START(acc, color, alpha) ((acc) = (color) * (alpha)) +#define BLEND_CONT(acc, color, alpha) ((acc) += (color) * (alpha)) +#define BLEND_OUT(acc) do {} while (0) +#endif + +/* Blend the given two colors */ +static inline unsigned blend_two_colors(unsigned c1, unsigned c2, unsigned a) +{ + a += a >> (ALPHA_COLOR_LOOKUP_SHIFT - 1); +#if (LCD_PIXELFORMAT == RGB565SWAPPED) + c1 = swap16(c1); + c2 = swap16(c2); +#endif + unsigned c1l = (c1 | (c1 << 16)) & 0x07e0f81f; + unsigned c2l = (c2 | (c2 << 16)) & 0x07e0f81f; + unsigned p; + BLEND_START(p, c1l, a); + BLEND_CONT(p, c2l, ALPHA_COLOR_LOOKUP_SIZE + 1 - a); + BLEND_OUT(p); + p = (p >> ALPHA_COLOR_LOOKUP_SHIFT) & 0x07e0f81f; + p |= (p >> 16); +#if (LCD_PIXELFORMAT == RGB565SWAPPED) + return swap16(p); +#else + return p; +#endif +} + +/* Blend the given color with the value from the alpha_color_lookup table */ +static inline unsigned blend_color(unsigned c, unsigned a) +{ + return blend_two_colors(c, current_vp->fg_pattern, a); +} + +void ICODE_ATTR lcd_alpha_bitmap_part(const unsigned char *src, int src_x, + int src_y, int stride, int x, int y, + int width, int height) +{ + fb_data *dst, *backdrop; + unsigned dmask = 0x00000000; + int drmode = current_vp->drawmode; + /* nothing to draw? */ + if ((width <= 0) || (height <= 0) || (x >= current_vp->width) || + (y >= current_vp->height) || (x + width <= 0) || (y + height <= 0)) + return; + /* initialize blending */ + BLEND_INIT; + + /* clipping */ + if (x < 0) + { + width += x; + src_x -= x; + x = 0; + } + if (y < 0) + { + height += y; + src_y -= y; + y = 0; + } + if (x + width > current_vp->width) + width = current_vp->width - x; + if (y + height > current_vp->height) + height = current_vp->height - y; + + if (drmode & DRMODE_INVERSEVID) + { + dmask = 0xffffffff; + drmode &= DRMODE_SOLID; /* mask out inversevid */ + } + if (drmode == DRMODE_BG) + { + dmask = ~dmask; + } + + dst = LCDADDR(current_vp->x + x, current_vp->y + y); + + int col, row = height; + unsigned data, pixels; + unsigned skip_end = (stride - width); + unsigned skip_start = src_y * stride + src_x; + +#ifdef ALPHA_BITMAP_READ_WORDS + uint32_t *src_w = (uint32_t *)((uintptr_t)src & ~3); + skip_start += ALPHA_COLOR_PIXEL_PER_BYTE * ((uintptr_t)src & 3); + src_w += skip_start / ALPHA_COLOR_PIXEL_PER_WORD; + data = letoh32(*src_w++) ^ dmask; + pixels = skip_start % ALPHA_COLOR_PIXEL_PER_WORD; +#else + src += skip_start / ALPHA_COLOR_PIXEL_PER_BYTE; + data = *src ^ dmask; + pixels = skip_start % ALPHA_COLOR_PIXEL_PER_BYTE; +#endif + data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT; +#ifdef ALPHA_BITMAP_READ_WORDS + pixels = 8 - pixels; +#endif + + do + { + col = width; +#ifdef ALPHA_BITMAP_READ_WORDS +#define UPDATE_SRC_ALPHA do { \ + if (--pixels) \ + data >>= ALPHA_COLOR_LOOKUP_SHIFT; \ + else \ + { \ + data = letoh32(*src_w++) ^ dmask; \ + pixels = ALPHA_COLOR_PIXEL_PER_WORD; \ + } \ + } while (0) +#elif ALPHA_COLOR_PIXEL_PER_BYTE == 2 +#define UPDATE_SRC_ALPHA do { \ + if (pixels ^= 1) \ + data >>= ALPHA_COLOR_LOOKUP_SHIFT; \ + else \ + data = *(++src) ^ dmask; \ + } while (0) +#else +#define UPDATE_SRC_ALPHA do { \ + if (pixels = (++pixels % ALPHA_COLOR_PIXEL_PER_BYTE)) \ + data >>= ALPHA_COLOR_LOOKUP_SHIFT; \ + else \ + data = *(++src) ^ dmask; \ + } while (0) +#endif + /* we don't want to have this in our inner + * loop and the codesize increase is minimal */ + switch (drmode) + { + case DRMODE_COMPLEMENT: + do + { + *dst=blend_two_colors(*dst, ~(*dst), + data & ALPHA_COLOR_LOOKUP_SIZE ); + dst++; + UPDATE_SRC_ALPHA; + } + while (--col); + break; + case DRMODE_BG: + if(lcd_backdrop) + { + backdrop = (fb_data *)((long)dst+lcd_backdrop_offset); + do + { + *dst=blend_two_colors(*dst, *(backdrop++), + data & ALPHA_COLOR_LOOKUP_SIZE ); + dst++; + UPDATE_SRC_ALPHA; + } + while (--col); + } + else + { + do + { + *dst=blend_two_colors(*dst, current_vp->bg_pattern, + data & ALPHA_COLOR_LOOKUP_SIZE ); + dst++; + UPDATE_SRC_ALPHA; + } + while (--col); + } + break; + case DRMODE_FG: + do + { + *dst=blend_color(*dst, data & ALPHA_COLOR_LOOKUP_SIZE ); + dst++; + UPDATE_SRC_ALPHA; + } + while (--col); + break; + case DRMODE_SOLID: + if(lcd_backdrop) + { + backdrop = (fb_data *)((long)dst+lcd_backdrop_offset); + do + { + *(dst++)=blend_color(*(backdrop++), + data & ALPHA_COLOR_LOOKUP_SIZE ); + UPDATE_SRC_ALPHA; + } + while (--col); + } + else + { + do + { + *(dst++)=blend_color(current_vp->bg_pattern, + data & ALPHA_COLOR_LOOKUP_SIZE ); + UPDATE_SRC_ALPHA; + } + while (--col); + } + break; + } +#ifdef ALPHA_BITMAP_READ_WORDS + if (skip_end < pixels) + { + pixels -= skip_end; + data >>= skip_end * ALPHA_COLOR_LOOKUP_SHIFT; + } else { + pixels = skip_end - pixels; + src_w += pixels / ALPHA_COLOR_PIXEL_PER_WORD; + pixels %= ALPHA_COLOR_PIXEL_PER_WORD; + data = letoh32(*src_w++) ^ dmask; + data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT; + pixels = 8 - pixels; + } +#else + if (skip_end) + { + pixels += skip_end; + if (pixels >= ALPHA_COLOR_PIXEL_PER_BYTE) + { + src += pixels / ALPHA_COLOR_PIXEL_PER_BYTE; + pixels %= ALPHA_COLOR_PIXEL_PER_BYTE; + data = *src ^ dmask; + data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT; + } else + data >>= skip_end * ALPHA_COLOR_LOOKUP_SHIFT; + } +#endif + dst += LCD_WIDTH - width; + } while (--row); +} + /* Draw a partial native bitmap */ void ICODE_ATTR lcd_bitmap_part(const fb_data *src, int src_x, int src_y, int stride, int x, int y, int width, diff --git a/firmware/drivers/lcd-bitmap-common.c b/firmware/drivers/lcd-bitmap-common.c index 364fb3c527..fc84fdd6a1 100644 --- a/firmware/drivers/lcd-bitmap-common.c +++ b/firmware/drivers/lcd-bitmap-common.c @@ -203,9 +203,15 @@ static void LCDFN(putsxyofs)(int x, int y, int ofs, const unsigned char *str) } bits = font_get_bits(pf, *ucs); - LCDFN(mono_bitmap_part)(bits, ofs, 0, width, x + base_ofs, y, - width - ofs, pf->height); +#if defined(MAIN_LCD) && defined(HAVE_LCD_COLOR) + if (pf->depth) + lcd_alpha_bitmap_part(bits, ofs, 0, width, x + base_ofs, y, + width - ofs, pf->height); + else +#endif + LCDFN(mono_bitmap_part)(bits, ofs, 0, width, x + base_ofs, + y, width - ofs, pf->height); if (is_diac) { current_vp->drawmode = drawmode; diff --git a/firmware/export/font.h b/firmware/export/font.h index 0a75768fa5..a85f95e6ae 100644 --- a/firmware/export/font.h +++ b/firmware/export/font.h @@ -68,7 +68,7 @@ enum { * USHORT maxwidth 2 font max width in pixels * USHORT height 2 font height in pixels * USHORT ascent 2 font ascent (baseline) in pixels - * USHORT pad 2 unused, pad to 32-bit boundary + * USHORT depth 2 depth of the font, 0=1bit and 1=4bit * ULONG firstchar 4 first character code in font * ULONG defaultchar 4 default character code in font * ULONG size 4 # characters in font @@ -92,6 +92,7 @@ struct font { int ascent; /* ascent (baseline) height*/ int firstchar; /* first character in bitmap*/ int size; /* font size in glyphs*/ + int depth; /* depth of the font, 0=1bit and 1=4bit */ const unsigned char *bits; /* 8-bit column bitmap data*/ const void *offset; /* offsets into bitmap data, uint16_t if bits_size < 0xFFDB else uint32_t*/ diff --git a/firmware/font.c b/firmware/font.c index 45ddef3afe..0f9f453ae2 100644 --- a/firmware/font.c +++ b/firmware/font.c @@ -89,7 +89,7 @@ static struct font* sysfonts[MAXFONTS] = { &sysfont, &font_ui, NULL}; /* Font cache structures */ -static void cache_create(struct font* pf, int maxwidth, int height); +static void cache_create(struct font* pf); static void glyph_cache_load(struct font* pf); /* End Font cache structures */ @@ -132,6 +132,13 @@ static int32_t readlong(struct font *pf) return l; } +static int glyph_bytes( struct font *pf, int width ) +{ + return pf->depth ? + (pf->height * width + 1) / 2: + width * ((pf->height + 7) / 8); +} + void font_reset(struct font *pf) { unsigned char* buffer = NULL; @@ -168,7 +175,7 @@ static struct font* font_load_header(struct font *pf) pf->maxwidth = readshort(pf); pf->height = readshort(pf); pf->ascent = readshort(pf); - pf->buffer_position += 2; /* Skip padding */ + pf->depth = readshort(pf); pf->firstchar = readlong(pf); pf->defaultchar = readlong(pf); pf->size = readlong(pf); @@ -308,7 +315,7 @@ static struct font* font_load_cached(struct font* pf) pf->buffer_position = oldfileptr; /* Create the cache */ - cache_create(pf, pf->maxwidth, pf->height); + cache_create(pf); return pf; } @@ -436,7 +443,8 @@ int font_load(struct font* pf, const char *path) { /* currently, font loading replaces earlier font allocation*/ buffer = (unsigned char *)(((intptr_t)main_buf + 3) & ~3); - buffer_size = MAX_FONT_SIZE; + /* make sure above doesn't exceed */ + buffer_size = MAX_FONT_SIZE-3; } else { @@ -516,24 +524,23 @@ load_cache_entry(struct font_cache_entry* p, void* callback_data) } else { - bitmap_offset = ((pf->height + 7) / 8) * p->width * char_code; + bitmap_offset = char_code * glyph_bytes(pf, p->width); } int32_t file_offset = FONT_HEADER_SIZE + bitmap_offset; lseek(pf->fd, file_offset, SEEK_SET); - - int src_bytes = p->width * ((pf->height + 7) / 8); + int src_bytes = glyph_bytes(pf, p->width); read(pf->fd, p->bitmap, src_bytes); } /* * Converts cbuf into a font cache */ -static void cache_create(struct font* pf, int maxwidth, int height) +static void cache_create(struct font* pf) { /* maximum size of rotated bitmap */ - int bitmap_size = maxwidth * ((height + 7) / 8); - + int bitmap_size = glyph_bytes( pf, pf->maxwidth); + /* Initialise cache */ font_cache_create(&pf->cache, pf->buffer_start, pf->buffer_size, bitmap_size); } @@ -578,7 +585,7 @@ const unsigned char* font_get_bits(struct font* pf, unsigned short char_code) bits += ((uint32_t*)(pf->offset))[char_code]; } else - bits += ((pf->height + 7) / 8) * pf->maxwidth * char_code; + bits += char_code * glyph_bytes(pf, pf->maxwidth); } return bits; @@ -655,7 +662,7 @@ int font_glyphs_to_bufsize(const char *path, int glyphs) bufsize = LRU_SLOT_OVERHEAD + sizeof(struct font_cache_entry) + sizeof( unsigned short); - bufsize += f.maxwidth * ((f.height + 7) / 8); + bufsize += glyph_bytes(&f, f.maxwidth); bufsize *= glyphs; if ( bufsize < FONT_HEADER_SIZE ) bufsize = FONT_HEADER_SIZE; diff --git a/tools/Makefile b/tools/Makefile index 0a69dde5da..c33d152c41 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -13,10 +13,10 @@ LDFLAGS := -g CLEANALL := scramble descramble iriver sh2d bmp2rb rdf2binary convbdf \ generate_rocklatin mkboot ipod_fw codepages uclpack mi4 gigabeat lngdump \ - telechips gigabeats creative hmac-sha1 rbspeexenc mkzenboot mk500boot + telechips gigabeats creative hmac-sha1 rbspeexenc mkzenboot mk500boot convttf all: scramble descramble sh2d rdf2binary mkboot mkzenboot convbdf codepages \ - uclpack rbspeexenc voicefont mk500boot + uclpack rbspeexenc voicefont mk500boot convttf scramble: scramble.o iriver.o mi4.o gigabeat.o gigabeats.o telechips.o \ iaudio_bl_flash.o creative.o hmac-sha1.o @@ -97,6 +97,11 @@ voicefont: voicefont.c usb_benchmark: usb_benchmark.c $(SILENT)$(CC) $(CFLAGS) -lusb $+ -o $@ +convttf: convttf.c + $(call PRINTS,CC $(@F)) + $(SILENT)$(CC) $(CFLAGS) -lm -std=c99 -O2 -Wall -g $+ -o $@ \ + `freetype-config --libs` `freetype-config --cflags` + clean: @echo "Cleaning tools" $(SILENT)rm -f $(CLEANALL) $(shell for f in $(CLEANALL) ; do \ diff --git a/tools/convbdf.c b/tools/convbdf.c index 5a563220fd..671312e66b 100644 --- a/tools/convbdf.c +++ b/tools/convbdf.c @@ -46,6 +46,7 @@ struct font { int ascent; /* ascent (baseline) height */ int firstchar; /* first character in bitmap */ int size; /* font size in glyphs ('holes' included) */ + int depth; /* depth of the font, 0=1bit 1=4bit */ bitmap_t* bits; /* 16-bit right-padded bitmap data */ int* offset; /* offsets into bitmap data */ unsigned char* width; /* character widths or NULL if fixed */ @@ -1220,6 +1221,7 @@ int gen_c_source(struct font* pf, char *path) " size: %d\n" " ascent: %d\n" " descent: %d\n" + " depth: %d\n" " first char: %d (0x%02x)\n" " last char: %d (0x%02x)\n" " default char: %d (0x%02x)\n" @@ -1245,7 +1247,7 @@ int gen_c_source(struct font* pf, char *path) pf->facename? pf->facename: "", pf->maxwidth, pf->height, pf->size, - pf->ascent, pf->descent, + pf->ascent, pf->descent, pf->depth, pf->firstchar, pf->firstchar, pf->firstchar+pf->size-1, pf->firstchar+pf->size-1, pf->defaultchar, pf->defaultchar, @@ -1392,6 +1394,7 @@ int gen_c_source(struct font* pf, char *path) " %d, /* ascent */\n" " %d, /* firstchar */\n" " %d, /* size */\n" + " %d, /* depth */\n" " _font_bits, /* bits */\n" " %s /* offset */\n" " %s\n" @@ -1411,7 +1414,7 @@ int gen_c_source(struct font* pf, char *path) pf->maxwidth, pf->height, pf->ascent, pf->firstchar, - pf->size, + pf->size, 0, obuf, buf, pf->defaultchar, @@ -1436,7 +1439,8 @@ int gen_h_header(struct font* pf, char *path) "#define SYSFONT_WIDTH %d\n" "#define SYSFONT_HEIGHT %d\n" "#define SYSFONT_SIZE %d\n" - "#define SYSFONT_ASCENT %d\n"; + "#define SYSFONT_ASCENT %d\n" + "#define SYSFONT_DEPTH %d\n"; char *hdr2 = "#define SYSFONT_DESCENT %d\n" "#define SYSFONT_FIRST_CHAR %d\n" @@ -1463,7 +1467,8 @@ int gen_h_header(struct font* pf, char *path) pf->maxwidth, pf->height, pf->size, - pf->ascent); + pf->ascent, + pf->depth); fprintf(ofp, hdr2, pf->descent, @@ -1546,7 +1551,7 @@ int gen_fnt_file(struct font* pf, char *path) writeshort(ofp, pf->maxwidth); writeshort(ofp, pf->height); writeshort(ofp, pf->ascent); - writeshort(ofp, 0); + writeshort(ofp, 0); /* depth = 0 for bdffonts */ writeint(ofp, pf->firstchar); writeint(ofp, pf->defaultchar); writeint(ofp, pf->size); diff --git a/tools/convttf.c b/tools/convttf.c new file mode 100644 index 0000000000..ca9e4fbb4d --- /dev/null +++ b/tools/convttf.c @@ -0,0 +1,1283 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 Jonas Hurrelmann + * + * A command-line tool to convert ttf file to bitmap fonts + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include +#include FT_FREETYPE_H +#include FT_GLYPH_H + +#include +#include +#ifdef WIN32 +#include +#else +#include +#include +#endif +#include FT_SFNT_NAMES_H +#include FT_TRUETYPE_TABLES_H + +#include +/* + * Set the default values used to generate a BDF font. + */ +#ifndef DEFAULT_PLATFORM_ID +#define DEFAULT_PLATFORM_ID 3 +#endif + +#ifndef DEFAULT_ENCODING_ID +#define DEFAULT_ENCODING_ID 1 +#endif + +#define ABS(x) (((x) < 0) ? -(x) : (x)) + +#define VERSION "RB12" +/* + * nameID macros for getting strings from the OT font. + */ +enum { + BDFOTF_COPYRIGHT_STRING = 0, + BDFOTF_FAMILY_STRING, + BDFOTF_SUBFAMILY_STRING, + BDFOTF_UNIQUEID_STRING, + BDFOTF_FULLNAME_STRING, + BDFOTF_VENDOR_STRING, + BDFOTF_POSTSCRIPT_STRING, + BDFOTF_TRADEMARK_STRING, +}; +/* + * String names for the string indexes. Used for error messages. + */ +static char *string_names[] = { + "\"Copyright\"", + "\"Family\"", + "\"SubFamily\"", + "\"Unique ID\"", + "\"Full Name\"", + "\"Vendor\"", + "\"Postscript Name\"", + "\"Trademark\"" +}; + + +/* + * The default platform and encoding ID's. + */ +static int pid = DEFAULT_PLATFORM_ID; +static int eid = DEFAULT_ENCODING_ID; + + +/* + * A flag indicating if a CMap was found or not. + */ +static FT_UShort nocmap; + +int pct = 0; /* display ttc table if it is not zero. */ +unsigned long max_char = 65535; +int pixel_size = 15; +unsigned long start_char = 0; +unsigned long limit_char; +unsigned long firstchar = 0; +unsigned long lastchar; +FT_Long ttc_index = -1; +int flg_all_ttc = 0; +short antialias = 1; /* smooth fonts with gray levels */ +int oflag = 0; +char outfile[1024]; +float between_chr = 0.0f; +float between_row = 0.0f; +int hv_resolution = 60; +int dump_glyphs = 0; +int trimming = 0; +int trim_dp = 0; /* trim descent percent */ +int trim_da = 0; /* trim descnet actual */ +int trim_ap = 0; /* trim ascent precent */ +int trim_aa = 0; /* trim ascent actual */ + +struct font_header_struct { + char header[4]; /* magic number and version bytes */ + unsigned short maxwidth; /* max width in pixels */ + unsigned short height; /* height in pixels */ + unsigned short ascent; /* ascent (baseline) height */ + unsigned short depth; /* depth 0=1-bit, 1=4-bit */ + unsigned long firstchar; /* first character in font */ + unsigned long defaultchar; /* default character in font */ + unsigned long size; /* # characters in font */ + unsigned long nbits; /* # bytes imagebits data in file */ /* = bits_size */ + + FT_Long noffset; /* # longs offset data in file */ + FT_Long nwidth; /* # bytes width data in file */ +}; + +struct font_struct { + struct font_header_struct header; + unsigned char *chars_data; + unsigned short *offset; + FT_Long *offset_long; + unsigned char *width; +}; + +struct ttc_table{ + FT_Long ttc_count; + char **ttf_name; +}; + +/* exit the program with given message */ +static void +panic( const char* message) +{ + fprintf( stderr, "%s\n", message ); + exit( 1 ); +} + +static void +arg_panic( const char* message, const char* arg ) +{ + fprintf( stderr, "%s: %s\n", message, arg ); + exit( 1 ); +} + +static int writebyte(FILE *fp, unsigned char c) +{ + return putc(c, fp) != EOF; +} + +static int writeshort(FILE *fp, unsigned short s) +{ + putc(s, fp); + return putc(s>>8, fp) != EOF; +} + +static int writeint(FILE *fp, unsigned int l) +{ + putc(l, fp); + putc(l>>8, fp); + putc(l>>16, fp); + return putc(l>>24, fp) != EOF; +} + +static int writestr(FILE *fp, char *str, int count) +{ + return (int)fwrite(str, 1, count, fp) == count; +} + +/* print usage information */ +void usage(void) +{ + char help[] = { + "Usage: convttf [options] [input-files]\n" + " convttf [options] [-o output-file] [single-input-file]\n\n" + " Default output-file : -.fnt.\n" + " When '-ta' or '-tc' is specified in command line,\n " + " default output-file is: \n" + " -.fnt.\n" + "Options:\n" + " -s N Start output at character encodings >= N\n" + " -l N Limit output to character encodings <= N\n" + " -p N Font size N in pixel (default N=15)\n" + " -c N Character separation in pixel.Insert space between lines.\n" + " -x Trim glyphs horizontally of nearly empty space\n" + " (to improve spacing on V's W's, etc.)\n" + " -X Set the horizontal and vertical resolution (default: 60)\n" + " -TA N Trim vertical ascent (N percent)\n" + " -TD N Trim vertical descent (N percent)\n" + " -Ta N Trim vertical ascent (N pixels)\n" + " -Td N Trim vertical descent (N pixels)\n" + " -r N Row separation in pixel.Insert space between characters\n" + " -d Debug: print converted glyph images\n" + " -tt Display the True Type Collection tables available in the font\n" + " -t N Index of true type collection. It must be start from 0.(default N=0).\n" + " -ta Convert all fonts in ttc (ignores outfile option)\n" + }; + fprintf(stderr, "%s", help); + exit( 1 ); +} + +/* remove directory prefix and file suffix from full path*/ +char *basename(char *path) +{ + char *p, *b; + static char base[256]; + + /* remove prepended path and extension*/ + b = path; + for (p=path; *p; ++p) { + if (*p == '/') + b = p + 1; + } + strcpy(base, b); + for (p=base; *p; ++p) { + if (*p == '.') { + *p = 0; + break; + } + } + return base; +} + + +void setcharmap(FT_Face face) +{ + FT_Long i; + + /* + * Get the requested cmap. + */ + for (i = 0; i < face->num_charmaps; i++) { + if (face->charmaps[i]->platform_id == pid && + face->charmaps[i]->encoding_id == eid) + break; + } + + if (i == face->num_charmaps && pid == 3 && eid == 1) { + /* + * Make a special case when this fails with pid == 3 and eid == 1. + * Change to eid == 0 and try again. This captures the two possible + * cases for MS fonts. Some other method should be used to cycle + * through all the alternatives later. + */ + for (i = 0; i < face->num_charmaps; i++) { + if (face->charmaps[i]->platform_id == pid && + face->charmaps[i]->encoding_id == 0) + break; + } + if (i < face->num_charmaps) { + pid = 3; + eid = 1; + FT_Set_Charmap(face, face->charmaps[i]); + } else { + /* + * No CMAP was found. + */ + nocmap = 1; + pid = eid = -1; + } + } else { + FT_Set_Charmap(face, face->charmaps[i]); + nocmap = 0; + } + +} + +/* + * quote in otf2bdf. + * A generic routine to get a name from the OT name table. This routine + * always looks for English language names and checks three possibilities: + * 1. English names with the MS Unicode encoding ID. + * 2. English names with the MS unknown encoding ID. + * 3. English names with the Apple Unicode encoding ID. + * + * The particular name ID mut be provided (e.g. nameID = 0 for copyright + * string, nameID = 6 for Postscript name, nameID = 1 for typeface name. + * + * If the `dash_to_space' flag is non-zero, all dashes (-) in the name will be + * replaced with the character passed. + * + * Returns the number of bytes added. + */ +static int +otf_get_english_string(FT_Face face, int nameID, int dash_to_space, + char *name, int name_size) +{ + + int j, encid; + FT_UInt i, nrec; + FT_SfntName sfntName; + unsigned char *s; + unsigned short slen; + + nrec = FT_Get_Sfnt_Name_Count(face); + + for (encid = 1, j = 0; j < 2; j++, encid--) { + /* + * Locate one of the MS English font names. + */ + for (i = 0; i < nrec; i++) { + FT_Get_Sfnt_Name(face, i, &sfntName); + if (sfntName.platform_id == 3 && + sfntName.encoding_id == encid && + sfntName.name_id == nameID && + (sfntName.language_id == 0x0409 || + sfntName.language_id == 0x0809 || + sfntName.language_id == 0x0c09 || + sfntName.language_id == 0x1009 || + sfntName.language_id == 0x1409 || + sfntName.language_id == 0x1809)) { + s = sfntName.string; + slen = sfntName.string_len; + break; + } + } + + if (i < nrec) { + if (slen >> 1 >= name_size) { + fprintf(stderr, "warning: %s string longer than buffer." + "Truncating to %d bytes.\n", string_names[nameID], name_size); + slen = name_size << 1; + } + + /* + * Found one of the MS English font names. The name is by + * definition encoded in Unicode, so copy every second byte into + * the `name' parameter, assuming there is enough space. + */ + for (i = 1; i < slen; i += 2) { + if (dash_to_space) + *name++ = (s[i] != '-') ? s[i] : ' '; + else if (s[i] == '\r' || s[i] == '\n') { + if (s[i] == '\r' && i + 2 < slen && s[i + 2] == '\n') + i += 2; + *name++ = ' '; + *name++ = ' '; + } else + *name++ = s[i]; + } + *name = 0; + return (slen >> 1); + } + } + + /* + * No MS English name found, attempt to find an Apple Unicode English + * name. + */ + for (i = 0; i < nrec; i++) { + FT_Get_Sfnt_Name(face, i, &sfntName); + if (sfntName.platform_id == 0 && sfntName.language_id == 0 && + sfntName.name_id == nameID) { + s = sfntName.string; + slen = sfntName.string_len; + break; + } + } + + if (i < nrec) { + if (slen >> 1 >= name_size) { + fprintf(stderr, "warning: %s string longer than buffer." + "Truncating to %d bytes.\n", string_names[nameID], name_size); + slen = name_size << 1; + } + + /* + * Found the Apple Unicode English name. The name is by definition + * encoded in Unicode, so copy every second byte into the `name' + * parameter, assuming there is enough space. + */ + for (i = 1; i < slen; i += 2) { + if (dash_to_space) + *name++ = (s[i] != '-') ? s[i] : ' '; + else if (s[i] == '\r' || s[i] == '\n') { + if (s[i] == '\r' && i + 2 < slen && s[i + 2] == '\n') + i += 2; + *name++ = ' '; + *name++ = ' '; + } else + *name++ = s[i]; + } + *name = 0; + return (slen >> 1); + } + + return 0; +} + + +int get_ttc_table(char *path, struct ttc_table *ttcname ) +{ + + + FT_Error error; + FT_Library library; + FT_Face face; + FT_Long i; + char xlfd[BUFSIZ]; + + /* init number of ttf in ttc */ + ttcname->ttc_count = 0; + + /* Initialize engine */ + if ( ( error = FT_Init_FreeType( &library ) ) != 0 ) + { + panic( "Error while initializing engine" ); + return error; + } + + + /* Load face */ + error = FT_New_Face( library, path, (FT_Long) 0, &face ); + if ( error ) + { + arg_panic( "Could not find/open font", path ); + return error; + } + + ttcname->ttc_count = face->num_faces; + ttcname->ttf_name = malloc( sizeof(char*) * ttcname->ttc_count); + + for(i = 0; i < ttcname->ttc_count; i++) + { + error = FT_New_Face( library, path, i, &face ); + if ( error == FT_Err_Cannot_Open_Stream ) + arg_panic( "Could not find/open font", path ); + otf_get_english_string(face, BDFOTF_POSTSCRIPT_STRING, 0, xlfd, + sizeof(xlfd)); + ttcname->ttf_name[i] = malloc(sizeof(char) * (strlen(xlfd) + 1 )); + strcpy(ttcname->ttf_name[i], xlfd); + } + return 0; +} + +void print_ttc_table(char* path) +{ + struct ttc_table ttcname; + FT_Long i; + + get_ttc_table(path, &ttcname); + printf("ttc header count = %ld \n\n", ttcname.ttc_count); + printf("Encoding tables available in the true type collection\n\n"); + printf("INDEX\tPOSTSCRIPT NAME\n"); + printf("-----------------------------------------------------\n"); + for(i = 0; i < ttcname.ttc_count; i++) + { + printf("%ld\t%s\n", i, ttcname.ttf_name[i]); + } + for(i = 0; i < ttcname.ttc_count; i++) + { + free(ttcname.ttf_name[i]); + } + printf("\n\n"); + free(ttcname.ttf_name); + + return; +} + +FT_Long getcharindex(FT_Face face, FT_Long code) +{ + FT_Long idx; + if (nocmap) { + if (code >= face->num_glyphs) + idx = 0; + else + idx = code; + } else + idx = FT_Get_Char_Index( face, code); + + if ( idx <= 0 || idx > face->num_glyphs) + return 0; + else + return idx; +} + +void print_raw_glyph( FT_Face face) +{ + int pixel,row,col,width; + width = face->glyph->metrics.width >> 6; + + printf("\n---Raw-Glyph---\n"); + for(row=0; row < face->glyph->metrics.height >> 6; row++) + { + printf("_"); + for(col=0; col < width; col++) + { + pixel = *(face->glyph->bitmap.buffer+width*row+col)/26; + if ( pixel ) printf("%d",pixel); else printf(" "); + } + printf("_\n"); + } + printf("----End-----\n"); +} + +int glyph_width( FT_Face face) +{ + int pitch, h_adv, width; + unsigned spacing = (unsigned)(between_chr * (1<<6));/* convert to fixed point */ + + pitch = ABS(face->glyph->bitmap.pitch); + h_adv = face->glyph->metrics.horiAdvance >> 6; + width = (face->glyph->metrics.width + spacing) >> 6; + + if(pitch == 0) pitch = h_adv; + if(width < pitch) width = pitch; + if(width == 0) return 0; + + return width; +} + + +void trim_glyph( FT_GlyphSlot glyph, int *empty_first_col, + int *empty_last_col, int *width ) +{ + int row; + int stride = glyph->bitmap.pitch; + int end = stride-1; + int trim_left = 2, trim_right = 2; + + const unsigned char limit = 64u; + const unsigned char *image = glyph->bitmap.buffer; + + if (*width < 2) + return; /* nothing to do? */ + + for(row=0; row< glyph->metrics.height >> 6; row++) + { + const unsigned char *column = image+row*stride; + if (*column++ < limit && trim_left) + { + if (*column >= limit/2) + trim_left = 1; + } + else + trim_left = 0; + + column = image+row*stride+end; + if (*column-- < limit && trim_right) + { + if (*column >= limit/2) + trim_right = 1; + } + else + trim_right = 0; + } + + + (*width) -= trim_left + trim_right; + if (*width < 0) *width = 0; + + *empty_first_col = trim_left; + *empty_last_col = trim_right; +} + +void convttf(char* path, char* destfile, FT_Long face_index) +{ + FT_Error error; + FT_Library library; + FT_Face face; + int w,h; + int row,col; + int empty_first_col, empty_last_col; + FT_Long charindex; + FT_Long index = 0; + FT_Long code; + + int depth = 2; + unsigned char bit_shift = 1 << depth; + unsigned char pixel_per_byte = CHAR_BIT / bit_shift; + + /* Initialize engine */ + if ( ( error = FT_Init_FreeType( &library ) ) != 0 ) + panic( "Error while initializing engine" ); + + /* Load face */ + error = FT_New_Face( library, path, (FT_Long) face_index, &face ); + if ( error == FT_Err_Cannot_Open_Stream ) + arg_panic( "Could not find/open font\n", path ); + else if ( error ) + arg_panic( "Error while opening font\n", path ); + + + setcharmap( face ); + /* Set font header data */ + struct font_struct export_font; + export_font.header.header[0] = 'R'; + export_font.header.header[1] = 'B'; + export_font.header.header[2] = '1'; + export_font.header.header[3] = '2'; + //export_font.header.height = 0; + //export_font.header.ascent = 0; + + float extra_space = (float)(between_row-trim_aa-trim_da); + FT_Set_Char_Size( face, 0, pixel_size << 6, hv_resolution, hv_resolution ); + export_font.header.ascent = + ((face->size->metrics.ascender*(100-trim_ap)/100) >> 6) - trim_aa; + + export_font.header.height = + (((face->size->metrics.ascender*(100-trim_ap)/100) - + (face->size->metrics.descender*(100-trim_dp)/100)) >> 6) + extra_space; + + printf("\n"); + printf("Please wait, converting %s:\n", path); + + /* "face->num_glyphs" is NG.; */ + if ( limit_char == 0 ) limit_char = max_char; + if ( limit_char > max_char ) limit_char = max_char; + + FT_Long char_count = 0; + + + + export_font.header.maxwidth = 1; + export_font.header.depth = 1; + firstchar = limit_char; + lastchar = start_char; + + /* calculate memory usage */ + for(code = start_char; code <= limit_char ; code++ ) + { + charindex = getcharindex( face, code); + if ( !(charindex) ) continue; + error = FT_Load_Glyph( face, charindex, + (FT_LOAD_RENDER | FT_LOAD_NO_BITMAP) ); + if ( error ) continue; + + w = glyph_width( face ); + if (w == 0) continue; + empty_first_col = empty_last_col = 0; + if(trimming) + trim_glyph( face->glyph, &empty_first_col, &empty_last_col, &w); + + if (export_font.header.maxwidth < w) + export_font.header.maxwidth = w; + + + char_count++; + index += (w*export_font.header.height + pixel_per_byte - 1)/pixel_per_byte; + + if (code >= lastchar) + lastchar = code; + + if (code <= firstchar) + firstchar = code; + } + export_font.header.defaultchar = firstchar; + export_font.header.firstchar = firstchar; + export_font.header.size = lastchar - firstchar + 1; + export_font.header.nbits = index; + export_font.header.noffset = export_font.header.size; + export_font.header.nwidth = export_font.header.size; + + /* check if we need to use long offsets */ + char use_long_offset = (export_font.header.nbits >= 0xFFDB ); + + /* allocate memory */ + export_font.offset = NULL; + export_font.offset_long = NULL; + if (use_long_offset) + export_font.offset_long = + malloc( sizeof(FT_Long)* export_font.header.noffset ); + else + export_font.offset = + malloc( sizeof(unsigned short)* export_font.header.noffset ); + + export_font.width = + malloc( sizeof(unsigned char) * export_font.header.nwidth ); + export_font.chars_data = + malloc( sizeof(unsigned char) * export_font.header.nbits ); + + /* for now we use the full height for each character */ + h = export_font.header.height; + + index = 0; + int done = 0; + char char_name[1024]; + int converted_char_count = 0; + int failed_char_count = 0; + + for( code = firstchar; code <= lastchar; code++ ) + { + /* Get gylph index from the char and render it */ + charindex = getcharindex( face, code); + if ( !charindex ) + { + if ( use_long_offset ) + export_font.offset_long[code - firstchar] = export_font.offset_long[0]; + else + export_font.offset[code - firstchar] = export_font.offset[0]; + export_font.width[code - firstchar] = export_font.width[0]; + continue; + } + + error = FT_Load_Glyph( face, charindex , + (FT_LOAD_RENDER | FT_LOAD_NO_BITMAP) ); + if ( error ) { + continue; + } + if FT_HAS_GLYPH_NAMES( face ) + FT_Get_Glyph_Name( face, charindex, char_name, 16); + else + char_name[0] = '\0'; + + FT_GlyphSlot slot = face->glyph; + FT_Bitmap* source = &slot->bitmap; + //print_raw_glyph( face ); + w = glyph_width( face ); + if (w == 0) continue; + empty_first_col = empty_last_col = 0; + + if(trimming) + trim_glyph( face->glyph, &empty_first_col, &empty_last_col, &w ); + + if ( use_long_offset ) + export_font.offset_long[code - firstchar] = index; + else + export_font.offset[code - firstchar] = index; + + export_font.width[code - firstchar] = w; + + /* copy the glyph bitmap to a full sized glyph bitmap */ + unsigned char* src = source->buffer; + unsigned char* tmpbuf = malloc(sizeof(unsigned char) * w * h); + memset(tmpbuf, 0xff, w*h); + int start_y = export_font.header.ascent - slot->bitmap_top; + + int glyph_height = source->rows; + int stride = source->pitch; + unsigned char* buf = tmpbuf; + unsigned char* endbuf = tmpbuf + w*h; + + int error = 0; + /* insert empty pixels on the left */ + int col_off = w - stride; + if (col_off > 1) col_off /= 2; + if (col_off < 0) col_off = 0; + + for(row=0; row < glyph_height; row++) + { + if(row+start_y < 0 || row+start_y >= h) + continue; + for(col = empty_first_col; col < stride; col++) + { + unsigned char *tsrc, *dst; + dst = buf + (w*(start_y+row)) + col + col_off; + tsrc = src + stride*row + col; + if (dst < endbuf && dst >= tmpbuf) + *dst = 0xff - *tsrc; + else { + error = 1; + printf("Error! row: %3d col: %3d\n", row, col); + } + } + } + if(error) print_raw_glyph(face); + + buf = tmpbuf; + int numbits; + unsigned int field; + field = 0; + numbits = pixel_per_byte; + + for(row=0; row < h; row++) + { + for(col=0; col < w; col++) + { + unsigned int src = *buf++; + unsigned int cur_col = (src + 8) / 17; + field |= (cur_col << (bit_shift*(pixel_per_byte-numbits))); + + if (--numbits == 0) + { + export_font.chars_data[index++] = (unsigned char)field; + numbits = pixel_per_byte; + field = 0; + } + } + } + + /* Pad last byte */ + if (numbits != pixel_per_byte) + { + export_font.chars_data[index++] = (unsigned char)field; + } + + if( dump_glyphs ) + { + /* debug: dump char */ + printf("\n---Converted Glyph Dump---\n"); + unsigned char bit_max = (1 << bit_shift) - 1; + if ( code > 32 && code < 255 ) { + row = h; + if(use_long_offset) + buf = &(export_font.chars_data[export_font.offset_long[ + code - firstchar]]); + else + buf = &(export_font.chars_data[export_font.offset[ + code - firstchar]]); + unsigned char current_data; + unsigned char font_bits; + numbits = pixel_per_byte; + current_data = *buf; + do + { + col = w; + printf("-"); + do + { + font_bits = current_data & bit_max; + if (font_bits==bit_max) + printf(" "); + else + { + if(font_bits > bit_max/2) + printf("."); + else + printf("@"); + } + if (--numbits == 0) + { + current_data = *(++buf); + numbits = pixel_per_byte; + } + else + { + current_data >>= bit_shift; + } + } while (--col); + printf("-\n"); + } while (--row); + } + buf = NULL; + printf("---End Glyph Dump---\n"); + } + + free(tmpbuf); + converted_char_count++; + done = (100*(converted_char_count))/char_count; + printf("Converted %s %d (%d%%)\e[K\r", + char_name,converted_char_count,done); fflush(stdout); + } + + FILE *file = fopen(destfile, "w"); + printf("Writing %s\n", destfile); + + /* font info */ + writestr(file, VERSION, 4); + writeshort(file, export_font.header.maxwidth); + writeshort(file, export_font.header.height); + writeshort(file, export_font.header.ascent); + writeshort(file, export_font.header.depth); + writeint(file, export_font.header.firstchar); + writeint(file, export_font.header.defaultchar); + writeint(file, export_font.header.size); + writeint(file, export_font.header.nbits); + writeint(file, export_font.header.noffset); + writeint(file, export_font.header.nwidth); + + fwrite( (char*)export_font.chars_data, 1, + export_font.header.nbits, file); + free(export_font.chars_data); + + int skip,i; + char pad[] = {0,0,0,0}; + if ( use_long_offset ) + { + skip = ((export_font.header.nbits + 3) & ~3) - + export_font.header.nbits; + fwrite(pad, 1, skip, file); /* pad */ + for(i = 0; i < export_font.header.noffset; i++) + writeint(file, export_font.offset_long[i]); + } + else + { + skip = ((export_font.header.nbits + 1) & ~1) - + export_font.header.nbits; + fwrite(pad, 1, skip, file); /* pad */ + for(i = 0; i < export_font.header.noffset; i++) + writeshort(file, export_font.offset[i]); + } + + for(i = 0; i < export_font.header.nwidth; i++) + writebyte(file, export_font.width[i]); + free(export_font.width); + + if ( use_long_offset ) + free(export_font.offset_long); + else + free(export_font.offset); + + fclose(file); + FT_Done_Face( face ); + FT_Done_FreeType( library ); + printf("done (converted %d glyphs, %d errors).\e[K\n\n", + converted_char_count, failed_char_count); + +} + +void convttc(char* path) +{ + struct ttc_table ttcname; + FT_Long i; + + get_ttc_table(path, &ttcname); + + if (ttcname.ttc_count == 0) + { + printf("This file is a not true type font.\n"); + return; + } + + /* default */ + if (!flg_all_ttc && ttc_index == -1) + { + if (!oflag) + { /* generate filename */ + snprintf(outfile, sizeof(outfile), + "%d-%s.fnt", pixel_size, basename(path)); + } + convttf(path, outfile, (FT_Long) 0); + } + + /* set face_index of ttc */ + else if (!flg_all_ttc) + { + print_ttc_table(path); + if ( !oflag ) + { + if (ttc_index >= 0 && + ttc_index < ttcname.ttc_count) + { + if (strcmp(ttcname.ttf_name[ttc_index], "") != 0) + { + snprintf(outfile, sizeof(outfile), "%d-%s.fnt", + pixel_size, ttcname.ttf_name[ttc_index]); + } + else + { + snprintf(outfile, sizeof(outfile), "%d-%s-%ld.fnt", + pixel_size, basename(path), ttc_index); + } + } + else + { + printf("illegal face index of ttc.\n"); + } + } + convttf(path, outfile, ttc_index); + } + else { /* convert all fonts */ + print_ttc_table(path); + for(i = 0; i < ttcname.ttc_count; i++) + { + snprintf(outfile, sizeof(outfile), "%d-%s.fnt", + pixel_size, ttcname.ttf_name[i]); + convttf(path, outfile, i); + } + } + + for(i = 0; i < ttcname.ttc_count; i++) + { + free(ttcname.ttf_name[i]); + } + free(ttcname.ttf_name); +} + + + +/* parse command line options*/ +void getopts(int *pac, char ***pav) +{ + char *p; + char **av; + int ac; + ac = *pac; + av = *pav; + + limit_char = max_char; + start_char = 0; + + while (ac > 0 && av[0][0] == '-') { + p = &av[0][1]; + while( *p) + switch(*p++) { + case 'h':case 'H': + usage(); + break; + case ' ': /* multiple -args on av[]*/ + while( *p && *p == ' ') + p++; + if( *p++ != '-') /* next option must have dash*/ + p = ""; + break; /* proceed to next option*/ + case 'o': /* set output file*/ + oflag = 1; + if (*p) { + strcpy(outfile, p); + while (*p && *p != ' ') + p++; + } + else { + av++; ac--; + if (ac > 0) + strcpy(outfile, av[0]); + } + break; + case 'l': /* set encoding limit*/ + if (*p) { + limit_char = atoi(p); + while (*p && *p != ' ') + p++; + } + else { + av++; ac--; + if (ac > 0) + limit_char = atoi(av[0]); + } + break; + case 's': /* set encoding start*/ + if (*p) { + start_char = atol(p); + while (*p && *p != ' ') + p++; + } + else { + av++; ac--; + if (ac > 0) + start_char = atol(av[0]); + } + break; + case 'p': /* set pixel size*/ + if (*p) { + pixel_size = atoi(p); + while (*p && *p != ' ') + p++; + } + else { + av++; ac--; + if (ac > 0) + pixel_size = atoi(av[0]); + } + break; + case 'c': /* set spaece between characters */ + { + if (*p) { + between_chr = atof(p); + while (*p && *p != ' ') + p++; + } + else { + av++; ac--; + if (ac > 0) + between_chr = atof(av[0]); + } + break; + } + case 'd': + dump_glyphs = 1; + while (*p && *p != ' ') + p++; + break; + case 'x': + trimming = 1; + while (*p && *p != ' ') + p++; + break; + case 'X': + if (*p) { + hv_resolution = atoi(p); + while (*p && *p != ' ') + p++; + } + else { + av++; ac--; + if (ac > 0) + hv_resolution = atoi(av[0]); + } + break; + case 'r': + if (*p) { + between_row = atof(p); + while (*p && *p != ' ') + p++; + } + else { + av++; ac--; + if (ac > 0) + between_row = atof(av[0]); + } + break; + case 'T': + if(*p == 'A') { + if(*(++p)) { + trim_ap = atoi(p); + while (*p && *p != ' ') + p++; + } + else { + av++; ac--; + if (ac > 0) + trim_ap = atoi(av[0]); + } + break; + } + if(*p == 'D') { + if(*(++p)) { + trim_dp = atoi(p); + while (*p && *p != ' ') + p++; + } + else { + av++; ac--; + if (ac > 0) + trim_dp = atoi(av[0]); + } + break; + } + if(*p == 'a') { + if(*(++p)) { + trim_aa = atoi(p); + while (*p && *p != ' ') + p++; + } + else { + av++; ac--; + if (ac > 0) + trim_aa = atoi(av[0]); + } + break; + } + if(*p == 'd') { + if(*(++p)) { + trim_da = atoi(p); + + } + else { + av++; ac--; + if (ac > 0) + trim_da = atoi(av[0]); + } + break; + } + fprintf(stderr, "Unknown option ignored: %s\n", p-1); + while (*p && *p != ' ') + p++; + break; + case 't': /* display ttc table */ + if (*p == 't') { + pct = 1; + while (*p && *p != ' ') + p++; + } + + else if (*p == 'a') { + flg_all_ttc = 1; + while (*p && *p != ' ') + p++; + } + + else if (*p) { + ttc_index = atoi(p); + while (*p && *p != ' ') + p++; + } + else { + av++; ac--; + if (ac > 0) + ttc_index = atoi(av[0]); + } + break; + + default: + fprintf(stderr, "Unknown option ignored: %s\n", p-1); + while (*p && *p != ' ') + p++; + } + ++av; --ac; + } + *pac = ac; + *pav = av; +} + + +int main(int ac, char **av) +{ + int ret = 0; + + ++av; --ac; /* skip av[0]*/ + + getopts(&ac, &av); /* read command line options*/ + + if (ac < 1) + { + usage(); + } + if (oflag) + { + if (ac > 1) + { + usage(); + } + } + + if (limit_char < start_char) + { + usage(); + exit(0); + } + + while (pct && ac > 0) + { + print_ttc_table(av[0]); + ++av; --ac; + exit(0); + } + + while (ac > 0) + { + convttc(av[0]); + ++av; --ac; + } + + exit(ret); +} + + + +/* + * Trie node structure. + */ +typedef struct { + unsigned short key; /* Key value. */ + unsigned short val; /* Data for the key. */ + unsigned long sibs; /* Offset of siblings from trie beginning. */ + unsigned long kids; /* Offset of children from trie beginning. */ +} node_t; + +/* + * The trie used for remapping codes. + */ +static node_t *nodes; +static unsigned long nodes_used = 0; + +int +otf2bdf_remap(unsigned short *code) +{ + unsigned long i, n, t; + unsigned short c, codes[2]; + + /* + * If no mapping table was loaded, then simply return the code. + */ + if (nodes_used == 0) + return 1; + + c = *code; + codes[0] = (c >> 8) & 0xff; + codes[1] = c & 0xff; + + for (i = n = 0; i < 2; i++) { + t = nodes[n].kids; + if (t == 0) + return 0; + for (; nodes[t].sibs && nodes[t].key != codes[i]; t = nodes[t].sibs); + if (nodes[t].key != codes[i]) + return 0; + n = t; + } + + *code = nodes[n].val; + return 1; +} diff --git a/tools/root.make b/tools/root.make index 85a996fc62..20b11ae1fb 100644 --- a/tools/root.make +++ b/tools/root.make @@ -21,7 +21,7 @@ ASMFLAGS = -D__ASSEMBLER__ # work around gcc 3.4.x bug with -std=gnu99, onl TOOLS = $(TOOLSDIR)/rdf2binary $(TOOLSDIR)/convbdf \ $(TOOLSDIR)/codepages $(TOOLSDIR)/scramble $(TOOLSDIR)/bmp2rb \ $(TOOLSDIR)/uclpack $(TOOLSDIR)/mkboot $(TOOLSDIR)/iaudio_bl_flash.c \ - $(TOOLSDIR)/iaudio_bl_flash.h + $(TOOLSDIR)/iaudio_bl_flash.h $(TOOLSDIR)/convttf ifeq (,$(PREFIX)) diff --git a/tools/tools.make b/tools/tools.make index 64a47c53de..c143157dab 100644 --- a/tools/tools.make +++ b/tools/tools.make @@ -33,6 +33,11 @@ $(TOOLSDIR)/uclpack: $(TOOLSDIR)/ucl/uclpack.c $(wildcard $(TOOLSDIR)/ucl/src/*. $(call PRINTS,CC $(@F))$(HOSTCC) $(TOOLSCFLAGS) -I$(TOOLSDIR)/ucl \ -I$(TOOLSDIR)/ucl/include -o $@ $^ +$(TOOLSDIR)/convttf: $(TOOLSDIR)/convttf.c + $(call PRINTS,CC $(@F)) + $(SILENT)$(HOSTCC) $(TOOLSFLAGS) -lm -O2 -Wall -g $+ -o $@ \ + `freetype-config --libs` `freetype-config --cflags` + # implicit rule for simple tools $(TOOLSDIR)/%: $(TOOLSDIR)/%.c $(call PRINTS,CC $(subst $(ROOTDIR)/,,$@))