github.com/onsi/ginkgo@v1.16.6-0.20211118180735-4e1925ba4c95/formatter/colorable_windows.go (about) 1 /* 2 These packages are used for colorize on Windows and contributed by mattn.jp@gmail.com 3 4 * go-colorable: <https://github.com/mattn/go-colorable> 5 * go-isatty: <https://github.com/mattn/go-isatty> 6 7 The MIT License (MIT) 8 9 Copyright (c) 2016 Yasuhiro Matsumoto 10 11 Permission is hereby granted, free of charge, to any person obtaining a copy 12 of this software and associated documentation files (the "Software"), to deal 13 in the Software without restriction, including without limitation the rights 14 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 copies of the Software, and to permit persons to whom the Software is 16 furnished to do so, subject to the following conditions: 17 18 The above copyright notice and this permission notice shall be included in all 19 copies or substantial portions of the Software. 20 21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 SOFTWARE. 28 */ 29 30 package formatter 31 32 import ( 33 "bytes" 34 "fmt" 35 "io" 36 "math" 37 "os" 38 "strconv" 39 "strings" 40 "syscall" 41 "unsafe" 42 ) 43 44 var ( 45 kernel32 = syscall.NewLazyDLL("kernel32.dll") 46 procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") 47 procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") 48 procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") 49 procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") 50 procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute") 51 procGetConsoleMode = kernel32.NewProc("GetConsoleMode") 52 ) 53 54 func isTerminal(fd uintptr) bool { 55 var st uint32 56 r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0) 57 return r != 0 && e == 0 58 } 59 60 const ( 61 foregroundBlue = 0x1 62 foregroundGreen = 0x2 63 foregroundRed = 0x4 64 foregroundIntensity = 0x8 65 foregroundMask = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity) 66 backgroundBlue = 0x10 67 backgroundGreen = 0x20 68 backgroundRed = 0x40 69 backgroundIntensity = 0x80 70 backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity) 71 ) 72 73 type wchar uint16 74 type short int16 75 type dword uint32 76 type word uint16 77 78 type coord struct { 79 x short 80 y short 81 } 82 83 type smallRect struct { 84 left short 85 top short 86 right short 87 bottom short 88 } 89 90 type consoleScreenBufferInfo struct { 91 size coord 92 cursorPosition coord 93 attributes word 94 window smallRect 95 maximumWindowSize coord 96 } 97 98 type writer struct { 99 out io.Writer 100 handle syscall.Handle 101 lastbuf bytes.Buffer 102 oldattr word 103 } 104 105 func newColorable(file *os.File) io.Writer { 106 if file == nil { 107 panic("nil passed instead of *os.File to NewColorable()") 108 } 109 110 if isTerminal(file.Fd()) { 111 var csbi consoleScreenBufferInfo 112 handle := syscall.Handle(file.Fd()) 113 procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) 114 return &writer{out: file, handle: handle, oldattr: csbi.attributes} 115 } else { 116 return file 117 } 118 } 119 120 var color256 = map[int]int{ 121 0: 0x000000, 122 1: 0x800000, 123 2: 0x008000, 124 3: 0x808000, 125 4: 0x000080, 126 5: 0x800080, 127 6: 0x008080, 128 7: 0xc0c0c0, 129 8: 0x808080, 130 9: 0xff0000, 131 10: 0x00ff00, 132 11: 0xffff00, 133 12: 0x0000ff, 134 13: 0xff00ff, 135 14: 0x00ffff, 136 15: 0xffffff, 137 16: 0x000000, 138 17: 0x00005f, 139 18: 0x000087, 140 19: 0x0000af, 141 20: 0x0000d7, 142 21: 0x0000ff, 143 22: 0x005f00, 144 23: 0x005f5f, 145 24: 0x005f87, 146 25: 0x005faf, 147 26: 0x005fd7, 148 27: 0x005fff, 149 28: 0x008700, 150 29: 0x00875f, 151 30: 0x008787, 152 31: 0x0087af, 153 32: 0x0087d7, 154 33: 0x0087ff, 155 34: 0x00af00, 156 35: 0x00af5f, 157 36: 0x00af87, 158 37: 0x00afaf, 159 38: 0x00afd7, 160 39: 0x00afff, 161 40: 0x00d700, 162 41: 0x00d75f, 163 42: 0x00d787, 164 43: 0x00d7af, 165 44: 0x00d7d7, 166 45: 0x00d7ff, 167 46: 0x00ff00, 168 47: 0x00ff5f, 169 48: 0x00ff87, 170 49: 0x00ffaf, 171 50: 0x00ffd7, 172 51: 0x00ffff, 173 52: 0x5f0000, 174 53: 0x5f005f, 175 54: 0x5f0087, 176 55: 0x5f00af, 177 56: 0x5f00d7, 178 57: 0x5f00ff, 179 58: 0x5f5f00, 180 59: 0x5f5f5f, 181 60: 0x5f5f87, 182 61: 0x5f5faf, 183 62: 0x5f5fd7, 184 63: 0x5f5fff, 185 64: 0x5f8700, 186 65: 0x5f875f, 187 66: 0x5f8787, 188 67: 0x5f87af, 189 68: 0x5f87d7, 190 69: 0x5f87ff, 191 70: 0x5faf00, 192 71: 0x5faf5f, 193 72: 0x5faf87, 194 73: 0x5fafaf, 195 74: 0x5fafd7, 196 75: 0x5fafff, 197 76: 0x5fd700, 198 77: 0x5fd75f, 199 78: 0x5fd787, 200 79: 0x5fd7af, 201 80: 0x5fd7d7, 202 81: 0x5fd7ff, 203 82: 0x5fff00, 204 83: 0x5fff5f, 205 84: 0x5fff87, 206 85: 0x5fffaf, 207 86: 0x5fffd7, 208 87: 0x5fffff, 209 88: 0x870000, 210 89: 0x87005f, 211 90: 0x870087, 212 91: 0x8700af, 213 92: 0x8700d7, 214 93: 0x8700ff, 215 94: 0x875f00, 216 95: 0x875f5f, 217 96: 0x875f87, 218 97: 0x875faf, 219 98: 0x875fd7, 220 99: 0x875fff, 221 100: 0x878700, 222 101: 0x87875f, 223 102: 0x878787, 224 103: 0x8787af, 225 104: 0x8787d7, 226 105: 0x8787ff, 227 106: 0x87af00, 228 107: 0x87af5f, 229 108: 0x87af87, 230 109: 0x87afaf, 231 110: 0x87afd7, 232 111: 0x87afff, 233 112: 0x87d700, 234 113: 0x87d75f, 235 114: 0x87d787, 236 115: 0x87d7af, 237 116: 0x87d7d7, 238 117: 0x87d7ff, 239 118: 0x87ff00, 240 119: 0x87ff5f, 241 120: 0x87ff87, 242 121: 0x87ffaf, 243 122: 0x87ffd7, 244 123: 0x87ffff, 245 124: 0xaf0000, 246 125: 0xaf005f, 247 126: 0xaf0087, 248 127: 0xaf00af, 249 128: 0xaf00d7, 250 129: 0xaf00ff, 251 130: 0xaf5f00, 252 131: 0xaf5f5f, 253 132: 0xaf5f87, 254 133: 0xaf5faf, 255 134: 0xaf5fd7, 256 135: 0xaf5fff, 257 136: 0xaf8700, 258 137: 0xaf875f, 259 138: 0xaf8787, 260 139: 0xaf87af, 261 140: 0xaf87d7, 262 141: 0xaf87ff, 263 142: 0xafaf00, 264 143: 0xafaf5f, 265 144: 0xafaf87, 266 145: 0xafafaf, 267 146: 0xafafd7, 268 147: 0xafafff, 269 148: 0xafd700, 270 149: 0xafd75f, 271 150: 0xafd787, 272 151: 0xafd7af, 273 152: 0xafd7d7, 274 153: 0xafd7ff, 275 154: 0xafff00, 276 155: 0xafff5f, 277 156: 0xafff87, 278 157: 0xafffaf, 279 158: 0xafffd7, 280 159: 0xafffff, 281 160: 0xd70000, 282 161: 0xd7005f, 283 162: 0xd70087, 284 163: 0xd700af, 285 164: 0xd700d7, 286 165: 0xd700ff, 287 166: 0xd75f00, 288 167: 0xd75f5f, 289 168: 0xd75f87, 290 169: 0xd75faf, 291 170: 0xd75fd7, 292 171: 0xd75fff, 293 172: 0xd78700, 294 173: 0xd7875f, 295 174: 0xd78787, 296 175: 0xd787af, 297 176: 0xd787d7, 298 177: 0xd787ff, 299 178: 0xd7af00, 300 179: 0xd7af5f, 301 180: 0xd7af87, 302 181: 0xd7afaf, 303 182: 0xd7afd7, 304 183: 0xd7afff, 305 184: 0xd7d700, 306 185: 0xd7d75f, 307 186: 0xd7d787, 308 187: 0xd7d7af, 309 188: 0xd7d7d7, 310 189: 0xd7d7ff, 311 190: 0xd7ff00, 312 191: 0xd7ff5f, 313 192: 0xd7ff87, 314 193: 0xd7ffaf, 315 194: 0xd7ffd7, 316 195: 0xd7ffff, 317 196: 0xff0000, 318 197: 0xff005f, 319 198: 0xff0087, 320 199: 0xff00af, 321 200: 0xff00d7, 322 201: 0xff00ff, 323 202: 0xff5f00, 324 203: 0xff5f5f, 325 204: 0xff5f87, 326 205: 0xff5faf, 327 206: 0xff5fd7, 328 207: 0xff5fff, 329 208: 0xff8700, 330 209: 0xff875f, 331 210: 0xff8787, 332 211: 0xff87af, 333 212: 0xff87d7, 334 213: 0xff87ff, 335 214: 0xffaf00, 336 215: 0xffaf5f, 337 216: 0xffaf87, 338 217: 0xffafaf, 339 218: 0xffafd7, 340 219: 0xffafff, 341 220: 0xffd700, 342 221: 0xffd75f, 343 222: 0xffd787, 344 223: 0xffd7af, 345 224: 0xffd7d7, 346 225: 0xffd7ff, 347 226: 0xffff00, 348 227: 0xffff5f, 349 228: 0xffff87, 350 229: 0xffffaf, 351 230: 0xffffd7, 352 231: 0xffffff, 353 232: 0x080808, 354 233: 0x121212, 355 234: 0x1c1c1c, 356 235: 0x262626, 357 236: 0x303030, 358 237: 0x3a3a3a, 359 238: 0x444444, 360 239: 0x4e4e4e, 361 240: 0x585858, 362 241: 0x626262, 363 242: 0x6c6c6c, 364 243: 0x767676, 365 244: 0x808080, 366 245: 0x8a8a8a, 367 246: 0x949494, 368 247: 0x9e9e9e, 369 248: 0xa8a8a8, 370 249: 0xb2b2b2, 371 250: 0xbcbcbc, 372 251: 0xc6c6c6, 373 252: 0xd0d0d0, 374 253: 0xdadada, 375 254: 0xe4e4e4, 376 255: 0xeeeeee, 377 } 378 379 func (w *writer) Write(data []byte) (n int, err error) { 380 var csbi consoleScreenBufferInfo 381 procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) 382 383 er := bytes.NewBuffer(data) 384 loop: 385 for { 386 r1, _, err := procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) 387 if r1 == 0 { 388 break loop 389 } 390 391 c1, _, err := er.ReadRune() 392 if err != nil { 393 break loop 394 } 395 if c1 != 0x1b { 396 fmt.Fprint(w.out, string(c1)) 397 continue 398 } 399 c2, _, err := er.ReadRune() 400 if err != nil { 401 w.lastbuf.WriteRune(c1) 402 break loop 403 } 404 if c2 != 0x5b { 405 w.lastbuf.WriteRune(c1) 406 w.lastbuf.WriteRune(c2) 407 continue 408 } 409 410 var buf bytes.Buffer 411 var m rune 412 for { 413 c, _, err := er.ReadRune() 414 if err != nil { 415 w.lastbuf.WriteRune(c1) 416 w.lastbuf.WriteRune(c2) 417 w.lastbuf.Write(buf.Bytes()) 418 break loop 419 } 420 if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { 421 m = c 422 break 423 } 424 buf.Write([]byte(string(c))) 425 } 426 427 var csbi consoleScreenBufferInfo 428 switch m { 429 case 'A': 430 n, err = strconv.Atoi(buf.String()) 431 if err != nil { 432 continue 433 } 434 procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) 435 csbi.cursorPosition.y -= short(n) 436 procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) 437 case 'B': 438 n, err = strconv.Atoi(buf.String()) 439 if err != nil { 440 continue 441 } 442 procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) 443 csbi.cursorPosition.y += short(n) 444 procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) 445 case 'C': 446 n, err = strconv.Atoi(buf.String()) 447 if err != nil { 448 continue 449 } 450 procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) 451 csbi.cursorPosition.x -= short(n) 452 procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) 453 case 'D': 454 n, err = strconv.Atoi(buf.String()) 455 if err != nil { 456 continue 457 } 458 if n, err = strconv.Atoi(buf.String()); err == nil { 459 var csbi consoleScreenBufferInfo 460 procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) 461 csbi.cursorPosition.x += short(n) 462 procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) 463 } 464 case 'E': 465 n, err = strconv.Atoi(buf.String()) 466 if err != nil { 467 continue 468 } 469 procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) 470 csbi.cursorPosition.x = 0 471 csbi.cursorPosition.y += short(n) 472 procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) 473 case 'F': 474 n, err = strconv.Atoi(buf.String()) 475 if err != nil { 476 continue 477 } 478 procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) 479 csbi.cursorPosition.x = 0 480 csbi.cursorPosition.y -= short(n) 481 procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) 482 case 'G': 483 n, err = strconv.Atoi(buf.String()) 484 if err != nil { 485 continue 486 } 487 procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) 488 csbi.cursorPosition.x = short(n) 489 procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) 490 case 'H': 491 token := strings.Split(buf.String(), ";") 492 if len(token) != 2 { 493 continue 494 } 495 n1, err := strconv.Atoi(token[0]) 496 if err != nil { 497 continue 498 } 499 n2, err := strconv.Atoi(token[1]) 500 if err != nil { 501 continue 502 } 503 csbi.cursorPosition.x = short(n2) 504 csbi.cursorPosition.x = short(n1) 505 procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) 506 case 'J': 507 n, err := strconv.Atoi(buf.String()) 508 if err != nil { 509 continue 510 } 511 var cursor coord 512 switch n { 513 case 0: 514 cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} 515 case 1: 516 cursor = coord{x: csbi.window.left, y: csbi.window.top} 517 case 2: 518 cursor = coord{x: csbi.window.left, y: csbi.window.top} 519 } 520 var count, written dword 521 count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.size.y-csbi.cursorPosition.y)*csbi.size.x) 522 procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) 523 procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) 524 case 'K': 525 n, err := strconv.Atoi(buf.String()) 526 if err != nil { 527 continue 528 } 529 var cursor coord 530 switch n { 531 case 0: 532 cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} 533 case 1: 534 cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y} 535 case 2: 536 cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y} 537 } 538 var count, written dword 539 count = dword(csbi.size.x - csbi.cursorPosition.x) 540 procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) 541 procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) 542 case 'm': 543 attr := csbi.attributes 544 cs := buf.String() 545 if cs == "" { 546 procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(w.oldattr)) 547 continue 548 } 549 token := strings.Split(cs, ";") 550 for i := 0; i < len(token); i += 1 { 551 ns := token[i] 552 if n, err = strconv.Atoi(ns); err == nil { 553 switch { 554 case n == 0 || n == 100: 555 attr = w.oldattr 556 case 1 <= n && n <= 5: 557 attr |= foregroundIntensity 558 case n == 7: 559 attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4) 560 case 22 == n || n == 25 || n == 25: 561 attr |= foregroundIntensity 562 case n == 27: 563 attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4) 564 case 30 <= n && n <= 37: 565 attr = (attr & backgroundMask) 566 if (n-30)&1 != 0 { 567 attr |= foregroundRed 568 } 569 if (n-30)&2 != 0 { 570 attr |= foregroundGreen 571 } 572 if (n-30)&4 != 0 { 573 attr |= foregroundBlue 574 } 575 case n == 38: // set foreground color. 576 if i < len(token)-2 && (token[i+1] == "5" || token[i+1] == "05") { 577 if n256, err := strconv.Atoi(token[i+2]); err == nil { 578 if n256foreAttr == nil { 579 n256setup() 580 } 581 attr &= backgroundMask 582 attr |= n256foreAttr[n256] 583 i += 2 584 } 585 } else { 586 attr = attr & (w.oldattr & backgroundMask) 587 } 588 case n == 39: // reset foreground color. 589 attr &= backgroundMask 590 attr |= w.oldattr & foregroundMask 591 case 40 <= n && n <= 47: 592 attr = (attr & foregroundMask) 593 if (n-40)&1 != 0 { 594 attr |= backgroundRed 595 } 596 if (n-40)&2 != 0 { 597 attr |= backgroundGreen 598 } 599 if (n-40)&4 != 0 { 600 attr |= backgroundBlue 601 } 602 case n == 48: // set background color. 603 if i < len(token)-2 && token[i+1] == "5" { 604 if n256, err := strconv.Atoi(token[i+2]); err == nil { 605 if n256backAttr == nil { 606 n256setup() 607 } 608 attr &= foregroundMask 609 attr |= n256backAttr[n256] 610 i += 2 611 } 612 } else { 613 attr = attr & (w.oldattr & foregroundMask) 614 } 615 case n == 49: // reset foreground color. 616 attr &= foregroundMask 617 attr |= w.oldattr & backgroundMask 618 case 90 <= n && n <= 97: 619 attr = (attr & backgroundMask) 620 attr |= foregroundIntensity 621 if (n-90)&1 != 0 { 622 attr |= foregroundRed 623 } 624 if (n-90)&2 != 0 { 625 attr |= foregroundGreen 626 } 627 if (n-90)&4 != 0 { 628 attr |= foregroundBlue 629 } 630 case 100 <= n && n <= 107: 631 attr = (attr & foregroundMask) 632 attr |= backgroundIntensity 633 if (n-100)&1 != 0 { 634 attr |= backgroundRed 635 } 636 if (n-100)&2 != 0 { 637 attr |= backgroundGreen 638 } 639 if (n-100)&4 != 0 { 640 attr |= backgroundBlue 641 } 642 } 643 procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(attr)) 644 } 645 } 646 } 647 } 648 return len(data) - w.lastbuf.Len(), nil 649 } 650 651 type consoleColor struct { 652 rgb int 653 red bool 654 green bool 655 blue bool 656 intensity bool 657 } 658 659 func (c consoleColor) foregroundAttr() (attr word) { 660 if c.red { 661 attr |= foregroundRed 662 } 663 if c.green { 664 attr |= foregroundGreen 665 } 666 if c.blue { 667 attr |= foregroundBlue 668 } 669 if c.intensity { 670 attr |= foregroundIntensity 671 } 672 return 673 } 674 675 func (c consoleColor) backgroundAttr() (attr word) { 676 if c.red { 677 attr |= backgroundRed 678 } 679 if c.green { 680 attr |= backgroundGreen 681 } 682 if c.blue { 683 attr |= backgroundBlue 684 } 685 if c.intensity { 686 attr |= backgroundIntensity 687 } 688 return 689 } 690 691 var color16 = []consoleColor{ 692 consoleColor{0x000000, false, false, false, false}, 693 consoleColor{0x000080, false, false, true, false}, 694 consoleColor{0x008000, false, true, false, false}, 695 consoleColor{0x008080, false, true, true, false}, 696 consoleColor{0x800000, true, false, false, false}, 697 consoleColor{0x800080, true, false, true, false}, 698 consoleColor{0x808000, true, true, false, false}, 699 consoleColor{0xc0c0c0, true, true, true, false}, 700 consoleColor{0x808080, false, false, false, true}, 701 consoleColor{0x0000ff, false, false, true, true}, 702 consoleColor{0x00ff00, false, true, false, true}, 703 consoleColor{0x00ffff, false, true, true, true}, 704 consoleColor{0xff0000, true, false, false, true}, 705 consoleColor{0xff00ff, true, false, true, true}, 706 consoleColor{0xffff00, true, true, false, true}, 707 consoleColor{0xffffff, true, true, true, true}, 708 } 709 710 type hsv struct { 711 h, s, v float32 712 } 713 714 func (a hsv) dist(b hsv) float32 { 715 dh := a.h - b.h 716 switch { 717 case dh > 0.5: 718 dh = 1 - dh 719 case dh < -0.5: 720 dh = -1 - dh 721 } 722 ds := a.s - b.s 723 dv := a.v - b.v 724 return float32(math.Sqrt(float64(dh*dh + ds*ds + dv*dv))) 725 } 726 727 func toHSV(rgb int) hsv { 728 r, g, b := float32((rgb&0xFF0000)>>16)/256.0, 729 float32((rgb&0x00FF00)>>8)/256.0, 730 float32(rgb&0x0000FF)/256.0 731 min, max := minmax3f(r, g, b) 732 h := max - min 733 if h > 0 { 734 if max == r { 735 h = (g - b) / h 736 if h < 0 { 737 h += 6 738 } 739 } else if max == g { 740 h = 2 + (b-r)/h 741 } else { 742 h = 4 + (r-g)/h 743 } 744 } 745 h /= 6.0 746 s := max - min 747 if max != 0 { 748 s /= max 749 } 750 v := max 751 return hsv{h: h, s: s, v: v} 752 } 753 754 type hsvTable []hsv 755 756 func toHSVTable(rgbTable []consoleColor) hsvTable { 757 t := make(hsvTable, len(rgbTable)) 758 for i, c := range rgbTable { 759 t[i] = toHSV(c.rgb) 760 } 761 return t 762 } 763 764 func (t hsvTable) find(rgb int) consoleColor { 765 hsv := toHSV(rgb) 766 n := 7 767 l := float32(5.0) 768 for i, p := range t { 769 d := hsv.dist(p) 770 if d < l { 771 l, n = d, i 772 } 773 } 774 return color16[n] 775 } 776 777 func minmax3f(a, b, c float32) (min, max float32) { 778 if a < b { 779 if b < c { 780 return a, c 781 } else if a < c { 782 return a, b 783 } else { 784 return c, b 785 } 786 } else { 787 if a < c { 788 return b, c 789 } else if b < c { 790 return b, a 791 } else { 792 return c, a 793 } 794 } 795 } 796 797 var n256foreAttr []word 798 var n256backAttr []word 799 800 func n256setup() { 801 n256foreAttr = make([]word, 256) 802 n256backAttr = make([]word, 256) 803 t := toHSVTable(color16) 804 for i, rgb := range color256 { 805 c := t.find(rgb) 806 n256foreAttr[i] = c.foregroundAttr() 807 n256backAttr[i] = c.backgroundAttr() 808 } 809 }