9fans.net/go@v0.0.5/draw/memdraw/line.go (about)

     1  package memdraw
     2  
     3  import (
     4  	"9fans.net/go/draw"
     5  )
     6  
     7  const (
     8  	_Arrow1 = 8
     9  	_Arrow2 = 10
    10  	_Arrow3 = 3
    11  )
    12  
    13  func lmin(a, b int) int {
    14  	if a < b {
    15  		return a
    16  	}
    17  	return b
    18  }
    19  
    20  func lmax(a int, b int) int {
    21  	if a > b {
    22  		return a
    23  	}
    24  	return b
    25  }
    26  
    27  // #ifdef NOTUSED
    28  /*
    29   * Rather than line clip, we run the Bresenham loop over the full line,
    30   * and clip on each pixel.  This is more expensive but means that
    31   * lines look the same regardless of how the windowing has tiled them.
    32   * For speed, we check for clipping outside the loop and make the
    33   * test easy when possible.
    34   */
    35  
    36  func horline1(dst *Image, p0 draw.Point, p1 draw.Point, srcval uint8, clipr draw.Rectangle) {
    37  	deltax := p1.X - p0.X
    38  	deltay := p1.Y - p0.Y
    39  	dd := int(dst.Width) * 4
    40  	dy := 1
    41  	if deltay < 0 {
    42  		dd = -dd
    43  		deltay = -deltay
    44  		dy = -1
    45  	}
    46  	maxx := lmin(p1.X, clipr.Max.X-1)
    47  	bpp := dst.Depth
    48  	m0 := uint8(0xFF) ^ (0xFF >> bpp)
    49  	m := m0 >> ((p0.X & (7 / dst.Depth)) * bpp)
    50  	easy := p0.In(clipr) && p1.In(clipr)
    51  	e := 2*deltay - deltax
    52  	y := p0.Y
    53  	d := byteaddr(dst, p0)
    54  	deltay *= 2
    55  	deltax = deltay - 2*deltax
    56  	for x := p0.X; x <= maxx; x++ {
    57  		if easy || (clipr.Min.X <= x && clipr.Min.Y <= y && y < clipr.Max.Y) {
    58  			d[0] ^= (d[0] ^ srcval) & m
    59  		}
    60  		if e > 0 {
    61  			y += dy
    62  			d = d[dd:]
    63  			e += deltax
    64  		} else {
    65  			e += deltay
    66  		}
    67  		d = d[1:]
    68  		m >>= bpp
    69  		if m == 0 {
    70  			m = m0
    71  		}
    72  	}
    73  }
    74  
    75  func verline1(dst *Image, p0 draw.Point, p1 draw.Point, srcval uint8, clipr draw.Rectangle) {
    76  	deltax := p1.X - p0.X
    77  	deltay := p1.Y - p0.Y
    78  	dd := 1
    79  	if deltax < 0 {
    80  		dd = -1
    81  		deltax = -deltax
    82  	}
    83  	maxy := lmin(p1.Y, clipr.Max.Y-1)
    84  	bpp := dst.Depth
    85  	m0 := uint8(0xFF) ^ (0xFF >> bpp)
    86  	m := m0 >> ((p0.X & (7 / dst.Depth)) * bpp)
    87  	easy := p0.In(clipr) && p1.In(clipr)
    88  	e := 2*deltax - deltay
    89  	x := p0.X
    90  	d := byteaddr(dst, p0)
    91  	deltax *= 2
    92  	deltay = deltax - 2*deltay
    93  	for y := p0.Y; y <= maxy; y++ {
    94  		if easy || (clipr.Min.Y <= y && clipr.Min.X <= x && x < clipr.Max.X) {
    95  			d[0] ^= (d[0] ^ srcval) & m
    96  		}
    97  		if e > 0 {
    98  			x += dd
    99  			d = d[dd:]
   100  			e += deltay
   101  		} else {
   102  			e += deltax
   103  		}
   104  		d = d[dst.Width*4:]
   105  		m >>= bpp
   106  		if m == 0 {
   107  			m = m0
   108  		}
   109  	}
   110  }
   111  
   112  func horliner(dst *Image, p0 draw.Point, p1 draw.Point, src *Image, dsrc draw.Point, clipr draw.Rectangle) {
   113  	deltax := p1.X - p0.X
   114  	deltay := p1.Y - p0.Y
   115  	sx := draw.ReplXY(src.R.Min.X, src.R.Max.X, p0.X+dsrc.X)
   116  	minx := lmax(p0.X, clipr.Min.X)
   117  	maxx := lmin(p1.X, clipr.Max.X-1)
   118  	bpp := dst.Depth
   119  	m0 := uint8(0xFF) ^ (0xFF >> bpp)
   120  	m := m0 >> ((minx & (7 / dst.Depth)) * bpp)
   121  	for x := minx; x <= maxx; x++ {
   122  		y := p0.Y + (deltay*(x-p0.X)+deltax/2)/deltax
   123  		if clipr.Min.Y <= y && y < clipr.Max.Y {
   124  			d := byteaddr(dst, draw.Pt(x, y))
   125  			sy := draw.ReplXY(src.R.Min.Y, src.R.Max.Y, y+dsrc.Y)
   126  			s := byteaddr(src, draw.Pt(sx, sy))
   127  			d[0] ^= (d[0] ^ s[0]) & m
   128  		}
   129  		sx++
   130  		if sx >= src.R.Max.X {
   131  			sx = src.R.Min.X
   132  		}
   133  		m >>= bpp
   134  		if m == 0 {
   135  			m = m0
   136  		}
   137  	}
   138  }
   139  
   140  func verliner(dst *Image, p0 draw.Point, p1 draw.Point, src *Image, dsrc draw.Point, clipr draw.Rectangle) {
   141  	deltax := p1.X - p0.X
   142  	deltay := p1.Y - p0.Y
   143  	sy := draw.ReplXY(src.R.Min.Y, src.R.Max.Y, p0.Y+dsrc.Y)
   144  	miny := lmax(p0.Y, clipr.Min.Y)
   145  	maxy := lmin(p1.Y, clipr.Max.Y-1)
   146  	bpp := dst.Depth
   147  	m0 := uint8(0xFF) ^ (0xFF >> bpp)
   148  	for y := miny; y <= maxy; y++ {
   149  		var x int
   150  		if deltay == 0 { /* degenerate line */
   151  			x = p0.X
   152  		} else {
   153  			x = p0.X + (deltax*(y-p0.Y)+deltay/2)/deltay
   154  		}
   155  		if clipr.Min.X <= x && x < clipr.Max.X {
   156  			m := m0 >> ((x & (7 / dst.Depth)) * bpp)
   157  			d := byteaddr(dst, draw.Pt(x, y))
   158  			sx := draw.ReplXY(src.R.Min.X, src.R.Max.X, x+dsrc.X)
   159  			s := byteaddr(src, draw.Pt(sx, sy))
   160  			d[0] ^= (d[0] ^ s[0]) & m
   161  		}
   162  		sy++
   163  		if sy >= src.R.Max.Y {
   164  			sy = src.R.Min.Y
   165  		}
   166  	}
   167  }
   168  
   169  func horline(dst *Image, p0 draw.Point, p1 draw.Point, src *Image, dsrc draw.Point, clipr draw.Rectangle) {
   170  	deltax := p1.X - p0.X
   171  	deltay := p1.Y - p0.Y
   172  	minx := lmax(p0.X, clipr.Min.X)
   173  	maxx := lmin(p1.X, clipr.Max.X-1)
   174  	bpp := dst.Depth
   175  	m0 := uint8(0xFF) ^ (0xFF >> bpp)
   176  	m := m0 >> ((minx & (7 / dst.Depth)) * bpp)
   177  	for x := minx; x <= maxx; x++ {
   178  		y := p0.Y + (deltay*(x-p0.X)+deltay/2)/deltax
   179  		if clipr.Min.Y <= y && y < clipr.Max.Y {
   180  			d := byteaddr(dst, draw.Pt(x, y))
   181  			s := byteaddr(src, dsrc.Add(draw.Pt(x, y)))
   182  			d[0] ^= (d[0] ^ s[0]) & m
   183  		}
   184  		m >>= bpp
   185  		if m == 0 {
   186  			m = m0
   187  		}
   188  	}
   189  }
   190  
   191  func verline(dst *Image, p0 draw.Point, p1 draw.Point, src *Image, dsrc draw.Point, clipr draw.Rectangle) {
   192  	deltax := p1.X - p0.X
   193  	deltay := p1.Y - p0.Y
   194  	miny := lmax(p0.Y, clipr.Min.Y)
   195  	maxy := lmin(p1.Y, clipr.Max.Y-1)
   196  	bpp := dst.Depth
   197  	m0 := uint8(0xFF) ^ (0xFF >> bpp)
   198  	for y := miny; y <= maxy; y++ {
   199  		var x int
   200  		if deltay == 0 { /* degenerate line */
   201  			x = p0.X
   202  		} else {
   203  			x = p0.X + deltax*(y-p0.Y)/deltay
   204  		}
   205  		if clipr.Min.X <= x && x < clipr.Max.X {
   206  			m := m0 >> ((x & (7 / dst.Depth)) * bpp)
   207  			d := byteaddr(dst, draw.Pt(x, y))
   208  			s := byteaddr(src, dsrc.Add(draw.Pt(x, y)))
   209  			d[0] ^= (d[0] ^ s[0]) & m
   210  		}
   211  	}
   212  }
   213  
   214  // #endif /* NOTUSED */
   215  
   216  var membrush_brush *Image
   217  var membrush_brushradius int
   218  
   219  func membrush(radius int) *Image {
   220  	if membrush_brush == nil || membrush_brushradius != radius {
   221  		Free(membrush_brush)
   222  		var err error
   223  		membrush_brush, err = AllocImage(draw.Rect(0, 0, 2*radius+1, 2*radius+1), Opaque.Pix)
   224  		if err == nil {
   225  			FillColor(membrush_brush, draw.Transparent) /* zeros */
   226  			Ellipse(membrush_brush, draw.Pt(radius, radius), radius, radius, -1, Opaque, draw.Pt(radius, radius), draw.S)
   227  		}
   228  		membrush_brushradius = radius
   229  	}
   230  	return membrush_brush
   231  }
   232  
   233  func discend(p draw.Point, radius int, dst *Image, src *Image, dsrc draw.Point, op draw.Op) {
   234  	disc := membrush(radius)
   235  	if disc != nil {
   236  		var r draw.Rectangle
   237  		r.Min.X = p.X - radius
   238  		r.Min.Y = p.Y - radius
   239  		r.Max.X = p.X + radius + 1
   240  		r.Max.Y = p.Y + radius + 1
   241  		Draw(dst, r, src, r.Min.Add(dsrc), disc, draw.Pt(0, 0), op)
   242  	}
   243  }
   244  
   245  func arrowend(tip draw.Point, pp []draw.Point, end draw.End, sin int, cos int, radius int) {
   246  	var x1 int
   247  	var x2 int
   248  	var x3 int
   249  	/* before rotation */
   250  	if end == draw.EndArrow {
   251  		x1 = _Arrow1
   252  		x2 = _Arrow2
   253  		x3 = _Arrow3
   254  	} else {
   255  		x1 = int(end>>5) & 0x1FF  /* distance along line from end of line to tip */
   256  		x2 = int(end>>14) & 0x1FF /* distance along line from barb to tip */
   257  		x3 = int(end>>23) & 0x1FF /* distance perpendicular from edge of line to barb */
   258  	}
   259  
   260  	/* comments follow track of right-facing arrowhead */
   261  	pp[0].X = tip.X + ((2*radius+1)*sin/2 - x1*cos) /* upper side of shaft */
   262  	pp[0].Y = tip.Y - ((2*radius+1)*cos/2 + x1*sin)
   263  
   264  	pp[1].X = tip.X + ((2*radius+2*x3+1)*sin/2 - x2*cos) /* upper barb */
   265  	pp[1].Y = tip.Y - ((2*radius+2*x3+1)*cos/2 + x2*sin)
   266  
   267  	pp[2].X = tip.X
   268  	pp[2].Y = tip.Y
   269  
   270  	pp[3].X = tip.X + (-(2*radius+2*x3+1)*sin/2 - x2*cos) /* lower barb */
   271  	pp[3].Y = tip.Y - (-(2*radius+2*x3+1)*cos/2 + x2*sin)
   272  
   273  	pp[4].X = tip.X + (-(2*radius+1)*sin/2 - x1*cos) /* lower side of shaft */
   274  	pp[4].Y = tip.Y + ((2*radius+1)*cos/2 - x1*sin)
   275  }
   276  
   277  func abs(x int) int {
   278  	if x < 0 {
   279  		return -x
   280  	}
   281  	return x
   282  }
   283  
   284  func _memimageline(dst *Image, p0 draw.Point, p1 draw.Point, end0, end1 draw.End, radius int, src *Image, sp draw.Point, clipr draw.Rectangle, op draw.Op) {
   285  	if radius < 0 {
   286  		return
   287  	}
   288  	if !draw.RectClip(&clipr, dst.R) {
   289  		return
   290  	}
   291  	if !draw.RectClip(&clipr, dst.Clipr) {
   292  		return
   293  	}
   294  	d := sp.Sub(p0)
   295  	if !draw.RectClip(&clipr, src.Clipr.Sub(d)) {
   296  		return
   297  	}
   298  	if src.Flags&Frepl == 0 && !draw.RectClip(&clipr, src.R.Sub(d)) {
   299  		return
   300  	}
   301  	/* this means that only verline() handles degenerate lines (p0==p1) */
   302  	hor := abs(p1.X-p0.X) > abs(p1.Y-p0.Y)
   303  	var q draw.Point
   304  	/*
   305  	 * Clipping is a little peculiar.  We can't use Sutherland-Cohen
   306  	 * clipping because lines are wide.  But this is probably just fine:
   307  	 * we do all math with the original p0 and p1, but clip when deciding
   308  	 * what pixels to draw.  This means the layer code can call this routine,
   309  	 * using clipr to define the region being written, and get the same set
   310  	 * of pixels regardless of the dicing.
   311  	 */
   312  	if (hor && p0.X > p1.X) || (!hor && p0.Y > p1.Y) {
   313  		q = p0
   314  		p0 = p1
   315  		p1 = q
   316  		t := end0
   317  		end0 = end1
   318  		end1 = t
   319  	}
   320  	var oclipr draw.Rectangle
   321  
   322  	if (p0.X == p1.X || p0.Y == p1.Y) && end0&0x1F == draw.EndSquare && end1&0x1F == draw.EndSquare {
   323  		var r draw.Rectangle
   324  		r.Min = p0
   325  		r.Max = p1
   326  		if p0.X == p1.X {
   327  			r.Min.X -= radius
   328  			r.Max.X += radius + 1
   329  		} else {
   330  			r.Min.Y -= radius
   331  			r.Max.Y += radius + 1
   332  		}
   333  		oclipr = dst.Clipr
   334  		dst.Clipr = clipr
   335  		dst.Draw(r, src, sp, Opaque, sp, op)
   336  		dst.Clipr = oclipr
   337  		return
   338  	}
   339  
   340  	/*    Hard: */
   341  	/* draw thick line using polygon fill */
   342  	cos, sin := draw.IntCosSin2(p1.X-p0.X, p1.Y-p0.Y)
   343  	dx := (sin * (2*radius + 1)) / 2
   344  	dy := (cos * (2*radius + 1)) / 2
   345  	var pts [10]draw.Point
   346  	pp := pts[:]
   347  	oclipr = dst.Clipr
   348  	dst.Clipr = clipr
   349  	q.X = draw.ICOSSCALE*p0.X + draw.ICOSSCALE/2 - cos/2
   350  	q.Y = draw.ICOSSCALE*p0.Y + draw.ICOSSCALE/2 - sin/2
   351  	switch end0 & 0x1F {
   352  	case draw.EndDisc:
   353  		discend(p0, radius, dst, src, d, op)
   354  		fallthrough
   355  	/* fall through */
   356  	case draw.EndSquare:
   357  		fallthrough
   358  	default:
   359  		pp[0].X = q.X - dx
   360  		pp[0].Y = q.Y + dy
   361  		pp[1].X = q.X + dx
   362  		pp[1].Y = q.Y - dy
   363  		pp = pp[2:]
   364  	case draw.EndArrow:
   365  		arrowend(q, pp, end0, -sin, -cos, radius)
   366  		_memfillpolysc(dst, pts[:5], ^0, src, pts[0].Add(d.Mul(draw.ICOSSCALE)), 1, 10, 1, op)
   367  		pp[1] = pp[4]
   368  		pp = pp[2:]
   369  	}
   370  	q.X = draw.ICOSSCALE*p1.X + draw.ICOSSCALE/2 + cos/2
   371  	q.Y = draw.ICOSSCALE*p1.Y + draw.ICOSSCALE/2 + sin/2
   372  	switch end1 & 0x1F {
   373  	case draw.EndDisc:
   374  		discend(p1, radius, dst, src, d, op)
   375  		fallthrough
   376  	/* fall through */
   377  	case draw.EndSquare:
   378  		fallthrough
   379  	default:
   380  		pp[0].X = q.X + dx
   381  		pp[0].Y = q.Y - dy
   382  		pp[1].X = q.X - dx
   383  		pp[1].Y = q.Y + dy
   384  		pp = pp[2:]
   385  	case draw.EndArrow:
   386  		arrowend(q, pp, end1, sin, cos, radius)
   387  		_memfillpolysc(dst, pp[:5], ^0, src, pts[0].Add(d.Mul(draw.ICOSSCALE)), 1, 10, 1, op)
   388  		pp[1] = pp[4]
   389  		pp = pp[2:]
   390  	}
   391  	_memfillpolysc(dst, pts[:len(pts)-len(pp)], ^0, src, pts[0].Add(d.Mul(draw.ICOSSCALE)), 0, 10, 1, op)
   392  	dst.Clipr = oclipr
   393  	return
   394  }
   395  
   396  func memimageline(dst *Image, p0 draw.Point, p1 draw.Point, end0, end1 draw.End, radius int, src *Image, sp draw.Point, op draw.Op) {
   397  	_memimageline(dst, p0, p1, end0, end1, radius, src, sp, dst.Clipr, op)
   398  }
   399  
   400  /*
   401   * Simple-minded conservative code to compute bounding box of line.
   402   * Result is probably a little larger than it needs to be.
   403   */
   404  func addbbox(r *draw.Rectangle, p draw.Point) {
   405  	if r.Min.X > p.X {
   406  		r.Min.X = p.X
   407  	}
   408  	if r.Min.Y > p.Y {
   409  		r.Min.Y = p.Y
   410  	}
   411  	if r.Max.X < p.X+1 {
   412  		r.Max.X = p.X + 1
   413  	}
   414  	if r.Max.Y < p.Y+1 {
   415  		r.Max.Y = p.Y + 1
   416  	}
   417  }
   418  
   419  func LineEndSize(end draw.End) int {
   420  	if end&0x3F != draw.EndArrow {
   421  		return 0
   422  	}
   423  	var x3 int
   424  	if end == draw.EndArrow {
   425  		x3 = _Arrow3
   426  	} else {
   427  		x3 = int(end>>23) & 0x1FF
   428  	}
   429  	return x3
   430  }
   431  
   432  func LineBBox(p0 draw.Point, p1 draw.Point, end0, end1 draw.End, radius int) draw.Rectangle {
   433  	var r draw.Rectangle
   434  	r.Min.X = 10000000
   435  	r.Min.Y = 10000000
   436  	r.Max.X = -10000000
   437  	r.Max.Y = -10000000
   438  	extra := lmax(LineEndSize(end0), LineEndSize(end1))
   439  	r1 := draw.Rpt(p0, p1).Canon().Inset(-(radius + extra))
   440  	addbbox(&r, r1.Min)
   441  	addbbox(&r, r1.Max)
   442  	return r
   443  }