| // 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 |