master
ブランチ master
LittleBeast / src / Std / Lib / Image / Vp8.php
74.4 KB、1989行 生 Blame 履歴
1<?php
2/*************************************************************
3# 076 License
4
5Copyright (c) テクニカル諏訪子
6
7Permission is hereby granted to any person obtaining a copy of the software
8Little Beast (the "Software") to use, modify, merge, copy, publish, distribute,
9sublicense, and/or sell copies of the Software, subject to the following conditions:
10
11 1. **Origin Attribution**:
12 - You must not misrepresent the origin of the Software; you must not claim
13 you created the original Software.
14 - If the Software is used in a product, you must either:
15 a. Provide clear attribution in the product's documentation, user interface,
16 or other visible areas, **OR**
17 b. Pay the original developers a fee they specify in writing.
18 2. **Usage Restriction**:
19 - The Software, or any derivative works, dependencies, or libraries
20 incorporating it, must not be used for censorship or to suppress freedom of
21 speech, expression, or creativity. Prohibited uses include, but are not
22 limited to:
23 - Censorship of so-called "hate speech", visuals, non-mainstream opinions,
24 ideas, or objective reality.
25 - Tools or systems designed to restrict access to information or
26 artistic works.
27 3. **Notice Preservation**:
28 - This license and the above copyright notice must remain intact in all copies
29 of the source code.
30
31THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
32INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
33PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
34HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
35CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
36OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
37 */
38namespace Std\Lib\Image;
39
40/**
41 * WebPの非可逆圧縮(VP8のキーフレーム、RFC 6386)
42 *
43 * 読み込み:セグメント、全ての予測モード、複数のパーティション、ループフィルター(通常・簡易)。
44 * 色はlibwebpと同じく、色差を滑らかに拡大(fancy upsampling)してRGBにする。
45 * 書き出し:16x16と4x4の予測を選び、係数の確率を画像に合わせて更新し、通常のループフィルターを掛ける。
46 */
47final class Vp8 {
48 private const DC_TABLE = [
49 4, 5, 6, 7, 8, 9, 10, 10, 11, 12, 13, 14, 15, 16, 17, 17,
50 18, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 25, 25, 26, 27, 28,
51 29, 30, 31, 32, 33, 34, 35, 36, 37, 37, 38, 39, 40, 41, 42, 43,
52 44, 45, 46, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
53 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
54 75, 76, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
55 91, 93, 95, 96, 98, 100, 101, 102, 104, 106, 108, 110, 112, 114, 116, 118,
56 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 143, 145, 148, 151, 154, 157,
57 ];
58 private const AC_TABLE = [
59 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
60 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
61 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
62 52, 53, 54, 55, 56, 57, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76,
63 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108,
64 110, 112, 114, 116, 119, 122, 125, 128, 131, 134, 137, 140, 143, 146, 149, 152,
65 155, 158, 161, 164, 167, 170, 173, 177, 181, 185, 189, 193, 197, 201, 205, 209,
66 213, 217, 221, 225, 229, 234, 239, 245, 249, 254, 259, 264, 269, 274, 279, 284,
67 ];
68 /** 係数の確率の既定値 [種類][帯][文脈][11] */
69 private const COEFF_PROBS = [
70 [
71 [
72 [128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128],
73 [128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128],
74 [128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128],
75 ],
76 [
77 [253, 136, 254, 255, 228, 219, 128, 128, 128, 128, 128],
78 [189, 129, 242, 255, 227, 213, 255, 219, 128, 128, 128],
79 [106, 126, 227, 252, 214, 209, 255, 255, 128, 128, 128],
80 ],
81 [
82 [1, 98, 248, 255, 236, 226, 255, 255, 128, 128, 128],
83 [181, 133, 238, 254, 221, 234, 255, 154, 128, 128, 128],
84 [78, 134, 202, 247, 198, 180, 255, 219, 128, 128, 128],
85 ],
86 [
87 [1, 185, 249, 255, 243, 255, 128, 128, 128, 128, 128],
88 [184, 150, 247, 255, 236, 224, 128, 128, 128, 128, 128],
89 [77, 110, 216, 255, 236, 230, 128, 128, 128, 128, 128],
90 ],
91 [
92 [1, 101, 251, 255, 241, 255, 128, 128, 128, 128, 128],
93 [170, 139, 241, 252, 236, 209, 255, 255, 128, 128, 128],
94 [37, 116, 196, 243, 228, 255, 255, 255, 128, 128, 128],
95 ],
96 [
97 [1, 204, 254, 255, 245, 255, 128, 128, 128, 128, 128],
98 [207, 160, 250, 255, 238, 128, 128, 128, 128, 128, 128],
99 [102, 103, 231, 255, 211, 171, 128, 128, 128, 128, 128],
100 ],
101 [
102 [1, 152, 252, 255, 240, 255, 128, 128, 128, 128, 128],
103 [177, 135, 243, 255, 234, 225, 128, 128, 128, 128, 128],
104 [80, 129, 211, 255, 194, 224, 128, 128, 128, 128, 128],
105 ],
106 [
107 [1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128],
108 [246, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128],
109 [255, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128],
110 ],
111 ],
112 [
113 [
114 [198, 35, 237, 223, 193, 187, 162, 160, 145, 155, 62],
115 [131, 45, 198, 221, 172, 176, 220, 157, 252, 221, 1],
116 [68, 47, 146, 208, 149, 167, 221, 162, 255, 223, 128],
117 ],
118 [
119 [1, 149, 241, 255, 221, 224, 255, 255, 128, 128, 128],
120 [184, 141, 234, 253, 222, 220, 255, 199, 128, 128, 128],
121 [81, 99, 181, 242, 176, 190, 249, 202, 255, 255, 128],
122 ],
123 [
124 [1, 129, 232, 253, 214, 197, 242, 196, 255, 255, 128],
125 [99, 121, 210, 250, 201, 198, 255, 202, 128, 128, 128],
126 [23, 91, 163, 242, 170, 187, 247, 210, 255, 255, 128],
127 ],
128 [
129 [1, 200, 246, 255, 234, 255, 128, 128, 128, 128, 128],
130 [109, 178, 241, 255, 231, 245, 255, 255, 128, 128, 128],
131 [44, 130, 201, 253, 205, 192, 255, 255, 128, 128, 128],
132 ],
133 [
134 [1, 132, 239, 251, 219, 209, 255, 165, 128, 128, 128],
135 [94, 136, 225, 251, 218, 190, 255, 255, 128, 128, 128],
136 [22, 100, 174, 245, 186, 161, 255, 199, 128, 128, 128],
137 ],
138 [
139 [1, 182, 249, 255, 232, 235, 128, 128, 128, 128, 128],
140 [124, 143, 241, 255, 227, 234, 128, 128, 128, 128, 128],
141 [35, 77, 181, 251, 193, 211, 255, 205, 128, 128, 128],
142 ],
143 [
144 [1, 157, 247, 255, 236, 231, 255, 255, 128, 128, 128],
145 [121, 141, 235, 255, 225, 227, 255, 255, 128, 128, 128],
146 [45, 99, 188, 251, 195, 217, 255, 224, 128, 128, 128],
147 ],
148 [
149 [1, 1, 251, 255, 213, 255, 128, 128, 128, 128, 128],
150 [203, 1, 248, 255, 255, 128, 128, 128, 128, 128, 128],
151 [137, 1, 177, 255, 224, 255, 128, 128, 128, 128, 128],
152 ],
153 ],
154 [
155 [
156 [253, 9, 248, 251, 207, 208, 255, 192, 128, 128, 128],
157 [175, 13, 224, 243, 193, 185, 249, 198, 255, 255, 128],
158 [73, 17, 171, 221, 161, 179, 236, 167, 255, 234, 128],
159 ],
160 [
161 [1, 95, 247, 253, 212, 183, 255, 255, 128, 128, 128],
162 [239, 90, 244, 250, 211, 209, 255, 255, 128, 128, 128],
163 [155, 77, 195, 248, 188, 195, 255, 255, 128, 128, 128],
164 ],
165 [
166 [1, 24, 239, 251, 218, 219, 255, 205, 128, 128, 128],
167 [201, 51, 219, 255, 196, 186, 128, 128, 128, 128, 128],
168 [69, 46, 190, 239, 201, 218, 255, 228, 128, 128, 128],
169 ],
170 [
171 [1, 191, 251, 255, 255, 128, 128, 128, 128, 128, 128],
172 [223, 165, 249, 255, 213, 255, 128, 128, 128, 128, 128],
173 [141, 124, 248, 255, 255, 128, 128, 128, 128, 128, 128],
174 ],
175 [
176 [1, 16, 248, 255, 255, 128, 128, 128, 128, 128, 128],
177 [190, 36, 230, 255, 236, 255, 128, 128, 128, 128, 128],
178 [149, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128],
179 ],
180 [
181 [1, 226, 255, 128, 128, 128, 128, 128, 128, 128, 128],
182 [247, 192, 255, 128, 128, 128, 128, 128, 128, 128, 128],
183 [240, 128, 255, 128, 128, 128, 128, 128, 128, 128, 128],
184 ],
185 [
186 [1, 134, 252, 255, 255, 128, 128, 128, 128, 128, 128],
187 [213, 62, 250, 255, 255, 128, 128, 128, 128, 128, 128],
188 [55, 93, 255, 128, 128, 128, 128, 128, 128, 128, 128],
189 ],
190 [
191 [128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128],
192 [128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128],
193 [128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128],
194 ],
195 ],
196 [
197 [
198 [202, 24, 213, 235, 186, 191, 220, 160, 240, 175, 255],
199 [126, 38, 182, 232, 169, 184, 228, 174, 255, 187, 128],
200 [61, 46, 138, 219, 151, 178, 240, 170, 255, 216, 128],
201 ],
202 [
203 [1, 112, 230, 250, 199, 191, 247, 159, 255, 255, 128],
204 [166, 109, 228, 252, 211, 215, 255, 174, 128, 128, 128],
205 [39, 77, 162, 232, 172, 180, 245, 178, 255, 255, 128],
206 ],
207 [
208 [1, 52, 220, 246, 198, 199, 249, 220, 255, 255, 128],
209 [124, 74, 191, 243, 183, 193, 250, 221, 255, 255, 128],
210 [24, 71, 130, 219, 154, 170, 243, 182, 255, 255, 128],
211 ],
212 [
213 [1, 182, 225, 249, 219, 240, 255, 224, 128, 128, 128],
214 [149, 150, 226, 252, 216, 205, 255, 171, 128, 128, 128],
215 [28, 108, 170, 242, 183, 194, 254, 223, 255, 255, 128],
216 ],
217 [
218 [1, 81, 230, 252, 204, 203, 255, 192, 128, 128, 128],
219 [123, 102, 209, 247, 188, 196, 255, 233, 128, 128, 128],
220 [20, 95, 153, 243, 164, 173, 255, 203, 128, 128, 128],
221 ],
222 [
223 [1, 222, 248, 255, 216, 213, 128, 128, 128, 128, 128],
224 [168, 175, 246, 252, 235, 205, 255, 255, 128, 128, 128],
225 [47, 116, 215, 255, 211, 212, 255, 255, 128, 128, 128],
226 ],
227 [
228 [1, 121, 236, 253, 212, 214, 255, 255, 128, 128, 128],
229 [141, 84, 213, 252, 201, 202, 255, 219, 128, 128, 128],
230 [42, 80, 160, 240, 162, 185, 255, 205, 128, 128, 128],
231 ],
232 [
233 [1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128],
234 [244, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128],
235 [238, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128],
236 ],
237 ],
238 ];
239 /** 係数の確率を更新する確率 */
240 private const COEFF_UPDATE_PROBS = [
241 [
242 [
243 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
244 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
245 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
246 ],
247 [
248 [176, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255],
249 [223, 241, 252, 255, 255, 255, 255, 255, 255, 255, 255],
250 [249, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255],
251 ],
252 [
253 [255, 244, 252, 255, 255, 255, 255, 255, 255, 255, 255],
254 [234, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255],
255 [253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
256 ],
257 [
258 [255, 246, 254, 255, 255, 255, 255, 255, 255, 255, 255],
259 [239, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255],
260 [254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255],
261 ],
262 [
263 [255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255],
264 [251, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255],
265 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
266 ],
267 [
268 [255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255],
269 [251, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255],
270 [254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255],
271 ],
272 [
273 [255, 254, 253, 255, 254, 255, 255, 255, 255, 255, 255],
274 [250, 255, 254, 255, 254, 255, 255, 255, 255, 255, 255],
275 [254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
276 ],
277 [
278 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
279 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
280 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
281 ],
282 ],
283 [
284 [
285 [217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
286 [225, 252, 241, 253, 255, 255, 254, 255, 255, 255, 255],
287 [234, 250, 241, 250, 253, 255, 253, 254, 255, 255, 255],
288 ],
289 [
290 [255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255],
291 [223, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255],
292 [238, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255],
293 ],
294 [
295 [255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255],
296 [249, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255],
297 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
298 ],
299 [
300 [255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255],
301 [247, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255],
302 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
303 ],
304 [
305 [255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255],
306 [252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
307 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
308 ],
309 [
310 [255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255],
311 [253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
312 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
313 ],
314 [
315 [255, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255],
316 [250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
317 [254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
318 ],
319 [
320 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
321 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
322 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
323 ],
324 ],
325 [
326 [
327 [186, 251, 250, 255, 255, 255, 255, 255, 255, 255, 255],
328 [234, 251, 244, 254, 255, 255, 255, 255, 255, 255, 255],
329 [251, 251, 243, 253, 254, 255, 254, 255, 255, 255, 255],
330 ],
331 [
332 [255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255],
333 [236, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255],
334 [251, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255],
335 ],
336 [
337 [255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255],
338 [254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255],
339 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
340 ],
341 [
342 [255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255],
343 [254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255],
344 [254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
345 ],
346 [
347 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
348 [254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
349 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
350 ],
351 [
352 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
353 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
354 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
355 ],
356 [
357 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
358 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
359 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
360 ],
361 [
362 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
363 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
364 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
365 ],
366 ],
367 [
368 [
369 [248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
370 [250, 254, 252, 254, 255, 255, 255, 255, 255, 255, 255],
371 [248, 254, 249, 253, 255, 255, 255, 255, 255, 255, 255],
372 ],
373 [
374 [255, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255],
375 [246, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255],
376 [252, 254, 251, 254, 254, 255, 255, 255, 255, 255, 255],
377 ],
378 [
379 [255, 254, 252, 255, 255, 255, 255, 255, 255, 255, 255],
380 [248, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255],
381 [253, 255, 254, 254, 255, 255, 255, 255, 255, 255, 255],
382 ],
383 [
384 [255, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255],
385 [245, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255],
386 [253, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255],
387 ],
388 [
389 [255, 251, 253, 255, 255, 255, 255, 255, 255, 255, 255],
390 [252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255],
391 [255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255],
392 ],
393 [
394 [255, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255],
395 [249, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255],
396 [255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255],
397 ],
398 [
399 [255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255],
400 [250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
401 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
402 ],
403 [
404 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
405 [254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
406 [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255],
407 ],
408 ],
409 ];
410 /** 4x4の予測モードの確率 [上][左][9] */
411 private const BMODE_PROBS = [
412 [
413 [231, 120, 48, 89, 115, 113, 120, 152, 112],
414 [152, 179, 64, 126, 170, 118, 46, 70, 95],
415 [175, 69, 143, 80, 85, 82, 72, 155, 103],
416 [56, 58, 10, 171, 218, 189, 17, 13, 152],
417 [114, 26, 17, 163, 44, 195, 21, 10, 173],
418 [121, 24, 80, 195, 26, 62, 44, 64, 85],
419 [144, 71, 10, 38, 171, 213, 144, 34, 26],
420 [170, 46, 55, 19, 136, 160, 33, 206, 71],
421 [63, 20, 8, 114, 114, 208, 12, 9, 226],
422 [81, 40, 11, 96, 182, 84, 29, 16, 36],
423 ],
424 [
425 [134, 183, 89, 137, 98, 101, 106, 165, 148],
426 [72, 187, 100, 130, 157, 111, 32, 75, 80],
427 [66, 102, 167, 99, 74, 62, 40, 234, 128],
428 [41, 53, 9, 178, 241, 141, 26, 8, 107],
429 [74, 43, 26, 146, 73, 166, 49, 23, 157],
430 [65, 38, 105, 160, 51, 52, 31, 115, 128],
431 [104, 79, 12, 27, 217, 255, 87, 17, 7],
432 [87, 68, 71, 44, 114, 51, 15, 186, 23],
433 [47, 41, 14, 110, 182, 183, 21, 17, 194],
434 [66, 45, 25, 102, 197, 189, 23, 18, 22],
435 ],
436 [
437 [88, 88, 147, 150, 42, 46, 45, 196, 205],
438 [43, 97, 183, 117, 85, 38, 35, 179, 61],
439 [39, 53, 200, 87, 26, 21, 43, 232, 171],
440 [56, 34, 51, 104, 114, 102, 29, 93, 77],
441 [39, 28, 85, 171, 58, 165, 90, 98, 64],
442 [34, 22, 116, 206, 23, 34, 43, 166, 73],
443 [107, 54, 32, 26, 51, 1, 81, 43, 31],
444 [68, 25, 106, 22, 64, 171, 36, 225, 114],
445 [34, 19, 21, 102, 132, 188, 16, 76, 124],
446 [62, 18, 78, 95, 85, 57, 50, 48, 51],
447 ],
448 [
449 [193, 101, 35, 159, 215, 111, 89, 46, 111],
450 [60, 148, 31, 172, 219, 228, 21, 18, 111],
451 [112, 113, 77, 85, 179, 255, 38, 120, 114],
452 [40, 42, 1, 196, 245, 209, 10, 25, 109],
453 [88, 43, 29, 140, 166, 213, 37, 43, 154],
454 [61, 63, 30, 155, 67, 45, 68, 1, 209],
455 [100, 80, 8, 43, 154, 1, 51, 26, 71],
456 [142, 78, 78, 16, 255, 128, 34, 197, 171],
457 [41, 40, 5, 102, 211, 183, 4, 1, 221],
458 [51, 50, 17, 168, 209, 192, 23, 25, 82],
459 ],
460 [
461 [138, 31, 36, 171, 27, 166, 38, 44, 229],
462 [67, 87, 58, 169, 82, 115, 26, 59, 179],
463 [63, 59, 90, 180, 59, 166, 93, 73, 154],
464 [40, 40, 21, 116, 143, 209, 34, 39, 175],
465 [47, 15, 16, 183, 34, 223, 49, 45, 183],
466 [46, 17, 33, 183, 6, 98, 15, 32, 183],
467 [57, 46, 22, 24, 128, 1, 54, 17, 37],
468 [65, 32, 73, 115, 28, 128, 23, 128, 205],
469 [40, 3, 9, 115, 51, 192, 18, 6, 223],
470 [87, 37, 9, 115, 59, 77, 64, 21, 47],
471 ],
472 [
473 [104, 55, 44, 218, 9, 54, 53, 130, 226],
474 [64, 90, 70, 205, 40, 41, 23, 26, 57],
475 [54, 57, 112, 184, 5, 41, 38, 166, 213],
476 [30, 34, 26, 133, 152, 116, 10, 32, 134],
477 [39, 19, 53, 221, 26, 114, 32, 73, 255],
478 [31, 9, 65, 234, 2, 15, 1, 118, 73],
479 [75, 32, 12, 51, 192, 255, 160, 43, 51],
480 [88, 31, 35, 67, 102, 85, 55, 186, 85],
481 [56, 21, 23, 111, 59, 205, 45, 37, 192],
482 [55, 38, 70, 124, 73, 102, 1, 34, 98],
483 ],
484 [
485 [125, 98, 42, 88, 104, 85, 117, 175, 82],
486 [95, 84, 53, 89, 128, 100, 113, 101, 45],
487 [75, 79, 123, 47, 51, 128, 81, 171, 1],
488 [57, 17, 5, 71, 102, 57, 53, 41, 49],
489 [38, 33, 13, 121, 57, 73, 26, 1, 85],
490 [41, 10, 67, 138, 77, 110, 90, 47, 114],
491 [115, 21, 2, 10, 102, 255, 166, 23, 6],
492 [101, 29, 16, 10, 85, 128, 101, 196, 26],
493 [57, 18, 10, 102, 102, 213, 34, 20, 43],
494 [117, 20, 15, 36, 163, 128, 68, 1, 26],
495 ],
496 [
497 [102, 61, 71, 37, 34, 53, 31, 243, 192],
498 [69, 60, 71, 38, 73, 119, 28, 222, 37],
499 [68, 45, 128, 34, 1, 47, 11, 245, 171],
500 [62, 17, 19, 70, 146, 85, 55, 62, 70],
501 [37, 43, 37, 154, 100, 163, 85, 160, 1],
502 [63, 9, 92, 136, 28, 64, 32, 201, 85],
503 [75, 15, 9, 9, 64, 255, 184, 119, 16],
504 [86, 6, 28, 5, 64, 255, 25, 248, 1],
505 [56, 8, 17, 132, 137, 255, 55, 116, 128],
506 [58, 15, 20, 82, 135, 57, 26, 121, 40],
507 ],
508 [
509 [164, 50, 31, 137, 154, 133, 25, 35, 218],
510 [51, 103, 44, 131, 131, 123, 31, 6, 158],
511 [86, 40, 64, 135, 148, 224, 45, 183, 128],
512 [22, 26, 17, 131, 240, 154, 14, 1, 209],
513 [45, 16, 21, 91, 64, 222, 7, 1, 197],
514 [56, 21, 39, 155, 60, 138, 23, 102, 213],
515 [83, 12, 13, 54, 192, 255, 68, 47, 28],
516 [85, 26, 85, 85, 128, 128, 32, 146, 171],
517 [18, 11, 7, 63, 144, 171, 4, 4, 246],
518 [35, 27, 10, 146, 174, 171, 12, 26, 128],
519 ],
520 [
521 [190, 80, 35, 99, 180, 80, 126, 54, 45],
522 [85, 126, 47, 87, 176, 51, 41, 20, 32],
523 [101, 75, 128, 139, 118, 146, 116, 128, 85],
524 [56, 41, 15, 176, 236, 85, 37, 9, 62],
525 [71, 30, 17, 119, 118, 255, 17, 18, 138],
526 [101, 38, 60, 138, 55, 70, 43, 26, 142],
527 [146, 36, 19, 30, 171, 255, 97, 27, 20],
528 [138, 45, 61, 62, 219, 1, 81, 188, 64],
529 [32, 41, 20, 117, 151, 142, 20, 21, 163],
530 [112, 19, 12, 61, 195, 128, 48, 4, 24],
531 ],
532 ];
533 private const ZIGZAG = [0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15];
534 private const BANDS = [0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 0];
535 /** 大きい係数の追加ビットの確率(DCT_CAT3~6) */
536 private const CAT = [[173, 148, 140], [176, 155, 140, 135], [180, 157, 141, 134, 130], [254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129]];
537 /** 4x4の予測モードの木(0:DC、1:TM、2:VE、3:HE、4:RD、5:VR、6:LD、7:VL、8:HD、9:HU) */
538 private const BMODE_TREE = [0, 1, -1, 2, -2, 3, 4, 6, -3, 5, -4, -5, -6, 7, -7, 8, -8, -9];
539 private const DC = 0;
540 private const TM = 1;
541 private const VE = 2;
542 private const HE = 3;
543 /** 作業用の配列の1行(左に1、右に4画素の余白) */
544 private const BPS = 32;
545
546 // 算術復号
547 private string $d = '';
548 private int $p = 0;
549 private int $value = 0;
550 private int $count = -8;
551 private int $range = 255;
552 /** 正規化のシフト量 */
553 private array $norm = [];
554
555 private array $probs = [];
556
557 public function __construct() {
558 for ($r = 1; $r < 128; $r++) {
559 $s = 0;
560 while (($r << $s) < 128) $s++;
561 $this->norm[$r] = $s;
562 }
563 }
564
565 //////// 読み込み
566
567 /**
568 * VP8チャンクの中身を読む
569 *
570 * @return array [幅, 丈, RGBA(アルファは255)]
571 */
572 public function decode(string $data): array {
573 $len = strlen($data);
574 if ($len < 10) throw new \RuntimeException('VP8のデータが短過ぎます。');
575 $tag = ord($data[0]) | (ord($data[1]) << 8) | (ord($data[2]) << 16);
576 if ($tag & 1) throw new \RuntimeException('VP8のキーフレームではありません。');
577 if ((($tag >> 1) & 7) > 3) throw new \RuntimeException('未対応のVP8の版です。');
578 $part = $tag >> 5;
579 if (substr($data, 3, 3) !== "\x9d\x01\x2a") throw new \RuntimeException('VP8の開始コードがありません。');
580 $w = unpack('v', substr($data, 6, 2))[1] & 0x3fff;
581 $h = unpack('v', substr($data, 8, 2))[1] & 0x3fff;
582 Bitmap::check($w, $h);
583 if (10 + $part > $len) throw new \RuntimeException('VP8のデータが途中で切れています。');
584 $this->init(substr($data, 10, $part), 0);
585
586 // フレームのヘッダー
587 $this->value(1); // 色空間
588 $this->value(1); // クランプ
589 $seg = ['use' => $this->value(1), 'map' => 0, 'abs' => 0, 'q' => [0, 0, 0, 0], 'lf' => [0, 0, 0, 0], 'probs' => [255, 255, 255]];
590 if ($seg['use']) {
591 $seg['map'] = $this->value(1);
592 if ($this->value(1)) {
593 $seg['abs'] = $this->value(1);
594 for ($s = 0; $s < 4; $s++) $seg['q'][$s] = $this->value(1) ? $this->signed(7) : 0;
595 for ($s = 0; $s < 4; $s++) $seg['lf'][$s] = $this->value(1) ? $this->signed(6) : 0;
596 }
597 if ($seg['map']) for ($s = 0; $s < 3; $s++) $seg['probs'][$s] = $this->value(1) ? $this->value(8) : 255;
598 }
599 $simple = $this->value(1);
600 $level = $this->value(6);
601 $sharp = $this->value(3);
602 $refDelta = $modeDelta = 0;
603 $useDelta = $this->value(1);
604 if ($useDelta && $this->value(1)) {
605 // 参照フレームとモード毎の差(キーフレームでは「現在のフレーム」と「B_PRED」だけ使う)
606 for ($i = 0; $i < 4; $i++) if ($this->value(1)) { $v = $this->signed(6); if ($i === 0) $refDelta = $v; }
607 for ($i = 0; $i < 4; $i++) if ($this->value(1)) { $v = $this->signed(6); if ($i === 0) $modeDelta = $v; }
608 }
609 $filterType = $level === 0 ? 0 : ($simple ? 1 : 2);
610 $numParts = 1 << $this->value(2);
611
612 // 量子化
613 $base = $this->value(7);
614 $dq = [];
615 for ($i = 0; $i < 5; $i++) $dq[$i] = $this->value(1) ? $this->signed(4) : 0;
616 $quant = [];
617 for ($s = 0; $s < 4; $s++) {
618 $q = $seg['use'] ? $seg['q'][$s] + ($seg['abs'] ? 0 : $base) : $base;
619 $c = fn(int $v, int $max = 127): int => $v < 0 ? 0 : ($v > $max ? $max : $v);
620 $quant[$s] = [
621 self::DC_TABLE[$c($q + $dq[0])], self::AC_TABLE[$c($q)],
622 self::DC_TABLE[$c($q + $dq[1])] * 2, max(8, (self::AC_TABLE[$c($q + $dq[2])] * 101581) >> 16),
623 self::DC_TABLE[$c($q + $dq[3], 117)], self::AC_TABLE[$c($q + $dq[4])],
624 ];
625 }
626 $this->value(1); // 確率を保存するか(静止画では関係無い)
627 $this->probs = self::COEFF_PROBS;
628 for ($t = 0; $t < 4; $t++) for ($b = 0; $b < 8; $b++) for ($c = 0; $c < 3; $c++) for ($i = 0; $i < 11; $i++) {
629 if ($this->bit(self::COEFF_UPDATE_PROBS[$t][$b][$c][$i])) $this->probs[$t][$b][$c][$i] = $this->value(8);
630 }
631 $useSkip = $this->value(1);
632 $skipProb = $useSkip ? $this->value(8) : 0;
633
634 // フィルターの強さ(セグメントと4x4の予測かで変わる)
635 $fstr = [];
636 for ($s = 0; $s < 4; $s++) {
637 $lvl = $seg['use'] ? $seg['lf'][$s] + ($seg['abs'] ? 0 : $level) : $level;
638 for ($i4 = 0; $i4 < 2; $i4++) {
639 $l = $lvl;
640 if ($useDelta) $l += $refDelta + ($i4 ? $modeDelta : 0);
641 $l = $l < 0 ? 0 : ($l > 63 ? 63 : $l);
642 if ($l === 0) {
643 $fstr[$s][$i4] = null;
644 continue;
645 }
646 $il = $l;
647 if ($sharp > 0) {
648 $il >>= $sharp > 4 ? 2 : 1;
649 if ($il > 9 - $sharp) $il = 9 - $sharp;
650 }
651 if ($il < 1) $il = 1;
652 $fstr[$s][$i4] = [2 * $l + $il, $il, $l >= 40 ? 2 : ($l >= 15 ? 1 : 0)];
653 }
654 }
655
656 $mbw = ($w + 15) >> 4;
657 $mbh = ($h + 15) >> 4;
658 // 全てのマクロブロックの予測モード(最初のパーティション)
659 $mbs = [];
660 $intraT = array_fill(0, 4 * $mbw, self::DC);
661 for ($my = 0; $my < $mbh; $my++) {
662 $intraL = [self::DC, self::DC, self::DC, self::DC];
663 for ($mx = 0; $mx < $mbw; $mx++) {
664 $mb = ['seg' => 0, 'skip' => 0];
665 if ($seg['map']) $mb['seg'] = !$this->bit($seg['probs'][0]) ? $this->bit($seg['probs'][1]) : 2 + $this->bit($seg['probs'][2]);
666 if ($useSkip) $mb['skip'] = $this->bit($skipProb);
667 $mb['i4'] = !$this->bit(145);
668 if (!$mb['i4']) {
669 $ym = $this->bit(156) ? ($this->bit(128) ? self::TM : self::HE) : ($this->bit(163) ? self::VE : self::DC);
670 $mb['y'] = $ym;
671 for ($i = 0; $i < 4; $i++) $intraT[4 * $mx + $i] = $intraL[$i] = $ym;
672 } else {
673 $modes = [];
674 for ($y = 0; $y < 4; $y++) {
675 $ym = $intraL[$y];
676 for ($x = 0; $x < 4; $x++) {
677 $prob = self::BMODE_PROBS[$intraT[4 * $mx + $x]][$ym];
678 $i = self::BMODE_TREE[$this->bit($prob[0])];
679 while ($i > 0) $i = self::BMODE_TREE[2 * $i + $this->bit($prob[$i])];
680 $ym = -$i;
681 $intraT[4 * $mx + $x] = $ym;
682 $modes[] = $ym;
683 }
684 $intraL[$y] = $ym;
685 }
686 $mb['b'] = $modes;
687 }
688 $mb['uv'] = !$this->bit(142) ? self::DC : (!$this->bit(114) ? self::VE : ($this->bit(183) ? self::TM : self::HE));
689 $mbs[] = $mb;
690 }
691 }
692 if ($this->eof()) throw new \RuntimeException('VP8の最初のパーティションが途中で切れています。');
693
694 // 係数のパーティション
695 $pos = 10 + $part;
696 $sizes = $pos;
697 $pos += 3 * ($numParts - 1);
698 if ($pos > $len) throw new \RuntimeException('VP8のデータが途中で切れています。');
699 $parts = [];
700 for ($i = 0; $i < $numParts; $i++) {
701 $psize = $i < $numParts - 1 ? ord($data[$sizes + 3 * $i]) | (ord($data[$sizes + 3 * $i + 1]) << 8) | (ord($data[$sizes + 3 * $i + 2]) << 16) : $len - $pos;
702 $psize = min($psize, $len - $pos);
703 $parts[] = substr($data, $pos, $psize);
704 $pos += $psize;
705 }
706
707 $bands = [];
708 for ($t = 0; $t < 4; $t++) for ($n = 0; $n < 17; $n++) $bands[$t][$n] = $this->probs[$t][self::BANDS[$n]];
709
710 $yw = $mbw * 16;
711 $uw = $mbw * 8;
712 $Y = str_repeat("\0", $yw * $mbh * 16);
713 $U = str_repeat("\0", $uw * $mbh * 8);
714 $V = $U;
715 $filters = [];
716 $tnz = array_fill(0, $mbw, array_fill(0, 9, 0));
717 $states = [];
718 for ($my = 0; $my < $mbh; $my++) {
719 $pi = $my % $numParts;
720 if (isset($states[$pi])) $this->restore($states[$pi]);
721 else $this->init($parts[$pi], 0);
722 $lnz = array_fill(0, 9, 0);
723 for ($mx = 0; $mx < $mbw; $mx++) {
724 $mb = $mbs[$my * $mbw + $mx];
725 $q = $quant[$mb['seg']];
726 $coef = array_fill(0, 384, 0);
727 $nz = array_fill(0, 24, false);
728 $t = &$tnz[$mx];
729 if ($mb['skip']) {
730 for ($i = 0; $i < 8; $i++) $t[$i] = $lnz[$i] = 0;
731 if (!$mb['i4']) $t[8] = $lnz[8] = 0;
732 $any = false;
733 } else {
734 $any = $this->residuals($mb['i4'], $q, $bands, $t, $lnz, $coef, $nz);
735 }
736 unset($t);
737 if ($filterType) {
738 $f = $fstr[$mb['seg']][$mb['i4'] ? 1 : 0];
739 $filters[] = $f === null ? null : [...$f, $mb['i4'] || $any];
740 }
741 $this->reconstruct($mb, $mx, $my, $mbw, $coef, $nz, $Y, $U, $V);
742 }
743 if ($this->eof()) throw new \RuntimeException('VP8の係数のデータが途中で切れています。');
744 $states[$pi] = $this->save();
745 }
746
747 if ($filterType) {
748 for ($my = 0, $i = 0; $my < $mbh; $my++) {
749 for ($mx = 0; $mx < $mbw; $mx++, $i++) {
750 if ($filters[$i] === null) continue;
751 [$limit, $il, $hev, $inner] = $filters[$i];
752 if ($filterType === 1) self::simpleFilter($Y, $yw, $mx, $my, $limit, $inner);
753 else self::normalFilter($Y, $U, $V, $yw, $uw, $mx, $my, $limit, $il, $hev, $inner);
754 }
755 }
756 }
757 return [$w, $h, self::toRgba($Y, $U, $V, $yw, $uw, $w, $h)];
758 }
759
760 private function init(string $data, int $pos): void {
761 $this->d = $data;
762 $this->p = $pos;
763 $this->value = 0;
764 $this->count = -8;
765 $this->range = 255;
766 }
767
768 /** データの終わりを超えて読んだか(先読みした分は除く) */
769 private function eof(): bool {
770 return $this->p - ($this->count >> 3) > strlen($this->d) + 1;
771 }
772
773 private function save(): array {
774 return [$this->d, $this->p, $this->value, $this->count, $this->range];
775 }
776
777 private function restore(array $s): void {
778 [$this->d, $this->p, $this->value, $this->count, $this->range] = $s;
779 }
780
781 private function bit(int $prob): int {
782 $split = 1 + ((($this->range - 1) * $prob) >> 8);
783 if ($this->count < 0) {
784 // 足りなければ数バイト読む(終わりの後は0)
785 do {
786 $this->value = ($this->value << 8) | ord($this->d[$this->p++] ?? "\0");
787 $this->count += 8;
788 } while ($this->count < 40);
789 }
790 $big = $split << $this->count;
791 if ($this->value >= $big) {
792 $this->range -= $split;
793 $this->value -= $big;
794 $bit = 1;
795 } else {
796 $this->range = $split;
797 $bit = 0;
798 }
799 if ($this->range < 128) {
800 $s = $this->norm[$this->range];
801 $this->range <<= $s;
802 $this->count -= $s;
803 }
804 return $bit;
805 }
806
807 private function value(int $n): int {
808 $v = 0;
809 while ($n-- > 0) $v = ($v << 1) | $this->bit(128);
810 return $v;
811 }
812
813 private function signed(int $n): int {
814 $v = $this->value($n);
815 return $this->bit(128) ? -$v : $v;
816 }
817
818 /**
819 * マクロブロックの係数を読む
820 *
821 * @param array $t 上の非ゼロ(Yの4列、Uの2列、Vの2列、Y2)
822 * @param array $l 左の非ゼロ
823 * @return bool 0以外の係数があるか
824 */
825 private function residuals(bool $i4, array $q, array $bands, array &$t, array &$l, array &$coef, array &$nz): bool {
826 $any = false;
827 if (!$i4) {
828 $dc = array_fill(0, 16, 0);
829 $n = $this->coeffs($bands[1], $t[8] + $l[8], $q[2], $q[3], 0, $dc, 0);
830 $t[8] = $l[8] = $n > 0 ? 1 : 0;
831 if ($n > 0) {
832 self::iwht($dc, $coef);
833 for ($b = 0; $b < 16; $b++) if ($coef[$b * 16] !== 0) $nz[$b] = $any = true;
834 }
835 $first = 1;
836 $ac = $bands[0];
837 } else {
838 $first = 0;
839 $ac = $bands[3];
840 }
841 for ($y = 0; $y < 4; $y++) {
842 $left = $l[$y];
843 for ($x = 0; $x < 4; $x++) {
844 $b = $y * 4 + $x;
845 $n = $this->coeffs($ac, $left + $t[$x], $q[0], $q[1], $first, $coef, $b * 16);
846 $left = $t[$x] = $n > $first ? 1 : 0;
847 if ($left) $nz[$b] = $any = true;
848 }
849 $l[$y] = $left;
850 }
851 for ($ch = 0; $ch < 2; $ch++) {
852 for ($y = 0; $y < 2; $y++) {
853 $left = $l[4 + $ch * 2 + $y];
854 for ($x = 0; $x < 2; $x++) {
855 $b = 16 + $ch * 4 + $y * 2 + $x;
856 $n = $this->coeffs($bands[2], $left + $t[4 + $ch * 2 + $x], $q[4], $q[5], 0, $coef, $b * 16);
857 $left = $t[4 + $ch * 2 + $x] = $n > 0 ? 1 : 0;
858 if ($left) $nz[$b] = $any = true;
859 }
860 $l[4 + $ch * 2 + $y] = $left;
861 }
862 }
863 return $any;
864 }
865
866 /**
867 * 1ブロックの係数を読む
868 *
869 * @return int 最後の0以外の係数の次の位置
870 */
871 private function coeffs(array $bp, int $ctx, int $dq0, int $dq1, int $n, array &$out, int $o): int {
872 $p = $bp[$n][$ctx];
873 for (; $n < 16; $n++) {
874 if (!$this->bit($p[0])) return $n;
875 while (!$this->bit($p[1])) {
876 $p = $bp[++$n][0];
877 if ($n === 16) return 16;
878 }
879 if (!$this->bit($p[2])) {
880 $v = 1;
881 $next = 1;
882 } else {
883 $v = $this->large($p);
884 $next = 2;
885 }
886 $out[$o + self::ZIGZAG[$n]] = ($this->bit(128) ? -$v : $v) * ($n > 0 ? $dq1 : $dq0);
887 $p = $bp[$n + 1][$next];
888 }
889 return 16;
890 }
891
892 private function large(array $p): int {
893 if (!$this->bit($p[3])) {
894 if (!$this->bit($p[4])) return 2;
895 return 3 + $this->bit($p[5]);
896 }
897 if (!$this->bit($p[6])) {
898 if (!$this->bit($p[7])) return 5 + $this->bit(159);
899 $v = 7 + 2 * $this->bit(165);
900 return $v + $this->bit(145);
901 }
902 $b1 = $this->bit($p[8]);
903 $cat = 2 * $b1 + $this->bit($p[9 + $b1]);
904 $v = 0;
905 foreach (self::CAT[$cat] as $prob) $v = 2 * $v + $this->bit($prob);
906 return $v + 3 + (8 << $cat);
907 }
908
909 /** Y2の逆ウォルシュ・アダマール変換(結果は各ブロックの直流) */
910 private static function iwht(array $in, array &$coef): void {
911 $tmp = [];
912 for ($i = 0; $i < 4; $i++) {
913 $a0 = $in[$i] + $in[12 + $i];
914 $a1 = $in[4 + $i] + $in[8 + $i];
915 $a2 = $in[4 + $i] - $in[8 + $i];
916 $a3 = $in[$i] - $in[12 + $i];
917 $tmp[$i] = $a0 + $a1;
918 $tmp[8 + $i] = $a0 - $a1;
919 $tmp[4 + $i] = $a3 + $a2;
920 $tmp[12 + $i] = $a3 - $a2;
921 }
922 for ($i = 0; $i < 4; $i++) {
923 $dc = $tmp[$i * 4] + 3;
924 $a0 = $dc + $tmp[$i * 4 + 3];
925 $a1 = $tmp[$i * 4 + 1] + $tmp[$i * 4 + 2];
926 $a2 = $tmp[$i * 4 + 1] - $tmp[$i * 4 + 2];
927 $a3 = $dc - $tmp[$i * 4 + 3];
928 $coef[($i * 4) * 16] = ($a0 + $a1) >> 3;
929 $coef[($i * 4 + 1) * 16] = ($a3 + $a2) >> 3;
930 $coef[($i * 4 + 2) * 16] = ($a0 - $a1) >> 3;
931 $coef[($i * 4 + 3) * 16] = ($a3 - $a2) >> 3;
932 }
933 }
934
935 /**
936 * 逆DCTして作業用の配列に足す
937 */
938 private static function idctAdd(array $c, int $o, array &$ws, int $at): void {
939 $tmp = [];
940 for ($i = 0; $i < 4; $i++) {
941 $c0 = $c[$o + $i];
942 $c4 = $c[$o + 4 + $i];
943 $c8 = $c[$o + 8 + $i];
944 $c12 = $c[$o + 12 + $i];
945 $a = $c0 + $c8;
946 $b = $c0 - $c8;
947 $cc = (($c4 * 35468) >> 16) - ((($c12 * 20091) >> 16) + $c12);
948 $d = ((($c4 * 20091) >> 16) + $c4) + (($c12 * 35468) >> 16);
949 $tmp[$i * 4] = $a + $d;
950 $tmp[$i * 4 + 1] = $b + $cc;
951 $tmp[$i * 4 + 2] = $b - $cc;
952 $tmp[$i * 4 + 3] = $a - $d;
953 }
954 for ($i = 0; $i < 4; $i++) {
955 $dc = $tmp[$i] + 4;
956 $a = $dc + $tmp[8 + $i];
957 $b = $dc - $tmp[8 + $i];
958 $cc = (($tmp[4 + $i] * 35468) >> 16) - ((($tmp[12 + $i] * 20091) >> 16) + $tmp[12 + $i]);
959 $d = ((($tmp[4 + $i] * 20091) >> 16) + $tmp[4 + $i]) + (($tmp[12 + $i] * 35468) >> 16);
960 $r = $at + $i * self::BPS;
961 foreach ([$a + $d, $b + $cc, $b - $cc, $a - $d] as $k => $v) {
962 $v = $ws[$r + $k] + ($v >> 3);
963 $ws[$r + $k] = $v < 0 ? 0 : ($v > 255 ? 255 : $v);
964 }
965 }
966 }
967
968 /**
969 * 予測して残差を足し、平面に書く
970 */
971 private function reconstruct(array $mb, int $mx, int $my, int $mbw, array $coef, array $nz, string &$Y, string &$U, string &$V): void {
972 $yw = $mbw * 16;
973 $ws = self::border($Y, $yw, 16, $mx, $my);
974 if ($mb['i4']) {
975 self::topRight($ws, $Y, $yw, $mx, $my, $mbw);
976 for ($b = 0; $b < 16; $b++) {
977 $at = (($b >> 2) * 4 + 1) * self::BPS + ($b & 3) * 4 + 1;
978 self::predict4($mb['b'][$b], $ws, $at);
979 if ($nz[$b]) self::idctAdd($coef, $b * 16, $ws, $at);
980 }
981 } else {
982 self::predictBlock($mb['y'], $ws, 16, $mx, $my);
983 for ($b = 0; $b < 16; $b++) {
984 if ($nz[$b]) self::idctAdd($coef, $b * 16, $ws, (($b >> 2) * 4 + 1) * self::BPS + ($b & 3) * 4 + 1);
985 }
986 }
987 self::store($Y, $yw, 16, $mx, $my, $ws);
988 $uw = $mbw * 8;
989 foreach ([&$U, &$V] as $ch => &$plane) {
990 $ws = self::border($plane, $uw, 8, $mx, $my);
991 self::predictBlock($mb['uv'], $ws, 8, $mx, $my);
992 for ($b = 0; $b < 4; $b++) {
993 $bi = 16 + $ch * 4 + $b;
994 if ($nz[$bi]) self::idctAdd($coef, $bi * 16, $ws, (($b >> 1) * 4 + 1) * self::BPS + ($b & 1) * 4 + 1);
995 }
996 self::store($plane, $uw, 8, $mx, $my, $ws);
997 }
998 unset($plane);
999 }
1000
1001 /**
1002 * 4x4の予測の右上:上のマクロブロックの行の続き(右端では最後の画素)。2行目以降の右端のブロックも同じ物を使う
1003 */
1004 private static function topRight(array &$ws, string $Y, int $yw, int $mx, int $my, int $mbw): void {
1005 for ($i = 0; $i < 4; $i++) {
1006 $tr = $my === 0 ? 127 : ($mx < $mbw - 1 ? ord($Y[($my * 16 - 1) * $yw + $mx * 16 + 16 + $i]) : ord($Y[($my * 16 - 1) * $yw + $mx * 16 + 15]));
1007 foreach ([0, 4, 8, 12] as $r) $ws[$r * self::BPS + 17 + $i] = $tr;
1008 }
1009 }
1010
1011 /**
1012 * 作業用の配列に上と左の画素を入れる(画像の外は上が127、左が129)
1013 */
1014 private static function border(string $plane, int $stride, int $size, int $mx, int $my): array {
1015 $ws = array_fill(0, ($size + 1) * self::BPS, 0);
1016 $x0 = $mx * $size;
1017 $y0 = $my * $size;
1018 if ($my > 0) {
1019 $o = ($y0 - 1) * $stride + $x0;
1020 for ($x = 0; $x < $size; $x++) $ws[1 + $x] = ord($plane[$o + $x]);
1021 } else {
1022 for ($x = 0; $x < $size; $x++) $ws[1 + $x] = 127;
1023 }
1024 $ws[0] = $my === 0 ? 127 : ($mx === 0 ? 129 : ord($plane[($y0 - 1) * $stride + $x0 - 1]));
1025 for ($y = 0; $y < $size; $y++) $ws[($y + 1) * self::BPS] = $mx === 0 ? 129 : ord($plane[($y0 + $y) * $stride + $x0 - 1]);
1026 return $ws;
1027 }
1028
1029 private static function store(string &$plane, int $stride, int $size, int $mx, int $my, array $ws): void {
1030 for ($y = 0; $y < $size; $y++) {
1031 $o = ($my * $size + $y) * $stride + $mx * $size;
1032 $r = ($y + 1) * self::BPS + 1;
1033 for ($x = 0; $x < $size; $x++) $plane[$o + $x] = chr($ws[$r + $x]);
1034 }
1035 }
1036
1037 /** 16x16(輝度)又は8x8(色差)の予測 */
1038 private static function predictBlock(int $mode, array &$ws, int $size, int $mx, int $my): void {
1039 $B = self::BPS;
1040 $shift = $size === 16 ? 4 : 3;
1041 if ($mode === self::DC) {
1042 if ($mx === 0 && $my === 0) {
1043 $dc = 128;
1044 } else {
1045 $sum = 0;
1046 if ($my > 0) for ($x = 0; $x < $size; $x++) $sum += $ws[1 + $x];
1047 if ($mx > 0) for ($y = 0; $y < $size; $y++) $sum += $ws[($y + 1) * $B];
1048 $s = ($mx > 0 && $my > 0) ? $shift + 1 : $shift;
1049 $dc = ($sum + (1 << ($s - 1))) >> $s;
1050 }
1051 for ($y = 0; $y < $size; $y++) for ($x = 0; $x < $size; $x++) $ws[($y + 1) * $B + 1 + $x] = $dc;
1052 return;
1053 }
1054 for ($y = 0; $y < $size; $y++) {
1055 $r = ($y + 1) * $B;
1056 $left = $ws[$r];
1057 for ($x = 0; $x < $size; $x++) {
1058 if ($mode === self::VE) {
1059 $v = $ws[1 + $x];
1060 } else if ($mode === self::HE) {
1061 $v = $left;
1062 } else {
1063 $v = $ws[1 + $x] + $left - $ws[0];
1064 $v = $v < 0 ? 0 : ($v > 255 ? 255 : $v);
1065 }
1066 $ws[$r + 1 + $x] = $v;
1067 }
1068 }
1069 }
1070
1071 /** 4x4の予測($atはブロックの左上) */
1072 private static function predict4(int $mode, array &$ws, int $at): void {
1073 $B = self::BPS;
1074 $t = $at - $B;
1075 $X = $ws[$t - 1];
1076 [$A, $Bb, $C, $D, $E, $F, $G, $H] = [$ws[$t], $ws[$t + 1], $ws[$t + 2], $ws[$t + 3], $ws[$t + 4], $ws[$t + 5], $ws[$t + 6], $ws[$t + 7]];
1077 [$I, $J, $K, $L] = [$ws[$at - 1], $ws[$at + $B - 1], $ws[$at + 2 * $B - 1], $ws[$at + 3 * $B - 1]];
1078 $avg3 = fn($a, $b, $c) => ($a + 2 * $b + $c + 2) >> 2;
1079 $avg2 = fn($a, $b) => ($a + $b + 1) >> 1;
1080 $o = [];
1081 switch ($mode) {
1082 case 0: // DC
1083 $dc = ($A + $Bb + $C + $D + $I + $J + $K + $L + 4) >> 3;
1084 $o = array_fill(0, 16, $dc);
1085 break;
1086 case 1: // TM
1087 foreach ([$I, $J, $K, $L] as $y => $l) {
1088 foreach ([$A, $Bb, $C, $D] as $x => $tp) {
1089 $v = $tp + $l - $X;
1090 $o[$y * 4 + $x] = $v < 0 ? 0 : ($v > 255 ? 255 : $v);
1091 }
1092 }
1093 break;
1094 case 2: // VE
1095 $row = [$avg3($X, $A, $Bb), $avg3($A, $Bb, $C), $avg3($Bb, $C, $D), $avg3($C, $D, $E)];
1096 $o = [...$row, ...$row, ...$row, ...$row];
1097 break;
1098 case 3: // HE
1099 foreach ([$avg3($X, $I, $J), $avg3($I, $J, $K), $avg3($J, $K, $L), $avg3($K, $L, $L)] as $y => $v) for ($x = 0; $x < 4; $x++) $o[$y * 4 + $x] = $v;
1100 break;
1101 case 4: // RD
1102 $o[12] = $avg3($J, $K, $L);
1103 $o[13] = $o[8] = $avg3($I, $J, $K);
1104 $o[14] = $o[9] = $o[4] = $avg3($X, $I, $J);
1105 $o[15] = $o[10] = $o[5] = $o[0] = $avg3($A, $X, $I);
1106 $o[11] = $o[6] = $o[1] = $avg3($Bb, $A, $X);
1107 $o[7] = $o[2] = $avg3($C, $Bb, $A);
1108 $o[3] = $avg3($D, $C, $Bb);
1109 break;
1110 case 5: // VR
1111 $o[0] = $o[9] = $avg2($X, $A);
1112 $o[1] = $o[10] = $avg2($A, $Bb);
1113 $o[2] = $o[11] = $avg2($Bb, $C);
1114 $o[3] = $avg2($C, $D);
1115 $o[12] = $avg3($K, $J, $I);
1116 $o[8] = $avg3($J, $I, $X);
1117 $o[4] = $o[13] = $avg3($I, $X, $A);
1118 $o[5] = $o[14] = $avg3($X, $A, $Bb);
1119 $o[6] = $o[15] = $avg3($A, $Bb, $C);
1120 $o[7] = $avg3($Bb, $C, $D);
1121 break;
1122 case 6: // LD
1123 $o[0] = $avg3($A, $Bb, $C);
1124 $o[1] = $o[4] = $avg3($Bb, $C, $D);
1125 $o[2] = $o[5] = $o[8] = $avg3($C, $D, $E);
1126 $o[3] = $o[6] = $o[9] = $o[12] = $avg3($D, $E, $F);
1127 $o[7] = $o[10] = $o[13] = $avg3($E, $F, $G);
1128 $o[11] = $o[14] = $avg3($F, $G, $H);
1129 $o[15] = $avg3($G, $H, $H);
1130 break;
1131 case 7: // VL
1132 $o[0] = $avg2($A, $Bb);
1133 $o[1] = $o[8] = $avg2($Bb, $C);
1134 $o[2] = $o[9] = $avg2($C, $D);
1135 $o[3] = $o[10] = $avg2($D, $E);
1136 $o[4] = $avg3($A, $Bb, $C);
1137 $o[5] = $o[12] = $avg3($Bb, $C, $D);
1138 $o[6] = $o[13] = $avg3($C, $D, $E);
1139 $o[7] = $o[14] = $avg3($D, $E, $F);
1140 $o[11] = $avg3($E, $F, $G);
1141 $o[15] = $avg3($F, $G, $H);
1142 break;
1143 case 8: // HD
1144 $o[0] = $o[6] = $avg2($I, $X);
1145 $o[4] = $o[10] = $avg2($J, $I);
1146 $o[8] = $o[14] = $avg2($K, $J);
1147 $o[12] = $avg2($L, $K);
1148 $o[3] = $avg3($A, $Bb, $C);
1149 $o[2] = $avg3($X, $A, $Bb);
1150 $o[1] = $o[7] = $avg3($I, $X, $A);
1151 $o[5] = $o[11] = $avg3($J, $I, $X);
1152 $o[9] = $o[15] = $avg3($K, $J, $I);
1153 $o[13] = $avg3($L, $K, $J);
1154 break;
1155 default: // HU
1156 $o[0] = $avg2($I, $J);
1157 $o[2] = $o[4] = $avg2($J, $K);
1158 $o[6] = $o[8] = $avg2($K, $L);
1159 $o[1] = $avg3($I, $J, $K);
1160 $o[3] = $o[5] = $avg3($J, $K, $L);
1161 $o[7] = $o[9] = $avg3($K, $L, $L);
1162 $o[11] = $o[10] = $o[12] = $o[13] = $o[14] = $o[15] = $L;
1163 }
1164 for ($i = 0; $i < 16; $i++) $ws[$at + ($i >> 2) * $B + ($i & 3)] = $o[$i];
1165 }
1166
1167 //////// 書き出し
1168
1169 // 算術符号化
1170 private string $out = '';
1171 private int $bottom = 0;
1172 private int $erange = 255;
1173 private int $shiftCount = 24;
1174 /** 確率を決める為に数えるだけ([種類][帯][文脈][番号] => [0の数, 1の数]) */
1175 private ?array $stats = null;
1176
1177 /**
1178 * RGBAをVP8チャンクの中身にする(アルファは無視)
1179 *
1180 * @param array $opt quality(0~100、既定80)、filter(normal・simple・none)、sharpness(0~7)、partitions(1・2・4・8)
1181 */
1182 public function encode(string $rgba, int $w, int $h, array $opt = []): string {
1183 $quality = (int)($opt['quality'] ?? 80);
1184 $filter = $opt['filter'] ?? 'normal';
1185 $sharp = max(0, min(7, (int)($opt['sharpness'] ?? 0)));
1186 $parts = (int)($opt['partitions'] ?? 1);
1187 if (!in_array($parts, [1, 2, 4, 8], true)) throw new \InvalidArgumentException('VP8のパーティションの数は1・2・4・8です。');
1188 $mbw = ($w + 15) >> 4;
1189 $mbh = ($h + 15) >> 4;
1190 [$SY, $SU, $SV] = self::toYuv($rgba, $w, $h, $mbw, $mbh);
1191 $yw = $mbw * 16;
1192 $uw = $mbw * 8;
1193
1194 // 品質から量子化(libwebpと同じ曲線)
1195 $c = max(0, min(100, $quality)) / 100;
1196 $c = $c < 0.75 ? $c * 2 / 3 : 2 * $c - 1;
1197 $q = max(0, min(127, (int)(127 * (1 - $c ** (1 / 3)))));
1198 $Q = [
1199 self::DC_TABLE[$q], self::AC_TABLE[$q],
1200 self::DC_TABLE[$q] * 2, max(8, (self::AC_TABLE[$q] * 101581) >> 16),
1201 self::DC_TABLE[min($q, 117)], self::AC_TABLE[$q],
1202 ];
1203 // 歪み(二乗誤差)とビット数の重み
1204 $lambda = 0.03 * $Q[1] * $Q[1];
1205
1206 $RY = str_repeat("\0", $yw * $mbh * 16);
1207 $RU = str_repeat("\0", $uw * $mbh * 8);
1208 $RV = $RU;
1209 $mbs = [];
1210 for ($my = 0; $my < $mbh; $my++) {
1211 for ($mx = 0; $mx < $mbw; $mx++) {
1212 $mbs[] = $this->encodeMb($SY, $SU, $SV, $RY, $RU, $RV, $mx, $my, $mbw, $Q, $lambda);
1213 }
1214 }
1215
1216 // 係数の確率を画像に合わせる
1217 $this->stats = [];
1218 $this->probs = self::COEFF_PROBS;
1219 $this->tokens($mbs, $mbw, 1);
1220 $updates = [];
1221 $cost = fn(int $p, int $bit): float => -log(($bit ? 256 - $p : $p) / 256, 2);
1222 for ($t = 0; $t < 4; $t++) for ($b = 0; $b < 8; $b++) for ($k = 0; $k < 3; $k++) for ($i = 0; $i < 11; $i++) {
1223 [$n0, $n1] = $this->stats[$t][$b][$k][$i] ?? [0, 0];
1224 if ($n0 + $n1 === 0) continue;
1225 $old = self::COEFF_PROBS[$t][$b][$k][$i];
1226 $new = max(1, min(255, (int)round(256 * $n0 / ($n0 + $n1))));
1227 $up = self::COEFF_UPDATE_PROBS[$t][$b][$k][$i];
1228 $saved = $n0 * ($cost($old, 0) - $cost($new, 0)) + $n1 * ($cost($old, 1) - $cost($new, 1)) - 8 - $cost($up, 1) + $cost($up, 0);
1229 if ($new !== $old && $saved > 0) {
1230 $updates[$t][$b][$k][$i] = $new;
1231 $this->probs[$t][$b][$k][$i] = $new;
1232 }
1233 }
1234 $this->stats = null;
1235 $skips = count(array_filter($mbs, fn($mb) => $mb['skip']));
1236 $skipProb = max(1, min(255, (int)round(256 * (count($mbs) - $skips) / count($mbs))));
1237
1238 // 最初のパーティション:ヘッダーと予測モード
1239 $this->startWrite();
1240 $this->putValue(0, 2); // 色空間、クランプ
1241 $this->putValue(0, 1); // セグメント無し
1242 $level = (self::AC_TABLE[$q] >> 2) * 300 / 356 * 1.2;
1243 $level = $level < 2 || $filter === 'none' ? 0 : min(63, (int)round($level));
1244 $this->putValue($filter === 'simple' ? 1 : 0, 1);
1245 $this->putValue($level, 6);
1246 $this->putValue($sharp, 3);
1247 $this->putValue(0, 1); // モード毎のフィルターの差は無し
1248 $this->putValue((int)log($parts, 2), 2);
1249 $this->putValue($q, 7);
1250 $this->putValue(0, 5); // 量子化の差は無し
1251 $this->putValue(0, 1);
1252 for ($t = 0; $t < 4; $t++) for ($b = 0; $b < 8; $b++) for ($k = 0; $k < 3; $k++) for ($i = 0; $i < 11; $i++) {
1253 $new = $updates[$t][$b][$k][$i] ?? null;
1254 $this->put(self::COEFF_UPDATE_PROBS[$t][$b][$k][$i], $new !== null ? 1 : 0);
1255 if ($new !== null) $this->putValue($new, 8);
1256 }
1257 $this->putValue(1, 1);
1258 $this->putValue($skipProb, 8);
1259 $paths = self::bmodePaths();
1260 $intraT = array_fill(0, 4 * $mbw, self::DC);
1261 foreach ($mbs as $n => $mb) {
1262 $mx = $n % $mbw;
1263 if ($mx === 0) $intraL = [self::DC, self::DC, self::DC, self::DC];
1264 $this->put($skipProb, $mb['skip'] ? 1 : 0);
1265 $this->put(145, $mb['i4'] ? 0 : 1);
1266 if (!$mb['i4']) {
1267 $ym = $mb['y'];
1268 if ($ym === self::DC || $ym === self::VE) {
1269 $this->put(156, 0);
1270 $this->put(163, $ym === self::VE ? 1 : 0);
1271 } else {
1272 $this->put(156, 1);
1273 $this->put(128, $ym === self::TM ? 1 : 0);
1274 }
1275 for ($i = 0; $i < 4; $i++) $intraT[4 * $mx + $i] = $intraL[$i] = $ym;
1276 } else {
1277 for ($y = 0; $y < 4; $y++) {
1278 for ($x = 0; $x < 4; $x++) {
1279 $m = $mb['b'][$y * 4 + $x];
1280 $prob = self::BMODE_PROBS[$intraT[4 * $mx + $x]][$intraL[$y]];
1281 foreach ($paths[$m] as [$pi, $bit]) $this->put($prob[$pi], $bit);
1282 $intraT[4 * $mx + $x] = $intraL[$y] = $m;
1283 }
1284 }
1285 }
1286 $uv = $mb['uv'];
1287 $this->put(142, $uv === self::DC ? 0 : 1);
1288 if ($uv !== self::DC) {
1289 $this->put(114, $uv === self::VE ? 0 : 1);
1290 if ($uv !== self::VE) $this->put(183, $uv === self::TM ? 1 : 0);
1291 }
1292 }
1293 $first = $this->finishWrite();
1294
1295 // 係数のパーティション(マクロブロックの行毎に順に使う)
1296 $tokens = $this->tokens($mbs, $mbw, $parts);
1297 $sizes = '';
1298 for ($i = 0; $i < $parts - 1; $i++) $sizes .= substr(pack('V', strlen($tokens[$i])), 0, 3);
1299
1300 $tag = (strlen($first) << 5) | 0x10;
1301 return chr($tag & 255).chr(($tag >> 8) & 255).chr($tag >> 16)."\x9d\x01\x2a".pack('vv', $w, $h).$first.$sizes.implode('', $tokens);
1302 }
1303
1304 /** 4x4の予測モードの木の道筋(モード => [[確率の番号, ビット], ...]) */
1305 private static function bmodePaths(): array {
1306 $paths = [];
1307 $walk = function(int $node, array $path) use (&$walk, &$paths): void {
1308 for ($b = 0; $b < 2; $b++) {
1309 $child = self::BMODE_TREE[2 * $node + $b];
1310 $p = [...$path, [$node, $b]];
1311 if ($child <= 0) $paths[-$child] = $p;
1312 else $walk($child, $p);
1313 }
1314 };
1315 $walk(0, []);
1316 return $paths;
1317 }
1318
1319 /**
1320 * RGBAをYUV 4:2:0にする(マクロブロックの大きさまで端の画素を伸ばす)
1321 */
1322 private static function toYuv(string $rgba, int $w, int $h, int $mbw, int $mbh): array {
1323 $yw = $mbw * 16;
1324 $uw = $mbw * 8;
1325 $Y = $U = $V = '';
1326 $prev = null;
1327 for ($y = 0; $y < $mbh * 16; $y++) {
1328 $row = array_values(unpack('C*', substr($rgba, min($y, $h - 1) * $w * 4, $w * 4)));
1329 $line = [];
1330 for ($x = 0; $x < $yw; $x++) {
1331 $o = min($x, $w - 1) * 4;
1332 $line[] = (16839 * $row[$o] + 33059 * $row[$o + 1] + 6420 * $row[$o + 2] + 32768 + (16 << 16)) >> 16;
1333 }
1334 $Y .= pack('C*', ...$line);
1335 if (!($y & 1)) {
1336 $prev = $row;
1337 continue;
1338 }
1339 $ul = $vl = [];
1340 for ($x = 0; $x < $uw; $x++) {
1341 $o0 = min(2 * $x, $w - 1) * 4;
1342 $o1 = min(2 * $x + 1, $w - 1) * 4;
1343 $r = $prev[$o0] + $prev[$o1] + $row[$o0] + $row[$o1];
1344 $g = $prev[$o0 + 1] + $prev[$o1 + 1] + $row[$o0 + 1] + $row[$o1 + 1];
1345 $b = $prev[$o0 + 2] + $prev[$o1 + 2] + $row[$o0 + 2] + $row[$o1 + 2];
1346 $u = (-9719 * $r - 19081 * $g + 28800 * $b + (1 << 17) + (128 << 18)) >> 18;
1347 $v = (28800 * $r - 24116 * $g - 4684 * $b + (1 << 17) + (128 << 18)) >> 18;
1348 $ul[] = $u < 0 ? 0 : ($u > 255 ? 255 : $u);
1349 $vl[] = $v < 0 ? 0 : ($v > 255 ? 255 : $v);
1350 }
1351 $U .= pack('C*', ...$ul);
1352 $V .= pack('C*', ...$vl);
1353 }
1354 return [$Y, $U, $V];
1355 }
1356
1357 /** 平面からブロックの画素を読む */
1358 private static function block(string $plane, int $stride, int $size, int $mx, int $my): array {
1359 $px = [];
1360 for ($y = 0; $y < $size; $y++) {
1361 foreach (unpack('C*', substr($plane, ($my * $size + $y) * $stride + $mx * $size, $size)) as $v) $px[] = $v;
1362 }
1363 return $px;
1364 }
1365
1366 /**
1367 * 1つのマクロブロックの予測モードを選んで量子化し、復元した画素を書く
1368 */
1369 private function encodeMb(string $SY, string $SU, string $SV, string &$RY, string &$RU, string &$RV, int $mx, int $my, int $mbw, array $Q, float $lambda): array {
1370 $B = self::BPS;
1371 $yw = $mbw * 16;
1372 $src = self::block($SY, $yw, 16, $mx, $my);
1373 $ws0 = self::border($RY, $yw, 16, $mx, $my);
1374
1375 // 16x16:予測の誤差が一番小さいモード
1376 $best = null;
1377 foreach ([self::DC, self::VE, self::HE, self::TM] as $m) {
1378 $ws = $ws0;
1379 self::predictBlock($m, $ws, 16, $mx, $my);
1380 $sad = 0;
1381 for ($i = 0; $i < 256; $i++) $sad += abs($src[$i] - $ws[(($i >> 4) + 1) * $B + ($i & 15) + 1]);
1382 if ($best === null || $sad < $best[0]) $best = [$sad, $m, $ws];
1383 }
1384 [, $m16, $ws16] = $best;
1385 $res = [];
1386 $dcs = [];
1387 for ($b = 0; $b < 16; $b++) {
1388 $at = (($b >> 2) * 4 + 1) * $B + ($b & 3) * 4 + 1;
1389 $s = (($b >> 2) * 4) * 16 + ($b & 3) * 4;
1390 $res[$b] = self::fdct($src, $s, 16, $ws16, $at);
1391 $dcs[] = $res[$b][0];
1392 }
1393 $lv2 = [];
1394 $dq2 = self::quant(self::fwht($dcs), $Q[2], $Q[3], 96, 108, 0, $lv2);
1395 $dc = array_fill(0, 384, 0);
1396 self::iwht($dq2, $dc);
1397 $lv16 = [];
1398 $bits16 = self::bits($lv2, 0) + 3;
1399 for ($b = 0; $b < 16; $b++) {
1400 $l = [];
1401 $dq = self::quant($res[$b], $Q[0], $Q[1], 96, 110, 1, $l);
1402 $lv16[$b] = $l;
1403 $bits16 += self::bits($l, 1);
1404 $dq[0] = $dc[$b * 16];
1405 if (array_filter($dq)) self::idctAdd($dq, 0, $ws16, (($b >> 2) * 4 + 1) * $B + ($b & 3) * 4 + 1);
1406 }
1407 $score16 = self::sse($src, $ws16, 16) + $lambda * $bits16;
1408
1409 // 4x4:ブロック毎に予測の誤差が一番小さいモード(先に復元したブロックから予測する)
1410 $ws4 = $ws0;
1411 self::topRight($ws4, $RY, $yw, $mx, $my, $mbw);
1412 $modes = [];
1413 $lv4 = [];
1414 $bits4 = 1;
1415 for ($b = 0; $b < 16; $b++) {
1416 $at = (($b >> 2) * 4 + 1) * $B + ($b & 3) * 4 + 1;
1417 $s = (($b >> 2) * 4) * 16 + ($b & 3) * 4;
1418 $bestM = 0;
1419 $bestSad = PHP_INT_MAX;
1420 for ($m = 0; $m < 10; $m++) {
1421 self::predict4($m, $ws4, $at);
1422 $sad = 0;
1423 for ($i = 0; $i < 16; $i++) $sad += abs($src[$s + ($i >> 2) * 16 + ($i & 3)] - $ws4[$at + ($i >> 2) * $B + ($i & 3)]);
1424 // DC以外は少し不利に(モードのビット)
1425 if ($m > 0) $sad += 4;
1426 if ($sad < $bestSad) {
1427 $bestSad = $sad;
1428 $bestM = $m;
1429 }
1430 }
1431 self::predict4($bestM, $ws4, $at);
1432 $modes[] = $bestM;
1433 $l = [];
1434 $dq = self::quant(self::fdct($src, $s, 16, $ws4, $at), $Q[0], $Q[1], 96, 110, 0, $l);
1435 $lv4[$b] = $l;
1436 $bits4 += self::bits($l, 0) + ($bestM === 0 ? 1 : 4);
1437 if (array_filter($dq)) self::idctAdd($dq, 0, $ws4, $at);
1438 }
1439 $score4 = self::sse($src, $ws4, 16) + $lambda * $bits4;
1440
1441 $mb = ['skip' => false];
1442 if ($score4 < $score16) {
1443 $mb['i4'] = true;
1444 $mb['b'] = $modes;
1445 $mb['lv'] = $lv4;
1446 $ws = $ws4;
1447 } else {
1448 $mb['i4'] = false;
1449 $mb['y'] = $m16;
1450 $mb['y2'] = $lv2;
1451 $mb['lv'] = $lv16;
1452 $ws = $ws16;
1453 }
1454 self::store($RY, $yw, 16, $mx, $my, $ws);
1455
1456 // 色差:UとVの予測の誤差の合計が一番小さいモード
1457 $uw = $mbw * 8;
1458 $srcU = self::block($SU, $uw, 8, $mx, $my);
1459 $srcV = self::block($SV, $uw, 8, $mx, $my);
1460 $wu0 = self::border($RU, $uw, 8, $mx, $my);
1461 $wv0 = self::border($RV, $uw, 8, $mx, $my);
1462 $best = null;
1463 foreach ([self::DC, self::VE, self::HE, self::TM] as $m) {
1464 $wu = $wu0;
1465 $wv = $wv0;
1466 self::predictBlock($m, $wu, 8, $mx, $my);
1467 self::predictBlock($m, $wv, 8, $mx, $my);
1468 $sad = 0;
1469 for ($i = 0; $i < 64; $i++) {
1470 $k = (($i >> 3) + 1) * $B + ($i & 7) + 1;
1471 $sad += abs($srcU[$i] - $wu[$k]) + abs($srcV[$i] - $wv[$k]);
1472 }
1473 if ($best === null || $sad < $best[0]) $best = [$sad, $m, $wu, $wv];
1474 }
1475 [, $mb['uv'], $wu, $wv] = $best;
1476 $mb['uvlv'] = [];
1477 foreach ([[$srcU, &$wu, &$RU], [$srcV, &$wv, &$RV]] as [$s8, &$w8, &$plane]) {
1478 for ($b = 0; $b < 4; $b++) {
1479 $at = (($b >> 1) * 4 + 1) * $B + ($b & 1) * 4 + 1;
1480 $l = [];
1481 $dq = self::quant(self::fdct($s8, (($b >> 1) * 4) * 8 + ($b & 1) * 4, 8, $w8, $at), $Q[4], $Q[5], 110, 115, 0, $l);
1482 $mb['uvlv'][] = $l;
1483 if (array_filter($dq)) self::idctAdd($dq, 0, $w8, $at);
1484 }
1485 self::store($plane, $uw, 8, $mx, $my, $w8);
1486 }
1487 unset($w8, $plane);
1488
1489 $mb['skip'] = !array_filter($mb['y2'] ?? []) && !array_filter(array_merge(...$mb['lv'], ...$mb['uvlv']));
1490 return $mb;
1491 }
1492
1493 /** 二乗誤差 */
1494 private static function sse(array $src, array $ws, int $size): int {
1495 $e = 0;
1496 for ($y = 0; $y < $size; $y++) {
1497 $r = ($y + 1) * self::BPS + 1;
1498 for ($x = 0; $x < $size; $x++) {
1499 $d = $src[$y * $size + $x] - $ws[$r + $x];
1500 $e += $d * $d;
1501 }
1502 }
1503 return $e;
1504 }
1505
1506 /** 係数のビット数の大体の見積もり */
1507 private static function bits(array $lv, int $first): int {
1508 $bits = 1;
1509 for ($n = $first; $n < 16; $n++) {
1510 $a = abs($lv[$n]);
1511 if ($a) $bits += 3 + 2 * (int)log($a, 2);
1512 else $bits++;
1513 }
1514 return $bits;
1515 }
1516
1517 /**
1518 * 4x4の残差の離散コサイン変換(libwebpと同じ整数の計算)
1519 *
1520 * @param int $s 元の画素の左上、$strideは1行の数
1521 */
1522 private static function fdct(array $src, int $s, int $stride, array $ws, int $at): array {
1523 $tmp = [];
1524 for ($i = 0; $i < 4; $i++) {
1525 $r = $s + $i * $stride;
1526 $p = $at + $i * self::BPS;
1527 $d0 = $src[$r] - $ws[$p];
1528 $d1 = $src[$r + 1] - $ws[$p + 1];
1529 $d2 = $src[$r + 2] - $ws[$p + 2];
1530 $d3 = $src[$r + 3] - $ws[$p + 3];
1531 $a0 = $d0 + $d3;
1532 $a1 = $d1 + $d2;
1533 $a2 = $d1 - $d2;
1534 $a3 = $d0 - $d3;
1535 $tmp[$i * 4] = ($a0 + $a1) * 8;
1536 $tmp[$i * 4 + 1] = ($a2 * 2217 + $a3 * 5352 + 1812) >> 9;
1537 $tmp[$i * 4 + 2] = ($a0 - $a1) * 8;
1538 $tmp[$i * 4 + 3] = ($a3 * 2217 - $a2 * 5352 + 937) >> 9;
1539 }
1540 $out = [];
1541 for ($i = 0; $i < 4; $i++) {
1542 $a0 = $tmp[$i] + $tmp[12 + $i];
1543 $a1 = $tmp[4 + $i] + $tmp[8 + $i];
1544 $a2 = $tmp[4 + $i] - $tmp[8 + $i];
1545 $a3 = $tmp[$i] - $tmp[12 + $i];
1546 $out[$i] = ($a0 + $a1 + 7) >> 4;
1547 $out[4 + $i] = (($a2 * 2217 + $a3 * 5352 + 12000) >> 16) + ($a3 !== 0 ? 1 : 0);
1548 $out[8 + $i] = ($a0 - $a1 + 7) >> 4;
1549 $out[12 + $i] = ($a3 * 2217 - $a2 * 5352 + 51000) >> 16;
1550 }
1551 ksort($out);
1552 return $out;
1553 }
1554
1555 /** 16個の直流のウォルシュ・アダマール変換 */
1556 private static function fwht(array $in): array {
1557 $tmp = [];
1558 for ($i = 0; $i < 4; $i++) {
1559 $a0 = $in[$i * 4] + $in[$i * 4 + 2];
1560 $a1 = $in[$i * 4 + 1] + $in[$i * 4 + 3];
1561 $a2 = $in[$i * 4 + 1] - $in[$i * 4 + 3];
1562 $a3 = $in[$i * 4] - $in[$i * 4 + 2];
1563 $tmp[$i * 4] = $a0 + $a1;
1564 $tmp[$i * 4 + 1] = $a3 + $a2;
1565 $tmp[$i * 4 + 2] = $a3 - $a2;
1566 $tmp[$i * 4 + 3] = $a0 - $a1;
1567 }
1568 $out = [];
1569 for ($i = 0; $i < 4; $i++) {
1570 $a0 = $tmp[$i] + $tmp[8 + $i];
1571 $a1 = $tmp[4 + $i] + $tmp[12 + $i];
1572 $a2 = $tmp[4 + $i] - $tmp[12 + $i];
1573 $a3 = $tmp[$i] - $tmp[8 + $i];
1574 $out[$i] = ($a0 + $a1) >> 1;
1575 $out[4 + $i] = ($a3 + $a2) >> 1;
1576 $out[8 + $i] = ($a3 - $a2) >> 1;
1577 $out[12 + $i] = ($a0 - $a1) >> 1;
1578 }
1579 ksort($out);
1580 return $out;
1581 }
1582
1583 /**
1584 * 量子化
1585 *
1586 * @param array $lv 量子化した値(ジグザグの順)
1587 * @return array 逆量子化した係数(復元用)
1588 */
1589 private static function quant(array $c, int $q0, int $q1, int $bias0, int $bias1, int $first, array &$lv): array {
1590 $dq = array_fill(0, 16, 0);
1591 $lv = array_fill(0, 16, 0);
1592 for ($n = $first; $n < 16; $n++) {
1593 $j = self::ZIGZAG[$n];
1594 [$q, $bias] = $n > 0 ? [$q1, $bias1] : [$q0, $bias0];
1595 $level = intdiv(abs($c[$j]) * 256 + $bias * $q, $q * 256);
1596 if ($level === 0) continue;
1597 $level = min(2047, $level);
1598 if ($c[$j] < 0) $level = -$level;
1599 $lv[$n] = $level;
1600 $dq[$j] = $level * $q;
1601 }
1602 return $dq;
1603 }
1604
1605 /**
1606 * 全ての係数を書く(statsが配列なら数えるだけ)
1607 *
1608 * @return array パーティション毎の中身
1609 */
1610 private function tokens(array $mbs, int $mbw, int $parts): array {
1611 $tnz = array_fill(0, $mbw, array_fill(0, 9, 0));
1612 $states = [];
1613 $cur = -1;
1614 foreach ($mbs as $n => $mb) {
1615 $mx = $n % $mbw;
1616 if ($mx === 0) {
1617 $lnz = array_fill(0, 9, 0);
1618 if ($this->stats === null) {
1619 if ($cur >= 0) $states[$cur] = [$this->out, $this->bottom, $this->erange, $this->shiftCount];
1620 $cur = intdiv($n, $mbw) % $parts;
1621 if (isset($states[$cur])) [$this->out, $this->bottom, $this->erange, $this->shiftCount] = $states[$cur];
1622 else $this->startWrite();
1623 }
1624 }
1625 $t = &$tnz[$mx];
1626 if ($mb['skip']) {
1627 for ($i = 0; $i < 8; $i++) $t[$i] = $lnz[$i] = 0;
1628 if (!$mb['i4']) $t[8] = $lnz[8] = 0;
1629 unset($t);
1630 continue;
1631 }
1632 if (!$mb['i4']) {
1633 $t[8] = $lnz[8] = $this->block1(1, $t[8] + $lnz[8], $mb['y2'], 0);
1634 $type = 0;
1635 $first = 1;
1636 } else {
1637 $type = 3;
1638 $first = 0;
1639 }
1640 for ($y = 0; $y < 4; $y++) {
1641 for ($x = 0; $x < 4; $x++) {
1642 $t[$x] = $lnz[$y] = $this->block1($type, $lnz[$y] + $t[$x], $mb['lv'][$y * 4 + $x], $first);
1643 }
1644 }
1645 for ($ch = 0; $ch < 2; $ch++) {
1646 for ($y = 0; $y < 2; $y++) {
1647 for ($x = 0; $x < 2; $x++) {
1648 $ti = 4 + $ch * 2 + $x;
1649 $li = 4 + $ch * 2 + $y;
1650 $t[$ti] = $lnz[$li] = $this->block1(2, $lnz[$li] + $t[$ti], $mb['uvlv'][$ch * 4 + $y * 2 + $x], 0);
1651 }
1652 }
1653 }
1654 unset($t);
1655 }
1656 if ($this->stats !== null) return [];
1657 $states[$cur] = [$this->out, $this->bottom, $this->erange, $this->shiftCount];
1658 $out = [];
1659 for ($i = 0; $i < $parts; $i++) {
1660 if (isset($states[$i])) [$this->out, $this->bottom, $this->erange, $this->shiftCount] = $states[$i];
1661 else $this->startWrite();
1662 $out[] = $this->finishWrite();
1663 }
1664 return $out;
1665 }
1666
1667 /**
1668 * 1ブロックの係数を書く
1669 *
1670 * @return int 0以外の係数があれば1
1671 */
1672 private function block1(int $type, int $ctx, array $lv, int $first): int {
1673 $last = -1;
1674 for ($n = 15; $n >= $first; $n--) {
1675 if ($lv[$n] !== 0) {
1676 $last = $n;
1677 break;
1678 }
1679 }
1680 $n = $first;
1681 $band = self::BANDS[$n];
1682 if ($last < 0) {
1683 $this->tok($type, $band, $ctx, 0, 0);
1684 return 0;
1685 }
1686 while (true) {
1687 $this->tok($type, $band, $ctx, 0, 1);
1688 while ($lv[$n] === 0) {
1689 $this->tok($type, $band, $ctx, 1, 0);
1690 $n++;
1691 $band = self::BANDS[$n];
1692 $ctx = 0;
1693 }
1694 $this->tok($type, $band, $ctx, 1, 1);
1695 $v = abs($lv[$n]);
1696 if ($v === 1) {
1697 $this->tok($type, $band, $ctx, 2, 0);
1698 $ctx = 1;
1699 } else {
1700 $this->tok($type, $band, $ctx, 2, 1);
1701 if ($v <= 4) {
1702 $this->tok($type, $band, $ctx, 3, 0);
1703 $this->tok($type, $band, $ctx, 4, $v === 2 ? 0 : 1);
1704 if ($v > 2) $this->tok($type, $band, $ctx, 5, $v - 3);
1705 } else if ($v <= 10) {
1706 $this->tok($type, $band, $ctx, 3, 1);
1707 $this->tok($type, $band, $ctx, 6, 0);
1708 if ($v <= 6) {
1709 $this->tok($type, $band, $ctx, 7, 0);
1710 $this->fixed(159, $v - 5);
1711 } else {
1712 $this->tok($type, $band, $ctx, 7, 1);
1713 $this->fixed(165, ($v - 7) >> 1);
1714 $this->fixed(145, ($v - 7) & 1);
1715 }
1716 } else {
1717 $this->tok($type, $band, $ctx, 3, 1);
1718 $this->tok($type, $band, $ctx, 6, 1);
1719 $cat = $v < 19 ? 0 : ($v < 35 ? 1 : ($v < 67 ? 2 : 3));
1720 $this->tok($type, $band, $ctx, 8, $cat >> 1);
1721 $this->tok($type, $band, $ctx, 9 + ($cat >> 1), $cat & 1);
1722 $extra = $v - 3 - (8 << $cat);
1723 $nb = count(self::CAT[$cat]);
1724 foreach (self::CAT[$cat] as $k => $prob) $this->fixed($prob, ($extra >> ($nb - 1 - $k)) & 1);
1725 }
1726 $ctx = 2;
1727 }
1728 $this->fixed(128, $lv[$n] < 0 ? 1 : 0);
1729 $n++;
1730 if ($n === 16) return 1;
1731 $band = self::BANDS[$n];
1732 if ($n > $last) {
1733 $this->tok($type, $band, $ctx, 0, 0);
1734 return 1;
1735 }
1736 }
1737 }
1738
1739 private function tok(int $t, int $b, int $c, int $i, int $bit): void {
1740 if ($this->stats !== null) {
1741 $this->stats[$t][$b][$c][$i][$bit] = ($this->stats[$t][$b][$c][$i][$bit] ?? 0) + 1;
1742 $this->stats[$t][$b][$c][$i][1 - $bit] ??= 0;
1743 return;
1744 }
1745 $this->put($this->probs[$t][$b][$c][$i], $bit);
1746 }
1747
1748 private function fixed(int $prob, int $bit): void {
1749 if ($this->stats === null) $this->put($prob, $bit);
1750 }
1751
1752 private function startWrite(): void {
1753 $this->out = '';
1754 $this->bottom = 0;
1755 $this->erange = 255;
1756 $this->shiftCount = 24;
1757 }
1758
1759 private function put(int $prob, int $bit): void {
1760 $split = 1 + ((($this->erange - 1) * $prob) >> 8);
1761 if ($bit) {
1762 $this->bottom += $split;
1763 $this->erange -= $split;
1764 } else {
1765 $this->erange = $split;
1766 }
1767 while ($this->erange < 128) {
1768 $this->erange <<= 1;
1769 if ($this->bottom & 0x80000000) $this->carry();
1770 $this->bottom = ($this->bottom << 1) & 0xffffffff;
1771 if (!--$this->shiftCount) {
1772 $this->out .= chr($this->bottom >> 24);
1773 $this->bottom &= 0xffffff;
1774 $this->shiftCount = 8;
1775 }
1776 }
1777 }
1778
1779 /** 桁上がりを書いた分に足す */
1780 private function carry(): void {
1781 for ($i = strlen($this->out) - 1; $i >= 0 && $this->out[$i] === "\xff"; $i--) $this->out[$i] = "\0";
1782 if ($i >= 0) $this->out[$i] = chr(ord($this->out[$i]) + 1);
1783 }
1784
1785 private function putValue(int $v, int $n): void {
1786 while ($n-- > 0) $this->put(128, ($v >> $n) & 1);
1787 }
1788
1789 private function finishWrite(): string {
1790 $c = $this->shiftCount;
1791 $v = $this->bottom;
1792 if ($v & (1 << (32 - $c))) $this->carry();
1793 $v = ($v << ($c & 7)) & 0xffffffff;
1794 for ($c >>= 3; --$c >= 0;) $v = ($v << 8) & 0xffffffff;
1795 for ($c = 0; $c < 4; $c++) {
1796 $this->out .= chr($v >> 24);
1797 $v = ($v << 8) & 0xffffffff;
1798 }
1799 return $this->out;
1800 }
1801
1802 //////// ループフィルター
1803
1804 private static function simpleFilter(string &$Y, int $stride, int $mx, int $my, int $limit, bool $inner): void {
1805 $o = $my * 16 * $stride + $mx * 16;
1806 if ($mx > 0) self::simpleEdge($Y, $o, 1, $stride, 2 * ($limit + 4) + 1);
1807 if ($inner) for ($k = 4; $k < 16; $k += 4) self::simpleEdge($Y, $o + $k, 1, $stride, 2 * $limit + 1);
1808 if ($my > 0) self::simpleEdge($Y, $o, $stride, 1, 2 * ($limit + 4) + 1);
1809 if ($inner) for ($k = 4; $k < 16; $k += 4) self::simpleEdge($Y, $o + $k * $stride, $stride, 1, 2 * $limit + 1);
1810 }
1811
1812 /**
1813 * @param int $hs 端を跨ぐ方向の1画素
1814 * @param int $vs 端に沿う方向の1画素
1815 */
1816 private static function simpleEdge(string &$p, int $o, int $hs, int $vs, int $t2): void {
1817 for ($i = 0; $i < 16; $i++, $o += $vs) {
1818 $p1 = ord($p[$o - 2 * $hs]);
1819 $p0 = ord($p[$o - $hs]);
1820 $q0 = ord($p[$o]);
1821 $q1 = ord($p[$o + $hs]);
1822 if (4 * abs($p0 - $q0) + abs($p1 - $q1) > $t2) continue;
1823 self::filter2($p, $o, $hs, $p1, $p0, $q0, $q1);
1824 }
1825 }
1826
1827 private static function normalFilter(string &$Y, string &$U, string &$V, int $yw, int $uw, int $mx, int $my, int $limit, int $il, int $hev, bool $inner): void {
1828 $oy = $my * 16 * $yw + $mx * 16;
1829 $ou = $my * 8 * $uw + $mx * 8;
1830 if ($mx > 0) {
1831 self::edge($Y, $oy, 1, $yw, 16, $limit + 4, $il, $hev, true);
1832 self::edge($U, $ou, 1, $uw, 8, $limit + 4, $il, $hev, true);
1833 self::edge($V, $ou, 1, $uw, 8, $limit + 4, $il, $hev, true);
1834 }
1835 if ($inner) {
1836 for ($k = 4; $k < 16; $k += 4) self::edge($Y, $oy + $k, 1, $yw, 16, $limit, $il, $hev, false);
1837 self::edge($U, $ou + 4, 1, $uw, 8, $limit, $il, $hev, false);
1838 self::edge($V, $ou + 4, 1, $uw, 8, $limit, $il, $hev, false);
1839 }
1840 if ($my > 0) {
1841 self::edge($Y, $oy, $yw, 1, 16, $limit + 4, $il, $hev, true);
1842 self::edge($U, $ou, $uw, 1, 8, $limit + 4, $il, $hev, true);
1843 self::edge($V, $ou, $uw, 1, 8, $limit + 4, $il, $hev, true);
1844 }
1845 if ($inner) {
1846 for ($k = 4; $k < 16; $k += 4) self::edge($Y, $oy + $k * $yw, $yw, 1, 16, $limit, $il, $hev, false);
1847 self::edge($U, $ou + 4 * $uw, $uw, 1, 8, $limit, $il, $hev, false);
1848 self::edge($V, $ou + 4 * $uw, $uw, 1, 8, $limit, $il, $hev, false);
1849 }
1850 }
1851
1852 /**
1853 * 通常のフィルター($mb:マクロブロックの端なら6画素、中なら4画素を変える)
1854 */
1855 private static function edge(string &$p, int $o, int $hs, int $vs, int $n, int $thresh, int $it, int $hevT, bool $mb): void {
1856 $t2 = 2 * $thresh + 1;
1857 for ($i = 0; $i < $n; $i++, $o += $vs) {
1858 $p1 = ord($p[$o - 2 * $hs]);
1859 $p0 = ord($p[$o - $hs]);
1860 $q0 = ord($p[$o]);
1861 $q1 = ord($p[$o + $hs]);
1862 if (4 * abs($p0 - $q0) + abs($p1 - $q1) > $t2) continue;
1863 $p3 = ord($p[$o - 4 * $hs]);
1864 $p2 = ord($p[$o - 3 * $hs]);
1865 $q2 = ord($p[$o + 2 * $hs]);
1866 $q3 = ord($p[$o + 3 * $hs]);
1867 if (abs($p3 - $p2) > $it || abs($p2 - $p1) > $it || abs($p1 - $p0) > $it || abs($q3 - $q2) > $it || abs($q2 - $q1) > $it || abs($q1 - $q0) > $it) continue;
1868 if (abs($p1 - $p0) > $hevT || abs($q1 - $q0) > $hevT) {
1869 self::filter2($p, $o, $hs, $p1, $p0, $q0, $q1);
1870 } else if ($mb) {
1871 $a = 3 * ($q0 - $p0) + self::sclip1($p1 - $q1);
1872 $a = $a < -128 ? -128 : ($a > 127 ? 127 : $a);
1873 $a1 = (27 * $a + 63) >> 7;
1874 $a2 = (18 * $a + 63) >> 7;
1875 $a3 = (9 * $a + 63) >> 7;
1876 $p[$o - 3 * $hs] = chr(self::clip($p2 + $a3));
1877 $p[$o - 2 * $hs] = chr(self::clip($p1 + $a2));
1878 $p[$o - $hs] = chr(self::clip($p0 + $a1));
1879 $p[$o] = chr(self::clip($q0 - $a1));
1880 $p[$o + $hs] = chr(self::clip($q1 - $a2));
1881 $p[$o + 2 * $hs] = chr(self::clip($q2 - $a3));
1882 } else {
1883 $a = 3 * ($q0 - $p0);
1884 $a1 = self::sclip2(($a + 4) >> 3);
1885 $a2 = self::sclip2(($a + 3) >> 3);
1886 $a3 = ($a1 + 1) >> 1;
1887 $p[$o - 2 * $hs] = chr(self::clip($p1 + $a3));
1888 $p[$o - $hs] = chr(self::clip($p0 + $a2));
1889 $p[$o] = chr(self::clip($q0 - $a1));
1890 $p[$o + $hs] = chr(self::clip($q1 - $a3));
1891 }
1892 }
1893 }
1894
1895 private static function filter2(string &$p, int $o, int $hs, int $p1, int $p0, int $q0, int $q1): void {
1896 $a = 3 * ($q0 - $p0) + self::sclip1($p1 - $q1);
1897 $a1 = self::sclip2(($a + 4) >> 3);
1898 $a2 = self::sclip2(($a + 3) >> 3);
1899 $p[$o - $hs] = chr(self::clip($p0 + $a2));
1900 $p[$o] = chr(self::clip($q0 - $a1));
1901 }
1902
1903 private static function sclip1(int $v): int {
1904 return $v < -128 ? -128 : ($v > 127 ? 127 : $v);
1905 }
1906
1907 private static function sclip2(int $v): int {
1908 return $v < -16 ? -16 : ($v > 15 ? 15 : $v);
1909 }
1910
1911 private static function clip(int $v): int {
1912 return $v < 0 ? 0 : ($v > 255 ? 255 : $v);
1913 }
1914
1915 //////// 色
1916
1917 /**
1918 * YUV 4:2:0をRGBAにする(色差は近い方の行と列に3:1の重みで補間)
1919 */
1920 private static function toRgba(string $Y, string $U, string $V, int $yw, int $uw, int $w, int $h): string {
1921 static $tab = null;
1922 if ($tab === null) {
1923 for ($i = 0; $i < 256; $i++) {
1924 $tab['y'][$i] = ($i * 19077) >> 8;
1925 $tab['rv'][$i] = (($i * 26149) >> 8) - 14234;
1926 $tab['gu'][$i] = ($i * 6419) >> 8;
1927 $tab['gv'][$i] = (($i * 13320) >> 8) - 8708;
1928 $tab['bu'][$i] = (($i * 33050) >> 8) - 17685;
1929 }
1930 // 14ビットの固定小数点を0~255にする
1931 for ($i = -20000; $i < 40000; $i++) $tab['clip'][$i] = $i < 0 ? 0 : ($i > 16383 ? 255 : $i >> 6);
1932 }
1933 ['y' => $ty, 'rv' => $trv, 'gu' => $tgu, 'gv' => $tgv, 'bu' => $tbu, 'clip' => $clip] = $tab;
1934 $rows = ($h + 1) >> 1;
1935 $cw = ($w + 1) >> 1;
1936 $last = ($w - 1) >> 1;
1937 $out = '';
1938 for ($y = 0; $y < $h; $y++) {
1939 if ($y === 0) {
1940 $n = $f = 0;
1941 } else if ($y & 1) {
1942 $n = ($y - 1) >> 1;
1943 $f = min($n + 1, $rows - 1);
1944 } else {
1945 $n = $y >> 1;
1946 $f = $n - 1;
1947 }
1948 $nu = array_values(unpack('C*', substr($U, $n * $uw, $cw)));
1949 $nv = array_values(unpack('C*', substr($V, $n * $uw, $cw)));
1950 $fu = array_values(unpack('C*', substr($U, $f * $uw, $cw)));
1951 $fv = array_values(unpack('C*', substr($V, $f * $uw, $cw)));
1952 $u = [(3 * $nu[0] + $fu[0] + 2) >> 2];
1953 $v = [(3 * $nv[0] + $fv[0] + 2) >> 2];
1954 for ($x = 1; $x <= $last; $x++) {
1955 $nl = $nu[$x - 1];
1956 $nr = $nu[$x];
1957 $fl = $fu[$x - 1];
1958 $fr = $fu[$x];
1959 $avg = $nl + $nr + $fl + $fr + 8;
1960 $u[2 * $x - 1] = ((($avg + 2 * ($nr + $fl)) >> 3) + $nl) >> 1;
1961 $u[2 * $x] = ((($avg + 2 * ($nl + $fr)) >> 3) + $nr) >> 1;
1962 $nl = $nv[$x - 1];
1963 $nr = $nv[$x];
1964 $fl = $fv[$x - 1];
1965 $fr = $fv[$x];
1966 $avg = $nl + $nr + $fl + $fr + 8;
1967 $v[2 * $x - 1] = ((($avg + 2 * ($nr + $fl)) >> 3) + $nl) >> 1;
1968 $v[2 * $x] = ((($avg + 2 * ($nl + $fr)) >> 3) + $nr) >> 1;
1969 }
1970 if (!($w & 1)) {
1971 $u[$w - 1] = (3 * $nu[$last] + $fu[$last] + 2) >> 2;
1972 $v[$w - 1] = (3 * $nv[$last] + $fv[$last] + 2) >> 2;
1973 }
1974 $row = [];
1975 $yrow = unpack('C*', substr($Y, $y * $yw, $w));
1976 for ($x = 0; $x < $w; $x++) {
1977 $l = $ty[$yrow[$x + 1]];
1978 $uu = $u[$x];
1979 $vv = $v[$x];
1980 $row[] = $clip[$l + $trv[$vv]];
1981 $row[] = $clip[$l - $tgu[$uu] - $tgv[$vv]];
1982 $row[] = $clip[$l + $tbu[$uu]];
1983 $row[] = 255;
1984 }
1985 $out .= pack('C*', ...$row);
1986 }
1987 return $out;
1988 }
1989}