@@ -14,11 +14,12 @@ use std::collections::HashMap;
use std::io::{Cursor, Read};
use std::sync::{Arc, Mutex};
use std::sync::mpsc::Sender;
use texture_cache::{TextureCache, TextureCacheItemId};
use texture_cache::TextureCache;
use webrender_traits::{ApiMsg, AuxiliaryLists, BuiltDisplayList, IdNamespace};
use webrender_traits::{PipelineId, RenderNotifier, WebGLContextId};
use batch::new_id;
use device::TextureId;
use tiling::FrameBuilderConfig;
use offscreen_gl_context::{ColorAttachmentType, GLContext};
use offscreen_gl_context::{NativeGLContext, NativeGLContextHandle};

@@ -49,18 +50,16 @@ impl RenderBackend {
payload_tx: IpcBytesSender,
result_tx: Sender<ResultMsg>,
device_pixel_ratio: f32,
white_image_id: TextureCacheItemId,
dummy_mask_image_id: TextureCacheItemId,
texture_cache: TextureCache,
enable_aa: bool,
notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
webrender_context_handle: Option<NativeGLContextHandle>) -> RenderBackend {
webrender_context_handle: Option<NativeGLContextHandle>,
config: FrameBuilderConfig,
debug: bool) -> RenderBackend {
let mut thread_pool = scoped_threadpool::Pool::new(8);

let resource_cache = ResourceCache::new(&mut thread_pool,
texture_cache,
white_image_id,
dummy_mask_image_id,
device_pixel_ratio,
enable_aa);

@@ -73,7 +72,7 @@ impl RenderBackend {
device_pixel_ratio: device_pixel_ratio,
resource_cache: resource_cache,
scene: Scene::new(),
frame: Frame::new(),
frame: Frame::new(debug, config),
next_namespace_id: IdNamespace(1),
notifier: notifier,
webrender_context_handle: webrender_context_handle,
@@ -212,6 +211,7 @@ impl RenderBackend {
ApiMsg::Scroll(delta, cursor, move_phase) => {
let frame = profile_counters.total_time.profile(|| {
if self.frame.scroll(delta, cursor, move_phase) {
self.build_scene();
Some(self.render())
} else {
None
@@ -229,6 +229,7 @@ impl RenderBackend {
ApiMsg::TickScrollingBounce => {
let frame = profile_counters.total_time.profile(|| {
self.frame.tick_scrolling_bounce_animations();
self.build_scene();
self.render()
});

@@ -237,7 +238,7 @@ impl RenderBackend {
ApiMsg::TranslatePointToLayerSpace(point, tx) => {
// First, find the specific layer that contains the point.
let point = point / self.device_pixel_ratio;
if let (Some(root_pipeline_id), Some(root_scroll_layer_id)) =
if let (Some(..), Some(root_scroll_layer_id)) =
(self.scene.root_pipeline_id,
self.frame.root_scroll_layer_id) {
if let Some(scroll_layer_id) =
@@ -388,8 +389,7 @@ impl RenderBackend {
fn publish_frame(&mut self,
frame: RendererFrame,
profile_counters: &mut BackendProfileCounters) {
let pending_updates = self.frame.pending_updates();
let msg = ResultMsg::NewFrame(frame, pending_updates, profile_counters.clone());
let msg = ResultMsg::NewFrame(frame, profile_counters.clone());
self.result_tx.send(msg).unwrap();
profile_counters.reset();
}

Large diffs are not rendered by default.

@@ -129,16 +129,11 @@ pub struct ResourceCache {
texture_cache: TextureCache,

pending_raster_jobs: Vec<GlyphRasterJob>,

white_image_id: TextureCacheItemId,
dummy_mask_image_id: TextureCacheItemId,
}

impl ResourceCache {
pub fn new(thread_pool: &mut scoped_threadpool::Pool,
texture_cache: TextureCache,
white_image_id: TextureCacheItemId,
dummy_mask_image_id: TextureCacheItemId,
device_pixel_ratio: f32,
enable_aa: bool) -> ResourceCache {

@@ -167,8 +162,6 @@ impl ResourceCache {
texture_cache: texture_cache,
pending_raster_jobs: Vec::new(),
device_pixel_ratio: device_pixel_ratio,
white_image_id: white_image_id,
dummy_mask_image_id: dummy_mask_image_id,
enable_aa: enable_aa,
}
}
@@ -307,11 +300,9 @@ impl ResourceCache {
}

pub fn raster_pending_glyphs(&mut self,
thread_pool: &mut scoped_threadpool::Pool,
frame_id: FrameId) {
// Run raster jobs in parallel
run_raster_jobs(thread_pool,
&mut self.pending_raster_jobs,
run_raster_jobs(&mut self.pending_raster_jobs,
&self.font_templates,
self.device_pixel_ratio,
self.enable_aa);
@@ -366,44 +357,14 @@ impl ResourceCache {
self.draw_lists.get(draw_list_id)
}

pub fn get_draw_list_mut(&mut self, draw_list_id: DrawListId) -> &mut DrawList {
self.draw_lists.get_mut(draw_list_id)
}

pub fn remove_draw_list(&mut self, draw_list_id: DrawListId) {
self.draw_lists.free(draw_list_id);
}

pub fn allocate_render_target(&mut self,
width: u32,
height: u32,
format: ImageFormat,
frame_id: FrameId)
-> TextureId {
self.texture_cache.allocate_render_target(width,
height,
format,
frame_id)
}

pub fn free_old_render_targets(&mut self) {
self.texture_cache.free_old_render_targets()
}

pub fn pending_updates(&mut self) -> TextureUpdateList {
self.texture_cache.pending_updates()
}

#[inline]
pub fn get_dummy_mask_image(&self) -> &TextureCacheItem {
self.texture_cache.get(self.dummy_mask_image_id)
}

#[inline]
pub fn get_dummy_color_image(&self) -> &TextureCacheItem {
self.texture_cache.get(self.white_image_id)
}

#[inline]
pub fn get_glyph(&self, glyph_key: &GlyphKey, frame_id: FrameId) -> Option<&TextureCacheItem> {
let image_id = self.cached_glyphs.get(glyph_key, frame_id);
@@ -420,62 +381,47 @@ impl ResourceCache {
self.texture_cache.get(image_info.texture_cache_id)
}

#[inline]
pub fn get_raster(&self, raster_item: &RasterItem, frame_id: FrameId) -> &TextureCacheItem {
let image_id = self.cached_rasters.get(raster_item, frame_id);
self.texture_cache.get(*image_id)
}

#[inline]
pub fn get_webgl_texture(&self, context_id: &WebGLContextId) -> TextureId {
self.webgl_textures.get(context_id).unwrap().clone()
}

pub fn device_pixel_ratio(&self) -> f32 {
self.device_pixel_ratio
}

pub fn expire_old_resources(&mut self, frame_id: FrameId) {
self.cached_glyphs.expire_old_resources(&mut self.texture_cache, frame_id);
self.cached_rasters.expire_old_resources(&mut self.texture_cache, frame_id);
self.cached_images.expire_old_resources(&mut self.texture_cache, frame_id);
}
}

fn run_raster_jobs(thread_pool: &mut scoped_threadpool::Pool,
pending_raster_jobs: &mut Vec<GlyphRasterJob>,
fn run_raster_jobs(pending_raster_jobs: &mut Vec<GlyphRasterJob>,
font_templates: &HashMap<FontKey, FontTemplate, BuildHasherDefault<FnvHasher>>,
device_pixel_ratio: f32,
enable_aa: bool) {
if pending_raster_jobs.is_empty() {
return
}

// Run raster jobs in parallel
thread_pool.scoped(|scope| {
for job in pending_raster_jobs {
scope.execute(|| {
let font_template = &font_templates[&job.glyph_key.font_key];
FONT_CONTEXT.with(move |font_context| {
let mut font_context = font_context.borrow_mut();
match *font_template {
FontTemplate::Raw(ref bytes) => {
font_context.add_raw_font(&job.glyph_key.font_key, &**bytes);
}
FontTemplate::Native(ref native_font_handle) => {
font_context.add_native_font(&job.glyph_key.font_key,
(*native_font_handle).clone());
}
}
job.result = font_context.get_glyph(job.glyph_key.font_key,
job.glyph_key.size,
job.glyph_key.index,
device_pixel_ratio,
enable_aa);
});
});
}
});
// TODO(gw): Run raster jobs in parallel again
for job in pending_raster_jobs {
let font_template = &font_templates[&job.glyph_key.font_key];
FONT_CONTEXT.with(move |font_context| {
let mut font_context = font_context.borrow_mut();
match *font_template {
FontTemplate::Raw(ref bytes) => {
font_context.add_raw_font(&job.glyph_key.font_key, &**bytes);
}
FontTemplate::Native(ref native_font_handle) => {
font_context.add_native_font(&job.glyph_key.font_key,
(*native_font_handle).clone());
}
}
job.result = font_context.get_glyph(job.glyph_key.font_key,
job.glyph_key.size,
job.glyph_key.index,
device_pixel_ratio,
enable_aa);
});
}
}

pub trait Resource {

Large diffs are not rendered by default.

@@ -101,5 +101,3 @@ fn next(cur: f32, prev: f32, dest: f32, stiffness: f32, damping: f32) -> f32 {
fn is_resting(cur: f32, prev: f32, dest: f32) -> bool {
(cur - prev).abs() < EPSILON && (cur - dest).abs() < EPSILON
}


This file was deleted.

@@ -10,14 +10,13 @@ use frame::FrameId;
use freelist::{FreeList, FreeListItem, FreeListItemId};
use internal_types::{TextureUpdate, TextureUpdateOp, TextureUpdateDetails};
use internal_types::{RasterItem, RenderTargetMode, TextureImage, TextureUpdateList};
use internal_types::{RectUv, DevicePixel, BasicRotationAngle};
use internal_types::{RectUv, DevicePixel};
use std::cmp::{self, Ordering};
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::hash::BuildHasherDefault;
use std::mem;
use std::slice::Iter;
use tessellator::BorderCornerTessellation;
use time;
use util;
use webrender_traits::ImageFormat;
@@ -28,9 +27,6 @@ const MAX_BYTES_PER_TEXTURE: u32 = 1024 * 1024 * 256; // 256MB
/// The number of RGBA pixels we're allowed to use for a texture.
const MAX_RGBA_PIXELS_PER_TEXTURE: u32 = MAX_BYTES_PER_TEXTURE / 4;

/// The total number of RGBA pixels we're allowed to use for our render targets.
const MAX_RGBA_PIXELS_IN_CACHED_RENDER_TARGETS: u32 = 4096 * 4096 * 2;

/// The desired initial size of each texture, in pixels.
const INITIAL_TEXTURE_SIZE: u32 = 1024;

@@ -317,7 +313,7 @@ impl TexturePage {
self.dirty = changed
}

fn clear(&mut self) {
pub fn clear(&mut self) {
self.free_list = FreeRectList::new();
self.free_list.push(&Rect::new(Point2D::new(0, 0),
Size2D::new(self.texture_size, self.texture_size)));
@@ -460,6 +456,8 @@ pub struct TextureCacheItem {
// bilinear filtering / texture bleeding purposes.
pub allocated_rect: Rect<u32>,
pub requested_rect: Rect<u32>,

pub is_opaque: bool,
}

// Structure squat the width/height fields to maintain the free list information :)
@@ -493,7 +491,8 @@ impl TextureCacheItem {
user_x0: i32, user_y0: i32,
allocated_rect: Rect<u32>,
requested_rect: Rect<u32>,
texture_size: &Size2D<u32>)
texture_size: &Size2D<u32>,
is_opaque: bool)
-> TextureCacheItem {
TextureCacheItem {
texture_id: texture_id,
@@ -514,6 +513,7 @@ impl TextureCacheItem {
},
allocated_rect: allocated_rect,
requested_rect: requested_rect,
is_opaque: is_opaque,
}
}

@@ -590,8 +590,6 @@ pub struct TextureCache {
// Vec<FreeTextureLevel>,
// BuildHasherDefault<FnvHasher>>,
items: FreeList<TextureCacheItem>,
cached_render_targets: Vec<CachedRenderTarget>,
total_pixel_count_of_cached_render_targets: u32,
arena: TextureCacheArena,
pending_updates: TextureUpdateList,
}
@@ -614,9 +612,6 @@ impl TextureCache {
free_texture_ids: free_texture_ids,
free_texture_levels: HashMap::with_hasher(Default::default()),
alternate_free_texture_levels: HashMap::with_hasher(Default::default()),
//render_target_free_texture_levels: HashMap::with_hasher(Default::default()),
cached_render_targets: vec![],
total_pixel_count_of_cached_render_targets: 0,
items: FreeList::new(),
pending_updates: TextureUpdateList::new(),
arena: TextureCacheArena::new(),
@@ -646,105 +641,11 @@ impl TextureCache {
requested_rect: Rect::zero(),
texture_size: Size2D::zero(),
texture_id: TextureId::invalid(),
is_opaque: false,
};
self.items.insert(new_item)
}

/*
pub fn free(&mut self,
_texture_id: TextureId,
_uv: &Rect<u32>,
_kind: TextureCacheItemKind) {
panic!("can't free texture items yet!");
}
*/

pub fn allocate_render_target(&mut self,
width: u32,
height: u32,
format: ImageFormat,
frame_id: FrameId)
-> TextureId {
let mut cached_render_target_index = None;
for (i, cached_render_target) in self.cached_render_targets.iter().enumerate() {
if cached_render_target.width == width &&
cached_render_target.height == height &&
cached_render_target.format == format &&
cached_render_target.frame_id != frame_id {
cached_render_target_index = Some(i);
break
}
}
if let Some(cached_render_target_index) = cached_render_target_index {
// Push to the end to mark as recently used.
let mut cached_render_target = self.cached_render_targets
.remove(cached_render_target_index);
cached_render_target.frame_id = frame_id;
self.cached_render_targets.push(cached_render_target);
return cached_render_target.texture_id
}

self.total_pixel_count_of_cached_render_targets += width * height;

let texture_id = self.free_texture_ids
.pop()
.expect("TODO: Handle running out of texture IDs!");
let op = TextureUpdateOp::Create(width,
height,
format,
TextureFilter::Linear,
RenderTargetMode::RenderTarget,
None);
let update_op = TextureUpdate {
id: texture_id,
op: op,
};
self.pending_updates.push(update_op);

self.cached_render_targets.push(CachedRenderTarget {
texture_id: texture_id,
width: width,
height: height,
format: format,
frame_id: frame_id,
});

texture_id
}

pub fn free_old_render_targets(&mut self) {
if self.total_pixel_count_of_cached_render_targets <=
MAX_RGBA_PIXELS_IN_CACHED_RENDER_TARGETS {
return
}

let mut cached_render_targets_to_destroy = 0;
for cached_render_target in &self.cached_render_targets {
let op = TextureUpdateOp::Update(0, 0, 0, 0,
TextureUpdateDetails::Blit(Vec::new()));
let update_op = TextureUpdate {
id: cached_render_target.texture_id,
op: op,
};
self.pending_updates.push(update_op);
self.free_texture_ids.push(cached_render_target.texture_id);

cached_render_targets_to_destroy += 1;

self.total_pixel_count_of_cached_render_targets -= cached_render_target.width *
cached_render_target.height;
if self.total_pixel_count_of_cached_render_targets <
MAX_RGBA_PIXELS_IN_CACHED_RENDER_TARGETS {
break
}
}

self.cached_render_targets = self.cached_render_targets[cached_render_targets_to_destroy..]
.iter()
.cloned()
.collect()
}

pub fn allocate(&mut self,
image_id: TextureCacheItemId,
user_x0: i32,
@@ -754,7 +655,8 @@ impl TextureCache {
format: ImageFormat,
kind: TextureCacheItemKind,
border_type: BorderType,
filter: TextureFilter)
filter: TextureFilter,
is_opaque: bool)
-> AllocationResult {
let requested_size = Size2D::new(requested_width, requested_height);

@@ -773,7 +675,8 @@ impl TextureCache {
user_x0, user_y0,
Rect::new(Point2D::zero(), requested_size),
Rect::new(Point2D::zero(), requested_size),
&requested_size);
&requested_size,
is_opaque);
*self.items.get_mut(image_id) = cache_item;

return AllocationResult {
@@ -835,7 +738,8 @@ impl TextureCache {
user_x0, user_y0,
allocated_rect,
requested_rect,
&Size2D::new(page.texture_size, page.texture_size));
&Size2D::new(page.texture_size, page.texture_size),
is_opaque);
*self.items.get_mut(image_id) = cache_item;

return AllocationResult {
@@ -900,56 +804,7 @@ impl TextureCache {
item: &RasterItem,
_device_pixel_ratio: f32) {
let update_op = match item {
&RasterItem::BorderRadius(ref op) => {
let rect = Rect::new(Point2D::zero(),
Size2D::new(op.outer_radius_x.as_f32(),
op.outer_radius_y.as_f32()));
let tessellated_rect = match op.index {
Some(index) => {
rect.tessellate_border_corner(
&Size2D::new(op.outer_radius_x.as_f32(),
op.outer_radius_y.as_f32()),
&Size2D::new(op.inner_radius_x.as_f32(),
op.inner_radius_y.as_f32()),
1.0,
BasicRotationAngle::Upright,
index)
}
None => rect,
};

let width = tessellated_rect.size.width.ceil() as u32;
let height = tessellated_rect.size.height.ceil() as u32;

let allocation = self.allocate(image_id,
0,
0,
width,
height,
op.image_format,
TextureCacheItemKind::Standard,
BorderType::SinglePixel,
TextureFilter::Linear);

assert!(allocation.kind == AllocationKind::TexturePage); // TODO: Handle large border radii not fitting in texture cache page

TextureUpdate {
id: allocation.item.texture_id,
op: TextureUpdateOp::Update(allocation.item.requested_rect.origin.x,
allocation.item.requested_rect.origin.y,
width,
height,
TextureUpdateDetails::BorderRadius(
op.outer_radius_x,
op.outer_radius_y,
op.inner_radius_x,
op.inner_radius_y,
op.index,
op.inverted,
BorderType::SinglePixel)),
}
}
&RasterItem::BoxShadow(ref op) => {
&RasterItem::_BoxShadow(ref op) => {
let allocation = self.allocate(image_id,
0,
0,
@@ -958,7 +813,8 @@ impl TextureCache {
ImageFormat::RGBA8,
TextureCacheItemKind::Standard,
BorderType::SinglePixel,
TextureFilter::Linear);
TextureFilter::Linear,
false);

// TODO(pcwalton): Handle large box shadows not fitting in texture cache page.
assert!(allocation.kind == AllocationKind::TexturePage);
@@ -1033,6 +889,23 @@ impl TextureCache {
insert_op: TextureInsertOp,
border_type: BorderType) {

let is_opaque = match (&insert_op, format) {
(&TextureInsertOp::Blit(ref bytes), ImageFormat::RGBA8) => {
let mut is_opaque = true;
for i in (0..bytes.len()).step_by(4) {
if bytes[i + 3] != 255 {
is_opaque = false;
break;
}
}
is_opaque
}
(&TextureInsertOp::Blit(..), ImageFormat::RGB8) => true,
(&TextureInsertOp::Blit(..), ImageFormat::A8) => false,
(&TextureInsertOp::Blit(..), ImageFormat::Invalid) => unreachable!(),
(&TextureInsertOp::Blur(..), _) => false,
};

let result = self.allocate(image_id,
x0,
y0,
@@ -1041,7 +914,8 @@ impl TextureCache {
format,
TextureCacheItemKind::Standard,
border_type,
filter);
filter,
is_opaque);

let op = match (result.kind, insert_op) {
(AllocationKind::TexturePage, TextureInsertOp::Blit(bytes)) => {
@@ -1137,14 +1011,16 @@ impl TextureCache {
ImageFormat::RGBA8,
TextureCacheItemKind::Standard,
BorderType::SinglePixel,
TextureFilter::Linear);
TextureFilter::Linear,
false);
self.allocate(horizontal_blur_image_id,
0, 0,
width, height,
ImageFormat::RGBA8,
TextureCacheItemKind::Alternate,
BorderType::SinglePixel,
TextureFilter::Linear);
TextureFilter::Linear,
false);
let unblurred_glyph_item = self.get(unblurred_glyph_image_id);
let horizontal_blur_item = self.get(horizontal_blur_image_id);
TextureUpdateOp::Update(

Large diffs are not rendered by default.

@@ -3,10 +3,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use euclid::{Matrix4D, Point2D, Point4D, Rect, Size2D};
use internal_types::{RectColors};
use internal_types::DevicePixel;
use num_traits::Zero;
use time::precise_time_ns;
use webrender_traits::ColorF;

#[allow(dead_code)]
pub struct ProfileScope {
@@ -26,12 +25,9 @@ impl ProfileScope {

impl Drop for ProfileScope {
fn drop(&mut self) {
/*
if self.name.chars().next() != Some(' ') {
let t1 = precise_time_ns();
let ms = (t1 - self.t0) as f64 / 1000000f64;
println!("{} {}", self.name, ms);
}*/
}
}

@@ -88,6 +84,8 @@ impl MatrixHelpers for Matrix4D<f32> {
pub trait RectHelpers {
fn from_points(a: &Point2D<f32>, b: &Point2D<f32>, c: &Point2D<f32>, d: &Point2D<f32>) -> Self;
fn contains_rect(&self, other: &Rect<f32>) -> bool;
fn from_floats(x0: f32, y0: f32, x1: f32, y1: f32) -> Rect<f32>;
fn is_well_formed_and_nonempty(&self) -> bool;
}

impl RectHelpers for Rect<f32> {
@@ -119,78 +117,14 @@ impl RectHelpers for Rect<f32> {
self.max_x() >= other.max_x() &&
self.max_y() >= other.max_y()
}
}

pub fn lerp(a: f32, b: f32, t: f32) -> f32 {
(b - a) * t + a
}

pub fn bilerp<V>(point: &Point2D<f32>, quad: &Rect<f32>, varyings: &V) -> V::Element
where V: RectVaryings, V::Element: VaryingElement {
let (x1, y1, x2, y2) = (quad.origin.x, quad.origin.y, quad.max_x(), quad.max_y());
let top_left = varyings.top_left().scale((x2 - point.x) * (y2 - point.y));
let top_right = varyings.top_right().scale((point.x - x1) * (y2 - point.y));
let bottom_left = varyings.bottom_left().scale((x2 - point.x) * (point.y - y1));
let bottom_right = varyings.bottom_right().scale((point.x - x1) * (point.y - y1));
let sum = VaryingElement::accumulate(&[top_left, top_right, bottom_left, bottom_right]);
sum.scale(1.0 / ((x2 - x1) * (y2 - y1)))
}

pub fn bilerp_rect<V>(clipped_rect: &Rect<f32>, quad: &Rect<f32>, varyings: &V) -> V
where V: RectVaryings, V::Element: VaryingElement {
let top_left = bilerp(&clipped_rect.origin, quad, varyings);
let top_right = bilerp(&clipped_rect.top_right(), quad, varyings);
let bottom_right = bilerp(&clipped_rect.bottom_right(), quad, varyings);
let bottom_left = bilerp(&clipped_rect.bottom_left(), quad, varyings);
V::from_elements(&[top_left, top_right, bottom_right, bottom_left])
}

pub trait RectVaryings {
type Element;
fn top_left(&self) -> Self::Element;
fn top_right(&self) -> Self::Element;
fn bottom_right(&self) -> Self::Element;
fn bottom_left(&self) -> Self::Element;
fn from_elements(elements: &[Self::Element; 4]) -> Self;
}

impl RectVaryings for RectColors {
type Element = ColorF;
fn top_left(&self) -> ColorF { self.top_left }
fn top_right(&self) -> ColorF { self.top_right }
fn bottom_right(&self) -> ColorF { self.bottom_right }
fn bottom_left(&self) -> ColorF { self.bottom_left }
fn from_elements(elements: &[ColorF; 4]) -> RectColors {
RectColors {
top_left: elements[0],
top_right: elements[1],
bottom_right: elements[2],
bottom_left: elements[3],
}
fn from_floats(x0: f32, y0: f32, x1: f32, y1: f32) -> Rect<f32> {
Rect::new(Point2D::new(x0, y0),
Size2D::new(x1 - x0, y1 - y0))
}
}

pub trait VaryingElement : Sized {
fn scale(&self, factor: f32) -> Self;
fn accumulate(values: &[Self; 4]) -> Self;
}

impl VaryingElement for ColorF {
fn scale(&self, factor: f32) -> ColorF {
ColorF {
r: self.r * factor,
g: self.g * factor,
b: self.b * factor,
a: self.a * factor,
}
}
fn accumulate(values: &[ColorF; 4]) -> ColorF {
ColorF {
r: values[0].r + values[1].r + values[2].r + values[3].r,
g: values[0].g + values[1].g + values[2].g + values[3].g,
b: values[0].b + values[1].b + values[2].b + values[3].b,
a: values[0].a + values[1].a + values[2].a + values[3].a,
}
fn is_well_formed_and_nonempty(&self) -> bool {
self.size.width > 0.0 && self.size.height > 0.0
}
}

@@ -200,59 +134,65 @@ pub fn rect_is_empty<N:PartialEq + Zero>(rect: &Rect<N>) -> bool {
rect.size.width == Zero::zero() || rect.size.height == Zero::zero()
}

/// Returns true if the rectangle's width and height are both strictly positive and false
/// otherwise.
pub fn rect_is_well_formed_and_nonempty(rect: &Rect<f32>) -> bool {
rect.size.width > 0.0 && rect.size.height > 0.0
#[inline]
pub fn rect_from_points(x0: DevicePixel,
y0: DevicePixel,
x1: DevicePixel,
y1: DevicePixel) -> Rect<DevicePixel> {
Rect::new(Point2D::new(x0, y0),
Size2D::new(x1 - x0, y1 - y0))
}

/// Multiplies all non-alpha components of a color by the given value.
pub fn scale_color(color: &ColorF, factor: f32) -> ColorF {
ColorF {
r: color.r * factor,
g: color.g * factor,
b: color.b * factor,
a: color.a,
}
#[inline]
pub fn rect_from_points_f(x0: f32,
y0: f32,
x1: f32,
y1: f32) -> Rect<f32> {
Rect::new(Point2D::new(x0, y0),
Size2D::new(x1 - x0, y1 - y0))
}

/// Subdivides a rectangle into quadrants formed by a point. The quadrants are returned in the
/// order of: top left, top right, bottom right, and bottom left.
pub fn subdivide_rect_into_quadrants(rect: &Rect<f32>, point: &Point2D<f32>) -> (Rect<f32>, Rect<f32>, Rect<f32>, Rect<f32>) {
let point = Point2D::new(clamp(point.x, rect.origin.x, rect.max_x()),
clamp(point.y, rect.origin.y, rect.max_y()));
let tl_rect = Rect::new(rect.origin,
Size2D::new(point.x - rect.origin.x, point.y - rect.origin.y));
let tr_rect = Rect::new(Point2D::new(point.x, rect.origin.y),
Size2D::new(rect.max_x() - point.x, point.y - rect.origin.y));
let br_rect = Rect::new(point,
Size2D::new(rect.max_x() - point.x, rect.max_y() - point.y));
let bl_rect = Rect::new(Point2D::new(rect.origin.x, point.y),
Size2D::new(point.x - rect.origin.x, rect.max_y() - point.y));
return (tl_rect, tr_rect, br_rect, bl_rect);

fn clamp(x: f32, lo: f32, hi: f32) -> f32 {
if x < lo {
lo
} else if x > hi {
hi
} else {
x
}
}
pub fn lerp(a: f32, b: f32, t: f32) -> f32 {
(b - a) * t + a
}

/// Returns the center point of the given rect.
pub fn rect_center(rect: &Rect<f32>) -> Point2D<f32> {
Point2D::new(rect.origin.x + rect.size.width / 2.0, rect.origin.y + rect.size.height / 2.0)
}
pub fn subtract_rect(rect: &Rect<f32>,
other: &Rect<f32>,
results: &mut Vec<Rect<f32>>) {
results.clear();

pub fn distance(a: &Point2D<f32>, b: &Point2D<f32>) -> f32 {
let (x, y) = (b.x - a.x, b.y - a.y);
(x * x + y * y).sqrt()
}
let int = rect.intersection(other);
match int {
Some(int) => {
let rx0 = rect.origin.x;
let ry0 = rect.origin.y;
let rx1 = rx0 + rect.size.width;
let ry1 = ry0 + rect.size.height;

pub fn lerp_points(a: &Point2D<f32>, b: &Point2D<f32>, t: f32) -> Point2D<f32> {
Point2D::new(lerp(a.x, b.x, t), lerp(a.y, b.y, t))
}
let ox0 = int.origin.x;
let oy0 = int.origin.y;
let ox1 = ox0 + int.size.width;
let oy1 = oy0 + int.size.height;

let r = rect_from_points_f(rx0, ry0, ox0, ry1);
if r.size.width > 0.0 && r.size.height > 0.0 {
results.push(r);
}
let r = rect_from_points_f(ox0, ry0, ox1, oy0);
if r.size.width > 0.0 && r.size.height > 0.0 {
results.push(r);
}
let r = rect_from_points_f(ox0, oy1, ox1, ry1);
if r.size.width > 0.0 && r.size.height > 0.0 {
results.push(r);
}
let r = rect_from_points_f(ox1, ry0, rx1, ry1);
if r.size.width > 0.0 && r.size.height > 0.0 {
results.push(r);
}
}
None => {
results.push(*rect);
}
}
}