Created
January 5, 2022 21:46
-
-
Save Muqsit/88ec549faaa616efb10a3a16a7f7e2de to your computer and use it in GitHub Desktop.
Converting head layer from minecraft skin data to a standard image format
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use image::*; | |
use image::imageops::FilterType; | |
use std::result::Result; | |
// Takes a byte array of skin data and returns the face layer encoded in a given | |
// standard image format. The `width` and `height` here are the widths and heights | |
// of the output image (not the input skin). | |
fn skin_to_image_data(bytes: &Vec<u8>, width: u32, height: u32, format: ImageFormat) -> Result<Vec<u8>, &'static str>{ | |
let blocks = skin_get_block_length(bytes.len())?; | |
let mut image: RgbImage = RgbImage::new(blocks, blocks); // (blocks x blocks) image is the smallest representation of the skin's head | |
skin_write_block(&bytes, &mut image, blocks, 1, 1); // write face layer first | |
skin_write_block(&bytes, &mut image, blocks, 5, 1); // write mask layer above face layer | |
let mut out: Vec<u8> = Vec::new(); | |
DynamicImage::ImageRgb8(image) | |
.resize(width, height, FilterType::Nearest) | |
.write_to(&mut out, format) | |
.map_err(|_| "failed to encode image")?; | |
Ok(out) | |
} | |
// Returns block size of the head layer | |
fn skin_get_block_length(skin_size: usize) -> Result<u32, &'static str>{ | |
match skin_size { | |
8192 | 16384 => Ok(8), // 64 x 32 and 64 x 64 skins have the same block size | |
65536 => Ok(16), // 128 x 128 skins have twice the block size of 64-wide skins | |
_ => Err("invalid skin size") | |
} | |
} | |
// Writes a specific head layer block of a skin data into an RgbImage | |
fn skin_write_block(bytes: &Vec<u8>, image: &mut RgbImage, blocks: u32, block_x: usize, block_y: usize){ | |
let blocks_usize = blocks as usize; | |
let mut offset = ((block_y * blocks_usize * blocks_usize * 8) + (block_x * blocks_usize)) * 4; | |
let mut x = 0; | |
let mut y = 0; | |
while y < blocks { | |
if bytes[offset + 3] > 0 { // only consider pixels with alpha (transparency) > 0 | |
image.put_pixel(x, y, Rgb([bytes[offset], bytes[offset + 1], bytes[offset + 2]])); | |
} | |
offset += 4; | |
x += 1; | |
if x == blocks { | |
x = 0; | |
y += 1; | |
offset += blocks_usize * 7 * 4; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment