asciiraster.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. /*
  2. * Public Domain Software
  3. *
  4. * I (Matthias Ladkau) am the author of the source code in this file.
  5. * I have placed the source code in this file in the public domain.
  6. *
  7. * For further information see: http://creativecommons.org/publicdomain/zero/1.0/
  8. */
  9. /*
  10. Package imageutil contains utility function to create/manipulate images.
  11. Asciiraster contains support for raster fonts for images. Using RenderSymbols you
  12. can add text and symbols to an image. By specifying a symbol map containing ASCII art
  13. it is possible to define how each rune should be rendered.
  14. */
  15. package imageutil
  16. import (
  17. "bufio"
  18. "bytes"
  19. "fmt"
  20. "image"
  21. "image/color"
  22. "unicode"
  23. )
  24. /*
  25. SymbolSpacing defines the spacing in pixels between two symbols
  26. */
  27. var SymbolSpacing = 1
  28. /*
  29. SpaceSymbolSpacing defines the space in pixels of a space character if the
  30. character is not defined in the font map
  31. */
  32. var SpaceSymbolSpacing = 5
  33. /*
  34. RenderSymbols renders the symbols in the given string str at the given point p in the
  35. given Image img in the color col using smap as symbol mapping.
  36. */
  37. func RenderSymbols(img image.Image, p image.Point, str string,
  38. col color.Color, smap map[rune]string) (image.Image, error) {
  39. var offset int
  40. imgc := wrapImage(img)
  41. // Iterate over the string
  42. for _, r := range str {
  43. sym, ok := smap[r]
  44. if !ok {
  45. if unicode.IsSpace(r) {
  46. // If a space character is encountered and it is not defined in the map
  47. // then just move the offset and continue
  48. offset += SpaceSymbolSpacing
  49. continue
  50. }
  51. return nil, fmt.Errorf("Cannot find mapping for rune: %q", r)
  52. }
  53. sline := 0
  54. rwidth := 0
  55. // Go through the symbold line by line
  56. scanner := bufio.NewScanner(bytes.NewBufferString(sym))
  57. for scanner.Scan() {
  58. line := scanner.Text()
  59. // Set max width of symbol
  60. if l := len(line); rwidth < l {
  61. rwidth = l
  62. }
  63. soffset := 0
  64. for _, sr := range line {
  65. // Draw each pixel
  66. if !(unicode.IsSpace(sr) || unicode.IsControl(sr)) {
  67. imgc.Set(offset+soffset+p.X, sline+p.Y, col)
  68. }
  69. soffset++
  70. }
  71. sline++
  72. }
  73. // Advance the offset
  74. offset += rwidth + SymbolSpacing
  75. }
  76. return imgc, nil
  77. }
  78. /*
  79. wrapImage wraps a given image.
  80. */
  81. func wrapImage(img image.Image) *imageWrapper {
  82. return &imageWrapper{img, make(map[image.Point]color.Color)}
  83. }
  84. /*
  85. imageWrapper is a wrapper class for images which allows setting single pixels.
  86. */
  87. type imageWrapper struct {
  88. image.Image // Original image
  89. pixMap map[image.Point]color.Color // Modified pixels
  90. }
  91. /*
  92. Set sets the color of the pixel at (x, y).
  93. */
  94. func (m *imageWrapper) Set(x, y int, c color.Color) {
  95. m.pixMap[image.Point{x, y}] = c
  96. }
  97. /*
  98. At returns the color of the pixel at (x, y).
  99. */
  100. func (m *imageWrapper) At(x, y int) color.Color {
  101. if c := m.pixMap[image.Point{x, y}]; c != nil {
  102. return c
  103. }
  104. return m.Image.At(x, y)
  105. }