/* bdf_tga.c Modes: BDF_BBX_MODE_MINIMAL 0 BDF_BBX_MODE_MAX 1 BDF_BBX_MODE_HEIGHT 2 For all modes, default reference should be the baseline. This is required for mode 0, but may be optional for 1 and 2 If (x,y) is the user provided baseline point for the glyph, then the decoding mus tbe start at (x..., y-h-descent) BDF_BBX_MODE_MINIMAL - exact space as intended by the font author - glyphs my overlap ("mj" with osb18) BDF_BBX_MODE_MAX - extra space may be added - glyphs do not overlap BDF_BBX_MODE_HEIGHT - extra space may be added - glyphs do not overlap */ #include #include #include #include #include #include "fd.h" static uint16_t tga_width; static uint16_t tga_height; static uint16_t tga_used_height; static uint8_t *tga_data = NULL; static uint8_t *tga_font; static int glyph_cnt; static int bits_per_0; static int bits_per_1; static int bits_per_char_width; static int bits_per_char_height; static int bits_per_char_x; static int bits_per_char_y; static int bits_per_delta_x; static int char_width; static int char_height; static int char_descent; static unsigned unicode_start_pos; static int tga_pixel_intersection; int tga_get_char_width(void) { return char_width; } int tga_get_char_height(void) { return char_height; } int tga_init(uint16_t w, uint16_t h) { tga_width = 0; tga_height = 0; tga_used_height = 0; tga_pixel_intersection = 0; if ( tga_data != NULL ) free(tga_data); tga_data = (uint8_t *)malloc((size_t)w*(size_t)h*3); if ( tga_data == NULL ) return 0; tga_width = w; tga_height = h; memset(tga_data, 255, tga_width*tga_height*3); return 1; } void tga_clear(void) { memset(tga_data, 255, tga_width*tga_height*3); } void tga_set_pixel(uint16_t x, uint16_t y, uint8_t r, uint8_t g, uint8_t b) { uint8_t *p; if ( y>= tga_height) return; if ( x>= tga_width) return; if ( tga_used_height < y ) tga_used_height = y; p = tga_data + (tga_height-y-1)*(size_t)tga_width*3 + (size_t)x*3; if ( p[0] != 255 || p[1] != 255 || p[2] != 255 ) tga_pixel_intersection = 1; //printf("tga_set_pixel %d %d\n", x, y); *p++ = b; *p++ = g; *p++ = r; } int tga_is_pixel_intersection(void) { return tga_pixel_intersection; } void tga_clear_pixel_intersection(void) { tga_pixel_intersection = 0; } void tga_write_byte(FILE *fp, uint8_t byte) { fputc(byte, fp); } void tga_write_word(FILE *fp, uint16_t word) { tga_write_byte(fp, word&255); tga_write_byte(fp, word>>8); } void tga_save(const char *name) { FILE *fp; fp = fopen(name, "wb"); if ( fp != NULL ) { tga_write_byte(fp, 0); /* no ID */ tga_write_byte(fp, 0); /* no color map */ tga_write_byte(fp, 2); /* uncompressed true color */ tga_write_word(fp, 0); tga_write_word(fp, 0); tga_write_byte(fp, 0); tga_write_word(fp, 0); /* x origin */ tga_write_word(fp, 0); /* y origin */ tga_write_word(fp, tga_width); /* width */ tga_write_word(fp, tga_used_height+1); /* height */ tga_write_byte(fp, 24); /* color depth */ tga_write_byte(fp, 0); fwrite(tga_data + (tga_height - (tga_used_height+1))*tga_width*3 , tga_width*3, tga_used_height+1, fp); tga_write_word(fp, 0); tga_write_word(fp, 0); tga_write_word(fp, 0); tga_write_word(fp, 0); fwrite("TRUEVISION-XFILE.", 18, 1, fp); fclose(fp); } } /* font data: offset bytes description 0 1 glyph_cnt number of glyphs 1 1 bbx_mode 0: proportional, 1: common height, 2: monospace, 3: multiple of 8 2 1 bits_per_0 glyph rle parameter 3 1 bits_per_1 glyph rle parameter 4 1 bits_per_char_width glyph rle parameter 5 1 bits_per_char_height glyph rle parameter 6 1 bits_per_char_x glyph rle parameter 7 1 bits_per_char_y glyph rle parameter 8 1 bits_per_delta_x glyph rle parameter 9 1 max_char_width 10 1 max_char_height 11 1 x offset 12 1 y offset (descent) 13 1 ascent (capital A) 14 1 descent (lower g) 15 1 ascent '(' 16 1 descent ')' 17 1 start pos 'A' high byte 18 1 start pos 'A' low byte 19 1 start pos 'a' high byte 20 1 start pos 'a' low byte 21 1 start pos unicode high byte 22 1 start pos unicode low byte */ void tga_set_font(uint8_t *font) { glyph_cnt = *font++; font++; /* bbx mode */ bits_per_0 = *font++; bits_per_1 = *font++; bits_per_char_width = *font++; bits_per_char_height = *font++; bits_per_char_x = *font++; bits_per_char_y = *font++; bits_per_delta_x = *font++; char_width = *font++; char_height = *font++; font++; /* x offset */ char_descent = *(int8_t *)font; font++; font++; font++; font++; font++; font++; font++; font++; font++; unicode_start_pos = *font++; unicode_start_pos <<= 8; unicode_start_pos |= *font++; tga_font = font; } uint8_t *tga_get_glyph_data(uint16_t encoding) { uint8_t *font = tga_font; if ( encoding <= 255 ) { for(;;) { if ( font[1] == 0 ) break; if ( font[0] == encoding ) { return font; } font += font[1]; } } else { uint16_t e; uint8_t *unicode_lookup_table; font += unicode_start_pos; unicode_lookup_table = font; /* search for the glyph start in the unicode lookup table */ do { font += ((unicode_lookup_table[0]<<8)|unicode_lookup_table[1]); unicode_lookup_table+=2; e = (unicode_lookup_table[0]<<8)|unicode_lookup_table[1]; unicode_lookup_table+=2; } while( e < encoding ); /* continue with the search in the font */ for(;;) { e = ((font[0]<<8)|font[1]); if ( e == 0 ) break; if ( e == encoding ) { return font; } font += font[2]; } } return NULL; } /* font decode */ struct tga_fd_struct { unsigned target_x; unsigned target_y; unsigned is_transparent; unsigned x; /* local coordinates, (0,0) is upper left */ unsigned y; unsigned glyph_width; unsigned glyph_height; const uint8_t *decode_ptr; /* pointer to the compressed data */ unsigned decode_bit_pos; /* bitpos inside a byte of the compressed data */ uint8_t bbx_x_max_bit_size; uint8_t bbx_y_max_bit_size; uint8_t bbx_w_max_bit_size; uint8_t bbx_h_max_bit_size; uint8_t dx_max_bit_size; }; typedef struct tga_fd_struct tga_fd_t; /* increment x and consider line wrap (inc y)*/ /* old procedure */ void tga_fd_inc(tga_fd_t *f) { unsigned x = f->x; x++; if ( x == f->glyph_width ) { x = 0; f->y++; } f->x = x; } unsigned tga_fd_get_unsigned_bits(tga_fd_t *f, unsigned cnt) { unsigned val; unsigned bit_pos = f->decode_bit_pos; val = *(f->decode_ptr); val >>= bit_pos; if ( bit_pos + cnt >= 8 ) { f->decode_ptr++; val |= *(f->decode_ptr) << (8-bit_pos); bit_pos -= 8; } val &= (1U<decode_bit_pos = bit_pos; return val; } /* 2 bit --> cnt = 2 -2,-1,0. 1 3 bit --> cnt = 3 -2,-1,0. 1 -4,-3,-2,-1,0,1,2,3 if ( x < 0 ) r = bits(x-1)+1; else r = bits(x)+1; */ int tga_fd_get_signed_bits(tga_fd_t *t, int cnt) { return (int)tga_fd_get_unsigned_bits(t, cnt) - ((1<>1); } void tga_fd_draw_fg_pixel(tga_fd_t *f, unsigned cnt) { //printf("%d ", cnt); /* cnt can be zero */ while( cnt > 0 ) { cnt--; tga_set_pixel(f->target_x+f->x+cnt, f->target_y+f->y, 0,0,0); } } void tga_fd_draw_bg_pixel(tga_fd_t *f, unsigned cnt) { //printf("%d ", cnt); /* cnt can be zero */ while( cnt > 0 ) { cnt--; if ( f->is_transparent == 0 ) tga_set_pixel(f->target_x+f->x+cnt, f->target_y+f->y, 0x0e8,0x0e8,0x0e8); } } void tga_draw_hline(unsigned x,unsigned y, unsigned cnt, unsigned is_foreground) { while( cnt > 0 ) { cnt--; if ( is_foreground == 0 ) tga_set_pixel(x+cnt, y, 0x0e8,0x0e8,0x0e8); else tga_set_pixel(x+cnt, y, 255,0,0); } } void tga_fd_draw_pixel(tga_fd_t *f, unsigned cnt, unsigned is_foreground) { if ( is_foreground ) { tga_fd_draw_fg_pixel(f, cnt); } else { tga_fd_draw_bg_pixel(f, cnt); } } void tga_fd_decode_len(tga_fd_t *f, unsigned len, unsigned is_foreground) { unsigned cnt, rem; cnt = len; for(;;) { rem = f->glyph_width; rem -= f->x; if ( cnt < rem ) break; tga_fd_draw_pixel(f,rem, is_foreground); cnt -= rem; f->x = 0; f->y++; } tga_fd_draw_pixel(f, cnt, is_foreground); f->x += cnt; } unsigned tga_fd_decode(tga_fd_t *f, uint8_t *glyph_data, int is_unicode) { unsigned a, b; //unsigned cnt, rem; int x, y; unsigned d = 0; f->decode_ptr = glyph_data; f->decode_bit_pos = 0; f->decode_ptr += 1; f->decode_ptr += 1; if ( is_unicode != 0 ) f->decode_ptr += 1; f->glyph_width = tga_fd_get_unsigned_bits(f, bits_per_char_width); f->glyph_height = tga_fd_get_unsigned_bits(f, bits_per_char_height); x = tga_fd_get_signed_bits(f, bits_per_char_x); y = tga_fd_get_signed_bits(f, bits_per_char_y); d = tga_fd_get_signed_bits(f, bits_per_delta_x); if ( f->glyph_width > 0 ) { f->target_x += x; f->target_y -= f->glyph_height ; f->target_y -=y ; /* reset local x/y position */ f->x = 0; f->y = 0; /* decode glyph */ for(;;) { a = tga_fd_get_unsigned_bits(f, bits_per_0); b = tga_fd_get_unsigned_bits(f, bits_per_1); do { tga_fd_decode_len(f, a, 0); tga_fd_decode_len(f, b, 1); } while( tga_fd_get_unsigned_bits(f, 1) != 0 ); if ( f->y >= f->glyph_height ) break; } } return d; } unsigned tga_draw_glyph(unsigned x, unsigned y, uint16_t encoding, int is_hints) { unsigned dx = 0; tga_fd_t f; f.target_x = x; f.target_y = y; f.is_transparent = !is_hints; uint8_t *glyph_data = tga_get_glyph_data(encoding); /* better skip the first 2 or 3 bytes */ if ( glyph_data != NULL ) { dx = tga_fd_decode(&f, glyph_data, encoding >= 255 ? 1 : 0); if ( is_hints ) { tga_set_pixel(x+dx, y, 28,133,240); /* orange: reference point */ tga_set_pixel(x, y, 255,164,0); /* blue: delta x (width) for this glyph */ } } return dx; } unsigned tga_draw_string(unsigned x, unsigned y, const char *s, int is_hints, unsigned max_dx) { unsigned dx = 0; while( *s != '\0' ) { dx += tga_draw_glyph(x+dx,y,*s, is_hints); if ( max_dx > 0 ) if ( dx > max_dx ) break; s++; } return dx; }