这次主要吧鼠标绘制出来,废了老劲了...
首先需要设置混合模式,不然鼠标的透明部分会呈现出纯黑色,不能直接将鼠标数据复制到抓取的桌面纹理中。
// 透明
ID3D11BlendState* blendState;
D3D11_BLEND_DESC blendDesc = {};
blendDesc.AlphaToCoverageEnable = FALSE;
blendDesc.IndependentBlendEnable = FALSE;
blendDesc.RenderTarget->BlendEnable = TRUE; // 是否开启混合
blendDesc.RenderTarget->SrcBlend = D3D11_BLEND_SRC_ALPHA; // 将源图的 alpha 作为 src rgb 的混合因子
blendDesc.RenderTarget->DestBlend = D3D11_BLEND_INV_SRC_ALPHA; // 将源图的 1-alpha 作为 dst rgb 的混合因子
blendDesc.RenderTarget->BlendOp = D3D11_BLEND_OP_ADD; // 进行相加操作
blendDesc.RenderTarget->SrcBlendAlpha = D3D11_BLEND_ONE; //
blendDesc.RenderTarget->DestBlendAlpha = D3D11_BLEND_ONE;
blendDesc.RenderTarget->BlendOpAlpha = D3D11_BLEND_OP_ADD;
blendDesc.RenderTarget->RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; // 可以写入的位置
this->m_device->CreateBlendState(&blendDesc, &blendState);
const FLOAT BlendFactor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
this->m_context->OMSetBlendState(blendState, BlendFactor, 0xffffffff);
SAFE_RELEASE(blendState);
以上是设置混合模式的代码,主要是将鼠标的透明部分正确显示。
需要将鼠标数据读取为单独的纹理,也就是每次渲染两个纹理视图,因此优化了渲染结构。
将所有可绑定到管线的组件统一封装,提供出接口。
class BindAbleInterface {
public:
BindAbleInterface() = default;
virtual void bind(ID3D11DeviceContext* _context) = 0;
virtual ~BindAbleInterface() = default;
};
class IndexBufferBindAble : public BindAbleInterface {
public:
IndexBufferBindAble(ID3D11Device* _device, const UINT16* _indices, size_t _indicesSize);
void bind(ID3D11DeviceContext* _context) override;
~IndexBufferBindAble() override;
private:
Microsoft::WRL::ComPtr<ID3D11Buffer> m_buffer = nullptr;
};
class VertexScalBufferBindAble: public BindAbleInterface {
public:
__declspec(align(16)) struct {
__declspec(align(16)) struct {
float x;
float y;
float z;
} Scal;
__declspec(align(16)) struct {
float x;
float y;
} Translation;
} m_psConstant;
VertexScalBufferBindAble(ID3D11Device* _device, float _x_rate, float _y_rate, float _z_rate, float _x, float _y);
void bind(ID3D11DeviceContext* _context) override;
void changeTranslation(ID3D11DeviceContext* _context, float _x, float _y);
~VertexScalBufferBindAble();
private:
Microsoft::WRL::ComPtr<ID3D11Buffer> m_ps_constant_buffer = nullptr;
};
void VertexScalBufferBindAble::bind(ID3D11DeviceContext* _context)
{
_context->VSSetConstantBuffers(0, 1, this->m_ps_constant_buffer.GetAddressOf());
}
void VertexScalBufferBindAble::changeTranslation(ID3D11DeviceContext* _context, float _x, float _y)
{
m_psConstant.Translation.x = _x;
m_psConstant.Translation.y = _y;
D3D11_MAPPED_SUBRESOURCE mapped_resource;
_context->Map(this->m_ps_constant_buffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource);
memcpy(mapped_resource.pData, (void*)&m_psConstant, sizeof(m_psConstant));
_context->Unmap(this->m_ps_constant_buffer.Get(), 0);
}
VertexScalBufferBindAble::~VertexScalBufferBindAble()
{
}
以上是VertexScalBufferBindAble的例子,给出缩放和平移矩阵,主要给鼠标纹理进行缩放和平移。
修改VertexShader,将变换矩阵乘进去
struct VSOut
{
float2 tex: TEXCOORD;
float4 pos : SV_POSITION;
};
struct Scal {
float x;
float y;
float z;
};
struct Translation {
float x;
float y;
};
cbuffer Cbuf {
Scal scal;
Translation translation;
};
VSOut main_VS(float3 pos : POSITION, float2 tex : TEXCOORD)
{
VSOut vsout;
vsout.pos = float4((pos.x * scal.x) + translation.x, (pos.y * scal.y) + translation.y, pos.z * scal.z, 1);
vsout.tex = tex;
return vsout;
}
接下来,绘制鼠标,其它逻辑详细可以看源码,这里说一下怎么将鼠标转为纹理
void CursorTextureDrawAble::drawCursor(MyDx11* _myDx11)
{
CURSORINFO ci = { 0 };
ci.cbSize = sizeof(ci);
if (!GetCursorInfo(&ci)) {
return;
}
float offset_x = ci.ptScreenPos.x / this->m_base_x;
float offset_y = ci.ptScreenPos.y / this->m_base_y;
offset_x = offset_x * 2 - 1;
offset_y = 1 - offset_y * 2;
this->m_vertex_scal_buffer->changeTranslation(this->getContext(_myDx11), offset_x, offset_y);
if (this->m_cache_cursor == nullptr || this->m_cache_cursor != ci.hCursor) {
this->m_cache_cursor = ci.hCursor;
}
else {
return;
}
}
缓存一下已经获取的鼠标句柄,如果没有变化就不需要更改纹理数据了,算一下鼠标的相对位置进行平移变换。
bool CursorTextureDrawAble::copyFromColor(MyDx11* _myDx11, ICONINFO& _icon_info, BITMAP& _bmp)
{
uint8_t* output = nullptr;
if (GetObject(_icon_info.hbmColor, sizeof(_bmp), &_bmp) == 0) {
return false;
}
unsigned int size = _bmp.bmHeight * _bmp.bmWidthBytes;
output = new uint8_t[size];
GetBitmapBits(_icon_info.hbmColor, size, output);
if (_bmp.bmWidth != this->m_width && _bmp.bmHeight != this->m_height) {
this->changeSize(_myDx11, _bmp.bmWidth, _bmp.bmHeight, false);
}
this->copyData(_myDx11, output, size);
delete[] output;
return true;
}
bool CursorTextureDrawAble::copyFromMask(MyDx11* _myDx11, ICONINFO& _icon_info, BITMAP& _bmp)
{
long pixels;
long bottom;
uint8_t pix_byte;
bool alpha = false;
if (GetObject(_icon_info.hbmMask, sizeof(_bmp), &_bmp) == 0) {
return false;
}
uint8_t* output;
uint8_t* mask;
unsigned int size = _bmp.bmHeight * _bmp.bmWidthBytes;
mask = new uint8_t[size];
GetBitmapBits(_icon_info.hbmMask, size, mask);
_bmp.bmHeight /= 2;
pixels = _bmp.bmHeight * _bmp.bmWidth;
output = new uint8_t[pixels * 4];
ZeroMemory(output, pixels * 4);
bottom = _bmp.bmWidthBytes * _bmp.bmHeight;
for (long i = 0; i < pixels; i++) {
uint8_t andMask = this->bitToAlpha(mask, i);
uint8_t xorMask = this->bitToAlpha(mask + bottom, i);
if (!andMask) {
// black in the AND mask
*(uint32_t*)&output[i * 4] =
!!xorMask ? 0x00FFFFFF /*always white*/
: 0xFF000000 /*always black*/;
}
else {
// white in the AND mask
*(uint32_t*)&output[i * 4] =
!!xorMask ? 0xFFFFFFFF /*source inverted*/
: 0 /*transparent*/;
}
}
if (_bmp.bmWidth != this->m_width && _bmp.bmHeight != this->m_height) {
this->changeSize(_myDx11, _bmp.bmWidth, _bmp.bmHeight, false);
}
this->copyData(_myDx11, output, pixels * 4);
delete[] output;
delete[] mask;
return true;
}
uint8_t CursorTextureDrawAble::bitToAlpha(uint8_t* data, long pixel)
{
uint8_t pix_byte = data[pixel / 8];
bool alpha = (pix_byte >> (7 - pixel % 8) & 1) != 0;
return alpha ? 0xFF : 0;
}
void TextureDrawAble::copyData(MyDx11* _myDx11, uint8_t* _buffer, size_t _size)
{
D3D11_MAPPED_SUBRESOURCE target_map_res;
UINT sub_resource = D3D11CalcSubresource(0, 0, 0);
ID3D11DeviceContext* context = this->getContext(_myDx11);
HRESULT hr = context->Map(this->m_texture.Get(), sub_resource, D3D11_MAP_WRITE_DISCARD, 0, &target_map_res);
if (FAILED(hr)) {
return;
}
memcpy_s(target_map_res.pData, _size, _buffer, _size);
context->Unmap(this->m_texture.Get(), sub_resource);
}
以上是通过鼠标句柄获取到bitmap,再copy到纹理中,参考了obs源码。之前提到当鼠标处于选中文字形状,也就是类似I的形状时获取不到鼠标。其原因在于需要从hbmMask中获取数据。里面的处理逻辑看不太明白,直接拿的obs的源码。
关于编码器性能问题,设置编码线程数可以解决部分问题,但性能依旧不太高。
this->m_encode_pCodecCtx->thread_count = 4;
如果实在不行,考虑使用硬编码方式。
具体详情转到源码。