JSFiddle - React, Tailwind, and code Playground
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
<!-- by AimeePlaysMSM -->
<h1>Sprite sheet to images converter</h1>
<small>red outline = rotated sprite. mouseover to view label. premultiplier and de-premultiplied alpha output supported. click to download sprite with rotation corrected). click download button to download all as a ZIP.</small><br />
Sprite bitmap:<br />
<input id="sprite" type="file" accept=".png" /><input id="depre" type="checkbox" checked="checked"><label for="depre" title="removes black fringing, but check with your graphics software to see what it expects">de-premultiply alpha</label><input id="respectorigins" type="checkbox"><label for="respectorigins">respect origins</label></label><br />
Sprite XML:<br />
<input id="xml" type="file" accept=".xml" disabled="disabled" />
<span id="xmldata"></span>
<hr />
Sheet:<br />
<img id="spriteimg" style="display:none;" /><!-- need to keep one in memory for reference anyway -->
<div style="display:flex; flex-flow:row wrap; align-items:flex-start; justify-content:start;">
<canvas id="spritecnv" style="display:none;"></canvas>
<span id="spritename"></span>
</div>
<canvas id="lutcnv" style="display:none;"></canvas> <!-- ditto -->
<button id="dlall" disabled="disabled">download all sprites (ZIP)</button>
CSS
body {
font-family: sans-serif;
font-size:8pt;
}
h1 {
padding:0;
margin:0;
}
JavaScript
let spriteRects = [];
let spriteName = "";
let lutdata;
sprite.onclick = function () {
sprite.value = "";
xml.disabled = true;
dlall.disabled = true;
spritecnv.style.display = "none";
}
sprite.onchange = function () {
spriteimg.onload = function() {
xml.disabled = false;
}
spriteimg.src = window.URL.createObjectURL(sprite.files[0])
spriteName = sprite.value.replace(/^.*[\\\/]/, '');
};
xml.onclick = function () {
xml.value = "";
}
xml.onchange = function () {
const file = xml.files[0];
const fr = new FileReader();
fr.onload = function (e) {
const parser = new DOMParser();
const data = parser.parseFromString(e.target.result, "text/xml");
const atlas = data.getElementsByTagName("TextureAtlas")[0];
const sprites = atlas.getElementsByTagName("sprite");
const numSprites = sprites.length;
xmldata.textContent = `Sprites found: ${numSprites}`;
spritecnv.width = lutcnv.width = spriteimg.naturalWidth;
spritecnv.height = lutcnv.height = spriteimg.naturalHeight;
const ctx = spritecnv.getContext("2d");
const lutctx = lutcnv.getContext("2d");
ctx.drawImage(spriteimg,0,0);
ctx.imageSmoothingEnabled = false;
lutctx.imageSmoothingEnabled = false;
if (depre.checked) {
// de-premultiply the sprite
const imageData = ctx.getImageData(0,0,spritecnv.width,spritecnv.height);
const pixels = imageData.data;
const pixelsLen = pixels.length;
for (let i = 0; i < pixelsLen; i += 4) {
const a = pixels[i+3]; // Alpha
if ((a < 255) && (a > 0)) {
const mult = a / 255.0
pixels[i] /= mult; // Red
pixels[i + 1] /= mult; // Green
pixels[i + 2] /= mult; // Blue
}
}
ctx.putImageData(imageData,0,0);
}
ctx.translate(0.5, 0.5); // to fix blurry edges
// lutctx.translate(0.5, 0.5);
for (let i = 0; i < numSprites; i += 1) {
const...