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
<?php | |
define('PREPARED_MOONS', [ | |
'apple_new_moon_with_face', | |
'apple_full_moon_with_face', | |
'apple_ios10_new_moon_with_face', | |
'apple_ios10_full_moon_with_face', | |
'noto_new_moon_with_face', | |
'noto_full_moon_with_face', | |
'segoe_new_moon_with_face', | |
'segoe_full_moon_with_face', | |
'apple_new_moon', | |
'apple_full_moon', | |
'apple_ios10_new_moon', | |
'apple_ios10_full_moon', | |
'noto_new_moon', | |
'noto_full_moon', | |
'segoe_new_moon', | |
'segoe_full_moon', | |
]); | |
class InvalidParameterException extends Exception {} | |
function expect_param($name) { | |
if (!array_key_exists($name, $_GET) || in_array($_GET[$name], [null, ''])) { | |
throw new InvalidParameterException("Parameter $name is required but not specified."); | |
} | |
return $_GET[$name]; | |
} | |
function rand_float($min, $max) { | |
return lcg_value() * ($max - $min) + $min; | |
} | |
if (!in_array($_SERVER['REQUEST_METHOD'], ['GET', 'HEAD'])) { | |
http_response_code(405); | |
header('Cache-Control: no-cache'); | |
header('Content-Type: text/plain'); | |
header('Content-Length: 20'); | |
echo "Method not allowed.\n"; | |
die(); | |
} | |
try { | |
$canvas_width = intval(expect_param('width')); | |
if ($canvas_width <= 0 || $canvas_width > 3840) { | |
throw new InvalidParameterException("Width must be a number between 0 and 3840."); | |
} | |
$canvas_height = intval(expect_param('height')); | |
if ($canvas_height <= 0 || $canvas_height > 2160) { | |
throw new InvalidParameterException("Height must be a number between 0 and 2160."); | |
} | |
$moon_count = intval(expect_param('count')); | |
if ($moon_count <= 0 || $moon_count > 512) { | |
throw new InvalidParameterException("Count must be a number between 0 and 512."); | |
} | |
$background = expect_param('background'); | |
if (!in_array($background, ['black', 'white', 'transparent'])) { | |
throw new InvalidParameterException("Background must be \"black\", \"white\" or \"transparent\"."); | |
} | |
$output_format = expect_param('format'); | |
if (!in_array($output_format, ['jpeg', 'png', 'webp'])) { | |
throw new InvalidParameterException("Format must be \"jpeg\", \"png\" or \"webp\"."); | |
} | |
$moon_settings = array_map(function($w) { | |
$q = explode(',', $w); | |
if (sizeof($q) !== 3) { | |
throw new InvalidParameterException('Each moons must have 3 parameters.'); | |
} | |
if (!in_array($q[0], PREPARED_MOONS)) { | |
throw new InvalidParameterException("$q[0] is not a valid moon name."); | |
} | |
$q[1] = (float) $q[1]; | |
if ($q[1] <= 0 || $q[1] > 128) { | |
throw new InvalidParameterException("Moon minimum scale size must be a number between 0 and 128."); | |
} | |
$q[2] = (float) $q[2]; | |
if ($q[2] <= 0 || $q[2] > 128) { | |
throw new InvalidParameterException("Moon maximum scale size must be a number between 0 and 128."); | |
} | |
return [ | |
'image_path' => 'prepared-moons/'.$q[0].'.png', | |
'min_scale' => $q[1], | |
'max_scale' => $q[2], | |
]; | |
}, explode(';', expect_param('moons'))); | |
if (sizeof($moon_settings) > 16) { | |
throw new InvalidParameterException("Too many moons."); | |
} | |
} catch (InvalidParameterException $e) { | |
http_response_code(400); | |
header('Cache-Control: no-cache'); | |
header('Content-Type: text/plain'); | |
header('Content-Length: '.(strlen($e->getMessage()) + 1)); | |
header('X-Error: '.$e->getMessage()); | |
echo $e->getMessage()."\n"; | |
die(); | |
} | |
$black = new ImagickPixel('black'); | |
$white = new ImagickPixel('white'); | |
$transparent = new ImagickPixel('transparent'); | |
$moons = array_map(function($moon) { | |
$moon['image'] = new Imagick($moon['image_path']); | |
return $moon; | |
}, $moon_settings); | |
$canvas = new Imagick(); | |
$canvas->newImage($canvas_width, $canvas_height, [ | |
'black' => $black, | |
'white' => $white, | |
'transparent' => $transparent, | |
][$background]); | |
for ($i = 0; $i < $moon_count; $i++) { | |
$moon = $moons[array_rand($moons)]; | |
$changed_moon = clone $moon['image']; | |
$scale = rand_float($moon['min_scale'], $moon['max_scale']); | |
$moon_width = round($changed_moon->getImageWidth() * $scale); | |
$moon_height = round($changed_moon->getImageHeight() * $scale); | |
$moon_size = min($moon_width, $moon_height); | |
$changed_moon->scaleImage($moon_width, $moon_height); | |
$changed_moon->rotateImage($transparent, rand_float(0, 360)); | |
$canvas->compositeImage( | |
$changed_moon, | |
imagick::COMPOSITE_OVER, | |
($moon_size - $changed_moon->getImageWidth()) / 2 + rand(0, $canvas_width - $moon_size), | |
($moon_size - $changed_moon->getImageHeight()) / 2 + rand(0, $canvas_height - $moon_size) | |
); | |
$changed_moon->clear(); | |
} | |
foreach ($moons as $moon) { | |
$moon['image']->clear(); | |
} | |
$canvas->setImageFormat($output_format); | |
$canvas->setImageCompressionQuality(90); | |
header('Content-Type: image/'.$output_format); | |
header('Cache-Control: no-cache'); | |
header('Content-Length: '.strlen($canvas)); | |
echo $canvas; | |
$canvas->clear(); | |
$black->clear(); | |
$white->clear(); | |
$transparent->clear(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment