mirror of
https://gitlab.com/cleanflash/installer.git
synced 2026-08-12 18:31:37 +08:00
Remove PNG loading
This commit is contained in:
@@ -2,7 +2,7 @@ use crate::install_flags::{self, InstallFlags};
|
||||
use crate::installer;
|
||||
use clean_flash_common::{uninstaller, redirection, update_checker, ProgressCallback};
|
||||
use clean_flash_ui::font::FontManager;
|
||||
use clean_flash_ui::renderer::{Renderer, RgbaImage};
|
||||
use clean_flash_ui::renderer::Renderer;
|
||||
use clean_flash_ui::widgets::button::GradientButton;
|
||||
use clean_flash_ui::widgets::checkbox::ImageCheckBox;
|
||||
use clean_flash_ui::widgets::label::Label;
|
||||
@@ -59,10 +59,7 @@ pub struct InstallForm {
|
||||
// Header
|
||||
pub title_text: String,
|
||||
pub subtitle_text: String,
|
||||
pub flash_logo: RgbaImage,
|
||||
// Checkbox images
|
||||
pub checkbox_on: RgbaImage,
|
||||
pub checkbox_off: RgbaImage,
|
||||
flash_logo_cache: clean_flash_ui::flash_logo::FlashLogoCache,
|
||||
// Navigation buttons
|
||||
pub prev_button: GradientButton,
|
||||
pub next_button: GradientButton,
|
||||
@@ -132,12 +129,6 @@ impl InstallForm {
|
||||
let title_text = "Clean Flash Player".to_string();
|
||||
let subtitle_text = format!("built from version {} (China)", version);
|
||||
|
||||
// Load images from the common resources folder.
|
||||
// These are loaded from the C# project's assets alongside the binary.
|
||||
let flash_logo = load_resource_image("flashLogo.png");
|
||||
let checkbox_on = load_resource_image("checkboxOn.png");
|
||||
let checkbox_off = load_resource_image("checkboxOff.png");
|
||||
|
||||
let fonts = FontManager::new();
|
||||
|
||||
Self {
|
||||
@@ -145,9 +136,7 @@ impl InstallForm {
|
||||
panel: Panel::Disclaimer,
|
||||
title_text,
|
||||
subtitle_text,
|
||||
flash_logo,
|
||||
checkbox_on,
|
||||
checkbox_off,
|
||||
flash_logo_cache: clean_flash_ui::flash_logo::FlashLogoCache::new(),
|
||||
prev_button: btn(90, 286, 138, 31, "QUIT"),
|
||||
next_button: btn(497, 286, 138, 31, "AGREE"),
|
||||
// Disclaimer panel
|
||||
@@ -240,10 +229,10 @@ The following details could be useful. Press the Retry button to try again.",
|
||||
// ----- Draw -----
|
||||
renderer.clear(BG_COLOR);
|
||||
|
||||
// Header: flash logo.
|
||||
let lw = (self.flash_logo.width as f32 * self.scale) as i32;
|
||||
let lh = (self.flash_logo.height as f32 * self.scale) as i32;
|
||||
renderer.draw_image_scaled(self.s(90), self.s(36), lw, lh, &self.flash_logo);
|
||||
// Header: flash logo (cached software render).
|
||||
self.flash_logo_cache.draw(
|
||||
renderer, self.s(90), self.s(36), self.s(109), self.s(107),
|
||||
);
|
||||
|
||||
// Title.
|
||||
self.fonts.draw_text(
|
||||
@@ -561,34 +550,27 @@ including Clean Flash Player and older versions of Adobe Flash Player."
|
||||
// ---- Drawing helpers ----
|
||||
|
||||
fn draw_disclaimer(&self, r: &mut Renderer) {
|
||||
self.disclaimer_box
|
||||
.draw(r, &self.checkbox_on, &self.checkbox_off);
|
||||
self.disclaimer_box.draw(r);
|
||||
self.disclaimer_label.draw(r, &self.fonts);
|
||||
}
|
||||
|
||||
fn draw_choice(&self, r: &mut Renderer) {
|
||||
self.browser_ask_label.draw(r, &self.fonts);
|
||||
self.pepper_box
|
||||
.draw(r, &self.checkbox_on, &self.checkbox_off);
|
||||
self.pepper_box.draw(r);
|
||||
self.pepper_label.draw(r, &self.fonts);
|
||||
self.netscape_box
|
||||
.draw(r, &self.checkbox_on, &self.checkbox_off);
|
||||
self.netscape_box.draw(r);
|
||||
self.netscape_label.draw(r, &self.fonts);
|
||||
self.activex_box
|
||||
.draw(r, &self.checkbox_on, &self.checkbox_off);
|
||||
self.activex_box.draw(r);
|
||||
self.activex_label.draw(r, &self.fonts);
|
||||
}
|
||||
|
||||
fn draw_player_choice(&self, r: &mut Renderer) {
|
||||
self.player_ask_label.draw(r, &self.fonts);
|
||||
self.player_box
|
||||
.draw(r, &self.checkbox_on, &self.checkbox_off);
|
||||
self.player_box.draw(r);
|
||||
self.player_label.draw(r, &self.fonts);
|
||||
self.player_desktop_box
|
||||
.draw(r, &self.checkbox_on, &self.checkbox_off);
|
||||
self.player_desktop_box.draw(r);
|
||||
self.player_desktop_label.draw(r, &self.fonts);
|
||||
self.player_start_menu_box
|
||||
.draw(r, &self.checkbox_on, &self.checkbox_off);
|
||||
self.player_start_menu_box.draw(r);
|
||||
self.player_start_menu_label.draw(r, &self.fonts);
|
||||
}
|
||||
|
||||
@@ -664,13 +646,3 @@ fn join_with_and(items: &[&str]) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to load a resource image from the original C# project's asset folder.
|
||||
fn load_resource_image(name: &str) -> RgbaImage {
|
||||
let bytes: &[u8] = match name {
|
||||
"flashLogo.png" => include_bytes!("../../../resources/flashLogo.png"),
|
||||
"checkboxOn.png" => include_bytes!("../../../resources/checkboxOn.png"),
|
||||
"checkboxOff.png" => include_bytes!("../../../resources/checkboxOff.png"),
|
||||
_ => return RgbaImage::empty(),
|
||||
};
|
||||
RgbaImage::from_png_bytes(bytes)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,5 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
ab_glyph = { workspace = true }
|
||||
image = { workspace = true }
|
||||
minifb = { workspace = true }
|
||||
windows-sys = { workspace = true }
|
||||
|
||||
@@ -0,0 +1,265 @@
|
||||
use crate::renderer::Renderer;
|
||||
|
||||
/// Cached flash logo bitmap. Stores a pre-rendered pixel buffer so the
|
||||
/// expensive MSAA polygon fill only runs when the target size changes.
|
||||
pub struct FlashLogoCache {
|
||||
width: i32,
|
||||
height: i32,
|
||||
/// Pre-rendered pixels in 0x00RRGGBB format, row-major, size = width * height.
|
||||
pixels: Vec<u32>,
|
||||
/// Shadow offset derived from width.
|
||||
shadow_off: i32,
|
||||
}
|
||||
|
||||
impl FlashLogoCache {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
width: 0,
|
||||
height: 0,
|
||||
pixels: Vec::new(),
|
||||
shadow_off: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw the flash logo at (x, y) with size (w, h).
|
||||
/// Re-renders into the cache only if the size changed.
|
||||
pub fn draw(&mut self, renderer: &mut Renderer, x: i32, y: i32, w: i32, h: i32) {
|
||||
if w <= 0 || h <= 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.width != w || self.height != h {
|
||||
self.rebuild(w, h);
|
||||
}
|
||||
|
||||
// Draw subtle drop shadow first.
|
||||
let shadow_color = Renderer::rgb(0, 0, 0);
|
||||
let shadow_alpha: u8 = 40;
|
||||
let so = self.shadow_off;
|
||||
for dy in 0..h {
|
||||
for dx in 0..w {
|
||||
renderer.blend_pixel(x + dx + so, y + dy + so, shadow_color, shadow_alpha);
|
||||
}
|
||||
}
|
||||
|
||||
// Blit cached pixels onto the renderer.
|
||||
for dy in 0..h {
|
||||
for dx in 0..w {
|
||||
let px = self.pixels[dy as usize * w as usize + dx as usize];
|
||||
renderer.set_pixel(x + dx, y + dy, px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Render the logo into the internal cache at the given size.
|
||||
fn rebuild(&mut self, w: i32, h: i32) {
|
||||
self.width = w;
|
||||
self.height = h;
|
||||
self.shadow_off = ((w as f32 * 0.02).round() as i32).max(1);
|
||||
|
||||
// Render into a temporary offscreen renderer.
|
||||
let mut tmp = Renderer::new(w as usize, h as usize);
|
||||
|
||||
let vw = 500.0_f32;
|
||||
let vh = 487.0_f32;
|
||||
let sx = w as f32 / vw;
|
||||
let sy = h as f32 / vh;
|
||||
|
||||
let blue = Renderer::rgb(0x00, 0x3A, 0x74);
|
||||
tmp.fill_rect(0, 0, w, h, blue);
|
||||
|
||||
let white = Renderer::rgb(255, 255, 255);
|
||||
let points = build_f_polygon();
|
||||
fill_polygon(&mut tmp, &points, 0.0, 0.0, sx, sy, white);
|
||||
|
||||
self.pixels = tmp.buffer;
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluate a cubic bezier curve and return `segments` points (excluding the start point).
|
||||
fn cubic_bezier(
|
||||
p0: (f32, f32),
|
||||
p1: (f32, f32),
|
||||
p2: (f32, f32),
|
||||
p3: (f32, f32),
|
||||
segments: usize,
|
||||
) -> Vec<(f32, f32)> {
|
||||
let mut pts = Vec::with_capacity(segments);
|
||||
for i in 1..=segments {
|
||||
let t = i as f32 / segments as f32;
|
||||
let u = 1.0 - t;
|
||||
let x = u * u * u * p0.0
|
||||
+ 3.0 * u * u * t * p1.0
|
||||
+ 3.0 * u * t * t * p2.0
|
||||
+ t * t * t * p3.0;
|
||||
let y = u * u * u * p0.1
|
||||
+ 3.0 * u * u * t * p1.1
|
||||
+ 3.0 * u * t * t * p2.1
|
||||
+ t * t * t * p3.1;
|
||||
pts.push((x, y));
|
||||
}
|
||||
pts
|
||||
}
|
||||
|
||||
/// Build the "f" shape polygon by tracing the SVG path data, flattening beziers
|
||||
/// into line segments. Coordinates are in the SVG viewBox space (500×487).
|
||||
fn build_f_polygon() -> Vec<(f32, f32)> {
|
||||
let mut pts = Vec::with_capacity(128);
|
||||
const N: usize = 16; // segments per bezier curve
|
||||
|
||||
// M 269.2,138.7
|
||||
pts.push((269.2, 138.7));
|
||||
|
||||
// c -22.5,27.5 -35.8,61.8 -48.7,95
|
||||
pts.extend(cubic_bezier(
|
||||
(269.2, 138.7),
|
||||
(246.7, 166.2),
|
||||
(233.4, 200.5),
|
||||
(220.5, 233.7),
|
||||
N,
|
||||
));
|
||||
|
||||
// c -26.1,67.3 -43.6,105.8 -99.7,105.8
|
||||
pts.extend(cubic_bezier(
|
||||
(220.5, 233.7),
|
||||
(194.4, 301.0),
|
||||
(176.9, 339.5),
|
||||
(120.8, 339.5),
|
||||
N,
|
||||
));
|
||||
|
||||
// V 402
|
||||
pts.push((120.8, 402.0));
|
||||
|
||||
// c 46.4,0 84.1,-17.2 112,-51.2
|
||||
pts.extend(cubic_bezier(
|
||||
(120.8, 402.0),
|
||||
(167.2, 402.0),
|
||||
(204.9, 384.8),
|
||||
(232.8, 350.8),
|
||||
N,
|
||||
));
|
||||
|
||||
// c 17.9,-21.9 30.3,-49.3 40.9,-75.8
|
||||
pts.extend(cubic_bezier(
|
||||
(232.8, 350.8),
|
||||
(250.7, 328.9),
|
||||
(263.1, 301.5),
|
||||
(273.7, 275.0),
|
||||
N,
|
||||
));
|
||||
|
||||
// h 74.1
|
||||
pts.push((347.8, 275.0));
|
||||
|
||||
// v -62.5
|
||||
pts.push((347.8, 212.5));
|
||||
|
||||
// h -48.8
|
||||
pts.push((299.0, 212.5));
|
||||
|
||||
// c 18.9,-40.6 39.2,-62.5 82.1,-62.5
|
||||
pts.extend(cubic_bezier(
|
||||
(299.0, 212.5),
|
||||
(317.9, 171.9),
|
||||
(338.2, 150.0),
|
||||
(381.1, 150.0),
|
||||
N,
|
||||
));
|
||||
|
||||
// V 87.5
|
||||
pts.push((381.1, 87.5));
|
||||
|
||||
// C 334.8,87.5 297.1,104.7 269.2,138.7 (absolute cubic, closes the shape)
|
||||
pts.extend(cubic_bezier(
|
||||
(381.1, 87.5),
|
||||
(334.8, 87.5),
|
||||
(297.1, 104.7),
|
||||
(269.2, 138.7),
|
||||
N,
|
||||
));
|
||||
|
||||
pts
|
||||
}
|
||||
|
||||
/// Test whether a point is inside the polygon using the even-odd (parity) rule.
|
||||
fn point_in_polygon(transformed: &[(f32, f32)], px: f32, py: f32) -> bool {
|
||||
let n = transformed.len();
|
||||
let mut inside = false;
|
||||
let mut j = n - 1;
|
||||
for i in 0..n {
|
||||
let (xi, yi) = transformed[i];
|
||||
let (xj, yj) = transformed[j];
|
||||
if ((yi > py) != (yj > py)) && (px < (xj - xi) * (py - yi) / (yj - yi) + xi) {
|
||||
inside = !inside;
|
||||
}
|
||||
j = i;
|
||||
}
|
||||
inside
|
||||
}
|
||||
|
||||
/// Scanline-fill a polygon with 4×4 MSAA antialiasing after transforming from
|
||||
/// viewBox to screen coordinates: screen_x = ox + vx * sx, screen_y = oy + vy * sy.
|
||||
fn fill_polygon(
|
||||
renderer: &mut Renderer,
|
||||
points: &[(f32, f32)],
|
||||
ox: f32,
|
||||
oy: f32,
|
||||
sx: f32,
|
||||
sy: f32,
|
||||
color: u32,
|
||||
) {
|
||||
if points.len() < 3 {
|
||||
return;
|
||||
}
|
||||
|
||||
// Transform to screen space.
|
||||
let transformed: Vec<(f32, f32)> = points
|
||||
.iter()
|
||||
.map(|&(px, py)| (ox + px * sx, oy + py * sy))
|
||||
.collect();
|
||||
|
||||
// Bounding box.
|
||||
let mut min_x = f32::MAX;
|
||||
let mut max_x = f32::MIN;
|
||||
let mut min_y = f32::MAX;
|
||||
let mut max_y = f32::MIN;
|
||||
for &(x, y) in &transformed {
|
||||
if x < min_x { min_x = x; }
|
||||
if x > max_x { max_x = x; }
|
||||
if y < min_y { min_y = y; }
|
||||
if y > max_y { max_y = y; }
|
||||
}
|
||||
|
||||
let x_start = (min_x.floor() as i32).max(0);
|
||||
let x_end = (max_x.ceil() as i32).min(renderer.width as i32 - 1);
|
||||
let y_start = (min_y.floor() as i32).max(0);
|
||||
let y_end = (max_y.ceil() as i32).min(renderer.height as i32 - 1);
|
||||
|
||||
// 4×4 sub-pixel grid offsets (16 samples per pixel).
|
||||
const GRID: usize = 4;
|
||||
const SAMPLES: usize = GRID * GRID;
|
||||
let step = 1.0 / GRID as f32;
|
||||
let half_step = step / 2.0;
|
||||
|
||||
for y in y_start..=y_end {
|
||||
for x in x_start..=x_end {
|
||||
let mut hits = 0u32;
|
||||
for sy_i in 0..GRID {
|
||||
let sample_y = y as f32 + half_step + sy_i as f32 * step;
|
||||
for sx_i in 0..GRID {
|
||||
let sample_x = x as f32 + half_step + sx_i as f32 * step;
|
||||
if point_in_polygon(&transformed, sample_x, sample_y) {
|
||||
hits += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if hits == SAMPLES as u32 {
|
||||
renderer.set_pixel(x, y, color);
|
||||
} else if hits > 0 {
|
||||
let alpha = ((hits * 255 + SAMPLES as u32 / 2) / SAMPLES as u32) as u8;
|
||||
renderer.blend_pixel(x, y, color, alpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod flash_logo;
|
||||
pub mod font;
|
||||
pub mod renderer;
|
||||
pub mod widgets;
|
||||
|
||||
@@ -108,77 +108,6 @@ impl Renderer {
|
||||
self.fill_rect(x + dx, y, 1, h, c);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
for ix in 0..img.width as i32 {
|
||||
let idx = (iy as usize * img.width + ix as usize) * 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 + ix, y + iy, Self::rgb(r, g, b));
|
||||
} else if a > 0 {
|
||||
self.blend_pixel(x + ix, y + iy, Self::rgb(r, g, b), a);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Simple RGBA image stored as raw bytes.
|
||||
pub struct RgbaImage {
|
||||
pub width: usize,
|
||||
pub height: usize,
|
||||
pub data: Vec<u8>, // RGBA, row-major
|
||||
}
|
||||
|
||||
impl RgbaImage {
|
||||
/// Load a PNG from embedded bytes.
|
||||
pub fn from_png_bytes(bytes: &[u8]) -> Self {
|
||||
let img = image::load_from_memory_with_format(bytes, image::ImageFormat::Png)
|
||||
.expect("Failed to decode PNG")
|
||||
.to_rgba8();
|
||||
Self {
|
||||
width: img.width() as usize,
|
||||
height: img.height() as usize,
|
||||
data: img.into_raw(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an empty (transparent) image.
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
width: 0,
|
||||
height: 0,
|
||||
data: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---- helpers ----
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::Rect;
|
||||
use crate::renderer::{Renderer, RgbaImage};
|
||||
use crate::renderer::Renderer;
|
||||
|
||||
/// An image-based checkbox matching the C# ImageCheckBox control.
|
||||
/// A software-rendered checkbox with a gray gradient, black outline, and white checkmark.
|
||||
pub struct ImageCheckBox {
|
||||
pub rect: Rect,
|
||||
pub checked: bool,
|
||||
@@ -32,59 +32,34 @@ impl ImageCheckBox {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(
|
||||
&self,
|
||||
renderer: &mut Renderer,
|
||||
checked_img: &RgbaImage,
|
||||
unchecked_img: &RgbaImage,
|
||||
) {
|
||||
pub fn draw(&self, renderer: &mut Renderer) {
|
||||
if !self.visible {
|
||||
return;
|
||||
}
|
||||
let img = if self.checked {
|
||||
checked_img
|
||||
} else {
|
||||
unchecked_img
|
||||
};
|
||||
if img.width > 0 && img.height > 0 {
|
||||
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 {
|
||||
Renderer::rgb(97, 147, 232)
|
||||
} else {
|
||||
Renderer::rgb(80, 80, 80)
|
||||
};
|
||||
renderer.fill_rect(self.rect.x, self.rect.y, self.rect.w, self.rect.h, bg);
|
||||
renderer.draw_rect(
|
||||
self.rect.x,
|
||||
self.rect.y,
|
||||
self.rect.w,
|
||||
self.rect.h,
|
||||
Renderer::rgb(160, 160, 160),
|
||||
);
|
||||
if self.checked {
|
||||
// Draw a simple checkmark.
|
||||
let cx = self.rect.x + 5;
|
||||
let cy = self.rect.y + 10;
|
||||
for i in 0..4 {
|
||||
renderer.set_pixel(cx + i, cy + i, Renderer::rgb(255, 255, 255));
|
||||
renderer.set_pixel(cx + i, cy + i + 1, Renderer::rgb(255, 255, 255));
|
||||
}
|
||||
for i in 0..8 {
|
||||
renderer.set_pixel(cx + 3 + i, cy + 3 - i, Renderer::rgb(255, 255, 255));
|
||||
renderer.set_pixel(cx + 3 + i, cy + 4 - i, Renderer::rgb(255, 255, 255));
|
||||
}
|
||||
}
|
||||
|
||||
let r = self.rect;
|
||||
|
||||
// Gray gradient background (matching button style).
|
||||
let color1 = Renderer::rgb(118, 118, 118);
|
||||
let color2 = Renderer::rgb(81, 81, 81);
|
||||
renderer.fill_gradient_v(r.x, r.y, r.w, r.h, color1, color2);
|
||||
|
||||
// Black 1px outline.
|
||||
renderer.draw_rect(r.x, r.y, r.w, r.h, Renderer::rgb(0, 0, 0));
|
||||
|
||||
// White checkmark when checked.
|
||||
if self.checked {
|
||||
let white = Renderer::rgb(255, 255, 255);
|
||||
Self::draw_checkmark(renderer, r.x, r.y, r.w, r.h, white);
|
||||
}
|
||||
|
||||
if !self.enabled {
|
||||
// Dim overlay.
|
||||
for dy in 0..self.rect.h {
|
||||
for dx in 0..self.rect.w {
|
||||
for dy in 0..r.h {
|
||||
for dx in 0..r.w {
|
||||
renderer.blend_pixel(
|
||||
self.rect.x + dx,
|
||||
self.rect.y + dy,
|
||||
r.x + dx,
|
||||
r.y + dy,
|
||||
Renderer::rgb(50, 51, 51),
|
||||
100,
|
||||
);
|
||||
@@ -92,4 +67,72 @@ impl ImageCheckBox {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw a checkmark scaled to fit within the given box.
|
||||
fn draw_checkmark(renderer: &mut Renderer, bx: i32, by: i32, bw: i32, bh: i32, color: u32) {
|
||||
// Checkmark geometry defined in a normalised coordinate space.
|
||||
// The check goes from bottom-left, down to a valley, then up to top-right.
|
||||
// Key points (in fractions of size):
|
||||
// start: (0.20, 0.50)
|
||||
// valley: (0.40, 0.72)
|
||||
// end: (0.80, 0.25)
|
||||
// We draw two thick line segments between these points.
|
||||
|
||||
let w = bw as f32;
|
||||
let h = bh as f32;
|
||||
|
||||
// Absolute coordinates of the three key points.
|
||||
let x0 = bx as f32 + w * 0.20;
|
||||
let y0 = by as f32 + h * 0.48;
|
||||
let x1 = bx as f32 + w * 0.40;
|
||||
let y1 = by as f32 + h * 0.72;
|
||||
let x2 = bx as f32 + w * 0.80;
|
||||
let y2 = by as f32 + h * 0.25;
|
||||
|
||||
// Line thickness scales with checkbox size.
|
||||
let thickness = (w * 0.14).max(1.5);
|
||||
|
||||
draw_thick_line(renderer, x0, y0, x1, y1, thickness, color);
|
||||
draw_thick_line(renderer, x1, y1, x2, y2, thickness, color);
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw a thick line between two points using filled circles along the path (round caps).
|
||||
fn draw_thick_line(
|
||||
renderer: &mut Renderer,
|
||||
x0: f32,
|
||||
y0: f32,
|
||||
x1: f32,
|
||||
y1: f32,
|
||||
thickness: f32,
|
||||
color: u32,
|
||||
) {
|
||||
let dx = x1 - x0;
|
||||
let dy = y1 - y0;
|
||||
let dist = (dx * dx + dy * dy).sqrt();
|
||||
let steps = (dist * 2.0).ceil() as i32;
|
||||
let half = thickness / 2.0;
|
||||
|
||||
for i in 0..=steps {
|
||||
let t = i as f32 / steps.max(1) as f32;
|
||||
let cx = x0 + dx * t;
|
||||
let cy = y0 + dy * t;
|
||||
|
||||
// Fill a small circle at (cx, cy).
|
||||
let min_x = (cx - half).floor() as i32;
|
||||
let max_x = (cx + half).ceil() as i32;
|
||||
let min_y = (cy - half).floor() as i32;
|
||||
let max_y = (cy + half).ceil() as i32;
|
||||
let r_sq = half * half;
|
||||
|
||||
for py in min_y..=max_y {
|
||||
for px in min_x..=max_x {
|
||||
let fdx = px as f32 + 0.5 - cx;
|
||||
let fdy = py as f32 + 0.5 - cy;
|
||||
if fdx * fdx + fdy * fdy <= r_sq {
|
||||
renderer.set_pixel(px, py, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use clean_flash_common::{redirection, uninstaller, update_checker, ProgressCallback};
|
||||
use clean_flash_ui::font::FontManager;
|
||||
use clean_flash_ui::renderer::{Renderer, RgbaImage};
|
||||
use clean_flash_ui::renderer::Renderer;
|
||||
use clean_flash_ui::widgets::button::GradientButton;
|
||||
use clean_flash_ui::widgets::label::Label;
|
||||
use clean_flash_ui::widgets::progress_bar::ProgressBar;
|
||||
@@ -43,7 +43,7 @@ pub struct UninstallForm {
|
||||
panel: Panel,
|
||||
title_text: String,
|
||||
subtitle_text: String,
|
||||
flash_logo: RgbaImage,
|
||||
flash_logo_cache: clean_flash_ui::flash_logo::FlashLogoCache,
|
||||
prev_button: GradientButton,
|
||||
next_button: GradientButton,
|
||||
// Before uninstall
|
||||
@@ -79,7 +79,7 @@ impl UninstallForm {
|
||||
};
|
||||
|
||||
let version = update_checker::FLASH_VERSION;
|
||||
let flash_logo = load_resource_image("flashLogo.png");
|
||||
|
||||
let fonts = FontManager::new();
|
||||
|
||||
Self {
|
||||
@@ -87,7 +87,7 @@ impl UninstallForm {
|
||||
panel: Panel::BeforeInstall,
|
||||
title_text: "Clean Flash Player".into(),
|
||||
subtitle_text: format!("built from version {} (China)", version),
|
||||
flash_logo,
|
||||
flash_logo_cache: clean_flash_ui::flash_logo::FlashLogoCache::new(),
|
||||
prev_button: btn(90, 286, 138, 31, "QUIT"),
|
||||
next_button: btn(497, 286, 138, 31, "UNINSTALL"),
|
||||
before_label: lbl(PANEL_X + 3, PANEL_Y + 2, BEFORE_TEXT, 15.0),
|
||||
@@ -156,9 +156,9 @@ The following details could be useful. Press the Retry button to try again.",
|
||||
|
||||
// Draw.
|
||||
renderer.clear(BG_COLOR);
|
||||
let lw = (self.flash_logo.width as f32 * self.scale) as i32;
|
||||
let lh = (self.flash_logo.height as f32 * self.scale) as i32;
|
||||
renderer.draw_image_scaled(self.s(90), self.s(36), lw, lh, &self.flash_logo);
|
||||
self.flash_logo_cache.draw(
|
||||
renderer, self.s(90), self.s(36), self.s(109), self.s(107),
|
||||
);
|
||||
|
||||
self.fonts
|
||||
.draw_text(renderer, self.s(233), self.s(54), &self.title_text, self.sf(32.0), FG_COLOR);
|
||||
@@ -288,10 +288,3 @@ impl ProgressCallback for ThreadProgressCallback {
|
||||
}
|
||||
}
|
||||
|
||||
fn load_resource_image(name: &str) -> RgbaImage {
|
||||
let bytes: &[u8] = match name {
|
||||
"flashLogo.png" => include_bytes!("../../../resources/flashLogo.png"),
|
||||
_ => return RgbaImage::empty(),
|
||||
};
|
||||
RgbaImage::from_png_bytes(bytes)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user