blob: cc37bf0d7b61b5d68aeb2440cd525a328d907519 [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/draw.h"
#include <math.h>
#include "pw_color/color.h"
#include "pw_draw/sprite_sheet.h"
#include "pw_framebuffer/rgb565.h"
namespace pw::draw {
void DrawLine(
pw::framebuffer::FramebufferRgb565* fb, int x1, int y1, int x2, int y2) {
// Bresenham's Line Algorithm
int16_t steep_gradient = abs(y2 - y1) > abs(x2 - x1);
// Swap values
int16_t temp;
if (steep_gradient) {
temp = x1;
x1 = y1;
y1 = temp;
temp = x2;
x2 = y2;
y2 = temp;
}
if (x1 > x2) {
temp = x1;
x1 = x2;
x2 = temp;
temp = y1;
y1 = y2;
y2 = temp;
}
int16_t dx = x2 - x1;
int16_t dy = abs(y2 - y1);
int16_t error_value = dx / 2;
int16_t ystep = y1 < y2 ? 1 : -1;
for (; x1 <= x2; x1++) {
if (steep_gradient) {
fb->SetPixel(y1, x1);
} else {
fb->SetPixel(x1, y1);
}
error_value -= dy;
if (error_value < 0) {
y1 += ystep;
error_value += dx;
}
}
}
void DrawLine(pw::framebuffer::FramebufferRgb565* fb,
int x1,
int y1,
int x2,
int y2,
pw::color::color_rgb565_t pen_color) {
fb->SetPenColor(pen_color);
DrawLine(fb, x1, y1, x2, y2);
}
// Draw a circle at center_x, center_y with given radius and color. Only a
// one-pixel outline is drawn if filled is false.
void DrawCircle(pw::framebuffer::FramebufferRgb565* fb,
int center_x,
int center_y,
int radius,
bool filled = false) {
int fx = 0, fy = 0;
int x = -radius, y = 0;
int error_value = 2 - 2 * radius;
while (x < 0) {
if (!filled) {
fx = x;
fy = y;
}
// Draw each quarter circle
for (int i = x; i <= fx; i++) {
// Lower right
fb->SetPixel(center_x - i, center_y + y);
// Upper left
fb->SetPixel(center_x + i, center_y - y);
}
for (int i = fy; i <= y; i++) {
// Lower left
fb->SetPixel(center_x - i, center_y - x);
// Upper right
fb->SetPixel(center_x + i, center_y + x);
}
radius = error_value;
if (radius <= y) {
y++;
error_value += y * 2 + 1;
}
if (radius > x || error_value > y) {
x++;
error_value += x * 2 + 1;
}
}
}
void DrawCircle(pw::framebuffer::FramebufferRgb565* fb,
int center_x,
int center_y,
int radius,
pw::color::color_rgb565_t pen_color,
bool filled = false) {
fb->SetPenColor(pen_color);
DrawCircle(fb, center_x, center_y, radius, filled);
}
void DrawHLine(pw::framebuffer::FramebufferRgb565* fb, int x1, int x2, int y) {
for (int i = x1; i <= x2; i++) {
fb->SetPixel(i, y);
}
}
void DrawHLine(pw::framebuffer::FramebufferRgb565* fb,
int x1,
int x2,
int y,
color_rgb565_t pen_color) {
fb->SetPenColor(pen_color);
DrawHLine(fb, x1, x2, y);
}
void DrawRect(pw::framebuffer::FramebufferRgb565* fb,
int x1,
int y1,
int x2,
int y2,
bool filled = false) {
// Draw top and bottom lines.
DrawHLine(fb, x1, x2, y1);
DrawHLine(fb, x1, x2, y2);
if (filled) {
for (int y = y1 + 1; y < y2; y++) {
DrawHLine(fb, x1, x2, y);
}
} else {
for (int y = y1 + 1; y < y2; y++) {
fb->SetPixel(x1, y);
fb->SetPixel(x2, y);
}
}
}
void DrawRect(pw::framebuffer::FramebufferRgb565* fb,
int x1,
int y1,
int x2,
int y2,
color_rgb565_t pen_color,
bool filled = false) {
fb->SetPenColor(pen_color);
DrawRect(fb, x1, y1, x2, y2, filled);
}
void DrawRectWH(pw::framebuffer::FramebufferRgb565* fb,
int x,
int y,
int w,
int h,
color_rgb565_t pen_color,
bool filled = false) {
fb->SetPenColor(pen_color);
DrawRect(fb, x, y, x - 1 + w, y - 1 + h, filled);
}
void DrawRectWH(pw::framebuffer::FramebufferRgb565* fb,
int x,
int y,
int w,
int h,
bool filled = false) {
DrawRect(fb, x, y, x - 1 + w, y - 1 + h, filled);
}
void Fill(pw::framebuffer::FramebufferRgb565* fb) { fb->Fill(); }
void Fill(pw::framebuffer::FramebufferRgb565* fb, color_rgb565_t pen_color) {
fb->SetPenColor(pen_color);
fb->Fill();
}
void DrawSprite(pw::framebuffer::FramebufferRgb565* fb,
int x,
int y,
pw::draw::SpriteSheet* sprite_sheet,
int integer_scale = 1) {
uint16_t color;
int start_x, start_y;
for (int current_x = 0; current_x < sprite_sheet->width; current_x++) {
for (int current_y = 0; current_y < sprite_sheet->height; current_y++) {
color = sprite_sheet->GetColor(
current_x, current_y, sprite_sheet->current_index);
if (color != sprite_sheet->transparent_color) {
if (integer_scale == 1) {
fb->SetPixel(x + current_x, y + current_y, color);
}
// If integer_scale > 1: draw a rectangle
else if (integer_scale > 1) {
start_x = x + (integer_scale * current_x);
start_y = y + (integer_scale * current_y);
DrawRectWH(
fb, start_x, start_y, integer_scale, integer_scale, color, true);
}
}
}
}
}
void DrawTestPattern(pw::framebuffer::FramebufferRgb565* fb) {
color_rgb565_t color = ColorRGBA(0x00, 0xFF, 0xFF).ToRgb565();
// Create a Test Pattern
for (int x = 0; x < fb->width; x++) {
for (int y = 0; y < fb->height; y++) {
if (y % 10 != x % 10) {
fb->SetPixel(x, y, color);
}
}
}
}
} // namespace pw::draw