文件
XLZEN/native/loader/src/wgfx.cpp
T
Administrator 0da7d49fd6
Build Loader / build (push) Canceled after 0s
Import OpenZen deobfuscated source
2026-09-15 03:14:09 +08:00

302 行
9.5 KiB
C++

#include "wgfx.h"
#include <chrono>
#include <cwchar>
#include <random>
#pragma comment(lib, "gdiplus.lib")
using namespace Gdiplus;
namespace ui {
double g_scale = 1.0;
void InitDpi() {
// Per-monitor v2 when available; plain aware as fallback. Either way the
// process stops getting bitmap-stretched by DWM and we can scale layout
// metrics by the real DPI.
using SetCtxFn = BOOL(WINAPI*)(DPI_AWARENESS_CONTEXT);
HMODULE user32 = GetModuleHandleW(L"user32.dll");
if (auto fn = reinterpret_cast<SetCtxFn>(
reinterpret_cast<void*>(GetProcAddress(
user32, "SetProcessDpiAwarenessContext")))) {
if (!fn(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) {
fn(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE);
}
} else {
SetProcessDPIAware();
}
HDC dc = GetDC(nullptr);
g_scale = GetDeviceCaps(dc, LOGPIXELSX) / 96.0;
ReleaseDC(nullptr, dc);
}
double Now() {
using namespace std::chrono;
return duration<double>(steady_clock::now().time_since_epoch()).count();
}
double EaseLinear(double t) { return t; }
double EaseOutCubic(double t) {
double u = 1.0 - t;
return 1.0 - u * u * u;
}
double EaseInCubic(double t) { return t * t * t; }
double EaseInOutSine(double t) { return 0.5 - 0.5 * cos(t * 3.14159265358979); }
double EaseInOutQuad(double t) {
return t < 0.5 ? 2.0 * t * t : 1.0 - pow(-2.0 * t + 2.0, 2.0) / 2.0;
}
double EaseOutBack(double t) {
// Mirrors Qt's OutBack default overshoot (s = 1.70158).
const double s = 1.70158;
double u = t - 1.0;
return 1.0 + (s + 1.0) * u * u * u + s * u * u;
}
double Since(double start, double dur) {
if (dur <= 0.0) return 1.0;
double t = (Now() - start) / dur;
if (t < 0.0) t = 0.0;
if (t > 1.0) t = 1.0;
return t;
}
double Phase(double t, double t0, double t1, double (*ease)(double)) {
if (t <= t0) return 0.0;
if (t >= t1) return 1.0;
return ease((t - t0) / (t1 - t0));
}
Gdiplus::Color Rgba(int r, int g, int b, int a) {
return Gdiplus::Color(static_cast<BYTE>(a), static_cast<BYTE>(r),
static_cast<BYTE>(g), static_cast<BYTE>(b));
}
Gdiplus::Color Hex(unsigned rgb, int a) {
return Rgba((rgb >> 16) & 0xff, (rgb >> 8) & 0xff, rgb & 0xff, a);
}
Gdiplus::GraphicsPath* RoundedRectPath(const Gdiplus::RectF& r, float radius) {
auto* p = new Gdiplus::GraphicsPath();
float rad = radius;
float m = r.Width < r.Height ? r.Width : r.Height;
m /= 2.0f;
if (rad > m) rad = m;
if (rad <= 0.0f) {
p->AddRectangle(r);
return p;
}
float d = rad * 2.0f;
p->AddArc(r.X, r.Y, d, d, 180.0f, 90.0f);
p->AddArc(r.X + r.Width - d, r.Y, d, d, 270.0f, 90.0f);
p->AddArc(r.X + r.Width - d, r.Y + r.Height - d, d, d, 0.0f, 90.0f);
p->AddArc(r.X, r.Y + r.Height - d, d, d, 90.0f, 90.0f);
p->CloseFigure();
return p;
}
namespace {
std::map<std::wstring, Gdiplus::Font*>& FontCache() {
static std::map<std::wstring, Gdiplus::Font*> cache;
return cache;
}
const Gdiplus::FontFamily& Family(const wchar_t* name) {
static std::map<std::wstring, Gdiplus::FontFamily*> fams;
auto it = fams.find(name);
if (it != fams.end()) return *it->second;
auto* f = new Gdiplus::FontFamily(name);
if (f->GetLastStatus() != Gdiplus::Ok) {
delete f;
f = new Gdiplus::FontFamily(L"Segoe UI"); // fallback
}
fams[name] = f;
return *f;
}
} // namespace
Gdiplus::StringFormat& NearFormat() {
static StringFormat sf(StringFormat::GenericDefault());
return sf;
}
Gdiplus::Font* Font(const wchar_t* family, float px, bool bold, bool italic) {
int style = bold ? FontStyleBold : FontStyleRegular;
if (italic) style |= FontStyleItalic;
wchar_t key[128];
_snwprintf_s(key, _TRUNCATE, L"%s|%.1f|%d", family, px, style);
auto& cache = FontCache();
auto it = cache.find(key);
if (it != cache.end()) return it->second;
auto* f = new Gdiplus::Font(&Family(family), px, style, UnitPixel);
cache[key] = f;
return f;
}
float LineHeight(Gdiplus::Graphics& g, Gdiplus::Font* f) {
return f->GetHeight(&g);
}
float TextWidth(Gdiplus::Graphics& g, Gdiplus::Font* f, const std::wstring& s) {
Gdiplus::RectF bounds;
g.MeasureString(s.c_str(), static_cast<INT>(s.size()), f,
Gdiplus::RectF(0, 0, 10000, 10000), &NearFormat(), &bounds);
return bounds.Width;
}
void DrawText(Gdiplus::Graphics& g, Gdiplus::Font* f, const std::wstring& s,
float x, float y, const Gdiplus::Color& c, float wrapWidth) {
Gdiplus::SolidBrush brush(c);
Gdiplus::RectF layout(x, y, wrapWidth, LineHeight(g, f) * 3.0f + 4.0f);
g.DrawString(s.c_str(), static_cast<INT>(s.size()), f, layout,
&NearFormat(), &brush);
}
void DrawTextCentered(Gdiplus::Graphics& g, Gdiplus::Font* f,
const std::wstring& s, float centerX, float y,
const Gdiplus::Color& c, float wrapWidth) {
Gdiplus::SolidBrush brush(c);
Gdiplus::RectF layout(centerX - wrapWidth / 2.0f, y, wrapWidth,
LineHeight(g, f) * 3.0f + 4.0f);
StringFormat sf(&NearFormat());
sf.SetAlignment(StringAlignmentCenter);
g.DrawString(s.c_str(), static_cast<INT>(s.size()), f, layout, &sf,
&brush);
}
std::wstring ElideMiddle(Gdiplus::Graphics& g, Gdiplus::Font* f,
const std::wstring& s, float maxW) {
if (TextWidth(g, f, s) <= maxW || s.size() < 3) return s;
size_t head = s.size() / 2, tail = s.size() / 2;
// Shrink the middle one char at a time until it fits, then insert an
// ellipsis in the gap.
while (head > 0 && tail < s.size()) {
std::wstring cut = s.substr(0, head) + L'\u2026' + s.substr(tail);
if (TextWidth(g, f, cut) <= maxW) return cut;
// Trim alternately from the middle outwards.
if (head > tail - s.size() / 2) --head; else ++tail;
}
return s.substr(0, head) + L'\u2026';
}
// ----- LayeredCanvas -----
LayeredCanvas::~LayeredCanvas() { Free(); }
void LayeredCanvas::Free() {
delete gfx_;
delete wrap_;
if (bmp_) DeleteObject(bmp_);
if (dc_) DeleteDC(dc_);
gfx_ = nullptr;
wrap_ = nullptr;
bmp_ = nullptr;
dc_ = nullptr;
bits_ = nullptr;
w_ = h_ = 0;
}
bool LayeredCanvas::Resize(int w, int h) {
if (w < 1 || h < 1) return false;
if (w == w_ && h == h_) return true;
Free();
BITMAPINFO bi{};
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biWidth = w;
bi.bmiHeader.biHeight = -h; // top-down
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 32;
bi.bmiHeader.biCompression = BI_RGB;
dc_ = CreateCompatibleDC(nullptr);
bmp_ = CreateDIBSection(nullptr, &bi, DIB_RGB_COLORS, &bits_, nullptr, 0);
if (!dc_ || !bmp_ || !bits_) {
Free();
return false;
}
SelectObject(dc_, bmp_);
// Wrap the DIB memory as a premultiplied-ARGB GDI+ bitmap: everything
// drawn through gfx_ lands directly in the DIB, already in the exact
// format UpdateLayeredWindow expects.
wrap_ = new Gdiplus::Bitmap(w, h, w * 4, PixelFormat32bppPARGB,
static_cast<BYTE*>(bits_));
gfx_ = new Gdiplus::Graphics(wrap_);
if (gfx_->GetLastStatus() != Gdiplus::Ok) {
Free();
return false;
}
gfx_->SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
gfx_->SetTextRenderingHint(Gdiplus::TextRenderingHintAntiAlias);
gfx_->SetInterpolationMode(Gdiplus::InterpolationModeHighQualityBicubic);
w_ = w;
h_ = h;
return true;
}
void LayeredCanvas::Clear() {
if (gfx_) gfx_->Clear(Gdiplus::Color(0, 0, 0, 0));
}
Gdiplus::Graphics& LayeredCanvas::g() { return *gfx_; }
bool LayeredCanvas::Present(HWND hwnd, BYTE constAlpha) {
if (!dc_ || !hwnd) return false;
HDC screen = GetDC(nullptr);
POINT src{0, 0};
SIZE sz{w_, h_};
BLENDFUNCTION bf{AC_SRC_OVER, 0, constAlpha, AC_SRC_ALPHA};
BOOL ok = UpdateLayeredWindow(hwnd, screen, nullptr, &sz, dc_, &src, 0,
&bf, ULW_ALPHA);
ReleaseDC(nullptr, screen);
return ok != FALSE;
}
std::wstring RandomIdent(int minLen, int maxLen) {
static const wchar_t kAlphabet[] =
L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
const int kLetters = 52;
const int kAll = static_cast<int>(wcslen(kAlphabet));
std::mt19937 rng(static_cast<unsigned>(
std::random_device{}() ^
(std::hash<double>{}(Now()) << 1)));
int len = minLen + static_cast<int>(rng() % (maxLen - minLen + 1));
std::wstring s;
s.reserve(len);
s += kAlphabet[rng() % kLetters];
for (int i = 1; i < len; ++i) s += kAlphabet[rng() % kAll];
return s;
}
void LogLine(const std::wstring& msg) {
wchar_t temp[MAX_PATH]{};
GetTempPathW(MAX_PATH, temp);
std::wstring path = std::wstring(temp) + L"openzen-loader.log";
HANDLE f = CreateFileW(path.c_str(), FILE_APPEND_DATA, FILE_SHARE_READ,
nullptr, OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL, nullptr);
if (f == INVALID_HANDLE_VALUE) return;
SYSTEMTIME st{};
GetLocalTime(&st);
wchar_t line[1024];
_snwprintf_s(line, _TRUNCATE,
L"[%02u:%02u:%02u.%03u] %s\r\n",
st.wHour, st.wMinute, st.wSecond, st.wMilliseconds,
msg.c_str());
DWORD written = 0;
WriteFile(f, line, static_cast<DWORD>(wcslen(line) * sizeof(wchar_t)),
&written, nullptr);
CloseHandle(f);
}
} // namespace ui