blob: 06e5531709263f98a8ea3dd3d32e475293faa989 [file] [log] [blame] [edit]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use anyhow::{format_err, Context, Result};
use display_utils::{Image, ImageId};
use fuchsia_image_format::{
image_format_minimum_row_bytes_2, image_format_stride_bytes_per_width_pixel_2,
};
use fuchsia_zircon as zx;
use mapped_vmo::Mapping;
use std::cmp::min;
// TODO(armansito): Extend this to support different patterns and tiled image formats.
pub struct MappedImage {
image: Image,
mapping: Mapping,
pixel_width: u32,
row_bytes: u32,
}
pub struct Frame {
pub pos_x: u32,
pub pos_y: u32,
pub width: u32,
pub height: u32,
}
impl MappedImage {
pub fn create(image: Image) -> Result<MappedImage> {
let size: usize = *image.buffer_settings.size_bytes.as_ref().unwrap() as usize;
let mapping = Mapping::create_from_vmo(
&image.vmo,
size,
zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE,
)
.context("failed to map VMO")?;
let constraints = &image.format_constraints;
let pixel_width = image_format_stride_bytes_per_width_pixel_2(
*constraints.pixel_format.as_ref().unwrap(),
)
.unwrap();
let row_bytes = image_format_minimum_row_bytes_2(constraints, image.parameters.width)?;
Ok(MappedImage { image, mapping, pixel_width, row_bytes })
}
pub fn id(&self) -> ImageId {
self.image.id
}
pub fn zero(&self) -> Result<()> {
self.image.vmo.op_range(zx::VmoOp::ZERO, 0, self.image.vmo.get_size()?)?;
Ok(())
}
/// Fill the image with the specified color. The size and interpretation of the given color
/// must match the underlying image format.
pub fn fill(&self, color: &[u8]) -> Result<()> {
self.fill_region(
color,
&Frame { pos_x: 0, pos_y: 0, width: self.width(), height: self.height() },
)
}
/// Fill the specified region of this image with the specified color. The size and
/// interpretation of the given color must match the underlying image format.
pub fn fill_region(&self, color: &[u8], frame: &Frame) -> Result<()> {
if self.pixel_width != color.len().try_into()? {
return Err(format_err!(
"provided color width ({}) does not match expected pixel format width ({})",
color.len(),
self.pixel_width
));
}
// First clip the frame to the image bounds.
let frame = {
let pos_x = min(self.width() - 1, frame.pos_x);
let pos_y = min(self.height() - 1, frame.pos_y);
Frame {
pos_x,
pos_y,
width: min(frame.width, self.width() - pos_x),
height: min(frame.height, self.height() - pos_y),
}
};
// Color the pixels within the frame.
let pixel_stride = self.row_bytes / self.width();
for row in 0..frame.height {
for col in 0..frame.width {
let idx = self.row_bytes * (row + frame.pos_y) + pixel_stride * (col + frame.pos_x);
self.mapping.write_at(idx.try_into()?, color);
}
}
Ok(())
}
/// Clean (write back) data caches, so previous writes are visible in main memory. This
/// operation must be called at once before this image buffer gets presented to avoid artifacts
/// during scanout in height refresh rates if the image is changing frequently. This operation
/// can be expensive on large images and should be performed sparingly.
pub fn cache_clean(&self) -> Result<()> {
self.image.vmo.op_range(zx::VmoOp::CACHE_CLEAN, 0, self.image.vmo.get_size()?)?;
Ok(())
}
fn width(&self) -> u32 {
self.image.parameters.width
}
fn height(&self) -> u32 {
self.image.parameters.height
}
}