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
79const PVRIC_TILES_PLANE: u32 = 0;
80const PVRIC_HEADER_PLANE: u32 = 3;
81const PVRIC_HEADER_ALIGNMENT: u32 = 256;
82
83fn is_pvric_pixel_format_supported(pixel_format: fimages2::PixelFormat) -> bool {
84    matches!(
85        pixel_format,
86        fimages2::PixelFormat::R8G8B8A8
87            | fimages2::PixelFormat::R8G8B8X8
88            | fimages2::PixelFormat::B8G8R8A8
89            | fimages2::PixelFormat::B8G8R8X8
90            | fimages2::PixelFormat::A2R10G10B10
91            | fimages2::PixelFormat::A2B10G10R10
92            | fimages2::PixelFormat::R5G6B5
93    )
94}
95
96#[derive(Clone, Copy, Debug, Eq, PartialEq)]
97pub struct PvricTilingData {
98    pub tile_rows: u32,
99    pub bytes_per_row_per_tile: u32,
100}
101
102pub fn pvric_tiling_data(modifier: fimages2::PixelFormatModifier) -> Option<PvricTilingData> {
103    match modifier {
104        fimages2::PixelFormatModifier::PvrFbcdc8X8V14 => {
105            Some(PvricTilingData { tile_rows: 8, bytes_per_row_per_tile: 32 })
106        }
107        fimages2::PixelFormatModifier::PvrFbcdc16X4V14 => {
108            Some(PvricTilingData { tile_rows: 4, bytes_per_row_per_tile: 64 })
109        }
110        _ => None,
111    }
112}
113
114/// Returns the number of bytes per row for a given plane.
115///
116/// Returns an error if no such number of bytes can be found, either because a number can't be
117/// generated from `image_format` or because the `plane` is unsupported.
118pub fn get_plane_row_bytes_2(
119    image_format: &fimages2::ImageFormat,
120    plane: u32,
121) -> Result<u32, Error> {
122    let bytes_per_row = *image_format
123        .bytes_per_row
124        .as_ref()
125        .ok_or_else(|| anyhow!("ImageFormat.bytes_per_row missing - tiled format?"))?;
126
127    // TODO(https://fxbug.dev/576739649): PVRIC is currently handled as a
128    // special case. Generalize.
129    if let Some(modifier) = image_format.pixel_format_modifier {
130        if let Some(tiling) = pvric_tiling_data(modifier) {
131            let pixel_format = *image_format
132                .pixel_format
133                .as_ref()
134                .ok_or_else(|| anyhow!("ImageFormat.pixel_format missing"))?;
135            if !is_pvric_pixel_format_supported(pixel_format) {
136                return Err(anyhow!("Invalid pixel format for PVRIC modifier."));
137            }
138            let width_in_tiles = bytes_per_row.div_ceil(tiling.bytes_per_row_per_tile);
139            return match plane {
140                PVRIC_TILES_PLANE => width_in_tiles
141                    .checked_mul(tiling.bytes_per_row_per_tile)
142                    .ok_or_else(|| anyhow!("Overflow calculating plane 0 row bytes.")),
143                PVRIC_HEADER_PLANE => Ok(width_in_tiles),
144                _ => Err(anyhow!("Invalid plane.")),
145            };
146        }
147    }
148    match plane {
149        0 => Ok(bytes_per_row),
150        1 => {
151            let pixel_format = image_format.pixel_format.as_ref().expect("pixel_format");
152            match pixel_format {
153                fimages2::PixelFormat::Nv12 => Ok(bytes_per_row),
154                fimages2::PixelFormat::I420 | fimages2::PixelFormat::Yv12 => Ok(bytes_per_row / 2),
155                _ => Err(anyhow!("Invalid pixel format for plane 1.")),
156            }
157        }
158        2 => {
159            let pixel_format = image_format.pixel_format.as_ref().expect("pixel_format");
160            match pixel_format {
161                fimages2::PixelFormat::I420 | fimages2::PixelFormat::Yv12 => Ok(bytes_per_row / 2),
162                _ => Err(anyhow!("Invalid pixel format for plane 2.")),
163            }
164        }
165        _ => Err(anyhow!("Invalid plane.")),
166    }
167}
168
169/// Returns the number of bytes per row for a given plane.
170///
171/// Returns an error if no such number of bytes can be found, either because a number can't be
172/// generated from `image_format` or because the `plane` is unsupported.
173pub fn get_plane_row_bytes(image_format: &fsysmem::ImageFormat2, plane: u32) -> Result<u32, Error> {
174    match plane {
175        0 => Ok(image_format.bytes_per_row),
176        1 => match image_format.pixel_format.type_ {
177            fsysmem::PixelFormatType::Nv12 => Ok(image_format.bytes_per_row),
178            fsysmem::PixelFormatType::I420 | fsysmem::PixelFormatType::Yv12 => {
179                Ok(image_format.bytes_per_row / 2)
180            }
181            _ => Err(anyhow!("Invalid pixel format for plane 1.")),
182        },
183        2 => match image_format.pixel_format.type_ {
184            fsysmem::PixelFormatType::I420 | fsysmem::PixelFormatType::Yv12 => {
185                Ok(image_format.bytes_per_row / 2)
186            }
187            _ => Err(anyhow!("Invalid pixel format for plane 2.")),
188        },
189        _ => Err(anyhow!("Invalid plane.")),
190    }
191}
192
193/// Returns the byte offset for the given plane.
194///
195/// Returns an error if the `plane` is unsupported or a valid offset can't be generated from
196/// `image_format`.
197pub fn image_format_plane_byte_offset_2(
198    image_format: &fimages2::ImageFormat,
199    plane: u32,
200) -> Result<u32, Error> {
201    // TODO(https://fxbug.dev/576739649): PVRIC is currently handled as a
202    // special case. Generalize.
203    if let Some(modifier) = image_format.pixel_format_modifier {
204        if let Some(tiling) = pvric_tiling_data(modifier) {
205            let pixel_format = *image_format
206                .pixel_format
207                .as_ref()
208                .ok_or_else(|| anyhow!("ImageFormat.pixel_format missing"))?;
209            if !is_pvric_pixel_format_supported(pixel_format) {
210                return Err(anyhow!("Invalid pixel format for PVRIC modifier."));
211            }
212            let bytes_per_row = *image_format
213                .bytes_per_row
214                .as_ref()
215                .ok_or_else(|| anyhow!("ImageFormat.bytes_per_row missing"))?;
216            let size =
217                image_format.size.as_ref().ok_or_else(|| anyhow!("ImageFormat.size missing"))?;
218            let width_in_tiles = bytes_per_row.div_ceil(tiling.bytes_per_row_per_tile);
219            let height_in_tiles = size.height.div_ceil(tiling.tile_rows);
220            let num_tiles = width_in_tiles
221                .checked_mul(height_in_tiles)
222                .ok_or_else(|| anyhow!("Overflow calculating num_tiles."))?;
223            let header_size = num_tiles
224                .checked_next_multiple_of(PVRIC_HEADER_ALIGNMENT)
225                .ok_or_else(|| anyhow!("Overflow calculating header_size."))?;
226            return match plane {
227                PVRIC_TILES_PLANE => Ok(header_size),
228                PVRIC_HEADER_PLANE => Ok(0),
229                _ => Err(anyhow!("Invalid plane.")),
230            };
231        }
232    }
233    match plane {
234        0 => Ok(0),
235        1 => match image_format.pixel_format.as_ref().expect("pixel_format") {
236            fimages2::PixelFormat::Nv12
237            | fimages2::PixelFormat::I420
238            | fimages2::PixelFormat::Yv12 => Ok(image_format.size.as_ref().expect("size").height
239                * image_format.bytes_per_row.as_ref().expect("bytes_per_row")),
240            _ => Err(anyhow!("Invalid pixelformat for plane 1.")),
241        },
242        2 => match image_format.pixel_format.as_ref().expect("pixel_format") {
243            fimages2::PixelFormat::I420 | fimages2::PixelFormat::Yv12 => {
244                let size = image_format.size.as_ref().expect("size");
245                let bytes_per_row = image_format.bytes_per_row.as_ref().expect("bytes_per_row");
246                Ok(size.height * bytes_per_row + size.height / 2 * bytes_per_row / 2)
247            }
248            _ => Err(anyhow!("Invalid pixelformat for plane 2.")),
249        },
250        _ => Err(anyhow!("Invalid plane.")),
251    }
252}
253
254/// Returns the byte offset for the given plane.
255///
256/// Returns an error if the `plane` is unsupported or a valid offset can't be generated from
257/// `image_format`.
258pub fn image_format_plane_byte_offset(
259    image_format: &fsysmem::ImageFormat2,
260    plane: u32,
261) -> Result<u32, Error> {
262    match plane {
263        0 => Ok(0),
264        1 => match image_format.pixel_format.type_ {
265            fsysmem::PixelFormatType::Nv12
266            | fsysmem::PixelFormatType::I420
267            | fsysmem::PixelFormatType::Yv12 => {
268                Ok(image_format.coded_height * image_format.bytes_per_row)
269            }
270            _ => Err(anyhow!("Invalid pixelformat for plane 1.")),
271        },
272        2 => match image_format.pixel_format.type_ {
273            fsysmem::PixelFormatType::I420 | fsysmem::PixelFormatType::Yv12 => {
274                Ok(image_format.coded_height * image_format.bytes_per_row
275                    + image_format.coded_height / 2 * image_format.bytes_per_row / 2)
276            }
277            _ => Err(anyhow!("Invalid pixelformat for plane 2.")),
278        },
279        _ => Err(anyhow!("Invalid plane.")),
280    }
281}
282
283/// Returns the linear size for the given `type_`.
284///
285/// Returns an error if `type_` is unsupported.
286pub fn linear_size(
287    coded_height: u32,
288    bytes_per_row: u32,
289    type_: &fsysmem::PixelFormatType,
290) -> Result<u32, Error> {
291    match type_ {
292        fsysmem::PixelFormatType::R8G8B8A8
293        | fsysmem::PixelFormatType::Bgra32
294        | fsysmem::PixelFormatType::Bgr24
295        | fsysmem::PixelFormatType::Rgb565
296        | fsysmem::PixelFormatType::Rgb332
297        | fsysmem::PixelFormatType::Rgb2220
298        | fsysmem::PixelFormatType::L8
299        | fsysmem::PixelFormatType::R8
300        | fsysmem::PixelFormatType::R8G8
301        | fsysmem::PixelFormatType::A2B10G10R10
302        | fsysmem::PixelFormatType::A2R10G10B10 => Ok(coded_height * bytes_per_row),
303        fsysmem::PixelFormatType::I420 => Ok(coded_height * bytes_per_row * 3 / 2),
304        fsysmem::PixelFormatType::M420 => Ok(coded_height * bytes_per_row * 3 / 2),
305        fsysmem::PixelFormatType::Nv12 => Ok(coded_height * bytes_per_row * 3 / 2),
306        fsysmem::PixelFormatType::Yuy2 => Ok(coded_height * bytes_per_row),
307        fsysmem::PixelFormatType::Yv12 => Ok(coded_height * bytes_per_row * 3 / 2),
308        _ => Err(anyhow!("Invalid pixel format.")),
309    }
310}
311
312/// Converts a `fsysmem::ImageFormatConstraints` to an `fimages2::ImageFormat`.
313pub fn constraints_to_image_format(
314    constraints: &fsysmem2::ImageFormatConstraints,
315    coded_width: u32,
316    coded_height: u32,
317) -> Result<fimages2::ImageFormat, Error> {
318    if let Some(min_size) = &constraints.min_size {
319        if coded_width < min_size.width {
320            return Err(anyhow!("Coded width < min_size.width"));
321        }
322        if coded_height < min_size.height {
323            return Err(anyhow!("Coded height < min_size.height"));
324        }
325    }
326    if let Some(max_size) = &constraints.max_size {
327        if coded_width > max_size.width {
328            return Err(anyhow!("Coded width > max_size.width"));
329        }
330        if coded_height > max_size.height {
331            return Err(anyhow!("Coded height > max_size.height"));
332        }
333    }
334
335    let pixel_format_and_modifier = first_pixel_format_and_modifier_from_constraints(constraints)?;
336
337    let color_space = if constraints.color_spaces.is_some()
338        && constraints.color_spaces.as_ref().unwrap().len() > 0
339    {
340        Some(constraints.color_spaces.as_ref().unwrap()[0])
341    } else {
342        None
343    };
344
345    let format = fimages2::ImageFormat {
346        pixel_format: Some(pixel_format_and_modifier.pixel_format),
347        pixel_format_modifier: Some(pixel_format_and_modifier.pixel_format_modifier),
348        size: Some(SizeU { width: coded_width, height: coded_height }),
349        bytes_per_row: image_format_minimum_row_bytes_2(constraints, coded_width).ok(),
350        color_space,
351        ..Default::default()
352    };
353
354    Ok(format)
355}
356
357/// Converts a `fsysmem::ImageFormatConstraints` to an `fsysmem::ImageFormat2`.
358pub fn constraints_to_format(
359    constraints: &fsysmem::ImageFormatConstraints,
360    coded_width: u32,
361    coded_height: u32,
362) -> Result<fsysmem::ImageFormat2, Error> {
363    if coded_width < constraints.min_coded_width
364        || (constraints.max_coded_width > 0 && coded_width > constraints.max_coded_width)
365    {
366        return Err(anyhow!("Coded width not within constraint bounds."));
367    }
368    if coded_height < constraints.min_coded_height
369        || (constraints.max_coded_height > 0 && coded_height > constraints.max_coded_height)
370    {
371        return Err(anyhow!("Coded height not within constraint bounds."));
372    }
373
374    let format = fsysmem::ImageFormat2 {
375        pixel_format: constraints.pixel_format,
376        coded_width,
377        coded_height,
378        bytes_per_row: image_format_minimum_row_bytes(constraints, coded_width).unwrap_or(0),
379        display_width: coded_width,
380        display_height: coded_height,
381        layers: 0,
382        color_space: if constraints.color_spaces_count > 0 {
383            constraints.color_space[0]
384        } else {
385            fsysmem::ColorSpace { type_: fsysmem::ColorSpaceType::Invalid }
386        },
387        has_pixel_aspect_ratio: false,
388        pixel_aspect_ratio_width: 0,
389        pixel_aspect_ratio_height: 0,
390    };
391
392    Ok(format)
393}
394
395fn first_pixel_format_and_modifier_from_constraints(
396    constraints: &fsysmem2::ImageFormatConstraints,
397) -> Result<fsysmem2::PixelFormatAndModifier, Error> {
398    Ok(if let Some(pixel_format) = constraints.pixel_format {
399        fsysmem2::PixelFormatAndModifier {
400            pixel_format,
401            pixel_format_modifier: *constraints
402                .pixel_format_modifier
403                .as_ref()
404                .unwrap_or(&fimages2::PixelFormatModifier::Linear),
405        }
406    } else {
407        constraints
408            .pixel_format_and_modifiers
409            .as_ref()
410            .ok_or_else(|| format_err!("missing pixel_format"))?
411            .get(0)
412            .ok_or_else(|| format_err!("missing pixel_format"))?
413            .clone()
414    })
415}
416
417/// Returns the minimum row bytes for the given constraints and width.
418///
419/// Returns an error if the width is invalid given the constraints, or the constraint pixel format
420/// modifier is invalid.
421pub fn image_format_minimum_row_bytes_2(
422    constraints: &fsysmem2::ImageFormatConstraints,
423    width: u32,
424) -> Result<u32, Error> {
425    // TODO(https://fxbug.dev/576739649): PVRIC is currently handled as a
426    // special case. Generalize.
427    let pvric_tiling = match constraints.pixel_format_modifier {
428        None | Some(fimages2::PixelFormatModifier::Linear) => None,
429        Some(modifier) => match pvric_tiling_data(modifier) {
430            Some(tiling) => Some(tiling),
431            None => return Err(anyhow!("Non-linear format modifier.")),
432        },
433    };
434    if pvric_tiling.is_some() {
435        let pixel_format =
436            *constraints.pixel_format.as_ref().ok_or_else(|| anyhow!("missing pixel_format"))?;
437        if !is_pvric_pixel_format_supported(pixel_format) {
438            return Err(anyhow!("Invalid pixel format for PVRIC modifier."));
439        }
440    }
441    if let Some(min_size) = constraints.min_size {
442        if width < min_size.width {
443            return Err(anyhow!("width < min_size.width"));
444        }
445    }
446    if let Some(max_size) = constraints.max_size {
447        if width > max_size.width {
448            return Err(anyhow!("width > max_size.width"));
449        }
450    }
451
452    let constraints_min_bytes_per_row = constraints.min_bytes_per_row.unwrap_or(0);
453
454    let maybe_stride_bytes_per_width_pixel = image_format_stride_bytes_per_width_pixel_2(
455        *constraints.pixel_format.as_ref().expect("pixel_format"),
456    )
457    .ok();
458
459    let mut bytes_per_row_divisor = constraints.bytes_per_row_divisor.unwrap_or(1);
460    if *constraints.require_bytes_per_row_at_pixel_boundary.as_ref().unwrap_or(&false) {
461        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"))?;
462        bytes_per_row_divisor =
463            num_integer::lcm(bytes_per_row_divisor, stride_bytes_per_width_pixel);
464    }
465    if let Some(tiling) = pvric_tiling {
466        bytes_per_row_divisor =
467            num_integer::lcm(bytes_per_row_divisor, tiling.bytes_per_row_per_tile);
468    }
469    let bytes_per_row_divisor = bytes_per_row_divisor;
470
471    round_up_to_increment(
472        std::cmp::max(
473            maybe_stride_bytes_per_width_pixel.unwrap_or(0) * width,
474            constraints_min_bytes_per_row,
475        ) as usize,
476        bytes_per_row_divisor as usize,
477    )
478    .map(|bytes| bytes as u32)
479}
480
481/// Returns the minimum row bytes for the given constraints and width.
482///
483/// Returns an error if the width is invalid given the constraints, or the constraint pixel format
484/// modifier is invalid.
485pub fn image_format_minimum_row_bytes(
486    constraints: &fsysmem::ImageFormatConstraints,
487    width: u32,
488) -> Result<u32, Error> {
489    if constraints.pixel_format.format_modifier.value != DRM_FORMAT_MOD_LINEAR {
490        return Err(anyhow!("Non-linear format modifier."));
491    }
492    if width < constraints.min_coded_width
493        || (constraints.max_coded_width > 0 && width > constraints.max_coded_width)
494    {
495        return Err(anyhow!("Width outside of constraints."));
496    }
497
498    let constraints_min_bytes_per_row = constraints.min_bytes_per_row;
499    let constraints_bytes_per_row_divisor = constraints.bytes_per_row_divisor;
500
501    round_up_to_increment(
502        std::cmp::max(
503            image_format_stride_bytes_per_width_pixel(&constraints.pixel_format) * width,
504            constraints_min_bytes_per_row,
505        ) as usize,
506        constraints_bytes_per_row_divisor as usize,
507    )
508    .map(|bytes| bytes as u32)
509}
510
511pub fn image_format_stride_bytes_per_width_pixel_2(
512    pixel_format: fimages2::PixelFormat,
513) -> Result<u32, Error> {
514    match pixel_format {
515        fimages2::PixelFormat::R8G8B8A8 => Ok(4),
516        fimages2::PixelFormat::R8G8B8X8 => Ok(4),
517        fimages2::PixelFormat::B8G8R8A8 => Ok(4),
518        fimages2::PixelFormat::B8G8R8X8 => Ok(4),
519        fimages2::PixelFormat::B8G8R8 => Ok(3),
520        fimages2::PixelFormat::R8G8B8 => Ok(3),
521        fimages2::PixelFormat::I420 => Ok(1),
522        fimages2::PixelFormat::M420 => Ok(1),
523        fimages2::PixelFormat::Nv12 => Ok(1),
524        fimages2::PixelFormat::Yuy2 => Ok(2),
525        fimages2::PixelFormat::Yv12 => Ok(1),
526        fimages2::PixelFormat::R5G6B5 => Ok(2),
527        fimages2::PixelFormat::R3G3B2 => Ok(1),
528        fimages2::PixelFormat::R2G2B2X2 => Ok(1),
529        fimages2::PixelFormat::L8 => Ok(1),
530        fimages2::PixelFormat::R8 => Ok(1),
531        fimages2::PixelFormat::R8G8 => Ok(2),
532        fimages2::PixelFormat::A2B10G10R10 => Ok(4),
533        fimages2::PixelFormat::A2R10G10B10 => Ok(4),
534        fimages2::PixelFormat::P010 => Ok(2),
535        _ => Err(format_err!(
536            "stride_bytes_per_width_pixel not available for pixel_format: {:?}",
537            pixel_format
538        )),
539    }
540}
541
542pub fn image_format_stride_bytes_per_width_pixel(pixel_format: &fsysmem::PixelFormat) -> u32 {
543    match pixel_format.type_ {
544        fsysmem::PixelFormatType::Invalid
545        | fsysmem::PixelFormatType::Mjpeg
546        | fsysmem::PixelFormatType::DoNotCare => 0,
547        fsysmem::PixelFormatType::R8G8B8A8 => 4,
548        fsysmem::PixelFormatType::Bgra32 => 4,
549        fsysmem::PixelFormatType::Bgr24 => 3,
550        fsysmem::PixelFormatType::I420 => 1,
551        fsysmem::PixelFormatType::M420 => 1,
552        fsysmem::PixelFormatType::Nv12 => 1,
553        fsysmem::PixelFormatType::Yuy2 => 2,
554        fsysmem::PixelFormatType::Yv12 => 1,
555        fsysmem::PixelFormatType::Rgb565 => 2,
556        fsysmem::PixelFormatType::Rgb332 => 1,
557        fsysmem::PixelFormatType::Rgb2220 => 1,
558        fsysmem::PixelFormatType::L8 => 1,
559        fsysmem::PixelFormatType::R8 => 1,
560        fsysmem::PixelFormatType::R8G8 => 2,
561        fsysmem::PixelFormatType::A2B10G10R10 => 4,
562        fsysmem::PixelFormatType::A2R10G10B10 => 4,
563    }
564}
565
566pub fn sysmem1_pixel_format_type_from_images2_pixel_format(
567    pixel_format: fimages2::PixelFormat,
568) -> Result<fsysmem::PixelFormatType, Error> {
569    fsysmem::PixelFormatType::from_primitive(pixel_format.into_primitive())
570        .ok_or_else(|| format_err!("pixel_format not convertible to sysmem1: {:?}", pixel_format))
571}
572
573pub fn images2_pixel_format_from_sysmem_pixel_format_type(
574    pixel_format: fsysmem::PixelFormatType,
575) -> Result<fimages2::PixelFormat, Error> {
576    fimages2::PixelFormat::from_primitive(pixel_format.into_primitive())
577        .ok_or_else(|| format_err!("pixel_format not convertible to images2: {:?}", pixel_format))
578}
579
580pub fn sysmem1_pixel_format_modifier_from_images2_pixel_format_modifier(
581    pixel_format_modifier: fimages2::PixelFormatModifier,
582) -> u64 {
583    if pixel_format_modifier == fimages2::PixelFormatModifier::GoogleGoldfishOptimal {
584        return fsysmem::FORMAT_MODIFIER_GOOGLE_GOLDFISH_OPTIMAL;
585    }
586    pixel_format_modifier.into_primitive()
587}
588
589pub fn images2_pixel_format_modifier_from_sysmem_pixel_format_modifier(
590    pixel_format_modifier: u64,
591) -> Result<fimages2::PixelFormatModifier, Error> {
592    if pixel_format_modifier == fsysmem::FORMAT_MODIFIER_GOOGLE_GOLDFISH_OPTIMAL {
593        return Ok(fimages2::PixelFormatModifier::GoogleGoldfishOptimal);
594    }
595    fimages2::PixelFormatModifier::from_primitive(pixel_format_modifier).ok_or_else(|| {
596        format_err!("pixel_format_modifier not convertible to images2: {:?}", pixel_format_modifier)
597    })
598}
599
600pub fn sysmem1_color_space_type_from_images2_color_space(
601    color_space: fimages2::ColorSpace,
602) -> Result<fsysmem::ColorSpaceType, Error> {
603    fsysmem::ColorSpaceType::from_primitive(color_space.into_primitive())
604        .ok_or_else(|| format_err!("color_space not convertible to sysmem1: {:?}", color_space))
605}
606
607pub fn images2_color_space_from_sysmem_color_space_type(
608    color_space: fsysmem::ColorSpaceType,
609) -> fimages2::ColorSpace {
610    // This can't fail because sysmem(1) ColorSpaceType isn't flexible and all primitive values also
611    // exist in images2::ColorSpace.
612    fimages2::ColorSpace::from_primitive(color_space.into_primitive()).unwrap()
613}
614
615pub fn sysmem1_image_format_from_images2_image_format(
616    image_format: &fimages2::ImageFormat,
617) -> Result<fsysmem::ImageFormat2, Error> {
618    let coded_size = image_format.size.as_ref().ok_or_else(|| format_err!("missing size"))?;
619    let format_modifier = sysmem1_pixel_format_modifier_from_images2_pixel_format_modifier(
620        *image_format
621            .pixel_format_modifier
622            .as_ref()
623            .unwrap_or(&fimages2::PixelFormatModifier::Linear),
624    );
625    // bytes_per_row 0 is only to be used by tiled formats; not intended to be optional for
626    // PixelFormatModifier::Linear
627    let bytes_per_row = match format_modifier {
628        fsysmem::FORMAT_MODIFIER_LINEAR => {
629            // bytes_per_row required
630            *image_format
631                .bytes_per_row
632                .as_ref()
633                .ok_or_else(|| format_err!("missing bytes_per_row (required when Linear)"))?
634        }
635        _ => {
636            // bytes_per_row can be un-set for tiled formats (un-set is preferred over 0 in images2, but we also allow 0 here)
637            *image_format.bytes_per_row.as_ref().unwrap_or(&0)
638        }
639    };
640    let display_rect = image_format.display_rect.as_ref();
641    let color_space_type = sysmem1_color_space_type_from_images2_color_space(
642        *image_format.color_space.as_ref().ok_or_else(|| format_err!("missing color_space"))?,
643    )?;
644    let pixel_aspect_ratio = image_format.pixel_aspect_ratio.as_ref();
645    Ok(fsysmem::ImageFormat2 {
646        pixel_format: fsysmem::PixelFormat {
647            type_: sysmem1_pixel_format_type_from_images2_pixel_format(
648                *image_format
649                    .pixel_format
650                    .as_ref()
651                    .ok_or_else(|| format_err!("missing pixel_format"))?,
652            )?,
653            // Semantically it should be safe to always set has_format_modifier true, but we
654            // preserve the "has" aspect here just in case any client code / test code is expecting
655            // to see false.
656            has_format_modifier: image_format.pixel_format_modifier.is_some(),
657            format_modifier: fsysmem::FormatModifier { value: format_modifier },
658        },
659        coded_width: coded_size.width,
660        coded_height: coded_size.height,
661        bytes_per_row,
662        display_width: display_rect.map(|rect| rect.width).unwrap_or(0),
663        display_height: display_rect.map(|rect| rect.height).unwrap_or(0),
664        layers: 1,
665        color_space: fsysmem::ColorSpace { type_: color_space_type },
666        has_pixel_aspect_ratio: pixel_aspect_ratio.is_some(),
667        pixel_aspect_ratio_width: pixel_aspect_ratio.map(|size| size.width).unwrap_or(0),
668        pixel_aspect_ratio_height: pixel_aspect_ratio.map(|size| size.height).unwrap_or(0),
669    })
670}
671
672pub fn images2_image_format_from_sysmem_image_format(
673    image_format: &fsysmem::ImageFormat2,
674) -> Result<fimages2::ImageFormat, Error> {
675    let pixel_format_modifier = if image_format.pixel_format.has_format_modifier {
676        Some(images2_pixel_format_modifier_from_sysmem_pixel_format_modifier(
677            image_format.pixel_format.format_modifier.value,
678        )?)
679    } else {
680        None
681    };
682    let display_rect = if image_format.display_width != 0 || image_format.display_height != 0 {
683        let display_width = if image_format.display_width != 0 {
684            image_format.display_width
685        } else {
686            image_format.coded_width
687        };
688        let display_height = if image_format.display_height != 0 {
689            image_format.display_height
690        } else {
691            image_format.coded_height
692        };
693        Some(RectU { x: 0, y: 0, width: display_width, height: display_height })
694    } else {
695        None
696    };
697    let pixel_aspect_ratio = if image_format.has_pixel_aspect_ratio {
698        Some(SizeU {
699            width: image_format.pixel_aspect_ratio_width,
700            height: image_format.pixel_aspect_ratio_height,
701        })
702    } else {
703        None
704    };
705    Ok(fimages2::ImageFormat {
706        pixel_format: Some(images2_pixel_format_from_sysmem_pixel_format_type(
707            image_format.pixel_format.type_,
708        )?),
709        pixel_format_modifier,
710        color_space: Some(images2_color_space_from_sysmem_color_space_type(
711            image_format.color_space.type_,
712        )),
713        size: Some(SizeU { width: image_format.coded_width, height: image_format.coded_height }),
714        bytes_per_row: Some(image_format.bytes_per_row),
715        display_rect,
716        pixel_aspect_ratio,
717        ..Default::default()
718    })
719}
720
721#[cfg(test)]
722mod tests {
723    use super::*;
724    use fidl_fuchsia_math as fmath;
725
726    #[test]
727    fn test_linear_row_bytes_2() {
728        let constraints = fsysmem2::ImageFormatConstraints {
729            pixel_format: Some(fimages2::PixelFormat::B8G8R8A8),
730            pixel_format_modifier: Some(fimages2::PixelFormatModifier::Linear),
731            min_size: Some(SizeU { width: 12, height: 12 }),
732            max_size: Some(SizeU { width: 100, height: 100 }),
733            bytes_per_row_divisor: Some(4 * 8),
734            max_bytes_per_row: Some(100000),
735            ..Default::default()
736        };
737
738        assert_eq!(image_format_minimum_row_bytes_2(&constraints, 17).unwrap(), 4 * 24);
739
740        assert!(image_format_minimum_row_bytes_2(&constraints, 11).is_err());
741        assert!(image_format_minimum_row_bytes_2(&constraints, 101).is_err());
742    }
743
744    #[test]
745    fn test_linear_row_bytes() {
746        let linear = fsysmem::PixelFormat {
747            type_: fsysmem::PixelFormatType::Bgra32,
748            has_format_modifier: true,
749            format_modifier: fsysmem::FormatModifier { value: fsysmem::FORMAT_MODIFIER_LINEAR },
750        };
751
752        let constraints = fsysmem::ImageFormatConstraints {
753            pixel_format: linear,
754            min_coded_width: 12,
755            max_coded_width: 100,
756            bytes_per_row_divisor: 4 * 8,
757            max_bytes_per_row: 100000,
758            ..IMAGE_FORMAT_CONSTRAINTS_DEFAULT
759        };
760
761        assert_eq!(image_format_minimum_row_bytes(&constraints, 17).unwrap(), 4 * 24);
762
763        assert!(image_format_minimum_row_bytes(&constraints, 11).is_err());
764        assert!(image_format_minimum_row_bytes(&constraints, 101).is_err());
765    }
766
767    #[test]
768    fn plane_byte_offset_2() {
769        let constraints = fsysmem2::ImageFormatConstraints {
770            pixel_format: Some(fimages2::PixelFormat::B8G8R8A8),
771            pixel_format_modifier: Some(fimages2::PixelFormatModifier::Linear),
772            min_size: Some(SizeU { width: 12, height: 12 }),
773            max_size: Some(SizeU { width: 100, height: 100 }),
774            bytes_per_row_divisor: Some(4 * 8),
775            max_bytes_per_row: Some(100000),
776            ..Default::default()
777        };
778
779        let image_format = constraints_to_image_format(&constraints, 18, 17).unwrap();
780        // The raw size would be 72 without bytes_per_row_divisor of 32.
781        assert_eq!(*image_format.bytes_per_row.as_ref().unwrap(), 96);
782
783        assert_eq!(image_format_plane_byte_offset_2(&image_format, 0).unwrap(), 0);
784        assert!(image_format_plane_byte_offset_2(&image_format, 1).is_err());
785
786        let constraints = fsysmem2::ImageFormatConstraints {
787            pixel_format: Some(fimages2::PixelFormat::I420),
788            pixel_format_modifier: Some(fimages2::PixelFormatModifier::Linear),
789            min_size: Some(SizeU { width: 12, height: 12 }),
790            max_size: Some(SizeU { width: 100, height: 100 }),
791            bytes_per_row_divisor: Some(4 * 8),
792            max_bytes_per_row: Some(100000),
793            ..Default::default()
794        };
795
796        const BYTES_PER_ROW: u32 = 32;
797        let image_format = constraints_to_image_format(&constraints, 18, 20).unwrap();
798        assert_eq!(*image_format.bytes_per_row.as_ref().unwrap(), BYTES_PER_ROW);
799
800        assert_eq!(image_format_plane_byte_offset_2(&image_format, 0).unwrap(), 0);
801        assert_eq!(image_format_plane_byte_offset_2(&image_format, 1).unwrap(), BYTES_PER_ROW * 20);
802        assert_eq!(
803            image_format_plane_byte_offset_2(&image_format, 2).unwrap(),
804            BYTES_PER_ROW * 20 + BYTES_PER_ROW / 2 * 20 / 2
805        );
806        assert!(image_format_plane_byte_offset_2(&image_format, 3).is_err());
807
808        assert_eq!(get_plane_row_bytes_2(&image_format, 0).unwrap(), BYTES_PER_ROW);
809        assert_eq!(get_plane_row_bytes_2(&image_format, 1).unwrap(), BYTES_PER_ROW / 2);
810        assert_eq!(get_plane_row_bytes_2(&image_format, 2).unwrap(), BYTES_PER_ROW / 2);
811        assert!(get_plane_row_bytes_2(&image_format, 3).is_err())
812    }
813
814    #[test]
815    fn plane_byte_offset() {
816        let constraints = fsysmem2::ImageFormatConstraints {
817            pixel_format: Some(fimages2::PixelFormat::B8G8R8A8),
818            pixel_format_modifier: Some(fimages2::PixelFormatModifier::Linear),
819            min_size: Some(SizeU { width: 12, height: 12 }),
820            max_size: Some(SizeU { width: 100, height: 100 }),
821            bytes_per_row_divisor: Some(4 * 8),
822            max_bytes_per_row: Some(100000),
823            ..Default::default()
824        };
825
826        let image_format = constraints_to_image_format(&constraints, 18, 17).unwrap();
827        // The raw size would be 72 without bytes_per_row_divisor of 32.
828        assert_eq!(*image_format.bytes_per_row.as_ref().unwrap(), 96);
829
830        assert_eq!(image_format_plane_byte_offset_2(&image_format, 0).unwrap(), 0);
831        assert!(image_format_plane_byte_offset_2(&image_format, 1).is_err());
832
833        let constraints = fsysmem2::ImageFormatConstraints {
834            pixel_format: Some(fimages2::PixelFormat::I420),
835            pixel_format_modifier: Some(fimages2::PixelFormatModifier::Linear),
836            min_size: Some(SizeU { width: 12, height: 12 }),
837            max_size: Some(SizeU { width: 100, height: 100 }),
838            bytes_per_row_divisor: Some(4 * 8),
839            max_bytes_per_row: Some(100000),
840            ..Default::default()
841        };
842
843        const BYTES_PER_ROW: u32 = 32;
844        let image_format = constraints_to_image_format(&constraints, 18, 20).unwrap();
845        assert_eq!(*image_format.bytes_per_row.as_ref().unwrap(), BYTES_PER_ROW);
846
847        assert_eq!(image_format_plane_byte_offset_2(&image_format, 0).unwrap(), 0);
848        assert_eq!(image_format_plane_byte_offset_2(&image_format, 1).unwrap(), BYTES_PER_ROW * 20);
849        assert_eq!(
850            image_format_plane_byte_offset_2(&image_format, 2).unwrap(),
851            BYTES_PER_ROW * 20 + BYTES_PER_ROW / 2 * 20 / 2
852        );
853        assert!(image_format_plane_byte_offset_2(&image_format, 3).is_err());
854
855        assert_eq!(get_plane_row_bytes_2(&image_format, 0).unwrap(), BYTES_PER_ROW);
856        assert_eq!(get_plane_row_bytes_2(&image_format, 1).unwrap(), BYTES_PER_ROW / 2);
857        assert_eq!(get_plane_row_bytes_2(&image_format, 2).unwrap(), BYTES_PER_ROW / 2);
858        assert!(get_plane_row_bytes_2(&image_format, 3).is_err())
859    }
860
861    #[test]
862    fn test_constraints_to_image_format() {
863        // The fields set to 37 are intentionally checking that those fields are ignored by the
864        // conversion, at least for now.
865        let constraints = fsysmem2::ImageFormatConstraints {
866            pixel_format: Some(fimages2::PixelFormat::R8G8B8),
867            pixel_format_modifier: Some(fimages2::PixelFormatModifier::Linear),
868            min_size: Some(SizeU { width: 12, height: 12 }),
869            max_size: Some(SizeU { width: 100, height: 100 }),
870            bytes_per_row_divisor: Some(4 * 8),
871            max_bytes_per_row: Some(100000),
872            color_spaces: Some(vec![fimages2::ColorSpace::Rec709]),
873            min_bytes_per_row: Some(24),
874            max_width_times_height: Some(12 * 12),
875            size_alignment: Some(fmath::SizeU { width: 37, height: 1 }),
876            display_rect_alignment: Some(fmath::SizeU { width: 37, height: 37 }),
877            required_min_size: Some(fmath::SizeU { width: 37, height: 37 }),
878            required_max_size: Some(fmath::SizeU { width: 37, height: 37 }),
879            start_offset_divisor: Some(37),
880            pixel_format_and_modifiers: Some(vec![fsysmem2::PixelFormatAndModifier {
881                pixel_format: fimages2::PixelFormat::Yv12,
882                pixel_format_modifier: fimages2::PixelFormatModifier::GoogleGoldfishOptimal,
883            }]),
884            require_bytes_per_row_at_pixel_boundary: Some(true),
885            ..Default::default()
886        };
887
888        let image_format = constraints_to_image_format(&constraints, 18, 17).unwrap();
889
890        assert_eq!(image_format.pixel_format, Some(fimages2::PixelFormat::R8G8B8));
891        assert_eq!(image_format.pixel_format_modifier, Some(fimages2::PixelFormatModifier::Linear));
892        assert_eq!(image_format.color_space, Some(fimages2::ColorSpace::Rec709));
893        assert_eq!(image_format.size, Some(fmath::SizeU { width: 18, height: 17 }));
894        assert_eq!(image_format.bytes_per_row, Some(96));
895
896        // We intentionally want these to be None when we don't have any explicit info for them.
897        assert_eq!(image_format.display_rect, None);
898        assert_eq!(image_format.valid_size, None);
899        assert_eq!(image_format.pixel_aspect_ratio, None);
900    }
901
902    #[test]
903    fn test_image_format_stride_bytes_per_width_pixel_2() {
904        assert_eq!(
905            4,
906            image_format_stride_bytes_per_width_pixel_2(fimages2::PixelFormat::R8G8B8A8).unwrap()
907        );
908        assert!(
909            image_format_stride_bytes_per_width_pixel_2(fimages2::PixelFormat::DoNotCare).is_err()
910        );
911    }
912
913    #[test]
914    fn test_sysmem1_pixel_format_type_from_images2_pixel_format() {
915        assert_eq!(
916            fsysmem::PixelFormatType::Bgra32,
917            sysmem1_pixel_format_type_from_images2_pixel_format(fimages2::PixelFormat::B8G8R8A8)
918                .unwrap()
919        );
920        assert!(
921            sysmem1_pixel_format_type_from_images2_pixel_format(
922                fimages2::PixelFormat::from_primitive_allow_unknown(1189673091)
923            )
924            .is_err()
925        );
926    }
927
928    #[test]
929    fn test_sysmem1_pixel_format_modifier_from_images2_pixel_format_modifier() {
930        assert_eq!(
931            fsysmem::FORMAT_MODIFIER_ARM_AFBC_16_X16_SPLIT_BLOCK_SPARSE_YUV_TE_TILED_HEADER,
932            sysmem1_pixel_format_modifier_from_images2_pixel_format_modifier(
933                fimages2::PixelFormatModifier::ArmAfbc16X16SplitBlockSparseYuvTeTiledHeader
934            )
935        );
936        assert_eq!(
937            fsysmem::FORMAT_MODIFIER_GOOGLE_GOLDFISH_OPTIMAL,
938            sysmem1_pixel_format_modifier_from_images2_pixel_format_modifier(
939                fimages2::PixelFormatModifier::GoogleGoldfishOptimal
940            )
941        );
942        assert_eq!(
943            fsysmem::FORMAT_MODIFIER_GOOGLE_GOLDFISH_OPTIMAL,
944            sysmem1_pixel_format_modifier_from_images2_pixel_format_modifier(
945                fimages2::PixelFormatModifier::from_primitive_allow_unknown(
946                    fsysmem::FORMAT_MODIFIER_GOOGLE_GOLDFISH_OPTIMAL
947                )
948            )
949        );
950        // We intentionally allow too-new values to convert.
951        assert_eq!(
952            1189673091,
953            sysmem1_pixel_format_modifier_from_images2_pixel_format_modifier(
954                fimages2::PixelFormatModifier::from_primitive_allow_unknown(1189673091)
955            )
956        );
957    }
958
959    #[test]
960    fn test_images2_pixel_format_from_sysmem_pixel_format_type() {
961        assert_eq!(
962            fimages2::PixelFormat::B8G8R8A8,
963            images2_pixel_format_from_sysmem_pixel_format_type(fsysmem::PixelFormatType::Bgra32)
964                .unwrap()
965        );
966        assert_eq!(
967            fimages2::PixelFormat::Yv12,
968            images2_pixel_format_from_sysmem_pixel_format_type(fsysmem::PixelFormatType::Yv12)
969                .unwrap()
970        );
971    }
972
973    #[test]
974    fn test_images2_pixel_format_modifier_from_sysmem_pixel_format_modifier() {
975        assert_eq!(
976            fimages2::PixelFormatModifier::ArmAfbc16X16SplitBlockSparseYuvTeTiledHeader,
977            images2_pixel_format_modifier_from_sysmem_pixel_format_modifier(
978                fsysmem::FORMAT_MODIFIER_ARM_AFBC_16_X16_SPLIT_BLOCK_SPARSE_YUV_TE_TILED_HEADER
979            )
980            .unwrap()
981        );
982        assert_eq!(
983            fimages2::PixelFormatModifier::GoogleGoldfishOptimal,
984            images2_pixel_format_modifier_from_sysmem_pixel_format_modifier(
985                fsysmem::FORMAT_MODIFIER_GOOGLE_GOLDFISH_OPTIMAL
986            )
987            .unwrap()
988        );
989        assert_eq!(
990            fimages2::PixelFormatModifier::GoogleGoldfishOptimal,
991            images2_pixel_format_modifier_from_sysmem_pixel_format_modifier(
992                fimages2::PixelFormatModifier::GoogleGoldfishOptimal.into_primitive()
993            )
994            .unwrap()
995        );
996        assert!(
997            images2_pixel_format_modifier_from_sysmem_pixel_format_modifier(1189673091).is_err()
998        );
999    }
1000
1001    #[test]
1002    fn test_sysmem1_color_space_type_from_images2_color_space() {
1003        assert_eq!(
1004            fsysmem::ColorSpaceType::Rec709,
1005            sysmem1_color_space_type_from_images2_color_space(fimages2::ColorSpace::Rec709)
1006                .unwrap()
1007        );
1008        assert!(
1009            sysmem1_color_space_type_from_images2_color_space(
1010                fimages2::ColorSpace::from_primitive_allow_unknown(1189673091)
1011            )
1012            .is_err()
1013        );
1014    }
1015
1016    #[test]
1017    fn test_images2_color_space_from_sysmem_color_space_type() {
1018        assert_eq!(
1019            fimages2::ColorSpace::Rec709,
1020            images2_color_space_from_sysmem_color_space_type(fsysmem::ColorSpaceType::Rec709)
1021        );
1022        assert_eq!(
1023            fimages2::ColorSpace::Srgb,
1024            images2_color_space_from_sysmem_color_space_type(fsysmem::ColorSpaceType::Srgb)
1025        );
1026    }
1027
1028    #[test]
1029    fn test_sysmem1_image_format_from_images2_image_format() {
1030        {
1031            let images2_format = fimages2::ImageFormat {
1032                pixel_format: Some(fimages2::PixelFormat::Yv12),
1033                pixel_format_modifier: None,
1034                color_space: Some(fimages2::ColorSpace::Rec709),
1035                size: Some(fmath::SizeU { width: 17, height: 13 }),
1036                bytes_per_row: Some(32),
1037                display_rect: Some(fmath::RectU { x: 0, y: 0, width: 15, height: 11 }),
1038                valid_size: Some(fmath::SizeU { width: 12, height: 10 }),
1039                pixel_aspect_ratio: Some(fmath::SizeU { width: 2, height: 3 }),
1040                ..Default::default()
1041            };
1042
1043            let sysmem_format =
1044                sysmem1_image_format_from_images2_image_format(&images2_format).unwrap();
1045
1046            assert_eq!(fsysmem::PixelFormatType::Yv12, sysmem_format.pixel_format.type_);
1047            assert_eq!(false, sysmem_format.pixel_format.has_format_modifier);
1048            assert_eq!(17, sysmem_format.coded_width);
1049            assert_eq!(13, sysmem_format.coded_height);
1050            assert_eq!(32, sysmem_format.bytes_per_row);
1051            assert_eq!(15, sysmem_format.display_width);
1052            assert_eq!(11, sysmem_format.display_height);
1053            assert_eq!(1, sysmem_format.layers);
1054            assert_eq!(fsysmem::ColorSpaceType::Rec709, sysmem_format.color_space.type_);
1055            assert_eq!(true, sysmem_format.has_pixel_aspect_ratio);
1056            assert_eq!(2, sysmem_format.pixel_aspect_ratio_width);
1057            assert_eq!(3, sysmem_format.pixel_aspect_ratio_height);
1058        }
1059
1060        {
1061            let images2_format = fimages2::ImageFormat {
1062                pixel_format: Some(fimages2::PixelFormat::Yv12),
1063                pixel_format_modifier: Some(
1064                    fimages2::PixelFormatModifier::ArmAfbc16X16SplitBlockSparseYuvTeTiledHeader,
1065                ),
1066                color_space: Some(fimages2::ColorSpace::Rec709),
1067                size: Some(fmath::SizeU { width: 17, height: 13 }),
1068                bytes_per_row: Some(32),
1069                display_rect: Some(fmath::RectU { x: 0, y: 0, width: 15, height: 11 }),
1070                valid_size: Some(fmath::SizeU { width: 12, height: 10 }),
1071                pixel_aspect_ratio: Some(fmath::SizeU { width: 2, height: 3 }),
1072                ..Default::default()
1073            };
1074
1075            let sysmem_format =
1076                sysmem1_image_format_from_images2_image_format(&images2_format).unwrap();
1077
1078            assert_eq!(fsysmem::PixelFormatType::Yv12, sysmem_format.pixel_format.type_);
1079            assert_eq!(true, sysmem_format.pixel_format.has_format_modifier);
1080            assert_eq!(
1081                fsysmem::FORMAT_MODIFIER_ARM_AFBC_16_X16_SPLIT_BLOCK_SPARSE_YUV_TE_TILED_HEADER,
1082                sysmem_format.pixel_format.format_modifier.value
1083            );
1084            assert_eq!(17, sysmem_format.coded_width);
1085            assert_eq!(13, sysmem_format.coded_height);
1086            assert_eq!(32, sysmem_format.bytes_per_row);
1087            assert_eq!(15, sysmem_format.display_width);
1088            assert_eq!(11, sysmem_format.display_height);
1089            assert_eq!(1, sysmem_format.layers);
1090            assert_eq!(fsysmem::ColorSpaceType::Rec709, sysmem_format.color_space.type_);
1091            assert_eq!(true, sysmem_format.has_pixel_aspect_ratio);
1092            assert_eq!(2, sysmem_format.pixel_aspect_ratio_width);
1093            assert_eq!(3, sysmem_format.pixel_aspect_ratio_height);
1094        }
1095    }
1096
1097    #[test]
1098    fn test_images2_image_format_from_sysmem_image_format() {
1099        let sysmem_format = fsysmem::ImageFormat2 {
1100            pixel_format: fsysmem::PixelFormat {
1101                type_: fsysmem::PixelFormatType::Yv12,
1102                has_format_modifier: true,
1103                format_modifier: fsysmem::FormatModifier {
1104                    value: fsysmem::FORMAT_MODIFIER_GOOGLE_GOLDFISH_OPTIMAL,
1105                },
1106            },
1107            coded_width: 17,
1108            coded_height: 13,
1109            bytes_per_row: 32,
1110            display_width: 15,
1111            display_height: 11,
1112            layers: 1,
1113            color_space: fsysmem::ColorSpace { type_: fsysmem::ColorSpaceType::Rec709 },
1114            has_pixel_aspect_ratio: true,
1115            pixel_aspect_ratio_width: 2,
1116            pixel_aspect_ratio_height: 3,
1117        };
1118
1119        let images2_format = images2_image_format_from_sysmem_image_format(&sysmem_format).unwrap();
1120
1121        assert_eq!(Some(fimages2::PixelFormat::Yv12), images2_format.pixel_format);
1122        assert_eq!(
1123            Some(fimages2::PixelFormatModifier::GoogleGoldfishOptimal),
1124            images2_format.pixel_format_modifier
1125        );
1126        assert_eq!(Some(fimages2::ColorSpace::Rec709), images2_format.color_space);
1127        assert_eq!(Some(fmath::SizeU { width: 17, height: 13 }), images2_format.size);
1128        assert_eq!(Some(32), images2_format.bytes_per_row);
1129        assert_eq!(
1130            Some(fmath::RectU { x: 0, y: 0, width: 15, height: 11 }),
1131            images2_format.display_rect
1132        );
1133        assert_eq!(None, images2_format.valid_size);
1134        assert_eq!(Some(fmath::SizeU { width: 2, height: 3 }), images2_format.pixel_aspect_ratio);
1135    }
1136
1137    #[test]
1138    fn test_pvric_tiling_data() {
1139        assert_eq!(
1140            pvric_tiling_data(fimages2::PixelFormatModifier::PvrFbcdc8X8V14),
1141            Some(PvricTilingData { tile_rows: 8, bytes_per_row_per_tile: 32 })
1142        );
1143        assert_eq!(
1144            pvric_tiling_data(fimages2::PixelFormatModifier::PvrFbcdc16X4V14),
1145            Some(PvricTilingData { tile_rows: 4, bytes_per_row_per_tile: 64 })
1146        );
1147        assert_eq!(pvric_tiling_data(fimages2::PixelFormatModifier::Linear), None);
1148    }
1149
1150    #[test]
1151    fn test_pvric_16x4_row_bytes_and_planes() {
1152        let supported_32bpp_formats = [
1153            fimages2::PixelFormat::R8G8B8A8,
1154            fimages2::PixelFormat::R8G8B8X8,
1155            fimages2::PixelFormat::B8G8R8A8,
1156            fimages2::PixelFormat::B8G8R8X8,
1157            fimages2::PixelFormat::A2R10G10B10,
1158            fimages2::PixelFormat::A2B10G10R10,
1159        ];
1160
1161        for format in supported_32bpp_formats {
1162            let constraints = fsysmem2::ImageFormatConstraints {
1163                pixel_format: Some(format),
1164                pixel_format_modifier: Some(fimages2::PixelFormatModifier::PvrFbcdc16X4V14),
1165                min_size: Some(SizeU { width: 12, height: 12 }),
1166                max_size: Some(SizeU { width: 100, height: 100 }),
1167                bytes_per_row_divisor: Some(32),
1168                max_bytes_per_row: Some(100000),
1169                ..Default::default()
1170            };
1171
1172            // For 18x17 with divisor 32: lcm(32, 64) = 64. 18 * 4 = 72 -> rounds up to 128 bytes/row.
1173            // width_in_tiles = 128 / 64 = 2.
1174            // height_in_tiles = ceil(17 / 4) = 5.
1175            // num_tiles = 2 * 5 = 10.
1176            // header_size = RoundUp(10, 256) = 256.
1177            assert_eq!(image_format_minimum_row_bytes_2(&constraints, 18).unwrap(), 128);
1178            assert!(image_format_minimum_row_bytes_2(&constraints, 11).is_err());
1179            assert!(image_format_minimum_row_bytes_2(&constraints, 101).is_err());
1180
1181            let image_format = constraints_to_image_format(&constraints, 18, 17).unwrap();
1182            assert_eq!(image_format.bytes_per_row, Some(128));
1183
1184            assert_eq!(image_format_plane_byte_offset_2(&image_format, 0).unwrap(), 256);
1185            assert!(image_format_plane_byte_offset_2(&image_format, 1).is_err());
1186            assert!(image_format_plane_byte_offset_2(&image_format, 2).is_err());
1187            assert_eq!(image_format_plane_byte_offset_2(&image_format, 3).unwrap(), 0);
1188            assert!(image_format_plane_byte_offset_2(&image_format, 4).is_err());
1189
1190            assert_eq!(get_plane_row_bytes_2(&image_format, 0).unwrap(), 128);
1191            assert!(get_plane_row_bytes_2(&image_format, 1).is_err());
1192            assert!(get_plane_row_bytes_2(&image_format, 2).is_err());
1193            assert_eq!(get_plane_row_bytes_2(&image_format, 3).unwrap(), 2);
1194            assert!(get_plane_row_bytes_2(&image_format, 4).is_err());
1195        }
1196
1197        // Large resolution: 1280x2856 with 32-bpp B8G8R8A8 and PvrFbcdc16X4V14.
1198        // bytes_per_row = 1280 * 4 = 5120.
1199        // width_in_tiles = 5120 / 64 = 80, height_in_tiles = 2856 / 4 = 714.
1200        // num_tiles = 80 * 714 = 57,120.
1201        // header_size = RoundUp(57,120, 256) = 57,344.
1202        let large_constraints = fsysmem2::ImageFormatConstraints {
1203            pixel_format: Some(fimages2::PixelFormat::B8G8R8A8),
1204            pixel_format_modifier: Some(fimages2::PixelFormatModifier::PvrFbcdc16X4V14),
1205            min_size: Some(SizeU { width: 1, height: 1 }),
1206            max_size: Some(SizeU { width: 4096, height: 4096 }),
1207            bytes_per_row_divisor: Some(64),
1208            ..Default::default()
1209        };
1210        assert_eq!(image_format_minimum_row_bytes_2(&large_constraints, 1280).unwrap(), 5120);
1211        let large_format = constraints_to_image_format(&large_constraints, 1280, 2856).unwrap();
1212        assert_eq!(large_format.bytes_per_row, Some(5120));
1213        assert_eq!(image_format_plane_byte_offset_2(&large_format, 0).unwrap(), 57_344);
1214        assert_eq!(image_format_plane_byte_offset_2(&large_format, 3).unwrap(), 0);
1215        assert_eq!(get_plane_row_bytes_2(&large_format, 0).unwrap(), 5120);
1216        assert_eq!(get_plane_row_bytes_2(&large_format, 3).unwrap(), 80);
1217
1218        // 16-bpp R5G6B5 with PvrFbcdc16X4V14 (bytes_per_row_per_tile = 64, tile_rows = 4).
1219        // For width = 18 (36 bytes): rounds up to 64 bytes/row (1 tile wide).
1220        // height = 17: ceil(17 / 4) = 5 tiles high.
1221        // num_tiles = 1 * 5 = 5, header_size = 256.
1222        let rgb565_constraints = fsysmem2::ImageFormatConstraints {
1223            pixel_format: Some(fimages2::PixelFormat::R5G6B5),
1224            pixel_format_modifier: Some(fimages2::PixelFormatModifier::PvrFbcdc16X4V14),
1225            min_size: Some(SizeU { width: 1, height: 1 }),
1226            max_size: Some(SizeU { width: 4096, height: 4096 }),
1227            ..Default::default()
1228        };
1229        let rgb565_format = constraints_to_image_format(&rgb565_constraints, 18, 17).unwrap();
1230        assert_eq!(rgb565_format.bytes_per_row, Some(64));
1231        assert_eq!(image_format_plane_byte_offset_2(&rgb565_format, 0).unwrap(), 256);
1232        assert_eq!(image_format_plane_byte_offset_2(&rgb565_format, 3).unwrap(), 0);
1233        assert_eq!(get_plane_row_bytes_2(&rgb565_format, 0).unwrap(), 64);
1234        assert_eq!(get_plane_row_bytes_2(&rgb565_format, 3).unwrap(), 1);
1235
1236        // Unsupported formats (Nv12, B8G8R8) must return Err.
1237        for unsupported in [fimages2::PixelFormat::Nv12, fimages2::PixelFormat::B8G8R8] {
1238            let bad_constraints = fsysmem2::ImageFormatConstraints {
1239                pixel_format: Some(unsupported),
1240                pixel_format_modifier: Some(fimages2::PixelFormatModifier::PvrFbcdc16X4V14),
1241                ..Default::default()
1242            };
1243            assert!(image_format_minimum_row_bytes_2(&bad_constraints, 64).is_err());
1244        }
1245    }
1246
1247    #[test]
1248    fn test_pvric_8x8_row_bytes_and_planes() {
1249        // Test 8x8 V14 with 32-bpp at 1920x1080 (bytes_per_row_per_tile = 32, tile_rows = 8).
1250        // bytes_per_row = 1920 * 4 = 7680.
1251        // width_in_tiles = 7680 / 32 = 240.
1252        // height_in_tiles = 1080 / 8 = 135.
1253        // num_tiles = 240 * 135 = 32,400.
1254        // header_size = RoundUp(32,400, 256) = 32,512.
1255        let constraints = fsysmem2::ImageFormatConstraints {
1256            pixel_format: Some(fimages2::PixelFormat::R8G8B8A8),
1257            pixel_format_modifier: Some(fimages2::PixelFormatModifier::PvrFbcdc8X8V14),
1258            min_size: Some(SizeU { width: 1, height: 1 }),
1259            max_size: Some(SizeU { width: 4096, height: 4096 }),
1260            ..Default::default()
1261        };
1262
1263        let image_format = constraints_to_image_format(&constraints, 1920, 1080).unwrap();
1264        assert_eq!(image_format.bytes_per_row, Some(7680));
1265        assert_eq!(image_format_plane_byte_offset_2(&image_format, 0).unwrap(), 32_512);
1266        assert!(image_format_plane_byte_offset_2(&image_format, 1).is_err());
1267        assert!(image_format_plane_byte_offset_2(&image_format, 2).is_err());
1268        assert_eq!(image_format_plane_byte_offset_2(&image_format, 3).unwrap(), 0);
1269        assert!(image_format_plane_byte_offset_2(&image_format, 4).is_err());
1270
1271        assert_eq!(get_plane_row_bytes_2(&image_format, 0).unwrap(), 7680);
1272        assert!(get_plane_row_bytes_2(&image_format, 1).is_err());
1273        assert!(get_plane_row_bytes_2(&image_format, 2).is_err());
1274        assert_eq!(get_plane_row_bytes_2(&image_format, 3).unwrap(), 240);
1275        assert!(get_plane_row_bytes_2(&image_format, 4).is_err());
1276    }
1277}