| /* |
| * Copyright 2016 Google Inc. |
| * |
| * See file CREDITS for list of people who contributed to this |
| * project. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation; either version 2 of |
| * the License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but without any warranty; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
| * MA 02111-1307 USA |
| */ |
| |
| #include "base/container_of.h" |
| #include "base/xalloc.h" |
| #include "drivers/display/framebuffer.h" |
| |
| static int fb_display_initialize(FbDisplay *display) |
| { |
| if (display->fb_info.buffer) |
| return 0; |
| |
| if (framebuffer_prepare(display->framebuffer, &display->fb_info)) { |
| display->fb_info.buffer = NULL; |
| return 1; |
| } |
| |
| if (display->monitor && monitor_enable(display->monitor)) |
| return 1; |
| |
| return 0; |
| } |
| |
| static inline void fb_display_set_pixel(FbDisplay *display, |
| uint32_t x, uint32_t y, uint32_t color) |
| { |
| const uint32_t xres = display->fb_info.resolution.x; |
| const uint8_t bpp = display->fb_info.bits_per_pixel; |
| |
| uint8_t *pixel = display->fb_info.buffer + (x + y * xres) * bpp / 8; |
| for (int i = 0; i < bpp / 8; i++) |
| pixel[i] = (color >> (i * 8)); |
| } |
| |
| static int fb_display_dimensions(DisplayOps *me, |
| uint32_t *height, uint32_t *width) |
| { |
| FbDisplay *display = container_of(me, FbDisplay, ops); |
| |
| if (fb_display_initialize(display)) |
| return 1; |
| |
| *height = display->fb_info.resolution.y; |
| *width = display->fb_info.resolution.x; |
| |
| return 0; |
| } |
| |
| static int fb_display_fill(DisplayOps *me, uint32_t x, uint32_t y, |
| uint32_t height, uint32_t width, |
| const DcColor *color) |
| { |
| FbDisplay *display = container_of(me, FbDisplay, ops); |
| |
| if (fb_display_initialize(display)) |
| return 1; |
| |
| if (height == 0 || width == 0) |
| return 0; |
| |
| const uint32_t pval = framebuffer_pixel_value(&display->fb_info, color); |
| |
| for (uint32_t offy = 0; offy < height; offy++) { |
| for (uint32_t offx = 0; offx < width; offx++) { |
| fb_display_set_pixel(display, x + offx, |
| y + offy, pval); |
| } |
| } |
| return 0; |
| } |
| |
| static void fb_display_move_row(FbDisplay *display, uint32_t x, uint32_t y, |
| uint32_t new_x, uint32_t new_y, uint32_t width) |
| { |
| const uint32_t xres = display->fb_info.resolution.x; |
| const uint8_t bpp = display->fb_info.bits_per_pixel; |
| |
| uint8_t *from = display->fb_info.buffer + (x + y * xres) * bpp / 8; |
| uint8_t *to = display->fb_info.buffer + |
| (new_x + new_y * xres) * bpp / 8; |
| memmove(to, from, width * bpp / 8); |
| } |
| |
| static int fb_display_move(DisplayOps *me, uint32_t x, uint32_t y, |
| uint32_t new_x, uint32_t new_y, |
| uint32_t height, uint32_t width) |
| { |
| FbDisplay *display = container_of(me, FbDisplay, ops); |
| |
| if (fb_display_initialize(display)) |
| return 1; |
| |
| if (height == 0 || width == 0) |
| return 0; |
| |
| for (uint32_t y_idx = 0; y_idx < height; y_idx++) { |
| uint32_t offy = (new_y > y) ? (height - y_idx - 1) : y_idx; |
| fb_display_move_row(display, x, y + offy, new_x, |
| new_y + offy, width); |
| } |
| |
| return 0; |
| } |
| |
| static int fb_display_draw(DisplayOps *me, uint32_t x, uint32_t y, |
| const DcBitmap *bitmap) |
| { |
| FbDisplay *display = container_of(me, FbDisplay, ops); |
| |
| if (fb_display_initialize(display)) |
| return 1; |
| |
| DcColor *pixel = bitmap->pixels; |
| for (uint32_t offy = 0; offy < bitmap->height; offy++) { |
| for (uint32_t offx = 0; offx < bitmap->width; offx++) { |
| uint32_t pval = framebuffer_pixel_value( |
| &display->fb_info, pixel++); |
| fb_display_set_pixel(display, x + offx, |
| y + offy, pval); |
| } |
| } |
| |
| return 0; |
| } |
| |
| FbDisplay *new_fb_display(FrameBufferOps *framebuffer, MonitorOps *monitor) |
| { |
| FbDisplay *display = xzalloc(sizeof(*display)); |
| |
| display->ops.dimensions = &fb_display_dimensions; |
| display->ops.fill = &fb_display_fill; |
| display->ops.move = &fb_display_move; |
| display->ops.draw = &fb_display_draw; |
| |
| display->framebuffer = framebuffer; |
| display->monitor = monitor; |
| |
| return display; |
| } |