From 04441bfd85bd666886f756560160823d9e985963 Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Fri, 17 Jul 2026 16:22:39 +1000 Subject: [PATCH] [obs] fix masked color and monochrome cursors --- obs/lg.c | 127 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 90 insertions(+), 37 deletions(-) diff --git a/obs/lg.c b/obs/lg.c index a110a5bb..eb246ad4 100644 --- a/obs/lg.c +++ b/obs/lg.c @@ -35,7 +35,6 @@ #include #include #include -#include #include "rgb24.effect.h" #include "hdrpq.effect.h" @@ -122,6 +121,7 @@ typedef struct bool cursorMono; gs_texture_t * cursorTex; + gs_texture_t * cursorXorTex; struct gs_rect cursorRect; bool cursorVisible; @@ -289,6 +289,12 @@ static void deinit(LGPlugin * this) this->cursorTex = NULL; } + if (this->cursorXorTex) + { + gs_texture_destroy(this->cursorXorTex); + this->cursorXorTex = NULL; + } + #if LIBOBS_API_MAJOR_VER >= 27 for (int i = 0 ; i < ARRAY_LENGTH(this->dmaInfo); ++i) if (this->dmaInfo[i].texture) @@ -436,13 +442,33 @@ static void * pointerThread(void * data) { case CURSOR_TYPE_MASKED_COLOR: { - dataSize = cursor->height * cursor->pitch; + const size_t pixels = (size_t)cursor->width * cursor->height; + dataSize = pixels * sizeof(uint32_t) * 2; allocCursorData(this, dataSize); - const uint32_t * s = (const uint32_t *)data; - uint32_t * d = this->cursorData; - for(int i = 0; i < dataSize / sizeof(uint32_t); ++i, ++s, ++d) - *d = (*s & ~0xFF000000) | (*s & 0xFF000000 ? 0x0 : 0xFF000000); + uint32_t * normal = this->cursorData; + uint32_t * xorData = normal + pixels; + for(int y = 0; y < cursor->height; ++y) + for(int x = 0; x < cursor->width; ++x) + { + const uint32_t src = *(const uint32_t *) + (data + cursor->pitch * y + sizeof(uint32_t) * x); + + const uint32_t rgb = src & 0x00FFFFFF; + const bool masked = src & 0xFF000000; + const size_t pos = y * cursor->width + x; + + if (masked) + { + normal [pos] = 0x00000000; + xorData[pos] = rgb ? rgb | 0xFF000000 : 0x00000000; + } + else + { + normal [pos] = rgb | 0xFF000000; + xorData[pos] = 0x00000000; + } + } break; } @@ -456,24 +482,22 @@ static void * pointerThread(void * data) case CURSOR_TYPE_MONOCHROME: { - dataSize = cursor->height * cursor->width * sizeof(uint32_t); + const int height = cursor->height / 2; + dataSize = height * cursor->width * sizeof(uint32_t); allocCursorData(this, dataSize); - const int hheight = cursor->height / 2; uint32_t * d = this->cursorData; - for(int y = 0; y < hheight; ++y) + for(int y = 0; y < height; ++y) for(int x = 0; x < cursor->width; ++x) { const uint8_t * srcAnd = data + (cursor->pitch * y) + (x / 8); - const uint8_t * srcXor = srcAnd + cursor->pitch * hheight; + const uint8_t * srcXor = srcAnd + cursor->pitch * height; const uint8_t mask = 0x80 >> (x % 8); - const uint32_t andMask = (*srcAnd & mask) ? - 0xFFFFFFFF : 0xFF000000; - const uint32_t xorMask = (*srcXor & mask) ? - 0x00FFFFFF : 0x00000000; - - d[y * cursor->width + x ] = andMask; - d[y * cursor->width + x + cursor->width * hheight] = xorMask; + const bool andMask = *srcAnd & mask; + const bool xorMask = *srcXor & mask; + d[y * cursor->width + x] = andMask ? + (xorMask ? 0xFFFFFFFF : 0x00000000) : + (xorMask ? 0x00FFFFFF : 0xFF000000); } break; @@ -486,7 +510,8 @@ static void * pointerThread(void * data) this->cursor.type = cursor->type; this->cursor.width = cursor->width; - this->cursor.height = cursor->height; + this->cursor.height = cursor->type == CURSOR_TYPE_MONOCHROME ? + cursor->height / 2 : cursor->height; atomic_fetch_add_explicit(&this->cursorVer, 1, memory_order_relaxed); os_sem_post(this->cursorSem); @@ -948,10 +973,37 @@ static void lgVideoTick(void * data, float seconds) this->cursorTex = NULL; } + if (this->cursorXorTex) + { + gs_texture_destroy(this->cursorXorTex); + this->cursorXorTex = NULL; + } + switch(this->cursor.type) { case CURSOR_TYPE_MASKED_COLOR: - /* fallthrough */ + { + const uint8_t * normal = (const uint8_t *)this->cursorData; + const uint8_t * xorData = normal + + this->cursor.width * this->cursor.height * sizeof(uint32_t); + + this->cursorMono = false; + this->cursorTex = gs_texture_create( + this->cursor.width, + this->cursor.height, + GS_BGRA, + 1, + &normal, + GS_DYNAMIC); + this->cursorXorTex = gs_texture_create( + this->cursor.width, + this->cursor.height, + GS_BGRA, + 1, + &xorData, + GS_DYNAMIC); + break; + } case CURSOR_TYPE_COLOR: this->cursorMono = false; @@ -1156,35 +1208,36 @@ static void lgVideoRender(void * data, gs_effect_t * effect) this->cursorRect.y / this->screenScale.y, 0.0f); + gs_blend_state_push(); + gs_enable_blending(true); + if (!this->cursorMono) { gs_blend_function(GS_BLEND_SRCALPHA, GS_BLEND_INVSRCALPHA); while (gs_effect_loop(effect, "Draw")) gs_draw_sprite(this->cursorTex, 0, 0, 0); - gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO); + + if (this->cursor.type == CURSOR_TYPE_MASKED_COLOR && + this->cursorXorTex) + { + gs_effect_set_texture(image, this->cursorXorTex); + gs_blend_function_separate( + GS_BLEND_INVDSTCOLOR, GS_BLEND_INVSRCALPHA, + GS_BLEND_ZERO , GS_BLEND_ONE); + while (gs_effect_loop(effect, "Draw")) + gs_draw_sprite(this->cursorXorTex, 0, 0, 0); + } } else { + gs_blend_function_separate( + GS_BLEND_INVDSTCOLOR, GS_BLEND_INVSRCALPHA, + GS_BLEND_ONE , GS_BLEND_INVSRCALPHA); while (gs_effect_loop(effect, "Draw")) - { - glEnable(GL_COLOR_LOGIC_OP); - - glLogicOp(GL_AND); - gs_draw_sprite_subregion( - this->cursorTex , 0, - 0 , 0, - this->cursorRect.cx, this->cursorRect.cy / 2); - - glLogicOp(GL_XOR); - gs_draw_sprite_subregion( - this->cursorTex , 0, - 0 , this->cursorRect.cy / 2, - this->cursorRect.cx, this->cursorRect.cy / 2); - - glDisable(GL_COLOR_LOGIC_OP); - } + gs_draw_sprite(this->cursorTex, 0, 0, 0); } + gs_blend_state_pop(); gs_matrix_pop(); gs_set_scissor_rect(NULL); }