|
1 |
| -import '../color/channel.dart'; |
2 |
| -import '../color/color.dart'; |
3 |
| -import '../font/bitmap_font.dart'; |
4 |
| -import '../image/image.dart'; |
5 |
| -import 'draw_pixel.dart'; |
6 |
| - |
7 |
| -/// Draw a string horizontally into [image] at |
8 |
| -/// position [x],[y] with the given [color]. |
9 |
| -/// If [x] is not specified, the string will be centered horizontally. |
10 |
| -/// If [y] is not specified, the string will be centered vertically. |
11 |
| -/// |
12 |
| -/// You can load your own font, or use one of the existing ones |
13 |
| -/// such as: arial14, arial24, or arial48. |
14 |
| -/// Fonts can be create with a tool such as: https://ttf2fnt.com/ |
15 |
| -Image drawString(Image image, String string, |
16 |
| - {required BitmapFont font, |
17 |
| - int? x, |
18 |
| - int? y, |
19 |
| - Color? color, |
20 |
| - bool rightJustify = false, |
21 |
| - bool wrap = false, |
22 |
| - Image? mask, |
23 |
| - Channel maskChannel = Channel.luminance}) { |
24 |
| - if (color?.a == 0) { |
25 |
| - return image; |
26 |
| - } |
27 |
| - |
28 |
| - var stringWidth = 0; |
29 |
| - var stringHeight = 0; |
30 |
| - |
31 |
| - final chars = string.codeUnits; |
32 |
| - for (var c in chars) { |
33 |
| - if (!font.characters.containsKey(c)) { |
34 |
| - continue; |
35 |
| - } |
36 |
| - final ch = font.characters[c]!; |
37 |
| - stringWidth += ch.xAdvance; |
38 |
| - if (ch.height + ch.yOffset > stringHeight) { |
39 |
| - stringHeight = ch.height + ch.yOffset; |
40 |
| - } |
41 |
| - } |
42 |
| - |
43 |
| - var sx = x ?? (image.width / 2).round() - (stringWidth / 2).round(); |
44 |
| - var sy = y ?? (image.height / 2).round() - (stringHeight / 2).round(); |
45 |
| - |
46 |
| - if (wrap) { |
47 |
| - final sentences = string.split(RegExp(r'\n')); |
48 |
| - |
49 |
| - for (var sentence in sentences) { |
50 |
| - final words = sentence.split(RegExp(r"\s+")); |
51 |
| - var subString = ""; |
52 |
| - var x2 = sx; |
53 |
| - |
54 |
| - for (var w in words) { |
55 |
| - final ws = StringBuffer() |
56 |
| - ..write(w) |
57 |
| - ..write(' '); |
58 |
| - w = ws.toString(); |
59 |
| - final chars = w.codeUnits; |
60 |
| - var wordWidth = 0; |
61 |
| - for (var c in chars) { |
62 |
| - if (c == 10) break; |
63 |
| - if (!font.characters.containsKey(c)) { |
64 |
| - wordWidth += font.base ~/ 2; |
65 |
| - continue; |
66 |
| - } |
67 |
| - final ch = font.characters[c]!; |
68 |
| - wordWidth += ch.xAdvance; |
69 |
| - } |
70 |
| - if ((x2 + wordWidth) > image.width) { |
71 |
| - // If there is a word that won't fit the starting x, stop drawing |
72 |
| - if ((sx == x2) || (sx + wordWidth > image.width)) { |
73 |
| - return image; |
74 |
| - } |
75 |
| - |
76 |
| - drawString(image, subString, |
77 |
| - font: font, |
78 |
| - x: sx, |
79 |
| - y: sy, |
80 |
| - color: color, |
81 |
| - mask: mask, |
82 |
| - maskChannel: maskChannel, |
83 |
| - rightJustify: rightJustify); |
84 |
| - |
85 |
| - subString = ""; |
86 |
| - x2 = sx; |
87 |
| - sy += stringHeight; |
88 |
| - subString += w; |
89 |
| - x2 += wordWidth; |
90 |
| - } else { |
91 |
| - subString += w; |
92 |
| - x2 += wordWidth; |
93 |
| - } |
94 |
| - |
95 |
| - if (subString.isNotEmpty) { |
96 |
| - drawString(image, subString, |
97 |
| - font: font, |
98 |
| - x: sx, |
99 |
| - y: sy, |
100 |
| - color: color, |
101 |
| - mask: mask, |
102 |
| - maskChannel: maskChannel, |
103 |
| - rightJustify: rightJustify); |
104 |
| - } |
105 |
| - } |
106 |
| - |
107 |
| - sy += stringHeight; |
108 |
| - } |
109 |
| - |
110 |
| - return image; |
111 |
| - } |
112 |
| - |
113 |
| - final origX = sx; |
114 |
| - final substrings = string.split(RegExp(r"[\n|\r]")); |
115 |
| - |
116 |
| - // print(substrings); |
117 |
| - |
118 |
| - for (var ss in substrings) { |
119 |
| - final chars = ss.codeUnits; |
120 |
| - // print("$ss = $chars"); |
121 |
| - if (rightJustify == true) { |
122 |
| - for (var c in chars) { |
123 |
| - if (!font.characters.containsKey(c)) { |
124 |
| - sx -= font.base ~/ 2; |
125 |
| - continue; |
126 |
| - } |
127 |
| - |
128 |
| - final ch = font.characters[c]!; |
129 |
| - sx -= ch.xAdvance; |
130 |
| - } |
131 |
| - } |
132 |
| - for (var c in chars) { |
133 |
| - if (!font.characters.containsKey(c)) { |
134 |
| - sx += font.base ~/ 2; |
135 |
| - continue; |
136 |
| - } |
137 |
| - |
138 |
| - final ch = font.characters[c]!; |
139 |
| - |
140 |
| - final x2 = sx + ch.width; |
141 |
| - final y2 = sy + ch.height; |
142 |
| - final cIter = ch.image.iterator..moveNext(); |
143 |
| - for (var yi = sy; yi < y2; ++yi) { |
144 |
| - for (var xi = sx; xi < x2; ++xi, cIter.moveNext()) { |
145 |
| - final p = cIter.current; |
146 |
| - drawPixel(image, xi + ch.xOffset, yi + ch.yOffset, p, |
147 |
| - filter: color, mask: mask, maskChannel: maskChannel); |
148 |
| - } |
149 |
| - } |
150 |
| - |
151 |
| - sx += ch.xAdvance; |
152 |
| - } |
153 |
| - |
154 |
| - sy = sy + stringHeight; |
155 |
| - sx = origX; |
156 |
| - } |
157 |
| - |
158 |
| - return image; |
159 |
| -} |
| 1 | +import '../color/channel.dart'; |
| 2 | +import '../color/color.dart'; |
| 3 | +import '../font/bitmap_font.dart'; |
| 4 | +import '../image/image.dart'; |
| 5 | +import 'draw_pixel.dart'; |
| 6 | + |
| 7 | +/// Draw a string horizontally into [image] at |
| 8 | +/// position [x],[y] with the given [color]. |
| 9 | +/// If [x] is not specified, the string will be centered horizontally. |
| 10 | +/// If [y] is not specified, the string will be centered vertically. |
| 11 | +/// |
| 12 | +/// You can load your own font, or use one of the existing ones |
| 13 | +/// such as: arial14, arial24, or arial48. |
| 14 | +/// Fonts can be create with a tool such as: https://ttf2fnt.com/ |
| 15 | +Image drawString(Image image, String string, |
| 16 | + {required BitmapFont font, |
| 17 | + int? x, |
| 18 | + int? y, |
| 19 | + Color? color, |
| 20 | + bool rightJustify = false, |
| 21 | + bool wrap = false, |
| 22 | + Image? mask, |
| 23 | + Channel maskChannel = Channel.luminance}) { |
| 24 | + if (color?.a == 0) { |
| 25 | + return image; |
| 26 | + } |
| 27 | + |
| 28 | + var stringWidth = 0; |
| 29 | + var stringHeight = 0; |
| 30 | + |
| 31 | + final chars = string.codeUnits; |
| 32 | + for (var c in chars) { |
| 33 | + if (!font.characters.containsKey(c)) { |
| 34 | + continue; |
| 35 | + } |
| 36 | + final ch = font.characters[c]!; |
| 37 | + stringWidth += ch.xAdvance; |
| 38 | + if (ch.height + ch.yOffset > stringHeight) { |
| 39 | + stringHeight = ch.height + ch.yOffset; |
| 40 | + } |
| 41 | + } |
| 42 | + |
| 43 | + var sx = x ?? (image.width / 2).round() - (stringWidth / 2).round(); |
| 44 | + var sy = y ?? (image.height / 2).round() - (stringHeight / 2).round(); |
| 45 | + |
| 46 | + if (wrap) { |
| 47 | + final sentences = string.split(RegExp(r'\n')); |
| 48 | + |
| 49 | + for (var sentence in sentences) { |
| 50 | + final words = sentence.split(RegExp(r"\s+")); |
| 51 | + var subString = ""; |
| 52 | + var x2 = sx; |
| 53 | + |
| 54 | + for (var w in words) { |
| 55 | + final ws = StringBuffer() |
| 56 | + ..write(w) |
| 57 | + ..write(' '); |
| 58 | + w = ws.toString(); |
| 59 | + final chars = w.codeUnits; |
| 60 | + var wordWidth = 0; |
| 61 | + for (var c in chars) { |
| 62 | + if (c == 10) break; |
| 63 | + if (!font.characters.containsKey(c)) { |
| 64 | + wordWidth += font.base ~/ 2; |
| 65 | + continue; |
| 66 | + } |
| 67 | + final ch = font.characters[c]!; |
| 68 | + wordWidth += ch.xAdvance; |
| 69 | + } |
| 70 | + if ((x2 + wordWidth) > image.width) { |
| 71 | + // If there is a word that won't fit the starting x, stop drawing |
| 72 | + if ((sx == x2) || (sx + wordWidth > image.width)) { |
| 73 | + return image; |
| 74 | + } |
| 75 | + |
| 76 | + drawString(image, subString, |
| 77 | + font: font, |
| 78 | + x: sx, |
| 79 | + y: sy, |
| 80 | + color: color, |
| 81 | + mask: mask, |
| 82 | + maskChannel: maskChannel, |
| 83 | + rightJustify: rightJustify); |
| 84 | + |
| 85 | + subString = ""; |
| 86 | + x2 = sx; |
| 87 | + sy += stringHeight; |
| 88 | + subString += w; |
| 89 | + x2 += wordWidth; |
| 90 | + } else { |
| 91 | + subString += w; |
| 92 | + x2 += wordWidth; |
| 93 | + } |
| 94 | + |
| 95 | + if (subString.isNotEmpty) { |
| 96 | + drawString(image, subString, |
| 97 | + font: font, |
| 98 | + x: sx, |
| 99 | + y: sy, |
| 100 | + color: color, |
| 101 | + mask: mask, |
| 102 | + maskChannel: maskChannel, |
| 103 | + rightJustify: rightJustify); |
| 104 | + } |
| 105 | + } |
| 106 | + |
| 107 | + sy += stringHeight; |
| 108 | + } |
| 109 | + |
| 110 | + return image; |
| 111 | + } |
| 112 | + |
| 113 | + final origX = sx; |
| 114 | + final substrings = string.split(RegExp(r"[\n\r]")); |
| 115 | + |
| 116 | + // print(substrings); |
| 117 | + |
| 118 | + for (var ss in substrings) { |
| 119 | + final chars = ss.codeUnits; |
| 120 | + // print("$ss = $chars"); |
| 121 | + if (rightJustify == true) { |
| 122 | + for (var c in chars) { |
| 123 | + if (!font.characters.containsKey(c)) { |
| 124 | + sx -= font.base ~/ 2; |
| 125 | + continue; |
| 126 | + } |
| 127 | + |
| 128 | + final ch = font.characters[c]!; |
| 129 | + sx -= ch.xAdvance; |
| 130 | + } |
| 131 | + } |
| 132 | + for (var c in chars) { |
| 133 | + if (!font.characters.containsKey(c)) { |
| 134 | + sx += font.base ~/ 2; |
| 135 | + continue; |
| 136 | + } |
| 137 | + |
| 138 | + final ch = font.characters[c]!; |
| 139 | + |
| 140 | + final x2 = sx + ch.width; |
| 141 | + final y2 = sy + ch.height; |
| 142 | + final cIter = ch.image.iterator..moveNext(); |
| 143 | + for (var yi = sy; yi < y2; ++yi) { |
| 144 | + for (var xi = sx; xi < x2; ++xi, cIter.moveNext()) { |
| 145 | + final p = cIter.current; |
| 146 | + drawPixel(image, xi + ch.xOffset, yi + ch.yOffset, p, |
| 147 | + filter: color, mask: mask, maskChannel: maskChannel); |
| 148 | + } |
| 149 | + } |
| 150 | + |
| 151 | + sx += ch.xAdvance; |
| 152 | + } |
| 153 | + |
| 154 | + sy = sy + stringHeight; |
| 155 | + sx = origX; |
| 156 | + } |
| 157 | + |
| 158 | + return image; |
| 159 | +} |
0 commit comments