git.lucas.co / go_mono
git clone https://git.lucas.co/go_mono.git

vector/vector.go (14.3K)

  1 // Copyright 2016 The Go Authors. All rights reserved.
  2 // Use of this source code is governed by a BSD-style
  3 // license that can be found in the LICENSE file.
  4 
  5 //go:generate go run gen.go
  6 //go:generate asmfmt -w acc_amd64.s
  7 
  8 // asmfmt is https://github.com/klauspost/asmfmt
  9 
 10 // Package vector provides a rasterizer for 2-D vector graphics.
 11 package vector // import "golang.org/x/image/vector"
 12 
 13 // The rasterizer's design follows
 14 // https://medium.com/@raphlinus/inside-the-fastest-font-renderer-in-the-world-75ae5270c445
 15 //
 16 // Proof of concept code is in
 17 // https://github.com/google/font-go
 18 //
 19 // See also:
 20 // http://nothings.org/gamedev/rasterize/
 21 // http://projects.tuxee.net/cl-vectors/section-the-cl-aa-algorithm
 22 // https://people.gnome.org/~mathieu/libart/internals.html#INTERNALS-SCANLINE
 23 
 24 import (
 25 	"image"
 26 	"image/color"
 27 	"image/draw"
 28 	"math"
 29 )
 30 
 31 // floatingPointMathThreshold is the width or height above which the rasterizer
 32 // chooses to used floating point math instead of fixed point math.
 33 //
 34 // Both implementations of line segmentation rasterization (see raster_fixed.go
 35 // and raster_floating.go) implement the same algorithm (in ideal, infinite
 36 // precision math) but they perform differently in practice. The fixed point
 37 // math version is roughly 1.25x faster (on GOARCH=amd64) on the benchmarks,
 38 // but at sufficiently large scales, the computations will overflow and hence
 39 // show rendering artifacts. The floating point math version has more
 40 // consistent quality over larger scales, but it is significantly slower.
 41 //
 42 // This constant determines when to use the faster implementation and when to
 43 // use the better quality implementation.
 44 //
 45 // The rationale for this particular value is that TestRasterizePolygon in
 46 // vector_test.go checks the rendering quality of polygon edges at various
 47 // angles, inscribed in a circle of diameter 512. It may be that a higher value
 48 // would still produce acceptable quality, but 512 seems to work.
 49 const floatingPointMathThreshold = 512
 50 
 51 func lerp(t, px, py, qx, qy float32) (x, y float32) {
 52 	return px + t*(qx-px), py + t*(qy-py)
 53 }
 54 
 55 func clamp(i, width int32) uint {
 56 	if i < 0 {
 57 		return 0
 58 	}
 59 	if i < width {
 60 		return uint(i)
 61 	}
 62 	return uint(width)
 63 }
 64 
 65 // NewRasterizer returns a new Rasterizer whose rendered mask image is bounded
 66 // by the given width and height.
 67 func NewRasterizer(w, h int) *Rasterizer {
 68 	z := &Rasterizer{}
 69 	z.Reset(w, h)
 70 	return z
 71 }
 72 
 73 // Raster is a 2-D vector graphics rasterizer.
 74 //
 75 // The zero value is usable, in that it is a Rasterizer whose rendered mask
 76 // image has zero width and zero height. Call Reset to change its bounds.
 77 type Rasterizer struct {
 78 	// bufXxx are buffers of float32 or uint32 values, holding either the
 79 	// individual or cumulative area values.
 80 	//
 81 	// We don't actually need both values at any given time, and to conserve
 82 	// memory, the integration of the individual to the cumulative could modify
 83 	// the buffer in place. In other words, we could use a single buffer, say
 84 	// of type []uint32, and add some math.Float32bits and math.Float32frombits
 85 	// calls to satisfy the compiler's type checking. As of Go 1.7, though,
 86 	// there is a performance penalty between:
 87 	//	bufF32[i] += x
 88 	// and
 89 	//	bufU32[i] = math.Float32bits(x + math.Float32frombits(bufU32[i]))
 90 	//
 91 	// See golang.org/issue/17220 for some discussion.
 92 	bufF32 []float32
 93 	bufU32 []uint32
 94 
 95 	useFloatingPointMath bool
 96 
 97 	size   image.Point
 98 	firstX float32
 99 	firstY float32
100 	penX   float32
101 	penY   float32
102 
103 	// DrawOp is the operator used for the Draw method.
104 	//
105 	// The zero value is draw.Over.
106 	DrawOp draw.Op
107 
108 	// TODO: an exported field equivalent to the mask point in the
109 	// draw.DrawMask function in the stdlib image/draw package?
110 }
111 
112 // Reset resets a Rasterizer as if it was just returned by NewRasterizer.
113 //
114 // This includes setting z.DrawOp to draw.Over.
115 func (z *Rasterizer) Reset(w, h int) {
116 	z.size = image.Point{w, h}
117 	z.firstX = 0
118 	z.firstY = 0
119 	z.penX = 0
120 	z.penY = 0
121 	z.DrawOp = draw.Over
122 
123 	z.setUseFloatingPointMath(w > floatingPointMathThreshold || h > floatingPointMathThreshold)
124 }
125 
126 func (z *Rasterizer) setUseFloatingPointMath(b bool) {
127 	z.useFloatingPointMath = b
128 
129 	// Make z.bufF32 or z.bufU32 large enough to hold width * height samples.
130 	if z.useFloatingPointMath {
131 		if n := z.size.X * z.size.Y; n > cap(z.bufF32) {
132 			z.bufF32 = make([]float32, n)
133 		} else {
134 			z.bufF32 = z.bufF32[:n]
135 			for i := range z.bufF32 {
136 				z.bufF32[i] = 0
137 			}
138 		}
139 	} else {
140 		if n := z.size.X * z.size.Y; n > cap(z.bufU32) {
141 			z.bufU32 = make([]uint32, n)
142 		} else {
143 			z.bufU32 = z.bufU32[:n]
144 			for i := range z.bufU32 {
145 				z.bufU32[i] = 0
146 			}
147 		}
148 	}
149 }
150 
151 // Size returns the width and height passed to NewRasterizer or Reset.
152 func (z *Rasterizer) Size() image.Point {
153 	return z.size
154 }
155 
156 // Bounds returns the rectangle from (0, 0) to the width and height passed to
157 // NewRasterizer or Reset.
158 func (z *Rasterizer) Bounds() image.Rectangle {
159 	return image.Rectangle{Max: z.size}
160 }
161 
162 // Pen returns the location of the path-drawing pen: the last argument to the
163 // most recent XxxTo call.
164 func (z *Rasterizer) Pen() (x, y float32) {
165 	return z.penX, z.penY
166 }
167 
168 // ClosePath closes the current path.
169 func (z *Rasterizer) ClosePath() {
170 	z.LineTo(z.firstX, z.firstY)
171 }
172 
173 // MoveTo starts a new path and moves the pen to (ax, ay).
174 //
175 // The coordinates are allowed to be out of the Rasterizer's bounds.
176 func (z *Rasterizer) MoveTo(ax, ay float32) {
177 	z.firstX = ax
178 	z.firstY = ay
179 	z.penX = ax
180 	z.penY = ay
181 }
182 
183 // LineTo adds a line segment, from the pen to (bx, by), and moves the pen to
184 // (bx, by).
185 //
186 // The coordinates are allowed to be out of the Rasterizer's bounds.
187 func (z *Rasterizer) LineTo(bx, by float32) {
188 	if z.useFloatingPointMath {
189 		z.floatingLineTo(bx, by)
190 	} else {
191 		z.fixedLineTo(bx, by)
192 	}
193 }
194 
195 // QuadTo adds a quadratic Bézier segment, from the pen via (bx, by) to (cx,
196 // cy), and moves the pen to (cx, cy).
197 //
198 // The coordinates are allowed to be out of the Rasterizer's bounds.
199 func (z *Rasterizer) QuadTo(bx, by, cx, cy float32) {
200 	ax, ay := z.penX, z.penY
201 	devsq := devSquared(ax, ay, bx, by, cx, cy)
202 	if devsq >= 0.333 {
203 		const tol = 3
204 		n := 1 + int(math.Sqrt(math.Sqrt(tol*float64(devsq))))
205 		t, nInv := float32(0), 1/float32(n)
206 		for i := 0; i < n-1; i++ {
207 			t += nInv
208 			abx, aby := lerp(t, ax, ay, bx, by)
209 			bcx, bcy := lerp(t, bx, by, cx, cy)
210 			z.LineTo(lerp(t, abx, aby, bcx, bcy))
211 		}
212 	}
213 	z.LineTo(cx, cy)
214 }
215 
216 // CubeTo adds a cubic Bézier segment, from the pen via (bx, by) and (cx, cy)
217 // to (dx, dy), and moves the pen to (dx, dy).
218 //
219 // The coordinates are allowed to be out of the Rasterizer's bounds.
220 func (z *Rasterizer) CubeTo(bx, by, cx, cy, dx, dy float32) {
221 	ax, ay := z.penX, z.penY
222 	devsq := devSquared(ax, ay, bx, by, dx, dy)
223 	if devsqAlt := devSquared(ax, ay, cx, cy, dx, dy); devsq < devsqAlt {
224 		devsq = devsqAlt
225 	}
226 	if devsq >= 0.333 {
227 		const tol = 3
228 		n := 1 + int(math.Sqrt(math.Sqrt(tol*float64(devsq))))
229 		t, nInv := float32(0), 1/float32(n)
230 		for i := 0; i < n-1; i++ {
231 			t += nInv
232 			abx, aby := lerp(t, ax, ay, bx, by)
233 			bcx, bcy := lerp(t, bx, by, cx, cy)
234 			cdx, cdy := lerp(t, cx, cy, dx, dy)
235 			abcx, abcy := lerp(t, abx, aby, bcx, bcy)
236 			bcdx, bcdy := lerp(t, bcx, bcy, cdx, cdy)
237 			z.LineTo(lerp(t, abcx, abcy, bcdx, bcdy))
238 		}
239 	}
240 	z.LineTo(dx, dy)
241 }
242 
243 // devSquared returns a measure of how curvy the sequence (ax, ay) to (bx, by)
244 // to (cx, cy) is. It determines how many line segments will approximate a
245 // Bézier curve segment.
246 //
247 // http://lists.nongnu.org/archive/html/freetype-devel/2016-08/msg00080.html
248 // gives the rationale for this evenly spaced heuristic instead of a recursive
249 // de Casteljau approach:
250 //
251 // The reason for the subdivision by n is that I expect the "flatness"
252 // computation to be semi-expensive (it's done once rather than on each
253 // potential subdivision) and also because you'll often get fewer subdivisions.
254 // Taking a circular arc as a simplifying assumption (i.e., a spherical cow),
255 // where I get n, a recursive approach would get 2^⌈lg n⌉, which, if I haven't
256 // made any horrible mistakes, is expected to be 33% more in the limit.
257 func devSquared(ax, ay, bx, by, cx, cy float32) float32 {
258 	devx := ax - 2*bx + cx
259 	devy := ay - 2*by + cy
260 	return devx*devx + devy*devy
261 }
262 
263 // Draw implements the Drawer interface from the standard library's image/draw
264 // package.
265 //
266 // The vector paths previously added via the XxxTo calls become the mask for
267 // drawing src onto dst.
268 func (z *Rasterizer) Draw(dst draw.Image, r image.Rectangle, src image.Image, sp image.Point) {
269 	// TODO: adjust r and sp (and mp?) if src.Bounds() doesn't contain
270 	// r.Add(sp.Sub(r.Min)).
271 
272 	if src, ok := src.(*image.Uniform); ok {
273 		srcR, srcG, srcB, srcA := src.RGBA()
274 		switch dst := dst.(type) {
275 		case *image.Alpha:
276 			// Fast path for glyph rendering.
277 			if srcA == 0xffff {
278 				if z.DrawOp == draw.Over {
279 					z.rasterizeDstAlphaSrcOpaqueOpOver(dst, r)
280 				} else {
281 					z.rasterizeDstAlphaSrcOpaqueOpSrc(dst, r)
282 				}
283 				return
284 			}
285 		case *image.RGBA:
286 			if z.DrawOp == draw.Over {
287 				z.rasterizeDstRGBASrcUniformOpOver(dst, r, srcR, srcG, srcB, srcA)
288 			} else {
289 				z.rasterizeDstRGBASrcUniformOpSrc(dst, r, srcR, srcG, srcB, srcA)
290 			}
291 			return
292 		}
293 	}
294 
295 	if z.DrawOp == draw.Over {
296 		z.rasterizeOpOver(dst, r, src, sp)
297 	} else {
298 		z.rasterizeOpSrc(dst, r, src, sp)
299 	}
300 }
301 
302 func (z *Rasterizer) accumulateMask() {
303 	if z.useFloatingPointMath {
304 		if n := z.size.X * z.size.Y; n > cap(z.bufU32) {
305 			z.bufU32 = make([]uint32, n)
306 		} else {
307 			z.bufU32 = z.bufU32[:n]
308 		}
309 		if haveAccumulateSIMD {
310 			floatingAccumulateMaskSIMD(z.bufU32, z.bufF32)
311 		} else {
312 			floatingAccumulateMask(z.bufU32, z.bufF32)
313 		}
314 	} else {
315 		if haveAccumulateSIMD {
316 			fixedAccumulateMaskSIMD(z.bufU32)
317 		} else {
318 			fixedAccumulateMask(z.bufU32)
319 		}
320 	}
321 }
322 
323 func (z *Rasterizer) rasterizeDstAlphaSrcOpaqueOpOver(dst *image.Alpha, r image.Rectangle) {
324 	// TODO: non-zero vs even-odd winding?
325 	if r == dst.Bounds() && r == z.Bounds() {
326 		// We bypass the z.accumulateMask step and convert straight from
327 		// z.bufF32 or z.bufU32 to dst.Pix.
328 		if z.useFloatingPointMath {
329 			if haveAccumulateSIMD {
330 				floatingAccumulateOpOverSIMD(dst.Pix, z.bufF32)
331 			} else {
332 				floatingAccumulateOpOver(dst.Pix, z.bufF32)
333 			}
334 		} else {
335 			if haveAccumulateSIMD {
336 				fixedAccumulateOpOverSIMD(dst.Pix, z.bufU32)
337 			} else {
338 				fixedAccumulateOpOver(dst.Pix, z.bufU32)
339 			}
340 		}
341 		return
342 	}
343 
344 	z.accumulateMask()
345 	pix := dst.Pix[dst.PixOffset(r.Min.X, r.Min.Y):]
346 	for y, y1 := 0, r.Max.Y-r.Min.Y; y < y1; y++ {
347 		for x, x1 := 0, r.Max.X-r.Min.X; x < x1; x++ {
348 			ma := z.bufU32[y*z.size.X+x]
349 			i := y*dst.Stride + x
350 
351 			// This formula is like rasterizeOpOver's, simplified for the
352 			// concrete dst type and opaque src assumption.
353 			a := 0xffff - ma
354 			pix[i] = uint8((uint32(pix[i])*0x101*a/0xffff + ma) >> 8)
355 		}
356 	}
357 }
358 
359 func (z *Rasterizer) rasterizeDstAlphaSrcOpaqueOpSrc(dst *image.Alpha, r image.Rectangle) {
360 	// TODO: non-zero vs even-odd winding?
361 	if r == dst.Bounds() && r == z.Bounds() {
362 		// We bypass the z.accumulateMask step and convert straight from
363 		// z.bufF32 or z.bufU32 to dst.Pix.
364 		if z.useFloatingPointMath {
365 			if haveAccumulateSIMD {
366 				floatingAccumulateOpSrcSIMD(dst.Pix, z.bufF32)
367 			} else {
368 				floatingAccumulateOpSrc(dst.Pix, z.bufF32)
369 			}
370 		} else {
371 			if haveAccumulateSIMD {
372 				fixedAccumulateOpSrcSIMD(dst.Pix, z.bufU32)
373 			} else {
374 				fixedAccumulateOpSrc(dst.Pix, z.bufU32)
375 			}
376 		}
377 		return
378 	}
379 
380 	z.accumulateMask()
381 	pix := dst.Pix[dst.PixOffset(r.Min.X, r.Min.Y):]
382 	for y, y1 := 0, r.Max.Y-r.Min.Y; y < y1; y++ {
383 		for x, x1 := 0, r.Max.X-r.Min.X; x < x1; x++ {
384 			ma := z.bufU32[y*z.size.X+x]
385 
386 			// This formula is like rasterizeOpSrc's, simplified for the
387 			// concrete dst type and opaque src assumption.
388 			pix[y*dst.Stride+x] = uint8(ma >> 8)
389 		}
390 	}
391 }
392 
393 func (z *Rasterizer) rasterizeDstRGBASrcUniformOpOver(dst *image.RGBA, r image.Rectangle, sr, sg, sb, sa uint32) {
394 	z.accumulateMask()
395 	pix := dst.Pix[dst.PixOffset(r.Min.X, r.Min.Y):]
396 	for y, y1 := 0, r.Max.Y-r.Min.Y; y < y1; y++ {
397 		for x, x1 := 0, r.Max.X-r.Min.X; x < x1; x++ {
398 			ma := z.bufU32[y*z.size.X+x]
399 
400 			// This formula is like rasterizeOpOver's, simplified for the
401 			// concrete dst type and uniform src assumption.
402 			a := 0xffff - (sa * ma / 0xffff)
403 			i := y*dst.Stride + 4*x
404 			pix[i+0] = uint8(((uint32(pix[i+0])*0x101*a + sr*ma) / 0xffff) >> 8)
405 			pix[i+1] = uint8(((uint32(pix[i+1])*0x101*a + sg*ma) / 0xffff) >> 8)
406 			pix[i+2] = uint8(((uint32(pix[i+2])*0x101*a + sb*ma) / 0xffff) >> 8)
407 			pix[i+3] = uint8(((uint32(pix[i+3])*0x101*a + sa*ma) / 0xffff) >> 8)
408 		}
409 	}
410 }
411 
412 func (z *Rasterizer) rasterizeDstRGBASrcUniformOpSrc(dst *image.RGBA, r image.Rectangle, sr, sg, sb, sa uint32) {
413 	z.accumulateMask()
414 	pix := dst.Pix[dst.PixOffset(r.Min.X, r.Min.Y):]
415 	for y, y1 := 0, r.Max.Y-r.Min.Y; y < y1; y++ {
416 		for x, x1 := 0, r.Max.X-r.Min.X; x < x1; x++ {
417 			ma := z.bufU32[y*z.size.X+x]
418 
419 			// This formula is like rasterizeOpSrc's, simplified for the
420 			// concrete dst type and uniform src assumption.
421 			i := y*dst.Stride + 4*x
422 			pix[i+0] = uint8((sr * ma / 0xffff) >> 8)
423 			pix[i+1] = uint8((sg * ma / 0xffff) >> 8)
424 			pix[i+2] = uint8((sb * ma / 0xffff) >> 8)
425 			pix[i+3] = uint8((sa * ma / 0xffff) >> 8)
426 		}
427 	}
428 }
429 
430 func (z *Rasterizer) rasterizeOpOver(dst draw.Image, r image.Rectangle, src image.Image, sp image.Point) {
431 	z.accumulateMask()
432 	out := color.RGBA64{}
433 	outc := color.Color(&out)
434 	for y, y1 := 0, r.Max.Y-r.Min.Y; y < y1; y++ {
435 		for x, x1 := 0, r.Max.X-r.Min.X; x < x1; x++ {
436 			sr, sg, sb, sa := src.At(sp.X+x, sp.Y+y).RGBA()
437 			ma := z.bufU32[y*z.size.X+x]
438 
439 			// This algorithm comes from the standard library's image/draw
440 			// package.
441 			dr, dg, db, da := dst.At(r.Min.X+x, r.Min.Y+y).RGBA()
442 			a := 0xffff - (sa * ma / 0xffff)
443 			out.R = uint16((dr*a + sr*ma) / 0xffff)
444 			out.G = uint16((dg*a + sg*ma) / 0xffff)
445 			out.B = uint16((db*a + sb*ma) / 0xffff)
446 			out.A = uint16((da*a + sa*ma) / 0xffff)
447 
448 			dst.Set(r.Min.X+x, r.Min.Y+y, outc)
449 		}
450 	}
451 }
452 
453 func (z *Rasterizer) rasterizeOpSrc(dst draw.Image, r image.Rectangle, src image.Image, sp image.Point) {
454 	z.accumulateMask()
455 	out := color.RGBA64{}
456 	outc := color.Color(&out)
457 	for y, y1 := 0, r.Max.Y-r.Min.Y; y < y1; y++ {
458 		for x, x1 := 0, r.Max.X-r.Min.X; x < x1; x++ {
459 			sr, sg, sb, sa := src.At(sp.X+x, sp.Y+y).RGBA()
460 			ma := z.bufU32[y*z.size.X+x]
461 
462 			// This algorithm comes from the standard library's image/draw
463 			// package.
464 			out.R = uint16(sr * ma / 0xffff)
465 			out.G = uint16(sg * ma / 0xffff)
466 			out.B = uint16(sb * ma / 0xffff)
467 			out.A = uint16(sa * ma / 0xffff)
468 
469 			dst.Set(r.Min.X+x, r.Min.Y+y, outc)
470 		}
471 	}
472 }