obs-studio/plugins/win-capture/dc-capture.c

242 lines
5.5 KiB
C

#include "dc-capture.h"
#define WIN32_MEAN_AND_LEAN
#include <windows.h>
static inline void init_textures(struct dc_capture *capture)
{
for (int i = 0; i < capture->num_textures; i++) {
if (capture->compatibility)
capture->textures[i] = gs_create_texture(
capture->width, capture->height,
GS_BGRA, 1, NULL, GS_DYNAMIC);
else
capture->textures[i] = gs_create_gdi_texture(
capture->width, capture->height);
if (!capture->textures[i]) {
blog(LOG_WARNING, "[dc_capture_init] Failed to "
"create textures");
return;
}
}
capture->valid = true;
}
void dc_capture_init(struct dc_capture *capture, int x, int y,
uint32_t width, uint32_t height, bool cursor,
bool compatibility)
{
memset(capture, 0, sizeof(struct dc_capture));
capture->x = x;
capture->y = y;
capture->width = width;
capture->height = height;
capture->capture_cursor = cursor;
gs_entercontext(obs_graphics());
if (!gs_gdi_texture_available())
compatibility = true;
capture->compatibility = compatibility;
capture->num_textures = compatibility ? 1 : 2;
init_textures(capture);
gs_leavecontext();
if (!capture->valid)
return;
if (compatibility) {
BITMAPINFO bi = {0};
BITMAPINFOHEADER *bih = &bi.bmiHeader;
bih->biSize = sizeof(BITMAPINFOHEADER);
bih->biBitCount = 32;
bih->biWidth = width;
bih->biHeight = height;
bih->biPlanes = 1;
capture->hdc = CreateCompatibleDC(NULL);
capture->bmp = CreateDIBSection(capture->hdc, &bi,
DIB_RGB_COLORS, (void**)&capture->bits,
NULL, 0);
capture->old_bmp = SelectObject(capture->hdc, capture->bmp);
}
}
void dc_capture_free(struct dc_capture *capture)
{
if (capture->hdc) {
SelectObject(capture->hdc, capture->old_bmp);
DeleteDC(capture->hdc);
DeleteObject(capture->bmp);
}
gs_entercontext(obs_graphics());
for (int i = 0; i < capture->num_textures; i++)
texture_destroy(capture->textures[i]);
gs_leavecontext();
memset(capture, 0, sizeof(struct dc_capture));
}
static void draw_cursor(struct dc_capture *capture, HDC hdc)
{
HICON icon;
ICONINFO ii;
CURSORINFO *ci = &capture->ci;
if (!(capture->ci.flags & CURSOR_SHOWING))
return;
icon = CopyIcon(capture->ci.hCursor);
if (!icon)
return;
if (GetIconInfo(icon, &ii)) {
POINT pos;
pos.x = ci->ptScreenPos.x - (int)ii.xHotspot - capture->x;
pos.y = ci->ptScreenPos.y - (int)ii.yHotspot - capture->y;
DrawIcon(hdc, pos.x, pos.y, icon);
DeleteObject(ii.hbmColor);
DeleteObject(ii.hbmMask);
}
DestroyIcon(icon);
}
static inline HDC dc_capture_get_dc(struct dc_capture *capture)
{
if (!capture->valid)
return NULL;
if (capture->compatibility)
return capture->hdc;
else
return texture_get_dc(capture->textures[capture->cur_tex]);
}
static inline void dc_capture_release_dc(struct dc_capture *capture)
{
if (capture->compatibility) {
texture_setimage(capture->textures[capture->cur_tex],
capture->bits, capture->width*4, false);
} else {
texture_release_dc(capture->textures[capture->cur_tex]);
}
}
void dc_capture_capture(struct dc_capture *capture, HWND window)
{
HDC hdc_target;
HDC hdc;
if (capture->capture_cursor) {
memset(&capture->ci, 0, sizeof(CURSORINFO));
capture->ci.cbSize = sizeof(CURSORINFO);
capture->cursor_captured = GetCursorInfo(&capture->ci);
}
if (++capture->cur_tex == capture->num_textures)
capture->cur_tex = 0;
hdc = dc_capture_get_dc(capture);
if (!hdc) {
blog(LOG_WARNING, "[capture_screen] Failed to get "
"texture DC");
return;
}
hdc_target = GetDC(window);
BitBlt(hdc, 0, 0, capture->width, capture->height,
hdc_target, capture->x, capture->y, SRCCOPY);
ReleaseDC(NULL, hdc_target);
if (capture->cursor_captured)
draw_cursor(capture, hdc);
dc_capture_release_dc(capture);
capture->textures_written[capture->cur_tex] = true;
}
static void draw_texture(struct dc_capture *capture, int id, effect_t effect)
{
texture_t texture = capture->textures[id];
technique_t tech = effect_gettechnique(effect, "Draw");
eparam_t image = effect_getparambyname(effect, "image");
size_t passes;
effect_settexture(effect, image, texture);
passes = technique_begin(tech);
for (size_t i = 0; i < passes; i++) {
if (technique_beginpass(tech, i)) {
if (capture->compatibility)
gs_draw_sprite(texture, GS_FLIP_V, 0, 0);
else
gs_draw_sprite(texture, 0, 0, 0);
technique_endpass(tech);
}
}
technique_end(tech);
}
void dc_capture_render(struct dc_capture *capture, effect_t effect)
{
int last_tex = (capture->cur_tex > 0) ?
capture->cur_tex-1 : capture->num_textures-1;
if (!capture->valid)
return;
if (capture->textures_written[last_tex])
draw_texture(capture, last_tex, effect);
}
effect_t create_opaque_effect(void)
{
effect_t opaque_effect;
char *effect_file;
char *error_string = NULL;
effect_file = obs_find_plugin_file("win-capture/opaque.effect");
if (!effect_file) {
blog(LOG_ERROR, "[create_opaque_effect] Could not find "
"opaque effect file");
return false;
}
gs_entercontext(obs_graphics());
opaque_effect = gs_create_effect_from_file(effect_file, &error_string);
if (!opaque_effect) {
if (error_string)
blog(LOG_ERROR, "[create_opaque_effect] Failed to "
"create opaque effect:\n%s",
error_string);
else
blog(LOG_ERROR, "[create_opaque_effect] Failed to "
"create opaque effect");
}
bfree(effect_file);
bfree(error_string);
gs_leavecontext();
return opaque_effect;
}