Passed
Push — master ( 9aa048...898b5e )
by Mattia
04:05
created

Avatar::calculateHelmStandardDeviation()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 41
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 32
c 1
b 0
f 0
nc 6
nop 1
dl 0
loc 41
rs 9.408
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 (
113
                ($this->redStdDev > self::DEFAULT_STANDARD_DEVIATION && $this->greenStdDev > self::DEFAULT_STANDARD_DEVIATION) ||
114
                ($this->redStdDev > self::DEFAULT_STANDARD_DEVIATION && $this->blueStdDev > self::DEFAULT_STANDARD_DEVIATION) ||
115
                ($this->greenStdDev > self::DEFAULT_STANDARD_DEVIATION && $this->blueStdDev > self::DEFAULT_STANDARD_DEVIATION)
116
            ) ||
117
            $this->meanAlpha === 127;
118
    }
119
120
    /**
121
     * Render avatar image.
122
     *
123
     * @param int    $size Avatar size
124
     * @param string $type Section rendered
125
     *
126
     * @throws \Minepic\Image\Exceptions\InvalidSectionSpecifiedException|\Minepic\Image\Exceptions\ImageTrueColorCreationFailedException
127
     * @throws \Minepic\Image\Exceptions\ImageCreateFromPngFailedException
128
     */
129
    public function render(int $size = 0, string $type = self::FRONT): void
130
    {
131
        if ($size <= 0 || $size > env('MAX_AVATAR_SIZE')) {
132
            $size = (int) env('DEFAULT_AVATAR_SIZE');
133
        }
134
        // generate png from url/path
135
        $baseSkinImage = $this->createImageFromPng($this->skinPath);
136
        \imagealphablending($baseSkinImage, false);
137
        \imagesavealpha($baseSkinImage, true);
138
139
        // Head
140
        $this->imgResource = $this->createTrueColorSquare($size);
141
142
        // Sections Coordinates
143
        $faceCoordinates = AvatarCoordinates::getAvatarSection($type);
144
        $helmCoordinates = AvatarCoordinates::getHelmSection($type);
145
146
        \imagecopyresampled($this->imgResource, $baseSkinImage, 0, 0, $faceCoordinates->getX(), $faceCoordinates->getY(), $size, $size, 8, 8);
147
148
        // if all pixel have transparency or the colors are not the same
149
        if ($this->hasHelm($baseSkinImage, $helmCoordinates)) {
150
            $helm = $this->createTrueColorSquare($size);
151
            \imagealphablending($helm, false);
152
            \imagesavealpha($helm, true);
153
            \imagefilledrectangle($helm, 0, 0, $size, $size, $this->colorAllocateAlpha($helm));
154
            \imagecopyresampled($helm, $baseSkinImage, 0, 0, $helmCoordinates->getX(), $helmCoordinates->getY(), $size, $size, 8, 8);
155
            $merge = $this->createTrueColorSquare($size);
156
            \imagecopy($merge, $this->imgResource, 0, 0, 0, 0, $size, $size);
157
            \imagecopy($merge, $helm, 0, 0, 0, 0, $size, $size);
158
            \imagecopymerge($this->imgResource, $merge, 0, 0, 0, 0, $size, $size, 0);
159
            $this->imgResource = $merge;
160
            unset($merge);
161
        }
162
    }
163
164
    /**
165
     * @param $image
166
     * @param \Minepic\Image\Point $helmCoordinates
167
     *
168
     * @throws \Minepic\Image\Exceptions\ImageTrueColorCreationFailedException
169
     *
170
     * @return resource
171
     */
172
    public function createHelmCheckImage($image, Point $helmCoordinates)
173
    {
174
        $helmCheckImage = $this->createTrueColorSquare(8);
175
        \imagealphablending($helmCheckImage, false);
176
        \imagesavealpha($helmCheckImage, true);
177
        \imagefilledrectangle($helmCheckImage, 0, 0, 8, 8, $this->colorAllocateAlpha($helmCheckImage));
178
        \imagecopyresampled($helmCheckImage, $image, 0, 0, $helmCoordinates->getX(), $helmCoordinates->getY(), 8, 8, 8, 8);
179
180
        return $helmCheckImage;
181
    }
182
}
183