blob: e2a269a179af179f69d084d806df18aaf6669843 [file] [log] [blame]
// Copyright 2022 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#include "pw_draw/text_area.h"
#include "pw_color/color.h"
#include "pw_draw/draw.h"
#include "pw_draw/font_set.h"
#include "pw_framebuffer/rgb565.h"
namespace pw::draw {
TextArea::TextArea(pw::framebuffer::FramebufferRgb565* fb,
const FontSet* font) {
framebuffer = fb;
SetFont(font);
// Default colors: White on Black
character_wrap_enabled = true;
foreground_color = 0xFFFF;
background_color = 0;
SetCursor(0, 0);
}
// Change the current font.
void TextArea::SetFont(const FontSet* new_font) { current_font = new_font; }
void TextArea::SetCursor(int x, int y) {
cursor_x = x;
cursor_y = y;
column_count = 0;
}
void TextArea::SetForegroundColor(color_rgb565_t color) {
foreground_color = color;
}
void TextArea::SetBackgroundColor(color_rgb565_t color) {
background_color = color;
}
void TextArea::SetBackgroundTransparent() {
background_color = framebuffer->transparent_color;
}
void TextArea::SetCharacterWrap(bool new_setting) {
character_wrap_enabled = new_setting;
}
void TextArea::MoveCursorRightOnce() {
cursor_x = cursor_x + current_font->width;
column_count++;
}
void TextArea::InsertLineBreak() {
cursor_y = cursor_y + current_font->height;
cursor_x = cursor_x - (column_count * current_font->width);
column_count = 0;
if (cursor_y >= framebuffer->height) {
ScrollUp(1);
cursor_y = cursor_y - current_font->height;
}
}
void TextArea::DrawSpace() {
for (int font_row = 0; font_row < current_font->height; font_row++) {
for (int font_column = 0; font_column < current_font->width;
font_column++) {
if (background_color != framebuffer->transparent_color) {
framebuffer->SetPixel(
cursor_x + font_column, cursor_y + font_row, background_color);
}
}
}
}
void TextArea::DrawCharacter(int character) {
if (character == '\n') {
InsertLineBreak();
return;
}
if (character == ' ') {
DrawSpace();
MoveCursorRightOnce();
return;
}
if ((int)character < current_font->starting_character ||
(int)character > current_font->ending_character) {
// Unprintable character
MoveCursorRightOnce();
return;
}
if (character_wrap_enabled &&
(current_font->width + cursor_x) > framebuffer->width) {
InsertLineBreak();
}
int character_index = (int)character - current_font->starting_character;
uint8_t pixel_on;
for (int font_row = 0; font_row < current_font->height; font_row++) {
for (int font_column = 0; font_column < current_font->width;
font_column++) {
pixel_on = PW_FONT_BIT(
current_font->width - font_column - 1,
current_font
->data[current_font->height * character_index + font_row]);
if (pixel_on) {
framebuffer->SetPixel(
cursor_x + font_column, cursor_y + font_row, foreground_color);
} else if (background_color != framebuffer->transparent_color) {
framebuffer->SetPixel(
cursor_x + font_column, cursor_y + font_row, background_color);
}
}
}
// Move cursor to the right by 1 glyph.
MoveCursorRightOnce();
}
void TextArea::DrawCharacter(int character, int x, int y) {
SetCursor(x, y);
DrawCharacter(character);
}
void TextArea::DrawTestFontSheet(int character_width, int x, int y) {
SetCursor(x, y);
for (int c = current_font->starting_character;
c <= current_font->ending_character;
c++) {
if (c % character_width == 0) {
DrawCharacter('\n');
}
DrawCharacter(c);
}
}
// DrawText at x, y (upper left pixel of font). Carriage returns will move
// text to the next line.
void TextArea::DrawText(const char* str) {
int character_index = 0;
int character;
do {
character = str[character_index];
DrawCharacter(character);
character_index++;
} while (character != '\0');
}
void TextArea::DrawText(const char* str, int x, int y) {
SetCursor(x, y);
DrawText(str);
}
void TextArea::DrawText(const wchar_t* str) {
int character_index = 0;
int character;
do {
character = str[character_index];
DrawCharacter(character);
character_index++;
} while (character != '\0');
}
void TextArea::DrawText(const wchar_t* str, int x, int y) {
SetCursor(x, y);
DrawText(str);
}
void TextArea::ScrollUp(int lines) {
int pixel_height = lines * current_font->height;
int start_x = 0;
int start_y = pixel_height;
color_rgb565_t pixel_color;
for (int current_x = 0; current_x < framebuffer->width; current_x++) {
for (int current_y = start_y; current_y < framebuffer->height;
current_y++) {
pixel_color = framebuffer->GetPixel(current_x, current_y);
framebuffer->SetPixel(
start_x + current_x, current_y - start_y, pixel_color);
}
}
// Draw a filled background_color rectangle at the bottom to erase the old
// text.
for (int x = 0; x < framebuffer->width; x++) {
for (int y = framebuffer->height - pixel_height; y < framebuffer->height;
y++) {
framebuffer->SetPixel(x, y, background_color);
}
}
}
} // namespace pw::draw