cursor-capture.c 5.2 KB

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