Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
小雨青年
freetype
提交
856a70c3
F
freetype
项目概览
小雨青年
/
freetype
通知
14
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
F
freetype
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
提交
856a70c3
编写于
8月 17, 2015
作者:
N
Nigel Tao
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Use the fixed.Int26_6 and fixed.Point26_6 types.
上级
d91c1d68
变更
7
隐藏空白更改
内联
并排
Showing
7 changed file
with
298 addition
and
306 deletion
+298
-306
example/gamma/main.go
example/gamma/main.go
+5
-4
example/raster/main.go
example/raster/main.go
+6
-5
example/round/main.go
example/round/main.go
+26
-25
freetype.go
freetype.go
+48
-46
raster/geom.go
raster/geom.go
+54
-95
raster/raster.go
raster/raster.go
+101
-86
raster/stroke.go
raster/stroke.go
+58
-45
未找到文件。
example/gamma/main.go
浏览文件 @
856a70c3
...
...
@@ -20,12 +20,13 @@ import (
"os"
"github.com/golang/freetype/raster"
"golang.org/x/image/math/fixed"
)
func
p
(
x
,
y
int
)
raster
.
Point
{
return
raster
.
Point
{
X
:
raster
.
Fix32
(
x
*
256
),
Y
:
raster
.
Fix32
(
y
*
256
),
func
p
(
x
,
y
int
)
fixed
.
Point26_6
{
return
fixed
.
Point26_6
{
X
:
fixed
.
Int26_6
(
x
*
64
),
Y
:
fixed
.
Int26_6
(
y
*
64
),
}
}
...
...
example/raster/main.go
浏览文件 @
856a70c3
...
...
@@ -21,6 +21,7 @@ import (
"os"
"github.com/golang/freetype/raster"
"golang.org/x/image/math/fixed"
)
type
node
struct
{
...
...
@@ -87,11 +88,11 @@ var inside = []node{
node
{
686
,
1274
,
-
1
},
}
func
p
(
n
node
)
raster
.
Point
{
func
p
(
n
node
)
fixed
.
Point26_6
{
x
,
y
:=
20
+
n
.
x
/
4
,
380
-
n
.
y
/
4
return
raster
.
Point
{
X
:
raster
.
Fix32
(
x
*
25
6
),
Y
:
raster
.
Fix32
(
y
*
25
6
),
return
fixed
.
Point26_6
{
X
:
fixed
.
Int26_6
(
x
<<
6
),
Y
:
fixed
.
Int26_6
(
y
<<
6
),
}
}
...
...
@@ -121,7 +122,7 @@ func contour(r *raster.Rasterizer, ns []node) {
func
showNodes
(
m
*
image
.
RGBA
,
ns
[]
node
)
{
for
_
,
n
:=
range
ns
{
p
:=
p
(
n
)
x
,
y
:=
int
(
p
.
X
)
/
256
,
int
(
p
.
Y
)
/
256
x
,
y
:=
int
(
p
.
X
)
/
64
,
int
(
p
.
Y
)
/
64
if
!
(
image
.
Point
{
x
,
y
})
.
In
(
m
.
Bounds
())
{
continue
}
...
...
example/round/main.go
浏览文件 @
856a70c3
...
...
@@ -27,22 +27,23 @@ import (
"os"
"github.com/golang/freetype/raster"
"golang.org/x/image/math/fixed"
)
// pDot returns the dot product p·q.
func
pDot
(
p
,
q
raster
.
Point
)
raster
.
Fix64
{
func
pDot
(
p
,
q
fixed
.
Point26_6
)
fixed
.
Int52_12
{
px
,
py
:=
int64
(
p
.
X
),
int64
(
p
.
Y
)
qx
,
qy
:=
int64
(
q
.
X
),
int64
(
q
.
Y
)
return
raster
.
Fix64
(
px
*
qx
+
py
*
qy
)
return
fixed
.
Int52_12
(
px
*
qx
+
py
*
qy
)
}
func
main
()
{
const
(
n
=
17
r
=
256
*
80
r
=
64
*
80
)
s
:=
raster
.
Fix32
(
r
*
math
.
Sqrt
(
2
)
/
2
)
t
:=
raster
.
Fix32
(
r
*
math
.
Tan
(
math
.
Pi
/
8
))
s
:=
fixed
.
Int26_6
(
r
*
math
.
Sqrt
(
2
)
/
2
)
t
:=
fixed
.
Int26_6
(
r
*
math
.
Tan
(
math
.
Pi
/
8
))
m
:=
image
.
NewRGBA
(
image
.
Rect
(
0
,
0
,
800
,
600
))
draw
.
Draw
(
m
,
m
.
Bounds
(),
image
.
NewUniform
(
color
.
RGBA
{
63
,
63
,
63
,
255
}),
image
.
ZP
,
draw
.
Src
)
...
...
@@ -51,38 +52,38 @@ func main() {
z
:=
raster
.
NewRasterizer
(
800
,
600
)
for
i
:=
0
;
i
<
n
;
i
++
{
cx
:=
raster
.
Fix32
(
25600
+
512
00
*
(
i
%
4
))
cy
:=
raster
.
Fix32
(
2560
+
32
000
*
(
i
/
4
))
c
:=
raster
.
Point
{
X
:
cx
,
Y
:
cy
}
cx
:=
fixed
.
Int26_6
(
6400
+
128
00
*
(
i
%
4
))
cy
:=
fixed
.
Int26_6
(
640
+
8
000
*
(
i
/
4
))
c
:=
fixed
.
Point26_6
{
X
:
cx
,
Y
:
cy
}
theta
:=
math
.
Pi
*
(
0.5
+
0.5
*
float64
(
i
)
/
(
n
-
1
))
dx
:=
raster
.
Fix32
(
r
*
math
.
Cos
(
theta
))
dy
:=
raster
.
Fix32
(
r
*
math
.
Sin
(
theta
))
d
:=
raster
.
Point
{
X
:
dx
,
Y
:
dy
}
dx
:=
fixed
.
Int26_6
(
r
*
math
.
Cos
(
theta
))
dy
:=
fixed
.
Int26_6
(
r
*
math
.
Sin
(
theta
))
d
:=
fixed
.
Point26_6
{
X
:
dx
,
Y
:
dy
}
// Draw a quarter-circle approximated by two quadratic segments,
// with each segment spanning 45 degrees.
z
.
Start
(
c
)
z
.
Add1
(
c
.
Add
(
raster
.
Point
{
X
:
r
,
Y
:
0
}))
z
.
Add2
(
c
.
Add
(
raster
.
Point
{
X
:
r
,
Y
:
t
}),
c
.
Add
(
raster
.
Point
{
X
:
s
,
Y
:
s
}))
z
.
Add2
(
c
.
Add
(
raster
.
Point
{
X
:
t
,
Y
:
r
}),
c
.
Add
(
raster
.
Point
{
X
:
0
,
Y
:
r
}))
// Add another quadratic segment whose angle ranges between 0 and 90
degrees.
//
For an explanation of the magic constants 22, 150, 181 and 256, read the
// comments in the freetype/raster package.
dot
:=
256
*
pDot
(
d
,
raster
.
Point
{
X
:
0
,
Y
:
r
})
/
(
r
*
r
)
multiple
:=
raster
.
Fix32
(
150
-
22
*
(
dot
-
181
)
/
(
256
-
181
))
z
.
Add2
(
c
.
Add
(
raster
.
Point
{
X
:
dx
,
Y
:
r
+
dy
}
.
Mul
(
multiple
)),
c
.
Add
(
d
))
z
.
Add1
(
c
.
Add
(
fixed
.
Point26_6
{
X
:
r
,
Y
:
0
}))
z
.
Add2
(
c
.
Add
(
fixed
.
Point26_6
{
X
:
r
,
Y
:
t
}),
c
.
Add
(
fixed
.
Point26_6
{
X
:
s
,
Y
:
s
}))
z
.
Add2
(
c
.
Add
(
fixed
.
Point26_6
{
X
:
t
,
Y
:
r
}),
c
.
Add
(
fixed
.
Point26_6
{
X
:
0
,
Y
:
r
}))
// Add another quadratic segment whose angle ranges between 0 and 90
//
degrees. For an explanation of the magic constants 128, 150, 181 and
//
256, read the
comments in the freetype/raster package.
dot
:=
256
*
pDot
(
d
,
fixed
.
Point26_6
{
X
:
0
,
Y
:
r
})
/
(
r
*
r
)
multiple
:=
fixed
.
Int26_6
(
150
-
(
150
-
128
)
*
(
dot
-
181
)
/
(
256
-
181
))
>>
2
z
.
Add2
(
c
.
Add
(
fixed
.
Point26_6
{
X
:
dx
,
Y
:
r
+
dy
}
.
Mul
(
multiple
)),
c
.
Add
(
d
))
// Close the curve.
z
.
Add1
(
c
)
}
z
.
Rasterize
(
mp
)
for
i
:=
0
;
i
<
n
;
i
++
{
cx
:=
raster
.
Fix32
(
25600
+
512
00
*
(
i
%
4
))
cy
:=
raster
.
Fix32
(
2560
+
32
000
*
(
i
/
4
))
cx
:=
fixed
.
Int26_6
(
6400
+
128
00
*
(
i
%
4
))
cy
:=
fixed
.
Int26_6
(
640
+
8
000
*
(
i
/
4
))
for
j
:=
0
;
j
<
n
;
j
++
{
theta
:=
math
.
Pi
*
float64
(
j
)
/
(
n
-
1
)
dx
:=
raster
.
Fix32
(
r
*
math
.
Cos
(
theta
))
dy
:=
raster
.
Fix32
(
r
*
math
.
Sin
(
theta
))
m
.
Set
(
int
((
cx
+
dx
)
/
256
),
int
((
cy
+
dy
)
/
256
),
color
.
RGBA
{
255
,
255
,
0
,
255
})
dx
:=
fixed
.
Int26_6
(
r
*
math
.
Cos
(
theta
))
dy
:=
fixed
.
Int26_6
(
r
*
math
.
Sin
(
theta
))
m
.
Set
(
int
((
cx
+
dx
)
/
64
),
int
((
cy
+
dy
)
/
64
),
color
.
RGBA
{
255
,
255
,
0
,
255
})
}
}
...
...
freetype.go
浏览文件 @
856a70c3
...
...
@@ -15,6 +15,7 @@ import (
"github.com/golang/freetype/raster"
"github.com/golang/freetype/truetype"
"golang.org/x/image/math/fixed"
)
// These constants determine the size of the glyph cache. The cache is keyed
...
...
@@ -33,7 +34,7 @@ const (
type
cacheEntry
struct
{
valid
bool
glyph
truetype
.
Index
advanceWidth
raster
.
Fix32
advanceWidth
fixed
.
Int26_6
mask
*
image
.
Alpha
offset
image
.
Point
}
...
...
@@ -45,12 +46,12 @@ func ParseFont(b []byte) (*truetype.Font, error) {
return
truetype
.
Parse
(
b
)
}
// Pt converts from a co-ordinate pair measured in pixels to a
raster.Point
// co-ordinate pair measured in
raster.Fix32
units.
func
Pt
(
x
,
y
int
)
raster
.
Point
{
return
raster
.
Point
{
X
:
raster
.
Fix32
(
x
<<
8
),
Y
:
raster
.
Fix32
(
y
<<
8
),
// Pt converts from a co-ordinate pair measured in pixels to a
fixed.Point26_6
// co-ordinate pair measured in
fixed.Int26_6
units.
func
Pt
(
x
,
y
int
)
fixed
.
Point26_6
{
return
fixed
.
Point26_6
{
X
:
fixed
.
Int26_6
(
x
<<
6
),
Y
:
fixed
.
Int26_6
(
y
<<
6
),
}
}
...
...
@@ -85,12 +86,12 @@ type Context struct {
// PointToFix32 converts the given number of points (as in ``a 12 point font'')
// into fixed point units.
func
(
c
*
Context
)
PointToFix32
(
x
float64
)
raster
.
Fix32
{
return
raster
.
Fix32
(
x
*
float64
(
c
.
dpi
)
*
(
256
.0
/
72.0
))
func
(
c
*
Context
)
PointToFix32
(
x
float64
)
fixed
.
Int26_6
{
return
fixed
.
Int26_6
(
x
*
float64
(
c
.
dpi
)
*
(
64
.0
/
72.0
))
}
// drawContour draws the given closed contour with the given offset.
func
(
c
*
Context
)
drawContour
(
ps
[]
truetype
.
Point
,
dx
,
dy
raster
.
Fix32
)
{
func
(
c
*
Context
)
drawContour
(
ps
[]
truetype
.
Point
,
dx
,
dy
fixed
.
Int26_6
)
{
if
len
(
ps
)
==
0
{
return
}
...
...
@@ -105,23 +106,23 @@ func (c *Context) drawContour(ps []truetype.Point, dx, dy raster.Fix32) {
// ps[0] is a truetype.Point measured in FUnits and positive Y going
// upwards. start is the same thing measured in fixed point units and
// positive Y going downwards, and offset by (dx, dy).
start
:=
raster
.
Point
{
X
:
dx
+
raster
.
Fix32
(
ps
[
0
]
.
X
<<
2
),
Y
:
dy
-
raster
.
Fix32
(
ps
[
0
]
.
Y
<<
2
),
start
:=
fixed
.
Point26_6
{
X
:
dx
+
fixed
.
Int26_6
(
ps
[
0
]
.
X
),
Y
:
dy
-
fixed
.
Int26_6
(
ps
[
0
]
.
Y
),
}
others
:=
[]
truetype
.
Point
(
nil
)
if
ps
[
0
]
.
Flags
&
0x01
!=
0
{
others
=
ps
[
1
:
]
}
else
{
last
:=
raster
.
Point
{
X
:
dx
+
raster
.
Fix32
(
ps
[
len
(
ps
)
-
1
]
.
X
<<
2
),
Y
:
dy
-
raster
.
Fix32
(
ps
[
len
(
ps
)
-
1
]
.
Y
<<
2
),
last
:=
fixed
.
Point26_6
{
X
:
dx
+
fixed
.
Int26_6
(
ps
[
len
(
ps
)
-
1
]
.
X
),
Y
:
dy
-
fixed
.
Int26_6
(
ps
[
len
(
ps
)
-
1
]
.
Y
),
}
if
ps
[
len
(
ps
)
-
1
]
.
Flags
&
0x01
!=
0
{
start
=
last
others
=
ps
[
:
len
(
ps
)
-
1
]
}
else
{
start
=
raster
.
Point
{
start
=
fixed
.
Point26_6
{
X
:
(
start
.
X
+
last
.
X
)
/
2
,
Y
:
(
start
.
Y
+
last
.
Y
)
/
2
,
}
...
...
@@ -131,9 +132,9 @@ func (c *Context) drawContour(ps []truetype.Point, dx, dy raster.Fix32) {
c
.
r
.
Start
(
start
)
q0
,
on0
:=
start
,
true
for
_
,
p
:=
range
others
{
q
:=
raster
.
Point
{
X
:
dx
+
raster
.
Fix32
(
p
.
X
<<
2
),
Y
:
dy
-
raster
.
Fix32
(
p
.
Y
<<
2
),
q
:=
fixed
.
Point26_6
{
X
:
dx
+
fixed
.
Int26_6
(
p
.
X
),
Y
:
dy
-
fixed
.
Int26_6
(
p
.
Y
),
}
on
:=
p
.
Flags
&
0x01
!=
0
if
on
{
...
...
@@ -146,7 +147,7 @@ func (c *Context) drawContour(ps []truetype.Point, dx, dy raster.Fix32) {
if
on0
{
// No-op.
}
else
{
mid
:=
raster
.
Point
{
mid
:=
fixed
.
Point26_6
{
X
:
(
q0
.
X
+
q
.
X
)
/
2
,
Y
:
(
q0
.
Y
+
q
.
Y
)
/
2
,
}
...
...
@@ -166,27 +167,27 @@ func (c *Context) drawContour(ps []truetype.Point, dx, dy raster.Fix32) {
// rasterize returns the advance width, glyph mask and integer-pixel offset
// to render the given glyph at the given sub-pixel offsets.
// The 24.8 fixed point arguments fx and fy must be in the range [0, 1).
func
(
c
*
Context
)
rasterize
(
glyph
truetype
.
Index
,
fx
,
fy
raster
.
Fix32
)
(
raster
.
Fix32
,
*
image
.
Alpha
,
image
.
Point
,
error
)
{
func
(
c
*
Context
)
rasterize
(
glyph
truetype
.
Index
,
fx
,
fy
fixed
.
Int26_6
)
(
fixed
.
Int26_6
,
*
image
.
Alpha
,
image
.
Point
,
error
)
{
if
err
:=
c
.
glyphBuf
.
Load
(
c
.
font
,
c
.
scale
,
glyph
,
truetype
.
Hinting
(
c
.
hinting
));
err
!=
nil
{
return
0
,
nil
,
image
.
Point
{},
err
}
// Calculate the integer-pixel bounds for the glyph.
xmin
:=
int
(
fx
+
raster
.
Fix32
(
c
.
glyphBuf
.
B
.
XMin
<<
2
))
>>
8
ymin
:=
int
(
fy
-
raster
.
Fix32
(
c
.
glyphBuf
.
B
.
YMax
<<
2
))
>>
8
xmax
:=
int
(
fx
+
raster
.
Fix32
(
c
.
glyphBuf
.
B
.
XMax
<<
2
)
+
0xff
)
>>
8
ymax
:=
int
(
fy
-
raster
.
Fix32
(
c
.
glyphBuf
.
B
.
YMin
<<
2
)
+
0xff
)
>>
8
xmin
:=
int
(
fx
+
fixed
.
Int26_6
(
c
.
glyphBuf
.
B
.
XMin
))
>>
6
ymin
:=
int
(
fy
-
fixed
.
Int26_6
(
c
.
glyphBuf
.
B
.
YMax
))
>>
6
xmax
:=
int
(
fx
+
fixed
.
Int26_6
(
c
.
glyphBuf
.
B
.
XMax
)
+
0xff
)
>>
6
ymax
:=
int
(
fy
-
fixed
.
Int26_6
(
c
.
glyphBuf
.
B
.
YMin
)
+
0xff
)
>>
6
if
xmin
>
xmax
||
ymin
>
ymax
{
return
0
,
nil
,
image
.
Point
{},
errors
.
New
(
"freetype: negative sized glyph"
)
}
// A TrueType's glyph's nodes can have negative co-ordinates, but the
// rasterizer clips anything left of x=0 or above y=0. xmin and ymin
//
are the pixel offsets, based on the font's FUnit metrics, that let
//
a negative co-ordinate in TrueType space be non-negative in
//
rasterizer
space. xmin and ymin are typically <= 0.
fx
+=
raster
.
Fix32
(
-
xmin
<<
8
)
fy
+=
raster
.
Fix32
(
-
ymin
<<
8
)
// rasterizer clips anything left of x=0 or above y=0. xmin and ymin
are
//
the pixel offsets, based on the font's FUnit metrics, that let a
//
negative co-ordinate in TrueType space be non-negative in rasterizer
// space. xmin and ymin are typically <= 0.
fx
+=
fixed
.
Int26_6
(
-
xmin
<<
6
)
fy
+=
fixed
.
Int26_6
(
-
ymin
<<
6
)
// Rasterize the glyph's vectors.
c
.
r
.
Clear
()
e0
:=
0
...
...
@@ -196,23 +197,23 @@ func (c *Context) rasterize(glyph truetype.Index, fx, fy raster.Fix32) (
}
a
:=
image
.
NewAlpha
(
image
.
Rect
(
0
,
0
,
xmax
-
xmin
,
ymax
-
ymin
))
c
.
r
.
Rasterize
(
raster
.
NewAlphaSrcPainter
(
a
))
return
raster
.
Fix32
(
c
.
glyphBuf
.
AdvanceWidth
<<
2
),
a
,
image
.
Point
{
xmin
,
ymin
},
nil
return
fixed
.
Int26_6
(
c
.
glyphBuf
.
AdvanceWidth
),
a
,
image
.
Point
{
xmin
,
ymin
},
nil
}
// glyph returns the advance width, glyph mask and integer-pixel offset to
// render the given glyph at the given sub-pixel point. It is a cache for the
// rasterize method. Unlike rasterize, p's co-ordinates do not have to be in
// the range [0, 1).
func
(
c
*
Context
)
glyph
(
glyph
truetype
.
Index
,
p
raster
.
Point
)
(
raster
.
Fix32
,
*
image
.
Alpha
,
image
.
Point
,
error
)
{
func
(
c
*
Context
)
glyph
(
glyph
truetype
.
Index
,
p
fixed
.
Point26_6
)
(
fixed
.
Int26_6
,
*
image
.
Alpha
,
image
.
Point
,
error
)
{
// Split p.X and p.Y into their integer and fractional parts.
ix
,
fx
:=
int
(
p
.
X
>>
8
),
p
.
X
&
0xf
f
iy
,
fy
:=
int
(
p
.
Y
>>
8
),
p
.
Y
&
0xf
f
ix
,
fx
:=
int
(
p
.
X
>>
6
),
p
.
X
&
0x3
f
iy
,
fy
:=
int
(
p
.
Y
>>
6
),
p
.
Y
&
0x3
f
// Calculate the index t into the cache array.
tg
:=
int
(
glyph
)
%
nGlyphs
tx
:=
int
(
fx
)
/
(
256
/
nXFractions
)
ty
:=
int
(
fy
)
/
(
256
/
nYFractions
)
tx
:=
int
(
fx
)
/
(
64
/
nXFractions
)
ty
:=
int
(
fy
)
/
(
64
/
nYFractions
)
t
:=
((
tg
*
nXFractions
)
+
tx
)
*
nYFractions
+
ty
// Check for a cache hit.
if
e
:=
c
.
cache
[
t
];
e
.
valid
&&
e
.
glyph
==
glyph
{
...
...
@@ -233,24 +234,25 @@ func (c *Context) glyph(glyph truetype.Index, p raster.Point) (
// above and to the right of the point, but some may be below or to the left.
// For example, drawing a string that starts with a 'J' in an italic font may
// affect pixels below and left of the point.
// p is a raster.Point and can therefore represent sub-pixel positions.
func
(
c
*
Context
)
DrawString
(
s
string
,
p
raster
.
Point
)
(
raster
.
Point
,
error
)
{
//
// p is a fixed.Point26_6 and can therefore represent sub-pixel positions.
func
(
c
*
Context
)
DrawString
(
s
string
,
p
fixed
.
Point26_6
)
(
fixed
.
Point26_6
,
error
)
{
if
c
.
font
==
nil
{
return
raster
.
Point
{},
errors
.
New
(
"freetype: DrawText called with a nil font"
)
return
fixed
.
Point26_6
{},
errors
.
New
(
"freetype: DrawText called with a nil font"
)
}
prev
,
hasPrev
:=
truetype
.
Index
(
0
),
false
for
_
,
rune
:=
range
s
{
index
:=
c
.
font
.
Index
(
rune
)
if
hasPrev
{
kern
:=
raster
.
Fix32
(
c
.
font
.
Kerning
(
c
.
scale
,
prev
,
index
))
<<
2
kern
:=
fixed
.
Int26_6
(
c
.
font
.
Kerning
(
c
.
scale
,
prev
,
index
))
if
c
.
hinting
!=
NoHinting
{
kern
=
(
kern
+
128
)
&^
255
kern
=
(
kern
+
32
)
&^
63
}
p
.
X
+=
kern
}
advanceWidth
,
mask
,
offset
,
err
:=
c
.
glyph
(
index
,
p
)
if
err
!=
nil
{
return
raster
.
Point
{},
err
return
fixed
.
Point26_6
{},
err
}
p
.
X
+=
advanceWidth
glyphRect
:=
mask
.
Bounds
()
.
Add
(
offset
)
...
...
raster/geom.go
浏览文件 @
856a70c3
...
...
@@ -8,36 +8,12 @@ package raster
import
(
"fmt"
"math"
)
// A Fix32 is a 24.8 fixed point number.
type
Fix32
int32
// A Fix64 is a 48.16 fixed point number.
type
Fix64
int64
// String returns a human-readable representation of a 24.8 fixed point number.
// For example, the number one-and-a-quarter becomes "1:064".
func
(
x
Fix32
)
String
()
string
{
if
x
<
0
{
x
=
-
x
return
fmt
.
Sprintf
(
"-%d:%03d"
,
int32
(
x
/
256
),
int32
(
x
%
256
))
}
return
fmt
.
Sprintf
(
"%d:%03d"
,
int32
(
x
/
256
),
int32
(
x
%
256
))
}
// String returns a human-readable representation of a 48.16 fixed point number.
// For example, the number one-and-a-quarter becomes "1:16384".
func
(
x
Fix64
)
String
()
string
{
if
x
<
0
{
x
=
-
x
return
fmt
.
Sprintf
(
"-%d:%05d"
,
int64
(
x
/
65536
),
int64
(
x
%
65536
))
}
return
fmt
.
Sprintf
(
"%d:%05d"
,
int64
(
x
/
65536
),
int64
(
x
%
65536
))
}
"golang.org/x/image/math/fixed"
)
// maxAbs returns the maximum of abs(a) and abs(b).
func
maxAbs
(
a
,
b
Fix32
)
Fix32
{
func
maxAbs
(
a
,
b
fixed
.
Int26_6
)
fixed
.
Int26_6
{
if
a
<
0
{
a
=
-
a
}
...
...
@@ -50,138 +26,112 @@ func maxAbs(a, b Fix32) Fix32 {
return
a
}
// A Point represents a two-dimensional point or vector, in 24.8 fixed point
// format.
type
Point
struct
{
X
,
Y
Fix32
}
// String returns a human-readable representation of a Point.
func
(
p
Point
)
String
()
string
{
return
"("
+
p
.
X
.
String
()
+
", "
+
p
.
Y
.
String
()
+
")"
}
// Add returns the vector p + q.
func
(
p
Point
)
Add
(
q
Point
)
Point
{
return
Point
{
p
.
X
+
q
.
X
,
p
.
Y
+
q
.
Y
}
}
// Sub returns the vector p - q.
func
(
p
Point
)
Sub
(
q
Point
)
Point
{
return
Point
{
p
.
X
-
q
.
X
,
p
.
Y
-
q
.
Y
}
}
// Mul returns the vector k * p.
func
(
p
Point
)
Mul
(
k
Fix32
)
Point
{
return
Point
{
p
.
X
*
k
/
256
,
p
.
Y
*
k
/
256
}
}
// pNeg returns the vector -p, or equivalently p rotated by 180 degrees.
func
pNeg
(
p
Point
)
Point
{
return
Point
{
-
p
.
X
,
-
p
.
Y
}
func
pNeg
(
p
fixed
.
Point26_6
)
fixed
.
Point26_6
{
return
fixed
.
Point26_6
{
-
p
.
X
,
-
p
.
Y
}
}
// pDot returns the dot product p·q.
func
pDot
(
p
,
q
Point
)
Fix64
{
func
pDot
(
p
fixed
.
Point26_6
,
q
fixed
.
Point26_6
)
fixed
.
Int52_12
{
px
,
py
:=
int64
(
p
.
X
),
int64
(
p
.
Y
)
qx
,
qy
:=
int64
(
q
.
X
),
int64
(
q
.
Y
)
return
Fix64
(
px
*
qx
+
py
*
qy
)
return
fixed
.
Int52_12
(
px
*
qx
+
py
*
qy
)
}
// pLen returns the length of the vector p.
func
pLen
(
p
Point
)
Fix32
{
func
pLen
(
p
fixed
.
Point26_6
)
fixed
.
Int26_6
{
// TODO(nigeltao): use fixed point math.
x
:=
float64
(
p
.
X
)
y
:=
float64
(
p
.
Y
)
return
Fix32
(
math
.
Sqrt
(
x
*
x
+
y
*
y
))
return
fixed
.
Int26_6
(
math
.
Sqrt
(
x
*
x
+
y
*
y
))
}
// pNorm returns the vector p normalized to the given length, or zero if p is
// degenerate.
func
pNorm
(
p
Point
,
length
Fix32
)
Point
{
func
pNorm
(
p
fixed
.
Point26_6
,
length
fixed
.
Int26_6
)
fixed
.
Point26_6
{
d
:=
pLen
(
p
)
if
d
==
0
{
return
Point
{}
return
fixed
.
Point26_6
{}
}
s
,
t
:=
int64
(
length
),
int64
(
d
)
x
:=
int64
(
p
.
X
)
*
s
/
t
y
:=
int64
(
p
.
Y
)
*
s
/
t
return
Point
{
Fix32
(
x
),
Fix32
(
y
)}
return
fixed
.
Point26_6
{
fixed
.
Int26_6
(
x
),
fixed
.
Int26_6
(
y
)}
}
// pRot45CW returns the vector p rotated clockwise by 45 degrees.
//
// Note that the Y-axis grows downwards, so {1, 0}.Rot45CW is {1/√2, 1/√2}.
func
pRot45CW
(
p
Point
)
Point
{
func
pRot45CW
(
p
fixed
.
Point26_6
)
fixed
.
Point26_6
{
// 181/256 is approximately 1/√2, or sin(π/4).
px
,
py
:=
int64
(
p
.
X
),
int64
(
p
.
Y
)
qx
:=
(
+
px
-
py
)
*
181
/
256
qy
:=
(
+
px
+
py
)
*
181
/
256
return
Point
{
Fix32
(
qx
),
Fix32
(
qy
)}
return
fixed
.
Point26_6
{
fixed
.
Int26_6
(
qx
),
fixed
.
Int26_6
(
qy
)}
}
// pRot90CW returns the vector p rotated clockwise by 90 degrees.
//
// Note that the Y-axis grows downwards, so {1, 0}.Rot90CW is {0, 1}.
func
pRot90CW
(
p
Point
)
Point
{
return
Point
{
-
p
.
Y
,
p
.
X
}
func
pRot90CW
(
p
fixed
.
Point26_6
)
fixed
.
Point26_6
{
return
fixed
.
Point26_6
{
-
p
.
Y
,
p
.
X
}
}
// pRot135CW returns the vector p rotated clockwise by 135 degrees.
//
// Note that the Y-axis grows downwards, so {1, 0}.Rot135CW is {-1/√2, 1/√2}.
func
pRot135CW
(
p
Point
)
Point
{
func
pRot135CW
(
p
fixed
.
Point26_6
)
fixed
.
Point26_6
{
// 181/256 is approximately 1/√2, or sin(π/4).
px
,
py
:=
int64
(
p
.
X
),
int64
(
p
.
Y
)
qx
:=
(
-
px
-
py
)
*
181
/
256
qy
:=
(
+
px
-
py
)
*
181
/
256
return
Point
{
Fix32
(
qx
),
Fix32
(
qy
)}
return
fixed
.
Point26_6
{
fixed
.
Int26_6
(
qx
),
fixed
.
Int26_6
(
qy
)}
}
// pRot45CCW returns the vector p rotated counter-clockwise by 45 degrees.
//
// Note that the Y-axis grows downwards, so {1, 0}.Rot45CCW is {1/√2, -1/√2}.
func
pRot45CCW
(
p
Point
)
Point
{
func
pRot45CCW
(
p
fixed
.
Point26_6
)
fixed
.
Point26_6
{
// 181/256 is approximately 1/√2, or sin(π/4).
px
,
py
:=
int64
(
p
.
X
),
int64
(
p
.
Y
)
qx
:=
(
+
px
+
py
)
*
181
/
256
qy
:=
(
-
px
+
py
)
*
181
/
256
return
Point
{
Fix32
(
qx
),
Fix32
(
qy
)}
return
fixed
.
Point26_6
{
fixed
.
Int26_6
(
qx
),
fixed
.
Int26_6
(
qy
)}
}
// pRot90CCW returns the vector p rotated counter-clockwise by 90 degrees.
//
// Note that the Y-axis grows downwards, so {1, 0}.Rot90CCW is {0, -1}.
func
pRot90CCW
(
p
Point
)
Point
{
return
Point
{
p
.
Y
,
-
p
.
X
}
func
pRot90CCW
(
p
fixed
.
Point26_6
)
fixed
.
Point26_6
{
return
fixed
.
Point26_6
{
p
.
Y
,
-
p
.
X
}
}
// pRot135CCW returns the vector p rotated counter-clockwise by 135 degrees.
//
// Note that the Y-axis grows downwards, so {1, 0}.Rot135CCW is {-1/√2, -1/√2}.
func
pRot135CCW
(
p
Point
)
Point
{
func
pRot135CCW
(
p
fixed
.
Point26_6
)
fixed
.
Point26_6
{
// 181/256 is approximately 1/√2, or sin(π/4).
px
,
py
:=
int64
(
p
.
X
),
int64
(
p
.
Y
)
qx
:=
(
-
px
+
py
)
*
181
/
256
qy
:=
(
-
px
-
py
)
*
181
/
256
return
Point
{
Fix32
(
qx
),
Fix32
(
qy
)}
return
fixed
.
Point26_6
{
fixed
.
Int26_6
(
qx
),
fixed
.
Int26_6
(
qy
)}
}
// An Adder accumulates points on a curve.
type
Adder
interface
{
// Start starts a new curve at the given point.
Start
(
a
Point
)
Start
(
a
fixed
.
Point26_6
)
// Add1 adds a linear segment to the current curve.
Add1
(
b
Point
)
Add1
(
b
fixed
.
Point26_6
)
// Add2 adds a quadratic segment to the current curve.
Add2
(
b
,
c
Point
)
Add2
(
b
,
c
fixed
.
Point26_6
)
// Add3 adds a cubic segment to the current curve.
Add3
(
b
,
c
,
d
Point
)
Add3
(
b
,
c
,
d
fixed
.
Point26_6
)
}
// A Path is a sequence of curves, and a curve is a start point followed by a
// sequence of linear, quadratic or cubic segments.
type
Path
[]
Fix32
type
Path
[]
fixed
.
Int26_6
// String returns a human-readable representation of a Path.
func
(
p
Path
)
String
()
string
{
...
...
@@ -192,16 +142,16 @@ func (p Path) String() string {
}
switch
p
[
i
]
{
case
0
:
s
+=
"S0"
+
fmt
.
Sprint
([]
Fix32
(
p
[
i
+
1
:
i
+
3
]))
s
+=
"S0"
+
fmt
.
Sprint
([]
fixed
.
Int26_6
(
p
[
i
+
1
:
i
+
3
]))
i
+=
4
case
1
:
s
+=
"A1"
+
fmt
.
Sprint
([]
Fix32
(
p
[
i
+
1
:
i
+
3
]))
s
+=
"A1"
+
fmt
.
Sprint
([]
fixed
.
Int26_6
(
p
[
i
+
1
:
i
+
3
]))
i
+=
4
case
2
:
s
+=
"A2"
+
fmt
.
Sprint
([]
Fix32
(
p
[
i
+
1
:
i
+
5
]))
s
+=
"A2"
+
fmt
.
Sprint
([]
fixed
.
Int26_6
(
p
[
i
+
1
:
i
+
5
]))
i
+=
6
case
3
:
s
+=
"A3"
+
fmt
.
Sprint
([]
Fix32
(
p
[
i
+
1
:
i
+
7
]))
s
+=
"A3"
+
fmt
.
Sprint
([]
fixed
.
Int26_6
(
p
[
i
+
1
:
i
+
7
]))
i
+=
8
default
:
panic
(
"freetype/raster: bad path"
)
...
...
@@ -216,22 +166,22 @@ func (p *Path) Clear() {
}
// Start starts a new curve at the given point.
func
(
p
*
Path
)
Start
(
a
Point
)
{
func
(
p
*
Path
)
Start
(
a
fixed
.
Point26_6
)
{
*
p
=
append
(
*
p
,
0
,
a
.
X
,
a
.
Y
,
0
)
}
// Add1 adds a linear segment to the current curve.
func
(
p
*
Path
)
Add1
(
b
Point
)
{
func
(
p
*
Path
)
Add1
(
b
fixed
.
Point26_6
)
{
*
p
=
append
(
*
p
,
1
,
b
.
X
,
b
.
Y
,
1
)
}
// Add2 adds a quadratic segment to the current curve.
func
(
p
*
Path
)
Add2
(
b
,
c
Point
)
{
func
(
p
*
Path
)
Add2
(
b
,
c
fixed
.
Point26_6
)
{
*
p
=
append
(
*
p
,
2
,
b
.
X
,
b
.
Y
,
c
.
X
,
c
.
Y
,
2
)
}
// Add3 adds a cubic segment to the current curve.
func
(
p
*
Path
)
Add3
(
b
,
c
,
d
Point
)
{
func
(
p
*
Path
)
Add3
(
b
,
c
,
d
fixed
.
Point26_6
)
{
*
p
=
append
(
*
p
,
3
,
b
.
X
,
b
.
Y
,
c
.
X
,
c
.
Y
,
d
.
X
,
d
.
Y
,
3
)
}
...
...
@@ -241,18 +191,18 @@ func (p *Path) AddPath(q Path) {
}
// AddStroke adds a stroked Path.
func
(
p
*
Path
)
AddStroke
(
q
Path
,
width
Fix32
,
cr
Capper
,
jr
Joiner
)
{
func
(
p
*
Path
)
AddStroke
(
q
Path
,
width
fixed
.
Int26_6
,
cr
Capper
,
jr
Joiner
)
{
Stroke
(
p
,
q
,
width
,
cr
,
jr
)
}
// firstPoint returns the first point in a non-empty Path.
func
(
p
Path
)
firstPoint
()
Point
{
return
Point
{
p
[
1
],
p
[
2
]}
func
(
p
Path
)
firstPoint
()
fixed
.
Point26_6
{
return
fixed
.
Point26_6
{
p
[
1
],
p
[
2
]}
}
// lastPoint returns the last point in a non-empty Path.
func
(
p
Path
)
lastPoint
()
Point
{
return
Point
{
p
[
len
(
p
)
-
3
],
p
[
len
(
p
)
-
2
]}
func
(
p
Path
)
lastPoint
()
fixed
.
Point26_6
{
return
fixed
.
Point26_6
{
p
[
len
(
p
)
-
3
],
p
[
len
(
p
)
-
2
]}
}
// addPathReversed adds q reversed to p.
...
...
@@ -272,13 +222,22 @@ func addPathReversed(p Adder, q Path) {
return
case
1
:
i
-=
4
p
.
Add1
(
Point
{
q
[
i
-
2
],
q
[
i
-
1
]})
p
.
Add1
(
fixed
.
Point26_6
{
q
[
i
-
2
],
q
[
i
-
1
]},
)
case
2
:
i
-=
6
p
.
Add2
(
Point
{
q
[
i
+
2
],
q
[
i
+
3
]},
Point
{
q
[
i
-
2
],
q
[
i
-
1
]})
p
.
Add2
(
fixed
.
Point26_6
{
q
[
i
+
2
],
q
[
i
+
3
]},
fixed
.
Point26_6
{
q
[
i
-
2
],
q
[
i
-
1
]},
)
case
3
:
i
-=
8
p
.
Add3
(
Point
{
q
[
i
+
4
],
q
[
i
+
5
]},
Point
{
q
[
i
+
2
],
q
[
i
+
3
]},
Point
{
q
[
i
-
2
],
q
[
i
-
1
]})
p
.
Add3
(
fixed
.
Point26_6
{
q
[
i
+
4
],
q
[
i
+
5
]},
fixed
.
Point26_6
{
q
[
i
+
2
],
q
[
i
+
3
]},
fixed
.
Point26_6
{
q
[
i
-
2
],
q
[
i
-
1
]},
)
default
:
panic
(
"freetype/raster: bad path"
)
}
...
...
raster/raster.go
浏览文件 @
856a70c3
...
...
@@ -3,20 +3,22 @@
// FreeType License or the GNU General Public License version 2 (or
// any later version), both of which can be found in the LICENSE file.
//
The raster package
provides an anti-aliasing 2-D rasterizer.
//
Package raster
provides an anti-aliasing 2-D rasterizer.
//
// It is part of the larger Freetype
-Go suite of font-related packages,
//
but the raster package is not specific to font rasterization, and can
//
be used standalone without any other Freetype-Go
package.
// It is part of the larger Freetype
suite of font-related packages, but the
//
raster package is not specific to font rasterization, and can be used
//
standalone without any other Freetype
package.
//
// Rasterization is done by the same area/coverage accumulation algorithm
//
as the Freetype "smooth" module, and the Anti-Grain Geometry library.
//
A
description of the area/coverage algorithm is at
// Rasterization is done by the same area/coverage accumulation algorithm
as
//
the Freetype "smooth" module, and the Anti-Grain Geometry library. A
// description of the area/coverage algorithm is at
// http://projects.tuxee.net/cl-vectors/section-the-cl-aa-algorithm
package
raster
// import "github.com/golang/freetype/raster"
import
(
"strconv"
"golang.org/x/image/math/fixed"
)
// A cell is part of a linked list (for a given yi co-ordinate) of accumulated
...
...
@@ -41,7 +43,7 @@ type Rasterizer struct {
splitScale2
,
splitScale3
int
// The current pen position.
a
Point
a
fixed
.
Point26_6
// The current cell and its area/coverage being accumulated.
xi
,
yi
int
area
,
cover
int
...
...
@@ -116,12 +118,12 @@ func (r *Rasterizer) setCell(xi, yi int) {
// scan accumulates area/coverage for the yi'th scanline, going from
// x0 to x1 in the horizontal direction (in 24.8 fixed point co-ordinates)
// and from y0f to y1f fractional vertical units within that scanline.
func
(
r
*
Rasterizer
)
scan
(
yi
int
,
x0
,
y0f
,
x1
,
y1f
Fix32
)
{
// Break the 2
4.8
fixed point X co-ordinates into integral and fractional parts.
x0i
:=
int
(
x0
)
/
256
x0f
:=
x0
-
Fix32
(
256
*
x0i
)
x1i
:=
int
(
x1
)
/
256
x1f
:=
x1
-
Fix32
(
256
*
x1i
)
func
(
r
*
Rasterizer
)
scan
(
yi
int
,
x0
,
y0f
,
x1
,
y1f
fixed
.
Int26_6
)
{
// Break the 2
6.6
fixed point X co-ordinates into integral and fractional parts.
x0i
:=
int
(
x0
)
/
64
x0f
:=
x0
-
fixed
.
Int26_6
(
64
*
x0i
)
x1i
:=
int
(
x1
)
/
64
x1f
:=
x1
-
fixed
.
Int26_6
(
64
*
x1i
)
// A perfectly horizontal scan.
if
y0f
==
y1f
{
...
...
@@ -137,17 +139,17 @@ func (r *Rasterizer) scan(yi int, x0, y0f, x1, y1f Fix32) {
}
// There are at least two cells. Apart from the first and last cells,
// all intermediate cells go through the full width of the cell,
// or
256 units in 24.8
fixed point format.
// or
64 units in 26.6
fixed point format.
var
(
p
,
q
,
edge0
,
edge1
Fix32
p
,
q
,
edge0
,
edge1
fixed
.
Int26_6
xiDelta
int
)
if
dx
>
0
{
p
,
q
=
(
256
-
x0f
)
*
dy
,
dx
edge0
,
edge1
,
xiDelta
=
0
,
256
,
1
p
,
q
=
(
64
-
x0f
)
*
dy
,
dx
edge0
,
edge1
,
xiDelta
=
0
,
64
,
1
}
else
{
p
,
q
=
x0f
*
dy
,
-
dx
edge0
,
edge1
,
xiDelta
=
256
,
0
,
-
1
edge0
,
edge1
,
xiDelta
=
64
,
0
,
-
1
}
yDelta
,
yRem
:=
p
/
q
,
p
%
q
if
yRem
<
0
{
...
...
@@ -162,7 +164,7 @@ func (r *Rasterizer) scan(yi int, x0, y0f, x1, y1f Fix32) {
r
.
setCell
(
xi
,
yi
)
if
xi
!=
x1i
{
// Do all the intermediate cells.
p
=
256
*
(
y1f
-
y
+
yDelta
)
p
=
64
*
(
y1f
-
y
+
yDelta
)
fullDelta
,
fullRem
:=
p
/
q
,
p
%
q
if
fullRem
<
0
{
fullDelta
-=
1
...
...
@@ -176,7 +178,7 @@ func (r *Rasterizer) scan(yi int, x0, y0f, x1, y1f Fix32) {
yDelta
+=
1
yRem
-=
q
}
r
.
area
+=
int
(
256
*
yDelta
)
r
.
area
+=
int
(
64
*
yDelta
)
r
.
cover
+=
int
(
yDelta
)
xi
,
y
=
xi
+
xiDelta
,
y
+
yDelta
r
.
setCell
(
xi
,
yi
)
...
...
@@ -189,21 +191,22 @@ func (r *Rasterizer) scan(yi int, x0, y0f, x1, y1f Fix32) {
}
// Start starts a new curve at the given point.
func
(
r
*
Rasterizer
)
Start
(
a
Point
)
{
r
.
setCell
(
int
(
a
.
X
/
256
),
int
(
a
.
Y
/
256
))
func
(
r
*
Rasterizer
)
Start
(
a
fixed
.
Point26_6
)
{
r
.
setCell
(
int
(
a
.
X
/
64
),
int
(
a
.
Y
/
64
))
r
.
a
=
a
}
// Add1 adds a linear segment to the current curve.
func
(
r
*
Rasterizer
)
Add1
(
b
Point
)
{
func
(
r
*
Rasterizer
)
Add1
(
b
fixed
.
Point26_6
)
{
x0
,
y0
:=
r
.
a
.
X
,
r
.
a
.
Y
x1
,
y1
:=
b
.
X
,
b
.
Y
dx
,
dy
:=
x1
-
x0
,
y1
-
y0
// Break the 24.8 fixed point Y co-ordinates into integral and fractional parts.
y0i
:=
int
(
y0
)
/
256
y0f
:=
y0
-
Fix32
(
256
*
y0i
)
y1i
:=
int
(
y1
)
/
256
y1f
:=
y1
-
Fix32
(
256
*
y1i
)
// Break the 26.6 fixed point Y co-ordinates into integral and fractional
// parts.
y0i
:=
int
(
y0
)
/
64
y0f
:=
y0
-
fixed
.
Int26_6
(
64
*
y0i
)
y1i
:=
int
(
y1
)
/
64
y1f
:=
y1
-
fixed
.
Int26_6
(
64
*
y1i
)
if
y0i
==
y1i
{
// There is only one scanline.
...
...
@@ -213,16 +216,16 @@ func (r *Rasterizer) Add1(b Point) {
// This is a vertical line segment. We avoid calling r.scan and instead
// manipulate r.area and r.cover directly.
var
(
edge0
,
edge1
Fix32
edge0
,
edge1
fixed
.
Int26_6
yiDelta
int
)
if
dy
>
0
{
edge0
,
edge1
,
yiDelta
=
0
,
256
,
1
edge0
,
edge1
,
yiDelta
=
0
,
64
,
1
}
else
{
edge0
,
edge1
,
yiDelta
=
256
,
0
,
-
1
edge0
,
edge1
,
yiDelta
=
64
,
0
,
-
1
}
x0i
,
yi
:=
int
(
x0
)
/
256
,
y0i
x0fTimes2
:=
(
int
(
x0
)
-
(
256
*
x0i
))
*
2
x0i
,
yi
:=
int
(
x0
)
/
64
,
y0i
x0fTimes2
:=
(
int
(
x0
)
-
(
64
*
x0i
))
*
2
// Do the first pixel.
dcover
:=
int
(
edge1
-
y0f
)
darea
:=
int
(
x0fTimes2
*
dcover
)
...
...
@@ -246,19 +249,19 @@ func (r *Rasterizer) Add1(b Point) {
r
.
cover
+=
dcover
}
else
{
// There are at least two scanlines. Apart from the first and last
scanlines,
//
all intermediate scanlines go through the full height of the row, or 256
//
units in 24.8
fixed point format.
// There are at least two scanlines. Apart from the first and last
//
scanlines, all intermediate scanlines go through the full height of
//
the row, or 64 units in 26.6
fixed point format.
var
(
p
,
q
,
edge0
,
edge1
Fix32
p
,
q
,
edge0
,
edge1
fixed
.
Int26_6
yiDelta
int
)
if
dy
>
0
{
p
,
q
=
(
256
-
y0f
)
*
dx
,
dy
edge0
,
edge1
,
yiDelta
=
0
,
256
,
1
p
,
q
=
(
64
-
y0f
)
*
dx
,
dy
edge0
,
edge1
,
yiDelta
=
0
,
64
,
1
}
else
{
p
,
q
=
y0f
*
dx
,
-
dy
edge0
,
edge1
,
yiDelta
=
256
,
0
,
-
1
edge0
,
edge1
,
yiDelta
=
64
,
0
,
-
1
}
xDelta
,
xRem
:=
p
/
q
,
p
%
q
if
xRem
<
0
{
...
...
@@ -269,10 +272,10 @@ func (r *Rasterizer) Add1(b Point) {
x
,
yi
:=
x0
,
y0i
r
.
scan
(
yi
,
x
,
y0f
,
x
+
xDelta
,
edge1
)
x
,
yi
=
x
+
xDelta
,
yi
+
yiDelta
r
.
setCell
(
int
(
x
)
/
256
,
yi
)
r
.
setCell
(
int
(
x
)
/
64
,
yi
)
if
yi
!=
y1i
{
// Do all the intermediate scanlines.
p
=
256
*
dx
p
=
64
*
dx
fullDelta
,
fullRem
:=
p
/
q
,
p
%
q
if
fullRem
<
0
{
fullDelta
-=
1
...
...
@@ -288,7 +291,7 @@ func (r *Rasterizer) Add1(b Point) {
}
r
.
scan
(
yi
,
x
,
edge0
,
x
+
xDelta
,
edge1
)
x
,
yi
=
x
+
xDelta
,
yi
+
yiDelta
r
.
setCell
(
int
(
x
)
/
256
,
yi
)
r
.
setCell
(
int
(
x
)
/
64
,
yi
)
}
}
// Do the last scanline.
...
...
@@ -299,10 +302,11 @@ func (r *Rasterizer) Add1(b Point) {
}
// Add2 adds a quadratic segment to the current curve.
func
(
r
*
Rasterizer
)
Add2
(
b
,
c
Point
)
{
// Calculate nSplit (the number of recursive decompositions) based on how `curvy' it is.
// Specifically, how much the middle point b deviates from (a+c)/2.
dev
:=
maxAbs
(
r
.
a
.
X
-
2
*
b
.
X
+
c
.
X
,
r
.
a
.
Y
-
2
*
b
.
Y
+
c
.
Y
)
/
Fix32
(
r
.
splitScale2
)
func
(
r
*
Rasterizer
)
Add2
(
b
,
c
fixed
.
Point26_6
)
{
// Calculate nSplit (the number of recursive decompositions) based on how
// `curvy' it is. Specifically, how much the middle point b deviates from
// (a+c)/2.
dev
:=
maxAbs
(
r
.
a
.
X
-
2
*
b
.
X
+
c
.
X
,
r
.
a
.
Y
-
2
*
b
.
Y
+
c
.
Y
)
/
fixed
.
Int26_6
(
r
.
splitScale2
)
nsplit
:=
0
for
dev
>
0
{
dev
/=
4
...
...
@@ -315,7 +319,7 @@ func (r *Rasterizer) Add2(b, c Point) {
}
// Recursively decompose the curve nSplit levels deep.
var
(
pStack
[
2
*
maxNsplit
+
3
]
Point
pStack
[
2
*
maxNsplit
+
3
]
fixed
.
Point26_6
sStack
[
maxNsplit
+
1
]
int
i
int
)
...
...
@@ -347,7 +351,7 @@ func (r *Rasterizer) Add2(b, c Point) {
// Replace the level-0 quadratic with a two-linear-piece approximation.
midx
:=
(
p
[
0
]
.
X
+
2
*
p
[
1
]
.
X
+
p
[
2
]
.
X
)
/
4
midy
:=
(
p
[
0
]
.
Y
+
2
*
p
[
1
]
.
Y
+
p
[
2
]
.
Y
)
/
4
r
.
Add1
(
Point
{
midx
,
midy
})
r
.
Add1
(
fixed
.
Point26_6
{
midx
,
midy
})
r
.
Add1
(
p
[
0
])
i
--
}
...
...
@@ -355,10 +359,10 @@ func (r *Rasterizer) Add2(b, c Point) {
}
// Add3 adds a cubic segment to the current curve.
func
(
r
*
Rasterizer
)
Add3
(
b
,
c
,
d
Point
)
{
func
(
r
*
Rasterizer
)
Add3
(
b
,
c
,
d
fixed
.
Point26_6
)
{
// Calculate nSplit (the number of recursive decompositions) based on how `curvy' it is.
dev2
:=
maxAbs
(
r
.
a
.
X
-
3
*
(
b
.
X
+
c
.
X
)
+
d
.
X
,
r
.
a
.
Y
-
3
*
(
b
.
Y
+
c
.
Y
)
+
d
.
Y
)
/
Fix32
(
r
.
splitScale2
)
dev3
:=
maxAbs
(
r
.
a
.
X
-
2
*
b
.
X
+
d
.
X
,
r
.
a
.
Y
-
2
*
b
.
Y
+
d
.
Y
)
/
Fix32
(
r
.
splitScale3
)
dev2
:=
maxAbs
(
r
.
a
.
X
-
3
*
(
b
.
X
+
c
.
X
)
+
d
.
X
,
r
.
a
.
Y
-
3
*
(
b
.
Y
+
c
.
Y
)
+
d
.
Y
)
/
fixed
.
Int26_6
(
r
.
splitScale2
)
dev3
:=
maxAbs
(
r
.
a
.
X
-
2
*
b
.
X
+
d
.
X
,
r
.
a
.
Y
-
2
*
b
.
Y
+
d
.
Y
)
/
fixed
.
Int26_6
(
r
.
splitScale3
)
nsplit
:=
0
for
dev2
>
0
||
dev3
>
0
{
dev2
/=
8
...
...
@@ -372,7 +376,7 @@ func (r *Rasterizer) Add3(b, c, d Point) {
}
// Recursively decompose the curve nSplit levels deep.
var
(
pStack
[
3
*
maxNsplit
+
4
]
Point
pStack
[
3
*
maxNsplit
+
4
]
fixed
.
Point26_6
sStack
[
maxNsplit
+
1
]
int
i
int
)
...
...
@@ -413,7 +417,7 @@ func (r *Rasterizer) Add3(b, c, d Point) {
// Replace the level-0 cubic with a two-linear-piece approximation.
midx
:=
(
p
[
0
]
.
X
+
3
*
(
p
[
1
]
.
X
+
p
[
2
]
.
X
)
+
p
[
3
]
.
X
)
/
8
midy
:=
(
p
[
0
]
.
Y
+
3
*
(
p
[
1
]
.
Y
+
p
[
2
]
.
Y
)
+
p
[
3
]
.
Y
)
/
8
r
.
Add1
(
Point
{
midx
,
midy
})
r
.
Add1
(
fixed
.
Point26_6
{
midx
,
midy
})
r
.
Add1
(
p
[
0
])
i
--
}
...
...
@@ -425,16 +429,27 @@ func (r *Rasterizer) AddPath(p Path) {
for
i
:=
0
;
i
<
len
(
p
);
{
switch
p
[
i
]
{
case
0
:
r
.
Start
(
Point
{
p
[
i
+
1
],
p
[
i
+
2
]})
r
.
Start
(
fixed
.
Point26_6
{
p
[
i
+
1
],
p
[
i
+
2
]},
)
i
+=
4
case
1
:
r
.
Add1
(
Point
{
p
[
i
+
1
],
p
[
i
+
2
]})
r
.
Add1
(
fixed
.
Point26_6
{
p
[
i
+
1
],
p
[
i
+
2
]},
)
i
+=
4
case
2
:
r
.
Add2
(
Point
{
p
[
i
+
1
],
p
[
i
+
2
]},
Point
{
p
[
i
+
3
],
p
[
i
+
4
]})
r
.
Add2
(
fixed
.
Point26_6
{
p
[
i
+
1
],
p
[
i
+
2
]},
fixed
.
Point26_6
{
p
[
i
+
3
],
p
[
i
+
4
]},
)
i
+=
6
case
3
:
r
.
Add3
(
Point
{
p
[
i
+
1
],
p
[
i
+
2
]},
Point
{
p
[
i
+
3
],
p
[
i
+
4
]},
Point
{
p
[
i
+
5
],
p
[
i
+
6
]})
r
.
Add3
(
fixed
.
Point26_6
{
p
[
i
+
1
],
p
[
i
+
2
]},
fixed
.
Point26_6
{
p
[
i
+
3
],
p
[
i
+
4
]},
fixed
.
Point26_6
{
p
[
i
+
5
],
p
[
i
+
6
]},
)
i
+=
8
default
:
panic
(
"freetype/raster: bad path"
)
...
...
@@ -443,43 +458,44 @@ func (r *Rasterizer) AddPath(p Path) {
}
// AddStroke adds a stroked Path.
func
(
r
*
Rasterizer
)
AddStroke
(
q
Path
,
width
Fix32
,
cr
Capper
,
jr
Joiner
)
{
func
(
r
*
Rasterizer
)
AddStroke
(
q
Path
,
width
fixed
.
Int26_6
,
cr
Capper
,
jr
Joiner
)
{
Stroke
(
r
,
q
,
width
,
cr
,
jr
)
}
//
Converts an area value to a uint32 alpha value. A completely filled pixel
//
corresponds to an area of 256*256
*2, and an alpha of 1<<32-1. The
//
areaToAlpha converts an area value to a uint32 alpha value. A completely
//
filled pixel corresponds to an area of 64*64
*2, and an alpha of 1<<32-1. The
// conversion of area values greater than this depends on the winding rule:
// even-odd or non-zero.
func
(
r
*
Rasterizer
)
areaToAlpha
(
area
int
)
uint32
{
// The C Freetype implementation (version 2.3.12) does "alpha := area>>1" without
// the +1. Round-to-nearest gives a more symmetric result than round-down.
// The C implementation also returns 8-bit alpha, not 32-bit alpha.
// The C Freetype implementation (version 2.3.12) does "alpha := area>>1"
// without the +1. Round-to-nearest gives a more symmetric result than
// round-down. The C implementation also returns 8-bit alpha, not 32-bit
// alpha.
a
:=
(
area
+
1
)
>>
1
if
a
<
0
{
a
=
-
a
}
alpha
:=
uint32
(
a
)
if
r
.
UseNonZeroWinding
{
if
alpha
>
0x
f
fff
{
alpha
=
0x
f
fff
if
alpha
>
0x
0
fff
{
alpha
=
0x
0
fff
}
}
else
{
alpha
&=
0x1fff
f
if
alpha
>
0x1000
0
{
alpha
=
0x2000
0
-
alpha
}
else
if
alpha
==
0x1000
0
{
alpha
=
0x0fff
f
alpha
&=
0x1fff
if
alpha
>
0x1000
{
alpha
=
0x2000
-
alpha
}
else
if
alpha
==
0x1000
{
alpha
=
0x0fff
}
}
alpha
|=
alpha
<<
16
return
alpha
alpha
>>=
4
return
alpha
*
0x01010101
}
// Rasterize converts r's accumulated curves into Spans for p. The Spans
//
passed to p are non-overlapping, and sorted by Y and then X. They all
//
have non-zero width (and 0 <= X0 < X1 <= r.width) and non-zero A, except
//
for the final
Span, which has Y, X0, X1 and A all equal to zero.
// Rasterize converts r's accumulated curves into Spans for p. The Spans
passed
//
to p are non-overlapping, and sorted by Y and then X. They all have non-zero
//
width (and 0 <= X0 < X1 <= r.width) and non-zero A, except for the final
// Span, which has Y, X0, X1 and A all equal to zero.
func
(
r
*
Rasterizer
)
Rasterize
(
p
Painter
)
{
r
.
saveCell
()
s
:=
0
...
...
@@ -487,7 +503,7 @@ func (r *Rasterizer) Rasterize(p Painter) {
xi
,
cover
:=
0
,
0
for
c
:=
r
.
cellIndex
[
yi
];
c
!=
-
1
;
c
=
r
.
cell
[
c
]
.
next
{
if
cover
!=
0
&&
r
.
cell
[
c
]
.
xi
>
xi
{
alpha
:=
r
.
areaToAlpha
(
cover
*
256
*
2
)
alpha
:=
r
.
areaToAlpha
(
cover
*
64
*
2
)
if
alpha
!=
0
{
xi0
,
xi1
:=
xi
,
r
.
cell
[
c
]
.
xi
if
xi0
<
0
{
...
...
@@ -503,7 +519,7 @@ func (r *Rasterizer) Rasterize(p Painter) {
}
}
cover
+=
r
.
cell
[
c
]
.
cover
alpha
:=
r
.
areaToAlpha
(
cover
*
256
*
2
-
r
.
cell
[
c
]
.
area
)
alpha
:=
r
.
areaToAlpha
(
cover
*
64
*
2
-
r
.
cell
[
c
]
.
area
)
xi
=
r
.
cell
[
c
]
.
xi
+
1
if
alpha
!=
0
{
xi0
,
xi1
:=
r
.
cell
[
c
]
.
xi
,
xi
...
...
@@ -529,7 +545,7 @@ func (r *Rasterizer) Rasterize(p Painter) {
// Clear cancels any previous calls to r.Start or r.AddXxx.
func
(
r
*
Rasterizer
)
Clear
()
{
r
.
a
=
Point
{}
r
.
a
=
fixed
.
Point26_6
{}
r
.
xi
=
0
r
.
yi
=
0
r
.
area
=
0
...
...
@@ -541,7 +557,7 @@ func (r *Rasterizer) Clear() {
}
// SetBounds sets the maximum width and height of the rasterized image and
// calls Clear. The width and height are in pixels, not
Fix32
units.
// calls Clear. The width and height are in pixels, not
fixed.Int26_6
units.
func
(
r
*
Rasterizer
)
SetBounds
(
width
,
height
int
)
{
if
width
<
0
{
width
=
0
...
...
@@ -549,10 +565,9 @@ func (r *Rasterizer) SetBounds(width, height int) {
if
height
<
0
{
height
=
0
}
// Use the same ssN heuristic as the C Freetype implementation.
// The C implementation uses the values 32, 16, but those are in
// 26.6 fixed point units, and we use 24.8 fixed point everywhere.
ss2
,
ss3
:=
128
,
64
// Use the same ssN heuristic as the C Freetype (version 2.4.0)
// implementation.
ss2
,
ss3
:=
32
,
16
if
width
>
24
||
height
>
24
{
ss2
,
ss3
=
2
*
ss2
,
2
*
ss3
if
width
>
120
||
height
>
120
{
...
...
raster/stroke.go
浏览文件 @
856a70c3
...
...
@@ -5,21 +5,25 @@
package
raster
import
(
"golang.org/x/image/math/fixed"
)
// Two points are considered practically equal if the square of the distance
// between them is less than one quarter (i.e. 1
6384 / 65536 in Fix64
).
const
epsilon
=
16384
// between them is less than one quarter (i.e. 1
024 / 4096
).
const
epsilon
=
fixed
.
Int52_12
(
1024
)
// A Capper signifies how to begin or end a stroked path.
type
Capper
interface
{
// Cap adds a cap to p given a pivot point and the normal vector of a
// terminal segment. The normal's length is half of the stroke width.
Cap
(
p
Adder
,
halfWidth
Fix32
,
pivot
,
n1
Point
)
Cap
(
p
Adder
,
halfWidth
fixed
.
Int26_6
,
pivot
,
n1
fixed
.
Point26_6
)
}
// The CapperFunc type adapts an ordinary function to be a Capper.
type
CapperFunc
func
(
Adder
,
Fix32
,
Point
,
Point
)
type
CapperFunc
func
(
Adder
,
fixed
.
Int26_6
,
fixed
.
Point26_6
,
fixed
.
Point26_6
)
func
(
f
CapperFunc
)
Cap
(
p
Adder
,
halfWidth
Fix32
,
pivot
,
n1
Point
)
{
func
(
f
CapperFunc
)
Cap
(
p
Adder
,
halfWidth
fixed
.
Int26_6
,
pivot
,
n1
fixed
.
Point26_6
)
{
f
(
p
,
halfWidth
,
pivot
,
n1
)
}
...
...
@@ -28,20 +32,20 @@ type Joiner interface {
// Join adds a join to the two sides of a stroked path given a pivot
// point and the normal vectors of the trailing and leading segments.
// Both normals have length equal to half of the stroke width.
Join
(
lhs
,
rhs
Adder
,
halfWidth
Fix32
,
pivot
,
n0
,
n1
Point
)
Join
(
lhs
,
rhs
Adder
,
halfWidth
fixed
.
Int26_6
,
pivot
,
n0
,
n1
fixed
.
Point26_6
)
}
// The JoinerFunc type adapts an ordinary function to be a Joiner.
type
JoinerFunc
func
(
lhs
,
rhs
Adder
,
halfWidth
Fix32
,
pivot
,
n0
,
n1
Point
)
type
JoinerFunc
func
(
lhs
,
rhs
Adder
,
halfWidth
fixed
.
Int26_6
,
pivot
,
n0
,
n1
fixed
.
Point26_6
)
func
(
f
JoinerFunc
)
Join
(
lhs
,
rhs
Adder
,
halfWidth
Fix32
,
pivot
,
n0
,
n1
Point
)
{
func
(
f
JoinerFunc
)
Join
(
lhs
,
rhs
Adder
,
halfWidth
fixed
.
Int26_6
,
pivot
,
n0
,
n1
fixed
.
Point26_6
)
{
f
(
lhs
,
rhs
,
halfWidth
,
pivot
,
n0
,
n1
)
}
// RoundCapper adds round caps to a stroked path.
var
RoundCapper
Capper
=
CapperFunc
(
roundCapper
)
func
roundCapper
(
p
Adder
,
halfWidth
Fix32
,
pivot
,
n1
Point
)
{
func
roundCapper
(
p
Adder
,
halfWidth
fixed
.
Int26_6
,
pivot
,
n1
fixed
.
Point26_6
)
{
// The cubic Bézier approximation to a circle involves the magic number
// (√2 - 1) * 4/3, which is approximately 141/256.
const
k
=
141
...
...
@@ -56,14 +60,14 @@ func roundCapper(p Adder, halfWidth Fix32, pivot, n1 Point) {
// ButtCapper adds butt caps to a stroked path.
var
ButtCapper
Capper
=
CapperFunc
(
buttCapper
)
func
buttCapper
(
p
Adder
,
halfWidth
Fix32
,
pivot
,
n1
Point
)
{
func
buttCapper
(
p
Adder
,
halfWidth
fixed
.
Int26_6
,
pivot
,
n1
fixed
.
Point26_6
)
{
p
.
Add1
(
pivot
.
Add
(
n1
))
}
// SquareCapper adds square caps to a stroked path.
var
SquareCapper
Capper
=
CapperFunc
(
squareCapper
)
func
squareCapper
(
p
Adder
,
halfWidth
Fix32
,
pivot
,
n1
Point
)
{
func
squareCapper
(
p
Adder
,
halfWidth
fixed
.
Int26_6
,
pivot
,
n1
fixed
.
Point26_6
)
{
e
:=
pRot90CCW
(
n1
)
side
:=
pivot
.
Add
(
e
)
p
.
Add1
(
side
.
Sub
(
n1
))
...
...
@@ -74,7 +78,7 @@ func squareCapper(p Adder, halfWidth Fix32, pivot, n1 Point) {
// RoundJoiner adds round joins to a stroked path.
var
RoundJoiner
Joiner
=
JoinerFunc
(
roundJoiner
)
func
roundJoiner
(
lhs
,
rhs
Adder
,
haflWidth
Fix32
,
pivot
,
n0
,
n1
Point
)
{
func
roundJoiner
(
lhs
,
rhs
Adder
,
haflWidth
fixed
.
Int26_6
,
pivot
,
n0
,
n1
fixed
.
Point26_6
)
{
dot
:=
pDot
(
pRot90CW
(
n0
),
n1
)
if
dot
>=
0
{
addArc
(
lhs
,
pivot
,
n0
,
n1
)
...
...
@@ -88,7 +92,7 @@ func roundJoiner(lhs, rhs Adder, haflWidth Fix32, pivot, n0, n1 Point) {
// BevelJoiner adds bevel joins to a stroked path.
var
BevelJoiner
Joiner
=
JoinerFunc
(
bevelJoiner
)
func
bevelJoiner
(
lhs
,
rhs
Adder
,
haflWidth
Fix32
,
pivot
,
n0
,
n1
Point
)
{
func
bevelJoiner
(
lhs
,
rhs
Adder
,
haflWidth
fixed
.
Int26_6
,
pivot
,
n0
,
n1
fixed
.
Point26_6
)
{
lhs
.
Add1
(
pivot
.
Add
(
n1
))
rhs
.
Add1
(
pivot
.
Sub
(
n1
))
}
...
...
@@ -96,7 +100,7 @@ func bevelJoiner(lhs, rhs Adder, haflWidth Fix32, pivot, n0, n1 Point) {
// addArc adds a circular arc from pivot+n0 to pivot+n1 to p. The shorter of
// the two possible arcs is taken, i.e. the one spanning <= 180 degrees. The
// two vectors n0 and n1 must be of equal length.
func
addArc
(
p
Adder
,
pivot
,
n0
,
n1
Point
)
{
func
addArc
(
p
Adder
,
pivot
,
n0
,
n1
fixed
.
Point26_6
)
{
// r2 is the square of the length of n0.
r2
:=
pDot
(
n0
,
n0
)
if
r2
<
epsilon
{
...
...
@@ -109,7 +113,7 @@ func addArc(p Adder, pivot, n0, n1 Point) {
// control points {1, 0}, {1, tan(π/8)} and {1/√2, 1/√2} suitably scaled,
// rotated and translated. tan(π/8) is approximately 106/256.
const
tpo8
=
106
var
s
Point
var
s
fixed
.
Point26_6
// We determine which octant the angle between n0 and n1 is in via three
// dot products. m0, m1 and m2 are n0 rotated clockwise by 45, 90 and 135
// degrees.
...
...
@@ -178,28 +182,28 @@ func addArc(p Adder, pivot, n0, n1 Point) {
// d is the normalized dot product between s and n1. Since the angle ranges
// between 0 and 45 degrees then d ranges between 256/256 and 181/256.
d
:=
256
*
pDot
(
s
,
n1
)
/
r2
multiple
:=
Fix32
(
150
-
22
*
(
d
-
181
)
/
(
256
-
181
))
multiple
:=
fixed
.
Int26_6
(
150
-
(
150
-
128
)
*
(
d
-
181
)
/
(
256
-
181
))
>>
2
p
.
Add2
(
pivot
.
Add
(
s
.
Add
(
n1
)
.
Mul
(
multiple
)),
pivot
.
Add
(
n1
))
}
// midpoint returns the midpoint of two Points.
func
midpoint
(
a
,
b
Point
)
Point
{
return
Point
{(
a
.
X
+
b
.
X
)
/
2
,
(
a
.
Y
+
b
.
Y
)
/
2
}
func
midpoint
(
a
,
b
fixed
.
Point26_6
)
fixed
.
Point26_6
{
return
fixed
.
Point26_6
{(
a
.
X
+
b
.
X
)
/
2
,
(
a
.
Y
+
b
.
Y
)
/
2
}
}
// angleGreaterThan45 returns whether the angle between two vectors is more
// than 45 degrees.
func
angleGreaterThan45
(
v0
,
v1
Point
)
bool
{
func
angleGreaterThan45
(
v0
,
v1
fixed
.
Point26_6
)
bool
{
v
:=
pRot45CCW
(
v0
)
return
pDot
(
v
,
v1
)
<
0
||
pDot
(
pRot90CW
(
v
),
v1
)
<
0
}
// interpolate returns the point (1-t)*a + t*b.
func
interpolate
(
a
,
b
Point
,
t
Fix64
)
Point
{
s
:=
1
<<
1
6
-
t
x
:=
s
*
Fix64
(
a
.
X
)
+
t
*
Fix64
(
b
.
X
)
y
:=
s
*
Fix64
(
a
.
Y
)
+
t
*
Fix64
(
b
.
Y
)
return
Point
{
Fix32
(
x
>>
16
),
Fix32
(
y
>>
16
)}
func
interpolate
(
a
,
b
fixed
.
Point26_6
,
t
fixed
.
Int52_12
)
fixed
.
Point26_6
{
s
:=
1
<<
1
2
-
t
x
:=
s
*
fixed
.
Int52_12
(
a
.
X
)
+
t
*
fixed
.
Int52_12
(
b
.
X
)
y
:=
s
*
fixed
.
Int52_12
(
a
.
Y
)
+
t
*
fixed
.
Int52_12
(
b
.
Y
)
return
fixed
.
Point26_6
{
fixed
.
Int26_6
(
x
>>
12
),
fixed
.
Int26_6
(
y
>>
12
)}
}
// curviest2 returns the value of t for which the quadratic parametric curve
...
...
@@ -216,15 +220,15 @@ func interpolate(a, b Point, t Fix64) Point {
// (x′²+y′²) is extreme. The first order condition is that
// 2*x′*x″+2*y′*y″ = 0, or (dx+ex*t)*ex + (dy+ey*t)*ey = 0.
// Solving for t gives t = -(dx*ex+dy*ey) / (ex*ex+ey*ey).
func
curviest2
(
a
,
b
,
c
Point
)
Fix64
{
func
curviest2
(
a
,
b
,
c
fixed
.
Point26_6
)
fixed
.
Int52_12
{
dx
:=
int64
(
b
.
X
-
a
.
X
)
dy
:=
int64
(
b
.
Y
-
a
.
Y
)
ex
:=
int64
(
c
.
X
-
2
*
b
.
X
+
a
.
X
)
ey
:=
int64
(
c
.
Y
-
2
*
b
.
Y
+
a
.
Y
)
if
ex
==
0
&&
ey
==
0
{
return
3276
8
return
204
8
}
return
Fix64
(
-
6553
6
*
(
dx
*
ex
+
dy
*
ey
)
/
(
ex
*
ex
+
ey
*
ey
))
return
fixed
.
Int52_12
(
-
409
6
*
(
dx
*
ex
+
dy
*
ey
)
/
(
ex
*
ex
+
ey
*
ey
))
}
// A stroker holds state for stroking a path.
...
...
@@ -232,7 +236,7 @@ type stroker struct {
// p is the destination that records the stroked path.
p
Adder
// u is the half-width of the stroke.
u
Fix32
u
fixed
.
Int26_6
// cr and jr specify how to end and connect path segments.
cr
Capper
jr
Joiner
...
...
@@ -242,19 +246,19 @@ type stroker struct {
r
Path
// a is the most recent segment point. anorm is the segment normal of
// length u at that point.
a
,
anorm
Point
a
,
anorm
fixed
.
Point26_6
}
// addNonCurvy2 adds a quadratic segment to the stroker, where the segment
// defined by (k.a, b, c) achieves maximum curvature at either k.a or c.
func
(
k
*
stroker
)
addNonCurvy2
(
b
,
c
Point
)
{
func
(
k
*
stroker
)
addNonCurvy2
(
b
,
c
fixed
.
Point26_6
)
{
// We repeatedly divide the segment at its middle until it is straight
// enough to approximate the stroke by just translating the control points.
// ds and ps are stacks of depths and points. t is the top of the stack.
const
maxDepth
=
5
var
(
ds
[
maxDepth
+
1
]
int
ps
[
2
*
maxDepth
+
3
]
Point
ps
[
2
*
maxDepth
+
3
]
fixed
.
Point26_6
t
int
)
// Initially the ps stack has one quadratic segment of depth zero.
...
...
@@ -263,7 +267,7 @@ func (k *stroker) addNonCurvy2(b, c Point) {
ps
[
1
]
=
b
ps
[
0
]
=
c
anorm
:=
k
.
anorm
var
cnorm
Point
var
cnorm
fixed
.
Point26_6
for
{
depth
:=
ds
[
t
]
...
...
@@ -272,8 +276,8 @@ func (k *stroker) addNonCurvy2(b, c Point) {
c
:=
ps
[
2
*
t
+
0
]
ab
:=
b
.
Sub
(
a
)
bc
:=
c
.
Sub
(
b
)
abIsSmall
:=
pDot
(
ab
,
ab
)
<
Fix64
(
1
<<
16
)
bcIsSmall
:=
pDot
(
bc
,
bc
)
<
Fix64
(
1
<<
16
)
abIsSmall
:=
pDot
(
ab
,
ab
)
<
fixed
.
Int52_12
(
1
<<
12
)
bcIsSmall
:=
pDot
(
bc
,
bc
)
<
fixed
.
Int52_12
(
1
<<
12
)
if
abIsSmall
&&
bcIsSmall
{
// Approximate the segment by a circular arc.
cnorm
=
pRot90CCW
(
pNorm
(
bc
,
k
.
u
))
...
...
@@ -310,7 +314,7 @@ func (k *stroker) addNonCurvy2(b, c Point) {
}
// Add1 adds a linear segment to the stroker.
func
(
k
*
stroker
)
Add1
(
b
Point
)
{
func
(
k
*
stroker
)
Add1
(
b
fixed
.
Point26_6
)
{
bnorm
:=
pRot90CCW
(
pNorm
(
b
.
Sub
(
k
.
a
),
k
.
u
))
if
len
(
k
.
r
)
==
0
{
k
.
p
.
Start
(
k
.
a
.
Add
(
bnorm
))
...
...
@@ -324,7 +328,7 @@ func (k *stroker) Add1(b Point) {
}
// Add2 adds a quadratic segment to the stroker.
func
(
k
*
stroker
)
Add2
(
b
,
c
Point
)
{
func
(
k
*
stroker
)
Add2
(
b
,
c
fixed
.
Point26_6
)
{
ab
:=
b
.
Sub
(
k
.
a
)
bc
:=
c
.
Sub
(
b
)
abnorm
:=
pRot90CCW
(
pNorm
(
ab
,
k
.
u
))
...
...
@@ -349,7 +353,7 @@ func (k *stroker) Add2(b, c Point) {
// The quadratic segment (k.a, b, c) has a point of maximum curvature.
// If this occurs at an end point, we process the segment as a whole.
t
:=
curviest2
(
k
.
a
,
b
,
c
)
if
t
<=
0
||
6553
6
<=
t
{
if
t
<=
0
||
409
6
<=
t
{
k
.
addNonCurvy2
(
b
,
c
)
return
}
...
...
@@ -364,7 +368,7 @@ func (k *stroker) Add2(b, c Point) {
// then the decomposition can become unstable, so we approximate the
// quadratic segment by two linear segments joined by an arc.
bcnorm
:=
pRot90CCW
(
pNorm
(
bc
,
k
.
u
))
if
pDot
(
abnorm
,
bcnorm
)
<
-
Fix64
(
k
.
u
)
*
Fix64
(
k
.
u
)
*
2047
/
2048
{
if
pDot
(
abnorm
,
bcnorm
)
<
-
fixed
.
Int52_12
(
k
.
u
)
*
fixed
.
Int52_12
(
k
.
u
)
*
2047
/
2048
{
pArc
:=
pDot
(
abnorm
,
bc
)
<
0
k
.
p
.
Add1
(
mabc
.
Add
(
abnorm
))
...
...
@@ -395,7 +399,7 @@ func (k *stroker) Add2(b, c Point) {
}
// Add3 adds a cubic segment to the stroker.
func
(
k
*
stroker
)
Add3
(
b
,
c
,
d
Point
)
{
func
(
k
*
stroker
)
Add3
(
b
,
c
,
d
fixed
.
Point26_6
)
{
panic
(
"freetype/raster: stroke unimplemented for cubic segments"
)
}
...
...
@@ -406,17 +410,26 @@ func (k *stroker) stroke(q Path) {
// path is accumulated in k.r. Once we've finished adding the LHS to k.p,
// we add the RHS in reverse order.
k
.
r
=
make
(
Path
,
0
,
len
(
q
))
k
.
a
=
Point
{
q
[
1
],
q
[
2
]}
k
.
a
=
fixed
.
Point26_6
{
q
[
1
],
q
[
2
]}
for
i
:=
4
;
i
<
len
(
q
);
{
switch
q
[
i
]
{
case
1
:
k
.
Add1
(
Point
{
q
[
i
+
1
],
q
[
i
+
2
]})
k
.
Add1
(
fixed
.
Point26_6
{
q
[
i
+
1
],
q
[
i
+
2
]},
)
i
+=
4
case
2
:
k
.
Add2
(
Point
{
q
[
i
+
1
],
q
[
i
+
2
]},
Point
{
q
[
i
+
3
],
q
[
i
+
4
]})
k
.
Add2
(
fixed
.
Point26_6
{
q
[
i
+
1
],
q
[
i
+
2
]},
fixed
.
Point26_6
{
q
[
i
+
3
],
q
[
i
+
4
]},
)
i
+=
6
case
3
:
k
.
Add3
(
Point
{
q
[
i
+
1
],
q
[
i
+
2
]},
Point
{
q
[
i
+
3
],
q
[
i
+
4
]},
Point
{
q
[
i
+
5
],
q
[
i
+
6
]})
k
.
Add3
(
fixed
.
Point26_6
{
q
[
i
+
1
],
q
[
i
+
2
]},
fixed
.
Point26_6
{
q
[
i
+
3
],
q
[
i
+
4
]},
fixed
.
Point26_6
{
q
[
i
+
5
],
q
[
i
+
6
]},
)
i
+=
8
default
:
panic
(
"freetype/raster: bad path"
)
...
...
@@ -430,13 +443,13 @@ func (k *stroker) stroke(q Path) {
k
.
cr
.
Cap
(
k
.
p
,
k
.
u
,
q
.
lastPoint
(),
pNeg
(
k
.
anorm
))
addPathReversed
(
k
.
p
,
k
.
r
)
pivot
:=
q
.
firstPoint
()
k
.
cr
.
Cap
(
k
.
p
,
k
.
u
,
pivot
,
pivot
.
Sub
(
Point
{
k
.
r
[
1
],
k
.
r
[
2
]}))
k
.
cr
.
Cap
(
k
.
p
,
k
.
u
,
pivot
,
pivot
.
Sub
(
fixed
.
Point26_6
{
k
.
r
[
1
],
k
.
r
[
2
]}))
}
// Stroke adds q stroked with the given width to p. The result is typically
// self-intersecting and should be rasterized with UseNonZeroWinding.
// cr and jr may be nil, which defaults to a RoundCapper or RoundJoiner.
func
Stroke
(
p
Adder
,
q
Path
,
width
Fix32
,
cr
Capper
,
jr
Joiner
)
{
func
Stroke
(
p
Adder
,
q
Path
,
width
fixed
.
Int26_6
,
cr
Capper
,
jr
Joiner
)
{
if
len
(
q
)
==
0
{
return
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录