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