Skip to content

Instantly share code, notes, and snippets.

@bedalton
Created June 28, 2020 06:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bedalton/3f79b044c317c3c3d4984de28a21a760 to your computer and use it in GitHub Desktop.
Save bedalton/3f79b044c317c3c3d4984de28a21a760 to your computer and use it in GitHub Desktop.
A PHP script to generate a Creatures 1 SPR sprite file from a directory or list of images.
<?php
/*
* Creates an Creatures 1 SPR file given a directory or a list of files
* If creating sprite from a complete directory, prefix with -d
* @example php ./mkspr.php -d ./piey
*
* Otherwise give a space separated list of images to use.
* @example php mkspr.php piey pie00.png pie01.png pie02.png
*
* Note> Files in directory are sorted by character not number, meaning that 10.png will come before 2.png
*/
define('DS', DIRECTORY_SEPARATOR);
define("IMAGE_HEADER_SIZE", 8);
$palette = [[0,0,0],[252,252,252],[252,252,252],[252,252,252],[252,252,252],[252,252,252],[252,252,252],[252,252,252],[252,252,252],[252,252,252],[252,252,252],[16,8,8],[20,24,40],[24,40,16],[24,36,48],[44,16,8],[40,24,36],[52,40,16],[48,44,48],[24,28,68],[20,52,84],[24,60,96],[36,28,68],[44,52,72],[44,56,104],[28,64,28],[28,64,40],[52,72,24],[52,72,44],[60,96,24],[60,96,40],[24,64,92],[28,64,100],[52,68,80],[44,76,104],[56,96,76],[60,96,112],[72,24,8],[72,28,36],[80,44,16],[72,52,44],[104,24,12],[108,28,36],[108,48,16],[104,52,36],[72,56,72],[72,56,104],[104,52,72],[116,52,104],[80,72,20],[80,72,48],[80,100,24],[76,104,44],[112,72,20],[108,76,44],[112,100,20],[116,100,48],[76,76,76],[76,84,108],[84,100,80],[84,100,112],[104,84,76],[104,88,104],[112,104,80],[108,108,108],[48,60,132],[56,92,144],[64,60,132],[76,88,140],[72,88,176],[80,104,140],[72,108,172],[100,88,136],[100,92,172],[108,112,140],[108,116,168],[76,92,196],[80,116,200],[92,112,236],[104,120,204],[100,120,244],[104,140,52],[92,132,76],[92,128,104],[108,140,76],[116,136,112],[120,164,76],[120,164,104],[88,128,140],[92,128,184],[112,132,148],[116,136,172],[124,164,140],[120,164,176],[88,132,204],[88,144,228],[88,164,240],[112,136,204],[116,136,252],[120,160,216],[112,164,236],[140,24,16],[144,28,36],[136,52,16],[140,52,40],[172,24,16],[172,28,32],[168,48,16],[172,48,40],[152,52,72],[140,76,20],[140,80,40],[144,104,20],[144,104,48],[172,80,20],[168,84,40],[176,104,20],[172,108,44],[136,84,72],[136,88,108],[140,108,76],[136,116,108],[172,80,72],[176,84,100],[168,116,72],[172,116,104],[208,44,28],[212,52,72],[200,84,20],[200,84,40],[204,104,20],[204,112,44],[232,80,20],[232,80,44],[232,116,20],[232,116,40],[204,80,72],[204,84,100],[204,116,72],[200,116,104],[232,80,80],[236,88,96],[240,112,72],[236,112,112],[144,60,132],[140,80,132],[132,120,144],[132,120,168],[168,120,136],[164,124,164],[128,124,196],[208,48,128],[216,112,136],[236,116,204],[164,136,44],[148,132,80],[144,136,112],[136,172,80],[140,172,108],[176,136,80],[172,140,108],[180,164,80],[180,168,112],[156,196,60],[164,208,92],[208,136,24],[208,136,48],[212,168,20],[208,168,44],[240,140,20],[236,140,44],[244,172,20],[244,172,48],[204,140,76],[200,148,104],[208,168,80],[208,168,112],[236,144,72],[236,144,100],[240,172,76],[236,176,108],[208,196,56],[244,204,12],[248,204,48],[252,240,12],[252,236,44],[212,196,80],[212,196,112],[200,244,80],[204,244,108],[248,200,76],[244,204,108],[248,236,76],[252,232,112],[140,136,144],[140,144,172],[144,168,144],[148,168,180],[168,144,140],[164,152,176],[176,168,144],[172,168,180],[136,148,204],[132,152,248],[148,164,208],[144,168,252],[160,156,196],[172,172,204],[164,184,244],[168,200,168],[152,196,196],[176,192,208],[168,196,252],[176,232,196],[184,228,232],[204,144,144],[200,152,164],[204,176,140],[200,176,176],[236,144,140],[236,144,164],[232,180,136],[232,180,168],[196,184,200],[196,188,224],[244,172,204],[212,200,144],[208,200,176],[204,240,136],[204,228,176],[240,204,144],[236,208,172],[248,232,144],[248,236,176],[212,200,204],[200,200,232],[212,228,204],[216,232,228],[228,212,208],[224,208,224],[240,232,208],[244,244,236],[252,252,252],[0,0,0],[0,0,0],[0,0,0],[252,252,252],[252,252,252],[252,252,252],[252,252,252],[252,252,252],[252,252,252],[252,252,252],[252,252,252],[252,252,252],[252,252,252]];
function starts_with($haystack, $needle)
{
$length = strlen($needle);
return (substr($haystack, 0, $length) === $needle);
}
function ends_with($haystack, $needle)
{
$length = strlen($needle);
if ($length == 0) {
return true;
}
return (substr($haystack, -$length) === $needle);
}
function compareColors($colorA, $colorB) {
return abs($colorA[0] - $colorB[0]) + abs($colorA[1] - $colorB[1]) + abs($colorA[2] - $colorB[2]);
}
function getColorIndex($colors, $inputColor) {
$selectedColor = 0;
$deviation = PHP_INT_MAX;
foreach ($colors as $i => $color) {
$curDev = compareColors($inputColor, $color);
if ($curDev < $deviation) {
$deviation = $curDev;
$selectedColor = $i;
}
}
return $selectedColor;
}
$black = getColorIndex($palette, [
0,
0,
0
]);
list($r,$g,$b) = $palette[$black];
if ($r !== 0 || $g !== 0 || $g !== 0) {
echo ("Color is not black. Found [$r,$g,$b]");
exit(2);
}
$directoryIndex = array_search("-d", $argv);
if ($directoryIndex !== FALSE) {
$directory = $argv[$directoryIndex + 1];
if (!starts_with($directory, DS)) {
$directory = realpath(getcwd() . DS . $directory);
}
$files = glob( $directory . DS ."*.{jpeg,jpg,png,gif,bmp}", GLOB_BRACE);
} else {
$files = array_slice($argv, 2);
foreach($files as $i => $file) {
if (!starts_with($file, DS)) {
$files[$i] = realpath(getcwd() . DS . $file);
if (empty($files[$i])) {
throw new Exception("Could not resolve path to $file");
}
}
}
}
if (count($files) < 1) {
echo "No images found in directory\n";
exit(2);
}
$hasTransparency = false;
$headers = "";
$headers .= pack("v*", count($files));
$imageOffset = 2;
$imageOffset += count($files) * IMAGE_HEADER_SIZE;
$imageBytes = "";
foreach ($files as $file) {
$extension = pathinfo($file, PATHINFO_EXTENSION);
switch ($extension) {
case "jpg":
case "jpeg":
$image = imagecreatefromjpeg($file);
break;
case "png":
$image = imagecreatefrompng($file);
$hasTransparency = true;
break;
case "gif":
$image = imagecreatefromgif($file);
$hasTransparency = true;
break;
case "bmp":
$image = imagecreatefrombmp($file);
break;
default:
throw new Exception("Non-Image file encountered. '$file' is not an image\n");
}
$imageSize = getimagesize($file);
$width = $imageSize[0];
$height = $imageSize[1];
if ($hasTransparency) {
$temp = imagecreatetruecolor($width, $height);
imagefill($temp, 0, 0, imagecolorallocate($temp, 0,0,0));
imagecopyresampled($temp, $image,0,0,0,0, $width, $height, $width, $height);
$image = $temp;
}
$headers .= pack("V", $imageOffset);
$headers .= pack("v*", $width, $height);
$imageOffset += $width * $height;
for ($y = 0; $y < $height; $y++) {
for ($x = 0; $x < $width; $x++) {
$rgb = imagecolorat($image, $x, $y);
$colors = imagecolorsforindex($image, $rgb);
$red = $colors['red'];
$green = $colors['green'];
$blue = $colors['blue'];
$closest = $hasTransparency && $colors['alpha'] > 1 ? $black : getColorIndex($palette, [
$red,
$green,
$blue
]);
$imageBytes .= pack("C", $closest);
}
}
}
$out = $headers . $imageBytes;
$output = $argv[1];
$file = pathinfo($output, PATHINFO_BASENAME);
$directory = pathinfo($output, PATHINFO_DIRNAME);
if (!starts_with($output, "/")) {
$outputDir = realpath(getcwd() . DS);
if (empty($outputDir)) {
$output = getcwd() . DS . $output;
} else {
$output = $outputDir .DS . $output;
}
}
if (!ends_with($output,".spr")) {
$output .= ".spr";
}
file_put_contents($output, $out);
echo "Created sprite with ".count($files) . " images to $output\n";
exit(0);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment