291 lines
9.7 KiB
PHP
291 lines
9.7 KiB
PHP
<?php
|
|
/**
|
|
* This file is part of Jdenticon for PHP.
|
|
* https://github.com/dmester/jdenticon-php/
|
|
*
|
|
* Copyright (c) 2018 Daniel Mester Pirttijärvi
|
|
*
|
|
* For full license information, please see the LICENSE file that was
|
|
* distributed with this source code.
|
|
*/
|
|
|
|
namespace Jdenticon\Rendering;
|
|
|
|
use Jdenticon\Shapes\Shape;
|
|
use Jdenticon\Shapes\ShapeCategory;
|
|
use Jdenticon\Shapes\ShapeDefinitions;
|
|
|
|
/**
|
|
* Generates identicons and render them to a
|
|
* {@link \Jdenticon\Rendering\RendererInterface}. This class dictates what
|
|
* shapes will be used in the generated icons. If you intend to customize the
|
|
* appearance of generated icons you probably wants to either subclass or modify
|
|
* this class.
|
|
*/
|
|
class IconGenerator
|
|
{
|
|
private $defaultShapes;
|
|
private static $instance;
|
|
|
|
protected function __construct()
|
|
{
|
|
$this->defaultShapes = array(
|
|
// Sides
|
|
new ShapeCategory(
|
|
/*$colorIndex=*/ 8,
|
|
/*$shapes=*/ ShapeDefinitions::getOuterShapes(),
|
|
/*$shapeIndex=*/ 2,
|
|
/*$rotationIndex=*/ 3,
|
|
/*$positions=*/ array(1,0, 2,0, 2,3, 1,3, 0,1, 3,1, 3,2, 0,2)
|
|
),
|
|
|
|
// Corners
|
|
new ShapeCategory(
|
|
/*$colorIndex=*/ 9,
|
|
/*$shapes=*/ ShapeDefinitions::getOuterShapes(),
|
|
/*$shapeIndex=*/ 4,
|
|
/*$rotationIndex=*/ 5,
|
|
/*$positions=*/ array(0,0, 3,0, 3,3, 0,3)
|
|
),
|
|
|
|
// Center
|
|
new ShapeCategory(
|
|
/*$colorIndex=*/ 10,
|
|
/*$shapes=*/ ShapeDefinitions::getCenterShapes(),
|
|
/*$shapeIndex=*/ 1,
|
|
/*$rotationIndex=*/ null,
|
|
/*$positions=*/ array(1,1, 2,1, 2,2, 1,2)
|
|
)
|
|
);
|
|
}
|
|
|
|
public static function getDefaultGenerator()
|
|
{
|
|
if (self::$instance === null) {
|
|
self::$instance = new IconGenerator();
|
|
}
|
|
return self::$instance;
|
|
}
|
|
|
|
/**
|
|
* Gets the number of cells in each direction of the icons generated by
|
|
* this IconGenerator.
|
|
*
|
|
* @return int
|
|
*/
|
|
public function getCellCount()
|
|
{
|
|
return 4;
|
|
}
|
|
|
|
/**
|
|
* Determines the hue to be used in an icon for the specified hash.
|
|
*
|
|
* @return float Hue in the range [0, 1].
|
|
*/
|
|
protected static function getHue($hash)
|
|
{
|
|
$value = hexdec(substr($hash, -7));
|
|
return $value / 0xfffffff;
|
|
}
|
|
|
|
/**
|
|
* Determines whether $newValue is duplicated in $source if all values
|
|
* in $duplicateValues are determined to be equal.
|
|
*
|
|
* @return bool
|
|
*/
|
|
private static function isDuplicate(
|
|
array $source, $newValue,
|
|
array $duplicateValues)
|
|
{
|
|
if (in_array($newValue, $duplicateValues, true)) {
|
|
foreach ($duplicateValues as $value) {
|
|
if (in_array($value, $source, true)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Gets the specified octet from a byte array.
|
|
*
|
|
* @param string $hash The hexstring from which the octet will be retrieved.
|
|
* @param int $index The zero-based index of the octet to be returned.
|
|
* @return int
|
|
*/
|
|
protected static function getOctet($hash, $index)
|
|
{
|
|
return hexdec($hash[$index]);
|
|
}
|
|
|
|
/**
|
|
* Gets an array of the shape categories to be rendered in icons generated
|
|
* by this IconGenerator.
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function getCategories()
|
|
{
|
|
return $this->defaultShapes;
|
|
}
|
|
|
|
/**
|
|
* Gets an enumeration of individual shapes to be rendered in an icon for a
|
|
* specific hash.
|
|
*
|
|
* @param \Jdenticon\Rendering\ColorTheme $colorTheme A color theme
|
|
* specifying the colors to be used in the icon.
|
|
* @param string $hash The hash for which the shapes will be returned.
|
|
* @return array(Jdenticon\Shapes\Shape)
|
|
*/
|
|
protected function getShapes($colorTheme, $hash)
|
|
{
|
|
$usedColorThemeIndexes = array();
|
|
$categories = self::getCategories();
|
|
$shapes = array();
|
|
$colorCount = $colorTheme->getCount();
|
|
|
|
foreach ($categories as $category) {
|
|
$colorThemeIndex =
|
|
self::getOctet($hash, $category->colorIndex) % $colorCount;
|
|
|
|
if (self::isDuplicate(
|
|
// Disallow dark gray and dark color combo
|
|
$usedColorThemeIndexes, $colorThemeIndex, array(0, 4)) ||
|
|
self::isDuplicate(
|
|
// Disallow light gray and light color combo
|
|
$usedColorThemeIndexes, $colorThemeIndex, array(2, 3))
|
|
) {
|
|
$colorThemeIndex = 1;
|
|
}
|
|
|
|
$usedColorThemeIndexes[] = $colorThemeIndex;
|
|
|
|
$startRotationIndex = $category->rotationIndex === null ?
|
|
0 : self::getOctet($hash, $category->rotationIndex);
|
|
$shapeIndex =
|
|
self::getOctet($hash, $category->shapeIndex) %
|
|
count($category->shapes);
|
|
$shape = $category->shapes[$shapeIndex];
|
|
|
|
$shapes[] = new Shape(
|
|
/*$definition=*/ $shape,
|
|
/*$color=*/ $colorTheme->getByIndex($colorThemeIndex),
|
|
/*$positions=*/ $category->positions,
|
|
/*$startRotationIndex=*/ $startRotationIndex
|
|
);
|
|
}
|
|
|
|
return $shapes;
|
|
}
|
|
|
|
/**
|
|
* Creates a quadratic copy of the specified
|
|
* {@link \Jdenticon\Rendering\Rectangle} with a multiple of the cell count
|
|
* as size.
|
|
*
|
|
* @param \Jdenticon\Rendering\Rectangle $rect The rectangle to be
|
|
* normalized.
|
|
*/
|
|
protected function normalizeRectangle(\Jdenticon\Rendering\Rectangle $rect)
|
|
{
|
|
$size = (int)min($rect->width, $rect->height);
|
|
|
|
// Make size a multiple of the cell count
|
|
$size -= $size % $this->getCellCount();
|
|
|
|
return new Rectangle(
|
|
(int)($rect->x + ($rect->width - $size) / 2),
|
|
(int)($rect->y + ($rect->height - $size) / 2),
|
|
$size,
|
|
$size);
|
|
}
|
|
|
|
/**
|
|
* Renders the background of an icon.
|
|
*
|
|
* @param \Jdenticon\Rendering\RendererInterface $renderer The renderer to
|
|
* be used for rendering the icon on the target surface.
|
|
* @param \Jdenticon\Rendering\Rectangle $rect The outer bounds of the icon.
|
|
* @param \Jdenticon\IdenticonStyle $style The style of the icon.
|
|
* @param \Jdenticon\Rendering\ColorTheme $colorTheme A color theme
|
|
* specifying the colors to be used in the icon.
|
|
* @param string $hash The hash to be used as basis for the generated icon.
|
|
*/
|
|
protected function renderBackground(
|
|
\Jdenticon\Rendering\RendererInterface $renderer,
|
|
\Jdenticon\Rendering\Rectangle $rect,
|
|
\Jdenticon\IdenticonStyle $style,
|
|
\Jdenticon\Rendering\ColorTheme $colorTheme,
|
|
$hash)
|
|
{
|
|
$renderer->setBackgroundColor($style->getBackgroundColor());
|
|
}
|
|
|
|
/**
|
|
* Renders the foreground of an icon.
|
|
*
|
|
* @param \Jdenticon\Rendering\RendererInterface $renderer The renderer to
|
|
* be used for rendering the icon on the target surface.
|
|
* @param \Jdenticon\Rendering\Rectangle $rect The outer bounds of the icon.
|
|
* @param \Jdenticon\IdenticonStyle $style The style of the icon.
|
|
* @param \Jdenticon\Rendering\ColorTheme $colorTheme A color theme
|
|
* specifying the colors to be used in the icon.
|
|
* @param string $hash The hash to be used as basis for the generated icon.
|
|
*/
|
|
protected function renderForeground(
|
|
\Jdenticon\Rendering\RendererInterface $renderer,
|
|
\Jdenticon\Rendering\Rectangle $rect,
|
|
\Jdenticon\IdenticonStyle $style,
|
|
\Jdenticon\Rendering\ColorTheme $colorTheme,
|
|
$hash)
|
|
{
|
|
// Ensure rect is quadratic and a multiple of the cell count
|
|
$normalizedRect = $this->normalizeRectangle($rect);
|
|
$cellSize = $normalizedRect->width / $this->getCellCount();
|
|
|
|
foreach ($this->getShapes($colorTheme, $hash) as $shape) {
|
|
$rotation = $shape->startRotationIndex;
|
|
|
|
$renderer->beginShape($shape->color);
|
|
|
|
$positionCount = count($shape->positions);
|
|
for ($i = 0; $i + 1 < $positionCount; $i += 2) {
|
|
$renderer->setTransform(new Transform(
|
|
$normalizedRect->x + $shape->positions[$i + 0] * $cellSize,
|
|
$normalizedRect->y + $shape->positions[$i + 1] * $cellSize,
|
|
$cellSize, $rotation++ % 4));
|
|
|
|
$shape->definition->__invoke($renderer, $cellSize, $i / 2);
|
|
}
|
|
|
|
$renderer->endShape();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates an identicon for the specified hash.
|
|
*
|
|
* @param \Jdenticon\Rendering\RendererInterface $renderer The renderer to
|
|
* be used for rendering the icon on the target surface.
|
|
* @param \Jdenticon\Rendering\Rectangle $rect The outer bounds of the icon.
|
|
* @param \Jdenticon\IdenticonStyle $style The style of the icon.
|
|
* @param string $hash The hash to be used as basis for the generated icon.
|
|
*/
|
|
public function generate(
|
|
\Jdenticon\Rendering\RendererInterface $renderer,
|
|
\Jdenticon\Rendering\Rectangle $rect,
|
|
\Jdenticon\IdenticonStyle $style,
|
|
$hash)
|
|
{
|
|
$hue = self::getHue($hash);
|
|
$colorTheme = new ColorTheme($hue, $style);
|
|
|
|
$this->renderBackground($renderer, $rect, $style, $colorTheme, $hash);
|
|
$this->renderForeground($renderer, $rect, $style, $colorTheme, $hash);
|
|
}
|
|
}
|