242 lines
5.5 KiB
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;
|
|
}
|