1
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:
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:
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:
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:
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 }
810
View as plain text