github.com/kintar/etxt@v0.0.9/emask/helper_funcs.go (about) 1 package emask 2 3 import "math" 4 import "image" 5 6 import "golang.org/x/image/math/fixed" 7 import "github.com/kintar/etxt/efixed" 8 9 // Given some glyph bounds and a fractional pixel position, it figures out 10 // what integer size must be used to fit the bounds, what normalization 11 // offset must be applied to keep the coordinates in the positive plane, 12 // and what final offset must be applied to the final mask to align its 13 // bounds to the glyph origin. This is used in NewContour functions. 14 func figureOutBounds(bounds fixed.Rectangle26_6, fract fixed.Point26_6) (image.Point, fixed.Point26_6, image.Point) { 15 floorMinX := efixed.Floor(bounds.Min.X) 16 floorMinY := efixed.Floor(bounds.Min.Y) 17 var maskCorrection image.Point 18 maskCorrection.X = int(floorMinX >> 6) 19 maskCorrection.Y = int(floorMinY >> 6) 20 21 var normOffset fixed.Point26_6 22 normOffset.X = -floorMinX + fract.X 23 normOffset.Y = -floorMinY + fract.Y 24 width := (bounds.Max.X + normOffset.X).Ceil() 25 height := (bounds.Max.Y + normOffset.Y).Ceil() 26 return image.Pt(width, height), normOffset, maskCorrection 27 } 28 29 // Around 9 times as fast as using a regular for loop. 30 // This can trivially be made generic, and can also be adapted 31 // to fill buffers with patterns (for example to fill 32 // images with a specific color). 33 func fastFillFloat64(buffer []float64, value float64) { 34 if len(buffer) <= 24 { // no-copy case 35 for i, _ := range buffer { 36 buffer[i] = value 37 } 38 } else { // copy case 39 for i, _ := range buffer[:16] { 40 buffer[i] = value 41 } 42 for i := 16; i < len(buffer); i *= 2 { 43 copy(buffer[i:], buffer[:i]) 44 } 45 } 46 } 47 48 func fastFillUint8(buffer []uint8, value uint8) { 49 if len(buffer) <= 24 { // no-copy case 50 for i, _ := range buffer { 51 buffer[i] = value 52 } 53 } else { // copy case 54 for i, _ := range buffer[:16] { 55 buffer[i] = value 56 } 57 for i := 16; i < len(buffer); i *= 2 { 58 copy(buffer[i:], buffer[:i]) 59 } 60 } 61 } 62 63 // linearly interpolate (ax, ay) and (bx, by) at the given t, which 64 // must be in [0, 1] 65 func lerp(ax, ay, bx, by float64, t float64) (float64, float64) { 66 return interpolateAt(ax, bx, t), interpolateAt(ay, by, t) 67 } 68 69 // interpolate a and b at the given t, which must be in [0, 1] 70 func interpolateAt(a, b float64, t float64) float64 { return a + t*(b-a) } 71 72 // Given two points of a line, it returns its A, B and C 73 // coefficients from the form "Ax + By + C = 0". 74 func toLinearFormABC(ox, oy, fx, fy float64) (float64, float64, float64) { 75 a, b, c := fy-oy, -(fx - ox), (fx-ox)*oy-(fy-oy)*ox 76 return a, b, c 77 } 78 79 // If we had two line equations like this: 80 // >> a1*x + b1*y = c1 81 // >> a2*x + b2*y = c2 82 // We would apply cramer's rule to solve the system: 83 // >> x = (b2*c1 - b1*c2)/(b2*a1 - b1*a2) 84 // This function solves this system, but assuming c1 and c2 have 85 // a negative sign (ax + by + c = 0), and taking a precomputed 86 // xdiv = (b2*a1 - b1*a2) value 87 func shortCramer(xdiv, a1, b1, c1, a2, b2, c2 float64) (float64, float64) { 88 if xdiv == 0 { 89 panic("parallel lines") 90 } 91 92 // actual application of cramer's rule 93 x := (b2*-c1 - b1*-c2) / xdiv 94 if b1 != 0 { 95 return x, (-c1 - a1*x) / b1 96 } 97 return x, (-c2 - a2*x) / b2 98 } 99 100 // given a line equation in Ax + By + C = 0 form and a point, finds 101 // the perpendicular ABC line equation that passes through the given 102 // point. the C is not in the parameters because it's not necessary 103 func perpendicularABC(a, b, x, y float64) (float64, float64, float64) { 104 // we have ax + by + c = 0, and we want to find dx + ey + f = 0... 105 // we can use d = b, e = -a and f = -d*x - e*y 106 d := b 107 e := -a 108 f := -d*x - e*y 109 return d, e, f 110 } 111 112 // given a line equation in the form Ax + By + C = 0, it returns 113 // C1 and C2 such that two new line equations can be created that 114 // are parallel to the original line, but at distance 'dist' from it 115 func parallelsAtDist(a, b, c float64, dist float64) (float64, float64) { 116 var c1, c2 float64 117 if a == 0 { // horizontal line 118 y := -c / b 119 c1 = -(y + dist) * b 120 c2 = -(y - dist) * b 121 } else if b == 0 { // vertical line 122 x := -c / a 123 c1 = -(x + dist) * a 124 c2 = -(x - dist) * a 125 } else { 126 // We use the formula for the distance between a point and a line: 127 // >> dist = |ax + by + c|/sqrt(a*a + b*b) 128 // We assume x = 0 and find the two y possible values. 129 // We use the points (0, y1) and (0, y2) to find the new c1 and c2. 130 f := dist * math.Sqrt(a*a+b*b) 131 y1 := (-c + f) / b 132 y2 := (-c - f) / b 133 c1 = -b * y1 134 c2 = -b * y2 135 } 136 return c1, c2 137 } 138 139 // Returns the distance between two points, squared. The squared value 140 // can be used to compare distances, only applying the square root if 141 // necessary later with math.Sqrt(). 142 func dist2(x1, y1, x2, y2 float64) float64 { 143 dx := x1 - x2 144 dy := y1 - y2 145 return dx*dx + dy*dy 146 } 147 148 func abs64(value float64) float64 { 149 if value >= 0 { 150 return value 151 } 152 return -value 153 } 154 155 func clampUnit64(value float64) float64 { 156 if value <= 1.0 { 157 return value 158 } 159 return 1.0 160 }