Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 11 additions & 26 deletions src/mpc-hc/DpiHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,9 @@ namespace
} MONITOR_DPI_TYPE;

typedef int (WINAPI* tpGetSystemMetricsForDpi)(int nIndex, UINT dpi);
HRESULT WINAPI GetDpiForMonitor(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT* dpiX, UINT* dpiY);
BOOL WINAPI SystemParametersInfoForDpi(UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni, UINT dpi);
int WINAPI GetSystemMetricsForDpi(int nIndex);
UINT WINAPI GetDpiForWindow(HWND hwnd);
double WINAPI TextScaleFactor(void);
typedef HRESULT WINAPI tpGetDpiForMonitor(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT* dpiX, UINT* dpiY);
typedef BOOL WINAPI tpSystemParametersInfoForDpi(UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni, UINT dpi);
typedef int WINAPI tpGetSystemMetricsForDpiFunc(int nIndex, UINT dpi);
}

DpiHelper::DpiHelper()
Expand All @@ -56,17 +54,8 @@ DpiHelper::DpiHelper()
m_dpiy = m_sdpiy;
}

UINT DpiHelper::GetDPIForWindow(HWND wnd) {
const WinapiFunc<decltype(GetDpiForWindow)>
fnGetDpiForWindow = { _T("user32.dll"), "GetDpiForWindow" };
if (fnGetDpiForWindow) {
return fnGetDpiForWindow(wnd);
}
return 0;
}

