Display icon and launch image (decode with stb_image)

This commit is contained in:
hikari_no_yume
2022-12-01 23:03:11 +01:00
parent fc98bc5aed
commit 85cd1c9120
10 changed files with 180 additions and 4 deletions

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "vendor/stb"]
path = vendor/stb
url = git@github.com:nothings/stb.git

1
Cargo.lock generated
View File

@@ -165,6 +165,7 @@ dependencies = [
name = "touchHLE"
version = "0.1.0"
dependencies = [
"cc",
"plist",
"sdl2",
"sdl2-sys",

View File

@@ -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
View 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()
);
}

View File

@@ -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
View 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()) }
}
}

View File

@@ -0,0 +1,4 @@
#define STB_IMAGE_IMPLEMENTATION
#define STB_ONLY_PNG
#define STB_NO_STDIO
#include "../../vendor/stb/stb_image.h"

View File

@@ -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 {

View File

@@ -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

Submodule vendor/stb added at 8b5f1f37b5