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 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
9 * @author  Andreas Gohr <andi@splitbrain.org>
10 * @author  Daniel Dias Rodrigues <danieldiasr@gmail.com> (modernization)
11 */
12
13declare(strict_types=1);
14
15if (php_sapi_name() !== 'cli') {
16    $seed = preg_replace('/[^a-f0-9]/i', '', $_GET['seed'] ?? '');
17    $size = (int) ($_GET['size'] ?? 120);
18    $size = max(20, min(512, $size)); // limits between 20 and 512 pixels
19
20    header('Content-Type: image/png');
21    header('Cache-Control: public, max-age=86400');
22
23    $image = generate_monster($seed, $size);
24    if ($image) {
25        imagepng($image);
26        imagedestroy($image);
27    } else {
28        http_response_code(500);
29        echo 'Erro ao gerar imagem.';
30    }
31    exit;
32}
33
34/**
35 * Generates monster image based on seed and size
36 */
37function generate_monster(string $seed, int $size): ?GdImage
38{
39    if (!function_exists('imagecreatetruecolor')) {
40        return null;
41    }
42
43    $hash = md5($seed);
44
45    $parts = [
46        'legs'  => get_part(substr($hash, 0, 2), 1, 5),
47        'hair'  => get_part(substr($hash, 2, 2), 1, 5),
48        'arms'  => get_part(substr($hash, 4, 2), 1, 5),
49        'body'  => get_part(substr($hash, 6, 2), 1, 15),
50        'eyes'  => get_part(substr($hash, 8, 2), 1, 15),
51        'mouth' => get_part(substr($hash, 10, 2), 1, 10),
52    ];
53
54    $monster = imagecreatetruecolor(120, 120);
55    if (!$monster) return null;
56
57    $white = @imagecolorallocate($monster, 255, 255, 255);
58    imagefill($monster, 0, 0, $white);
59
60    foreach ($parts as $part => $index) {
61        $filename = __DIR__ . '/parts/' . $part . '_' . $index . '.png';
62        if (!file_exists($filename)) continue;
63        $part_img = imagecreatefrompng($filename);
64        imageSaveAlpha($part_img, true);
65        if ($part_img) {
66            imagecopy($monster, $part_img, 0, 0, 0, 0, 120, 120);
67            imagedestroy($part_img);
68        }
69    }
70
71    if ($size !== 120) {
72        $resized = imagecreatetruecolor($size, $size);
73        imagefill($resized, 0, 0, $white);
74        imagecopyresampled($resized, $monster, 0, 0, 0, 0, $size, $size, 120, 120);
75        imagedestroy($monster);
76        return $resized;
77    }
78
79    return $monster;
80}
81
82/**
83 * Converts part of the hash into an image index
84 */
85function get_part(string $hex, int $min, int $max): int
86{
87    $val = hexdec($hex);
88    return ($val % ($max - $min + 1)) + $min;
89}
90
91