Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tiled image support #897

Merged
merged 6 commits into from Feb 24, 2017
Prev

Address some review comments.

  • Loading branch information
nical committed Feb 23, 2017
commit 3a10682e980daa7dc0bc54cd5a71488201f9e1db
@@ -105,7 +105,7 @@ fn main() {
let vector_img = api.generate_image_key();
api.add_image(
vector_img,
ImageDescriptor::new(100, 100, ImageFormat::RGBA8).with_opaque_flag(true),
ImageDescriptor::new(100, 100, ImageFormat::RGBA8, true),
ImageData::new_blob_image(Vec::new()),
None,
);
@@ -143,7 +143,7 @@ fn main() {
let mask_image = api.generate_image_key();
api.add_image(
mask_image,
ImageDescriptor::new(2, 2, ImageFormat::A8).with_opaque_flag(true),
ImageDescriptor::new(2, 2, ImageFormat::A8, true),
ImageData::new(vec![0, 80, 180, 255]),
None,
);
@@ -29,6 +29,7 @@ static DEFAULT_SCROLLBAR_COLOR: ColorF = ColorF { r: 0.3, g: 0.3, b: 0.3, a: 0.6
struct FlattenContext<'a> {
scene: &'a Scene,
builder: &'a mut FrameBuilder,
resource_cache: &'a mut ResourceCache,
}

// TODO: doc
@@ -276,6 +277,7 @@ impl Frame {
let mut context = FlattenContext {
scene: scene,
builder: &mut frame_builder,
resource_cache: resource_cache
};

let mut traversal = DisplayListTraversal::new_skipping_first(display_list);
@@ -295,7 +297,6 @@ impl Frame {
&root_clip.main.size);

self.flatten_stacking_context(&mut traversal,
resource_cache,
root_pipeline_id,
&mut context,
reference_frame_id,
@@ -315,7 +316,6 @@ impl Frame {

fn flatten_scroll_layer<'a>(&mut self,
traversal: &mut DisplayListTraversal<'a>,
resource_cache: &mut ResourceCache,
pipeline_id: PipelineId,
context: &mut FlattenContext,
current_reference_frame_id: ScrollLayerId,
@@ -343,7 +343,6 @@ impl Frame {
&content_size);

self.flatten_items(traversal,
resource_cache,
pipeline_id,
context,
current_reference_frame_id,
@@ -356,7 +355,6 @@ impl Frame {

fn flatten_stacking_context<'a>(&mut self,
traversal: &mut DisplayListTraversal<'a>,
resource_cache: &mut ResourceCache,
pipeline_id: PipelineId,
context: &mut FlattenContext,
current_reference_frame_id: ScrollLayerId,
@@ -446,7 +444,6 @@ impl Frame {
composition_operations);

self.flatten_items(traversal,
resource_cache,
pipeline_id,
context,
reference_frame_id,
@@ -467,7 +464,6 @@ impl Frame {
}

fn flatten_iframe<'a>(&mut self,
resource_cache: &mut ResourceCache,
pipeline_id: PipelineId,
bounds: &LayerRect,
context: &mut FlattenContext,
@@ -525,7 +521,6 @@ impl Frame {
let mut traversal = DisplayListTraversal::new_skipping_first(display_list);

self.flatten_stacking_context(&mut traversal,
resource_cache,
pipeline_id,
context,
iframe_reference_frame_id,
@@ -541,7 +536,6 @@ impl Frame {

fn flatten_items<'a>(&mut self,
traversal: &mut DisplayListTraversal<'a>,
resource_cache: &mut ResourceCache,
pipeline_id: PipelineId,
context: &mut FlattenContext,
current_reference_frame_id: ScrollLayerId,
@@ -555,7 +549,7 @@ impl Frame {
&item.clip, info.context_id);
}
SpecificDisplayItem::Image(ref info) => {
let image = resource_cache.get_image_properties(info.image_key);
let image = context.resource_cache.get_image_properties(info.image_key);
if let Some(tile_size) = image.tiling {
// The image resource is tiled. We have to generate an image primitive
// for each tile.
@@ -629,7 +623,6 @@ impl Frame {
}
SpecificDisplayItem::PushStackingContext(ref info) => {
self.flatten_stacking_context(traversal,
resource_cache,
pipeline_id,
context,
current_reference_frame_id,
@@ -641,7 +634,6 @@ impl Frame {
}
SpecificDisplayItem::PushScrollLayer(ref info) => {
self.flatten_scroll_layer(traversal,
resource_cache,
pipeline_id,
context,
current_reference_frame_id,
@@ -653,8 +645,7 @@ impl Frame {
info.id);
}
SpecificDisplayItem::Iframe(ref info) => {
self.flatten_iframe(resource_cache,
info.pipeline_id,
self.flatten_iframe(info.pipeline_id,
&item.rect,
context,
current_scroll_layer_id,
@@ -674,7 +665,7 @@ impl Frame {
tile_size: u32) {
// The image resource is tiled. We have to generate an image primitive
// for each tile.
// We need to do this because the image is borken up into smaller tiles in the texture
// We need to do this because the image is broken up into smaller tiles in the texture
// cache and the image shader is not able to work with this type of sparse representation.

// The tiling logic works as follows:
@@ -829,12 +820,6 @@ impl Frame {
stretched_size,
);

if !item.rect.contains(&prim_rect.origin) {
// Due to stretching, this tile is outside of the item's rectangle,
// so it isn't visible.
return;
}

if repeat_x {
assert_eq!(tile_offset.x, 0);
prim_rect.size.width = item.rect.size.width;
@@ -846,24 +831,16 @@ impl Frame {
}

// Fix up the primitive's rect if it overflows the original item rect.
let x_overflow = prim_rect.max_x() - item.rect.max_x();
if x_overflow > 0.0 {
prim_rect.size.width -= x_overflow;
if let Some(prim_rect) = prim_rect.intersection(&item.rect) {
context.builder.add_image(prim_rect,
&item.clip,
&stretched_size,
&info.tile_spacing,
None,
info.image_key,
info.image_rendering,
Some(tile_offset));
}

let y_overflow = prim_rect.max_y() - item.rect.max_y();
if y_overflow > 0.0 {
prim_rect.size.height -= y_overflow;
}

context.builder.add_image(prim_rect,
&item.clip,
&stretched_size,
&info.tile_spacing,
None,
info.image_key,
info.image_rendering,
Some(tile_offset));
}

pub fn build(&mut self,
@@ -698,13 +698,13 @@ impl Renderer {
// TODO: Ensure that the white texture can never get evicted when the cache supports LRU eviction!
let white_image_id = texture_cache.new_item_id();
texture_cache.insert(white_image_id,
ImageDescriptor::new(2, 2, ImageFormat::RGBA8),
ImageDescriptor::new(2, 2, ImageFormat::RGBA8, false),
TextureFilter::Linear,
ImageData::Raw(Arc::new(white_pixels)));

let dummy_mask_image_id = texture_cache.new_item_id();
texture_cache.insert(dummy_mask_image_id,
ImageDescriptor::new(2, 2, ImageFormat::A8),
ImageDescriptor::new(2, 2, ImageFormat::A8, false),
TextureFilter::Linear,
ImageData::Raw(Arc::new(mask_pixels)));

@@ -23,7 +23,7 @@ use thread_profiler::register_thread_with_profiler;
use webrender_traits::{Epoch, FontKey, GlyphKey, ImageKey, ImageFormat, ImageRendering};
use webrender_traits::{FontRenderMode, ImageData, GlyphDimensions, WebGLContextId};
use webrender_traits::{DevicePoint, DeviceIntSize, ImageDescriptor, ColorF};
use webrender_traits::{ExternalImageId, GlyphOptions, GlyphInstance, TileOffset};
use webrender_traits::{ExternalImageId, GlyphOptions, GlyphInstance, TileOffset, TileSize};
use webrender_traits::{BlobImageRenderer, BlobImageDescriptor, BlobImageError};
use threadpool::ThreadPool;
use euclid::Point2D;
@@ -92,7 +92,7 @@ impl RenderedGlyphKey {
pub struct ImageProperties {
pub descriptor: ImageDescriptor,
pub external_id: Option<ExternalImageId>,
pub tiling: Option<u16>,
pub tiling: Option<TileSize>,
}

#[derive(Debug, Copy, Clone, PartialEq)]
@@ -106,7 +106,7 @@ struct ImageResource {
data: ImageData,
descriptor: ImageDescriptor,
epoch: Epoch,
tiling: Option<u16>,
tiling: Option<TileSize>,
}

struct CachedImageInfo {
@@ -263,7 +263,7 @@ impl ResourceCache {
image_key: ImageKey,
descriptor: ImageDescriptor,
data: ImageData,
mut tiling: Option<u16>) {
mut tiling: Option<TileSize>) {
if descriptor.width > self.max_texture_size() || descriptor.height > self.max_texture_size() {
// We aren't going to be able to upload a texture this big, so tile it, even
// if tiling was not requested.
@@ -9,7 +9,7 @@ use std::cell::Cell;
use {ApiMsg, ColorF, DisplayListBuilder, Epoch, ImageDescriptor};
use {FontKey, IdNamespace, ImageKey, NativeFontHandle, PipelineId};
use {RenderApiSender, ResourceId, ScrollEventPhase, ScrollLayerState, ScrollLocation, ServoScrollRootId};
use {GlyphKey, GlyphDimensions, ImageData, WebGLContextId, WebGLCommand};
use {GlyphKey, GlyphDimensions, ImageData, WebGLContextId, WebGLCommand, TileSize};
use {DeviceIntSize, DynamicProperties, LayoutPoint, LayoutSize, WorldPoint, PropertyBindingKey, PropertyBindingId};
use VRCompositorCommand;
use ExternalEvent;
@@ -93,7 +93,7 @@ impl RenderApi {
key: ImageKey,
descriptor: ImageDescriptor,
data: ImageData,
tiling: Option<u16>) {
tiling: Option<TileSize>) {
let msg = ApiMsg::AddImage(key, descriptor, data, tiling);
self.api_sender.send(msg).unwrap();
}
@@ -24,14 +24,16 @@ pub enum RendererKind {
OSMesa,
}

pub type TileSize = u16;

#[derive(Clone, Deserialize, Serialize)]
pub enum ApiMsg {
AddRawFont(FontKey, Vec<u8>),
AddNativeFont(FontKey, NativeFontHandle),
/// Gets the glyph dimensions
GetGlyphDimensions(Vec<GlyphKey>, MsgSender<Vec<Option<GlyphDimensions>>>),
/// Adds an image from the resource cache.
AddImage(ImageKey, ImageDescriptor, ImageData, Option<u16>),
AddImage(ImageKey, ImageDescriptor, ImageData, Option<TileSize>),
/// Updates the the resource cache with the new image data.
UpdateImage(ImageKey, ImageDescriptor, Vec<u8>),
/// Drops an image from the resource cache.
@@ -368,27 +370,17 @@ pub struct ImageDescriptor {
}

impl ImageDescriptor {
pub fn new(width: u32, height: u32, format: ImageFormat) -> Self {
pub fn new(width: u32, height: u32, format: ImageFormat, is_opaque: bool) -> Self {
ImageDescriptor {
width: width,
height: height,
format: format,
stride: None,
offset: 0,
is_opaque: format == ImageFormat::RGB8,
is_opaque: is_opaque,
}
}

pub fn with_opaque_flag(mut self, is_opaque: bool) -> Self {
self.is_opaque = is_opaque;
return self;
}

pub fn with_stride(mut self, stride: u32) -> Self {
self.stride = Some(stride);
return self;
}

pub fn compute_stride(&self) -> u32 {
self.stride.unwrap_or(self.width * self.format.bytes_per_pixel().unwrap())
}
@@ -303,8 +303,10 @@ impl Wrench {
_ => panic!("We don't support whatever your crazy image type is, come on"),
};
let bytes = image.raw_pixels();
let descriptor = ImageDescriptor::new(image_dims.0, image_dims.1, format)
.with_opaque_flag(is_image_opaque(format, &bytes[..]));
let descriptor = ImageDescriptor::new(image_dims.0,
image_dims.1,
format,
is_image_opaque(format, &bytes[..]));
let data = ImageData::new(bytes);
(descriptor, data)
}
@@ -425,7 +427,7 @@ fn is_image_opaque(format: ImageFormat, bytes: &[u8]) -> bool {
}

fn generate_xy_gradient_image(w: u32, h: u32) -> (ImageDescriptor, ImageData) {
let mut pixels = Vec::with_capacity((w*h*4) as usize);
let mut pixels = Vec::with_capacity((w * h * 4) as usize);
for y in 0..h {
for x in 0..w {
let grid = if x % 100 < 3 || y % 100 < 3 { 0.9 } else { 1.0 };
@@ -437,30 +439,30 @@ fn generate_xy_gradient_image(w: u32, h: u32) -> (ImageDescriptor, ImageData) {
}

return (
ImageDescriptor::new(w, h, ImageFormat::RGBA8).with_opaque_flag(true),
ImageDescriptor::new(w, h, ImageFormat::RGBA8, true),
ImageData::new(pixels)
);
}

fn generate_solid_color_image(r: u8, g: u8, b: u8, a: u8, w: u32, h: u32) -> (ImageDescriptor, ImageData) {
let buf_size = (w*h*4) as usize;
let buf_size = (w * h * 4) as usize;
let mut pixels = Vec::with_capacity(buf_size);
// Unsafely filling the buffer is horrible. Unfortunately doing this idiomatically
// is terribly slow in debug builds to the point that reftests/image/very-big.yaml
// takes more than 20 seconds to run on a recent laptop.
unsafe {
pixels.set_len(buf_size);
let color: u32 = ::std::mem::transmute([b,g,r,a]);
let color: u32 = ::std::mem::transmute([b, g, r, a]);
let mut ptr: *mut u32 = ::std::mem::transmute(&mut pixels[0]);
let end = ptr.offset((w*h) as isize);
let end = ptr.offset((w * h) as isize);
while ptr < end {
*ptr = color;
ptr = ptr.offset(1);
}
}

return (
ImageDescriptor::new(w, h, ImageFormat::RGBA8).with_opaque_flag(a == 255),
ImageDescriptor::new(w, h, ImageFormat::RGBA8, a == 255),
ImageData::new(pixels)
);
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.