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

vector/raster_fixed.go (9.9K)

  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 package vector
  6 
  7 // This file contains a fixed point math implementation of the vector
  8 // graphics rasterizer.
  9 
 10 const (
 11 	// ϕ is the number of binary digits after the fixed point.
 12 	//
 13 	// For example, if ϕ == 10 (and int1ϕ is based on the int32 type) then we
 14 	// are using 22.10 fixed point math.
 15 	//
 16 	// When changing this number, also change the assembly code (search for ϕ
 17 	// in the .s files).
 18 	ϕ = 9
 19 
 20 	fxOne          int1ϕ = 1 << ϕ
 21 	fxOneAndAHalf  int1ϕ = 1<<ϕ + 1<<(ϕ-1)
 22 	fxOneMinusIota int1ϕ = 1<<ϕ - 1 // Used for rounding up.
 23 )
 24 
 25 // int1ϕ is a signed fixed-point number with 1*ϕ binary digits after the fixed
 26 // point.
 27 type int1ϕ int32
 28 
 29 // int2ϕ is a signed fixed-point number with 2*ϕ binary digits after the fixed
 30 // point.
 31 //
 32 // The Rasterizer's bufU32 field, nominally of type []uint32 (since that slice
 33 // is also used by other code), can be thought of as a []int2ϕ during the
 34 // fixedLineTo method. Lines of code that are actually like:
 35 //
 36 //	buf[i] += uint32(etc) // buf has type []uint32.
 37 //
 38 // can be thought of as
 39 //
 40 //	buf[i] += int2ϕ(etc)  // buf has type []int2ϕ.
 41 type int2ϕ int32
 42 
 43 func fixedFloor(x int1ϕ) int32 { return int32(x >> ϕ) }
 44 func fixedCeil(x int1ϕ) int32  { return int32((x + fxOneMinusIota) >> ϕ) }
 45 
 46 func (z *Rasterizer) fixedLineTo(bx, by float32) {
 47 	ax, ay := z.penX, z.penY
 48 	z.penX, z.penY = bx, by
 49 	dir := int1ϕ(1)
 50 	if ay > by {
 51 		dir, ax, ay, bx, by = -1, bx, by, ax, ay
 52 	}
 53 	// Horizontal line segments yield no change in coverage. Almost horizontal
 54 	// segments would yield some change, in ideal math, but the computation
 55 	// further below, involving 1 / (by - ay), is unstable in fixed point math,
 56 	// so we treat the segment as if it was perfectly horizontal.
 57 	if by-ay <= 0.000001 {
 58 		return
 59 	}
 60 	dxdy := (bx - ax) / (by - ay)
 61 
 62 	ayϕ := int1ϕ(ay * float32(fxOne))
 63 	byϕ := int1ϕ(by * float32(fxOne))
 64 
 65 	x := int1ϕ(ax * float32(fxOne))
 66 	y := fixedFloor(ayϕ)
 67 	yMax := fixedCeil(byϕ)
 68 	if yMax > int32(z.size.Y) {
 69 		yMax = int32(z.size.Y)
 70 	}
 71 	width := int32(z.size.X)
 72 
 73 	for ; y < yMax; y++ {
 74 		dy := min(int1ϕ(y+1)<<ϕ, byϕ) - max(int1ϕ(y)<<ϕ, ayϕ)
 75 		xNext := x + int1ϕ(float32(dy)*dxdy)
 76 		if y < 0 {
 77 			x = xNext
 78 			continue
 79 		}
 80 		buf := z.bufU32[y*width:]
 81 		d := dy * dir // d ranges up to ±1<<(1*ϕ).
 82 		x0, x1 := x, xNext
 83 		if x > xNext {
 84 			x0, x1 = x1, x0
 85 		}
 86 		x0i := fixedFloor(x0)
 87 		x0Floor := int1ϕ(x0i) << ϕ
 88 		x1i := fixedCeil(x1)
 89 		x1Ceil := int1ϕ(x1i) << ϕ
 90 
 91 		if x1i <= x0i+1 {
 92 			xmf := (x+xNext)>>1 - x0Floor
 93 			if i := clamp(x0i+0, width); i < uint(len(buf)) {
 94 				buf[i] += uint32(d * (fxOne - xmf))
 95 			}
 96 			if i := clamp(x0i+1, width); i < uint(len(buf)) {
 97 				buf[i] += uint32(d * xmf)
 98 			}
 99 		} else {
100 			oneOverS := x1 - x0
101 			twoOverS := 2 * oneOverS
102 			x0f := x0 - x0Floor
103 			oneMinusX0f := fxOne - x0f
104 			oneMinusX0fSquared := oneMinusX0f * oneMinusX0f
105 			x1f := x1 - x1Ceil + fxOne
106 			x1fSquared := x1f * x1f
107 
108 			// These next two variables are unused, as rounding errors are
109 			// minimized when we delay the division by oneOverS for as long as
110 			// possible. These lines of code (and the "In ideal math" comments
111 			// below) are commented out instead of deleted in order to aid the
112 			// comparison with the floating point version of the rasterizer.
113 			//
114 			// a0 := ((oneMinusX0f * oneMinusX0f) >> 1) / oneOverS
115 			// am := ((x1f * x1f) >> 1) / oneOverS
116 
117 			if i := clamp(x0i, width); i < uint(len(buf)) {
118 				// In ideal math: buf[i] += uint32(d * a0)
119 				D := oneMinusX0fSquared // D ranges up to ±1<<(2*ϕ).
120 				D *= d                  // D ranges up to ±1<<(3*ϕ).
121 				D /= twoOverS
122 				buf[i] += uint32(D)
123 			}
124 
125 			if x1i == x0i+2 {
126 				if i := clamp(x0i+1, width); i < uint(len(buf)) {
127 					// In ideal math: buf[i] += uint32(d * (fxOne - a0 - am))
128 					//
129 					// (x1i == x0i+2) and (twoOverS == 2 * (x1 - x0)) implies
130 					// that twoOverS ranges up to +1<<(1*ϕ+2).
131 					D := twoOverS<<ϕ - oneMinusX0fSquared - x1fSquared // D ranges up to ±1<<(2*ϕ+2).
132 					D *= d                                             // D ranges up to ±1<<(3*ϕ+2).
133 					D /= twoOverS
134 					buf[i] += uint32(D)
135 				}
136 			} else {
137 				// This is commented out for the same reason as a0 and am.
138 				//
139 				// a1 := ((fxOneAndAHalf - x0f) << ϕ) / oneOverS
140 
141 				if i := clamp(x0i+1, width); i < uint(len(buf)) {
142 					// In ideal math:
143 					//	buf[i] += uint32(d * (a1 - a0))
144 					// or equivalently (but better in non-ideal, integer math,
145 					// with respect to rounding errors),
146 					//	buf[i] += uint32(A * d / twoOverS)
147 					// where
148 					//	A = (a1 - a0) * twoOverS
149 					//	  = a1*twoOverS - a0*twoOverS
150 					// Noting that twoOverS/oneOverS equals 2, substituting for
151 					// a0 and then a1, given above, yields:
152 					//	A = a1*twoOverS - oneMinusX0fSquared
153 					//	  = (fxOneAndAHalf-x0f)<<(ϕ+1) - oneMinusX0fSquared
154 					//	  = fxOneAndAHalf<<(ϕ+1) - x0f<<(ϕ+1) - oneMinusX0fSquared
155 					//
156 					// This is a positive number minus two non-negative
157 					// numbers. For an upper bound on A, the positive number is
158 					//	P = fxOneAndAHalf<<(ϕ+1)
159 					//	  < (2*fxOne)<<(ϕ+1)
160 					//	  = fxOne<<(ϕ+2)
161 					//	  = 1<<(2*ϕ+2)
162 					//
163 					// For a lower bound on A, the two non-negative numbers are
164 					//	N = x0f<<(ϕ+1) + oneMinusX0fSquared
165 					//	  ≤ x0f<<(ϕ+1) + fxOne*fxOne
166 					//	  = x0f<<(ϕ+1) + 1<<(2*ϕ)
167 					//	  < x0f<<(ϕ+1) + 1<<(2*ϕ+1)
168 					//	  ≤ fxOne<<(ϕ+1) + 1<<(2*ϕ+1)
169 					//	  = 1<<(2*ϕ+1) + 1<<(2*ϕ+1)
170 					//	  = 1<<(2*ϕ+2)
171 					//
172 					// Thus, A ranges up to ±1<<(2*ϕ+2). It is possible to
173 					// derive a tighter bound, but this bound is sufficient to
174 					// reason about overflow.
175 					D := (fxOneAndAHalf-x0f)<<(ϕ+1) - oneMinusX0fSquared // D ranges up to ±1<<(2*ϕ+2).
176 					D *= d                                               // D ranges up to ±1<<(3*ϕ+2).
177 					D /= twoOverS
178 					buf[i] += uint32(D)
179 				}
180 				dTimesS := uint32((d << (2 * ϕ)) / oneOverS)
181 				for xi := x0i + 2; xi < x1i-1; xi++ {
182 					if i := clamp(xi, width); i < uint(len(buf)) {
183 						buf[i] += dTimesS
184 					}
185 				}
186 
187 				// This is commented out for the same reason as a0 and am.
188 				//
189 				// a2 := a1 + (int1ϕ(x1i-x0i-3)<<(2*ϕ))/oneOverS
190 
191 				if i := clamp(x1i-1, width); i < uint(len(buf)) {
192 					// In ideal math:
193 					//	buf[i] += uint32(d * (fxOne - a2 - am))
194 					// or equivalently (but better in non-ideal, integer math,
195 					// with respect to rounding errors),
196 					//	buf[i] += uint32(A * d / twoOverS)
197 					// where
198 					//	A = (fxOne - a2 - am) * twoOverS
199 					//	  = twoOverS<<ϕ - a2*twoOverS - am*twoOverS
200 					// Noting that twoOverS/oneOverS equals 2, substituting for
201 					// am and then a2, given above, yields:
202 					//	A = twoOverS<<ϕ - a2*twoOverS - x1f*x1f
203 					//	  = twoOverS<<ϕ - a1*twoOverS - (int1ϕ(x1i-x0i-3)<<(2*ϕ))*2 - x1f*x1f
204 					//	  = twoOverS<<ϕ - a1*twoOverS - int1ϕ(x1i-x0i-3)<<(2*ϕ+1) - x1f*x1f
205 					// Substituting for a1, given above, yields:
206 					//	A = twoOverS<<ϕ - ((fxOneAndAHalf-x0f)<<ϕ)*2 - int1ϕ(x1i-x0i-3)<<(2*ϕ+1) - x1f*x1f
207 					//	  = twoOverS<<ϕ - (fxOneAndAHalf-x0f)<<(ϕ+1) - int1ϕ(x1i-x0i-3)<<(2*ϕ+1) - x1f*x1f
208 					//	  = B<<ϕ - x1f*x1f
209 					// where
210 					//	B = twoOverS - (fxOneAndAHalf-x0f)<<1 - int1ϕ(x1i-x0i-3)<<(ϕ+1)
211 					//	  = (x1-x0)<<1 - (fxOneAndAHalf-x0f)<<1 - int1ϕ(x1i-x0i-3)<<(ϕ+1)
212 					//
213 					// Re-arranging the defintions given above:
214 					//	x0Floor := int1ϕ(x0i) << ϕ
215 					//	x0f := x0 - x0Floor
216 					//	x1Ceil := int1ϕ(x1i) << ϕ
217 					//	x1f := x1 - x1Ceil + fxOne
218 					// combined with fxOne = 1<<ϕ yields:
219 					//	x0 = x0f + int1ϕ(x0i)<<ϕ
220 					//	x1 = x1f + int1ϕ(x1i-1)<<ϕ
221 					// so that expanding (x1-x0) yields:
222 					//	B = (x1f-x0f + int1ϕ(x1i-x0i-1)<<ϕ)<<1 - (fxOneAndAHalf-x0f)<<1 - int1ϕ(x1i-x0i-3)<<(ϕ+1)
223 					//	  = (x1f-x0f)<<1 + int1ϕ(x1i-x0i-1)<<(ϕ+1) - (fxOneAndAHalf-x0f)<<1 - int1ϕ(x1i-x0i-3)<<(ϕ+1)
224 					// A large part of the second and fourth terms cancel:
225 					//	B = (x1f-x0f)<<1 - (fxOneAndAHalf-x0f)<<1 - int1ϕ(-2)<<(ϕ+1)
226 					//	  = (x1f-x0f)<<1 - (fxOneAndAHalf-x0f)<<1 + 1<<(ϕ+2)
227 					//	  = (x1f - fxOneAndAHalf)<<1 + 1<<(ϕ+2)
228 					// The first term, (x1f - fxOneAndAHalf)<<1, is a negative
229 					// number, bounded below by -fxOneAndAHalf<<1, which is
230 					// greater than -fxOne<<2, or -1<<(ϕ+2). Thus, B ranges up
231 					// to ±1<<(ϕ+2). One final simplification:
232 					//	B = x1f<<1 + (1<<(ϕ+2) - fxOneAndAHalf<<1)
233 					const C = 1<<(ϕ+2) - fxOneAndAHalf<<1
234 					D := x1f<<1 + C // D ranges up to ±1<<(1*ϕ+2).
235 					D <<= ϕ         // D ranges up to ±1<<(2*ϕ+2).
236 					D -= x1fSquared // D ranges up to ±1<<(2*ϕ+3).
237 					D *= d          // D ranges up to ±1<<(3*ϕ+3).
238 					D /= twoOverS
239 					buf[i] += uint32(D)
240 				}
241 			}
242 
243 			if i := clamp(x1i, width); i < uint(len(buf)) {
244 				// In ideal math: buf[i] += uint32(d * am)
245 				D := x1fSquared // D ranges up to ±1<<(2*ϕ).
246 				D *= d          // D ranges up to ±1<<(3*ϕ).
247 				D /= twoOverS
248 				buf[i] += uint32(D)
249 			}
250 		}
251 
252 		x = xNext
253 	}
254 }
255 
256 func fixedAccumulateOpOver(dst []uint8, src []uint32) {
257 	// Sanity check that len(dst) >= len(src).
258 	if len(dst) < len(src) {
259 		return
260 	}
261 
262 	acc := int2ϕ(0)
263 	for i, v := range src {
264 		acc += int2ϕ(v)
265 		a := acc
266 		if a < 0 {
267 			a = -a
268 		}
269 		a >>= 2*ϕ - 16
270 		if a > 0xffff {
271 			a = 0xffff
272 		}
273 		// This algorithm comes from the standard library's image/draw package.
274 		dstA := uint32(dst[i]) * 0x101
275 		maskA := uint32(a)
276 		outA := dstA*(0xffff-maskA)/0xffff + maskA
277 		dst[i] = uint8(outA >> 8)
278 	}
279 }
280 
281 func fixedAccumulateOpSrc(dst []uint8, src []uint32) {
282 	// Sanity check that len(dst) >= len(src).
283 	if len(dst) < len(src) {
284 		return
285 	}
286 
287 	acc := int2ϕ(0)
288 	for i, v := range src {
289 		acc += int2ϕ(v)
290 		a := acc
291 		if a < 0 {
292 			a = -a
293 		}
294 		a >>= 2*ϕ - 8
295 		if a > 0xff {
296 			a = 0xff
297 		}
298 		dst[i] = uint8(a)
299 	}
300 }
301 
302 func fixedAccumulateMask(buf []uint32) {
303 	acc := int2ϕ(0)
304 	for i, v := range buf {
305 		acc += int2ϕ(v)
306 		a := acc
307 		if a < 0 {
308 			a = -a
309 		}
310 		a >>= 2*ϕ - 16
311 		if a > 0xffff {
312 			a = 0xffff
313 		}
314 		buf[i] = uint32(a)
315 	}
316 }