mirror of
https://github.com/touchHLE/touchHLE.git
synced 2026-01-31 01:25:24 +01:00
Display icon and launch image (decode with stb_image)
This commit is contained in:
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "vendor/stb"]
|
||||
path = vendor/stb
|
||||
url = git@github.com:nothings/stb.git
|
||||
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -165,6 +165,7 @@ dependencies = [
|
||||
name = "touchHLE"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"plist",
|
||||
"sdl2",
|
||||
"sdl2-sys",
|
||||
|
||||
@@ -12,3 +12,6 @@ plist = "1.3.1"
|
||||
# _CHHapticDynamicParameterIDHapticIntensityControl etc)
|
||||
sdl2 = { version = "=0.35.1", features = ["bundled", "static-link"] }
|
||||
sdl2-sys = "=0.35.1"
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0.77"
|
||||
|
||||
17
build.rs
Normal file
17
build.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
use std::path::Path;
|
||||
|
||||
fn main() {
|
||||
let root = Path::new(file!()).parent().unwrap();
|
||||
|
||||
cc::Build::new()
|
||||
.file(root.join("src/image/stb_image_wrapper.c"))
|
||||
.compile("stb_image_wrapper");
|
||||
println!(
|
||||
"cargo:rerun-if-changed={}",
|
||||
root.join("src/image/stb_image_wrapper.c").to_str().unwrap()
|
||||
);
|
||||
println!(
|
||||
"cargo:rerun-if-changed={}",
|
||||
root.join("vendor/stb").to_str().unwrap()
|
||||
);
|
||||
}
|
||||
@@ -13,6 +13,7 @@ use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Bundle {
|
||||
path: PathBuf,
|
||||
plist: Dictionary,
|
||||
}
|
||||
|
||||
@@ -38,10 +39,24 @@ impl Bundle {
|
||||
.into_dictionary()
|
||||
.ok_or("plist root value is not a dictionary")?;
|
||||
|
||||
Ok(Bundle { plist })
|
||||
Ok(Bundle { path, plist })
|
||||
}
|
||||
|
||||
pub fn display_name(&self) -> &str {
|
||||
self.plist["CFBundleDisplayName"].as_string().unwrap()
|
||||
}
|
||||
|
||||
pub fn launch_image_path(&self) -> PathBuf {
|
||||
if let Some(base_name) = self.plist.get("UILaunchImageFile") {
|
||||
self.path
|
||||
.join(&format!("{}.png", base_name.as_string().unwrap()))
|
||||
} else {
|
||||
self.path.join("Default.png")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn icon_path(&self) -> PathBuf {
|
||||
self.path
|
||||
.join(self.plist["CFBundleIconFile"].as_string().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
84
src/image.rs
Normal file
84
src/image.rs
Normal file
@@ -0,0 +1,84 @@
|
||||
//! Image decoding. Currently only supports PNG.
|
||||
//!
|
||||
//! Implemented as a wrapper around the C library stb_image, since it supports
|
||||
//! "CgBI" PNG files (an Apple proprietary extension used in iOS apps).
|
||||
|
||||
use std::ffi::{c_int, c_uchar, c_void};
|
||||
|
||||
// See build.rs, src/image/stb_image_wrapper.c and vendor/stb/stb_image.h
|
||||
extern "C" {
|
||||
fn stbi_convert_iphone_png_to_rgb(flag_true_if_should_convert: c_int);
|
||||
fn stbi_set_unpremultiply_on_load(flag_true_if_should_unpremultiply: c_int);
|
||||
fn stbi_load_from_memory(
|
||||
buffer: *const c_uchar,
|
||||
len: c_int,
|
||||
x: *mut c_int,
|
||||
y: *mut c_int,
|
||||
channels_in_file: *mut c_int,
|
||||
desired_channels: c_int,
|
||||
) -> *mut c_uchar;
|
||||
fn stbi_image_free(retval_from_stbi_load: *mut c_void);
|
||||
}
|
||||
|
||||
pub struct Image {
|
||||
pixels: *mut c_uchar,
|
||||
dimensions: (u32, u32),
|
||||
}
|
||||
|
||||
impl Image {
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Image, ()> {
|
||||
let len: c_int = bytes.len().try_into().map_err(|_| ())?;
|
||||
|
||||
let mut x: c_int = 0;
|
||||
let mut y: c_int = 0;
|
||||
let mut _channels_in_file: c_int = 0;
|
||||
|
||||
let pixels = unsafe {
|
||||
stbi_convert_iphone_png_to_rgb(1);
|
||||
stbi_set_unpremultiply_on_load(1);
|
||||
stbi_load_from_memory(
|
||||
bytes.as_ptr(),
|
||||
len,
|
||||
&mut x,
|
||||
&mut y,
|
||||
&mut _channels_in_file,
|
||||
4,
|
||||
)
|
||||
};
|
||||
if pixels.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let width: u32 = x.try_into().unwrap();
|
||||
let height: u32 = y.try_into().unwrap();
|
||||
|
||||
Ok(Image {
|
||||
pixels,
|
||||
dimensions: (width, height),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_file<P: AsRef<std::path::Path>>(path: P) -> Result<Image, ()> {
|
||||
Self::from_bytes(&std::fs::read(path).map_err(|_| ())?)
|
||||
}
|
||||
|
||||
pub fn dimensions(&self) -> (u32, u32) {
|
||||
self.dimensions
|
||||
}
|
||||
|
||||
/// Get image data as bytes (8 bits per channel RGBA)
|
||||
pub fn pixels(&self) -> &[u8] {
|
||||
unsafe {
|
||||
std::slice::from_raw_parts(
|
||||
self.pixels,
|
||||
self.dimensions.0 as usize * self.dimensions.1 as usize * 4,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Image {
|
||||
fn drop(&mut self) {
|
||||
unsafe { stbi_image_free(self.pixels.cast()) }
|
||||
}
|
||||
}
|
||||
4
src/image/stb_image_wrapper.c
Normal file
4
src/image/stb_image_wrapper.c
Normal file
@@ -0,0 +1,4 @@
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STB_ONLY_PNG
|
||||
#define STB_NO_STDIO
|
||||
#include "../../vendor/stb/stb_image.h"
|
||||
12
src/main.rs
12
src/main.rs
@@ -3,6 +3,7 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
mod bundle;
|
||||
mod image;
|
||||
mod window;
|
||||
|
||||
use std::path::PathBuf;
|
||||
@@ -45,7 +46,16 @@ fn main() -> Result<(), String> {
|
||||
}
|
||||
};
|
||||
|
||||
let mut window = window::Window::new(&format!("{} (touchHLE)", bundle.display_name()));
|
||||
let icon = image::Image::from_file(bundle.icon_path())
|
||||
.map_err(|_| "Could not load icon".to_string())?;
|
||||
let launch_image = image::Image::from_file(bundle.launch_image_path())
|
||||
.map_err(|_| "Could not load launch image".to_string())?;
|
||||
|
||||
let mut window = window::Window::new(
|
||||
&format!("{} (touchHLE)", bundle.display_name()),
|
||||
icon,
|
||||
launch_image,
|
||||
);
|
||||
|
||||
let mut events = Vec::new(); // re-use each iteration for efficiency
|
||||
loop {
|
||||
|
||||
@@ -6,10 +6,36 @@
|
||||
//! window system interaction in general, because it is assumed only one window
|
||||
//! will be needed for the runtime of the app.
|
||||
|
||||
use crate::image::Image;
|
||||
use sdl2::pixels::PixelFormatEnum;
|
||||
use sdl2::rect::Rect;
|
||||
use sdl2::surface::Surface;
|
||||
|
||||
pub enum Event {
|
||||
Quit,
|
||||
}
|
||||
|
||||
fn surface_from_image(image: &Image) -> Surface {
|
||||
let src_pixels = image.pixels();
|
||||
let (width, height) = image.dimensions();
|
||||
|
||||
let mut surface = Surface::new(width, height, PixelFormatEnum::RGBA32).unwrap();
|
||||
let (width, height) = (width as usize, height as usize);
|
||||
let pitch = surface.pitch() as usize;
|
||||
surface.with_lock_mut(|dst_pixels| {
|
||||
for y in 0..(height as usize) {
|
||||
for x in 0..(width as usize) {
|
||||
for channel in 0..4 {
|
||||
let src_idx = y * width * 4 + x * 4 + channel;
|
||||
let dst_idx = y * pitch + x * 4 + channel;
|
||||
dst_pixels[dst_idx] = src_pixels[src_idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
surface
|
||||
}
|
||||
|
||||
pub struct Window {
|
||||
_sdl_ctx: sdl2::Sdl,
|
||||
_video_ctx: sdl2::VideoSubsystem,
|
||||
@@ -17,18 +43,30 @@ pub struct Window {
|
||||
event_pump: sdl2::EventPump,
|
||||
}
|
||||
impl Window {
|
||||
pub fn new(title: &str) -> Window {
|
||||
pub fn new(title: &str, icon: Image, launch_image: Image) -> Window {
|
||||
let sdl_ctx = sdl2::init().unwrap();
|
||||
let video_ctx = sdl_ctx.video().unwrap();
|
||||
|
||||
let window = video_ctx
|
||||
let mut window = video_ctx
|
||||
.window(title, 320, 480)
|
||||
.position_centered()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
window.set_icon(surface_from_image(&icon));
|
||||
|
||||
let event_pump = sdl_ctx.event_pump().unwrap();
|
||||
|
||||
let mut window_surface = window.surface(&event_pump).unwrap();
|
||||
surface_from_image(&launch_image)
|
||||
.blit(
|
||||
Rect::new(0, 0, 320, 480),
|
||||
&mut window_surface,
|
||||
Rect::new(0, 0, 320, 480),
|
||||
)
|
||||
.unwrap();
|
||||
window_surface.finish().unwrap();
|
||||
|
||||
Window {
|
||||
_sdl_ctx: sdl_ctx,
|
||||
_video_ctx: video_ctx,
|
||||
|
||||
1
vendor/stb
vendored
Submodule
1
vendor/stb
vendored
Submodule
Submodule vendor/stb added at 8b5f1f37b5
Reference in New Issue
Block a user