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