1use crate::decoder::alpha::{apply_alpha_plane, decode_alpha_plane};
4use crate::decoder::header::parse_still_webp;
5use crate::decoder::vp8::{parse_macroblock_data, FilterType, MacroBlockData, MacroBlockDataFrame};
6use crate::decoder::vp8i::{
7 WebpFormat, B_DC_PRED, B_HD_PRED, B_HE_PRED, B_HU_PRED, B_LD_PRED, B_RD_PRED, B_TM_PRED,
8 B_VE_PRED, B_VL_PRED, B_VR_PRED, DC_PRED, H_PRED, TM_PRED, V_PRED,
9};
10use crate::decoder::DecoderError;
11
12const VP8_TRANSFORM_AC3_C1: i32 = 20_091;
13const VP8_TRANSFORM_AC3_C2: i32 = 35_468;
14
15const RGB_Y_COEFF: i32 = 19_077;
16const RGB_V_TO_R_COEFF: i32 = 26_149;
17const RGB_U_TO_G_COEFF: i32 = 6_419;
18const RGB_V_TO_G_COEFF: i32 = 13_320;
19const RGB_U_TO_B_COEFF: i32 = 33_050;
20const RGB_R_BIAS: i32 = 14_234;
21const RGB_G_BIAS: i32 = 8_708;
22const RGB_B_BIAS: i32 = 17_685;
23const YUV_FIX2: i32 = 6;
24const YUV_MASK2: i32 = (256 << YUV_FIX2) - 1;
25
26#[derive(Debug, Clone, PartialEq, Eq)]
28pub struct DecodedImage {
29 pub width: usize,
31 pub height: usize,
33 pub rgba: Vec<u8>,
35}
36
37#[derive(Debug, Clone, PartialEq, Eq)]
39pub struct DecodedYuvImage {
40 pub width: usize,
42 pub height: usize,
44 pub y_stride: usize,
46 pub uv_stride: usize,
48 pub y: Vec<u8>,
50 pub u: Vec<u8>,
52 pub v: Vec<u8>,
54}
55
56struct Planes {
57 width: usize,
58 height: usize,
59 y_stride: usize,
60 uv_stride: usize,
61 y: Vec<u8>,
62 u: Vec<u8>,
63 v: Vec<u8>,
64}
65
66impl Planes {
67 fn new(frame: &MacroBlockDataFrame) -> Self {
68 let y_stride = frame.frame.macroblock_width * 16;
69 let uv_stride = frame.frame.macroblock_width * 8;
70 let height = frame.frame.macroblock_height * 16;
71 let uv_height = frame.frame.macroblock_height * 8;
72 Self {
73 width: frame.frame.picture.width as usize,
74 height: frame.frame.picture.height as usize,
75 y_stride,
76 uv_stride,
77 y: vec![0; y_stride * height],
78 u: vec![0; uv_stride * uv_height],
79 v: vec![0; uv_stride * uv_height],
80 }
81 }
82
83 fn y_width(&self) -> usize {
84 self.y_stride
85 }
86
87 fn uv_width(&self) -> usize {
88 self.uv_stride
89 }
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq)]
93struct FilterInfo {
94 f_limit: u8,
95 f_ilevel: u8,
96 f_inner: bool,
97 hev_thresh: u8,
98}
99
100fn abs_diff(a: u8, b: u8) -> i32 {
101 (a as i32 - b as i32).abs()
102}
103
104fn clip_signed(value: i32) -> i32 {
105 value.clamp(-128, 127)
106}
107
108fn clip_filter_value(value: i32) -> i32 {
109 value.clamp(-16, 15)
110}
111
112fn do_filter2(plane: &mut [u8], pos: usize, step: usize) {
113 let p1 = plane[pos - 2 * step] as i32;
114 let p0 = plane[pos - step] as i32;
115 let q0 = plane[pos] as i32;
116 let q1 = plane[pos + step] as i32;
117 let a = 3 * (q0 - p0) + clip_signed(p1 - q1);
118 let a1 = clip_filter_value((a + 4) >> 3);
119 let a2 = clip_filter_value((a + 3) >> 3);
120 plane[pos - step] = clip_byte(p0 + a2);
121 plane[pos] = clip_byte(q0 - a1);
122}
123
124fn do_filter4(plane: &mut [u8], pos: usize, step: usize) {
125 let p1 = plane[pos - 2 * step] as i32;
126 let p0 = plane[pos - step] as i32;
127 let q0 = plane[pos] as i32;
128 let q1 = plane[pos + step] as i32;
129 let a = 3 * (q0 - p0);
130 let a1 = clip_filter_value((a + 4) >> 3);
131 let a2 = clip_filter_value((a + 3) >> 3);
132 let a3 = (a1 + 1) >> 1;
133 plane[pos - 2 * step] = clip_byte(p1 + a3);
134 plane[pos - step] = clip_byte(p0 + a2);
135 plane[pos] = clip_byte(q0 - a1);
136 plane[pos + step] = clip_byte(q1 - a3);
137}
138
139fn do_filter6(plane: &mut [u8], pos: usize, step: usize) {
140 let p2 = plane[pos - 3 * step] as i32;
141 let p1 = plane[pos - 2 * step] as i32;
142 let p0 = plane[pos - step] as i32;
143 let q0 = plane[pos] as i32;
144 let q1 = plane[pos + step] as i32;
145 let q2 = plane[pos + 2 * step] as i32;
146 let a = clip_signed(3 * (q0 - p0) + clip_signed(p1 - q1));
147 let a1 = (27 * a + 63) >> 7;
148 let a2 = (18 * a + 63) >> 7;
149 let a3 = (9 * a + 63) >> 7;
150 plane[pos - 3 * step] = clip_byte(p2 + a3);
151 plane[pos - 2 * step] = clip_byte(p1 + a2);
152 plane[pos - step] = clip_byte(p0 + a1);
153 plane[pos] = clip_byte(q0 - a1);
154 plane[pos + step] = clip_byte(q1 - a2);
155 plane[pos + 2 * step] = clip_byte(q2 - a3);
156}
157
158fn hev(plane: &[u8], pos: usize, step: usize, thresh: i32) -> bool {
159 let p1 = plane[pos - 2 * step];
160 let p0 = plane[pos - step];
161 let q0 = plane[pos];
162 let q1 = plane[pos + step];
163 abs_diff(p1, p0) > thresh || abs_diff(q1, q0) > thresh
164}
165
166fn needs_filter(plane: &[u8], pos: usize, step: usize, thresh: i32) -> bool {
167 let p1 = plane[pos - 2 * step];
168 let p0 = plane[pos - step];
169 let q0 = plane[pos];
170 let q1 = plane[pos + step];
171 4 * abs_diff(p0, q0) + abs_diff(p1, q1) <= thresh
172}
173
174fn needs_filter2(plane: &[u8], pos: usize, step: usize, thresh: i32, inner_thresh: i32) -> bool {
175 let p3 = plane[pos - 4 * step];
176 let p2 = plane[pos - 3 * step];
177 let p1 = plane[pos - 2 * step];
178 let p0 = plane[pos - step];
179 let q0 = plane[pos];
180 let q1 = plane[pos + step];
181 let q2 = plane[pos + 2 * step];
182 let q3 = plane[pos + 3 * step];
183 if 4 * abs_diff(p0, q0) + abs_diff(p1, q1) > thresh {
184 return false;
185 }
186 abs_diff(p3, p2) <= inner_thresh
187 && abs_diff(p2, p1) <= inner_thresh
188 && abs_diff(p1, p0) <= inner_thresh
189 && abs_diff(q3, q2) <= inner_thresh
190 && abs_diff(q2, q1) <= inner_thresh
191 && abs_diff(q1, q0) <= inner_thresh
192}
193
194fn simple_v_filter16(plane: &mut [u8], pos: usize, stride: usize, thresh: i32) {
195 let thresh2 = 2 * thresh + 1;
196 for i in 0..16 {
197 let edge = pos + i;
198 if needs_filter(plane, edge, stride, thresh2) {
199 do_filter2(plane, edge, stride);
200 }
201 }
202}
203
204fn simple_h_filter16(plane: &mut [u8], pos: usize, stride: usize, thresh: i32) {
205 let thresh2 = 2 * thresh + 1;
206 for i in 0..16 {
207 let edge = pos + i * stride;
208 if needs_filter(plane, edge, 1, thresh2) {
209 do_filter2(plane, edge, 1);
210 }
211 }
212}
213
214fn simple_v_filter16i(plane: &mut [u8], mut pos: usize, stride: usize, thresh: i32) {
215 for _ in (1..=3).rev() {
216 pos += 4 * stride;
217 simple_v_filter16(plane, pos, stride, thresh);
218 }
219}
220
221fn simple_h_filter16i(plane: &mut [u8], mut pos: usize, stride: usize, thresh: i32) {
222 for _ in (1..=3).rev() {
223 pos += 4;
224 simple_h_filter16(plane, pos, stride, thresh);
225 }
226}
227
228fn filter_loop26(
229 plane: &mut [u8],
230 mut pos: usize,
231 hstride: usize,
232 vstride: usize,
233 size: usize,
234 thresh: i32,
235 inner_thresh: i32,
236 hev_thresh: i32,
237) {
238 let thresh2 = 2 * thresh + 1;
239 for _ in 0..size {
240 if needs_filter2(plane, pos, hstride, thresh2, inner_thresh) {
241 if hev(plane, pos, hstride, hev_thresh) {
242 do_filter2(plane, pos, hstride);
243 } else {
244 do_filter6(plane, pos, hstride);
245 }
246 }
247 pos += vstride;
248 }
249}
250
251fn filter_loop24(
252 plane: &mut [u8],
253 mut pos: usize,
254 hstride: usize,
255 vstride: usize,
256 size: usize,
257 thresh: i32,
258 inner_thresh: i32,
259 hev_thresh: i32,
260) {
261 let thresh2 = 2 * thresh + 1;
262 for _ in 0..size {
263 if needs_filter2(plane, pos, hstride, thresh2, inner_thresh) {
264 if hev(plane, pos, hstride, hev_thresh) {
265 do_filter2(plane, pos, hstride);
266 } else {
267 do_filter4(plane, pos, hstride);
268 }
269 }
270 pos += vstride;
271 }
272}
273
274fn v_filter16(
275 plane: &mut [u8],
276 pos: usize,
277 stride: usize,
278 thresh: i32,
279 inner_thresh: i32,
280 hev_thresh: i32,
281) {
282 filter_loop26(plane, pos, stride, 1, 16, thresh, inner_thresh, hev_thresh);
283}
284
285fn h_filter16(
286 plane: &mut [u8],
287 pos: usize,
288 stride: usize,
289 thresh: i32,
290 inner_thresh: i32,
291 hev_thresh: i32,
292) {
293 filter_loop26(plane, pos, 1, stride, 16, thresh, inner_thresh, hev_thresh);
294}
295
296fn v_filter16i(
297 plane: &mut [u8],
298 mut pos: usize,
299 stride: usize,
300 thresh: i32,
301 inner_thresh: i32,
302 hev_thresh: i32,
303) {
304 for _ in (1..=3).rev() {
305 pos += 4 * stride;
306 filter_loop24(plane, pos, stride, 1, 16, thresh, inner_thresh, hev_thresh);
307 }
308}
309
310fn h_filter16i(
311 plane: &mut [u8],
312 mut pos: usize,
313 stride: usize,
314 thresh: i32,
315 inner_thresh: i32,
316 hev_thresh: i32,
317) {
318 for _ in (1..=3).rev() {
319 pos += 4;
320 filter_loop24(plane, pos, 1, stride, 16, thresh, inner_thresh, hev_thresh);
321 }
322}
323
324fn v_filter8(
325 plane_u: &mut [u8],
326 plane_v: &mut [u8],
327 pos: usize,
328 stride: usize,
329 thresh: i32,
330 inner_thresh: i32,
331 hev_thresh: i32,
332) {
333 filter_loop26(plane_u, pos, stride, 1, 8, thresh, inner_thresh, hev_thresh);
334 filter_loop26(plane_v, pos, stride, 1, 8, thresh, inner_thresh, hev_thresh);
335}
336
337fn h_filter8(
338 plane_u: &mut [u8],
339 plane_v: &mut [u8],
340 pos: usize,
341 stride: usize,
342 thresh: i32,
343 inner_thresh: i32,
344 hev_thresh: i32,
345) {
346 filter_loop26(plane_u, pos, 1, stride, 8, thresh, inner_thresh, hev_thresh);
347 filter_loop26(plane_v, pos, 1, stride, 8, thresh, inner_thresh, hev_thresh);
348}
349
350fn v_filter8i(
351 plane_u: &mut [u8],
352 plane_v: &mut [u8],
353 pos: usize,
354 stride: usize,
355 thresh: i32,
356 inner_thresh: i32,
357 hev_thresh: i32,
358) {
359 filter_loop24(
360 plane_u,
361 pos + 4 * stride,
362 stride,
363 1,
364 8,
365 thresh,
366 inner_thresh,
367 hev_thresh,
368 );
369 filter_loop24(
370 plane_v,
371 pos + 4 * stride,
372 stride,
373 1,
374 8,
375 thresh,
376 inner_thresh,
377 hev_thresh,
378 );
379}
380
381fn h_filter8i(
382 plane_u: &mut [u8],
383 plane_v: &mut [u8],
384 pos: usize,
385 stride: usize,
386 thresh: i32,
387 inner_thresh: i32,
388 hev_thresh: i32,
389) {
390 filter_loop24(
391 plane_u,
392 pos + 4,
393 1,
394 stride,
395 8,
396 thresh,
397 inner_thresh,
398 hev_thresh,
399 );
400 filter_loop24(
401 plane_v,
402 pos + 4,
403 1,
404 stride,
405 8,
406 thresh,
407 inner_thresh,
408 hev_thresh,
409 );
410}
411
412fn macroblock_filter_info(
413 frame: &MacroBlockDataFrame,
414 macroblock: &MacroBlockData,
415) -> Option<FilterInfo> {
416 let filter = &frame.frame.filter;
417 if filter.filter_type == FilterType::Off {
418 return None;
419 }
420
421 let segment = &frame.frame.segment;
422 let mut base_level = if segment.use_segment {
423 let level = segment.filter_strength[macroblock.header.segment as usize] as i32;
424 if segment.absolute_delta {
425 level
426 } else {
427 level + filter.level as i32
428 }
429 } else {
430 filter.level as i32
431 };
432
433 if filter.use_lf_delta {
434 base_level += filter.ref_lf_delta[0] as i32;
435 if macroblock.header.is_i4x4 {
436 base_level += filter.mode_lf_delta[0] as i32;
437 }
438 }
439
440 let level = base_level.clamp(0, 63);
441 if level == 0 {
442 return None;
443 }
444
445 let mut ilevel = level;
446 if filter.sharpness > 0 {
447 if filter.sharpness > 4 {
448 ilevel >>= 2;
449 } else {
450 ilevel >>= 1;
451 }
452 ilevel = ilevel.min(9 - filter.sharpness as i32);
453 }
454 if ilevel < 1 {
455 ilevel = 1;
456 }
457
458 Some(FilterInfo {
459 f_limit: (2 * level + ilevel) as u8,
460 f_ilevel: ilevel as u8,
461 f_inner: macroblock.header.is_i4x4 || (macroblock.non_zero_y | macroblock.non_zero_uv) != 0,
462 hev_thresh: if level >= 40 {
463 2
464 } else if level >= 15 {
465 1
466 } else {
467 0
468 },
469 })
470}
471
472fn filter_macroblock(
473 frame: &MacroBlockDataFrame,
474 planes: &mut Planes,
475 mb_x: usize,
476 mb_y: usize,
477 macroblock: &MacroBlockData,
478) {
479 let Some(info) = macroblock_filter_info(frame, macroblock) else {
480 return;
481 };
482
483 let y_pos = mb_y * 16 * planes.y_stride + mb_x * 16;
484 let uv_pos = mb_y * 8 * planes.uv_stride + mb_x * 8;
485 let limit = info.f_limit as i32;
486 let inner = info.f_ilevel as i32;
487 let hev = info.hev_thresh as i32;
488
489 match frame.frame.filter.filter_type {
490 FilterType::Off => {}
491 FilterType::Simple => {
492 if mb_x > 0 {
493 simple_h_filter16(&mut planes.y, y_pos, planes.y_stride, limit + 4);
494 }
495 if info.f_inner {
496 simple_h_filter16i(&mut planes.y, y_pos, planes.y_stride, limit);
497 }
498 if mb_y > 0 {
499 simple_v_filter16(&mut planes.y, y_pos, planes.y_stride, limit + 4);
500 }
501 if info.f_inner {
502 simple_v_filter16i(&mut planes.y, y_pos, planes.y_stride, limit);
503 }
504 }
505 FilterType::Complex => {
506 if mb_x > 0 {
507 h_filter16(&mut planes.y, y_pos, planes.y_stride, limit + 4, inner, hev);
508 h_filter8(
509 &mut planes.u,
510 &mut planes.v,
511 uv_pos,
512 planes.uv_stride,
513 limit + 4,
514 inner,
515 hev,
516 );
517 }
518 if info.f_inner {
519 h_filter16i(&mut planes.y, y_pos, planes.y_stride, limit, inner, hev);
520 h_filter8i(
521 &mut planes.u,
522 &mut planes.v,
523 uv_pos,
524 planes.uv_stride,
525 limit,
526 inner,
527 hev,
528 );
529 }
530 if mb_y > 0 {
531 v_filter16(&mut planes.y, y_pos, planes.y_stride, limit + 4, inner, hev);
532 v_filter8(
533 &mut planes.u,
534 &mut planes.v,
535 uv_pos,
536 planes.uv_stride,
537 limit + 4,
538 inner,
539 hev,
540 );
541 }
542 if info.f_inner {
543 v_filter16i(&mut planes.y, y_pos, planes.y_stride, limit, inner, hev);
544 v_filter8i(
545 &mut planes.u,
546 &mut planes.v,
547 uv_pos,
548 planes.uv_stride,
549 limit,
550 inner,
551 hev,
552 );
553 }
554 }
555 }
556}
557
558fn apply_loop_filter(frame: &MacroBlockDataFrame, planes: &mut Planes) {
559 if frame.frame.filter.filter_type == FilterType::Off {
560 return;
561 }
562
563 for mb_y in 0..frame.frame.macroblock_height {
564 for mb_x in 0..frame.frame.macroblock_width {
565 let macroblock = &frame.macroblocks[mb_y * frame.frame.macroblock_width + mb_x];
566 filter_macroblock(frame, planes, mb_x, mb_y, macroblock);
567 }
568 }
569}
570
571fn mul1(value: i32) -> i32 {
572 ((value * VP8_TRANSFORM_AC3_C1) >> 16) + value
573}
574
575fn mul2(value: i32) -> i32 {
576 (value * VP8_TRANSFORM_AC3_C2) >> 16
577}
578
579fn clip_byte(value: i32) -> u8 {
580 value.clamp(0, 255) as u8
581}
582
583fn avg2(a: u8, b: u8) -> u8 {
584 ((a as u16 + b as u16 + 1) >> 1) as u8
585}
586
587fn avg3(a: u8, b: u8, c: u8) -> u8 {
588 ((a as u16 + 2 * b as u16 + c as u16 + 2) >> 2) as u8
589}
590
591fn top_left_sample(plane: &[u8], stride: usize, x: usize, y: usize) -> u8 {
592 if y == 0 {
593 127
594 } else if x == 0 {
595 129
596 } else {
597 plane[(y - 1) * stride + (x - 1)]
598 }
599}
600
601fn top_samples<const N: usize>(
602 plane: &[u8],
603 stride: usize,
604 plane_width: usize,
605 x: usize,
606 y: usize,
607) -> [u8; N] {
608 let mut out = [0u8; N];
609 if y == 0 {
610 out.fill(127);
611 return out;
612 }
613 let row = (y - 1) * stride;
614 for (i, sample) in out.iter_mut().enumerate() {
615 let src_x = (x + i).min(plane_width - 1);
616 *sample = plane[row + src_x];
617 }
618 out
619}
620
621fn top_samples_luma4(
622 plane: &[u8],
623 stride: usize,
624 plane_width: usize,
625 x: usize,
626 y: usize,
627) -> [u8; 8] {
628 let mut out = [0u8; 8];
629 if y == 0 {
630 out.fill(127);
631 return out;
632 }
633
634 let row = (y - 1) * stride;
635 for (i, sample) in out.iter_mut().enumerate().take(4) {
636 let src_x = (x + i).min(plane_width - 1);
637 *sample = plane[row + src_x];
638 }
639
640 let local_x = x & 15;
641 let local_y = y & 15;
642 if local_x == 12 && local_y != 0 {
643 let macroblock_y = y - local_y;
644 if macroblock_y == 0 {
645 out[4..].fill(127);
646 } else {
647 let top_row = (macroblock_y - 1) * stride;
648 for (i, sample) in out.iter_mut().enumerate().skip(4) {
649 let src_x = (x + i).min(plane_width - 1);
650 *sample = plane[top_row + src_x];
651 }
652 }
653 } else {
654 for (i, sample) in out.iter_mut().enumerate().skip(4) {
655 let src_x = (x + i).min(plane_width - 1);
656 *sample = plane[row + src_x];
657 }
658 }
659 out
660}
661
662fn left_samples<const N: usize>(plane: &[u8], stride: usize, x: usize, y: usize) -> [u8; N] {
663 let mut out = [0u8; N];
664 if x == 0 {
665 out.fill(129);
666 return out;
667 }
668 let src_x = x - 1;
669 for (i, sample) in out.iter_mut().enumerate() {
670 *sample = plane[(y + i) * stride + src_x];
671 }
672 out
673}
674
675fn fill_block(
676 plane: &mut [u8],
677 stride: usize,
678 x: usize,
679 y: usize,
680 width: usize,
681 height: usize,
682 value: u8,
683) {
684 for row in 0..height {
685 let offset = (y + row) * stride + x;
686 plane[offset..offset + width].fill(value);
687 }
688}
689
690fn predict_true_motion(
691 plane: &mut [u8],
692 stride: usize,
693 plane_width: usize,
694 x: usize,
695 y: usize,
696 size: usize,
697) {
698 let top = if size == 4 {
699 top_samples::<4>(plane, stride, plane_width, x, y).to_vec()
700 } else if size == 8 {
701 top_samples::<8>(plane, stride, plane_width, x, y).to_vec()
702 } else {
703 top_samples::<16>(plane, stride, plane_width, x, y).to_vec()
704 };
705 let left = if size == 4 {
706 left_samples::<4>(plane, stride, x, y).to_vec()
707 } else if size == 8 {
708 left_samples::<8>(plane, stride, x, y).to_vec()
709 } else {
710 left_samples::<16>(plane, stride, x, y).to_vec()
711 };
712 let top_left = top_left_sample(plane, stride, x, y) as i32;
713 for row in 0..size {
714 let left_value = left[row] as i32;
715 let offset = (y + row) * stride + x;
716 for col in 0..size {
717 plane[offset + col] = clip_byte(left_value + top[col] as i32 - top_left);
718 }
719 }
720}
721
722fn predict_luma16(
723 plane: &mut [u8],
724 stride: usize,
725 plane_width: usize,
726 x: usize,
727 y: usize,
728 mode: u8,
729) -> Result<(), DecoderError> {
730 match mode {
731 DC_PRED => {
732 let has_top = y > 0;
733 let has_left = x > 0;
734 let value = match (has_top, has_left) {
735 (true, true) => {
736 let top = top_samples::<16>(plane, stride, plane_width, x, y);
737 let left = left_samples::<16>(plane, stride, x, y);
738 let sum_top: u32 = top.into_iter().map(u32::from).sum();
739 let sum_left: u32 = left.into_iter().map(u32::from).sum();
740 ((sum_top + sum_left + 16) >> 5) as u8
741 }
742 (true, false) => {
743 let top = top_samples::<16>(plane, stride, plane_width, x, y);
744 let sum_top: u32 = top.into_iter().map(u32::from).sum();
745 ((sum_top + 8) >> 4) as u8
746 }
747 (false, true) => {
748 let left = left_samples::<16>(plane, stride, x, y);
749 let sum_left: u32 = left.into_iter().map(u32::from).sum();
750 ((sum_left + 8) >> 4) as u8
751 }
752 (false, false) => 128,
753 };
754 fill_block(plane, stride, x, y, 16, 16, value);
755 }
756 TM_PRED => predict_true_motion(plane, stride, plane_width, x, y, 16),
757 V_PRED => {
758 let top = top_samples::<16>(plane, stride, plane_width, x, y);
759 for row in 0..16 {
760 let offset = (y + row) * stride + x;
761 plane[offset..offset + 16].copy_from_slice(&top);
762 }
763 }
764 H_PRED => {
765 let left = left_samples::<16>(plane, stride, x, y);
766 for (row, value) in left.into_iter().enumerate() {
767 let offset = (y + row) * stride + x;
768 plane[offset..offset + 16].fill(value);
769 }
770 }
771 _ => return Err(DecoderError::Bitstream("invalid luma prediction mode")),
772 }
773 Ok(())
774}
775
776fn predict_chroma8(
777 plane: &mut [u8],
778 stride: usize,
779 plane_width: usize,
780 x: usize,
781 y: usize,
782 mode: u8,
783) -> Result<(), DecoderError> {
784 match mode {
785 DC_PRED => {
786 let has_top = y > 0;
787 let has_left = x > 0;
788 let value = match (has_top, has_left) {
789 (true, true) => {
790 let top = top_samples::<8>(plane, stride, plane_width, x, y);
791 let left = left_samples::<8>(plane, stride, x, y);
792 let sum_top: u32 = top.into_iter().map(u32::from).sum();
793 let sum_left: u32 = left.into_iter().map(u32::from).sum();
794 ((sum_top + sum_left + 8) >> 4) as u8
795 }
796 (true, false) => {
797 let top = top_samples::<8>(plane, stride, plane_width, x, y);
798 let sum_top: u32 = top.into_iter().map(u32::from).sum();
799 ((sum_top + 4) >> 3) as u8
800 }
801 (false, true) => {
802 let left = left_samples::<8>(plane, stride, x, y);
803 let sum_left: u32 = left.into_iter().map(u32::from).sum();
804 ((sum_left + 4) >> 3) as u8
805 }
806 (false, false) => 128,
807 };
808 fill_block(plane, stride, x, y, 8, 8, value);
809 }
810 TM_PRED => predict_true_motion(plane, stride, plane_width, x, y, 8),
811 V_PRED => {
812 let top = top_samples::<8>(plane, stride, plane_width, x, y);
813 for row in 0..8 {
814 let offset = (y + row) * stride + x;
815 plane[offset..offset + 8].copy_from_slice(&top);
816 }
817 }
818 H_PRED => {
819 let left = left_samples::<8>(plane, stride, x, y);
820 for (row, value) in left.into_iter().enumerate() {
821 let offset = (y + row) * stride + x;
822 plane[offset..offset + 8].fill(value);
823 }
824 }
825 _ => return Err(DecoderError::Bitstream("invalid chroma prediction mode")),
826 }
827 Ok(())
828}
829
830fn predict_luma4(
831 plane: &mut [u8],
832 stride: usize,
833 plane_width: usize,
834 x: usize,
835 y: usize,
836 mode: u8,
837) -> Result<(), DecoderError> {
838 let x0 = top_left_sample(plane, stride, x, y);
839 let top = top_samples_luma4(plane, stride, plane_width, x, y);
840 let left = left_samples::<4>(plane, stride, x, y);
841
842 let a = top[0];
843 let b = top[1];
844 let c = top[2];
845 let d = top[3];
846 let e = top[4];
847 let f = top[5];
848 let g = top[6];
849 let h = top[7];
850 let i = left[0];
851 let j = left[1];
852 let k = left[2];
853 let l = left[3];
854
855 let mut block = [0u8; 16];
856 match mode {
857 B_DC_PRED => {
858 let sum_top: u32 = [a, b, c, d].into_iter().map(u32::from).sum();
859 let sum_left: u32 = [i, j, k, l].into_iter().map(u32::from).sum();
860 let dc = ((sum_top + sum_left + 4) >> 3) as u8;
861 block.fill(dc);
862 }
863 B_TM_PRED => {
864 let top_left = x0 as i32;
865 for row in 0..4 {
866 let left_value = left[row] as i32;
867 for col in 0..4 {
868 block[row * 4 + col] = clip_byte(left_value + top[col] as i32 - top_left);
869 }
870 }
871 }
872 B_VE_PRED => {
873 let vals = [avg3(x0, a, b), avg3(a, b, c), avg3(b, c, d), avg3(c, d, e)];
874 for row in 0..4 {
875 block[row * 4..row * 4 + 4].copy_from_slice(&vals);
876 }
877 }
878 B_HE_PRED => {
879 let vals = [avg3(x0, i, j), avg3(i, j, k), avg3(j, k, l), avg3(k, l, l)];
880 for (row, value) in vals.into_iter().enumerate() {
881 block[row * 4..row * 4 + 4].fill(value);
882 }
883 }
884 B_RD_PRED => {
885 block[12] = avg3(j, k, l);
886 block[13] = avg3(i, j, k);
887 block[8] = block[13];
888 block[14] = avg3(x0, i, j);
889 block[9] = block[14];
890 block[4] = block[14];
891 block[15] = avg3(a, x0, i);
892 block[10] = block[15];
893 block[5] = block[15];
894 block[0] = block[15];
895 block[11] = avg3(b, a, x0);
896 block[6] = block[11];
897 block[1] = block[11];
898 block[7] = avg3(c, b, a);
899 block[2] = block[7];
900 block[3] = avg3(d, c, b);
901 }
902 B_LD_PRED => {
903 block[0] = avg3(a, b, c);
904 block[1] = avg3(b, c, d);
905 block[4] = block[1];
906 block[2] = avg3(c, d, e);
907 block[5] = block[2];
908 block[8] = block[2];
909 block[3] = avg3(d, e, f);
910 block[6] = block[3];
911 block[9] = block[3];
912 block[12] = block[3];
913 block[7] = avg3(e, f, g);
914 block[10] = block[7];
915 block[13] = block[7];
916 block[11] = avg3(f, g, h);
917 block[14] = block[11];
918 block[15] = avg3(g, h, h);
919 }
920 B_VR_PRED => {
921 block[0] = avg2(x0, a);
922 block[9] = block[0];
923 block[1] = avg2(a, b);
924 block[10] = block[1];
925 block[2] = avg2(b, c);
926 block[11] = block[2];
927 block[3] = avg2(c, d);
928 block[12] = avg3(k, j, i);
929 block[8] = avg3(j, i, x0);
930 block[4] = avg3(i, x0, a);
931 block[13] = block[4];
932 block[5] = avg3(x0, a, b);
933 block[14] = block[5];
934 block[6] = avg3(a, b, c);
935 block[15] = block[6];
936 block[7] = avg3(b, c, d);
937 }
938 B_VL_PRED => {
939 block[0] = avg2(a, b);
940 block[1] = avg2(b, c);
941 block[2] = avg2(c, d);
942 block[3] = avg2(d, e);
943 block[4] = avg3(a, b, c);
944 block[5] = avg3(b, c, d);
945 block[6] = avg3(c, d, e);
946 block[7] = avg3(d, e, f);
947 block[8] = block[1];
948 block[9] = block[2];
949 block[10] = block[3];
950 block[11] = avg3(e, f, g);
951 block[12] = block[5];
952 block[13] = block[6];
953 block[14] = block[7];
954 block[15] = avg3(f, g, h);
955 }
956 B_HD_PRED => {
957 block[0] = avg2(i, x0);
958 block[1] = avg3(i, x0, a);
959 block[2] = avg3(x0, a, b);
960 block[3] = avg3(a, b, c);
961 block[4] = avg2(j, i);
962 block[5] = avg3(j, i, x0);
963 block[6] = block[0];
964 block[7] = block[1];
965 block[8] = avg2(k, j);
966 block[9] = avg3(k, j, i);
967 block[10] = block[4];
968 block[11] = block[5];
969 block[12] = avg2(l, k);
970 block[13] = avg3(l, k, j);
971 block[14] = block[8];
972 block[15] = block[9];
973 }
974 B_HU_PRED => {
975 block[0] = avg2(i, j);
976 block[2] = avg2(j, k);
977 block[4] = block[2];
978 block[6] = avg2(k, l);
979 block[8] = block[6];
980 block[1] = avg3(i, j, k);
981 block[3] = avg3(j, k, l);
982 block[5] = block[3];
983 block[7] = avg3(k, l, l);
984 block[9] = block[7];
985 block[11] = l;
986 block[10] = l;
987 block[12] = l;
988 block[13] = l;
989 block[14] = l;
990 block[15] = l;
991 }
992 _ => return Err(DecoderError::Bitstream("invalid 4x4 prediction mode")),
993 }
994
995 for row in 0..4 {
996 let offset = (y + row) * stride + x;
997 plane[offset..offset + 4].copy_from_slice(&block[row * 4..row * 4 + 4]);
998 }
999 Ok(())
1000}
1001
1002fn add_transform(plane: &mut [u8], stride: usize, x: usize, y: usize, coeffs: &[i16]) {
1003 if coeffs.iter().all(|&coeff| coeff == 0) {
1004 return;
1005 }
1006
1007 let mut tmp = [0i32; 16];
1008 for i in 0..4 {
1009 let a = coeffs[i] as i32 + coeffs[8 + i] as i32;
1010 let b = coeffs[i] as i32 - coeffs[8 + i] as i32;
1011 let c = mul2(coeffs[4 + i] as i32) - mul1(coeffs[12 + i] as i32);
1012 let d = mul1(coeffs[4 + i] as i32) + mul2(coeffs[12 + i] as i32);
1013 let base = i * 4;
1014 tmp[base] = a + d;
1015 tmp[base + 1] = b + c;
1016 tmp[base + 2] = b - c;
1017 tmp[base + 3] = a - d;
1018 }
1019
1020 for row in 0..4 {
1021 let dc = tmp[row] + 4;
1022 let a = dc + tmp[8 + row];
1023 let b = dc - tmp[8 + row];
1024 let c = mul2(tmp[4 + row]) - mul1(tmp[12 + row]);
1025 let d = mul1(tmp[4 + row]) + mul2(tmp[12 + row]);
1026 let offset = (y + row) * stride + x;
1027 plane[offset] = clip_byte(plane[offset] as i32 + ((a + d) >> 3));
1028 plane[offset + 1] = clip_byte(plane[offset + 1] as i32 + ((b + c) >> 3));
1029 plane[offset + 2] = clip_byte(plane[offset + 2] as i32 + ((b - c) >> 3));
1030 plane[offset + 3] = clip_byte(plane[offset + 3] as i32 + ((a - d) >> 3));
1031 }
1032}
1033
1034fn reconstruct_macroblock(
1035 planes: &mut Planes,
1036 mb_x: usize,
1037 mb_y: usize,
1038 macroblock: &MacroBlockData,
1039) -> Result<(), DecoderError> {
1040 let y_x = mb_x * 16;
1041 let y_y = mb_y * 16;
1042 let y_width = planes.y_width();
1043 let uv_width = planes.uv_width();
1044
1045 if macroblock.header.is_i4x4 {
1046 for sub_y in 0..4 {
1047 for sub_x in 0..4 {
1048 let block_index = sub_y * 4 + sub_x;
1049 let dst_x = y_x + sub_x * 4;
1050 let dst_y = y_y + sub_y * 4;
1051 predict_luma4(
1052 &mut planes.y,
1053 planes.y_stride,
1054 y_width,
1055 dst_x,
1056 dst_y,
1057 macroblock.header.sub_modes[block_index],
1058 )?;
1059 let coeff_offset = block_index * 16;
1060 add_transform(
1061 &mut planes.y,
1062 planes.y_stride,
1063 dst_x,
1064 dst_y,
1065 ¯oblock.coeffs[coeff_offset..coeff_offset + 16],
1066 );
1067 }
1068 }
1069 } else {
1070 predict_luma16(
1071 &mut planes.y,
1072 planes.y_stride,
1073 y_width,
1074 y_x,
1075 y_y,
1076 macroblock.header.luma_mode,
1077 )?;
1078 for sub_y in 0..4 {
1079 for sub_x in 0..4 {
1080 let block_index = sub_y * 4 + sub_x;
1081 let coeff_offset = block_index * 16;
1082 add_transform(
1083 &mut planes.y,
1084 planes.y_stride,
1085 y_x + sub_x * 4,
1086 y_y + sub_y * 4,
1087 ¯oblock.coeffs[coeff_offset..coeff_offset + 16],
1088 );
1089 }
1090 }
1091 }
1092
1093 let uv_x = mb_x * 8;
1094 let uv_y = mb_y * 8;
1095 predict_chroma8(
1096 &mut planes.u,
1097 planes.uv_stride,
1098 uv_width,
1099 uv_x,
1100 uv_y,
1101 macroblock.header.uv_mode,
1102 )?;
1103 predict_chroma8(
1104 &mut planes.v,
1105 planes.uv_stride,
1106 uv_width,
1107 uv_x,
1108 uv_y,
1109 macroblock.header.uv_mode,
1110 )?;
1111 for sub_y in 0..2 {
1112 for sub_x in 0..2 {
1113 let block_index = sub_y * 2 + sub_x;
1114 let dst_x = uv_x + sub_x * 4;
1115 let dst_y = uv_y + sub_y * 4;
1116 let u_offset = 16 * 16 + block_index * 16;
1117 let v_offset = 20 * 16 + block_index * 16;
1118 add_transform(
1119 &mut planes.u,
1120 planes.uv_stride,
1121 dst_x,
1122 dst_y,
1123 ¯oblock.coeffs[u_offset..u_offset + 16],
1124 );
1125 add_transform(
1126 &mut planes.v,
1127 planes.uv_stride,
1128 dst_x,
1129 dst_y,
1130 ¯oblock.coeffs[v_offset..v_offset + 16],
1131 );
1132 }
1133 }
1134
1135 Ok(())
1136}
1137
1138fn reconstruct_planes(frame: &MacroBlockDataFrame) -> Result<Planes, DecoderError> {
1139 let expected = frame.frame.macroblock_width * frame.frame.macroblock_height;
1140 if frame.macroblocks.len() != expected {
1141 return Err(DecoderError::Bitstream("macroblock count mismatch"));
1142 }
1143
1144 let mut planes = Planes::new(frame);
1145 for mb_y in 0..frame.frame.macroblock_height {
1146 for mb_x in 0..frame.frame.macroblock_width {
1147 let macroblock = &frame.macroblocks[mb_y * frame.frame.macroblock_width + mb_x];
1148 reconstruct_macroblock(&mut planes, mb_x, mb_y, macroblock)?;
1149 }
1150 }
1151 apply_loop_filter(frame, &mut planes);
1152 Ok(planes)
1153}
1154
1155fn mult_hi(value: i32, coeff: i32) -> i32 {
1156 (value * coeff) >> 8
1157}
1158
1159fn clip_rgb(value: i32) -> u8 {
1160 if (value & !YUV_MASK2) == 0 {
1161 (value >> YUV_FIX2) as u8
1162 } else if value < 0 {
1163 0
1164 } else {
1165 255
1166 }
1167}
1168
1169fn write_rgba(yy: u8, u: i32, v: i32, dst: &mut [u8], offset: usize) {
1170 let yy = yy as i32;
1171 dst[offset] = clip_rgb(mult_hi(yy, RGB_Y_COEFF) + mult_hi(v, RGB_V_TO_R_COEFF) - RGB_R_BIAS);
1172 dst[offset + 1] = clip_rgb(
1173 mult_hi(yy, RGB_Y_COEFF) - mult_hi(u, RGB_U_TO_G_COEFF) - mult_hi(v, RGB_V_TO_G_COEFF)
1174 + RGB_G_BIAS,
1175 );
1176 dst[offset + 2] =
1177 clip_rgb(mult_hi(yy, RGB_Y_COEFF) + mult_hi(u, RGB_U_TO_B_COEFF) - RGB_B_BIAS);
1178 dst[offset + 3] = 255;
1179}
1180
1181fn upsample_rgba_line_pair(
1182 top_y: &[u8],
1183 bottom_y: Option<&[u8]>,
1184 top_u: &[u8],
1185 top_v: &[u8],
1186 cur_u: &[u8],
1187 cur_v: &[u8],
1188 rgba: &mut [u8],
1189 top_offset: usize,
1190 bottom_offset: Option<usize>,
1191 len: usize,
1192) {
1193 let last_pixel_pair = (len - 1) >> 1;
1194 let mut tl_u = top_u[0] as i32;
1195 let mut tl_v = top_v[0] as i32;
1196 let mut l_u = cur_u[0] as i32;
1197 let mut l_v = cur_v[0] as i32;
1198
1199 let uv0_u = (3 * tl_u + l_u + 2) >> 2;
1200 let uv0_v = (3 * tl_v + l_v + 2) >> 2;
1201 write_rgba(top_y[0], uv0_u, uv0_v, rgba, top_offset);
1202 if let (Some(row), Some(offset)) = (bottom_y, bottom_offset) {
1203 let uv0_u = (3 * l_u + tl_u + 2) >> 2;
1204 let uv0_v = (3 * l_v + tl_v + 2) >> 2;
1205 write_rgba(row[0], uv0_u, uv0_v, rgba, offset);
1206 }
1207
1208 for x in 1..=last_pixel_pair {
1209 let t_u = top_u[x] as i32;
1210 let t_v = top_v[x] as i32;
1211 let u = cur_u[x] as i32;
1212 let v = cur_v[x] as i32;
1213
1214 let avg_u = tl_u + t_u + l_u + u + 8;
1215 let avg_v = tl_v + t_v + l_v + v + 8;
1216 let diag_12_u = (avg_u + 2 * (t_u + l_u)) >> 3;
1217 let diag_12_v = (avg_v + 2 * (t_v + l_v)) >> 3;
1218 let diag_03_u = (avg_u + 2 * (tl_u + u)) >> 3;
1219 let diag_03_v = (avg_v + 2 * (tl_v + v)) >> 3;
1220
1221 let top_left = (2 * x - 1) * 4;
1222 let top_right = 2 * x * 4;
1223 write_rgba(
1224 top_y[2 * x - 1],
1225 (diag_12_u + tl_u) >> 1,
1226 (diag_12_v + tl_v) >> 1,
1227 rgba,
1228 top_offset + top_left,
1229 );
1230 write_rgba(
1231 top_y[2 * x],
1232 (diag_03_u + t_u) >> 1,
1233 (diag_03_v + t_v) >> 1,
1234 rgba,
1235 top_offset + top_right,
1236 );
1237
1238 if let (Some(row), Some(offset)) = (bottom_y, bottom_offset) {
1239 write_rgba(
1240 row[2 * x - 1],
1241 (diag_03_u + l_u) >> 1,
1242 (diag_03_v + l_v) >> 1,
1243 rgba,
1244 offset + top_left,
1245 );
1246 write_rgba(
1247 row[2 * x],
1248 (diag_12_u + u) >> 1,
1249 (diag_12_v + v) >> 1,
1250 rgba,
1251 offset + top_right,
1252 );
1253 }
1254
1255 tl_u = t_u;
1256 tl_v = t_v;
1257 l_u = u;
1258 l_v = v;
1259 }
1260
1261 if len & 1 == 0 {
1262 let last = (len - 1) * 4;
1263 let uv0_u = (3 * tl_u + l_u + 2) >> 2;
1264 let uv0_v = (3 * tl_v + l_v + 2) >> 2;
1265 write_rgba(top_y[len - 1], uv0_u, uv0_v, rgba, top_offset + last);
1266 if let (Some(row), Some(offset)) = (bottom_y, bottom_offset) {
1267 let uv0_u = (3 * l_u + tl_u + 2) >> 2;
1268 let uv0_v = (3 * l_v + tl_v + 2) >> 2;
1269 write_rgba(row[len - 1], uv0_u, uv0_v, rgba, offset + last);
1270 }
1271 }
1272}
1273
1274fn yuv_to_rgba_fancy(planes: &Planes) -> Vec<u8> {
1275 let mut rgba = vec![0u8; planes.width * planes.height * 4];
1276 if planes.width == 0 || planes.height == 0 {
1277 return rgba;
1278 }
1279
1280 let uv_width = planes.width.div_ceil(2);
1281 let uv_height = planes.height.div_ceil(2);
1282
1283 let top_y = &planes.y[..planes.width];
1284 let top_u = &planes.u[..uv_width];
1285 let top_v = &planes.v[..uv_width];
1286 upsample_rgba_line_pair(
1287 top_y,
1288 None,
1289 top_u,
1290 top_v,
1291 top_u,
1292 top_v,
1293 &mut rgba,
1294 0,
1295 None,
1296 planes.width,
1297 );
1298
1299 for uv_row in 1..uv_height {
1300 let top_row = 2 * uv_row - 1;
1301 let bottom_row = top_row + 1;
1302 if bottom_row >= planes.height {
1303 break;
1304 }
1305 let top_y = &planes.y[top_row * planes.y_stride..top_row * planes.y_stride + planes.width];
1306 let bottom_y =
1307 &planes.y[bottom_row * planes.y_stride..bottom_row * planes.y_stride + planes.width];
1308 let prev_u =
1309 &planes.u[(uv_row - 1) * planes.uv_stride..(uv_row - 1) * planes.uv_stride + uv_width];
1310 let prev_v =
1311 &planes.v[(uv_row - 1) * planes.uv_stride..(uv_row - 1) * planes.uv_stride + uv_width];
1312 let cur_u = &planes.u[uv_row * planes.uv_stride..uv_row * planes.uv_stride + uv_width];
1313 let cur_v = &planes.v[uv_row * planes.uv_stride..uv_row * planes.uv_stride + uv_width];
1314 upsample_rgba_line_pair(
1315 top_y,
1316 Some(bottom_y),
1317 prev_u,
1318 prev_v,
1319 cur_u,
1320 cur_v,
1321 &mut rgba,
1322 top_row * planes.width * 4,
1323 Some(bottom_row * planes.width * 4),
1324 planes.width,
1325 );
1326 }
1327
1328 if planes.height > 1 && planes.height & 1 == 0 {
1329 let last_row = planes.height - 1;
1330 let uv_row = uv_height - 1;
1331 let y_row =
1332 &planes.y[last_row * planes.y_stride..last_row * planes.y_stride + planes.width];
1333 let u_row = &planes.u[uv_row * planes.uv_stride..uv_row * planes.uv_stride + uv_width];
1334 let v_row = &planes.v[uv_row * planes.uv_stride..uv_row * planes.uv_stride + uv_width];
1335 upsample_rgba_line_pair(
1336 y_row,
1337 None,
1338 u_row,
1339 v_row,
1340 u_row,
1341 v_row,
1342 &mut rgba,
1343 last_row * planes.width * 4,
1344 None,
1345 planes.width,
1346 );
1347 }
1348
1349 rgba
1350}
1351
1352fn into_decoded_yuv(planes: Planes) -> DecodedYuvImage {
1353 DecodedYuvImage {
1354 width: planes.width,
1355 height: planes.height,
1356 y_stride: planes.y_stride,
1357 uv_stride: planes.uv_stride,
1358 y: planes.y,
1359 u: planes.u,
1360 v: planes.v,
1361 }
1362}
1363
1364pub fn decode_lossy_vp8_to_yuv(data: &[u8]) -> Result<DecodedYuvImage, DecoderError> {
1366 let frame = parse_macroblock_data(data)?;
1367 let planes = reconstruct_planes(&frame)?;
1368 Ok(into_decoded_yuv(planes))
1369}
1370
1371pub fn decode_lossy_vp8_to_rgba(data: &[u8]) -> Result<DecodedImage, DecoderError> {
1373 let yuv = decode_lossy_vp8_to_yuv(data)?;
1374 Ok(DecodedImage {
1375 width: yuv.width,
1376 height: yuv.height,
1377 rgba: yuv_to_rgba_fancy(&Planes {
1378 width: yuv.width,
1379 height: yuv.height,
1380 y_stride: yuv.y_stride,
1381 uv_stride: yuv.uv_stride,
1382 y: yuv.y,
1383 u: yuv.u,
1384 v: yuv.v,
1385 }),
1386 })
1387}
1388
1389pub(crate) fn apply_lossy_alpha(
1390 image: &mut DecodedImage,
1391 alpha_data: &[u8],
1392) -> Result<(), DecoderError> {
1393 let alpha = decode_alpha_plane(alpha_data, image.width, image.height)?;
1394 apply_alpha_plane(&mut image.rgba, &alpha)
1395}
1396
1397pub(crate) fn decode_lossy_vp8_frame_to_rgba(
1398 data: &[u8],
1399 alpha_data: Option<&[u8]>,
1400) -> Result<DecodedImage, DecoderError> {
1401 let mut image = decode_lossy_vp8_to_rgba(data)?;
1402 if let Some(alpha_data) = alpha_data {
1403 apply_lossy_alpha(&mut image, alpha_data)?;
1404 }
1405 Ok(image)
1406}
1407
1408pub fn decode_lossy_webp_to_rgba(data: &[u8]) -> Result<DecodedImage, DecoderError> {
1413 let parsed = parse_still_webp(data)?;
1414 if parsed.features.format != WebpFormat::Lossy {
1415 return Err(DecoderError::Unsupported(
1416 "only still lossy WebP is supported",
1417 ));
1418 }
1419 decode_lossy_vp8_frame_to_rgba(parsed.image_data, parsed.alpha_data)
1420}
1421
1422pub fn decode_lossy_webp_to_yuv(data: &[u8]) -> Result<DecodedYuvImage, DecoderError> {
1427 let parsed = parse_still_webp(data)?;
1428 if parsed.features.format != WebpFormat::Lossy {
1429 return Err(DecoderError::Unsupported(
1430 "only still lossy WebP is supported",
1431 ));
1432 }
1433 if parsed.alpha_data.is_some() {
1434 return Err(DecoderError::Unsupported("lossy alpha is not implemented"));
1435 }
1436 decode_lossy_vp8_to_yuv(parsed.image_data)
1437}
1438
1439#[cfg(test)]
1440mod tests {
1441 use super::top_samples_luma4;
1442
1443 #[test]
1444 fn top_samples_luma4_uses_macroblock_top_right_for_copy_down() {
1445 let stride = 32usize;
1446 let width = 32usize;
1447 let mut plane = vec![0u8; stride * 32];
1448
1449 plane[19 * stride + 12..19 * stride + 16].copy_from_slice(&[10, 11, 12, 13]);
1450 plane[15 * stride + 16..15 * stride + 20].copy_from_slice(&[20, 21, 22, 23]);
1451
1452 let top = top_samples_luma4(&plane, stride, width, 12, 20);
1453
1454 assert_eq!(top, [10, 11, 12, 13, 20, 21, 22, 23]);
1455 }
1456}