Passed
Push — master ( 5b0c2e...3b0a13 )
by Mattia
05:10
created

Avatar::createHelmCheckImage()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 6
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 8
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace App\Image\Sections;
6
7
use App\Image\Point;
8
use App\Image\ImageSection;
9
use App\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
     * @return bool
103
     * @throws \App\Image\Exceptions\ImageTrueColorCreationFailedException
104
     */
105
    private function hasHelm($baseSkinImage, Point $helmCoordinates): bool
106
    {
107
        $helmCheckImage = $this->createHelmCheckImage($baseSkinImage, $helmCoordinates);
108
        $this->calculateHelmStandardDeviation($helmCheckImage);
109
110
        return (
111
                ($this->redStdDev > self::DEFAULT_STANDARD_DEVIATION && $this->greenStdDev > self::DEFAULT_STANDARD_DEVIATION) ||
112
                ($this->redStdDev > self::DEFAULT_STANDARD_DEVIATION && $this->blueStdDev > self::DEFAULT_STANDARD_DEVIATION) ||
113
                ($this->greenStdDev > self::DEFAULT_STANDARD_DEVIATION && $this->blueStdDev > self::DEFAULT_STANDARD_DEVIATION)
114
            ) ||
115
            $this->meanAlpha === 127;
116
    }
117
118
    /**
119
     * Render avatar image.
120
     *
121
     * @param int    $size Avatar size
122
     * @param string $type Section rendered
123
     *
124
     * @throws \App\Image\Exceptions\InvalidSectionSpecifiedException|\App\Image\Exceptions\ImageTrueColorCreationFailedException
125
     */
126
    public function renderAvatar(int $size = 0, string $type = self::FRONT): void
127
    {
128
        if ($size <= 0 || $size > env('MAX_AVATAR_SIZE')) {
129
            $size = (int) env('DEFAULT_AVATAR_SIZE');
130
        }
131
        // generate png from url/path
132
        $baseSkinImage = \imagecreatefrompng($this->skinPath);
133
        \imagealphablending($baseSkinImage, false);
0 ignored issues
show
Bug introduced by
It seems like $baseSkinImage can also be of type false; however, parameter $image of imagealphablending() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

133
        \imagealphablending(/** @scrutinizer ignore-type */ $baseSkinImage, false);
Loading history...
134
        \imagesavealpha($baseSkinImage, true);
0 ignored issues
show
Bug introduced by
It seems like $baseSkinImage can also be of type false; however, parameter $image of imagesavealpha() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

134
        \imagesavealpha(/** @scrutinizer ignore-type */ $baseSkinImage, true);
Loading history...
135
136
        // Head
137
        $this->imgResource = $this->createTrueColorSquare($size);
138
139
        // Sections Coordinates
140
        $faceCoordinates = AvatarCoordinates::getAvatarSection($type);
141
        $helmCoordinates = AvatarCoordinates::getHelmSection($type);
142
143
        \imagecopyresampled($this->imgResource, $baseSkinImage, 0, 0, $faceCoordinates->getX(), $faceCoordinates->getY(), $size, $size, 8, 8);
0 ignored issues
show
Bug introduced by
It seems like $baseSkinImage can also be of type false; however, parameter $src_image of imagecopyresampled() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

143
        \imagecopyresampled($this->imgResource, /** @scrutinizer ignore-type */ $baseSkinImage, 0, 0, $faceCoordinates->getX(), $faceCoordinates->getY(), $size, $size, 8, 8);
Loading history...
144
145
        // if all pixel have transparency or the colors are not the same
146
        if ($this->hasHelm($baseSkinImage, $helmCoordinates)) {
0 ignored issues
show
Bug introduced by
It seems like $baseSkinImage can also be of type false; however, parameter $baseSkinImage of App\Image\Sections\Avatar::hasHelm() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

146
        if ($this->hasHelm(/** @scrutinizer ignore-type */ $baseSkinImage, $helmCoordinates)) {
Loading history...
147
            $helm = $this->createTrueColorSquare($size);
148
            \imagealphablending($helm, false);
149
            \imagesavealpha($helm, true);
150
            \imagefilledrectangle($helm, 0, 0, $size, $size, $this->colorAllocateAlpha($helm));
151
            \imagecopyresampled($helm, $baseSkinImage, 0, 0, $helmCoordinates->getX(), $helmCoordinates->getY(), $size, $size, 8, 8);
152
            $merge = $this->createTrueColorSquare($size);
153
            \imagecopy($merge, $this->imgResource, 0, 0, 0, 0, $size, $size);
154
            \imagecopy($merge, $helm, 0, 0, 0, 0, $size, $size);
155
            \imagecopymerge($this->imgResource, $merge, 0, 0, 0, 0, $size, $size, 0);
156
            $this->imgResource = $merge;
157
            unset($merge);
158
        }
159
    }
160
161
    /**
162
     * @param $image
163
     * @param \App\Image\Point $helmCoordinates
164
     * @return resource
165
     * @throws \App\Image\Exceptions\ImageTrueColorCreationFailedException
166
     */
167
    public function createHelmCheckImage($image, \App\Image\Point $helmCoordinates)
168
    {
169
        $helmCheckImage = $this->createTrueColorSquare(8);
170
        \imagealphablending($helmCheckImage, false);
171
        \imagesavealpha($helmCheckImage, true);
172
        \imagefilledrectangle($helmCheckImage, 0, 0, 8, 8, $this->colorAllocateAlpha($helmCheckImage));
173
        \imagecopyresampled($helmCheckImage, $image, 0, 0, $helmCoordinates->getX(), $helmCoordinates->getY(), 8, 8, 8, 8);
174
        return $helmCheckImage;
175
    }
176
}
177