DimensionService::mapDatabaseRatio()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.9666
c 0
b 0
f 0
cc 2
nc 2
nop 1
1
<?php
2
3
/**
4
 * Calc dimensions for focus point cropping.
5
 */
6
7
namespace HDNET\Focuspoint\Service;
8
9
use HDNET\Focuspoint\Domain\Repository\DimensionRepository;
10
use TYPO3\CMS\Core\Utility\GeneralUtility;
11
use TYPO3\CMS\Core\Utility\MathUtility;
12
13
/**
14
 * Calc dimensions for focus point cropping.
15
 */
16
class DimensionService extends AbstractService
17
{
18
    /**
19
     * Crop mode. Not necessary.
20
     */
21
    const CROP_NONE = 0;
22
23
    /**
24
     * Crop in landscape.
25
     */
26
    const CROP_LANDSCAPE = 1;
27
28
    /**
29
     * Crop in portrait.
30
     */
31
    const CROP_PORTRAIT = 2;
32
33
    /**
34
     * Calc the ratio and check if the crop is landscape or portrait relevant.
35
     *
36
     * @param int    $width
37
     * @param int    $height
38
     * @param string $ratio  In format "1:1"
39
     *
40
     * @return int
41
     */
42
    public function getCropMode(int $width, int $height, string $ratio): int
43
    {
44
        $ratio = $this->getRatio($ratio);
45
        $widthDiff = $width / $ratio[0];
46
        $heightDiff = $height / $ratio[1];
47
        if ($widthDiff === $heightDiff) {
48
            return self::CROP_NONE;
49
        }
50
51
        return $widthDiff > $heightDiff ? self::CROP_LANDSCAPE : self::CROP_PORTRAIT;
52
    }
53
54
    /**
55
     * Calc the focus width and height by the given ratio.
56
     *
57
     * @param int    $width
58
     * @param int    $height
59
     * @param string $ratio  In format "1:1"
60
     *
61
     * @return array
62
     */
63
    public function getFocusWidthAndHeight(int $width, int $height, string $ratio): array
64
    {
65
        $width = (int) $width;
66
        $height = (int) $height;
67
        $ratio = $this->getRatio($ratio);
68
        $widthDiff = $width / $ratio[0];
69
        $heightDiff = $height / $ratio[1];
70
71
        if ($widthDiff < $heightDiff) {
72
            return [
73
                $width,
74
                (int) \ceil($widthDiff / $heightDiff * $height),
75
            ];
76
        }
77
        if ($widthDiff > $heightDiff) {
78
            return [
79
                (int) \ceil($heightDiff / $widthDiff * $width),
80
                $height,
81
            ];
82
        }
83
84
        return [
85
            $width,
86
            $height,
87
        ];
88
    }
89
90
    /**
91
     * Calculate the source X and Y position.
92
     *
93
     * @param int $cropMode
94
     * @param int $width
95
     * @param int $height
96
     * @param int $focusWidth
97
     * @param int $focusHeight
98
     * @param int $focusPointX
99
     * @param int $focusPointY
100
     *
101
     * @return array
102
     */
103
    public function calculateSourcePosition(
104
        int $cropMode,
105
        int $width,
106
        int $height,
107
        int $focusWidth,
108
        int $focusHeight,
109
        int $focusPointX,
110
        int $focusPointY
111
    ): array {
112
        if (self::CROP_PORTRAIT === $cropMode) {
113
            return \array_reverse($this->getShiftedFocusAreaPosition($height, $focusHeight, $focusPointY, true));
114
        }
115
        if (self::CROP_LANDSCAPE === $cropMode) {
116
            return $this->getShiftedFocusAreaPosition($width, $focusWidth, $focusPointX);
117
        }
118
119
        return [
120
            0,
121
            0,
122
        ];
123
    }
124
125
    /**
126
     * get the shifted focus point point position
127
     * for e.g. Frontend handling of the new created image.
128
     *
129
     * @param int    $imgWidth
130
     * @param int    $imgHeight
131
     * @param int    $focusX
132
     * @param int    $focusY
133
     * @param string $ratio
134
     *
135
     * @return array
136
     */
137
    public function getShiftedFocusPointPosition(int $imgWidth, int $imgHeight, int $focusX, int $focusY, string $ratio): array
138
    {
139
        $halfWidth = $imgWidth / 2;
140
        $halfHeight = $imgHeight / 2;
141
142
        $realFocusX = $halfWidth + ($focusX / 100 * $halfWidth);
143
        $realFocusY = $halfHeight - ($focusY / 100 * $halfHeight);
144
145
        list($focusWidth, $focusHeight) = $this->getFocusWidthAndHeight($imgWidth, $imgHeight, $ratio);
146
147
        list($sourceX, $sourceY) = $this->calculateSourcePosition(
148
            $imgWidth,
149
            $imgHeight,
150
            $focusWidth,
151
            $focusHeight,
152
            $focusX,
153
            $focusY,
154
            $ratio
155
        );
156
157
        $newHalfWidth = $focusWidth / 2;
158
        $newHalfHeight = $focusHeight / 2;
159
160
        $newRealFocusX = $realFocusX - $sourceX;
161
        $newRealFocusY = $realFocusY - $sourceY;
162
163
        $newFocusX = ($newRealFocusX - $newHalfWidth) * 100 / ($newHalfWidth);
164
        $newFocusY = ($newHalfHeight - $newRealFocusY) * 100 / ($newHalfHeight);
165
        $newFocusX = (int) \round($newFocusX, 0);
166
        $newFocusY = (int) \round($newFocusY, 0);
167
168
        return [$newFocusX, $newFocusY];
169
    }
170
171
    /**
172
     * Check the ratio and create an array.
173
     *
174
     * @param string $ratio
175
     *
176
     * @throws \Exception
177
     *
178
     * @return array
179
     */
180
    public function getRatio(string $ratio): array
181
    {
182
        $ratio = $this->mapDatabaseRatio($ratio);
183
        $ratio = \explode(':', $ratio);
184
        if (2 !== \count($ratio)) {
185
            throw new \Exception('Ratio have to be in the format of e.g. "1:1" or "16:9"', 1475144026);
186
        }
187
188
        return [
189
            MathUtility::canBeInterpretedAsInteger($ratio[0]) ? (int) $ratio[0] : (float) \str_replace(
190
                ',',
191
                '.',
192
                $ratio[0]
193
            ),
194
            MathUtility::canBeInterpretedAsInteger($ratio[1]) ? (int) $ratio[1] : (float) \str_replace(
195
                ',',
196
                '.',
197
                $ratio[1]
198
            ),
199
        ];
200
    }
201
202
    /**
203
     * Calc the shifted focus area.
204
     *
205
     * @param int  $length
206
     * @param int  $focusLength
207
     * @param int  $focusPosition
208
     * @param bool $invertScala
209
     *
210
     * @return array
211
     */
212
    protected function getShiftedFocusAreaPosition(int $length, int $focusLength, int $focusPosition, bool $invertScala = false): array
213
    {
214
        $halfWidth = $length / 2;
215
        $pixelPosition = (int) \floor($halfWidth * $focusPosition / 100 + $halfWidth);
216
        if ($invertScala) {
217
            $pixelPosition = $length - $pixelPosition;
218
        }
219
        $crop1 = (int) ($pixelPosition - \floor($focusLength / 2));
220
        $crop2 = (int) ($crop1 + $focusLength);
221
        if ($crop1 < 0) {
222
            $crop1 -= $crop1;
223
        } elseif ($crop2 > $length) {
224
            $diff = $crop2 - $length;
225
            $crop1 -= $diff;
226
        }
227
228
        $sourceX = $crop1;
229
        $sourceY = 0;
230
231
        return [
232
            $sourceX,
233
            $sourceY,
234
        ];
235
    }
236
237
    /**
238
     * Map the given ratio to the DB table.
239
     *
240
     * @param string $ratio
241
     *
242
     * @return string
243
     */
244
    protected function mapDatabaseRatio(string $ratio): string
245
    {
246
        $row = GeneralUtility::makeInstance(DimensionRepository::class)->findOneByIdentifier($ratio);
247
        if (isset($row['dimension'])) {
248
            return (string) $row['dimension'];
249
        }
250
251
        return $ratio;
252
    }
253
}
254