blob: eac3b95900d22e492daeaf89fa0082d932847856 [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/framebuffer.h"
#include "pw_framebuffer/writer.h"
using pw::color::color_rgb565_t;
using pw::framebuffer::FramebufferWriter;
using pw::math::Vector2;
namespace pw::draw {
TextArea::TextArea(pw::framebuffer::Framebuffer& 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::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.size().height) {
ScrollUp(1);
cursor_y = cursor_y - current_font->height;
}
}
void TextArea::DrawCharacter(int character) {
if (character == '\n') {
InsertLineBreak();
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.size().width) {
InsertLineBreak();
}
pw::draw::DrawCharacter(character,
Vector2<int>{cursor_x, cursor_y},
foreground_color,
background_color,
*current_font,
framebuffer);
// 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_column_width, int x, int y) {
SetCursor(x, y);
for (int c = current_font->starting_character;
c <= current_font->ending_character;
c++) {
int index = c - current_font->starting_character;
if (index > 0 && index % character_column_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) {
for (const char* ch = str; *ch != '\0'; ch++) {
DrawCharacter(*ch);
}
}
void TextArea::DrawText(const char* str, int x, int y) {
SetCursor(x, y);
DrawText(str);
}
void TextArea::DrawText(const wchar_t* str) {
for (const wchar_t* ch = str; *ch != L'\0'; ch++) {
DrawCharacter(*ch);
}
}
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;
FramebufferWriter writer(framebuffer);
for (int current_x = 0; current_x < framebuffer.size().width; current_x++) {
for (int current_y = start_y; current_y < framebuffer.size().height;
current_y++) {
if (auto pixel_color = writer.GetPixel(current_x, current_y);
pixel_color.ok()) {
writer.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.size().width; x++) {
for (int y = framebuffer.size().height - pixel_height;
y < framebuffer.size().height;
y++) {
writer.SetPixel(x, y, background_color);
}
}
}
} // namespace pw::draw