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
Merged
Changes from 1 commit
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

Add image tiling support in wrench and implement some simple tests.

  • Loading branch information
nical committed Feb 23, 2017
commit b0f1595d298e2c2445add919a80727576bbf6e22
@@ -0,0 +1,2 @@
== tile-size.yaml tile-size-ref.yaml
== very-big.yaml very-big-ref.yaml
@@ -0,0 +1,14 @@
root:
items:
- image: xy_gradient(512, 512)
bounds: 0 0 512 512
stretch_size: 512 512
- image: xy_gradient(512, 512)
bounds: 512 0 512 512
stretch_size: 512 512
- image: xy_gradient(512, 512)
bounds: 0 512 512 512
stretch_size: 512 512
- image: xy_gradient(512, 512)
bounds: 512 512 512 512
stretch_size: 512 512
@@ -0,0 +1,19 @@
root:
items:
- image: xy_gradient(512, 512)
bounds: 0 0 512 512
stretch_size: 512 512
tile-size: 64
- image: xy_gradient(512, 512)
bounds: 512 0 512 512
stretch_size: 512 512
tile-size: 128
- image: xy_gradient(512, 512)
bounds: 0 512 512 512
stretch_size: 512 512
tile-size: 256
# tile size bigger than the image itself
- image: xy_gradient(512, 512)
bounds: 512 512 512 512
stretch_size: 512 512
tile-size: 4096
@@ -0,0 +1,5 @@
root:
items:
- type: rect
bounds: 0 0 500 500
color: red
@@ -0,0 +1,5 @@
root:
items:
- image: solid_color(255, 0, 0, 255, 100000, 1000)
bounds: 0 0 500 500
stretch_size: 1000000 1000
@@ -3,3 +3,4 @@ include mask/reftest.list
include scrolling/reftest.list
include filters/reftest.list
include boxshadow/reftest.list
include image/reftest.list
@@ -212,7 +212,7 @@ impl webrender::ApiRecordingReceiver for JsonFrameWriter {
self.fonts.insert(*key, CachedFont::Native(native_font_handle.clone()));
}

