/**
* \file
*
* \brief Font and text drawing routines
*
* Copyright (c) 2010-2015 Atmel Corporation. All rights reserved.
*
* \asf_license_start
*
* \page License
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. The name of Atmel may not be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 4. This software may only be redistributed and used in connection with an
* Atmel microcontroller product.
*
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* \asf_license_stop
*
*/
/*
* Support and FAQ: visit Atmel Support
*/
#include "stddef.h"
#include "assert.h"
#include "gfx_mono.h"
#include "gfx_mono_text.h"
#include
#ifndef CONFIG_FONT_PIXELS_PER_BYTE
# define CONFIG_FONT_PIXELS_PER_BYTE 8
#endif
#define EXTMEM_BUF_SIZE 20
#if defined(CONFIG_HUGEMEM) || defined(__DOXYGEN__)
/**
* \internal
* \brief Helper function that draws a character from a font in hugemem
* to the display
*
* This function will first calculate the start offset in the font character
* data before iterating over the specific character data.
*
* Only pixels in the character that should be enabled are done so, the caller
* is required to prepare the drawing area before printing a character to it.
* This is done by the gfx_mono_draw_string() and
* gfx_mono_draw_progmem_string() functions.
*
* \param[in] ch Character to be drawn
* \param[in] x X coordinate on screen.
* \param[in] y Y coordinate on screen.
* \param[in] font Font to draw character in
*/
static void gfx_mono_draw_char_hugemem(const char ch, const gfx_coord_t x,
const gfx_coord_t y, const struct font *font)
{
uint8_t i;
uint8_t char_row_size;
uint8_t glyph_size;
uint16_t glyph_data_offset;
uint8_t char_buff[EXTMEM_BUF_SIZE];
uint8_t buffer_pos;
uint8_t rows_left;
/* Sanity check on parameters, assert if font is NULL. */
Assert(font != NULL);
gfx_coord_t inc_x = x;
gfx_coord_t inc_y = y;
char_row_size = font->width / CONFIG_FONT_PIXELS_PER_BYTE;
if (font->width % CONFIG_FONT_PIXELS_PER_BYTE) {
char_row_size++;
}
glyph_size = char_row_size * font->height;
glyph_data_offset = glyph_size * ((uint8_t)ch - font->first_char);
buffer_pos = EXTMEM_BUF_SIZE;
rows_left = font->height;
do {
static uint8_t glyph_byte = 0;
uint8_t pixelsToDraw = font->width;
for (i = 0; i < pixelsToDraw; i++) {
if (i % CONFIG_FONT_PIXELS_PER_BYTE == 0) {
/* Read another byte from hugemem */
if (buffer_pos >= EXTMEM_BUF_SIZE) {
hugemem_ptr_t source
= font->data.hugemem;
source = (hugemem_ptr_t)
((uint32_t)source +
glyph_data_offset);
hugemem_read_block(char_buff, source,
EXTMEM_BUF_SIZE);
glyph_data_offset += EXTMEM_BUF_SIZE;
buffer_pos = 0;
}
glyph_byte = char_buff[buffer_pos];
buffer_pos++;
}
/* Draw bit of glyph to screen */
if ((glyph_byte & 0x80)) {
gfx_mono_draw_pixel(inc_x, inc_y,
GFX_PIXEL_SET);
}
inc_x += 1;
glyph_byte <<= 1;
}
inc_y += 1;
inc_x = x;
} while (--rows_left > 0);
}
#endif
/**
* \internal
* \brief Helper function that draws a character from a font in progmem
* to the display
*
* This function will first calculate the start offset in the font character
* data before iterating over the specific character data.
*
* Only pixels in the character that should be enabled are done so, the caller
* is required to prepare the drawing area before printing a character to it.
* This is done by the gfx_mono_draw_string() and
* gfx_mono_draw_progmem_string() functions.
*
* \param[in] ch Character to be drawn
* \param[in] x X coordinate on screen.
* \param[in] y Y coordinate on screen.
* \param[in] font Font to draw character in
*/
static void gfx_mono_draw_char_progmem(const char ch, const gfx_coord_t x,
const gfx_coord_t y, const struct font *font)
{
uint8_t PROGMEM_PTR_T glyph_data;
uint16_t glyph_data_offset;
uint8_t char_row_size;
uint8_t rows_left;
uint8_t i;
/* Sanity check on parameters, assert if font is NULL. */
Assert(font != NULL);
gfx_coord_t inc_x = x;
gfx_coord_t inc_y = y;
char_row_size = font->width / CONFIG_FONT_PIXELS_PER_BYTE;
if (font->width % CONFIG_FONT_PIXELS_PER_BYTE) {
char_row_size++;
}
glyph_data_offset = char_row_size * font->height *
((uint8_t)ch - font->first_char);
glyph_data = font->data.progmem + glyph_data_offset;
rows_left = font->height;
do {
uint8_t glyph_byte = 0;
uint8_t pixelsToDraw = font->width;
for (i = 0; i < pixelsToDraw; i++) {
if (i % CONFIG_FONT_PIXELS_PER_BYTE == 0) {
glyph_byte = PROGMEM_READ_BYTE(glyph_data);
glyph_data++;
}
if ((glyph_byte & 0x80)) {
gfx_mono_draw_pixel(inc_x, inc_y,
GFX_PIXEL_SET);
}
inc_x += 1;
glyph_byte <<= 1;
}
inc_y += 1;
inc_x = x;
rows_left--;
} while (rows_left > 0);
}
/**
* \brief Draws a character to the display
*
* \param[in] c Character to be drawn
* \param[in] x X coordinate on screen.
* \param[in] y Y coordinate on screen.
* \param[in] font Font to draw character in
*/
void gfx_mono_draw_char(const char c, const gfx_coord_t x, const gfx_coord_t y,
const struct font *font)
{
gfx_mono_draw_filled_rect(x, y, font->width, font->height,
GFX_PIXEL_CLR);
switch (font->type) {
case FONT_LOC_PROGMEM:
gfx_mono_draw_char_progmem(c, x, y, font);
break;
#ifdef CONFIG_HUGEMEM
case FONT_LOC_HUGEMEM:
gfx_mono_draw_char_hugemem(c, x, y, font);
break;
#endif
default:
/* Unsupported mode, call assert */
Assert(false);
break;
}
}
/**
* \brief Draws a string to the display
*
* This function will draw a string located in memory to the display.
*
* \param[in] str Pointer to string
* \param[in] x X coordinate on screen.
* \param[in] y Y coordinate on screen.
* \param[in] font Font to draw string in
*/
void gfx_mono_draw_string(const char *str, gfx_coord_t x, gfx_coord_t y,
const struct font *font)
{
/* Save X in order to know where to return to on CR. */
const gfx_coord_t start_of_string_position_x = x;
/* Sanity check on parameters, assert if str or font is NULL. */
Assert(str != NULL);
Assert(font != NULL);
/* Draw characters until trailing null byte */
do {
/* Handle '\n' as newline, draw normal characters. */
if (*str == '\n') {
x = start_of_string_position_x;
y += font->height + 1;
} else if (*str == '\r') {
/* Skip '\r' characters. */
} else {
gfx_mono_draw_char(*str, x, y, font);
x += font->width;
}
} while (*(++str));
}
/**
* \brief Draws a string located in program memory to the display
*
* This function will draw a string located in program memory to the display,
* this differs from gfx_mono_draw_string() by using constant string data from
* the program memory instead of string data in RAM.
*
* Using program memory for constant strings will reduce the applications need
* for RAM, and thus lower the overall size footprint.
*
* \param[in] str Pointer to string located in program memory
* \param[in] x X coordinate on screen.
* \param[in] y Y coordinate on screen.
* \param[in] font Font to draw string in
*/
void gfx_mono_draw_progmem_string(char PROGMEM_PTR_T str, gfx_coord_t x,
gfx_coord_t y, const struct font *font)
{
char temp_char;
/* Sanity check on parameters, assert if str or font is NULL. */
Assert(str != NULL);
Assert(font != NULL);
/* Save X in order to know where to return to on CR. */
const gfx_coord_t start_of_string_position_x = x;
/* Draw characters until trailing null byte */
temp_char = PROGMEM_READ_BYTE((uint8_t PROGMEM_PTR_T)str);
while (temp_char) {
/* Handle '\n' as newline, draw normal characters. */
if (temp_char == '\n') {
x = start_of_string_position_x;
y += font->height + 1;
} else if (temp_char == '\r') {
/* Skip '\r' characters. */
} else {
gfx_mono_draw_char(temp_char, x, y, font);
x += font->width;
}
temp_char = PROGMEM_READ_BYTE((uint8_t PROGMEM_PTR_T)(++str));
}
}
/**
* \brief Computes the bounding box of a string
*
* \note If string is empty the returned width will be 1 pixel and the height
* equal to the font height.
*
* \param[in] str String to calculate bounding box for
* \param[in] font Font used
* \param[in] width Pointer to width result
* \param[in] height Pointer to height result
*/
void gfx_mono_get_string_bounding_box(const char *str, const struct font *font,
gfx_coord_t *width, gfx_coord_t *height)
{
gfx_coord_t font_width = font->width;
gfx_coord_t font_height = font->height;
gfx_coord_t max_width = 1;
gfx_coord_t max_height = font_height;
gfx_coord_t x = 0;
/* Sanity check on parameters, assert if str or font is NULL. */
Assert(str != NULL);
Assert(font != NULL);
/* Handle each character until trailing null byte */
do {
/* Handle '\n' as newline, draw normal characters. */
if (*str == '\n') {
x = 0;
max_height += font_height;
} else if (*str == '\r') {
/* Skip '\r' characters. */
} else {
x += font_width;
if (x > max_width) {
max_width = x;
}
}
} while (*(++str));
/* Return values through references */
*width = max_width;
*height = max_height;
}
/**
* \brief Computes the bounding box of a string located in program memory
*
* \note If string is empty the returned width will be 1 pixel and the height
* equal to the font height.
*
* \param[in] str String in program memory to calculate bounding box for
* \param[in] font Font used
* \param[in] width Pointer to width result
* \param[in] height Pointer to height result
*/
void gfx_mono_get_progmem_string_bounding_box(char PROGMEM_PTR_T str,
const struct font *font, gfx_coord_t *width,
gfx_coord_t *height)
{
gfx_coord_t font_width = font->width;
gfx_coord_t font_height = font->height;
char temp_char;
gfx_coord_t max_width = 1;
gfx_coord_t max_height = font_height;
gfx_coord_t x = 0;
/* Sanity check on parameters, assert if str or font is NULL. */
Assert(str != NULL);
Assert(font != NULL);
/* Handle each character until trailing null byte */
temp_char = PROGMEM_READ_BYTE((uint8_t PROGMEM_PTR_T)str);
while (temp_char) {
/* Handle '\n' as newline, draw normal characters. */
if (temp_char == '\n') {
x = 0;
max_height += font_height;
} else if (*str == '\r') {
/* Skip '\r' characters. */
} else {
x += font_width;
if (x > max_width) {
max_width = x;
}
}
temp_char = PROGMEM_READ_BYTE((uint8_t PROGMEM_PTR_T)(++str));
}
/* Return values through references */
*width = max_width;
*height = max_height;
}