Passed
Push — master ( 165425...e22481 )
by Mattia
04:28
created

Avatar   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 172
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 17
eloc 71
dl 0
loc 172
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A calculateHelmStandardDeviation() 0 41 4
A isValidHelmStandardDeviation() 0 5 6
A render() 0 32 4
A createHelmCheckImage() 0 9 1
A hasHelm() 0 6 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Minepic\Image\Sections;
6
7
use Minepic\Image\ImageSection;
8
use Minepic\Image\Point;
9
use Minepic\Image\Sections\Avatar\Coordinates as AvatarCoordinates;
10
11
/**
12
 * Class Avatar.
13
 */
14
class Avatar extends ImageSection
15
{
16
    /**
17
     * Max Standard Deviation value for helm check.
18
     */
19
    private const DEFAULT_STANDARD_DEVIATION = 0.2;
20
21
    /**
22
     * Mean Alpha value (Helm).
23
     *
24
     * @var int
25
     */
26
    private int $meanAlpha = 0;
27
28
    /**
29
     * Red Standard Deviation value (Helm).
30
     *
31
     * @var float
32
     */
33
    private float $redStdDev = 0.0;
34
35
    /**
36
     * Green Standard Deviation value (Helm).
37
     *
38
     * @var float
39
     */
40
    private float $greenStdDev = 0.0;
41
42
    /**
43
     * Blue Standard Deviation value (Helm).
44
     *
45
     * @var float
46
     */
47
    private float $blueStdDev = 0;
48
49
    /**
50
     * Calculate sttdev for merging helm.
51
     *
52
     * @param $headSection
53
     */
54
    protected function calculateHelmStandardDeviation($headSection): void
55
    {
56
        // Check for helm image
57
        $allRed = [];
58
        $allGreen = [];
59
        $allBlue = [];
60
        $allAlpha = [];
61
        $x = 0;
62
        while ($x < 8) {
63
            $y = 0;
64
            while ($y < 8) {
65
                $color = \imagecolorat($headSection, $x, $y);
66
                $colors = \imagecolorsforindex($headSection, $color);
67
                $allRed[] = $colors['red'];
68
                $allGreen[] = $colors['green'];
69
                $allBlue[] = $colors['blue'];
70
                $allAlpha[] = $colors['alpha'];
71
                ++$y;
72
            }
73
            ++$x;
74
        }
75
        // mean value for each color
76
        $meanRed = \array_sum($allRed) / 64;
77
        $meanGreen = \array_sum($allGreen) / 64;
78
        $meanBlue = \array_sum($allBlue) / 64;
79
        $this->meanAlpha = (int) \round(\array_sum($allAlpha) / 64);
80
        // Arrays deviation
81
        $devsRed = [];
82
        $devsGreen = [];
83
        $devsBlue = [];
84
        $i = 0;
85
        while ($i < 64) {
86
            $devsRed[] = ($allRed[$i] - $meanRed) ** 2;
87
            $devsGreen[] = ($allGreen[$i] - $meanGreen) ** 2;
88
            $devsBlue[] = ($allBlue[$i] - $meanBlue) ** 2;
89
            ++$i;
90
        }
91
        // stddev for each color
92
        $this->redStdDev = \sqrt(\array_sum($devsRed) / 64);
93
        $this->greenStdDev = \sqrt(\array_sum($devsGreen) / 64);
94
        $this->blueStdDev = \sqrt(\array_sum($devsBlue) / 64);
95
    }
96
97
    /**
98
     * Checks if base image has helm for section.
99
     *
100
     * @param resource $baseSkinImage
101
     * @param Point    $helmCoordinates
102
     *
103
     * @throws \Minepic\Image\Exceptions\ImageTrueColorCreationFailedException
104
     *
105
     * @return bool
106
     */
107
    private function hasHelm($baseSkinImage, Point $helmCoordinates): bool
108
    {
109
        $helmCheckImage = $this->createHelmCheckImage($baseSkinImage, $helmCoordinates);
110
        $this->calculateHelmStandardDeviation($helmCheckImage);
111
112
        return $this->isValidHelmStandardDeviation() || $this->meanAlpha === 127;
113
    }
114
115
    /**
116
     * @return bool
117
     */
118
    private function isValidHelmStandardDeviation(): bool
119
    {
120
        return ($this->redStdDev > self::DEFAULT_STANDARD_DEVIATION && $this->greenStdDev > self::DEFAULT_STANDARD_DEVIATION) ||
121
            ($this->redStdDev > self::DEFAULT_STANDARD_DEVIATION && $this->blueStdDev > self::DEFAULT_STANDARD_DEVIATION) ||
122
            ($this->greenStdDev > self::DEFAULT_STANDARD_DEVIATION && $this->blueStdDev > self::DEFAULT_STANDARD_DEVIATION);
123
    }
124
125
    /**
126
     * Render avatar image.
127
     *
128
     * @param int    $size Avatar size
129
     * @param string $type Section rendered
130
     *
131
     * @throws \Minepic\Image\Exceptions\ImageCreateFromPngFailedException
132
     * @throws \Minepic\Image\Exceptions\ImageTrueColorCreationFailedException
133
     */
134
    public function render(int $size = 0, string $type = self::FRONT): void
135
    {
136
        if ($size <= 0 || $size > (int) env('MAX_AVATAR_SIZE')) {
137
            $size = (int) env('DEFAULT_AVATAR_SIZE');
138
        }
139
        // generate png from url/path
140
        $baseSkinImage = $this->createImageFromPng($this->skinPath);
141
        \imagealphablending($baseSkinImage, false);
142
        \imagesavealpha($baseSkinImage, true);
143
144
        // Head
145
        $this->imgResource = $this->createTrueColorSquare($size);
146
147
        // Sections Coordinates
148
        $faceCoordinates = AvatarCoordinates::getAvatarSection($type);
149
        $helmCoordinates = AvatarCoordinates::getHelmSection($type);
150
151
        \imagecopyresampled($this->imgResource, $baseSkinImage, 0, 0, $faceCoordinates->getX(), $faceCoordinates->getY(), $size, $size, 8, 8);
152
153
        // if all pixel have transparency or the colors are not the same
154
        if ($this->hasHelm($baseSkinImage, $helmCoordinates)) {
155
            $helm = $this->createTrueColorSquare($size);
156
            \imagealphablending($helm, false);
157
            \imagesavealpha($helm, true);
158
            \imagefilledrectangle($helm, 0, 0, $size, $size, $this->colorAllocateAlpha($helm));
159
            \imagecopyresampled($helm, $baseSkinImage, 0, 0, $helmCoordinates->getX(), $helmCoordinates->getY(), $size, $size, 8, 8);
160
            $merge = $this->createTrueColorSquare($size);
161
            \imagecopy($merge, $this->imgResource, 0, 0, 0, 0, $size, $size);
162
            \imagecopy($merge, $helm, 0, 0, 0, 0, $size, $size);
163
            \imagecopymerge($this->imgResource, $merge, 0, 0, 0, 0, $size, $size, 0);
164
            $this->imgResource = $merge;
165
            unset($merge);
166
        }
167
    }
168
169
    /**
170
     * @param $image
171
     * @param \Minepic\Image\Point $helmCoordinates
172
     *
173
     * @throws \Minepic\Image\Exceptions\ImageTrueColorCreationFailedException
174
     *
175
     * @return resource
176
     */
177
    public function createHelmCheckImage($image, Point $helmCoordinates)
178
    {
179
        $helmCheckImage = $this->createTrueColorSquare(8);
180
        \imagealphablending($helmCheckImage, false);
181
        \imagesavealpha($helmCheckImage, true);
182
        \imagefilledrectangle($helmCheckImage, 0, 0, 8, 8, $this->colorAllocateAlpha($helmCheckImage));
183
        \imagecopyresampled($helmCheckImage, $image, 0, 0, $helmCoordinates->getX(), $helmCoordinates->getY(), 8, 8, 8, 8);
184
185
        return $helmCheckImage;
186
    }
187
}
188