1
0
Fork 0
mirror of https://github.com/ctruLua/ctruLua.git synced 2025-10-27 16:39:29 +00:00

Support for special characters in gfx.text; added scrolling to the default shell; updated sftdlib.

There is a known bug with different text size in sftdlib.
This commit is contained in:
Reuh 2015-08-22 16:45:32 +02:00
parent 56b47153b7
commit 2a5513473d
10 changed files with 516 additions and 39 deletions

View file

@ -0,0 +1,36 @@
#ifndef BIN_PACKING_2D_H
#define BIN_PACKING_2D_H
#ifdef __cplusplus
extern "C" {
#endif
typedef struct bp2d_position {
int x, y;
} bp2d_position;
typedef struct bp2d_size {
int w, h;
} bp2d_size;
typedef struct bp2d_rectangle {
int x, y, w, h;
} bp2d_rectangle;
typedef struct bp2d_node {
struct bp2d_node *left;
struct bp2d_node *right;
bp2d_rectangle rect;
int filled;
} bp2d_node;
bp2d_node *bp2d_create(const bp2d_rectangle *rect);
void bp2d_free(bp2d_node *node);
// 1 success, 0 failure
int bp2d_insert(bp2d_node *node, const bp2d_size *in_size, bp2d_position *out_pos);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,34 @@
#ifndef INT_HTAB_H
#define INT_HTAB_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#define INT_HTAB_MAX_LOAD (70) // over 100
typedef struct int_htab_entry {
unsigned int key;
void *value;
} int_htab_entry;
typedef struct int_htab {
size_t size;
size_t used;
int_htab_entry *entries;
} int_htab;
int_htab *int_htab_create(size_t size);
void int_htab_free(int_htab *htab);
int int_htab_insert(int_htab *htab, unsigned int key, void *value);
void *int_htab_find(const int_htab *htab, unsigned int key);
int int_htab_erase(const int_htab *htab, unsigned int key);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,36 @@
#ifndef TEXTURE_ATLAS_H
#define TEXTURE_ATLAS_H
#include "sf2d.h"
#include "bin_packing_2d.h"
#include "int_htab.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct atlas_htab_entry {
bp2d_rectangle rect;
int bitmap_left;
int bitmap_top;
int advance_x;
int advance_y;
} atlas_htab_entry;
typedef struct texture_atlas {
sf2d_texture *tex;
bp2d_node *bp_root;
int_htab *htab;
} texture_atlas;
texture_atlas *texture_atlas_create(int width, int height, sf2d_texfmt format, sf2d_place place);
void texture_atlas_free(texture_atlas *atlas);
int texture_atlas_insert(texture_atlas *atlas, unsigned int character, const void *image, int width, int height, int bitmap_left, int bitmap_top, int advance_x, int advance_y);
int texture_atlas_exists(texture_atlas *atlas, unsigned int character);
void texture_atlas_get(texture_atlas *atlas, unsigned int character, bp2d_rectangle *rect, int *bitmap_left, int *bitmap_top, int *advance_x, int *advance_y);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,86 @@
#include <stdlib.h>
#include "bin_packing_2d.h"
bp2d_node *bp2d_create(const bp2d_rectangle *rect)
{
bp2d_node *node = malloc(sizeof(*node));
if (!node)
return NULL;
node->left = NULL;
node->right = NULL;
node->rect.x = rect->x;
node->rect.y = rect->y;
node->rect.w = rect->w;
node->rect.h = rect->h;
node->filled = 0;
return node;
}
void bp2d_free(bp2d_node *node)
{
if (node->left) {
bp2d_free(node->left);
}
if (node->right) {
bp2d_free(node->right);
}
free(node);
}
int bp2d_insert(bp2d_node *node, const bp2d_size *in_size, bp2d_position *out_pos)
{
if (node->left != NULL || node->right != NULL) {
int ret = bp2d_insert(node->left, in_size, out_pos);
if (ret == 0) {
return bp2d_insert(node->right, in_size, out_pos);
}
return ret;
} else {
if (node->filled)
return 0;
if (in_size->w > node->rect.w || in_size->h > node->rect.h)
return 0;
if (in_size->w == node->rect.w && in_size->h == node->rect.h) {
out_pos->x = node->rect.x;
out_pos->y = node->rect.y;
node->filled = 1;
return 1;
}
int dw = node->rect.w - in_size->w;
int dh = node->rect.h - in_size->h;
bp2d_rectangle left_rect, right_rect;
if (dw > dh) {
left_rect.x = node->rect.x;
left_rect.y = node->rect.y;
left_rect.w = in_size->w;
left_rect.h = node->rect.h;
right_rect.x = node->rect.x + in_size->w;
right_rect.y = node->rect.y;
right_rect.w = node->rect.w - in_size->w;
right_rect.h = node->rect.h;
} else {
left_rect.x = node->rect.x;
left_rect.y = node->rect.y;
left_rect.w = node->rect.w;
left_rect.h = in_size->h;
right_rect.x = node->rect.x;
right_rect.y = node->rect.y + in_size->h;
right_rect.w = node->rect.w;
right_rect.h = node->rect.h - in_size->h;
}
node->left = bp2d_create(&left_rect);
node->right = bp2d_create(&right_rect);
return bp2d_insert(node->left, in_size, out_pos);
}
}

