hackrf/firmware/common/rad1o_render.c
2022-01-09 23:27:00 +01:00

623 lines
16 KiB
C

#include "rad1o_render.h"
#include "rad1o_decoder.h"
#include "rad1o_fonts.h"
#include "rad1o_smallfonts.h"
#include "rad1o_display.h"
#include <string.h>
#include <math.h>
void swap( int * a, int * b )
{
int x = *a;
*a = *b;
*b = x;
}
void swapd( float * a, float * b )
{
float x = *a;
*a = *b;
*b = x;
}
typedef struct
{
float x, y, z;
} Vector3D;
Vector3D addVec3D( Vector3D a, Vector3D b )
{
Vector3D v = {a.x+b.x, a.y+b.y, a.z+b.z};
return v;
}
Vector3D subVec3D( Vector3D a, Vector3D b )
{
Vector3D v = {a.x-b.x, a.y-b.y, a.z-b.z};
return v;
}
float lengthVec3D( Vector3D v )
{
return sqrt( v.x*v.x + v.y*v.y + v.z*v.z );
}
Vector3D roundVec3D( Vector3D v )
{
Vector3D r = { (int)(v.x + 0.5),
(int)(v.y + 0.5),
(int)(v.z + 0.5) };
return r;
}
Vector3D multMat3DVec3D( float* mat, Vector3D v )
{
Vector3D r = { mat[0] * v.x + mat[1] * v.y + mat[2] * v.z,
mat[3] * v.x + mat[4] * v.y + mat[5] * v.z,
mat[6] * v.x + mat[7] * v.y + mat[8] * v.z };
return r;
}
/* Global Variables */
const struct FONT_DEF * font = NULL;
struct EXTFONT efont;
uint8_t color_bg = 0xff; /* background color */
uint8_t color_fg = 0x00; /* foreground color */
/* Exported Functions */
void setTextColor(uint8_t bg, uint8_t fg){
color_bg = bg;
color_fg = fg;
}
void setIntFont(const struct FONT_DEF * newfont){
memcpy(&efont.def,newfont,sizeof(struct FONT_DEF));
efont.type=FONT_INTERNAL;
font=&efont.def;
}
void setExtFont(const char *fname){
#if 0
if(strlen(fname)>8+4)
return;
strcpy(efont.name,fname);
efont.type=FONT_EXTERNAL;
font=NULL;
UINT res;
res=f_open(&file, efont.name, FA_OPEN_EXISTING|FA_READ);
if(res!=FR_OK){
efont.type=0;
font=&Font_7x8;
}else{
_getFontData(START_FONT,0);
font=&efont.def;
};
#endif
}
int getFontHeight(void){
if(font)
return font->u8Height;
return 8; // XXX: Should be done right.
}
#if 0
static uint8_t read_byte (void)
{
UINT readbytes;
uint8_t byte;
UINT res;
res=f_read(&file, &byte, sizeof(uint8_t), &readbytes);
if(res!=FR_OK || readbytes!=1){
ASSERT(0) ;
};
return byte;
}
int _getFontData(int type, int offset){
UINT readbytes;
UINT res;
static uint16_t extras;
static uint16_t character;
// static const void * ptr;
if(efont.type == FONT_EXTERNAL){
if (type == START_FONT){
efont.def.u8Width = read_byte ();
efont.def.u8Height = read_byte ();
efont.def.u8FirstChar = read_byte ();
efont.def.u8LastChar = read_byte ();
res = f_read(&file, &extras, sizeof(uint16_t), &readbytes);
ASSERT(res==FR_OK);
return 0;
};
if (type == SEEK_EXTRAS){
f_lseek(&file,6);
return 0;
};
if(type == GET_EXTRAS){
uint16_t word;
res = f_read(&file, &word, sizeof(uint16_t), &readbytes);
ASSERT(res==FR_OK);
return word;
};
if (type == SEEK_WIDTH){
f_lseek(&file,6+(extras*sizeof(uint16_t)));
return 0;
};
if(type == GET_WIDTH || type == GET_DATA){
return read_byte ();
};
if(type == SEEK_DATA){
character=offset;
f_lseek(&file,6+
(extras*sizeof(uint16_t))+
((extras+font->u8LastChar-font->u8FirstChar)*sizeof(uint8_t))+
(offset*sizeof(uint8_t))
);
return 0;
};
if(type == PEEK_DATA){
uint8_t width;
width = read_byte ();
f_lseek(&file,6+
(extras*sizeof(uint16_t))+
((extras+font->u8LastChar-font->u8FirstChar)*sizeof(uint8_t))+
(character*sizeof(uint8_t))
);
return width;
};
#ifdef NOTYET
}else{ // efont.type==FONT_INTERNAL
if (type == START_FONT){
memcpy(&efont.def,font,sizeof(struct FONT_DEF));
return 0;
};
if (type == SEEK_EXTRAS){
ptr=efont.def.charExtra;
return 0;
};
if(type == GET_EXTRAS){
uint16_t word;
word=*(uint16_t*)ptr;
ptr=((uint16_t*)ptr)+1;
return word;
};
if (type == SEEK_WIDTH){
ptr=efont.def.charInfo;
return 0;
};
if(type == GET_WIDTH || type == GET_DATA){
uint8_t width;
width=*(uint8_t*)ptr;
ptr=((uint8_t*)ptr)+1;
return width;
};
if(type == SEEK_DATA){
if(offset==REPEAT_LAST_CHARACTER)
offset=character;
else
character=offset;
ptr=efont.def.au8FontTable;
return 0;
};
#endif
};
/* NOTREACHED */
return 0;
}
#endif
static int _getIndex(int c){
#define ERRCHR (font->u8FirstChar+1)
/* Does this font provide this character? */
if(c<font->u8FirstChar)
c=ERRCHR;
if(c>font->u8LastChar && efont.type!=FONT_EXTERNAL && font->charExtra == NULL)
c=ERRCHR;
if(c>font->u8LastChar && (efont.type==FONT_EXTERNAL || font->charExtra != NULL)){
if(efont.type==FONT_EXTERNAL){
#if 0
_getFontData(SEEK_EXTRAS,0);
int cc=0;
int cache;
while( (cache=_getFontData(GET_EXTRAS,0)) < c)
cc++;
if( cache > c)
c=ERRCHR;
else
c=font->u8LastChar+cc+1;
#endif
}else{
int cc=0;
while( font->charExtra[cc] < c)
cc++;
if(font->charExtra[cc] > c)
c=ERRCHR;
else
c=font->u8LastChar+cc+1;
};
};
c-=font->u8FirstChar;
return c;
}
uint8_t charBuf[MAXCHR];
int DoChar(int sx, int sy, int c){
if(font==NULL){
font=&Font_7x8;
};
/* how many bytes is it high? */
char height=(font->u8Height-1)/8+1;
char hoff=(8-(font->u8Height%8))%8;
const uint8_t * data;
int width,preblank=0,postblank=0;
do { /* Get Character data */
/* Get intex into character list */
c=_getIndex(c);
/* starting offset into character source data */
int toff=0;
if(font->u8Width==0){
if(efont.type == FONT_EXTERNAL){
#if 0
_getFontData(SEEK_WIDTH,0);
for(int y=0;y<c;y++)
toff+=_getFontData(GET_WIDTH,0);
width=_getFontData(GET_WIDTH,0);
_getFontData(SEEK_DATA,toff);
UINT res;
UINT readbytes;
UINT size = width * height;
if(size > MAXCHR) size = MAXCHR;
res = f_read(&file, charBuf, size, &readbytes);
if(res != FR_OK || readbytes<width*height)
return sx;
data=charBuf;
#endif
}else{
for(int y=0;y<c;y++)
toff+=font->charInfo[y].widthBits;
width=font->charInfo[c].widthBits;
toff*=height;
data=&font->au8FontTable[toff];
};
postblank=1;
}else if(font->u8Width==1){ // NEW CODE
if(efont.type == FONT_EXTERNAL){
#if 0
_getFontData(SEEK_WIDTH,0);
for(int y=0;y<c;y++)
toff+=_getFontData(GET_WIDTH,0);
width=_getFontData(GET_WIDTH,0);
_getFontData(SEEK_DATA,toff);
UINT res;
UINT readbytes;
uint8_t testbyte;
testbyte = read_byte ();
if(testbyte>>4 ==15){
preblank = read_byte ();
postblank = read_byte ();
width-=3;
width/=height;
UINT size = width * height;
if(size > MAXCHR) size = MAXCHR;
res = f_read(&file, charBuf, size, &readbytes);
if(res != FR_OK || readbytes<width*height)
return sx;
data=charBuf;
}else{
_getFontData(SEEK_DATA,toff);
data=pk_decode(NULL,&width); // Hackety-hack
};
#endif
}else{
// Find offset and length for our character
for(int y=0;y<c;y++)
toff+=font->charInfo[y].widthBits;
width=font->charInfo[c].widthBits;
if(font->au8FontTable[toff]>>4 == 15){ // It's a raw character!
preblank = font->au8FontTable[toff+1];
postblank= font->au8FontTable[toff+2];
data=&font->au8FontTable[toff+3];
width=(width-3/height);
}else{
data=pk_decode(&font->au8FontTable[toff],&width);
}
};
}else{
toff=(c)*font->u8Width*1;
width=font->u8Width;
data=&font->au8FontTable[toff];
};
}while(0);
#define xy_(x,y) ((x<0||y<0||x>=RESX||y>=RESY)?0:(y)*RESX+(x))
#define gPx(x,y) (data[x*height+(height-y/8-1)]&(1<<(y%8)))
int x=0;
/* Our display height is non-integer. Adjust for that. */
// sy+=RESY%8;
/* Fonts may not be byte-aligned, shift up so the top matches */
sy-=hoff;
sx+=preblank;
/* per line */
for(int y=hoff;y<height*8;y++){
if(sy+y>=RESY)
continue;
/* Optional: empty space to the left */
for(int b=1;b<=preblank;b++){
if(sx>=RESX)
continue;
lcdBuffer[xy_(sx-b,sy+y)]=color_bg;
};
/* Render character */
for(x=0;x<width;x++){
if(sx+x>=RESX)
continue;
if (gPx(x,y)){
lcdBuffer[xy_(sx+x,sy+y)]=color_fg;
}else{
lcdBuffer[xy_(sx+x,sy+y)]=color_bg;
};
};
/* Optional: empty space to the right */
for(int m=0;m<postblank;m++){
if(sx+x+m>=RESX)
continue;
lcdBuffer[xy_(sx+x+m,sy+y)]=color_bg;
};
};
return sx+(width+postblank);
}
#define UTF8
// decode 2 and 3-byte utf-8 strings.
#define UT2(a) ( ((a[0]&31)<<6) + (a[1]&63) )
#define UT3(a) ( ((a[0]&15)<<12) + ((a[1]&63)<<6) + (a[2]&63) )
int DoString(int sx, int sy, const char *s){
const char *c;
int uc;
for(c=s;*c!=0;c++){
#ifdef UTF8
/* will b0rk on non-utf8 */
if((*c&(128+64+32))==(128+64) && c[1]!=0){
uc=UT2(c); c+=1;
sx=DoChar(sx,sy,uc);
}else if( (*c&(128+64+32+16))==(128+64+32) && c[1]!=0 && c[2] !=0){
uc=UT3(c); c+=2;
sx=DoChar(sx,sy,uc);
}else
#endif
sx=DoChar(sx,sy,*c);
};
return sx;
}
void DoRect( int x, int y, int width, int height )
{
for(int py = y; py < y+height; ++py)
{
uint8_t * p = &lcdBuffer[py*RESX];
for(int px = x; px < x+width; ++px)
{
p[px] = color_fg;
}
}
}
int abs( int x )
{
return x < 0 ? -x : x;
}
void DoLine(int x1, int y1, int x2, int y2 )
{
if( (x1 < 0 && x2 < 0)
|| (y1 < 0 && y2 < 0)
|| (x1 >= RESX && x2 >= RESX )
|| (y1 >= RESY && y2 >= RESY )) return;
int dx = abs(x2-x1), sx = x1<x2 ? 1 : -1;
int dy = abs(y2-y1), sy = y1<y2 ? 1 : -1;
if(dx != 0)
{
float m = dy/(float)(dx*sx*sy);
float n = y1 - m*x1+0.5;
// y = mx+n
if( x1 < 0 )
{
x1 = 0; y1 = (int)(n);
}
else if( x1 >= RESX )
{
x1 = RESX - 1; y1 = (int)(x1*m + n);
}
if( x2 < 0 )
{
x2 = 0; y2 = (int)(n);
}
else if( x2 >= RESX)
{
x2 = RESX - 1; y2 = (int)(x2*m + n);
}
}
if(dy != 0)
{
float q = dx/(float)(dy*sx*sy);
float r = x1 - q*y1+0.5;
// x = qy+r
if( y1 < 0 )
{
y1 = 0; x1 = (int)(r);
}
else if( y1 >= RESY)
{
y1 = RESY - 1; x1 = (int)(y1*q + r);
}
if( y2 < 0 )
{
y2 = 0; x2 = (int)(r);
}
else if( y2 >= RESY)
{
y2 = RESY - 1; x2 = (int)(y2*q + r);
}
}
if(x1 < 0 || y1 < 0 || x2 < 0 || y2 < 0
|| x1 >= RESX || y1 >= RESY
|| x2 >= RESX || y2 >= RESY)
return;
if(y1>y2)
{
int xz = x1, yz = y1;
x1 = x2; y1 = y2;
x2 = xz; y2 = yz;
}
dx = abs(x2-x1); sx = x1<x2 ? 1 : -1;
dy = y1-y2; sy = y1<y2 ? 1 : -1;
int err = dx+dy, e2;
int* pxmin = x1<x2 ? &x1: &x2;
int* pxmax = x1>x2 ? &x1: &x2;
while(*pxmin < *pxmax || y1 < y2)
{
lcdBuffer[xy_(x1,y1)]=color_fg;
e2 = 2*err;
if (e2 > dy) { err += dy; x1 += sx; }
if (e2 < dx) { err += dx; y1 ++; }
}
}
void DoCube( int* p, int len, float* r )
{
int px = p[0];
int py = p[1];
int pz = p[2];
float rx = r[0];
float ry = r[1];
float rz = r[2];
float cx = cos(rx), sx = sin(rx);
float cy = cos(ry), sy = sin(ry);
float cz = cos(rz), sz = sin(rz);
float rotation[9] = { cy*cz, -cy*sz, sy,
cx*sz+cz*sx*sy, cx*cz-sx*sy*sz, -cy*sx,
sx*sz-cx*cz*sy, cz*sx+cx*sy*sz, cx*cy };
Vector3D xAxis = { len * 0.5, 0, 0 };
Vector3D yAxis = { 0, len * 0.5, 0 };
Vector3D zAxis = { 0, 0, len * 0.5 };
xAxis = multMat3DVec3D( rotation, xAxis );
yAxis = multMat3DVec3D( rotation, yAxis );
zAxis = multMat3DVec3D( rotation, zAxis );
Vector3D p000 = { px, py, pz };
Vector3D p100 = { px, py, pz };
Vector3D p010 = { px, py, pz };
Vector3D p110 = { px, py, pz };
Vector3D p001 = { px, py, pz };
Vector3D p101 = { px, py, pz };
Vector3D p011 = { px, py, pz };
Vector3D p111 = { px, py, pz };
p000 = roundVec3D( subVec3D( subVec3D( subVec3D( p000, xAxis ), yAxis ), zAxis ) );
p100 = roundVec3D( subVec3D( subVec3D( addVec3D( p100, xAxis ), yAxis ), zAxis ) );
p010 = roundVec3D( subVec3D( addVec3D( subVec3D( p010, xAxis ), yAxis ), zAxis ) );
p110 = roundVec3D( subVec3D( addVec3D( addVec3D( p110, xAxis ), yAxis ), zAxis ) );
p001 = roundVec3D( addVec3D( subVec3D( subVec3D( p001, xAxis ), yAxis ), zAxis ) );
p101 = roundVec3D( addVec3D( subVec3D( addVec3D( p101, xAxis ), yAxis ), zAxis ) );
p011 = roundVec3D( addVec3D( addVec3D( subVec3D( p011, xAxis ), yAxis ), zAxis ) );
p111 = roundVec3D( addVec3D( addVec3D( addVec3D( p111, xAxis ), yAxis ), zAxis ) );
// 010_________110
// /| /|
// / | / |
// 000/__+____/100|
// | | | |
// 011|____|___|111
// | / | /
// | / | /
// |/______|/
// 001 101
DoLine( p000.x, p000.y, p100.x, p100.y );
DoLine( p000.x, p000.y, p010.x, p010.y );
DoLine( p000.x, p000.y, p001.x, p001.y );
DoLine( p111.x, p111.y, p011.x, p011.y );
DoLine( p111.x, p111.y, p101.x, p101.y );
DoLine( p111.x, p111.y, p110.x, p110.y );
DoLine( p011.x, p011.y, p001.x, p001.y );
DoLine( p011.x, p011.y, p010.x, p010.y );
DoLine( p010.x, p010.y, p110.x, p110.y );
DoLine( p100.x, p100.y, p110.x, p110.y );
DoLine( p100.x, p100.y, p101.x, p101.y );
DoLine( p001.x, p001.y, p101.x, p101.y );
}
void DoMesh( float* verts, int numbVerts, int* faces, int numbfaces, float* rot, int* p, int scale )
{
float rx = rot[0];
float ry = rot[1];
float rz = rot[2];
float cx = cos(rx), sx = sin(rx);
float cy = cos(ry), sy = sin(ry);
float cz = cos(rz), sz = sin(rz);
float rotation[9] = { scale*cy*cz, scale*-cy*sz, -scale*sy,
scale*cx*sz+cz*sx*sy, scale*cx*cz-sx*sy*sz, -scale*-cy*sx,
scale*sx*sz-cx*cz*sy, scale*cz*sx+cx*sy*sz, -scale*cx*cy };
Vector3D transformed[numbVerts];
Vector3D* vertsVec = (Vector3D*)verts;
Vector3D pos = {p[0], p[1], p[2] };
for( int i = 0; i < numbVerts; ++i )
{
transformed[i] = roundVec3D( addVec3D( multMat3DVec3D( rotation, vertsVec[i]), pos ) );
}
for( int i = 0; i < numbfaces; ++i )
{
Vector3D v1 = transformed[ faces[i*3 + 0] ];
Vector3D v2 = transformed[ faces[i*3 + 1] ];
Vector3D v3 = transformed[ faces[i*3 + 2] ];
DoLine( v1.x, v1.y, v2.x, v2.y );
DoLine( v2.x, v2.y, v3.x, v3.y );
DoLine( v3.x, v3.y, v1.x, v1.y );
}
}