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 }