blob.rs (7344B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 // A very basic BlobImageRasterizer that can only render a checkerboard pattern. 6 7 use std::collections::HashMap; 8 use std::sync::Arc; 9 use std::sync::Mutex; 10 use webrender::api::*; 11 use webrender::api::units::{BlobDirtyRect, BlobToDeviceTranslation, TileOffset}; 12 use webrender::api::units::DeviceIntRect; 13 14 // Serialize/deserialize the blob. 15 16 pub fn serialize_blob(color: ColorU) -> Arc<Vec<u8>> { 17 Arc::new(vec![color.r, color.g, color.b, color.a]) 18 } 19 20 fn deserialize_blob(blob: &[u8]) -> Result<ColorU, ()> { 21 let mut iter = blob.iter(); 22 match (iter.next(), iter.next(), iter.next(), iter.next()) { 23 (Some(&r), Some(&g), Some(&b), Some(&a)) => Ok(ColorU::new(r, g, b, a)), 24 (Some(&a), None, None, None) => Ok(ColorU::new(a, a, a, a)), 25 _ => Err(()), 26 } 27 } 28 29 // perform floor((x * a) / 255. + 0.5) see "Three wrongs make a right" for derivation 30 fn premul(x: u8, a: u8) -> u8 { 31 let t = (x as u32) * (a as u32) + 128; 32 ((t + (t >> 8)) >> 8) as u8 33 } 34 35 // This is the function that applies the deserialized drawing commands and generates 36 // actual image data. 37 fn render_blob( 38 color: ColorU, 39 descriptor: &BlobImageDescriptor, 40 tile: TileOffset, 41 _tile_size: TileSize, 42 dirty_rect: &BlobDirtyRect, 43 ) -> BlobImageResult { 44 // Allocate storage for the result. Right now the resource cache expects the 45 // tiles to have have no stride or offset. 46 let buf_size = descriptor.rect.area() * 47 descriptor.format.bytes_per_pixel(); 48 let mut texels = vec![0u8; (buf_size) as usize]; 49 50 // Generate a per-tile pattern to see it in the demo. For a real use case it would not 51 // make sense for the rendered content to depend on its tile. 52 let tile_checker = (tile.x % 2 == 0) != (tile.y % 2 == 0); 53 54 let dirty_rect = dirty_rect.to_subrect_of(&descriptor.rect); 55 56 // We want the dirty rect local to the tile rather than the whole image. 57 let tx: BlobToDeviceTranslation = (-descriptor.rect.min.to_vector()).into(); 58 59 let rasterized_rect = tx.transform_box(&dirty_rect); 60 61 for y in rasterized_rect.min.y .. rasterized_rect.max.y { 62 for x in rasterized_rect.min.x .. rasterized_rect.max.x { 63 // Apply the tile's offset. This is important: all drawing commands should be 64 // translated by this offset to give correct results with tiled blob images. 65 let x2 = x + descriptor.rect.min.x; 66 let y2 = y + descriptor.rect.min.y; 67 68 // Render a simple checkerboard pattern 69 let checker = if (x2 % 20 >= 10) != (y2 % 20 >= 10) { 70 1 71 } else { 72 0 73 }; 74 // ..nested in the per-tile checkerboard pattern 75 let tc = if tile_checker { 0 } else { (1 - checker) * 40 }; 76 77 match descriptor.format { 78 ImageFormat::BGRA8 => { 79 let a = color.a * checker + tc; 80 let pixel_offset = ((y * descriptor.rect.width() + x) * 4) as usize; 81 texels[pixel_offset ] = premul(color.b * checker + tc, a); 82 texels[pixel_offset + 1] = premul(color.g * checker + tc, a); 83 texels[pixel_offset + 2] = premul(color.r * checker + tc, a); 84 texels[pixel_offset + 3] = a; 85 } 86 ImageFormat::R8 => { 87 texels[(y * descriptor.rect.width() + x) as usize] = color.a * checker + tc; 88 } 89 _ => { 90 return Err(BlobImageError::Other( 91 format!("Unsupported image format {:?}", descriptor.format), 92 )); 93 } 94 } 95 } 96 } 97 98 Ok(RasterizedBlobImage { 99 data: Arc::new(texels), 100 rasterized_rect, 101 }) 102 } 103 104 /// See rawtest.rs. We use this to test that blob images are requested the right 105 /// amount of times. 106 pub struct BlobCallbacks { 107 pub request: Box<dyn Fn(&[BlobImageParams]) + Send + 'static>, 108 } 109 110 impl BlobCallbacks { 111 pub fn new() -> Self { 112 BlobCallbacks { request: Box::new(|_|()) } 113 } 114 } 115 116 pub struct CheckerboardRenderer { 117 image_cmds: HashMap<BlobImageKey, (ColorU, TileSize)>, 118 callbacks: Arc<Mutex<BlobCallbacks>>, 119 } 120 121 impl CheckerboardRenderer { 122 pub fn new(callbacks: Arc<Mutex<BlobCallbacks>>) -> Self { 123 CheckerboardRenderer { 124 callbacks, 125 image_cmds: HashMap::new(), 126 } 127 } 128 } 129 130 impl BlobImageHandler for CheckerboardRenderer { 131 fn create_similar(&self) -> Box<dyn BlobImageHandler> { 132 Box::new(CheckerboardRenderer::new(Arc::clone(&self.callbacks))) 133 } 134 135 fn add(&mut self, key: BlobImageKey, cmds: Arc<BlobImageData>, 136 _visible_rect: &DeviceIntRect, tile_size: TileSize) { 137 self.image_cmds 138 .insert(key, (deserialize_blob(&cmds[..]).unwrap(), tile_size)); 139 } 140 141 fn update(&mut self, key: BlobImageKey, cmds: Arc<BlobImageData>, 142 _visible_rect: &DeviceIntRect, _dirty_rect: &BlobDirtyRect) { 143 // Here, updating is just replacing the current version of the commands with 144 // the new one (no incremental updates). 145 self.image_cmds.get_mut(&key).unwrap().0 = deserialize_blob(&cmds[..]).unwrap(); 146 } 147 148 fn delete(&mut self, key: BlobImageKey) { 149 self.image_cmds.remove(&key); 150 } 151 152 fn delete_font(&mut self, _key: FontKey) {} 153 154 fn delete_font_instance(&mut self, _key: FontInstanceKey) {} 155 156 fn clear_namespace(&mut self, _namespace: IdNamespace) {} 157 158 fn prepare_resources( 159 &mut self, 160 _services: &dyn BlobImageResources, 161 requests: &[BlobImageParams], 162 ) { 163 if !requests.is_empty() { 164 (self.callbacks.lock().unwrap().request)(requests); 165 } 166 } 167 168 fn create_blob_rasterizer(&mut self) -> Box<dyn AsyncBlobImageRasterizer> { 169 Box::new(Rasterizer { image_cmds: self.image_cmds.clone() }) 170 } 171 172 fn enable_multithreading(&mut self, _enable: bool) {} 173 } 174 175 struct Command { 176 request: BlobImageRequest, 177 color: ColorU, 178 descriptor: BlobImageDescriptor, 179 tile: TileOffset, 180 tile_size: TileSize, 181 dirty_rect: BlobDirtyRect, 182 } 183 184 struct Rasterizer { 185 image_cmds: HashMap<BlobImageKey, (ColorU, TileSize)>, 186 } 187 188 impl AsyncBlobImageRasterizer for Rasterizer { 189 fn rasterize( 190 &mut self, 191 requests: &[BlobImageParams], 192 _low_priority: bool, 193 _tile_pool: &mut BlobTilePool, 194 ) -> Vec<(BlobImageRequest, BlobImageResult)> { 195 let requests: Vec<Command> = requests.iter().map( 196 |item| { 197 let (color, tile_size) = self.image_cmds[&item.request.key]; 198 199 Command { 200 request: item.request, 201 color, 202 tile_size, 203 tile: item.request.tile, 204 descriptor: item.descriptor, 205 dirty_rect: item.dirty_rect, 206 } 207 } 208 ).collect(); 209 210 requests.iter().map(|cmd| { 211 (cmd.request, render_blob(cmd.color, &cmd.descriptor, cmd.tile, cmd.tile_size, &cmd.dirty_rect)) 212 }).collect() 213 } 214 }