add libttf
This commit is contained in:
parent
198f10bb25
commit
db71e19005
10 changed files with 3951 additions and 0 deletions
168
sys/include/ttf.h
Normal file
168
sys/include/ttf.h
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
#pragma src "/sys/src/libttf"
|
||||||
|
#pragma lib "libttf.a"
|
||||||
|
|
||||||
|
typedef struct TTTable TTTable;
|
||||||
|
typedef struct TTChMap TTChMap;
|
||||||
|
typedef struct TTPoint TTPoint;
|
||||||
|
typedef struct TTGlyph TTGlyph;
|
||||||
|
typedef struct TTGlyphInfo TTGlyphInfo;
|
||||||
|
typedef struct TTFontU TTFontU;
|
||||||
|
typedef struct TTFont TTFont;
|
||||||
|
typedef struct TTFunction TTFunction;
|
||||||
|
typedef struct TTGState TTGState;
|
||||||
|
typedef struct TTBitmap TTBitmap;
|
||||||
|
typedef struct TTKern TTKern;
|
||||||
|
typedef struct Biobuf Biobuf;
|
||||||
|
|
||||||
|
struct TTTable {
|
||||||
|
u32int tag;
|
||||||
|
u32int csum;
|
||||||
|
u32int offset;
|
||||||
|
u32int len;
|
||||||
|
};
|
||||||
|
struct TTChMap {
|
||||||
|
int start, end, delta;
|
||||||
|
int *tab;
|
||||||
|
enum {
|
||||||
|
TTCDELTA16 = 1,
|
||||||
|
TTCINVALID = 2,
|
||||||
|
} flags;
|
||||||
|
int temp;
|
||||||
|
};
|
||||||
|
struct TTPoint {
|
||||||
|
int x, y;
|
||||||
|
u8int flags;
|
||||||
|
};
|
||||||
|
struct TTBitmap {
|
||||||
|
u8int *bit;
|
||||||
|
int width, height, stride;
|
||||||
|
};
|
||||||
|
struct TTGlyph {
|
||||||
|
TTBitmap;
|
||||||
|
int idx;
|
||||||
|
int xmin, xmax, ymin, ymax;
|
||||||
|
int xminpx, xmaxpx, yminpx, ymaxpx;
|
||||||
|
int advanceWidthpx;
|
||||||
|
TTPoint *pt;
|
||||||
|
TTPoint *ptorg;
|
||||||
|
int npt;
|
||||||
|
u16int *confst;
|
||||||
|
int ncon;
|
||||||
|
u8int *hint;
|
||||||
|
int nhint;
|
||||||
|
TTFont *font;
|
||||||
|
TTGlyphInfo *info;
|
||||||
|
};
|
||||||
|
struct TTGlyphInfo {
|
||||||
|
int loca;
|
||||||
|
u16int advanceWidth;
|
||||||
|
short lsb;
|
||||||
|
};
|
||||||
|
struct TTFunction {
|
||||||
|
u8int *pgm;
|
||||||
|
int npgm;
|
||||||
|
};
|
||||||
|
struct TTGState {
|
||||||
|
int pvx, pvy;
|
||||||
|
int dpvx, dpvy;
|
||||||
|
int fvx, fvy;
|
||||||
|
u32int instctrl;
|
||||||
|
u32int scanctrl;
|
||||||
|
u32int scantype;
|
||||||
|
int rperiod, rphase, rthold;
|
||||||
|
u8int zp;
|
||||||
|
int rp[3];
|
||||||
|
int cvci;
|
||||||
|
int loop;
|
||||||
|
int mindist;
|
||||||
|
int deltabase, deltashift;
|
||||||
|
u8int autoflip;
|
||||||
|
u32int singlewval, singlewci;
|
||||||
|
};
|
||||||
|
struct TTKern {
|
||||||
|
u32int idx;
|
||||||
|
int val;
|
||||||
|
};
|
||||||
|
struct TTFontU {
|
||||||
|
int ref;
|
||||||
|
|
||||||
|
Biobuf *bin;
|
||||||
|
|
||||||
|
TTTable *tab;
|
||||||
|
u16int ntab;
|
||||||
|
|
||||||
|
TTChMap *cmap;
|
||||||
|
int ncmap;
|
||||||
|
|
||||||
|
short *cvtu;
|
||||||
|
int ncvtu;
|
||||||
|
|
||||||
|
u16int flags;
|
||||||
|
int emsize;
|
||||||
|
short xmin, ymin, xmax, ymax;
|
||||||
|
u16int longloca;
|
||||||
|
|
||||||
|
TTGlyphInfo *ginfo;
|
||||||
|
|
||||||
|
u16int numGlyphs;
|
||||||
|
u16int maxPoints;
|
||||||
|
u16int maxCountours;
|
||||||
|
u16int maxComponentPoints;
|
||||||
|
u16int maxComponentCountours;
|
||||||
|
u16int maxZones;
|
||||||
|
u16int maxTwilightPoints;
|
||||||
|
u16int maxStorage;
|
||||||
|
u16int maxFunctionDefs;
|
||||||
|
u16int maxInstructionDefs;
|
||||||
|
u16int maxStackElements;
|
||||||
|
u16int maxSizeOfInstructions;
|
||||||
|
u16int maxComponentElements;
|
||||||
|
u16int maxComponentDepth;
|
||||||
|
|
||||||
|
int ascent, descent;
|
||||||
|
|
||||||
|
u16int advanceWidthMax;
|
||||||
|
u16int minLeftSideBearing;
|
||||||
|
u16int minRightSideBearing;
|
||||||
|
u16int xMaxExtent;
|
||||||
|
u16int numOfLongHorMetrics;
|
||||||
|
|
||||||
|
TTKern *kern;
|
||||||
|
int nkern;
|
||||||
|
};
|
||||||
|
struct TTFont {
|
||||||
|
TTFontU *u;
|
||||||
|
int ascentpx, descentpx;
|
||||||
|
int ppem;
|
||||||
|
TTGState;
|
||||||
|
TTGState defstate;
|
||||||
|
TTPoint *twilight, *twiorg;
|
||||||
|
u32int *hintstack;
|
||||||
|
TTFunction *func;
|
||||||
|
u32int *storage;
|
||||||
|
int *cvt;
|
||||||
|
int ncvt;
|
||||||
|
};
|
||||||
|
|
||||||
|
TTFont *ttfopen(char *, int, int);
|
||||||
|
TTFont *ttfscale(TTFont *, int, int);
|
||||||
|
void ttfclose(TTFont *);
|
||||||
|
int ttffindchar(TTFont *, Rune);
|
||||||
|
int ttfenumchar(TTFont *, Rune, Rune *);
|
||||||
|
TTGlyph *ttfgetglyph(TTFont *, int, int);
|
||||||
|
void ttfputglyph(TTGlyph *);
|
||||||
|
int ttfgetcontour(TTGlyph *, int, float **, int *);
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TTFLALIGN = 0,
|
||||||
|
TTFRALIGN = 1,
|
||||||
|
TTFCENTER = 2,
|
||||||
|
TTFMODE = 3,
|
||||||
|
TTFJUSTIFY = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
TTBitmap *ttfrender(TTFont *, char *, char *, int, int, int, char **);
|
||||||
|
TTBitmap *ttfrunerender(TTFont *, Rune *, Rune *, int, int, int, Rune **);
|
||||||
|
TTBitmap *ttfnewbitmap(int, int);
|
||||||
|
void ttffreebitmap(TTBitmap *);
|
||||||
|
void ttfblit(TTBitmap *, int, int, TTBitmap *, int, int, int, int);
|
211
sys/man/2/ttf
Normal file
211
sys/man/2/ttf
Normal file
|
@ -0,0 +1,211 @@
|
||||||
|
.TH TTF 2
|
||||||
|
.SH NAME
|
||||||
|
ttfopen, ttfscale, ttfclose, ttffindchar, ttfenumchar, ttfgetglyph,
|
||||||
|
ttfputglyph, ttfgetcontour, ttfrender, ttfrunerender, ttfnewbitmap,
|
||||||
|
ttffreebitmap, ttfblit \- TrueType renderer
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.de PB
|
||||||
|
.PP
|
||||||
|
.ft L
|
||||||
|
.nf
|
||||||
|
..
|
||||||
|
.PB
|
||||||
|
#include <u.h>
|
||||||
|
#include <libc.h>
|
||||||
|
#include <bio.h>
|
||||||
|
#include <ttf.h>
|
||||||
|
.PB
|
||||||
|
struct TTBitmap {
|
||||||
|
u8int *bit;
|
||||||
|
int width, height, stride;
|
||||||
|
};
|
||||||
|
.PB
|
||||||
|
struct TTGlyph {
|
||||||
|
TTBitmap;
|
||||||
|
int xminpx, xmaxpx, yminpx, ymaxpx, advanceWidthpx;
|
||||||
|
/* + internals */
|
||||||
|
};
|
||||||
|
.PB
|
||||||
|
struct TTFont {
|
||||||
|
int ppem, ascentpx, descentpx;
|
||||||
|
/* + internals */
|
||||||
|
};
|
||||||
|
.PB
|
||||||
|
.ta +\w'\fLTTBitmap* \fP'u
|
||||||
|
TTFont* ttfopen(char *filename, int size, int flags);
|
||||||
|
TTFont* ttfscale(TTFont *f, int size, int flags);
|
||||||
|
void ttfclose(TTFont *f);
|
||||||
|
.PB
|
||||||
|
int ttffindchar(TTFont *f, Rune r);
|
||||||
|
int ttfenumchar(TTFont *f, Rune r, Rune *rp);
|
||||||
|
.PB
|
||||||
|
TTGlyph* ttfgetglyph(TTFont *f, int glyphidx, int render);
|
||||||
|
void ttfputglyph(TTGlyph *g);
|
||||||
|
int ttfgetcontour(TTGlyph *g, int idx, float **fp, int *nfp);
|
||||||
|
.PB
|
||||||
|
TTBitmap* ttfrender(TTFont *f, char *s, char *e, int w, int h,
|
||||||
|
int flags, char **pp);
|
||||||
|
TTBitmap* ttfrunerender(TTFont *f, Rune *s, Rune *e, int w, int h,
|
||||||
|
int flags, char **pp);
|
||||||
|
.PB
|
||||||
|
TTBitmap* ttfnewbitmap(int w, int h);
|
||||||
|
void ttfblit(TTBitmap *dst, int dstx, int dsty, TTBitmap *src,
|
||||||
|
int srcx, int srcy, int w, int h);
|
||||||
|
void ttffreebitmap(TTBitmap *);
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.PP
|
||||||
|
.I Libttf
|
||||||
|
is a parser and renderer of TrueType fonts.
|
||||||
|
Given a \fLttf\fR font file it can produce the rendered versions of characters at a given size.
|
||||||
|
.PP
|
||||||
|
.I Ttfopen
|
||||||
|
opens the font at
|
||||||
|
.I filename
|
||||||
|
and initialises it for rendering at size
|
||||||
|
.I size
|
||||||
|
(specified in pixels per em).
|
||||||
|
.I Flags
|
||||||
|
is reserved for future use and should be zero.
|
||||||
|
If rendering at multiple sizes is desired,
|
||||||
|
.I ttfscale
|
||||||
|
reopens the font at a different size (internally the size-independent data is shared).
|
||||||
|
.I TTfclose
|
||||||
|
closes an opened font.
|
||||||
|
Each instance of a font created by
|
||||||
|
.I ttfopen
|
||||||
|
and
|
||||||
|
.I ttfscale
|
||||||
|
must be closed separately.
|
||||||
|
.PP
|
||||||
|
A character in a TrueType font is called a glyph.
|
||||||
|
Glyphs are numbered starting from 0 and the glyph indices do not need to follow any established coding scheme.
|
||||||
|
.I Ttffindchar
|
||||||
|
finds the glyph number of a given rune (Unicode codepoint).
|
||||||
|
If the character does not exist in the font, zero is returned.
|
||||||
|
Note that, in TrueType fonts, glyph 0 conventionally contains the "glyph not found" character.
|
||||||
|
.I Ttfenumchar
|
||||||
|
is like
|
||||||
|
.I ttffindchar
|
||||||
|
but will continue searching if the character is not in the font, returning the rune number for which it found a glyph in
|
||||||
|
.BR *rp .
|
||||||
|
It returns character in ascending Unicode order and it can be used to enumerate the characters in a font.
|
||||||
|
Zero is returned if there are no further characters.
|
||||||
|
.PP
|
||||||
|
.I Ttfgetglyph
|
||||||
|
interprets the actual data for a glyph specified by its index
|
||||||
|
.IR glyphidx .
|
||||||
|
With
|
||||||
|
.I render
|
||||||
|
set to zero, the data is left uninterpreted; currently its only use is
|
||||||
|
.I ttfgetcontour.
|
||||||
|
With
|
||||||
|
.I render
|
||||||
|
set to one, the glyph is also rendered, i.e. a pixel representation is produced and stored in the
|
||||||
|
.I TTBitmap
|
||||||
|
embedded in the
|
||||||
|
.I TTGlyph
|
||||||
|
structure it returns.
|
||||||
|
Although TrueType uses a right handed coordinate system (y increases going up), the bitmap data returns follows Plan 9 conventions (and is compatible with the
|
||||||
|
.IR draw (3)
|
||||||
|
mask argument).
|
||||||
|
The bottom left hand corner is at position (\fIxmin\fR, \fIymin\fR) in the TrueType coordinate system.
|
||||||
|
.I Ttfputglyph
|
||||||
|
should be used to return
|
||||||
|
.I TTGlyph
|
||||||
|
structures for cleanup.
|
||||||
|
.PP
|
||||||
|
.I Ttfgetcontour
|
||||||
|
can be used to obtain raw contour data for a glyph.
|
||||||
|
Given an index
|
||||||
|
.I i
|
||||||
|
it returns the corresponding contour (counting from zero), storing a pointer to a list of (\fIx\fR, \fIy\fR) pairs in
|
||||||
|
.BR *fp .
|
||||||
|
The array is allocated with
|
||||||
|
.BR malloc (2).
|
||||||
|
The (always odd) number of points is stored in
|
||||||
|
.BR *np .
|
||||||
|
The contours correspond to closed quadratic Bézier curves and the points with odd indices are the control points.
|
||||||
|
For an invalid index, zero is returned and
|
||||||
|
.B *fp
|
||||||
|
and
|
||||||
|
.B *np
|
||||||
|
are not accessed.
|
||||||
|
For a valid index, the number returned is the number of contours with index ≥ \fIi\fR.
|
||||||
|
.PP
|
||||||
|
.I Ttfrender
|
||||||
|
and
|
||||||
|
.I ttfrunerender
|
||||||
|
typeset a string of text (specified as UTF-8 or raw Unicode, respectively) and return a bitmap of size
|
||||||
|
.I w
|
||||||
|
and
|
||||||
|
.IR h .
|
||||||
|
It attempts to typeset text starting from
|
||||||
|
.I s
|
||||||
|
and up to and not including
|
||||||
|
.IR e .
|
||||||
|
If
|
||||||
|
.I e
|
||||||
|
is
|
||||||
|
.BR nil ,
|
||||||
|
text is typeset until a null byte is encountered.
|
||||||
|
.I Flags
|
||||||
|
specifies the alignment.
|
||||||
|
.BR TTFLALIGN ,
|
||||||
|
.BR TTFRALIGN
|
||||||
|
and
|
||||||
|
.B TTFCENTER
|
||||||
|
specify left-aligned, right-aligned and centered text, respectively.
|
||||||
|
.B TTFJUSTIFY
|
||||||
|
can be or'ed with these three options to produce text where any ``wrapped'' line is justified.
|
||||||
|
.PP
|
||||||
|
For reasons of efficiency and simplicity,
|
||||||
|
.I libttf
|
||||||
|
includes its own format for 1 bpp bitmaps.
|
||||||
|
In these bitmaps,
|
||||||
|
0 corresponds to transparent and 1 corresponds to opaque.
|
||||||
|
Otherwise, the format is identical to
|
||||||
|
.B k1
|
||||||
|
.IR image (6)
|
||||||
|
bitmaps.
|
||||||
|
.I Ttfnewbitmap
|
||||||
|
and
|
||||||
|
.I ttffreebitmap
|
||||||
|
allocate and deallocate such bitmaps, respectively.
|
||||||
|
.I TTGlyph
|
||||||
|
structures can be used in place of bitmaps but must be deallocated with
|
||||||
|
.IR ttfputglyph ,
|
||||||
|
not
|
||||||
|
.IR ttffreebitmap .
|
||||||
|
.I Ttfblit
|
||||||
|
copies part of one bitmap onto another.
|
||||||
|
Note that bits are or'ed together \(-- blitting a transparent over an opaque pixel does not produce an transparent pixel.
|
||||||
|
.SH SOURCE
|
||||||
|
.B /sys/src/libttf
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
Apple, ``TrueType™ Reference Manual''.
|
||||||
|
.br
|
||||||
|
Microsoft, ``OpenType® specification''.
|
||||||
|
.br
|
||||||
|
FreeType, source code (the only accurate source).
|
||||||
|
.br
|
||||||
|
.IR ttfrender (1).
|
||||||
|
.SH DIAGNOSTICS
|
||||||
|
Following standard conventions, routines returning pointers return
|
||||||
|
.B nil
|
||||||
|
on error and return an error message in
|
||||||
|
.BR errstr .
|
||||||
|
.SH BUGS
|
||||||
|
Both ``standards'' are packages of contradictions and lies.
|
||||||
|
.PP
|
||||||
|
Apple Advanced Typography and Microsoft OpenType extensions are not supported; similarly non-TrueType (Postscript, Bitmap) fonts packaged as
|
||||||
|
.B .ttf
|
||||||
|
files are not supported.
|
||||||
|
.PP
|
||||||
|
The library is immature and interfaces are virtually guaranteed to change.
|
||||||
|
.PP
|
||||||
|
Fonts packaged as
|
||||||
|
.B .ttc
|
||||||
|
files are not supported.
|
||||||
|
.SH HISTORY
|
||||||
|
.I Libttf
|
||||||
|
first appeared in 9front in June, 2018.
|
92
sys/src/libttf/bit.c
Normal file
92
sys/src/libttf/bit.c
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
#include <u.h>
|
||||||
|
#include <libc.h>
|
||||||
|
#include <bio.h>
|
||||||
|
#include <ttf.h>
|
||||||
|
#include "impl.h"
|
||||||
|
|
||||||
|
TTBitmap *
|
||||||
|
ttfnewbitmap(int w, int h)
|
||||||
|
{
|
||||||
|
TTBitmap *b;
|
||||||
|
|
||||||
|
b = mallocz(sizeof(TTBitmap), 1);
|
||||||
|
if(b == nil) return nil;
|
||||||
|
b->width = w;
|
||||||
|
b->height = h;
|
||||||
|
b->stride = w + 7 >> 3;
|
||||||
|
b->bit = mallocz(b->stride * h, 1);
|
||||||
|
if(b->bit == nil){
|
||||||
|
free(b);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ttffreebitmap(TTBitmap *b)
|
||||||
|
{
|
||||||
|
if(b == nil) return;
|
||||||
|
free(b->bit);
|
||||||
|
free(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ttfblit(TTBitmap *dst, int dx, int dy, TTBitmap *src, int sx0, int sy0, int sx1, int sy1)
|
||||||
|
{
|
||||||
|
uchar *sp, *dp;
|
||||||
|
u32int b;
|
||||||
|
int x, y, ss, ds, dx1, dy1;
|
||||||
|
|
||||||
|
if(sx0 < 0) sx0 = 0;
|
||||||
|
if(sy0 < 0) sy0 = 0;
|
||||||
|
if(sx1 > src->width) sx1 = src->width;
|
||||||
|
if(sy1 > src->height) sy1 = src->height;
|
||||||
|
if(dx < 0){
|
||||||
|
sx0 -= dx;
|
||||||
|
dx = 0;
|
||||||
|
}
|
||||||
|
if(dy < 0){
|
||||||
|
sy0 -= dy;
|
||||||
|
dy = 0;
|
||||||
|
}
|
||||||
|
dx1 = dx + sx1 - sx0;
|
||||||
|
dy1 = dy + sy1 - sy0;
|
||||||
|
if(dx1 > dst->width){
|
||||||
|
sx1 -= dx1 - dst->width;
|
||||||
|
dx1 = dst->width;
|
||||||
|
}
|
||||||
|
if(dy1 > dst->height) sy1 -= dy1 - dst->height;
|
||||||
|
if(sx1 <= sx0 || sy1 <= sy0) return;
|
||||||
|
ss = src->stride - ((sx1-1 >> 3) - (sx0 >> 3) + 1);
|
||||||
|
ds = dst->stride - ((dx1-1 >> 3) - (dx >> 3) + 1);
|
||||||
|
sp = src->bit + sy0 * src->stride + (sx0 >> 3);
|
||||||
|
dp = dst->bit + dy * dst->stride + (dx >> 3);
|
||||||
|
y = sy1 - sy0;
|
||||||
|
do{
|
||||||
|
if(sx0 >> 3 == sx1 >> 3){
|
||||||
|
b = (*sp++ << 8 & 0xff << 8-(sx0 & 7)) & -0x10000 >> (sx1 & 7);
|
||||||
|
if((sx0 & 7) == 0) b >>= 8;
|
||||||
|
x = (dx & 7) + (sx1 - sx0);
|
||||||
|
}else{
|
||||||
|
if((sx0 & 7) != 0)
|
||||||
|
b = *sp++ << 8 & 0xff << (-sx0 & 7);
|
||||||
|
else
|
||||||
|
b = 0;
|
||||||
|
x = (sx1 >> 3) - (sx0+7 >> 3);
|
||||||
|
while(--x >= 0){
|
||||||
|
b |= *sp++;
|
||||||
|
*dp++ |= b >> (dx & 7) + (-sx0 & 7);
|
||||||
|
b <<= 8;
|
||||||
|
}
|
||||||
|
if((sx1 & 7) != 0)
|
||||||
|
b |= *sp++ & -0x100 >> (sx1 & 7);
|
||||||
|
x = (dx & 7) + (-sx0 & 7) + (sx1 & 7);
|
||||||
|
}
|
||||||
|
for(; x > 0; x -= 8){
|
||||||
|
*dp++ |= b >> (dx & 7) + (-sx0 & 7);
|
||||||
|
b <<= 8;
|
||||||
|
}
|
||||||
|
sp += ss;
|
||||||
|
dp += ds;
|
||||||
|
}while(--y > 0);
|
||||||
|
}
|
322
sys/src/libttf/cmap.c
Normal file
322
sys/src/libttf/cmap.c
Normal file
|
@ -0,0 +1,322 @@
|
||||||
|
#include <u.h>
|
||||||
|
#include <libc.h>
|
||||||
|
#include <bio.h>
|
||||||
|
#include <ttf.h>
|
||||||
|
#include "impl.h"
|
||||||
|
|
||||||
|
int
|
||||||
|
ttffindchar(TTFont *fx, Rune r)
|
||||||
|
{
|
||||||
|
int i, j, k, rv;
|
||||||
|
TTChMap *p;
|
||||||
|
TTFontU *f;
|
||||||
|
|
||||||
|
f = fx->u;
|
||||||
|
i = 0;
|
||||||
|
j = f->ncmap - 1;
|
||||||
|
if(r < f->cmap[0].start || r > f->cmap[j].end) return 0;
|
||||||
|
while(i < j){
|
||||||
|
k = (i + j) / 2;
|
||||||
|
if(f->cmap[k].end < r)
|
||||||
|
i = k+1;
|
||||||
|
else if(f->cmap[k].start > r)
|
||||||
|
j = k-1;
|
||||||
|
else
|
||||||
|
i = j = k;
|
||||||
|
}
|
||||||
|
if(i > j) return 0;
|
||||||
|
p = &f->cmap[i];
|
||||||
|
if(r < p->start || r > p->end) return 0;
|
||||||
|
if((p->flags & TTCINVALID) != 0) return 0;
|
||||||
|
if(p->tab != nil)
|
||||||
|
return p->tab[r - p->start];
|
||||||
|
rv = r + p->delta;
|
||||||
|
if((p->flags & TTCDELTA16) != 0)
|
||||||
|
rv = (u16int)rv;
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ttfenumchar(TTFont *fx, Rune r, Rune *rp)
|
||||||
|
{
|
||||||
|
int i, j, k, rv;
|
||||||
|
TTChMap *p;
|
||||||
|
TTFontU *f;
|
||||||
|
|
||||||
|
f = fx->u;
|
||||||
|
i = 0;
|
||||||
|
j = f->ncmap - 1;
|
||||||
|
if(r > f->cmap[j].end) return 0;
|
||||||
|
while(i < j){
|
||||||
|
k = (i + j) / 2;
|
||||||
|
if(f->cmap[k].end < r)
|
||||||
|
i = k+1;
|
||||||
|
else if(f->cmap[k].start > r)
|
||||||
|
j = k-1;
|
||||||
|
else
|
||||||
|
i = j = k;
|
||||||
|
}
|
||||||
|
if(j < 0) j = 0;
|
||||||
|
for(p = &f->cmap[j]; p < &f->cmap[f->ncmap]; p++){
|
||||||
|
if((p->flags & TTCINVALID) != 0)
|
||||||
|
continue;
|
||||||
|
if(r < p->start)
|
||||||
|
r = p->start;
|
||||||
|
if(p->tab != nil){
|
||||||
|
SET(rv);
|
||||||
|
while(r <= p->end && (rv = p->tab[r - p->start], rv == 0))
|
||||||
|
r++;
|
||||||
|
if(r > p->end)
|
||||||
|
continue;
|
||||||
|
if(rp != nil)
|
||||||
|
*rp = r;
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
while(r < p->end){
|
||||||
|
rv = r + p->delta;
|
||||||
|
if((p->flags & TTCDELTA16) != 0)
|
||||||
|
rv = (u16int) rv;
|
||||||
|
if(rv != 0){
|
||||||
|
if(rp != nil)
|
||||||
|
*rp = r;
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ttfgotosub(TTFontU *f)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
u16int nsub, id, sid, off;
|
||||||
|
int rank, maxrank;
|
||||||
|
u32int maxoff;
|
||||||
|
#define SUBID(a,b) ((a)<<16|(b))
|
||||||
|
|
||||||
|
if(ttfgototable(f, "cmap") < 0)
|
||||||
|
return -1;
|
||||||
|
ttfunpack(f, ".. w", &nsub);
|
||||||
|
maxrank = 0;
|
||||||
|
maxoff = 0;
|
||||||
|
for(i = 0; i < nsub; i++){
|
||||||
|
ttfunpack(f, "wwl", &id, &sid, &off);
|
||||||
|
switch(id << 16 | sid){
|
||||||
|
case SUBID(0, 4): /* Unicode 2.0 or later (BMP and non-BMP) */
|
||||||
|
rank = 100;
|
||||||
|
break;
|
||||||
|
case SUBID(0, 0): /* Unicode default */
|
||||||
|
case SUBID(0, 1): /* Unicode 1.1 */
|
||||||
|
case SUBID(0, 2): /* ISO 10646 */
|
||||||
|
case SUBID(0, 3): /* Unicode 2.0 (BMP) */
|
||||||
|
rank = 80;
|
||||||
|
break;
|
||||||
|
case SUBID(3, 10): /* Windows, UCS-4 */
|
||||||
|
rank = 60;
|
||||||
|
break;
|
||||||
|
case SUBID(3, 1): /* Windows, UCS-2 */
|
||||||
|
rank = 40;
|
||||||
|
break;
|
||||||
|
case SUBID(3, 0): /* Windows, Symbol */
|
||||||
|
rank = 20;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
rank = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(rank > maxrank){
|
||||||
|
maxrank = rank;
|
||||||
|
maxoff = off;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(maxrank == 0){
|
||||||
|
werrstr("no suitable character table");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(ttfgototable(f, "cmap") < 0)
|
||||||
|
return -1;
|
||||||
|
Bseek(f->bin, maxoff, 1);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cmap0(TTFontU *f)
|
||||||
|
{
|
||||||
|
u16int len;
|
||||||
|
int i;
|
||||||
|
u8int *p;
|
||||||
|
int *q;
|
||||||
|
|
||||||
|
ttfunpack(f, "w2", &len);
|
||||||
|
if(len < 262){
|
||||||
|
werrstr("character table too short");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
f->cmap = mallocz(sizeof(TTChMap), 1);
|
||||||
|
if(f->cmap == nil)
|
||||||
|
return -1;
|
||||||
|
f->ncmap = 1;
|
||||||
|
f->cmap->start = 0;
|
||||||
|
f->cmap->end = 0xff;
|
||||||
|
f->cmap->tab = mallocz(256 * sizeof(int), 1);
|
||||||
|
if(f->cmap->tab == nil)
|
||||||
|
return -1;
|
||||||
|
Bread(f->bin, f->cmap->tab, 256 * sizeof(int));
|
||||||
|
p = (u8int*)f->cmap->tab + 256;
|
||||||
|
q = f->cmap->tab + 256;
|
||||||
|
for(i = 255; i >= 0; i--)
|
||||||
|
*--q = *--p;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cmap4(TTFontU *f)
|
||||||
|
{
|
||||||
|
u16int len, ncmap;
|
||||||
|
int i, j, n, n0, off;
|
||||||
|
u16int v;
|
||||||
|
u8int *buf;
|
||||||
|
|
||||||
|
ttfunpack(f, "w2", &len);
|
||||||
|
if(len < 16){
|
||||||
|
werrstr("character table too short");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ttfunpack(f, "w6", &ncmap);
|
||||||
|
ncmap /= 2;
|
||||||
|
if(len < 16 + 8 * ncmap){
|
||||||
|
werrstr("character table too short");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
f->cmap = mallocz(sizeof(TTChMap) * ncmap, 1);
|
||||||
|
if(f->cmap == nil) return -1;
|
||||||
|
f->ncmap = ncmap;
|
||||||
|
for(i = 0; i < ncmap; i++)
|
||||||
|
f->cmap[i].flags = TTCDELTA16;
|
||||||
|
for(i = 0; i < ncmap; i++)
|
||||||
|
ttfunpack(f, "W", &f->cmap[i].end);
|
||||||
|
ttfunpack(f, "..");
|
||||||
|
for(i = 0; i < ncmap; i++)
|
||||||
|
ttfunpack(f, "W", &f->cmap[i].start);
|
||||||
|
for(i = 0; i < ncmap; i++)
|
||||||
|
ttfunpack(f, "W", &f->cmap[i].delta);
|
||||||
|
for(i = 0; i < ncmap; i++)
|
||||||
|
ttfunpack(f, "W", &f->cmap[i].temp);
|
||||||
|
len -= 10 + 8 * ncmap;
|
||||||
|
buf = malloc(len);
|
||||||
|
if(buf == nil)
|
||||||
|
return -1;
|
||||||
|
Bread(f->bin, buf, len);
|
||||||
|
for(i = 0; i < ncmap; i++){
|
||||||
|
if(f->cmap[i].temp == 0) continue;
|
||||||
|
n0 = f->cmap[i].end - f->cmap[i].start + 1;
|
||||||
|
n = n0;
|
||||||
|
off = f->cmap[i].temp - (ncmap - i) * 2;
|
||||||
|
if(off + 2 * n > len) n = (len - off) / 2;
|
||||||
|
if(off < 0 || n <= 0){
|
||||||
|
f->cmap[i].flags |= TTCINVALID;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
f->cmap[i].tab = mallocz(n0 * sizeof(int), 1);
|
||||||
|
if(f->cmap[i].tab == nil)
|
||||||
|
return -1;
|
||||||
|
for(j = 0; j < n; j++){
|
||||||
|
v = buf[off + 2*j] << 8 | buf[off + 2*j + 1];
|
||||||
|
if(v != 0) v += f->cmap[i].delta;
|
||||||
|
f->cmap[i].tab[j] = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cmap6(TTFontU *f)
|
||||||
|
{
|
||||||
|
u16int len, first, cnt, v;
|
||||||
|
int *p;
|
||||||
|
u8int *q;
|
||||||
|
|
||||||
|
ttfunpack(f, "w2", &len);
|
||||||
|
if(len < 12){
|
||||||
|
werrstr("character table too short");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ttfunpack(f, "ww", &first, &cnt);
|
||||||
|
f->cmap = mallocz(sizeof(TTChMap), 1);
|
||||||
|
if(f->cmap == nil)
|
||||||
|
return -1;
|
||||||
|
f->ncmap = 1;
|
||||||
|
f->cmap->start = first;
|
||||||
|
f->cmap->end = first + len - 1;
|
||||||
|
f->cmap->tab = mallocz(cnt * sizeof(int), 1);
|
||||||
|
if(f->cmap->tab == nil)
|
||||||
|
return -1;
|
||||||
|
if(len < 10 + 2 * cnt){
|
||||||
|
werrstr("character table too short");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
Bread(f->bin, f->cmap->tab, 2 * cnt);
|
||||||
|
p = f->cmap->tab + cnt;
|
||||||
|
q = (u8int*) f->cmap->tab + 2 * cnt;
|
||||||
|
while(p > f->cmap->tab){
|
||||||
|
v = *--q;
|
||||||
|
v |= *--q << 8;
|
||||||
|
*--p = v;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cmap12(TTFontU *f)
|
||||||
|
{
|
||||||
|
u32int len;
|
||||||
|
u32int ncmap;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
ttfunpack(f, "2l4", &len);
|
||||||
|
if(len < 16){
|
||||||
|
werrstr("character table too short");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ttfunpack(f, "l", &ncmap);
|
||||||
|
if(len < 16 + 12 * ncmap){
|
||||||
|
werrstr("character table too short");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
f->cmap = mallocz(sizeof(TTChMap) * ncmap, 1);
|
||||||
|
if(f->cmap == nil)
|
||||||
|
return -1;
|
||||||
|
f->ncmap = ncmap;
|
||||||
|
for(i = 0; i < ncmap; i++){
|
||||||
|
ttfunpack(f, "lll", &f->cmap[i].start, &f->cmap[i].end, &f->cmap[i].delta);
|
||||||
|
f->cmap[i].delta -= f->cmap[i].start;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int (*cmaphand[])(TTFontU *) = {
|
||||||
|
[0] cmap0,
|
||||||
|
[4] cmap4,
|
||||||
|
[6] cmap6,
|
||||||
|
[12] cmap12,
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
ttfparsecmap(TTFontU *f)
|
||||||
|
{
|
||||||
|
u16int format;
|
||||||
|
|
||||||
|
if(ttfgotosub(f) < 0)
|
||||||
|
return -1;
|
||||||
|
ttfunpack(f, "w", &format);
|
||||||
|
if(format >= nelem(cmaphand) || cmaphand[format] == nil){
|
||||||
|
werrstr("character table in unknown format %d", format);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(cmaphand[format](f) < 0)
|
||||||
|
return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
371
sys/src/libttf/glyf.c
Normal file
371
sys/src/libttf/glyf.c
Normal file
|
@ -0,0 +1,371 @@
|
||||||
|
#include <u.h>
|
||||||
|
#include <libc.h>
|
||||||
|
#include <bio.h>
|
||||||
|
#include <draw.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <ttf.h>
|
||||||
|
#include "impl.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
ttfputglyph(TTGlyph *g)
|
||||||
|
{
|
||||||
|
if(g == nil) return;
|
||||||
|
free(g->pt);
|
||||||
|
free(g->ptorg);
|
||||||
|
free(g->confst);
|
||||||
|
free(g->bit);
|
||||||
|
free(g->hint);
|
||||||
|
free(g);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
glyphscale(TTGlyph *g)
|
||||||
|
{
|
||||||
|
TTFont *f;
|
||||||
|
int i;
|
||||||
|
TTPoint *p;
|
||||||
|
|
||||||
|
f = g->font;
|
||||||
|
for(i = 0; i < g->npt; i++){
|
||||||
|
p = &g->pt[i];
|
||||||
|
p->x = ttfrounddiv(p->x * f->ppem * 64, f->u->emsize);
|
||||||
|
p->y = ttfrounddiv(p->y * f->ppem * 64, f->u->emsize);
|
||||||
|
}
|
||||||
|
memmove(g->ptorg, g->pt, sizeof(TTPoint) * g->npt);
|
||||||
|
g->pt[g->npt - 1].x = g->pt[g->npt - 1].x + 32 & -64;
|
||||||
|
}
|
||||||
|
|
||||||
|
static TTGlyph *
|
||||||
|
emptyglyph(TTFont *fs, int glyph, int render)
|
||||||
|
{
|
||||||
|
TTGlyph *g;
|
||||||
|
|
||||||
|
g = mallocz(sizeof(TTGlyph), 1);
|
||||||
|
if(g == nil)
|
||||||
|
return nil;
|
||||||
|
g->font = fs;
|
||||||
|
g->info = &fs->u->ginfo[glyph];
|
||||||
|
g->confst = malloc(sizeof(int));
|
||||||
|
g->npt = 2;
|
||||||
|
g->pt = mallocz(sizeof(TTPoint) * 2, 1);
|
||||||
|
g->ptorg = mallocz(sizeof(TTPoint) * 2, 1);
|
||||||
|
if(g->confst == nil || g->pt == nil || g->ptorg == nil){
|
||||||
|
ttfputglyph(g);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
g->pt[1].x = g->info->advanceWidth;
|
||||||
|
g->npt = 2;
|
||||||
|
if(render)
|
||||||
|
glyphscale(g);
|
||||||
|
g->xmin = 0;
|
||||||
|
g->ymin = 0;
|
||||||
|
g->xmax = g->info->advanceWidth;
|
||||||
|
g->ymax = 1;
|
||||||
|
if(render){
|
||||||
|
g->xminpx = 0;
|
||||||
|
g->xmaxpx = (g->xmax * fs->ppem + fs->u->emsize - 1) / fs->u->emsize;
|
||||||
|
g->yminpx = 0;
|
||||||
|
g->ymaxpx = 1;
|
||||||
|
}
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
static TTGlyph *
|
||||||
|
simpglyph(TTFont *fs, int glyph, int nc, int render)
|
||||||
|
{
|
||||||
|
u16int np;
|
||||||
|
short x;
|
||||||
|
u16int len;
|
||||||
|
u16int temp16;
|
||||||
|
u8int temp8;
|
||||||
|
u8int *flags, *fp, *fq;
|
||||||
|
TTPoint *p;
|
||||||
|
int i, j, r;
|
||||||
|
short lastx, lasty;
|
||||||
|
TTFontU *f;
|
||||||
|
TTGlyph *g;
|
||||||
|
|
||||||
|
flags = nil;
|
||||||
|
f = fs->u;
|
||||||
|
g = mallocz(sizeof(TTGlyph), 1);
|
||||||
|
if(g == nil)
|
||||||
|
return nil;
|
||||||
|
g->font = fs;
|
||||||
|
g->info = &f->ginfo[glyph];
|
||||||
|
g->confst = malloc(sizeof(u16int) * (nc + 1));
|
||||||
|
if(g->confst == nil)
|
||||||
|
goto err;
|
||||||
|
x = -1;
|
||||||
|
for(i = g->ncon; i < nc; i++){
|
||||||
|
g->confst[i] = x + 1;
|
||||||
|
ttfunpack(f, "w", &x);
|
||||||
|
}
|
||||||
|
g->confst[i] = x + 1;
|
||||||
|
g->ncon = nc;
|
||||||
|
|
||||||
|
np = x + 1;
|
||||||
|
ttfunpack(f, "w", &len);
|
||||||
|
g->nhint = len;
|
||||||
|
g->hint = mallocz(len, 1);
|
||||||
|
if(g->hint == nil)
|
||||||
|
goto err;
|
||||||
|
Bread(f->bin, g->hint, len);
|
||||||
|
|
||||||
|
flags = mallocz(np, 1);
|
||||||
|
if(flags == nil)
|
||||||
|
goto err;
|
||||||
|
for(i = 0; i < np; i++){
|
||||||
|
j = Bgetc(f->bin);
|
||||||
|
flags[i] = j;
|
||||||
|
if((j & 8) != 0){
|
||||||
|
r = Bgetc(f->bin);
|
||||||
|
while(r-- > 0)
|
||||||
|
flags[++i] = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fp = flags;
|
||||||
|
fq = flags;
|
||||||
|
lastx = lasty = 0;
|
||||||
|
g->pt = malloc(sizeof(TTPoint) * (np + 2));
|
||||||
|
if(g->pt == nil)
|
||||||
|
goto err;
|
||||||
|
g->ptorg = malloc(sizeof(TTPoint) * (np + 2));
|
||||||
|
if(g->ptorg == nil)
|
||||||
|
goto err;
|
||||||
|
for(i = 0; i < np; i++){
|
||||||
|
p = &g->pt[g->npt + i];
|
||||||
|
p->flags = *fp & 1;
|
||||||
|
switch(*fp++ & 0x12){
|
||||||
|
case 0x00: ttfunpack(f, "w", &temp16); p->x = lastx += temp16; break;
|
||||||
|
case 0x02: ttfunpack(f, "b", &temp8); p->x = lastx -= temp8; break;
|
||||||
|
case 0x10: p->x = lastx; break;
|
||||||
|
case 0x12: ttfunpack(f, "b", &temp8); p->x = lastx += temp8; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(i = 0; i < np; i++){
|
||||||
|
p = &g->pt[g->npt + i];
|
||||||
|
switch(*fq++ & 0x24){
|
||||||
|
case 0x00: ttfunpack(f, "w", &temp16); p->y = lasty += temp16; break;
|
||||||
|
case 0x04: ttfunpack(f, "b", &temp8); p->y = lasty -= temp8; break;
|
||||||
|
case 0x20: p->y = lasty; break;
|
||||||
|
case 0x24: ttfunpack(f, "b", &temp8); p->y = lasty += temp8; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g->pt[np] = (TTPoint){0,0,0};
|
||||||
|
g->pt[np+1] = (TTPoint){f->ginfo[glyph].advanceWidth,0,0};
|
||||||
|
g->npt = np + 2;
|
||||||
|
free(flags);
|
||||||
|
if(render){
|
||||||
|
glyphscale(g);
|
||||||
|
ttfhint(g);
|
||||||
|
}
|
||||||
|
return g;
|
||||||
|
err:
|
||||||
|
free(flags);
|
||||||
|
ttfputglyph(g);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
static TTGlyph *getglyph(TTFont *, int, int);
|
||||||
|
|
||||||
|
enum {
|
||||||
|
ARG_1_AND_2_ARE_WORDS = 1<<0,
|
||||||
|
ARGS_ARE_XY_VALUES = 1<<1,
|
||||||
|
ROUND_XY_TO_GRID = 1<<2,
|
||||||
|
WE_HAVE_A_SCALE = 1<<3,
|
||||||
|
MORE_COMPONENTS = 1<<5,
|
||||||
|
WE_HAVE_AN_X_AND_Y_SCALE = 1<<6,
|
||||||
|
WE_HAVE_A_TWO_BY_TWO = 1<<7,
|
||||||
|
WE_HAVE_INSTRUCTIONS = 1<<8,
|
||||||
|
USE_MY_METRICS = 1<<9,
|
||||||
|
OVERLAP_COMPOUND = 1<<10,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
mergeglyph(TTGlyph *g, TTGlyph *h, int flags, int x, int y, int a, int b, int c, int d, int render)
|
||||||
|
{
|
||||||
|
int i, m;
|
||||||
|
TTPoint *p;
|
||||||
|
TTFont *f;
|
||||||
|
int dx, dy;
|
||||||
|
|
||||||
|
f = g->font;
|
||||||
|
g->confst = realloc(g->confst, sizeof(int) * (g->ncon + h->ncon + 1));
|
||||||
|
for(i = 1; i <= h->ncon; i++)
|
||||||
|
g->confst[g->ncon + i] = g->confst[g->ncon] + h->confst[i];
|
||||||
|
g->ncon += h->ncon;
|
||||||
|
g->pt = realloc(g->pt, sizeof(TTPoint) * (g->npt + h->npt - 2));
|
||||||
|
if((flags & USE_MY_METRICS) == 0){
|
||||||
|
memmove(g->pt + g->npt + h->npt - 4, g->pt + g->npt - 2, 2 * sizeof(TTPoint));
|
||||||
|
m = h->npt - 2;
|
||||||
|
}else
|
||||||
|
m = h->npt;
|
||||||
|
for(i = 0; i < m; i++){
|
||||||
|
p = &g->pt[g->npt - 2 + i];
|
||||||
|
*p = h->pt[i];
|
||||||
|
dx = ttfrounddiv(p->x * a + p->y * b, 16384);
|
||||||
|
dy = ttfrounddiv(p->x * c + p->y * d, 16384);
|
||||||
|
p->x = dx;
|
||||||
|
p->y = dy;
|
||||||
|
if((flags & ARGS_ARE_XY_VALUES) != 0){
|
||||||
|
if(render){
|
||||||
|
dx = ttfrounddiv(x * f->ppem * 64, f->u->emsize);
|
||||||
|
dy = ttfrounddiv(y * f->ppem * 64, f->u->emsize);
|
||||||
|
if((flags & ROUND_XY_TO_GRID) != 0){
|
||||||
|
dx = dx + 32 & -64;
|
||||||
|
dy = dy + 32 & -64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p->x += dx;
|
||||||
|
p->y += dy;
|
||||||
|
}else
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
g->npt += h->npt - 2;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static TTGlyph *
|
||||||
|
compglyph(TTFont *fs, int glyph, int render)
|
||||||
|
{
|
||||||
|
u16int flags, idx;
|
||||||
|
int x, y;
|
||||||
|
int a, b, c, d;
|
||||||
|
TTFontU *f;
|
||||||
|
uvlong off;
|
||||||
|
TTGlyph *g, *h;
|
||||||
|
u16int len;
|
||||||
|
|
||||||
|
f = fs->u;
|
||||||
|
g = mallocz(sizeof(TTGlyph), 1);
|
||||||
|
if(g == nil)
|
||||||
|
return nil;
|
||||||
|
g->font = fs;
|
||||||
|
g->info = &f->ginfo[glyph];
|
||||||
|
g->pt = mallocz(sizeof(TTPoint) * 2, 1);
|
||||||
|
if(g->pt == nil){
|
||||||
|
err:
|
||||||
|
ttfputglyph(g);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
g->pt[1].x = ttfrounddiv(f->ginfo[glyph].advanceWidth * fs->ppem * 64, f->emsize);
|
||||||
|
g->npt = 2;
|
||||||
|
g->confst = mallocz(sizeof(int), 1);
|
||||||
|
if(g->confst == nil)
|
||||||
|
goto err;
|
||||||
|
do{
|
||||||
|
ttfunpack(f, "ww", &flags, &idx);
|
||||||
|
switch(flags & (ARG_1_AND_2_ARE_WORDS | ARGS_ARE_XY_VALUES)){
|
||||||
|
case 0: ttfunpack(f, "BB", &x, &y); break;
|
||||||
|
case ARGS_ARE_XY_VALUES: ttfunpack(f, "BB", &x, &y); x = (char)x; y = (char)y; break;
|
||||||
|
case ARG_1_AND_2_ARE_WORDS: ttfunpack(f, "WW", &x, &y); break;
|
||||||
|
case ARG_1_AND_2_ARE_WORDS | ARGS_ARE_XY_VALUES: ttfunpack(f, "WW", &x, &y); x = (short)x; y = (short)y; break;
|
||||||
|
}
|
||||||
|
if((flags & WE_HAVE_A_SCALE) != 0){
|
||||||
|
ttfunpack(f, "S", &a);
|
||||||
|
d = a;
|
||||||
|
b = c = 0;
|
||||||
|
}else if((flags & WE_HAVE_AN_X_AND_Y_SCALE) != 0){
|
||||||
|
ttfunpack(f, "SS", &a, &d);
|
||||||
|
b = c = 0;
|
||||||
|
}else if((flags & WE_HAVE_A_TWO_BY_TWO) != 0)
|
||||||
|
ttfunpack(f, "SSSS", &a, &b, &c, &d);
|
||||||
|
else{
|
||||||
|
a = d = 1<<14;
|
||||||
|
b = c = 0;
|
||||||
|
}
|
||||||
|
off = Bseek(f->bin, 0, 1);
|
||||||
|
h = getglyph(fs, idx, render);
|
||||||
|
if(h == nil){
|
||||||
|
ttfputglyph(g);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
if(mergeglyph(g, h, flags, x, y, a, b, c, d, render) < 0){
|
||||||
|
ttfputglyph(h);
|
||||||
|
ttfputglyph(g);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
ttfputglyph(h);
|
||||||
|
Bseek(f->bin, off, 0);
|
||||||
|
}while((flags & MORE_COMPONENTS) != 0);
|
||||||
|
g->ptorg = malloc(sizeof(TTPoint) * g->npt);
|
||||||
|
memmove(g->ptorg, g->pt, sizeof(TTPoint) * g->npt);
|
||||||
|
// g->pt[g->npt - 1].x = g->pt[g->npt - 1].x + 32 & -64;
|
||||||
|
if(render && (flags & WE_HAVE_INSTRUCTIONS) != 0){
|
||||||
|
ttfunpack(f, "w", &len);
|
||||||
|
g->nhint = len;
|
||||||
|
g->hint = mallocz(len, 1);
|
||||||
|
if(g->hint == nil)
|
||||||
|
goto err;
|
||||||
|
Bread(f->bin, g->hint, len);
|
||||||
|
ttfhint(g);
|
||||||
|
}
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
static TTGlyph *
|
||||||
|
getglyph(TTFont *fs, int glyph, int render)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
short xmin, ymin, xmax, ymax, nc;
|
||||||
|
TTFontU *f;
|
||||||
|
TTGlyph *g;
|
||||||
|
|
||||||
|
f = fs->u;
|
||||||
|
if((uint)glyph >= f->numGlyphs){
|
||||||
|
werrstr("no such glyph %d", glyph);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
if(f->ginfo[glyph].loca == f->ginfo[glyph+1].loca){
|
||||||
|
return emptyglyph(fs, glyph, render);
|
||||||
|
}
|
||||||
|
if(ttfgototable(f, "glyf") < 0)
|
||||||
|
return nil;
|
||||||
|
Bseek(f->bin, f->ginfo[glyph].loca, 1);
|
||||||
|
ttfunpack(f, "wwwww", &nc, &xmin, &ymin, &xmax, &ymax);
|
||||||
|
if(nc < 0)
|
||||||
|
g = compglyph(fs, glyph, render);
|
||||||
|
else
|
||||||
|
g = simpglyph(fs, glyph, nc, render);
|
||||||
|
if(g == nil)
|
||||||
|
return nil;
|
||||||
|
g->xmin = g->pt[0].x;
|
||||||
|
g->xmax = g->pt[0].x;
|
||||||
|
g->ymin = g->pt[0].y;
|
||||||
|
g->ymax = g->pt[0].y;
|
||||||
|
for(i = 1; i < g->npt - 2; i++){
|
||||||
|
if(g->pt[i].x < g->xmin)
|
||||||
|
g->xmin = g->pt[i].x;
|
||||||
|
if(g->pt[i].x > g->xmax)
|
||||||
|
g->xmax = g->pt[i].x;
|
||||||
|
if(g->pt[i].y < g->ymin)
|
||||||
|
g->ymin = g->pt[i].y;
|
||||||
|
if(g->pt[i].y > g->ymax)
|
||||||
|
g->ymax = g->pt[i].y;
|
||||||
|
}
|
||||||
|
if(render){
|
||||||
|
g->xminpx = g->xmin >> 6;
|
||||||
|
g->xmaxpx = g->xmax + 63 >> 6;
|
||||||
|
g->yminpx = g->ymin >> 6;
|
||||||
|
g->ymaxpx = g->ymax + 63 >> 6;
|
||||||
|
}
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
TTGlyph *
|
||||||
|
ttfgetglyph(TTFont *fs, int glyph, int render)
|
||||||
|
{
|
||||||
|
TTGlyph *g;
|
||||||
|
|
||||||
|
g = getglyph(fs, glyph, render);
|
||||||
|
if(g == nil)
|
||||||
|
return nil;
|
||||||
|
g->idx = glyph;
|
||||||
|
if(render){
|
||||||
|
ttfscan(g);
|
||||||
|
g->advanceWidthpx = (g->pt[g->npt - 1].x - g->pt[g->npt - 2].x + 63) / 64;
|
||||||
|
}
|
||||||
|
setmalloctag(g, getcallerpc(&fs));
|
||||||
|
return g;
|
||||||
|
}
|
344
sys/src/libttf/head.c
Normal file
344
sys/src/libttf/head.c
Normal file
|
@ -0,0 +1,344 @@
|
||||||
|
#include <u.h>
|
||||||
|
#include <libc.h>
|
||||||
|
#include <bio.h>
|
||||||
|
#include <draw.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <ttf.h>
|
||||||
|
#include "impl.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
ttfunpack(TTFontU *f, char *p, ...)
|
||||||
|
{
|
||||||
|
va_list va;
|
||||||
|
int n;
|
||||||
|
uchar *p1;
|
||||||
|
u16int *p2;
|
||||||
|
u32int *p4;
|
||||||
|
|
||||||
|
va_start(va, p);
|
||||||
|
for(; *p != 0; p++)
|
||||||
|
switch(*p){
|
||||||
|
case 'b':
|
||||||
|
p1 = va_arg(va, u8int *);
|
||||||
|
*p1 = Bgetc(f->bin);
|
||||||
|
break;
|
||||||
|
case 'B':
|
||||||
|
p4 = va_arg(va, u32int *);
|
||||||
|
*p4 = Bgetc(f->bin);
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
p2 = va_arg(va, u16int *);
|
||||||
|
*p2 = Bgetc(f->bin) << 8;
|
||||||
|
*p2 |= Bgetc(f->bin);
|
||||||
|
break;
|
||||||
|
case 'W':
|
||||||
|
p4 = va_arg(va, u32int *);
|
||||||
|
*p4 = Bgetc(f->bin) << 8;
|
||||||
|
*p4 |= Bgetc(f->bin);
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
p4 = va_arg(va, u32int *);
|
||||||
|
*p4 = (char)Bgetc(f->bin) << 8;
|
||||||
|
*p4 |= Bgetc(f->bin);
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
p4 = va_arg(va, u32int *);
|
||||||
|
*p4 = Bgetc(f->bin) << 24;
|
||||||
|
*p4 |= Bgetc(f->bin) << 16;
|
||||||
|
*p4 |= Bgetc(f->bin) << 8;
|
||||||
|
*p4 |= Bgetc(f->bin);
|
||||||
|
break;
|
||||||
|
case '.': Bgetc(f->bin); break;
|
||||||
|
case ' ': break;
|
||||||
|
default:
|
||||||
|
if(isdigit(*p)){
|
||||||
|
n = strtol(p, &p, 10);
|
||||||
|
p--;
|
||||||
|
Bseek(f->bin, n, 1);
|
||||||
|
}else abort();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
directory(TTFontU *f)
|
||||||
|
{
|
||||||
|
u32int scaler;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
ttfunpack(f, "lw .. .. ..", &scaler, &f->ntab);
|
||||||
|
if(scaler != 0x74727565 && scaler != 0x10000){
|
||||||
|
werrstr("unknown scaler type %#ux", scaler);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
f->tab = mallocz(sizeof(TTTable) * f->ntab, 1);
|
||||||
|
if(f->tab == nil) return -1;
|
||||||
|
for(i = 0; i < f->ntab; i++)
|
||||||
|
ttfunpack(f, "llll", &f->tab[i].tag, &f->tab[i].csum, &f->tab[i].offset, &f->tab[i].len);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ttfgototable(TTFontU *f, char *str)
|
||||||
|
{
|
||||||
|
TTTable *t;
|
||||||
|
u32int tag;
|
||||||
|
|
||||||
|
tag = (u8int)str[0] << 24 | (u8int)str[1] << 16 | (u8int)str[2] << 8 | (u8int)str[3];
|
||||||
|
for(t = f->tab; t < f->tab + f->ntab; t++)
|
||||||
|
if(t->tag == tag){
|
||||||
|
Bseek(f->bin, t->offset, 0);
|
||||||
|
return t->len;
|
||||||
|
}
|
||||||
|
werrstr("no such table '%s'", str);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ttfparseloca(TTFontU *f)
|
||||||
|
{
|
||||||
|
int len, i;
|
||||||
|
u32int x;
|
||||||
|
|
||||||
|
len = ttfgototable(f, "loca");
|
||||||
|
if(len < 0) return -1;
|
||||||
|
x = 0;
|
||||||
|
if(f->longloca){
|
||||||
|
if(len > (f->numGlyphs + 1) * 4) len = (f->numGlyphs + 1) * 4;
|
||||||
|
for(i = 0; i < len/4; i++){
|
||||||
|
x = Bgetc(f->bin) << 24;
|
||||||
|
x |= Bgetc(f->bin) << 16;
|
||||||
|
x |= Bgetc(f->bin) << 8;
|
||||||
|
x |= Bgetc(f->bin);
|
||||||
|
f->ginfo[i].loca = x;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if(len > (f->numGlyphs + 1) * 2) len = (f->numGlyphs + 1) * 2;
|
||||||
|
for(i = 0; i < len/2; i++){
|
||||||
|
x = Bgetc(f->bin) << 8;
|
||||||
|
x |= Bgetc(f->bin);
|
||||||
|
f->ginfo[i].loca = x * 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(; i < f->numGlyphs; i++)
|
||||||
|
f->ginfo[i].loca = x;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ttfparsehmtx(TTFontU *f)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
u16int x, y;
|
||||||
|
int len;
|
||||||
|
int maxlsb;
|
||||||
|
|
||||||
|
len = ttfgototable(f, "hmtx");
|
||||||
|
if(len < 0)
|
||||||
|
return -1;
|
||||||
|
if(f->numOfLongHorMetrics > f->numGlyphs){
|
||||||
|
werrstr("nonsensical header: numOfLongHorMetrics > numGlyphs");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
for(i = 0; i < f->numOfLongHorMetrics; i++){
|
||||||
|
ttfunpack(f, "ww", &x, &y);
|
||||||
|
f->ginfo[i].advanceWidth = x;
|
||||||
|
f->ginfo[i].lsb = y;
|
||||||
|
}
|
||||||
|
maxlsb = (len - 2 * f->numOfLongHorMetrics) / 2;
|
||||||
|
if(maxlsb > f->numGlyphs){
|
||||||
|
werrstr("nonsensical header: maxlsb > f->numGlyphs");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
for(; i < maxlsb; i++){
|
||||||
|
ttfunpack(f, "w", &y);
|
||||||
|
f->ginfo[i].advanceWidth = x;
|
||||||
|
f->ginfo[i].lsb = y;
|
||||||
|
}
|
||||||
|
for(; i < f->numGlyphs; i++){
|
||||||
|
f->ginfo[i].advanceWidth = x;
|
||||||
|
f->ginfo[i].lsb = y;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ttfparsecvt(TTFontU *f)
|
||||||
|
{
|
||||||
|
int len;
|
||||||
|
int i;
|
||||||
|
int x;
|
||||||
|
u8int *p;
|
||||||
|
short *w;
|
||||||
|
|
||||||
|
len = ttfgototable(f, "cvt ");
|
||||||
|
if(len <= 0) return 0;
|
||||||
|
f->cvtu = mallocz(len, 1);
|
||||||
|
if(f->cvtu == 0) return -1;
|
||||||
|
Bread(f->bin, f->cvtu, len);
|
||||||
|
p = (u8int *) f->cvtu;
|
||||||
|
f->ncvtu = len / 2;
|
||||||
|
w = f->cvtu;
|
||||||
|
for(i = 0; i < f->ncvtu; i++){
|
||||||
|
x = (short)(p[0] << 8 | p[1]);
|
||||||
|
p += 2;
|
||||||
|
*w++ = x;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ttfparseos2(TTFontU *f)
|
||||||
|
{
|
||||||
|
int len;
|
||||||
|
u16int usWinAscent, usWinDescent;
|
||||||
|
|
||||||
|
len = ttfgototable(f, "OS/2 ");
|
||||||
|
if(len < 0)
|
||||||
|
return -1;
|
||||||
|
if(len < 78){
|
||||||
|
werrstr("OS/2 table too short");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ttfunpack(f, "68 6 ww", &usWinAscent, &usWinDescent);
|
||||||
|
f->ascent = usWinAscent;
|
||||||
|
f->descent = usWinDescent;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ttfcloseu(TTFontU *u)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(u == nil) return;
|
||||||
|
Bterm(u->bin);
|
||||||
|
for(i = 0; i < u->ncmap; i++)
|
||||||
|
free(u->cmap[i].tab);
|
||||||
|
free(u->cmap);
|
||||||
|
free(u->ginfo);
|
||||||
|
free(u->tab);
|
||||||
|
free(u->cvtu);
|
||||||
|
free(u);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ttfclose(TTFont *f)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(f == nil) return;
|
||||||
|
if(--f->u->ref <= 0)
|
||||||
|
ttfcloseu(f->u);
|
||||||
|
for(i = 0; i < f->u->maxFunctionDefs; i++)
|
||||||
|
free(f->func[i].pgm);
|
||||||
|
free(f->hintstack);
|
||||||
|
free(f->func);
|
||||||
|
free(f->storage);
|
||||||
|
free(f->twilight);
|
||||||
|
free(f->twiorg);
|
||||||
|
free(f->cvt);
|
||||||
|
free(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TTFont *
|
||||||
|
ttfscaleu(TTFontU *u, int ppem)
|
||||||
|
{
|
||||||
|
TTFont *f;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
f = mallocz(sizeof(TTFont), 1);
|
||||||
|
if(f == nil) return nil;
|
||||||
|
f->u = u;
|
||||||
|
u->ref++;
|
||||||
|
f->ppem = ppem;
|
||||||
|
f->ncvt = u->ncvtu;
|
||||||
|
f->cvt = malloc(sizeof(int) * u->ncvtu);
|
||||||
|
if(f->cvt == nil) goto error;
|
||||||
|
for(i = 0; i < u->ncvtu; i++)
|
||||||
|
f->cvt[i] = ttfrounddiv(u->cvtu[i] * ppem * 64, u->emsize);
|
||||||
|
f->hintstack = mallocz(sizeof(u32int) * u->maxStackElements, 1);
|
||||||
|
f->func = mallocz(sizeof(TTFunction) * u->maxFunctionDefs, 1);
|
||||||
|
f->storage = mallocz(sizeof(u32int) * u->maxStorage, 1);
|
||||||
|
f->twilight = mallocz(sizeof(TTPoint) * u->maxTwilightPoints, 1);
|
||||||
|
f->twiorg = mallocz(sizeof(TTPoint) * u->maxTwilightPoints, 1);
|
||||||
|
if(f->hintstack == nil || f->func == nil || f->storage == nil || f->twilight == nil || f->twiorg == nil) goto error;
|
||||||
|
f->ascentpx = (u->ascent * ppem + u->emsize - 1) / (u->emsize);
|
||||||
|
f->descentpx = (u->descent * ppem + u->emsize - 1) / (u->emsize);
|
||||||
|
if(ttfrunfpgm(f) < 0) goto error;
|
||||||
|
if(ttfruncvt(f) < 0) goto error;
|
||||||
|
return f;
|
||||||
|
|
||||||
|
error:
|
||||||
|
ttfclose(f);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
TTFont *
|
||||||
|
ttfopen(char *name, int ppem, int)
|
||||||
|
{
|
||||||
|
Biobuf *b;
|
||||||
|
TTFontU *u;
|
||||||
|
|
||||||
|
if(ppem < 0){
|
||||||
|
werrstr("invalid ppem argument");
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
b = Bopen(name, OREAD);
|
||||||
|
if(b == nil)
|
||||||
|
return nil;
|
||||||
|
u = mallocz(sizeof(TTFontU), 1);
|
||||||
|
if(u == nil)
|
||||||
|
return nil;
|
||||||
|
u->bin = b;
|
||||||
|
u->nkern = -1;
|
||||||
|
directory(u);
|
||||||
|
if(ttfgototable(u, "head") < 0) goto error;
|
||||||
|
ttfunpack(u, "16 w W 16 wwww 6 w", &u->flags, &u->emsize, &u->xmin, &u->ymin, &u->xmax, &u->ymax, &u->longloca);
|
||||||
|
if(ttfgototable(u, "maxp") < 0) goto error;
|
||||||
|
ttfunpack(u, "4 wwwwwwwwwwwwww",
|
||||||
|
&u->numGlyphs, &u->maxPoints, &u->maxCountours, &u->maxComponentPoints, &u->maxComponentCountours,
|
||||||
|
&u->maxZones, &u->maxTwilightPoints, &u->maxStorage, &u->maxFunctionDefs, &u->maxInstructionDefs,
|
||||||
|
&u->maxStackElements, &u->maxSizeOfInstructions, &u->maxComponentElements, &u->maxComponentDepth);
|
||||||
|
u->ginfo = mallocz(sizeof(TTGlyphInfo) * (u->numGlyphs + 1), 1);
|
||||||
|
if(u->ginfo == nil)
|
||||||
|
goto error;
|
||||||
|
if(ttfgototable(u, "hhea") < 0) goto error;
|
||||||
|
ttfunpack(u, "10 wwww 16 w", &u->advanceWidthMax, &u->minLeftSideBearing, &u->minRightSideBearing, &u->xMaxExtent, &u->numOfLongHorMetrics);
|
||||||
|
if(ttfparseloca(u) < 0) goto error;
|
||||||
|
if(ttfparsehmtx(u) < 0) goto error;
|
||||||
|
if(ttfparsecvt(u) < 0) goto error;
|
||||||
|
if(ttfparsecmap(u) < 0) goto error;
|
||||||
|
if(ttfparseos2(u) < 0) goto error;
|
||||||
|
return ttfscaleu(u, ppem);
|
||||||
|
|
||||||
|
error:
|
||||||
|
ttfcloseu(u);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
TTFont *
|
||||||
|
ttfscale(TTFont *f, int ppem, int)
|
||||||
|
{
|
||||||
|
return ttfscaleu(f->u, ppem);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ttfrounddiv(int a, int b)
|
||||||
|
{
|
||||||
|
if(b < 0){ a = -a; b = -b; }
|
||||||
|
if(a > 0)
|
||||||
|
return (a + b/2) / b;
|
||||||
|
else
|
||||||
|
return (a - b/2) / b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ttfvrounddiv(vlong a, int b)
|
||||||
|
{
|
||||||
|
if(b < 0){ a = -a; b = -b; }
|
||||||
|
if(a > 0)
|
||||||
|
return (a + b/2) / b;
|
||||||
|
else
|
||||||
|
return (a - b/2) / b;
|
||||||
|
}
|
1668
sys/src/libttf/hint.c
Normal file
1668
sys/src/libttf/hint.c
Normal file
File diff suppressed because it is too large
Load diff
23
sys/src/libttf/mkfile
Normal file
23
sys/src/libttf/mkfile
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
</$objtype/mkfile
|
||||||
|
|
||||||
|
LIB=/$objtype/lib/libttf.a
|
||||||
|
|
||||||
|
OFILES=\
|
||||||
|
head.$O \
|
||||||
|
cmap.$O \
|
||||||
|
hint.$O \
|
||||||
|
scan.$O \
|
||||||
|
glyf.$O \
|
||||||
|
render.$O \
|
||||||
|
bit.$O \
|
||||||
|
|
||||||
|
HFILES=\
|
||||||
|
/sys/include/ttf.h\
|
||||||
|
impl.h\
|
||||||
|
|
||||||
|
UPDATE=\
|
||||||
|
mkfile\
|
||||||
|
$HFILES\
|
||||||
|
${OFILES:%.$O=%.c}\
|
||||||
|
|
||||||
|
</sys/src/cmd/mksyslib
|
274
sys/src/libttf/render.c
Normal file
274
sys/src/libttf/render.c
Normal file
|
@ -0,0 +1,274 @@
|
||||||
|
#include <u.h>
|
||||||
|
#include <libc.h>
|
||||||
|
#include <bio.h>
|
||||||
|
#include <ttf.h>
|
||||||
|
#include "impl.h"
|
||||||
|
|
||||||
|
static int
|
||||||
|
ttfparsekern(TTFontU *f)
|
||||||
|
{
|
||||||
|
u16int ver, len, cov, ntab;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(ttfgototable(f, "kern") < 0)
|
||||||
|
return -1;
|
||||||
|
ttfunpack(f, "ww", &ver, &ntab);
|
||||||
|
if(ver != 0)
|
||||||
|
return -1;
|
||||||
|
if(ntab == 0)
|
||||||
|
return -1;
|
||||||
|
for(i = 0; i < ntab; i++){
|
||||||
|
ttfunpack(f, "www", &ver, &len, &cov);
|
||||||
|
if((cov & 1) != 0) break;
|
||||||
|
Bseek(f->bin, len - 6, 1);
|
||||||
|
}
|
||||||
|
ttfunpack(f, "w6", &len);
|
||||||
|
f->nkern = len;
|
||||||
|
f->kern = mallocz(sizeof(TTKern) * len, 1);
|
||||||
|
for(i = 0; i < len; i++)
|
||||||
|
ttfunpack(f, "lS", &f->kern[i].idx, &f->kern[i].val);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ttfkern(TTFont *f, int l, int r)
|
||||||
|
{
|
||||||
|
u32int idx;
|
||||||
|
int a, b, c;
|
||||||
|
TTFontU *u;
|
||||||
|
|
||||||
|
u = f->u;
|
||||||
|
if(u->nkern == 0)
|
||||||
|
return 0;
|
||||||
|
idx = l << 16 | r;
|
||||||
|
a = 0;
|
||||||
|
b = u->nkern - 1;
|
||||||
|
if(u->kern[a].idx > idx || u->kern[b].idx < idx)
|
||||||
|
return 0;
|
||||||
|
while(a <= b){
|
||||||
|
c = (a + b) / 2;
|
||||||
|
if(u->kern[c].idx < idx){
|
||||||
|
a = c + 1;
|
||||||
|
}else if(u->kern[c].idx > idx){
|
||||||
|
b = c - 1;
|
||||||
|
}else
|
||||||
|
return ttfrounddiv(u->kern[c].val * f->ppem, u->emsize);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
TTBitmap *b;
|
||||||
|
TTFont *font;
|
||||||
|
TTGlyph **glyph;
|
||||||
|
int *gwidth;
|
||||||
|
char **cpos;
|
||||||
|
char *pp;
|
||||||
|
int nglyph, aglyph;
|
||||||
|
int linew;
|
||||||
|
int adj;
|
||||||
|
int nspc;
|
||||||
|
int oy, lh;
|
||||||
|
int spcidx;
|
||||||
|
int flags;
|
||||||
|
int spcw;
|
||||||
|
int spcminus;
|
||||||
|
} Render;
|
||||||
|
|
||||||
|
static int
|
||||||
|
addglyph(Render *r, char *p, TTGlyph *g)
|
||||||
|
{
|
||||||
|
void *v;
|
||||||
|
int k;
|
||||||
|
|
||||||
|
if(r->nglyph >= r->aglyph){
|
||||||
|
r->aglyph += 32;
|
||||||
|
v = realloc(r->glyph, sizeof(TTGlyph *) * r->aglyph);
|
||||||
|
if(v == nil) return -1;
|
||||||
|
r->glyph = v;
|
||||||
|
v = realloc(r->gwidth, sizeof(int) * r->aglyph);
|
||||||
|
if(v == nil) return -1;
|
||||||
|
r->gwidth = v;
|
||||||
|
v = realloc(r->cpos, sizeof(char *) * r->aglyph);
|
||||||
|
if(v == nil) return -1;
|
||||||
|
r->cpos = v;
|
||||||
|
}
|
||||||
|
r->glyph[r->nglyph] = g;
|
||||||
|
r->cpos[r->nglyph] = p;
|
||||||
|
r->gwidth[r->nglyph] = g->advanceWidthpx;
|
||||||
|
if(r->nglyph > 0){
|
||||||
|
k = ttfkern(r->font, r->glyph[r->nglyph-1]->idx, g->idx);
|
||||||
|
r->gwidth[r->nglyph-1] += k;
|
||||||
|
r->linew += k;
|
||||||
|
}
|
||||||
|
r->nglyph++;
|
||||||
|
r->linew += r->gwidth[r->nglyph-1];
|
||||||
|
if(g->idx == r->spcidx)
|
||||||
|
r->nspc++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
flushglyphs(Render *r, int justify)
|
||||||
|
{
|
||||||
|
int i, n, k, x, y;
|
||||||
|
int llen;
|
||||||
|
int adj, spcw, nspc, c;
|
||||||
|
TTFont *f;
|
||||||
|
|
||||||
|
f = r->font;
|
||||||
|
if((r->flags & TTFMODE) == TTFLALIGN && !justify)
|
||||||
|
while(r->nglyph > 0 && r->glyph[r->nglyph - 1]->idx == r->spcidx){
|
||||||
|
r->linew -= r->gwidth[--r->nglyph];
|
||||||
|
r->nspc--;
|
||||||
|
}
|
||||||
|
llen = r->linew;
|
||||||
|
k = n = r->nglyph;
|
||||||
|
nspc = r->nspc;
|
||||||
|
adj = (nspc * r->spcminus + 63) / 64;
|
||||||
|
if(r->linew - adj > r->b->width){
|
||||||
|
n = r->nglyph;
|
||||||
|
while(n > 0 && r->glyph[n - 1]->idx != r->spcidx)
|
||||||
|
llen -= r->gwidth[--n];
|
||||||
|
k = n;
|
||||||
|
while(n > 0 && r->glyph[n - 1]->idx == r->spcidx){
|
||||||
|
llen -= r->gwidth[--n];
|
||||||
|
nspc--;
|
||||||
|
}
|
||||||
|
if(n == 0){
|
||||||
|
while(n < r->nglyph && llen + r->gwidth[n] < r->b->width)
|
||||||
|
llen += r->gwidth[n++];
|
||||||
|
k = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(justify){
|
||||||
|
if(nspc == 0)
|
||||||
|
spcw = 0;
|
||||||
|
else
|
||||||
|
spcw = (r->b->width - llen + nspc * r->spcw) * 64 / nspc;
|
||||||
|
}else
|
||||||
|
spcw = r->spcw * 64;
|
||||||
|
switch(r->flags & TTFMODE | justify * TTFJUSTIFY){
|
||||||
|
case TTFRALIGN:
|
||||||
|
x = r->b->width - llen;
|
||||||
|
break;
|
||||||
|
case TTFCENTER:
|
||||||
|
x = (r->b->width - llen)/2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
x = 0;
|
||||||
|
}
|
||||||
|
y = r->oy + f->ascentpx;
|
||||||
|
c = 0;
|
||||||
|
for(i = 0; i < k; i++){
|
||||||
|
if(r->glyph[i]->idx == r->spcidx){
|
||||||
|
c += spcw;
|
||||||
|
x += c >> 6;
|
||||||
|
c &= 63;
|
||||||
|
r->nspc--;
|
||||||
|
}else{
|
||||||
|
ttfblit(r->b, x + r->glyph[i]->xminpx, y - r->glyph[i]->ymaxpx, r->glyph[i], 0, 0, r->glyph[i]->width, r->glyph[i]->height);
|
||||||
|
x += r->gwidth[i];
|
||||||
|
}
|
||||||
|
r->linew -= r->gwidth[i];
|
||||||
|
ttfputglyph(r->glyph[i]);
|
||||||
|
}
|
||||||
|
if(n > 0)
|
||||||
|
r->pp = r->cpos[n-1];
|
||||||
|
r->oy += r->lh;
|
||||||
|
memmove(r->glyph, r->glyph + k, (r->nglyph - k) * sizeof(TTGlyph *));
|
||||||
|
memmove(r->cpos, r->cpos + k, (r->nglyph - k) * sizeof(char *));
|
||||||
|
memmove(r->gwidth, r->gwidth + k, (r->nglyph - k) * sizeof(int));
|
||||||
|
r->nglyph -= k;
|
||||||
|
}
|
||||||
|
|
||||||
|
TTBitmap *
|
||||||
|
_ttfrender(TTFont *f, int (*getrune)(Rune *, char *), char *p, char *end, int w, int h, int flags, char **rp)
|
||||||
|
{
|
||||||
|
Render r;
|
||||||
|
Rune ch;
|
||||||
|
int i, adj;
|
||||||
|
TTGlyph *g;
|
||||||
|
|
||||||
|
if(rp != nil) *rp = p;
|
||||||
|
if(f->u->nkern < 0 && ttfparsekern(f->u) < 0)
|
||||||
|
f->u->nkern = 0;
|
||||||
|
memset(&r, 0, sizeof(Render));
|
||||||
|
r.flags = flags;
|
||||||
|
r.font = f;
|
||||||
|
r.b = ttfnewbitmap(w, h);
|
||||||
|
if(r.b == nil) goto error;
|
||||||
|
r.oy = 0;
|
||||||
|
r.lh = f->ascentpx + f->descentpx;
|
||||||
|
r.pp = p;
|
||||||
|
|
||||||
|
g = ttfgetglyph(f, ttffindchar(f, ' '), 1);
|
||||||
|
r.spcidx = g->idx;
|
||||||
|
r.spcw = g->advanceWidthpx;
|
||||||
|
if((flags & TTFJUSTIFY) != 0)
|
||||||
|
r.spcminus = r.spcw * 21;
|
||||||
|
else
|
||||||
|
r.spcminus = 0;
|
||||||
|
|
||||||
|
while(p < end && r.oy + r.lh < h){
|
||||||
|
p += getrune(&ch, p);
|
||||||
|
if(ch == '\n'){
|
||||||
|
flushglyphs(&r, 0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
g = ttfgetglyph(f, ttffindchar(f, ch), 1);
|
||||||
|
if(g == nil){
|
||||||
|
g = ttfgetglyph(f, 0, 1);
|
||||||
|
if(g == nil)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(addglyph(&r, p, g) < 0)
|
||||||
|
goto error;
|
||||||
|
adj = (r.nspc * r.spcminus + 63) / 64;
|
||||||
|
if(r.linew - adj > r.b->width){
|
||||||
|
flushglyphs(&r, (flags & TTFJUSTIFY) != 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(r.oy + r.lh < h)
|
||||||
|
flushglyphs(&r, 0);
|
||||||
|
for(i = 0; i < r.nglyph; i++)
|
||||||
|
ttfputglyph(r.glyph[i]);
|
||||||
|
free(r.glyph);
|
||||||
|
free(r.gwidth);
|
||||||
|
free(r.cpos);
|
||||||
|
if(rp != nil)
|
||||||
|
*rp = r.pp;
|
||||||
|
return r.b;
|
||||||
|
error:
|
||||||
|
ttffreebitmap(r.b);
|
||||||
|
free(r.glyph);
|
||||||
|
free(r.gwidth);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
TTBitmap *
|
||||||
|
ttfrender(TTFont *f, char *str, char *end, int w, int h, int flags, char **rstr)
|
||||||
|
{
|
||||||
|
if(str == nil)
|
||||||
|
end = nil;
|
||||||
|
else if(end == nil)
|
||||||
|
end = str + strlen(str);
|
||||||
|
return _ttfrender(f, chartorune, str, end, w, h, flags, rstr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
incrune(Rune *r, char *s)
|
||||||
|
{
|
||||||
|
*r = *(Rune*)s;
|
||||||
|
return sizeof(Rune);
|
||||||
|
}
|
||||||
|
|
||||||
|
TTBitmap *
|
||||||
|
ttfrunerender(TTFont *f, Rune *str, Rune *end, int w, int h, int flags, Rune **rstr)
|
||||||
|
{
|
||||||
|
if(str == nil)
|
||||||
|
end = nil;
|
||||||
|
else if(end == nil)
|
||||||
|
end = str + runestrlen(str);
|
||||||
|
return _ttfrender(f, incrune, (char *) str, (char *) end, w, h, flags, (char **) rstr);
|
||||||
|
}
|
478
sys/src/libttf/scan.c
Normal file
478
sys/src/libttf/scan.c
Normal file
|
@ -0,0 +1,478 @@
|
||||||
|
#include <u.h>
|
||||||
|
#include <libc.h>
|
||||||
|
#include <bio.h>
|
||||||
|
#include <ttf.h>
|
||||||
|
#include "impl.h"
|
||||||
|
|
||||||
|
typedef struct Scan Scan;
|
||||||
|
typedef struct TTLine TTLine;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
LINEBLOCK = 32,
|
||||||
|
PTBLOCK = 64,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TTLine {
|
||||||
|
int x0, y0;
|
||||||
|
int x1, y1;
|
||||||
|
int link;
|
||||||
|
u8int dir;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Scan {
|
||||||
|
enum {
|
||||||
|
DROPOUTS = 1,
|
||||||
|
STUBDET = 2,
|
||||||
|
SMART = 4,
|
||||||
|
} flags;
|
||||||
|
|
||||||
|
TTGlyph *g;
|
||||||
|
|
||||||
|
TTLine *lines;
|
||||||
|
int nlines;
|
||||||
|
|
||||||
|
int *hpts, *vpts;
|
||||||
|
int nhpts, nvpts;
|
||||||
|
int *hscanl, *vscanl;
|
||||||
|
|
||||||
|
u8int *bit;
|
||||||
|
int width, height;
|
||||||
|
int stride;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
dobezier(Scan *s, TTPoint p, TTPoint q, TTPoint r)
|
||||||
|
{
|
||||||
|
vlong m, n;
|
||||||
|
TTLine *l;
|
||||||
|
|
||||||
|
m = (vlong)(q.x - p.x) * (r.y - p.y) - (vlong)(q.y - p.y) * (r.x - p.x);
|
||||||
|
n = (vlong)(r.x - p.x) * (r.x - p.x) + (vlong)(r.y - p.y) * (r.y - p.y);
|
||||||
|
if(m * m > 4 * n){
|
||||||
|
dobezier(s, p, (TTPoint){(p.x+q.x+1)/2, (p.y+q.y+1)/2, 0}, (TTPoint){(p.x+2*q.x+r.x+2)/4, (p.y+2*q.y+r.y+2)/4, 0});
|
||||||
|
dobezier(s, (TTPoint){(p.x+2*q.x+r.x+2)/4, (p.y+2*q.y+r.y+2)/4, 0}, (TTPoint){(r.x+q.x+1)/2, (r.y+q.y+1)/2, 0}, r);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if((s->nlines & LINEBLOCK - 1) == 0)
|
||||||
|
s->lines = realloc(s->lines, sizeof(TTLine) * (s->nlines + LINEBLOCK));
|
||||||
|
l = &s->lines[s->nlines++];
|
||||||
|
if(p.y < r.y){
|
||||||
|
l->x0 = p.x;
|
||||||
|
l->y0 = p.y;
|
||||||
|
l->x1 = r.x;
|
||||||
|
l->y1 = r.y;
|
||||||
|
l->dir = 0;
|
||||||
|
}else{
|
||||||
|
l->x0 = r.x;
|
||||||
|
l->y0 = r.y;
|
||||||
|
l->x1 = p.x;
|
||||||
|
l->y1 = p.y;
|
||||||
|
l->dir = 1;
|
||||||
|
}
|
||||||
|
l->link = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
hlinecmp(void *va, void *vb)
|
||||||
|
{
|
||||||
|
TTLine *a, *b;
|
||||||
|
|
||||||
|
a = va;
|
||||||
|
b = vb;
|
||||||
|
if(a->y0 < b->y0) return -1;
|
||||||
|
if(a->y0 > b->y0) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vlinecmp(void *va, void *vb)
|
||||||
|
{
|
||||||
|
TTLine *a, *b;
|
||||||
|
|
||||||
|
a = va;
|
||||||
|
b = vb;
|
||||||
|
if(a->x0 < b->x0) return -1;
|
||||||
|
if(a->x0 > b->x0) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
intcmp(void *va, void *vb)
|
||||||
|
{
|
||||||
|
int a, b;
|
||||||
|
|
||||||
|
a = *(int*)va;
|
||||||
|
b = *(int*)vb;
|
||||||
|
return (a>b) - (a<b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
hprep(Scan *s)
|
||||||
|
{
|
||||||
|
int i, j, x, y;
|
||||||
|
TTLine *l;
|
||||||
|
int watch, act, *p;
|
||||||
|
|
||||||
|
qsort(s->lines, s->nlines, sizeof(TTLine), hlinecmp);
|
||||||
|
s->hscanl = calloc(sizeof(int), (s->height + 1));
|
||||||
|
act = -1;
|
||||||
|
watch = 0;
|
||||||
|
p = &act;
|
||||||
|
for(i = 0; i < s->height; i++){
|
||||||
|
y = 64 * i + 32;
|
||||||
|
for(; watch < s->nlines && s->lines[watch].y0 <= y; watch++){
|
||||||
|
if(s->lines[watch].y1 <= y || s->lines[watch].y0 == s->lines[watch].y1)
|
||||||
|
continue;
|
||||||
|
s->lines[watch].link = -1;
|
||||||
|
*p = watch;
|
||||||
|
p = &s->lines[watch].link;
|
||||||
|
}
|
||||||
|
s->hscanl[i] = s->nhpts;
|
||||||
|
p = &act;
|
||||||
|
while(j = *p, j >= 0){
|
||||||
|
l = &s->lines[j];
|
||||||
|
if(l->y1 <= y){
|
||||||
|
j = l->link;
|
||||||
|
l->link = -1;
|
||||||
|
*p = j;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
x = l->x0 + ttfvrounddiv((vlong)(y - l->y0)*(l->x1 - l->x0), l->y1 - l->y0);
|
||||||
|
if((s->nhpts & PTBLOCK - 1) == 0)
|
||||||
|
s->hpts = realloc(s->hpts, (s->nhpts + PTBLOCK) * sizeof(int));
|
||||||
|
s->hpts[s->nhpts++] = x << 1 | l->dir;
|
||||||
|
p = &l->link;
|
||||||
|
}
|
||||||
|
qsort(s->hpts + s->hscanl[i], s->nhpts - s->hscanl[i], sizeof(int), intcmp);
|
||||||
|
}
|
||||||
|
s->hscanl[i] = s->nhpts;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
iswhite(Scan *s, int x, int y)
|
||||||
|
{
|
||||||
|
return (s->bit[(s->height - 1 - y) * s->stride + (x>>3)] >> 7-(x&7) & 1)==0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pixel(Scan *s, int x, int y)
|
||||||
|
{
|
||||||
|
assert(x >= 0 && x < s->width && y >= 0 && y < s->height);
|
||||||
|
s->bit[(s->height - 1 - y) * s->stride + (x>>3)] |= (1<<7-(x&7));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
intersectsh(Scan *s, int x, int y)
|
||||||
|
{
|
||||||
|
int a, b, c, vc, v;
|
||||||
|
|
||||||
|
a = s->hscanl[y];
|
||||||
|
b = s->hscanl[y+1]-1;
|
||||||
|
v = x * 64 + 32;
|
||||||
|
if(a > b || s->hpts[a]>>1 > v + 64 || s->hpts[b]>>1 < v) return 0;
|
||||||
|
while(a <= b){
|
||||||
|
c = (a + b) / 2;
|
||||||
|
vc = s->hpts[c]>>1;
|
||||||
|
if(vc < v)
|
||||||
|
a = c + 1;
|
||||||
|
else if(vc > v + 64)
|
||||||
|
b = c - 1;
|
||||||
|
else
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
intersectsv(Scan *s, int x, int y)
|
||||||
|
{
|
||||||
|
int a, b, c, vc, v;
|
||||||
|
|
||||||
|
a = s->vscanl[x];
|
||||||
|
b = s->vscanl[x+1]-1;
|
||||||
|
v = y * 64 + 32;
|
||||||
|
if(a > b || s->vpts[a]>>1 > v + 64 || s->vpts[b]>>1 < v) return 0;
|
||||||
|
while(a <= b){
|
||||||
|
c = (a + b) / 2;
|
||||||
|
vc = s->vpts[c]>>1;
|
||||||
|
if(vc < v)
|
||||||
|
a = c + 1;
|
||||||
|
else if(vc > v + 64)
|
||||||
|
b = c - 1;
|
||||||
|
else
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
hscan(Scan *s)
|
||||||
|
{
|
||||||
|
int i, j, k, e;
|
||||||
|
int wind, match, seen, x;
|
||||||
|
|
||||||
|
for(i = 0; i < s->height; i++){
|
||||||
|
e = s->hscanl[i+1];
|
||||||
|
k = s->hscanl[i];
|
||||||
|
if(k == e) continue;
|
||||||
|
wind = 0;
|
||||||
|
for(j = 0; j < s->width; j++){
|
||||||
|
x = 64 * j + 32;
|
||||||
|
match = 0;
|
||||||
|
seen = 0;
|
||||||
|
while(k < e && (s->hpts[k] >> 1) <= x){
|
||||||
|
wind += (s->hpts[k] & 1) * 2 - 1;
|
||||||
|
seen |= 1<<(s->hpts[k] & 1);
|
||||||
|
if((s->hpts[k] >> 1) == x)
|
||||||
|
match++;
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
if(match || wind)
|
||||||
|
pixel(s, j, i);
|
||||||
|
else if((s->flags & DROPOUTS) != 0 && seen == 3 && j > 0 && iswhite(s, j-1, i)){
|
||||||
|
if((s->flags & STUBDET) == 0){
|
||||||
|
pixel(s, j-1, i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(i <= 0 || i > s->height - 1 || j <= 0 || j > s->width - 1)
|
||||||
|
continue;
|
||||||
|
if(!intersectsv(s, j-1, i-1) && !intersectsh(s, j-1, i-1) && !intersectsv(s, j, i-1) || !intersectsv(s, j-1, i) && !intersectsh(s, j-1, i+1) && !intersectsv(s, j, i))
|
||||||
|
continue;
|
||||||
|
pixel(s, j-1, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vprep(Scan *s)
|
||||||
|
{
|
||||||
|
int i, j, x, y;
|
||||||
|
TTLine *l;
|
||||||
|
int watch, act, *p;
|
||||||
|
|
||||||
|
for(i = 0; i < s->nlines; i++){
|
||||||
|
l = &s->lines[i];
|
||||||
|
if(l->x0 > l->x1){
|
||||||
|
x = l->x0, l->x0 = l->x1, l->x1 = x;
|
||||||
|
x = l->y0, l->y0 = l->y1, l->y1 = x;
|
||||||
|
l->dir ^= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
qsort(s->lines, s->nlines, sizeof(TTLine), vlinecmp);
|
||||||
|
s->vscanl = calloc(sizeof(int), (s->width + 1));
|
||||||
|
act = -1;
|
||||||
|
watch = 0;
|
||||||
|
p = &act;
|
||||||
|
for(i = 0; i < s->width; i++){
|
||||||
|
x = 64 * i + 32;
|
||||||
|
for(; watch < s->nlines && s->lines[watch].x0 <= x; watch++){
|
||||||
|
if(s->lines[watch].x1 <= x || s->lines[watch].x0 == s->lines[watch].x1)
|
||||||
|
continue;
|
||||||
|
s->lines[watch].link = -1;
|
||||||
|
*p = watch;
|
||||||
|
p = &s->lines[watch].link;
|
||||||
|
}
|
||||||
|
s->vscanl[i] = s->nvpts;
|
||||||
|
p = &act;
|
||||||
|
while(j = *p, j >= 0){
|
||||||
|
l = &s->lines[j];
|
||||||
|
if(l->x1 <= x){
|
||||||
|
j = l->link;
|
||||||
|
l->link = -1;
|
||||||
|
*p = j;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
y = l->y0 + ttfvrounddiv((vlong)(x - l->x0) * (l->y1 - l->y0), l->x1 - l->x0);
|
||||||
|
if((s->nvpts & PTBLOCK - 1) == 0)
|
||||||
|
s->vpts = realloc(s->vpts, (s->nvpts + PTBLOCK) * sizeof(int));
|
||||||
|
s->vpts[s->nvpts++] = y << 1 | l->dir;
|
||||||
|
p = &l->link;
|
||||||
|
}
|
||||||
|
qsort(s->vpts + s->vscanl[i], s->nvpts - s->vscanl[i], sizeof(int), intcmp);
|
||||||
|
}
|
||||||
|
s->vscanl[i] = s->nvpts;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vscan(Scan *s)
|
||||||
|
{
|
||||||
|
int i, j, k, e;
|
||||||
|
int seen, y;
|
||||||
|
|
||||||
|
for(i = 0; i < s->width; i++){
|
||||||
|
e = s->vscanl[i+1];
|
||||||
|
k = s->vscanl[i];
|
||||||
|
if(k == e) continue;
|
||||||
|
for(j = 0; j < s->height; j++){
|
||||||
|
y = 64 * j + 32;
|
||||||
|
seen = 0;
|
||||||
|
while(k < e && (s->vpts[k] >> 1) <= y){
|
||||||
|
seen |= 1<<(s->vpts[k] & 1);
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
if(seen == 3 && j > 0 && iswhite(s, i, j-1) && iswhite(s, i, j)){
|
||||||
|
if((s->flags & STUBDET) == 0){
|
||||||
|
pixel(s, j-1, i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(i <= 0 || i > s->width - 1 || j <= 0 || j > s->height - 1)
|
||||||
|
continue;
|
||||||
|
if(!intersectsv(s, i-1, j-1) & !intersectsh(s, i-1, j-1) & !intersectsh(s, i-1, j) | !intersectsv(s, i+1, j-1) & !intersectsh(s, i, j-1) & !intersectsh(s, i, j))
|
||||||
|
continue;
|
||||||
|
pixel(s, i, j-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ttfscan(TTGlyph *g)
|
||||||
|
{
|
||||||
|
int i, j, c;
|
||||||
|
TTPoint p, q, r;
|
||||||
|
Scan s;
|
||||||
|
|
||||||
|
memset(&s, 0, sizeof(s));
|
||||||
|
s.g = g;
|
||||||
|
s.flags = 0;
|
||||||
|
c = g->font->scanctrl;
|
||||||
|
if((c & 1<<8) != 0 && g->font->ppem <= (c & 0xff))
|
||||||
|
s.flags |= DROPOUTS;
|
||||||
|
if((c & 1<<11) != 0 && g->font->ppem > (c & 0xff))
|
||||||
|
s.flags &= ~DROPOUTS;
|
||||||
|
if((c & 3<<12) != 0)
|
||||||
|
s.flags &= ~DROPOUTS;
|
||||||
|
if((s.flags & DROPOUTS) != 0)
|
||||||
|
switch(g->font->scantype){
|
||||||
|
case 0: break;
|
||||||
|
case 1: s.flags |= STUBDET; break;
|
||||||
|
case 2: case 3: case 6: case 7: s.flags &= ~DROPOUTS; break;
|
||||||
|
case 4: s.flags |= SMART; break;
|
||||||
|
case 5: s.flags |= SMART | STUBDET; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// s.width = (g->pt[g->npt - 1].x + 63) / 64;
|
||||||
|
// s.height = g->font->ascentpx + g->font->descentpx;
|
||||||
|
s.width = -g->xminpx + g->xmaxpx;
|
||||||
|
s.height = -g->yminpx + g->ymaxpx;
|
||||||
|
s.stride = s.width + 7 >> 3;
|
||||||
|
s.bit = mallocz(s.height * s.stride, 1);
|
||||||
|
assert(s.bit != nil);
|
||||||
|
for(i = 0; i < g->npt; i++){
|
||||||
|
g->pt[i].x -= g->xminpx * 64;
|
||||||
|
g->pt[i].y -= g->yminpx * 64;
|
||||||
|
// g->pt[i].y += g->font->descentpx * 64;
|
||||||
|
}
|
||||||
|
for(i = 0; i < g->ncon; i++){
|
||||||
|
if(g->confst[i] + 1 >= g->confst[i+1]) continue;
|
||||||
|
p = g->pt[g->confst[i]];
|
||||||
|
assert((p.flags & 1) != 0);
|
||||||
|
for(j = g->confst[i]; j++ < g->confst[i+1]; ){
|
||||||
|
if(j < g->confst[i+1] && (g->pt[j].flags & 1) == 0)
|
||||||
|
q = g->pt[j++];
|
||||||
|
else
|
||||||
|
q = p;
|
||||||
|
if(j >= g->confst[i+1])
|
||||||
|
r = g->pt[g->confst[i]];
|
||||||
|
else{
|
||||||
|
r = g->pt[j];
|
||||||
|
if((g->pt[j].flags & 1) == 0){
|
||||||
|
r.x = (r.x + q.x) / 2;
|
||||||
|
r.y = (r.y + q.y) / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dobezier(&s, p, q, r);
|
||||||
|
p = r;
|
||||||
|
if(j < g->confst[i+1] && (g->pt[j].flags & 1) == 0)
|
||||||
|
j--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hprep(&s);
|
||||||
|
if((s.flags & DROPOUTS) != 0)
|
||||||
|
vprep(&s);
|
||||||
|
hscan(&s);
|
||||||
|
if((s.flags & DROPOUTS) != 0)
|
||||||
|
vscan(&s);
|
||||||
|
free(s.hpts);
|
||||||
|
free(s.vpts);
|
||||||
|
free(s.hscanl);
|
||||||
|
free(s.vscanl);
|
||||||
|
free(s.lines);
|
||||||
|
g->bit = s.bit;
|
||||||
|
g->width = s.width;
|
||||||
|
g->height = s.height;
|
||||||
|
g->stride = s.stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ttfgetcontour(TTGlyph *g, int i, float **fp, int *np)
|
||||||
|
{
|
||||||
|
float offx, offy, scale;
|
||||||
|
float *nf;
|
||||||
|
int n, j;
|
||||||
|
TTPoint p, q, r;
|
||||||
|
|
||||||
|
if((uint)i >= g->ncon)
|
||||||
|
return 0;
|
||||||
|
if(g->confst[i]+1 >= g->confst[i+1]){
|
||||||
|
if(np != nil)
|
||||||
|
*np = 0;
|
||||||
|
if(fp != nil)
|
||||||
|
*fp = malloc(0);
|
||||||
|
return g->ncon - i;
|
||||||
|
}
|
||||||
|
if(g->bit != nil){
|
||||||
|
scale = 1.0f / 64;
|
||||||
|
offx = g->xminpx;
|
||||||
|
offy = g->yminpx;
|
||||||
|
}else{
|
||||||
|
scale = 1.0f * g->font->ppem / g->font->u->emsize;
|
||||||
|
offx = 0;
|
||||||
|
offy = 0;
|
||||||
|
}
|
||||||
|
p = g->pt[g->confst[i]];
|
||||||
|
n = 1;
|
||||||
|
if(fp != nil){
|
||||||
|
*fp = malloc(2 * sizeof(float));
|
||||||
|
if(*fp == nil) return -1;
|
||||||
|
(*fp)[0] = p.x * scale;
|
||||||
|
(*fp)[1] = p.y * scale + offy;
|
||||||
|
}
|
||||||
|
assert((p.flags & 1) != 0);
|
||||||
|
for(j = g->confst[i]; j++ < g->confst[i+1]; ){
|
||||||
|
if(j < g->confst[i+1] && (g->pt[j].flags & 1) == 0)
|
||||||
|
q = g->pt[j++];
|
||||||
|
else
|
||||||
|
q = p;
|
||||||
|
if(j >= g->confst[i+1])
|
||||||
|
r = g->pt[g->confst[i]];
|
||||||
|
else{
|
||||||
|
r = g->pt[j];
|
||||||
|
if((g->pt[j].flags & 1) == 0){
|
||||||
|
r.x = (r.x + q.x) / 2;
|
||||||
|
r.y = (r.y + q.y) / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(fp != nil){
|
||||||
|
nf = realloc(*fp, sizeof(float) * 2 * (n + 2));
|
||||||
|
if(nf == nil){
|
||||||
|
free(*fp);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
*fp = nf;
|
||||||
|
nf[2*n] = q.x * scale;
|
||||||
|
nf[2*n+1] = q.y * scale + offy;
|
||||||
|
nf[2*n+2] = r.x * scale;
|
||||||
|
nf[2*n+3] = r.y * scale + offy;
|
||||||
|
}
|
||||||
|
p = r;
|
||||||
|
n += 2;
|
||||||
|
if(j < g->confst[i+1] && (g->pt[j].flags & 1) == 0)
|
||||||
|
j--;
|
||||||
|
}
|
||||||
|
if(np != nil)
|
||||||
|
*np = n;
|
||||||
|
return g->ncon - i;
|
||||||
|
}
|
Loading…
Reference in a new issue