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 super::image::*;
16 use super::types::*;
17
18 use crate::image::*;
19 use crate::internal_utils::pixels::*;
20 use crate::internal_utils::*;
21 use crate::reformat::rgb;
22 use crate::*;
23
24 /// cbindgen:rename-all=CamelCase
25 #[repr(C)]
26 pub struct avifRGBImage {
27 pub width: u32,
28 pub height: u32,
29 pub depth: u32,
30 pub format: rgb::Format,
31 pub chroma_upsampling: rgb::ChromaUpsampling,
32 pub chroma_downsampling: rgb::ChromaDownsampling,
33 pub ignore_alpha: bool,
34 pub alpha_premultiplied: bool,
35 pub is_float: bool,
36 pub max_threads: i32,
37 pub pixels: *mut u8,
38 pub row_bytes: u32,
39 }
40
41 impl From<rgb::Image> for avifRGBImage {
from(mut rgb: rgb::Image) -> avifRGBImage42 fn from(mut rgb: rgb::Image) -> avifRGBImage {
43 avifRGBImage {
44 width: rgb.width,
45 height: rgb.height,
46 depth: rgb.depth as u32,
47 format: rgb.format,
48 chroma_upsampling: rgb.chroma_upsampling,
49 chroma_downsampling: rgb.chroma_downsampling,
50 ignore_alpha: false,
51 alpha_premultiplied: rgb.premultiply_alpha,
52 is_float: rgb.is_float,
53 max_threads: rgb.max_threads,
54 pixels: rgb.pixels(),
55 row_bytes: rgb.row_bytes,
56 }
57 }
58 }
59
60 impl From<&avifRGBImage> for rgb::Image {
from(rgb: &avifRGBImage) -> rgb::Image61 fn from(rgb: &avifRGBImage) -> rgb::Image {
62 let dst = rgb::Image {
63 width: rgb.width,
64 height: rgb.height,
65 depth: rgb.depth as u8,
66 format: rgb.format,
67 chroma_upsampling: rgb.chroma_upsampling,
68 chroma_downsampling: rgb.chroma_downsampling,
69 premultiply_alpha: rgb.alpha_premultiplied,
70 is_float: rgb.is_float,
71 max_threads: rgb.max_threads,
72 pixels: Pixels::from_raw_pointer(rgb.pixels, rgb.depth, rgb.height, rgb.row_bytes).ok(),
73 row_bytes: rgb.row_bytes,
74 };
75 let format = match (rgb.format, rgb.ignore_alpha) {
76 (rgb::Format::Rgb, _) => rgb::Format::Rgb,
77 (rgb::Format::Rgba, true) => rgb::Format::Rgb,
78 (rgb::Format::Rgba, false) => rgb::Format::Rgba,
79 (rgb::Format::Argb, true) => rgb::Format::Rgb,
80 (rgb::Format::Argb, false) => rgb::Format::Argb,
81 (rgb::Format::Bgr, _) => rgb::Format::Bgr,
82 (rgb::Format::Bgra, true) => rgb::Format::Bgr,
83 (rgb::Format::Bgra, false) => rgb::Format::Bgra,
84 (rgb::Format::Abgr, true) => rgb::Format::Bgr,
85 (rgb::Format::Abgr, false) => rgb::Format::Abgr,
86 (rgb::Format::Rgb565, _) => rgb::Format::Rgb565,
87 (rgb::Format::Rgba1010102, _) => rgb::Format::Rgba1010102,
88 };
89 dst.shuffle_channels_to(format).unwrap()
90 }
91 }
92
93 impl From<&avifImage> for image::Image {
94 // Only copies fields necessary for reformatting.
from(image: &avifImage) -> image::Image95 fn from(image: &avifImage) -> image::Image {
96 image::Image {
97 width: image.width,
98 height: image.height,
99 depth: image.depth as u8,
100 yuv_format: image.yuvFormat,
101 yuv_range: image.yuvRange,
102 alpha_present: !image.alphaPlane.is_null(),
103 alpha_premultiplied: image.alphaPremultiplied == AVIF_TRUE,
104 planes: [
105 Pixels::from_raw_pointer(
106 image.yuvPlanes[0],
107 image.depth,
108 image.height,
109 image.yuvRowBytes[0],
110 )
111 .ok(),
112 Pixels::from_raw_pointer(
113 image.yuvPlanes[1],
114 image.depth,
115 image.height,
116 image.yuvRowBytes[1],
117 )
118 .ok(),
119 Pixels::from_raw_pointer(
120 image.yuvPlanes[2],
121 image.depth,
122 image.height,
123 image.yuvRowBytes[2],
124 )
125 .ok(),
126 Pixels::from_raw_pointer(
127 image.alphaPlane,
128 image.depth,
129 image.height,
130 image.alphaRowBytes,
131 )
132 .ok(),
133 ],
134 row_bytes: [
135 image.yuvRowBytes[0],
136 image.yuvRowBytes[1],
137 image.yuvRowBytes[2],
138 image.alphaRowBytes,
139 ],
140 color_primaries: image.colorPrimaries,
141 transfer_characteristics: image.transferCharacteristics,
142 matrix_coefficients: image.matrixCoefficients,
143 ..Default::default()
144 }
145 }
146 }
147
148 #[no_mangle]
crabby_avifRGBImageSetDefaults( rgb: *mut avifRGBImage, image: *const avifImage, )149 pub unsafe extern "C" fn crabby_avifRGBImageSetDefaults(
150 rgb: *mut avifRGBImage,
151 image: *const avifImage,
152 ) {
153 let rgb = unsafe { &mut (*rgb) };
154 let image: image::Image = unsafe { &(*image) }.into();
155 *rgb = rgb::Image::create_from_yuv(&image).into();
156 }
157
158 #[no_mangle]
crabby_avifImageYUVToRGB( image: *const avifImage, rgb: *mut avifRGBImage, ) -> avifResult159 pub unsafe extern "C" fn crabby_avifImageYUVToRGB(
160 image: *const avifImage,
161 rgb: *mut avifRGBImage,
162 ) -> avifResult {
163 unsafe {
164 if (*image).yuvPlanes[0].is_null() {
165 return avifResult::Ok;
166 }
167 }
168 let mut rgb: rgb::Image = unsafe { &(*rgb) }.into();
169 let image: image::Image = unsafe { &(*image) }.into();
170 to_avifResult(&rgb.convert_from_yuv(&image))
171 }
172
CopyPlanes(dst: &mut avifImage, src: &Image) -> AvifResult<()>173 fn CopyPlanes(dst: &mut avifImage, src: &Image) -> AvifResult<()> {
174 for plane in ALL_PLANES {
175 if !src.has_plane(plane) {
176 continue;
177 }
178 let plane_data = src.plane_data(plane).unwrap();
179 if src.depth == 8 {
180 let dst_planes = [
181 dst.yuvPlanes[0],
182 dst.yuvPlanes[1],
183 dst.yuvPlanes[2],
184 dst.alphaPlane,
185 ];
186 let dst_row_bytes = [
187 dst.yuvRowBytes[0],
188 dst.yuvRowBytes[1],
189 dst.yuvRowBytes[2],
190 dst.alphaRowBytes,
191 ];
192 for y in 0..plane_data.height {
193 let src_slice = &src.row(plane, y).unwrap()[..plane_data.width as usize];
194 let dst_slice = unsafe {
195 std::slice::from_raw_parts_mut(
196 dst_planes[plane.as_usize()]
197 .offset(isize_from_u32(y * dst_row_bytes[plane.as_usize()])?),
198 usize_from_u32(plane_data.width)?,
199 )
200 };
201 dst_slice.copy_from_slice(src_slice);
202 }
203 } else {
204 // When scaling a P010 image, the scaling code converts the image into Yuv420 with
205 // an explicit V plane. So if the V plane is missing in |dst|, we will have to allocate
206 // it here. It is safe to do so since it will be free'd with the other plane buffers
207 // when the image object is destroyed.
208 if plane == Plane::V && dst.yuvPlanes[2].is_null() {
209 let plane_size = usize_from_u32(plane_data.width * plane_data.height * 2)?;
210 dst.yuvPlanes[2] = unsafe { crabby_avifAlloc(plane_size) } as *mut _;
211 dst.yuvRowBytes[2] = plane_data.width * 2;
212 }
213 let dst_planes = [
214 dst.yuvPlanes[0] as *mut u16,
215 dst.yuvPlanes[1] as *mut u16,
216 dst.yuvPlanes[2] as *mut u16,
217 dst.alphaPlane as *mut u16,
218 ];
219 let dst_row_bytes = [
220 dst.yuvRowBytes[0] / 2,
221 dst.yuvRowBytes[1] / 2,
222 dst.yuvRowBytes[2] / 2,
223 dst.alphaRowBytes / 2,
224 ];
225 for y in 0..plane_data.height {
226 let src_slice = &src.row16(plane, y).unwrap()[..plane_data.width as usize];
227 let dst_slice = unsafe {
228 std::slice::from_raw_parts_mut(
229 dst_planes[plane.as_usize()]
230 .offset(isize_from_u32(y * dst_row_bytes[plane.as_usize()])?),
231 usize_from_u32(plane_data.width)?,
232 )
233 };
234 dst_slice.copy_from_slice(src_slice);
235 }
236 }
237 }
238 Ok(())
239 }
240
241 #[no_mangle]
crabby_avifImageScale( image: *mut avifImage, dstWidth: u32, dstHeight: u32, _diag: *mut avifDiagnostics, ) -> avifResult242 pub unsafe extern "C" fn crabby_avifImageScale(
243 image: *mut avifImage,
244 dstWidth: u32,
245 dstHeight: u32,
246 _diag: *mut avifDiagnostics,
247 ) -> avifResult {
248 // To avoid buffer reallocations, we only support scaling to a smaller size.
249 let dst_image = unsafe { &mut (*image) };
250 if dstWidth > dst_image.width || dstHeight > dst_image.height {
251 return avifResult::NotImplemented;
252 }
253
254 let mut rust_image: image::Image = unsafe { &(*image) }.into();
255 let res = rust_image.scale(dstWidth, dstHeight, Category::Color);
256 if res.is_err() {
257 return to_avifResult(&res);
258 }
259 // The scale function is designed to work only for one category at a time.
260 // Restore the width and height to the original values before scaling the
261 // alpha plane.
262 rust_image.width = unsafe { (*image).width };
263 rust_image.height = unsafe { (*image).height };
264 let res = rust_image.scale(dstWidth, dstHeight, Category::Alpha);
265 if res.is_err() {
266 return to_avifResult(&res);
267 }
268
269 dst_image.width = rust_image.width;
270 dst_image.height = rust_image.height;
271 dst_image.depth = rust_image.depth as _;
272 dst_image.yuvFormat = rust_image.yuv_format;
273 to_avifResult(&CopyPlanes(dst_image, &rust_image))
274 }
275