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  }