tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }