cursor-capture.c 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  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 % 7) & 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 inline bool cursor_capture_icon(struct cursor_data *data, HICON icon)
  107. {
  108. uint8_t *bitmap;
  109. uint32_t height;
  110. uint32_t width;
  111. ICONINFO ii;
  112. gs_texture_destroy(data->texture);
  113. data->texture = NULL;
  114. if (!icon) {
  115. return false;
  116. }
  117. if (!GetIconInfo(icon, &ii)) {
  118. return false;
  119. }
  120. bitmap = cursor_capture_icon_bitmap(&ii, &width, &height);
  121. if (bitmap) {
  122. data->texture = gs_texture_create(width, height, GS_BGRA,
  123. 1, &bitmap, 0);
  124. bfree(bitmap);
  125. data->x_hotspot = ii.xHotspot;
  126. data->y_hotspot = ii.yHotspot;
  127. }
  128. DeleteObject(ii.hbmColor);
  129. DeleteObject(ii.hbmMask);
  130. return !!data->texture;
  131. }
  132. void cursor_capture(struct cursor_data *data)
  133. {
  134. CURSORINFO ci = {0};
  135. HICON icon;
  136. ci.cbSize = sizeof(ci);
  137. if (!GetCursorInfo(&ci)) {
  138. data->visible = false;
  139. return;
  140. }
  141. memcpy(&data->cursor_pos, &ci.ptScreenPos, sizeof(data->cursor_pos));
  142. if (data->current_cursor == ci.hCursor) {
  143. return;
  144. }
  145. icon = CopyIcon(ci.hCursor);
  146. data->visible = cursor_capture_icon(data, icon);
  147. data->current_cursor = ci.hCursor;
  148. if ((ci.flags & CURSOR_SHOWING) == 0)
  149. data->visible = false;
  150. DestroyIcon(icon);
  151. }
  152. void cursor_draw(struct cursor_data *data, long x_offset, long y_offset,
  153. float x_scale, float y_scale, long width, long height)
  154. {
  155. long x = data->cursor_pos.x + x_offset;
  156. long y = data->cursor_pos.y + y_offset;
  157. long x_draw = x - data->x_hotspot;
  158. long y_draw = y - data->y_hotspot;
  159. if (x < 0 || x > width || y < 0 || y > height)
  160. return;
  161. if (data->visible && !!data->texture) {
  162. gs_matrix_push();
  163. gs_matrix_scale3f(x_scale, y_scale, 1.0f);
  164. obs_source_draw(data->texture, x_draw, y_draw, 0, 0, false);
  165. gs_matrix_pop();
  166. }
  167. }
  168. void cursor_data_free(struct cursor_data *data)
  169. {
  170. gs_texture_destroy(data->texture);
  171. memset(data, 0, sizeof(*data));
  172. }