123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- #include <windows.h>
- #include <obs.h>
- #include "cursor-capture.h"
- static uint8_t *get_bitmap_data(HBITMAP hbmp, BITMAP *bmp)
- {
- if (GetObject(hbmp, sizeof(*bmp), bmp) != 0) {
- uint8_t *output;
- unsigned int size = bmp->bmHeight * bmp->bmWidthBytes;
- if (!size) {
- return NULL;
- }
- output = bmalloc(size);
- GetBitmapBits(hbmp, size, output);
- return output;
- }
- return NULL;
- }
- static inline uint8_t bit_to_alpha(uint8_t *data, long pixel, bool invert)
- {
- uint8_t pix_byte = data[pixel / 8];
- bool alpha = (pix_byte >> (7 - pixel % 8) & 1) != 0;
- if (invert) {
- return alpha ? 0xFF : 0;
- } else {
- return alpha ? 0 : 0xFF;
- }
- }
- static inline bool bitmap_has_alpha(uint8_t *data, long num_pixels)
- {
- for (long i = 0; i < num_pixels; i++) {
- if (data[i * 4 + 3] != 0) {
- return true;
- }
- }
- return false;
- }
- static inline void apply_mask(uint8_t *color, uint8_t *mask, BITMAP *bmp_mask)
- {
- long mask_pix_offs;
- for (long y = 0; y < bmp_mask->bmHeight; y++) {
- for (long x = 0; x < bmp_mask->bmWidth; x++) {
- mask_pix_offs = y * (bmp_mask->bmWidthBytes * 8) + x;
- color[(y * bmp_mask->bmWidth + x) * 4 + 3] = bit_to_alpha(mask, mask_pix_offs, false);
- }
- }
- }
- static inline uint8_t *copy_from_color(ICONINFO *ii, uint32_t *width, uint32_t *height)
- {
- BITMAP bmp_color;
- BITMAP bmp_mask;
- uint8_t *color;
- uint8_t *mask;
- color = get_bitmap_data(ii->hbmColor, &bmp_color);
- if (!color) {
- return NULL;
- }
- if (bmp_color.bmBitsPixel < 32) {
- bfree(color);
- return NULL;
- }
- mask = get_bitmap_data(ii->hbmMask, &bmp_mask);
- if (mask) {
- long pixels = bmp_color.bmHeight * bmp_color.bmWidth;
- if (!bitmap_has_alpha(color, pixels))
- apply_mask(color, mask, &bmp_mask);
- bfree(mask);
- }
- *width = bmp_color.bmWidth;
- *height = bmp_color.bmHeight;
- return color;
- }
- static inline uint8_t *copy_from_mask(ICONINFO *ii, uint32_t *width, uint32_t *height)
- {
- uint8_t *output;
- uint8_t *mask;
- long pixels;
- long bottom;
- BITMAP bmp;
- mask = get_bitmap_data(ii->hbmMask, &bmp);
- if (!mask) {
- return NULL;
- }
- bmp.bmHeight /= 2;
- pixels = bmp.bmHeight * bmp.bmWidth;
- if (!pixels) {
- return NULL;
- }
- output = bzalloc(pixels * 4);
- bottom = bmp.bmWidthBytes * bmp.bmHeight;
- for (long i = 0; i < pixels; i++) {
- uint8_t andMask = bit_to_alpha(mask, i, true);
- uint8_t xorMask = bit_to_alpha(mask + bottom, i, true);
- if (!andMask) {
- // black in the AND mask
- *(uint32_t *)&output[i * 4] = !!xorMask ? 0x00FFFFFF /*always white*/
- : 0xFF000000 /*always black*/;
- } else {
- // white in the AND mask
- *(uint32_t *)&output[i * 4] = !!xorMask ? 0xFFFFFFFF /*source inverted*/
- : 0 /*transparent*/;
- }
- }
- bfree(mask);
- *width = bmp.bmWidth;
- *height = bmp.bmHeight;
- return output;
- }
- static inline uint8_t *cursor_capture_icon_bitmap(ICONINFO *ii, uint32_t *width, uint32_t *height, bool *monochrome)
- {
- uint8_t *output;
- *monochrome = false;
- output = copy_from_color(ii, width, height);
- if (!output) {
- *monochrome = true;
- output = copy_from_mask(ii, width, height);
- }
- return output;
- }
- static gs_texture_t *get_cached_texture(struct cursor_data *data, uint32_t cx, uint32_t cy)
- {
- struct cached_cursor cc;
- for (size_t i = 0; i < data->cached_textures.num; i++) {
- struct cached_cursor *pcc = &data->cached_textures.array[i];
- if (pcc->cx == cx && pcc->cy == cy)
- return pcc->texture;
- }
- cc.texture = gs_texture_create(cx, cy, GS_BGRA, 1, NULL, GS_DYNAMIC);
- cc.cx = cx;
- cc.cy = cy;
- da_push_back(data->cached_textures, &cc);
- return cc.texture;
- }
- static inline bool cursor_capture_icon(struct cursor_data *data, HICON icon)
- {
- uint8_t *bitmap;
- uint32_t height;
- uint32_t width;
- ICONINFO ii;
- if (!icon) {
- return false;
- }
- if (!GetIconInfo(icon, &ii)) {
- return false;
- }
- bitmap = cursor_capture_icon_bitmap(&ii, &width, &height, &data->monochrome);
- if (bitmap) {
- if (data->last_cx != width || data->last_cy != height) {
- data->texture = get_cached_texture(data, width, height);
- data->last_cx = width;
- data->last_cy = height;
- }
- gs_texture_set_image(data->texture, bitmap, width * 4, false);
- bfree(bitmap);
- data->x_hotspot = ii.xHotspot;
- data->y_hotspot = ii.yHotspot;
- }
- DeleteObject(ii.hbmColor);
- DeleteObject(ii.hbmMask);
- return !!data->texture;
- }
- void cursor_capture(struct cursor_data *data)
- {
- CURSORINFO ci = {0};
- HICON icon;
- ci.cbSize = sizeof(ci);
- if (!GetCursorInfo(&ci)) {
- data->visible = false;
- return;
- }
- memcpy(&data->cursor_pos, &ci.ptScreenPos, sizeof(data->cursor_pos));
- if (data->current_cursor == ci.hCursor) {
- return;
- }
- icon = CopyIcon(ci.hCursor);
- data->visible = cursor_capture_icon(data, icon);
- data->current_cursor = ci.hCursor;
- if ((ci.flags & CURSOR_SHOWING) == 0)
- data->visible = false;
- DestroyIcon(icon);
- }
- void cursor_draw(struct cursor_data *data, long x_offset, long y_offset, long width, long height)
- {
- long x = data->cursor_pos.x + x_offset;
- long y = data->cursor_pos.y + y_offset;
- long x_draw = x - data->x_hotspot;
- long y_draw = y - data->y_hotspot;
- if (x < 0 || x > width || y < 0 || y > height)
- return;
- if (data->visible && !!data->texture) {
- gs_blend_state_push();
- enum gs_blend_type blendMode = data->monochrome ? GS_BLEND_INVDSTCOLOR : GS_BLEND_SRCALPHA;
- gs_blend_function_separate(blendMode, /*src_color*/
- GS_BLEND_INVSRCALPHA /*dest_color*/, GS_BLEND_ONE /*src_alpha*/,
- GS_BLEND_INVSRCALPHA /*dest_alpha*/);
- gs_matrix_push();
- obs_source_draw(data->texture, x_draw, y_draw, 0, 0, false);
- gs_matrix_pop();
- gs_blend_state_pop();
- }
- }
- void cursor_data_free(struct cursor_data *data)
- {
- for (size_t i = 0; i < data->cached_textures.num; i++) {
- struct cached_cursor *pcc = &data->cached_textures.array[i];
- gs_texture_destroy(pcc->texture);
- }
- da_free(data->cached_textures);
- memset(data, 0, sizeof(*data));
- }
|