1 /**
2  * \file
3  *
4  * \brief Font and text drawing routines
5  *
6  * Copyright (c) 2010-2015 Atmel Corporation. All rights reserved.
7  *
8  * \asf_license_start
9  *
10  * \page License
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions are met:
14  *
15  * 1. Redistributions of source code must retain the above copyright notice,
16  *    this list of conditions and the following disclaimer.
17  *
18  * 2. Redistributions in binary form must reproduce the above copyright notice,
19  *    this list of conditions and the following disclaimer in the documentation
20  *    and/or other materials provided with the distribution.
21  *
22  * 3. The name of Atmel may not be used to endorse or promote products derived
23  *    from this software without specific prior written permission.
24  *
25  * 4. This software may only be redistributed and used in connection with an
26  *    Atmel microcontroller product.
27  *
28  * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
29  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
30  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
31  * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
32  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
36  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
37  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38  * POSSIBILITY OF SUCH DAMAGE.
39  *
40  * \asf_license_stop
41  *
42  */
43 /*
44  * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
45  */
46 #include "stddef.h"
47 #include "assert.h"
48 
49 #include "gfx_mono.h"
50 #include "gfx_mono_text.h"
51 #include <asf.h>
52 
53 #ifndef CONFIG_FONT_PIXELS_PER_BYTE
54 # define CONFIG_FONT_PIXELS_PER_BYTE    8
55 #endif
56 
57 #define EXTMEM_BUF_SIZE                 20
58 
59 #if defined(CONFIG_HUGEMEM) || defined(__DOXYGEN__)
60 
61 /**
62  * \internal
63  * \brief Helper function that draws a character from a font in hugemem
64  *        to the display
65  *
66  * This function will first calculate the start offset in the font character
67  * data before iterating over the specific character data.
68  *
69  * Only pixels in the character that should be enabled are done so, the caller
70  * is required to prepare the drawing area before printing a character to it.
71  * This is done by the gfx_mono_draw_string() and
72  * gfx_mono_draw_progmem_string() functions.
73  *
74  * \param[in] ch       Character to be drawn
75  * \param[in] x        X coordinate on screen.
76  * \param[in] y        Y coordinate on screen.
77  * \param[in] font     Font to draw character in
78  */
gfx_mono_draw_char_hugemem(const char ch,const gfx_coord_t x,const gfx_coord_t y,const struct font * font)79 static void gfx_mono_draw_char_hugemem(const char ch, const gfx_coord_t x,
80 		const gfx_coord_t y, const struct font *font)
81 {
82 	uint8_t i;
83 	uint8_t char_row_size;
84 	uint8_t glyph_size;
85 	uint16_t glyph_data_offset;
86 	uint8_t char_buff[EXTMEM_BUF_SIZE];
87 	uint8_t buffer_pos;
88 	uint8_t rows_left;
89 
90 	/* Sanity check on parameters, assert if font is NULL. */
91 	Assert(font != NULL);
92 
93 	gfx_coord_t inc_x = x;
94 	gfx_coord_t inc_y = y;
95 
96 	char_row_size = font->width / CONFIG_FONT_PIXELS_PER_BYTE;
97 	if (font->width % CONFIG_FONT_PIXELS_PER_BYTE) {
98 		char_row_size++;
99 	}
100 
101 	glyph_size = char_row_size * font->height;
102 	glyph_data_offset = glyph_size * ((uint8_t)ch - font->first_char);
103 	buffer_pos = EXTMEM_BUF_SIZE;
104 	rows_left = font->height;
105 
106 	do {
107 		static uint8_t glyph_byte = 0;
108 		uint8_t pixelsToDraw = font->width;
109 
110 		for (i = 0; i < pixelsToDraw; i++) {
111 			if (i % CONFIG_FONT_PIXELS_PER_BYTE == 0) {
112 				/* Read another byte from hugemem */
113 				if (buffer_pos >= EXTMEM_BUF_SIZE) {
114 					hugemem_ptr_t source
115 						= font->data.hugemem;
116 					source = (hugemem_ptr_t)
117 							((uint32_t)source +
118 							glyph_data_offset);
119 
120 					hugemem_read_block(char_buff, source,
121 							EXTMEM_BUF_SIZE);
122 
123 					glyph_data_offset += EXTMEM_BUF_SIZE;
124 					buffer_pos = 0;
125 				}
126 
127 				glyph_byte = char_buff[buffer_pos];
128 				buffer_pos++;
129 			}
130 
131 			/* Draw bit of glyph to screen */
132 			if ((glyph_byte & 0x80)) {
133 				gfx_mono_draw_pixel(inc_x, inc_y,
134 						GFX_PIXEL_SET);
135 			}
136 
137 			inc_x += 1;
138 			glyph_byte <<= 1;
139 		}
140 
141 		inc_y += 1;
142 		inc_x = x;
143 	} while (--rows_left > 0);
144 }
145 
146 #endif
147 
148 /**
149  * \internal
150  * \brief Helper function that draws a character from a font in progmem
151  *        to the display
152  *
153  * This function will first calculate the start offset in the font character
154  * data before iterating over the specific character data.
155  *
156  * Only pixels in the character that should be enabled are done so, the caller
157  * is required to prepare the drawing area before printing a character to it.
158  * This is done by the gfx_mono_draw_string() and
159  * gfx_mono_draw_progmem_string() functions.
160  *
161  * \param[in] ch       Character to be drawn
162  * \param[in] x        X coordinate on screen.
163  * \param[in] y        Y coordinate on screen.
164  * \param[in] font     Font to draw character in
165  */
gfx_mono_draw_char_progmem(const char ch,const gfx_coord_t x,const gfx_coord_t y,const struct font * font)166 static void gfx_mono_draw_char_progmem(const char ch, const gfx_coord_t x,
167 		const gfx_coord_t y, const struct font *font)
168 {
169 	uint8_t PROGMEM_PTR_T glyph_data;
170 	uint16_t glyph_data_offset;
171 	uint8_t char_row_size;
172 	uint8_t rows_left;
173 	uint8_t i;
174 
175 	/* Sanity check on parameters, assert if font is NULL. */
176 	Assert(font != NULL);
177 
178 	gfx_coord_t inc_x = x;
179 	gfx_coord_t inc_y = y;
180 
181 	char_row_size = font->width / CONFIG_FONT_PIXELS_PER_BYTE;
182 	if (font->width % CONFIG_FONT_PIXELS_PER_BYTE) {
183 		char_row_size++;
184 	}
185 
186 	glyph_data_offset = char_row_size * font->height *
187 			((uint8_t)ch - font->first_char);
188 	glyph_data = font->data.progmem + glyph_data_offset;
189 	rows_left = font->height;
190 
191 	do {
192 		uint8_t glyph_byte = 0;
193 		uint8_t pixelsToDraw = font->width;
194 
195 		for (i = 0; i < pixelsToDraw; i++) {
196 			if (i % CONFIG_FONT_PIXELS_PER_BYTE == 0) {
197 				glyph_byte = PROGMEM_READ_BYTE(glyph_data);
198 				glyph_data++;
199 			}
200 
201 			if ((glyph_byte & 0x80)) {
202 				gfx_mono_draw_pixel(inc_x, inc_y,
203 						GFX_PIXEL_SET);
204 			}
205 
206 			inc_x += 1;
207 			glyph_byte <<= 1;
208 		}
209 
210 		inc_y += 1;
211 		inc_x = x;
212 		rows_left--;
213 	} while (rows_left > 0);
214 }
215 
216 /**
217  * \brief Draws a character to the display
218  *
219  * \param[in] c        Character to be drawn
220  * \param[in] x        X coordinate on screen.
221  * \param[in] y        Y coordinate on screen.
222  * \param[in] font     Font to draw character in
223  */
gfx_mono_draw_char(const char c,const gfx_coord_t x,const gfx_coord_t y,const struct font * font)224 void gfx_mono_draw_char(const char c, const gfx_coord_t x, const gfx_coord_t y,
225 		const struct font *font)
226 {
227 	gfx_mono_draw_filled_rect(x, y, font->width, font->height,
228 			GFX_PIXEL_CLR);
229 
230 	switch (font->type) {
231 	case FONT_LOC_PROGMEM:
232 		gfx_mono_draw_char_progmem(c, x, y, font);
233 		break;
234 
235 #ifdef CONFIG_HUGEMEM
236 	case FONT_LOC_HUGEMEM:
237 		gfx_mono_draw_char_hugemem(c, x, y, font);
238 		break;
239 
240 #endif
241 	default:
242 		/* Unsupported mode, call assert */
243 		Assert(false);
244 		break;
245 	}
246 }
247 
248 /**
249  * \brief Draws a string to the display
250  *
251  * This function will draw a string located in memory to the display.
252  *
253  * \param[in] str       Pointer to string
254  * \param[in] x         X coordinate on screen.
255  * \param[in] y         Y coordinate on screen.
256  * \param[in] font      Font to draw string in
257  */
gfx_mono_draw_string(const char * str,gfx_coord_t x,gfx_coord_t y,const struct font * font)258 void gfx_mono_draw_string(const char *str, gfx_coord_t x, gfx_coord_t y,
259 		const struct font *font)
260 {
261 	/* Save X in order to know where to return to on CR. */
262 	const gfx_coord_t start_of_string_position_x = x;
263 
264 	/* Sanity check on parameters, assert if str or font is NULL. */
265 	Assert(str != NULL);
266 	Assert(font != NULL);
267 
268 	/* Draw characters until trailing null byte */
269 	do {
270 		/* Handle '\n' as newline, draw normal characters. */
271 		if (*str == '\n') {
272 			x = start_of_string_position_x;
273 			y += font->height + 1;
274 		} else if (*str == '\r') {
275 			/* Skip '\r' characters. */
276 		} else {
277 			gfx_mono_draw_char(*str, x, y, font);
278 			x += font->width;
279 		}
280 	} while (*(++str));
281 }
282 
283 /**
284  * \brief Draws a string located in program memory to the display
285  *
286  * This function will draw a string located in program memory to the display,
287  * this differs from gfx_mono_draw_string() by using constant string data from
288  * the program memory instead of string data in RAM.
289  *
290  * Using program memory for constant strings will reduce the applications need
291  * for RAM, and thus lower the overall size footprint.
292  *
293  * \param[in] str       Pointer to string located in program memory
294  * \param[in] x         X coordinate on screen.
295  * \param[in] y         Y coordinate on screen.
296  * \param[in] font      Font to draw string in
297  */
gfx_mono_draw_progmem_string(char PROGMEM_PTR_T str,gfx_coord_t x,gfx_coord_t y,const struct font * font)298 void gfx_mono_draw_progmem_string(char PROGMEM_PTR_T str, gfx_coord_t x,
299 		gfx_coord_t y, const struct font *font)
300 {
301 	char temp_char;
302 
303 	/* Sanity check on parameters, assert if str or font is NULL. */
304 	Assert(str != NULL);
305 	Assert(font != NULL);
306 
307 	/* Save X in order to know where to return to on CR. */
308 	const gfx_coord_t start_of_string_position_x = x;
309 
310 	/* Draw characters until trailing null byte */
311 	temp_char = PROGMEM_READ_BYTE((uint8_t PROGMEM_PTR_T)str);
312 
313 	while (temp_char) {
314 		/* Handle '\n' as newline, draw normal characters. */
315 		if (temp_char == '\n') {
316 			x = start_of_string_position_x;
317 			y += font->height + 1;
318 		} else if (temp_char == '\r') {
319 			/* Skip '\r' characters. */
320 		} else {
321 			gfx_mono_draw_char(temp_char, x, y, font);
322 			x += font->width;
323 		}
324 
325 		temp_char = PROGMEM_READ_BYTE((uint8_t PROGMEM_PTR_T)(++str));
326 	}
327 }
328 
329 /**
330  * \brief Computes the bounding box of a string
331  *
332  * \note If string is empty the returned width will be 1 pixel and the height
333  * equal to the font height.
334  *
335  * \param[in] str      String to calculate bounding box for
336  * \param[in] font     Font used
337  * \param[in] width    Pointer to width result
338  * \param[in] height   Pointer to height result
339  */
gfx_mono_get_string_bounding_box(const char * str,const struct font * font,gfx_coord_t * width,gfx_coord_t * height)340 void gfx_mono_get_string_bounding_box(const char *str, const struct font *font,
341 		gfx_coord_t *width, gfx_coord_t *height)
342 {
343 	gfx_coord_t font_width = font->width;
344 	gfx_coord_t font_height = font->height;
345 
346 	gfx_coord_t max_width = 1;
347 	gfx_coord_t max_height = font_height;
348 	gfx_coord_t x = 0;
349 
350 	/* Sanity check on parameters, assert if str or font is NULL. */
351 	Assert(str != NULL);
352 	Assert(font != NULL);
353 
354 	/* Handle each character until trailing null byte */
355 	do {
356 		/* Handle '\n' as newline, draw normal characters. */
357 		if (*str == '\n') {
358 			x = 0;
359 			max_height += font_height;
360 		} else if (*str == '\r') {
361 			/* Skip '\r' characters. */
362 		} else {
363 			x += font_width;
364 			if (x > max_width) {
365 				max_width = x;
366 			}
367 		}
368 	} while (*(++str));
369 
370 	/* Return values through references */
371 	*width = max_width;
372 	*height = max_height;
373 }
374 
375 /**
376  * \brief Computes the bounding box of a string located in program memory
377  *
378  * \note If string is empty the returned width will be 1 pixel and the height
379  * equal to the font height.
380  *
381  * \param[in] str      String in program memory to calculate bounding box for
382  * \param[in] font     Font used
383  * \param[in] width    Pointer to width result
384  * \param[in] height   Pointer to height result
385  */
gfx_mono_get_progmem_string_bounding_box(char PROGMEM_PTR_T str,const struct font * font,gfx_coord_t * width,gfx_coord_t * height)386 void gfx_mono_get_progmem_string_bounding_box(char PROGMEM_PTR_T str,
387 		const struct font *font, gfx_coord_t *width,
388 		gfx_coord_t *height)
389 {
390 	gfx_coord_t font_width = font->width;
391 	gfx_coord_t font_height = font->height;
392 
393 	char temp_char;
394 	gfx_coord_t max_width = 1;
395 	gfx_coord_t max_height = font_height;
396 	gfx_coord_t x = 0;
397 
398 	/* Sanity check on parameters, assert if str or font is NULL. */
399 	Assert(str != NULL);
400 	Assert(font != NULL);
401 
402 	/* Handle each character until trailing null byte */
403 	temp_char = PROGMEM_READ_BYTE((uint8_t PROGMEM_PTR_T)str);
404 
405 	while (temp_char) {
406 		/* Handle '\n' as newline, draw normal characters. */
407 		if (temp_char == '\n') {
408 			x = 0;
409 			max_height += font_height;
410 		} else if (*str == '\r') {
411 			/* Skip '\r' characters. */
412 		} else {
413 			x += font_width;
414 			if (x > max_width) {
415 				max_width = x;
416 			}
417 		}
418 
419 		temp_char = PROGMEM_READ_BYTE((uint8_t PROGMEM_PTR_T)(++str));
420 	}
421 
422 	/* Return values through references */
423 	*width = max_width;
424 	*height = max_height;
425 }
426