Source File
raster_fixed.go
Belonging Package
golang.org/x/image/vector
// Copyright 2016 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.package vector// This file contains a fixed point math implementation of the vector// graphics rasterizer.const (// ϕ is the number of binary digits after the fixed point.//// For example, if ϕ == 10 (and int1ϕ is based on the int32 type) then we// are using 22.10 fixed point math.//// When changing this number, also change the assembly code (search for ϕ// in the .s files).ϕ = 9fxOne int1ϕ = 1 << ϕfxOneAndAHalf int1ϕ = 1<<ϕ + 1<<(ϕ-1)fxOneMinusIota int1ϕ = 1<<ϕ - 1 // Used for rounding up.)// int1ϕ is a signed fixed-point number with 1*ϕ binary digits after the fixed// point.type int1ϕ int32// int2ϕ is a signed fixed-point number with 2*ϕ binary digits after the fixed// point.//// The Rasterizer's bufU32 field, nominally of type []uint32 (since that slice// is also used by other code), can be thought of as a []int2ϕ during the// fixedLineTo method. Lines of code that are actually like://// buf[i] += uint32(etc) // buf has type []uint32.//// can be thought of as//// buf[i] += int2ϕ(etc) // buf has type []int2ϕ.type int2ϕ int32func fixedMax(, int1ϕ) int1ϕ {if > {return}return}func fixedMin(, int1ϕ) int1ϕ {if < {return}return}func fixedFloor( int1ϕ) int32 { return int32( >> ϕ) }func fixedCeil( int1ϕ) int32 { return int32(( + fxOneMinusIota) >> ϕ) }func ( *Rasterizer) (, float32) {, := .penX, .penY.penX, .penY = ,:= int1ϕ(1)if > {, , , , = -1, , , ,}// Horizontal line segments yield no change in coverage. Almost horizontal// segments would yield some change, in ideal math, but the computation// further below, involving 1 / (by - ay), is unstable in fixed point math,// so we treat the segment as if it was perfectly horizontal.if - <= 0.000001 {return}:= ( - ) / ( - ):= int1ϕ( * float32(fxOne)):= int1ϕ( * float32(fxOne)):= int1ϕ( * float32(fxOne)):= fixedFloor():= fixedCeil()if > int32(.size.Y) {= int32(.size.Y)}:= int32(.size.X)for ; < ; ++ {:= fixedMin(int1ϕ(+1)<<ϕ, ) - fixedMax(int1ϕ()<<ϕ, ):= + int1ϕ(float32()*)if < 0 {=continue}:= .bufU32[*:]:= * // d ranges up to ±1<<(1*ϕ)., := ,if > {, = ,}:= fixedFloor():= int1ϕ() << ϕ:= fixedCeil():= int1ϕ() << ϕif <= +1 {:= (+)>>1 -if := clamp(+0, ); < uint(len()) {[] += uint32( * (fxOne - ))}if := clamp(+1, ); < uint(len()) {[] += uint32( * )}} else {:= -:= 2 *:= -:= fxOne -:= *:= - + fxOne:= *// These next two variables are unused, as rounding errors are// minimized when we delay the division by oneOverS for as long as// possible. These lines of code (and the "In ideal math" comments// below) are commented out instead of deleted in order to aid the// comparison with the floating point version of the rasterizer.//// a0 := ((oneMinusX0f * oneMinusX0f) >> 1) / oneOverS// am := ((x1f * x1f) >> 1) / oneOverSif := clamp(, ); < uint(len()) {// In ideal math: buf[i] += uint32(d * a0):= // D ranges up to ±1<<(2*ϕ).*= // D ranges up to ±1<<(3*ϕ)./=[] += uint32()}if == +2 {if := clamp(+1, ); < uint(len()) {// In ideal math: buf[i] += uint32(d * (fxOne - a0 - am))//// (x1i == x0i+2) and (twoOverS == 2 * (x1 - x0)) implies// that twoOverS ranges up to +1<<(1*ϕ+2).:= <<ϕ - - // D ranges up to ±1<<(2*ϕ+2).*= // D ranges up to ±1<<(3*ϕ+2)./=[] += uint32()}} else {// This is commented out for the same reason as a0 and am.//// a1 := ((fxOneAndAHalf - x0f) << ϕ) / oneOverSif := clamp(+1, ); < uint(len()) {// In ideal math:// buf[i] += uint32(d * (a1 - a0))// or equivalently (but better in non-ideal, integer math,// with respect to rounding errors),// buf[i] += uint32(A * d / twoOverS)// where// A = (a1 - a0) * twoOverS// = a1*twoOverS - a0*twoOverS// Noting that twoOverS/oneOverS equals 2, substituting for// a0 and then a1, given above, yields:// A = a1*twoOverS - oneMinusX0fSquared// = (fxOneAndAHalf-x0f)<<(ϕ+1) - oneMinusX0fSquared// = fxOneAndAHalf<<(ϕ+1) - x0f<<(ϕ+1) - oneMinusX0fSquared//// This is a positive number minus two non-negative// numbers. For an upper bound on A, the positive number is// P = fxOneAndAHalf<<(ϕ+1)// < (2*fxOne)<<(ϕ+1)// = fxOne<<(ϕ+2)// = 1<<(2*ϕ+2)//// For a lower bound on A, the two non-negative numbers are// N = x0f<<(ϕ+1) + oneMinusX0fSquared// ≤ x0f<<(ϕ+1) + fxOne*fxOne// = x0f<<(ϕ+1) + 1<<(2*ϕ)// < x0f<<(ϕ+1) + 1<<(2*ϕ+1)// ≤ fxOne<<(ϕ+1) + 1<<(2*ϕ+1)// = 1<<(2*ϕ+1) + 1<<(2*ϕ+1)// = 1<<(2*ϕ+2)//// Thus, A ranges up to ±1<<(2*ϕ+2). It is possible to// derive a tighter bound, but this bound is sufficient to// reason about overflow.:= (fxOneAndAHalf-)<<(ϕ+1) - // D ranges up to ±1<<(2*ϕ+2).*= // D ranges up to ±1<<(3*ϕ+2)./=[] += uint32()}:= uint32(( << (2 * ϕ)) / )for := + 2; < -1; ++ {if := clamp(, ); < uint(len()) {[] +=}}// This is commented out for the same reason as a0 and am.//// a2 := a1 + (int1ϕ(x1i-x0i-3)<<(2*ϕ))/oneOverSif := clamp(-1, ); < uint(len()) {// In ideal math:// buf[i] += uint32(d * (fxOne - a2 - am))// or equivalently (but better in non-ideal, integer math,// with respect to rounding errors),// buf[i] += uint32(A * d / twoOverS)// where// A = (fxOne - a2 - am) * twoOverS// = twoOverS<<ϕ - a2*twoOverS - am*twoOverS// Noting that twoOverS/oneOverS equals 2, substituting for// am and then a2, given above, yields:// A = twoOverS<<ϕ - a2*twoOverS - x1f*x1f// = twoOverS<<ϕ - a1*twoOverS - (int1ϕ(x1i-x0i-3)<<(2*ϕ))*2 - x1f*x1f// = twoOverS<<ϕ - a1*twoOverS - int1ϕ(x1i-x0i-3)<<(2*ϕ+1) - x1f*x1f// Substituting for a1, given above, yields:// A = twoOverS<<ϕ - ((fxOneAndAHalf-x0f)<<ϕ)*2 - int1ϕ(x1i-x0i-3)<<(2*ϕ+1) - x1f*x1f// = twoOverS<<ϕ - (fxOneAndAHalf-x0f)<<(ϕ+1) - int1ϕ(x1i-x0i-3)<<(2*ϕ+1) - x1f*x1f// = B<<ϕ - x1f*x1f// where// B = twoOverS - (fxOneAndAHalf-x0f)<<1 - int1ϕ(x1i-x0i-3)<<(ϕ+1)// = (x1-x0)<<1 - (fxOneAndAHalf-x0f)<<1 - int1ϕ(x1i-x0i-3)<<(ϕ+1)//// Re-arranging the defintions given above:// x0Floor := int1ϕ(x0i) << ϕ// x0f := x0 - x0Floor// x1Ceil := int1ϕ(x1i) << ϕ// x1f := x1 - x1Ceil + fxOne// combined with fxOne = 1<<ϕ yields:// x0 = x0f + int1ϕ(x0i)<<ϕ// x1 = x1f + int1ϕ(x1i-1)<<ϕ// so that expanding (x1-x0) yields:// B = (x1f-x0f + int1ϕ(x1i-x0i-1)<<ϕ)<<1 - (fxOneAndAHalf-x0f)<<1 - int1ϕ(x1i-x0i-3)<<(ϕ+1)// = (x1f-x0f)<<1 + int1ϕ(x1i-x0i-1)<<(ϕ+1) - (fxOneAndAHalf-x0f)<<1 - int1ϕ(x1i-x0i-3)<<(ϕ+1)// A large part of the second and fourth terms cancel:// B = (x1f-x0f)<<1 - (fxOneAndAHalf-x0f)<<1 - int1ϕ(-2)<<(ϕ+1)// = (x1f-x0f)<<1 - (fxOneAndAHalf-x0f)<<1 + 1<<(ϕ+2)// = (x1f - fxOneAndAHalf)<<1 + 1<<(ϕ+2)// The first term, (x1f - fxOneAndAHalf)<<1, is a negative// number, bounded below by -fxOneAndAHalf<<1, which is// greater than -fxOne<<2, or -1<<(ϕ+2). Thus, B ranges up// to ±1<<(ϕ+2). One final simplification:// B = x1f<<1 + (1<<(ϕ+2) - fxOneAndAHalf<<1)const = 1<<(ϕ+2) - fxOneAndAHalf<<1:= <<1 + // D ranges up to ±1<<(1*ϕ+2).<<= ϕ // D ranges up to ±1<<(2*ϕ+2).-= // D ranges up to ±1<<(2*ϕ+3).*= // D ranges up to ±1<<(3*ϕ+3)./=[] += uint32()}}if := clamp(, ); < uint(len()) {// In ideal math: buf[i] += uint32(d * am):= // D ranges up to ±1<<(2*ϕ).*= // D ranges up to ±1<<(3*ϕ)./=[] += uint32()}}=}}func fixedAccumulateOpOver( []uint8, []uint32) {// Sanity check that len(dst) >= len(src).if len() < len() {return}:= int2ϕ(0)for , := range {+= int2ϕ():=if < 0 {= -}>>= 2*ϕ - 16if > 0xffff {= 0xffff}// This algorithm comes from the standard library's image/draw package.:= uint32([]) * 0x101:= uint32():= *(0xffff-)/0xffff +[] = uint8( >> 8)}}func fixedAccumulateOpSrc( []uint8, []uint32) {// Sanity check that len(dst) >= len(src).if len() < len() {return}:= int2ϕ(0)for , := range {+= int2ϕ():=if < 0 {= -}>>= 2*ϕ - 8if > 0xff {= 0xff}[] = uint8()}}func fixedAccumulateMask( []uint32) {:= int2ϕ(0)for , := range {+= int2ϕ():=if < 0 {= -}>>= 2*ϕ - 16if > 0xffff {= 0xffff}[] = uint32()}}
![]() |
The pages are generated with Golds v0.8.2. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |