Skip to content

Commit e419d36

Browse files
committed
Use Scanx rasterizer, fixes #334
1 parent 59be125 commit e419d36

File tree

4 files changed

+55
-82
lines changed

4 files changed

+55
-82
lines changed

path.go

+4-5
Original file line numberDiff line numberDiff line change
@@ -2341,10 +2341,8 @@ func (p *Path) ToPDF() string {
23412341
return sb.String()[1:] // remove the first space
23422342
}
23432343

2344-
// ToRasterizer rasterizes the path using the given rasterizer and resolution.
2345-
func (p *Path) ToRasterizer(ras *vector.Rasterizer, resolution Resolution) {
2346-
// TODO: smoothen path using Ramer-...
2347-
2344+
// ToVectorRasterizer rasterizes the path to *vector.Rasterizer using the given rasterizer and resolution.
2345+
func (p *Path) ToVectorRasterizer(ras *vector.Rasterizer, resolution Resolution) {
23482346
dpmm := resolution.DPMM()
23492347
tolerance := PixelTolerance / dpmm // tolerance of 1/10 of a pixel
23502348
dy := float64(ras.Bounds().Size().Y)
@@ -2401,7 +2399,8 @@ func fixedPoint26_6(x, y float64) fixed.Point26_6 {
24012399
}
24022400
}
24032401

2404-
func (p *Path) ToScanx(ras *scanx.Scanner, dy float64, resolution Resolution) {
2402+
// ToScanxScanner rasterizes the path to *scanx.Scanner using the given rasterizer, height, and resolution.
2403+
func (p *Path) ToScanxScanner(ras *scanx.Scanner, dy float64, resolution Resolution) {
24052404
dpmm := resolution.DPMM()
24062405
tolerance := PixelTolerance / dpmm // tolerance of 1/10 of a pixel
24072406
for i := 0; i < len(p.d); {

path_test.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -950,7 +950,7 @@ func TestPathLengthParametrization(t *testing.T) {
950950
}
951951

952952
func BenchmarkToRasterizer(b *testing.B) {
953-
res := DPMM(0.5)
953+
res := DPMM(10.0)
954954
data, err := os.ReadFile("resources/netherlands.path")
955955
if err != nil {
956956
panic(err)
@@ -969,7 +969,7 @@ func BenchmarkToRasterizer(b *testing.B) {
969969
ras.Reset(int(w), int(h))
970970

971971
src := image.NewUniform(image.Black)
972-
p.ToRasterizer(ras, res)
972+
p.ToVectorRasterizer(ras, res)
973973
ras.Draw(img, rect, src, image.Point{0, 0})
974974

975975
f, _ := os.Create("ToRasterizer.png")
@@ -979,13 +979,13 @@ func BenchmarkToRasterizer(b *testing.B) {
979979
b.ResetTimer()
980980
for i := 0; i < b.N; i++ {
981981
ras.Reset(int(w), int(h))
982-
p.ToRasterizer(ras, res)
982+
p.ToVectorRasterizer(ras, res)
983983
ras.Draw(img, rect, src, image.Point{0, 0})
984984
}
985985
}
986986

987987
func BenchmarkToScanx(b *testing.B) {
988-
res := DPMM(0.5)
988+
res := DPMM(10.0)
989989
data, err := os.ReadFile("resources/netherlands.path")
990990
if err != nil {
991991
panic(err)
@@ -1003,7 +1003,7 @@ func BenchmarkToScanx(b *testing.B) {
10031003
scanner := scanx.NewScanner(spanner, int(w), int(h))
10041004

10051005
scanner.SetColor(image.Black)
1006-
p.ToScanx(scanner, h, res)
1006+
p.ToScanxScanner(scanner, h, res)
10071007
scanner.Draw()
10081008

10091009
f, _ := os.Create("ToScanx.png")
@@ -1013,7 +1013,7 @@ func BenchmarkToScanx(b *testing.B) {
10131013
b.ResetTimer()
10141014
for i := 0; i < b.N; i++ {
10151015
scanner.Clear()
1016-
p.ToScanx(scanner, h, res)
1016+
p.ToScanxScanner(scanner, h, res)
10171017
scanner.Draw()
10181018
}
10191019
}

patterns.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
type Pattern interface {
99
SetView(Matrix) Pattern
1010
SetColorSpace(ColorSpace) Pattern
11-
ClipTo(Renderer, *Path)
11+
RenderTo(Renderer, *Path)
1212
}
1313

1414
//type CanvasPattern struct {
@@ -126,8 +126,8 @@ func (p *HatchPattern) Tile(clip *Path) *Path {
126126
return hatch
127127
}
128128

129-
// ClipTo tiles the hatch pattern to the clipping path and renders it to the renderer.
130-
func (p *HatchPattern) ClipTo(r Renderer, clip *Path) {
129+
// RenderTo tiles the hatch pattern to the clipping path and renders it to the renderer.
130+
func (p *HatchPattern) RenderTo(r Renderer, clip *Path) {
131131
hatch := p.Tile(clip)
132132
r.RenderPath(hatch, Style{Fill: p.Fill}, Identity)
133133
}

renderers/rasterizer/rasterizer.go

+42-68
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ package rasterizer
22

33
import (
44
"image"
5+
"image/color"
56
"math"
67

8+
"github.com/srwiley/rasterx"
9+
"github.com/srwiley/scanx"
710
"github.com/tdewolff/canvas"
811
"golang.org/x/image/draw"
912
"golang.org/x/image/math/f64"
10-
"golang.org/x/image/vector"
1113
)
1214

1315
// TODO: add ASM optimized version for NRGBA images, since those are much faster to write as PNG
@@ -27,7 +29,8 @@ type Rasterizer struct {
2729
resolution canvas.Resolution
2830
colorSpace canvas.ColorSpace
2931

30-
ras *vector.Rasterizer // reuse
32+
spanner *scanx.ImgSpanner
33+
scanner *scanx.Scanner
3134
}
3235

3336
// New returns a renderer that draws to a rasterized image. The final width and height of the image is the width and height (mm) multiplied by the resolution (px/mm), thus a higher resolution results in larger images. By default the linear color space is used, which assumes input and output colors are in linearRGB. If the sRGB color space is used for drawing with an average of gamma=2.2, the input and output colors are assumed to be in sRGB (a common assumption) and blending happens in linearRGB. Be aware that for text this results in thin stems for black-on-white (but wide stems for white-on-black).
@@ -48,12 +51,14 @@ func FromImage(img draw.Image, resolution canvas.Resolution, colorSpace canvas.C
4851
if colorSpace == nil {
4952
colorSpace = canvas.DefaultColorSpace
5053
}
54+
spanner := scanx.NewImgSpanner(img)
5155
return &Rasterizer{
5256
Image: img,
5357
resolution: resolution,
5458
colorSpace: colorSpace,
5559

56-
ras: &vector.Rasterizer{},
60+
spanner: spanner,
61+
scanner: scanx.NewScanner(spanner, bounds.Dx(), bounds.Dy()),
5762
}
5863
}
5964

@@ -95,90 +100,59 @@ func (r *Rasterizer) RenderPath(path *canvas.Path, style canvas.Style, m canvas.
95100
}
96101
}
97102

98-
padding := 2
99-
dx, dy := 0, 0
100-
origin := r.Bounds().Min
101103
size := r.Bounds().Size()
102-
dpmm := r.resolution.DPMM()
103-
x := int(bounds.X0*dpmm) - padding
104-
y := size.Y - int((bounds.Y1)*dpmm) - padding
105-
w := int(bounds.W()*dpmm) + 2*padding
106-
h := int(bounds.H()*dpmm) + 2*padding
107-
if (x+w <= origin.X || origin.X+size.X <= x) && (y+h <= origin.Y || origin.Y+size.Y <= y) {
108-
return // outside canvas
109-
}
110-
111-
zp := image.Point{x, y}
112-
if x < origin.X {
113-
dx = -x
114-
x = origin.X
115-
}
116-
if y < origin.Y {
117-
dy = -y
118-
y = origin.Y
119-
}
120-
if origin.X+size.X <= x+w {
121-
w = origin.X + size.X - x
122-
}
123-
if origin.Y+size.Y <= y+h {
124-
h = origin.Y + size.Y - y
125-
}
126-
if w <= 0 || h <= 0 {
127-
return // has no size
128-
}
129-
130104
if style.HasFill() {
131105
if style.Fill.IsPattern() {
132106
if hatch, ok := style.Fill.Pattern.(*canvas.HatchPattern); ok {
133107
style.Fill = hatch.Fill
134108
fill = hatch.Tile(fill)
109+
} else {
110+
pattern := style.Fill.Pattern.SetColorSpace(r.colorSpace)
111+
pattern.RenderTo(r, fill)
135112
}
136113
}
137-
138-
var src image.Image
139-
if style.Fill.IsColor() {
140-
c := r.colorSpace.ToLinear(style.Fill.Color)
141-
src = image.NewUniform(r.Image.ColorModel().Convert(c))
142-
} else if style.Fill.IsGradient() {
114+
if style.Fill.IsGradient() {
143115
gradient := style.Fill.Gradient.SetColorSpace(r.colorSpace)
144-
src = NewGradientImage(gradient, zp, size, r.resolution)
145-
// TODO: convert to dst color model
146-
} else if style.Fill.IsPattern() {
147-
pattern := style.Fill.Pattern.SetColorSpace(r.colorSpace)
148-
pattern.ClipTo(r, fill)
149-
}
150-
if src != nil {
151-
r.ras.Reset(w, h)
152-
fill = fill.Translate(-float64(x)/dpmm, -float64(size.Y-y-h)/dpmm)
153-
fill.ToRasterizer(r.ras, r.resolution)
154-
r.ras.Draw(r.Image, image.Rect(x, y, x+w, y+h), src, image.Point{dx, dy})
116+
r.scanner.Clear()
117+
r.scanner.SetColor(rasterx.ColorFunc(func(x, y int) color.Color {
118+
// TODO: convert to dst color model
119+
return gradient.At(float64(x), float64(y))
120+
}))
121+
fill.ToScanxScanner(r.scanner, float64(size.Y), r.resolution)
122+
r.scanner.Draw()
123+
} else if style.Fill.IsColor() {
124+
c := r.colorSpace.ToLinear(style.Fill.Color)
125+
r.scanner.Clear()
126+
r.scanner.SetColor(color.Color(r.Image.ColorModel().Convert(c)))
127+
fill.ToScanxScanner(r.scanner, float64(size.Y), r.resolution)
128+
r.scanner.Draw()
155129
}
156130
}
157131
if style.HasStroke() {
158132
if style.Stroke.IsPattern() {
159133
if hatch, ok := style.Stroke.Pattern.(*canvas.HatchPattern); ok {
160134
style.Stroke = hatch.Fill
161135
stroke = hatch.Tile(stroke)
136+
} else {
137+
pattern := style.Stroke.Pattern.SetColorSpace(r.colorSpace)
138+
pattern.RenderTo(r, stroke)
162139
}
163140
}
164-
165-
var src image.Image
166-
if style.Stroke.IsColor() {
167-
c := r.colorSpace.ToLinear(style.Stroke.Color)
168-
src = image.NewUniform(r.Image.ColorModel().Convert(c))
169-
} else if style.Stroke.IsGradient() {
141+
if style.Stroke.IsGradient() {
170142
gradient := style.Stroke.Gradient.SetColorSpace(r.colorSpace)
171-
src = NewGradientImage(gradient, zp, size, r.resolution)
172-
// TODO: convert to dst color model
173-
} else if style.Stroke.IsPattern() {
174-
pattern := style.Stroke.Pattern.SetColorSpace(r.colorSpace)
175-
pattern.ClipTo(r, stroke)
176-
}
177-
if src != nil {
178-
r.ras.Reset(w, h)
179-
stroke = stroke.Translate(-float64(x)/dpmm, -float64(size.Y-y-h)/dpmm)
180-
stroke.ToRasterizer(r.ras, r.resolution)
181-
r.ras.Draw(r.Image, image.Rect(x, y, x+w, y+h), src, image.Point{dx, dy})
143+
r.scanner.Clear()
144+
r.scanner.SetColor(rasterx.ColorFunc(func(x, y int) color.Color {
145+
// TODO: convert to dst color model
146+
return gradient.At(float64(x), float64(y))
147+
}))
148+
stroke.ToScanxScanner(r.scanner, float64(size.Y), r.resolution)
149+
r.scanner.Draw()
150+
} else if style.Stroke.IsColor() {
151+
c := r.colorSpace.ToLinear(style.Stroke.Color)
152+
r.scanner.Clear()
153+
r.scanner.SetColor(color.Color(r.Image.ColorModel().Convert(c)))
154+
stroke.ToScanxScanner(r.scanner, float64(size.Y), r.resolution)
155+
r.scanner.Draw()
182156
}
183157
}
184158
}

0 commit comments

Comments
 (0)