From a0c2b8d8fd3600d76c73644a94c573f929561b5b Mon Sep 17 00:00:00 2001 From: Edgaru089 Date: Fri, 22 Oct 2021 00:28:13 +0800 Subject: [PATCH] graphics: render mouse using copying overlay, optimize copying --- graphics/graphics.c | 56 +++++++++++++++------------- graphics/internal_helpers.h | 73 +++++++++++++++++++++++++++++++++++++ util/minmax.h | 24 ++++++++++++ 3 files changed, 127 insertions(+), 26 deletions(-) create mode 100644 graphics/internal_helpers.h diff --git a/graphics/graphics.c b/graphics/graphics.c index 391423e..5e3b7ff 100644 --- a/graphics/graphics.c +++ b/graphics/graphics.c @@ -2,6 +2,7 @@ #include "graphics.h" #include "color.h" +#include "internal_helpers.h" #include "unifont.h" #include "../runtime/stdio.h" #include "../util/minmax.h" @@ -36,8 +37,11 @@ void * graphics_Framebuffer; uint64_t graphics_FramebufferSize; uint32_t graphics_Doublebuffer[2048 * 1024]; +#define MOUSE_OVERLAY_SIZE 32 xcursor_ChunkHeader_Image *graphics_Cursor; int graphics_MouseCursorX, graphics_MouseCursorY; +static int __lastMouseX, __lastMouseY; // Last of mouse **IMAGE** position +static uint32_t __mouseOverlay[MOUSE_OVERLAY_SIZE * MOUSE_OVERLAY_SIZE]; void graphics_Init() { @@ -160,36 +164,35 @@ void graphics_ClearBuffer(const HelosGraphics_Color *color) { } } -void graphics_SwapBuffer() { - memcpy(graphics_DeviceFramebuffer, graphics_Framebuffer, graphics_FramebufferSize); +static void __graphics__UpdateMouse() { + if (!graphics_Cursor) + return; - if (graphics_Cursor) { - // TODO Optimize mouse cursor overlay render - /*if (graphics_SystemVideoMode.PixelFormat == PixelBlueGreenRedReserved8BitPerColor) { - for (int i = 0; i < graphics_Cursor->height; i++) { - if (graphics_MouseCursorY + i >= graphics_SystemVideoMode.Height) - break; - memcpy( - graphics_DeviceFramebuffer + graphics_SystemVideoMode.PixelsPerLine * 4 * graphics_CursorY + 4 * graphics_CursorX, - &graphics_Cursor->pixels[i * graphics_Cursor->width], - min(graphics_Cursor->width, graphics_SystemVideoMode.Width - graphics_MouseCursorX) * 4); - } - } else {*/ - int imgX = graphics_MouseCursorX - graphics_Cursor->xhot + 1; - int imgY = graphics_MouseCursorY - graphics_Cursor->yhot + 1; - for (int y = intmax(imgY, 0); y < intmin(imgY + graphics_Cursor->height, graphics_SystemVideoMode.Height); y++) { - for (int x = intmax(imgX, 0); x < intmin(imgX + graphics_Cursor->width, graphics_SystemVideoMode.Width); x++) { - //graphics_SetPixel_RGB(x + graphics_MouseCursorX, y + graphics_MouseCursorY, &graphics_Cursor->pixels[y * graphics_Cursor->width + x]); - HelosGraphics_Color *pixel = &graphics_Cursor->pixels[(y - imgY) * graphics_Cursor->width + (x - imgX)]; - if (pixel->A <= 0x7f) - continue; - *((uint32_t *)(graphics_DeviceFramebuffer + graphics_SystemVideoMode.PixelsPerLine * 4 * y + 4 * x)) = (*((uint32_t *)pixel)) & 0x00ffffffu; - } - } - //} + int imgX = graphics_MouseCursorX - graphics_Cursor->xhot + 1; + int imgY = graphics_MouseCursorY - graphics_Cursor->yhot + 1; + if (imgX != __lastMouseX || imgY != __lastMouseY) { // moved + __graphics_CopyBuffer32( + __mouseOverlay, 0, 0, MOUSE_OVERLAY_SIZE, MOUSE_OVERLAY_SIZE, + graphics_Framebuffer, __lastMouseX, __lastMouseY, graphics_SystemVideoMode.Width, graphics_SystemVideoMode.Height, + graphics_Cursor->width, graphics_Cursor->height); + __graphics_CopyBuffer32( + graphics_Framebuffer, imgX, imgY, graphics_SystemVideoMode.Width, graphics_SystemVideoMode.Height, + __mouseOverlay, 0, 0, MOUSE_OVERLAY_SIZE, MOUSE_OVERLAY_SIZE, + graphics_Cursor->width, graphics_Cursor->height); + __graphics_RenderBuffer32( + graphics_Cursor->pixels, 0, 0, graphics_Cursor->width, graphics_Cursor->height, + graphics_Framebuffer, imgX, imgY, graphics_SystemVideoMode.Width, graphics_SystemVideoMode.Height, + graphics_Cursor->width, graphics_Cursor->height); + __lastMouseX = imgX; + __lastMouseY = imgY; } } +void graphics_SwapBuffer() { + __graphics__UpdateMouse(); + memcpy(graphics_DeviceFramebuffer, graphics_Framebuffer, graphics_FramebufferSize); +} + void graphics_FillPixel(int startX, int startY, int endX, int endY, const HelosGraphics_Color *color) { // TODO Optimize this! This is too sloooow if (graphics_SystemVideoMode.PixelFormat == PixelBlueGreenRedReserved8BitPerColor) @@ -221,6 +224,7 @@ void graphics_Scroll(int scrollY) { 0, sizeof(uint32_t) * graphics_SystemVideoMode.PixelsPerLine * (scrollY));*/ graphics_FillPixel(0, graphics_SystemVideoMode.Height - scrollY, graphics_SystemVideoMode.Width, graphics_SystemVideoMode.Height, &HelosGraphics_Color_Black); + __lastMouseY -= scrollY; } void graphics_ElementSize(int sizeX, int sizeY) { diff --git a/graphics/internal_helpers.h b/graphics/internal_helpers.h new file mode 100644 index 0000000..2c4e55d --- /dev/null +++ b/graphics/internal_helpers.h @@ -0,0 +1,73 @@ +#pragma once + +#include "../util/minmax.h" +#include "color.h" +#include "stdint.h" +#include "string.h" + + +static inline void __graphics_CopyBuffer32( + void *from, // Source image of the copy + int fromOffsetX, // OffsetX of the region to copy from + int fromOffsetY, // OffsetY of the region to copy from + int fromSizeX, // SizeX of the entire source image + int fromSizeY, // SizeY of the entire source image + void *to, // Destination image of the copy + int toOffsetX, // OffsetX of the target region + int toOffsetY, // OffsetY of the target region + int toSizeX, // SizeX of the entire destination image + int toSizeY, // SizeY of the entire destination image + int countX, // Size of the copying region + int countY) { + int beginX = intmax3(0, -fromOffsetX, -toOffsetX), beginY = intmax3(0, -fromOffsetY, -toOffsetY); + int endX = intmin3(countX, fromSizeX - fromOffsetX, toSizeX - toOffsetX), + endY = intmin3(countY, fromSizeY - fromOffsetY, toSizeY - toOffsetY); + +#define PIXEL_BYTES ((intptr_t)4) + for (int i = beginY; i < endY; i++) + memcpy( + ((intptr_t)toOffsetX + beginX + (intptr_t)toSizeX * (toOffsetY + i)) * PIXEL_BYTES + (uint8_t *)to, + ((intptr_t)fromOffsetX + beginX + (intptr_t)fromSizeX * (fromOffsetY + i)) * PIXEL_BYTES + (uint8_t *)from, + (endX - beginX) * PIXEL_BYTES); +#undef PIXEL_BYTES +} + +// Blends ARGB Premultiplied Alpha image onto (opaque) buffer +// See: https://apoorvaj.io/alpha-compositing-opengl-blending-and-premultiplied-alpha/ +static inline void __graphics_RenderBuffer32( + void *from, // Source image of the render + int fromOffsetX, // OffsetX of the region to render from + int fromOffsetY, // OffsetY of the region to render from + int fromSizeX, // SizeX of the entire source image + int fromSizeY, // SizeY of the entire source image + void *to, // Destination image of the render + int toOffsetX, // OffsetX of the target region + int toOffsetY, // OffsetY of the target region + int toSizeX, // SizeX of the entire destination image + int toSizeY, // SizeY of the entire destination image + int countX, // Size of the render region + int countY) { + int beginX = intmax3(0, -fromOffsetX, -toOffsetX), beginY = intmax3(0, -fromOffsetY, -toOffsetY); + int endX = intmin3(countX, fromSizeX - fromOffsetX, toSizeX - toOffsetX), + endY = intmin3(countY, fromSizeY - fromOffsetY, toSizeY - toOffsetY); + + for (int x = beginX; x < endX; x++) + for (int y = beginY; y < endY; y++) { + HelosGraphics_Color + *src = ((intptr_t)fromOffsetX + x + (intptr_t)fromSizeX * (fromOffsetY + y)) + (HelosGraphics_Color *)from, + *dest = ((intptr_t)toOffsetX + x + (intptr_t)toSizeX * (toOffsetY + y)) + (HelosGraphics_Color *)to; + + if (src->A == 0) + continue; + else if (src->A == 0xff) + *(uint32_t *)dest = *(uint32_t *)src & 0x00ffffffu; + else { // blend + HelosGraphics_Color color = { + .B = (uint8_t)(src->B + ((int)dest->B) * (0xff - src->A) / 0xff), + .G = (uint8_t)(src->G + ((int)dest->G) * (0xff - src->A) / 0xff), + .R = (uint8_t)(src->R + ((int)dest->R) * (0xff - src->A) / 0xff), + .A = 0}; + *dest = color; + } + } +} diff --git a/util/minmax.h b/util/minmax.h index c1a438e..3e35bba 100644 --- a/util/minmax.h +++ b/util/minmax.h @@ -8,3 +8,27 @@ static inline int intmin(int x, int y) { static inline int intmax(int x, int y) { return x > y ? x : y; } + +static inline int intmin3(int x, int y, int z) { + if (x < y) + if (x < z) + return x; + else + return z; + else if (y < z) + return y; + else + return z; +} + +static inline int intmax3(int x, int y, int z) { + if (x > y) + if (x > z) + return x; + else + return z; + else if (y > z) + return y; + else + return z; +}