blob: 23bfa5de7f1acc27ef9093bb824b7c2b15e22200 [file] [log] [blame]
zongqiang.zhang0c6a0882019-08-07 14:48:21 +08001
2/**
3 \file text.c
4 \brief Functions relating to using text fonts of all sizes.
5 \author Andy Gock
6 */
7
8/*
9 Copyright (c) 2012, Andy Gock
10
11 All rights reserved.
12
13 Redistribution and use in source and binary forms, with or without
14 modification, are permitted provided that the following conditions are met:
15 * Redistributions of source code must retain the above copyright
16 notice, this list of conditions and the following disclaimer.
17 * Redistributions in binary form must reproduce the above copyright
18 notice, this list of conditions and the following disclaimer in the
19 documentation and/or other materials provided with the distribution.
20 * Neither the name of Andy Gock nor the
21 names of its contributors may be used to endorse or promote products
22 derived from this software without specific prior written permission.
23
24 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
25 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 DISCLAIMED. IN NO EVENT SHALL ANDY GOCK BE LIABLE FOR ANY
28 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34*/
35
36#include "glcd.h"
37
38extern uint8_t *glcd_buffer_selected;
39extern glcd_BoundingBox_t *glcd_bbox_selected;
40
41glcd_FontConfig_t font_current;
42
43#if defined(GLCD_DEVICE_AVR8)
44void glcd_set_font(PGM_P font_table, uint8_t width, uint8_t height, char start_char, char end_char)
45#else
46void glcd_set_font(const char * font_table, uint8_t width, uint8_t height, char start_char, char end_char)
47#endif
48{
49 /* Supports variable width fonts */
50 font_current.font_table = font_table;
51 font_current.width = width;
52 font_current.height = height;
53 font_current.start_char = start_char;
54 font_current.end_char = end_char;
55 font_current.table_type = MIKRO; /* Only supports MikroElektronika generated format at the moment */
56}
57
58#if defined(GLCD_DEVICE_AVR8)
59void glcd_font(PGM_P font_table, uint8_t width, uint8_t height, char start_char, char end_char, font_table_type_t type)
60#else
61void glcd_font(const char * font_table, uint8_t width, uint8_t height, char start_char, char end_char, font_table_type_t type)
62#endif
63{
64 /* Supports variable width fonts */
65 font_current.font_table = font_table;
66 font_current.width = width;
67 font_current.height = height;
68 font_current.start_char = start_char;
69 font_current.end_char = end_char;
70 font_current.table_type = type; /* Only supports MikroElektronika generated format at the moment */
71}
72
73uint8_t glcd_draw_char_xy(uint8_t x, uint8_t y, char c)
74{
75 if (c < font_current.start_char || c > font_current.end_char) {
76 c = '.';
77 }
78
79 if (font_current.table_type == STANG) {
80 /* Font table in Pascal Stang format (single byte height with with no width specifier) */
81 /* Maximum height of 8 bits only */
82
83 uint8_t i;
84 for ( i = 0; i < font_current.width; i++ ) {
85#if defined(GLCD_DEVICE_AVR8)
86 uint8_t dat = pgm_read_byte( font_current.font_table + ((c - font_current.start_char) * (font_current.width)) + i );
87#else
88 uint8_t dat = *( font_current.font_table + ((c - font_current.start_char) * (font_current.width)) + i );
89#endif
90 uint8_t j;
91
92 if(glcd_get_reverse_sta())
93 dat = ~dat;
94 for (j = 0; j < 8; j++) {
95 /* Set pixel color for each bit of the column (8-bits) */
96 if (x+i >= GLCD_LCD_WIDTH || y+j >= GLCD_LCD_HEIGHT) {
97 /* Don't try and write past the dimensions of the LCD */
98 return 0;
99 }
100 if (dat & (1<<j)) {
101 glcd_set_pixel(x+i,y+j,BLACK);
102 } else {
103 glcd_set_pixel(x+i,y+j,WHITE);
104 }
105 }
106 }
107
108 /* always return how many pixels of width were written */
109 /* here for "stang" format fonts, it is always fixed */
110 return font_current.width;
111
112 } else if (font_current.table_type == MIKRO) {
113 /* Font table in MikroElecktronica format
114 - multi row fonts allowed (more than 8 pixels high)
115 - variable width fonts allowed
116 a complete column is written before moving to the next */
117
118 uint8_t i;
119 uint8_t var_width;
120 uint8_t bytes_high;
121 uint8_t bytes_per_char;
122 const char *p;
123
124 if ((font_current.height % 8) > 0){
125 bytes_high = (font_current.height / 8) + 1;
126 }
127 else{
128 bytes_high = (font_current.height / 8);
129 }
130 bytes_per_char = font_current.width * bytes_high + 1; /* The +1 is the width byte at the start */
131
132 p = font_current.font_table + (c - font_current.start_char) * bytes_per_char;
133
134 /* The first byte per character is always the width of the character */
135#if defined(GLCD_DEVICE_AVR8)
136 var_width = pgm_read_byte(p);
137#else
138 var_width = *p;
139#endif
140 p++; /* Step over the variable width field */
141
142 /*
143 if (x+var_width >= GLCD_LCD_WIDTH || y+font_current.height >= GLCD_LCD_HEIGHT) {
144 return;
145 }
146 */
147
148 for ( i = 0; i < var_width; i++ ) {
149 uint8_t j;
150 for ( j = 0; j < bytes_high; j++ ) {
151#if defined(GLCD_DEVICE_AVR8)
152 uint8_t dat = pgm_read_byte( p + i*bytes_high + j );
153#else
154 uint8_t dat = *( p + i*bytes_high + j );
155#endif
156 uint8_t bit;
157
158 if(glcd_get_reverse_sta())
159 dat = ~dat;
160 for (bit = 0; bit < 8; bit++) {
161
162 if (x+i >= GLCD_LCD_WIDTH || y+j*8+bit >= GLCD_LCD_HEIGHT) {
163 /* Don't write past the dimensions of the LCD, skip the entire char */
164 return 0;
165 }
166
167 /* We should not write if the y bit exceeds font height */
168 if ((j*8 + bit) >= font_current.height) {
169 /* Skip the bit */
170 continue;
171 }
172
173 if (dat & (1<<bit)) {
174 glcd_set_pixel(x+i,y+j*8+bit,BLACK);
175 } else {
176 glcd_set_pixel(x+i,y+j*8+bit,WHITE);
177 }
178 }
179 }
180 }
181 return var_width;
182
183 } else if (font_current.table_type == GLCD_UTILS) {
184 /* Font table format of glcd-utils
185 - A complete row is written first (not completed columns)
186 - Width not stored, but we can search and determine it
187 - Not yet supported */
188
189 uint8_t var_width, n;
190 uint8_t bytes_high, bytes_per_char;
191 const char *p;
192 uint8_t j;
193
194 bytes_high = font_current.height / 8 + 1;
195 bytes_per_char = font_current.width * bytes_high;
196
197 /* Point to chars first byte */
198 p = font_current.font_table + (c - font_current.start_char) * bytes_per_char;
199
200 /* Determine the width of the character */
201 var_width = font_current.width;
202
203 n = 0; /* How many columns back from the end */
204
205 while (1) {
206 uint8_t max_byte = 0;
207 uint8_t row = 0;
208
209 for (row = 0; row < bytes_high; row++) {
210 uint8_t offset;
211 offset = (font_current.width - 1 - n) * row;
212 max_byte = *(p + offset);
213 }
214 if (max_byte == 0) {
215 /* column is empty for all rows, go left and test again */
216 /* reduce variable width by 1 */
217 var_width--;
218 if (var_width == 0) {
219 break;
220 }
221 } else {
222 break; /* Part of a character was found */
223 }
224 n++;
225 }
226
227 /* Uncomment line below, to force fixed width, for debugging only */
228 //var_width = font_current.width; // bypass auto width detection, treat as fixed width
229
230 /* For glcd-utils format, we write one complete row at a time */
231 /* loop as rows, 1st row, j=0 */
232 for ( j = 0; j < bytes_high; j++ ) {
233 /* Loop one row at a time */
234
235 uint8_t i;
236 for ( i = 0; i < var_width; i++ ) {
237 /* Loop one column at a time */
238
239 uint8_t dat, bit;
240
241#if defined(GLCD_DEVICE_AVR8)
242 dat = pgm_read_byte( p + j*font_current.width + i );
243#else
244 dat = *( p + j*font_current.width + i );
245#endif
246
247 if(glcd_get_reverse_sta())
248 dat = ~dat;
249
250 for (bit = 0; bit < 8; bit++) {
251
252 if ((x+i) >= GLCD_LCD_WIDTH || (y+j*8+bit) >= GLCD_LCD_HEIGHT) {
253 /* Don't write past the dimensions of the LCD, skip the entire char */
254 return 0;
255 }
256
257 /* We should not write if the y bit exceeds font height */
258 if ((j*8 + bit) >= font_current.height) {
259 /* Skip the bit */
260 continue;
261 }
262
263 if (dat & (1<<bit)) {
264 glcd_set_pixel(x+i,y+j*8+bit,BLACK);
265 } else {
266 glcd_set_pixel(x+i,y+j*8+bit,WHITE);
267 }
268 }
269 } /* i */
270 } /* j */
271
272 return var_width; /* Number of columns written to display */
273
274 } else {
275 /* Don't recognise the font table */
276 return 0;
277
278 }
279
280}
281
282void glcd_draw_string_xy(uint8_t x, uint8_t y, char *c)
283{
284 uint8_t width;
285
286 if (y > (GLCD_LCD_HEIGHT - font_current.height - 1)) {
287 /* Character won't fit */
288 return;
289 }
290
291 while (*c) {
292 width = glcd_draw_char_xy(x,y,*c);
293 x += (width + 1);
294 c++;
295 }
296}
297
298void glcd_draw_string_xy_P(uint8_t x, uint8_t y, const char *str)
299{
300 uint8_t width;
301
302 if (y > (GLCD_LCD_HEIGHT - font_current.height - 1)) {
303 /* Character won't fit */
304 return;
305 }
306
307 while (1) {
308#if defined(GLCD_DEVICE_AVR8)
309 char c = pgm_read_byte(str++);
310#else
311 char c = *(str++);
312#endif
313 if (!c)
314 return;
315
316 width = glcd_draw_char_xy(x,y,c);
317 x += (width + 1);
318 c++;
319 }
320}
321