tor-browser

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

blob.rs (10321B)


      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 extern crate gleam;
      6 extern crate glutin;
      7 extern crate rayon;
      8 extern crate webrender;
      9 extern crate winit;
     10 
     11 #[path = "common/boilerplate.rs"]
     12 mod boilerplate;
     13 
     14 use crate::boilerplate::{Example, HandyDandyRectBuilder};
     15 use rayon::{ThreadPool, ThreadPoolBuilder};
     16 use rayon::prelude::*;
     17 use std::collections::HashMap;
     18 use std::sync::Arc;
     19 use webrender::api::{self, DisplayListBuilder, DocumentId, PipelineId, PrimitiveFlags};
     20 use webrender::api::{ColorF, CommonItemProperties, SpaceAndClipInfo, ImageDescriptorFlags};
     21 use webrender::api::BlobTilePool;
     22 use webrender::api::units::*;
     23 use webrender::render_api::*;
     24 use webrender::euclid::size2;
     25 
     26 // This example shows how to implement a very basic BlobImageHandler that can only render
     27 // a checkerboard pattern.
     28 
     29 // The deserialized command list internally used by this example is just a color.
     30 type ImageRenderingCommands = api::ColorU;
     31 
     32 // Serialize/deserialize the blob.
     33 // For real usecases you should probably use serde rather than doing it by hand.
     34 
     35 fn serialize_blob(color: api::ColorU) -> Arc<Vec<u8>> {
     36    Arc::new(vec![color.r, color.g, color.b, color.a])
     37 }
     38 
     39 fn deserialize_blob(blob: &[u8]) -> Result<ImageRenderingCommands, ()> {
     40    let mut iter = blob.iter();
     41    return match (iter.next(), iter.next(), iter.next(), iter.next()) {
     42        (Some(&r), Some(&g), Some(&b), Some(&a)) => Ok(api::ColorU::new(r, g, b, a)),
     43        (Some(&a), None, None, None) => Ok(api::ColorU::new(a, a, a, a)),
     44        _ => Err(()),
     45    };
     46 }
     47 
     48 // This is the function that applies the deserialized drawing commands and generates
     49 // actual image data.
     50 fn render_blob(
     51    commands: Arc<ImageRenderingCommands>,
     52    descriptor: &api::BlobImageDescriptor,
     53    tile: TileOffset,
     54 ) -> api::BlobImageResult {
     55    let color = *commands;
     56 
     57    // Note: This implementation ignores the dirty rect which isn't incorrect
     58    // but is a missed optimization.
     59 
     60    // Allocate storage for the result. Right now the resource cache expects the
     61    // tiles to have have no stride or offset.
     62    let bpp = 4;
     63    let mut texels = Vec::with_capacity((descriptor.rect.area() * bpp) as usize);
     64 
     65    // Generate a per-tile pattern to see it in the demo. For a real use case it would not
     66    // make sense for the rendered content to depend on its tile.
     67    let tile_checker = (tile.x % 2 == 0) != (tile.y % 2 == 0);
     68 
     69    let [w, h] = descriptor.rect.size().to_array();
     70    let offset = descriptor.rect.min;
     71 
     72    for y in 0..h {
     73        for x in 0..w {
     74            // Apply the tile's offset. This is important: all drawing commands should be
     75            // translated by this offset to give correct results with tiled blob images.
     76            let x2 = x + offset.x;
     77            let y2 = y + offset.y;
     78 
     79            // Render a simple checkerboard pattern
     80            let checker = if (x2 % 20 >= 10) != (y2 % 20 >= 10) {
     81                1
     82            } else {
     83                0
     84            };
     85            // ..nested in the per-tile checkerboard pattern
     86            let tc = if tile_checker { 0 } else { (1 - checker) * 40 };
     87 
     88            match descriptor.format {
     89                api::ImageFormat::BGRA8 => {
     90                    texels.push(color.b * checker + tc);
     91                    texels.push(color.g * checker + tc);
     92                    texels.push(color.r * checker + tc);
     93                    texels.push(color.a * checker + tc);
     94                }
     95                api::ImageFormat::R8 => {
     96                    texels.push(color.a * checker + tc);
     97                }
     98                _ => {
     99                    return Err(api::BlobImageError::Other(
    100                        format!("Unsupported image format"),
    101                    ));
    102                }
    103            }
    104        }
    105    }
    106 
    107    Ok(api::RasterizedBlobImage {
    108        data: Arc::new(texels),
    109        rasterized_rect: size2(w, h).into(),
    110    })
    111 }
    112 
    113 struct CheckerboardRenderer {
    114    // We are going to defer the rendering work to worker threads.
    115    // Using a pre-built Arc<ThreadPool> rather than creating our own threads
    116    // makes it possible to share the same thread pool as the glyph renderer (if we
    117    // want to).
    118    workers: Arc<ThreadPool>,
    119 
    120    // The deserialized drawing commands.
    121    // In this example we store them in Arcs. This isn't necessary since in this simplified
    122    // case the command list is a simple 32 bits value and would be cheap to clone before sending
    123    // to the workers. But in a more realistic scenario the commands would typically be bigger
    124    // and more expensive to clone, so let's pretend it is also the case here.
    125    image_cmds: HashMap<api::BlobImageKey, Arc<ImageRenderingCommands>>,
    126 }
    127 
    128 impl CheckerboardRenderer {
    129    fn new(workers: Arc<ThreadPool>) -> Self {
    130        CheckerboardRenderer {
    131            image_cmds: HashMap::new(),
    132            workers,
    133        }
    134    }
    135 }
    136 
    137 impl api::BlobImageHandler for CheckerboardRenderer {
    138    fn create_similar(&self) -> Box<dyn api::BlobImageHandler> {
    139        Box::new(CheckerboardRenderer::new(Arc::clone(&self.workers)))
    140    }
    141 
    142    fn add(&mut self, key: api::BlobImageKey, cmds: Arc<api::BlobImageData>,
    143           _visible_rect: &DeviceIntRect, _: api::TileSize) {
    144        self.image_cmds
    145            .insert(key, Arc::new(deserialize_blob(&cmds[..]).unwrap()));
    146    }
    147 
    148    fn update(&mut self, key: api::BlobImageKey, cmds: Arc<api::BlobImageData>,
    149              _visible_rect: &DeviceIntRect, _dirty_rect: &BlobDirtyRect) {
    150        // Here, updating is just replacing the current version of the commands with
    151        // the new one (no incremental updates).
    152        self.image_cmds
    153            .insert(key, Arc::new(deserialize_blob(&cmds[..]).unwrap()));
    154    }
    155 
    156    fn delete(&mut self, key: api::BlobImageKey) {
    157        self.image_cmds.remove(&key);
    158    }
    159 
    160    fn prepare_resources(
    161        &mut self,
    162        _services: &dyn api::BlobImageResources,
    163        _requests: &[api::BlobImageParams],
    164    ) {}
    165 
    166    fn enable_multithreading(&mut self, _: bool) {}
    167    fn delete_font(&mut self, _font: api::FontKey) {}
    168    fn delete_font_instance(&mut self, _instance: api::FontInstanceKey) {}
    169    fn clear_namespace(&mut self, _namespace: api::IdNamespace) {}
    170    fn create_blob_rasterizer(&mut self) -> Box<dyn api::AsyncBlobImageRasterizer> {
    171        Box::new(Rasterizer {
    172            workers: Arc::clone(&self.workers),
    173            image_cmds: self.image_cmds.clone(),
    174        })
    175    }
    176 }
    177 
    178 struct Rasterizer {
    179    workers: Arc<ThreadPool>,
    180    image_cmds: HashMap<api::BlobImageKey, Arc<ImageRenderingCommands>>,
    181 }
    182 
    183 impl api::AsyncBlobImageRasterizer for Rasterizer {
    184    fn rasterize(
    185        &mut self,
    186        requests: &[api::BlobImageParams],
    187        _low_priority: bool,
    188        _tile_pool: &mut BlobTilePool,
    189    ) -> Vec<(api::BlobImageRequest, api::BlobImageResult)> {
    190        let requests: Vec<(&api::BlobImageParams, Arc<ImageRenderingCommands>)> = requests.into_iter().map(|params| {
    191            (params, Arc::clone(&self.image_cmds[&params.request.key]))
    192        }).collect();
    193 
    194        self.workers.install(|| {
    195            requests.into_par_iter().map(|(params, commands)| {
    196                (params.request, render_blob(commands, &params.descriptor, params.request.tile))
    197            }).collect()
    198        })
    199    }
    200 }
    201 
    202 struct App {}
    203 
    204 impl Example for App {
    205    fn render(
    206        &mut self,
    207        api: &mut RenderApi,
    208        builder: &mut DisplayListBuilder,
    209        txn: &mut Transaction,
    210        _device_size: DeviceIntSize,
    211        pipeline_id: PipelineId,
    212        _document_id: DocumentId,
    213    ) {
    214        let space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
    215 
    216        builder.push_simple_stacking_context(
    217            LayoutPoint::zero(),
    218            space_and_clip.spatial_id,
    219            PrimitiveFlags::IS_BACKFACE_VISIBLE,
    220        );
    221 
    222        let size1 = DeviceIntSize::new(500, 500);
    223        let blob_img1 = api.generate_blob_image_key();
    224        txn.add_blob_image(
    225            blob_img1,
    226            api::ImageDescriptor::new(
    227                size1.width,
    228                size1.height,
    229                api::ImageFormat::BGRA8,
    230                ImageDescriptorFlags::IS_OPAQUE,
    231            ),
    232            serialize_blob(api::ColorU::new(50, 50, 150, 255)),
    233            size1.into(),
    234            Some(128),
    235        );
    236        let bounds = (30, 30).by(size1.width, size1.height);
    237        builder.push_image(
    238            &CommonItemProperties::new(bounds, space_and_clip),
    239            bounds,
    240            api::ImageRendering::Auto,
    241            api::AlphaType::PremultipliedAlpha,
    242            blob_img1.as_image(),
    243            ColorF::WHITE,
    244        );
    245 
    246        let size2 = DeviceIntSize::new(256, 256);
    247        let blob_img2 = api.generate_blob_image_key();
    248        txn.add_blob_image(
    249            blob_img2,
    250            api::ImageDescriptor::new(
    251                size2.width,
    252                size2.height,
    253                api::ImageFormat::BGRA8,
    254                ImageDescriptorFlags::IS_OPAQUE,
    255            ),
    256            serialize_blob(api::ColorU::new(50, 150, 50, 255)),
    257            size2.into(),
    258            None,
    259        );
    260        let bounds = (600, 600).by(size2.width, size2.height);
    261        builder.push_image(
    262            &CommonItemProperties::new(bounds, space_and_clip),
    263            bounds,
    264            api::ImageRendering::Auto,
    265            api::AlphaType::PremultipliedAlpha,
    266            blob_img2.as_image(),
    267            ColorF::WHITE,
    268        );
    269 
    270        builder.pop_stacking_context();
    271    }
    272 }
    273 
    274 fn main() {
    275    let workers =
    276        ThreadPoolBuilder::new().thread_name(|idx| format!("WebRender:Worker#{}", idx))
    277                                .build();
    278 
    279    let workers = Arc::new(workers.unwrap());
    280 
    281    let opts = webrender::WebRenderOptions {
    282        workers: Some(Arc::clone(&workers)),
    283        // Register our blob renderer, so that WebRender integrates it in the resource cache..
    284        // Share the same pool of worker threads between WebRender and our blob renderer.
    285        blob_image_handler: Some(Box::new(CheckerboardRenderer::new(Arc::clone(&workers)))),
    286        ..Default::default()
    287    };
    288 
    289    let mut app = App {};
    290 
    291    boilerplate::main_wrapper(&mut app, Some(opts));
    292 }