BilinearReader::mix()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 23
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 20
nc 2
nop 1
dl 0
loc 23
rs 9.0856
c 0
b 0
f 0
1
<?php
2
3
namespace AmaTeam\Image\Projection\Type;
4
5
use AmaTeam\Image\Projection\API\SpecificationInterface;
6
use AmaTeam\Image\Projection\API\Tile\TileInterface;
7
use AmaTeam\Image\Projection\API\Type\MappingInterface;
8
use AmaTeam\Image\Projection\API\Type\ReaderInterface;
9
10
class BilinearReader implements ReaderInterface
11
{
12
    /**
13
     * @var MappingInterface
14
     */
15
    private $mapping;
16
    /**
17
     * @var TileInterface[][][]
18
     */
19
    private $tiles;
20
    /**
21
     * @var int
22
     */
23
    private $height;
24
    /**
25
     * @var int
26
     */
27
    private $width;
28
    /**
29
     * @var int
30
     */
31
    private $tileWidth;
32
    /**
33
     * @var int
34
     */
35
    private $tileHeight;
36
37
    /**
38
     * @param SpecificationInterface $specification
39
     * @param MappingInterface $mapping
40
     * @param TileInterface[][][] $tiles
41
     */
42
    public function __construct(
43
        SpecificationInterface $specification,
44
        MappingInterface $mapping,
45
        array $tiles
46
    ) {
47
        $this->mapping = $mapping;
48
        $this->tiles = $tiles;
49
        $this->width = $specification->getSize()->getWidth();
50
        $this->height = $specification->getSize()->getHeight();
51
        $this->tileWidth = $specification->getTileSize()->getWidth();
52
        $this->tileHeight = $specification->getTileSize()->getHeight();
53
    }
54
55
    /**
56
     * @inheritDoc
57
     */
58
    public function getColorAt($latitude, $longitude)
59
    {
60
        $position = $this
61
            ->mapping
62
            ->getPosition($latitude, $longitude);
63
        return self::mix($this->getColorsAt($position[0], $position[1], $position[2]));
64
    }
65
66
    /**
67
     * This method samples texel this specific point belongs to, as
68
     * well as three closest to it. After all texels have been sampled,
69
     * each is mixed in according to it's weight (proximity to UV
70
     * point).
71
     *
72
     * @param string|int $face
73
     * @param float $u
74
     * @param float $v
75
     * @return array
76
     */
77
    private function getColorsAt($face, $u, $v)
78
    {
79
        $colors = [];
80
        $normalizedU = $u * $this->width - 0.5;
81
        $normalizedV = $v * $this->height - 0.5;
82
        $x1 = (int) $normalizedU;
83
        $y1 = (int) $normalizedV;
84
        $x2 = $x1 + 1;
85
        $y2 = $y1 + 1;
86
        foreach ([$x1, $x2] as $x) {
87
            foreach ([$y1, $y2] as $y) {
88
                if ($x < 0 || $x >= $this->width || $y < 0 || $y >= $this->height) {
89
                    continue;
90
                }
91
                $column = (int) ($x / $this->tileWidth);
92
                $row = (int) ($y / $this->tileHeight);
93
                $tileX = $x % $this->tileWidth;
94
                $tileY = $y % $this->tileHeight;
95
                $color = $this
96
                    ->tiles[$face][$row][$column]
97
                    ->getImage()
98
                    ->getColorAt($tileX, $tileY);
99
                $diffX = $x - $normalizedU;
100
                $diffY = $y - $normalizedV;
101
                $weight = 1 / sqrt($diffY * $diffY + $diffX * $diffX);
102
                $colors[] = [$color, $weight];
103
            }
104
        }
105
        return $colors;
106
    }
107
108
    private static function mix(array $colors)
109
    {
110
        $red = 0;
111
        $green = 0;
112
        $blue = 0;
113
        $alpha = 0;
114
        $weight = 0;
115
        foreach ($colors as $source) {
116
            $sourceRed = ($source[0] >> 24) & 0xFF;
117
            $sourceGreen = ($source[0] >> 16) & 0xFF;
118
            $sourceBlue = ($source[0] >> 8) & 0xFF;
119
            $sourceAlpha = $source[0] & 0xFF;
120
            $weight += $source[1];
121
            $red += $sourceRed * $source[1];
122
            $green += $sourceGreen * $source[1];
123
            $blue += $sourceBlue * $source[1];
124
            $alpha += $sourceAlpha * $source[1];
125
        }
126
        $red /= $weight;
127
        $blue /= $weight;
128
        $green /= $weight;
129
        $alpha /= $weight;
130
        return ((int) $red) << 24 | ((int) $green) << 16 | ((int) $blue) << 8 | ((int) $alpha);
131
    }
132
}
133