📄 gu_font.c
字号:
/*
PMP Mod
Copyright (C) 2006 Raphael
E-mail: raphael@fx-world.org
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
font rendering system
*/
#include <pspkernel.h>#include <pspiofilemgr.h>#include <pspgu.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include "valloc.h"
#include "gu_font.h"
unsigned short __attribute__((aligned(16))) gu_font_clut[16];
struct gu_font_struct* gu_cur_font = 0; // Current set font
static struct gu_font_manager_struct gu_font_manager;
static struct gu_glyph_cache_manager_struct gu_glyph_cache_manager;
static struct gu_glyph_cache_struct gu_glyph_temp_cache; // temporary texture for bypassing glyph cache system
static int gu_font_initialized = 0;
static int gu_font_border = 1;
static int gu_font_color = 0xffffff;
static int gu_font_border_color = 0;
#define IsSet(val,flag) (val&flag)==flag
#define FONTHEIGHT (gu_cur_font==0?0:gu_cur_font->chars[(unsigned char)'I'].byte_height+3)
#define CHARWIDTH(c) (gu_cur_font==0?0:gu_cur_font->chars[(unsigned char)c].pixel_width)
#define CHARHEIGHT(c) (gu_cur_font==0?0:gu_cur_font->chars[(unsigned char)c].byte_height)
#ifdef DEBUG
void gu_debug_print_glyph_cache()
{
FILE* f = fopen("debug.txt","a+");
if (!f) return;
fprintf(f, "\nDEBUGPRINT GLYPH CACHE:\n");
fprintf(f, "--------------------------------------------\n\n");
struct gu_glyph_cache_list_struct* list = gu_glyph_cache_manager.root;
if (list == 0) return;
int i = 1;
while (list!=0)
{
{
fprintf(f, "----------------\n");
fprintf(f, "GLYPH CACHE NO. %i\n", i);
fprintf(f, "list->\n");
fprintf(f, "----------------\n");
fprintf(f, "lru_index : %i\n", list->lru_index);
fprintf(f, "cache->cacheptr: 0x%x\n", list->cache->cacheptr);
fprintf(f, "cache->size : 0x%x\n", list->cache->size);
fprintf(f, "cache->width : %i\n", list->cache->width);
fprintf(f, "cache->height : %i\n", list->cache->height);
fprintf(f, "cache->flags : %i\n", list->cache->flags);
fprintf(f, "cache->font_id : %i\n", list->cache->font_id);
fprintf(f, "cache->dirty : %i\n", list->cache->dirty);
fprintf(f, "cache->string : %s\n", list->cache->string);
fprintf(f, "----------------\n\n");
}
list = list->next;
i++;
}
fprintf(f,"\n\n--------------------------------------------\nFINISHED\n");
fclose(f);
}
void gu_debug_print_charset(){ if (gu_cur_font == 0) return; FILE* f = fopen("debug.txt","a+");
if (!f) return;
fprintf(f, "\nDEBUGPRINT CHARSET:\n");
fprintf(f, "--------------------------------------------\n\n"); int i;
char letter[2];
letter[1] = '\0';
for (i=0;i<256;i++)
{
{
letter[0] = i;
fprintf(f, "----------------\n");
fprintf(f, "CHARACTER: %s(%i)\n", letter, i);
fprintf(f, "----------------\n");
fprintf(f, "byte_width : %i\n", gu_cur_font->chars[i].byte_width);
fprintf(f, "byte_height : %i\n", gu_cur_font->chars[i].byte_height);
fprintf(f, "x_offset : %i\n", gu_cur_font->chars[i].x_offset);
fprintf(f, "y_offset : %i\n", gu_cur_font->chars[i].y_offset);
fprintf(f, "pixel_width : %i\n", gu_cur_font->chars[i].pixel_width);
fprintf(f, "pixel_height : %i\n", gu_cur_font->chars[i].pixel_height);
}
}
fprintf(f,"\n\n--------------------------------------------\nFINISHED\n");
fclose(f);}
#endif
void gu_glyph_cache_safe_constructor(struct gu_glyph_cache_struct *p)
{
if (p==0) return;
p->cacheptr = 0;
p->width = 0;
p->height = 0;
p->flags = 0;
p->string[0] = '\0';
}
void gu_glyph_cache_safe_destructor(struct gu_glyph_cache_struct *p)
{
if (p==0) return;
if (p->cacheptr!=0)
vfree( p->cacheptr );
p->cacheptr = 0;
p->width = 0;
p->height = 0;
p->flags = 0;
p->string[0] = '\0';
p->dirty = 0;
}
void gu_glyph_cache_list_safe_constructor(struct gu_glyph_cache_list_struct *p)
{
if (p==0) return;
p->cache = 0;
p->next = 0;
p->prev = 0;
p->lru_index = 0;
}
void gu_glyph_cache_list_safe_destructor(struct gu_glyph_cache_list_struct *p)
{
if (p==0) return;
gu_glyph_cache_list_safe_destructor(p->next);
gu_glyph_cache_safe_destructor(p->cache);
p->cache = 0;
p->next = 0;
p->prev = 0;
p->lru_index = 0;
}
struct gu_glyph_cache_struct* gu_glyph_cache_create( unsigned int size )
{
struct gu_glyph_cache_struct* p = malloc(sizeof(struct gu_glyph_cache_struct));
if (p==0) return(0);
gu_glyph_cache_safe_constructor(p);
p->cacheptr = valloc( size );
if (p->cacheptr==0)
{
free(p);
return(0);
}
p->flags = GLYPH_CACHE_VRAM;
p->size = size;
p->dirty = 1;
return(p);
}
struct gu_glyph_cache_list_struct* gu_glyph_cache_list_create( unsigned int size )
{
struct gu_glyph_cache_list_struct *p = malloc(sizeof(struct gu_glyph_cache_list_struct));
if (p==0) return(0);
gu_glyph_cache_list_safe_constructor(p);
p->cache = gu_glyph_cache_create( size );
if (p->cache==0)
{
free(p);
return(0);
}
return(p);
}
char* gu_glyph_cache_init()
{
if (gu_font_initialized==1) return(0);
gu_glyph_cache_manager.lru_counter = 0;
gu_glyph_cache_manager.num_caches = 0;
if (gu_glyph_cache_manager.root==0)
gu_glyph_cache_manager.root = gu_glyph_cache_list_create( 256*MAX_CACHE_LINES*16 );
if (gu_glyph_cache_manager.root==0)
return("gu_glyph_cache_init: failed to create glyph cache root");
gu_glyph_cache_manager.num_caches++;
struct gu_glyph_cache_list_struct* new_cache;
while(gu_glyph_cache_manager.num_caches<MAX_GLYPH_CACHES)
{
new_cache = gu_glyph_cache_list_create( 256*MAX_CACHE_LINES*16 );
if (new_cache==0)
break;
new_cache->next = gu_glyph_cache_manager.root;
gu_glyph_cache_manager.root->prev = new_cache;
gu_glyph_cache_manager.root = new_cache;
gu_glyph_cache_manager.num_caches++;
}
// Try to allocate temporary cache in VRAM first
gu_glyph_cache_safe_constructor(&gu_glyph_temp_cache);
//gu_glyph_temp_cache.cacheptr = valloc( 256*MAX_CACHE_LINES*16 );
gu_glyph_temp_cache.flags = GLYPH_CACHE_VRAM;
if (gu_glyph_temp_cache.cacheptr==0)
{
gu_glyph_temp_cache.cacheptr = memalign(16, 256*MAX_CACHE_LINES*16 );
if (gu_glyph_temp_cache.cacheptr==0)
return("gu_glyph_cache_init: memalign failed on gu_glyph_temp_cache");
gu_glyph_temp_cache.flags = GLYPH_CACHE_SYSMEM;
}
gu_glyph_temp_cache.dirty = 1; // temporary cache is always dirty
return(0);
}
void gu_glyph_cache_free()
{
if (IsSet(gu_glyph_temp_cache.flags,GLYPH_CACHE_VRAM))
vfree( gu_glyph_temp_cache.cacheptr );
else if (IsSet(gu_glyph_temp_cache.flags,GLYPH_CACHE_SYSMEM))
free( gu_glyph_temp_cache.cacheptr );
gu_glyph_temp_cache.flags = 0;
gu_glyph_cache_list_safe_destructor( gu_glyph_cache_manager.root );
gu_glyph_cache_manager.num_caches = 0;
gu_glyph_cache_manager.lru_counter = 0;
}
// return a pointer to a cache texture useable by the GU
struct gu_glyph_cache_struct* gu_glyph_cache_manager_get( char* s, int width, int height, int flags, int font_id )
{
if (IsSet(flags,FLAG_NOCACHE) || gu_glyph_cache_manager.num_caches==0)
{
if (gu_glyph_temp_cache.cacheptr==0) return(0);
memset(gu_glyph_temp_cache.cacheptr, 0, 256*height);
sceKernelDcacheWritebackAll();
return(&gu_glyph_temp_cache);
}
// Check if a cache with s, flags and font_id already exists
struct gu_glyph_cache_list_struct* list = gu_glyph_cache_manager.root;
if (list == 0) return(0);
while (list!=0)
{
if (list->cache->flags==(flags&FLAG_ALIGN_MASK) &&
list->cache->font_id==font_id &&
strcmp(list->cache->string,s)==0)
{
// cache-hit
list->lru_index = gu_glyph_cache_manager.lru_counter++;
list->cache->width = width;
list->cache->height = height;
list->cache->dirty = 0; // cache doesn't need to be regenerated
return(list->cache);
}
list = list->next;
}
list = gu_glyph_cache_manager.root;
struct gu_glyph_cache_list_struct* lru = list;
// cache-miss
while (list->next!=0)
{
list = list->next;
if (lru->lru_index>list->lru_index)
lru = list;
}
lru->lru_index = gu_glyph_cache_manager.lru_counter++;
if (lru->cache==0) return(0);
lru->cache->width = width;
lru->cache->height = height;
lru->cache->dirty = 1;
lru->cache->font_id = font_id;
lru->cache->flags = (flags&FLAG_ALIGN_MASK);
strncpy((char*)lru->cache->string,s,MAX_CACHE_STRING-1);
memset(lru->cache->cacheptr, 0, 256*height );
sceKernelDcacheWritebackAll();
return(lru->cache);
}
/* sw always 16 so there's always 64bit to copy for each line s1 s2 s = |xxxxxxxx|yyyyyyyy| sx = 0 s1 << (3*4) = |000xxxxx| s1 >> (5*4) = |xxx00000| s2 << (3*4) = |000yyyyy| s2 >> (5*4) = |yyy00000| d = |00000000|00000000|00000000| dx = 3 => d' = |000xxxxx|xxxyyyyy|yyy00000| */inline void gu_font_copy_glyph( int sx, int sy, int sh, char *s, int dx, int dy, char *d ){ int i; if ((dx&0x7)==0) { unsigned int* u32s = (unsigned int*)((unsigned int)s+((sx+(sy<<8)) >> 1)); unsigned int* u32d = (unsigned int*)((unsigned int)d+((dx+(dy<<9)) >> 1)); // can do fast copy for (i=0;i<sh;i++) { // 32bit copy part *u32d++ = *u32s++; *u32d++ = *u32s++; u32s += (128-8)>>2; u32d += (256-8)>>2; } } else { unsigned int* u32s = (unsigned int*)((unsigned int)s+((sx+(sy<<8)) >> 1)); unsigned int* u32d = (unsigned int*)((unsigned int)d+(((dx>>3)<<2)+(dy<<8))); unsigned int mask = 0; unsigned int shift = (dx&0x7)<<2; for (i=0;i<(dx&0x7);i++) { mask <<= 4; mask |= 0xf; } for (i=0;i<sh;i++) { unsigned int s1 = *u32s++; unsigned int s2 = *u32s++; // copy first halfbytes *u32d++ = ((*u32d) & mask)|(s1 << shift); // dst = |000xxxxx| *u32d++ = (s1 >> (32-shift)) | (s2 << shift); // dst = |000xxxxx|xxxyyyyy| // copy last halfbytes *u32d++ = ((*u32d) & ~mask)|(s2 >> (32-shift)); // dst = |000xxxxx|xxxyyyyy|yyy00000| u32s += (128-8)>>2; u32d += (256-12)>>2; } }}inline void gu_font_cache_glyph( int x, int y, char c, char* cache ) { int sx = (int)((unsigned char)c % 16)*16; int sy = (int)((unsigned char)c / 16)*16;
int sw = gu_cur_font->chars[(unsigned char)c].pixel_width; int sh = gu_cur_font->chars[(unsigned char)c].byte_height; int dx = x+gu_cur_font->chars[(unsigned char)c].x_offset; int dy = y+gu_cur_font->chars[(unsigned char)c].y_offset; if (dy<0) { sy-=dy; sh+=dy; dy=0; } if (dx<0) { sx-=dx; sw+=dx; dx=0; } if (dy+sh>=272) { sh=271-dy; } //if (dx+sw>=480) { sw=479-dx; } //sw = (sw+3)>>2; if (sh==0 || sw==0) return; /*if ((dx&0x3)==0) sceGuCopyImage(GU_PSM_4444,sx>>2,sy,sw,sh,256>>2,g_font_tex[g_cur_font],dx>>2,dy,512>>2,(void*)((unsigned int)g_glyph_cache | 0x40000000)); else*/ gu_font_copy_glyph( sx, sy, sh, (char*)gu_cur_font->data, dx, dy, (char*)((unsigned int)cache | 0x40000000) ); }
inline void blit_fast( int x, int y, int w, int h, int x2, int y2 ){ int start; for (start = 0; start < w; start += 64, x2 += 64) { struct VertexInt* vertices = (struct VertexInt*)sceGuGetMemory(2 * sizeof(struct VertexInt)); int width = (start + 64) < w ? 64 : w-start; vertices[0].u = x + start; vertices[0].v = y; vertices[0].color = 0x3000; vertices[0].x = x2; vertices[0].y = y2; vertices[0].z = 0; vertices[1].u = x + start + width; vertices[1].v = y + h; vertices[1].color = 0x3000; vertices[1].x = x2 + width; vertices[1].y = y2 + h; vertices[1].z = 0; sceGuDrawArray(GU_SPRITES,GU_TEXTURE_16BIT|GU_COLOR_4444|GU_VERTEX_16BIT|GU_TRANSFORM_2D,2,0,vertices); }}
int strpos( const char* s, const char c ) { if (s==0) return -1; char* t = s; int i = 0; while (*t!='\0' && *t!=c)
{ t++;
i++; }
if (*t!=c) return -1; return i; }
void gu_font_cache_string( char* s, int maxwidth, int flags, char* cache )
{
char* c = s; int i = 0; int x = 0; int y = 10; // don't know why this needs to be, with 0 it will draw the first line off-screen
int linesize;
int numlines = 0; if (IsSet(flags,FLAG_ALIGN_CENTER))
{ x = ((maxwidth-gu_font_line_width_get( s))/2); } else if (IsSet(flags,FLAG_ALIGN_RIGHT)) { //linesize = strpos( s, '\n' );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -