dc-capture.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. #include "dc-capture.h"
  2. #define WIN32_LEAN_AND_MEAN
  3. #include <windows.h>
  4. static inline void init_textures(struct dc_capture *capture)
  5. {
  6. if (capture->compatibility) {
  7. capture->texture = gs_texture_create(capture->width, capture->height, GS_BGRA, 1, NULL, GS_DYNAMIC);
  8. } else {
  9. capture->texture = gs_texture_create_gdi(capture->width, capture->height);
  10. if (capture->texture) {
  11. capture->extra_texture =
  12. gs_texture_create(capture->width, capture->height, GS_BGRA, 1, NULL, 0);
  13. if (!capture->extra_texture) {
  14. blog(LOG_WARNING, "[dc_capture_init] Failed to "
  15. "create textures");
  16. gs_texture_destroy(capture->texture);
  17. capture->texture = NULL;
  18. }
  19. }
  20. }
  21. if (!capture->texture) {
  22. blog(LOG_WARNING, "[dc_capture_init] Failed to "
  23. "create textures");
  24. return;
  25. }
  26. capture->valid = true;
  27. }
  28. void dc_capture_init(struct dc_capture *capture, int x, int y, uint32_t width, uint32_t height, bool cursor,
  29. bool compatibility)
  30. {
  31. memset(capture, 0, sizeof(struct dc_capture));
  32. capture->x = x;
  33. capture->y = y;
  34. capture->width = width;
  35. capture->height = height;
  36. capture->capture_cursor = cursor;
  37. obs_enter_graphics();
  38. if (!gs_gdi_texture_available())
  39. compatibility = true;
  40. capture->compatibility = compatibility;
  41. init_textures(capture);
  42. obs_leave_graphics();
  43. if (!capture->valid)
  44. return;
  45. if (compatibility) {
  46. BITMAPINFO bi = {0};
  47. BITMAPINFOHEADER *bih = &bi.bmiHeader;
  48. bih->biSize = sizeof(BITMAPINFOHEADER);
  49. bih->biBitCount = 32;
  50. bih->biWidth = width;
  51. bih->biHeight = height;
  52. bih->biPlanes = 1;
  53. const HDC hdc = CreateCompatibleDC(NULL);
  54. if (hdc) {
  55. const HBITMAP bmp =
  56. CreateDIBSection(capture->hdc, &bi, DIB_RGB_COLORS, (void **)&capture->bits, NULL, 0);
  57. if (bmp) {
  58. capture->hdc = hdc;
  59. capture->bmp = bmp;
  60. capture->old_bmp = SelectObject(capture->hdc, capture->bmp);
  61. } else {
  62. DeleteDC(hdc);
  63. }
  64. }
  65. }
  66. }
  67. void dc_capture_free(struct dc_capture *capture)
  68. {
  69. if (capture->hdc) {
  70. SelectObject(capture->hdc, capture->old_bmp);
  71. DeleteDC(capture->hdc);
  72. DeleteObject(capture->bmp);
  73. }
  74. obs_enter_graphics();
  75. gs_texture_destroy(capture->extra_texture);
  76. gs_texture_destroy(capture->texture);
  77. obs_leave_graphics();
  78. memset(capture, 0, sizeof(struct dc_capture));
  79. }
  80. static void draw_cursor(struct dc_capture *capture, HDC hdc, HWND window)
  81. {
  82. HICON icon;
  83. ICONINFO ii;
  84. CURSORINFO *ci = &capture->ci;
  85. POINT win_pos = {capture->x, capture->y};
  86. if (!(capture->ci.flags & CURSOR_SHOWING))
  87. return;
  88. icon = CopyIcon(capture->ci.hCursor);
  89. if (!icon)
  90. return;
  91. if (GetIconInfo(icon, &ii)) {
  92. POINT pos;
  93. if (window)
  94. ClientToScreen(window, &win_pos);
  95. pos.x = ci->ptScreenPos.x - (int)ii.xHotspot - win_pos.x;
  96. pos.y = ci->ptScreenPos.y - (int)ii.yHotspot - win_pos.y;
  97. DrawIconEx(hdc, pos.x, pos.y, icon, 0, 0, 0, NULL, DI_NORMAL);
  98. DeleteObject(ii.hbmColor);
  99. DeleteObject(ii.hbmMask);
  100. }
  101. DestroyIcon(icon);
  102. }
  103. static inline HDC dc_capture_get_dc(struct dc_capture *capture)
  104. {
  105. if (!capture->valid)
  106. return NULL;
  107. if (capture->compatibility)
  108. return capture->hdc;
  109. else
  110. return gs_texture_get_dc(capture->texture);
  111. }
  112. static inline void dc_capture_release_dc(struct dc_capture *capture)
  113. {
  114. if (capture->compatibility) {
  115. gs_texture_set_image(capture->texture, capture->bits, capture->width * 4, false);
  116. } else {
  117. gs_texture_release_dc(capture->texture);
  118. }
  119. }
  120. void dc_capture_capture(struct dc_capture *capture, HWND window)
  121. {
  122. HDC hdc_target;
  123. HDC hdc;
  124. if (capture->capture_cursor) {
  125. memset(&capture->ci, 0, sizeof(CURSORINFO));
  126. capture->ci.cbSize = sizeof(CURSORINFO);
  127. capture->cursor_captured = GetCursorInfo(&capture->ci);
  128. }
  129. hdc = dc_capture_get_dc(capture);
  130. if (!hdc) {
  131. blog(LOG_WARNING, "[capture_screen] Failed to get "
  132. "texture DC");
  133. return;
  134. }
  135. hdc_target = GetDC(window);
  136. BitBlt(hdc, 0, 0, capture->width, capture->height, hdc_target, capture->x, capture->y, SRCCOPY);
  137. ReleaseDC(NULL, hdc_target);
  138. if (capture->cursor_captured && !capture->cursor_hidden)
  139. draw_cursor(capture, hdc, window);
  140. dc_capture_release_dc(capture);
  141. capture->texture_written = true;
  142. }
  143. void dc_capture_render(struct dc_capture *capture, bool texcoords_centered)
  144. {
  145. if (capture->valid && capture->texture_written) {
  146. gs_texture_t *texture = capture->texture;
  147. const bool compatibility = capture->compatibility;
  148. bool linear_sample = compatibility;
  149. if (!linear_sample && !texcoords_centered) {
  150. gs_texture_t *const extra_texture = capture->extra_texture;
  151. gs_copy_texture(extra_texture, texture);
  152. texture = extra_texture;
  153. linear_sample = true;
  154. }
  155. const char *tech_name = "Draw";
  156. float multiplier = 1.f;
  157. switch (gs_get_color_space()) {
  158. case GS_CS_SRGB_16F:
  159. case GS_CS_709_EXTENDED:
  160. if (!linear_sample)
  161. tech_name = "DrawSrgbDecompress";
  162. break;
  163. case GS_CS_709_SCRGB:
  164. if (linear_sample)
  165. tech_name = "DrawMultiply";
  166. else
  167. tech_name = "DrawSrgbDecompressMultiply";
  168. multiplier = obs_get_video_sdr_white_level() / 80.f;
  169. }
  170. gs_effect_t *effect = obs_get_base_effect(OBS_EFFECT_OPAQUE);
  171. gs_technique_t *tech = gs_effect_get_technique(effect, tech_name);
  172. gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image");
  173. const bool previous = gs_framebuffer_srgb_enabled();
  174. gs_enable_framebuffer_srgb(linear_sample);
  175. gs_enable_blending(false);
  176. if (linear_sample)
  177. gs_effect_set_texture_srgb(image, texture);
  178. else
  179. gs_effect_set_texture(image, texture);
  180. gs_eparam_t *multiplier_param = gs_effect_get_param_by_name(effect, "multiplier");
  181. gs_effect_set_float(multiplier_param, multiplier);
  182. const uint32_t flip = compatibility ? GS_FLIP_V : 0;
  183. const size_t passes = gs_technique_begin(tech);
  184. for (size_t i = 0; i < passes; i++) {
  185. if (gs_technique_begin_pass(tech, i)) {
  186. gs_draw_sprite(texture, flip, 0, 0);
  187. gs_technique_end_pass(tech);
  188. }
  189. }
  190. gs_technique_end(tech);
  191. gs_enable_blending(true);
  192. gs_enable_framebuffer_srgb(previous);
  193. }
  194. }