2011-08-05 17:17:31 +00:00
|
|
|
/*
|
|
|
|
* o - a project called circle - that draws stuff
|
|
|
|
*
|
|
|
|
* Copyright (c) 2011 Øyvind Kolås <pippin@gimp.org>
|
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <display.h>
|
|
|
|
|
|
|
|
#ifdef SIMULATOR
|
|
|
|
#include <stdio.h>
|
|
|
|
#else
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define WIDTH RESX
|
|
|
|
#define HEIGHT RESY
|
|
|
|
|
|
|
|
#include "o.h"
|
|
|
|
|
|
|
|
/* the following defines turn on and of capabilities, most capabilities can
|
|
|
|
* be independently toggled, and the rest of the API should work with a smaller
|
|
|
|
* code footprint as things are removed.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define O_ENABLE_FILL
|
2011-08-11 07:16:31 +00:00
|
|
|
//#define O_ENABLE_STROKE
|
2011-08-05 17:17:31 +00:00
|
|
|
#define O_ENABLE_USER_SHADER
|
|
|
|
#define O_ENABLE_BW /* pick one ,. */
|
|
|
|
//#define O_ENABLE_GRAY
|
|
|
|
//#define O_ENABLE_GRAY_EXTRA
|
|
|
|
//#define O_ENABLE_RECTANGLE
|
|
|
|
//#define O_ENABLE_STACK
|
|
|
|
//#define O_ENABLE_CLIP
|
2011-08-06 00:37:43 +00:00
|
|
|
#define O_ENABLE_TRANSFORM
|
2011-08-05 17:17:31 +00:00
|
|
|
//#define O_ENABLE_TRANSFORM_FUNCS
|
|
|
|
//#define O_ENABLE_RENDER
|
|
|
|
//#define O_ENABLE_TEXT
|
|
|
|
//#define O_ENABLE_EXTERNAL_FONT /* needs O_ENABLE_TEXT */
|
|
|
|
//#define O_ENABLE_FONT_DUMP /* dumps font to font path upon first use */
|
|
|
|
//#define O_ENABLE_FILE /* compile file accessing calls */
|
|
|
|
|
|
|
|
#ifdef O_ENABLE_EXTERNAL_FONT
|
|
|
|
#ifndef O_ENABLE_FILE
|
|
|
|
#define O_ENABLE_FILE
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef O_ENABLE_EXTERNAL_CLIP
|
|
|
|
#ifndef O_ENABLE_FILL
|
|
|
|
#define O_ENABLE_FILL
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef O_ENABLE_TEXT
|
|
|
|
#ifndef O_ENABLE_STROKE
|
|
|
|
#define O_ENABLE_STROKE
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#define BEZIER_SEGMENTS 16
|
|
|
|
#define MAX_SPANS_PER_LINE 7 /* maximum number of intersecting spans on one line */
|
|
|
|
#define SPP 10 /* sup pixel precision divider, each pixel is 6 such units internally */
|
|
|
|
#define STACK_DEPTH 3 /* needs O_ENABLE_STACK */
|
|
|
|
#define FONT_PATH "/tmp/font.bin" /* if O_ENABLE_EXTERNAL_FONT */
|
|
|
|
#define FIXED_ONE 1023 /* XXX: using 1023 instead of 1024 gives smaller
|
|
|
|
code size.. evil.. */
|
|
|
|
|
|
|
|
typedef struct _Node Node;
|
|
|
|
typedef struct _Path Path;
|
|
|
|
typedef struct _Context Context;
|
|
|
|
|
|
|
|
#define CLAMP(val,min,max) ((val)<(min)?(min):(val)>(max)?(max):(val))
|
|
|
|
|
|
|
|
/* Default color generator shader */
|
|
|
|
static int shader_gray (int x, int y, void *data);
|
|
|
|
|
|
|
|
struct _Node
|
|
|
|
{
|
|
|
|
short int x;
|
|
|
|
short int y;
|
|
|
|
unsigned char type; /* can maybe be removed and put in context?
|
|
|
|
or < 0 values indicate move, and -127 indicate curve_to? */
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef O_ENABLE_CLIP
|
|
|
|
typedef struct _Clip Clip;
|
|
|
|
struct _Clip
|
|
|
|
{
|
|
|
|
unsigned char row[HEIGHT][2]; /* 0 start 1 end */
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
struct _Context
|
|
|
|
{
|
|
|
|
#ifdef O_ENABLE_USER_SHADER
|
|
|
|
ShaderFunction shader;
|
|
|
|
#endif
|
|
|
|
void *shader_data;
|
|
|
|
#ifdef O_ENABLE_TRANSFORM
|
|
|
|
OMatrix transform;
|
|
|
|
#endif
|
|
|
|
#ifdef O_ENABLE_STROKE
|
|
|
|
char line_width;
|
|
|
|
#endif
|
|
|
|
#ifdef O_ENABLE_TEXT
|
|
|
|
char font_size;
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
struct _Path
|
|
|
|
{
|
|
|
|
signed short int x;
|
|
|
|
signed short int y;
|
|
|
|
int count;
|
|
|
|
int max_size;
|
|
|
|
Node *nodes;
|
|
|
|
#ifdef O_ENABLE_STACK
|
|
|
|
Context *context;
|
|
|
|
int context_no;
|
|
|
|
#else
|
|
|
|
Context context;
|
|
|
|
#endif
|
|
|
|
#ifdef O_ENABLE_CLIP /* would be better if it was part of the
|
|
|
|
* context stack, but it uses a lot of ram
|
|
|
|
*/
|
|
|
|
Clip clip;
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
static Path *path = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
/* the actual inner draw function used for doing the real painting */
|
|
|
|
static void o_render_span(int x0, int y,
|
|
|
|
int x1,
|
|
|
|
#ifdef O_ENABLE_USER_SHADER
|
|
|
|
ShaderFunction shader,
|
|
|
|
#endif
|
|
|
|
void *shader_data)
|
|
|
|
{
|
|
|
|
#ifdef O_ENABLE_CLIP
|
|
|
|
x0 = CLAMP (x0, path->clip.row[y][0], path->clip.row[y][1]);
|
|
|
|
x1 = CLAMP (x1, path->clip.row[y][0], path->clip.row[y][1]);
|
|
|
|
#else
|
|
|
|
x0 = CLAMP (x0, 0, WIDTH);
|
|
|
|
x1 = CLAMP (x1, 0, WIDTH);
|
|
|
|
#endif
|
|
|
|
if (y <0 || y>=HEIGHT)
|
|
|
|
return;
|
|
|
|
for(int x=x0; x<x1; x++)
|
|
|
|
{
|
|
|
|
#ifdef O_ENABLE_USER_SHADER
|
|
|
|
lcdSetPixel(x,y, shader(x,y,shader_data));
|
|
|
|
#else
|
|
|
|
lcdSetPixel(x,y, shader_gray (x,y,shader_data));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef O_ENABLE_STACK
|
|
|
|
static Context *context (void)
|
|
|
|
{
|
|
|
|
return &path->context[path->context_no];
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#define context() (&path->context)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef O_ENABLE_TRANSFORM
|
|
|
|
|
|
|
|
/* copied from twin */
|
|
|
|
static void
|
|
|
|
o_matrix_multiply (OMatrix *result,
|
|
|
|
const OMatrix *a,
|
|
|
|
const OMatrix *b)
|
|
|
|
{
|
|
|
|
OMatrix r;
|
|
|
|
|
|
|
|
for (int row = 0; row < 3; row++)
|
|
|
|
for (int col = 0; col < 2; col++)
|
|
|
|
{
|
|
|
|
int t = (row == 2) ? t = b->m[2][col] : 0;
|
|
|
|
|
|
|
|
for (int n = 0; n < 2; n++)
|
|
|
|
t = t + ((a->m[row][n] * b->m[n][col])) / FIXED_ONE;
|
|
|
|
r.m[row][col] = t;
|
|
|
|
}
|
|
|
|
*result = r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
o_matrix_identity (OMatrix *m)
|
|
|
|
{
|
|
|
|
m->m[0][0] = m->m[1][1] = FIXED_ONE;
|
|
|
|
m->m[0][1] = m->m[1][0] = m->m[2][0] = m->m[2][1] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void o_transform (OMatrix *matrix, int replace)
|
|
|
|
{
|
|
|
|
OMatrix *m = &context()->transform;
|
|
|
|
if (replace)
|
|
|
|
context()->transform = *matrix;
|
|
|
|
else
|
|
|
|
o_matrix_multiply (m, matrix, m);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
o_identity (void)
|
|
|
|
{
|
|
|
|
o_matrix_identity (&context()->transform);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef O_ENABLE_TRANSFORM_FUNCS
|
|
|
|
static int o_sin(int x)
|
|
|
|
{
|
|
|
|
#define qN 13
|
|
|
|
#define qA 12
|
|
|
|
#define qP 15
|
|
|
|
#define qR (2*qN-qP)
|
|
|
|
#define qS (qN+qP+1-qA)
|
|
|
|
|
|
|
|
x= x<<(30-qN); // shift to full s32 range (Q13->Q30)
|
|
|
|
|
|
|
|
if( (x^(x<<1)) < 0) // test for quadrant 1 or 2
|
|
|
|
x= (1<<31) - x;
|
|
|
|
|
|
|
|
x= x>>(30-qN);
|
|
|
|
|
|
|
|
return (x * ( (3<<qP) - (x*x>>qR) ) >> qS );
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int o_cos(int x)
|
|
|
|
{
|
|
|
|
return o_sin(x + 8192);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
o_translate (int tx, int ty)
|
|
|
|
{
|
|
|
|
OMatrix t;
|
|
|
|
OMatrix *m = &context()->transform;
|
|
|
|
o_matrix_identity (&t);
|
|
|
|
|
2011-08-06 00:37:43 +00:00
|
|
|
t.m[2][0] = tx * SPP * FIXED_ONE / 1000;
|
|
|
|
t.m[2][1] = ty * SPP * FIXED_ONE / 1000;
|
2011-08-05 17:17:31 +00:00
|
|
|
o_matrix_multiply (m, &t, m);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
o_scale (int sx, int sy)
|
|
|
|
{
|
|
|
|
OMatrix t;
|
|
|
|
OMatrix *m = &context()->transform;
|
|
|
|
o_matrix_identity (&t);
|
|
|
|
t.m[0][0] = sx * FIXED_ONE / 1000;
|
|
|
|
t.m[1][1] = sy * FIXED_ONE / 1000;
|
|
|
|
o_matrix_multiply (m, &t, m);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
o_rotate (int angle)
|
|
|
|
{
|
|
|
|
OMatrix *m = &context()->transform;
|
|
|
|
OMatrix t;
|
|
|
|
int c = o_cos ( angle * 4 * 8192 / 3600 ) / 4;
|
|
|
|
int s = o_sin ( angle * 4 * 8192 / 3600 ) / 4;
|
|
|
|
|
|
|
|
t.m[0][0] = c; t.m[0][1] = s;
|
|
|
|
t.m[1][0] = -s; t.m[1][1] = c;
|
|
|
|
t.m[2][0] = 0; t.m[2][1] = 0;
|
|
|
|
o_matrix_multiply (m, &t, m);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef O_ENABLE_STROKE
|
|
|
|
|
|
|
|
static unsigned int o_sqrt (unsigned int n)
|
|
|
|
{
|
|
|
|
unsigned int root = 0, bit, trial;
|
|
|
|
bit = (n >= 0x10000) ? 1<<30 : 1<<14;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
trial = root+bit;
|
|
|
|
if (n >= trial)
|
|
|
|
{
|
|
|
|
n -= trial;
|
|
|
|
root = trial+bit;
|
|
|
|
}
|
|
|
|
root >>= 1;
|
|
|
|
bit >>= 2;
|
|
|
|
} while (bit);
|
|
|
|
return root;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
point_dist (Node *a,
|
|
|
|
Node *b)
|
|
|
|
{
|
|
|
|
return o_sqrt ((a->x-b->x)*(a->x-b->x) +
|
|
|
|
(a->y-b->y)*(a->y-b->y));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* linear interpolation between two points */
|
|
|
|
static void
|
|
|
|
lerp (Node *dest,
|
|
|
|
Node *a,
|
|
|
|
Node *b,
|
|
|
|
int t)
|
|
|
|
{
|
|
|
|
dest->x = a->x + (((b->x-a->x) * t) / FIXED_ONE);
|
|
|
|
dest->y = a->y + (((b->y-a->y) * t) / FIXED_ONE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* evaluate a point on a bezier-curve.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
bezier (Node **curve,
|
|
|
|
Node *dest,
|
|
|
|
int t)
|
|
|
|
{
|
|
|
|
Node ab,bc,cd,abbc,bccd;
|
|
|
|
|
|
|
|
lerp (&ab, curve[0], curve[1], t);
|
|
|
|
lerp (&bc, curve[1], curve[2], t);
|
|
|
|
lerp (&cd, curve[2], curve[3], t);
|
|
|
|
lerp (&abbc, &ab, &bc,t);
|
|
|
|
lerp (&bccd, &bc, &cd,t);
|
|
|
|
lerp (dest, &abbc, &bccd, t);
|
|
|
|
}
|
|
|
|
|
|
|
|
void o_current_point (int *x, int *y)
|
|
|
|
{
|
2011-08-06 00:37:43 +00:00
|
|
|
if (x) *x = path->x / SPP;
|
|
|
|
if (y) *y = path->y / SPP;
|
2011-08-05 17:17:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef O_ENABLE_STACK
|
|
|
|
void o_save (void)
|
|
|
|
{
|
|
|
|
path->context_no ++;
|
|
|
|
#ifdef SIMULATOR
|
|
|
|
if (path->context_no >= STACK_DEPTH)
|
|
|
|
{
|
|
|
|
fprintf (stderr, "stack depth bust\n");
|
|
|
|
}
|
|
|
|
/* copy existing state */
|
|
|
|
path->context[path->context_no] = path->context[path->context_no-1];
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void o_restore (void)
|
|
|
|
{
|
|
|
|
path->context_no --;
|
|
|
|
#ifdef SIMULATOR
|
|
|
|
if (path->context_no < 0)
|
|
|
|
fprintf (stderr, "stack underflow\n");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* types:
|
|
|
|
* 's' : initialized state (last pen coordinates)
|
|
|
|
* 'm' : move_to 'l' : line_to
|
|
|
|
* 'c' : curve_to '.' : curve_to_ 'C' : curve_to__
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
o_add (unsigned char type,
|
|
|
|
int x,
|
|
|
|
int y)
|
|
|
|
{
|
|
|
|
Node *iter = NULL;
|
|
|
|
|
2011-08-06 00:37:43 +00:00
|
|
|
#if 1
|
2011-08-05 17:17:31 +00:00
|
|
|
if (type != 'A')
|
|
|
|
{
|
|
|
|
x *= SPP;
|
|
|
|
y *= SPP;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* transform inputed coords here */
|
|
|
|
path->x = x;
|
|
|
|
path->y = y;
|
|
|
|
|
|
|
|
if (type=='A')
|
|
|
|
{
|
|
|
|
type = 'l';
|
|
|
|
}
|
|
|
|
#ifdef O_ENABLE_TRANSFORM
|
|
|
|
else
|
|
|
|
{
|
|
|
|
OMatrix *m = &context()->transform;
|
|
|
|
x = ((path->x * m->m[0][0]) + (path->y * m->m[1][0]) + m->m[2][0]) / FIXED_ONE;
|
|
|
|
y = ((path->y * m->m[1][1]) + (path->x * m->m[0][1]) + m->m[2][1]) / FIXED_ONE;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
x = CLAMP(x, -32000, 32000);
|
|
|
|
y = CLAMP(y, -32000, 32000);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (path->count)
|
|
|
|
iter = &path->nodes[0];
|
|
|
|
if (path->count+1 >= path->max_size)
|
|
|
|
return;
|
|
|
|
|
|
|
|
iter = &path->nodes[path->count++];
|
|
|
|
|
|
|
|
iter->type=type;
|
|
|
|
iter->x = x;
|
|
|
|
iter->y = y;
|
|
|
|
}
|
|
|
|
|
|
|
|
void o_init (void *data, int data_size);
|
|
|
|
|
|
|
|
#ifdef O_ENABLE_USER_SHADER
|
|
|
|
void o_set_shader (ShaderFunction shader, void *shader_data)
|
|
|
|
{
|
|
|
|
context()->shader = shader;
|
|
|
|
context()->shader_data = shader_data;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef O_ENABLE_STROKE
|
|
|
|
void
|
|
|
|
o_set_line_width (int line_width)
|
|
|
|
{
|
|
|
|
context()->line_width = line_width;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef O_ENABLE_BW
|
|
|
|
#define GRAY_PRECISION 8
|
|
|
|
static int shader_gray (int x, int y, void *data)
|
|
|
|
{
|
|
|
|
return (int)(data); /* stored gray value as 8bit value */
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-08-06 00:37:43 +00:00
|
|
|
#ifdef O_ENABLE_GRAY
|
|
|
|
#define GRAY_PRECISION 4
|
2011-08-05 17:17:31 +00:00
|
|
|
/* Default color generator shader */
|
|
|
|
static int shader_gray (int x, int y, void *data)
|
|
|
|
{
|
|
|
|
int value = (int)(data); /* stored gray value as 8bit value */
|
|
|
|
if (value >= 3) return 1;
|
|
|
|
else if (value > 0)
|
|
|
|
return (x%2==0) ? (y %2)? 1:0:
|
|
|
|
(y %2)? 0:1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-08-06 00:37:43 +00:00
|
|
|
#ifdef O_ENABLE_GRAY_EXTRA
|
|
|
|
#define GRAY_PRECISION 8
|
2011-08-05 17:17:31 +00:00
|
|
|
/* Default color generator shader */
|
|
|
|
static int shader_gray (int x, int y, void *data)
|
|
|
|
{
|
|
|
|
int value = (int)(data); /* stored gray value as 8bit value */
|
|
|
|
switch (value) /* the dither patterns are can be seen */
|
|
|
|
{ /* at the end of the lines */
|
|
|
|
case 0: /* 0.0 */
|
|
|
|
return 0;
|
|
|
|
case 1: /* 0.16 */
|
|
|
|
return (x%3==0) ? (y %2)? 0:0:
|
|
|
|
(x%3==1) ? (y %2)? 0:0:
|
|
|
|
(y %2)? 0:1;
|
|
|
|
case 2: /* 0.25 */
|
|
|
|
return (x%2) ? (y %2)? 1:0:
|
|
|
|
(y %2)? 0:0;
|
|
|
|
case 3: /* 0.33 */
|
|
|
|
return (x%3==0) ? (y %2)? 1:0:
|
|
|
|
(x%3==1) ? (y %2)? 0:0:
|
|
|
|
(y %2)? 0:1;
|
|
|
|
case 4: /* 0.50 */
|
|
|
|
return (x%2==0) ? (y %2)? 1:0:
|
|
|
|
(y %2)? 0:1;
|
|
|
|
case 5: /* 0.66 */
|
|
|
|
return (x%3==0) ? (y %2)? 0:1:
|
|
|
|
(x%3==1) ? (y %2)? 1:1:
|
|
|
|
(y %2)? 1:0;
|
|
|
|
case 6: /* 0.75 */
|
|
|
|
return (x%2) ? (y %2)? 1:0:
|
|
|
|
(y %2)? 1:1;
|
|
|
|
case 7: /* 0.85 */
|
|
|
|
return (x%3==0) ? (y %2)? 1:1:
|
|
|
|
(x%3==1) ? (y %2)? 1:0:
|
|
|
|
(y %2)? 1:1;
|
|
|
|
case 8: /* 1.0 */
|
|
|
|
return 1;
|
|
|
|
default: // return ((char)(rnd1())) < value;
|
|
|
|
/* XXX: use a faster "random" source
|
|
|
|
for this fallback */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef GRAY_PRECISION
|
|
|
|
#define GRAY_PRECISION 8
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void o_set_gray (int value)
|
|
|
|
{
|
|
|
|
#ifdef O_ENABLE_USER_SHADER
|
|
|
|
#ifdef O_ENABLE_BW /* hack, that permits setting a user shader for grayscale..
|
|
|
|
when only bw has been enabled, _and_ we have user shader */
|
|
|
|
context()->shader_data = (void*)((int)(value * GRAY_PRECISION / 1000));
|
|
|
|
#else
|
|
|
|
o_set_shader (shader_gray, (void*)((int)(value * GRAY_PRECISION / 1000)));
|
|
|
|
#endif
|
|
|
|
#else
|
|
|
|
context()->shader_data = (void*)((int)(value * GRAY_PRECISION / 1000));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef O_ENABLE_CLIP
|
|
|
|
void o_reset_clip (void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i=0; i<HEIGHT; i++)
|
|
|
|
{
|
|
|
|
path->clip.row[i][0] = 0;
|
|
|
|
path->clip.row[i][1] = WIDTH;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void
|
|
|
|
o_init (void *dat, int data_size)
|
|
|
|
{
|
|
|
|
char *data = ((char*)dat);
|
|
|
|
path = (void*)data;
|
|
|
|
|
|
|
|
data += sizeof (Path);
|
|
|
|
data_size -= sizeof (Path);
|
|
|
|
#ifdef O_ENABLE_STACK
|
|
|
|
path->context = (void*)data;
|
|
|
|
|
|
|
|
data += sizeof (Context) * STACK_DEPTH;
|
|
|
|
data_size -= sizeof (Context) * STACK_DEPTH;
|
|
|
|
#endif
|
|
|
|
path->nodes = (void*)data;
|
|
|
|
|
|
|
|
path->max_size = data_size / sizeof (Node);
|
|
|
|
|
|
|
|
#ifdef O_ENABLE_STACK
|
|
|
|
path->context_no = 0;
|
|
|
|
#endif
|
|
|
|
#ifdef O_ENABLE_STROKE
|
|
|
|
context()->line_width = 1;
|
|
|
|
#endif
|
|
|
|
#ifdef O_ENABLE_TEXT
|
|
|
|
context()->font_size = 7;
|
|
|
|
#endif
|
|
|
|
o_path_new ();
|
|
|
|
#ifdef O_ENABLE_TRANSFORM
|
|
|
|
o_matrix_identity (&context()->transform);
|
|
|
|
#endif
|
|
|
|
#ifdef O_ENABLE_CLIP
|
|
|
|
o_reset_clip ();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
o_set_gray (1000);
|
|
|
|
#ifdef O_ENABLE_BW /* hack, that permits setting a user shader for grayscale..
|
|
|
|
when only bw has been enabled, _and_ we have user shader */
|
|
|
|
context()->shader = shader_gray;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
o_path_new (void)
|
|
|
|
{
|
|
|
|
path->count = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void o_move_to (int x, int y)
|
|
|
|
{
|
|
|
|
o_add ('m', x, y);
|
|
|
|
}
|
|
|
|
|
|
|
|
void o_line_to (int x, int y)
|
|
|
|
{
|
|
|
|
o_add ('l', x, y);
|
|
|
|
}
|
|
|
|
|
|
|
|
void o_curve_to (int x1, int y1,
|
|
|
|
int x2, int y2,
|
|
|
|
int x3, int y3)
|
|
|
|
{
|
|
|
|
o_add ('c', x1, y1);
|
|
|
|
o_add ('.', x2, y2);
|
|
|
|
o_add ('C', x3, y3);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef O_ENABLE_STROKE
|
|
|
|
|
|
|
|
static void
|
|
|
|
o_line (int x0, int y0, int x1, int y1, int *need_to_travel, int *traveled_length)
|
|
|
|
{
|
|
|
|
Node a, b;
|
|
|
|
int spacing, local_pos, distance, offset, leftover;
|
|
|
|
|
|
|
|
a.x = x0;
|
|
|
|
a.y = y0;
|
|
|
|
|
|
|
|
b.x = x1;
|
|
|
|
b.y = y1;
|
|
|
|
|
|
|
|
spacing = SPP - 1; /* *0.99 with divider!=1 */
|
|
|
|
|
|
|
|
distance = point_dist (&a, &b);
|
|
|
|
|
|
|
|
leftover = *need_to_travel - *traveled_length;
|
|
|
|
offset = spacing - leftover;
|
|
|
|
|
|
|
|
local_pos = offset;
|
|
|
|
|
|
|
|
if (distance > 0)
|
|
|
|
for (;local_pos <= distance; local_pos += spacing)
|
|
|
|
{
|
|
|
|
Node spot;
|
|
|
|
int ratio = (local_pos * FIXED_ONE) / distance;
|
|
|
|
int line_width;
|
|
|
|
line_width = context()->line_width;
|
|
|
|
|
|
|
|
/* scale line width by scaling factor */
|
|
|
|
|
|
|
|
lerp (&spot, &a, &b, ratio);
|
|
|
|
/* only 1px wide... */
|
|
|
|
if (line_width <= 1)
|
|
|
|
{
|
|
|
|
#ifdef O_ENABLE_USER_SHADER
|
|
|
|
lcdSetPixel (spot.x / SPP, spot.y / SPP,
|
|
|
|
context()->shader(spot.x / SPP, spot.y / SPP,
|
|
|
|
context()->shader_data));
|
|
|
|
#else
|
|
|
|
lcdSetPixel (spot.x / SPP, spot.y / SPP,
|
|
|
|
shader_gray(spot.x / SPP, spot.y / SPP,
|
|
|
|
context()->shader_data));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int line_radius = line_width /2;
|
|
|
|
/* should use a more circular brush */
|
|
|
|
int y;
|
|
|
|
for (y = spot.y/SPP - line_radius;
|
|
|
|
y < spot.y/SPP + line_radius; y++)
|
|
|
|
o_render_span (spot.x/SPP - line_radius,
|
|
|
|
y,
|
|
|
|
spot.x/SPP + line_radius,
|
|
|
|
#ifdef O_ENABLE_USER_SHADER
|
|
|
|
context()->shader,
|
|
|
|
#endif
|
|
|
|
context()->shader_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
*traveled_length += spacing;
|
|
|
|
}
|
|
|
|
*need_to_travel += distance;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
o_stroke (void)
|
|
|
|
{
|
|
|
|
Node *iter = &path->nodes[0];
|
|
|
|
int traveled_length = 0;
|
|
|
|
int need_to_travel = 0;
|
|
|
|
int x = 0, y = 0;
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
i=0;
|
|
|
|
while (i < path->count)
|
|
|
|
{
|
|
|
|
switch (iter->type)
|
|
|
|
{
|
|
|
|
case 'm':
|
|
|
|
need_to_travel = 0;
|
|
|
|
traveled_length = 0;
|
|
|
|
x = iter->x;
|
|
|
|
y = iter->y;
|
|
|
|
break;
|
|
|
|
case 'C':
|
|
|
|
{ /* create piecevize linear approximation of bezier curve */
|
|
|
|
int i;
|
|
|
|
Node *pts[4];
|
|
|
|
|
|
|
|
for (i=0;i<4;i++)
|
|
|
|
pts[i]=&iter[i-3];
|
|
|
|
for (i=0; i< FIXED_ONE; i+= FIXED_ONE/ BEZIER_SEGMENTS)
|
|
|
|
{
|
|
|
|
Node iter2;
|
|
|
|
bezier (pts, &iter2, i);
|
|
|
|
o_line (x, y, iter2.x, iter2.y, &need_to_travel, &traveled_length);
|
|
|
|
x = iter2.x;
|
|
|
|
y = iter2.y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case 'l':
|
|
|
|
o_line (x, y, iter->x, iter->y, &need_to_travel, &traveled_length);
|
|
|
|
x = iter->x;
|
|
|
|
y = iter->y;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
iter++;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef O_ENABLE_FILL
|
|
|
|
|
|
|
|
#define EMPTY_SCAN_SLOT -126
|
|
|
|
|
|
|
|
/* insertion sort integer into array */
|
|
|
|
static void insert_sorted (signed char *array, int value)
|
|
|
|
{
|
|
|
|
for (int j=0; j<MAX_SPANS_PER_LINE; j++)
|
|
|
|
{
|
|
|
|
if (array[j] == EMPTY_SCAN_SLOT)
|
|
|
|
{
|
|
|
|
array[j] = value;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (array[j] > value)
|
|
|
|
{
|
|
|
|
for (int k=MAX_SPANS_PER_LINE -1; k >= j+1; k--)
|
|
|
|
array[k] = array[k-1];
|
|
|
|
array[j] = value;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void fill_line (int prev_x, int prev_y, int dest_x, int dest_y,
|
|
|
|
signed char scanlines[HEIGHT][MAX_SPANS_PER_LINE],
|
|
|
|
int *lastdir, int *lastline)
|
|
|
|
{
|
|
|
|
int ydir;
|
|
|
|
int dx, dy;
|
|
|
|
dx = dest_x - prev_x;
|
|
|
|
dy = dest_y - prev_y;
|
|
|
|
|
|
|
|
ydir = (dy < 0) ? -1 : 1;
|
|
|
|
|
|
|
|
/* do linear interpolation between vertexes */
|
|
|
|
for (int y = prev_y; y != dest_y; y += ydir)
|
|
|
|
if (y >= 0 && y < HEIGHT && *lastline != y)
|
|
|
|
{
|
|
|
|
int x = prev_x + (dx * (y-prev_y)) / dy;
|
|
|
|
insert_sorted(&scanlines[y][0], x);
|
|
|
|
if (ydir != *lastdir) // && *lastdir != -2) //XXX: ugly optimizing hack
|
|
|
|
insert_sorted(&scanlines[y][0], x);
|
|
|
|
|
|
|
|
*lastdir = ydir;
|
|
|
|
*lastline = y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void o_fill (void)
|
|
|
|
{
|
|
|
|
if (path->count < 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
{
|
|
|
|
signed char scanlines[HEIGHT][MAX_SPANS_PER_LINE];
|
|
|
|
/* Stack allocated scanline intersection list */
|
|
|
|
Node *iter;
|
|
|
|
int prev_x, prev_y;
|
|
|
|
int first_x, first_y;
|
|
|
|
int lastline = -1;
|
|
|
|
int lastdir = -2;
|
|
|
|
|
|
|
|
for (int i = 0; i<HEIGHT; i++)
|
|
|
|
for (int j=0; j<MAX_SPANS_PER_LINE; j++)
|
|
|
|
scanlines[i][j] = EMPTY_SCAN_SLOT;
|
|
|
|
|
|
|
|
first_x = prev_x = path->nodes[0].x / SPP;
|
|
|
|
first_y = prev_y = path->nodes[0].y / SPP;
|
|
|
|
|
|
|
|
/* saturate scanline intersection list */
|
|
|
|
iter = &path->nodes[1];
|
|
|
|
for (int i=1; i<path->count; i++, iter++)
|
|
|
|
{
|
|
|
|
switch (iter->type)
|
|
|
|
{
|
|
|
|
int dest_x;
|
|
|
|
int dest_y;
|
|
|
|
case 'm':
|
|
|
|
{
|
|
|
|
first_x = prev_x = iter->x / SPP;
|
|
|
|
first_y = prev_y = iter->y / SPP;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'l':
|
|
|
|
{
|
|
|
|
dest_x = iter->x / SPP;
|
|
|
|
dest_y = iter->y / SPP;
|
|
|
|
fill_close: /* label used for goto to close last segment */
|
|
|
|
|
|
|
|
fill_line (prev_x, prev_y, dest_x, dest_y, scanlines, &lastdir, &lastline);
|
|
|
|
|
|
|
|
prev_x = dest_x;
|
|
|
|
prev_y = dest_y;
|
|
|
|
/* if we're on the last knot, fake the first vertex being a next one */
|
|
|
|
if (i+1 == path->count)
|
|
|
|
{
|
|
|
|
dest_x = first_x;
|
|
|
|
dest_y = first_y;
|
|
|
|
i++; /* to make the loop finally end */
|
|
|
|
goto fill_close;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'C':
|
|
|
|
{ /* create piecevize linear approximation of bezier curve */
|
|
|
|
int i;
|
|
|
|
Node *pts[4];
|
|
|
|
|
|
|
|
for (i=0;i<4;i++)
|
|
|
|
pts[i]=&iter[i-3];
|
|
|
|
for (i=0; i< FIXED_ONE; i+= FIXED_ONE/ BEZIER_SEGMENTS)
|
|
|
|
{
|
|
|
|
Node iter2;
|
|
|
|
bezier (pts, &iter2, i);
|
|
|
|
fill_line (prev_x, prev_y, iter2.x/SPP, iter2.y/SPP, scanlines, &lastdir, &lastline);
|
|
|
|
prev_x = iter2.x / SPP;
|
|
|
|
prev_y = iter2.y / SPP;
|
|
|
|
}
|
|
|
|
if (i+1 == path->count)
|
|
|
|
{
|
|
|
|
dest_x = first_x;
|
|
|
|
dest_y = first_y;
|
|
|
|
i++; /* to make the loop finally end */
|
|
|
|
goto fill_close;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Fill the spans */
|
|
|
|
for (int i=0; i < HEIGHT; i++)
|
|
|
|
{
|
|
|
|
for (int j=0; j<MAX_SPANS_PER_LINE-2; j+=2)
|
|
|
|
{
|
|
|
|
if (scanlines[i][j] > EMPTY_SCAN_SLOT)
|
|
|
|
{
|
|
|
|
o_render_span (scanlines[i][j], i, scanlines[i][j+1],
|
|
|
|
#ifdef O_ENABLE_USER_SHADER
|
|
|
|
context()->shader,
|
|
|
|
#endif
|
|
|
|
context()->shader_data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef O_ENABLE_CLIP
|
|
|
|
void o_clip (void)
|
|
|
|
{
|
|
|
|
/* massive code duplication with o_fill */
|
|
|
|
if (path->count < 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
{
|
|
|
|
Node *iter;
|
|
|
|
int prev_x, prev_y;
|
|
|
|
int first_x, first_y;
|
|
|
|
int lastline = -1;
|
|
|
|
|
|
|
|
first_x = prev_x = path->nodes[0].x / SPP;
|
|
|
|
first_y = prev_y = path->nodes[0].y / SPP;
|
|
|
|
|
|
|
|
/* saturate scanline intersection list */
|
|
|
|
iter = &path->nodes[1];
|
|
|
|
for (int i=1; i<path->count; i++, iter++)
|
|
|
|
{
|
|
|
|
int dest_x = iter->x / SPP;
|
|
|
|
int dest_y = iter->y / SPP;
|
|
|
|
int ydir;
|
|
|
|
int dx, dy;
|
|
|
|
fill_close: /* label used for goto to close last segment */
|
|
|
|
dx = dest_x - prev_x;
|
|
|
|
dy = dest_y - prev_y;
|
|
|
|
|
|
|
|
ydir = (dy < 0) ? -1 : 1;
|
|
|
|
|
|
|
|
/* do linear interpolation between vertexes */
|
|
|
|
for (int y = prev_y; y != dest_y; y += ydir)
|
|
|
|
if (y >= 0 && y < HEIGHT && lastline != y)
|
|
|
|
{
|
|
|
|
int x = prev_x + (dx * (y-prev_y)) / dy;
|
|
|
|
if (path->clip.row[y][0] < x)
|
|
|
|
path->clip.row[y][0] = x;
|
|
|
|
if (path->clip.row[y][1] > x)
|
|
|
|
path->clip.row[y][1] = x;
|
|
|
|
lastline = y;
|
|
|
|
}
|
|
|
|
|
|
|
|
prev_x = dest_x;
|
|
|
|
prev_y = dest_y;
|
|
|
|
/* if we're on the last knot, fake the first vertex being a next one */
|
|
|
|
if (i+1 == path->count)
|
|
|
|
{
|
|
|
|
dest_x = first_x;
|
|
|
|
dest_y = first_y;
|
|
|
|
i++; /* to make the loop finally end */
|
|
|
|
goto fill_close;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
void o_close (void)
|
|
|
|
{
|
|
|
|
Node *iter;
|
|
|
|
int i = path->count;
|
|
|
|
if (path->count == 0)
|
|
|
|
return;
|
|
|
|
iter = &path->nodes[path->count];
|
|
|
|
while (i>0 && iter->type != 'm') /* find previous move to */
|
|
|
|
{
|
|
|
|
iter --;
|
|
|
|
i--;
|
|
|
|
}
|
|
|
|
if (i>=0 && iter->type == 'm')
|
|
|
|
o_add ('A', iter->x, iter->y);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef O_ENABLE_RECTANGLE
|
|
|
|
void o_orectangle (int x0, int y0, int width, int height)
|
|
|
|
{
|
|
|
|
o_path_new ();
|
|
|
|
o_move_to (x0, y0);
|
|
|
|
o_line_to (x0 + width, y0);
|
|
|
|
o_line_to (x0 + width, y0+height);
|
|
|
|
o_line_to (x0, y0+height);
|
|
|
|
o_close ();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static const signed char *_o_bounds_op (const signed char *g,
|
|
|
|
int *xmin, int *ymin, int *xmax, int *ymax)
|
|
|
|
{
|
|
|
|
int ccount = 0;
|
|
|
|
switch (*g++) {
|
|
|
|
case 'l': case 'm': ccount = 1; break;
|
|
|
|
case 'c': ccount = 3; break;
|
|
|
|
case 'g': case 'w': g++; break;
|
|
|
|
case 'e': case '.': /* end */ return NULL;
|
|
|
|
default:
|
|
|
|
case 'z': case 'f': case 's': case ' ': break;
|
|
|
|
}
|
|
|
|
for (int c = 0; c < ccount; c++)
|
|
|
|
{
|
|
|
|
if (xmin && *xmin > g[c*2+0])
|
|
|
|
*xmin = g[c*2+0];
|
|
|
|
if (ymin && *ymin > g[c*2+1])
|
|
|
|
*ymin = g[c*2+1];
|
|
|
|
if (xmax && *xmax < g[c*2+0])
|
|
|
|
*xmax = g[c*2+0];
|
|
|
|
if (ymax && *ymax < g[c*2+1])
|
|
|
|
*ymax = g[c*2+1];
|
|
|
|
}
|
|
|
|
g += ccount * 2;
|
|
|
|
return g;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef O_ENABLE_RENDER
|
|
|
|
static const signed char * _o_process_op (const signed char *g)
|
|
|
|
{
|
|
|
|
switch (*g++) {
|
|
|
|
|
|
|
|
case ' ': o_path_new (); break;
|
|
|
|
/* all of these path commands are directly in integer coordinates */
|
|
|
|
case 'm': o_move_to (g[0], g[1]); g += 2; break;
|
|
|
|
case 'l': o_line_to (g[0], g[1]); g += 2; break;
|
|
|
|
case 'c': o_curve_to (g[0], g[1], g[2], g[3], g[4], g[5]); g += 6; break;
|
|
|
|
case 'z': o_close (); break;
|
|
|
|
|
|
|
|
/* directly in px XXX: should be 10 = 1.0 instead? */
|
|
|
|
case 'w': o_set_line_width (g[0]); g ++; break;
|
|
|
|
/* 0 = black, 50 = gray, 100 = white */
|
|
|
|
case 'g': o_set_gray (g[0]*10); g ++; break;
|
|
|
|
|
|
|
|
#ifdef O_ENABLE_FILL
|
|
|
|
case 'f': o_fill (); break;
|
|
|
|
#endif
|
|
|
|
#ifdef O_ENABLE_STROKE
|
|
|
|
case 's': o_stroke (); break;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef O_ENABLE_FLUFF_CODE
|
|
|
|
case 'r': o_rectangle (g[0], g[1], g[2], g[3]); g += 4; break;
|
|
|
|
#endif
|
|
|
|
#ifdef O_ENABLE_STACK
|
|
|
|
case '+': o_save (); break;
|
|
|
|
case '-': o_restore (); break;
|
|
|
|
#endif
|
|
|
|
#ifdef O_ENABLE_TRANSFORM
|
|
|
|
/* 1 = 1 10 = 10 100 = 100 */
|
|
|
|
case '!': o_identity (); break;
|
|
|
|
case 'T': o_translate (g[0] * 100, g[1] * 100); g+=2; break;
|
|
|
|
/* 1 = 0.01 10 = 0.1 50 = 0.5 100 = 10x */
|
|
|
|
case 'S': o_scale (g[0] * 10, g[1] * 10); g+=2; break;
|
|
|
|
/* -128 = -360 64 = 180 128 = 360 */
|
|
|
|
case 'R': o_rotate ((g[0] * 3600)/127); g+=1; break;
|
|
|
|
#else
|
|
|
|
case 'T': case 'S': g+=2;break;
|
|
|
|
case 'R': g+=1;break;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef O_ENABLE_CLIP
|
|
|
|
case 'x': o_clip (); break;
|
|
|
|
case 'X': o_reset_clip (); break;
|
|
|
|
#endif
|
|
|
|
#ifdef O_ENABLE_TEXT_API
|
|
|
|
case 't': o_text_path (g); while (*g) g++; break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
case '\0':
|
|
|
|
case '.': /* end */
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return g;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
o_render (const signed char *g)
|
|
|
|
{
|
|
|
|
for (; g; g = _o_process_op (g));
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
o_bounds (const signed char *g,
|
|
|
|
int *minx, int *miny,
|
|
|
|
int *maxx, int *maxy)
|
|
|
|
{
|
|
|
|
if (minx) *minx= 10000;
|
|
|
|
if (miny) *miny= 10000;
|
|
|
|
if (maxx) *maxx= -10000;
|
|
|
|
if (maxy) *maxy= -10000;
|
|
|
|
for (; g; g = _o_bounds_op (g, minx, miny, maxx, maxy));
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef O_ENABLE_FILE
|
|
|
|
|
|
|
|
#ifdef SIMULATOR
|
|
|
|
static FILE *file;
|
|
|
|
#else
|
|
|
|
static FIL file; /* font file */
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void o_render_file (const char *file_path, int offset)
|
|
|
|
{
|
|
|
|
unsigned int readbytes;
|
|
|
|
#ifdef SIMULATOR
|
|
|
|
if (!(file = fopen (file_path, "r")))
|
|
|
|
#else
|
|
|
|
unsigned int res;
|
|
|
|
if ((res = f_open (&file, file_path, FA_OPEN_EXISTING|FA_READ)))
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
/* XX: failed to open */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#ifdef SIMULATOR
|
|
|
|
fseek(file, offset, SEEK_SET);
|
|
|
|
#else
|
|
|
|
f_lseek(&file, offset);
|
|
|
|
#endif
|
|
|
|
signed char buf[8];
|
|
|
|
const signed char *b;
|
|
|
|
int chunk = sizeof (buf);
|
|
|
|
do {
|
|
|
|
#ifdef SIMULATOR
|
|
|
|
readbytes = fread (buf + sizeof(buf) - chunk, 1, chunk, file);
|
|
|
|
#else
|
|
|
|
res = f_read (&file, buf + sizeof(buf)-chunk, chunk, &readbytes);
|
|
|
|
#endif
|
|
|
|
b = buf;
|
|
|
|
b = _o_process_op (b);
|
|
|
|
chunk = b-buf;
|
|
|
|
if (b)
|
|
|
|
memmove (buf, b, sizeof (buf) - chunk);
|
|
|
|
} while (b && readbytes);
|
|
|
|
if (b)
|
|
|
|
for (b=buf;b;b = _o_process_op (b));
|
|
|
|
#ifdef SIMULATOR
|
|
|
|
fclose (file);
|
|
|
|
#else
|
|
|
|
f_close (&file);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
o_file_offset (const char *file_path,
|
|
|
|
int no)
|
|
|
|
{
|
|
|
|
short count;
|
|
|
|
short offset;
|
|
|
|
#ifdef SIMULATOR
|
|
|
|
if (!(file = fopen (file_path, "r")))
|
|
|
|
#else
|
|
|
|
unsigned int readbytes;
|
|
|
|
unsigned int res;
|
|
|
|
if ((res = f_open (&file, file_path, FA_OPEN_EXISTING|FA_READ)))
|
|
|
|
#endif
|
|
|
|
/* XX: failed to open */
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
#ifdef SIMULATOR
|
|
|
|
fread (&count, sizeof(count), 1, file);
|
|
|
|
#else
|
|
|
|
res = f_read (&file, &count, sizeof(count), &readbytes);
|
|
|
|
#endif
|
|
|
|
if (no > count)
|
|
|
|
{
|
|
|
|
#ifdef SIMULATOR
|
|
|
|
fclose (file);
|
|
|
|
#else
|
|
|
|
f_close (&file);
|
|
|
|
#endif
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef SIMULATOR
|
|
|
|
fseek(file, 2 * (no+1), SEEK_SET);
|
|
|
|
#else
|
|
|
|
f_lseek(&file, 2 * (no+1));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIMULATOR
|
|
|
|
fread (&offset, sizeof(offset), 1, file);
|
|
|
|
#else
|
|
|
|
res = f_read (&file, &offset, sizeof(offset), &readbytes);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef SIMULATOR
|
|
|
|
fclose (file);
|
|
|
|
#else
|
|
|
|
f_close (&file);
|
|
|
|
#endif
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
o_bounds_file (const char *file_path,
|
|
|
|
int offset,
|
|
|
|
int *minx, int *miny,
|
|
|
|
int *maxx, int *maxy)
|
|
|
|
{
|
|
|
|
unsigned int readbytes;
|
|
|
|
unsigned int res;
|
|
|
|
if (minx) *minx= 10000;
|
|
|
|
if (miny) *miny= 10000;
|
|
|
|
if (maxx) *maxx= -10000;
|
|
|
|
if (maxy) *maxy= -10000;
|
|
|
|
#ifdef SIMULATOR
|
|
|
|
if (!(file = fopen (file_path, "r")))
|
|
|
|
#else
|
|
|
|
if ((res = f_open (&file, file_path, FA_OPEN_EXISTING|FA_READ)))
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
/* XX: failed to open */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#ifdef SIMULATOR
|
|
|
|
fseek(file, offset, SEEK_SET);
|
|
|
|
#else
|
|
|
|
f_lseek(&file, offset);
|
|
|
|
#endif
|
|
|
|
signed char buf[8];
|
|
|
|
const signed char *b;
|
|
|
|
int chunk = sizeof (buf);
|
|
|
|
do {
|
|
|
|
#ifdef SIMULATOR
|
|
|
|
readbytes = fread (buf + sizeof(buf) - chunk, 1, chunk, file);
|
|
|
|
#else
|
|
|
|
res = f_read (&file, buf + sizeof(buf)-chunk, chunk, &readbytes);
|
|
|
|
#endif
|
|
|
|
b = buf;
|
|
|
|
b = _o_bounds_op (b, minx, miny, maxx, maxy);
|
|
|
|
chunk = b-buf;
|
|
|
|
if (b)
|
|
|
|
memmove (buf, b, sizeof (buf) - chunk);
|
|
|
|
} while (b && readbytes);
|
|
|
|
if (b)
|
|
|
|
for (b=buf;b;b = _o_bounds_op (b, minx, miny, maxx, maxy));
|
|
|
|
#ifdef SIMULATOR
|
|
|
|
fclose (file);
|
|
|
|
#else
|
|
|
|
f_close (&file);
|
|
|
|
#endif
|
|
|
|
/* */
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
#ifndef O_ENABLE_TEXT
|
|
|
|
|
|
|
|
void o_set_font_size (int font_size)
|
|
|
|
{/*adding the symbol to make things still compile without having fontsupport*/}
|
|
|
|
|
|
|
|
void o_show_text (const char *ascii,
|
|
|
|
int wrap_width)
|
|
|
|
{/*adding the symbol to make things still compile without having fontsupport*/}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
void o_set_font_size (int font_size)
|
|
|
|
{
|
|
|
|
context()->font_size = font_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef O_ENABLE_EXTERNAL_FONT
|
|
|
|
#include "o-glyphs.c" /* include the font data directly */
|
|
|
|
|
|
|
|
static const signed char *
|
|
|
|
glyph_data (char ascii)
|
|
|
|
{
|
|
|
|
int no = 0;
|
|
|
|
int pos = 0;
|
|
|
|
ascii -= 31;
|
|
|
|
if (ascii > 126)
|
|
|
|
return _o_glyphs;
|
|
|
|
|
|
|
|
while (no < ascii)
|
|
|
|
{
|
|
|
|
int val = _o_glyphs[pos];
|
|
|
|
if (val == '.')
|
|
|
|
no ++;
|
|
|
|
pos++;
|
|
|
|
}
|
|
|
|
if (no == ascii)
|
|
|
|
return _o_glyphs + pos;
|
|
|
|
return _o_glyphs;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* end of glue */
|
|
|
|
|
|
|
|
#define PARA_DET 3 /* 1.5 */
|
|
|
|
#define PARA_NOM 2
|
|
|
|
#define WORD_DET 4 /* 0.4 */
|
|
|
|
#define WORD_NOM 10
|
|
|
|
#define LINE_DET 12 /* 1.2 */
|
|
|
|
#define LINE_NOM 10
|
|
|
|
|
|
|
|
static int
|
|
|
|
block_width (int font_size,
|
|
|
|
char ascii)
|
|
|
|
{
|
|
|
|
int maxx = 0;
|
|
|
|
#ifndef O_ENABLE_EXTERNAL_FONT
|
|
|
|
const signed char *g = glyph_data (ascii);
|
|
|
|
o_bounds (g, NULL, NULL, &maxx, NULL);
|
|
|
|
#else
|
|
|
|
int offset = o_file_offset (FONT_PATH, ascii);
|
|
|
|
o_bounds_file (FONT_PATH, offset, NULL, NULL, &maxx, NULL);
|
|
|
|
#endif
|
|
|
|
return (maxx + 12) * font_size / 42;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
font_ascii_path (int font_size,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
char ascii)
|
|
|
|
{
|
|
|
|
OMatrix orig_transform;
|
|
|
|
|
|
|
|
int origin_x, origin_y;
|
|
|
|
int next_x;
|
|
|
|
int baseline;
|
|
|
|
|
|
|
|
o_current_point (&origin_x, &origin_y);
|
|
|
|
next_x = origin_x + block_width (font_size, ascii);
|
|
|
|
baseline = origin_y;
|
|
|
|
orig_transform = context()->transform;
|
|
|
|
o_translate (origin_x * 1000, origin_y * 1000);
|
|
|
|
o_scale (font_size * 1000 / 42, font_size * 1000 / 42);
|
|
|
|
#ifndef O_ENABLE_EXTERNAL_FONT
|
|
|
|
const signed char *g = glyph_data (ascii);
|
|
|
|
o_render (g);
|
|
|
|
#else
|
|
|
|
int offset = o_file_offset (FONT_PATH, ascii);
|
|
|
|
o_render_file (FONT_PATH, offset);
|
|
|
|
#endif
|
|
|
|
o_move_to (next_x, baseline);
|
|
|
|
context()->transform = orig_transform;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
wordlength (int font_size,
|
|
|
|
const char *ascii)
|
|
|
|
{
|
|
|
|
int sum = 0;
|
|
|
|
while (*ascii &&
|
|
|
|
*ascii != ' ' &&
|
|
|
|
*ascii != '\n')
|
|
|
|
{
|
|
|
|
sum += block_width (font_size, *ascii);
|
|
|
|
ascii++;
|
|
|
|
}
|
|
|
|
return sum;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
font_path (int font_size,
|
|
|
|
int width,
|
|
|
|
const char *ascii)
|
|
|
|
{
|
|
|
|
int x = path->x / SPP;
|
|
|
|
int y = path->y / SPP;
|
|
|
|
|
|
|
|
while (*ascii)
|
|
|
|
{
|
|
|
|
if (*ascii == '\n')
|
|
|
|
{
|
|
|
|
o_move_to (x, y += (font_size * PARA_DET) / PARA_NOM);
|
|
|
|
}
|
|
|
|
else if (*ascii == ' ')
|
|
|
|
{
|
|
|
|
int pen_x;
|
|
|
|
o_current_point (&pen_x, NULL);
|
|
|
|
if (width &&
|
|
|
|
pen_x + wordlength (font_size, ascii+1) > x + width &&
|
|
|
|
wordlength (font_size, ascii+1) < width)
|
|
|
|
{
|
|
|
|
o_move_to (x, y += (font_size * LINE_DET) / LINE_NOM);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* word spacing */
|
|
|
|
o_move_to (pen_x + (font_size * WORD_DET) / WORD_NOM, y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
font_ascii_path (font_size, x,y, *ascii);
|
|
|
|
}
|
|
|
|
ascii++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef SIMULATOR
|
|
|
|
#ifdef O_ENABLE_FONT_DUMP
|
|
|
|
|
|
|
|
#define wordout(w) \
|
|
|
|
{ short int temp = (w);\
|
|
|
|
fwrite (&temp, sizeof (temp), 1, file); \
|
|
|
|
}
|
|
|
|
#define byteout(c) \
|
|
|
|
{ signed char temp = (c);\
|
|
|
|
fwrite (&temp, sizeof (temp), 1, file); \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define FUDGE 3
|
|
|
|
#define FIRST_CHAR 32 /* padding out of first few entires */
|
|
|
|
|
|
|
|
void o_write_font (void)
|
|
|
|
{
|
|
|
|
FILE *file;
|
|
|
|
static char done = 0;
|
|
|
|
if (done)
|
|
|
|
return;
|
|
|
|
int count = 0;
|
|
|
|
done = 1;
|
|
|
|
fprintf (stderr, "@write the font\n");
|
|
|
|
file = fopen (FONT_PATH, "w");
|
|
|
|
|
|
|
|
for (int pos = 0; pos < sizeof (_o_glyphs); pos++)
|
|
|
|
{
|
|
|
|
int val = _o_glyphs[pos];
|
|
|
|
if (val == '.')
|
|
|
|
count ++;
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf (stderr, "%d\n", count + FIRST_CHAR);
|
|
|
|
wordout(count+FIRST_CHAR);
|
|
|
|
int no = 0;
|
|
|
|
for (int pos = 0; pos < FIRST_CHAR; pos ++)
|
|
|
|
{
|
|
|
|
fprintf (stderr, "%i, ", 0);
|
|
|
|
wordout (0 + (2 * (count + FIRST_CHAR) + FUDGE));
|
|
|
|
}
|
|
|
|
for (int pos = 0; pos < sizeof (_o_glyphs); pos++)
|
|
|
|
{
|
|
|
|
int val = _o_glyphs[pos];
|
|
|
|
if (val == '.' && no < 129)
|
|
|
|
{
|
|
|
|
fprintf (stderr, "%i, ", pos + (2 * (count + FIRST_CHAR) + FUDGE));
|
|
|
|
wordout (pos + (2 * (count + FIRST_CHAR)) + FUDGE );
|
|
|
|
no ++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
no = FIRST_CHAR - 1; //31; XXX: might not work quite out..
|
|
|
|
|
|
|
|
fprintf (stderr, "\n\n /* unknown box 0x%x - %i*/\n", 0,
|
|
|
|
0 + (2 * (count + FIRST_CHAR) + FUDGE ));
|
|
|
|
|
|
|
|
for (int pos = 0; pos < sizeof (_o_glyphs); pos++)
|
|
|
|
{
|
|
|
|
int val = _o_glyphs[pos];
|
|
|
|
if (((val >= 'a') && (val <= 'z')) || (val == ' ') || (val == '.'))
|
|
|
|
fprintf (stderr, "\n'%c', ", _o_glyphs[pos]);
|
|
|
|
else
|
|
|
|
fprintf (stderr, "%i, ", _o_glyphs[pos]);
|
|
|
|
byteout (_o_glyphs[pos]);
|
|
|
|
if (val == '.')
|
|
|
|
{
|
|
|
|
no ++;
|
|
|
|
fprintf (stderr, "\n\n /* '%c' 0x%x - %i*/\n", no, no,
|
|
|
|
pos + (2 * (count + FIRST_CHAR)) + FUDGE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fprintf (stderr, "\n");
|
|
|
|
fclose (file);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void o_show_text (const char *ascii, int wrap_width)
|
|
|
|
{
|
|
|
|
#ifdef SIMULATOR
|
|
|
|
#ifdef O_ENABLE_FONT_DUMP
|
|
|
|
o_write_font ();
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
font_path (context()->font_size, wrap_width, ascii);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|