use std::cell::Cell;
use dom_struct::dom_struct;
use euclid::default::Size2D;
use paint_api::SerializableImageData;
use pixels::Snapshot;
use script_bindings::cell::DomRefCell;
use script_bindings::reflector::{Reflector, reflect_dom_object};
use servo_base::Epoch;
use webrender_api::units::DeviceIntSize;
use webrender_api::{ImageDescriptor, ImageFormat, ImageKey};
use crate::canvas_context::{CanvasContext, CanvasHelpers, HTMLCanvasElementOrOffscreenCanvas};
use crate::dom::bindings::codegen::Bindings::ImageBitmapBinding::ImageBitmapMethods;
use crate::dom::bindings::codegen::Bindings::ImageBitmapRenderingContextBinding::ImageBitmapRenderingContextMethods;
use crate::dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanvas as RootedHTMLCanvasElementOrOffscreenCanvas;
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::root::DomRoot;
use crate::dom::globalscope::GlobalScope;
use crate::dom::html::htmlcanvaselement::HTMLCanvasElement;
use crate::dom::imagebitmap::ImageBitmap;
use crate::dom::node::node::NodeTraits;
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct ImageBitmapRenderingContext {
reflector_: Reflector,
canvas: HTMLCanvasElementOrOffscreenCanvas,
#[no_trace]
bitmap: DomRefCell<Option<Snapshot>>,
origin_clean: Cell<bool>,
#[no_trace]
image_key: Cell<Option<ImageKey>>,
image_added: Cell<bool>,
}
impl ImageBitmapRenderingContext {
#[cfg_attr(crown, expect(crown::unrooted_must_root))]
fn new_inherited(canvas: HTMLCanvasElementOrOffscreenCanvas) -> ImageBitmapRenderingContext {
ImageBitmapRenderingContext {
reflector_: Reflector::new(),
canvas,
bitmap: DomRefCell::new(None),
origin_clean: Cell::new(true),
image_key: Cell::new(None),
image_added: Cell::new(false),
}
}
pub(crate) fn new(
global: &GlobalScope,
canvas: &RootedHTMLCanvasElementOrOffscreenCanvas,
can_gc: CanGc,
) -> DomRoot<ImageBitmapRenderingContext> {
reflect_dom_object(
Box::new(ImageBitmapRenderingContext::new_inherited(
HTMLCanvasElementOrOffscreenCanvas::from(canvas),
)),
global,
can_gc,
)
}
pub(crate) fn set_image_key(&self, image_key: ImageKey) {
self.image_key.set(Some(image_key));
}
pub(crate) fn update_rendering(&self, epoch: Epoch) -> bool {
let Some(image_key) = self.image_key.get() else {
return false;
};
let Some(snapshot) = self.bitmap.borrow().as_ref().cloned() else {
return false;
};
let size = snapshot.size();
let format = match snapshot.format() {
pixels::SnapshotPixelFormat::RGBA => ImageFormat::RGBA8,
pixels::SnapshotPixelFormat::BGRA => ImageFormat::BGRA8,
};
let shared = snapshot.to_shared();
let descriptor = ImageDescriptor {
format,
size: DeviceIntSize::new(size.width as i32, size.height as i32),
stride: None,
offset: 0,
flags: webrender_api::ImageDescriptorFlags::empty(),
};
let data = SerializableImageData::Raw(shared.shared_memory());
if let HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(ref canvas) = self.canvas {
let canvas: &HTMLCanvasElement = canvas;
let doc = canvas.owner_document();
let paint_api = doc.window().paint_api();
if self.image_added.get() {
paint_api.update_image(image_key, descriptor, data, Some(epoch));
} else {
paint_api.add_image(image_key, descriptor, data, false);
self.image_added.set(true);
}
}
false
}
fn set_bitmap(&self, image_bitmap: Option<&ImageBitmap>) {
match image_bitmap {
Some(image_bitmap) => {
*self.bitmap.borrow_mut() = image_bitmap.bitmap_data().clone();
self.origin_clean.set(image_bitmap.origin_is_clean());
},
None => {
*self.bitmap.borrow_mut() = None;
self.origin_clean.set(true);
},
}
}
}
impl CanvasContext for ImageBitmapRenderingContext {
type ID = ();
fn context_id(&self) -> Self::ID {}
fn canvas(&self) -> Option<RootedHTMLCanvasElementOrOffscreenCanvas> {
Some(RootedHTMLCanvasElementOrOffscreenCanvas::from(&self.canvas))
}
fn resize(&self) {
}
fn reset_bitmap(&self) {
if self.bitmap.borrow().is_none() {
return;
}
let size = self.bitmap.borrow().as_ref().unwrap().size();
*self.bitmap.borrow_mut() = Some(Snapshot::cleared(size));
}
fn get_image_data(&self) -> Option<Snapshot> {
match self.bitmap.borrow().as_ref() {
Some(bitmap) => Some(bitmap.clone()),
None => {
let size = self.canvas.size();
if size.is_empty() ||
pixels::compute_rgba8_byte_length_if_within_limit(
size.width as usize,
size.height as usize,
)
.is_none()
{
None
} else {
Some(Snapshot::cleared(size))
}
},
}
}
fn origin_is_clean(&self) -> bool {
self.origin_clean.get()
}
fn size(&self) -> Size2D<u32> {
self.bitmap
.borrow()
.as_ref()
.map_or_else(|| self.canvas.size(), |bitmap| bitmap.size())
}
fn mark_as_dirty(&self) {
self.canvas.mark_as_dirty();
}
}
impl ImageBitmapRenderingContextMethods<crate::DomTypeHolder> for ImageBitmapRenderingContext {
fn Canvas(&self) -> RootedHTMLCanvasElementOrOffscreenCanvas {
RootedHTMLCanvasElementOrOffscreenCanvas::from(&self.canvas)
}
fn TransferFromImageBitmap(&self, image_bitmap: Option<&ImageBitmap>) -> Fallible<()> {
let Some(image_bitmap) = image_bitmap else {
self.set_bitmap(None);
self.mark_as_dirty();
return Ok(());
};
if image_bitmap.is_detached() {
return Err(Error::InvalidState(None));
}
self.set_bitmap(Some(image_bitmap));
self.mark_as_dirty();
image_bitmap.Close();
Ok(())
}
}