1 // Copyright 2024 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #[cfg(feature = "libyuv")]
16 use super::libyuv;
17
18 use super::rgb;
19
20 use crate::image::Plane;
21 use crate::internal_utils::*;
22 use crate::reformat::rgb::Format;
23 use crate::*;
24
premultiply_u8(pixel: u8, alpha: u8) -> u825 fn premultiply_u8(pixel: u8, alpha: u8) -> u8 {
26 ((pixel as f32) * (alpha as f32) / 255.0).floor() as u8
27 }
28
premultiply_u16(pixel: u16, alpha: u16, max_channel_f: f32) -> u1629 fn premultiply_u16(pixel: u16, alpha: u16, max_channel_f: f32) -> u16 {
30 ((pixel as f32) * (alpha as f32) / max_channel_f).floor() as u16
31 }
32
unpremultiply_u8(pixel: u8, alpha: u8) -> u833 fn unpremultiply_u8(pixel: u8, alpha: u8) -> u8 {
34 ((pixel as f32) * 255.0 / (alpha as f32)).floor().min(255.0) as u8
35 }
36
unpremultiply_u16(pixel: u16, alpha: u16, max_channel_f: f32) -> u1637 fn unpremultiply_u16(pixel: u16, alpha: u16, max_channel_f: f32) -> u16 {
38 ((pixel as f32) * max_channel_f / (alpha as f32))
39 .floor()
40 .min(max_channel_f) as u16
41 }
42
43 macro_rules! alpha_index_in_rgba_1010102 {
44 ($x:expr) => {{
45 // The index of the alpha pixel depends on the endianness since each pixel is a u32 in this
46 // case. The alpha value is the 2-bit MSB of the pixel at this index.
47 $x * 2 + if cfg!(target_endian = "little") { 1 } else { 0 }
48 }};
49 }
50
51 impl rgb::Image {
premultiply_alpha(&mut self) -> AvifResult<()>52 pub(crate) fn premultiply_alpha(&mut self) -> AvifResult<()> {
53 if self.pixels().is_null() || self.row_bytes == 0 {
54 return Err(AvifError::ReformatFailed);
55 }
56 if !self.has_alpha() {
57 return Err(AvifError::InvalidArgument);
58 }
59
60 #[cfg(feature = "libyuv")]
61 match libyuv::process_alpha(self, true) {
62 Ok(_) => return Ok(()),
63 Err(err) => {
64 if err != AvifError::NotImplemented {
65 return Err(err);
66 }
67 }
68 }
69
70 let (alpha_offset, rgb_offsets) = match self.format {
71 Format::Rgba | Format::Bgra => (3, [0, 1, 2]),
72 _ => (0, [1, 2, 3]),
73 };
74
75 if self.depth > 8 {
76 let max_channel = self.max_channel();
77 let max_channel_f = self.max_channel_f();
78 for j in 0..self.height {
79 let width = self.width;
80 let row = self.row16_mut(j)?;
81 for i in 0..width as usize {
82 let offset = i * 4;
83 let alpha = row[offset + alpha_offset];
84 if alpha >= max_channel {
85 continue;
86 }
87 if alpha == 0 {
88 for rgb_offset in rgb_offsets {
89 row[offset + rgb_offset] = 0;
90 }
91 continue;
92 }
93 for rgb_offset in rgb_offsets {
94 row[offset + rgb_offset] =
95 premultiply_u16(row[offset + rgb_offset], alpha, max_channel_f);
96 }
97 }
98 }
99 } else {
100 for j in 0..self.height {
101 let width = self.width;
102 let row = self.row_mut(j)?;
103 for i in 0..width as usize {
104 let offset = i * 4;
105 let alpha = row[offset + alpha_offset];
106 match alpha {
107 0 => {
108 for rgb_offset in rgb_offsets {
109 row[offset + rgb_offset] = 0;
110 }
111 }
112 255 => {}
113 _ => {
114 for rgb_offset in rgb_offsets {
115 row[offset + rgb_offset] =
116 premultiply_u8(row[offset + rgb_offset], alpha);
117 }
118 }
119 }
120 }
121 }
122 }
123 Ok(())
124 }
125
unpremultiply_alpha(&mut self) -> AvifResult<()>126 pub(crate) fn unpremultiply_alpha(&mut self) -> AvifResult<()> {
127 if self.pixels().is_null() || self.row_bytes == 0 {
128 return Err(AvifError::ReformatFailed);
129 }
130 if !self.has_alpha() {
131 return Err(AvifError::InvalidArgument);
132 }
133
134 #[cfg(feature = "libyuv")]
135 match libyuv::process_alpha(self, false) {
136 Ok(_) => return Ok(()),
137 Err(err) => {
138 if err != AvifError::NotImplemented {
139 return Err(err);
140 }
141 }
142 }
143
144 let (alpha_offset, rgb_offsets) = match self.format {
145 Format::Rgba | Format::Bgra => (3, [0, 1, 2]),
146 _ => (0, [1, 2, 3]),
147 };
148
149 if self.depth > 8 {
150 let max_channel = self.max_channel();
151 let max_channel_f = self.max_channel_f();
152 for j in 0..self.height {
153 let width = self.width;
154 let row = self.row16_mut(j)?;
155 for i in 0..width as usize {
156 let offset = i * 4;
157 let alpha = row[offset + alpha_offset];
158 if alpha >= max_channel {
159 continue;
160 }
161 if alpha == 0 {
162 for rgb_offset in rgb_offsets {
163 row[offset + rgb_offset] = 0;
164 }
165 continue;
166 }
167 for rgb_offset in rgb_offsets {
168 row[offset + rgb_offset] =
169 unpremultiply_u16(row[offset + rgb_offset], alpha, max_channel_f);
170 }
171 }
172 }
173 } else {
174 for j in 0..self.height {
175 let width = self.width;
176 let row = self.row_mut(j)?;
177 for i in 0..width as usize {
178 let offset = i * 4;
179 let alpha = row[offset + alpha_offset];
180 match alpha {
181 0 => {
182 for rgb_offset in rgb_offsets {
183 row[offset + rgb_offset] = 0;
184 }
185 }
186 255 => {}
187 _ => {
188 for rgb_offset in rgb_offsets {
189 row[offset + rgb_offset] =
190 unpremultiply_u8(row[offset + rgb_offset], alpha);
191 }
192 }
193 }
194 }
195 }
196 }
197 Ok(())
198 }
199
set_opaque(&mut self) -> AvifResult<()>200 pub(crate) fn set_opaque(&mut self) -> AvifResult<()> {
201 if !self.has_alpha() {
202 return Ok(());
203 }
204 if self.format == rgb::Format::Rgb565 {
205 return Err(AvifError::NotImplemented);
206 }
207 let alpha_offset = self.format.alpha_offset();
208 let width = usize_from_u32(self.width)?;
209 if self.depth > 8 {
210 let max_channel = self.max_channel();
211 for y in 0..self.height {
212 let row = self.row16_mut(y)?;
213 for x in 0..width {
214 row[(x * 4) + alpha_offset] = max_channel;
215 }
216 }
217 } else {
218 for y in 0..self.height {
219 let row = self.row_mut(y)?;
220 for x in 0..width {
221 row[(x * 4) + alpha_offset] = 255;
222 }
223 }
224 }
225 Ok(())
226 }
227
rescale_alpha_value(value: u16, src_max_channel_f: f32, dst_max_channel: u16) -> u16228 fn rescale_alpha_value(value: u16, src_max_channel_f: f32, dst_max_channel: u16) -> u16 {
229 let alpha_f = (value as f32) / src_max_channel_f;
230 let dst_max_channel_f = dst_max_channel as f32;
231 let alpha = (0.5 + (alpha_f * dst_max_channel_f)) as u16;
232 clamp_u16(alpha, 0, dst_max_channel)
233 }
234
import_alpha_from(&mut self, image: &image::Image) -> AvifResult<()>235 pub(crate) fn import_alpha_from(&mut self, image: &image::Image) -> AvifResult<()> {
236 if !self.has_alpha()
237 || !image.has_alpha()
238 || self.width != image.width
239 || self.height != image.height
240 {
241 return Err(AvifError::InvalidArgument);
242 }
243 let width = usize_from_u32(self.width)?;
244 if self.format == Format::Rgba1010102 {
245 // Clippy warns about the loops using x as an index for src_row. But it is also used to
246 // compute the index for dst_row. Disable the warnings.
247 #[allow(clippy::needless_range_loop)]
248 if image.depth > 8 {
249 for y in 0..self.height {
250 let dst_row = self.row16_mut(y)?;
251 let src_row = image.row16(Plane::A, y)?;
252 for x in 0..width {
253 let alpha_pixel = (src_row[x]) >> (image.depth - 2);
254 let index = alpha_index_in_rgba_1010102!(x);
255 dst_row[index] = (dst_row[index] & 0x3fff) | (alpha_pixel << 14);
256 }
257 }
258 } else {
259 for y in 0..self.height {
260 let dst_row = self.row16_mut(y)?;
261 let src_row = image.row(Plane::A, y)?;
262 for x in 0..width {
263 let alpha_pixel = ((src_row[x]) >> 6) as u16;
264 let index = alpha_index_in_rgba_1010102!(x);
265 dst_row[index] = (dst_row[index] & 0x3fff) | (alpha_pixel << 14);
266 }
267 }
268 }
269 return Ok(());
270 }
271 let dst_alpha_offset = self.format.alpha_offset();
272 if self.depth == image.depth {
273 if self.depth > 8 {
274 for y in 0..self.height {
275 let dst_row = self.row16_mut(y)?;
276 let src_row = image.row16(Plane::A, y)?;
277 for x in 0..width {
278 dst_row[(x * 4) + dst_alpha_offset] = src_row[x];
279 }
280 }
281 return Ok(());
282 }
283 for y in 0..self.height {
284 let dst_row = self.row_mut(y)?;
285 let src_row = image.row(Plane::A, y)?;
286 for x in 0..width {
287 dst_row[(x * 4) + dst_alpha_offset] = src_row[x];
288 }
289 }
290 return Ok(());
291 }
292 let max_channel = self.max_channel();
293 if image.depth > 8 {
294 if self.depth > 8 {
295 // u16 to u16 depth rescaling.
296 for y in 0..self.height {
297 let dst_row = self.row16_mut(y)?;
298 let src_row = image.row16(Plane::A, y)?;
299 for x in 0..width {
300 dst_row[(x * 4) + dst_alpha_offset] = Self::rescale_alpha_value(
301 src_row[x],
302 image.max_channel_f(),
303 max_channel,
304 );
305 }
306 }
307 return Ok(());
308 }
309 // u16 to u8 depth rescaling.
310 for y in 0..self.height {
311 let dst_row = self.row_mut(y)?;
312 let src_row = image.row16(Plane::A, y)?;
313 for x in 0..width {
314 dst_row[(x * 4) + dst_alpha_offset] =
315 Self::rescale_alpha_value(src_row[x], image.max_channel_f(), max_channel)
316 as u8;
317 }
318 }
319 return Ok(());
320 }
321 // u8 to u16 depth rescaling.
322 for y in 0..self.height {
323 let dst_row = self.row16_mut(y)?;
324 let src_row = image.row(Plane::A, y)?;
325 for x in 0..width {
326 dst_row[(x * 4) + dst_alpha_offset] = Self::rescale_alpha_value(
327 src_row[x] as u16,
328 image.max_channel_f(),
329 max_channel,
330 );
331 }
332 }
333 Ok(())
334 }
335 }
336
337 impl image::Image {
alpha_to_full_range(&mut self) -> AvifResult<()>338 pub(crate) fn alpha_to_full_range(&mut self) -> AvifResult<()> {
339 if self.planes[3].is_none() {
340 return Ok(());
341 }
342 let width = self.width as usize;
343 let depth = self.depth;
344 if self.planes[3].unwrap_ref().is_pointer() {
345 let src = image::Image {
346 width: self.width,
347 height: self.height,
348 depth: self.depth,
349 yuv_format: self.yuv_format,
350 planes: [
351 None,
352 None,
353 None,
354 Some(self.planes[3].unwrap_ref().try_clone()?),
355 ],
356 row_bytes: [0, 0, 0, self.row_bytes[3]],
357 ..image::Image::default()
358 };
359 self.allocate_planes(Category::Alpha)?;
360 if depth > 8 {
361 for y in 0..self.height {
362 let src_row = src.row16(Plane::A, y)?;
363 let dst_row = self.row16_mut(Plane::A, y)?;
364 for x in 0..width {
365 dst_row[x] = limited_to_full_y(depth, src_row[x]);
366 }
367 }
368 } else {
369 for y in 0..self.height {
370 let src_row = src.row(Plane::A, y)?;
371 let dst_row = self.row_mut(Plane::A, y)?;
372 for x in 0..width {
373 dst_row[x] = limited_to_full_y(8, src_row[x] as u16) as u8;
374 }
375 }
376 }
377 } else if depth > 8 {
378 for y in 0..self.height {
379 let row = self.row16_mut(Plane::A, y)?;
380 for pixel in row.iter_mut().take(width) {
381 *pixel = limited_to_full_y(depth, *pixel);
382 }
383 }
384 } else {
385 for y in 0..self.height {
386 let row = self.row_mut(Plane::A, y)?;
387 for pixel in row.iter_mut().take(width) {
388 *pixel = limited_to_full_y(8, *pixel as u16) as u8;
389 }
390 }
391 }
392 Ok(())
393 }
394 }
395
396 #[cfg(test)]
397 mod tests {
398 use super::*;
399
400 use crate::internal_utils::pixels::*;
401
402 use rand::Rng;
403 use test_case::test_matrix;
404
405 const ALPHA_RGB_FORMATS: [rgb::Format; 4] = [
406 rgb::Format::Rgba,
407 rgb::Format::Argb,
408 rgb::Format::Bgra,
409 rgb::Format::Abgr,
410 ];
411
rgb_image( width: u32, height: u32, depth: u8, format: rgb::Format, use_pointer: bool, buffer: &mut Vec<u8>, ) -> AvifResult<rgb::Image>412 fn rgb_image(
413 width: u32,
414 height: u32,
415 depth: u8,
416 format: rgb::Format,
417 use_pointer: bool,
418 buffer: &mut Vec<u8>,
419 ) -> AvifResult<rgb::Image> {
420 let mut rgb = rgb::Image {
421 width,
422 height,
423 depth,
424 format,
425 ..rgb::Image::default()
426 };
427 if use_pointer {
428 let pixel_size = if depth == 8 { 1 } else { 2 };
429 let buffer_size = (width * height * 4 * pixel_size) as usize;
430 buffer.reserve_exact(buffer_size);
431 buffer.resize(buffer_size, 0);
432 rgb.row_bytes = width * 4 * pixel_size;
433 // Use a pointer to mimic C API calls.
434 rgb.pixels = Some(Pixels::from_raw_pointer(
435 buffer.as_mut_ptr(),
436 rgb.depth as u32,
437 height,
438 rgb.row_bytes,
439 )?);
440 } else {
441 rgb.allocate()?;
442 }
443 Ok(rgb)
444 }
445
446 #[test_matrix(20, 10, [8, 10, 12, 16], 0..4, [true, false])]
fill_alpha( width: u32, height: u32, depth: u8, format_index: usize, use_pointer: bool, ) -> AvifResult<()>447 fn fill_alpha(
448 width: u32,
449 height: u32,
450 depth: u8,
451 format_index: usize,
452 use_pointer: bool,
453 ) -> AvifResult<()> {
454 let format = ALPHA_RGB_FORMATS[format_index];
455 let mut buffer: Vec<u8> = vec![];
456 let mut rgb = rgb_image(width, height, depth, format, use_pointer, &mut buffer)?;
457
458 rgb.set_opaque()?;
459
460 let alpha_offset = rgb.format.alpha_offset();
461 if depth == 8 {
462 for y in 0..height {
463 let row = rgb.row(y)?;
464 assert_eq!(row.len(), (width * 4) as usize);
465 for x in 0..width as usize {
466 for idx in 0..4usize {
467 let expected_value = if idx == alpha_offset { 255 } else { 0 };
468 assert_eq!(row[x * 4 + idx], expected_value);
469 }
470 }
471 }
472 } else {
473 let max_channel = ((1 << depth) - 1) as u16;
474 for y in 0..height {
475 let row = rgb.row16(y)?;
476 assert_eq!(row.len(), (width * 4) as usize);
477 for x in 0..width as usize {
478 for idx in 0..4usize {
479 let expected_value = if idx == alpha_offset { max_channel } else { 0 };
480 assert_eq!(row[x * 4 + idx], expected_value);
481 }
482 }
483 }
484 }
485 Ok(())
486 }
487
488 #[test]
rescale_alpha_value()489 fn rescale_alpha_value() {
490 // 8bit to 10bit.
491 assert_eq!(rgb::Image::rescale_alpha_value(0, 255.0, 1023), 0);
492 assert_eq!(rgb::Image::rescale_alpha_value(100, 255.0, 1023), 401);
493 assert_eq!(rgb::Image::rescale_alpha_value(128, 255.0, 1023), 514);
494 assert_eq!(rgb::Image::rescale_alpha_value(255, 255.0, 1023), 1023);
495 // 10bit to 8bit.
496 assert_eq!(rgb::Image::rescale_alpha_value(0, 1023.0, 255), 0);
497 assert_eq!(rgb::Image::rescale_alpha_value(401, 1023.0, 255), 100);
498 assert_eq!(rgb::Image::rescale_alpha_value(514, 1023.0, 255), 128);
499 assert_eq!(rgb::Image::rescale_alpha_value(1023, 1023.0, 255), 255);
500 // 8bit to 12bit.
501 assert_eq!(rgb::Image::rescale_alpha_value(0, 255.0, 4095), 0);
502 assert_eq!(rgb::Image::rescale_alpha_value(100, 255.0, 4095), 1606);
503 assert_eq!(rgb::Image::rescale_alpha_value(128, 255.0, 4095), 2056);
504 assert_eq!(rgb::Image::rescale_alpha_value(255, 255.0, 4095), 4095);
505 // 12bit to 8bit.
506 assert_eq!(rgb::Image::rescale_alpha_value(0, 4095.0, 255), 0);
507 assert_eq!(rgb::Image::rescale_alpha_value(1606, 4095.0, 255), 100);
508 assert_eq!(rgb::Image::rescale_alpha_value(2056, 4095.0, 255), 128);
509 assert_eq!(rgb::Image::rescale_alpha_value(4095, 4095.0, 255), 255);
510 // 10bit to 12bit.
511 assert_eq!(rgb::Image::rescale_alpha_value(0, 1023.0, 4095), 0);
512 assert_eq!(rgb::Image::rescale_alpha_value(401, 1023.0, 4095), 1605);
513 assert_eq!(rgb::Image::rescale_alpha_value(514, 1023.0, 4095), 2058);
514 assert_eq!(rgb::Image::rescale_alpha_value(1023, 1023.0, 4095), 4095);
515 // 12bit to 10bit.
516 assert_eq!(rgb::Image::rescale_alpha_value(0, 4095.0, 1023), 0);
517 assert_eq!(rgb::Image::rescale_alpha_value(1606, 4095.0, 1023), 401);
518 assert_eq!(rgb::Image::rescale_alpha_value(2056, 4095.0, 1023), 514);
519 assert_eq!(rgb::Image::rescale_alpha_value(4095, 4095.0, 1023), 1023);
520 }
521
522 #[test_matrix(20, 10, [8, 10, 12, 16], 0..4, [8, 10, 12], [true, false])]
reformat_alpha( width: u32, height: u32, rgb_depth: u8, format_index: usize, yuv_depth: u8, use_pointer: bool, ) -> AvifResult<()>523 fn reformat_alpha(
524 width: u32,
525 height: u32,
526 rgb_depth: u8,
527 format_index: usize,
528 yuv_depth: u8,
529 use_pointer: bool,
530 ) -> AvifResult<()> {
531 // Note: This test simply makes sure reformat_alpha puts the alpha pixels in the right
532 // place in the rgb image (with scaling). It does not check for the actual validity of the
533 // scaled pixels.
534 let format = ALPHA_RGB_FORMATS[format_index];
535 let mut buffer: Vec<u8> = vec![];
536 let mut rgb = rgb_image(width, height, rgb_depth, format, use_pointer, &mut buffer)?;
537
538 let mut image = image::Image::default();
539 image.width = width;
540 image.height = height;
541 image.depth = yuv_depth;
542 image.allocate_planes(Category::Alpha)?;
543
544 let mut rng = rand::thread_rng();
545 let mut expected_values: Vec<u16> = Vec::new();
546 let image_max_channel_f = image.max_channel_f();
547 if yuv_depth == 8 {
548 for y in 0..height {
549 let row = image.row_mut(Plane::A, y)?;
550 for x in 0..width as usize {
551 let value = rng.gen_range(0..256) as u8;
552 if rgb.depth == 8 {
553 expected_values.push(value as u16);
554 } else {
555 expected_values.push(rgb::Image::rescale_alpha_value(
556 value as u16,
557 image_max_channel_f,
558 rgb.max_channel(),
559 ));
560 }
561 row[x] = value;
562 }
563 }
564 } else {
565 for y in 0..height {
566 let row = image.row16_mut(Plane::A, y)?;
567 for x in 0..width as usize {
568 let value = rng.gen_range(0..(1i32 << yuv_depth)) as u16;
569 if rgb.depth == yuv_depth {
570 expected_values.push(value);
571 } else {
572 expected_values.push(rgb::Image::rescale_alpha_value(
573 value as u16,
574 image_max_channel_f,
575 rgb.max_channel(),
576 ));
577 }
578 row[x] = value;
579 }
580 }
581 }
582
583 rgb.import_alpha_from(&image)?;
584
585 let alpha_offset = rgb.format.alpha_offset();
586 let mut expected_values = expected_values.into_iter();
587 if rgb_depth == 8 {
588 for y in 0..height {
589 let rgb_row = rgb.row(y)?;
590 assert_eq!(rgb_row.len(), (width * 4) as usize);
591 for x in 0..width as usize {
592 for idx in 0..4usize {
593 let expected_value =
594 if idx == alpha_offset { expected_values.next().unwrap() } else { 0 };
595 assert_eq!(rgb_row[x * 4 + idx], expected_value as u8);
596 }
597 }
598 }
599 } else {
600 for y in 0..height {
601 let rgb_row = rgb.row16(y)?;
602 assert_eq!(rgb_row.len(), (width * 4) as usize);
603 for x in 0..width as usize {
604 for idx in 0..4usize {
605 let expected_value =
606 if idx == alpha_offset { expected_values.next().unwrap() } else { 0 };
607 assert_eq!(rgb_row[x * 4 + idx], expected_value);
608 }
609 }
610 }
611 }
612 Ok(())
613 }
614
615 #[test_matrix(20, 10, 10, [8, 10, 12])]
reformat_alpha_rgba1010102( width: u32, height: u32, rgb_depth: u8, yuv_depth: u8, ) -> AvifResult<()>616 fn reformat_alpha_rgba1010102(
617 width: u32,
618 height: u32,
619 rgb_depth: u8,
620 yuv_depth: u8,
621 ) -> AvifResult<()> {
622 let format = rgb::Format::Rgba1010102;
623 let mut buffer: Vec<u8> = vec![];
624 let mut rgb = rgb_image(
625 width,
626 height,
627 rgb_depth,
628 format,
629 /*use_pointer*/ false,
630 &mut buffer,
631 )?;
632
633 let mut image = image::Image::default();
634 image.width = width;
635 image.height = height;
636 image.depth = yuv_depth;
637 image.allocate_planes(Category::Alpha)?;
638
639 let mut rng = rand::thread_rng();
640 let mut expected_values: Vec<u16> = Vec::new();
641 if yuv_depth == 8 {
642 for y in 0..height {
643 let row = image.row_mut(Plane::A, y)?;
644 for x in 0..width as usize {
645 let value = rng.gen_range(0..256) as u8;
646 expected_values.push((value >> 6) as u16);
647 row[x] = value;
648 }
649 }
650 } else {
651 for y in 0..height {
652 let row = image.row16_mut(Plane::A, y)?;
653 for x in 0..width as usize {
654 let value = rng.gen_range(0..(1i32 << yuv_depth)) as u16;
655 expected_values.push(value >> (yuv_depth - 2));
656 row[x] = value;
657 }
658 }
659 }
660
661 rgb.import_alpha_from(&image)?;
662
663 let mut expected_values = expected_values.into_iter();
664 for y in 0..height {
665 let rgb_row = rgb.row16(y)?;
666 assert_eq!(rgb_row.len(), (width * 2) as usize);
667 for x in 0..width as usize {
668 assert_eq!(
669 rgb_row[alpha_index_in_rgba_1010102!(x)] >> 14,
670 expected_values.next().unwrap()
671 );
672 }
673 }
674 Ok(())
675 }
676 }
677