1<?php 2/** 3 * MonsterID Generator for DokuWiki Avatar Plugin 4 * 5 * Generates identicon-style monster avatars based on a seed. 6 * Requires PHP GD extension. 7 * 8 * 9 * MIT License 10 * 11 * Copyright (c) 2007 Andreas Gohr <andi@splitbrain.org> 12 * @source: https://github.com/splitbrain/monsterID 13 * 14 * Copyright (c) 2009 Gina Häußge <github.com/foosel> 15 * @source: https://github.com/dokufreaks/plugin-avatar 16 * 17 * Copyright (c) 2025 Daniel Dias Rodrigues <danieldiasr@gmail.com> 18 * @source: https://github.com/nerun/dokuwiki-plugin-avatar 19 * 20 * Permission is hereby granted, free of charge, to any person obtaining a copy 21 * of this software and associated documentation files (the "Software"), to deal 22 * in the Software without restriction, including without limitation the rights 23 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 24 * copies of the Software, and to permit persons to whom the Software is 25 * furnished to do so, subject to the following conditions: 26 * 27 * The above copyright notice and this permission notice shall be included in 28 * all copies or substantial portions of the Software. 29 * 30 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 31 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 32 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 33 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 34 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 35 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 36 * SOFTWARE. 37 */ 38 39declare(strict_types=1); 40 41if (php_sapi_name() !== 'cli') { 42 $seed = preg_replace('/[^a-f0-9]/i', '', $_GET['seed'] ?? ''); 43 $size = (int) ($_GET['size'] ?? 120); 44 $size = max(20, min(400, $size)); // limits between 20 and 400 pixels 45 46 header('Content-Type: image/png'); 47 header('Cache-Control: public, max-age=86400'); 48 49 $image = generate_monster($seed, $size); 50 if ($image) { 51 imagepng($image); 52 imagedestroy($image); 53 } else { 54 http_response_code(500); 55 echo 'Error generating image.'; 56 } 57 exit; 58} 59 60/** 61 * Generates monster image based on seed and size 62 */ 63function generate_monster(string $seed, int $size): ?GdImage 64{ 65 if (!function_exists('imagecreatetruecolor')) { 66 return null; 67 } 68 69 $hash = md5($seed); 70 71 $parts = [ 72 'legs' => get_part(substr($hash, 0, 2), 1, 5), 73 'hair' => get_part(substr($hash, 2, 2), 1, 5), 74 'arms' => get_part(substr($hash, 4, 2), 1, 5), 75 'body' => get_part(substr($hash, 6, 2), 1, 15), 76 'eyes' => get_part(substr($hash, 8, 2), 1, 15), 77 'mouth' => get_part(substr($hash, 10, 2), 1, 10), 78 ]; 79 80 $monster = imagecreatetruecolor(120, 120); 81 if (!$monster) return null; 82 83 $white = @imagecolorallocate($monster, 255, 255, 255); 84 imagefill($monster, 0, 0, $white); 85 86 foreach ($parts as $part => $index) { 87 $filename = __DIR__ . '/parts/' . $part . '_' . $index . '.png'; 88 if (!file_exists($filename)) continue; 89 $part_img = imagecreatefrompng($filename); 90 imageSaveAlpha($part_img, true); 91 if ($part_img) { 92 imagecopy($monster, $part_img, 0, 0, 0, 0, 120, 120); 93 imagedestroy($part_img); 94 95 // color the body 96 if ($part === 'body') { 97 $r = get_part(substr($hash, 0, 4), 20, 235); 98 $g = get_part(substr($hash, 4, 4), 20, 235); 99 $b = get_part(substr($hash, 8, 4), 20, 235); 100 $color = imagecolorallocate($monster, $r, $g, $b); 101 imagefill($monster, 60, 60, $color); 102 } 103 } 104 } 105 106 if ($size !== 120) { 107 $resized = imagecreatetruecolor($size, $size); 108 imagefill($resized, 0, 0, $white); 109 imagecopyresampled($resized, $monster, 0, 0, 0, 0, $size, $size, 120, 120); 110 imagedestroy($monster); 111 return $resized; 112 } 113 114 return $monster; 115} 116 117/** 118 * Converts part of the hash into an image index 119 */ 120function get_part(string $hex, int $min, int $max): int 121{ 122 $val = hexdec($hex); 123 return ($val % ($max - $min + 1)) + $min; 124} 125 126