View file

@ -0,0 +1,124 @@
#include <stdlib.h>
#include <string.h>
#include "int_htab.h"
static inline unsigned int FNV_1a(unsigned int key)
{
unsigned char *bytes = (unsigned char *)&key;
unsigned int hash = 2166136261U;
hash = (16777619U * hash) ^ bytes[0];
hash = (16777619U * hash) ^ bytes[1];
hash = (16777619U * hash) ^ bytes[2];
hash = (16777619U * hash) ^ bytes[3];
return hash;
}
int_htab *int_htab_create(size_t size)
{
int_htab *htab = malloc(sizeof(*htab));
if (!htab)
return NULL;
htab->size = size;
htab->used = 0;
htab->entries = malloc(htab->size * sizeof(*htab->entries));
memset(htab->entries, 0, htab->size * sizeof(*htab->entries));
return htab;
}
void int_htab_free(int_htab *htab)
{
int i;
for (i = 0; i < htab->size; i++) {
if (htab->entries[i].value != NULL)
free(htab->entries[i].value);
}
free(htab);
}
void int_htab_resize(int_htab *htab, unsigned int new_size)
{
int i;
int_htab_entry *old_entries;
unsigned int old_size;
old_entries = htab->entries;
old_size = htab->size;
htab->size = new_size;
htab->used = 0;
htab->entries = malloc(new_size * sizeof(*htab->entries));
memset(htab->entries, 0, new_size * sizeof(*htab->entries));
for (i = 0; i < old_size; i++) {
if (old_entries[i].value != NULL) {
int_htab_insert(htab, old_entries[i].key, old_entries[i].value);
}
}
free(old_entries);
}
int int_htab_insert(int_htab *htab, unsigned int key, void *value)
{
if (value == NULL)
return 0;
/* Calculate the current load factor */
if (((htab->used + 1)*100)/htab->size > INT_HTAB_MAX_LOAD) {
int_htab_resize(htab, 2*htab->size);
}
unsigned int mask = htab->size - 1;
unsigned int idx = FNV_1a(key) & mask;
/* Open addressing, linear probing */
while (htab->entries[idx].value != NULL) {
idx = (idx + 1) & mask;
}
htab->entries[idx].key = key;
htab->entries[idx].value = value;
htab->used++;
return 1;
}
void *int_htab_find(const int_htab *htab, unsigned int key)
{
unsigned int mask = htab->size - 1;
unsigned int idx = FNV_1a(key) & mask;
/* Open addressing, linear probing */
while (htab->entries[idx].key != key && htab->entries[idx].value != NULL) {
idx = (idx + 1) & mask;
}
/* Found the key */
if (htab->entries[idx].key == key) {
return htab->entries[idx].value;
}
return NULL;
}
int int_htab_erase(const int_htab *htab, unsigned int key)
{
unsigned int mask = htab->size - 1;
unsigned int idx = FNV_1a(key) & mask;
/* Open addressing, linear probing */
while (htab->entries[idx].key != key && htab->entries[idx].value != NULL) {
idx = (idx + 1) & mask;
}
/* Found the key */
if (htab->entries[idx].key == key) {
htab->entries[idx].value = NULL;
return 1;
}
return 0;
}

View file

