cursor-capture.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. #include <windows.h>
  2. #include <obs.h>
  3. #include "cursor-capture.h"
  4. static uint8_t *get_bitmap_data(HBITMAP hbmp, BITMAP *bmp)
  5. {
  6. if (GetObject(hbmp, sizeof(*bmp), bmp) != 0) {
  7. uint8_t *output;
  8. unsigned int size = bmp->bmHeight * bmp->bmWidthBytes;
  9. output = bmalloc(size);
  10. GetBitmapBits(hbmp, size, output);
  11. return output;
  12. }
  13. return NULL;
  14. }
  15. static inline uint8_t bit_to_alpha(uint8_t *data, long pixel, bool invert)
  16. {
  17. uint8_t pix_byte = data[pixel / 8];
  18. bool alpha = (pix_byte >> (7 - pixel % 8) & 1) != 0;
  19. if (invert) {
  20. return alpha ? 0xFF : 0;
  21. } else {
  22. return alpha ? 0 : 0xFF;
  23. }
  24. }
  25. static inline bool bitmap_has_alpha(uint8_t *data, long num_pixels)
  26. {
  27. for (long i = 0; i < num_pixels; i++) {
  28. if (data[i * 4 + 3] != 0) {
  29. return true;
  30. }
  31. }
  32. return false;
  33. }
  34. static inline void apply_mask(uint8_t *color, uint8_t *mask, BITMAP *bmp_mask)
  35. {
  36. long mask_pix_offs;
  37. for (long y = 0; y < bmp_mask->bmHeight; y++) {
  38. for (long x = 0; x < bmp_mask->bmWidth; x++) {
  39. mask_pix_offs = y * (bmp_mask->bmWidthBytes * 8) + x;
  40. color[(y * bmp_mask->bmWidth + x) * 4 + 3] =
  41. bit_to_alpha(mask, mask_pix_offs, false);
  42. }
  43. }
  44. }
  45. static inline uint8_t *copy_from_color(ICONINFO *ii, uint32_t *width,
  46. uint32_t *height)
  47. {
  48. BITMAP bmp_color;
  49. BITMAP bmp_mask;
  50. uint8_t *color;
  51. uint8_t *mask;
  52. color = get_bitmap_data(ii->hbmColor, &bmp_color);
  53. if (!color) {
  54. return NULL;
  55. }
  56. if (bmp_color.bmBitsPixel < 32) {
  57. bfree(color);
  58. return NULL;
  59. }
  60. mask = get_bitmap_data(ii->hbmMask, &bmp_mask);
  61. if (mask) {
  62. long pixels = bmp_color.bmHeight * bmp_color.bmWidth;
  63. if (!bitmap_has_alpha(color, pixels))
  64. apply_mask(color, mask, &bmp_mask);
  65. bfree(mask);
  66. }
  67. *width = bmp_color.bmWidth;
  68. *height = bmp_color.bmHeight;
  69. return color;
  70. }
  71. static inline uint8_t *copy_from_mask(ICONINFO *ii, uint32_t *width,
  72. uint32_t *height)
  73. {
  74. uint8_t *output;
  75. uint8_t *mask;
  76. long pixels;
  77. long bottom;
  78. BITMAP bmp;
  79. mask = get_bitmap_data(ii->hbmMask, &bmp);
  80. if (!mask) {
  81. return NULL;
  82. }
  83. bmp.bmHeight /= 2;
  84. pixels = bmp.bmHeight * bmp.bmWidth;
  85. output = bzalloc(pixels * 4);
  86. bottom = bmp.bmWidthBytes * bmp.bmHeight;
  87. for (long i = 0; i < pixels; i++) {
  88. uint8_t andMask = bit_to_alpha(mask, i, true);
  89. uint8_t xorMask = bit_to_alpha(mask + bottom, i, true);
  90. if (!andMask) {
  91. // black in the AND mask
  92. *(uint32_t *)&output[i * 4] =
  93. !!xorMask ? 0x00FFFFFF /*always white*/
  94. : 0xFF000000 /*always black*/;
  95. } else {
  96. // white in the AND mask
  97. *(uint32_t *)&output[i * 4] =
  98. !!xorMask ? 0xFFFFFFFF /*source inverted*/
  99. : 0 /*transparent*/;
  100. }
  101. }
  102. bfree(mask);
  103. *width = bmp.bmWidth;
  104. *height = bmp.bmHeight;
  105. return output;
  106. }
  107. static inline uint8_t *cursor_capture_icon_bitmap(ICONINFO *ii, uint32_t *width,
  108. uint32_t *height,
  109. bool *monochrome)
  110. {
  111. uint8_t *output;
  112. *monochrome = false;
  113. output = copy_from_color(ii, width, height);
  114. if (!output) {
  115. *monochrome = true;
  116. output = copy_from_mask(ii, width, height);
  117. }
  118. return output;
  119. }
  120. static gs_texture_t *get_cached_texture(struct cursor_data *data, uint32_t cx,
  121. uint32_t cy)
  122. {
  123. struct cached_cursor cc;
  124. for (size_t i = 0; i < data->cached_textures.num; i++) {
  125. struct cached_cursor *pcc = &data->cached_textures.array[i];
  126. if (pcc->cx == cx && pcc->cy == cy)
  127. return pcc->texture;
  128. }
  129. cc.texture = gs_texture_create(cx, cy, GS_BGRA, 1, NULL, GS_DYNAMIC);
  130. cc.cx = cx;
  131. cc.cy = cy;
  132. da_push_back(data->cached_textures, &cc);
  133. return cc.texture;
  134. }
  135. static inline bool cursor_capture_icon(struct cursor_data *data, HICON icon)
  136. {
  137. uint8_t *bitmap;
  138. uint32_t height;
  139. uint32_t width;
  140. ICONINFO ii;
  141. if (!icon) {
  142. return false;
  143. }
  144. if (!GetIconInfo(icon, &ii)) {
  145. return false;
  146. }
  147. bitmap = cursor_capture_icon_bitmap(&ii, &width, &height,
  148. &data->monochrome);
  149. if (bitmap) {
  150. if (data->last_cx != width || data->last_cy != height) {
  151. data->texture = get_cached_texture(data, width, height);
  152. data->last_cx = width;
  153. data->last_cy = height;
  154. }
  155. gs_texture_set_image(data->texture, bitmap, width * 4, false);
  156. bfree(bitmap);
  157. data->x_hotspot = ii.xHotspot;
  158. data->y_hotspot = ii.yHotspot;
  159. }
  160. DeleteObject(ii.hbmColor);
  161. DeleteObject(ii.hbmMask);
  162. return !!data->texture;
  163. }
  164. void cursor_capture(struct cursor_data *data)
  165. {
  166. CURSORINFO ci = {0};
  167. HICON icon;
  168. ci.cbSize = sizeof(ci);
  169. if (!GetCursorInfo(&ci)) {
  170. data->visible = false;
  171. return;
  172. }
  173. memcpy(&data->cursor_pos, &ci.ptScreenPos, sizeof(data->cursor_pos));
  174. if (data->current_cursor == ci.hCursor) {
  175. return;
  176. }
  177. icon = CopyIcon(ci.hCursor);
  178. data->visible = cursor_capture_icon(data, icon);
  179. data->current_cursor = ci.hCursor;
  180. if ((ci.flags & CURSOR_SHOWING) == 0)
  181. data->visible = false;
  182. DestroyIcon(icon);
  183. }
  184. void cursor_draw(struct cursor_data *data, long x_offset, long y_offset,
  185. long width, long height)
  186. {
  187. long x = data->cursor_pos.x + x_offset;
  188. long y = data->cursor_pos.y + y_offset;
  189. long x_draw = x - data->x_hotspot;
  190. long y_draw = y - data->y_hotspot;
  191. if (x < 0 || x > width || y < 0 || y > height)
  192. return;
  193. if (data->visible && !!data->texture) {
  194. gs_blend_state_push();
  195. enum gs_blend_type blendMode = data->monochrome
  196. ? GS_BLEND_INVDSTCOLOR
  197. : GS_BLEND_SRCALPHA;
  198. gs_blend_function_separate(blendMode, /*src_color*/
  199. GS_BLEND_INVSRCALPHA /*dest_color*/,
  200. GS_BLEND_ONE /*src_alpha*/,
  201. GS_BLEND_INVSRCALPHA /*dest_alpha*/);
  202. gs_matrix_push();
  203. obs_source_draw(data->texture, x_draw, y_draw, 0, 0, false);
  204. gs_matrix_pop();
  205. gs_blend_state_pop();
  206. }
  207. }
  208. void cursor_data_free(struct cursor_data *data)
  209. {
  210. for (size_t i = 0; i < data->cached_textures.num; i++) {
  211. struct cached_cursor *pcc = &data->cached_textures.array[i];
  212. gs_texture_destroy(pcc->texture);
  213. }
  214. da_free(data->cached_textures);
  215. memset(data, 0, sizeof(*data));
  216. }