Skip to content

Instantly share code, notes, and snippets.

@Muqsit
Created January 5, 2022 21:46
Show Gist options
  • Save Muqsit/88ec549faaa616efb10a3a16a7f7e2de to your computer and use it in GitHub Desktop.
Save Muqsit/88ec549faaa616efb10a3a16a7f7e2de to your computer and use it in GitHub Desktop.
Converting head layer from minecraft skin data to a standard image format
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