@ -1,10 +1,15 @@
#include "sftd.h"
#include "texture_atlas.h"
#include "bin_packing_2d.h"
#include <wchar.h>
#include <sf2d.h>
#include <ft2build.h>
#include FT_CACHE_H
#include FT_FREETYPE_H
#define ATLAS_DEFAULT_W 512
#define ATLAS_DEFAULT_H 512
static int sftd_initialized = 0;
static FT_Library ftlibrary;
static FTC_Manager ftcmanager;
@ -25,6 +30,7 @@ struct sftd_font {
};
FTC_CMapCache cmapcache;
FTC_ImageCache imagecache;
texture_atlas *tex_atlas;
};
static FT_Error ftc_face_requester(FTC_FaceID face_id, FT_Library library, FT_Pointer request_data, FT_Face *face)
@ -106,6 +112,8 @@ sftd_font *sftd_load_font_file(const char *filename)
FTC_ImageCache_New(ftcmanager, &font->imagecache);
font->from = SFTD_LOAD_FROM_FILE;
font->tex_atlas = texture_atlas_create(ATLAS_DEFAULT_W, ATLAS_DEFAULT_H,
TEXFMT_RGBA8, SF2D_PLACE_RAM);
return font;
}
@ -120,6 +128,8 @@ sftd_font *sftd_load_font_mem(const void *buffer, unsigned int size)
FTC_ImageCache_New(ftcmanager, &font->imagecache);
font->from = SFTD_LOAD_FROM_MEM;
font->tex_atlas = texture_atlas_create(ATLAS_DEFAULT_W, ATLAS_DEFAULT_H,
TEXFMT_RGBA8, SF2D_PLACE_RAM);
return font;
}
@ -132,25 +142,40 @@ void sftd_free_font(sftd_font *font)
if (font->from == SFTD_LOAD_FROM_FILE) {
free(font->filename);
}
texture_atlas_free(font->tex_atlas);
free(font);
}
}
static void draw_bitmap(FT_Bitmap *bitmap, int x, int y, unsigned int color)
static int atlas_add_glyph(texture_atlas *atlas, unsigned int glyph_index, const FT_BitmapGlyph bitmap_glyph)
{
//This is too ugly
sf2d_texture *tex = sf2d_create_texture(bitmap->width, bitmap->rows, GPU_RGBA8, SF2D_PLACE_TEMP);
const FT_Bitmap *bitmap = &bitmap_glyph->bitmap;
unsigned int *buffer = malloc(bitmap->width * bitmap->rows * 4);
unsigned int w = bitmap->width;
unsigned int h = bitmap->rows;
int j, k;
for (j = 0; j < bitmap->rows; j++) {
for (k = 0; k < bitmap->width; k++) {
((u32 *)tex->data)[j*tex->pow2_w + k] = __builtin_bswap32((color & ~0xFF) | bitmap->buffer[j*bitmap->width + k]);
for (j = 0; j < h; j++) {
for (k = 0; k < w; k++) {
if (bitmap->pixel_mode == FT_PIXEL_MODE_MONO) {
buffer[j*w + k] =
(bitmap->buffer[j*bitmap->pitch + k/8] & (1 << (7 - k%8)))
? 0xFF : 0;
} else {
buffer[j*w + k] = 0x00FFFFFF | (bitmap->buffer[j*bitmap->pitch + k] << 24);
}
}
}
sf2d_texture_tile32(tex);
sf2d_draw_texture(tex, x, y);
sf2d_free_texture(tex);
int ret = texture_atlas_insert(atlas, glyph_index, buffer,
bitmap->width, bitmap->rows,
bitmap_glyph->left, bitmap_glyph->top,
bitmap_glyph->root.advance.x, bitmap_glyph->root.advance.y);
free(buffer);
return ret;
}
void sftd_draw_text(sftd_font *font, int x, int y, unsigned int color, unsigned int size, const char *text)
@ -166,7 +191,7 @@ void sftd_draw_text(sftd_font *font, int x, int y, unsigned int color, unsigned
FT_Bool use_kerning = FT_HAS_KERNING(face);
FT_UInt glyph_index, previous = 0;
int pen_x = x;
int pen_y = y;
int pen_y = y + size;
FTC_ScalerRec scaler;
scaler.face_id = face_id;
@ -185,16 +210,31 @@ void sftd_draw_text(sftd_font *font, int x, int y, unsigned int color, unsigned
pen_x += delta.x >> 6;
}
FTC_ImageCache_LookupScaler(font->imagecache, &scaler, flags, glyph_index, &glyph, NULL);
if (glyph->format == FT_GLYPH_FORMAT_BITMAP) {
FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)glyph;
if (!texture_atlas_exists(font->tex_atlas, glyph_index)) {
FTC_ImageCache_LookupScaler(font->imagecache, &scaler, flags, glyph_index, &glyph, NULL);
draw_bitmap(&bitmap_glyph->bitmap, pen_x + bitmap_glyph->left + x, pen_y - bitmap_glyph->top + y, color);
pen_x += bitmap_glyph->root.advance.x >> 16;
pen_y += bitmap_glyph->root.advance.y >> 16;
if (!atlas_add_glyph(font->tex_atlas, glyph_index, (FT_BitmapGlyph)glyph)) {
continue;
}
}
bp2d_rectangle rect;
int bitmap_left, bitmap_top;
int advance_x, advance_y;
texture_atlas_get(font->tex_atlas, glyph_index,
&rect, &bitmap_left, &bitmap_top,
&advance_x, &advance_y);
sf2d_draw_texture_part_blend(font->tex_atlas->tex,
pen_x + bitmap_left,
pen_y - bitmap_top,
rect.x, rect.y, rect.w, rect.h,
color);
pen_x += advance_x >> 16;
pen_y += advance_y >> 16;
previous = glyph_index;
text++;
}
@ -223,7 +263,7 @@ void sftd_draw_wtext(sftd_font *font, int x, int y, unsigned int color, unsigned
FT_Bool use_kerning = FT_HAS_KERNING(face);
FT_UInt glyph_index, previous = 0;
int pen_x = x;
int pen_y = y;
int pen_y = y + size;
FTC_ScalerRec scaler;
scaler.face_id = face_id;
@ -242,16 +282,31 @@ void sftd_draw_wtext(sftd_font *font, int x, int y, unsigned int color, unsigned
pen_x += delta.x >> 6;
}
FTC_ImageCache_LookupScaler(font->imagecache, &scaler, flags, glyph_index, &glyph, NULL);
if (glyph->format == FT_GLYPH_FORMAT_BITMAP) {
FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)glyph;
if (!texture_atlas_exists(font->tex_atlas, glyph_index)) {
FTC_ImageCache_LookupScaler(font->imagecache, &scaler, flags, glyph_index, &glyph, NULL);
draw_bitmap(&bitmap_glyph->bitmap, pen_x + bitmap_glyph->left + x, pen_y - bitmap_glyph->top + y, color);
pen_x += bitmap_glyph->root.advance.x >> 16;
pen_y += bitmap_glyph->root.advance.y >> 16;
if (!atlas_add_glyph(font->tex_atlas, glyph_index, (FT_BitmapGlyph)glyph)) {
continue;
}
}
bp2d_rectangle rect;
int bitmap_left, bitmap_top;
int advance_x, advance_y;
texture_atlas_get(font->tex_atlas, glyph_index,
&rect, &bitmap_left, &bitmap_top,
&advance_x, &advance_y);
sf2d_draw_texture_part_blend(font->tex_atlas->tex,
pen_x + bitmap_left,
pen_y - bitmap_top,
rect.x, rect.y, rect.w, rect.h,
color);
pen_x += advance_x >> 16;
pen_y += advance_y >> 16;
previous = glyph_index;
text++;
}

View file

@ -0,0 +1,86 @@
#include <stdlib.h>
#include <string.h>
#include "texture_atlas.h"
texture_atlas *texture_atlas_create(int width, int height, sf2d_texfmt format, sf2d_place place)
{
texture_atlas *atlas = malloc(sizeof(*atlas));
if (!atlas)
return NULL;
bp2d_rectangle rect;
rect.x = 0;
rect.y = 0;
rect.w = width;
rect.h = height;
atlas->tex = sf2d_create_texture(width, height, format, place);
sf2d_texture_tile32(atlas->tex);
atlas->bp_root = bp2d_create(&rect);
atlas->htab = int_htab_create(256);
return atlas;
}
void texture_atlas_free(texture_atlas *atlas)
{
sf2d_free_texture(atlas->tex);
bp2d_free(atlas->bp_root);
int_htab_free(atlas->htab);
free(atlas);
}
int texture_atlas_insert(texture_atlas *atlas, unsigned int character, const void *image, int width, int height, int bitmap_left, int bitmap_top, int advance_x, int advance_y)
{
bp2d_size size;
size.w = width;
size.h = height;
bp2d_position pos;
if (bp2d_insert(atlas->bp_root, &size, &pos) == 0)
return 0;
atlas_htab_entry *entry = malloc(sizeof(*entry));
entry->rect.x = pos.x;
entry->rect.y = pos.y;
entry->rect.w = width;
entry->rect.h = height;
entry->bitmap_left = bitmap_left;
entry->bitmap_top = bitmap_top;
entry->advance_x = advance_x;
entry->advance_y = advance_y;
int_htab_insert(atlas->htab, character, entry);
int i, j;
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
sf2d_set_pixel(atlas->tex, pos.x + j, pos.y + i, *(unsigned int *)(image + (j + i*width)*4));
}
}
GSPGPU_FlushDataCache(NULL, atlas->tex->data, atlas->tex->data_size);
return 1;
}
int texture_atlas_exists(texture_atlas *atlas, unsigned int character)
{
return int_htab_find(atlas->htab, character) != NULL;
}
void texture_atlas_get(texture_atlas *atlas, unsigned int character, bp2d_rectangle *rect, int *bitmap_left, int *bitmap_top, int *advance_x, int *advance_y)
{
atlas_htab_entry *entry = int_htab_find(atlas->htab, character);
rect->x = entry->rect.x;
rect->y = entry->rect.y;
rect->w = entry->rect.w;
rect->h = entry->rect.h;
*bitmap_left = entry->bitmap_left;
*bitmap_top = entry->bitmap_top;
*advance_x = entry->advance_x;
*advance_y = entry->advance_y;
}