Created
March 16, 2022 03:39
-
-
Save cgsdev0/26affdfe65710379b8420153ae53e5d2 to your computer and use it in GitHub Desktop.
slices a minecraft skin into multiple images; currently hosted here https://cgs.dev/minecraft_skin_exploder.html
This file contains hidden or 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
<!doctype html> | |
<html> | |
<head> | |
<title>Minecraft Skin Exploder</title> | |
</head> | |
<script type="text/javascript"> | |
const maskdata = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAZhJREFUeJztm71KA0EURjPaiJAmgitiYbWY3iewsNQm2FjZiZBewVLQUhDFTgR/QFL5HBaCxYattNIFkyYidmstCXcIN3DA/U6dO3P4uLOZ2UlCLcLC4VwZ+4zFx1EveOrLi6Zr/rCXmfNPeQb/DygAWoBGAdACNAqAFqAJ3u95L++L8+T06gAFQAvQKABagEYB0AI0Q2fl9Kww9wV5OzHP143VLde+ov/0YI5frKTm+Ek3H+v9Q+U7QAHQAjQhtuZjfF63J+Uykuzr2VUfeyZUvgMUAC1AowBoARoFQAvQKABagEYB0AI0Q/vkRr1lng36g465ty6bP777/GzGHP929socf/t7R+8DxkEB0AI0IbbmY/SWbiblMpK7t3tXfeyZUPkOUAC0AI0CoAVoFAAtQKMAaAEaBUAL0Aztk+utDfNsMOg8mnvr18K+v19O7Lu6tLD/H5An9u//dw/2zfrL45M/9ZXvAAVAC9CE2JqP8XLedQms16Zd9Wunm676yneAAqAFaBQALUCjAGgBGgVAC9AoAFqA5heamUiO3GpTvwAAAABJRU5ErkJggg=="; | |
const old_colormap = { | |
"left_arm": "#e92826", | |
"right_arm": "#26e929", | |
"right_leg": "#e0e926", | |
"left_leg": "#2693e9", | |
"left_leg_outer": "#120d54", | |
"right_leg_outer": "#0d544e", | |
"right_arm_outer": "#687978", | |
"left_arm_outer": "#a50a9f", | |
"chest": "#123456", | |
"jacket": "#ff29f8", | |
"hat": "#ff29f8", | |
"head": "#187c13" | |
} | |
const colormap = { | |
"arms": [ "#e92826", "#26e929" ], | |
"legs": [ "#e0e926", "#2693e9" ], | |
"legs_outer": [ "#120d54", "#0d544e" ], | |
"arms_outer": [ "#687978", "#a50a9f" ], | |
"chest": "#123456", | |
"jacket": "#ff29f8", | |
"hat": "#ff29f8", | |
"head": "#187c13" | |
} | |
var uploaded_name = "unknown"; | |
const componentToHex = (c) => { | |
var hex = c.toString(16); | |
return hex.length == 1 ? "0" + hex : hex; | |
} | |
const color_comp = (r, g, b, hex) => { | |
const c = "#" + | |
componentToHex(r) + | |
componentToHex(g) + | |
componentToHex(b); | |
return c.toLowerCase() === hex.toLowerCase(); | |
} | |
const clear_canvas = () => { | |
const canvas = document.querySelector("canvas"); | |
var ctx = canvas.getContext("2d"); | |
ctx.clearRect(0, 0, canvas.width, canvas.height); | |
} | |
const create_canvas = (colormap, ...segments) => { | |
const img = document.querySelector("#mask"); | |
const skin = document.querySelector("#preview"); | |
const canvas = document.querySelector("canvas"); | |
var ctx = canvas.getContext("2d"); | |
ctx.clearRect(0, 0, canvas.width, canvas.height); | |
ctx.drawImage(img, 0, 0); | |
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); | |
const data = imageData.data; | |
for (var i = 0; i < data.length; i += 4) { | |
const r = data[i]; | |
const g = data[i + 1]; | |
const b = data[i + 2]; | |
if (!segments.some((seg) => color_comp(r,g,b,seg))) { | |
data[i] = 0; | |
data[i + 1] = 0; | |
data[i + 2] = 0; | |
data[i + 3] = 255; | |
} | |
else { | |
data[i] = 255; | |
data[i + 1] = 255; | |
data[i + 2] = 255; | |
data[i + 3] = 255; | |
} | |
} | |
ctx.putImageData(imageData, 0, 0); | |
var idata = ctx.getImageData(0, 0, canvas.width, canvas.height); | |
var data32 = new Uint32Array(idata.data.buffer); | |
var i = 0, len = data32.length; | |
while(i < len) data32[i] = data32[i++] << 8; | |
ctx.putImageData(idata, 0, 0); | |
ctx.globalCompositeOperation = "source-in"; | |
ctx.drawImage(skin, 0, 0); | |
ctx.globalCompositeOperation = "source-over"; | |
} | |
// | |
function split_and_download() { | |
var colormap_edited = JSON.parse(document.querySelector("#mask_editor").value); | |
for(const [k, v] of Object.entries(colormap_edited)) { | |
if (Array.isArray(v)) { | |
create_canvas(colormap_edited, ...v); | |
} else { | |
create_canvas(colormap_edited, v); | |
} | |
var link = document.createElement('a'); | |
link.setAttribute('download', uploaded_name + "_" + k + '.png'); | |
link.setAttribute('href', document.querySelector("canvas").toDataURL("image/png").replace("image/png", "image/octet-stream")); | |
link.click(); | |
} | |
clear_canvas(); | |
} | |
window.addEventListener('load', () => { | |
document.querySelector('#upload').addEventListener('change', function() { | |
if (this.files && this.files[0]) { | |
var img = document.querySelector('#preview'); | |
img.onload = () => { | |
URL.revokeObjectURL(img.src); // no longer needed, free memory | |
} | |
img.src = URL.createObjectURL(this.files[0]); // set src to blob url | |
const uploaded_name_arr = this.files[0].name.split("."); | |
if(uploaded_name_arr.length > 1) { | |
uploaded_name_arr.pop(); | |
uploaded_name = uploaded_name_arr.join(""); | |
} | |
else { | |
uploaded_name = uploaded_name_arr[0]; | |
} | |
document.querySelector("#download").disabled = false; | |
} | |
}); | |
document.querySelector('#upload_mask').addEventListener('change', function() { | |
if (this.files && this.files[0]) { | |
var img = document.querySelector('#mask'); | |
img.onload = () => { | |
URL.revokeObjectURL(img.src); // no longer needed, free memory | |
} | |
img.src = URL.createObjectURL(this.files[0]); // set src to blob url | |
uploaded_name = this.files[0].name; | |
} | |
}); | |
document.querySelector("#download").addEventListener('click', split_and_download) | |
document.querySelector("#download").disabled = true | |
document.querySelector("#mask_editor").value = JSON.stringify(colormap, null, 4) | |
function blobToBase64(img) { | |
var c = document.createElement('canvas'); | |
c.height = img.naturalHeight; | |
c.width = img.naturalWidth; | |
var ctx = c.getContext('2d'); | |
ctx.drawImage(img, 0, 0, c.width, c.height); | |
var base64String = c.toDataURL(); | |
return base64String; | |
} | |
// load settings | |
const mask_image = window.localStorage.getItem("mask_image"); | |
const mask_settings = window.localStorage.getItem("mask_settings"); | |
if (mask_settings) { | |
document.querySelector("#mask_editor").value = mask_settings | |
} | |
if (mask_image) { | |
document.querySelector("#mask").src = mask_image | |
} | |
document.querySelector("#save").addEventListener('click', async () => { | |
window.localStorage.setItem("mask_settings", document.querySelector("#mask_editor").value) | |
window.localStorage.setItem("mask_image", blobToBase64(document.querySelector("#mask"))) | |
}) | |
document.querySelector("#reset").addEventListener('click', () => { | |
document.querySelector("#mask_editor").value = JSON.stringify(colormap, null, 4) | |
document.querySelector("#mask").setAttribute("src", maskdata); | |
}) | |
}); | |
</script> | |
<h1>Minecraft Skin Exploder</h1> | |
<p>recommended: chrome > settings > advanced > downloads and turn off "Ask where to save each file before downloading"</p> | |
<img id="preview" width="64" height="64" /> | |
<input type="file" id="upload" /> | |
<br /> | |
<button type="submit" id="download">Download</button> | |
<br /> | |
<canvas width="64" height="64"></canvas> | |
<br /> | |
Current layer mask: | |
<img id="mask" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAZhJREFUeJztm71KA0EURjPaiJAmgitiYbWY3iewsNQm2FjZiZBewVLQUhDFTgR/QFL5HBaCxYattNIFkyYidmstCXcIN3DA/U6dO3P4uLOZ2UlCLcLC4VwZ+4zFx1EveOrLi6Zr/rCXmfNPeQb/DygAWoBGAdACNAqAFqAJ3u95L++L8+T06gAFQAvQKABagEYB0AI0Q2fl9Kww9wV5OzHP143VLde+ov/0YI5frKTm+Ek3H+v9Q+U7QAHQAjQhtuZjfF63J+Uykuzr2VUfeyZUvgMUAC1AowBoARoFQAvQKABagEYB0AI0Q/vkRr1lng36g465ty6bP777/GzGHP929socf/t7R+8DxkEB0AI0IbbmY/SWbiblMpK7t3tXfeyZUPkOUAC0AI0CoAVoFAAtQKMAaAEaBUAL0Aztk+utDfNsMOg8mnvr18K+v19O7Lu6tLD/H5An9u//dw/2zfrL45M/9ZXvAAVAC9CE2JqP8XLedQms16Zd9Wunm676yneAAqAFaBQALUCjAGgBGgVAC9AoAFqA5heamUiO3GpTvwAAAABJRU5ErkJggg==" /> | |
<br /> | |
Upload new mask: | |
<input type="file" id="upload_mask" /> | |
<br> | |
<textarea rows="25" cols="50" id="mask_editor"></textarea> | |
<br> | |
<button type="submit" id="save">Save Mask + Settings</button> | |
<button type="submit" id="reset">Reset to defaults</button> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment