avatar-privacy/includes/avatar-privacy/avatar-handlers/default-icons/generators/class-retro.php

199 lines
6.1 KiB
PHP

<?php
/**
* This file is part of Avatar Privacy.
*
* Copyright 2018-2023 Peter Putzer.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright (c) 2013, 2014, 2016 Benjamin Laugueux <benjamin@yzalis.com>
* Copyright (c) 2015 Grummfy <grummfy@gmail.com>
* Copyright (c) 2016, 2017 Lucas Michot
* Copyright (c) 2019 Arjen van der Meijden
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* ***
*
* @package mundschenk-at/avatar-privacy
* @license http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Avatar_Privacy\Avatar_Handlers\Default_Icons\Generators;
use Avatar_Privacy\Avatar_Handlers\Default_Icons\Generator;
use Avatar_Privacy\Tools\Number_Generator;
use Avatar_Privacy\Tools\Template;
use Avatar_Privacy\Vendor\Colors\RandomColor;
/**
* Generates a "retro" SVG icon based on a hash.
*
* @since 1.0.0
* @since 2.0.0 Moved to Avatar_Privacy\Avatar_Handlers\Default_Icons\Generators
* @since 2.7.0 Now directly incorporates the SVG generation code from the
* deprecated package `yzalis/identicon`.
*
* @author Peter Putzer <github@mundschenk.at>
* @author Grummfy <grummfy@gmail.com>
*/
class Retro implements Generator {
private const NUMBER_OF_PIXELS = 5;
private const DIVIDER = 3;
private const POSSIBLE_COLUMNS = [
0 => [ 0, 4 ],
1 => [ 1, 3 ],
2 => [ 2 ],
];
/**
* The random number generator.
*
* @since 2.3.0
*
* @var Number_Generator
*/
protected Number_Generator $number_generator;
/**
* The templating handler.
*
* @since 2.7.0
*
* @var Template
*/
protected Template $template;
/**
* Creates a new instance.
*
* @since 2.1.0 Parameter `$identicon` added.
* @since 2.3.0 Parameter `$number_generator` added.
* @since 2.7.0 Parameter `$template` added.
*
* @param Number_Generator $number_generator A pseudo-random number generator.
* @param Template $template The templating handler.
*/
public function __construct( Number_Generator $number_generator, Template $template ) {
$this->number_generator = $number_generator;
$this->template = $template;
}
/**
* Builds an icon based on the given seed returns the image data.
*
* @param string $seed The seed data (hash).
* @param int $size Optional. The size in pixels. Default 128 (but really ignored).
*
* @return string
*/
public function build( $seed, $size = 128 ) {
// Initialize random number with seed.
$this->number_generator->seed( $seed );
// Generate icon.
$bitmap = $this->get_bitmap( \md5( $seed ) ); // The seed is already hashed, but we want to generate the same result as earlier versions did using `yzalis/identicon`.
$args = [
'rows' => \count( $bitmap ),
'columns' => \count( $bitmap[1] ),
'path' => $this->draw_path( $bitmap ),
'color' => RandomColor::one( [ 'luminosity' => 'bright' ] ),
'bg_color' => RandomColor::one( [ 'luminosity' => 'light' ] ),
];
$result = $this->template->get_partial( 'public/partials/retro/svg.php', $args );
// Restore randomness.
$this->number_generator->reset();
return $result;
}
/**
* Converts the hash into an two-dimensional array of boolean.
*
* @since 2.7.0
*
* @param string $hash The MD5 hash.
*
* @return array<int, array<int, bool>>
*/
protected function get_bitmap( string $hash ): array {
$bitmap = [];
foreach ( \array_slice( \str_split( $hash, 2 ), 0, self::NUMBER_OF_PIXELS * self::DIVIDER ) as $i => $hex_tuple ) {
$row = (int) ( $i / self::DIVIDER );
$pixel = (bool) \round( \hexdec( $hex_tuple[0] ) / 10 );
foreach ( self::POSSIBLE_COLUMNS[ $i % self::DIVIDER ] as $column ) {
$bitmap[ $row ][ $column ] = $pixel;
}
\ksort( $bitmap[ $row ] );
}
return $bitmap;
}
/**
* Draws an SVG path from the given bitmap.
*
* @since 2.7.0
*
* @param array $bitmap A two-dimensional array of boolean pixel values.
*
* @return string
*
* @phpstan-param array<int, array<int, bool>> $bitmap
*/
protected function draw_path( array $bitmap ): string {
$rects = [];
foreach ( $bitmap as $line_key => $line_value ) {
foreach ( $line_value as $col_key => $col_value ) {
if ( true === $col_value ) {
$rects[] = 'M' . $col_key . ',' . $line_key . 'h1v1h-1v-1';
}
}
}
return \implode( '', $rects );
}
}