Opt: improve adaptive-size rendering
This commit is contained in:
@@ -281,6 +281,13 @@ CScreenSpyDlg::~CScreenSpyDlg()
|
|||||||
::DeleteDC(m_hFullMemDC); //Create匹配内存DC
|
::DeleteDC(m_hFullMemDC); //Create匹配内存DC
|
||||||
|
|
||||||
::DeleteObject(m_BitmapHandle);
|
::DeleteObject(m_BitmapHandle);
|
||||||
|
|
||||||
|
if (m_hComposeDC) {
|
||||||
|
SelectObject(m_hComposeDC, m_hComposeOldBmp);
|
||||||
|
DeleteObject(m_hComposeBmp);
|
||||||
|
DeleteDC(m_hComposeDC);
|
||||||
|
m_hComposeDC = NULL;
|
||||||
|
}
|
||||||
if (m_BitmapData_Full!=NULL) {
|
if (m_BitmapData_Full!=NULL) {
|
||||||
m_BitmapData_Full = NULL;
|
m_BitmapData_Full = NULL;
|
||||||
}
|
}
|
||||||
@@ -595,6 +602,34 @@ void CScreenSpyDlg::OnLButtonDblClk(UINT nFlags, CPoint point)
|
|||||||
__super::OnLButtonDblClk(nFlags, point);
|
__super::OnLButtonDblClk(nFlags, point);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 计算自适应模式的等比缩放布局。
|
||||||
|
// 纯计算,无副作用,供 OnPaint / OnSize / PrepareDrawing 共用。
|
||||||
|
static void ComputeAdaptiveLayout(
|
||||||
|
int srcW, int srcH, int dstW, int dstH,
|
||||||
|
int& renderW, int& renderH, int& offsetX, int& offsetY,
|
||||||
|
double& wZoom, double& hZoom)
|
||||||
|
{
|
||||||
|
if (srcW <= 0 || srcH <= 0 || dstW <= 0 || dstH <= 0) {
|
||||||
|
renderW = dstW; renderH = dstH;
|
||||||
|
offsetX = offsetY = 0;
|
||||||
|
wZoom = hZoom = 1.0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((long long)srcW * dstH > (long long)srcH * dstW) {
|
||||||
|
renderW = dstW;
|
||||||
|
renderH = (int)((long long)dstW * srcH / srcW);
|
||||||
|
} else {
|
||||||
|
renderH = dstH;
|
||||||
|
renderW = (int)((long long)dstH * srcW / srcH);
|
||||||
|
}
|
||||||
|
if (renderW < 1) renderW = 1;
|
||||||
|
if (renderH < 1) renderH = 1;
|
||||||
|
offsetX = (dstW - renderW) / 2;
|
||||||
|
offsetY = (dstH - renderH) / 2;
|
||||||
|
wZoom = (double)srcW / renderW;
|
||||||
|
hZoom = (double)srcH / renderH;
|
||||||
|
}
|
||||||
|
|
||||||
void CScreenSpyDlg::PrepareDrawing(const LPBITMAPINFO bmp)
|
void CScreenSpyDlg::PrepareDrawing(const LPBITMAPINFO bmp)
|
||||||
{
|
{
|
||||||
if (m_hFullDC) ::ReleaseDC(m_hWnd, m_hFullDC);
|
if (m_hFullDC) ::ReleaseDC(m_hWnd, m_hFullDC);
|
||||||
@@ -624,8 +659,17 @@ void CScreenSpyDlg::PrepareDrawing(const LPBITMAPINFO bmp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
GetClientRect(&m_CRect);
|
GetClientRect(&m_CRect);
|
||||||
m_wZoom = ((double)bmp->bmiHeader.biWidth) / ((double)(m_CRect.Width()));
|
if (m_bAdaptiveSize) {
|
||||||
m_hZoom = ((double)bmp->bmiHeader.biHeight) / ((double)(m_CRect.Height()));
|
int renderW, renderH;
|
||||||
|
ComputeAdaptiveLayout(bmp->bmiHeader.biWidth, bmp->bmiHeader.biHeight,
|
||||||
|
m_CRect.Width(), m_CRect.Height(),
|
||||||
|
renderW, renderH, m_offsetX, m_offsetY,
|
||||||
|
m_wZoom, m_hZoom);
|
||||||
|
} else {
|
||||||
|
m_offsetX = m_offsetY = 0;
|
||||||
|
m_wZoom = m_CRect.Width() > 0 ? (double)bmp->bmiHeader.biWidth / m_CRect.Width() : 1.0;
|
||||||
|
m_hZoom = m_CRect.Height() > 0 ? (double)bmp->bmiHeader.biHeight / m_CRect.Height() : 1.0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL CScreenSpyDlg::OnInitDialog()
|
BOOL CScreenSpyDlg::OnInitDialog()
|
||||||
@@ -1660,14 +1704,22 @@ bool CScreenSpyDlg::Decode(LPBYTE Buffer, int size)
|
|||||||
//I420 ---> ARGB.
|
//I420 ---> ARGB.
|
||||||
//WaitForSingleObject(m_hMutex,INFINITE);
|
//WaitForSingleObject(m_hMutex,INFINITE);
|
||||||
|
|
||||||
|
{
|
||||||
|
int W = m_BitmapInfor_Full->bmiHeader.biWidth;
|
||||||
|
int H = m_BitmapInfor_Full->bmiHeader.biHeight;
|
||||||
|
// biClrImportant==1: top-down H264 (Android MediaCodec standard output).
|
||||||
|
// 精确匹配 1,避免调色板 DIB(biClrImportant=N 表示重要色数量)被误判。
|
||||||
|
// libyuv 负高度只翻转目标写入方向,源指针不变,data[0] 仍指向第一行。
|
||||||
|
bool topDown = (m_BitmapInfor_Full->bmiHeader.biClrImportant == 1);
|
||||||
libyuv::I420ToARGB(
|
libyuv::I420ToARGB(
|
||||||
m_AVFrame.data[0], m_AVFrame.linesize[0],
|
m_AVFrame.data[0], m_AVFrame.linesize[0],
|
||||||
m_AVFrame.data[1], m_AVFrame.linesize[1],
|
m_AVFrame.data[1], m_AVFrame.linesize[1],
|
||||||
m_AVFrame.data[2], m_AVFrame.linesize[2],
|
m_AVFrame.data[2], m_AVFrame.linesize[2],
|
||||||
(uint8_t*)m_BitmapData_Full,
|
(uint8_t*)m_BitmapData_Full,
|
||||||
m_BitmapInfor_Full->bmiHeader.biWidth*4,
|
W * 4,
|
||||||
m_BitmapInfor_Full->bmiHeader.biWidth,
|
W,
|
||||||
m_BitmapInfor_Full->bmiHeader.biHeight);
|
topDown ? -H : H);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Mprintf("avcodec_receive_frame failed with error: %d\n", err);
|
Mprintf("avcodec_receive_frame failed with error: %d\n", err);
|
||||||
@@ -1715,8 +1767,53 @@ void CScreenSpyDlg::OnPaint()
|
|||||||
m_rcZoomSrc.Width(), m_rcZoomSrc.Height(),
|
m_rcZoomSrc.Width(), m_rcZoomSrc.Height(),
|
||||||
SRCCOPY);
|
SRCCOPY);
|
||||||
} else if (m_bAdaptiveSize) {
|
} else if (m_bAdaptiveSize) {
|
||||||
// 尺寸相同时用 BitBlt(更快),否则用 StretchBlt
|
// 保持宽高比居中缩放,多余区域填黑(信箱 / 柱状黑边)
|
||||||
if (srcW == dstW && srcH == dstH) {
|
// 同步更新 m_offsetX/Y 和 m_wZoom/hZoom,供坐标映射使用
|
||||||
|
int renderW, renderH;
|
||||||
|
ComputeAdaptiveLayout(srcW, srcH, dstW, dstH,
|
||||||
|
renderW, renderH, m_offsetX, m_offsetY,
|
||||||
|
m_wZoom, m_hZoom);
|
||||||
|
|
||||||
|
// 图像未填满窗口(含整除截断 renderH=dstH-1 导致 offset=0 的情况)时走合成路径
|
||||||
|
if (renderW < dstW || renderH < dstH) {
|
||||||
|
// 先在内存 DC 合成完整帧再一次性上屏,避免"先黑后图"的闪烁
|
||||||
|
// 按窗口尺寸缓存 DC/Bitmap,尺寸不变时直接复用,不重复分配 GDI 资源
|
||||||
|
if (!m_hComposeDC || m_nComposeCX != dstW || m_nComposeCY != dstH) {
|
||||||
|
if (m_hComposeDC) {
|
||||||
|
SelectObject(m_hComposeDC, m_hComposeOldBmp);
|
||||||
|
DeleteObject(m_hComposeBmp);
|
||||||
|
DeleteDC(m_hComposeDC);
|
||||||
|
m_hComposeDC = NULL; m_hComposeBmp = NULL; m_hComposeOldBmp = NULL;
|
||||||
|
}
|
||||||
|
m_hComposeDC = CreateCompatibleDC(m_hFullDC);
|
||||||
|
if (m_hComposeDC) {
|
||||||
|
m_hComposeBmp = CreateCompatibleBitmap(m_hFullDC, dstW, dstH);
|
||||||
|
if (m_hComposeBmp) {
|
||||||
|
m_hComposeOldBmp = (HBITMAP)SelectObject(m_hComposeDC, m_hComposeBmp);
|
||||||
|
m_nComposeCX = dstW;
|
||||||
|
m_nComposeCY = dstH;
|
||||||
|
} else {
|
||||||
|
DeleteDC(m_hComposeDC);
|
||||||
|
m_hComposeDC = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m_hComposeDC) {
|
||||||
|
SetStretchBltMode(m_hComposeDC, GetFastStretchMode() ? COLORONCOLOR : HALFTONE);
|
||||||
|
if (!GetFastStretchMode()) SetBrushOrgEx(m_hComposeDC, 0, 0, NULL);
|
||||||
|
RECT rcFull = { 0, 0, dstW, dstH };
|
||||||
|
FillRect(m_hComposeDC, &rcFull, (HBRUSH)GetStockObject(BLACK_BRUSH));
|
||||||
|
StretchBlt(m_hComposeDC, m_offsetX, m_offsetY, renderW, renderH,
|
||||||
|
m_hFullMemDC, 0, 0, srcW, srcH, SRCCOPY);
|
||||||
|
BitBlt(m_hFullDC, 0, 0, dstW, dstH, m_hComposeDC, 0, 0, SRCCOPY);
|
||||||
|
} else {
|
||||||
|
// GDI 资源不足,退化为直接渲染(可能闪一帧黑边,但不崩溃)
|
||||||
|
RECT rcFull = { 0, 0, dstW, dstH };
|
||||||
|
FillRect(m_hFullDC, &rcFull, (HBRUSH)GetStockObject(BLACK_BRUSH));
|
||||||
|
StretchBlt(m_hFullDC, m_offsetX, m_offsetY, renderW, renderH,
|
||||||
|
m_hFullMemDC, 0, 0, srcW, srcH, SRCCOPY);
|
||||||
|
}
|
||||||
|
} else if (srcW == dstW && srcH == dstH) {
|
||||||
BitBlt(m_hFullDC, 0, 0, srcW, srcH, m_hFullMemDC, 0, 0, SRCCOPY);
|
BitBlt(m_hFullDC, 0, 0, srcW, srcH, m_hFullMemDC, 0, 0, SRCCOPY);
|
||||||
} else {
|
} else {
|
||||||
StretchBlt(m_hFullDC, 0, 0, dstW, dstH, m_hFullMemDC, 0, 0, srcW, srcH, SRCCOPY);
|
StretchBlt(m_hFullDC, 0, 0, dstW, dstH, m_hFullMemDC, 0, 0, srcW, srcH, SRCCOPY);
|
||||||
@@ -1784,9 +1881,9 @@ void CScreenSpyDlg::OnPaint()
|
|||||||
// 只有当本地鼠标不在工具栏区域时,才绘制远程位图光标
|
// 只有当本地鼠标不在工具栏区域时,才绘制远程位图光标
|
||||||
if (!rcToolbar.PtInRect(ptLocal)) {
|
if (!rcToolbar.PtInRect(ptLocal)) {
|
||||||
|
|
||||||
// 1. 计算缩放位置
|
// 1. 计算缩放位置(自适应模式需加黑边偏移)
|
||||||
int drawX = m_bAdaptiveSize ? (int)(m_ClientCursorPos.x / m_wZoom) : (m_ClientCursorPos.x - m_ulHScrollPos);
|
int drawX = m_bAdaptiveSize ? (int)(m_ClientCursorPos.x / m_wZoom) + m_offsetX : (m_ClientCursorPos.x - m_ulHScrollPos);
|
||||||
int drawY = m_bAdaptiveSize ? (int)(m_ClientCursorPos.y / m_hZoom) : (m_ClientCursorPos.y - m_ulVScrollPos);
|
int drawY = m_bAdaptiveSize ? (int)(m_ClientCursorPos.y / m_hZoom) + m_offsetY : (m_ClientCursorPos.y - m_ulVScrollPos);
|
||||||
|
|
||||||
// 2. 获取光标句柄(支持自定义光标)
|
// 2. 获取光标句柄(支持自定义光标)
|
||||||
HCURSOR hCursor;
|
HCURSOR hCursor;
|
||||||
@@ -2557,8 +2654,18 @@ void CScreenSpyDlg::SendScaledMouseMessage(MSG* pMsg, bool makeLP)
|
|||||||
|
|
||||||
MYMSG msg(*pMsg);
|
MYMSG msg(*pMsg);
|
||||||
LONG x = LOWORD(pMsg->lParam), y = HIWORD(pMsg->lParam);
|
LONG x = LOWORD(pMsg->lParam), y = HIWORD(pMsg->lParam);
|
||||||
LONG low = m_bAdaptiveSize ? x * m_wZoom : x + m_ulHScrollPos;
|
LONG low, high;
|
||||||
LONG high = m_bAdaptiveSize ? y * m_hZoom : y + m_ulVScrollPos;
|
if (m_bAdaptiveSize) {
|
||||||
|
// 减去黑边偏移后映射到远端坐标,并 clamp 到合法范围
|
||||||
|
// 防止黑边区域的点击转换为负数后经 MAKELPARAM 截断为 WORD 大正数
|
||||||
|
int srcW = m_BitmapInfor_Full ? m_BitmapInfor_Full->bmiHeader.biWidth : 0;
|
||||||
|
int srcH = m_BitmapInfor_Full ? abs(m_BitmapInfor_Full->bmiHeader.biHeight) : 0;
|
||||||
|
low = max(0L, min((LONG)((x - m_offsetX) * m_wZoom), (LONG)(srcW - 1)));
|
||||||
|
high = max(0L, min((LONG)((y - m_offsetY) * m_hZoom), (LONG)(srcH - 1)));
|
||||||
|
} else {
|
||||||
|
low = x + m_ulHScrollPos;
|
||||||
|
high = y + m_ulVScrollPos;
|
||||||
|
}
|
||||||
if (makeLP) msg.lParam = MAKELPARAM(low, high);
|
if (makeLP) msg.lParam = MAKELPARAM(low, high);
|
||||||
msg.pt.x = low;
|
msg.pt.x = low;
|
||||||
msg.pt.y = high;
|
msg.pt.y = high;
|
||||||
@@ -2945,8 +3052,9 @@ CPoint CScreenSpyDlg::ScreenToImage(CPoint pt)
|
|||||||
(int)(m_rcZoomSrc.top + pt.y * scaleY)
|
(int)(m_rcZoomSrc.top + pt.y * scaleY)
|
||||||
);
|
);
|
||||||
} else if (m_bAdaptiveSize) {
|
} else if (m_bAdaptiveSize) {
|
||||||
// 自适应模式:按比例缩放
|
// 自适应模式:先减去黑边偏移,再乘缩放比
|
||||||
return CPoint((int)(pt.x * m_wZoom), (int)(pt.y * m_hZoom));
|
return CPoint((int)((pt.x - m_offsetX) * m_wZoom),
|
||||||
|
(int)((pt.y - m_offsetY) * m_hZoom));
|
||||||
} else {
|
} else {
|
||||||
// 滚动模式:加上滚动偏移
|
// 滚动模式:加上滚动偏移
|
||||||
return CPoint(pt.x + m_ulHScrollPos, pt.y + m_ulVScrollPos);
|
return CPoint(pt.x + m_ulHScrollPos, pt.y + m_ulVScrollPos);
|
||||||
@@ -2972,8 +3080,10 @@ CPoint CScreenSpyDlg::ImageToScreen(CPoint pt)
|
|||||||
(int)((pt.y - m_rcZoomSrc.top) * scaleY)
|
(int)((pt.y - m_rcZoomSrc.top) * scaleY)
|
||||||
);
|
);
|
||||||
} else if (m_bAdaptiveSize) {
|
} else if (m_bAdaptiveSize) {
|
||||||
|
// 自适应模式:除以缩放比,再加黑边偏移
|
||||||
if (m_wZoom > 0 && m_hZoom > 0) {
|
if (m_wZoom > 0 && m_hZoom > 0) {
|
||||||
return CPoint((int)(pt.x / m_wZoom), (int)(pt.y / m_hZoom));
|
return CPoint((int)(pt.x / m_wZoom) + m_offsetX,
|
||||||
|
(int)(pt.y / m_hZoom) + m_offsetY);
|
||||||
}
|
}
|
||||||
return pt;
|
return pt;
|
||||||
} else {
|
} else {
|
||||||
@@ -3159,10 +3269,11 @@ bool CScreenSpyDlg::ScreenRectToImageRect(const CRect& rcScreen, CRect& rcImage)
|
|||||||
rcImage.right = (int)(m_rcZoomSrc.left + rcScreen.right * scaleX);
|
rcImage.right = (int)(m_rcZoomSrc.left + rcScreen.right * scaleX);
|
||||||
rcImage.bottom = (int)(m_rcZoomSrc.top + rcScreen.bottom * scaleY);
|
rcImage.bottom = (int)(m_rcZoomSrc.top + rcScreen.bottom * scaleY);
|
||||||
} else if (m_bAdaptiveSize) {
|
} else if (m_bAdaptiveSize) {
|
||||||
rcImage.left = (int)(rcScreen.left * m_wZoom);
|
// 先减去黑边偏移,再乘缩放比,得到原图坐标
|
||||||
rcImage.top = (int)(rcScreen.top * m_hZoom);
|
rcImage.left = (int)((rcScreen.left - m_offsetX) * m_wZoom);
|
||||||
rcImage.right = (int)(rcScreen.right * m_wZoom);
|
rcImage.top = (int)((rcScreen.top - m_offsetY) * m_hZoom);
|
||||||
rcImage.bottom = (int)(rcScreen.bottom * m_hZoom);
|
rcImage.right = (int)((rcScreen.right - m_offsetX) * m_wZoom);
|
||||||
|
rcImage.bottom = (int)((rcScreen.bottom - m_offsetY) * m_hZoom);
|
||||||
} else {
|
} else {
|
||||||
rcImage.left = rcScreen.left + m_ulHScrollPos;
|
rcImage.left = rcScreen.left + m_ulHScrollPos;
|
||||||
rcImage.top = rcScreen.top + m_ulVScrollPos;
|
rcImage.top = rcScreen.top + m_ulVScrollPos;
|
||||||
@@ -3381,8 +3492,18 @@ void CScreenSpyDlg::OnSize(UINT nType, int cx, int cy)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
GetClientRect(&m_CRect);
|
GetClientRect(&m_CRect);
|
||||||
m_wZoom = ((double)m_BitmapInfor_Full->bmiHeader.biWidth) / ((double)(m_CRect.Width()));
|
if (!m_BitmapInfor_Full) return;
|
||||||
m_hZoom = ((double)m_BitmapInfor_Full->bmiHeader.biHeight) / ((double)(m_CRect.Height()));
|
if (m_bAdaptiveSize) {
|
||||||
|
int renderW, renderH;
|
||||||
|
ComputeAdaptiveLayout(m_BitmapInfor_Full->bmiHeader.biWidth, m_BitmapInfor_Full->bmiHeader.biHeight,
|
||||||
|
m_CRect.Width(), m_CRect.Height(),
|
||||||
|
renderW, renderH, m_offsetX, m_offsetY,
|
||||||
|
m_wZoom, m_hZoom);
|
||||||
|
} else {
|
||||||
|
m_offsetX = m_offsetY = 0;
|
||||||
|
m_wZoom = m_CRect.Width() > 0 ? (double)m_BitmapInfor_Full->bmiHeader.biWidth / m_CRect.Width() : 1.0;
|
||||||
|
m_hZoom = m_CRect.Height() > 0 ? (double)m_BitmapInfor_Full->bmiHeader.biHeight / m_CRect.Height() : 1.0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CScreenSpyDlg::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
|
void CScreenSpyDlg::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
|
||||||
|
|||||||
@@ -280,8 +280,16 @@ public:
|
|||||||
BOOL m_bUseCustomCursor = TRUE; // 是否使用自定义光标
|
BOOL m_bUseCustomCursor = TRUE; // 是否使用自定义光标
|
||||||
CRect m_CRect;
|
CRect m_CRect;
|
||||||
double m_wZoom=1, m_hZoom=1;
|
double m_wZoom=1, m_hZoom=1;
|
||||||
|
int m_offsetX=0, m_offsetY=0; // 自适应模式黑边偏移(像素)
|
||||||
bool m_bMouseTracking = false;
|
bool m_bMouseTracking = false;
|
||||||
|
|
||||||
|
// 自适应黑边合成缓冲(按窗口尺寸缓存,避免每帧分配 GDI 资源)
|
||||||
|
HDC m_hComposeDC = NULL;
|
||||||
|
HBITMAP m_hComposeBmp = NULL;
|
||||||
|
HBITMAP m_hComposeOldBmp = NULL;
|
||||||
|
int m_nComposeCX = 0;
|
||||||
|
int m_nComposeCY = 0;
|
||||||
|
|
||||||
// ========== 局部放大功能 ==========
|
// ========== 局部放大功能 ==========
|
||||||
bool m_bZoomedIn = false; // 是否处于放大状态
|
bool m_bZoomedIn = false; // 是否处于放大状态
|
||||||
CRect m_rcZoomSrc; // 放大区域(原图坐标)
|
CRect m_rcZoomSrc; // 放大区域(原图坐标)
|
||||||
|
|||||||
Reference in New Issue
Block a user