UINT DpiHelper::GetDPIForMonitor(HMONITOR hMonitor) {
const WinapiFunc<decltype(GetDpiForMonitor)>
const WinapiFunc<tpGetDpiForMonitor>
fnGetDpiForMonitor = { _T("Shcore.dll"), "GetDpiForMonitor" };

if (hMonitor && fnGetDpiForMonitor) {
Expand All @@ -80,7 +69,7 @@ UINT DpiHelper::GetDPIForMonitor(HMONITOR hMonitor) {

void DpiHelper::Override(HWND hWindow)
{
const WinapiFunc<decltype(GetDpiForMonitor)>
const WinapiFunc<tpGetDpiForMonitor>
fnGetDpiForMonitor = { _T("Shcore.dll"), "GetDpiForMonitor" };

if (hWindow && fnGetDpiForMonitor) {
Expand Down Expand Up @@ -121,7 +110,7 @@ void DpiHelper::GetMessageFont(LOGFONT* lf) {
}

bool DpiHelper::GetNonClientMetrics(PNONCLIENTMETRICSW ncm, bool& dpiCorrected) {
const WinapiFunc<decltype(SystemParametersInfoForDpi)>
const WinapiFunc<tpSystemParametersInfoForDpi>
fnSystemParametersInfoForDpi = { L"user32.dll", "SystemParametersInfoForDpi" };

ZeroMemory(ncm, sizeof(NONCLIENTMETRICS));
Expand All @@ -141,19 +130,15 @@ bool DpiHelper::GetNonClientMetrics(PNONCLIENTMETRICSW ncm, bool& dpiCorrected)
}

int DpiHelper::GetSystemMetrics(int type) {
const WinapiFunc<decltype(GetSystemMetricsForDpi)>
const WinapiFunc<tpGetSystemMetricsForDpiFunc>
fnGetSystemMetricsForDpi = { L"user32.dll", "GetSystemMetricsForDpi" };

bool dpiCorrected = false;

if (fnGetSystemMetricsForDpi) {
dpiCorrected = true;
return fnGetSystemMetricsForDpi(type);
}
if (!dpiCorrected) {
int ret = fnGetSystemMetricsForDpi(type);
return ScaleSystemToOverrideY(ret);
return fnGetSystemMetricsForDpi(type, m_dpix);
}

int ret = ::GetSystemMetrics(type);
return ScaleSystemToOverrideY(ret);
}

bool DpiHelper::CanUsePerMonitorV2() {
Expand Down
1 change: 0 additions & 1 deletion src/mpc-hc/DpiHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ class DpiHelper final
void GetMessageFont(LOGFONT* lf);
bool GetNonClientMetrics(PNONCLIENTMETRICSW, bool& dpiCorrected);
int GetSystemMetrics(int type);
static UINT GetDPIForWindow(HWND wnd);
static UINT GetDPIForMonitor(HMONITOR hMonitor);
static double GetTextScaleFactor();
int CalculateListCtrlItemHeight(CListCtrl* wnd);
Expand Down
149 changes: 121 additions & 28 deletions src/mpc-hc/MainFrm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@

#include "stb/stb_image.h"
#include "stb/stb_image_resize2.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb/stb_image_write.h"

#include <dwmapi.h>
#undef SubclassWindow
Expand Down Expand Up @@ -2209,6 +2211,11 @@ void CMainFrame::OnTimer(UINT_PTR nIDEvent)
}

m_wndStatusBar.SetStatusTimer(rtNow, rtDur, IsSubresyncBarVisible(), GetTimeFormat());

// Update media transport controls timeline
if (m_media_trans_control.IsActive() && rtDur > 0) {
m_media_trans_control.UpdateTimelineProperties(0, rtDur, rtNow);
}
}
break;
case PM_DVD:
Expand Down Expand Up @@ -2239,6 +2246,11 @@ void CMainFrame::OnTimer(UINT_PTR nIDEvent)
}
}
m_wndStatusBar.SetStatusTimer(rtNow, rtDur, IsSubresyncBarVisible(), GetTimeFormat());

// Update media transport controls timeline for DVD
if (m_media_trans_control.IsActive() && rtDur > 0) {
m_media_trans_control.UpdateTimelineProperties(0, rtDur, rtNow);
}
break;
case PM_ANALOG_CAPTURE:
g_bExternalSubtitleTime = true;
Expand Down Expand Up @@ -5644,26 +5656,31 @@ bool CMainFrame::GetDIB(BYTE** ppData, long& size, bool fSilent)
return true;
}

void CMainFrame::SaveDIB(LPCTSTR fn, BYTE* pData, long size)
{
CPath path(fn);
// Callback for stb_image_write to append to vector
static void stbi_write_to_vector(void* context, void* data, int size) {
std::vector<BYTE>* vec = (std::vector<BYTE>*)context;
size_t oldSize = vec->size();
vec->resize(oldSize + size);
memcpy(vec->data() + oldSize, data, size);
}

BYTE* CMainFrame::ConvertDIBTo24bppRGB(BYTE* pData, long size, int& outWidth, int& outHeight, int& outPitch)
{
PBITMAPINFO bi = reinterpret_cast<PBITMAPINFO>(pData);
PBITMAPINFOHEADER bih = &bi->bmiHeader;
int bpp = bih->biBitCount;

if (bpp != 16 && bpp != 24 && bpp != 32) {
AfxMessageBox(IDS_SCREENSHOT_ERROR, MB_ICONWARNING | MB_OK, 0);
return;
return nullptr;
}

bool topdown = (bih->biHeight < 0);
int w = bih->biWidth;
int h = abs(bih->biHeight);
int srcpitch = w * (bpp >> 3);
int dstpitch = (w * 3 + 3) / 4 * 4; // round w * 3 to next multiple of 4

BYTE* p = DEBUG_NEW BYTE[dstpitch * h];

const BYTE* src = pData + sizeof(*bih);

if (topdown) {
Expand All @@ -5672,6 +5689,23 @@ void CMainFrame::SaveDIB(LPCTSTR fn, BYTE* pData, long size)
BitBltFromRGBToRGB(w, h, p, dstpitch, 24, (BYTE*)src + srcpitch * (h - 1), -srcpitch, bpp);
}

outWidth = w;
outHeight = h;
outPitch = dstpitch;
return p;
}

void CMainFrame::SaveDIB(LPCTSTR fn, BYTE* pData, long size)
{
CPath path(fn);

int w, h, dstpitch;
BYTE* p = ConvertDIBTo24bppRGB(pData, size, w, h, dstpitch);
if (!p) {
AfxMessageBox(IDS_SCREENSHOT_ERROR, MB_ICONWARNING | MB_OK, 0);
return;
}

{
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Expand Down Expand Up @@ -5746,6 +5780,43 @@ void CMainFrame::SaveDIB(LPCTSTR fn, BYTE* pData, long size)
SendStatusMessage(m_wndStatusBar.PreparePathStatusMessage(path), 3000);
}

bool CMainFrame::CaptureVideoThumbnail(std::vector<BYTE>& thumbnail)
{
// Get the current video frame as DIB
std::vector<BYTE> dib;
CString errmsg;
HRESULT hr = GetCurrentFrame(dib, errmsg);
if (FAILED(hr) || dib.empty()) {
return false;
}

// Convert DIB to 24bpp BGR
int w, h, dstpitch;
BYTE* bgr = ConvertDIBTo24bppRGB(dib.data(), (long)dib.size(), w, h, dstpitch);
if (!bgr) {
return false;
}

// Allocate buffer for RGB output (tightly packed, no padding)
int rgbPitch = w * 3;
BYTE* rgb = DEBUG_NEW BYTE[rgbPitch * h];

// Convert BGR to RGB using stb_image_resize2 (even with same dimensions, it can reorder channels)
STBIR_RESIZE resize;
stbir_resize_init(&resize, bgr, w, h, dstpitch, rgb, w, h, rgbPitch, STBIR_BGR, STBIR_TYPE_UINT8);
stbir_set_pixel_layouts(&resize, STBIR_BGR, STBIR_RGB);
stbir_resize_extended(&resize);

delete[] bgr;

// Encode to JPEG using stb_image_write
int quality = AfxGetAppSettings().nJpegQuality;
int result = stbi_write_jpg_to_func(stbi_write_to_vector, &thumbnail, w, h, 3, rgb, quality);

delete[] rgb;
return result != 0;
}

HRESULT GetBasicVideoFrame(IBasicVideo* pBasicVideo, std::vector<BYTE>& dib) {
// IBasicVideo::GetCurrentImage() gives the original frame

Expand Down Expand Up @@ -18419,6 +18490,14 @@ void CMainFrame::DoSeekTo(REFERENCE_TIME rtPos, bool bShowOSD /*= true*/)
if (abRepeat.positionA && rtPos == abRepeat.positionA) {
DisableABRepeat();
}
} else {
// Update media transport controls timeline after seek
if (m_media_trans_control.IsActive()) {
REFERENCE_TIME rtDur = 0;
if (SUCCEEDED(m_pMS->GetDuration(&rtDur)) && rtDur > 0) {
m_media_trans_control.UpdateTimelineProperties(0, rtDur, rtPos);
}
}
}
UpdateChapterInInfoBar();
if (fs == State_Stopped) {
Expand Down Expand Up @@ -22550,35 +22629,49 @@ void CMainFrame::MediaTransportControlSetMedia() {
}

// Thumbnail
CComQIPtr<IFilterGraph> pFilterGraph = m_pGB;
std::vector<BYTE> internalCover;
if (CoverArt::FindEmbedded(pFilterGraph, internalCover)) {
m_media_trans_control.loadThumbnail(internalCover.data(), internalCover.size());
} else {
CPlaylistItem pli;
if (m_wndPlaylistBar.GetCur(pli) && !pli.m_cover.IsEmpty()) {
m_media_trans_control.loadThumbnail(pli.m_cover);
if (m_fAudioOnly) {
// For audio files, look for cover art
CComQIPtr<IFilterGraph> pFilterGraph = m_pGB;
std::vector<BYTE> internalCover;
if (CoverArt::FindEmbedded(pFilterGraph, internalCover)) {
m_media_trans_control.loadThumbnail(internalCover.data(), internalCover.size());
} else {
CString filename = m_wndPlaylistBar.GetCurFileName();
CString filename_no_ext;
CString filedir;
if (!PathUtils::IsURL(filename)) {
CPath path = CPath(filename);
if (path.FileExists()) {
path.RemoveExtension();
filename_no_ext = path.m_strPath;
path.RemoveFileSpec();
filedir = path.m_strPath;
bool is_file_art = false;
CString img = CoverArt::FindExternal(filename_no_ext, filedir, author, is_file_art);
if (!img.IsEmpty()) {
if (m_fAudioOnly || is_file_art) {
CPlaylistItem pli;
if (m_wndPlaylistBar.GetCur(pli) && !pli.m_cover.IsEmpty()) {
m_media_trans_control.loadThumbnail(pli.m_cover);
} else {
CString filename = m_wndPlaylistBar.GetCurFileName();
if (!PathUtils::IsURL(filename)) {
CPath path = CPath(filename);
if (path.FileExists()) {
path.RemoveExtension();
CString filename_no_ext = path.m_strPath;
path.RemoveFileSpec();
CString filedir = path.m_strPath;
bool is_file_art = false;
CString img = CoverArt::FindExternal(filename_no_ext, filedir, author, is_file_art);
if (!img.IsEmpty()) {
m_media_trans_control.loadThumbnail(img);
}
}
}
}
}
} else {
// For video files, capture current frame as thumbnail
std::vector<BYTE> thumbnail;
if (CaptureVideoThumbnail(thumbnail)) {
m_media_trans_control.loadThumbnail(thumbnail.data(), thumbnail.size());
}
}

// Update timeline properties (duration and position)
if (m_pMS) {
REFERENCE_TIME rtDur = 0, rtNow = 0;
if (SUCCEEDED(m_pMS->GetDuration(&rtDur)) && rtDur > 0) {
m_pMS->GetCurrentPosition(&rtNow);
m_media_trans_control.UpdateTimelineProperties(0, rtDur, rtNow);
}
}

// Update data and status
Expand Down
2 changes: 2 additions & 0 deletions src/mpc-hc/MainFrm.h
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,9 @@ class CMainFrame : public CFrameWnd, public CDropClient
HRESULT GetOriginalFrame(std::vector<BYTE>& dib, CString& errmsg);
HRESULT RenderCurrentSubtitles(BYTE* pData);
bool GetDIB(BYTE** ppData, long& size, bool fSilent = false);
BYTE* ConvertDIBTo24bppRGB(BYTE* pData, long size, int& outWidth, int& outHeight, int& outPitch);
void SaveDIB(LPCTSTR fn, BYTE* pData, long size);
bool CaptureVideoThumbnail(std::vector<BYTE>& thumbnail);
CString MakeSnapshotFileName(BOOL thumbnails);
BOOL IsRendererCompatibleWithSaveImage();
void SaveImage(LPCTSTR fn, bool displayed, bool includeSubtitles);
Expand Down
Loading