xref: /plugin/avatar/monsterid.php (revision b92285bbe65635e90c694a11a7ffbe62b2cc188e)
1401a61e7Swikidesign<?php
21affe9b7Swikidesign/**
36e2b7c94Snerun * MonsterID Generator for DokuWiki Avatar Plugin
46e2b7c94Snerun *
56e2b7c94Snerun * Generates identicon-style monster avatars based on a seed.
66e2b7c94Snerun * Requires PHP GD extension.
76e2b7c94Snerun *
8984623feSnerun *
9984623feSnerun * MIT License
10984623feSnerun *
11984623feSnerun * Copyright (c) 2007 Andreas Gohr <andi@splitbrain.org>
12984623feSnerun *     @source: https://github.com/splitbrain/monsterID
13984623feSnerun *
14984623feSnerun * Copyright (c) 2009 Gina Häußge <github.com/foosel>
15984623feSnerun *     @source: https://github.com/dokufreaks/plugin-avatar
16984623feSnerun *
17984623feSnerun * Copyright (c) 2025 Daniel Dias Rodrigues <danieldiasr@gmail.com>
18984623feSnerun *     @source: https://github.com/nerun/dokuwiki-plugin-avatar
19984623feSnerun *
20984623feSnerun * Permission is hereby granted, free of charge, to any person obtaining a copy
21984623feSnerun * of this software and associated documentation files (the "Software"), to deal
22984623feSnerun * in the Software without restriction, including without limitation the rights
23984623feSnerun * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
24984623feSnerun * copies of the Software, and to permit persons to whom the Software is
25984623feSnerun * furnished to do so, subject to the following conditions:
26984623feSnerun *
27984623feSnerun * The above copyright notice and this permission notice shall be included in
28984623feSnerun * all copies or substantial portions of the Software.
29984623feSnerun *
30984623feSnerun * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31984623feSnerun * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
32984623feSnerun * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
33984623feSnerun * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
34984623feSnerun * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
35984623feSnerun * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
36984623feSnerun * SOFTWARE.
371affe9b7Swikidesign */
38401a61e7Swikidesign
396e2b7c94Snerundeclare(strict_types=1);
406e2b7c94Snerun
416e2b7c94Snerunif (php_sapi_name() !== 'cli') {
426e2b7c94Snerun    $seed = preg_replace('/[^a-f0-9]/i', '', $_GET['seed'] ?? '');
436e2b7c94Snerun    $size = (int) ($_GET['size'] ?? 120);
44*b92285bbSnerun    $size = max(20, min(400, $size)); // limits between 20 and 400 pixels
456e2b7c94Snerun
466e2b7c94Snerun    header('Content-Type: image/png');
476e2b7c94Snerun    header('Cache-Control: public, max-age=86400');
486e2b7c94Snerun
496e2b7c94Snerun    $image = generate_monster($seed, $size);
506e2b7c94Snerun    if ($image) {
516e2b7c94Snerun        imagepng($image);
526e2b7c94Snerun        imagedestroy($image);
536e2b7c94Snerun    } else {
546e2b7c94Snerun        http_response_code(500);
55984623feSnerun        echo 'Error generating image.';
566e2b7c94Snerun    }
576e2b7c94Snerun    exit;
586e2b7c94Snerun}
59401a61e7Swikidesign
601affe9b7Swikidesign/**
616e2b7c94Snerun * Generates monster image based on seed and size
621affe9b7Swikidesign */
636e2b7c94Snerunfunction generate_monster(string $seed, int $size): ?GdImage
646e2b7c94Snerun{
656e2b7c94Snerun    if (!function_exists('imagecreatetruecolor')) {
666e2b7c94Snerun        return null;
676e2b7c94Snerun    }
686e2b7c94Snerun
692e0c1c6eSGina Haeussge    $hash = md5($seed);
70401a61e7Swikidesign
716e2b7c94Snerun    $parts = [
726e2b7c94Snerun        'legs'  => get_part(substr($hash, 0, 2), 1, 5),
736e2b7c94Snerun        'hair'  => get_part(substr($hash, 2, 2), 1, 5),
746e2b7c94Snerun        'arms'  => get_part(substr($hash, 4, 2), 1, 5),
756e2b7c94Snerun        'body'  => get_part(substr($hash, 6, 2), 1, 15),
766e2b7c94Snerun        'eyes'  => get_part(substr($hash, 8, 2), 1, 15),
776e2b7c94Snerun        'mouth' => get_part(substr($hash, 10, 2), 1, 10),
786e2b7c94Snerun    ];
79401a61e7Swikidesign
806e2b7c94Snerun    $monster = imagecreatetruecolor(120, 120);
816e2b7c94Snerun    if (!$monster) return null;
826e2b7c94Snerun
834bc5a549Snerun    $white = @imagecolorallocate($monster, 255, 255, 255);
84401a61e7Swikidesign    imagefill($monster, 0, 0, $white);
85401a61e7Swikidesign
866e2b7c94Snerun    foreach ($parts as $part => $index) {
874bc5a549Snerun        $filename = __DIR__ . '/parts/' . $part . '_' . $index . '.png';
886e2b7c94Snerun        if (!file_exists($filename)) continue;
896e2b7c94Snerun        $part_img = imagecreatefrompng($filename);
904bc5a549Snerun        imageSaveAlpha($part_img, true);
916e2b7c94Snerun        if ($part_img) {
926e2b7c94Snerun            imagecopy($monster, $part_img, 0, 0, 0, 0, 120, 120);
936e2b7c94Snerun            imagedestroy($part_img);
94*b92285bbSnerun
95*b92285bbSnerun             // color the body
96*b92285bbSnerun            if ($part === 'body') {
97*b92285bbSnerun                $r = get_part(substr($hash, 0, 4), 20, 235);
98*b92285bbSnerun                $g = get_part(substr($hash, 4, 4), 20, 235);
99*b92285bbSnerun                $b = get_part(substr($hash, 8, 4), 20, 235);
100*b92285bbSnerun                $color = imagecolorallocate($monster, $r, $g, $b);
101*b92285bbSnerun                imagefill($monster, 60, 60, $color);
102*b92285bbSnerun            }
103401a61e7Swikidesign        }
104401a61e7Swikidesign    }
105401a61e7Swikidesign
1066e2b7c94Snerun    if ($size !== 120) {
1076e2b7c94Snerun        $resized = imagecreatetruecolor($size, $size);
1086e2b7c94Snerun        imagefill($resized, 0, 0, $white);
1096e2b7c94Snerun        imagecopyresampled($resized, $monster, 0, 0, 0, 0, $size, $size, 120, 120);
110401a61e7Swikidesign        imagedestroy($monster);
1116e2b7c94Snerun        return $resized;
112401a61e7Swikidesign    }
1132e0c1c6eSGina Haeussge
1146e2b7c94Snerun    return $monster;
1152e0c1c6eSGina Haeussge}
1162e0c1c6eSGina Haeussge
1176e2b7c94Snerun/**
1186e2b7c94Snerun * Converts part of the hash into an image index
1196e2b7c94Snerun */
1206e2b7c94Snerunfunction get_part(string $hex, int $min, int $max): int
1216e2b7c94Snerun{
1226e2b7c94Snerun    $val = hexdec($hex);
1236e2b7c94Snerun    return ($val % ($max - $min + 1)) + $min;
124401a61e7Swikidesign}
12523415f0dSnerun
126