Skip to main content

fuchsia_image_format/
image_format.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use anyhow::{Error, anyhow, format_err};
6use fidl_fuchsia_images2 as fimages2;
7use fidl_fuchsia_math::{RectU, SizeU};
8use fidl_fuchsia_sysmem as fsysmem;
9use fidl_fuchsia_sysmem2 as fsysmem2;
10
11use super::linux_drm::DRM_FORMAT_MOD_LINEAR;
12use super::round_up_to_increment;
13
14/// The default image format constraints for allocating buffers.
15pub const IMAGE_FORMAT_CONSTRAINTS_DEFAULT: fsysmem::ImageFormatConstraints =
16    fsysmem::ImageFormatConstraints {
17        pixel_format: fsysmem::PixelFormat {
18            type_: fsysmem::PixelFormatType::Nv12,
19            has_format_modifier: false,
20            format_modifier: fsysmem::FormatModifier { value: 0 },
21        },
22        color_spaces_count: 0,
23        color_space: [fsysmem::ColorSpace { type_: fsysmem::ColorSpaceType::Invalid }; 32],
24        min_coded_width: 0,
25        max_coded_width: 0,
26        min_coded_height: 0,
27        max_coded_height: 0,
28        min_bytes_per_row: 0,
29        max_bytes_per_row: 0,
30        max_coded_width_times_coded_height: 0,
31        layers: 0,
32        coded_width_divisor: 0,
33        coded_height_divisor: 0,
34        bytes_per_row_divisor: 0,
35        start_offset_divisor: 0,
36        display_width_divisor: 0,
37        display_height_divisor: 0,
38        required_min_coded_width: 0,
39        required_max_coded_width: 0,
40        required_min_coded_height: 0,
41        required_max_coded_height: 0,
42        required_min_bytes_per_row: 0,
43        required_max_bytes_per_row: 0,
44    };
45
46/// The default buffers usage for allocating buffers.
47pub const BUFFER_USAGE_DEFAULT: fsysmem::BufferUsage =
48    fsysmem::BufferUsage { none: 0, cpu: 0, vulkan: 0, display: 0, video: 0 };
49
50/// The default buffer memory constraints for allocating buffers.
51pub const BUFFER_MEMORY_CONSTRAINTS_DEFAULT: fsysmem::BufferMemoryConstraints =
52    fsysmem::BufferMemoryConstraints {
53        min_size_bytes: 0,
54        max_size_bytes: u32::MAX,
55        physically_contiguous_required: false,
56        secure_required: false,
57        ram_domain_supported: false,
58        cpu_domain_supported: true,
59        inaccessible_domain_supported: false,
60        heap_permitted_count: 0,
61        heap_permitted: [fsysmem::HeapType::SystemRam; 32],
62    };
63
64/// The default buffer collection constraints for allocating buffers.
65pub const BUFFER_COLLECTION_CONSTRAINTS_DEFAULT: fsysmem::BufferCollectionConstraints =
66    fsysmem::BufferCollectionConstraints {
67        usage: BUFFER_USAGE_DEFAULT,
68        min_buffer_count_for_camping: 0,
69        min_buffer_count_for_dedicated_slack: 0,
70        min_buffer_count_for_shared_slack: 0,
71        min_buffer_count: 0,
72        max_buffer_count: 0,
73        has_buffer_memory_constraints: false,
74        buffer_memory_constraints: BUFFER_MEMORY_CONSTRAINTS_DEFAULT,
75        image_format_constraints_count: 0,
76        image_format_constraints: [IMAGE_FORMAT_CONSTRAINTS_DEFAULT; 32],
77    };
78
79/// Returns the number of bytes per row for a given plane.
80///
81/// Returns an error if no such number of bytes can be found, either because a number can't be
82/// generated from `image_format` or because the `plane` is unsupported.
83pub fn get_plane_row_bytes_2(
84    image_format: &fimages2::ImageFormat,
85    plane: u32,
86) -> Result<u32, Error> {
87    let bytes_per_row = *image_format
88        .bytes_per_row
89        .as_ref()
90        .ok_or_else(|| anyhow!("ImageFormat.bytes_per_row missing - tiled format?"))?;
91    match plane {
92        0 => Ok(bytes_per_row),
93        1 => {
94            let pixel_format = image_format.pixel_format.as_ref().expect("pixel_format");
95            match pixel_format {
96                fimages2::PixelFormat::Nv12 => Ok(bytes_per_row),
97                fimages2::PixelFormat::I420 | fimages2::PixelFormat::Yv12 => Ok(bytes_per_row / 2),
98                _ => Err(anyhow!("Invalid pixel format for plane 1.")),
99            }
100        }
101        2 => {
102            let pixel_format = image_format.pixel_format.as_ref().expect("pixel_format");
103            match pixel_format {
104                fimages2::PixelFormat::I420 | fimages2::PixelFormat::Yv12 => Ok(bytes_per_row / 2),
105                _ => Err(anyhow!("Invalid pixel format for plane 2.")),
106            }
107        }
108        _ => Err(anyhow!("Invalid plane.")),
109    }
110}
111
112/// Returns the number of bytes per row for a given plane.
113///
114/// Returns an error if no such number of bytes can be found, either because a number can't be
115/// generated from `image_format` or because the `plane` is unsupported.
116pub fn get_plane_row_bytes(image_format: &fsysmem::ImageFormat2, plane: u32) -> Result<u32, Error> {
117    match plane {
118        0 => Ok(image_format.bytes_per_row),
119        1 => match image_format.pixel_format.type_ {
120            fsysmem::PixelFormatType::Nv12 => Ok(image_format.bytes_per_row),
121            fsysmem::PixelFormatType::I420 | fsysmem::PixelFormatType::Yv12 => {
122                Ok(image_format.bytes_per_row / 2)
123            }
124            _ => Err(anyhow!("Invalid pixel format for plane 1.")),
125        },
126        2 => match image_format.pixel_format.type_ {
127            fsysmem::PixelFormatType::I420 | fsysmem::PixelFormatType::Yv12 => {
128                Ok(image_format.bytes_per_row / 2)
129            }
130            _ => Err(anyhow!("Invalid pixel format for plane 2.")),
131        },
132        _ => Err(anyhow!("Invalid plane.")),
133    }
134}
135
136/// Returns the byte offset for the given plane.
137///
138/// Returns an error if the `plane` is unsupported or a valid offset can't be generated from
139/// `image_format`.
140pub fn image_format_plane_byte_offset_2(
141    image_format: &fimages2::ImageFormat,
142    plane: u32,
143) -> Result<u32, Error> {
144    match plane {
145        0 => Ok(0),
146        1 => match image_format.pixel_format.as_ref().expect("pixel_format") {
147            fimages2::PixelFormat::Nv12
148            | fimages2::PixelFormat::I420
149            | fimages2::PixelFormat::Yv12 => Ok(image_format.size.as_ref().expect("size").height
150                * image_format.bytes_per_row.as_ref().expect("bytes_per_row")),
151            _ => Err(anyhow!("Invalid pixelformat for plane 1.")),
152        },
153        2 => match image_format.pixel_format.as_ref().expect("pixel_format") {
154            fimages2::PixelFormat::I420 | fimages2::PixelFormat::Yv12 => {
155                let size = image_format.size.as_ref().expect("size");
156                let bytes_per_row = image_format.bytes_per_row.as_ref().expect("bytes_per_row");
157                Ok(size.height * bytes_per_row + size.height / 2 * bytes_per_row / 2)
158            }
159            _ => Err(anyhow!("Invalid pixelformat for plane 2.")),
160        },
161        _ => Err(anyhow!("Invalid plane.")),
162    }
163}
164
165/// Returns the byte offset for the given plane.
166///
167/// Returns an error if the `plane` is unsupported or a valid offset can't be generated from
168/// `image_format`.
169pub fn image_format_plane_byte_offset(
170    image_format: &fsysmem::ImageFormat2,
171    plane: u32,
172) -> Result<u32, Error> {
173    match plane {
174        0 => Ok(0),
175        1 => match image_format.pixel_format.type_ {
176            fsysmem::PixelFormatType::Nv12
177            | fsysmem::PixelFormatType::I420
178            | fsysmem::PixelFormatType::Yv12 => {
179                Ok(image_format.coded_height * image_format.bytes_per_row)
180            }
181            _ => Err(anyhow!("Invalid pixelformat for plane 1.")),
182        },
183        2 => match image_format.pixel_format.type_ {
184            fsysmem::PixelFormatType::I420 | fsysmem::PixelFormatType::Yv12 => {
185                Ok(image_format.coded_height * image_format.bytes_per_row
186                    + image_format.coded_height / 2 * image_format.bytes_per_row / 2)
187            }
188            _ => Err(anyhow!("Invalid pixelformat for plane 2.")),
189        },
190        _ => Err(anyhow!("Invalid plane.")),
191    }
192}
193
194/// Returns the linear size for the given `type_`.
195///
196/// Returns an error if `type_` is unsupported.
197pub fn linear_size(
198    coded_height: u32,
199    bytes_per_row: u32,
200    type_: &fsysmem::PixelFormatType,
201) -> Result<u32, Error> {
202    match type_ {
203        fsysmem::PixelFormatType::R8G8B8A8
204        | fsysmem::PixelFormatType::Bgra32
205        | fsysmem::PixelFormatType::Bgr24
206        | fsysmem::PixelFormatType::Rgb565
207        | fsysmem::PixelFormatType::Rgb332
208        | fsysmem::PixelFormatType::Rgb2220
209        | fsysmem::PixelFormatType::L8
210        | fsysmem::PixelFormatType::R8
211        | fsysmem::PixelFormatType::R8G8
212        | fsysmem::PixelFormatType::A2B10G10R10
213        | fsysmem::PixelFormatType::A2R10G10B10 => Ok(coded_height * bytes_per_row),
214        fsysmem::PixelFormatType::I420 => Ok(coded_height * bytes_per_row * 3 / 2),
215        fsysmem::PixelFormatType::M420 => Ok(coded_height * bytes_per_row * 3 / 2),
216        fsysmem::PixelFormatType::Nv12 => Ok(coded_height * bytes_per_row * 3 / 2),
217        fsysmem::PixelFormatType::Yuy2 => Ok(coded_height * bytes_per_row),
218        fsysmem::PixelFormatType::Yv12 => Ok(coded_height * bytes_per_row * 3 / 2),
219        _ => Err(anyhow!("Invalid pixel format.")),
220    }
221}
222
223/// Converts a `fsysmem::ImageFormatConstraints` to an `fimages2::ImageFormat`.
224pub fn constraints_to_image_format(
225    constraints: &fsysmem2::ImageFormatConstraints,
226    coded_width: u32,
227    coded_height: u32,
228) -> Result<fimages2::ImageFormat, Error> {
229    if let Some(min_size) = &constraints.min_size {
230        if coded_width < min_size.width {
231            return Err(anyhow!("Coded width < min_size.width"));
232        }
233        if coded_height < min_size.height {
234            return Err(anyhow!("Coded height < min_size.height"));
235        }
236    }
237    if let Some(max_size) = &constraints.max_size {
238        if coded_width > max_size.width {
239            return Err(anyhow!("Coded width > max_size.width"));
240        }
241        if coded_height > max_size.height {
242            return Err(anyhow!("Coded height > max_size.height"));
243        }
244    }
245
246    let pixel_format_and_modifier = first_pixel_format_and_modifier_from_constraints(constraints)?;
247
248    let color_space = if constraints.color_spaces.is_some()
249        && constraints.color_spaces.as_ref().unwrap().len() > 0
250    {
251        Some(constraints.color_spaces.as_ref().unwrap()[0])
252    } else {
253        None
254    };
255
256    let format = fimages2::ImageFormat {
257        pixel_format: Some(pixel_format_and_modifier.pixel_format),
258        pixel_format_modifier: Some(pixel_format_and_modifier.pixel_format_modifier),
259        size: Some(SizeU { width: coded_width, height: coded_height }),
260        bytes_per_row: image_format_minimum_row_bytes_2(constraints, coded_width).ok(),
261        color_space,
262        ..Default::default()
263    };
264
265    Ok(format)
266}
267
268/// Converts a `fsysmem::ImageFormatConstraints` to an `fsysmem::ImageFormat2`.
269pub fn constraints_to_format(
270    constraints: &fsysmem::ImageFormatConstraints,
271    coded_width: u32,
272    coded_height: u32,
273) -> Result<fsysmem::ImageFormat2, Error> {
274    if coded_width < constraints.min_coded_width
275        || (constraints.max_coded_width > 0 && coded_width > constraints.max_coded_width)
276    {
277        return Err(anyhow!("Coded width not within constraint bounds."));
278    }
279    if coded_height < constraints.min_coded_height
280        || (constraints.max_coded_height > 0 && coded_height > constraints.max_coded_height)
281    {
282        return Err(anyhow!("Coded height not within constraint bounds."));
283    }
284
285    let format = fsysmem::ImageFormat2 {
286        pixel_format: constraints.pixel_format,
287        coded_width,
288        coded_height,
289        bytes_per_row: image_format_minimum_row_bytes(constraints, coded_width).unwrap_or(0),
290        display_width: coded_width,
291        display_height: coded_height,
292        layers: 0,
293        color_space: if constraints.color_spaces_count > 0 {
294            constraints.color_space[0]
295        } else {
296            fsysmem::ColorSpace { type_: fsysmem::ColorSpaceType::Invalid }
297        },
298        has_pixel_aspect_ratio: false,
299        pixel_aspect_ratio_width: 0,
300        pixel_aspect_ratio_height: 0,
301    };
302
303    Ok(format)
304}
305
306fn first_pixel_format_and_modifier_from_constraints(
307    constraints: &fsysmem2::ImageFormatConstraints,
308) -> Result<fsysmem2::PixelFormatAndModifier, Error> {
309    Ok(if let Some(pixel_format) = constraints.pixel_format {
310        fsysmem2::PixelFormatAndModifier {
311            pixel_format,
312            pixel_format_modifier: *constraints
313                .pixel_format_modifier
314                .as_ref()
315                .unwrap_or(&fimages2::PixelFormatModifier::Linear),
316        }
317    } else {
318        constraints
319            .pixel_format_and_modifiers
320            .as_ref()
321            .ok_or_else(|| format_err!("missing pixel_format"))?
322            .get(0)
323            .ok_or_else(|| format_err!("missing pixel_format"))?
324            .clone()
325    })
326}
327
328/// Returns the minimum row bytes for the given constraints and width.
329///
330/// Returns an error if the width is invalid given the constraints, or the constraint pixel format
331/// modifier is invalid.
332pub fn image_format_minimum_row_bytes_2(
333    constraints: &fsysmem2::ImageFormatConstraints,
334    width: u32,
335) -> Result<u32, Error> {
336    if let Some(pixel_format_modifier) = constraints.pixel_format_modifier {
337        if pixel_format_modifier != fimages2::PixelFormatModifier::Linear {
338            return Err(anyhow!("Non-linear format modifier."));
339        }
340    }
341    if let Some(min_size) = constraints.min_size {
342        if width < min_size.width {
343            return Err(anyhow!("width < min_size.width"));
344        }
345    }
346    if let Some(max_size) = constraints.max_size {
347        if width > max_size.width {
348            return Err(anyhow!("width > max_size.width"));
349        }
350    }
351
352    let constraints_min_bytes_per_row = constraints.min_bytes_per_row.unwrap_or(0);
353
354    let maybe_stride_bytes_per_width_pixel = image_format_stride_bytes_per_width_pixel_2(
355        *constraints.pixel_format.as_ref().expect("pixel_format"),
356    )
357    .ok();
358
359    let mut bytes_per_row_divisor = constraints.bytes_per_row_divisor.unwrap_or(1);
360    if *constraints.require_bytes_per_row_at_pixel_boundary.as_ref().unwrap_or(&false) {
361        let stride_bytes_per_width_pixel = *maybe_stride_bytes_per_width_pixel.as_ref().ok_or_else(|| format_err!("stride_bytes_per_width_pixel required when require_bytes_per_row_at_pixel_boundary true"))?;
362        bytes_per_row_divisor =
363            num_integer::lcm(bytes_per_row_divisor, stride_bytes_per_width_pixel);
364    }
365    let bytes_per_row_divisor = bytes_per_row_divisor;
366
367    round_up_to_increment(
368        std::cmp::max(
369            maybe_stride_bytes_per_width_pixel.unwrap_or(0) * width,
370            constraints_min_bytes_per_row,
371        ) as usize,
372        bytes_per_row_divisor as usize,
373    )
374    .map(|bytes| bytes as u32)
375}
376
377/// Returns the minimum row bytes for the given constraints and width.
378///
379/// Returns an error if the width is invalid given the constraints, or the constraint pixel format
380/// modifier is invalid.
381pub fn image_format_minimum_row_bytes(
382    constraints: &fsysmem::ImageFormatConstraints,
383    width: u32,
384) -> Result<u32, Error> {
385    if constraints.pixel_format.format_modifier.value != DRM_FORMAT_MOD_LINEAR {
386        return Err(anyhow!("Non-linear format modifier."));
387    }
388    if width < constraints.min_coded_width
389        || (constraints.max_coded_width > 0 && width > constraints.max_coded_width)
390    {
391        return Err(anyhow!("Width outside of constraints."));
392    }
393
394    let constraints_min_bytes_per_row = constraints.min_bytes_per_row;
395    let constraints_bytes_per_row_divisor = constraints.bytes_per_row_divisor;
396
397    round_up_to_increment(
398        std::cmp::max(
399            image_format_stride_bytes_per_width_pixel(&constraints.pixel_format) * width,
400            constraints_min_bytes_per_row,
401        ) as usize,
402        constraints_bytes_per_row_divisor as usize,
403    )
404    .map(|bytes| bytes as u32)
405}
406
407pub fn image_format_stride_bytes_per_width_pixel_2(
408    pixel_format: fimages2::PixelFormat,
409) -> Result<u32, Error> {
410    match pixel_format {
411        fimages2::PixelFormat::R8G8B8A8 => Ok(4),
412        fimages2::PixelFormat::R8G8B8X8 => Ok(4),
413        fimages2::PixelFormat::B8G8R8A8 => Ok(4),
414        fimages2::PixelFormat::B8G8R8X8 => Ok(4),
415        fimages2::PixelFormat::B8G8R8 => Ok(3),
416        fimages2::PixelFormat::R8G8B8 => Ok(3),
417        fimages2::PixelFormat::I420 => Ok(1),
418        fimages2::PixelFormat::M420 => Ok(1),
419        fimages2::PixelFormat::Nv12 => Ok(1),
420        fimages2::PixelFormat::Yuy2 => Ok(2),
421        fimages2::PixelFormat::Yv12 => Ok(1),
422        fimages2::PixelFormat::R5G6B5 => Ok(2),
423        fimages2::PixelFormat::R3G3B2 => Ok(1),
424        fimages2::PixelFormat::R2G2B2X2 => Ok(1),
425        fimages2::PixelFormat::L8 => Ok(1),
426        fimages2::PixelFormat::R8 => Ok(1),
427        fimages2::PixelFormat::R8G8 => Ok(2),
428        fimages2::PixelFormat::A2B10G10R10 => Ok(4),
429        fimages2::PixelFormat::A2R10G10B10 => Ok(4),
430        fimages2::PixelFormat::P010 => Ok(2),
431        _ => Err(format_err!(
432            "stride_bytes_per_width_pixel not available for pixel_format: {:?}",
433            pixel_format
434        )),
435    }
436}
437
438pub fn image_format_stride_bytes_per_width_pixel(pixel_format: &fsysmem::PixelFormat) -> u32 {
439    match pixel_format.type_ {
440        fsysmem::PixelFormatType::Invalid
441        | fsysmem::PixelFormatType::Mjpeg
442        | fsysmem::PixelFormatType::DoNotCare => 0,
443        fsysmem::PixelFormatType::R8G8B8A8 => 4,
444        fsysmem::PixelFormatType::Bgra32 => 4,
445        fsysmem::PixelFormatType::Bgr24 => 3,
446        fsysmem::PixelFormatType::I420 => 1,
447        fsysmem::PixelFormatType::M420 => 1,
448        fsysmem::PixelFormatType::Nv12 => 1,
449        fsysmem::PixelFormatType::Yuy2 => 2,
450        fsysmem::PixelFormatType::Yv12 => 1,
451        fsysmem::PixelFormatType::Rgb565 => 2,
452        fsysmem::PixelFormatType::Rgb332 => 1,
453        fsysmem::PixelFormatType::Rgb2220 => 1,
454        fsysmem::PixelFormatType::L8 => 1,
455        fsysmem::PixelFormatType::R8 => 1,
456        fsysmem::PixelFormatType::R8G8 => 2,
457        fsysmem::PixelFormatType::A2B10G10R10 => 4,
458        fsysmem::PixelFormatType::A2R10G10B10 => 4,
459    }
460}
461
462pub fn sysmem1_pixel_format_type_from_images2_pixel_format(
463    pixel_format: fimages2::PixelFormat,
464) -> Result<fsysmem::PixelFormatType, Error> {
465    fsysmem::PixelFormatType::from_primitive(pixel_format.into_primitive())
466        .ok_or_else(|| format_err!("pixel_format not convertible to sysmem1: {:?}", pixel_format))
467}
468
469pub fn images2_pixel_format_from_sysmem_pixel_format_type(
470    pixel_format: fsysmem::PixelFormatType,
471) -> Result<fimages2::PixelFormat, Error> {
472    fimages2::PixelFormat::from_primitive(pixel_format.into_primitive())
473        .ok_or_else(|| format_err!("pixel_format not convertible to images2: {:?}", pixel_format))
474}
475
476pub fn sysmem1_pixel_format_modifier_from_images2_pixel_format_modifier(
477    pixel_format_modifier: fimages2::PixelFormatModifier,
478) -> u64 {
479    if pixel_format_modifier == fimages2::PixelFormatModifier::GoogleGoldfishOptimal {
480        return fsysmem::FORMAT_MODIFIER_GOOGLE_GOLDFISH_OPTIMAL;
481    }
482    pixel_format_modifier.into_primitive()
483}
484
485pub fn images2_pixel_format_modifier_from_sysmem_pixel_format_modifier(
486    pixel_format_modifier: u64,
487) -> Result<fimages2::PixelFormatModifier, Error> {
488    if pixel_format_modifier == fsysmem::FORMAT_MODIFIER_GOOGLE_GOLDFISH_OPTIMAL {
489        return Ok(fimages2::PixelFormatModifier::GoogleGoldfishOptimal);
490    }
491    fimages2::PixelFormatModifier::from_primitive(pixel_format_modifier).ok_or_else(|| {
492        format_err!("pixel_format_modifier not convertible to images2: {:?}", pixel_format_modifier)
493    })
494}
495
496pub fn sysmem1_color_space_type_from_images2_color_space(
497    color_space: fimages2::ColorSpace,
498) -> Result<fsysmem::ColorSpaceType, Error> {
499    fsysmem::ColorSpaceType::from_primitive(color_space.into_primitive())
500        .ok_or_else(|| format_err!("color_space not convertible to sysmem1: {:?}", color_space))
501}
502
503pub fn images2_color_space_from_sysmem_color_space_type(
504    color_space: fsysmem::ColorSpaceType,
505) -> fimages2::ColorSpace {
506    // This can't fail because sysmem(1) ColorSpaceType isn't flexible and all primitive values also
507    // exist in images2::ColorSpace.
508    fimages2::ColorSpace::from_primitive(color_space.into_primitive()).unwrap()
509}
510
511pub fn sysmem1_image_format_from_images2_image_format(
512    image_format: &fimages2::ImageFormat,
513) -> Result<fsysmem::ImageFormat2, Error> {
514    let coded_size = image_format.size.as_ref().ok_or_else(|| format_err!("missing size"))?;
515    let format_modifier = sysmem1_pixel_format_modifier_from_images2_pixel_format_modifier(
516        *image_format
517            .pixel_format_modifier
518            .as_ref()
519            .unwrap_or(&fimages2::PixelFormatModifier::Linear),
520    );
521    // bytes_per_row 0 is only to be used by tiled formats; not intended to be optional for
522    // PixelFormatModifier::Linear
523    let bytes_per_row = match format_modifier {
524        fsysmem::FORMAT_MODIFIER_LINEAR => {
525            // bytes_per_row required
526            *image_format
527                .bytes_per_row
528                .as_ref()
529                .ok_or_else(|| format_err!("missing bytes_per_row (required when Linear)"))?
530        }
531        _ => {
532            // bytes_per_row can be un-set for tiled formats (un-set is preferred over 0 in images2, but we also allow 0 here)
533            *image_format.bytes_per_row.as_ref().unwrap_or(&0)
534        }
535    };
536    let display_rect = image_format.display_rect.as_ref();
537    let color_space_type = sysmem1_color_space_type_from_images2_color_space(
538        *image_format.color_space.as_ref().ok_or_else(|| format_err!("missing color_space"))?,
539    )?;
540    let pixel_aspect_ratio = image_format.pixel_aspect_ratio.as_ref();
541    Ok(fsysmem::ImageFormat2 {
542        pixel_format: fsysmem::PixelFormat {
543            type_: sysmem1_pixel_format_type_from_images2_pixel_format(
544                *image_format
545                    .pixel_format
546                    .as_ref()
547                    .ok_or_else(|| format_err!("missing pixel_format"))?,
548            )?,
549            // Semantically it should be safe to always set has_format_modifier true, but we
550            // preserve the "has" aspect here just in case any client code / test code is expecting
551            // to see false.
552            has_format_modifier: image_format.pixel_format_modifier.is_some(),
553            format_modifier: fsysmem::FormatModifier { value: format_modifier },
554        },
555        coded_width: coded_size.width,
556        coded_height: coded_size.height,
557        bytes_per_row,
558        display_width: display_rect.map(|rect| rect.width).unwrap_or(0),
559        display_height: display_rect.map(|rect| rect.height).unwrap_or(0),
560        layers: 1,
561        color_space: fsysmem::ColorSpace { type_: color_space_type },
562        has_pixel_aspect_ratio: pixel_aspect_ratio.is_some(),
563        pixel_aspect_ratio_width: pixel_aspect_ratio.map(|size| size.width).unwrap_or(0),
564        pixel_aspect_ratio_height: pixel_aspect_ratio.map(|size| size.height).unwrap_or(0),
565    })
566}
567
568pub fn images2_image_format_from_sysmem_image_format(
569    image_format: &fsysmem::ImageFormat2,
570) -> Result<fimages2::ImageFormat, Error> {
571    let pixel_format_modifier = if image_format.pixel_format.has_format_modifier {
572        Some(images2_pixel_format_modifier_from_sysmem_pixel_format_modifier(
573            image_format.pixel_format.format_modifier.value,
574        )?)
575    } else {
576        None
577    };
578    let display_rect = if image_format.display_width != 0 || image_format.display_height != 0 {
579        let display_width = if image_format.display_width != 0 {
580            image_format.display_width
581        } else {
582            image_format.coded_width
583        };
584        let display_height = if image_format.display_height != 0 {
585            image_format.display_height
586        } else {
587            image_format.coded_height
588        };
589        Some(RectU { x: 0, y: 0, width: display_width, height: display_height })
590    } else {
591        None
592    };
593    let pixel_aspect_ratio = if image_format.has_pixel_aspect_ratio {
594        Some(SizeU {
595            width: image_format.pixel_aspect_ratio_width,
596            height: image_format.pixel_aspect_ratio_height,
597        })
598    } else {
599        None
600    };
601    Ok(fimages2::ImageFormat {
602        pixel_format: Some(images2_pixel_format_from_sysmem_pixel_format_type(
603            image_format.pixel_format.type_,
604        )?),
605        pixel_format_modifier,
606        color_space: Some(images2_color_space_from_sysmem_color_space_type(
607            image_format.color_space.type_,
608        )),
609        size: Some(SizeU { width: image_format.coded_width, height: image_format.coded_height }),
610        bytes_per_row: Some(image_format.bytes_per_row),
611        display_rect,
612        pixel_aspect_ratio,
613        ..Default::default()
614    })
615}
616
617#[cfg(test)]
618mod tests {
619    use super::*;
620    use fidl_fuchsia_math as fmath;
621
622    #[test]
623    fn test_linear_row_bytes_2() {
624        let constraints = fsysmem2::ImageFormatConstraints {
625            pixel_format: Some(fimages2::PixelFormat::B8G8R8A8),
626            pixel_format_modifier: Some(fimages2::PixelFormatModifier::Linear),
627            min_size: Some(SizeU { width: 12, height: 12 }),
628            max_size: Some(SizeU { width: 100, height: 100 }),
629            bytes_per_row_divisor: Some(4 * 8),
630            max_bytes_per_row: Some(100000),
631            ..Default::default()
632        };
633
634        assert_eq!(image_format_minimum_row_bytes_2(&constraints, 17).unwrap(), 4 * 24);
635
636        assert!(image_format_minimum_row_bytes_2(&constraints, 11).is_err());
637        assert!(image_format_minimum_row_bytes_2(&constraints, 101).is_err());
638    }
639
640    #[test]
641    fn test_linear_row_bytes() {
642        let linear = fsysmem::PixelFormat {
643            type_: fsysmem::PixelFormatType::Bgra32,
644            has_format_modifier: true,
645            format_modifier: fsysmem::FormatModifier { value: fsysmem::FORMAT_MODIFIER_LINEAR },
646        };
647
648        let constraints = fsysmem::ImageFormatConstraints {
649            pixel_format: linear,
650            min_coded_width: 12,
651            max_coded_width: 100,
652            bytes_per_row_divisor: 4 * 8,
653            max_bytes_per_row: 100000,
654            ..IMAGE_FORMAT_CONSTRAINTS_DEFAULT
655        };
656
657        assert_eq!(image_format_minimum_row_bytes(&constraints, 17).unwrap(), 4 * 24);
658
659        assert!(image_format_minimum_row_bytes(&constraints, 11).is_err());
660        assert!(image_format_minimum_row_bytes(&constraints, 101).is_err());
661    }
662
663    #[test]
664    fn plane_byte_offset_2() {
665        let constraints = fsysmem2::ImageFormatConstraints {
666            pixel_format: Some(fimages2::PixelFormat::B8G8R8A8),
667            pixel_format_modifier: Some(fimages2::PixelFormatModifier::Linear),
668            min_size: Some(SizeU { width: 12, height: 12 }),
669            max_size: Some(SizeU { width: 100, height: 100 }),
670            bytes_per_row_divisor: Some(4 * 8),
671            max_bytes_per_row: Some(100000),
672            ..Default::default()
673        };
674
675        let image_format = constraints_to_image_format(&constraints, 18, 17).unwrap();
676        // The raw size would be 72 without bytes_per_row_divisor of 32.
677        assert_eq!(*image_format.bytes_per_row.as_ref().unwrap(), 96);
678
679        assert_eq!(image_format_plane_byte_offset_2(&image_format, 0).unwrap(), 0);
680        assert!(image_format_plane_byte_offset_2(&image_format, 1).is_err());
681
682        let constraints = fsysmem2::ImageFormatConstraints {
683            pixel_format: Some(fimages2::PixelFormat::I420),
684            pixel_format_modifier: Some(fimages2::PixelFormatModifier::Linear),
685            min_size: Some(SizeU { width: 12, height: 12 }),
686            max_size: Some(SizeU { width: 100, height: 100 }),
687            bytes_per_row_divisor: Some(4 * 8),
688            max_bytes_per_row: Some(100000),
689            ..Default::default()
690        };
691
692        const BYTES_PER_ROW: u32 = 32;
693        let image_format = constraints_to_image_format(&constraints, 18, 20).unwrap();
694        assert_eq!(*image_format.bytes_per_row.as_ref().unwrap(), BYTES_PER_ROW);
695
696        assert_eq!(image_format_plane_byte_offset_2(&image_format, 0).unwrap(), 0);
697        assert_eq!(image_format_plane_byte_offset_2(&image_format, 1).unwrap(), BYTES_PER_ROW * 20);
698        assert_eq!(
699            image_format_plane_byte_offset_2(&image_format, 2).unwrap(),
700            BYTES_PER_ROW * 20 + BYTES_PER_ROW / 2 * 20 / 2
701        );
702        assert!(image_format_plane_byte_offset_2(&image_format, 3).is_err());
703
704        assert_eq!(get_plane_row_bytes_2(&image_format, 0).unwrap(), BYTES_PER_ROW);
705        assert_eq!(get_plane_row_bytes_2(&image_format, 1).unwrap(), BYTES_PER_ROW / 2);
706        assert_eq!(get_plane_row_bytes_2(&image_format, 2).unwrap(), BYTES_PER_ROW / 2);
707        assert!(get_plane_row_bytes_2(&image_format, 3).is_err())
708    }
709
710    #[test]
711    fn plane_byte_offset() {
712        let constraints = fsysmem2::ImageFormatConstraints {
713            pixel_format: Some(fimages2::PixelFormat::B8G8R8A8),
714            pixel_format_modifier: Some(fimages2::PixelFormatModifier::Linear),
715            min_size: Some(SizeU { width: 12, height: 12 }),
716            max_size: Some(SizeU { width: 100, height: 100 }),
717            bytes_per_row_divisor: Some(4 * 8),
718            max_bytes_per_row: Some(100000),
719            ..Default::default()
720        };
721
722        let image_format = constraints_to_image_format(&constraints, 18, 17).unwrap();
723        // The raw size would be 72 without bytes_per_row_divisor of 32.
724        assert_eq!(*image_format.bytes_per_row.as_ref().unwrap(), 96);
725
726        assert_eq!(image_format_plane_byte_offset_2(&image_format, 0).unwrap(), 0);
727        assert!(image_format_plane_byte_offset_2(&image_format, 1).is_err());
728
729        let constraints = fsysmem2::ImageFormatConstraints {
730            pixel_format: Some(fimages2::PixelFormat::I420),
731            pixel_format_modifier: Some(fimages2::PixelFormatModifier::Linear),
732            min_size: Some(SizeU { width: 12, height: 12 }),
733            max_size: Some(SizeU { width: 100, height: 100 }),
734            bytes_per_row_divisor: Some(4 * 8),
735            max_bytes_per_row: Some(100000),
736            ..Default::default()
737        };
738
739        const BYTES_PER_ROW: u32 = 32;
740        let image_format = constraints_to_image_format(&constraints, 18, 20).unwrap();
741        assert_eq!(*image_format.bytes_per_row.as_ref().unwrap(), BYTES_PER_ROW);
742
743        assert_eq!(image_format_plane_byte_offset_2(&image_format, 0).unwrap(), 0);
744        assert_eq!(image_format_plane_byte_offset_2(&image_format, 1).unwrap(), BYTES_PER_ROW * 20);
745        assert_eq!(
746            image_format_plane_byte_offset_2(&image_format, 2).unwrap(),
747            BYTES_PER_ROW * 20 + BYTES_PER_ROW / 2 * 20 / 2
748        );
749        assert!(image_format_plane_byte_offset_2(&image_format, 3).is_err());
750
751        assert_eq!(get_plane_row_bytes_2(&image_format, 0).unwrap(), BYTES_PER_ROW);
752        assert_eq!(get_plane_row_bytes_2(&image_format, 1).unwrap(), BYTES_PER_ROW / 2);
753        assert_eq!(get_plane_row_bytes_2(&image_format, 2).unwrap(), BYTES_PER_ROW / 2);
754        assert!(get_plane_row_bytes_2(&image_format, 3).is_err())
755    }
756
757    #[test]
758    fn test_constraints_to_image_format() {
759        // The fields set to 37 are intentionally checking that those fields are ignored by the
760        // conversion, at least for now.
761        let constraints = fsysmem2::ImageFormatConstraints {
762            pixel_format: Some(fimages2::PixelFormat::R8G8B8),
763            pixel_format_modifier: Some(fimages2::PixelFormatModifier::Linear),
764            min_size: Some(SizeU { width: 12, height: 12 }),
765            max_size: Some(SizeU { width: 100, height: 100 }),
766            bytes_per_row_divisor: Some(4 * 8),
767            max_bytes_per_row: Some(100000),
768            color_spaces: Some(vec![fimages2::ColorSpace::Rec709]),
769            min_bytes_per_row: Some(24),
770            max_width_times_height: Some(12 * 12),
771            size_alignment: Some(fmath::SizeU { width: 37, height: 1 }),
772            display_rect_alignment: Some(fmath::SizeU { width: 37, height: 37 }),
773            required_min_size: Some(fmath::SizeU { width: 37, height: 37 }),
774            required_max_size: Some(fmath::SizeU { width: 37, height: 37 }),
775            start_offset_divisor: Some(37),
776            pixel_format_and_modifiers: Some(vec![fsysmem2::PixelFormatAndModifier {
777                pixel_format: fimages2::PixelFormat::Yv12,
778                pixel_format_modifier: fimages2::PixelFormatModifier::GoogleGoldfishOptimal,
779            }]),
780            require_bytes_per_row_at_pixel_boundary: Some(true),
781            ..Default::default()
782        };
783
784        let image_format = constraints_to_image_format(&constraints, 18, 17).unwrap();
785
786        assert_eq!(image_format.pixel_format, Some(fimages2::PixelFormat::R8G8B8));
787        assert_eq!(image_format.pixel_format_modifier, Some(fimages2::PixelFormatModifier::Linear));
788        assert_eq!(image_format.color_space, Some(fimages2::ColorSpace::Rec709));
789        assert_eq!(image_format.size, Some(fmath::SizeU { width: 18, height: 17 }));
790        assert_eq!(image_format.bytes_per_row, Some(96));
791
792        // We intentionally want these to be None when we don't have any explicit info for them.
793        assert_eq!(image_format.display_rect, None);
794        assert_eq!(image_format.valid_size, None);
795        assert_eq!(image_format.pixel_aspect_ratio, None);
796    }
797
798    #[test]
799    fn test_image_format_stride_bytes_per_width_pixel_2() {
800        assert_eq!(
801            4,
802            image_format_stride_bytes_per_width_pixel_2(fimages2::PixelFormat::R8G8B8A8).unwrap()
803        );
804        assert!(
805            image_format_stride_bytes_per_width_pixel_2(fimages2::PixelFormat::DoNotCare).is_err()
806        );
807    }
808
809    #[test]
810    fn test_sysmem1_pixel_format_type_from_images2_pixel_format() {
811        assert_eq!(
812            fsysmem::PixelFormatType::Bgra32,
813            sysmem1_pixel_format_type_from_images2_pixel_format(fimages2::PixelFormat::B8G8R8A8)
814                .unwrap()
815        );
816        assert!(
817            sysmem1_pixel_format_type_from_images2_pixel_format(
818                fimages2::PixelFormat::from_primitive_allow_unknown(1189673091)
819            )
820            .is_err()
821        );
822    }
823
824    #[test]
825    fn test_sysmem1_pixel_format_modifier_from_images2_pixel_format_modifier() {
826        assert_eq!(
827            fsysmem::FORMAT_MODIFIER_ARM_AFBC_16_X16_SPLIT_BLOCK_SPARSE_YUV_TE_TILED_HEADER,
828            sysmem1_pixel_format_modifier_from_images2_pixel_format_modifier(
829                fimages2::PixelFormatModifier::ArmAfbc16X16SplitBlockSparseYuvTeTiledHeader
830            )
831        );
832        assert_eq!(
833            fsysmem::FORMAT_MODIFIER_GOOGLE_GOLDFISH_OPTIMAL,
834            sysmem1_pixel_format_modifier_from_images2_pixel_format_modifier(
835                fimages2::PixelFormatModifier::GoogleGoldfishOptimal
836            )
837        );
838        assert_eq!(
839            fsysmem::FORMAT_MODIFIER_GOOGLE_GOLDFISH_OPTIMAL,
840            sysmem1_pixel_format_modifier_from_images2_pixel_format_modifier(
841                fimages2::PixelFormatModifier::from_primitive_allow_unknown(
842                    fsysmem::FORMAT_MODIFIER_GOOGLE_GOLDFISH_OPTIMAL
843                )
844            )
845        );
846        // We intentionally allow too-new values to convert.
847        assert_eq!(
848            1189673091,
849            sysmem1_pixel_format_modifier_from_images2_pixel_format_modifier(
850                fimages2::PixelFormatModifier::from_primitive_allow_unknown(1189673091)
851            )
852        );
853    }
854
855    #[test]
856    fn test_images2_pixel_format_from_sysmem_pixel_format_type() {
857        assert_eq!(
858            fimages2::PixelFormat::B8G8R8A8,
859            images2_pixel_format_from_sysmem_pixel_format_type(fsysmem::PixelFormatType::Bgra32)
860                .unwrap()
861        );
862        assert_eq!(
863            fimages2::PixelFormat::Yv12,
864            images2_pixel_format_from_sysmem_pixel_format_type(fsysmem::PixelFormatType::Yv12)
865                .unwrap()
866        );
867    }
868
869    #[test]
870    fn test_images2_pixel_format_modifier_from_sysmem_pixel_format_modifier() {
871        assert_eq!(
872            fimages2::PixelFormatModifier::ArmAfbc16X16SplitBlockSparseYuvTeTiledHeader,
873            images2_pixel_format_modifier_from_sysmem_pixel_format_modifier(
874                fsysmem::FORMAT_MODIFIER_ARM_AFBC_16_X16_SPLIT_BLOCK_SPARSE_YUV_TE_TILED_HEADER
875            )
876            .unwrap()
877        );
878        assert_eq!(
879            fimages2::PixelFormatModifier::GoogleGoldfishOptimal,
880            images2_pixel_format_modifier_from_sysmem_pixel_format_modifier(
881                fsysmem::FORMAT_MODIFIER_GOOGLE_GOLDFISH_OPTIMAL
882            )
883            .unwrap()
884        );
885        assert_eq!(
886            fimages2::PixelFormatModifier::GoogleGoldfishOptimal,
887            images2_pixel_format_modifier_from_sysmem_pixel_format_modifier(
888                fimages2::PixelFormatModifier::GoogleGoldfishOptimal.into_primitive()
889            )
890            .unwrap()
891        );
892        assert!(
893            images2_pixel_format_modifier_from_sysmem_pixel_format_modifier(1189673091).is_err()
894        );
895    }
896
897    #[test]
898    fn test_sysmem1_color_space_type_from_images2_color_space() {
899        assert_eq!(
900            fsysmem::ColorSpaceType::Rec709,
901            sysmem1_color_space_type_from_images2_color_space(fimages2::ColorSpace::Rec709)
902                .unwrap()
903        );
904        assert!(
905            sysmem1_color_space_type_from_images2_color_space(
906                fimages2::ColorSpace::from_primitive_allow_unknown(1189673091)
907            )
908            .is_err()
909        );
910    }
911
912    #[test]
913    fn test_images2_color_space_from_sysmem_color_space_type() {
914        assert_eq!(
915            fimages2::ColorSpace::Rec709,
916            images2_color_space_from_sysmem_color_space_type(fsysmem::ColorSpaceType::Rec709)
917        );
918        assert_eq!(
919            fimages2::ColorSpace::Srgb,
920            images2_color_space_from_sysmem_color_space_type(fsysmem::ColorSpaceType::Srgb)
921        );
922    }
923
924    #[test]
925    fn test_sysmem1_image_format_from_images2_image_format() {
926        {
927            let images2_format = fimages2::ImageFormat {
928                pixel_format: Some(fimages2::PixelFormat::Yv12),
929                pixel_format_modifier: None,
930                color_space: Some(fimages2::ColorSpace::Rec709),
931                size: Some(fmath::SizeU { width: 17, height: 13 }),
932                bytes_per_row: Some(32),
933                display_rect: Some(fmath::RectU { x: 0, y: 0, width: 15, height: 11 }),
934                valid_size: Some(fmath::SizeU { width: 12, height: 10 }),
935                pixel_aspect_ratio: Some(fmath::SizeU { width: 2, height: 3 }),
936                ..Default::default()
937            };
938
939            let sysmem_format =
940                sysmem1_image_format_from_images2_image_format(&images2_format).unwrap();
941
942            assert_eq!(fsysmem::PixelFormatType::Yv12, sysmem_format.pixel_format.type_);
943            assert_eq!(false, sysmem_format.pixel_format.has_format_modifier);
944            assert_eq!(17, sysmem_format.coded_width);
945            assert_eq!(13, sysmem_format.coded_height);
946            assert_eq!(32, sysmem_format.bytes_per_row);
947            assert_eq!(15, sysmem_format.display_width);
948            assert_eq!(11, sysmem_format.display_height);
949            assert_eq!(1, sysmem_format.layers);
950            assert_eq!(fsysmem::ColorSpaceType::Rec709, sysmem_format.color_space.type_);
951            assert_eq!(true, sysmem_format.has_pixel_aspect_ratio);
952            assert_eq!(2, sysmem_format.pixel_aspect_ratio_width);
953            assert_eq!(3, sysmem_format.pixel_aspect_ratio_height);
954        }
955
956        {
957            let images2_format = fimages2::ImageFormat {
958                pixel_format: Some(fimages2::PixelFormat::Yv12),
959                pixel_format_modifier: Some(
960                    fimages2::PixelFormatModifier::ArmAfbc16X16SplitBlockSparseYuvTeTiledHeader,
961                ),
962                color_space: Some(fimages2::ColorSpace::Rec709),
963                size: Some(fmath::SizeU { width: 17, height: 13 }),
964                bytes_per_row: Some(32),
965                display_rect: Some(fmath::RectU { x: 0, y: 0, width: 15, height: 11 }),
966                valid_size: Some(fmath::SizeU { width: 12, height: 10 }),
967                pixel_aspect_ratio: Some(fmath::SizeU { width: 2, height: 3 }),
968                ..Default::default()
969            };
970
971            let sysmem_format =
972                sysmem1_image_format_from_images2_image_format(&images2_format).unwrap();
973
974            assert_eq!(fsysmem::PixelFormatType::Yv12, sysmem_format.pixel_format.type_);
975            assert_eq!(true, sysmem_format.pixel_format.has_format_modifier);
976            assert_eq!(
977                fsysmem::FORMAT_MODIFIER_ARM_AFBC_16_X16_SPLIT_BLOCK_SPARSE_YUV_TE_TILED_HEADER,
978                sysmem_format.pixel_format.format_modifier.value
979            );
980            assert_eq!(17, sysmem_format.coded_width);
981            assert_eq!(13, sysmem_format.coded_height);
982            assert_eq!(32, sysmem_format.bytes_per_row);
983            assert_eq!(15, sysmem_format.display_width);
984            assert_eq!(11, sysmem_format.display_height);
985            assert_eq!(1, sysmem_format.layers);
986            assert_eq!(fsysmem::ColorSpaceType::Rec709, sysmem_format.color_space.type_);
987            assert_eq!(true, sysmem_format.has_pixel_aspect_ratio);
988            assert_eq!(2, sysmem_format.pixel_aspect_ratio_width);
989            assert_eq!(3, sysmem_format.pixel_aspect_ratio_height);
990        }
991    }
992
993    #[test]
994    fn test_images2_image_format_from_sysmem_image_format() {
995        let sysmem_format = fsysmem::ImageFormat2 {
996            pixel_format: fsysmem::PixelFormat {
997                type_: fsysmem::PixelFormatType::Yv12,
998                has_format_modifier: true,
999                format_modifier: fsysmem::FormatModifier {
1000                    value: fsysmem::FORMAT_MODIFIER_GOOGLE_GOLDFISH_OPTIMAL,
1001                },
1002            },
1003            coded_width: 17,
1004            coded_height: 13,
1005            bytes_per_row: 32,
1006            display_width: 15,
1007            display_height: 11,
1008            layers: 1,
1009            color_space: fsysmem::ColorSpace { type_: fsysmem::ColorSpaceType::Rec709 },
1010            has_pixel_aspect_ratio: true,
1011            pixel_aspect_ratio_width: 2,
1012            pixel_aspect_ratio_height: 3,
1013        };
1014
1015        let images2_format = images2_image_format_from_sysmem_image_format(&sysmem_format).unwrap();
1016
1017        assert_eq!(Some(fimages2::PixelFormat::Yv12), images2_format.pixel_format);
1018        assert_eq!(
1019            Some(fimages2::PixelFormatModifier::GoogleGoldfishOptimal),
1020            images2_format.pixel_format_modifier
1021        );
1022        assert_eq!(Some(fimages2::ColorSpace::Rec709), images2_format.color_space);
1023        assert_eq!(Some(fmath::SizeU { width: 17, height: 13 }), images2_format.size);
1024        assert_eq!(Some(32), images2_format.bytes_per_row);
1025        assert_eq!(
1026            Some(fmath::RectU { x: 0, y: 0, width: 15, height: 11 }),
1027            images2_format.display_rect
1028        );
1029        assert_eq!(None, images2_format.valid_size);
1030        assert_eq!(Some(fmath::SizeU { width: 2, height: 3 }), images2_format.pixel_aspect_ratio);
1031    }
1032}