• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 use crate::image::*;
16 use crate::internal_utils::*;
17 use crate::*;
18 
19 use libyuv_sys::bindings::*;
20 
21 impl Image {
scale(&mut self, width: u32, height: u32, category: Category) -> AvifResult<()>22     pub(crate) fn scale(&mut self, width: u32, height: u32, category: Category) -> AvifResult<()> {
23         if self.width == width && self.height == height {
24             return Ok(());
25         }
26         if width == 0 || height == 0 {
27             return Err(AvifError::InvalidArgument);
28         }
29         let planes: &[Plane] = match category {
30             Category::Color | Category::Gainmap => &YUV_PLANES,
31             Category::Alpha => &A_PLANE,
32         };
33         let src =
34             if category != Category::Alpha && self.yuv_format == PixelFormat::AndroidP010 {
35                 // P010 images cannot be scaled using ScalePlane_12 since the U and V planes are
36                 // interleaved. Convert them into I010 and then scale each plane using
37                 // ScalePlane_12.
38                 let mut i010 = image::Image {
39                     width: self.width,
40                     height: self.height,
41                     depth: 10,
42                     yuv_format: PixelFormat::Yuv420,
43                     ..image::Image::default()
44                 };
45                 i010.allocate_planes(Category::Color)?;
46                 let src_y_pd = self.plane_data(Plane::Y).unwrap();
47                 let src_uv_pd = self.plane_data(Plane::U).unwrap();
48                 let src_y = self.planes[Plane::Y.as_usize()].unwrap_ref().ptr16();
49                 let src_uv = self.planes[Plane::U.as_usize()].unwrap_ref().ptr16();
50                 let dst_y_pd = i010.plane_data(Plane::Y).unwrap();
51                 let dst_u_pd = i010.plane_data(Plane::U).unwrap();
52                 let dst_v_pd = i010.plane_data(Plane::V).unwrap();
53                 let dst_y = i010.planes[Plane::Y.as_usize()].unwrap_mut().ptr16_mut();
54                 let dst_u = i010.planes[Plane::U.as_usize()].unwrap_mut().ptr16_mut();
55                 let dst_v = i010.planes[Plane::V.as_usize()].unwrap_mut().ptr16_mut();
56                 // SAFETY: This function calls into libyuv which is a C++ library. We pass in
57                 // pointers and strides to rust slices that are guaranteed to be valid.
58                 let ret = unsafe {
59                     P010ToI010(
60                         src_y,
61                         i32_from_u32(src_y_pd.row_bytes / 2)?,
62                         src_uv,
63                         i32_from_u32(src_uv_pd.row_bytes / 2)?,
64                         dst_y,
65                         i32_from_u32(dst_y_pd.row_bytes / 2)?,
66                         dst_u,
67                         i32_from_u32(dst_u_pd.row_bytes / 2)?,
68                         dst_v,
69                         i32_from_u32(dst_v_pd.row_bytes / 2)?,
70                         i32_from_u32(self.width)?,
71                         i32_from_u32(self.height)?,
72                     )
73                 };
74                 if ret != 0 {
75                     return Err(AvifError::ReformatFailed);
76                 }
77                 i010
78             } else {
79                 image::Image {
80                     width: self.width,
81                     height: self.height,
82                     depth: self.depth,
83                     yuv_format: self.yuv_format,
84                     planes: self
85                         .planes
86                         .as_ref()
87                         .iter()
88                         .map(|plane| {
89                             if plane.is_some() {
90                                 plane.unwrap_ref().try_clone().ok()
91                             } else {
92                                 None
93                             }
94                         })
95                         .collect::<Vec<_>>()
96                         .try_into()
97                         .unwrap(),
98                     row_bytes: self.row_bytes,
99                     ..image::Image::default()
100                 }
101             };
102 
103         self.width = width;
104         self.height = height;
105         self.depth = src.depth;
106         self.yuv_format = src.yuv_format;
107         if src.has_plane(Plane::Y) || src.has_plane(Plane::A) {
108             if src.width > 16384 || src.height > 16384 {
109                 return Err(AvifError::NotImplemented);
110             }
111             if src.has_plane(Plane::Y) && category != Category::Alpha {
112                 self.allocate_planes(Category::Color)?;
113             }
114             if src.has_plane(Plane::A) && category == Category::Alpha {
115                 self.allocate_planes(Category::Alpha)?;
116             }
117         }
118 
119         if category != Category::Alpha
120             && (self.yuv_format == PixelFormat::AndroidNv12
121                 || self.yuv_format == PixelFormat::AndroidNv21)
122         {
123             let src_y_pd = src.plane_data(Plane::Y).unwrap();
124             let src_uv_pd = src.plane_data(Plane::U).unwrap();
125             let src_y = src.planes[Plane::Y.as_usize()].unwrap_ref().ptr();
126             let src_uv = src.planes[Plane::U.as_usize()].unwrap_ref().ptr();
127             let dst_y_pd = self.plane_data(Plane::Y).unwrap();
128             let dst_uv_pd = self.plane_data(Plane::U).unwrap();
129             let dst_y = self.planes[Plane::Y.as_usize()].unwrap_mut().ptr_mut();
130             let dst_uv = self.planes[Plane::U.as_usize()].unwrap_mut().ptr_mut();
131             // SAFETY: This function calls into libyuv which is a C++ library. We pass in pointers
132             // and strides to rust slices that are guaranteed to be valid.
133             let ret = unsafe {
134                 NV12Scale(
135                     src_y,
136                     i32_from_u32(src_y_pd.row_bytes)?,
137                     src_uv,
138                     i32_from_u32(src_uv_pd.row_bytes)?,
139                     i32_from_u32(src_y_pd.width)?,
140                     i32_from_u32(src_y_pd.height)?,
141                     dst_y,
142                     i32_from_u32(dst_y_pd.row_bytes)?,
143                     dst_uv,
144                     i32_from_u32(dst_uv_pd.row_bytes)?,
145                     i32_from_u32(dst_y_pd.width)?,
146                     i32_from_u32(dst_y_pd.height)?,
147                     FilterMode_kFilterBox,
148                 )
149             };
150             if ret != 0 {
151                 return Err(AvifError::ReformatFailed);
152             } else {
153                 return Ok(());
154             }
155         }
156 
157         for plane in planes {
158             if !src.has_plane(*plane) || !self.has_plane(*plane) {
159                 continue;
160             }
161             let src_pd = src.plane_data(*plane).unwrap();
162             let dst_pd = self.plane_data(*plane).unwrap();
163             // SAFETY: This function calls into libyuv which is a C++ library. We pass in pointers
164             // and strides to rust slices that are guaranteed to be valid.
165             //
166             // libyuv versions >= 1880 reports a return value here. Older versions do not. Ignore
167             // the return value for now.
168             #[allow(clippy::let_unit_value)]
169             let _ret = unsafe {
170                 if src.depth > 8 {
171                     let source_ptr = src.planes[plane.as_usize()].unwrap_ref().ptr16();
172                     let dst_ptr = self.planes[plane.as_usize()].unwrap_mut().ptr16_mut();
173                     ScalePlane_12(
174                         source_ptr,
175                         i32_from_u32(src_pd.row_bytes / 2)?,
176                         i32_from_u32(src_pd.width)?,
177                         i32_from_u32(src_pd.height)?,
178                         dst_ptr,
179                         i32_from_u32(dst_pd.row_bytes / 2)?,
180                         i32_from_u32(dst_pd.width)?,
181                         i32_from_u32(dst_pd.height)?,
182                         FilterMode_kFilterBox,
183                     )
184                 } else {
185                     let source_ptr = src.planes[plane.as_usize()].unwrap_ref().ptr();
186                     let dst_ptr = self.planes[plane.as_usize()].unwrap_mut().ptr_mut();
187                     ScalePlane(
188                         source_ptr,
189                         i32_from_u32(src_pd.row_bytes)?,
190                         i32_from_u32(src_pd.width)?,
191                         i32_from_u32(src_pd.height)?,
192                         dst_ptr,
193                         i32_from_u32(dst_pd.row_bytes)?,
194                         i32_from_u32(dst_pd.width)?,
195                         i32_from_u32(dst_pd.height)?,
196                         FilterMode_kFilterBox,
197                     )
198                 }
199             };
200         }
201         Ok(())
202     }
203 }
204 
205 #[cfg(test)]
206 mod tests {
207     use super::*;
208     use crate::internal_utils::pixels::*;
209     use test_case::test_matrix;
210 
211     #[test_matrix([PixelFormat::Yuv444, PixelFormat::Yuv422, PixelFormat::Yuv420, PixelFormat::Yuv400], [false, true], [false, true])]
scale(yuv_format: PixelFormat, use_alpha: bool, is_pointer_input: bool)212     fn scale(yuv_format: PixelFormat, use_alpha: bool, is_pointer_input: bool) {
213         let mut yuv = image::Image {
214             width: 2,
215             height: 2,
216             depth: 8,
217             yuv_format,
218             ..Default::default()
219         };
220 
221         let planes: &[Plane] = match (yuv_format, use_alpha) {
222             (PixelFormat::Yuv400, false) => &[Plane::Y],
223             (PixelFormat::Yuv400, true) => &[Plane::Y, Plane::A],
224             (_, false) => &YUV_PLANES,
225             (_, true) => &ALL_PLANES,
226         };
227         let mut values = [
228             10, 20, //
229             30, 40,
230         ];
231         for plane in planes {
232             yuv.planes[plane.as_usize()] = Some(if is_pointer_input {
233                 Pixels::Pointer(unsafe {
234                     PointerSlice::create(values.as_mut_ptr(), values.len()).unwrap()
235                 })
236             } else {
237                 Pixels::Buffer(values.to_vec())
238             });
239             yuv.row_bytes[plane.as_usize()] = 2;
240             yuv.image_owns_planes[plane.as_usize()] = !is_pointer_input;
241         }
242         let categories: &[Category] =
243             if use_alpha { &[Category::Color, Category::Alpha] } else { &[Category::Color] };
244         for category in categories {
245             // Scale will update the width and height when scaling YUV planes. Reset it back before
246             // calling it again.
247             yuv.width = 2;
248             yuv.height = 2;
249             assert!(yuv.scale(4, 4, *category).is_ok());
250         }
251         for plane in planes {
252             let expected_samples: &[u8] = match (yuv_format, plane) {
253                 (PixelFormat::Yuv422, Plane::U | Plane::V) => &[
254                     10, 10, //
255                     10, 10, //
256                     30, 30, //
257                     30, 30,
258                 ],
259                 (PixelFormat::Yuv420, Plane::U | Plane::V) => &[
260                     10, 10, //
261                     10, 10,
262                 ],
263                 (_, _) => &[
264                     10, 13, 18, 20, //
265                     15, 18, 23, 25, //
266                     25, 28, 33, 35, //
267                     30, 33, 38, 40,
268                 ],
269             };
270             match &yuv.planes[plane.as_usize()] {
271                 Some(Pixels::Buffer(samples)) => {
272                     assert_eq!(*samples, expected_samples)
273                 }
274                 _ => panic!(),
275             }
276         }
277     }
278 }
279