/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::api::{BlobImageKey, ImageDescriptor, DirtyRect, TileSize}; use crate::api::{BlobImageHandler, AsyncBlobImageRasterizer, BlobImageData, BlobImageParams}; use crate::api::{BlobImageRequest, BlobImageDescriptor, FontTemplate}; use crate::api::units::*; use glyph_rasterizer::{SharedFontResources, BaseFontInstance}; use crate::render_api::{ResourceUpdate, TransactionMsg, AddFont}; use crate::image_tiling::*; use crate::profiler; use std::collections::HashMap; use std::mem; use std::sync::Arc; /// We use this to generate the async blob rendering requests. struct BlobImageTemplate { descriptor: ImageDescriptor, tile_size: TileSize, dirty_rect: BlobDirtyRect, /// See ImageResource::visible_rect. visible_rect: DeviceIntRect, // If the active rect of the blob changes, this represents the // range of tiles that remain valid. This must be taken into // account in addition to the valid rect when submitting blob // rasterization requests. // `None` means the bounds have not changed (tiles are still valid). // `Some(TileRange::zero())` means all of the tiles are invalid. valid_tiles_after_bounds_change: Option, } pub struct ApiResources { blob_image_templates: HashMap, pub blob_image_handler: Option>, fonts: SharedFontResources, } impl ApiResources { pub fn new( blob_image_handler: Option>, fonts: SharedFontResources, ) -> Self { ApiResources { blob_image_templates: HashMap::new(), blob_image_handler, fonts, } } pub fn get_fonts(&self) -> SharedFontResources { self.fonts.clone() } pub fn update(&mut self, transaction: &mut TransactionMsg) { let mut blobs_to_rasterize = Vec::new(); for update in &mut transaction.resource_updates { match *update { ResourceUpdate::AddBlobImage(ref img) => { self.blob_image_handler .as_mut() .expect("no blob image handler") .add(img.key, Arc::clone(&img.data), &img.visible_rect, img.tile_size); self.blob_image_templates.insert( img.key, BlobImageTemplate { descriptor: img.descriptor, tile_size: img.tile_size, dirty_rect: DirtyRect::All, valid_tiles_after_bounds_change: None, visible_rect: img.visible_rect, }, ); blobs_to_rasterize.push(img.key); } ResourceUpdate::UpdateBlobImage(ref img) => { debug_assert_eq!(img.visible_rect.size(), img.descriptor.size); self.update_blob_image( img.key, Some(&img.descriptor), Some(&img.dirty_rect), Some(Arc::clone(&img.data)), &img.visible_rect, ); blobs_to_rasterize.push(img.key); } ResourceUpdate::DeleteBlobImage(key) => { transaction.use_scene_builder_thread = true; self.blob_image_templates.remove(&key); if let Some(ref mut handler) = self.blob_image_handler { handler.delete(key); } } ResourceUpdate::SetBlobImageVisibleArea(ref key, ref area) => { self.update_blob_image(*key, None, None, None, area); blobs_to_rasterize.push(*key); } ResourceUpdate::AddFont(ref font) => { let (key, template) = match font { AddFont::Raw(key, bytes, index) => { (key, FontTemplate::Raw(Arc::clone(bytes), *index)) } AddFont::Native(key, native_font_handle) => { (key, FontTemplate::Native(native_font_handle.clone())) } }; if let Some(shared_key) = self.fonts.font_keys.add_key(key, &template) { self.fonts.templates.add_font(shared_key, template); } } ResourceUpdate::AddFontInstance(ref mut instance) => { let shared_font_key = self.fonts.font_keys.map_key(&instance.font_key); assert!(self.fonts.templates.has_font(&shared_font_key)); // AddFontInstance will only be processed here, not in the resource cache, so it // is safe to take the options rather than clone them. let base = BaseFontInstance::new( instance.key, shared_font_key, instance.glyph_size, mem::take(&mut instance.options), mem::take(&mut instance.platform_options), mem::take(&mut instance.variations), ); if let Some(shared_instance) = self.fonts.instance_keys.add_key(base) { self.fonts.instances.add_font_instance(shared_instance); } } ResourceUpdate::DeleteFont(_key) => { transaction.use_scene_builder_thread = true; } ResourceUpdate::DeleteFontInstance(_key) => { transaction.use_scene_builder_thread = true; // We will delete from the shared font instance map in the resource cache // after scene swap. } ResourceUpdate::DeleteImage(..) => { transaction.use_scene_builder_thread = true; } _ => {} } } let (rasterizer, requests) = self.create_blob_scene_builder_requests(&blobs_to_rasterize); transaction.profile.set(profiler::RASTERIZED_BLOBS, blobs_to_rasterize.len()); transaction.profile.set(profiler::RASTERIZED_BLOB_TILES, requests.len()); transaction.use_scene_builder_thread |= !requests.is_empty(); transaction.use_scene_builder_thread |= !transaction.scene_ops.is_empty(); transaction.blob_rasterizer = rasterizer; transaction.blob_requests = requests; } pub fn enable_multithreading(&mut self, enable: bool) { if let Some(ref mut handler) = self.blob_image_handler { handler.enable_multithreading(enable); } } fn update_blob_image( &mut self, key: BlobImageKey, descriptor: Option<&ImageDescriptor>, dirty_rect: Option<&BlobDirtyRect>, data: Option>, visible_rect: &DeviceIntRect, ) { if let Some(data) = data { let dirty_rect = dirty_rect.expect("no dirty rect"); self.blob_image_handler .as_mut() .expect("no blob image handler") .update(key, data, visible_rect, dirty_rect); } let image = self.blob_image_templates .get_mut(&key) .expect("Attempt to update non-existent blob image"); let mut valid_tiles_after_bounds_change = compute_valid_tiles_if_bounds_change( &image.visible_rect, visible_rect, image.tile_size, ); match (image.valid_tiles_after_bounds_change, valid_tiles_after_bounds_change) { (Some(old), Some(ref mut new)) => { *new = new.intersection(&old).unwrap_or_else(TileRange::zero); } (Some(old), None) => { valid_tiles_after_bounds_change = Some(old); } _ => {} } let blob_size = visible_rect.size(); if let Some(descriptor) = descriptor { image.descriptor = *descriptor; } else { // make sure the descriptor size matches the visible rect. // This might not be necessary but let's stay on the safe side. image.descriptor.size = blob_size; } if let Some(dirty_rect) = dirty_rect { image.dirty_rect = image.dirty_rect.union(dirty_rect); } image.valid_tiles_after_bounds_change = valid_tiles_after_bounds_change; image.visible_rect = *visible_rect; } pub fn create_blob_scene_builder_requests( &mut self, keys: &[BlobImageKey] ) -> (Option>, Vec) { if self.blob_image_handler.is_none() || keys.is_empty() { return (None, Vec::new()); } let mut blob_request_params = Vec::new(); for key in keys { let template = self.blob_image_templates.get_mut(key) .expect("no blob image template"); // If we know that only a portion of the blob image is in the viewport, // only request these visible tiles since blob images can be huge. let tiles = compute_tile_range( &template.visible_rect, template.tile_size, ); // Don't request tiles that weren't invalidated. let dirty_tiles = match template.dirty_rect { DirtyRect::Partial(dirty_rect) => { compute_tile_range( &dirty_rect.cast_unit(), template.tile_size, ) } DirtyRect::All => tiles, }; for_each_tile_in_range(&tiles, |tile| { let still_valid = template.valid_tiles_after_bounds_change .map(|valid_tiles| valid_tiles.contains(tile)) .unwrap_or(true); if still_valid && !dirty_tiles.contains(tile) { return; } let descriptor = BlobImageDescriptor { rect: compute_tile_rect( &template.visible_rect, template.tile_size, tile, ).cast_unit(), format: template.descriptor.format, }; assert!(descriptor.rect.width() > 0 && descriptor.rect.height() > 0); blob_request_params.push( BlobImageParams { request: BlobImageRequest { key: *key, tile }, descriptor, dirty_rect: DirtyRect::All, } ); }); template.dirty_rect = DirtyRect::empty(); template.valid_tiles_after_bounds_change = None; } let handler = self.blob_image_handler.as_mut() .expect("no blob image handler"); handler.prepare_resources(&self.fonts, &blob_request_params); (Some(handler.create_blob_rasterizer()), blob_request_params) } }