github.com/xxf098/lite-proxy@v0.15.1-0.20230422081941-12c69f323218/web/render/path.go (about) 1 package render 2 3 import ( 4 "math" 5 6 "github.com/golang/freetype/raster" 7 "golang.org/x/image/math/fixed" 8 ) 9 10 func flattenPath(p raster.Path) [][]Point { 11 var result [][]Point 12 var path []Point 13 var cx, cy float64 14 for i := 0; i < len(p); { 15 switch p[i] { 16 case 0: 17 if len(path) > 0 { 18 result = append(result, path) 19 path = nil 20 } 21 x := unfix(p[i+1]) 22 y := unfix(p[i+2]) 23 path = append(path, Point{x, y}) 24 cx, cy = x, y 25 i += 4 26 case 1: 27 x := unfix(p[i+1]) 28 y := unfix(p[i+2]) 29 path = append(path, Point{x, y}) 30 cx, cy = x, y 31 i += 4 32 case 2: 33 x1 := unfix(p[i+1]) 34 y1 := unfix(p[i+2]) 35 x2 := unfix(p[i+3]) 36 y2 := unfix(p[i+4]) 37 points := QuadraticBezier(cx, cy, x1, y1, x2, y2) 38 path = append(path, points...) 39 cx, cy = x2, y2 40 i += 6 41 case 3: 42 x1 := unfix(p[i+1]) 43 y1 := unfix(p[i+2]) 44 x2 := unfix(p[i+3]) 45 y2 := unfix(p[i+4]) 46 x3 := unfix(p[i+5]) 47 y3 := unfix(p[i+6]) 48 points := CubicBezier(cx, cy, x1, y1, x2, y2, x3, y3) 49 path = append(path, points...) 50 cx, cy = x3, y3 51 i += 8 52 default: 53 panic("bad path") 54 } 55 } 56 if len(path) > 0 { 57 result = append(result, path) 58 } 59 return result 60 } 61 62 func dashPath(paths [][]Point, dashes []float64, offset float64) [][]Point { 63 var result [][]Point 64 if len(dashes) == 0 { 65 return paths 66 } 67 if len(dashes) == 1 { 68 dashes = append(dashes, dashes[0]) 69 } 70 for _, path := range paths { 71 if len(path) < 2 { 72 continue 73 } 74 previous := path[0] 75 pathIndex := 1 76 dashIndex := 0 77 segmentLength := 0.0 78 79 // offset 80 if offset != 0 { 81 var totalLength float64 82 for _, dashLength := range dashes { 83 totalLength += dashLength 84 } 85 offset = math.Mod(offset, totalLength) 86 if offset < 0 { 87 offset += totalLength 88 } 89 for i, dashLength := range dashes { 90 offset -= dashLength 91 if offset < 0 { 92 dashIndex = i 93 segmentLength = dashLength + offset 94 break 95 } 96 } 97 } 98 99 var segment []Point 100 segment = append(segment, previous) 101 for pathIndex < len(path) { 102 dashLength := dashes[dashIndex] 103 point := path[pathIndex] 104 d := previous.Distance(point) 105 maxd := dashLength - segmentLength 106 if d > maxd { 107 t := maxd / d 108 p := previous.Interpolate(point, t) 109 segment = append(segment, p) 110 if dashIndex%2 == 0 && len(segment) > 1 { 111 result = append(result, segment) 112 } 113 segment = nil 114 segment = append(segment, p) 115 segmentLength = 0 116 previous = p 117 dashIndex = (dashIndex + 1) % len(dashes) 118 } else { 119 segment = append(segment, point) 120 previous = point 121 segmentLength += d 122 pathIndex++ 123 } 124 } 125 if dashIndex%2 == 0 && len(segment) > 1 { 126 result = append(result, segment) 127 } 128 } 129 return result 130 } 131 132 func rasterPath(paths [][]Point) raster.Path { 133 var result raster.Path 134 for _, path := range paths { 135 var previous fixed.Point26_6 136 for i, point := range path { 137 f := point.Fixed() 138 if i == 0 { 139 result.Start(f) 140 } else { 141 dx := f.X - previous.X 142 dy := f.Y - previous.Y 143 if dx < 0 { 144 dx = -dx 145 } 146 if dy < 0 { 147 dy = -dy 148 } 149 if dx+dy > 8 { 150 // TODO: this is a hack for cases where two points are 151 // too close - causes rendering issues with joins / caps 152 result.Add1(f) 153 } 154 } 155 previous = f 156 } 157 } 158 return result 159 } 160 161 func dashed(path raster.Path, dashes []float64, offset float64) raster.Path { 162 return rasterPath(dashPath(flattenPath(path), dashes, offset)) 163 }