diff --git a/Screenshot/Screenshot.png b/Screenshot/Screenshot.png
deleted file mode 100644
index dd198c9..0000000
Binary files a/Screenshot/Screenshot.png and /dev/null differ
diff --git a/Screenshot/Screenshot1.png b/Screenshot/Screenshot1.png
deleted file mode 100644
index 8935b74..0000000
Binary files a/Screenshot/Screenshot1.png and /dev/null differ
diff --git a/Screenshot/Screenshot2.png b/Screenshot/Screenshot2.png
deleted file mode 100644
index ae42be5..0000000
Binary files a/Screenshot/Screenshot2.png and /dev/null differ
diff --git a/Screenshot/Screenshot3.png b/Screenshot/Screenshot3.png
deleted file mode 100644
index c28254c..0000000
Binary files a/Screenshot/Screenshot3.png and /dev/null differ
diff --git a/Screenshot/Screenshot4.png b/Screenshot/Screenshot4.png
deleted file mode 100644
index d05c6e1..0000000
Binary files a/Screenshot/Screenshot4.png and /dev/null differ
diff --git a/Screenshot/Screenshot5.png b/Screenshot/Screenshot5.png
deleted file mode 100644
index dbd58c1..0000000
Binary files a/Screenshot/Screenshot5.png and /dev/null differ
diff --git a/Screenshot/Screenshot6.png b/Screenshot/Screenshot6.png
deleted file mode 100644
index 7765fe7..0000000
Binary files a/Screenshot/Screenshot6.png and /dev/null differ
diff --git a/VMware Player Fix/README.md b/VMware Player Fix/README.md
deleted file mode 100644
index 30081dc..0000000
--- a/VMware Player Fix/README.md
+++ /dev/null
@@ -1,15 +0,0 @@
-# VMware Player Fix
-This fix will work for Linux Mint 22 and Ubuntu 24.04. This works as of 7/26/24.
-## Download
-- Create a broadcom account here: https://profile.broadcom.com/web/registration
-- Download the latest version of VMware Player here: https://support.broadcom.com/group/ecx/productdownloads?subfamily=VMware+Workstation+Pro
-
-## What does this script do?
-- This script will patch the **vmmon** and **vmnet** VMware modules required to run VMware Player.
-
-## How do I run it?
-1. Download **VMware Player** and **vmware-fix.sh** to ~/Downloads
-2. Open a **Terminal** from ~/Downloads:
- 1. Type: `chmod u+x vmware-fix.sh`
- 2. Type: `./vmware-fix.sh`
-4. Once the script is complete, fire up **VMware Player** (SUPER->Administrator->VMware Player)
diff --git a/VMware Player Fix/vmware-fix.sh b/VMware Player Fix/vmware-fix.sh
deleted file mode 100644
index b1a1770..0000000
--- a/VMware Player Fix/vmware-fix.sh
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-printf '[0;32m=====================================\n'
-printf '[1;32mThe Linux IT Guy - Linux Mint Scripts\n'
-printf '[1;32mVMware Player Fix\n'
-printf '[0;32m=====================================[0m\n'
-
-DOWNLOADS="${HOME}/Downloads"
-MODULE_VERSION="workstation-17.5.0"
-MODULE_URL="https://github.com/mkubecek/vmware-host-modules/archive/${MODULE_VERSION}.tar.gz"
-MODULE_ARCHIVE="${DOWNLOADS}/${MODULE_VERSION}.tar.gz"
-MODULE_DIR="${DOWNLOADS}/vmware-host-modules-${MODULE_VERSION}"
-
-if [[ ! -d "$DOWNLOADS" ]]; then
- echo "Expected ${DOWNLOADS} to exist and contain the VMware Player bundle." >&2
- exit 1
-fi
-
-shopt -s nullglob
-bundles=("${DOWNLOADS}"/VMware-*.bundle)
-if [[ ${#bundles[@]} -ne 1 ]]; then
- echo "Place exactly one VMware-*.bundle file in ${DOWNLOADS} and rerun this script." >&2
- exit 1
-fi
-
-bundle=${bundles[0]}
-chmod u+x "$bundle"
-sudo "$bundle"
-
-wget -O "$MODULE_ARCHIVE" "$MODULE_URL"
-tar -xzf "$MODULE_ARCHIVE" -C "$DOWNLOADS"
-
-if [[ ! -d "$MODULE_DIR/vmmon-only" || ! -d "$MODULE_DIR/vmnet-only" ]]; then
- echo "VMware host modules archive did not contain the expected directories." >&2
- exit 1
-fi
-
-tar -cf "${MODULE_DIR}/vmmon.tar" -C "$MODULE_DIR" vmmon-only
-tar -cf "${MODULE_DIR}/vmnet.tar" -C "$MODULE_DIR" vmnet-only
-
-sudo cp -v "${MODULE_DIR}/vmmon.tar" "${MODULE_DIR}/vmnet.tar" /usr/lib/vmware/modules/source/
-sudo vmware-modconfig --console --install-all
-
-printf '[0;32m=====================================\n'
-printf '[1;32mThe Linux IT Guy - Linux Mint Scripts\n'
-printf '[1;32mVMware Player Fix - Complete\n'
-printf '[1;32mFire up Super->Administration->VMware Player to complete the installation.\n'
-printf '[0;32m=====================================[0m\n'
diff --git a/apps_config.csv b/apps_config.csv
index f1986ab..57d3d24 100644
--- a/apps_config.csv
+++ b/apps_config.csv
@@ -1,5 +1,6 @@
Category,Label,Package Name,Flatpak ID,Exec Name,Notes
Browsers,Brave Browser,,com.brave.Browser,brave,
+Browsers,Brave Origin,brave-origin,,brave-origin,Light Privacy-focused browser
Browsers,Google Chrome,,com.google.Chrome,google-chrome,
Browsers,Firefox,firefox,,firefox,
Browsers,Microsoft Edge,,com.microsoft.Edge,microsoft-edge,
diff --git a/assets/keys/brave-install.sh.asc b/assets/keys/brave-install.sh.asc
new file mode 100644
index 0000000..fa0cf99
--- /dev/null
+++ b/assets/keys/brave-install.sh.asc
@@ -0,0 +1,31 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQINBGfPDCoBEACouDRqDFEP/AbVvlNktBGPa8TzeDWoi0ybIZRGHMhHb/JAHKd7
+ziD44gvjCgrq1O+w1I70VZA4JwJelWY9rmlIUwTTZwmV2gBpIeZ1F6+dUU7BRWSg
+Lnc5uhfXFTNe81aY81Ne4WbskUSRB1Go13m/mHZV7vJdwcqwcxmZGqhWPbvVInes
+Y6Bx9oe/sHs2dtVKD74JGtdLWYF6VzTMqyx0tDv8Dgkjr5iO1XnZcH5vl0/3Rc/x
+TlUKw265yjV9SQcd+l0mTcUFlwCGvjGuNa/Z4IwkR2Q19IF94B8/YbYXUk5B/RET
+JtZcW2xt8lsUTUdGAUOr0Cj3Kt01xcj+zWBUVs6gdgxdWTHAB6MVUx8BG5ScR+r8
+77/Udx6jkiG4zOAC2kR99pGFkLFwaIyqi9aLufPcROwmRcCZ2wXpGPGgsDPbWwh9
+KgMXidbcTD2j4H3SSr9cJfITB9Vf3Jh8YJiMbjHq0kI5LxqLL01H58FKa2Hsg8vQ
+60BxRqPiQNKlPp9RQa3kEwC8MB7YdZ8a3/Bfsk9sKHQXlRhdgwtRsdvVFlHHWoAW
+H6gOGfqwSWLOVrPXk/YCEvfF3MDk07eH3Bp4lfgtIum9bPzPf72bWE9LZyvgys/X
+rTuMnDt/y0GFeUjhdlkXout2UFIPLWWsMw2qdRsfCpdeIy1A4CFABjVXGQARAQAB
+tFJPbmUgTGluZSBJbnN0YWxsZXIgZm9yIEJyYXZlIChPbmUgTGluZSBJbnN0YWxs
+ZXIgZm9yIEJyYXZlKSA8aW5zdGFsbC1zaEBicmF2ZS5jb20+iQJ+BBMBCABoAhsD
+BQkSzAMABQsJCAcCAiICBhUKCQgLAgQWAgMBAh4HAheAFiEE0WFmByys3yyUKcvx
+G/QeN9A59pEFAmfPDC0mFIAAAAAADwAObGFiZWxAYnJhdmUuY29taW5zdGFsbF9z
+aF9tcmsACgkQG/QeN9A59pHQ0A/8DnJIl56ld9Xgy0rxoyLaC2T+5dSsPAqKCAVX
+jqwumxu2iukwXjzoI/yZf+oNEBpy1NtyuOb7AKmijzK1XIFYfkmYAlFnezGGWVjo
+coE9vNoYZxsxHdc8iJx5NoW7BeQw/oOCr6XAj1KLdSomeyZfI7NOpTxy2apGYDSq
+buaBWbZIasNSSh1asQwn7oW0IErgEBzz2Lag9lo5mt4qf0BLZ2vTs5q/R1pqQXXc
+DJSM7/LOEOJfeHPjlvuIix9oTP+hqkGR770ziOA9TxZKPeJq1LEvOreUmBzz1EKI
+u3LGhIKtsR9gqDP27PX7sNNT4E/0kqhTzWFg+eEk9tMPz/V1UKWDxaYSklt7R7rF
+UVIp6ZLT7r8ROnTx3pPNWJjOJyRmTJ3mdRYmLoPcMKjW3JF2gJuaX+OEzFW8dagK
+01Rv8epOAxxyCKBCFEJ2iTfIZn3YLwI7ItU45O4xmruHhhOPp7fe59pQDK9JMt04
+Cir/TsImNaWDdTBUv0Cx4fxu38w3by0tQZz3CVJ/oepC/fwfz8IQ36IOIUrLBrzg
+T5lmUUehgRnLXsJ167TFUVHDiwSqAT0eEw2gz36CEa1xWroCQ5ZEJuYCqgi+Vn64
+SXuNvb3Q0vqXtiqy24wjy/AX4kMgbxAQpro9pLKyEVD9ygptcNFHYJI0IStuLDQA
+FBMz8IY=
+=oWX1
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/assets/icons/audacity.png b/assets/logos/audacity.png
similarity index 100%
rename from assets/icons/audacity.png
rename to assets/logos/audacity.png
diff --git a/assets/icons/bottles.png b/assets/logos/bottles.png
similarity index 100%
rename from assets/icons/bottles.png
rename to assets/logos/bottles.png
diff --git a/assets/icons/boxes.png b/assets/logos/boxes.png
similarity index 100%
rename from assets/icons/boxes.png
rename to assets/logos/boxes.png
diff --git a/assets/logos/brave-origin.svg b/assets/logos/brave-origin.svg
new file mode 100644
index 0000000..fedebaf
--- /dev/null
+++ b/assets/logos/brave-origin.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/brave.png b/assets/logos/brave.png
similarity index 100%
rename from assets/icons/brave.png
rename to assets/logos/brave.png
diff --git a/assets/icons/discord.png b/assets/logos/discord.png
similarity index 100%
rename from assets/icons/discord.png
rename to assets/logos/discord.png
diff --git a/assets/icons/firefox.png b/assets/logos/firefox.png
similarity index 100%
rename from assets/icons/firefox.png
rename to assets/logos/firefox.png
diff --git a/assets/icons/gimp.png b/assets/logos/gimp.png
similarity index 100%
rename from assets/icons/gimp.png
rename to assets/logos/gimp.png
diff --git a/assets/icons/google-chrome.png b/assets/logos/google-chrome.png
similarity index 100%
rename from assets/icons/google-chrome.png
rename to assets/logos/google-chrome.png
diff --git a/assets/icons/localsend.png b/assets/logos/localsend.png
similarity index 100%
rename from assets/icons/localsend.png
rename to assets/logos/localsend.png
diff --git a/assets/icons/lutris.png b/assets/logos/lutris.png
similarity index 100%
rename from assets/icons/lutris.png
rename to assets/logos/lutris.png
diff --git a/assets/icons/microsoft-edge.png b/assets/logos/microsoft-edge.png
similarity index 100%
rename from assets/icons/microsoft-edge.png
rename to assets/logos/microsoft-edge.png
diff --git a/assets/icons/obs-studio.png b/assets/logos/obs-studio.png
similarity index 100%
rename from assets/icons/obs-studio.png
rename to assets/logos/obs-studio.png
diff --git a/assets/icons/onlyoffice.png b/assets/logos/onlyoffice.png
similarity index 100%
rename from assets/icons/onlyoffice.png
rename to assets/logos/onlyoffice.png
diff --git a/assets/icons/opera.png b/assets/logos/opera.png
similarity index 100%
rename from assets/icons/opera.png
rename to assets/logos/opera.png
diff --git a/assets/icons/protonup-qt.png b/assets/logos/protonup-qt.png
similarity index 100%
rename from assets/icons/protonup-qt.png
rename to assets/logos/protonup-qt.png
diff --git a/assets/icons/pycharm-community.png b/assets/logos/pycharm-community.png
similarity index 100%
rename from assets/icons/pycharm-community.png
rename to assets/logos/pycharm-community.png
diff --git a/assets/icons/signal.png b/assets/logos/signal.png
similarity index 100%
rename from assets/icons/signal.png
rename to assets/logos/signal.png
diff --git a/assets/icons/slack.png b/assets/logos/slack.png
similarity index 100%
rename from assets/icons/slack.png
rename to assets/logos/slack.png
diff --git a/assets/icons/steam.png b/assets/logos/steam.png
similarity index 100%
rename from assets/icons/steam.png
rename to assets/logos/steam.png
diff --git a/assets/icons/thunderbird.png b/assets/logos/thunderbird.png
similarity index 100%
rename from assets/icons/thunderbird.png
rename to assets/logos/thunderbird.png
diff --git a/assets/icons/visual-studio-code.png b/assets/logos/visual-studio-code.png
similarity index 100%
rename from assets/icons/visual-studio-code.png
rename to assets/logos/visual-studio-code.png
diff --git a/assets/icons/vivaldi.png b/assets/logos/vivaldi.png
similarity index 100%
rename from assets/icons/vivaldi.png
rename to assets/logos/vivaldi.png
diff --git a/assets/icons/vlc.png b/assets/logos/vlc.png
similarity index 100%
rename from assets/icons/vlc.png
rename to assets/logos/vlc.png
diff --git a/assets/icons/zen.png b/assets/logos/zen.png
similarity index 100%
rename from assets/icons/zen.png
rename to assets/logos/zen.png
diff --git a/main.sh b/main.sh
index 9bd3771..98df1fd 100644
--- a/main.sh
+++ b/main.sh
@@ -212,9 +212,88 @@ ensure_flatpak() {
fi
}
+brave_origin_is_installed() {
+ local candidate
+ while IFS= read -r candidate; do
+ require_package_name "$candidate"
+ if native_installed "$candidate"; then
+ return 0
+ fi
+ done < <(brave_origin_package_candidates "$PACKAGE_MANAGER")
+ return 1
+}
+
+# Download the pinned Brave installer, GPG-verify it, then run that file with
+# FLAVOR=origin. Never curl|sh and never eval.
+install_brave_origin() {
+ local tmp key_file wrap_dir
+
+ if brave_origin_is_installed; then
+ echo "brave-origin is already installed. Skipping installation."
+ return 0
+ fi
+
+ if ! command -v curl >/dev/null 2>&1; then
+ echo "curl is required to download the Brave installer." >&2
+ exit 1
+ fi
+ if ! command -v gpg >/dev/null 2>&1; then
+ echo "gpg is required to verify the Brave installer." >&2
+ exit 1
+ fi
+
+ key_file="${SCRIPT_DIR}/assets/keys/brave-install.sh.asc"
+ if [[ ! -f "$key_file" ]]; then
+ echo "Brave installer signing key was not found at $key_file." >&2
+ exit 1
+ fi
+
+ tmp=$(mktemp -d) || exit 1
+ chmod 700 "$tmp"
+ wrap_dir="$tmp/sudo-wrap"
+
+ if ! download_pinned_brave_file "$BRAVE_INSTALL_SH_URL" "$tmp/install.sh"; then
+ rm -rf "$tmp"
+ exit 1
+ fi
+ if ! download_pinned_brave_file "$BRAVE_INSTALL_SH_ASC_URL" "$tmp/install.sh.asc"; then
+ rm -rf "$tmp"
+ exit 1
+ fi
+
+ echo "Verifying Brave installer signature..."
+ if ! verify_brave_install_script "$tmp/install.sh" "$tmp/install.sh.asc" "$key_file"; then
+ rm -rf "$tmp"
+ echo "Refusing to run unverified Brave installer." >&2
+ exit 1
+ fi
+
+ chmod 700 "$tmp/install.sh"
+ if ! install_noninteractive_sudo_wrapper "$wrap_dir"; then
+ rm -rf "$tmp"
+ exit 1
+ fi
+
+ echo "Running verified Brave Origin installer..."
+ # Execute the downloaded file. Do not pipe curl to sh and do not eval.
+ if ! PATH="$wrap_dir:$PATH" FLAVOR=origin CHANNEL=release "$tmp/install.sh"; then
+ rm -rf "$tmp"
+ echo "Brave Origin installer failed." >&2
+ exit 1
+ fi
+
+ rm -rf "$tmp"
+}
+
install_native() {
local package=$1
require_package_name "$package"
+
+ if [[ "$package" == "brave-origin" ]]; then
+ install_brave_origin
+ return
+ fi
+
if native_installed "$package"; then
echo "$package is already installed. Skipping installation."
return 0
@@ -240,7 +319,7 @@ install_native() {
esac
}
-remove_native() {
+remove_native_package() {
local package=$1
require_package_name "$package"
if ! native_installed "$package"; then
@@ -261,6 +340,28 @@ remove_native() {
esac
}
+remove_native() {
+ local package=$1
+ require_package_name "$package"
+
+ if [[ "$package" == "brave-origin" ]]; then
+ local candidate removed=0
+ while IFS= read -r candidate; do
+ require_package_name "$candidate"
+ if native_installed "$candidate"; then
+ remove_native_package "$candidate"
+ removed=1
+ fi
+ done < <(brave_origin_package_candidates "$PACKAGE_MANAGER")
+ if [[ "$removed" -eq 0 ]]; then
+ echo "brave-origin is not installed. Skipping removal."
+ fi
+ return 0
+ fi
+
+ remove_native_package "$package"
+}
+
install_flatpak_app() {
local app_id=$1
require_flatpak_id "$app_id"
diff --git a/src/catalog.rs b/src/catalog.rs
index 0647b8e..b78f9b7 100644
--- a/src/catalog.rs
+++ b/src/catalog.rs
@@ -557,6 +557,48 @@ mod tests {
assert!(!load.apps.is_empty());
}
+ #[test]
+ fn shipped_catalog_has_brave_origin_native_and_keeps_flatpak_brave() {
+ let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
+ let load = load_apps(&root);
+ assert!(load.error.is_none(), "{:?}", load.error);
+ assert!(load.warnings.is_empty(), "{:?}", load.warnings);
+
+ let origin = load
+ .apps
+ .iter()
+ .find(|app| app.label == "Brave Origin")
+ .expect("Brave Origin catalog row");
+ assert_eq!(origin.category, "Browsers");
+ assert_eq!(origin.package_name, "brave-origin");
+ assert!(origin.flatpak_id.is_empty());
+ assert_eq!(origin.exec_name, "brave-origin");
+ assert_eq!(origin.source_label(), "native");
+
+ let command = origin.try_command(&root, "install").unwrap();
+ assert_eq!(command[0], "bash");
+ assert!(command[1].ends_with("main.sh"));
+ assert!(command.contains(&"--package".to_owned()));
+ assert!(command.contains(&"brave-origin".to_owned()));
+ assert!(command.contains(&"--exec".to_owned()));
+ assert!(!command.contains(&"--flatpak".to_owned()));
+ assert_eq!(command.last().map(String::as_str), Some("install"));
+
+ let remove = origin.try_command(&root, "remove").unwrap();
+ assert_eq!(remove.last().map(String::as_str), Some("remove"));
+ assert!(remove.contains(&"--package".to_owned()));
+ assert!(!remove.contains(&"--flatpak".to_owned()));
+
+ let flatpak_brave = load
+ .apps
+ .iter()
+ .find(|app| app.label == "Brave Browser")
+ .expect("Brave Browser Flatpak catalog row");
+ assert!(flatpak_brave.package_name.is_empty());
+ assert_eq!(flatpak_brave.flatpak_id, "com.brave.Browser");
+ assert_eq!(flatpak_brave.exec_name, "brave");
+ }
+
fn empty_dir(label: &str) -> PathBuf {
let seq = TEST_DIR_SEQ.fetch_add(1, Ordering::Relaxed);
let path = std::env::temp_dir().join(format!(
diff --git a/src/logos.rs b/src/logos.rs
new file mode 100644
index 0000000..1631e13
--- /dev/null
+++ b/src/logos.rs
@@ -0,0 +1,227 @@
+//! Logos compiled into the binary.
+//!
+//! Distro chips and the Brave Origin tile use SVG markup via `include_str!`.
+//! Other app tiles use Chris's original PNGs via `include_bytes!`. Nothing
+//! under `assets/` is read from disk at runtime.
+
+use eframe::egui::ColorImage;
+
+/// Distro SVG markup keyed the same way the header chips look them up.
+pub const DISTRO_SVGS: &[(&str, &str)] = &[
+ ("arch", include_str!("../assets/distros/arch.svg")),
+ ("debian", include_str!("../assets/distros/debian.svg")),
+ ("fedora", include_str!("../assets/distros/fedora.svg")),
+];
+
+/// Chris's Brave Origin tile: white outlined lion on a dark rounded square.
+/// Do not reuse the orange Brave Browser PNG.
+pub const BRAVE_ORIGIN_SVG: &str = include_str!("../assets/logos/brave-origin.svg");
+
+/// Original app tile PNGs. Brave Browser is `brave`; Origin is not in this list.
+pub const APP_PNGS: &[(&str, &[u8])] = &[
+ ("audacity", include_bytes!("../assets/logos/audacity.png")),
+ ("bottles", include_bytes!("../assets/logos/bottles.png")),
+ ("boxes", include_bytes!("../assets/logos/boxes.png")),
+ ("brave", include_bytes!("../assets/logos/brave.png")),
+ ("discord", include_bytes!("../assets/logos/discord.png")),
+ ("firefox", include_bytes!("../assets/logos/firefox.png")),
+ ("gimp", include_bytes!("../assets/logos/gimp.png")),
+ (
+ "google-chrome",
+ include_bytes!("../assets/logos/google-chrome.png"),
+ ),
+ ("localsend", include_bytes!("../assets/logos/localsend.png")),
+ ("lutris", include_bytes!("../assets/logos/lutris.png")),
+ (
+ "microsoft-edge",
+ include_bytes!("../assets/logos/microsoft-edge.png"),
+ ),
+ (
+ "obs-studio",
+ include_bytes!("../assets/logos/obs-studio.png"),
+ ),
+ (
+ "onlyoffice",
+ include_bytes!("../assets/logos/onlyoffice.png"),
+ ),
+ ("opera", include_bytes!("../assets/logos/opera.png")),
+ (
+ "protonup-qt",
+ include_bytes!("../assets/logos/protonup-qt.png"),
+ ),
+ (
+ "pycharm-community",
+ include_bytes!("../assets/logos/pycharm-community.png"),
+ ),
+ ("signal", include_bytes!("../assets/logos/signal.png")),
+ ("slack", include_bytes!("../assets/logos/slack.png")),
+ ("steam", include_bytes!("../assets/logos/steam.png")),
+ (
+ "thunderbird",
+ include_bytes!("../assets/logos/thunderbird.png"),
+ ),
+ (
+ "visual-studio-code",
+ include_bytes!("../assets/logos/visual-studio-code.png"),
+ ),
+ ("vivaldi", include_bytes!("../assets/logos/vivaldi.png")),
+ ("vlc", include_bytes!("../assets/logos/vlc.png")),
+ ("zen", include_bytes!("../assets/logos/zen.png")),
+];
+
+pub struct Raster {
+ pub size: [usize; 2],
+ pub rgba: Vec,
+}
+
+fn svg_parse_options() -> usvg::Options<'static> {
+ let mut options = usvg::Options::default();
+ options.image_href_resolver.resolve_string = Box::new(|_, _| None);
+ options
+}
+
+/// Rasterize SVG markup that is already in memory. This never opens a path.
+pub fn rasterize_svg_markup(svg: &str, size: u32) -> Option {
+ let options = svg_parse_options();
+ let tree = usvg::Tree::from_str(svg, &options).ok()?;
+ let mut pixmap = tiny_skia::Pixmap::new(size, size)?;
+ let tree_size = tree.size();
+ let scale = (size as f32 / tree_size.width()).min(size as f32 / tree_size.height());
+ let tx = (size as f32 - tree_size.width() * scale) / 2.0;
+ let ty = (size as f32 - tree_size.height() * scale) / 2.0;
+ let transform = tiny_skia::Transform::from_translate(tx, ty).pre_scale(scale, scale);
+ resvg::render(&tree, transform, &mut pixmap.as_mut());
+ Some(Raster {
+ size: [size as usize, size as usize],
+ rgba: pixmap.data().to_vec(),
+ })
+}
+
+pub fn color_image_from_raster(raster: &Raster) -> ColorImage {
+ ColorImage::from_rgba_unmultiplied(raster.size, &raster.rgba)
+}
+
+pub fn decode_png(bytes: &[u8]) -> Option {
+ let image = image::load_from_memory(bytes).ok()?;
+ let rgba = image.to_rgba8();
+ Some(ColorImage::from_rgba_unmultiplied(
+ [rgba.width() as usize, rgba.height() as usize],
+ rgba.as_raw(),
+ ))
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::path::PathBuf;
+
+ fn assert_markup(name: &str, svg: &str) {
+ assert!(
+ svg.contains("