/** * HULK Framebuffer support. */ module hulk.fb; import hulk.memory; import hulk.kfont; /** * Represent a graphical frame buffer. * * TODO: Handle other pixel formats. Currently only BGRx is supported. */ struct Fb { /** Device frame buffer base address. */ private __gshared static uint * m_device_buffer; /** Frame buffer 1 base address. */ private __gshared static uint * m_buffer1; /** Frame buffer width. */ private __gshared static uint m_width; /** Frame buffer height. */ private __gshared static uint m_height; /** Frame buffer stride. */ private __gshared static uint m_stride; /** Number of pixels in the frame buffer (whether visible or not). */ private __gshared static uint m_buffer_size; /** * Get the framebuffer width. */ public static @property uint width() { return m_width; } /** * Get the framebuffer height. */ public static @property uint height() { return m_height; } /** * Initialize a frame buffer. * * @param device_buffer Device frame buffer base address. * @param buffer1 Frame buffer 1 base address. * @param width Frame buffer width. * @param height Frame buffer height. * @param stride Frame buffer stride. */ static void initialize(uint * device_buffer, uint * buffer1, uint width, uint height, uint stride) { m_device_buffer = device_buffer; m_buffer1 = buffer1; m_width = width; m_height = height; m_stride = stride; m_buffer_size = stride * height; } /** * Clear the frame buffer to the given color. * * @param color Color to clear to. */ static void clear(uint color = 0u) { memset32(m_buffer1, color, m_buffer_size); memset32(m_device_buffer, color, m_buffer_size); } /** * Draw a solid rectangle on the framebuffer. * * @param x X coordinate of left side of rectangle. * @param y Y coordinate of bottom side of rectangle. * @param width Width of rectangle. * @param height Height of rectangle. * @param color Color of rectangle. */ static void rect(size_t x, size_t y, size_t width, size_t height, uint color) { for (size_t iy = 0u; iy < height; iy++) { size_t buffer_index = buffer_index(x, y); memset32(&m_buffer1[buffer_index], color, width); memset32(&m_device_buffer[buffer_index], color, width); y++; } } /** * Draw a horizontal line. * * @param x X coordinate of left side of line. * @param y Y coordinate of line. * @param width Width of line. * @param color Color of line. */ static void hline(size_t x, size_t y, size_t width, uint color) { size_t buffer_index = buffer_index(x, y); memset32(&m_buffer1[buffer_index], color, width); memset32(&m_device_buffer[buffer_index], color, width); } /** * Draw a vertical line. * * @param x X coordinate of line. * @param y Y coordinate of bottom of line. * @param height Height of line. * @param color Color of line. */ static void vline(size_t x, size_t y, size_t height, uint color) { size_t buffer_index = buffer_index(x, y); for (size_t iy = 0u; iy < height; iy++) { m_buffer1[buffer_index] = color; m_device_buffer[buffer_index] = color; buffer_index -= m_stride; } } /** * Draw a character. * * @param x X coordinate of left side of character. * @param y Y coordinate of bottom side of character. * @param ch Character to draw. * @param color Color of character. */ static void character(int x, int y, char ch, uint color) { const(CharInfo) * ci = &Kfont.chars[ch]; blend_alpha_bitmap(x + ci.left, y + Kfont.baseline_offset + ci.top - ci.height, ci.bitmap, ci.width, ci.height, color); } /** * Blend an 8-bit alpha bitmap to the framebuffer. * * @param x X coordinate of left side of target location. * @param y Y coordinate of bottom side of target location. * @param alpha_bitmap 8-bit alpha-channel bitmap. * @param width Bitmap width. * @param height Bitmap height. * @param color Color to blend with alpha value. */ static void blend_alpha_bitmap(size_t x, size_t y, const(ubyte) * alpha_bitmap, size_t width, size_t height, uint color) { y += height - 1u; size_t bitmap_index; for (size_t iy = 0u; iy < height; iy++) { size_t row_buffer_index = buffer_index(x, y); for (size_t ix = 0u; ix < width; ix++) { size_t buffer_index = row_buffer_index + ix; ubyte alpha = alpha_bitmap[bitmap_index]; uint current_color = m_buffer1[buffer_index]; uint in_color_scaled = scale_color(color, alpha); uint old_color_scaled = scale_color(current_color, 255u - alpha); uint new_color = old_color_scaled + in_color_scaled; m_buffer1[buffer_index] = new_color; bitmap_index++; } memcpy32(&m_device_buffer[row_buffer_index], &m_buffer1[row_buffer_index], width); y--; } } /** * Blend an 8-bit alpha bitmap to the framebuffer. * * @param x X coordinate of left side of target location. * @param y Y coordinate of bottom side of target location. * @param alpha_bitmap 8-bit alpha-channel bitmap. * @param width Bitmap width. * @param height Bitmap height. * @param color Color to blend with alpha value. */ static void blend_alpha_bitmap(size_t x, size_t y, const(ubyte)[] alpha_bitmap, size_t width, size_t height, uint color) { blend_alpha_bitmap(x, y, alpha_bitmap.ptr, width, height, color); } /** * Blit an 8-bit alpha bitmap to the framebuffer. * The foreground will be white (based on the alpha value), and the * background black. * * @param x X coordinate of left side of target location. * @param y Y coordinate of bottom side of target location. * @param alpha_bitmap 8-bit alpha-channel bitmap. * @param width Bitmap width. * @param height Bitmap height. */ static void blit_alpha_bitmap(size_t x, size_t y, const(ubyte) * alpha_bitmap, size_t width, size_t height) { y += height - 1u; size_t bitmap_index; for (size_t iy = 0u; iy < height; iy++) { size_t row_buffer_index = buffer_index(x, y); for (size_t ix = 0u; ix < width; ix++) { size_t buffer_index = row_buffer_index + ix; ubyte alpha = alpha_bitmap[bitmap_index]; m_buffer1[buffer_index] = (alpha << 16u) | (alpha << 8u) | alpha; bitmap_index++; } memcpy32(&m_device_buffer[row_buffer_index], &m_buffer1[row_buffer_index], width); y--; } } /** * Blit an 8-bit alpha bitmap to the framebuffer. * The foreground will be white (based on the alpha value), and the * background black. * * @param x X coordinate of left side of target location. * @param y Y coordinate of bottom side of target location. * @param alpha_bitmap 8-bit alpha-channel bitmap. * @param width Bitmap width. * @param height Bitmap height. */ static void blit_alpha_bitmap(size_t x, size_t y, const(ubyte)[] alpha_bitmap, size_t width, size_t height) { blit_alpha_bitmap(x, y, alpha_bitmap.ptr, width, height); } /** * Copy framebuffer rows up in the framebuffer. * * @param y Y coordinate of bottom side of region to copy up. * @param height Height of region to copy up. * @param offset Number of rows to copy region up by. */ static void copy_rows_up(size_t y, size_t height, size_t offset) { size_t dest_buffer_index = buffer_index(0u, y + height + offset - 1u); size_t src_buffer_index = buffer_index(0u, y + height - 1u); memcpy32(&m_buffer1[dest_buffer_index], &m_buffer1[src_buffer_index], (m_stride * height)); memcpy32(&m_device_buffer[dest_buffer_index], &m_buffer1[dest_buffer_index], (m_stride * height)); } /** * Scale a color value by an alpha amount. * * @param color Color value. * @param alpha Alpha amount. * * @return Scaled color value. */ private static uint scale_color(uint color, ubyte alpha) { return ((((color & 0xFFu) * alpha) >> 8u) & 0xFFu) | ((((color & 0xFF00u) * alpha) >> 8u) & 0xFF00u) | ((((color & 0xFF0000u) * alpha) >> 8u) & 0xFF0000u); } /** * Return the buffer index for the given X and Y coordinates. * * @param x X coordinate from left side of framebuffer. * @param y Y coordinate from bottom side of framebuffer. * * @return Buffer index for the given X and Y coordinates. */ private static size_t buffer_index(size_t x, size_t y) { return (m_height - y - 1u) * m_stride + x; } }