&ApiMsg::AddImage(ref key, ref descriptor, ref data) => {
&ApiMsg::AddImage(ref key, ref descriptor, ref data, _) => {
let stride = descriptor.stride.unwrap_or(
descriptor.width * descriptor.format.bytes_per_pixel().unwrap()
);
@@ -37,7 +37,7 @@ pub fn parse_function(s: &str) -> (&str, Vec<&str>) {

let mut end = p.start;
while let Some(k) = p.o {
if !k.1.is_alphabetic() {
if !k.1.is_alphabetic() && k.1 != '_' {
break;
}
end = k.0 + k.1.len_utf8();
@@ -15,9 +15,9 @@ use glutin::WindowProxy;
use image;
use image::GenericImage;
use json_frame_writer::JsonFrameWriter;
use parse_function::parse_function;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use time;
use webrender;
use webrender_traits::*;
@@ -124,7 +124,7 @@ pub struct Wrench {

window_title_to_set: Option<String>,

image_map: HashMap<PathBuf, (ImageKey, LayoutSize)>,
image_map: HashMap<(PathBuf, Option<i64>), (ImageKey, LayoutSize)>,

gl_renderer: String,
gl_version: String,
@@ -287,35 +287,57 @@ impl Wrench {
(key, None)
}

pub fn add_or_get_image(&mut self, file: &Path) -> (ImageKey, LayoutSize) {
let key = file.to_owned();
pub fn add_or_get_image(&mut self, file: &Path, tiling: Option<i64>) -> (ImageKey, LayoutSize) {
let key = (file.to_owned(), tiling);
if let Some(k) = self.image_map.get(&key) {
return *k
}

let image = image::open(file).unwrap();
let image_dims = image.dimensions();
let format = match image {
image::ImageLuma8(_) => ImageFormat::A8,
image::ImageRgb8(_) => ImageFormat::RGB8,
image::ImageRgba8(_) => ImageFormat::RGBA8,
_ => panic!("We don't support whatever your crazy image type is, come on"),
let (descriptor, image_data) = match image::open(file) {
Ok(image) => {
let image_dims = image.dimensions();
let format = match image {
image::ImageLuma8(_) => ImageFormat::A8,
image::ImageRgb8(_) => ImageFormat::RGB8,
image::ImageRgba8(_) => ImageFormat::RGBA8,
_ => 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 data = ImageData::new(bytes);
(descriptor, data)
}
_ => {
// This is a hack but it is convenient when generating test cases and avoids
// bloating the repository.
match parse_function(file.components().last().unwrap().as_os_str().to_str().unwrap()) {
("xy_gradient", args) => {
generate_xy_gradient_image(
args.get(0).unwrap_or(&"1000").parse::<u32>().unwrap(),
args.get(1).unwrap_or(&"1000").parse::<u32>().unwrap()
)
}
("solid_color", args) => {
generate_solid_color_image(
args.get(0).unwrap_or(&"255").parse::<u8>().unwrap(),
args.get(1).unwrap_or(&"255").parse::<u8>().unwrap(),
args.get(2).unwrap_or(&"255").parse::<u8>().unwrap(),
args.get(3).unwrap_or(&"255").parse::<u8>().unwrap(),
args.get(4).unwrap_or(&"1000").parse::<u32>().unwrap(),
args.get(5).unwrap_or(&"1000").parse::<u32>().unwrap()
)
}
_ => {
panic!("Failed to load image {:?}", file.to_str());
}
}
}
};
let bytes = image.raw_pixels();
let tiling = tiling.map(|tile_size|{ tile_size as u16 });
let image_key = self.api.generate_image_key();
self.api.add_image(
image_key,
ImageDescriptor {
width: image_dims.0,
height: image_dims.1,
format: format,
stride: None,
offset: 0,
is_opaque: is_image_opaque(format, &bytes[..]),
},
ImageData::Raw(Arc::new(bytes)));

let val = (image_key, LayoutSize::new(image_dims.0 as f32, image_dims.1 as f32));
self.api.add_image(image_key, descriptor, image_data, tiling);
let val = (image_key, LayoutSize::new(descriptor.width as f32, descriptor.height as f32));
self.image_map.insert(key, val);
val
}
@@ -401,3 +423,44 @@ fn is_image_opaque(format: ImageFormat, bytes: &[u8]) -> bool {
ImageFormat::Invalid | ImageFormat::RGBAF32 => unreachable!(),
}
}

fn generate_xy_gradient_image(w: u32, h: u32) -> (ImageDescriptor, ImageData) {
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 };
pixels.push((y as f32 / h as f32 * 255.0 * grid) as u8);
pixels.push(0);
pixels.push((x as f32 / w as f32 * 255.0 * grid) as u8);
pixels.push(255);
}
}

return (
ImageDescriptor::new(w, h, ImageFormat::RGBA8).with_opaque_flag(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 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 mut ptr: *mut u32 = ::std::mem::transmute(&mut pixels[0]);
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),
ImageData::new(pixels)
);
}
@@ -170,7 +170,7 @@ impl YamlFrameReader {
let image_mask = if item["image_mask"].as_hash().is_some() {
let image_mask = &item["image_mask"];
let (image_key, image_dims) =
wrench.add_or_get_image(&self.rsrc_path(&image_mask["image"]));
wrench.add_or_get_image(&self.rsrc_path(&image_mask["image"]), None);
let image_rect =
image_mask["rect"].as_rect().unwrap_or(LayoutRect::new(LayoutPoint::zero(),
image_dims));
@@ -323,7 +323,8 @@ impl YamlFrameReader {

fn handle_image(&mut self, wrench: &mut Wrench, clip_region: &ClipRegion, item: &Yaml) {
let filename = &item[if item["type"].is_badvalue() { "image" } else { "src" }];
let (image_key, image_dims) = wrench.add_or_get_image(&self.rsrc_path(filename));
let tiling = item["tile-size"].as_i64();
let (image_key, image_dims) = wrench.add_or_get_image(&self.rsrc_path(filename), tiling);

let bounds_raws = item["bounds"].as_vec_f32().unwrap();
let bounds = if bounds_raws.len() == 2 {
@@ -85,7 +85,6 @@ fn matrix4d_node<U1, U2>(parent: &mut Table, key: &str, value: &TypedMatrix4D<f3
f32_vec_node(parent, key, &value.to_row_major_array());
}

#[cfg(target_os = "windows")]
fn u32_node(parent: &mut Table, key: &str, value: u32) {
yaml_node(parent, key, Yaml::Integer(value as i64));
}
@@ -228,6 +227,7 @@ struct CachedImage {
format: ImageFormat,
bytes: Option<Vec<u8>>,
path: Option<PathBuf>,
tiling: Option<u16>,
}

pub struct YamlFrameWriter {
@@ -546,6 +546,9 @@ impl YamlFrameWriter {
if let Some(path) = self.path_for_image(&item.image_key) {
path_node(&mut v, "image", &path);
}
if let Some(&CachedImage { tiling: Some(tile_size), .. }) = self.images.get(&item.image_key) {
u32_node(&mut v, "tile-size", tile_size as u32);
}
size_node(&mut v, "strech", &item.stretch_size);
size_node(&mut v, "spacing", &item.tile_spacing);
match item.image_rendering {
@@ -694,7 +697,7 @@ impl webrender::ApiRecordingReceiver for YamlFrameWriterReceiver {
self.frame_writer.fonts.insert(*key, CachedFont::Native(native_font_handle.clone()));
}

&ApiMsg::AddImage(ref key, ref descriptor, ref data) => {
&ApiMsg::AddImage(ref key, ref descriptor, ref data, ref tiling) => {
let stride = descriptor.stride.unwrap_or(
descriptor.width * descriptor.format.bytes_per_pixel().unwrap()
);
@@ -711,6 +714,7 @@ impl webrender::ApiRecordingReceiver for YamlFrameWriterReceiver {
format: descriptor.format,
bytes: Some(bytes),
path: None,
tiling: *tiling,
});
}

ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.