Implement HiDPI scaling

This commit is contained in:
darktohka
2026-03-17 03:10:12 +02:00
parent f8a1dde721
commit 821435c8a3
16 changed files with 522 additions and 134 deletions
+14
View File
@@ -5,6 +5,20 @@ pub mod widgets;
pub use font::FontManager;
pub use renderer::Renderer;
/// Query the system DPI scale factor (1.0 = 100%, 1.5 = 150%, 2.0 = 200%).
/// The process must already be DPI-aware (declared in the manifest) for this
/// to return the true hardware DPI rather than the virtualised 96.
pub fn get_dpi_scale() -> f32 {
#[cfg(target_os = "windows")]
unsafe {
use windows_sys::Win32::UI::HiDpi::GetDpiForSystem;
let dpi = GetDpiForSystem();
if dpi == 0 { 1.0 } else { dpi as f32 / 96.0 }
}
#[cfg(not(target_os = "windows"))]
{ 1.0 }
}
/// Set the window icon from the icon resource already embedded in the binary
/// by `winresource` (resource ID 1). No-op on non-Windows platforms.
pub fn set_window_icon(window: &minifb::Window) {
@@ -109,6 +109,29 @@ impl Renderer {
}
}
/// Draw an RGBA image scaled to (w, h) at (x, y) using nearest-neighbor interpolation.
pub fn draw_image_scaled(&mut self, x: i32, y: i32, w: i32, h: i32, img: &RgbaImage) {
if w <= 0 || h <= 0 || img.width == 0 || img.height == 0 {
return;
}
for dy in 0..h {
for dx in 0..w {
let src_x = (dx as f32 * img.width as f32 / w as f32) as usize;
let src_y = (dy as f32 * img.height as f32 / h as f32) as usize;
let idx = (src_y * img.width + src_x) * 4;
let r = img.data[idx];
let g = img.data[idx + 1];
let b = img.data[idx + 2];
let a = img.data[idx + 3];
if a == 255 {
self.set_pixel(x + dx, y + dy, Self::rgb(r, g, b));
} else if a > 0 {
self.blend_pixel(x + dx, y + dy, Self::rgb(r, g, b), a);
}
}
}
}
/// Draw an RGBA image onto the framebuffer at (x, y).
pub fn draw_image(&mut self, x: i32, y: i32, img: &RgbaImage) {
for iy in 0..img.height as i32 {
@@ -6,6 +6,7 @@ use crate::renderer::Renderer;
pub struct GradientButton {
pub rect: Rect,
pub text: String,
pub font_size: f32,
pub color1: u32,
pub color2: u32,
pub back_color: u32,
@@ -23,6 +24,7 @@ impl GradientButton {
Self {
rect: Rect::new(x, y, w, h),
text: text.to_string(),
font_size: 13.0,
color1: Renderer::rgb(118, 118, 118),
color2: Renderer::rgb(81, 81, 81),
back_color: Renderer::rgb(0, 0, 0),
@@ -77,7 +79,7 @@ impl GradientButton {
renderer.draw_rect(r.x, r.y, r.w, r.h, bg);
// Measure text to centre it.
let font_size = 13.0;
let font_size = self.font_size;
let (tw, th) = fonts.measure_text(&self.text, font_size);
let tx = r.x + ((r.w as f32 - tw) / 2.0) as i32;
let ty = r.y + ((r.h as f32 - th) / 2.0) as i32;
@@ -11,8 +11,12 @@ pub struct ImageCheckBox {
impl ImageCheckBox {
pub fn new(x: i32, y: i32) -> Self {
Self::with_size(x, y, 21)
}
pub fn with_size(x: i32, y: i32, size: i32) -> Self {
Self {
rect: Rect::new(x, y, 21, 21),
rect: Rect::new(x, y, size, size),
checked: true,
enabled: true,
visible: true,
@@ -43,7 +47,7 @@ impl ImageCheckBox {
unchecked_img
};
if img.width > 0 && img.height > 0 {
renderer.draw_image(self.rect.x, self.rect.y, img);
renderer.draw_image_scaled(self.rect.x, self.rect.y, self.rect.w, self.rect.h, img);
} else {
// Fallback: draw a simple square.
let bg = if self.checked {
@@ -8,6 +8,7 @@ pub struct Label {
pub text: String,
pub color: u32,
pub font_size: f32,
pub line_spacing: f32,
pub visible: bool,
}
@@ -18,6 +19,7 @@ impl Label {
text: text.to_string(),
color: Renderer::rgb(245, 245, 245),
font_size,
line_spacing: 2.0,
visible: true,
}
}
@@ -33,7 +35,7 @@ impl Label {
&self.text,
self.font_size,
self.color,
2.0,
self.line_spacing,
);
}