diff --git a/gfx/doc/README.webrender b/gfx/doc/README.webrender index ad21309de527..d7dff60684bb 100644 --- a/gfx/doc/README.webrender +++ b/gfx/doc/README.webrender @@ -79,4 +79,4 @@ to make sure that mozjs_sys also has its Cargo.lock file updated if needed, henc the need to run the cargo update command in js/src as well. Hopefully this will be resolved soon. -Latest Commit: 76a3213080ca5c2e2a612c3023c50c81a111fd55 +Latest Commit: a54cc729259588dd1ff52c86d0c62cb2a1767137 diff --git a/gfx/layers/client/TextureClient.cpp b/gfx/layers/client/TextureClient.cpp index c5d71e2979a6..0989c5a4633a 100644 --- a/gfx/layers/client/TextureClient.cpp +++ b/gfx/layers/client/TextureClient.cpp @@ -134,8 +134,7 @@ private: AddRef(); } void ReleaseIPDLReference() { - MOZ_ASSERT(mIPCOpen == true); - mIPCOpen = false; + MOZ_ASSERT(mIPCOpen == false); Release(); } @@ -253,6 +252,8 @@ void TextureChild::ActorDestroy(ActorDestroyReason why) { PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS); + MOZ_ASSERT(mIPCOpen); + mIPCOpen = false; if (mTextureData) { DestroyTextureData(mTextureData, GetAllocator(), mOwnsTextureData, mMainThreadOnly); diff --git a/gfx/layers/wr/StackingContextHelper.cpp b/gfx/layers/wr/StackingContextHelper.cpp index 4ce855038554..df3f60de7c14 100644 --- a/gfx/layers/wr/StackingContextHelper.cpp +++ b/gfx/layers/wr/StackingContextHelper.cpp @@ -26,9 +26,9 @@ StackingContextHelper::StackingContextHelper(const StackingContextHelper& aParen WrRect scBounds = aParentSC.ToRelativeWrRect(aLayer->BoundsForStackingContext()); Layer* layer = aLayer->GetLayer(); mTransform = aTransform.valueOr(layer->GetTransform()); - mBuilder->PushStackingContext(scBounds, - 1.0f, - mTransform, + float opacity = 1.0f; + mBuilder->PushStackingContext(scBounds, 0, &opacity, + mTransform.IsIdentity() ? nullptr : &mTransform, wr::ToWrMixBlendMode(layer->GetMixBlendMode())); mOrigin = aLayer->Bounds().TopLeft(); } diff --git a/gfx/layers/wr/WebRenderBridgeParent.cpp b/gfx/layers/wr/WebRenderBridgeParent.cpp index 1d4efb5d17d2..c525b55f6c72 100644 --- a/gfx/layers/wr/WebRenderBridgeParent.cpp +++ b/gfx/layers/wr/WebRenderBridgeParent.cpp @@ -566,7 +566,8 @@ WebRenderBridgeParent::ProcessWebRenderCommands(const gfx::IntSize &aSize, LayoutDeviceIntSize size = mWidget->GetClientSize(); mApi->SetWindowParameters(size); } - mApi->SetRootDisplayList(gfx::Color(0.3f, 0.f, 0.f, 1.f), aEpoch, LayerSize(aSize.width, aSize.height), + gfx::Color color = mWidget ? gfx::Color(0.3f, 0.f, 0.f, 1.f) : gfx::Color(0.f, 0.f, 0.f, 0.f); + mApi->SetRootDisplayList(color, aEpoch, LayerSize(aSize.width, aSize.height), mPipelineId, aContentSize, dlDesc, dl.mData, dl.mLength); diff --git a/gfx/layers/wr/WebRenderContainerLayer.cpp b/gfx/layers/wr/WebRenderContainerLayer.cpp index 408f40cee977..611b04f63d3a 100644 --- a/gfx/layers/wr/WebRenderContainerLayer.cpp +++ b/gfx/layers/wr/WebRenderContainerLayer.cpp @@ -107,6 +107,13 @@ WebRenderContainerLayer::RenderLayer(wr::DisplayListBuilder& aBuilder, transformForSC = nullptr; } + if (transformForSC && transform.IsIdentity()) { + // If the transform is an identity transform, strip it out so that WR + // doesn't turn this stacking context into a reference frame, as it + // affects positioning. Bug 1345577 tracks a better fix. + transformForSC = nullptr; + } + ScrollingLayersHelper scroller(this, aBuilder, aSc); StackingContextHelper sc(aSc, aBuilder, this, animationsId, opacityForSC, transformForSC); diff --git a/gfx/layers/wr/WebRenderImageLayer.cpp b/gfx/layers/wr/WebRenderImageLayer.cpp index 473156d91c5a..861fb228b012 100644 --- a/gfx/layers/wr/WebRenderImageLayer.cpp +++ b/gfx/layers/wr/WebRenderImageLayer.cpp @@ -223,7 +223,7 @@ WebRenderImageLayer::RenderLayer(wr::DisplayListBuilder& aBuilder, if (gfx::gfxVars::CanUseHardwareVideoDecoding()) { // Use the hardware MacIOSurface with YCbCr interleaved format. MOZ_ASSERT(mVideoKeys.Length() == 1); - aBuilder.PushYCbCrInterleavedImage(sc.ToRelativeWrRect(rect), clip, mVideoKeys[0], WrYuvColorSpace::Rec601); + aBuilder.PushYCbCrInterleavedImage(sc.ToRelativeWrRect(rect), clip, mVideoKeys[0], WrYuvColorSpace::Rec601, filter); } else { // Use libyuv to convert the buffer to rgba format. MOZ_ASSERT(mVideoKeys.Length() == 1); diff --git a/gfx/webrender/Cargo.toml b/gfx/webrender/Cargo.toml index 25c528fb4f5f..8c0dbdbc63f2 100644 --- a/gfx/webrender/Cargo.toml +++ b/gfx/webrender/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "webrender" -version = "0.39.0" +version = "0.40.0" authors = ["Glenn Watson "] license = "MPL-2.0" repository = "https://github.com/servo/webrender" @@ -17,7 +17,7 @@ app_units = "0.4" bincode = "1.0.0-alpha6" bit-set = "0.4" byteorder = "1.0" -euclid = "0.11.2" +euclid = "0.13" fnv = "1.0" gleam = "0.4.3" lazy_static = "0.2" @@ -30,7 +30,7 @@ webrender_traits = {path = "../webrender_traits"} bitflags = "0.7" gamma-lut = "0.2" thread_profiler = "0.1.1" -plane-split = "0.3" +plane-split = "0.4" [dev-dependencies] angle = {git = "https://github.com/servo/angle", branch = "servo"} diff --git a/gfx/webrender/examples/yuv.rs b/gfx/webrender/examples/yuv.rs index 06659729f181..ca91f62fed52 100644 --- a/gfx/webrender/examples/yuv.rs +++ b/gfx/webrender/examples/yuv.rs @@ -16,7 +16,7 @@ use std::env; use std::path::PathBuf; use webrender_traits::{ColorF, Epoch}; use webrender_traits::{DeviceIntPoint, DeviceUintSize, LayoutPoint, LayoutRect, LayoutSize}; -use webrender_traits::{ImageData, ImageDescriptor, ImageFormat}; +use webrender_traits::{ImageData, ImageDescriptor, ImageFormat, ImageRendering}; use webrender_traits::{PipelineId, TransformStyle}; use webrender_traits::{YuvColorSpace, YuvData}; @@ -282,6 +282,7 @@ fn main() { clip, YuvData::NV12(yuv_chanel1, yuv_chanel2), YuvColorSpace::Rec601, + ImageRendering::Auto, ); let clip = builder.push_clip_region(&bounds, vec![], None); @@ -290,6 +291,7 @@ fn main() { clip, YuvData::PlanarYCbCr(yuv_chanel1, yuv_chanel2_1, yuv_chanel3), YuvColorSpace::Rec601, + ImageRendering::Auto, ); builder.pop_stacking_context(); diff --git a/gfx/webrender/res/prim_shared.glsl b/gfx/webrender/res/prim_shared.glsl index ce3abde4b89f..e5299a83afee 100644 --- a/gfx/webrender/res/prim_shared.glsl +++ b/gfx/webrender/res/prim_shared.glsl @@ -84,23 +84,16 @@ vec2 clamp_rect(vec2 point, RectWithEndpoint rect) { return clamp(point, rect.p0, rect.p1); } -// Clamp 2 points at once. -vec4 clamp_rect(vec4 points, RectWithSize rect) { - return clamp(points, rect.p0.xyxy, rect.p0.xyxy + rect.size.xyxy); -} - -vec4 clamp_rect(vec4 points, RectWithEndpoint rect) { - return clamp(points, rect.p0.xyxy, rect.p1.xyxy); +RectWithEndpoint intersect_rect(RectWithEndpoint a, RectWithEndpoint b) { + vec2 p0 = clamp_rect(a.p0, b); + vec2 p1 = clamp_rect(a.p1, b); + return RectWithEndpoint(p0, max(p0, p1)); } RectWithSize intersect_rect(RectWithSize a, RectWithSize b) { - vec4 p = clamp_rect(vec4(a.p0, a.p0 + a.size), b); - return RectWithSize(p.xy, max(vec2(0.0), p.zw - p.xy)); -} - -RectWithEndpoint intersect_rect(RectWithEndpoint a, RectWithEndpoint b) { - vec4 p = clamp_rect(vec4(a.p0, a.p1), b); - return RectWithEndpoint(p.xy, max(p.xy, p.zw)); + RectWithEndpoint r = intersect_rect(to_rect_with_endpoint(a), + to_rect_with_endpoint(b)); + return to_rect_with_size(r); } float distance_to_line(vec2 p0, vec2 perp_dir, vec2 p) { @@ -563,6 +556,30 @@ vec4 get_layer_pos(vec2 pos, Layer layer) { return untransform(pos, n, a, layer.inv_transform); } +// Compute a snapping offset in world space (adjusted to pixel ratio), +// given local position on the layer and a snap rectangle. +vec2 compute_snap_offset(vec2 local_pos, + RectWithSize local_clip_rect, + Layer layer, + RectWithSize raw_snap_rect) { + // Clamp the snap rectangle. + RectWithSize snap_rect = intersect_rect(intersect_rect(raw_snap_rect, local_clip_rect), + layer.local_clip_rect); + // Transform the snap corners to the world space. + vec4 world_snap_p0 = layer.transform * vec4(snap_rect.p0, 0.0, 1.0); + vec4 world_snap_p1 = layer.transform * vec4(snap_rect.p0 + snap_rect.size, 0.0, 1.0); + // Snap bounds in world coordinates, adjusted for pixel ratio. XY = top left, ZW = bottom right + vec4 world_snap = uDevicePixelRatio * vec4(world_snap_p0.xy, world_snap_p1.xy) / + vec4(world_snap_p0.ww, world_snap_p1.ww); + /// World offsets applied to the corners of the snap rectangle. + vec4 snap_offsets = floor(world_snap + 0.5) - world_snap; + + /// Compute the position of this vertex inside the snap rectangle. + vec2 normalized_snap_pos = (local_pos - snap_rect.p0) / snap_rect.size; + /// Compute the actual world offset for this vertex needed to make it snap. + return mix(snap_offsets.xy, snap_offsets.zw, normalized_snap_pos); +} + struct VertexInfo { vec2 local_pos; vec2 screen_pos; @@ -573,38 +590,32 @@ VertexInfo write_vertex(RectWithSize instance_rect, float z, Layer layer, AlphaBatchTask task, - vec2 snap_ref) { + RectWithSize snap_rect) { + // Select the corner of the local rect that we are processing. vec2 local_pos = instance_rect.p0 + instance_rect.size * aPosition.xy; - // xy = top left corner of the local rect, zw = position of current vertex. - vec4 local_p0_pos = vec4(snap_ref, local_pos); - // Clamp to the two local clip rects. - local_p0_pos = clamp_rect(local_p0_pos, local_clip_rect); - local_p0_pos = clamp_rect(local_p0_pos, layer.local_clip_rect); + vec2 clamped_local_pos = clamp_rect(clamp_rect(local_pos, local_clip_rect), + layer.local_clip_rect); - // Transform the top corner and current vertex to world space. - vec4 world_p0 = layer.transform * vec4(local_p0_pos.xy, 0.0, 1.0); - world_p0.xyz /= world_p0.w; - vec4 world_pos = layer.transform * vec4(local_p0_pos.zw, 0.0, 1.0); - world_pos.xyz /= world_pos.w; + /// Compute the snapping offset. + vec2 snap_offset = compute_snap_offset(clamped_local_pos, local_clip_rect, layer, snap_rect); - // Convert the world positions to device pixel space. xy=top left corner. zw=current vertex. - vec4 device_p0_pos = vec4(world_p0.xy, world_pos.xy) * uDevicePixelRatio; + // Transform the current vertex to the world cpace. + vec4 world_pos = layer.transform * vec4(clamped_local_pos, 0.0, 1.0); - // Calculate the distance to snap the vertex by (snap top left corner). - vec2 snap_delta = device_p0_pos.xy - floor(device_p0_pos.xy + 0.5); + // Convert the world positions to device pixel space. + vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio; // Apply offsets for the render task to get correct screen location. - vec2 final_pos = device_p0_pos.zw - - snap_delta - + vec2 final_pos = device_pos + snap_offset - task.screen_space_origin + task.render_target_origin; gl_Position = uTransform * vec4(final_pos, z, 1.0); - VertexInfo vi = VertexInfo(local_p0_pos.zw, device_p0_pos.zw); + VertexInfo vi = VertexInfo(clamped_local_pos, device_pos); return vi; } @@ -639,7 +650,7 @@ TransformVertexInfo write_transform_vertex(RectWithSize instance_rect, float z, Layer layer, AlphaBatchTask task, - vec2 snap_ref) { + RectWithSize snap_rect) { RectWithEndpoint local_rect = to_rect_with_endpoint(instance_rect); vec2 current_local_pos, prev_local_pos, next_local_pos; @@ -698,14 +709,14 @@ TransformVertexInfo write_transform_vertex(RectWithSize instance_rect, adjusted_next_p0, adjusted_next_p1); - // Calculate the snap amount based on the first vertex as a reference point. - vec4 world_p0 = layer.transform * vec4(snap_ref, 0.0, 1.0); - vec2 device_p0 = uDevicePixelRatio * world_p0.xy / world_p0.w; - vec2 snap_delta = device_p0 - floor(device_p0 + 0.5); + vec4 layer_pos = get_layer_pos(device_pos / uDevicePixelRatio, layer); + + /// Compute the snapping offset. + vec2 snap_offset = compute_snap_offset(layer_pos.xy / layer_pos.w, + local_clip_rect, layer, snap_rect); // Apply offsets for the render task to get correct screen location. - vec2 final_pos = device_pos - - snap_delta - + vec2 final_pos = device_pos + snap_offset - task.screen_space_origin + task.render_target_origin; @@ -713,8 +724,6 @@ TransformVertexInfo write_transform_vertex(RectWithSize instance_rect, vLocalBounds = vec4(local_rect.p0, local_rect.p1); - vec4 layer_pos = get_layer_pos(device_pos / uDevicePixelRatio, layer); - return TransformVertexInfo(layer_pos.xyw, device_pos); } diff --git a/gfx/webrender/res/ps_angle_gradient.vs.glsl b/gfx/webrender/res/ps_angle_gradient.vs.glsl index 734c5ff18a9c..65d5fd6f38de 100644 --- a/gfx/webrender/res/ps_angle_gradient.vs.glsl +++ b/gfx/webrender/res/ps_angle_gradient.vs.glsl @@ -12,7 +12,7 @@ void main(void) { prim.z, prim.layer, prim.task, - prim.local_rect.p0); + prim.local_rect); vPos = vi.local_pos - prim.local_rect.p0; diff --git a/gfx/webrender/res/ps_border_corner.vs.glsl b/gfx/webrender/res/ps_border_corner.vs.glsl index 016ff6236cb7..01be59b9b4b2 100644 --- a/gfx/webrender/res/ps_border_corner.vs.glsl +++ b/gfx/webrender/res/ps_border_corner.vs.glsl @@ -27,15 +27,19 @@ void set_radii(int style, case BORDER_STYLE_RIDGE: case BORDER_STYLE_GROOVE: vRadii1.xy = radii - adjusted_widths; - vRadii1.zw = -widths; + // See comment in default branch + vRadii1.zw = vec2(-100.0); break; case BORDER_STYLE_DOUBLE: vRadii1.xy = get_radii(radii - adjusted_widths, -widths); vRadii1.zw = get_radii(radii - widths + adjusted_widths, -widths); break; default: - vRadii1.xy = -widths; - vRadii1.zw = -widths; + // These aren't needed, so we set them to some reasonably large + // negative value so later computations will discard them. This + // avoids branches and numerical issues in the fragment shader. + vRadii1.xy = vec2(-100.0); + vRadii1.zw = vec2(-100.0); break; } } @@ -267,14 +271,14 @@ void main(void) { prim.z, prim.layer, prim.task, - prim.local_rect.p0); + prim.local_rect); #else VertexInfo vi = write_vertex(segment_rect, prim.local_clip_rect, prim.z, prim.layer, prim.task, - prim.local_rect.p0); + prim.local_rect); #endif vLocalPos = vi.local_pos; diff --git a/gfx/webrender/res/ps_border_edge.vs.glsl b/gfx/webrender/res/ps_border_edge.vs.glsl index 638ff966acc5..30453fe70a53 100644 --- a/gfx/webrender/res/ps_border_edge.vs.glsl +++ b/gfx/webrender/res/ps_border_edge.vs.glsl @@ -184,14 +184,14 @@ void main(void) { prim.z, prim.layer, prim.task, - prim.local_rect.p0); + prim.local_rect); #else VertexInfo vi = write_vertex(segment_rect, prim.local_clip_rect, prim.z, prim.layer, prim.task, - prim.local_rect.p0); + prim.local_rect); #endif vLocalPos = vi.local_pos; diff --git a/gfx/webrender/res/ps_box_shadow.vs.glsl b/gfx/webrender/res/ps_box_shadow.vs.glsl index f8394aed3965..62f366fea4f4 100644 --- a/gfx/webrender/res/ps_box_shadow.vs.glsl +++ b/gfx/webrender/res/ps_box_shadow.vs.glsl @@ -13,7 +13,7 @@ void main(void) { prim.z, prim.layer, prim.task, - prim.local_rect.p0); + prim.local_rect); RenderTaskData child_task = fetch_render_task(prim.user_data1); vUv.z = child_task.data1.x; diff --git a/gfx/webrender/res/ps_cache_image.vs.glsl b/gfx/webrender/res/ps_cache_image.vs.glsl index 5a9de6490a63..a2f96e6180de 100644 --- a/gfx/webrender/res/ps_cache_image.vs.glsl +++ b/gfx/webrender/res/ps_cache_image.vs.glsl @@ -14,7 +14,7 @@ void main(void) { prim.z, prim.layer, prim.task, - prim.local_rect.p0); + prim.local_rect); RenderTaskData child_task = fetch_render_task(prim.user_data1); vUv.z = child_task.data1.x; diff --git a/gfx/webrender/res/ps_gradient.vs.glsl b/gfx/webrender/res/ps_gradient.vs.glsl index 6249a8793174..dcfdd5315f43 100644 --- a/gfx/webrender/res/ps_gradient.vs.glsl +++ b/gfx/webrender/res/ps_gradient.vs.glsl @@ -62,7 +62,7 @@ void main(void) { prim.z, prim.layer, prim.task, - prim.local_rect.p0); + prim.local_rect); vLocalPos = vi.local_pos; vec2 f = (vi.local_pos.xy - prim.local_rect.p0) / prim.local_rect.size; #else @@ -71,7 +71,7 @@ void main(void) { prim.z, prim.layer, prim.task, - prim.local_rect.p0); + prim.local_rect); vec2 f = (vi.local_pos - segment_rect.p0) / segment_rect.size; vPos = vi.local_pos; diff --git a/gfx/webrender/res/ps_image.vs.glsl b/gfx/webrender/res/ps_image.vs.glsl index 8e80ee61114b..1e94b65f352e 100644 --- a/gfx/webrender/res/ps_image.vs.glsl +++ b/gfx/webrender/res/ps_image.vs.glsl @@ -14,7 +14,7 @@ void main(void) { prim.z, prim.layer, prim.task, - prim.local_rect.p0); + prim.local_rect); vLocalPos = vi.local_pos; #else VertexInfo vi = write_vertex(prim.local_rect, @@ -22,7 +22,7 @@ void main(void) { prim.z, prim.layer, prim.task, - prim.local_rect.p0); + prim.local_rect); vLocalPos = vi.local_pos - prim.local_rect.p0; #endif diff --git a/gfx/webrender/res/ps_radial_gradient.vs.glsl b/gfx/webrender/res/ps_radial_gradient.vs.glsl index 6dc6c89496c7..e3dfb4106268 100644 --- a/gfx/webrender/res/ps_radial_gradient.vs.glsl +++ b/gfx/webrender/res/ps_radial_gradient.vs.glsl @@ -12,7 +12,7 @@ void main(void) { prim.z, prim.layer, prim.task, - prim.local_rect.p0); + prim.local_rect); vPos = vi.local_pos - prim.local_rect.p0; diff --git a/gfx/webrender/res/ps_rectangle.vs.glsl b/gfx/webrender/res/ps_rectangle.vs.glsl index b3cfac6fc111..8d557a6051db 100644 --- a/gfx/webrender/res/ps_rectangle.vs.glsl +++ b/gfx/webrender/res/ps_rectangle.vs.glsl @@ -13,7 +13,7 @@ void main(void) { prim.z, prim.layer, prim.task, - prim.local_rect.p0); + prim.local_rect); vLocalPos = vi.local_pos; #else VertexInfo vi = write_vertex(prim.local_rect, @@ -21,7 +21,7 @@ void main(void) { prim.z, prim.layer, prim.task, - prim.local_rect.p0); + prim.local_rect); #endif #ifdef WR_FEATURE_CLIP diff --git a/gfx/webrender/res/ps_text_run.vs.glsl b/gfx/webrender/res/ps_text_run.vs.glsl index a2c620a0170c..4dbf14bb1056 100644 --- a/gfx/webrender/res/ps_text_run.vs.glsl +++ b/gfx/webrender/res/ps_text_run.vs.glsl @@ -18,7 +18,7 @@ void main(void) { prim.z, prim.layer, prim.task, - local_rect.p0); + local_rect); vLocalPos = vi.local_pos; vec2 f = (vi.local_pos.xy / vi.local_pos.z - local_rect.p0) / local_rect.size; #else @@ -27,7 +27,7 @@ void main(void) { prim.z, prim.layer, prim.task, - local_rect.p0); + local_rect); vec2 f = (vi.local_pos - local_rect.p0) / local_rect.size; #endif diff --git a/gfx/webrender/res/ps_yuv_image.vs.glsl b/gfx/webrender/res/ps_yuv_image.vs.glsl index 76aa01a5fe16..b1504276dc97 100644 --- a/gfx/webrender/res/ps_yuv_image.vs.glsl +++ b/gfx/webrender/res/ps_yuv_image.vs.glsl @@ -11,7 +11,7 @@ void main(void) { prim.z, prim.layer, prim.task, - prim.local_rect.p0); + prim.local_rect); vLocalPos = vi.local_pos; #else VertexInfo vi = write_vertex(prim.local_rect, @@ -19,7 +19,7 @@ void main(void) { prim.z, prim.layer, prim.task, - prim.local_rect.p0); + prim.local_rect); vLocalPos = vi.local_pos - prim.local_rect.p0; #endif diff --git a/gfx/webrender/src/border.rs b/gfx/webrender/src/border.rs index ba53db665649..b03fda27f99c 100644 --- a/gfx/webrender/src/border.rs +++ b/gfx/webrender/src/border.rs @@ -348,7 +348,9 @@ impl FrameBuilder { *e == BorderEdgeKind::Solid || *e == BorderEdgeKind::None }); - if all_corners_simple && all_edges_simple { + let has_no_curve = radius.is_zero(); + + if has_no_curve && all_corners_simple && all_edges_simple { let p0 = rect.origin; let p1 = rect.bottom_right(); let rect_width = rect.size.width; diff --git a/gfx/webrender/src/frame.rs b/gfx/webrender/src/frame.rs index 4261f5176429..b0456d62542e 100644 --- a/gfx/webrender/src/frame.rs +++ b/gfx/webrender/src/frame.rs @@ -13,6 +13,7 @@ use clip_scroll_tree::{ClipScrollTree, ScrollStates}; use profiler::TextureCacheProfileCounters; use resource_cache::ResourceCache; use scene::{Scene, SceneProperties}; +use std::cmp; use std::collections::HashMap; use std::hash::BuildHasherDefault; use tiling::{CompositeOps, DisplayListMap, PrimitiveFlags}; @@ -466,7 +467,8 @@ impl Frame { item.rect(), item.clip_region(), info.yuv_data, - info.color_space); + info.color_space, + info.image_rendering); } SpecificDisplayItem::Text(ref text_info) => { context.builder.add_text(clip_and_scroll, @@ -976,7 +978,10 @@ impl Frame { display_lists, device_pixel_ratio, texture_cache_profile); - resource_cache.expire_old_resources(self.id); + // Expire any resources that haven't been used for `cache_expiry_frames`. + let num_frames_back = self.frame_builder_config.cache_expiry_frames; + let expiry_frame = FrameId(cmp::max(num_frames_back, self.id.0) - num_frames_back); + resource_cache.expire_old_resources(expiry_frame); frame } diff --git a/gfx/webrender/src/frame_builder.rs b/gfx/webrender/src/frame_builder.rs index 71fa785aa3ca..b89d0d68a355 100644 --- a/gfx/webrender/src/frame_builder.rs +++ b/gfx/webrender/src/frame_builder.rs @@ -104,19 +104,7 @@ pub struct FrameBuilderConfig { pub enable_scrollbars: bool, pub default_font_render_mode: FontRenderMode, pub debug: bool, -} - -impl FrameBuilderConfig { - pub fn new(enable_scrollbars: bool, - default_font_render_mode: FontRenderMode, - debug: bool) - -> FrameBuilderConfig { - FrameBuilderConfig { - enable_scrollbars: enable_scrollbars, - default_font_render_mode: default_font_render_mode, - debug: debug, - } - } + pub cache_expiry_frames: u32, } pub struct FrameBuilder { @@ -1075,7 +1063,8 @@ impl FrameBuilder { rect: LayerRect, clip_region: &ClipRegion, yuv_data: YuvData, - color_space: YuvColorSpace) { + color_space: YuvColorSpace, + image_rendering: ImageRendering) { let format = yuv_data.get_format(); let yuv_key = match yuv_data { YuvData::NV12(plane_0, plane_1) => [plane_0, plane_1, ImageKey::new(0, 0)], @@ -1091,6 +1080,7 @@ impl FrameBuilder { yuv_resource_address: GpuStoreAddress(0), format: format, color_space: color_space, + image_rendering: image_rendering, }; let prim_gpu = YuvImagePrimitiveGpu::new(rect.size); diff --git a/gfx/webrender/src/glyph_rasterizer.rs b/gfx/webrender/src/glyph_rasterizer.rs new file mode 100644 index 000000000000..c2d47af1de4a --- /dev/null +++ b/gfx/webrender/src/glyph_rasterizer.rs @@ -0,0 +1,395 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use app_units::Au; +use device::TextureFilter; +use frame::FrameId; +use platform::font::{FontContext, RasterizedGlyph}; +use profiler::TextureCacheProfileCounters; +use rayon::ThreadPool; +use rayon::prelude::*; +use resource_cache::ResourceClassCache; +use std::sync::{Arc, Mutex, MutexGuard}; +use std::sync::mpsc::{channel, Receiver, Sender}; +use std::collections::HashSet; +use std::mem; +use texture_cache::{TextureCacheItemId, TextureCache}; +use internal_types::FontTemplate; +use webrender_traits::{FontKey, FontRenderMode, ImageData, ImageFormat}; +use webrender_traits::{ImageDescriptor, ColorF, LayoutPoint}; +use webrender_traits::{GlyphKey, GlyphOptions, GlyphInstance, GlyphDimensions}; + +pub type GlyphCache = ResourceClassCache>; + +pub struct FontContexts { + // These worker are mostly accessed from their corresponding worker threads. + // The goal is that there should be no noticeable contention on the muteces. + worker_contexts: Vec>, + + // This worker should be accessed by threads that don't belong to thre thread pool + // (in theory that's only the render backend thread so no contention expected either). + shared_context: Mutex, + + // Stored here as a convenience to get the current thread index. + workers: Arc, +} + +impl FontContexts { + /// Get access to the font context associated to the current thread. + pub fn lock_current_context(&self) -> MutexGuard { + let id = self.current_worker_id(); + self.lock_context(id) + } + + /// Get access to any particular font context. + /// + /// The id is ```Some(i)``` where i is an index between 0 and num_worker_contexts + /// for font contexts associated to the thread pool, and None for the shared + /// global font context for use outside of the thread pool. + pub fn lock_context(&self, id: Option) -> MutexGuard { + match id { + Some(index) => self.worker_contexts[index].lock().unwrap(), + None => self.shared_context.lock().unwrap(), + } + } + + /// Get access to the font context usable outside of the thread pool. + pub fn lock_shared_context(&self) -> MutexGuard { + self.shared_context.lock().unwrap() + } + + // number of contexts associated to workers + pub fn num_worker_contexts(&self) -> usize { + self.worker_contexts.len() + } + + fn current_worker_id(&self) -> Option { + self.workers.current_thread_index() + } +} + +pub struct GlyphRasterizer { + workers: Arc, + font_contexts: Arc, + + // Receives the rendered glyphs. + glyph_rx: Receiver>, + glyph_tx: Sender>, + + // Maintain a set of glyphs that have been requested this + // frame. This ensures the glyph thread won't rasterize + // the same glyph more than once in a frame. This is required + // because the glyph cache hash table is not updated + // until the end of the frame when we wait for glyph requests + // to be resolved. + pending_glyphs: HashSet, + + // We defer removing fonts to the end of the frame so that: + // - this work is done outside of the critical path, + // - we don't have to worry about the ordering of events if a font is used on + // a frame where it is used (although it seems unlikely). + fonts_to_remove: Vec, +} + +impl GlyphRasterizer { + pub fn new(workers: Arc) -> Self { + let (glyph_tx, glyph_rx) = channel(); + + let num_workers = workers.current_num_threads(); + let mut contexts = Vec::with_capacity(num_workers); + + for _ in 0..num_workers { + contexts.push(Mutex::new(FontContext::new())); + } + + GlyphRasterizer { + font_contexts: Arc::new( + FontContexts { + worker_contexts: contexts, + shared_context: Mutex::new(FontContext::new()), + workers: Arc::clone(&workers), + } + ), + glyph_rx: glyph_rx, + glyph_tx: glyph_tx, + pending_glyphs: HashSet::new(), + workers: workers, + fonts_to_remove: Vec::new(), + } + } + + pub fn add_font(&mut self, font_key: FontKey, template: FontTemplate) { + let font_contexts = Arc::clone(&self.font_contexts); + // It's important to synchronously add the font for the shared context because + // we use it to check that fonts have been properly added when requesting glyphs. + font_contexts.lock_shared_context().add_font(&font_key, &template); + + // TODO: this locks each font context while adding the font data, probably not a big deal, + // but if there is contention on this lock we could easily have a queue of per-context + // operations to add and delete fonts, and have these queues lazily processed by each worker + // before rendering a glyph. + // We can also move this into a worker to free up some cycles in the calling (render backend) + // thread. + for i in 0..font_contexts.num_worker_contexts() { + font_contexts.lock_context(Some(i)).add_font(&font_key, &template); + } + } + + pub fn delete_font(&mut self, font_key: FontKey) { + self.fonts_to_remove.push(font_key); + } + + pub fn request_glyphs( + &mut self, + glyph_cache: &mut GlyphCache, + current_frame_id: FrameId, + font_key: FontKey, + size: Au, + color: ColorF, + glyph_instances: &[GlyphInstance], + render_mode: FontRenderMode, + glyph_options: Option, + ) { + assert!(self.font_contexts.lock_shared_context().has_font(&font_key)); + + let mut glyphs = Vec::with_capacity(glyph_instances.len()); + + { + // TODO: If this takes too long we can resurect a dedicated glyph + // dispatch thread, hopefully not. + profile_scope!("glyph-requests"); + + // select glyphs that have not been requested yet. + for glyph in glyph_instances { + let glyph_request = GlyphRequest::new( + font_key, + size, + color, + glyph.index, + glyph.point, + render_mode, + glyph_options, + ); + + glyph_cache.mark_as_needed(&glyph_request, current_frame_id); + if !glyph_cache.contains_key(&glyph_request) && !self.pending_glyphs.contains(&glyph_request) { + self.pending_glyphs.insert(glyph_request.clone()); + glyphs.push(glyph_request); + } + } + } + + if glyphs.is_empty() { + return; + } + + let font_contexts = Arc::clone(&self.font_contexts); + let glyph_tx = self.glyph_tx.clone(); + // spawn an async task to get off of the render backend thread as early as + // possible and in that task use rayon's fork join dispatch to rasterize the + // glyphs in the thread pool. + self.workers.spawn_async(move || { + let jobs = glyphs.par_iter().map(|request: &GlyphRequest| { + profile_scope!("glyph-raster"); + let mut context = font_contexts.lock_current_context(); + let job = GlyphRasterJob { + request: request.clone(), + result: context.rasterize_glyph( + &request.key, + request.render_mode, + request.glyph_options + ), + }; + + // Sanity check. + if let Some(ref glyph) = job.result { + let bpp = 4; // We always render glyphs in 32 bits RGBA format. + assert_eq!(glyph.bytes.len(), bpp * (glyph.width * glyph.height) as usize); + } + + job + }).collect(); + + glyph_tx.send(jobs).unwrap(); + }); + } + + pub fn get_glyph_dimensions(&mut self, glyph_key: &GlyphKey) -> Option { + self.font_contexts.lock_shared_context().get_glyph_dimensions(glyph_key) + } + + pub fn resolve_glyphs( + &mut self, + current_frame_id: FrameId, + glyph_cache: &mut GlyphCache, + texture_cache: &mut TextureCache, + texture_cache_profile: &mut TextureCacheProfileCounters, + ) { + let mut rasterized_glyphs = Vec::with_capacity(self.pending_glyphs.len()); + + // Pull rasterized glyphs from the queue. + while !self.pending_glyphs.is_empty() { + // TODO: rather than blocking until all pending glyphs are available + // we could try_recv and steal work from the thread pool to take advantage + // of the fact that this thread is alive and we avoid the added latency + // of blocking it. + let raster_jobs = self.glyph_rx.recv().expect("BUG: Should be glyphs pending!"); + for job in raster_jobs { + debug_assert!(self.pending_glyphs.contains(&job.request)); + self.pending_glyphs.remove(&job.request); + + rasterized_glyphs.push(job); + } + } + + // Ensure that the glyphs are always processed in the same + // order for a given text run (since iterating a hash set doesn't + // guarantee order). This can show up as very small float inaccuacry + // differences in rasterizers due to the different coordinates + // that text runs get associated with by the texture cache allocator. + rasterized_glyphs.sort_by(|a, b| a.request.cmp(&b.request)); + + // Update the caches. + for job in rasterized_glyphs { + let image_id = job.result.and_then( + |glyph| if glyph.width > 0 && glyph.height > 0 { + let image_id = texture_cache.new_item_id(); + texture_cache.insert( + image_id, + ImageDescriptor { + width: glyph.width, + height: glyph.height, + stride: None, + format: ImageFormat::RGBA8, + is_opaque: false, + offset: 0, + }, + TextureFilter::Linear, + ImageData::Raw(Arc::new(glyph.bytes)), + texture_cache_profile, + ); + Some(image_id) + } else { + None + } + ); + + glyph_cache.insert(job.request, image_id, current_frame_id); + } + + // Now that we are done with the critical path (rendering the glyphs), + // we can schedule removing the fonts if needed. + if !self.fonts_to_remove.is_empty() { + let font_contexts = Arc::clone(&self.font_contexts); + let fonts_to_remove = mem::replace(&mut self.fonts_to_remove, Vec::new()); + self.workers.spawn_async(move || { + for font_key in &fonts_to_remove { + font_contexts.lock_shared_context().delete_font(font_key); + } + for i in 0..font_contexts.num_worker_contexts() { + let mut context = font_contexts.lock_context(Some(i)); + for font_key in &fonts_to_remove { + context.delete_font(font_key); + } + } + }); + } + } +} + +impl FontContext { + fn add_font(&mut self, font_key: &FontKey, template: &FontTemplate) { + match template { + &FontTemplate::Raw(ref bytes, index) => { + self.add_raw_font(&font_key, &**bytes, index); + } + &FontTemplate::Native(ref native_font_handle) => { + self.add_native_font(&font_key, (*native_font_handle).clone()); + } + } + } +} + +#[derive(Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)] +pub struct GlyphRequest { + pub key: GlyphKey, + pub render_mode: FontRenderMode, + pub glyph_options: Option, +} + +impl GlyphRequest { + pub fn new( + font_key: FontKey, + size: Au, + color: ColorF, + index: u32, + point: LayoutPoint, + render_mode: FontRenderMode, + glyph_options: Option, + ) -> GlyphRequest { + GlyphRequest { + key: GlyphKey::new(font_key, size, color, index, point, render_mode), + render_mode: render_mode, + glyph_options: glyph_options, + } + } +} + +struct GlyphRasterJob { + request: GlyphRequest, + result: Option, +} + +#[test] +fn raterize_200_glyphs() { + // This test loads a font from disc, the renders 4 requests containing + // 50 glyphs each, deletes the font and waits for the result. + + use rayon::Configuration; + use std::fs::File; + use std::io::Read; + + let workers = Arc::new(ThreadPool::new(Configuration::new()).unwrap()); + let mut glyph_rasterizer = GlyphRasterizer::new(workers); + let mut glyph_cache = GlyphCache::new(); + + let mut font_file = File::open("../wrench/reftests/text/VeraBd.ttf").expect("Couldn't open font file"); + let mut font_data = vec![]; + font_file.read_to_end(&mut font_data).expect("failed to read font file"); + + let font_key = FontKey::new(0, 0); + glyph_rasterizer.add_font(font_key, FontTemplate::Raw(Arc::new(font_data), 0)); + + let frame_id = FrameId(1); + + let mut glyph_instances = Vec::with_capacity(200); + for i in 0..200 { + glyph_instances.push(GlyphInstance { + index: i, // It doesn't matter which glyphs we are actually rendering. + point: LayoutPoint::new(0.0, 0.0), + }); + } + + for i in 0..4 { + glyph_rasterizer.request_glyphs( + &mut glyph_cache, + frame_id, + font_key, + Au::from_px(32), + ColorF::new(0.0, 0.0, 0.0, 1.0), + &glyph_instances[(50 * i)..(50 * (i + 1))], + FontRenderMode::Subpixel, + None, + ); + } + + glyph_rasterizer.delete_font(font_key); + + glyph_rasterizer.resolve_glyphs( + frame_id, + &mut glyph_cache, + &mut TextureCache::new(4096), + &mut TextureCacheProfileCounters::new(), + ); +} diff --git a/gfx/webrender/src/lib.rs b/gfx/webrender/src/lib.rs index 243f213720ca..85d2cd86a042 100644 --- a/gfx/webrender/src/lib.rs +++ b/gfx/webrender/src/lib.rs @@ -57,6 +57,7 @@ mod frame; mod frame_builder; mod freelist; mod geometry; +mod glyph_rasterizer; mod gpu_store; mod internal_types; mod mask_cache; diff --git a/gfx/webrender/src/platform/macos/font.rs b/gfx/webrender/src/platform/macos/font.rs index 95077cff43e5..c87402606c2a 100644 --- a/gfx/webrender/src/platform/macos/font.rs +++ b/gfx/webrender/src/platform/macos/font.rs @@ -26,6 +26,10 @@ pub struct FontContext { gamma_lut: GammaLut, } +// core text is safe to use on multiple threads and non-shareable resources are +// all hidden inside their font context. +unsafe impl Send for FontContext {} + pub struct RasterizedGlyph { pub width: u32, pub height: u32, @@ -76,6 +80,22 @@ fn get_glyph_metrics(ct_font: &CTFont, subpixel_point: &SubpixelPoint) -> GlyphMetrics { let bounds = ct_font.get_bounding_rects_for_glyphs(kCTFontDefaultOrientation, &[glyph]); + if bounds.origin.x.is_nan() || bounds.origin.y.is_nan() || + bounds.size.width.is_nan() || bounds.size.height.is_nan() { + // If an unexpected glyph index is requested, core text will return NaN values + // which causes us to do bad thing as the value is cast into an integer and + // overflow when expanding the bounds a few lines below. + // Instead we are better off returning zero-sized metrics because this special + // case is handled by the callers of this method. + return GlyphMetrics { + rasterized_left: 0, + rasterized_width: 0, + rasterized_height: 0, + rasterized_ascent: 0, + rasterized_descent: 0, + }; + } + let (x_offset, y_offset) = subpixel_point.to_f64(); // First round out to pixel boundaries @@ -127,6 +147,10 @@ impl FontContext { } } + pub fn has_font(&self, font_key: &FontKey) -> bool { + self.cg_fonts.contains_key(font_key) + } + pub fn add_raw_font(&mut self, font_key: &FontKey, bytes: &[u8], index: u32) { if self.cg_fonts.contains_key(font_key) { return diff --git a/gfx/webrender/src/platform/unix/font.rs b/gfx/webrender/src/platform/unix/font.rs index 45670cd551ae..fe4424b77b62 100644 --- a/gfx/webrender/src/platform/unix/font.rs +++ b/gfx/webrender/src/platform/unix/font.rs @@ -27,6 +27,11 @@ pub struct FontContext { faces: HashMap, } +// FreeType resources are safe to move between threads as long as they +// are not concurrently accessed. In our case, everything is hidden inside +// a given FontContext so it is safe to move the latter between threads. +unsafe impl Send for FontContext {} + pub struct RasterizedGlyph { pub width: u32, pub height: u32, @@ -63,6 +68,10 @@ impl FontContext { } } + pub fn has_font(&self, font_key: &FontKey) -> bool { + self.faces.contains_key(font_key) + } + pub fn add_raw_font(&mut self, font_key: &FontKey, bytes: &[u8], index: u32) { if !self.faces.contains_key(&font_key) { let mut face: FT_Face = ptr::null_mut(); @@ -171,7 +180,11 @@ impl FontContext { return None; } - let dimensions = Self::get_glyph_dimensions_impl(slot).unwrap(); + let dimensions = match Self::get_glyph_dimensions_impl(slot) { + Some(val) => val, + None => return None, + }; + let bitmap = unsafe { &(*slot).bitmap }; let pixel_mode = unsafe { mem::transmute(bitmap.pixel_mode as u32) }; info!("Rasterizing {:?} as {:?} with dimensions {:?}", key, render_mode, dimensions); diff --git a/gfx/webrender/src/platform/windows/font.rs b/gfx/webrender/src/platform/windows/font.rs index c588361df63b..e0a27cb4325a 100644 --- a/gfx/webrender/src/platform/windows/font.rs +++ b/gfx/webrender/src/platform/windows/font.rs @@ -24,6 +24,10 @@ pub struct FontContext { gdi_gamma_lut: GammaLut, } +// DirectWrite is safe to use on multiple threads and non-shareable resources are +// all hidden inside their font context. +unsafe impl Send for FontContext {} + pub struct RasterizedGlyph { pub width: u32, pub height: u32, @@ -117,6 +121,10 @@ impl FontContext { } } + pub fn has_font(&self, font_key: &FontKey) -> bool { + self.fonts.contains_key(font_key) + } + pub fn add_raw_font(&mut self, font_key: &FontKey, data: &[u8], index: u32) { if self.fonts.contains_key(font_key) { return @@ -271,9 +279,11 @@ impl FontContext { let width = (bounds.right - bounds.left) as usize; let height = (bounds.bottom - bounds.top) as usize; - // We should not get here since glyph_dimensions would return - // None for empty glyphs. - assert!(width > 0 && height > 0); + // Alpha texture bounds can sometimes return an empty rect + // Such as for spaces + if width == 0 || height == 0 { + return None; + } let mut pixels = analysis.create_alpha_texture(texture_type, bounds); diff --git a/gfx/webrender/src/prim_store.rs b/gfx/webrender/src/prim_store.rs index f67270613d9a..c312312a1455 100644 --- a/gfx/webrender/src/prim_store.rs +++ b/gfx/webrender/src/prim_store.rs @@ -197,6 +197,8 @@ pub struct YuvImagePrimitiveCpu { // The first address of yuv resource_address. Use "yuv_resource_address + N-th" to get the N-th channel data. // e.g. yuv_resource_address + 0 => y channel resource_address pub yuv_resource_address: GpuStoreAddress, + + pub image_rendering: ImageRendering, } #[derive(Debug, Clone)] @@ -1092,7 +1094,7 @@ impl PrimitiveStore { &mut deferred_resolves, image_cpu.yuv_key[channel], resource_address, - ImageRendering::Auto, + image_cpu.image_rendering, None); // texture_id image_cpu.yuv_texture_id[channel] = texture_id; @@ -1330,7 +1332,7 @@ impl PrimitiveStore { let channel_num = image_cpu.format.get_plane_num(); debug_assert!(channel_num <= 3); for channel in 0..channel_num { - resource_cache.request_image(image_cpu.yuv_key[channel], ImageRendering::Auto, None); + resource_cache.request_image(image_cpu.yuv_key[channel], image_cpu.image_rendering, None); } // TODO(nical): Currently assuming no tile_spacing for yuv images. diff --git a/gfx/webrender/src/renderer.rs b/gfx/webrender/src/renderer.rs index 292eabbbc243..1e7c05a6c45c 100644 --- a/gfx/webrender/src/renderer.rs +++ b/gfx/webrender/src/renderer.rs @@ -65,8 +65,8 @@ pub const MAX_VERTEX_TEXTURE_WIDTH: usize = 1024; const GPU_TAG_CACHE_BOX_SHADOW: GpuProfileTag = GpuProfileTag { label: "C_BoxShadow", color: debug_colors::BLACK }; const GPU_TAG_CACHE_CLIP: GpuProfileTag = GpuProfileTag { label: "C_Clip", color: debug_colors::PURPLE }; const GPU_TAG_CACHE_TEXT_RUN: GpuProfileTag = GpuProfileTag { label: "C_TextRun", color: debug_colors::MISTYROSE }; -const GPU_TAG_INIT: GpuProfileTag = GpuProfileTag { label: "Init", color: debug_colors::WHITE }; -const GPU_TAG_SETUP_TARGET: GpuProfileTag = GpuProfileTag { label: "Target", color: debug_colors::SLATEGREY }; +const GPU_TAG_SETUP_TARGET: GpuProfileTag = GpuProfileTag { label: "target", color: debug_colors::SLATEGREY }; +const GPU_TAG_SETUP_DATA: GpuProfileTag = GpuProfileTag { label: "data init", color: debug_colors::LIGHTGREY }; const GPU_TAG_PRIM_RECT: GpuProfileTag = GpuProfileTag { label: "Rect", color: debug_colors::RED }; const GPU_TAG_PRIM_IMAGE: GpuProfileTag = GpuProfileTag { label: "Image", color: debug_colors::GREEN }; const GPU_TAG_PRIM_YUV_IMAGE: GpuProfileTag = GpuProfileTag { label: "YuvImage", color: debug_colors::DARKGREEN }; @@ -575,6 +575,7 @@ pub struct Renderer { max_recorded_profiles: usize, clear_framebuffer: bool, clear_color: ColorF, + enable_clear_scissor: bool, debug: DebugRenderer, render_target_debug: bool, enable_batcher: bool, @@ -1055,9 +1056,12 @@ impl Renderer { (false, _) => FontRenderMode::Mono, }; - let config = FrameBuilderConfig::new(options.enable_scrollbars, - default_font_render_mode, - options.debug); + let config = FrameBuilderConfig { + enable_scrollbars: options.enable_scrollbars, + default_font_render_mode, + debug: options.debug, + cache_expiry_frames: options.cache_expiry_frames, + }; let device_pixel_ratio = options.device_pixel_ratio; let render_target_debug = options.render_target_debug; @@ -1130,6 +1134,7 @@ impl Renderer { max_recorded_profiles: options.max_recorded_profiles, clear_framebuffer: options.clear_framebuffer, clear_color: options.clear_color, + enable_clear_scissor: options.enable_clear_scissor, last_time: 0, color_render_targets: Vec::new(), alpha_render_targets: Vec::new(), @@ -1276,31 +1281,36 @@ impl Renderer { if let Some(ref mut frame) = frame.frame { let mut profile_timers = RendererProfileTimers::new(); - // Block CPU waiting for last frame's GPU profiles to arrive. - // In general this shouldn't block unless heavily GPU limited. - if let Some((gpu_frame_id, samples)) = self.gpu_profile.build_samples() { - if self.max_recorded_profiles > 0 { - while self.gpu_profiles.len() >= self.max_recorded_profiles { - self.gpu_profiles.pop_front(); + { + //Note: avoiding `self.gpu_profile.add_marker` - it would block here + let _gm = GpuMarker::new(self.device.rc_gl(), "build samples"); + // Block CPU waiting for last frame's GPU profiles to arrive. + // In general this shouldn't block unless heavily GPU limited. + if let Some((gpu_frame_id, samples)) = self.gpu_profile.build_samples() { + if self.max_recorded_profiles > 0 { + while self.gpu_profiles.len() >= self.max_recorded_profiles { + self.gpu_profiles.pop_front(); + } + self.gpu_profiles.push_back(GpuProfile::new(gpu_frame_id, &samples)); } - self.gpu_profiles.push_back(GpuProfile::new(gpu_frame_id, &samples)); + profile_timers.gpu_samples = samples; } - profile_timers.gpu_samples = samples; } let cpu_frame_id = profile_timers.cpu_time.profile(|| { - let cpu_frame_id = self.device.begin_frame(frame.device_pixel_ratio); - self.gpu_profile.begin_frame(cpu_frame_id); - { - let _gm = self.gpu_profile.add_marker(GPU_TAG_INIT); + let cpu_frame_id = { + let _gm = GpuMarker::new(self.device.rc_gl(), "begin frame"); + let frame_id = self.device.begin_frame(frame.device_pixel_ratio); + self.gpu_profile.begin_frame(frame_id); self.device.disable_scissor(); self.device.disable_depth(); self.device.set_blend(false); - //self.update_shaders(); self.update_texture_cache(); - } + + frame_id + }; self.draw_tile_frame(frame, &framebuffer_size); @@ -1337,7 +1347,10 @@ impl Renderer { let debug_size = DeviceUintSize::new(framebuffer_size.width as u32, framebuffer_size.height as u32); self.debug.render(&mut self.device, &debug_size); - self.device.end_frame(); + { + let _gm = GpuMarker::new(self.device.rc_gl(), "end frame"); + self.device.end_frame(); + } self.last_time = current_time; } @@ -1693,7 +1706,7 @@ impl Renderer { self.device.set_blend(false); self.device.set_blend_mode_alpha(); match render_target { - Some(..) => { + Some(..) if self.enable_clear_scissor => { // TODO(gw): Applying a scissor rect and minimal clear here // is a very large performance win on the Intel and nVidia // GPUs that I have tested with. It's possible it may be a @@ -1703,7 +1716,7 @@ impl Renderer { Some(1.0), target.used_rect()); } - None => { + _ => { self.device.clear_target(clear_color, Some(1.0)); } } @@ -1968,6 +1981,66 @@ impl Renderer { } } + fn start_frame(&mut self, frame: &mut Frame) { + let _gm = self.gpu_profile.add_marker(GPU_TAG_SETUP_DATA); + + // Assign render targets to the passes. + for pass in &mut frame.passes { + debug_assert!(pass.color_texture_id.is_none()); + debug_assert!(pass.alpha_texture_id.is_none()); + + if pass.needs_render_target_kind(RenderTargetKind::Color) { + pass.color_texture_id = Some(self.color_render_targets + .pop() + .unwrap_or_else(|| { + self.device + .create_texture_ids(1, TextureTarget::Array)[0] + })); + } + + if pass.needs_render_target_kind(RenderTargetKind::Alpha) { + pass.alpha_texture_id = Some(self.alpha_render_targets + .pop() + .unwrap_or_else(|| { + self.device + .create_texture_ids(1, TextureTarget::Array)[0] + })); + } + } + + + // Init textures and render targets to match this scene. + for pass in &frame.passes { + if let Some(texture_id) = pass.color_texture_id { + let target_count = pass.required_target_count(RenderTargetKind::Color); + self.device.init_texture(texture_id, + frame.cache_size.width as u32, + frame.cache_size.height as u32, + ImageFormat::RGBA8, + TextureFilter::Linear, + RenderTargetMode::LayerRenderTarget(target_count as i32), + None); + } + if let Some(texture_id) = pass.alpha_texture_id { + let target_count = pass.required_target_count(RenderTargetKind::Alpha); + self.device.init_texture(texture_id, + frame.cache_size.width as u32, + frame.cache_size.height as u32, + ImageFormat::A8, + TextureFilter::Nearest, + RenderTargetMode::LayerRenderTarget(target_count as i32), + None); + } + } + + // TODO(gw): This is a hack / workaround for #728. + // We should find a better way to implement these updates rather + // than wasting this extra memory, but for now it removes a large + // number of driver stalls. + self.gpu_data_textures[self.gdt_index].init_frame(&mut self.device, frame); + self.gdt_index = (self.gdt_index + 1) % GPU_DATA_TEXTURE_POOL; + } + fn draw_tile_frame(&mut self, frame: &mut Frame, framebuffer_size: &DeviceUintSize) { @@ -1987,60 +2060,7 @@ impl Renderer { if frame.passes.is_empty() { self.device.clear_target(Some(self.clear_color.to_array()), Some(1.0)); } else { - // Assign render targets to the passes. - for pass in &mut frame.passes { - debug_assert!(pass.color_texture_id.is_none()); - debug_assert!(pass.alpha_texture_id.is_none()); - - if pass.needs_render_target_kind(RenderTargetKind::Color) { - pass.color_texture_id = Some(self.color_render_targets - .pop() - .unwrap_or_else(|| { - self.device - .create_texture_ids(1, TextureTarget::Array)[0] - })); - } - - if pass.needs_render_target_kind(RenderTargetKind::Alpha) { - pass.alpha_texture_id = Some(self.alpha_render_targets - .pop() - .unwrap_or_else(|| { - self.device - .create_texture_ids(1, TextureTarget::Array)[0] - })); - } - } - - // Init textures and render targets to match this scene. - for pass in &frame.passes { - if let Some(texture_id) = pass.color_texture_id { - let target_count = pass.required_target_count(RenderTargetKind::Color); - self.device.init_texture(texture_id, - frame.cache_size.width as u32, - frame.cache_size.height as u32, - ImageFormat::RGBA8, - TextureFilter::Linear, - RenderTargetMode::LayerRenderTarget(target_count as i32), - None); - } - if let Some(texture_id) = pass.alpha_texture_id { - let target_count = pass.required_target_count(RenderTargetKind::Alpha); - self.device.init_texture(texture_id, - frame.cache_size.width as u32, - frame.cache_size.height as u32, - ImageFormat::A8, - TextureFilter::Nearest, - RenderTargetMode::LayerRenderTarget(target_count as i32), - None); - } - } - - // TODO(gw): This is a hack / workaround for #728. - // We should find a better way to implement these updates rather - // than wasting this extra memory, but for now it removes a large - // number of driver stalls. - self.gpu_data_textures[self.gdt_index].init_frame(&mut self.device, frame); - self.gdt_index = (self.gdt_index + 1) % GPU_DATA_TEXTURE_POOL; + self.start_frame(frame); let mut src_color_id = self.dummy_cache_texture_id; let mut src_alpha_id = self.dummy_cache_texture_id; @@ -2257,9 +2277,11 @@ pub struct RendererOptions { pub enable_subpixel_aa: bool, pub clear_framebuffer: bool, pub clear_color: ColorF, + pub enable_clear_scissor: bool, pub enable_batcher: bool, pub render_target_debug: bool, pub max_texture_size: Option, + pub cache_expiry_frames: u32, pub workers: Option>, pub blob_image_renderer: Option>, pub recorder: Option>, @@ -2281,9 +2303,11 @@ impl Default for RendererOptions { enable_subpixel_aa: false, clear_framebuffer: true, clear_color: ColorF::new(1.0, 1.0, 1.0, 1.0), + enable_clear_scissor: true, enable_batcher: true, render_target_debug: false, max_texture_size: None, + cache_expiry_frames: 600, // roughly, 10 seconds workers: None, blob_image_renderer: None, recorder: None, diff --git a/gfx/webrender/src/resource_cache.rs b/gfx/webrender/src/resource_cache.rs index e9c91f575af1..4cb8f06c1573 100644 --- a/gfx/webrender/src/resource_cache.rs +++ b/gfx/webrender/src/resource_cache.rs @@ -7,54 +7,26 @@ use device::TextureFilter; use fnv::FnvHasher; use frame::FrameId; use internal_types::{FontTemplate, SourceTexture, TextureUpdateList}; -use platform::font::{FontContext, RasterizedGlyph}; use profiler::TextureCacheProfileCounters; -use std::cell::RefCell; use std::collections::{HashMap, HashSet}; use std::collections::hash_map::Entry::{self, Occupied, Vacant}; use std::fmt::Debug; use std::hash::BuildHasherDefault; use std::hash::Hash; use std::mem; -use std::sync::{Arc, Barrier}; -use std::sync::mpsc::{channel, Receiver, Sender}; -use std::thread; +use std::sync::Arc; use texture_cache::{TextureCache, TextureCacheItemId}; -use thread_profiler::register_thread_with_profiler; -use webrender_traits::{Epoch, FontKey, GlyphKey, ImageKey, ImageFormat, ImageRendering}; +use webrender_traits::{Epoch, FontKey, GlyphKey, ImageKey, ImageRendering}; use webrender_traits::{FontRenderMode, ImageData, GlyphDimensions, WebGLContextId}; use webrender_traits::{DevicePoint, DeviceIntSize, DeviceUintRect, ImageDescriptor, ColorF}; use webrender_traits::{GlyphOptions, GlyphInstance, TileOffset, TileSize}; use webrender_traits::{BlobImageRenderer, BlobImageDescriptor, BlobImageError, BlobImageRequest, BlobImageData, ImageStore}; use webrender_traits::{ExternalImageData, ExternalImageType, LayoutPoint}; use rayon::ThreadPool; +use glyph_rasterizer::{GlyphRasterizer, GlyphCache, GlyphRequest}; const DEFAULT_TILE_SIZE: TileSize = 512; -thread_local!(pub static FONT_CONTEXT: RefCell = RefCell::new(FontContext::new())); - -type GlyphCache = ResourceClassCache>; - -/// Message sent from the resource cache to the glyph cache thread. -enum GlyphCacheMsg { - /// Begin the frame - pass ownership of the glyph cache to the thread. - BeginFrame(FrameId, GlyphCache), - /// Add a new font. - AddFont(FontKey, FontTemplate), - /// Request glyphs for a text run. - RequestGlyphs(FontKey, Au, ColorF, Vec, FontRenderMode, Option), - // Remove an existing font. - DeleteFont(FontKey), - /// Finished requesting glyphs. Reply with new glyphs. - EndFrame, -} - -/// Results send from glyph cache thread back to main resource cache. -enum GlyphCacheResultMsg { - /// Return the glyph cache, and a list of newly rasterized glyphs. - EndFrame(GlyphCache, Vec), -} - // These coordinates are always in texels. // They are converted to normalized ST // values in the vertex shader. The reason @@ -70,30 +42,6 @@ pub struct CacheItem { pub uv1: DevicePoint, } -#[derive(Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)] -pub struct RenderedGlyphKey { - pub key: GlyphKey, - pub render_mode: FontRenderMode, - pub glyph_options: Option, -} - -impl RenderedGlyphKey { - pub fn new(font_key: FontKey, - size: Au, - color: ColorF, - index: u32, - point: LayoutPoint, - render_mode: FontRenderMode, - glyph_options: Option) -> RenderedGlyphKey { - RenderedGlyphKey { - key: GlyphKey::new(font_key, size, color, index, - point, render_mode), - render_mode: render_mode, - glyph_options: glyph_options, - } - } -} - pub struct ImageProperties { pub descriptor: ImageDescriptor, pub external_image: Option, @@ -160,14 +108,14 @@ pub struct ResourceClassCache { } impl ResourceClassCache where K: Clone + Hash + Eq + Debug, V: Resource { - fn new() -> ResourceClassCache { + pub fn new() -> ResourceClassCache { ResourceClassCache { resources: HashMap::default(), last_access_times: HashMap::default(), } } - fn contains_key(&self, key: &K) -> bool { + pub fn contains_key(&self, key: &K) -> bool { self.resources.contains_key(key) } @@ -181,7 +129,7 @@ impl ResourceClassCache where K: Clone + Hash + Eq + Debug, V: Resourc self.resources.get(key).expect("Didn't find a cached resource with that ID!") } - fn insert(&mut self, key: K, value: V, frame: FrameId) { + pub fn insert(&mut self, key: K, value: V, frame: FrameId) { self.last_access_times.insert(key.clone(), frame); self.resources.insert(key, value); } @@ -191,7 +139,7 @@ impl ResourceClassCache where K: Clone + Hash + Eq + Debug, V: Resourc self.resources.entry(key) } - fn mark_as_needed(&mut self, key: &K, frame: FrameId) { + pub fn mark_as_needed(&mut self, key: &K, frame: FrameId) { self.last_access_times.insert((*key).clone(), frame); } @@ -232,18 +180,13 @@ impl Into for ImageRequest { } } -struct GlyphRasterJob { - key: RenderedGlyphKey, - result: Option, -} - struct WebGLTexture { id: SourceTexture, size: DeviceIntSize, } pub struct ResourceCache { - cached_glyphs: Option, + cached_glyphs: GlyphCache, cached_images: ResourceClassCache, // TODO(pcwalton): Figure out the lifecycle of these. @@ -259,8 +202,7 @@ pub struct ResourceCache { // TODO(gw): We should expire (parts of) this cache semi-regularly! cached_glyph_dimensions: HashMap, BuildHasherDefault>, pending_image_requests: Vec, - glyph_cache_tx: Sender, - glyph_cache_result_queue: Receiver, + glyph_rasterizer: GlyphRasterizer, blob_image_renderer: Option>, blob_image_requests: HashSet, @@ -270,10 +212,8 @@ impl ResourceCache { pub fn new(texture_cache: TextureCache, workers: Arc, blob_image_renderer: Option>) -> ResourceCache { - let (glyph_cache_tx, glyph_cache_result_queue) = spawn_glyph_cache_thread(workers); - ResourceCache { - cached_glyphs: Some(ResourceClassCache::new()), + cached_glyphs: ResourceClassCache::new(), cached_images: ResourceClassCache::new(), webgl_textures: HashMap::default(), font_templates: HashMap::default(), @@ -283,8 +223,7 @@ impl ResourceCache { state: State::Idle, current_frame_id: FrameId(0), pending_image_requests: Vec::new(), - glyph_cache_tx: glyph_cache_tx, - glyph_cache_result_queue: glyph_cache_result_queue, + glyph_rasterizer: GlyphRasterizer::new(workers), blob_image_renderer: blob_image_renderer, blob_image_requests: HashSet::new(), @@ -309,18 +248,14 @@ impl ResourceCache { } pub fn add_font_template(&mut self, font_key: FontKey, template: FontTemplate) { - // Push the new font to the glyph cache thread, and also store + // Push the new font to the font renderer, and also store // it locally for glyph metric requests. - self.glyph_cache_tx - .send(GlyphCacheMsg::AddFont(font_key, template.clone())) - .unwrap(); + self.glyph_rasterizer.add_font(font_key, template.clone()); self.font_templates.insert(font_key, template); } pub fn delete_font_template(&mut self, font_key: FontKey) { - self.glyph_cache_tx - .send(GlyphCacheMsg::DeleteFont(font_key)) - .unwrap(); + self.glyph_rasterizer.delete_font(font_key); self.font_templates.remove(&font_key); } @@ -492,16 +427,17 @@ impl ResourceCache { render_mode: FontRenderMode, glyph_options: Option) { debug_assert_eq!(self.state, State::AddResources); - // Immediately request that the glyph cache thread start - // rasterizing glyphs from this request if they aren't - // already cached. - let msg = GlyphCacheMsg::RequestGlyphs(key, - size, - color, - glyph_instances.to_vec(), - render_mode, - glyph_options); - self.glyph_cache_tx.send(msg).unwrap(); + + self.glyph_rasterizer.request_glyphs( + &mut self.cached_glyphs, + self.current_frame_id, + key, + size, + color, + glyph_instances, + render_mode, + glyph_options, + ); } pub fn pending_updates(&mut self) -> TextureUpdateList { @@ -517,20 +453,21 @@ impl ResourceCache { glyph_options: Option, mut f: F) -> SourceTexture where F: FnMut(usize, DevicePoint, DevicePoint) { debug_assert_eq!(self.state, State::QueryResources); - let cache = self.cached_glyphs.as_ref().unwrap(); - let mut glyph_key = RenderedGlyphKey::new(font_key, - size, - color, - 0, - LayoutPoint::new(0.0, 0.0), - render_mode, - glyph_options); + let mut glyph_key = GlyphRequest::new( + font_key, + size, + color, + 0, + LayoutPoint::new(0.0, 0.0), + render_mode, + glyph_options + ); let mut texture_id = None; for (loop_index, glyph_instance) in glyph_instances.iter().enumerate() { glyph_key.key.index = glyph_instance.index; glyph_key.key.subpixel_point.set_offset(glyph_instance.point, render_mode); - let image_id = cache.get(&glyph_key, self.current_frame_id); + let image_id = self.cached_glyphs.get(&glyph_key, self.current_frame_id); let cache_item = image_id.map(|image_id| self.texture_cache.get(image_id)); if let Some(cache_item) = cache_item { let uv0 = DevicePoint::new(cache_item.pixel_rect.top_left.x as f32, @@ -551,25 +488,7 @@ impl ResourceCache { match self.cached_glyph_dimensions.entry(glyph_key.clone()) { Occupied(entry) => *entry.get(), Vacant(entry) => { - let mut dimensions = None; - let font_template = &self.font_templates[&glyph_key.font_key]; - - FONT_CONTEXT.with(|font_context| { - let mut font_context = font_context.borrow_mut(); - match *font_template { - FontTemplate::Raw(ref bytes, index) => { - font_context.add_raw_font(&glyph_key.font_key, &**bytes, index); - } - FontTemplate::Native(ref native_font_handle) => { - font_context.add_native_font(&glyph_key.font_key, - (*native_font_handle).clone()); - } - } - - dimensions = font_context.get_glyph_dimensions(glyph_key); - }); - - *entry.insert(dimensions) + *entry.insert(self.glyph_rasterizer.get_glyph_dimensions(glyph_key)) } } } @@ -638,17 +557,13 @@ impl ResourceCache { pub fn expire_old_resources(&mut self, frame_id: FrameId) { self.cached_images.expire_old_resources(&mut self.texture_cache, frame_id); - - let cached_glyphs = self.cached_glyphs.as_mut().unwrap(); - cached_glyphs.expire_old_resources(&mut self.texture_cache, frame_id); + self.cached_glyphs.expire_old_resources(&mut self.texture_cache, frame_id); } pub fn begin_frame(&mut self, frame_id: FrameId) { debug_assert_eq!(self.state, State::Idle); self.state = State::AddResources; self.current_frame_id = frame_id; - let glyph_cache = self.cached_glyphs.take().unwrap(); - self.glyph_cache_tx.send(GlyphCacheMsg::BeginFrame(frame_id, glyph_cache)).ok(); } pub fn block_until_all_resources_added(&mut self, @@ -658,53 +573,12 @@ impl ResourceCache { debug_assert_eq!(self.state, State::AddResources); self.state = State::QueryResources; - // Tell the glyph cache thread that all glyphs have been requested - // and block, waiting for any pending glyphs to be rasterized. In the - // future, we will expand this to have a timeout. If the glyph rasterizing - // takes longer than the timeout, then we will select the best glyphs - // available in the cache, render with those, and then re-render at - // a later point when the correct resolution glyphs finally become - // available. - self.glyph_cache_tx.send(GlyphCacheMsg::EndFrame).unwrap(); - - // Loop until the end frame message is retrieved here. This loop - // doesn't serve any real purpose right now, but in the future - // it will be receiving small amounts of glyphs at a time, up until - // it decides that it should just render the frame. - while let Ok(result) = self.glyph_cache_result_queue.recv() { - match result { - GlyphCacheResultMsg::EndFrame(mut cache, glyph_jobs) => { - // Add any newly rasterized glyphs to the texture cache. - for job in glyph_jobs { - let image_id = job.result.and_then(|glyph| { - if glyph.width > 0 && glyph.height > 0 { - let image_id = self.texture_cache.new_item_id(); - self.texture_cache.insert(image_id, - ImageDescriptor { - width: glyph.width, - height: glyph.height, - stride: None, - format: ImageFormat::RGBA8, - is_opaque: false, - offset: 0, - }, - TextureFilter::Linear, - ImageData::Raw(Arc::new(glyph.bytes)), - texture_cache_profile); - Some(image_id) - } else { - None - } - }); - - cache.insert(job.key, image_id, self.current_frame_id); - } - - self.cached_glyphs = Some(cache); - break; - } - } - } + self.glyph_rasterizer.resolve_glyphs( + self.current_frame_id, + &mut self.cached_glyphs, + &mut self.texture_cache, + texture_cache_profile, + ); let mut image_requests = mem::replace(&mut self.pending_image_requests, Vec::new()); for request in image_requests.drain(..) { @@ -877,174 +751,6 @@ impl Resource for CachedImageInfo { } } -fn spawn_glyph_cache_thread(workers: Arc) -> (Sender, Receiver) { - let worker_count = { - workers.current_num_threads() - }; - // Used for messages from resource cache -> glyph cache thread. - let (msg_tx, msg_rx) = channel(); - // Used for returning results from glyph cache thread -> resource cache. - let (result_tx, result_rx) = channel(); - // Used for rasterizer worker threads to send glyphs -> glyph cache thread. - let (glyph_tx, glyph_rx) = channel(); - - thread::Builder::new().name("GlyphCache".to_string()).spawn(move|| { - let mut glyph_cache = None; - let mut current_frame_id = FrameId(0); - - register_thread_with_profiler("GlyphCache".to_string()); - - let barrier = Arc::new(Barrier::new(worker_count)); - for i in 0..worker_count { - let barrier = Arc::clone(&barrier); - workers.spawn_async(move || { - register_thread_with_profiler(format!("Glyph Worker {}", i)); - barrier.wait(); - }); - } - - // Maintain a set of glyphs that have been requested this - // frame. This ensures the glyph thread won't rasterize - // the same glyph more than once in a frame. This is required - // because the glyph cache hash table is not updated - // until the glyph cache is passed back to the resource - // cache which is able to add the items to the texture cache. - let mut pending_glyphs = HashSet::new(); - - while let Ok(msg) = msg_rx.recv() { - profile_scope!("handle_msg"); - match msg { - GlyphCacheMsg::BeginFrame(frame_id, cache) => { - profile_scope!("BeginFrame"); - - // We are beginning a new frame. Take ownership of the glyph - // cache hash map, so we can easily see which glyph requests - // actually need to be rasterized. - current_frame_id = frame_id; - glyph_cache = Some(cache); - } - GlyphCacheMsg::AddFont(font_key, font_template) => { - profile_scope!("AddFont"); - - // Add a new font to the font context in each worker thread. - // Use a barrier to ensure that each worker in the pool handles - // one of these messages, to ensure that the new font gets - // added to each worker thread. - let barrier = Arc::new(Barrier::new(worker_count)); - for _ in 0..worker_count { - let barrier = Arc::clone(&barrier); - let font_template = font_template.clone(); - workers.spawn_async(move || { - FONT_CONTEXT.with(|font_context| { - let mut font_context = font_context.borrow_mut(); - match font_template { - FontTemplate::Raw(ref bytes, index) => { - font_context.add_raw_font(&font_key, &**bytes, index); - } - FontTemplate::Native(ref native_font_handle) => { - font_context.add_native_font(&font_key, - (*native_font_handle).clone()); - } - } - }); - - barrier.wait(); - }); - } - } - GlyphCacheMsg::DeleteFont(font_key) => { - profile_scope!("DeleteFont"); - - // Delete a font from the font context in each worker thread. - let barrier = Arc::new(Barrier::new(worker_count)); - for _ in 0..worker_count { - let barrier = Arc::clone(&barrier); - workers.spawn_async(move || { - FONT_CONTEXT.with(|font_context| { - let mut font_context = font_context.borrow_mut(); - font_context.delete_font(&font_key); - }); - barrier.wait(); - }); - } - - } - GlyphCacheMsg::RequestGlyphs(key, size, color, glyph_instances, render_mode, glyph_options) => { - profile_scope!("RequestGlyphs"); - - // Request some glyphs for a text run. - // For any glyph that isn't currently in the cache, - // immeediately push a job to the worker thread pool - // to start rasterizing this glyph now! - let glyph_cache = glyph_cache.as_mut().unwrap(); - - for glyph_instance in glyph_instances { - let glyph_key = RenderedGlyphKey::new(key, - size, - color, - glyph_instance.index, - glyph_instance.point, - render_mode, - glyph_options); - - glyph_cache.mark_as_needed(&glyph_key, current_frame_id); - if !glyph_cache.contains_key(&glyph_key) && - !pending_glyphs.contains(&glyph_key) { - let glyph_tx = glyph_tx.clone(); - pending_glyphs.insert(glyph_key.clone()); - workers.spawn_async(move || { - profile_scope!("glyph"); - FONT_CONTEXT.with(move |font_context| { - let mut font_context = font_context.borrow_mut(); - let result = font_context.rasterize_glyph(&glyph_key.key, - render_mode, - glyph_options); - if let Some(ref glyph) = result { - assert_eq!(glyph.bytes.len(), 4 * (glyph.width * glyph.height) as usize); - } - glyph_tx.send((glyph_key, result)).unwrap(); - }); - }); - } - } - } - GlyphCacheMsg::EndFrame => { - profile_scope!("EndFrame"); - - // The resource cache has finished requesting glyphs. Block - // on completion of any pending glyph rasterizing jobs, and then - // return the list of new glyphs to the resource cache. - let cache = glyph_cache.take().unwrap(); - let mut rasterized_glyphs = Vec::new(); - while !pending_glyphs.is_empty() { - let (key, glyph) = glyph_rx.recv() - .expect("BUG: Should be glyphs pending!"); - debug_assert!(pending_glyphs.contains(&key)); - pending_glyphs.remove(&key); - if let Some(ref v) = glyph { - debug!("received {}x{} data len {}", v.width, v.height, v.bytes.len()); - } - rasterized_glyphs.push(GlyphRasterJob { - key: key, - result: glyph, - }); - } - // Ensure that the glyphs are always processed in the same - // order for a given text run (since iterating a hash set doesn't - // guarantee order). This can show up as very small float inaccuacry - // differences in rasterizers due to the different coordinates - // that text runs get associated with by the texture cache allocator. - rasterized_glyphs.sort_by(|a, b| { - a.key.cmp(&b.key) - }); - result_tx.send(GlyphCacheResultMsg::EndFrame(cache, rasterized_glyphs)).unwrap(); - } - } - } - }).unwrap(); - - (msg_tx, result_rx) -} // Compute the width and height of a tile depending on its position in the image. pub fn compute_tile_size(descriptor: &ImageDescriptor, diff --git a/gfx/webrender/src/tiling.rs b/gfx/webrender/src/tiling.rs index b0d4a8846fd5..108f741c6d1f 100644 --- a/gfx/webrender/src/tiling.rs +++ b/gfx/webrender/src/tiling.rs @@ -29,8 +29,7 @@ use webrender_traits::{BuiltDisplayList, ClipAndScrollInfo, ClipId, ColorF, Devi use webrender_traits::{DeviceIntRect, DeviceIntSize, DeviceUintPoint, DeviceUintSize}; use webrender_traits::{ExternalImageType, FontRenderMode, ImageRendering, LayerPoint, LayerRect}; use webrender_traits::{LayerToWorldTransform, MixBlendMode, PipelineId, TransformStyle}; -use webrender_traits::{WorldPoint4D, WorldToLayerTransform}; -use webrender_traits::{YuvColorSpace, YuvFormat}; +use webrender_traits::{WorldToLayerTransform, YuvColorSpace, YuvFormat}; // Special sentinel value recognized by the shader. It is considered to be // a dummy task that doesn't mask out anything. diff --git a/gfx/webrender/src/util.rs b/gfx/webrender/src/util.rs index afa807ce825d..1b299a95de8f 100644 --- a/gfx/webrender/src/util.rs +++ b/gfx/webrender/src/util.rs @@ -150,6 +150,15 @@ pub fn subtract_rect(rect: &TypedRect, } } } + +pub fn get_normal(x: f32) -> Option { + if x.is_normal() { + Some(x) + } else { + None + } +} + #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[repr(u8)] pub enum TransformedRectKind { @@ -234,8 +243,8 @@ impl TransformedRect { for (vertex, (x, y)) in vertices.iter().zip(xs.iter_mut().zip(ys.iter_mut())) { let inv_w = 1.0 / vertex.w; - *x = vertex.x * inv_w; - *y = vertex.y * inv_w; + *x = get_normal(vertex.x * inv_w).unwrap_or(0.0); + *y = get_normal(vertex.y * inv_w).unwrap_or(0.0); } xs.sort_by(|a, b| a.partial_cmp(b).unwrap()); @@ -254,11 +263,11 @@ impl TransformedRect { local_rect: *rect, vertices: vertices, bounding_rect: DeviceIntRect::new(outer_min_dp, - DeviceIntSize::new(outer_max_dp.x - outer_min_dp.x, - outer_max_dp.y - outer_min_dp.y)), + DeviceIntSize::new(outer_max_dp.x.saturating_sub(outer_min_dp.x), + outer_max_dp.y.saturating_sub(outer_min_dp.y))), inner_rect: DeviceIntRect::new(inner_min_dp, - DeviceIntSize::new(inner_max_dp.x - inner_min_dp.x, - inner_max_dp.y - inner_min_dp.y)), + DeviceIntSize::new(inner_max_dp.x.saturating_sub(inner_min_dp.x), + inner_max_dp.y.saturating_sub(inner_min_dp.y))), kind: kind, } /* diff --git a/gfx/webrender_bindings/Cargo.toml b/gfx/webrender_bindings/Cargo.toml index 950fcb72d6a3..b7da11df1401 100644 --- a/gfx/webrender_bindings/Cargo.toml +++ b/gfx/webrender_bindings/Cargo.toml @@ -5,12 +5,12 @@ authors = ["The Mozilla Project Developers"] license = "MPL-2.0" [dependencies] -webrender_traits = {path = "../webrender_traits", version = "0.39.0"} -euclid = "0.11" +webrender_traits = {path = "../webrender_traits", version = "0.40.0"} +euclid = "0.13" app_units = "0.4" gleam = "0.4" [dependencies.webrender] path = "../webrender" -version = "0.39.0" +version = "0.40.0" default-features = false diff --git a/gfx/webrender_bindings/WebRenderAPI.cpp b/gfx/webrender_bindings/WebRenderAPI.cpp index f52ed7e06ff3..3b03c0ec0186 100644 --- a/gfx/webrender_bindings/WebRenderAPI.cpp +++ b/gfx/webrender_bindings/WebRenderAPI.cpp @@ -216,6 +216,7 @@ WebRenderAPI::SetRootDisplayList(gfx::Color aBgColor, size_t dl_size) { wr_api_set_root_display_list(mWrApi, + ToWrColor(aBgColor), aEpoch, aViewportSize.width, aViewportSize.height, pipeline_id, @@ -568,16 +569,6 @@ DisplayListBuilder::PushStackingContext(const WrRect& aBounds, maybeTransform, aMixBlendMode); } -void -DisplayListBuilder::PushStackingContext(const WrRect& aBounds, - const float aOpacity, - const gfx::Matrix4x4& aTransform, - const WrMixBlendMode& aMixBlendMode) -{ - PushStackingContext(aBounds, 0, &aOpacity, - &aTransform, aMixBlendMode); -} - void DisplayListBuilder::PopStackingContext() { @@ -703,7 +694,8 @@ DisplayListBuilder::PushYCbCrPlanarImage(const WrRect& aBounds, wr::ImageKey aImageChannel0, wr::ImageKey aImageChannel1, wr::ImageKey aImageChannel2, - WrYuvColorSpace aColorSpace) + WrYuvColorSpace aColorSpace, + wr::ImageRendering aRendering) { wr_dp_push_yuv_planar_image(mWrState, aBounds, @@ -711,7 +703,8 @@ DisplayListBuilder::PushYCbCrPlanarImage(const WrRect& aBounds, aImageChannel0, aImageChannel1, aImageChannel2, - aColorSpace); + aColorSpace, + aRendering); } void @@ -719,27 +712,31 @@ DisplayListBuilder::PushNV12Image(const WrRect& aBounds, const WrClipRegionToken aClip, wr::ImageKey aImageChannel0, wr::ImageKey aImageChannel1, - WrYuvColorSpace aColorSpace) + WrYuvColorSpace aColorSpace, + wr::ImageRendering aRendering) { wr_dp_push_yuv_NV12_image(mWrState, aBounds, aClip, aImageChannel0, aImageChannel1, - aColorSpace); + aColorSpace, + aRendering); } void DisplayListBuilder::PushYCbCrInterleavedImage(const WrRect& aBounds, const WrClipRegionToken aClip, wr::ImageKey aImageChannel0, - WrYuvColorSpace aColorSpace) + WrYuvColorSpace aColorSpace, + wr::ImageRendering aRendering) { wr_dp_push_yuv_interleaved_image(mWrState, aBounds, aClip, aImageChannel0, - aColorSpace); + aColorSpace, + aRendering); } void diff --git a/gfx/webrender_bindings/WebRenderAPI.h b/gfx/webrender_bindings/WebRenderAPI.h index 6b6813748891..5b8e6d2fac86 100644 --- a/gfx/webrender_bindings/WebRenderAPI.h +++ b/gfx/webrender_bindings/WebRenderAPI.h @@ -144,11 +144,6 @@ public: void Finalize(WrSize& aOutContentSize, wr::BuiltDisplayList& aOutDisplayList); - void PushStackingContext(const WrRect& aBounds, // TODO: We should work with strongly typed rects - const float aOpacity, - const gfx::Matrix4x4& aTransform, - const WrMixBlendMode& aMixBlendMode); - void PushStackingContext(const WrRect& aBounds, // TODO: We should work with strongly typed rects const uint64_t& aAnimationId, const float* aOpacity, @@ -207,18 +202,21 @@ public: wr::ImageKey aImageChannel0, wr::ImageKey aImageChannel1, wr::ImageKey aImageChannel2, - WrYuvColorSpace aColorSpace); + WrYuvColorSpace aColorSpace, + wr::ImageRendering aFilter); void PushNV12Image(const WrRect& aBounds, const WrClipRegionToken aClip, wr::ImageKey aImageChannel0, wr::ImageKey aImageChannel1, - WrYuvColorSpace aColorSpace); + WrYuvColorSpace aColorSpace, + wr::ImageRendering aFilter); void PushYCbCrInterleavedImage(const WrRect& aBounds, const WrClipRegionToken aClip, wr::ImageKey aImageChannel0, - WrYuvColorSpace aColorSpace); + WrYuvColorSpace aColorSpace, + wr::ImageRendering aFilter); void PushIFrame(const WrRect& aBounds, const WrClipRegionToken aClip, diff --git a/gfx/webrender_bindings/src/bindings.rs b/gfx/webrender_bindings/src/bindings.rs index b62e0f1e6cce..027284e417fd 100644 --- a/gfx/webrender_bindings/src/bindings.rs +++ b/gfx/webrender_bindings/src/bindings.rs @@ -881,6 +881,7 @@ pub extern "C" fn wr_window_new(window_id: WrWindowId, enable_profiler: enable_profiler, recorder: recorder, blob_image_renderer: Some(Box::new(Moz2dImageRenderer::new())), + cache_expiry_frames: 60, // see https://github.com/servo/webrender/pull/1294#issuecomment-304318800 ..Default::default() }; @@ -1009,6 +1010,7 @@ pub extern "C" fn wr_api_set_window_parameters(api: &mut WrAPI, #[no_mangle] pub unsafe extern "C" fn wr_api_set_root_display_list(api: &mut WrAPI, + color: WrColor, epoch: WrEpoch, viewport_width: f32, viewport_height: f32, @@ -1017,7 +1019,11 @@ pub unsafe extern "C" fn wr_api_set_root_display_list(api: &mut WrAPI, dl_descriptor: WrBuiltDisplayListDescriptor, dl_data: *mut u8, dl_size: usize) { - let root_background_color = ColorF::new(0.3, 0.0, 0.0, 1.0); + let color = if color.a == 0.0 { + None + } else { + Some(color.into()) + }; // See the documentation of set_display_list in api.rs. I don't think // it makes a difference in gecko at the moment(until APZ is figured out) // but I suppose it is a good default. @@ -1029,7 +1035,7 @@ pub unsafe extern "C" fn wr_api_set_root_display_list(api: &mut WrAPI, dl_vec.extend_from_slice(dl_slice); let dl = BuiltDisplayList::from_data(dl_vec, dl_descriptor); - api.set_display_list(Some(root_background_color), + api.set_display_list(color, epoch, LayoutSize::new(viewport_width, viewport_height), (pipeline_id, content_size.into(), dl), @@ -1040,11 +1046,10 @@ pub unsafe extern "C" fn wr_api_set_root_display_list(api: &mut WrAPI, pub unsafe extern "C" fn wr_api_clear_root_display_list(api: &mut WrAPI, epoch: WrEpoch, pipeline_id: WrPipelineId) { - let root_background_color = ColorF::new(0.3, 0.0, 0.0, 1.0); let preserve_frame_state = true; let frame_builder = WebRenderFrameBuilder::new(pipeline_id, WrSize::zero()); - api.set_display_list(Some(root_background_color), + api.set_display_list(None, epoch, LayoutSize::new(0.0, 0.0), frame_builder.dl_builder.finalize(), @@ -1256,16 +1261,19 @@ pub extern "C" fn wr_dp_push_stacking_context(state: &mut WrState, } let transform = unsafe { transform.as_ref() }; - let transform_binding = match transform { - Some(transform) => PropertyBinding::Value(transform.into()), - None => PropertyBinding::Binding(PropertyBindingKey::new(animation_id)), + let transform_binding = match animation_id { + 0 => match transform { + Some(transform) => Some(PropertyBinding::Value(transform.into())), + None => None, + }, + _ => Some(PropertyBinding::Binding(PropertyBindingKey::new(animation_id))), }; state.frame_builder .dl_builder .push_stacking_context(webrender_traits::ScrollPolicy::Scrollable, bounds, - Some(transform_binding), + transform_binding, TransformStyle::Flat, None, mix_blend_mode, @@ -1378,7 +1386,8 @@ pub extern "C" fn wr_dp_push_yuv_planar_image(state: &mut WrState, image_key_0: WrImageKey, image_key_1: WrImageKey, image_key_2: WrImageKey, - color_space: WrYuvColorSpace) { + color_space: WrYuvColorSpace, + image_rendering: WrImageRendering) { assert!(unsafe { is_in_main_thread() }); state.frame_builder @@ -1386,7 +1395,8 @@ pub extern "C" fn wr_dp_push_yuv_planar_image(state: &mut WrState, .push_yuv_image(bounds.into(), clip.into(), YuvData::PlanarYCbCr(image_key_0, image_key_1, image_key_2), - color_space); + color_space, + image_rendering); } /// Push a 2 planar NV12 image. @@ -1396,7 +1406,8 @@ pub extern "C" fn wr_dp_push_yuv_NV12_image(state: &mut WrState, clip: WrClipRegionToken, image_key_0: WrImageKey, image_key_1: WrImageKey, - color_space: WrYuvColorSpace) { + color_space: WrYuvColorSpace, + image_rendering: WrImageRendering) { assert!(unsafe { is_in_main_thread() }); state.frame_builder @@ -1404,7 +1415,8 @@ pub extern "C" fn wr_dp_push_yuv_NV12_image(state: &mut WrState, .push_yuv_image(bounds.into(), clip.into(), YuvData::NV12(image_key_0, image_key_1), - color_space); + color_space, + image_rendering); } /// Push a yuv interleaved image. @@ -1413,7 +1425,8 @@ pub extern "C" fn wr_dp_push_yuv_interleaved_image(state: &mut WrState, bounds: WrRect, clip: WrClipRegionToken, image_key_0: WrImageKey, - color_space: WrYuvColorSpace) { + color_space: WrYuvColorSpace, + image_rendering: WrImageRendering) { assert!(unsafe { is_in_main_thread() }); state.frame_builder @@ -1421,7 +1434,8 @@ pub extern "C" fn wr_dp_push_yuv_interleaved_image(state: &mut WrState, .push_yuv_image(bounds.into(), clip.into(), YuvData::InterleavedYCbCr(image_key_0), - color_space); + color_space, + image_rendering); } #[no_mangle] diff --git a/gfx/webrender_bindings/webrender_ffi_generated.h b/gfx/webrender_bindings/webrender_ffi_generated.h index 7d6ce078bd66..08506b069a52 100644 --- a/gfx/webrender_bindings/webrender_ffi_generated.h +++ b/gfx/webrender_bindings/webrender_ffi_generated.h @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -/* Generated with cbindgen:0.1.7 */ +/* Generated with cbindgen:0.1.10 */ /* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen. * To generate this file, clone `https://github.com/rlhunt/cbindgen` or run `cargo install cbindgen`, @@ -116,6 +116,12 @@ enum class WrYuvColorSpace : uint32_t { struct WrAPI; +struct WrRenderedEpochs; + +struct WrRenderer; + +struct WrState; + struct WrImageKey { uint32_t mNamespace; uint32_t mHandle; @@ -143,7 +149,7 @@ struct WrImageDescriptor { }; struct WrByteSlice { - const uint8_t* buffer; + const uint8_t *buffer; size_t len; bool operator==(const WrByteSlice& aOther) const { @@ -194,8 +200,6 @@ struct WrPipelineId { } }; -struct WrState; - struct WrSize { float width; float height; @@ -219,7 +223,7 @@ struct WrBuiltDisplayListDescriptor { }; struct WrVecU8 { - uint8_t* data; + uint8_t *data; size_t length; size_t capacity; @@ -263,6 +267,20 @@ struct WrIdNamespace { } }; +struct WrColor { + float r; + float g; + float b; + float a; + + bool operator==(const WrColor& aOther) const { + return r == aOther.r && + g == aOther.g && + b == aOther.b && + a == aOther.a; + } +}; + struct WrRect { float x; float y; @@ -299,20 +317,6 @@ struct WrBorderWidths { } }; -struct WrColor { - float r; - float g; - float b; - float a; - - bool operator==(const WrColor& aOther) const { - return r == aOther.r && - g == aOther.g && - b == aOther.b && - a == aOther.a; - } -}; - struct WrBorderSide { WrColor color; WrBorderStyle style; @@ -430,7 +434,7 @@ struct WrGlyphInstance { }; struct MutByteSlice { - uint8_t* buffer; + uint8_t *buffer; size_t len; bool operator==(const MutByteSlice& aOther) const { @@ -453,10 +457,6 @@ struct WrWindowId { } }; -struct WrRenderedEpochs; - -struct WrRenderer; - struct WrExternalImage { WrExternalImageType image_type; uint32_t handle; @@ -464,7 +464,7 @@ struct WrExternalImage { float v0; float u1; float v1; - const uint8_t* buff; + const uint8_t *buff; size_t size; bool operator==(const WrExternalImage& aOther) const { @@ -484,7 +484,7 @@ typedef WrExternalImage (*LockExternalImageCallback)(void*, WrExternalImageId, u typedef void (*UnlockExternalImageCallback)(void*, WrExternalImageId, uint8_t); struct WrExternalImageHandler { - void* external_image_obj; + void *external_image_obj; LockExternalImageCallback lock_func; UnlockExternalImageCallback unlock_func; @@ -500,145 +500,146 @@ struct WrExternalImageHandler { * then run `cbindgen gfx/webrender_bindings/ -o gfx/webrender_bindings/webrender_ffi_generated.h` */ WR_INLINE -void wr_api_add_blob_image(WrAPI* aApi, +void wr_api_add_blob_image(WrAPI *aApi, WrImageKey aImageKey, - const WrImageDescriptor* aDescriptor, + const WrImageDescriptor *aDescriptor, WrByteSlice aBytes) WR_FUNC; WR_INLINE -void wr_api_add_external_image(WrAPI* aApi, +void wr_api_add_external_image(WrAPI *aApi, WrImageKey aImageKey, - const WrImageDescriptor* aDescriptor, + const WrImageDescriptor *aDescriptor, WrExternalImageId aExternalImageId, WrExternalImageBufferType aBufferType, uint8_t aChannelIndex) WR_FUNC; WR_INLINE -void wr_api_add_external_image_buffer(WrAPI* aApi, +void wr_api_add_external_image_buffer(WrAPI *aApi, WrImageKey aImageKey, - const WrImageDescriptor* aDescriptor, + const WrImageDescriptor *aDescriptor, WrExternalImageId aExternalImageId) WR_FUNC; WR_INLINE -void wr_api_add_image(WrAPI* aApi, +void wr_api_add_image(WrAPI *aApi, WrImageKey aImageKey, - const WrImageDescriptor* aDescriptor, + const WrImageDescriptor *aDescriptor, WrByteSlice aBytes) WR_FUNC; WR_INLINE -void wr_api_add_raw_font(WrAPI* aApi, +void wr_api_add_raw_font(WrAPI *aApi, WrFontKey aKey, - uint8_t* aFontBuffer, + uint8_t *aFontBuffer, size_t aBufferSize, uint32_t aIndex) WR_FUNC; WR_INLINE -void wr_api_clear_root_display_list(WrAPI* aApi, +void wr_api_clear_root_display_list(WrAPI *aApi, WrEpoch aEpoch, WrPipelineId aPipelineId) WR_FUNC; WR_INLINE -void wr_api_delete(WrAPI* aApi) +void wr_api_delete(WrAPI *aApi) WR_DESTRUCTOR_SAFE_FUNC; WR_INLINE -void wr_api_delete_font(WrAPI* aApi, +void wr_api_delete_font(WrAPI *aApi, WrFontKey aKey) WR_FUNC; WR_INLINE -void wr_api_delete_image(WrAPI* aApi, +void wr_api_delete_image(WrAPI *aApi, WrImageKey aKey) WR_FUNC; WR_INLINE -void wr_api_finalize_builder(WrState* aState, - WrSize* aContentSize, - WrBuiltDisplayListDescriptor* aDlDescriptor, - WrVecU8* aDlData) +void wr_api_finalize_builder(WrState *aState, + WrSize *aContentSize, + WrBuiltDisplayListDescriptor *aDlDescriptor, + WrVecU8 *aDlData) WR_FUNC; WR_INLINE -void wr_api_generate_frame(WrAPI* aApi) +void wr_api_generate_frame(WrAPI *aApi) WR_FUNC; WR_INLINE -void wr_api_generate_frame_with_properties(WrAPI* aApi, - const WrOpacityProperty* aOpacityArray, +void wr_api_generate_frame_with_properties(WrAPI *aApi, + const WrOpacityProperty *aOpacityArray, size_t aOpacityCount, - const WrTransformProperty* aTransformArray, + const WrTransformProperty *aTransformArray, size_t aTransformCount) WR_FUNC; WR_INLINE -WrIdNamespace wr_api_get_namespace(WrAPI* aApi) +WrIdNamespace wr_api_get_namespace(WrAPI *aApi) WR_FUNC; WR_INLINE -void wr_api_send_external_event(WrAPI* aApi, +void wr_api_send_external_event(WrAPI *aApi, size_t aEvt) WR_DESTRUCTOR_SAFE_FUNC; WR_INLINE -void wr_api_set_root_display_list(WrAPI* aApi, +void wr_api_set_root_display_list(WrAPI *aApi, + WrColor aColor, WrEpoch aEpoch, float aViewportWidth, float aViewportHeight, WrPipelineId aPipelineId, WrSize aContentSize, WrBuiltDisplayListDescriptor aDlDescriptor, - uint8_t* aDlData, + uint8_t *aDlData, size_t aDlSize) WR_FUNC; WR_INLINE -void wr_api_set_root_pipeline(WrAPI* aApi, +void wr_api_set_root_pipeline(WrAPI *aApi, WrPipelineId aPipelineId) WR_FUNC; WR_INLINE -void wr_api_set_window_parameters(WrAPI* aApi, +void wr_api_set_window_parameters(WrAPI *aApi, int32_t aWidth, int32_t aHeight) WR_FUNC; WR_INLINE -void wr_api_update_image(WrAPI* aApi, +void wr_api_update_image(WrAPI *aApi, WrImageKey aKey, - const WrImageDescriptor* aDescriptor, + const WrImageDescriptor *aDescriptor, WrByteSlice aBytes) WR_FUNC; WR_INLINE -void wr_dp_begin(WrState* aState, +void wr_dp_begin(WrState *aState, uint32_t aWidth, uint32_t aHeight) WR_FUNC; WR_INLINE -void wr_dp_end(WrState* aState) +void wr_dp_end(WrState *aState) WR_FUNC; WR_INLINE -void wr_dp_pop_clip(WrState* aState) +void wr_dp_pop_clip(WrState *aState) WR_FUNC; WR_INLINE -void wr_dp_pop_scroll_layer(WrState* aState) +void wr_dp_pop_scroll_layer(WrState *aState) WR_FUNC; WR_INLINE -void wr_dp_pop_stacking_context(WrState* aState) +void wr_dp_pop_stacking_context(WrState *aState) WR_FUNC; WR_INLINE -void wr_dp_push_border(WrState* aState, +void wr_dp_push_border(WrState *aState, WrRect aRect, WrClipRegionToken aClip, WrBorderWidths aWidths, @@ -650,20 +651,20 @@ void wr_dp_push_border(WrState* aState, WR_FUNC; WR_INLINE -void wr_dp_push_border_gradient(WrState* aState, +void wr_dp_push_border_gradient(WrState *aState, WrRect aRect, WrClipRegionToken aClip, WrBorderWidths aWidths, WrPoint aStartPoint, WrPoint aEndPoint, - const WrGradientStop* aStops, + const WrGradientStop *aStops, size_t aStopsCount, WrGradientExtendMode aExtendMode, WrSideOffsets2Df32 aOutset) WR_FUNC; WR_INLINE -void wr_dp_push_border_image(WrState* aState, +void wr_dp_push_border_image(WrState *aState, WrRect aRect, WrClipRegionToken aClip, WrBorderWidths aWidths, @@ -675,20 +676,20 @@ void wr_dp_push_border_image(WrState* aState, WR_FUNC; WR_INLINE -void wr_dp_push_border_radial_gradient(WrState* aState, +void wr_dp_push_border_radial_gradient(WrState *aState, WrRect aRect, WrClipRegionToken aClip, WrBorderWidths aWidths, WrPoint aCenter, WrSize aRadius, - const WrGradientStop* aStops, + const WrGradientStop *aStops, size_t aStopsCount, WrGradientExtendMode aExtendMode, WrSideOffsets2Df32 aOutset) WR_FUNC; WR_INLINE -void wr_dp_push_box_shadow(WrState* aState, +void wr_dp_push_box_shadow(WrState *aState, WrRect aRect, WrClipRegionToken aClip, WrRect aBoxBounds, @@ -701,34 +702,34 @@ void wr_dp_push_box_shadow(WrState* aState, WR_FUNC; WR_INLINE -void wr_dp_push_built_display_list(WrState* aState, +void wr_dp_push_built_display_list(WrState *aState, WrBuiltDisplayListDescriptor aDlDescriptor, WrVecU8 aDlData) WR_FUNC; WR_INLINE -void wr_dp_push_clip(WrState* aState, +void wr_dp_push_clip(WrState *aState, WrRect aClipRect, - const WrImageMask* aMask) + const WrImageMask *aMask) WR_FUNC; WR_INLINE -WrClipRegionToken wr_dp_push_clip_region(WrState* aState, +WrClipRegionToken wr_dp_push_clip_region(WrState *aState, WrRect aMain, - const WrComplexClipRegion* aComplex, + const WrComplexClipRegion *aComplex, size_t aComplexCount, - const WrImageMask* aImageMask) + const WrImageMask *aImageMask) WR_FUNC; WR_INLINE -void wr_dp_push_iframe(WrState* aState, +void wr_dp_push_iframe(WrState *aState, WrRect aRect, WrClipRegionToken aClip, WrPipelineId aPipelineId) WR_FUNC; WR_INLINE -void wr_dp_push_image(WrState* aState, +void wr_dp_push_image(WrState *aState, WrRect aBounds, WrClipRegionToken aClip, WrSize aStretchSize, @@ -738,12 +739,12 @@ void wr_dp_push_image(WrState* aState, WR_FUNC; WR_INLINE -void wr_dp_push_linear_gradient(WrState* aState, +void wr_dp_push_linear_gradient(WrState *aState, WrRect aRect, WrClipRegionToken aClip, WrPoint aStartPoint, WrPoint aEndPoint, - const WrGradientStop* aStops, + const WrGradientStop *aStops, size_t aStopsCount, WrGradientExtendMode aExtendMode, WrSize aTileSize, @@ -751,12 +752,12 @@ void wr_dp_push_linear_gradient(WrState* aState, WR_FUNC; WR_INLINE -void wr_dp_push_radial_gradient(WrState* aState, +void wr_dp_push_radial_gradient(WrState *aState, WrRect aRect, WrClipRegionToken aClip, WrPoint aCenter, WrSize aRadius, - const WrGradientStop* aStops, + const WrGradientStop *aStops, size_t aStopsCount, WrGradientExtendMode aExtendMode, WrSize aTileSize, @@ -764,127 +765,130 @@ void wr_dp_push_radial_gradient(WrState* aState, WR_FUNC; WR_INLINE -void wr_dp_push_rect(WrState* aState, +void wr_dp_push_rect(WrState *aState, WrRect aRect, WrClipRegionToken aClip, WrColor aColor) WR_FUNC; WR_INLINE -void wr_dp_push_scroll_layer(WrState* aState, +void wr_dp_push_scroll_layer(WrState *aState, uint64_t aScrollId, WrRect aContentRect, WrRect aClipRect) WR_FUNC; WR_INLINE -void wr_dp_push_stacking_context(WrState* aState, +void wr_dp_push_stacking_context(WrState *aState, WrRect aBounds, uint64_t aAnimationId, - const float* aOpacity, - const WrMatrix* aTransform, + const float *aOpacity, + const WrMatrix *aTransform, WrMixBlendMode aMixBlendMode) WR_FUNC; WR_INLINE -void wr_dp_push_text(WrState* aState, +void wr_dp_push_text(WrState *aState, WrRect aBounds, WrClipRegionToken aClip, WrColor aColor, WrFontKey aFontKey, - const WrGlyphInstance* aGlyphs, + const WrGlyphInstance *aGlyphs, uint32_t aGlyphCount, float aGlyphSize) WR_FUNC; WR_INLINE -void wr_dp_push_yuv_NV12_image(WrState* aState, +void wr_dp_push_yuv_NV12_image(WrState *aState, WrRect aBounds, WrClipRegionToken aClip, WrImageKey aImageKey0, WrImageKey aImageKey1, - WrYuvColorSpace aColorSpace) + WrYuvColorSpace aColorSpace, + WrImageRendering aImageRendering) WR_FUNC; WR_INLINE -void wr_dp_push_yuv_interleaved_image(WrState* aState, +void wr_dp_push_yuv_interleaved_image(WrState *aState, WrRect aBounds, WrClipRegionToken aClip, WrImageKey aImageKey0, - WrYuvColorSpace aColorSpace) + WrYuvColorSpace aColorSpace, + WrImageRendering aImageRendering) WR_FUNC; WR_INLINE -void wr_dp_push_yuv_planar_image(WrState* aState, +void wr_dp_push_yuv_planar_image(WrState *aState, WrRect aBounds, WrClipRegionToken aClip, WrImageKey aImageKey0, WrImageKey aImageKey1, WrImageKey aImageKey2, - WrYuvColorSpace aColorSpace) + WrYuvColorSpace aColorSpace, + WrImageRendering aImageRendering) WR_FUNC; WR_INLINE -void wr_rendered_epochs_delete(WrRenderedEpochs* aPipelineEpochs) +void wr_rendered_epochs_delete(WrRenderedEpochs *aPipelineEpochs) WR_DESTRUCTOR_SAFE_FUNC; WR_INLINE -bool wr_rendered_epochs_next(WrRenderedEpochs* aPipelineEpochs, - WrPipelineId* aOutPipeline, - WrEpoch* aOutEpoch) +bool wr_rendered_epochs_next(WrRenderedEpochs *aPipelineEpochs, + WrPipelineId *aOutPipeline, + WrEpoch *aOutEpoch) WR_FUNC; WR_INLINE -bool wr_renderer_current_epoch(WrRenderer* aRenderer, +bool wr_renderer_current_epoch(WrRenderer *aRenderer, WrPipelineId aPipelineId, - WrEpoch* aOutEpoch) + WrEpoch *aOutEpoch) WR_FUNC; WR_INLINE -void wr_renderer_delete(WrRenderer* aRenderer) +void wr_renderer_delete(WrRenderer *aRenderer) WR_DESTRUCTOR_SAFE_FUNC; WR_INLINE -WrRenderedEpochs* wr_renderer_flush_rendered_epochs(WrRenderer* aRenderer) +WrRenderedEpochs* wr_renderer_flush_rendered_epochs(WrRenderer *aRenderer) WR_FUNC; WR_INLINE -void wr_renderer_readback(WrRenderer* aRenderer, +void wr_renderer_readback(WrRenderer *aRenderer, uint32_t aWidth, uint32_t aHeight, - uint8_t* aDstBuffer, + uint8_t *aDstBuffer, size_t aBufferSize) WR_FUNC; WR_INLINE -void wr_renderer_render(WrRenderer* aRenderer, +void wr_renderer_render(WrRenderer *aRenderer, uint32_t aWidth, uint32_t aHeight) WR_FUNC; WR_INLINE -void wr_renderer_set_external_image_handler(WrRenderer* aRenderer, - WrExternalImageHandler* aExternalImageHandler) +void wr_renderer_set_external_image_handler(WrRenderer *aRenderer, + WrExternalImageHandler *aExternalImageHandler) WR_FUNC; WR_INLINE -void wr_renderer_set_profiler_enabled(WrRenderer* aRenderer, +void wr_renderer_set_profiler_enabled(WrRenderer *aRenderer, bool aEnabled) WR_FUNC; WR_INLINE -void wr_renderer_update(WrRenderer* aRenderer) +void wr_renderer_update(WrRenderer *aRenderer) WR_FUNC; WR_INLINE -void wr_scroll_layer_with_id(WrAPI* aApi, +void wr_scroll_layer_with_id(WrAPI *aApi, WrPipelineId aPipelineId, uint64_t aScrollId, WrPoint aNewScrollOrigin) WR_FUNC; WR_INLINE -void wr_state_delete(WrState* aState) +void wr_state_delete(WrState *aState) WR_DESTRUCTOR_SAFE_FUNC; WR_INLINE @@ -900,10 +904,10 @@ WR_INLINE bool wr_window_new(WrWindowId aWindowId, uint32_t aWindowWidth, uint32_t aWindowHeight, - void* aGlContext, + void *aGlContext, bool aEnableProfiler, - WrAPI** aOutApi, - WrRenderer** aOutRenderer) + WrAPI **aOutApi, + WrRenderer **aOutRenderer) WR_FUNC; } // extern "C" diff --git a/gfx/webrender_traits/Cargo.toml b/gfx/webrender_traits/Cargo.toml index c95861d5ab1d..62541184b30f 100644 --- a/gfx/webrender_traits/Cargo.toml +++ b/gfx/webrender_traits/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "webrender_traits" -version = "0.39.0" +version = "0.40.0" authors = ["Glenn Watson "] license = "MPL-2.0" repository = "https://github.com/servo/webrender" @@ -14,9 +14,9 @@ webgl = ["offscreen_gl_context"] app_units = "0.4" bincode = "1.0.0-alpha2" byteorder = "1.0" -euclid = "0.11" +euclid = "0.13" gleam = "0.4.5" -heapsize = "0.3.6" +heapsize = ">= 0.3.6, < 0.5" ipc-channel = {version = "0.7.2", optional = true} offscreen_gl_context = {version = "0.8", features = ["serde"], optional = true} serde = "0.9" diff --git a/gfx/webrender_traits/src/display_item.rs b/gfx/webrender_traits/src/display_item.rs index 916c137d3a83..ee17a83cb9f3 100644 --- a/gfx/webrender_traits/src/display_item.rs +++ b/gfx/webrender_traits/src/display_item.rs @@ -350,6 +350,7 @@ pub enum ImageRendering { pub struct YuvImageDisplayItem { pub yuv_data: YuvData, pub color_space: YuvColorSpace, + pub image_rendering: ImageRendering } #[repr(u32)] @@ -427,7 +428,7 @@ pub struct ClipRegion { pub complex_clips: ItemRange, #[serde(default, skip_serializing, skip_deserializing)] pub complex_clip_count: usize, -} +} #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] pub struct ComplexClipRegion { diff --git a/gfx/webrender_traits/src/display_list.rs b/gfx/webrender_traits/src/display_list.rs index 80e5f9db0fdf..ee75d120c2c6 100644 --- a/gfx/webrender_traits/src/display_list.rs +++ b/gfx/webrender_traits/src/display_list.rs @@ -535,10 +535,12 @@ impl DisplayListBuilder { rect: LayoutRect, _token: ClipRegionToken, yuv_data: YuvData, - color_space: YuvColorSpace) { + color_space: YuvColorSpace, + image_rendering: ImageRendering) { let item = SpecificDisplayItem::YuvImage(YuvImageDisplayItem { yuv_data: yuv_data, color_space: color_space, + image_rendering: image_rendering, }); self.push_item(item, rect); } diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp index 4262bbb5f16c..0ae09ec1c253 100644 --- a/layout/painting/nsDisplayList.cpp +++ b/layout/painting/nsDisplayList.cpp @@ -5190,14 +5190,6 @@ nsDisplayBoxShadowOuter::CanBuildWebRenderDisplayItems() } } - for (uint32_t j = shadows->Length(); j > 0; j--) { - nsCSSShadowItem* shadow = shadows->ShadowAt(j - 1); - // Need WR support for clip out. - if (shadow->mRadius <= 0) { - return false; - } - } - return true; } @@ -5267,38 +5259,15 @@ nsDisplayBoxShadowOuter::CreateWebRenderCommands(wr::DisplayListBuilder& aBuilde : 0.0; float spreadRadius = float(shadow->mSpread) / float(appUnitsPerDevPixel); - if (blurRadius <= 0) { - MOZ_ASSERT(false, "WR needs clip out first"); - // TODO: See nsContextBoxBlur::BlurRectangle. Just need to fill - // a rect here with the proper clip in/out, but WR doesn't support - // clip out yet - if (hasBorderRadius) { - LayerSize borderRadiusSize(borderRadius, borderRadius); - WrComplexClipRegion roundedRect = - wr::ToWrComplexClipRegion(deviceBoxRect, - borderRadiusSize); - nsTArray clips; - clips.AppendElement(roundedRect); - aBuilder.PushRect(deviceBoxRect, - aBuilder.PushClipRegion(deviceClipRect, - clips), - wr::ToWrColor(shadowColor)); - } else { - aBuilder.PushRect(deviceBoxRect, - aBuilder.PushClipRegion(deviceClipRect), - wr::ToWrColor(shadowColor)); - } - } else { - aBuilder.PushBoxShadow(deviceBoxRect, - aBuilder.PushClipRegion(deviceClipRect), - deviceBoxRect, - wr::ToWrPoint(shadowOffset), - wr::ToWrColor(shadowColor), - blurRadius, - spreadRadius, - borderRadius, - WrBoxShadowClipMode::Outset); - } + aBuilder.PushBoxShadow(deviceBoxRect, + aBuilder.PushClipRegion(deviceClipRect), + deviceBoxRect, + wr::ToWrPoint(shadowOffset), + wr::ToWrColor(shadowColor), + blurRadius, + spreadRadius, + borderRadius, + WrBoxShadowClipMode::Outset); } } } diff --git a/layout/reftests/box-shadow/reftest.list b/layout/reftests/box-shadow/reftest.list index 1430ed234000..c85213809246 100644 --- a/layout/reftests/box-shadow/reftest.list +++ b/layout/reftests/box-shadow/reftest.list @@ -16,7 +16,7 @@ fuzzy-if(OSX==1010,1,24) fuzzy-if(d2d,16,908) fuzzy-if(webrender,48,2040) == box fails-if(Android) == boxshadow-fileupload.html boxshadow-fileupload-ref.html fuzzy-if(skiaContent,13,28) == boxshadow-inner-basic.html boxshadow-inner-basic-ref.svg random-if(layersGPUAccelerated) == boxshadow-mixed.html boxshadow-mixed-ref.html -random-if(d2d) fuzzy-if(skiaContent,1,100) == boxshadow-rounded-spread.html boxshadow-rounded-spread-ref.html +random-if(d2d) fuzzy-if(skiaContent,1,100) fuzzy-if(webrender,127,3528) == boxshadow-rounded-spread.html boxshadow-rounded-spread-ref.html fuzzy-if(skiaContent,1,50) HTTP(..) == boxshadow-dynamic.xul boxshadow-dynamic-ref.xul random-if(d2d) == boxshadow-onecorner.html boxshadow-onecorner-ref.html random-if(d2d) == boxshadow-twocorners.html boxshadow-twocorners-ref.html diff --git a/layout/reftests/columns/reftest.list b/layout/reftests/columns/reftest.list index 77563515abc5..ccfb40f5b9bb 100644 --- a/layout/reftests/columns/reftest.list +++ b/layout/reftests/columns/reftest.list @@ -11,7 +11,7 @@ == column-balancing-overflow-002.html column-balancing-overflow-002.ref.html == column-balancing-overflow-003.html column-balancing-overflow-003.ref.html == column-balancing-overflow-004.html column-balancing-overflow-004.ref.html -fuzzy-if(webrender,126,216) == column-balancing-overflow-005.html column-balancing-overflow-005.ref.html +fuzzy-if(webrender,126,364) == column-balancing-overflow-005.html column-balancing-overflow-005.ref.html == column-balancing-000.html column-balancing-000.ref.html == column-balancing-001.html column-balancing-000.ref.html == column-balancing-002.html column-balancing-002.ref.html diff --git a/layout/reftests/svg/svg-integration/clip-path/reftest.list b/layout/reftests/svg/svg-integration/clip-path/reftest.list index b7a50a91f123..fa8c7b6d350f 100644 --- a/layout/reftests/svg/svg-integration/clip-path/reftest.list +++ b/layout/reftests/svg/svg-integration/clip-path/reftest.list @@ -34,7 +34,7 @@ fuzzy-if(skiaContent,1,20) == clip-path-polygon-013.html clip-path-stripes-003-r == clip-path-circle-014.html clip-path-circle-007-ref.html == clip-path-circle-015.html clip-path-circle-008-ref.html == clip-path-circle-016.html clip-path-circle-009-ref.html -== clip-path-circle-017.html clip-path-circle-007-ref.html +fuzzy-if(webrender,128,714) == clip-path-circle-017.html clip-path-circle-007-ref.html == clip-path-circle-018.html clip-path-circle-010-ref.html == clip-path-circle-019.html clip-path-circle-002-ref.html == clip-path-circle-020.html clip-path-circle-002-ref.html diff --git a/layout/reftests/w3c-css/submitted/masking/reftest.list b/layout/reftests/w3c-css/submitted/masking/reftest.list index 8847f65540c8..bfa43e69a5ba 100644 --- a/layout/reftests/w3c-css/submitted/masking/reftest.list +++ b/layout/reftests/w3c-css/submitted/masking/reftest.list @@ -27,8 +27,8 @@ fuzzy-if(skiaContent||winWidget,1,20000) == mask-image-2.html mask-image-2-ref.h fuzzy-if(skiaContent||winWidget,1,43) == mask-image-3c.html mask-image-3-ref.html fuzzy-if(skiaContent||winWidget,1,43) == mask-image-3d.html mask-image-3-ref.html == mask-image-3e.html mask-image-3-ref.html -fuzzy-if(skiaContent||winWidget,50,85) == mask-image-3f.html mask-image-3-ref.html -fuzzy-if(skiaContent||winWidget,50,85) == mask-image-3g.html mask-image-3-ref.html +fuzzy-if(skiaContent||winWidget,50,85) fuzzy-if(webrender,1,126) == mask-image-3f.html mask-image-3-ref.html +fuzzy-if(skiaContent||winWidget,50,85) fuzzy-if(webrender,1,126) == mask-image-3g.html mask-image-3-ref.html pref(layout.css.clip-path-shapes.enabled,true) fuzzy-if(winWidget,1,3) fuzzy-if(skiaContent,2,12) == mask-image-3h.html mask-image-3-ref.html fuzzy-if(skiaContent,71,203) == mask-image-3i.html mask-image-3-ref.html == mask-image-4a.html blank.html diff --git a/third_party/rust/euclid-0.11.3/.cargo-checksum.json b/third_party/rust/euclid-0.11.3/.cargo-checksum.json new file mode 100644 index 000000000000..becd539a3c86 --- /dev/null +++ b/third_party/rust/euclid-0.11.3/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"118514fd9c4958df0d25584cda4917186c46011569f55ef350530c1ad3fbdb48",".travis.yml":"13d3e5a7bf83b04c8e8cfa14f0297bd8366d68391d977dd547f64707dffc275a","COPYRIGHT":"ec82b96487e9e778ee610c7ab245162464782cfa1f555c2299333f8dbe5c036a","Cargo.toml":"10cfe5580ee83ae883a60d96f504dda8ae7885ae5fd3a3faf95c2a2b8b38fad0","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"62065228e42caebca7e7d7db1204cbb867033de5982ca4009928915e4095f3a3","README.md":"52f974f01c1e15182413e4321c8817d5e66fe4d92c5ec223c857dd0440f5c229","src/approxeq.rs":"2987e046c90d948b6c7d7ddba52d10c8b7520d71dc0a50dbe7665de128d7410e","src/length.rs":"d7c6369f2fe2a17c845b57749bd48c471159f0571a7314d3bf90737d53f697d3","src/lib.rs":"e2e621f05304278d020429d0349acf7a4e7c7a9a72bd23fc0e55680267472ee9","src/macros.rs":"b63dabdb52df84ea170dc1dab5fe8d7a78c054562d1566bab416124708d2d7af","src/matrix2d.rs":"2361338f59813adf4eebaab76e4dd82be0fbfb9ff2461da8dd9ac9d43583b322","src/matrix4d.rs":"b8547bed6108b037192021c97169c00ad456120b849e9b7ac7bec40363edaec1","src/num.rs":"62286aa642ce3afa7ebd950f50bf2197d8722907f2e23a2e2ea6690484d8b250","src/point.rs":"53f3c9018c822e0a6dc5018005e153775479f41fe55c082d0be10f331fda773f","src/rect.rs":"db62b3af8939529509ae21b3bf6ae498d73a95b4ff3a6eba4db614be08e95f8b","src/scale_factor.rs":"df6dbd1f0f9f63210b92809f84a383dad982a74f09789cf22c7d8f9b62199d39","src/side_offsets.rs":"f85526a421ffda63ff01a3478d4162c8717eef68e942acfa2fd9a1adee02ebb2","src/size.rs":"19d1c08f678d793c6eff49a44f69e5b7179e574aa9b81fb4e73210733af38718","src/trig.rs":"6b207980052d13c625272f2a70a22f7741b59513c2a4882385926f497c763a63"},"package":"f5517462c626a893f3b027615e88d7102cc6dd3f7f1bcb90c7220fb1da4970b5"} \ No newline at end of file diff --git a/third_party/rust/euclid-0.11.3/.cargo-ok b/third_party/rust/euclid-0.11.3/.cargo-ok new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/third_party/rust/euclid-0.11.3/.gitignore b/third_party/rust/euclid-0.11.3/.gitignore new file mode 100644 index 000000000000..80faedecfd74 --- /dev/null +++ b/third_party/rust/euclid-0.11.3/.gitignore @@ -0,0 +1,2 @@ +Cargo.lock +/target/ diff --git a/third_party/rust/euclid-0.11.3/.travis.yml b/third_party/rust/euclid-0.11.3/.travis.yml new file mode 100644 index 000000000000..a12141757c47 --- /dev/null +++ b/third_party/rust/euclid-0.11.3/.travis.yml @@ -0,0 +1,19 @@ +language: rust + +notifications: + webhooks: http://build.servo.org:54856/travis + +matrix: + include: + - rust: stable + env: FEATURES="" + - rust: beta + env: FEATURES="" + - rust: nightly + env: FEATURES="" + - rust: nightly + env: FEATURES="unstable" + +script: + - cargo build --verbose --features "$FEATURES" + - cargo test --verbose --features "$FEATURES" diff --git a/third_party/rust/euclid-0.11.3/COPYRIGHT b/third_party/rust/euclid-0.11.3/COPYRIGHT new file mode 100644 index 000000000000..8b7291ad281c --- /dev/null +++ b/third_party/rust/euclid-0.11.3/COPYRIGHT @@ -0,0 +1,5 @@ +Licensed under the Apache License, Version 2.0 or the MIT license +, at your +option. All files in the project carrying such notice may not be +copied, modified, or distributed except according to those terms. diff --git a/third_party/rust/euclid-0.11.3/Cargo.toml b/third_party/rust/euclid-0.11.3/Cargo.toml new file mode 100644 index 000000000000..f628a082c81f --- /dev/null +++ b/third_party/rust/euclid-0.11.3/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "euclid" +version = "0.11.3" +authors = ["The Servo Project Developers"] +description = "Geometry primitives" +documentation = "https://docs.rs/euclid/" +repository = "https://github.com/servo/euclid" +license = "MIT / Apache-2.0" + +[features] +unstable = [] + +[dependencies] +heapsize = "0.3" +rustc-serialize = "0.3.2" +num-traits = {version = "0.1.32", default-features = false} +log = "0.3.1" +serde = "0.9" + +[dev-dependencies] +rand = "0.3.7" +serde_test = "0.9" diff --git a/third_party/rust/euclid-0.11.3/LICENSE-APACHE b/third_party/rust/euclid-0.11.3/LICENSE-APACHE new file mode 100644 index 000000000000..16fe87b06e80 --- /dev/null +++ b/third_party/rust/euclid-0.11.3/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/third_party/rust/euclid-0.11.3/LICENSE-MIT b/third_party/rust/euclid-0.11.3/LICENSE-MIT new file mode 100644 index 000000000000..807526f57f3a --- /dev/null +++ b/third_party/rust/euclid-0.11.3/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2012-2013 Mozilla Foundation + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/third_party/rust/euclid-0.11.3/README.md b/third_party/rust/euclid-0.11.3/README.md new file mode 100644 index 000000000000..310c514cd405 --- /dev/null +++ b/third_party/rust/euclid-0.11.3/README.md @@ -0,0 +1,5 @@ +# euclid + +This is a small library for geometric types. + +[Documentation](https://docs.rs/euclid/) diff --git a/third_party/rust/euclid-0.11.3/src/approxeq.rs b/third_party/rust/euclid-0.11.3/src/approxeq.rs new file mode 100644 index 000000000000..10151110669a --- /dev/null +++ b/third_party/rust/euclid-0.11.3/src/approxeq.rs @@ -0,0 +1,47 @@ +// Copyright 2013 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +/// Trait for testing approximate equality +pub trait ApproxEq { + fn approx_epsilon() -> Eps; + fn approx_eq(&self, other: &Self) -> bool; + fn approx_eq_eps(&self, other: &Self, approx_epsilon: &Eps) -> bool; +} + +impl ApproxEq for f32 { + #[inline] + fn approx_epsilon() -> f32 { 1.0e-6 } + + #[inline] + fn approx_eq(&self, other: &f32) -> bool { + self.approx_eq_eps(other, &1.0e-6) + } + + #[inline] + fn approx_eq_eps(&self, other: &f32, approx_epsilon: &f32) -> bool { + (*self - *other).abs() < *approx_epsilon + } +} + + +impl ApproxEq for f64 { + #[inline] + fn approx_epsilon() -> f64 { 1.0e-6 } + + #[inline] + fn approx_eq(&self, other: &f64) -> bool { + self.approx_eq_eps(other, &1.0e-6) + } + + #[inline] + fn approx_eq_eps(&self, other: &f64, approx_epsilon: &f64) -> bool { + (*self - *other).abs() < *approx_epsilon + } +} diff --git a/third_party/rust/euclid-0.11.3/src/length.rs b/third_party/rust/euclid-0.11.3/src/length.rs new file mode 100644 index 000000000000..4d20bb93c55b --- /dev/null +++ b/third_party/rust/euclid-0.11.3/src/length.rs @@ -0,0 +1,449 @@ +// Copyright 2014 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +//! A one-dimensional length, tagged with its units. + +use scale_factor::ScaleFactor; +use num::Zero; + +use heapsize::HeapSizeOf; +use num_traits::{NumCast, Saturating}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use std::cmp::Ordering; +use std::ops::{Add, Sub, Mul, Div, Neg}; +use std::ops::{AddAssign, SubAssign}; +use std::marker::PhantomData; +use std::fmt; + +/// A one-dimensional distance, with value represented by `T` and unit of measurement `Unit`. +/// +/// `T` can be any numeric type, for example a primitive type like `u64` or `f32`. +/// +/// `Unit` is not used in the representation of a `Length` value. It is used only at compile time +/// to ensure that a `Length` stored with one unit is converted explicitly before being used in an +/// expression that requires a different unit. It may be a type without values, such as an empty +/// enum. +/// +/// You can multiply a `Length` by a `scale_factor::ScaleFactor` to convert it from one unit to +/// another. See the `ScaleFactor` docs for an example. +// Uncomment the derive, and remove the macro call, once heapsize gets +// PhantomData support. +#[repr(C)] +#[derive(RustcDecodable, RustcEncodable)] +pub struct Length(pub T, PhantomData); + +impl Clone for Length { + fn clone(&self) -> Self { + Length(self.0.clone(), PhantomData) + } +} + +impl Copy for Length {} + +impl HeapSizeOf for Length { + fn heap_size_of_children(&self) -> usize { + self.0.heap_size_of_children() + } +} + +impl Deserialize for Length where T: Deserialize { + fn deserialize(deserializer: D) -> Result,D::Error> + where D: Deserializer { + Ok(Length(try!(Deserialize::deserialize(deserializer)), PhantomData)) + } +} + +impl Serialize for Length where T: Serialize { + fn serialize(&self, serializer: S) -> Result where S: Serializer { + self.0.serialize(serializer) + } +} + +impl Length { + pub fn new(x: T) -> Length { + Length(x, PhantomData) + } +} + +impl Length { + pub fn get(&self) -> T { + self.0.clone() + } +} + +impl fmt::Debug for Length { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.get().fmt(f) + } +} + +impl fmt::Display for Length { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.get().fmt(f) + } +} + +// length + length +impl> Add for Length { + type Output = Length; + fn add(self, other: Length) -> Length { + Length::new(self.get() + other.get()) + } +} + +// length += length +impl> AddAssign for Length { + fn add_assign(&mut self, other: Length) { + self.0 += other.get(); + } +} + +// length - length +impl> Sub> for Length { + type Output = Length; + fn sub(self, other: Length) -> ::Output { + Length::new(self.get() - other.get()) + } +} + +// length -= length +impl> SubAssign for Length { + fn sub_assign(&mut self, other: Length) { + self.0 -= other.get(); + } +} + +// Saturating length + length and length - length. +impl Saturating for Length { + fn saturating_add(self, other: Length) -> Length { + Length::new(self.get().saturating_add(other.get())) + } + + fn saturating_sub(self, other: Length) -> Length { + Length::new(self.get().saturating_sub(other.get())) + } +} + +// length / length +impl> Div> for Length { + type Output = ScaleFactor; + #[inline] + fn div(self, other: Length) -> ScaleFactor { + ScaleFactor::new(self.get() / other.get()) + } +} + +// length * scaleFactor +impl> Mul> for Length { + type Output = Length; + #[inline] + fn mul(self, scale: ScaleFactor) -> Length { + Length::new(self.get() * scale.get()) + } +} + +// length / scaleFactor +impl> Div> for Length { + type Output = Length; + #[inline] + fn div(self, scale: ScaleFactor) -> Length { + Length::new(self.get() / scale.get()) + } +} + +// -length +impl > Neg for Length { + type Output = Length; + #[inline] + fn neg(self) -> Length { + Length::new(-self.get()) + } +} + +impl Length { + /// Cast from one numeric representation to another, preserving the units. + pub fn cast(&self) -> Option> { + NumCast::from(self.get()).map(Length::new) + } +} + +impl PartialEq for Length { + fn eq(&self, other: &Length) -> bool { self.get().eq(&other.get()) } +} + +impl PartialOrd for Length { + fn partial_cmp(&self, other: &Length) -> Option { + self.get().partial_cmp(&other.get()) + } +} + +impl Eq for Length {} + +impl Ord for Length { + fn cmp(&self, other: &Length) -> Ordering { self.get().cmp(&other.get()) } +} + +impl Zero for Length { + fn zero() -> Length { + Length::new(Zero::zero()) + } +} + +#[cfg(test)] +mod tests { + use super::Length; + use num::Zero; + + use heapsize::HeapSizeOf; + use num_traits::Saturating; + use scale_factor::ScaleFactor; + use std::f32::INFINITY; + + extern crate serde_test; + use self::serde_test::Token; + use self::serde_test::assert_tokens; + + enum Inch {} + enum Mm {} + enum Cm {} + enum Second {} + + #[test] + fn test_clone() { + // A cloned Length is a separate length with the state matching the + // original Length at the point it was cloned. + let mut variable_length: Length = Length::new(12.0); + + let one_foot = variable_length.clone(); + variable_length.0 = 24.0; + + assert_eq!(one_foot.get(), 12.0); + assert_eq!(variable_length.get(), 24.0); + } + + #[test] + fn test_heapsizeof_builtins() { + // Heap size of built-ins is zero by default. + let one_foot: Length = Length::new(12.0); + + let heap_size_length_f32 = one_foot.heap_size_of_children(); + + assert_eq!(heap_size_length_f32, 0); + } + + #[test] + fn test_heapsizeof_length_vector() { + // Heap size of any Length is just the heap size of the length value. + for n in 0..5 { + let length: Length, Inch> = Length::new(Vec::with_capacity(n)); + + assert_eq!(length.heap_size_of_children(), length.0.heap_size_of_children()); + } + } + + #[test] + fn test_length_serde() { + let one_cm: Length = Length::new(10.0); + + assert_tokens(&one_cm, &[Token::F32(10.0)]); + } + + #[test] + fn test_get_clones_length_value() { + // Calling get returns a clone of the Length's value. + // To test this, we need something clone-able - hence a vector. + let mut length: Length, Inch> = Length::new(vec![1, 2, 3]); + + let value = length.get(); + length.0.push(4); + + assert_eq!(value, vec![1, 2, 3]); + assert_eq!(length.get(), vec![1, 2, 3, 4]); + } + + #[test] + fn test_fmt_debug() { + // Debug and display format the value only. + let one_cm: Length = Length::new(10.0); + + let result = format!("{:?}", one_cm); + + assert_eq!(result, "10"); + } + + #[test] + fn test_fmt_display() { + // Debug and display format the value only. + let one_cm: Length = Length::new(10.0); + + let result = format!("{}", one_cm); + + assert_eq!(result, "10"); + } + + #[test] + fn test_add() { + let length1: Length = Length::new(250); + let length2: Length = Length::new(5); + + let result = length1 + length2; + + assert_eq!(result.get(), 255); + } + + #[test] + fn test_addassign() { + let one_cm: Length = Length::new(10.0); + let mut measurement: Length = Length::new(5.0); + + measurement += one_cm; + + assert_eq!(measurement.get(), 15.0); + } + + #[test] + fn test_sub() { + let length1: Length = Length::new(250); + let length2: Length = Length::new(5); + + let result = length1 - length2; + + assert_eq!(result.get(), 245); + } + + #[test] + fn test_subassign() { + let one_cm: Length = Length::new(10.0); + let mut measurement: Length = Length::new(5.0); + + measurement -= one_cm; + + assert_eq!(measurement.get(), -5.0); + } + + #[test] + fn test_saturating_add() { + let length1: Length = Length::new(250); + let length2: Length = Length::new(6); + + let result = length1.saturating_add(length2); + + assert_eq!(result.get(), 255); + } + + #[test] + fn test_saturating_sub() { + let length1: Length = Length::new(5); + let length2: Length = Length::new(10); + + let result = length1.saturating_sub(length2); + + assert_eq!(result.get(), 0); + } + + #[test] + fn test_division_by_length() { + // Division results in a ScaleFactor from denominator units + // to numerator units. + let length: Length = Length::new(5.0); + let duration: Length = Length::new(10.0); + + let result = length / duration; + + let expected: ScaleFactor = ScaleFactor::new(0.5); + assert_eq!(result, expected); + } + + #[test] + fn test_multiplication() { + let length_mm: Length = Length::new(10.0); + let cm_per_mm: ScaleFactor = ScaleFactor::new(0.1); + + let result = length_mm * cm_per_mm; + + let expected: Length = Length::new(1.0); + assert_eq!(result, expected); + } + + #[test] + fn test_division_by_scalefactor() { + let length: Length = Length::new(5.0); + let cm_per_second: ScaleFactor = ScaleFactor::new(10.0); + + let result = length / cm_per_second; + + let expected: Length = Length::new(0.5); + assert_eq!(result, expected); + } + + #[test] + fn test_negation() { + let length: Length = Length::new(5.0); + + let result = -length; + + let expected: Length = Length::new(-5.0); + assert_eq!(result, expected); + } + + #[test] + fn test_cast() { + let length_as_i32: Length = Length::new(5); + + let result: Length = length_as_i32.cast().unwrap(); + + let length_as_f32: Length = Length::new(5.0); + assert_eq!(result, length_as_f32); + } + + #[test] + fn test_equality() { + let length_5_point_0: Length = Length::new(5.0); + let length_5_point_1: Length = Length::new(5.1); + let length_0_point_1: Length = Length::new(0.1); + + assert!(length_5_point_0 == length_5_point_1 - length_0_point_1); + assert!(length_5_point_0 != length_5_point_1); + } + + #[test] + fn test_order() { + let length_5_point_0: Length = Length::new(5.0); + let length_5_point_1: Length = Length::new(5.1); + let length_0_point_1: Length = Length::new(0.1); + + assert!(length_5_point_0 < length_5_point_1); + assert!(length_5_point_0 <= length_5_point_1); + assert!(length_5_point_0 <= length_5_point_1 - length_0_point_1); + assert!(length_5_point_1 > length_5_point_0); + assert!(length_5_point_1 >= length_5_point_0); + assert!(length_5_point_0 >= length_5_point_1 - length_0_point_1); + } + + #[test] + fn test_zero_add() { + type LengthCm = Length; + let length: LengthCm = Length::new(5.0); + + let result = length - LengthCm::zero(); + + assert_eq!(result, length); + } + + #[test] + fn test_zero_division() { + type LengthCm = Length; + let length: LengthCm = Length::new(5.0); + let length_zero: LengthCm = Length::zero(); + + let result = length / length_zero; + + let expected: ScaleFactor = ScaleFactor::new(INFINITY); + assert_eq!(result, expected); + } +} diff --git a/third_party/rust/euclid-0.11.3/src/lib.rs b/third_party/rust/euclid-0.11.3/src/lib.rs new file mode 100644 index 000000000000..2ed3edde9696 --- /dev/null +++ b/third_party/rust/euclid-0.11.3/src/lib.rs @@ -0,0 +1,113 @@ +// Copyright 2013 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![cfg_attr(feature = "unstable", feature(asm, repr_simd, test))] + +//! A collection of strongly typed math tools for computer graphics with an inclination +//! towards 2d graphics and layout. +//! +//! All types are generic over the scalar type of their component (`f32`, `i32`, etc.), +//! and tagged with a generic Unit parameter which is useful to prevent mixing +//! values from different spaces. For example it should not be legal to translate +//! a screen-space position by a world-space vector and this can be expressed using +//! the generic Unit parameter. +//! +//! This unit system is not mandatory and all Typed* structures have an alias +//! with the default unit: `UnknownUnit`. +//! for example ```Point2D``` is equivalent to ```TypedPoint2D```. +//! Client code typically creates a set of aliases for each type and doesn't need +//! to deal with the specifics of typed units further. For example: +//! +//! All euclid types are marked `#[repr(C)]` in order to facilitate exposing them to +//! foreign function interfaces (provided the underlying scalar type is also `repr(C)`). +//! +//! ```rust +//! use euclid::*; +//! pub struct ScreenSpace; +//! pub type ScreenPoint = TypedPoint2D; +//! pub type ScreenSize = TypedSize2D; +//! pub struct WorldSpace; +//! pub type WorldPoint = TypedPoint3D; +//! pub type ProjectionMatrix = TypedMatrix4D; +//! // etc... +//! ``` +//! +//! Components are accessed in their scalar form by default for convenience, and most +//! types additionally implement strongly typed accessors which return typed ```Length``` wrappers. +//! For example: +//! +//! ```rust +//! # use euclid::*; +//! # pub struct WorldSpace; +//! # pub type WorldPoint = TypedPoint3D; +//! let p = WorldPoint::new(0.0, 1.0, 1.0); +//! // p.x is an f32. +//! println!("p.x = {:?} ", p.x); +//! // p.x is a Length. +//! println!("p.x_typed() = {:?} ", p.x_typed()); +//! // Length::get returns the scalar value (f32). +//! assert_eq!(p.x, p.x_typed().get()); +//! ``` + +extern crate heapsize; + +#[cfg_attr(test, macro_use)] +extern crate log; +extern crate rustc_serialize; +extern crate serde; + +#[cfg(test)] +extern crate rand; +#[cfg(feature = "unstable")] +extern crate test; +extern crate num_traits; + +pub use length::Length; +pub use scale_factor::ScaleFactor; +pub use matrix2d::{Matrix2D, TypedMatrix2D}; +pub use matrix4d::{Matrix4D, TypedMatrix4D}; +pub use point::{ + Point2D, TypedPoint2D, + Point3D, TypedPoint3D, + Point4D, TypedPoint4D, +}; +pub use rect::{Rect, TypedRect}; +pub use side_offsets::{SideOffsets2D, TypedSideOffsets2D}; +#[cfg(feature = "unstable")] pub use side_offsets::SideOffsets2DSimdI32; +pub use size::{Size2D, TypedSize2D}; + +pub mod approxeq; +pub mod length; +#[macro_use] +mod macros; +pub mod matrix2d; +pub mod matrix4d; +pub mod num; +pub mod point; +pub mod rect; +pub mod scale_factor; +pub mod side_offsets; +pub mod size; +pub mod trig; + +/// The default unit. +#[derive(Clone, Copy, RustcDecodable, RustcEncodable)] +pub struct UnknownUnit; + +/// Unit for angles in radians. +pub struct Rad; + +/// Unit for angles in degrees. +pub struct Deg; + +/// A value in radians. +pub type Radians = Length; + +/// A value in Degrees. +pub type Degrees = Length; diff --git a/third_party/rust/euclid-0.11.3/src/macros.rs b/third_party/rust/euclid-0.11.3/src/macros.rs new file mode 100644 index 000000000000..2f597cb08190 --- /dev/null +++ b/third_party/rust/euclid-0.11.3/src/macros.rs @@ -0,0 +1,87 @@ +// Copyright 2013 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +macro_rules! define_matrix { + ( + $(#[$attr:meta])* + pub struct $name:ident { + $(pub $field:ident: T,)+ + } + ) => ( + #[repr(C)] + $(#[$attr])* + pub struct $name { + $(pub $field: T,)+ + _unit: PhantomData<($($phantom),+)> + } + + impl Clone for $name { + fn clone(&self) -> Self { + $name { + $($field: self.$field.clone(),)+ + _unit: PhantomData, + } + } + } + + impl Copy for $name {} + + impl ::heapsize::HeapSizeOf for $name + where T: ::heapsize::HeapSizeOf + { + fn heap_size_of_children(&self) -> usize { + $(self.$field.heap_size_of_children() +)+ 0 + } + } + + impl ::serde::Deserialize for $name + where T: ::serde::Deserialize + { + fn deserialize(deserializer: D) -> Result + where D: ::serde::Deserializer + { + let ($($field,)+) = + try!(::serde::Deserialize::deserialize(deserializer)); + Ok($name { + $($field: $field,)+ + _unit: PhantomData, + }) + } + } + + impl ::serde::Serialize for $name + where T: ::serde::Serialize + { + fn serialize(&self, serializer: S) -> Result + where S: ::serde::Serializer + { + ($(&self.$field,)+).serialize(serializer) + } + } + + impl ::std::cmp::Eq for $name + where T: ::std::cmp::Eq {} + + impl ::std::cmp::PartialEq for $name + where T: ::std::cmp::PartialEq + { + fn eq(&self, other: &Self) -> bool { + true $(&& self.$field == other.$field)+ + } + } + + impl ::std::hash::Hash for $name + where T: ::std::hash::Hash + { + fn hash(&self, h: &mut H) { + $(self.$field.hash(h);)+ + } + } + ) +} diff --git a/third_party/rust/euclid-0.11.3/src/matrix2d.rs b/third_party/rust/euclid-0.11.3/src/matrix2d.rs new file mode 100644 index 000000000000..5c86a7ee36e7 --- /dev/null +++ b/third_party/rust/euclid-0.11.3/src/matrix2d.rs @@ -0,0 +1,431 @@ +// Copyright 2013 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::{UnknownUnit, Radians}; +use num::{One, Zero}; +use point::TypedPoint2D; +use rect::TypedRect; +use std::ops::{Add, Mul, Div, Sub}; +use std::marker::PhantomData; +use approxeq::ApproxEq; +use trig::Trig; +use std::fmt; + +define_matrix! { + /// A 2d transform stored as a 2 by 3 matrix in row-major order in memory, + /// useful to represent 2d transformations. + /// + /// Matrices can be parametrized over the source and destination units, to describe a + /// transformation from a space to another. + /// For example, `TypedMatrix2D::transform_point4d` + /// takes a `TypedPoint2D` and returns a `TypedPoint2D`. + /// + /// Matrices expose a set of convenience methods for pre- and post-transformations. + /// A pre-transformation corresponds to adding an operation that is applied before + /// the rest of the transformation, while a post-transformation adds an operation + /// that is applied after. + pub struct TypedMatrix2D { + pub m11: T, pub m12: T, + pub m21: T, pub m22: T, + pub m31: T, pub m32: T, + } +} + +/// The default 2d matrix type with no units. +pub type Matrix2D = TypedMatrix2D; + +impl TypedMatrix2D { + /// Create a matrix specifying its components in row-major order. + pub fn row_major(m11: T, m12: T, m21: T, m22: T, m31: T, m32: T) -> TypedMatrix2D { + TypedMatrix2D { + m11: m11, m12: m12, + m21: m21, m22: m22, + m31: m31, m32: m32, + _unit: PhantomData, + } + } + + /// Create a matrix specifying its components in column-major order. + pub fn column_major(m11: T, m21: T, m31: T, m12: T, m22: T, m32: T) -> TypedMatrix2D { + TypedMatrix2D { + m11: m11, m12: m12, + m21: m21, m22: m22, + m31: m31, m32: m32, + _unit: PhantomData, + } + } + + /// Returns an array containing this matrix's terms in row-major order (the order + /// in which the matrix is actually laid out in memory). + pub fn to_row_major_array(&self) -> [T; 6] { + [ + self.m11, self.m12, + self.m21, self.m22, + self.m31, self.m32 + ] + } + + /// Returns an array containing this matrix's terms in column-major order. + pub fn to_column_major_array(&self) -> [T; 6] { + [ + self.m11, self.m21, self.m31, + self.m12, self.m22, self.m32 + ] + } + + /// Drop the units, preserving only the numeric value. + pub fn to_untyped(&self) -> Matrix2D { + Matrix2D::row_major( + self.m11, self.m12, + self.m21, self.m22, + self.m31, self.m32 + ) + } + + /// Tag a unitless value with units. + pub fn from_untyped(p: &Matrix2D) -> TypedMatrix2D { + TypedMatrix2D::row_major( + p.m11, p.m12, + p.m21, p.m22, + p.m31, p.m32 + ) + } +} + +impl TypedMatrix2D +where T: Copy + + PartialEq + + One + Zero { + pub fn identity() -> TypedMatrix2D { + let (_0, _1) = (Zero::zero(), One::one()); + TypedMatrix2D::row_major( + _1, _0, + _0, _1, + _0, _0 + ) + } + + // Intentional not public, because it checks for exact equivalence + // while most consumers will probably want some sort of approximate + // equivalence to deal with floating-point errors. + fn is_identity(&self) -> bool { + *self == TypedMatrix2D::identity() + } +} + +impl TypedMatrix2D +where T: Copy + Clone + + Add + + Mul + + Div + + Sub + + Trig + + PartialOrd + + One + Zero { + + /// Returns the multiplication of the two matrices such that mat's transformation + /// applies after self's transformation. + pub fn post_mul(&self, mat: &TypedMatrix2D) -> TypedMatrix2D { + TypedMatrix2D::row_major( + self.m11 * mat.m11 + self.m12 * mat.m21, + self.m11 * mat.m12 + self.m12 * mat.m22, + self.m21 * mat.m11 + self.m22 * mat.m21, + self.m21 * mat.m12 + self.m22 * mat.m22, + self.m31 * mat.m11 + self.m32 * mat.m21 + mat.m31, + self.m31 * mat.m12 + self.m32 * mat.m22 + mat.m32, + ) + } + + /// Returns the multiplication of the two matrices such that mat's transformation + /// applies before self's transformation. + pub fn pre_mul(&self, mat: &TypedMatrix2D) -> TypedMatrix2D { + mat.post_mul(self) + } + + /// Returns a translation matrix. + pub fn create_translation(x: T, y: T) -> TypedMatrix2D { + let (_0, _1): (T, T) = (Zero::zero(), One::one()); + TypedMatrix2D::row_major( + _1, _0, + _0, _1, + x, y + ) + } + + /// Applies a translation after self's transformation and returns the resulting matrix. + pub fn post_translated(&self, x: T, y: T) -> TypedMatrix2D { + self.post_mul(&TypedMatrix2D::create_translation(x, y)) + } + + /// Applies a translation before self's transformation and returns the resulting matrix. + pub fn pre_translated(&self, x: T, y: T) -> TypedMatrix2D { + self.pre_mul(&TypedMatrix2D::create_translation(x, y)) + } + + /// Returns a scale matrix. + pub fn create_scale(x: T, y: T) -> TypedMatrix2D { + let _0 = Zero::zero(); + TypedMatrix2D::row_major( + x, _0, + _0, y, + _0, _0 + ) + } + + /// Applies a scale after self's transformation and returns the resulting matrix. + pub fn post_scaled(&self, x: T, y: T) -> TypedMatrix2D { + self.post_mul(&TypedMatrix2D::create_scale(x, y)) + } + + /// Applies a scale before self's transformation and returns the resulting matrix. + pub fn pre_scaled(&self, x: T, y: T) -> TypedMatrix2D { + TypedMatrix2D::row_major( + self.m11 * x, self.m12, + self.m21, self.m22 * y, + self.m31, self.m32 + ) + } + + /// Returns a rotation matrix. + pub fn create_rotation(theta: Radians) -> TypedMatrix2D { + let _0 = Zero::zero(); + let cos = theta.get().cos(); + let sin = theta.get().sin(); + TypedMatrix2D::row_major( + cos, _0 - sin, + sin, cos, + _0, _0 + ) + } + + /// Applies a rotation after self's transformation and returns the resulting matrix. + pub fn post_rotated(&self, theta: Radians) -> TypedMatrix2D { + self.post_mul(&TypedMatrix2D::create_rotation(theta)) + } + + /// Applies a rotation after self's transformation and returns the resulting matrix. + pub fn pre_rotated(&self, theta: Radians) -> TypedMatrix2D { + self.pre_mul(&TypedMatrix2D::create_rotation(theta)) + } + + /// Returns the given point transformed by this matrix. + #[inline] + pub fn transform_point(&self, point: &TypedPoint2D) -> TypedPoint2D { + TypedPoint2D::new(point.x * self.m11 + point.y * self.m21 + self.m31, + point.x * self.m12 + point.y * self.m22 + self.m32) + } + + /// Returns a rectangle that encompasses the result of transforming the given rectangle by this + /// matrix. + #[inline] + pub fn transform_rect(&self, rect: &TypedRect) -> TypedRect { + TypedRect::from_points(&[ + self.transform_point(&rect.origin), + self.transform_point(&rect.top_right()), + self.transform_point(&rect.bottom_left()), + self.transform_point(&rect.bottom_right()), + ]) + } + + /// Computes and returns the determinant of this matrix. + pub fn determinant(&self) -> T { + self.m11 * self.m22 - self.m12 * self.m21 + } + + /// Returns the inverse matrix if possible. + pub fn inverse(&self) -> Option> { + let det = self.determinant(); + + let _0: T = Zero::zero(); + let _1: T = One::one(); + + if det == _0 { + return None; + } + + let inv_det = _1 / det; + Some(TypedMatrix2D::row_major( + inv_det * self.m22, + inv_det * (_0 - self.m12), + inv_det * (_0 - self.m21), + inv_det * self.m11, + inv_det * (self.m21 * self.m32 - self.m22 * self.m31), + inv_det * (self.m31 * self.m12 - self.m11 * self.m32), + )) + } + + /// Returns the same matrix with a different destination unit. + #[inline] + pub fn with_destination(&self) -> TypedMatrix2D { + TypedMatrix2D::row_major( + self.m11, self.m12, + self.m21, self.m22, + self.m31, self.m32, + ) + } + + /// Returns the same matrix with a different source unit. + #[inline] + pub fn with_source(&self) -> TypedMatrix2D { + TypedMatrix2D::row_major( + self.m11, self.m12, + self.m21, self.m22, + self.m31, self.m32, + ) + } +} + +impl, Src, Dst> TypedMatrix2D { + pub fn approx_eq(&self, other: &Self) -> bool { + self.m11.approx_eq(&other.m11) && self.m12.approx_eq(&other.m12) && + self.m21.approx_eq(&other.m21) && self.m22.approx_eq(&other.m22) && + self.m31.approx_eq(&other.m31) && self.m32.approx_eq(&other.m32) + } +} + +impl fmt::Debug for TypedMatrix2D +where T: Copy + fmt::Debug + + PartialEq + + One + Zero { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.is_identity() { + write!(f, "[I]") + } else { + self.to_row_major_array().fmt(f) + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use approxeq::ApproxEq; + use point::Point2D; + use Radians; + + use std::f32::consts::FRAC_PI_2; + + type Mat = Matrix2D; + + fn rad(v: f32) -> Radians { Radians::new(v) } + + #[test] + pub fn test_translation() { + let t1 = Mat::create_translation(1.0, 2.0); + let t2 = Mat::identity().pre_translated(1.0, 2.0); + let t3 = Mat::identity().post_translated(1.0, 2.0); + assert_eq!(t1, t2); + assert_eq!(t1, t3); + + assert_eq!(t1.transform_point(&Point2D::new(1.0, 1.0)), Point2D::new(2.0, 3.0)); + + assert_eq!(t1.post_mul(&t1), Mat::create_translation(2.0, 4.0)); + } + + #[test] + pub fn test_rotation() { + let r1 = Mat::create_rotation(rad(FRAC_PI_2)); + let r2 = Mat::identity().pre_rotated(rad(FRAC_PI_2)); + let r3 = Mat::identity().post_rotated(rad(FRAC_PI_2)); + assert_eq!(r1, r2); + assert_eq!(r1, r3); + + assert!(r1.transform_point(&Point2D::new(1.0, 2.0)).approx_eq(&Point2D::new(2.0, -1.0))); + + assert!(r1.post_mul(&r1).approx_eq(&Mat::create_rotation(rad(FRAC_PI_2*2.0)))); + } + + #[test] + pub fn test_scale() { + let s1 = Mat::create_scale(2.0, 3.0); + let s2 = Mat::identity().pre_scaled(2.0, 3.0); + let s3 = Mat::identity().post_scaled(2.0, 3.0); + assert_eq!(s1, s2); + assert_eq!(s1, s3); + + assert!(s1.transform_point(&Point2D::new(2.0, 2.0)).approx_eq(&Point2D::new(4.0, 6.0))); + } + + #[test] + fn test_column_major() { + assert_eq!( + Mat::row_major( + 1.0, 2.0, + 3.0, 4.0, + 5.0, 6.0 + ), + Mat::column_major( + 1.0, 3.0, 5.0, + 2.0, 4.0, 6.0, + ) + ); + } + + #[test] + pub fn test_inverse_simple() { + let m1 = Mat::identity(); + let m2 = m1.inverse().unwrap(); + assert!(m1.approx_eq(&m2)); + } + + #[test] + pub fn test_inverse_scale() { + let m1 = Mat::create_scale(1.5, 0.3); + let m2 = m1.inverse().unwrap(); + assert!(m1.pre_mul(&m2).approx_eq(&Mat::identity())); + } + + #[test] + pub fn test_inverse_translate() { + let m1 = Mat::create_translation(-132.0, 0.3); + let m2 = m1.inverse().unwrap(); + assert!(m1.pre_mul(&m2).approx_eq(&Mat::identity())); + } + + #[test] + fn test_inverse_none() { + assert!(Mat::create_scale(2.0, 0.0).inverse().is_none()); + assert!(Mat::create_scale(2.0, 2.0).inverse().is_some()); + } + + #[test] + pub fn test_pre_post() { + let m1 = Matrix2D::identity().post_scaled(1.0, 2.0).post_translated(1.0, 2.0); + let m2 = Matrix2D::identity().pre_translated(1.0, 2.0).pre_scaled(1.0, 2.0); + assert!(m1.approx_eq(&m2)); + + let r = Mat::create_rotation(rad(FRAC_PI_2)); + let t = Mat::create_translation(2.0, 3.0); + + let a = Point2D::new(1.0, 1.0); + + assert!(r.post_mul(&t).transform_point(&a).approx_eq(&Point2D::new(3.0, 2.0))); + assert!(t.post_mul(&r).transform_point(&a).approx_eq(&Point2D::new(4.0, -3.0))); + assert!(t.post_mul(&r).transform_point(&a).approx_eq(&r.transform_point(&t.transform_point(&a)))); + + assert!(r.pre_mul(&t).transform_point(&a).approx_eq(&Point2D::new(4.0, -3.0))); + assert!(t.pre_mul(&r).transform_point(&a).approx_eq(&Point2D::new(3.0, 2.0))); + assert!(t.pre_mul(&r).transform_point(&a).approx_eq(&t.transform_point(&r.transform_point(&a)))); + } + + #[test] + fn test_size_of() { + use std::mem::size_of; + assert_eq!(size_of::>(), 6*size_of::()); + assert_eq!(size_of::>(), 6*size_of::()); + } + + #[test] + pub fn test_is_identity() { + let m1 = Matrix2D::identity(); + assert!(m1.is_identity()); + let m2 = m1.post_translated(0.1, 0.0); + assert!(!m2.is_identity()); + } +} diff --git a/third_party/rust/euclid-0.11.3/src/matrix4d.rs b/third_party/rust/euclid-0.11.3/src/matrix4d.rs new file mode 100644 index 000000000000..2bccf8de3f95 --- /dev/null +++ b/third_party/rust/euclid-0.11.3/src/matrix4d.rs @@ -0,0 +1,825 @@ +// Copyright 2013 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::{UnknownUnit, Radians}; +use approxeq::ApproxEq; +use trig::Trig; +use point::{TypedPoint2D, TypedPoint3D, TypedPoint4D}; +use rect::TypedRect; +use matrix2d::TypedMatrix2D; +use scale_factor::ScaleFactor; +use num::{One, Zero}; +use std::ops::{Add, Mul, Sub, Div, Neg}; +use std::marker::PhantomData; +use std::fmt; + +define_matrix! { + /// A 4 by 4 matrix stored in row-major order in memory, useful to represent + /// 3d transformations. + /// + /// Matrices can be parametrized over the source and destination units, to describe a + /// transformation from a space to another. + /// For example, `TypedMatrix4D::transform_point4d` + /// takes a `TypedPoint4D` and returns a `TypedPoint4D`. + /// + /// Matrices expose a set of convenience methods for pre- and post-transformations. + /// A pre-transformation corresponds to adding an operation that is applied before + /// the rest of the transformation, while a post-transformation adds an operation + /// that is applied after. + pub struct TypedMatrix4D { + pub m11: T, pub m12: T, pub m13: T, pub m14: T, + pub m21: T, pub m22: T, pub m23: T, pub m24: T, + pub m31: T, pub m32: T, pub m33: T, pub m34: T, + pub m41: T, pub m42: T, pub m43: T, pub m44: T, + } +} + +/// The default 4d matrix type with no units. +pub type Matrix4D = TypedMatrix4D; + +impl TypedMatrix4D { + /// Create a matrix specifying its components in row-major order. + /// + /// For example, the translation terms m41, m42, m43 on the last row with the + /// row-major convention) are the 13rd, 14th and 15th parameters. + #[inline] + pub fn row_major( + m11: T, m12: T, m13: T, m14: T, + m21: T, m22: T, m23: T, m24: T, + m31: T, m32: T, m33: T, m34: T, + m41: T, m42: T, m43: T, m44: T) + -> TypedMatrix4D { + TypedMatrix4D { + m11: m11, m12: m12, m13: m13, m14: m14, + m21: m21, m22: m22, m23: m23, m24: m24, + m31: m31, m32: m32, m33: m33, m34: m34, + m41: m41, m42: m42, m43: m43, m44: m44, + _unit: PhantomData, + } + } + + /// Create a matrix specifying its components in column-major order. + /// + /// For example, the translation terms m41, m42, m43 on the last column with the + /// column-major convention) are the 4th, 8th and 12nd parameters. + #[inline] + pub fn column_major( + m11: T, m21: T, m31: T, m41: T, + m12: T, m22: T, m32: T, m42: T, + m13: T, m23: T, m33: T, m43: T, + m14: T, m24: T, m34: T, m44: T) + -> TypedMatrix4D { + TypedMatrix4D { + m11: m11, m12: m12, m13: m13, m14: m14, + m21: m21, m22: m22, m23: m23, m24: m24, + m31: m31, m32: m32, m33: m33, m34: m34, + m41: m41, m42: m42, m43: m43, m44: m44, + _unit: PhantomData, + } + } +} + +impl TypedMatrix4D +where T: Copy + Clone + + PartialEq + + One + Zero { + #[inline] + pub fn identity() -> TypedMatrix4D { + let (_0, _1): (T, T) = (Zero::zero(), One::one()); + TypedMatrix4D::row_major( + _1, _0, _0, _0, + _0, _1, _0, _0, + _0, _0, _1, _0, + _0, _0, _0, _1 + ) + } + + // Intentional not public, because it checks for exact equivalence + // while most consumers will probably want some sort of approximate + // equivalence to deal with floating-point errors. + #[inline] + fn is_identity(&self) -> bool { + *self == TypedMatrix4D::identity() + } +} + +impl TypedMatrix4D +where T: Copy + Clone + + Add + + Sub + + Mul + + Div + + Neg + + ApproxEq + + PartialOrd + + Trig + + One + Zero { + + /// Create a 4 by 4 matrix representing a 2d transformation, specifying its components + /// in row-major order. + #[inline] + pub fn row_major_2d(m11: T, m12: T, m21: T, m22: T, m41: T, m42: T) -> TypedMatrix4D { + let (_0, _1): (T, T) = (Zero::zero(), One::one()); + TypedMatrix4D::row_major( + m11, m12, _0, _0, + m21, m22, _0, _0, + _0, _0, _1, _0, + m41, m42, _0, _1 + ) + } + + /// Create an orthogonal projection matrix. + pub fn ortho(left: T, right: T, + bottom: T, top: T, + near: T, far: T) -> TypedMatrix4D { + let tx = -((right + left) / (right - left)); + let ty = -((top + bottom) / (top - bottom)); + let tz = -((far + near) / (far - near)); + + let (_0, _1): (T, T) = (Zero::zero(), One::one()); + let _2 = _1 + _1; + TypedMatrix4D::row_major( + _2 / (right - left), _0 , _0 , _0, + _0 , _2 / (top - bottom), _0 , _0, + _0 , _0 , -_2 / (far - near), _0, + tx , ty , tz , _1 + ) + } + + /// Returns true if this matrix can be represented with a TypedMatrix2D. + /// + /// See https://drafts.csswg.org/css-transforms/#2d-matrix + #[inline] + pub fn is_2d(&self) -> bool { + let (_0, _1): (T, T) = (Zero::zero(), One::one()); + self.m31 == _0 && self.m32 == _0 && + self.m13 == _0 && self.m23 == _0 && + self.m43 == _0 && self.m14 == _0 && + self.m24 == _0 && self.m34 == _0 && + self.m33 == _1 && self.m44 == _1 + } + + /// Create a 2D matrix picking the relevent terms from this matrix. + /// + /// This method assumes that self represents a 2d transformation, callers + /// should check that self.is_2d() returns true beforehand. + pub fn to_2d(&self) -> TypedMatrix2D { + TypedMatrix2D::row_major( + self.m11, self.m12, + self.m21, self.m22, + self.m41, self.m42 + ) + } + + pub fn approx_eq(&self, other: &TypedMatrix4D) -> bool { + self.m11.approx_eq(&other.m11) && self.m12.approx_eq(&other.m12) && + self.m13.approx_eq(&other.m13) && self.m14.approx_eq(&other.m14) && + self.m21.approx_eq(&other.m21) && self.m22.approx_eq(&other.m22) && + self.m23.approx_eq(&other.m23) && self.m24.approx_eq(&other.m24) && + self.m31.approx_eq(&other.m31) && self.m32.approx_eq(&other.m32) && + self.m33.approx_eq(&other.m33) && self.m34.approx_eq(&other.m34) && + self.m41.approx_eq(&other.m41) && self.m42.approx_eq(&other.m42) && + self.m43.approx_eq(&other.m43) && self.m44.approx_eq(&other.m44) + } + + /// Returns the same matrix with a different destination unit. + #[inline] + pub fn with_destination(&self) -> TypedMatrix4D { + TypedMatrix4D::row_major( + self.m11, self.m12, self.m13, self.m14, + self.m21, self.m22, self.m23, self.m24, + self.m31, self.m32, self.m33, self.m34, + self.m41, self.m42, self.m43, self.m44, + ) + } + + /// Returns the same matrix with a different source unit. + #[inline] + pub fn with_source(&self) -> TypedMatrix4D { + TypedMatrix4D::row_major( + self.m11, self.m12, self.m13, self.m14, + self.m21, self.m22, self.m23, self.m24, + self.m31, self.m32, self.m33, self.m34, + self.m41, self.m42, self.m43, self.m44, + ) + } + + /// Drop the units, preserving only the numeric value. + #[inline] + pub fn to_untyped(&self) -> Matrix4D { + Matrix4D::row_major( + self.m11, self.m12, self.m13, self.m14, + self.m21, self.m22, self.m23, self.m24, + self.m31, self.m32, self.m33, self.m34, + self.m41, self.m42, self.m43, self.m44, + ) + } + + /// Tag a unitless value with units. + #[inline] + pub fn from_untyped(m: &Matrix4D) -> Self { + TypedMatrix4D::row_major( + m.m11, m.m12, m.m13, m.m14, + m.m21, m.m22, m.m23, m.m24, + m.m31, m.m32, m.m33, m.m34, + m.m41, m.m42, m.m43, m.m44, + ) + } + + /// Returns the multiplication of the two matrices such that mat's transformation + /// applies after self's transformation. + pub fn post_mul(&self, mat: &TypedMatrix4D) -> TypedMatrix4D { + TypedMatrix4D::row_major( + self.m11 * mat.m11 + self.m12 * mat.m21 + self.m13 * mat.m31 + self.m14 * mat.m41, + self.m11 * mat.m12 + self.m12 * mat.m22 + self.m13 * mat.m32 + self.m14 * mat.m42, + self.m11 * mat.m13 + self.m12 * mat.m23 + self.m13 * mat.m33 + self.m14 * mat.m43, + self.m11 * mat.m14 + self.m12 * mat.m24 + self.m13 * mat.m34 + self.m14 * mat.m44, + self.m21 * mat.m11 + self.m22 * mat.m21 + self.m23 * mat.m31 + self.m24 * mat.m41, + self.m21 * mat.m12 + self.m22 * mat.m22 + self.m23 * mat.m32 + self.m24 * mat.m42, + self.m21 * mat.m13 + self.m22 * mat.m23 + self.m23 * mat.m33 + self.m24 * mat.m43, + self.m21 * mat.m14 + self.m22 * mat.m24 + self.m23 * mat.m34 + self.m24 * mat.m44, + self.m31 * mat.m11 + self.m32 * mat.m21 + self.m33 * mat.m31 + self.m34 * mat.m41, + self.m31 * mat.m12 + self.m32 * mat.m22 + self.m33 * mat.m32 + self.m34 * mat.m42, + self.m31 * mat.m13 + self.m32 * mat.m23 + self.m33 * mat.m33 + self.m34 * mat.m43, + self.m31 * mat.m14 + self.m32 * mat.m24 + self.m33 * mat.m34 + self.m34 * mat.m44, + self.m41 * mat.m11 + self.m42 * mat.m21 + self.m43 * mat.m31 + self.m44 * mat.m41, + self.m41 * mat.m12 + self.m42 * mat.m22 + self.m43 * mat.m32 + self.m44 * mat.m42, + self.m41 * mat.m13 + self.m42 * mat.m23 + self.m43 * mat.m33 + self.m44 * mat.m43, + self.m41 * mat.m14 + self.m42 * mat.m24 + self.m43 * mat.m34 + self.m44 * mat.m44, + ) + } + + /// Returns the multiplication of the two matrices such that mat's transformation + /// applies before self's transformation. + pub fn pre_mul(&self, mat: &TypedMatrix4D) -> TypedMatrix4D { + mat.post_mul(self) + } + + /// Returns the inverse matrix if possible. + pub fn inverse(&self) -> Option> { + let det = self.determinant(); + + if det == Zero::zero() { + return None; + } + + // todo(gw): this could be made faster by special casing + // for simpler matrix types. + let m = TypedMatrix4D::row_major( + self.m23*self.m34*self.m42 - self.m24*self.m33*self.m42 + + self.m24*self.m32*self.m43 - self.m22*self.m34*self.m43 - + self.m23*self.m32*self.m44 + self.m22*self.m33*self.m44, + + self.m14*self.m33*self.m42 - self.m13*self.m34*self.m42 - + self.m14*self.m32*self.m43 + self.m12*self.m34*self.m43 + + self.m13*self.m32*self.m44 - self.m12*self.m33*self.m44, + + self.m13*self.m24*self.m42 - self.m14*self.m23*self.m42 + + self.m14*self.m22*self.m43 - self.m12*self.m24*self.m43 - + self.m13*self.m22*self.m44 + self.m12*self.m23*self.m44, + + self.m14*self.m23*self.m32 - self.m13*self.m24*self.m32 - + self.m14*self.m22*self.m33 + self.m12*self.m24*self.m33 + + self.m13*self.m22*self.m34 - self.m12*self.m23*self.m34, + + self.m24*self.m33*self.m41 - self.m23*self.m34*self.m41 - + self.m24*self.m31*self.m43 + self.m21*self.m34*self.m43 + + self.m23*self.m31*self.m44 - self.m21*self.m33*self.m44, + + self.m13*self.m34*self.m41 - self.m14*self.m33*self.m41 + + self.m14*self.m31*self.m43 - self.m11*self.m34*self.m43 - + self.m13*self.m31*self.m44 + self.m11*self.m33*self.m44, + + self.m14*self.m23*self.m41 - self.m13*self.m24*self.m41 - + self.m14*self.m21*self.m43 + self.m11*self.m24*self.m43 + + self.m13*self.m21*self.m44 - self.m11*self.m23*self.m44, + + self.m13*self.m24*self.m31 - self.m14*self.m23*self.m31 + + self.m14*self.m21*self.m33 - self.m11*self.m24*self.m33 - + self.m13*self.m21*self.m34 + self.m11*self.m23*self.m34, + + self.m22*self.m34*self.m41 - self.m24*self.m32*self.m41 + + self.m24*self.m31*self.m42 - self.m21*self.m34*self.m42 - + self.m22*self.m31*self.m44 + self.m21*self.m32*self.m44, + + self.m14*self.m32*self.m41 - self.m12*self.m34*self.m41 - + self.m14*self.m31*self.m42 + self.m11*self.m34*self.m42 + + self.m12*self.m31*self.m44 - self.m11*self.m32*self.m44, + + self.m12*self.m24*self.m41 - self.m14*self.m22*self.m41 + + self.m14*self.m21*self.m42 - self.m11*self.m24*self.m42 - + self.m12*self.m21*self.m44 + self.m11*self.m22*self.m44, + + self.m14*self.m22*self.m31 - self.m12*self.m24*self.m31 - + self.m14*self.m21*self.m32 + self.m11*self.m24*self.m32 + + self.m12*self.m21*self.m34 - self.m11*self.m22*self.m34, + + self.m23*self.m32*self.m41 - self.m22*self.m33*self.m41 - + self.m23*self.m31*self.m42 + self.m21*self.m33*self.m42 + + self.m22*self.m31*self.m43 - self.m21*self.m32*self.m43, + + self.m12*self.m33*self.m41 - self.m13*self.m32*self.m41 + + self.m13*self.m31*self.m42 - self.m11*self.m33*self.m42 - + self.m12*self.m31*self.m43 + self.m11*self.m32*self.m43, + + self.m13*self.m22*self.m41 - self.m12*self.m23*self.m41 - + self.m13*self.m21*self.m42 + self.m11*self.m23*self.m42 + + self.m12*self.m21*self.m43 - self.m11*self.m22*self.m43, + + self.m12*self.m23*self.m31 - self.m13*self.m22*self.m31 + + self.m13*self.m21*self.m32 - self.m11*self.m23*self.m32 - + self.m12*self.m21*self.m33 + self.m11*self.m22*self.m33 + ); + + let _1: T = One::one(); + Some(m.mul_s(_1 / det)) + } + + /// Compute the determinant of the matrix. + pub fn determinant(&self) -> T { + self.m14 * self.m23 * self.m32 * self.m41 - + self.m13 * self.m24 * self.m32 * self.m41 - + self.m14 * self.m22 * self.m33 * self.m41 + + self.m12 * self.m24 * self.m33 * self.m41 + + self.m13 * self.m22 * self.m34 * self.m41 - + self.m12 * self.m23 * self.m34 * self.m41 - + self.m14 * self.m23 * self.m31 * self.m42 + + self.m13 * self.m24 * self.m31 * self.m42 + + self.m14 * self.m21 * self.m33 * self.m42 - + self.m11 * self.m24 * self.m33 * self.m42 - + self.m13 * self.m21 * self.m34 * self.m42 + + self.m11 * self.m23 * self.m34 * self.m42 + + self.m14 * self.m22 * self.m31 * self.m43 - + self.m12 * self.m24 * self.m31 * self.m43 - + self.m14 * self.m21 * self.m32 * self.m43 + + self.m11 * self.m24 * self.m32 * self.m43 + + self.m12 * self.m21 * self.m34 * self.m43 - + self.m11 * self.m22 * self.m34 * self.m43 - + self.m13 * self.m22 * self.m31 * self.m44 + + self.m12 * self.m23 * self.m31 * self.m44 + + self.m13 * self.m21 * self.m32 * self.m44 - + self.m11 * self.m23 * self.m32 * self.m44 - + self.m12 * self.m21 * self.m33 * self.m44 + + self.m11 * self.m22 * self.m33 * self.m44 + } + + /// Multiplies all of the matrix's component by a scalar and returns the result. + pub fn mul_s(&self, x: T) -> TypedMatrix4D { + TypedMatrix4D::row_major( + self.m11 * x, self.m12 * x, self.m13 * x, self.m14 * x, + self.m21 * x, self.m22 * x, self.m23 * x, self.m24 * x, + self.m31 * x, self.m32 * x, self.m33 * x, self.m34 * x, + self.m41 * x, self.m42 * x, self.m43 * x, self.m44 * x + ) + } + + /// Convenience function to create a scale matrix from a ScaleFactor. + pub fn from_scale_factor(scale: ScaleFactor) -> TypedMatrix4D { + TypedMatrix4D::create_scale(scale.get(), scale.get(), scale.get()) + } + + /// Returns the given 2d point transformed by this matrix. + /// + /// The input point must be use the unit Src, and the returned point has the unit Dst. + #[inline] + pub fn transform_point(&self, p: &TypedPoint2D) -> TypedPoint2D { + self.transform_point4d(&TypedPoint4D::new(p.x, p.y, Zero::zero(), One::one())).to_2d() + } + + /// Returns the given 3d point transformed by this matrix. + /// + /// The input point must be use the unit Src, and the returned point has the unit Dst. + #[inline] + pub fn transform_point3d(&self, p: &TypedPoint3D) -> TypedPoint3D { + self.transform_point4d(&TypedPoint4D::new(p.x, p.y, p.z, One::one())).to_3d() + } + + /// Returns the given 4d point transformed by this matrix. + /// + /// The input point must be use the unit Src, and the returned point has the unit Dst. + #[inline] + pub fn transform_point4d(&self, p: &TypedPoint4D) -> TypedPoint4D { + let x = p.x * self.m11 + p.y * self.m21 + p.z * self.m31 + p.w * self.m41; + let y = p.x * self.m12 + p.y * self.m22 + p.z * self.m32 + p.w * self.m42; + let z = p.x * self.m13 + p.y * self.m23 + p.z * self.m33 + p.w * self.m43; + let w = p.x * self.m14 + p.y * self.m24 + p.z * self.m34 + p.w * self.m44; + TypedPoint4D::new(x, y, z, w) + } + + /// Returns a rectangle that encompasses the result of transforming the given rectangle by this + /// matrix. + pub fn transform_rect(&self, rect: &TypedRect) -> TypedRect { + TypedRect::from_points(&[ + self.transform_point(&rect.origin), + self.transform_point(&rect.top_right()), + self.transform_point(&rect.bottom_left()), + self.transform_point(&rect.bottom_right()), + ]) + } + + /// Create a 3d translation matrix + pub fn create_translation(x: T, y: T, z: T) -> TypedMatrix4D { + let (_0, _1): (T, T) = (Zero::zero(), One::one()); + TypedMatrix4D::row_major( + _1, _0, _0, _0, + _0, _1, _0, _0, + _0, _0, _1, _0, + x, y, z, _1 + ) + } + + /// Returns a matrix with a translation applied before self's transformation. + pub fn pre_translated(&self, x: T, y: T, z: T) -> TypedMatrix4D { + self.pre_mul(&TypedMatrix4D::create_translation(x, y, z)) + } + + /// Returns a matrix with a translation applied after self's transformation. + pub fn post_translated(&self, x: T, y: T, z: T) -> TypedMatrix4D { + self.post_mul(&TypedMatrix4D::create_translation(x, y, z)) + } + + /// Create a 3d scale matrix + pub fn create_scale(x: T, y: T, z: T) -> TypedMatrix4D { + let (_0, _1): (T, T) = (Zero::zero(), One::one()); + TypedMatrix4D::row_major( + x, _0, _0, _0, + _0, y, _0, _0, + _0, _0, z, _0, + _0, _0, _0, _1 + ) + } + + /// Returns a matrix with a scale applied before self's transformation. + pub fn pre_scaled(&self, x: T, y: T, z: T) -> TypedMatrix4D { + TypedMatrix4D::row_major( + self.m11 * x, self.m12, self.m13, self.m14, + self.m21 , self.m22 * y, self.m23, self.m24, + self.m31 , self.m32, self.m33 * z, self.m34, + self.m41 , self.m42, self.m43, self.m44 + ) + } + + /// Returns a matrix with a scale applied after self's transformation. + pub fn post_scaled(&self, x: T, y: T, z: T) -> TypedMatrix4D { + self.post_mul(&TypedMatrix4D::create_scale(x, y, z)) + } + + /// Create a 3d rotation matrix from an angle / axis. + /// The supplied axis must be normalized. + pub fn create_rotation(x: T, y: T, z: T, theta: Radians) -> TypedMatrix4D { + let (_0, _1): (T, T) = (Zero::zero(), One::one()); + let _2 = _1 + _1; + + let xx = x * x; + let yy = y * y; + let zz = z * z; + + let half_theta = theta.get() / _2; + let sc = half_theta.sin() * half_theta.cos(); + let sq = half_theta.sin() * half_theta.sin(); + + TypedMatrix4D::row_major( + _1 - _2 * (yy + zz) * sq, + _2 * (x * y * sq - z * sc), + _2 * (x * z * sq + y * sc), + _0, + + _2 * (x * y * sq + z * sc), + _1 - _2 * (xx + zz) * sq, + _2 * (y * z * sq - x * sc), + _0, + + _2 * (x * z * sq - y * sc), + _2 * (y * z * sq + x * sc), + _1 - _2 * (xx + yy) * sq, + _0, + + _0, + _0, + _0, + _1 + ) + } + + /// Returns a matrix with a rotation applied after self's transformation. + pub fn post_rotated(&self, x: T, y: T, z: T, theta: Radians) -> TypedMatrix4D { + self.post_mul(&TypedMatrix4D::create_rotation(x, y, z, theta)) + } + + /// Returns a matrix with a rotation applied before self's transformation. + pub fn pre_rotated(&self, x: T, y: T, z: T, theta: Radians) -> TypedMatrix4D { + self.pre_mul(&TypedMatrix4D::create_rotation(x, y, z, theta)) + } + + /// Create a 2d skew matrix. + /// + /// See https://drafts.csswg.org/css-transforms/#funcdef-skew + pub fn create_skew(alpha: Radians, beta: Radians) -> TypedMatrix4D { + let (_0, _1): (T, T) = (Zero::zero(), One::one()); + let (sx, sy) = (beta.get().tan(), alpha.get().tan()); + TypedMatrix4D::row_major( + _1, sx, _0, _0, + sy, _1, _0, _0, + _0, _0, _1, _0, + _0, _0, _0, _1 + ) + } + + /// Create a simple perspective projection matrix + pub fn create_perspective(d: T) -> TypedMatrix4D { + let (_0, _1): (T, T) = (Zero::zero(), One::one()); + TypedMatrix4D::row_major( + _1, _0, _0, _0, + _0, _1, _0, _0, + _0, _0, _1, -_1 / d, + _0, _0, _0, _1 + ) + } +} + +impl TypedMatrix4D { + /// Returns an array containing this matrix's terms in row-major order (the order + /// in which the matrix is actually laid out in memory). + pub fn to_row_major_array(&self) -> [T; 16] { + [ + self.m11, self.m12, self.m13, self.m14, + self.m21, self.m22, self.m23, self.m24, + self.m31, self.m32, self.m33, self.m34, + self.m41, self.m42, self.m43, self.m44 + ] + } + + /// Returns an array containing this matrix's terms in column-major order. + pub fn to_column_major_array(&self) -> [T; 16] { + [ + self.m11, self.m21, self.m31, self.m41, + self.m12, self.m22, self.m32, self.m42, + self.m13, self.m23, self.m33, self.m43, + self.m14, self.m24, self.m34, self.m44 + ] + } + + /// Returns an array containing this matrix's 4 rows in (in row-major order) + /// as arrays. + /// + /// This is a convenience method to interface with other libraries like glium. + pub fn to_row_arrays(&self) -> [[T; 4];4] { + [ + [self.m11, self.m12, self.m13, self.m14], + [self.m21, self.m22, self.m23, self.m24], + [self.m31, self.m32, self.m33, self.m34], + [self.m41, self.m42, self.m43, self.m44] + ] + } + + /// Returns an array containing this matrix's 4 columns in (in row-major order, + /// or 4 rows in column-major order) as arrays. + /// + /// This is a convenience method to interface with other libraries like glium. + pub fn to_column_arrays(&self) -> [[T; 4]; 4] { + [ + [self.m11, self.m21, self.m31, self.m41], + [self.m12, self.m22, self.m32, self.m42], + [self.m13, self.m23, self.m33, self.m43], + [self.m14, self.m24, self.m34, self.m44] + ] + } +} + +impl fmt::Debug for TypedMatrix4D +where T: Copy + fmt::Debug + + PartialEq + + One + Zero { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.is_identity() { + write!(f, "[I]") + } else { + self.to_row_major_array().fmt(f) + } + } +} + +#[cfg(test)] +mod tests { + use approxeq::ApproxEq; + use matrix2d::Matrix2D; + use point::{Point2D, Point3D, Point4D}; + use Radians; + use super::*; + + use std::f32::consts::FRAC_PI_2; + + type Mf32 = Matrix4D; + + // For convenience. + fn rad(v: f32) -> Radians { Radians::new(v) } + + #[test] + pub fn test_translation() { + let t1 = Mf32::create_translation(1.0, 2.0, 3.0); + let t2 = Mf32::identity().pre_translated(1.0, 2.0, 3.0); + let t3 = Mf32::identity().post_translated(1.0, 2.0, 3.0); + assert_eq!(t1, t2); + assert_eq!(t1, t3); + + assert_eq!(t1.transform_point3d(&Point3D::new(1.0, 1.0, 1.0)), Point3D::new(2.0, 3.0, 4.0)); + assert_eq!(t1.transform_point(&Point2D::new(1.0, 1.0)), Point2D::new(2.0, 3.0)); + + assert_eq!(t1.post_mul(&t1), Mf32::create_translation(2.0, 4.0, 6.0)); + + assert!(!t1.is_2d()); + assert_eq!(Mf32::create_translation(1.0, 2.0, 3.0).to_2d(), Matrix2D::create_translation(1.0, 2.0)); + } + + #[test] + pub fn test_rotation() { + let r1 = Mf32::create_rotation(0.0, 0.0, 1.0, rad(FRAC_PI_2)); + let r2 = Mf32::identity().pre_rotated(0.0, 0.0, 1.0, rad(FRAC_PI_2)); + let r3 = Mf32::identity().post_rotated(0.0, 0.0, 1.0, rad(FRAC_PI_2)); + assert_eq!(r1, r2); + assert_eq!(r1, r3); + + assert!(r1.transform_point3d(&Point3D::new(1.0, 2.0, 3.0)).approx_eq(&Point3D::new(2.0, -1.0, 3.0))); + assert!(r1.transform_point(&Point2D::new(1.0, 2.0)).approx_eq(&Point2D::new(2.0, -1.0))); + + assert!(r1.post_mul(&r1).approx_eq(&Mf32::create_rotation(0.0, 0.0, 1.0, rad(FRAC_PI_2*2.0)))); + + assert!(r1.is_2d()); + assert!(r1.to_2d().approx_eq(&Matrix2D::create_rotation(rad(FRAC_PI_2)))); + } + + #[test] + pub fn test_scale() { + let s1 = Mf32::create_scale(2.0, 3.0, 4.0); + let s2 = Mf32::identity().pre_scaled(2.0, 3.0, 4.0); + let s3 = Mf32::identity().post_scaled(2.0, 3.0, 4.0); + assert_eq!(s1, s2); + assert_eq!(s1, s3); + + assert!(s1.transform_point3d(&Point3D::new(2.0, 2.0, 2.0)).approx_eq(&Point3D::new(4.0, 6.0, 8.0))); + assert!(s1.transform_point(&Point2D::new(2.0, 2.0)).approx_eq(&Point2D::new(4.0, 6.0))); + + assert_eq!(s1.post_mul(&s1), Mf32::create_scale(4.0, 9.0, 16.0)); + + assert!(!s1.is_2d()); + assert_eq!(Mf32::create_scale(2.0, 3.0, 0.0).to_2d(), Matrix2D::create_scale(2.0, 3.0)); + } + + #[test] + pub fn test_ortho() { + let (left, right, bottom, top) = (0.0f32, 1.0f32, 0.1f32, 1.0f32); + let (near, far) = (-1.0f32, 1.0f32); + let result = Mf32::ortho(left, right, bottom, top, near, far); + let expected = Mf32::row_major( + 2.0, 0.0, 0.0, 0.0, + 0.0, 2.22222222, 0.0, 0.0, + 0.0, 0.0, -1.0, 0.0, + -1.0, -1.22222222, -0.0, 1.0 + ); + debug!("result={:?} expected={:?}", result, expected); + assert!(result.approx_eq(&expected)); + } + + #[test] + pub fn test_is_2d() { + assert!(Mf32::identity().is_2d()); + assert!(Mf32::create_rotation(0.0, 0.0, 1.0, rad(0.7854)).is_2d()); + assert!(!Mf32::create_rotation(0.0, 1.0, 0.0, rad(0.7854)).is_2d()); + } + + #[test] + pub fn test_row_major_2d() { + let m1 = Mf32::row_major_2d(1.0, 2.0, 3.0, 4.0, 5.0, 6.0); + let m2 = Mf32::row_major( + 1.0, 2.0, 0.0, 0.0, + 3.0, 4.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 5.0, 6.0, 0.0, 1.0 + ); + assert_eq!(m1, m2); + } + + #[test] + fn test_column_major() { + assert_eq!( + Mf32::row_major( + 1.0, 2.0, 3.0, 4.0, + 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, + 13.0, 14.0, 15.0, 16.0, + ), + Mf32::column_major( + 1.0, 5.0, 9.0, 13.0, + 2.0, 6.0, 10.0, 14.0, + 3.0, 7.0, 11.0, 15.0, + 4.0, 8.0, 12.0, 16.0, + ) + ); + } + + #[test] + pub fn test_inverse_simple() { + let m1 = Mf32::identity(); + let m2 = m1.inverse().unwrap(); + assert!(m1.approx_eq(&m2)); + } + + #[test] + pub fn test_inverse_scale() { + let m1 = Mf32::create_scale(1.5, 0.3, 2.1); + let m2 = m1.inverse().unwrap(); + assert!(m1.pre_mul(&m2).approx_eq(&Mf32::identity())); + } + + #[test] + pub fn test_inverse_translate() { + let m1 = Mf32::create_translation(-132.0, 0.3, 493.0); + let m2 = m1.inverse().unwrap(); + assert!(m1.pre_mul(&m2).approx_eq(&Mf32::identity())); + } + + #[test] + pub fn test_inverse_rotate() { + let m1 = Mf32::create_rotation(0.0, 1.0, 0.0, rad(1.57)); + let m2 = m1.inverse().unwrap(); + assert!(m1.pre_mul(&m2).approx_eq(&Mf32::identity())); + } + + #[test] + pub fn test_inverse_transform_point_2d() { + let m1 = Mf32::create_translation(100.0, 200.0, 0.0); + let m2 = m1.inverse().unwrap(); + assert!(m1.pre_mul(&m2).approx_eq(&Mf32::identity())); + + let p1 = Point2D::new(1000.0, 2000.0); + let p2 = m1.transform_point(&p1); + assert!(p2.eq(&Point2D::new(1100.0, 2200.0))); + + let p3 = m2.transform_point(&p2); + assert!(p3.eq(&p1)); + } + + #[test] + fn test_inverse_none() { + assert!(Mf32::create_scale(2.0, 0.0, 2.0).inverse().is_none()); + assert!(Mf32::create_scale(2.0, 2.0, 2.0).inverse().is_some()); + } + + #[test] + pub fn test_pre_post() { + let m1 = Matrix4D::identity().post_scaled(1.0, 2.0, 3.0).post_translated(1.0, 2.0, 3.0); + let m2 = Matrix4D::identity().pre_translated(1.0, 2.0, 3.0).pre_scaled(1.0, 2.0, 3.0); + assert!(m1.approx_eq(&m2)); + + let r = Mf32::create_rotation(0.0, 0.0, 1.0, rad(FRAC_PI_2)); + let t = Mf32::create_translation(2.0, 3.0, 0.0); + + let a = Point3D::new(1.0, 1.0, 1.0); + + assert!(r.post_mul(&t).transform_point3d(&a).approx_eq(&Point3D::new(3.0, 2.0, 1.0))); + assert!(t.post_mul(&r).transform_point3d(&a).approx_eq(&Point3D::new(4.0, -3.0, 1.0))); + assert!(t.post_mul(&r).transform_point3d(&a).approx_eq(&r.transform_point3d(&t.transform_point3d(&a)))); + + assert!(r.pre_mul(&t).transform_point3d(&a).approx_eq(&Point3D::new(4.0, -3.0, 1.0))); + assert!(t.pre_mul(&r).transform_point3d(&a).approx_eq(&Point3D::new(3.0, 2.0, 1.0))); + assert!(t.pre_mul(&r).transform_point3d(&a).approx_eq(&t.transform_point3d(&r.transform_point3d(&a)))); + } + + #[test] + fn test_size_of() { + use std::mem::size_of; + assert_eq!(size_of::>(), 16*size_of::()); + assert_eq!(size_of::>(), 16*size_of::()); + } + + #[test] + pub fn test_transform_associativity() { + let m1 = Mf32::row_major(3.0, 2.0, 1.5, 1.0, + 0.0, 4.5, -1.0, -4.0, + 0.0, 3.5, 2.5, 40.0, + 0.0, 3.0, 0.0, 1.0); + let m2 = Mf32::row_major(1.0, -1.0, 3.0, 0.0, + -1.0, 0.5, 0.0, 2.0, + 1.5, -2.0, 6.0, 0.0, + -2.5, 6.0, 1.0, 1.0); + + let p = Point4D::new(1.0, 3.0, 5.0, 1.0); + let p1 = m2.pre_mul(&m1).transform_point4d(&p); + let p2 = m2.transform_point4d(&m1.transform_point4d(&p)); + assert!(p1.approx_eq(&p2)); + } + + #[test] + pub fn test_is_identity() { + let m1 = Matrix4D::identity(); + assert!(m1.is_identity()); + let m2 = m1.post_translated(0.1, 0.0, 0.0); + assert!(!m2.is_identity()); + } +} diff --git a/third_party/rust/euclid-0.11.3/src/num.rs b/third_party/rust/euclid-0.11.3/src/num.rs new file mode 100644 index 000000000000..b612624273ed --- /dev/null +++ b/third_party/rust/euclid-0.11.3/src/num.rs @@ -0,0 +1,66 @@ +// Copyright 2014 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +//! A one-dimensional length, tagged with its units. + +use num_traits; + + +pub trait Zero { + fn zero() -> Self; +} + +impl Zero for T { + fn zero() -> T { num_traits::Zero::zero() } +} + +pub trait One { + fn one() -> Self; +} + +impl One for T { + fn one() -> T { num_traits::One::one() } +} + +pub trait Round : Copy { fn round(self) -> Self; } +pub trait Floor : Copy { fn floor(self) -> Self; } +pub trait Ceil : Copy { fn ceil(self) -> Self; } + +impl Round for f32 { fn round(self) -> Self { self.round() } } +impl Round for f64 { fn round(self) -> Self { self.round() } } +impl Round for i16 { fn round(self) -> Self { self } } +impl Round for u16 { fn round(self) -> Self { self } } +impl Round for i32 { fn round(self) -> Self { self } } +impl Round for i64 { fn round(self) -> Self { self } } +impl Round for u32 { fn round(self) -> Self { self } } +impl Round for u64 { fn round(self) -> Self { self } } +impl Round for usize { fn round(self) -> Self { self } } +impl Round for isize { fn round(self) -> Self { self } } + +impl Floor for f32 { fn floor(self) -> Self { self.floor() } } +impl Floor for f64 { fn floor(self) -> Self { self.floor() } } +impl Floor for i16 { fn floor(self) -> Self { self } } +impl Floor for u16 { fn floor(self) -> Self { self } } +impl Floor for i32 { fn floor(self) -> Self { self } } +impl Floor for i64 { fn floor(self) -> Self { self } } +impl Floor for u32 { fn floor(self) -> Self { self } } +impl Floor for u64 { fn floor(self) -> Self { self } } +impl Floor for usize { fn floor(self) -> Self { self } } +impl Floor for isize { fn floor(self) -> Self { self } } + +impl Ceil for f32 { fn ceil(self) -> Self { self.ceil() } } +impl Ceil for f64 { fn ceil(self) -> Self { self.ceil() } } +impl Ceil for i16 { fn ceil(self) -> Self { self } } +impl Ceil for u16 { fn ceil(self) -> Self { self } } +impl Ceil for i32 { fn ceil(self) -> Self { self } } +impl Ceil for i64 { fn ceil(self) -> Self { self } } +impl Ceil for u32 { fn ceil(self) -> Self { self } } +impl Ceil for u64 { fn ceil(self) -> Self { self } } +impl Ceil for usize { fn ceil(self) -> Self { self } } +impl Ceil for isize { fn ceil(self) -> Self { self } } + diff --git a/third_party/rust/euclid-0.11.3/src/point.rs b/third_party/rust/euclid-0.11.3/src/point.rs new file mode 100644 index 000000000000..6239fbe5181a --- /dev/null +++ b/third_party/rust/euclid-0.11.3/src/point.rs @@ -0,0 +1,995 @@ +// Copyright 2013 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::UnknownUnit; +use approxeq::ApproxEq; +use length::Length; +use scale_factor::ScaleFactor; +use size::TypedSize2D; +use num::*; +use num_traits::{Float, NumCast}; +use std::fmt; +use std::ops::{Add, Neg, Mul, Sub, Div}; +use std::marker::PhantomData; + +define_matrix! { + /// A 2d Point tagged with a unit. + #[derive(RustcDecodable, RustcEncodable)] + pub struct TypedPoint2D { + pub x: T, + pub y: T, + } +} + +/// Default 2d point type with no unit. +/// +/// `Point2D` provides the same methods as `TypedPoint2D`. +pub type Point2D = TypedPoint2D; + +impl TypedPoint2D { + /// Constructor, setting all components to zero. + #[inline] + pub fn zero() -> TypedPoint2D { + TypedPoint2D::new(Zero::zero(), Zero::zero()) + } + + /// Convert into a 3d point. + #[inline] + pub fn to_3d(&self) -> TypedPoint3D { + TypedPoint3D::new(self.x, self.y, Zero::zero()) + } +} + +impl fmt::Debug for TypedPoint2D { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "({:?},{:?})", self.x, self.y) + } +} + +impl fmt::Display for TypedPoint2D { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "({},{})", self.x, self.y) + } +} + +impl TypedPoint2D { + /// Constructor taking scalar values directly. + #[inline] + pub fn new(x: T, y: T) -> TypedPoint2D { + TypedPoint2D { x: x, y: y, _unit: PhantomData } + } + + /// Constructor taking properly typed Lengths instead of scalar values. + #[inline] + pub fn from_lengths(x: Length, y: Length) -> TypedPoint2D { + TypedPoint2D::new(x.0, y.0) + } + + /// Returns self.x as a Length carrying the unit. + #[inline] + pub fn x_typed(&self) -> Length { Length::new(self.x) } + + /// Returns self.y as a Length carrying the unit. + #[inline] + pub fn y_typed(&self) -> Length { Length::new(self.y) } + + /// Drop the units, preserving only the numeric value. + #[inline] + pub fn to_untyped(&self) -> Point2D { + TypedPoint2D::new(self.x, self.y) + } + + /// Tag a unitless value with units. + #[inline] + pub fn from_untyped(p: &Point2D) -> TypedPoint2D { + TypedPoint2D::new(p.x, p.y) + } + + #[inline] + pub fn to_array(&self) -> [T; 2] { + [self.x, self.y] + } +} + +impl TypedPoint2D +where T: Copy + Mul + Add + Sub { + /// Dot product. + #[inline] + pub fn dot(self, other: TypedPoint2D) -> T { + self.x * other.x + self.y * other.y + } + + /// Returns the norm of the cross product [self.x, self.y, 0] x [other.x, other.y, 0].. + #[inline] + pub fn cross(self, other: TypedPoint2D) -> T { + self.x * other.y - self.y * other.x + } + + #[inline] + pub fn normalize(self) -> Self where T: Float + ApproxEq { + let dot = self.dot(self); + if dot.approx_eq(&T::zero()) { + self + } else { + self / dot.sqrt() + } + } +} + +impl, U> Add for TypedPoint2D { + type Output = TypedPoint2D; + fn add(self, other: TypedPoint2D) -> TypedPoint2D { + TypedPoint2D::new(self.x + other.x, self.y + other.y) + } +} + +impl, U> Add> for TypedPoint2D { + type Output = TypedPoint2D; + fn add(self, other: TypedSize2D) -> TypedPoint2D { + TypedPoint2D::new(self.x + other.width, self.y + other.height) + } +} + +impl, U> TypedPoint2D { + pub fn add_size(&self, other: &TypedSize2D) -> TypedPoint2D { + TypedPoint2D::new(self.x + other.width, self.y + other.height) + } +} + +impl, U> Sub for TypedPoint2D { + type Output = TypedPoint2D; + fn sub(self, other: TypedPoint2D) -> TypedPoint2D { + TypedPoint2D::new(self.x - other.x, self.y - other.y) + } +} + +impl , U> Neg for TypedPoint2D { + type Output = TypedPoint2D; + #[inline] + fn neg(self) -> TypedPoint2D { + TypedPoint2D::new(-self.x, -self.y) + } +} + +impl TypedPoint2D { + pub fn min(self, other: TypedPoint2D) -> TypedPoint2D { + TypedPoint2D::new(self.x.min(other.x), self.y.min(other.y)) + } + + pub fn max(self, other: TypedPoint2D) -> TypedPoint2D { + TypedPoint2D::new(self.x.max(other.x), self.y.max(other.y)) + } +} + +impl, U> Mul for TypedPoint2D { + type Output = TypedPoint2D; + #[inline] + fn mul(self, scale: T) -> TypedPoint2D { + TypedPoint2D::new(self.x * scale, self.y * scale) + } +} + +impl, U> Div for TypedPoint2D { + type Output = TypedPoint2D; + #[inline] + fn div(self, scale: T) -> TypedPoint2D { + TypedPoint2D::new(self.x / scale, self.y / scale) + } +} + +impl, U1, U2> Mul> for TypedPoint2D { + type Output = TypedPoint2D; + #[inline] + fn mul(self, scale: ScaleFactor) -> TypedPoint2D { + TypedPoint2D::new(self.x * scale.get(), self.y * scale.get()) + } +} + +impl, U1, U2> Div> for TypedPoint2D { + type Output = TypedPoint2D; + #[inline] + fn div(self, scale: ScaleFactor) -> TypedPoint2D { + TypedPoint2D::new(self.x / scale.get(), self.y / scale.get()) + } +} + +impl TypedPoint2D { + /// Rounds each component to the nearest integer value. + /// + /// This behavior is preserved for negative values (unlike the basic cast). + /// For example `{ -0.1, -0.8 }.round() == { 0.0, -1.0 }`. + pub fn round(&self) -> Self { + TypedPoint2D::new(self.x.round(), self.y.round()) + } +} + +impl TypedPoint2D { + /// Rounds each component to the smallest integer equal or greater than the original value. + /// + /// This behavior is preserved for negative values (unlike the basic cast). + /// For example `{ -0.1, -0.8 }.ceil() == { 0.0, 0.0 }`. + pub fn ceil(&self) -> Self { + TypedPoint2D::new(self.x.ceil(), self.y.ceil()) + } +} + +impl TypedPoint2D { + /// Rounds each component to the biggest integer equal or lower than the original value. + /// + /// This behavior is preserved for negative values (unlike the basic cast). + /// For example `{ -0.1, -0.8 }.floor() == { -1.0, -1.0 }`. + pub fn floor(&self) -> Self { + TypedPoint2D::new(self.x.floor(), self.y.floor()) + } +} + +impl TypedPoint2D { + /// Cast from one numeric representation to another, preserving the units. + /// + /// When casting from floating point to integer coordinates, the decimals are truncated + /// as one would expect from a simple cast, but this behavior does not always make sense + /// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting. + pub fn cast(&self) -> Option> { + match (NumCast::from(self.x), NumCast::from(self.y)) { + (Some(x), Some(y)) => Some(TypedPoint2D::new(x, y)), + _ => None + } + } + + // Convenience functions for common casts + + /// Cast into an `f32` point. + pub fn to_f32(&self) -> TypedPoint2D { + self.cast().unwrap() + } + + /// Cast into an `usize` point, truncating decimals if any. + /// + /// When casting from floating point points, it is worth considering whether + /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain + /// the desired conversion behavior. + pub fn to_uint(&self) -> TypedPoint2D { + self.cast().unwrap() + } + + /// Cast into an i32 point, truncating decimals if any. + /// + /// When casting from floating point points, it is worth considering whether + /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain + /// the desired conversion behavior. + pub fn to_i32(&self) -> TypedPoint2D { + self.cast().unwrap() + } + + /// Cast into an i64 point, truncating decimals if any. + /// + /// When casting from floating point points, it is worth considering whether + /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain + /// the desired conversion behavior. + pub fn to_i64(&self) -> TypedPoint2D { + self.cast().unwrap() + } +} + +impl, U> ApproxEq> for TypedPoint2D { + #[inline] + fn approx_epsilon() -> Self { + TypedPoint2D::new(T::approx_epsilon(), T::approx_epsilon()) + } + + #[inline] + fn approx_eq(&self, other: &Self) -> bool { + self.x.approx_eq(&other.x) && self.y.approx_eq(&other.y) + } + + #[inline] + fn approx_eq_eps(&self, other: &Self, eps: &Self) -> bool { + self.x.approx_eq_eps(&other.x, &eps.x) && self.y.approx_eq_eps(&other.y, &eps.y) + } +} + +define_matrix! { + /// A 3d Point tagged with a unit. + #[derive(RustcDecodable, RustcEncodable)] + pub struct TypedPoint3D { + pub x: T, + pub y: T, + pub z: T, + } +} + +/// Default 3d point type with no unit. +/// +/// `Point3D` provides the same methods as `TypedPoint3D`. +pub type Point3D = TypedPoint3D; + +impl TypedPoint3D { + /// Constructor, setting all copmonents to zero. + #[inline] + pub fn zero() -> TypedPoint3D { + TypedPoint3D::new(Zero::zero(), Zero::zero(), Zero::zero()) + } +} + +impl fmt::Debug for TypedPoint3D { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "({:?},{:?},{:?})", self.x, self.y, self.z) + } +} + +impl fmt::Display for TypedPoint3D { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "({},{},{})", self.x, self.y, self.z) + } +} + +impl TypedPoint3D { + /// Constructor taking scalar values directly. + #[inline] + pub fn new(x: T, y: T, z: T) -> TypedPoint3D { + TypedPoint3D { x: x, y: y, z: z, _unit: PhantomData } + } + + /// Constructor taking properly typed Lengths instead of scalar values. + #[inline] + pub fn from_lengths(x: Length, y: Length, z: Length) -> TypedPoint3D { + TypedPoint3D::new(x.0, y.0, z.0) + } + + /// Returns self.x as a Length carrying the unit. + #[inline] + pub fn x_typed(&self) -> Length { Length::new(self.x) } + + /// Returns self.y as a Length carrying the unit. + #[inline] + pub fn y_typed(&self) -> Length { Length::new(self.y) } + + /// Returns self.z as a Length carrying the unit. + #[inline] + pub fn z_typed(&self) -> Length { Length::new(self.z) } + + #[inline] + pub fn to_array(&self) -> [T; 3] { [self.x, self.y, self.z] } + + /// Drop the units, preserving only the numeric value. + #[inline] + pub fn to_untyped(&self) -> Point3D { + TypedPoint3D::new(self.x, self.y, self.z) + } + + /// Tag a unitless value with units. + #[inline] + pub fn from_untyped(p: &Point3D) -> TypedPoint3D { + TypedPoint3D::new(p.x, p.y, p.z) + } + + /// Convert into a 2d point. + #[inline] + pub fn to_2d(&self) -> TypedPoint2D { + TypedPoint2D::new(self.x, self.y) + } +} + +impl + + Add + + Sub + + Copy, U> TypedPoint3D { + + // Dot product. + #[inline] + pub fn dot(self, other: TypedPoint3D) -> T { + self.x * other.x + + self.y * other.y + + self.z * other.z + } + + // Cross product. + #[inline] + pub fn cross(self, other: TypedPoint3D) -> TypedPoint3D { + TypedPoint3D::new(self.y * other.z - self.z * other.y, + self.z * other.x - self.x * other.z, + self.x * other.y - self.y * other.x) + } + + #[inline] + pub fn normalize(self) -> Self where T: Float + ApproxEq { + let dot = self.dot(self); + if dot.approx_eq(&T::zero()) { + self + } else { + self / dot.sqrt() + } + } +} + +impl, U> Add for TypedPoint3D { + type Output = TypedPoint3D; + fn add(self, other: TypedPoint3D) -> TypedPoint3D { + TypedPoint3D::new(self.x + other.x, + self.y + other.y, + self.z + other.z) + } +} + +impl, U> Sub for TypedPoint3D { + type Output = TypedPoint3D; + fn sub(self, other: TypedPoint3D) -> TypedPoint3D { + TypedPoint3D::new(self.x - other.x, + self.y - other.y, + self.z - other.z) + } +} + +impl , U> Neg for TypedPoint3D { + type Output = TypedPoint3D; + #[inline] + fn neg(self) -> TypedPoint3D { + TypedPoint3D::new(-self.x, -self.y, -self.z) + } +} + +impl, U> Mul for TypedPoint3D { + type Output = Self; + #[inline] + fn mul(self, scale: T) -> Self { + Self::new(self.x * scale, self.y * scale, self.z * scale) + } +} + +impl, U> Div for TypedPoint3D { + type Output = Self; + #[inline] + fn div(self, scale: T) -> Self { + Self::new(self.x / scale, self.y / scale, self.z / scale) + } +} + +impl TypedPoint3D { + pub fn min(self, other: TypedPoint3D) -> TypedPoint3D { + TypedPoint3D::new(self.x.min(other.x), + self.y.min(other.y), + self.z.min(other.z)) + } + + pub fn max(self, other: TypedPoint3D) -> TypedPoint3D { + TypedPoint3D::new(self.x.max(other.x), self.y.max(other.y), + self.z.max(other.z)) + } +} + +impl TypedPoint3D { + /// Rounds each component to the nearest integer value. + /// + /// This behavior is preserved for negative values (unlike the basic cast). + pub fn round(&self) -> Self { + TypedPoint3D::new(self.x.round(), self.y.round(), self.z.round()) + } +} + +impl TypedPoint3D { + /// Rounds each component to the smallest integer equal or greater than the original value. + /// + /// This behavior is preserved for negative values (unlike the basic cast). + pub fn ceil(&self) -> Self { + TypedPoint3D::new(self.x.ceil(), self.y.ceil(), self.z.ceil()) + } +} + +impl TypedPoint3D { + /// Rounds each component to the biggest integer equal or lower than the original value. + /// + /// This behavior is preserved for negative values (unlike the basic cast). + pub fn floor(&self) -> Self { + TypedPoint3D::new(self.x.floor(), self.y.floor(), self.z.floor()) + } +} + +impl TypedPoint3D { + /// Cast from one numeric representation to another, preserving the units. + /// + /// When casting from floating point to integer coordinates, the decimals are truncated + /// as one would expect from a simple cast, but this behavior does not always make sense + /// geometrically. Consider using round(), ceil or floor() before casting. + pub fn cast(&self) -> Option> { + match (NumCast::from(self.x), + NumCast::from(self.y), + NumCast::from(self.z)) { + (Some(x), Some(y), Some(z)) => Some(TypedPoint3D::new(x, y, z)), + _ => None + } + } + + // Convenience functions for common casts + + /// Cast into an `f32` point. + pub fn to_f32(&self) -> TypedPoint3D { + self.cast().unwrap() + } + + /// Cast into an `usize` point, truncating decimals if any. + /// + /// When casting from floating point points, it is worth considering whether + /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain + /// the desired conversion behavior. + pub fn to_uint(&self) -> TypedPoint3D { + self.cast().unwrap() + } + + /// Cast into an `i32` point, truncating decimals if any. + /// + /// When casting from floating point points, it is worth considering whether + /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain + /// the desired conversion behavior. + pub fn to_i32(&self) -> TypedPoint3D { + self.cast().unwrap() + } + + /// Cast into an `i64` point, truncating decimals if any. + /// + /// When casting from floating point points, it is worth considering whether + /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain + /// the desired conversion behavior. + pub fn to_i64(&self) -> TypedPoint3D { + self.cast().unwrap() + } +} + +impl, U> ApproxEq> for TypedPoint3D { + #[inline] + fn approx_epsilon() -> Self { + TypedPoint3D::new(T::approx_epsilon(), T::approx_epsilon(), T::approx_epsilon()) + } + + #[inline] + fn approx_eq(&self, other: &Self) -> bool { + self.x.approx_eq(&other.x) + && self.y.approx_eq(&other.y) + && self.z.approx_eq(&other.z) + } + + #[inline] + fn approx_eq_eps(&self, other: &Self, eps: &Self) -> bool { + self.x.approx_eq_eps(&other.x, &eps.x) + && self.y.approx_eq_eps(&other.y, &eps.y) + && self.z.approx_eq_eps(&other.z, &eps.z) + } +} + +define_matrix! { + /// A 4d Point tagged with a unit. + #[derive(RustcDecodable, RustcEncodable)] + pub struct TypedPoint4D { + pub x: T, + pub y: T, + pub z: T, + pub w: T, + } +} + +/// Default 4d point with no unit. +/// +/// `Point4D` provides the same methods as `TypedPoint4D`. +pub type Point4D = TypedPoint4D; + +impl TypedPoint4D { + /// Constructor, setting all copmonents to zero. + #[inline] + pub fn zero() -> TypedPoint4D { + TypedPoint4D::new(Zero::zero(), Zero::zero(), Zero::zero(), Zero::zero()) + } +} + +impl fmt::Debug for TypedPoint4D { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "({:?},{:?},{:?},{:?})", self.x, self.y, self.z, self.w) + } +} + +impl fmt::Display for TypedPoint4D { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "({},{},{},{})", self.x, self.y, self.z, self.w) + } +} + +impl TypedPoint4D { + /// Constructor taking scalar values directly. + #[inline] + pub fn new(x: T, y: T, z: T, w: T) -> TypedPoint4D { + TypedPoint4D { x: x, y: y, z: z, w: w, _unit: PhantomData } + } + + /// Constructor taking properly typed Lengths instead of scalar values. + #[inline] + pub fn from_lengths(x: Length, + y: Length, + z: Length, + w: Length) -> TypedPoint4D { + TypedPoint4D::new(x.0, y.0, z.0, w.0) + } + + /// Returns self.x as a Length carrying the unit. + #[inline] + pub fn x_typed(&self) -> Length { Length::new(self.x) } + + /// Returns self.y as a Length carrying the unit. + #[inline] + pub fn y_typed(&self) -> Length { Length::new(self.y) } + + /// Returns self.z as a Length carrying the unit. + #[inline] + pub fn z_typed(&self) -> Length { Length::new(self.z) } + + /// Returns self.w as a Length carrying the unit. + #[inline] + pub fn w_typed(&self) -> Length { Length::new(self.w) } + + /// Drop the units, preserving only the numeric value. + #[inline] + pub fn to_untyped(&self) -> Point4D { + TypedPoint4D::new(self.x, self.y, self.z, self.w) + } + + /// Tag a unitless value with units. + #[inline] + pub fn from_untyped(p: &Point4D) -> TypedPoint4D { + TypedPoint4D::new(p.x, p.y, p.z, p.w) + } + + #[inline] + pub fn to_array(&self) -> [T; 4] { + [self.x, self.y, self.z, self.w] + } +} + +impl, U> TypedPoint4D { + /// Convert into a 2d point. + #[inline] + pub fn to_2d(self) -> TypedPoint2D { + TypedPoint2D::new(self.x / self.w, self.y / self.w) + } + + /// Convert into a 3d point. + #[inline] + pub fn to_3d(self) -> TypedPoint3D { + TypedPoint3D::new(self.x / self.w, self.y / self.w, self.z / self.w) + } +} + +impl, U> Add for TypedPoint4D { + type Output = TypedPoint4D; + fn add(self, other: TypedPoint4D) -> TypedPoint4D { + TypedPoint4D::new(self.x + other.x, + self.y + other.y, + self.z + other.z, + self.w + other.w) + } +} + +impl, U> Sub for TypedPoint4D { + type Output = TypedPoint4D; + fn sub(self, other: TypedPoint4D) -> TypedPoint4D { + TypedPoint4D::new(self.x - other.x, + self.y - other.y, + self.z - other.z, + self.w - other.w) + } +} + +impl , U> Neg for TypedPoint4D { + type Output = TypedPoint4D; + #[inline] + fn neg(self) -> TypedPoint4D { + TypedPoint4D::new(-self.x, -self.y, -self.z, -self.w) + } +} + +impl TypedPoint4D { + pub fn min(self, other: TypedPoint4D) -> TypedPoint4D { + TypedPoint4D::new(self.x.min(other.x), self.y.min(other.y), + self.z.min(other.z), self.w.min(other.w)) + } + + pub fn max(self, other: TypedPoint4D) -> TypedPoint4D { + TypedPoint4D::new(self.x.max(other.x), self.y.max(other.y), + self.z.max(other.z), self.w.max(other.w)) + } +} + +impl TypedPoint4D { + /// Rounds each component to the nearest integer value. + /// + /// This behavior is preserved for negative values (unlike the basic cast). + pub fn round(&self) -> Self { + TypedPoint4D::new(self.x.round(), self.y.round(), self.z.round(), self.w.round()) + } +} + +impl TypedPoint4D { + /// Rounds each component to the smallest integer equal or greater than the original value. + /// + /// This behavior is preserved for negative values (unlike the basic cast). + pub fn ceil(&self) -> Self { + TypedPoint4D::new(self.x.ceil(), self.y.ceil(), self.z.ceil(), self.w.ceil()) + } +} + +impl TypedPoint4D { + /// Rounds each component to the biggest integer equal or lower than the original value. + /// + /// This behavior is preserved for negative values (unlike the basic cast). + pub fn floor(&self) -> Self { + TypedPoint4D::new(self.x.floor(), self.y.floor(), self.z.floor(), self.w.floor()) + } +} + +impl TypedPoint4D { + /// Cast from one numeric representation to another, preserving the units. + /// + /// When casting from floating point to integer coordinates, the decimals are truncated + /// as one would expect from a simple cast, but this behavior does not always make sense + /// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting. + pub fn cast(&self) -> Option> { + match (NumCast::from(self.x), + NumCast::from(self.y), + NumCast::from(self.z), + NumCast::from(self.w)) { + (Some(x), Some(y), Some(z), Some(w)) => Some(TypedPoint4D::new(x, y, z, w)), + _ => None + } + } + + // Convenience functions for common casts + + /// Cast into an `f32` point. + pub fn to_f32(&self) -> TypedPoint4D { + self.cast().unwrap() + } + + /// Cast into an `usize` point, truncating decimals if any. + /// + /// When casting from floating point points, it is worth considering whether + /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain + /// the desired conversion behavior. + pub fn to_uint(&self) -> TypedPoint4D { + self.cast().unwrap() + } + + /// Cast into an `i32` point, truncating decimals if any. + /// + /// When casting from floating point points, it is worth considering whether + /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain + /// the desired conversion behavior. + pub fn to_i32(&self) -> TypedPoint4D { + self.cast().unwrap() + } + + /// Cast into an `i64` point, truncating decimals if any. + /// + /// When casting from floating point points, it is worth considering whether + /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain + /// the desired conversion behavior. + pub fn to_i64(&self) -> TypedPoint4D { + self.cast().unwrap() + } +} + +impl, U> ApproxEq for TypedPoint4D { + fn approx_epsilon() -> T { + T::approx_epsilon() + } + + fn approx_eq_eps(&self, other: &Self, approx_epsilon: &T) -> bool { + self.x.approx_eq_eps(&other.x, approx_epsilon) + && self.y.approx_eq_eps(&other.y, approx_epsilon) + && self.z.approx_eq_eps(&other.z, approx_epsilon) + && self.w.approx_eq_eps(&other.w, approx_epsilon) + } + + fn approx_eq(&self, other: &Self) -> bool { + self.approx_eq_eps(&other, &Self::approx_epsilon()) + } +} + +pub fn point2(x: T, y: T) -> TypedPoint2D { + TypedPoint2D::new(x, y) +} + +pub fn point3(x: T, y: T, z: T) -> TypedPoint3D { + TypedPoint3D::new(x, y, z) +} + +pub fn point4(x: T, y: T, z: T, w: T) -> TypedPoint4D { + TypedPoint4D::new(x, y, z, w) +} + +#[cfg(test)] +mod point2d { + use super::Point2D; + + #[test] + pub fn test_scalar_mul() { + let p1: Point2D = Point2D::new(3.0, 5.0); + + let result = p1 * 5.0; + + assert_eq!(result, Point2D::new(15.0, 25.0)); + } + + #[test] + pub fn test_dot() { + let p1: Point2D = Point2D::new(2.0, 7.0); + let p2: Point2D = Point2D::new(13.0, 11.0); + assert_eq!(p1.dot(p2), 103.0); + } + + #[test] + pub fn test_cross() { + let p1: Point2D = Point2D::new(4.0, 7.0); + let p2: Point2D = Point2D::new(13.0, 8.0); + let r = p1.cross(p2); + assert_eq!(r, -59.0); + } + + #[test] + pub fn test_normalize() { + let p0: Point2D = Point2D::zero(); + let p1: Point2D = Point2D::new(4.0, 0.0); + let p2: Point2D = Point2D::new(3.0, -4.0); + assert_eq!(p0.normalize(), p0); + assert_eq!(p1.normalize(), Point2D::new(1.0, 0.0)); + assert_eq!(p2.normalize(), Point2D::new(0.6, -0.8)); + } + + #[test] + pub fn test_min() { + let p1 = Point2D::new(1.0, 3.0); + let p2 = Point2D::new(2.0, 2.0); + + let result = p1.min(p2); + + assert_eq!(result, Point2D::new(1.0, 2.0)); + } + + #[test] + pub fn test_max() { + let p1 = Point2D::new(1.0, 3.0); + let p2 = Point2D::new(2.0, 2.0); + + let result = p1.max(p2); + + assert_eq!(result, Point2D::new(2.0, 3.0)); + } +} + +#[cfg(test)] +mod typedpoint2d { + use super::TypedPoint2D; + use scale_factor::ScaleFactor; + + pub enum Mm {} + pub enum Cm {} + + pub type Point2DMm = TypedPoint2D; + pub type Point2DCm = TypedPoint2D; + + #[test] + pub fn test_add() { + let p1 = Point2DMm::new(1.0, 2.0); + let p2 = Point2DMm::new(3.0, 4.0); + + let result = p1 + p2; + + assert_eq!(result, Point2DMm::new(4.0, 6.0)); + } + + #[test] + pub fn test_scalar_mul() { + let p1 = Point2DMm::new(1.0, 2.0); + let cm_per_mm: ScaleFactor = ScaleFactor::new(0.1); + + let result = p1 * cm_per_mm; + + assert_eq!(result, Point2DCm::new(0.1, 0.2)); + } +} + +#[cfg(test)] +mod point3d { + use super::Point3D; + + #[test] + pub fn test_dot() { + let p1 = Point3D::new(7.0, 21.0, 32.0); + let p2 = Point3D::new(43.0, 5.0, 16.0); + assert_eq!(p1.dot(p2), 918.0); + } + + #[test] + pub fn test_cross() { + let p1 = Point3D::new(4.0, 7.0, 9.0); + let p2 = Point3D::new(13.0, 8.0, 3.0); + let p3 = p1.cross(p2); + assert_eq!(p3, Point3D::new(-51.0, 105.0, -59.0)); + } + + #[test] + pub fn test_normalize() { + let p0: Point3D = Point3D::zero(); + let p1: Point3D = Point3D::new(0.0, -6.0, 0.0); + let p2: Point3D = Point3D::new(1.0, 2.0, -2.0); + assert_eq!(p0.normalize(), p0); + assert_eq!(p1.normalize(), Point3D::new(0.0, -1.0, 0.0)); + assert_eq!(p2.normalize(), Point3D::new(1.0/3.0, 2.0/3.0, -2.0/3.0)); + } + + #[test] + pub fn test_min() { + let p1 = Point3D::new(1.0, 3.0, 5.0); + let p2 = Point3D::new(2.0, 2.0, -1.0); + + let result = p1.min(p2); + + assert_eq!(result, Point3D::new(1.0, 2.0, -1.0)); + } + + #[test] + pub fn test_max() { + let p1 = Point3D::new(1.0, 3.0, 5.0); + let p2 = Point3D::new(2.0, 2.0, -1.0); + + let result = p1.max(p2); + + assert_eq!(result, Point3D::new(2.0, 3.0, 5.0)); + } +} + +#[cfg(test)] +mod point4d { + use super::Point4D; + + #[test] + pub fn test_add() { + let p1 = Point4D::new(7.0, 21.0, 32.0, 1.0); + let p2 = Point4D::new(43.0, 5.0, 16.0, 2.0); + + let result = p1 + p2; + + assert_eq!(result, Point4D::new(50.0, 26.0, 48.0, 3.0)); + } + + #[test] + pub fn test_sub() { + let p1 = Point4D::new(7.0, 21.0, 32.0, 1.0); + let p2 = Point4D::new(43.0, 5.0, 16.0, 2.0); + + let result = p1 - p2; + + assert_eq!(result, Point4D::new(-36.0, 16.0, 16.0, -1.0)); + } + + #[test] + pub fn test_min() { + let p1 = Point4D::new(1.0, 3.0, 5.0, 7.0); + let p2 = Point4D::new(2.0, 2.0, -1.0, 10.0); + + let result = p1.min(p2); + + assert_eq!(result, Point4D::new(1.0, 2.0, -1.0, 7.0)); + } + + #[test] + pub fn test_max() { + let p1 = Point4D::new(1.0, 3.0, 5.0, 7.0); + let p2 = Point4D::new(2.0, 2.0, -1.0, 10.0); + + let result = p1.max(p2); + + assert_eq!(result, Point4D::new(2.0, 3.0, 5.0, 10.0)); + } +} diff --git a/third_party/rust/euclid-0.11.3/src/rect.rs b/third_party/rust/euclid-0.11.3/src/rect.rs new file mode 100644 index 000000000000..835c1b50c911 --- /dev/null +++ b/third_party/rust/euclid-0.11.3/src/rect.rs @@ -0,0 +1,671 @@ +// Copyright 2013 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::UnknownUnit; +use length::Length; +use scale_factor::ScaleFactor; +use num::*; +use point::TypedPoint2D; +use size::TypedSize2D; + +use heapsize::HeapSizeOf; +use num_traits::NumCast; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use std::cmp::PartialOrd; +use std::fmt; +use std::ops::{Add, Sub, Mul, Div}; + +/// A 2d Rectangle optionally tagged with a unit. +#[derive(RustcDecodable, RustcEncodable)] +pub struct TypedRect { + pub origin: TypedPoint2D, + pub size: TypedSize2D, +} + +/// The default rectangle type with no unit. +pub type Rect = TypedRect; + +impl HeapSizeOf for TypedRect { + fn heap_size_of_children(&self) -> usize { + self.origin.heap_size_of_children() + self.size.heap_size_of_children() + } +} + +impl Deserialize for TypedRect { + fn deserialize(deserializer: D) -> Result + where D: Deserializer + { + let (origin, size) = try!(Deserialize::deserialize(deserializer)); + Ok(TypedRect::new(origin, size)) + } +} + +impl Serialize for TypedRect { + fn serialize(&self, serializer: S) -> Result + where S: Serializer + { + (&self.origin, &self.size).serialize(serializer) + } +} + +impl Copy for TypedRect {} + +impl Clone for TypedRect { + fn clone(&self) -> TypedRect { *self } +} + +impl PartialEq> for TypedRect { + fn eq(&self, other: &TypedRect) -> bool { + self.origin.eq(&other.origin) && self.size.eq(&other.size) + } +} + +impl Eq for TypedRect {} + +impl fmt::Debug for TypedRect { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "TypedRect({:?} at {:?})", self.size, self.origin) + } +} + +impl fmt::Display for TypedRect { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "Rect({} at {})", self.size, self.origin) + } +} + +impl TypedRect { + /// Constructor. + pub fn new(origin: TypedPoint2D, size: TypedSize2D) -> TypedRect { + TypedRect { + origin: origin, + size: size, + } + } +} + +impl TypedRect +where T: Copy + Clone + Zero + PartialOrd + PartialEq + Add + Sub { + #[inline] + pub fn intersects(&self, other: &TypedRect) -> bool { + self.origin.x < other.origin.x + other.size.width && + other.origin.x < self.origin.x + self.size.width && + self.origin.y < other.origin.y + other.size.height && + other.origin.y < self.origin.y + self.size.height + } + + #[inline] + pub fn max_x(&self) -> T { + self.origin.x + self.size.width + } + + #[inline] + pub fn min_x(&self) -> T { + self.origin.x + } + + #[inline] + pub fn max_y(&self) -> T { + self.origin.y + self.size.height + } + + #[inline] + pub fn min_y(&self) -> T { + self.origin.y + } + + #[inline] + pub fn max_x_typed(&self) -> Length { + Length::new(self.max_x()) + } + + #[inline] + pub fn min_x_typed(&self) -> Length { + Length::new(self.min_x()) + } + + #[inline] + pub fn max_y_typed(&self) -> Length { + Length::new(self.max_y()) + } + + #[inline] + pub fn min_y_typed(&self) -> Length { + Length::new(self.min_y()) + } + + #[inline] + pub fn intersection(&self, other: &TypedRect) -> Option> { + if !self.intersects(other) { + return None; + } + + let upper_left = TypedPoint2D::new(max(self.min_x(), other.min_x()), + max(self.min_y(), other.min_y())); + let lower_right_x = min(self.max_x(), other.max_x()); + let lower_right_y = min(self.max_y(), other.max_y()); + + Some(TypedRect::new(upper_left, TypedSize2D::new(lower_right_x - upper_left.x, + lower_right_y - upper_left.y))) + } + + /// Translates the rect by a vector. + #[inline] + pub fn translate(&self, other: &TypedPoint2D) -> TypedRect { + TypedRect::new( + TypedPoint2D::new(self.origin.x + other.x, self.origin.y + other.y), + self.size + ) + } + + /// Returns true if this rectangle contains the point. Points are considered + /// in the rectangle if they are on the left or top edge, but outside if they + /// are on the right or bottom edge. + #[inline] + pub fn contains(&self, other: &TypedPoint2D) -> bool { + self.origin.x <= other.x && other.x < self.origin.x + self.size.width && + self.origin.y <= other.y && other.y < self.origin.y + self.size.height + } + + /// Returns true if this rectangle contains the interior of rect. Always + /// returns true if rect is empty, and always returns false if rect is + /// nonempty but this rectangle is empty. + #[inline] + pub fn contains_rect(&self, rect: &TypedRect) -> bool { + rect.is_empty() || + (self.min_x() <= rect.min_x() && rect.max_x() <= self.max_x() && + self.min_y() <= rect.min_y() && rect.max_y() <= self.max_y()) + } + + #[inline] + pub fn inflate(&self, width: T, height: T) -> TypedRect { + TypedRect::new( + TypedPoint2D::new(self.origin.x - width, self.origin.y - height), + TypedSize2D::new(self.size.width + width + width, self.size.height + height + height), + ) + } + + #[inline] + pub fn inflate_typed(&self, width: Length, height: Length) -> TypedRect { + self.inflate(width.get(), height.get()) + } + + #[inline] + pub fn top_right(&self) -> TypedPoint2D { + TypedPoint2D::new(self.max_x(), self.origin.y) + } + + #[inline] + pub fn bottom_left(&self) -> TypedPoint2D { + TypedPoint2D::new(self.origin.x, self.max_y()) + } + + #[inline] + pub fn bottom_right(&self) -> TypedPoint2D { + TypedPoint2D::new(self.max_x(), self.max_y()) + } + + #[inline] + pub fn translate_by_size(&self, size: &TypedSize2D) -> TypedRect { + self.translate(&TypedPoint2D::new(size.width, size.height)) + } + + /// Returns the smallest rectangle containing the four points. + pub fn from_points(points: &[TypedPoint2D]) -> Self { + if points.len() == 0 { + return TypedRect::zero(); + } + let (mut min_x, mut min_y) = (points[0].x, points[0].y); + let (mut max_x, mut max_y) = (min_x, min_y); + for point in &points[1..] { + if point.x < min_x { + min_x = point.x + } + if point.x > max_x { + max_x = point.x + } + if point.y < min_y { + min_y = point.y + } + if point.y > max_y { + max_y = point.y + } + } + TypedRect::new(TypedPoint2D::new(min_x, min_y), + TypedSize2D::new(max_x - min_x, max_y - min_y)) + } +} + +impl TypedRect +where T: Copy + Clone + PartialOrd + Add + Sub + Zero { + #[inline] + pub fn union(&self, other: &TypedRect) -> TypedRect { + if self.size == Zero::zero() { + return *other; + } + if other.size == Zero::zero() { + return *self; + } + + let upper_left = TypedPoint2D::new(min(self.min_x(), other.min_x()), + min(self.min_y(), other.min_y())); + + let lower_right_x = max(self.max_x(), other.max_x()); + let lower_right_y = max(self.max_y(), other.max_y()); + + TypedRect::new( + upper_left, + TypedSize2D::new(lower_right_x - upper_left.x, lower_right_y - upper_left.y) + ) + } +} + +impl TypedRect { + #[inline] + pub fn scale(&self, x: Scale, y: Scale) -> TypedRect + where T: Copy + Clone + Mul { + TypedRect::new( + TypedPoint2D::new(self.origin.x * x, self.origin.y * y), + TypedSize2D::new(self.size.width * x, self.size.height * y) + ) + } +} + +impl TypedRect { + /// Constructor, setting all sides to zero. + pub fn zero() -> TypedRect { + TypedRect::new( + TypedPoint2D::zero(), + TypedSize2D::zero(), + ) + } + + /// Returns true if the size is zero, regardless of the origin's value. + pub fn is_empty(&self) -> bool { + self.size.width == Zero::zero() || self.size.height == Zero::zero() + } +} + + +pub fn min(x: T, y: T) -> T { + if x <= y { x } else { y } +} + +pub fn max(x: T, y: T) -> T { + if x >= y { x } else { y } +} + +impl, U> Mul for TypedRect { + type Output = TypedRect; + #[inline] + fn mul(self, scale: T) -> TypedRect { + TypedRect::new(self.origin * scale, self.size * scale) + } +} + +impl, U> Div for TypedRect { + type Output = TypedRect; + #[inline] + fn div(self, scale: T) -> TypedRect { + TypedRect::new(self.origin / scale, self.size / scale) + } +} + +impl, U1, U2> Mul> for TypedRect { + type Output = TypedRect; + #[inline] + fn mul(self, scale: ScaleFactor) -> TypedRect { + TypedRect::new(self.origin * scale, self.size * scale) + } +} + +impl, U1, U2> Div> for TypedRect { + type Output = TypedRect; + #[inline] + fn div(self, scale: ScaleFactor) -> TypedRect { + TypedRect::new(self.origin / scale, self.size / scale) + } +} + +impl TypedRect { + /// Drop the units, preserving only the numeric value. + pub fn to_untyped(&self) -> Rect { + TypedRect::new(self.origin.to_untyped(), self.size.to_untyped()) + } + + /// Tag a unitless value with units. + pub fn from_untyped(r: &Rect) -> TypedRect { + TypedRect::new(TypedPoint2D::from_untyped(&r.origin), TypedSize2D::from_untyped(&r.size)) + } +} + +impl TypedRect { + /// Cast from one numeric representation to another, preserving the units. + /// + /// When casting from floating point to integer coordinates, the decimals are truncated + /// as one would expect from a simple cast, but this behavior does not always make sense + /// geometrically. Consider using round(), round_in or round_out() before casting. + pub fn cast(&self) -> Option> { + match (self.origin.cast(), self.size.cast()) { + (Some(origin), Some(size)) => Some(TypedRect::new(origin, size)), + _ => None + } + } +} + +impl + Sub, U> TypedRect { + /// Return a rectangle with edges rounded to integer coordinates, such that + /// the returned rectangle has the same set of pixel centers as the original + /// one. + /// Edges at offset 0.5 round up. + /// Suitable for most places where integral device coordinates + /// are needed, but note that any translation should be applied first to + /// avoid pixel rounding errors. + /// Note that this is *not* rounding to nearest integer if the values are negative. + /// They are always rounding as floor(n + 0.5). + pub fn round(&self) -> Self { + let origin = self.origin.round(); + let size = self.origin.add_size(&self.size).round() - origin; + TypedRect::new(origin, TypedSize2D::new(size.x, size.y)) + } + + /// Return a rectangle with edges rounded to integer coordinates, such that + /// the original rectangle contains the resulting rectangle. + pub fn round_in(&self) -> Self { + let origin = self.origin.ceil(); + let size = self.origin.add_size(&self.size).floor() - origin; + TypedRect::new(origin, TypedSize2D::new(size.x, size.y)) + } + + /// Return a rectangle with edges rounded to integer coordinates, such that + /// the original rectangle is contained in the resulting rectangle. + pub fn round_out(&self) -> Self { + let origin = self.origin.floor(); + let size = self.origin.add_size(&self.size).ceil() - origin; + TypedRect::new(origin, TypedSize2D::new(size.x, size.y)) + } +} + +// Convenience functions for common casts +impl TypedRect { + /// Cast into an `f32` rectangle. + pub fn to_f32(&self) -> TypedRect { + self.cast().unwrap() + } + + /// Cast into an `usize` rectangle, truncating decimals if any. + /// + /// When casting from floating point rectangles, it is worth considering whether + /// to `round()`, `round_in()` or `round_out()` before the cast in order to + /// obtain the desired conversion behavior. + pub fn to_uint(&self) -> TypedRect { + self.cast().unwrap() + } + + /// Cast into an `i32` rectangle, truncating decimals if any. + /// + /// When casting from floating point rectangles, it is worth considering whether + /// to `round()`, `round_in()` or `round_out()` before the cast in order to + /// obtain the desired conversion behavior. + pub fn to_i32(&self) -> TypedRect { + self.cast().unwrap() + } + + /// Cast into an `i64` rectangle, truncating decimals if any. + /// + /// When casting from floating point rectangles, it is worth considering whether + /// to `round()`, `round_in()` or `round_out()` before the cast in order to + /// obtain the desired conversion behavior. + pub fn to_i64(&self) -> TypedRect { + self.cast().unwrap() + } +} + +/// Shorthand for `TypedRect::new(TypedPoint2D::new(x, y), TypedSize2D::new(w, h))`. +pub fn rect(x: T, y: T, w: T, h: T) -> TypedRect { + TypedRect::new(TypedPoint2D::new(x, y), TypedSize2D::new(w, h)) +} + +#[cfg(test)] +mod tests { + use point::Point2D; + use size::Size2D; + use super::*; + + #[test] + fn test_min_max() { + assert!(min(0u32, 1u32) == 0u32); + assert!(min(-1.0f32, 0.0f32) == -1.0f32); + + assert!(max(0u32, 1u32) == 1u32); + assert!(max(-1.0f32, 0.0f32) == 0.0f32); + } + + #[test] + fn test_translate() { + let p = Rect::new(Point2D::new(0u32, 0u32), Size2D::new(50u32, 40u32)); + let pp = p.translate(&Point2D::new(10,15)); + + assert!(pp.size.width == 50); + assert!(pp.size.height == 40); + assert!(pp.origin.x == 10); + assert!(pp.origin.y == 15); + + + let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40)); + let rr = r.translate(&Point2D::new(0,-10)); + + assert!(rr.size.width == 50); + assert!(rr.size.height == 40); + assert!(rr.origin.x == -10); + assert!(rr.origin.y == -15); + } + + #[test] + fn test_translate_by_size() { + let p = Rect::new(Point2D::new(0u32, 0u32), Size2D::new(50u32, 40u32)); + let pp = p.translate_by_size(&Size2D::new(10,15)); + + assert!(pp.size.width == 50); + assert!(pp.size.height == 40); + assert!(pp.origin.x == 10); + assert!(pp.origin.y == 15); + + + let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40)); + let rr = r.translate_by_size(&Size2D::new(0,-10)); + + assert!(rr.size.width == 50); + assert!(rr.size.height == 40); + assert!(rr.origin.x == -10); + assert!(rr.origin.y == -15); + } + + #[test] + fn test_union() { + let p = Rect::new(Point2D::new(0, 0), Size2D::new(50, 40)); + let q = Rect::new(Point2D::new(20,20), Size2D::new(5, 5)); + let r = Rect::new(Point2D::new(-15, -30), Size2D::new(200, 15)); + let s = Rect::new(Point2D::new(20, -15), Size2D::new(250, 200)); + + let pq = p.union(&q); + assert!(pq.origin == Point2D::new(0, 0)); + assert!(pq.size == Size2D::new(50, 40)); + + let pr = p.union(&r); + assert!(pr.origin == Point2D::new(-15, -30)); + assert!(pr.size == Size2D::new(200, 70)); + + let ps = p.union(&s); + assert!(ps.origin == Point2D::new(0, -15)); + assert!(ps.size == Size2D::new(270, 200)); + + } + + #[test] + fn test_intersection() { + let p = Rect::new(Point2D::new(0, 0), Size2D::new(10, 20)); + let q = Rect::new(Point2D::new(5, 15), Size2D::new(10, 10)); + let r = Rect::new(Point2D::new(-5, -5), Size2D::new(8, 8)); + + let pq = p.intersection(&q); + assert!(pq.is_some()); + let pq = pq.unwrap(); + assert!(pq.origin == Point2D::new(5, 15)); + assert!(pq.size == Size2D::new(5, 5)); + + let pr = p.intersection(&r); + assert!(pr.is_some()); + let pr = pr.unwrap(); + assert!(pr.origin == Point2D::new(0, 0)); + assert!(pr.size == Size2D::new(3, 3)); + + let qr = q.intersection(&r); + assert!(qr.is_none()); + } + + #[test] + fn test_contains() { + let r = Rect::new(Point2D::new(-20, 15), Size2D::new(100, 200)); + + assert!(r.contains(&Point2D::new(0, 50))); + assert!(r.contains(&Point2D::new(-10, 200))); + + // The `contains` method is inclusive of the top/left edges, but not the + // bottom/right edges. + assert!(r.contains(&Point2D::new(-20, 15))); + assert!(!r.contains(&Point2D::new(80, 15))); + assert!(!r.contains(&Point2D::new(80, 215))); + assert!(!r.contains(&Point2D::new(-20, 215))); + + // Points beyond the top-left corner. + assert!(!r.contains(&Point2D::new(-25, 15))); + assert!(!r.contains(&Point2D::new(-15, 10))); + + // Points beyond the top-right corner. + assert!(!r.contains(&Point2D::new(85, 20))); + assert!(!r.contains(&Point2D::new(75, 10))); + + // Points beyond the bottom-right corner. + assert!(!r.contains(&Point2D::new(85, 210))); + assert!(!r.contains(&Point2D::new(75, 220))); + + // Points beyond the bottom-left corner. + assert!(!r.contains(&Point2D::new(-25, 210))); + assert!(!r.contains(&Point2D::new(-15, 220))); + + let r = Rect::new(Point2D::new(-20.0, 15.0), Size2D::new(100.0, 200.0)); + assert!(r.contains_rect(&r)); + assert!(!r.contains_rect(&r.translate(&Point2D::new( 0.1, 0.0)))); + assert!(!r.contains_rect(&r.translate(&Point2D::new(-0.1, 0.0)))); + assert!(!r.contains_rect(&r.translate(&Point2D::new( 0.0, 0.1)))); + assert!(!r.contains_rect(&r.translate(&Point2D::new( 0.0, -0.1)))); + // Empty rectangles are always considered as contained in other rectangles, + // even if their origin is not. + let p = Point2D::new(1.0, 1.0); + assert!(!r.contains(&p)); + assert!(r.contains_rect(&Rect::new(p, Size2D::zero()))); + } + + #[test] + fn test_scale() { + let p = Rect::new(Point2D::new(0u32, 0u32), Size2D::new(50u32, 40u32)); + let pp = p.scale(10, 15); + + assert!(pp.size.width == 500); + assert!(pp.size.height == 600); + assert!(pp.origin.x == 0); + assert!(pp.origin.y == 0); + + let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40)); + let rr = r.scale(1, 20); + + assert!(rr.size.width == 50); + assert!(rr.size.height == 800); + assert!(rr.origin.x == -10); + assert!(rr.origin.y == -100); + } + + #[test] + fn test_inflate() { + let p = Rect::new(Point2D::new(0, 0), Size2D::new(10, 10)); + let pp = p.inflate(10, 20); + + assert!(pp.size.width == 30); + assert!(pp.size.height == 50); + assert!(pp.origin.x == -10); + assert!(pp.origin.y == -20); + + let r = Rect::new(Point2D::new(0, 0), Size2D::new(10, 20)); + let rr = r.inflate(-2, -5); + + assert!(rr.size.width == 6); + assert!(rr.size.height == 10); + assert!(rr.origin.x == 2); + assert!(rr.origin.y == 5); + } + + #[test] + fn test_min_max_x_y() { + let p = Rect::new(Point2D::new(0u32, 0u32), Size2D::new(50u32, 40u32)); + assert!(p.max_y() == 40); + assert!(p.min_y() == 0); + assert!(p.max_x() == 50); + assert!(p.min_x() == 0); + + let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40)); + assert!(r.max_y() == 35); + assert!(r.min_y() == -5); + assert!(r.max_x() == 40); + assert!(r.min_x() == -10); + } + + #[test] + fn test_is_empty() { + assert!(Rect::new(Point2D::new(0u32, 0u32), Size2D::new(0u32, 0u32)).is_empty()); + assert!(Rect::new(Point2D::new(0u32, 0u32), Size2D::new(10u32, 0u32)).is_empty()); + assert!(Rect::new(Point2D::new(0u32, 0u32), Size2D::new(0u32, 10u32)).is_empty()); + assert!(!Rect::new(Point2D::new(0u32, 0u32), Size2D::new(1u32, 1u32)).is_empty()); + assert!(Rect::new(Point2D::new(10u32, 10u32), Size2D::new(0u32, 0u32)).is_empty()); + assert!(Rect::new(Point2D::new(10u32, 10u32), Size2D::new(10u32, 0u32)).is_empty()); + assert!(Rect::new(Point2D::new(10u32, 10u32), Size2D::new(0u32, 10u32)).is_empty()); + assert!(!Rect::new(Point2D::new(10u32, 10u32), Size2D::new(1u32, 1u32)).is_empty()); + } + + #[test] + fn test_round() { + let mut x = -2.0; + let mut y = -2.0; + let mut w = -2.0; + let mut h = -2.0; + while x < 2.0 { + while y < 2.0 { + while w < 2.0 { + while h < 2.0 { + let rect = Rect::new(Point2D::new(x, y), Size2D::new(w, h)); + + assert!(rect.contains_rect(&rect.round_in())); + assert!(rect.round_in().inflate(1.0, 1.0).contains_rect(&rect)); + + assert!(rect.round_out().contains_rect(&rect)); + assert!(rect.inflate(1.0, 1.0).contains_rect(&rect.round_out())); + + assert!(rect.inflate(1.0, 1.0).contains_rect(&rect.round())); + assert!(rect.round().inflate(1.0, 1.0).contains_rect(&rect)); + + h += 0.1; + } + w += 0.1; + } + y += 0.1; + } + x += 0.1 + } + } +} diff --git a/third_party/rust/euclid-0.11.3/src/scale_factor.rs b/third_party/rust/euclid-0.11.3/src/scale_factor.rs new file mode 100644 index 000000000000..4ecfc4bc8dc5 --- /dev/null +++ b/third_party/rust/euclid-0.11.3/src/scale_factor.rs @@ -0,0 +1,172 @@ +// Copyright 2014 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +//! A type-checked scaling factor between units. + +use num::One; + +use heapsize::HeapSizeOf; +use num_traits::NumCast; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use std::fmt; +use std::ops::{Add, Mul, Sub, Div}; +use std::marker::PhantomData; + +/// A scaling factor between two different units of measurement. +/// +/// This is effectively a type-safe float, intended to be used in combination with other types like +/// `length::Length` to enforce conversion between systems of measurement at compile time. +/// +/// `Src` and `Dst` represent the units before and after multiplying a value by a `ScaleFactor`. They +/// may be types without values, such as empty enums. For example: +/// +/// ```rust +/// use euclid::scale_factor::ScaleFactor; +/// use euclid::length::Length; +/// enum Mm {}; +/// enum Inch {}; +/// +/// let mm_per_inch: ScaleFactor = ScaleFactor::new(25.4); +/// +/// let one_foot: Length = Length::new(12.0); +/// let one_foot_in_mm: Length = one_foot * mm_per_inch; +/// ``` +#[repr(C)] +#[derive(RustcDecodable, RustcEncodable)] +pub struct ScaleFactor(pub T, PhantomData<(Src, Dst)>); + +impl HeapSizeOf for ScaleFactor { + fn heap_size_of_children(&self) -> usize { + self.0.heap_size_of_children() + } +} + +impl Deserialize for ScaleFactor where T: Deserialize { + fn deserialize(deserializer: D) -> Result, D::Error> + where D: Deserializer { + Ok(ScaleFactor(try!(Deserialize::deserialize(deserializer)), PhantomData)) + } +} + +impl Serialize for ScaleFactor where T: Serialize { + fn serialize(&self, serializer: S) -> Result where S: Serializer { + self.0.serialize(serializer) + } +} + +impl ScaleFactor { + pub fn new(x: T) -> ScaleFactor { + ScaleFactor(x, PhantomData) + } +} + +impl ScaleFactor { + pub fn get(&self) -> T { + self.0.clone() + } +} + +impl, Src, Dst> ScaleFactor { + /// The inverse ScaleFactor (1.0 / self). + pub fn inv(&self) -> ScaleFactor { + let one: T = One::one(); + ScaleFactor::new(one / self.get()) + } +} + +// scale0 * scale1 +impl, A, B, C> +Mul> for ScaleFactor { + type Output = ScaleFactor; + #[inline] + fn mul(self, other: ScaleFactor) -> ScaleFactor { + ScaleFactor::new(self.get() * other.get()) + } +} + +// scale0 + scale1 +impl, Src, Dst> Add for ScaleFactor { + type Output = ScaleFactor; + #[inline] + fn add(self, other: ScaleFactor) -> ScaleFactor { + ScaleFactor::new(self.get() + other.get()) + } +} + +// scale0 - scale1 +impl, Src, Dst> Sub for ScaleFactor { + type Output = ScaleFactor; + #[inline] + fn sub(self, other: ScaleFactor) -> ScaleFactor { + ScaleFactor::new(self.get() - other.get()) + } +} + +impl ScaleFactor { + /// Cast from one numeric representation to another, preserving the units. + pub fn cast(&self) -> Option> { + NumCast::from(self.get()).map(ScaleFactor::new) + } +} + +// FIXME: Switch to `derive(PartialEq, Clone)` after this Rust issue is fixed: +// https://github.com/mozilla/rust/issues/7671 + +impl PartialEq for ScaleFactor { + fn eq(&self, other: &ScaleFactor) -> bool { + self.0 == other.0 + } +} + +impl Clone for ScaleFactor { + fn clone(&self) -> ScaleFactor { + ScaleFactor::new(self.get()) + } +} + +impl Copy for ScaleFactor {} + +impl fmt::Debug for ScaleFactor { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +impl fmt::Display for ScaleFactor { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +#[cfg(test)] +mod tests { + use super::ScaleFactor; + + enum Inch {} + enum Cm {} + enum Mm {} + + #[test] + fn test_scale_factor() { + let mm_per_inch: ScaleFactor = ScaleFactor::new(25.4); + let cm_per_mm: ScaleFactor = ScaleFactor::new(0.1); + + let mm_per_cm: ScaleFactor = cm_per_mm.inv(); + assert_eq!(mm_per_cm.get(), 10.0); + + let cm_per_inch: ScaleFactor = mm_per_inch * cm_per_mm; + assert_eq!(cm_per_inch, ScaleFactor::new(2.54)); + + let a: ScaleFactor = ScaleFactor::new(2); + let b: ScaleFactor = ScaleFactor::new(3); + assert!(a != b); + assert_eq!(a, a.clone()); + assert_eq!(a.clone() + b.clone(), ScaleFactor::new(5)); + assert_eq!(a - b, ScaleFactor::new(-1)); + } +} diff --git a/third_party/rust/euclid-0.11.3/src/side_offsets.rs b/third_party/rust/euclid-0.11.3/src/side_offsets.rs new file mode 100644 index 000000000000..7a1a7487e2e7 --- /dev/null +++ b/third_party/rust/euclid-0.11.3/src/side_offsets.rs @@ -0,0 +1,283 @@ +// Copyright 2013 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A group of side offsets, which correspond to top/left/bottom/right for borders, padding, +//! and margins in CSS. + +use super::UnknownUnit; +use length::Length; +use num::Zero; +use std::fmt; +use std::ops::Add; +use std::marker::PhantomData; + +#[cfg(feature = "unstable")] +use heapsize::HeapSizeOf; + +/// A group of side offsets, which correspond to top/left/bottom/right for borders, padding, +/// and margins in CSS, optionally tagged with a unit. +define_matrix! { + pub struct TypedSideOffsets2D { + pub top: T, + pub right: T, + pub bottom: T, + pub left: T, + } +} + +impl fmt::Debug for TypedSideOffsets2D { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "({:?},{:?},{:?},{:?})", + self.top, self.right, self.bottom, self.left) + } +} + +/// The default side offset type with no unit. +pub type SideOffsets2D = TypedSideOffsets2D; + +impl TypedSideOffsets2D { + /// Constructor taking a scalar for each side. + pub fn new(top: T, right: T, bottom: T, left: T) -> TypedSideOffsets2D { + TypedSideOffsets2D { + top: top, + right: right, + bottom: bottom, + left: left, + _unit: PhantomData, + } + } + + /// Constructor taking a typed Length for each side. + pub fn from_lengths(top: Length, + right: Length, + bottom: Length, + left: Length) -> TypedSideOffsets2D { + TypedSideOffsets2D::new(top.0, right.0, bottom.0, left.0) + } + + /// Access self.top as a typed Length instead of a scalar value. + pub fn top_typed(&self) -> Length { Length::new(self.top) } + + /// Access self.right as a typed Length instead of a scalar value. + pub fn right_typed(&self) -> Length { Length::new(self.right) } + + /// Access self.bottom as a typed Length instead of a scalar value. + pub fn bottom_typed(&self) -> Length { Length::new(self.bottom) } + + /// Access self.left as a typed Length instead of a scalar value. + pub fn left_typed(&self) -> Length { Length::new(self.left) } + + /// Constructor setting the same value to all sides, taking a scalar value directly. + pub fn new_all_same(all: T) -> TypedSideOffsets2D { + TypedSideOffsets2D::new(all, all, all, all) + } + + /// Constructor setting the same value to all sides, taking a typed Length. + pub fn from_length_all_same(all: Length) -> TypedSideOffsets2D { + TypedSideOffsets2D::new_all_same(all.0) + } +} + +impl TypedSideOffsets2D where T: Add + Copy { + pub fn horizontal(&self) -> T { + self.left + self.right + } + + pub fn vertical(&self) -> T { + self.top + self.bottom + } + + pub fn horizontal_typed(&self) -> Length { + Length::new(self.horizontal()) + } + + pub fn vertical_typed(&self) -> Length { + Length::new(self.vertical()) + } +} + +impl Add for TypedSideOffsets2D where T : Copy + Add { + type Output = TypedSideOffsets2D; + fn add(self, other: TypedSideOffsets2D) -> TypedSideOffsets2D { + TypedSideOffsets2D::new( + self.top + other.top, + self.right + other.right, + self.bottom + other.bottom, + self.left + other.left, + ) + } +} + +impl TypedSideOffsets2D { + /// Constructor, setting all sides to zero. + pub fn zero() -> TypedSideOffsets2D { + TypedSideOffsets2D::new( + Zero::zero(), + Zero::zero(), + Zero::zero(), + Zero::zero(), + ) + } +} + +/// A SIMD enabled version of TypedSideOffsets2D specialized for i32. +#[cfg(feature = "unstable")] +#[derive(Clone, Copy, PartialEq)] +#[repr(simd)] +pub struct SideOffsets2DSimdI32 { + pub top: i32, + pub bottom: i32, + pub right: i32, + pub left: i32, +} + +#[cfg(feature = "unstable")] +impl HeapSizeOf for SideOffsets2DSimdI32 { + fn heap_size_of_children(&self) -> usize { 0 } +} + +#[cfg(feature = "unstable")] +impl SideOffsets2DSimdI32 { + #[inline] + pub fn new(top: i32, right: i32, bottom: i32, left: i32) -> SideOffsets2DSimdI32 { + SideOffsets2DSimdI32 { + top: top, + bottom: bottom, + right: right, + left: left, + } + } +} + +#[cfg(feature = "unstable")] +impl SideOffsets2DSimdI32 { + #[inline] + pub fn new_all_same(all: i32) -> SideOffsets2DSimdI32 { + SideOffsets2DSimdI32::new(all.clone(), all.clone(), all.clone(), all.clone()) + } +} + +#[cfg(feature = "unstable")] +impl SideOffsets2DSimdI32 { + #[inline] + pub fn horizontal(&self) -> i32 { + self.left + self.right + } + + #[inline] + pub fn vertical(&self) -> i32 { + self.top + self.bottom + } +} + +/*impl Add for SideOffsets2DSimdI32 { + type Output = SideOffsets2DSimdI32; + #[inline] + fn add(self, other: SideOffsets2DSimdI32) -> SideOffsets2DSimdI32 { + self + other // Use SIMD addition + } +}*/ + +#[cfg(feature = "unstable")] +impl SideOffsets2DSimdI32 { + #[inline] + pub fn zero() -> SideOffsets2DSimdI32 { + SideOffsets2DSimdI32 { + top: 0, + bottom: 0, + right: 0, + left: 0, + } + } + + #[cfg(not(target_arch = "x86_64"))] + #[inline] + pub fn is_zero(&self) -> bool { + self.top == 0 && self.right == 0 && self.bottom == 0 && self.left == 0 + } + + #[cfg(target_arch = "x86_64")] + #[inline] + pub fn is_zero(&self) -> bool { + let is_zero: bool; + unsafe { + asm! { + "ptest $1, $1 + setz $0" + : "=r"(is_zero) + : "x"(*self) + : + : "intel" + }; + } + is_zero + } +} + +#[cfg(feature = "unstable")] +#[cfg(test)] +mod tests { + use super::SideOffsets2DSimdI32; + + #[test] + fn test_is_zero() { + assert!(SideOffsets2DSimdI32::new_all_same(0).is_zero()); + assert!(!SideOffsets2DSimdI32::new_all_same(1).is_zero()); + assert!(!SideOffsets2DSimdI32::new(1, 0, 0, 0).is_zero()); + assert!(!SideOffsets2DSimdI32::new(0, 1, 0, 0).is_zero()); + assert!(!SideOffsets2DSimdI32::new(0, 0, 1, 0).is_zero()); + assert!(!SideOffsets2DSimdI32::new(0, 0, 0, 1).is_zero()); + } +} + +#[cfg(feature = "unstable")] +#[cfg(bench)] +mod bench { + use test::BenchHarness; + use std::num::Zero; + use rand::{XorShiftRng, Rng}; + use super::SideOffsets2DSimdI32; + + #[cfg(target_arch = "x86")] + #[cfg(target_arch = "x86_64")] + #[bench] + fn bench_naive_is_zero(bh: &mut BenchHarness) { + fn is_zero(x: &SideOffsets2DSimdI32) -> bool { + x.top.is_zero() && x.right.is_zero() && x.bottom.is_zero() && x.left.is_zero() + } + let mut rng = XorShiftRng::new().unwrap(); + bh.iter(|| is_zero(&rng.gen::())) + } + + #[bench] + fn bench_is_zero(bh: &mut BenchHarness) { + let mut rng = XorShiftRng::new().unwrap(); + bh.iter(|| rng.gen::().is_zero()) + } + + #[bench] + fn bench_naive_add(bh: &mut BenchHarness) { + fn add(x: &SideOffsets2DSimdI32, y: &SideOffsets2DSimdI32) -> SideOffsets2DSimdI32 { + SideOffsets2DSimdI32 { + top: x.top + y.top, + right: x.right + y.right, + bottom: x.bottom + y.bottom, + left: x.left + y.left, + } + } + let mut rng = XorShiftRng::new().unwrap(); + bh.iter(|| add(&rng.gen::(), &rng.gen::())) + } + + #[bench] + fn bench_add(bh: &mut BenchHarness) { + let mut rng = XorShiftRng::new().unwrap(); + bh.iter(|| rng.gen::() + rng.gen::()) + } +} diff --git a/third_party/rust/euclid-0.11.3/src/size.rs b/third_party/rust/euclid-0.11.3/src/size.rs new file mode 100644 index 000000000000..3b5288fa24f4 --- /dev/null +++ b/third_party/rust/euclid-0.11.3/src/size.rs @@ -0,0 +1,276 @@ +// Copyright 2013 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::UnknownUnit; +use length::Length; +use scale_factor::ScaleFactor; +use num::*; + +use num_traits::NumCast; +use std::fmt; +use std::ops::{Add, Div, Mul, Sub}; +use std::marker::PhantomData; + +/// A 2d size tagged with a unit. +define_matrix! { + #[derive(RustcDecodable, RustcEncodable)] + pub struct TypedSize2D { + pub width: T, + pub height: T, + } +} + +/// Default 2d size type with no unit. +/// +/// `Size2D` provides the same methods as `TypedSize2D`. +pub type Size2D = TypedSize2D; + +impl fmt::Debug for TypedSize2D { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}×{:?}", self.width, self.height) + } +} + +impl fmt::Display for TypedSize2D { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "({}x{})", self.width, self.height) + } +} + +impl TypedSize2D { + /// Constructor taking scalar values. + pub fn new(width: T, height: T) -> TypedSize2D { + TypedSize2D { + width: width, + height: height, + _unit: PhantomData, + } + } +} + +impl TypedSize2D { + /// Constructor taking scalar strongly typed lengths. + pub fn from_lengths(width: Length, height: Length) -> TypedSize2D { + TypedSize2D::new(width.get(), height.get()) + } +} + +impl TypedSize2D { + /// Rounds each component to the nearest integer value. + /// + /// This behavior is preserved for negative values (unlike the basic cast). + pub fn round(&self) -> Self { + TypedSize2D::new(self.width.round(), self.height.round()) + } +} + +impl TypedSize2D { + /// Rounds each component to the smallest integer equal or greater than the original value. + /// + /// This behavior is preserved for negative values (unlike the basic cast). + pub fn ceil(&self) -> Self { + TypedSize2D::new(self.width.ceil(), self.height.ceil()) + } +} + +impl TypedSize2D { + /// Rounds each component to the biggest integer equal or lower than the original value. + /// + /// This behavior is preserved for negative values (unlike the basic cast). + pub fn floor(&self) -> Self { + TypedSize2D::new(self.width.floor(), self.height.floor()) + } +} + +impl, U> Add for TypedSize2D { + type Output = TypedSize2D; + fn add(self, other: TypedSize2D) -> TypedSize2D { + TypedSize2D::new(self.width + other.width, self.height + other.height) + } +} + +impl, U> Sub for TypedSize2D { + type Output = TypedSize2D; + fn sub(self, other: TypedSize2D) -> TypedSize2D { + TypedSize2D::new(self.width - other.width, self.height - other.height) + } +} + +impl, U> TypedSize2D { + pub fn area(&self) -> U { self.width * self.height } +} + +impl TypedSize2D { + pub fn zero() -> TypedSize2D { + TypedSize2D::new( + Zero::zero(), + Zero::zero(), + ) + } +} + +impl Zero for TypedSize2D { + fn zero() -> TypedSize2D { + TypedSize2D::new( + Zero::zero(), + Zero::zero(), + ) + } +} + +impl, U> Mul for TypedSize2D { + type Output = TypedSize2D; + #[inline] + fn mul(self, scale: T) -> TypedSize2D { + TypedSize2D::new(self.width * scale, self.height * scale) + } +} + +impl, U> Div for TypedSize2D { + type Output = TypedSize2D; + #[inline] + fn div(self, scale: T) -> TypedSize2D { + TypedSize2D::new(self.width / scale, self.height / scale) + } +} + +impl, U1, U2> Mul> for TypedSize2D { + type Output = TypedSize2D; + #[inline] + fn mul(self, scale: ScaleFactor) -> TypedSize2D { + TypedSize2D::new(self.width * scale.get(), self.height * scale.get()) + } +} + +impl, U1, U2> Div> for TypedSize2D { + type Output = TypedSize2D; + #[inline] + fn div(self, scale: ScaleFactor) -> TypedSize2D { + TypedSize2D::new(self.width / scale.get(), self.height / scale.get()) + } +} + +impl TypedSize2D { + /// Returns self.width as a Length carrying the unit. + #[inline] + pub fn width_typed(&self) -> Length { Length::new(self.width) } + + /// Returns self.height as a Length carrying the unit. + #[inline] + pub fn height_typed(&self) -> Length { Length::new(self.height) } + + #[inline] + pub fn to_array(&self) -> [T; 2] { [self.width, self.height] } + + /// Drop the units, preserving only the numeric value. + pub fn to_untyped(&self) -> Size2D { + TypedSize2D::new(self.width, self.height) + } + + /// Tag a unitless value with units. + pub fn from_untyped(p: &Size2D) -> TypedSize2D { + TypedSize2D::new(p.width, p.height) + } +} + +impl TypedSize2D { + /// Cast from one numeric representation to another, preserving the units. + /// + /// When casting from floating point to integer coordinates, the decimals are truncated + /// as one would expect from a simple cast, but this behavior does not always make sense + /// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting. + pub fn cast(&self) -> Option> { + match (NumCast::from(self.width), NumCast::from(self.height)) { + (Some(w), Some(h)) => Some(TypedSize2D::new(w, h)), + _ => None + } + } + + // Convenience functions for common casts + + /// Cast into an `f32` size. + pub fn to_f32(&self) -> TypedSize2D { + self.cast().unwrap() + } + + /// Cast into an `uint` size, truncating decimals if any. + /// + /// When casting from floating point sizes, it is worth considering whether + /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain + /// the desired conversion behavior. + pub fn to_uint(&self) -> TypedSize2D { + self.cast().unwrap() + } + + /// Cast into an `i32` size, truncating decimals if any. + /// + /// When casting from floating point sizes, it is worth considering whether + /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain + /// the desired conversion behavior. + pub fn to_i32(&self) -> TypedSize2D { + self.cast().unwrap() + } + + /// Cast into an `i64` size, truncating decimals if any. + /// + /// When casting from floating point sizes, it is worth considering whether + /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain + /// the desired conversion behavior. + pub fn to_i64(&self) -> TypedSize2D { + self.cast().unwrap() + } +} + +/// Shorthand for `TypedSize2D::new(w, h)`. +pub fn size2(w: T, h: T) -> TypedSize2D { + TypedSize2D::new(w, h) +} + +#[cfg(test)] +mod size2d { + use super::Size2D; + + #[test] + pub fn test_add() { + let p1 = Size2D::new(1.0, 2.0); + let p2 = Size2D::new(3.0, 4.0); + assert_eq!(p1 + p2, Size2D::new(4.0, 6.0)); + + let p1 = Size2D::new(1.0, 2.0); + let p2 = Size2D::new(0.0, 0.0); + assert_eq!(p1 + p2, Size2D::new(1.0, 2.0)); + + let p1 = Size2D::new(1.0, 2.0); + let p2 = Size2D::new(-3.0, -4.0); + assert_eq!(p1 + p2, Size2D::new(-2.0, -2.0)); + + let p1 = Size2D::new(0.0, 0.0); + let p2 = Size2D::new(0.0, 0.0); + assert_eq!(p1 + p2, Size2D::new(0.0, 0.0)); + } + + #[test] + pub fn test_sub() { + let p1 = Size2D::new(1.0, 2.0); + let p2 = Size2D::new(3.0, 4.0); + assert_eq!(p1 - p2, Size2D::new(-2.0, -2.0)); + + let p1 = Size2D::new(1.0, 2.0); + let p2 = Size2D::new(0.0, 0.0); + assert_eq!(p1 - p2, Size2D::new(1.0, 2.0)); + + let p1 = Size2D::new(1.0, 2.0); + let p2 = Size2D::new(-3.0, -4.0); + assert_eq!(p1 - p2, Size2D::new(4.0, 6.0)); + + let p1 = Size2D::new(0.0, 0.0); + let p2 = Size2D::new(0.0, 0.0); + assert_eq!(p1 - p2, Size2D::new(0.0, 0.0)); + } +} diff --git a/third_party/rust/euclid-0.11.3/src/trig.rs b/third_party/rust/euclid-0.11.3/src/trig.rs new file mode 100644 index 000000000000..6f3f348a9b1a --- /dev/null +++ b/third_party/rust/euclid-0.11.3/src/trig.rs @@ -0,0 +1,50 @@ +// Copyright 2013 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +/// Trait for basic trigonometry functions, so they can be used on generic numeric types +pub trait Trig { + fn sin(self) -> Self; + fn cos(self) -> Self; + fn tan(self) -> Self; +} + +impl Trig for f32 { + #[inline] + fn sin(self) -> f32 { + self.sin() + } + + #[inline] + fn cos(self) -> f32 { + self.cos() + } + + #[inline] + fn tan(self) -> f32 { + self.tan() + } +} + +impl Trig for f64 { + #[inline] + fn sin(self) -> f64 { + self.sin() + } + + #[inline] + fn cos(self) -> f64 { + self.cos() + } + + #[inline] + fn tan(self) -> f64 { + self.tan() + } +} diff --git a/third_party/rust/euclid/.cargo-checksum.json b/third_party/rust/euclid/.cargo-checksum.json index becd539a3c86..994eada63ff9 100644 --- a/third_party/rust/euclid/.cargo-checksum.json +++ b/third_party/rust/euclid/.cargo-checksum.json @@ -1 +1 @@ -{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"118514fd9c4958df0d25584cda4917186c46011569f55ef350530c1ad3fbdb48",".travis.yml":"13d3e5a7bf83b04c8e8cfa14f0297bd8366d68391d977dd547f64707dffc275a","COPYRIGHT":"ec82b96487e9e778ee610c7ab245162464782cfa1f555c2299333f8dbe5c036a","Cargo.toml":"10cfe5580ee83ae883a60d96f504dda8ae7885ae5fd3a3faf95c2a2b8b38fad0","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"62065228e42caebca7e7d7db1204cbb867033de5982ca4009928915e4095f3a3","README.md":"52f974f01c1e15182413e4321c8817d5e66fe4d92c5ec223c857dd0440f5c229","src/approxeq.rs":"2987e046c90d948b6c7d7ddba52d10c8b7520d71dc0a50dbe7665de128d7410e","src/length.rs":"d7c6369f2fe2a17c845b57749bd48c471159f0571a7314d3bf90737d53f697d3","src/lib.rs":"e2e621f05304278d020429d0349acf7a4e7c7a9a72bd23fc0e55680267472ee9","src/macros.rs":"b63dabdb52df84ea170dc1dab5fe8d7a78c054562d1566bab416124708d2d7af","src/matrix2d.rs":"2361338f59813adf4eebaab76e4dd82be0fbfb9ff2461da8dd9ac9d43583b322","src/matrix4d.rs":"b8547bed6108b037192021c97169c00ad456120b849e9b7ac7bec40363edaec1","src/num.rs":"62286aa642ce3afa7ebd950f50bf2197d8722907f2e23a2e2ea6690484d8b250","src/point.rs":"53f3c9018c822e0a6dc5018005e153775479f41fe55c082d0be10f331fda773f","src/rect.rs":"db62b3af8939529509ae21b3bf6ae498d73a95b4ff3a6eba4db614be08e95f8b","src/scale_factor.rs":"df6dbd1f0f9f63210b92809f84a383dad982a74f09789cf22c7d8f9b62199d39","src/side_offsets.rs":"f85526a421ffda63ff01a3478d4162c8717eef68e942acfa2fd9a1adee02ebb2","src/size.rs":"19d1c08f678d793c6eff49a44f69e5b7179e574aa9b81fb4e73210733af38718","src/trig.rs":"6b207980052d13c625272f2a70a22f7741b59513c2a4882385926f497c763a63"},"package":"f5517462c626a893f3b027615e88d7102cc6dd3f7f1bcb90c7220fb1da4970b5"} \ No newline at end of file +{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"118514fd9c4958df0d25584cda4917186c46011569f55ef350530c1ad3fbdb48",".travis.yml":"13d3e5a7bf83b04c8e8cfa14f0297bd8366d68391d977dd547f64707dffc275a","COPYRIGHT":"ec82b96487e9e778ee610c7ab245162464782cfa1f555c2299333f8dbe5c036a","Cargo.toml":"c91c98dc9510ef29a7ce0d7c78294f15cb139c9afecca38e5fda56b0a6984954","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"62065228e42caebca7e7d7db1204cbb867033de5982ca4009928915e4095f3a3","README.md":"625bec69c76ce5423fdd05cfe46922b2680ec517f97c5854ce34798d1d8a9541","src/approxeq.rs":"6cf810ad389c73a27141a7a67454ed12d4b01c3c16605b9a7414b389bc0615dd","src/length.rs":"d7c6369f2fe2a17c845b57749bd48c471159f0571a7314d3bf90737d53f697d3","src/lib.rs":"e2e621f05304278d020429d0349acf7a4e7c7a9a72bd23fc0e55680267472ee9","src/macros.rs":"b63dabdb52df84ea170dc1dab5fe8d7a78c054562d1566bab416124708d2d7af","src/matrix2d.rs":"2361338f59813adf4eebaab76e4dd82be0fbfb9ff2461da8dd9ac9d43583b322","src/matrix4d.rs":"b8547bed6108b037192021c97169c00ad456120b849e9b7ac7bec40363edaec1","src/num.rs":"749b201289fc6663199160a2f9204e17925fd3053f8ab7779e7bfb377ad06227","src/point.rs":"dbf12a3ad35dc2502b7f2637856d8ee40f5a96e37ed00f3ee3272bf5752c060c","src/rect.rs":"0a255046dd11a6093d9a77e327e1df31802808536b4d87e4e3b80ff6b208de0f","src/scale_factor.rs":"df6dbd1f0f9f63210b92809f84a383dad982a74f09789cf22c7d8f9b62199d39","src/side_offsets.rs":"f85526a421ffda63ff01a3478d4162c8717eef68e942acfa2fd9a1adee02ebb2","src/size.rs":"ae1b647e300600b50a21dba8c1d915801782ebae82baeb5e49017e6d68a49b28","src/trig.rs":"ef290927af252ca90a29ba9f17158b591ed591604e66cb9df045dd47b9cfdca5"},"package":"6083f113c422ff9cd855a1cf6cc8ec0903606c0eb43a0c6a0ced3bdc9731e4c1"} \ No newline at end of file diff --git a/third_party/rust/euclid/Cargo.toml b/third_party/rust/euclid/Cargo.toml index f628a082c81f..9ee733c8243b 100644 --- a/third_party/rust/euclid/Cargo.toml +++ b/third_party/rust/euclid/Cargo.toml @@ -1,17 +1,19 @@ [package] name = "euclid" -version = "0.11.3" +version = "0.13.0" authors = ["The Servo Project Developers"] description = "Geometry primitives" documentation = "https://docs.rs/euclid/" repository = "https://github.com/servo/euclid" +keywords = ["matrix", "vector", "linear-algebra", "geometry"] +categories = ["science"] license = "MIT / Apache-2.0" [features] unstable = [] [dependencies] -heapsize = "0.3" +heapsize = "0.4" rustc-serialize = "0.3.2" num-traits = {version = "0.1.32", default-features = false} log = "0.3.1" diff --git a/third_party/rust/euclid/README.md b/third_party/rust/euclid/README.md index 310c514cd405..13b488da9f69 100644 --- a/third_party/rust/euclid/README.md +++ b/third_party/rust/euclid/README.md @@ -1,5 +1,8 @@ # euclid -This is a small library for geometric types. +This is a small library for geometric types with a focus on 2d graphics and +layout. -[Documentation](https://docs.rs/euclid/) +* [Documentation](https://docs.rs/euclid/) +* [Release notes](https://github.com/servo/euclid/releases) +* [crates.io](https://crates.io/crates/euclid) diff --git a/third_party/rust/euclid/src/approxeq.rs b/third_party/rust/euclid/src/approxeq.rs index 10151110669a..81f4beb536f6 100644 --- a/third_party/rust/euclid/src/approxeq.rs +++ b/third_party/rust/euclid/src/approxeq.rs @@ -15,33 +15,22 @@ pub trait ApproxEq { fn approx_eq_eps(&self, other: &Self, approx_epsilon: &Eps) -> bool; } -impl ApproxEq for f32 { - #[inline] - fn approx_epsilon() -> f32 { 1.0e-6 } - - #[inline] - fn approx_eq(&self, other: &f32) -> bool { - self.approx_eq_eps(other, &1.0e-6) - } - - #[inline] - fn approx_eq_eps(&self, other: &f32, approx_epsilon: &f32) -> bool { - (*self - *other).abs() < *approx_epsilon - } +macro_rules! approx_eq { + ($ty:ty, $eps:expr) => ( + impl ApproxEq<$ty> for $ty { + #[inline] + fn approx_epsilon() -> $ty { $eps } + #[inline] + fn approx_eq(&self, other: &$ty) -> bool { + self.approx_eq_eps(other, &$eps) + } + #[inline] + fn approx_eq_eps(&self, other: &$ty, approx_epsilon: &$ty) -> bool { + (*self - *other).abs() < *approx_epsilon + } + } + ) } - -impl ApproxEq for f64 { - #[inline] - fn approx_epsilon() -> f64 { 1.0e-6 } - - #[inline] - fn approx_eq(&self, other: &f64) -> bool { - self.approx_eq_eps(other, &1.0e-6) - } - - #[inline] - fn approx_eq_eps(&self, other: &f64, approx_epsilon: &f64) -> bool { - (*self - *other).abs() < *approx_epsilon - } -} +approx_eq!(f32, 1.0e-6); +approx_eq!(f64, 1.0e-6); diff --git a/third_party/rust/euclid/src/num.rs b/third_party/rust/euclid/src/num.rs index b612624273ed..5b57a1949bc6 100644 --- a/third_party/rust/euclid/src/num.rs +++ b/third_party/rust/euclid/src/num.rs @@ -27,40 +27,51 @@ impl One for T { fn one() -> T { num_traits::One::one() } } + pub trait Round : Copy { fn round(self) -> Self; } pub trait Floor : Copy { fn floor(self) -> Self; } pub trait Ceil : Copy { fn ceil(self) -> Self; } -impl Round for f32 { fn round(self) -> Self { self.round() } } -impl Round for f64 { fn round(self) -> Self { self.round() } } -impl Round for i16 { fn round(self) -> Self { self } } -impl Round for u16 { fn round(self) -> Self { self } } -impl Round for i32 { fn round(self) -> Self { self } } -impl Round for i64 { fn round(self) -> Self { self } } -impl Round for u32 { fn round(self) -> Self { self } } -impl Round for u64 { fn round(self) -> Self { self } } -impl Round for usize { fn round(self) -> Self { self } } -impl Round for isize { fn round(self) -> Self { self } } - -impl Floor for f32 { fn floor(self) -> Self { self.floor() } } -impl Floor for f64 { fn floor(self) -> Self { self.floor() } } -impl Floor for i16 { fn floor(self) -> Self { self } } -impl Floor for u16 { fn floor(self) -> Self { self } } -impl Floor for i32 { fn floor(self) -> Self { self } } -impl Floor for i64 { fn floor(self) -> Self { self } } -impl Floor for u32 { fn floor(self) -> Self { self } } -impl Floor for u64 { fn floor(self) -> Self { self } } -impl Floor for usize { fn floor(self) -> Self { self } } -impl Floor for isize { fn floor(self) -> Self { self } } - -impl Ceil for f32 { fn ceil(self) -> Self { self.ceil() } } -impl Ceil for f64 { fn ceil(self) -> Self { self.ceil() } } -impl Ceil for i16 { fn ceil(self) -> Self { self } } -impl Ceil for u16 { fn ceil(self) -> Self { self } } -impl Ceil for i32 { fn ceil(self) -> Self { self } } -impl Ceil for i64 { fn ceil(self) -> Self { self } } -impl Ceil for u32 { fn ceil(self) -> Self { self } } -impl Ceil for u64 { fn ceil(self) -> Self { self } } -impl Ceil for usize { fn ceil(self) -> Self { self } } -impl Ceil for isize { fn ceil(self) -> Self { self } } +macro_rules! num_int { + ($ty:ty) => ( + impl Round for $ty { + #[inline] + fn round(self) -> $ty { self } + } + impl Floor for $ty { + #[inline] + fn floor(self) -> $ty { self } + } + impl Ceil for $ty { + #[inline] + fn ceil(self) -> $ty { self } + } + ) +} +macro_rules! num_float { + ($ty:ty) => ( + impl Round for $ty { + #[inline] + fn round(self) -> $ty { self.round() } + } + impl Floor for $ty { + #[inline] + fn floor(self) -> $ty { self.floor() } + } + impl Ceil for $ty { + #[inline] + fn ceil(self) -> $ty { self.ceil() } + } + ) +} +num_int!(i16); +num_int!(u16); +num_int!(i32); +num_int!(u32); +num_int!(i64); +num_int!(u64); +num_int!(isize); +num_int!(usize); +num_float!(f32); +num_float!(f64); diff --git a/third_party/rust/euclid/src/point.rs b/third_party/rust/euclid/src/point.rs index 6239fbe5181a..8e6986a4e704 100644 --- a/third_party/rust/euclid/src/point.rs +++ b/third_party/rust/euclid/src/point.rs @@ -254,7 +254,7 @@ impl TypedPoint2D { /// When casting from floating point points, it is worth considering whether /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain /// the desired conversion behavior. - pub fn to_uint(&self) -> TypedPoint2D { + pub fn to_usize(&self) -> TypedPoint2D { self.cast().unwrap() } @@ -517,7 +517,7 @@ impl TypedPoint3D { /// When casting from floating point points, it is worth considering whether /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain /// the desired conversion behavior. - pub fn to_uint(&self) -> TypedPoint3D { + pub fn to_usize(&self) -> TypedPoint3D { self.cast().unwrap() } @@ -756,7 +756,7 @@ impl TypedPoint4D { /// When casting from floating point points, it is worth considering whether /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain /// the desired conversion behavior. - pub fn to_uint(&self) -> TypedPoint4D { + pub fn to_usize(&self) -> TypedPoint4D { self.cast().unwrap() } diff --git a/third_party/rust/euclid/src/rect.rs b/third_party/rust/euclid/src/rect.rs index 835c1b50c911..6d759b171c73 100644 --- a/third_party/rust/euclid/src/rect.rs +++ b/third_party/rust/euclid/src/rect.rs @@ -404,7 +404,7 @@ impl TypedRect { /// When casting from floating point rectangles, it is worth considering whether /// to `round()`, `round_in()` or `round_out()` before the cast in order to /// obtain the desired conversion behavior. - pub fn to_uint(&self) -> TypedRect { + pub fn to_usize(&self) -> TypedRect { self.cast().unwrap() } diff --git a/third_party/rust/euclid/src/size.rs b/third_party/rust/euclid/src/size.rs index 3b5288fa24f4..0a97e4aeecd2 100644 --- a/third_party/rust/euclid/src/size.rs +++ b/third_party/rust/euclid/src/size.rs @@ -204,7 +204,7 @@ impl TypedSize2D { /// When casting from floating point sizes, it is worth considering whether /// to `round()`, `ceil()` or `floor()` before the cast in order to obtain /// the desired conversion behavior. - pub fn to_uint(&self) -> TypedSize2D { + pub fn to_usize(&self) -> TypedSize2D { self.cast().unwrap() } diff --git a/third_party/rust/euclid/src/trig.rs b/third_party/rust/euclid/src/trig.rs index 6f3f348a9b1a..7f3658227095 100644 --- a/third_party/rust/euclid/src/trig.rs +++ b/third_party/rust/euclid/src/trig.rs @@ -15,36 +15,18 @@ pub trait Trig { fn tan(self) -> Self; } -impl Trig for f32 { - #[inline] - fn sin(self) -> f32 { - self.sin() - } - - #[inline] - fn cos(self) -> f32 { - self.cos() - } - - #[inline] - fn tan(self) -> f32 { - self.tan() - } +macro_rules! trig { + ($ty:ty) => ( + impl Trig for $ty { + #[inline] + fn sin(self) -> $ty { self.sin() } + #[inline] + fn cos(self) -> $ty { self.cos() } + #[inline] + fn tan(self) -> $ty { self.tan() } + } + ) } -impl Trig for f64 { - #[inline] - fn sin(self) -> f64 { - self.sin() - } - - #[inline] - fn cos(self) -> f64 { - self.cos() - } - - #[inline] - fn tan(self) -> f64 { - self.tan() - } -} +trig!(f32); +trig!(f64); diff --git a/third_party/rust/heapsize-0.3.8/.cargo-checksum.json b/third_party/rust/heapsize-0.3.8/.cargo-checksum.json new file mode 100644 index 000000000000..590b4466a86a --- /dev/null +++ b/third_party/rust/heapsize-0.3.8/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"97503c8cf1fc53fd41e237662402477c0ab257225d25fe21470494a0b1bbec3c",".travis.yml":"1108708721703f4562646e1e7c6f6c924fa997835714bcc6a3ff8a58382134f1","Cargo.toml":"723e5918946fdb518ed1ad3e03ae9104b980cbe85bbee1989dc4197570ba6d73","README.md":"9a38b16bccde5db28c34d79134f02d2cdcbbab224b9a68ace93c5b85b5ef38f2","appveyor.yml":"130e820ab60abf8d08f3a91d4b0158e6a581c180385e12850113adb362eb158c","build.rs":"e13e88ed285a829256d3c6987563a663c37e335457d090125a3e19b1a97fec8e","src/lib.rs":"ab4e0a2e6d0ac700df5dbb7a2c83542cb82c94d4e46c632a4114fec93d6aba0a","tests/tests.rs":"f642da7b54b6cde55cf25fe84b2e6b27356d26b351d42a38e944b93e0c1fa24f"},"package":"5a376f7402b85be6e0ba504243ecbc0709c48019ecc6286d0540c2e359050c88"} \ No newline at end of file diff --git a/third_party/rust/heapsize-0.3.8/.cargo-ok b/third_party/rust/heapsize-0.3.8/.cargo-ok new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/third_party/rust/heapsize-0.3.8/.gitignore b/third_party/rust/heapsize-0.3.8/.gitignore new file mode 100644 index 000000000000..d4f917d3d50d --- /dev/null +++ b/third_party/rust/heapsize-0.3.8/.gitignore @@ -0,0 +1,3 @@ +target +Cargo.lock +*.swp diff --git a/third_party/rust/heapsize-0.3.8/.travis.yml b/third_party/rust/heapsize-0.3.8/.travis.yml new file mode 100644 index 000000000000..ef8e20c3ffe5 --- /dev/null +++ b/third_party/rust/heapsize-0.3.8/.travis.yml @@ -0,0 +1,19 @@ +language: rust +rust: + - 1.8.0 + - nightly + - beta + - stable + +os: + - linux + - osx + +notifications: + webhooks: http://build.servo.org:54856/travis + +script: + - cargo test + - "[ $TRAVIS_RUST_VERSION != nightly ] || cargo test --features unstable" + - "[ $TRAVIS_RUST_VERSION != nightly ] || cargo test --manifest-path derive/Cargo.toml" + diff --git a/third_party/rust/heapsize-0.3.8/Cargo.toml b/third_party/rust/heapsize-0.3.8/Cargo.toml new file mode 100644 index 000000000000..62be5b66b37a --- /dev/null +++ b/third_party/rust/heapsize-0.3.8/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "heapsize" +version = "0.3.8" +authors = [ "The Servo Project Developers" ] +description = "Infrastructure for measuring the total runtime size of an object on the heap" +license = "MPL-2.0" +repository = "https://github.com/servo/heapsize" +build = "build.rs" + +[target.'cfg(windows)'.dependencies] +kernel32-sys = "0.2.1" + +[features] +unstable = [] diff --git a/third_party/rust/heapsize-0.3.8/README.md b/third_party/rust/heapsize-0.3.8/README.md new file mode 100644 index 000000000000..f89a960cba85 --- /dev/null +++ b/third_party/rust/heapsize-0.3.8/README.md @@ -0,0 +1,5 @@ +# heapsize + +In support of measuring heap allocations in Rust programs. + +[API Documentation](https://doc.servo.org/heapsize/) diff --git a/third_party/rust/heapsize-0.3.8/appveyor.yml b/third_party/rust/heapsize-0.3.8/appveyor.yml new file mode 100644 index 000000000000..595b08c2d166 --- /dev/null +++ b/third_party/rust/heapsize-0.3.8/appveyor.yml @@ -0,0 +1,23 @@ +environment: + matrix: + - FEATURES: "" + - FEATURES: "unstable" + +platform: + - i686-pc-windows-gnu + - i686-pc-windows-msvc + - x86_64-pc-windows-gnu + - x86_64-pc-windows-msvc + +install: + - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-nightly-${env:PLATFORM}.exe" + - rust-nightly-%PLATFORM%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust" + - SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin + - rustc -V + - cargo -V + +build_script: + - cargo build --verbose --features "%FEATURES%" + +test_script: + - cargo test --verbose --features "%FEATURES%" diff --git a/third_party/rust/heapsize-0.3.8/build.rs b/third_party/rust/heapsize-0.3.8/build.rs new file mode 100644 index 000000000000..9e299e6b19d6 --- /dev/null +++ b/third_party/rust/heapsize-0.3.8/build.rs @@ -0,0 +1,37 @@ +use std::env::var; +use std::process::Command; +use std::str; + +fn main() { + let verbose = Command::new(var("RUSTC").unwrap_or("rustc".into())) + .arg("--version") + .arg("--verbose") + .output() + .unwrap() + .stdout; + let verbose = str::from_utf8(&verbose).unwrap(); + let mut commit_date = None; + let mut release = None; + for line in verbose.lines() { + let mut parts = line.split(':'); + match parts.next().unwrap().trim() { + "commit-date" => commit_date = Some(parts.next().unwrap().trim()), + "release" => release = Some(parts.next().unwrap().trim()), + _ => {} + } + } + let version = release.unwrap().split('-').next().unwrap();; + let mut version_components = version.split('.').map(|s| s.parse::().unwrap()); + let version = ( + version_components.next().unwrap(), + version_components.next().unwrap(), + version_components.next().unwrap(), + // "unknown" sorts after "2016-02-14", which is what we want to defaut to unprefixed + // https://github.com/servo/heapsize/pull/44#issuecomment-187935883 + commit_date.unwrap() + ); + assert_eq!(version_components.next(), None); + if version < (1, 8, 0, "2016-02-14") { + println!("cargo:rustc-cfg=prefixed_jemalloc"); + } +} diff --git a/third_party/rust/heapsize-0.3.8/src/lib.rs b/third_party/rust/heapsize-0.3.8/src/lib.rs new file mode 100644 index 000000000000..d27789aa59e1 --- /dev/null +++ b/third_party/rust/heapsize-0.3.8/src/lib.rs @@ -0,0 +1,323 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! Data structure measurement. + +#[cfg(target_os = "windows")] +extern crate kernel32; + +#[cfg(target_os = "windows")] +use kernel32::{GetProcessHeap, HeapSize, HeapValidate}; +use std::borrow::Cow; +use std::cell::{Cell, RefCell}; +use std::collections::{BTreeMap, HashSet, HashMap, LinkedList, VecDeque}; +use std::hash::BuildHasher; +use std::hash::Hash; +use std::marker::PhantomData; +use std::mem::size_of; +use std::net::{Ipv4Addr, Ipv6Addr}; +use std::os::raw::c_void; +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, AtomicIsize, AtomicUsize}; +use std::rc::Rc; + +/// Get the size of a heap block. +/// +/// Ideally Rust would expose a function like this in std::rt::heap. +/// +/// `unsafe` because the caller must ensure that the pointer is from jemalloc. +/// FIXME: This probably interacts badly with custom allocators: +/// https://doc.rust-lang.org/book/custom-allocators.html +pub unsafe fn heap_size_of(ptr: *const c_void) -> usize { + if ptr == 0x01 as *const c_void { + 0 + } else { + heap_size_of_impl(ptr) + } +} + +#[cfg(not(target_os = "windows"))] +unsafe fn heap_size_of_impl(ptr: *const c_void) -> usize { + // The C prototype is `je_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void *ptr)`. On some + // platforms `JEMALLOC_USABLE_SIZE_CONST` is `const` and on some it is empty. But in practice + // this function doesn't modify the contents of the block that `ptr` points to, so we use + // `*const c_void` here. + extern "C" { + #[cfg_attr(any(prefixed_jemalloc, target_os = "macos", target_os = "android"), link_name = "je_malloc_usable_size")] + fn malloc_usable_size(ptr: *const c_void) -> usize; + } + malloc_usable_size(ptr) +} + +#[cfg(target_os = "windows")] +pub unsafe fn heap_size_of_impl(mut ptr: *const c_void) -> usize { + let heap = GetProcessHeap(); + + if HeapValidate(heap, 0, ptr) == 0 { + ptr = *(ptr as *const *const c_void).offset(-1); + } + + HeapSize(heap, 0, ptr) as usize +} + +// The simplest trait for measuring the size of heap data structures. More complex traits that +// return multiple measurements -- e.g. measure text separately from images -- are also possible, +// and should be used when appropriate. +// +pub trait HeapSizeOf { + /// Measure the size of any heap-allocated structures that hang off this value, but not the + /// space taken up by the value itself (i.e. what size_of:: measures, more or less); that + /// space is handled by the implementation of HeapSizeOf for Box below. + fn heap_size_of_children(&self) -> usize; +} + +// There are two possible ways to measure the size of `self` when it's on the heap: compute it +// (with `::std::rt::heap::usable_size(::std::mem::size_of::(), 0)`) or measure it directly +// using the heap allocator (with `heap_size_of`). We do the latter, for the following reasons. +// +// * The heap allocator is the true authority for the sizes of heap blocks; its measurement is +// guaranteed to be correct. In comparison, size computations are error-prone. (For example, the +// `rt::heap::usable_size` function used in some of Rust's non-default allocator implementations +// underestimate the true usable size of heap blocks, which is safe in general but would cause +// under-measurement here.) +// +// * If we measure something that isn't a heap block, we'll get a crash. This keeps us honest, +// which is important because unsafe code is involved and this can be gotten wrong. +// +// However, in the best case, the two approaches should give the same results. +// +impl HeapSizeOf for Box { + fn heap_size_of_children(&self) -> usize { + // Measure size of `self`. + unsafe { + heap_size_of(&**self as *const T as *const c_void) + (**self).heap_size_of_children() + } + } +} + +impl HeapSizeOf for [T] { + fn heap_size_of_children(&self) -> usize { + self.iter().fold(0, |size, item| size + item.heap_size_of_children()) + } +} + +impl HeapSizeOf for String { + fn heap_size_of_children(&self) -> usize { + unsafe { + heap_size_of(self.as_ptr() as *const c_void) + } + } +} + +impl<'a, T: ?Sized> HeapSizeOf for &'a T { + fn heap_size_of_children(&self) -> usize { + 0 + } +} + +impl HeapSizeOf for Option { + fn heap_size_of_children(&self) -> usize { + match *self { + None => 0, + Some(ref x) => x.heap_size_of_children() + } + } +} + +impl HeapSizeOf for Result { + fn heap_size_of_children(&self) -> usize { + match *self { + Ok(ref x) => x.heap_size_of_children(), + Err(ref e) => e.heap_size_of_children(), + } + } +} + +impl<'a, B: ?Sized + ToOwned> HeapSizeOf for Cow<'a, B> where B::Owned: HeapSizeOf { + fn heap_size_of_children(&self) -> usize { + match *self { + Cow::Borrowed(_) => 0, + Cow::Owned(ref b) => b.heap_size_of_children(), + } + } +} + +impl HeapSizeOf for () { + fn heap_size_of_children(&self) -> usize { + 0 + } +} + +impl HeapSizeOf for (T1, T2) + where T1: HeapSizeOf, T2 :HeapSizeOf +{ + fn heap_size_of_children(&self) -> usize { + self.0.heap_size_of_children() + + self.1.heap_size_of_children() + } +} + +impl HeapSizeOf for (T1, T2, T3) + where T1: HeapSizeOf, T2 :HeapSizeOf, T3: HeapSizeOf +{ + fn heap_size_of_children(&self) -> usize { + self.0.heap_size_of_children() + + self.1.heap_size_of_children() + + self.2.heap_size_of_children() + } +} + +impl HeapSizeOf for (T1, T2, T3, T4) + where T1: HeapSizeOf, T2 :HeapSizeOf, T3: HeapSizeOf, T4: HeapSizeOf +{ + fn heap_size_of_children(&self) -> usize { + self.0.heap_size_of_children() + + self.1.heap_size_of_children() + + self.2.heap_size_of_children() + + self.3.heap_size_of_children() + } +} + +impl HeapSizeOf for (T1, T2, T3, T4, T5) + where T1: HeapSizeOf, T2 :HeapSizeOf, T3: HeapSizeOf, T4: HeapSizeOf, T5: HeapSizeOf +{ + fn heap_size_of_children(&self) -> usize { + self.0.heap_size_of_children() + + self.1.heap_size_of_children() + + self.2.heap_size_of_children() + + self.3.heap_size_of_children() + + self.4.heap_size_of_children() + } +} + +impl HeapSizeOf for Arc { + fn heap_size_of_children(&self) -> usize { + (**self).heap_size_of_children() + } +} + +impl HeapSizeOf for RefCell { + fn heap_size_of_children(&self) -> usize { + self.borrow().heap_size_of_children() + } +} + +impl HeapSizeOf for Cell { + fn heap_size_of_children(&self) -> usize { + self.get().heap_size_of_children() + } +} + +impl HeapSizeOf for Vec { + fn heap_size_of_children(&self) -> usize { + self.iter().fold( + unsafe { heap_size_of(self.as_ptr() as *const c_void) }, + |n, elem| n + elem.heap_size_of_children()) + } +} + +impl HeapSizeOf for VecDeque { + fn heap_size_of_children(&self) -> usize { + self.iter().fold( + // FIXME: get the buffer pointer for heap_size_of(), capacity() is a lower bound: + self.capacity() * size_of::(), + |n, elem| n + elem.heap_size_of_children()) + } +} + +impl HeapSizeOf for Vec> { + fn heap_size_of_children(&self) -> usize { + // The fate of measuring Rc is still undecided, but we still want to measure + // the space used for storing them. + unsafe { + heap_size_of(self.as_ptr() as *const c_void) + } + } +} + +impl HeapSizeOf for HashSet + where T: Eq + Hash, S: BuildHasher { + fn heap_size_of_children(&self) -> usize { + //TODO(#6908) measure actual bucket memory usage instead of approximating + let size = self.capacity() * (size_of::() + size_of::()); + self.iter().fold(size, |n, value| { + n + value.heap_size_of_children() + }) + } +} + +impl HeapSizeOf for HashMap + where K: Eq + Hash, S: BuildHasher { + fn heap_size_of_children(&self) -> usize { + //TODO(#6908) measure actual bucket memory usage instead of approximating + let size = self.capacity() * (size_of::() + size_of::() + size_of::()); + self.iter().fold(size, |n, (key, value)| { + n + key.heap_size_of_children() + value.heap_size_of_children() + }) + } +} + +// PhantomData is always 0. +impl HeapSizeOf for PhantomData { + fn heap_size_of_children(&self) -> usize { + 0 + } +} + +// A linked list has an overhead of two words per item. +impl HeapSizeOf for LinkedList { + fn heap_size_of_children(&self) -> usize { + let mut size = 0; + for item in self { + size += 2 * size_of::() + size_of::() + item.heap_size_of_children(); + } + size + } +} + +// FIXME: Overhead for the BTreeMap nodes is not accounted for. +impl HeapSizeOf for BTreeMap { + fn heap_size_of_children(&self) -> usize { + let mut size = 0; + for (key, value) in self.iter() { + size += size_of::<(K, V)>() + + key.heap_size_of_children() + + value.heap_size_of_children(); + } + size + } +} + +/// For use on types defined in external crates +/// with known heap sizes. +#[macro_export] +macro_rules! known_heap_size( + ($size:expr, $($ty:ty),+) => ( + $( + impl $crate::HeapSizeOf for $ty { + #[inline(always)] + fn heap_size_of_children(&self) -> usize { + $size + } + } + )+ + ); + ($size: expr, $($ty:ident<$($gen:ident),+>),+) => ( + $( + impl<$($gen: $crate::HeapSizeOf),+> $crate::HeapSizeOf for $ty<$($gen),+> { + #[inline(always)] + fn heap_size_of_children(&self) -> usize { + $size + } + } + )+ + ); +); + +known_heap_size!(0, char, str); +known_heap_size!(0, u8, u16, u32, u64, usize); +known_heap_size!(0, i8, i16, i32, i64, isize); +known_heap_size!(0, bool, f32, f64); +known_heap_size!(0, AtomicBool, AtomicIsize, AtomicUsize); +known_heap_size!(0, Ipv4Addr, Ipv6Addr); diff --git a/third_party/rust/heapsize-0.3.8/tests/tests.rs b/third_party/rust/heapsize-0.3.8/tests/tests.rs new file mode 100644 index 000000000000..97da29de7b7f --- /dev/null +++ b/third_party/rust/heapsize-0.3.8/tests/tests.rs @@ -0,0 +1,171 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#![cfg_attr(feature= "unstable", feature(alloc, heap_api, repr_simd))] + +extern crate heapsize; + +use heapsize::{HeapSizeOf, heap_size_of}; +use std::os::raw::c_void; + +pub const EMPTY: *mut () = 0x1 as *mut (); + +#[cfg(feature = "unstable")] +mod unstable { + extern crate alloc; + + use heapsize::{HeapSizeOf, heap_size_of}; + use std::os::raw::c_void; + + #[repr(C, simd)] + struct OverAligned(u64, u64, u64, u64); + + #[test] + fn check_empty() { + assert_eq!(::EMPTY, alloc::heap::EMPTY); + } + + #[cfg(not(target_os = "windows"))] + #[test] + fn test_alloc() { + unsafe { + // A 64 byte request is allocated exactly. + let x = alloc::heap::allocate(64, 0); + assert_eq!(heap_size_of(x as *const c_void), 64); + alloc::heap::deallocate(x, 64, 0); + + // A 255 byte request is rounded up to 256 bytes. + let x = alloc::heap::allocate(255, 0); + assert_eq!(heap_size_of(x as *const c_void), 256); + alloc::heap::deallocate(x, 255, 0); + + // A 1MiB request is allocated exactly. + let x = alloc::heap::allocate(1024 * 1024, 0); + assert_eq!(heap_size_of(x as *const c_void), 1024 * 1024); + alloc::heap::deallocate(x, 1024 * 1024, 0); + + // An overaligned 1MiB request is allocated exactly. + let x = alloc::heap::allocate(1024 * 1024, 32); + assert_eq!(heap_size_of(x as *const c_void), 1024 * 1024); + alloc::heap::deallocate(x, 1024 * 1024, 32); + } + } + + #[cfg(target_os = "windows")] + #[test] + fn test_alloc() { + unsafe { + // A 64 byte request is allocated exactly. + let x = alloc::heap::allocate(64, 0); + assert_eq!(heap_size_of(x as *const c_void), 64); + alloc::heap::deallocate(x, 64, 0); + + // A 255 byte request is allocated exactly. + let x = alloc::heap::allocate(255, 0); + assert_eq!(heap_size_of(x as *const c_void), 255); + alloc::heap::deallocate(x, 255, 0); + + // A 1MiB request is allocated exactly. + let x = alloc::heap::allocate(1024 * 1024, 0); + assert_eq!(heap_size_of(x as *const c_void), 1024 * 1024); + alloc::heap::deallocate(x, 1024 * 1024, 0); + + // An overaligned 1MiB request is over-allocated. + let x = alloc::heap::allocate(1024 * 1024, 32); + assert_eq!(heap_size_of(x as *const c_void), 1024 * 1024 + 32); + alloc::heap::deallocate(x, 1024 * 1024, 32); + } + } + + #[cfg(not(target_os = "windows"))] + #[test] + fn test_simd() { + let x = Box::new(OverAligned(0, 0, 0, 0)); + assert_eq!(unsafe { heap_size_of(&*x as *const _ as *const c_void) }, 32); + } + + #[cfg(target_os = "windows")] + #[test] + fn test_simd() { + let x = Box::new(OverAligned(0, 0, 0, 0)); + assert_eq!(unsafe { heap_size_of(&*x as *const _ as *const c_void) }, 32 + 32); + } + + #[test] + fn test_boxed_str() { + let x = "raclette".to_owned().into_boxed_str(); + assert_eq!(x.heap_size_of_children(), 8); + } +} + +#[test] +fn test_heap_size() { + + // Note: jemalloc often rounds up request sizes. However, it does not round up for request + // sizes of 8 and higher that are powers of two. We take advantage of knowledge here to make + // the sizes of various heap-allocated blocks predictable. + + //----------------------------------------------------------------------- + // Start with basic heap block measurement. + + unsafe { + // EMPTY is the special non-null address used to represent zero-size allocations. + assert_eq!(heap_size_of(EMPTY as *const c_void), 0); + } + + //----------------------------------------------------------------------- + // Test HeapSizeOf implementations for various built-in types. + + // Not on the heap; 0 bytes. + let x = 0i64; + assert_eq!(x.heap_size_of_children(), 0); + + // An i64 is 8 bytes. + let x = Box::new(0i64); + assert_eq!(x.heap_size_of_children(), 8); + + // An ascii string with 16 chars is 16 bytes in UTF-8. + let string = String::from("0123456789abcdef"); + assert_eq!(string.heap_size_of_children(), 16); + + let string_ref: (&String, ()) = (&string, ()); + assert_eq!(string_ref.heap_size_of_children(), 0); + + let slice: &str = &*string; + assert_eq!(slice.heap_size_of_children(), 0); + + // Not on the heap. + let x: Option = None; + assert_eq!(x.heap_size_of_children(), 0); + + // Not on the heap. + let x = Some(0i64); + assert_eq!(x.heap_size_of_children(), 0); + + // The `Some` is not on the heap, but the Box is. + let x = Some(Box::new(0i64)); + assert_eq!(x.heap_size_of_children(), 8); + + // Not on the heap. + let x = ::std::sync::Arc::new(0i64); + assert_eq!(x.heap_size_of_children(), 0); + + // The `Arc` is not on the heap, but the Box is. + let x = ::std::sync::Arc::new(Box::new(0i64)); + assert_eq!(x.heap_size_of_children(), 8); + + // Zero elements, no heap storage. + let x: Vec = vec![]; + assert_eq!(x.heap_size_of_children(), 0); + + // Four elements, 8 bytes per element. + let x = vec![0i64, 1i64, 2i64, 3i64]; + assert_eq!(x.heap_size_of_children(), 32); +} + +#[test] +fn test_boxed_slice() { + let x = vec![1i64, 2i64].into_boxed_slice(); + assert_eq!(x.heap_size_of_children(), 16) +} diff --git a/third_party/rust/heapsize/.cargo-checksum.json b/third_party/rust/heapsize/.cargo-checksum.json index 590b4466a86a..ebc5c28eb3e8 100644 --- a/third_party/rust/heapsize/.cargo-checksum.json +++ b/third_party/rust/heapsize/.cargo-checksum.json @@ -1 +1 @@ -{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"97503c8cf1fc53fd41e237662402477c0ab257225d25fe21470494a0b1bbec3c",".travis.yml":"1108708721703f4562646e1e7c6f6c924fa997835714bcc6a3ff8a58382134f1","Cargo.toml":"723e5918946fdb518ed1ad3e03ae9104b980cbe85bbee1989dc4197570ba6d73","README.md":"9a38b16bccde5db28c34d79134f02d2cdcbbab224b9a68ace93c5b85b5ef38f2","appveyor.yml":"130e820ab60abf8d08f3a91d4b0158e6a581c180385e12850113adb362eb158c","build.rs":"e13e88ed285a829256d3c6987563a663c37e335457d090125a3e19b1a97fec8e","src/lib.rs":"ab4e0a2e6d0ac700df5dbb7a2c83542cb82c94d4e46c632a4114fec93d6aba0a","tests/tests.rs":"f642da7b54b6cde55cf25fe84b2e6b27356d26b351d42a38e944b93e0c1fa24f"},"package":"5a376f7402b85be6e0ba504243ecbc0709c48019ecc6286d0540c2e359050c88"} \ No newline at end of file +{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"97503c8cf1fc53fd41e237662402477c0ab257225d25fe21470494a0b1bbec3c",".travis.yml":"ff4b4eeea4c3d6636633496f884b85e83e3613ad2bb84358b357f0cb8b8b1618","Cargo.toml":"f3a8db502210ebefe0565223738d41e1f6327bc283545789bea68fc93a599393","README.md":"9a38b16bccde5db28c34d79134f02d2cdcbbab224b9a68ace93c5b85b5ef38f2","appveyor.yml":"130e820ab60abf8d08f3a91d4b0158e6a581c180385e12850113adb362eb158c","build.rs":"e13e88ed285a829256d3c6987563a663c37e335457d090125a3e19b1a97fec8e","src/lib.rs":"024183eb6acfd9ebaa0b4bdc31aecd39dcb8bf92ab22228921f154b450b628a3","tests/tests.rs":"28ec35b89867f04be2b1a43116ee82b6f45e34efa53938e29c6727ad4da46ead"},"package":"4c7593b1522161003928c959c20a2ca421c68e940d63d75573316a009e48a6d4"} \ No newline at end of file diff --git a/third_party/rust/heapsize/.travis.yml b/third_party/rust/heapsize/.travis.yml index ef8e20c3ffe5..913ebf5633fa 100644 --- a/third_party/rust/heapsize/.travis.yml +++ b/third_party/rust/heapsize/.travis.yml @@ -15,5 +15,5 @@ notifications: script: - cargo test - "[ $TRAVIS_RUST_VERSION != nightly ] || cargo test --features unstable" - - "[ $TRAVIS_RUST_VERSION != nightly ] || cargo test --manifest-path derive/Cargo.toml" + - "[[ $TRAVIS_RUST_VERSION != nightly && $TRAVIS_RUST_VERSION != beta ]] || cargo test --manifest-path derive/Cargo.toml" diff --git a/third_party/rust/heapsize/Cargo.toml b/third_party/rust/heapsize/Cargo.toml index 62be5b66b37a..afb2135fae83 100644 --- a/third_party/rust/heapsize/Cargo.toml +++ b/third_party/rust/heapsize/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "heapsize" -version = "0.3.8" +version = "0.4.0" authors = [ "The Servo Project Developers" ] description = "Infrastructure for measuring the total runtime size of an object on the heap" license = "MPL-2.0" @@ -12,3 +12,6 @@ kernel32-sys = "0.2.1" [features] unstable = [] + +# https://github.com/servo/heapsize/issues/74 +flexible-tests = [] diff --git a/third_party/rust/heapsize/src/lib.rs b/third_party/rust/heapsize/src/lib.rs index d27789aa59e1..fc5900b8b5ac 100644 --- a/third_party/rust/heapsize/src/lib.rs +++ b/third_party/rust/heapsize/src/lib.rs @@ -15,7 +15,7 @@ use std::collections::{BTreeMap, HashSet, HashMap, LinkedList, VecDeque}; use std::hash::BuildHasher; use std::hash::Hash; use std::marker::PhantomData; -use std::mem::size_of; +use std::mem::{size_of, align_of}; use std::net::{Ipv4Addr, Ipv6Addr}; use std::os::raw::c_void; use std::sync::Arc; @@ -29,11 +29,11 @@ use std::rc::Rc; /// `unsafe` because the caller must ensure that the pointer is from jemalloc. /// FIXME: This probably interacts badly with custom allocators: /// https://doc.rust-lang.org/book/custom-allocators.html -pub unsafe fn heap_size_of(ptr: *const c_void) -> usize { - if ptr == 0x01 as *const c_void { +pub unsafe fn heap_size_of(ptr: *const T) -> usize { + if ptr as usize <= align_of::() { 0 } else { - heap_size_of_impl(ptr) + heap_size_of_impl(ptr as *const c_void) } } @@ -51,7 +51,7 @@ unsafe fn heap_size_of_impl(ptr: *const c_void) -> usize { } #[cfg(target_os = "windows")] -pub unsafe fn heap_size_of_impl(mut ptr: *const c_void) -> usize { +unsafe fn heap_size_of_impl(mut ptr: *const c_void) -> usize { let heap = GetProcessHeap(); if HeapValidate(heap, 0, ptr) == 0 { @@ -105,7 +105,7 @@ impl HeapSizeOf for [T] { impl HeapSizeOf for String { fn heap_size_of_children(&self) -> usize { unsafe { - heap_size_of(self.as_ptr() as *const c_void) + heap_size_of(self.as_ptr()) } } } @@ -116,6 +116,25 @@ impl<'a, T: ?Sized> HeapSizeOf for &'a T { } } +// The implementations for *mut T and *const T are designed for use cases like LinkedHashMap where +// you have a data structure which internally maintains an e.g. HashMap parameterized with raw +// pointers. We want to be able to rely on the standard HeapSizeOf implementation for `HashMap`, +// and can handle the contribution of the raw pointers manually. +// +// These have to return 0 since we don't know if the pointer is pointing to a heap allocation or +// even valid memory. +impl HeapSizeOf for *mut T { + fn heap_size_of_children(&self) -> usize { + 0 + } +} + +impl HeapSizeOf for *const T { + fn heap_size_of_children(&self) -> usize { + 0 + } +} + impl HeapSizeOf for Option { fn heap_size_of_children(&self) -> usize { match *self { @@ -212,7 +231,7 @@ impl HeapSizeOf for Cell { impl HeapSizeOf for Vec { fn heap_size_of_children(&self) -> usize { self.iter().fold( - unsafe { heap_size_of(self.as_ptr() as *const c_void) }, + unsafe { heap_size_of(self.as_ptr()) }, |n, elem| n + elem.heap_size_of_children()) } } @@ -231,7 +250,7 @@ impl HeapSizeOf for Vec> { // The fate of measuring Rc is still undecided, but we still want to measure // the space used for storing them. unsafe { - heap_size_of(self.as_ptr() as *const c_void) + heap_size_of(self.as_ptr()) } } } diff --git a/third_party/rust/heapsize/tests/tests.rs b/third_party/rust/heapsize/tests/tests.rs index 97da29de7b7f..afe5e4e147fd 100644 --- a/third_party/rust/heapsize/tests/tests.rs +++ b/third_party/rust/heapsize/tests/tests.rs @@ -9,13 +9,32 @@ extern crate heapsize; use heapsize::{HeapSizeOf, heap_size_of}; use std::os::raw::c_void; -pub const EMPTY: *mut () = 0x1 as *mut (); +const EMPTY: *mut () = 0x1 as *mut (); + +/// https://github.com/servo/heapsize/issues/74 +#[cfg(feature = "flexible-tests")] +macro_rules! assert_size { + ($actual: expr, $expected: expr) => { + { + let actual = $actual; + let expected = $expected; + assert!(actual >= expected, "expected {:?} >= {:?}", actual, expected) + } + } +} + +#[cfg(not(feature = "flexible-tests"))] +macro_rules! assert_size { + ($actual: expr, $expected: expr) => { + assert_eq!($actual, $expected) + } +} #[cfg(feature = "unstable")] mod unstable { extern crate alloc; - use heapsize::{HeapSizeOf, heap_size_of}; + use heapsize::heap_size_of; use std::os::raw::c_void; #[repr(C, simd)] @@ -32,22 +51,22 @@ mod unstable { unsafe { // A 64 byte request is allocated exactly. let x = alloc::heap::allocate(64, 0); - assert_eq!(heap_size_of(x as *const c_void), 64); + assert_size!(heap_size_of(x as *const c_void), 64); alloc::heap::deallocate(x, 64, 0); // A 255 byte request is rounded up to 256 bytes. let x = alloc::heap::allocate(255, 0); - assert_eq!(heap_size_of(x as *const c_void), 256); + assert_size!(heap_size_of(x as *const c_void), 256); alloc::heap::deallocate(x, 255, 0); // A 1MiB request is allocated exactly. let x = alloc::heap::allocate(1024 * 1024, 0); - assert_eq!(heap_size_of(x as *const c_void), 1024 * 1024); + assert_size!(heap_size_of(x as *const c_void), 1024 * 1024); alloc::heap::deallocate(x, 1024 * 1024, 0); // An overaligned 1MiB request is allocated exactly. let x = alloc::heap::allocate(1024 * 1024, 32); - assert_eq!(heap_size_of(x as *const c_void), 1024 * 1024); + assert_size!(heap_size_of(x as *const c_void), 1024 * 1024); alloc::heap::deallocate(x, 1024 * 1024, 32); } } @@ -58,22 +77,22 @@ mod unstable { unsafe { // A 64 byte request is allocated exactly. let x = alloc::heap::allocate(64, 0); - assert_eq!(heap_size_of(x as *const c_void), 64); + assert_size!(heap_size_of(x as *const c_void), 64); alloc::heap::deallocate(x, 64, 0); // A 255 byte request is allocated exactly. let x = alloc::heap::allocate(255, 0); - assert_eq!(heap_size_of(x as *const c_void), 255); + assert_size!(heap_size_of(x as *const c_void), 255); alloc::heap::deallocate(x, 255, 0); // A 1MiB request is allocated exactly. let x = alloc::heap::allocate(1024 * 1024, 0); - assert_eq!(heap_size_of(x as *const c_void), 1024 * 1024); + assert_size!(heap_size_of(x as *const c_void), 1024 * 1024); alloc::heap::deallocate(x, 1024 * 1024, 0); // An overaligned 1MiB request is over-allocated. let x = alloc::heap::allocate(1024 * 1024, 32); - assert_eq!(heap_size_of(x as *const c_void), 1024 * 1024 + 32); + assert_size!(heap_size_of(x as *const c_void), 1024 * 1024 + 32); alloc::heap::deallocate(x, 1024 * 1024, 32); } } @@ -82,21 +101,21 @@ mod unstable { #[test] fn test_simd() { let x = Box::new(OverAligned(0, 0, 0, 0)); - assert_eq!(unsafe { heap_size_of(&*x as *const _ as *const c_void) }, 32); + assert_size!(unsafe { heap_size_of(&*x as *const _ as *const c_void) }, 32); } #[cfg(target_os = "windows")] #[test] fn test_simd() { let x = Box::new(OverAligned(0, 0, 0, 0)); - assert_eq!(unsafe { heap_size_of(&*x as *const _ as *const c_void) }, 32 + 32); + assert_size!(unsafe { heap_size_of(&*x as *const _ as *const c_void) }, 32 + 32); } +} - #[test] - fn test_boxed_str() { - let x = "raclette".to_owned().into_boxed_str(); - assert_eq!(x.heap_size_of_children(), 8); - } +#[test] +fn test_boxed_str() { + let x = "raclette".to_owned().into_boxed_str(); + assert_size!(x.heap_size_of_children(), 8); } #[test] @@ -111,7 +130,7 @@ fn test_heap_size() { unsafe { // EMPTY is the special non-null address used to represent zero-size allocations. - assert_eq!(heap_size_of(EMPTY as *const c_void), 0); + assert_size!(heap_size_of(EMPTY as *const c_void), 0); } //----------------------------------------------------------------------- @@ -119,53 +138,53 @@ fn test_heap_size() { // Not on the heap; 0 bytes. let x = 0i64; - assert_eq!(x.heap_size_of_children(), 0); + assert_size!(x.heap_size_of_children(), 0); // An i64 is 8 bytes. let x = Box::new(0i64); - assert_eq!(x.heap_size_of_children(), 8); + assert_size!(x.heap_size_of_children(), 8); // An ascii string with 16 chars is 16 bytes in UTF-8. let string = String::from("0123456789abcdef"); - assert_eq!(string.heap_size_of_children(), 16); + assert_size!(string.heap_size_of_children(), 16); let string_ref: (&String, ()) = (&string, ()); - assert_eq!(string_ref.heap_size_of_children(), 0); + assert_size!(string_ref.heap_size_of_children(), 0); let slice: &str = &*string; - assert_eq!(slice.heap_size_of_children(), 0); + assert_size!(slice.heap_size_of_children(), 0); // Not on the heap. let x: Option = None; - assert_eq!(x.heap_size_of_children(), 0); + assert_size!(x.heap_size_of_children(), 0); // Not on the heap. let x = Some(0i64); - assert_eq!(x.heap_size_of_children(), 0); + assert_size!(x.heap_size_of_children(), 0); // The `Some` is not on the heap, but the Box is. let x = Some(Box::new(0i64)); - assert_eq!(x.heap_size_of_children(), 8); + assert_size!(x.heap_size_of_children(), 8); // Not on the heap. let x = ::std::sync::Arc::new(0i64); - assert_eq!(x.heap_size_of_children(), 0); + assert_size!(x.heap_size_of_children(), 0); // The `Arc` is not on the heap, but the Box is. let x = ::std::sync::Arc::new(Box::new(0i64)); - assert_eq!(x.heap_size_of_children(), 8); + assert_size!(x.heap_size_of_children(), 8); // Zero elements, no heap storage. let x: Vec = vec![]; - assert_eq!(x.heap_size_of_children(), 0); + assert_size!(x.heap_size_of_children(), 0); // Four elements, 8 bytes per element. let x = vec![0i64, 1i64, 2i64, 3i64]; - assert_eq!(x.heap_size_of_children(), 32); + assert_size!(x.heap_size_of_children(), 32); } #[test] fn test_boxed_slice() { let x = vec![1i64, 2i64].into_boxed_slice(); - assert_eq!(x.heap_size_of_children(), 16) + assert_size!(x.heap_size_of_children(), 16) } diff --git a/third_party/rust/plane-split/.cargo-checksum.json b/third_party/rust/plane-split/.cargo-checksum.json index 5a634341d372..a8c0929057de 100644 --- a/third_party/rust/plane-split/.cargo-checksum.json +++ b/third_party/rust/plane-split/.cargo-checksum.json @@ -1 +1 @@ -{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"f9b1ca6ae27d1c18215265024629a8960c31379f206d9ed20f64e0b2dcf79805",".travis.yml":"b76d49f66f842c652d40825c67791352364a6b6bbb7d8d1009f2ac79eb413e66","Cargo.toml":"3100d538a6cd25f84ae7d502f37ec09603f4a7fb902e46583df5bdbb50b73538","LICENSE":"b946744aeda89b467929585fe8eeb5461847695220c1b168fb375d8abd4ea3d0","README.md":"62f99334c17b451342fcea70eb1cc27b26612616b7c1a58fab50dd493f766f32","benches/split.rs":"49befe22321f34280106fdea53d93644b7757873407376247f86f9d55d09b4ab","src/bsp.rs":"83f1a84b4dda5668727eb27070f22676d1f77ab9eaff0c777b32651f75ecf5b6","src/lib.rs":"c6bdf6ac6db519b79b3dbf0d4805bbba3e761e0d5cf19b4ae5388a5b93ff7454","src/naive.rs":"c7e50de094d24b609f03e3dc9599bb040a6baef84bce93ffab7af7f049fb805b","tests/main.rs":"915d915c5ca82befef82f1604cc974b072238a8d69043341589d8dd569d412d3","tests/split.rs":"a4681a788f9a9a515d4084d97ba33406a54bc0725711ade9fc955348d1703368"},"package":"8b3624c9e5e728dcc6347bde5762406b0f0707bea527d585e8f7b6ac44fdd33a"} \ No newline at end of file +{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"f9b1ca6ae27d1c18215265024629a8960c31379f206d9ed20f64e0b2dcf79805",".travis.yml":"b76d49f66f842c652d40825c67791352364a6b6bbb7d8d1009f2ac79eb413e66","Cargo.toml":"6a8c18281f4854b2f184e335d2efb7702ed920f3e66adbe84ce2013215215068","LICENSE":"b946744aeda89b467929585fe8eeb5461847695220c1b168fb375d8abd4ea3d0","README.md":"62f99334c17b451342fcea70eb1cc27b26612616b7c1a58fab50dd493f766f32","benches/split.rs":"49befe22321f34280106fdea53d93644b7757873407376247f86f9d55d09b4ab","src/bsp.rs":"1bc961e97b47f6d918384858310c60a20f9490e11404a89f379a2ad6c5705071","src/lib.rs":"c7f52a46d9ebdb9c1346b39312110aaba75821297e5f446c81a8a25706d850f5","src/naive.rs":"c7e50de094d24b609f03e3dc9599bb040a6baef84bce93ffab7af7f049fb805b","tests/main.rs":"915d915c5ca82befef82f1604cc974b072238a8d69043341589d8dd569d412d3","tests/split.rs":"a4681a788f9a9a515d4084d97ba33406a54bc0725711ade9fc955348d1703368"},"package":"f00d5b0bef85e7e218329cde2f9b75784967c62c0cc9b7faa491d81c2d35eb2a"} \ No newline at end of file diff --git a/third_party/rust/plane-split/Cargo.toml b/third_party/rust/plane-split/Cargo.toml index 9b7605502152..3deb2ff616c8 100644 --- a/third_party/rust/plane-split/Cargo.toml +++ b/third_party/rust/plane-split/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "plane-split" -version = "0.3.0" +version = "0.4.1" description = "Plane splitting" authors = ["Dzmitry Malyshau "] license = "MPL-2.0" @@ -10,6 +10,6 @@ documentation = "https://docs.rs/plane-split" [dependencies] binary-space-partition = "0.1.2" -euclid = "0.11.2" +euclid = "0.13" log = "0.3" num-traits = {version = "0.1.37", default-features = false} diff --git a/third_party/rust/plane-split/src/bsp.rs b/third_party/rust/plane-split/src/bsp.rs index 83f191b4d7ae..e239490eed84 100644 --- a/third_party/rust/plane-split/src/bsp.rs +++ b/third_party/rust/plane-split/src/bsp.rs @@ -19,11 +19,11 @@ impl Plane for Polygon where match self.intersect(&plane) { Intersection::Coplanar if dist.approx_eq(&T::zero()) => { - debug!("\t\tcoplanar and matching"); + debug!("\t\tCoplanar and matching"); PlaneCut::Sibling(plane) } Intersection::Coplanar | Intersection::Outside => { - debug!("\t\tcoplanar at {:?}", dist); + debug!("\t\tCoplanar at {:?}", dist); if dist > T::zero() { PlaneCut::Cut { front: vec![plane], @@ -48,7 +48,7 @@ impl Plane for Polygon where back.push(sub) } } - debug!("\t\tcut across {:?} by {} in front and {} in back", + debug!("\t\tCut across {:?} by {} in front and {} in back", line, front.len(), back.len()); PlaneCut::Cut { diff --git a/third_party/rust/plane-split/src/lib.rs b/third_party/rust/plane-split/src/lib.rs index 79ee2d7dd81c..4fa29d1aefc9 100644 --- a/third_party/rust/plane-split/src/lib.rs +++ b/third_party/rust/plane-split/src/lib.rs @@ -298,20 +298,20 @@ impl Polygon where pub fn intersect(&self, other: &Self) -> Intersection> { if self.are_outside(&other.points) || other.are_outside(&self.points) { // one is completely outside the other - debug!("\t\toutside"); + debug!("\t\tOutside"); return Intersection::Outside } let cross_dir = self.normal.cross(other.normal); if cross_dir.dot(cross_dir) < T::approx_epsilon() { // polygons are co-planar - debug!("\t\tcoplanar"); + debug!("\t\tCoplanar"); return Intersection::Coplanar } let self_proj = self.project_on(&cross_dir); let other_proj = other.project_on(&cross_dir); if !self_proj.intersect(&other_proj) { // projections on the line don't intersect - debug!("\t\tprojection outside"); + debug!("\t\tProjection outside"); return Intersection::Outside } // compute any point on the intersection between planes diff --git a/toolkit/library/gtest/rust/Cargo.lock b/toolkit/library/gtest/rust/Cargo.lock index 6046160aa966..be5dd36320bf 100644 --- a/toolkit/library/gtest/rust/Cargo.lock +++ b/toolkit/library/gtest/rust/Cargo.lock @@ -290,6 +290,18 @@ dependencies = [ "serde 0.9.9 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "euclid" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.9.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "fnv" version = "1.0.5" @@ -386,6 +398,14 @@ dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "heapsize" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "idna" version = "0.1.2" @@ -623,11 +643,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "plane-split" -version = "0.3.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "binary-space-partition 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "euclid 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1072,7 +1092,7 @@ dependencies = [ [[package]] name = "webrender" -version = "0.39.0" +version = "0.40.0" dependencies = [ "app_units 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 1.0.0-alpha6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1082,7 +1102,7 @@ dependencies = [ "core-graphics 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "core-text 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "dwrote 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "euclid 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "freetype 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "gamma-lut 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1090,11 +1110,11 @@ dependencies = [ "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", - "plane-split 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "plane-split 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", - "webrender_traits 0.39.0", + "webrender_traits 0.40.0", ] [[package]] @@ -1102,15 +1122,15 @@ name = "webrender_bindings" version = "0.1.0" dependencies = [ "app_units 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "euclid 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "gleam 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "webrender 0.39.0", - "webrender_traits 0.39.0", + "webrender 0.40.0", + "webrender_traits 0.40.0", ] [[package]] name = "webrender_traits" -version = "0.39.0" +version = "0.40.0" dependencies = [ "app_units 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 1.0.0-alpha6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1118,7 +1138,7 @@ dependencies = [ "core-foundation 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "dwrote 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "euclid 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "gleam 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.9.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1175,6 +1195,7 @@ dependencies = [ "checksum dwrote 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "74114b6b49d6731835da7a28a3642651451e315f7f9b9d04e907e65a45681796" "checksum env_logger 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ed39959122ea027670b704fb70539f4286ddf4a49eefede23bf0b4b2a069ec03" "checksum euclid 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f5517462c626a893f3b027615e88d7102cc6dd3f7f1bcb90c7220fb1da4970b5" +"checksum euclid 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6083f113c422ff9cd855a1cf6cc8ec0903606c0eb43a0c6a0ced3bdc9731e4c1" "checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344" "checksum freetype 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fde23272c687e4570aefec06cb71174ec0f5284b725deac4e77ba2665d635faf" "checksum futures 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "55f0008e13fc853f79ea8fc86e931486860d4c4c156cdffb59fa5f7fa833660a" @@ -1184,6 +1205,7 @@ dependencies = [ "checksum gleam 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a86944a6a4d7f54507f8ee930192d971f18a7b1da526ff529b7a0d4043935380" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" "checksum heapsize 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "5a376f7402b85be6e0ba504243ecbc0709c48019ecc6286d0540c2e359050c88" +"checksum heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4c7593b1522161003928c959c20a2ca421c68e940d63d75573316a009e48a6d4" "checksum idna 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2233d4940b1f19f0418c158509cd7396b8d70a5db5705ce410914dc8fa603b37" "checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" @@ -1211,7 +1233,7 @@ dependencies = [ "checksum phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "6b07ffcc532ccc85e3afc45865469bf5d9e4ef5bfcf9622e3cfe80c2d275ec03" "checksum phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "07e24b0ca9643bdecd0632f2b3da6b1b89bbb0030e0b992afc1113b23a7bc2f2" "checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903" -"checksum plane-split 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8b3624c9e5e728dcc6347bde5762406b0f0707bea527d585e8f7b6ac44fdd33a" +"checksum plane-split 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f00d5b0bef85e7e218329cde2f9b75784967c62c0cc9b7faa491d81c2d35eb2a" "checksum precomputed-hash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf1fc3616b3ef726a847f2cd2388c646ef6a1f1ba4835c2629004da48184150" "checksum procedural-masquerade 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9f566249236c6ca4340f7ca78968271f0ed2b0f234007a61b66f9ecd0af09260" "checksum quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18c45c4854d6d1cf5d531db97c75880feb91c958b0720f4ec1057135fec358b3" diff --git a/toolkit/library/rust/Cargo.lock b/toolkit/library/rust/Cargo.lock index 2138f6644725..deb0d4bc13db 100644 --- a/toolkit/library/rust/Cargo.lock +++ b/toolkit/library/rust/Cargo.lock @@ -288,6 +288,18 @@ dependencies = [ "serde 0.9.9 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "euclid" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.9.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "fnv" version = "1.0.5" @@ -384,6 +396,14 @@ dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "heapsize" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "idna" version = "0.1.2" @@ -610,11 +630,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "plane-split" -version = "0.3.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "binary-space-partition 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "euclid 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1059,7 +1079,7 @@ dependencies = [ [[package]] name = "webrender" -version = "0.39.0" +version = "0.40.0" dependencies = [ "app_units 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 1.0.0-alpha6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1069,7 +1089,7 @@ dependencies = [ "core-graphics 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "core-text 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "dwrote 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "euclid 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "freetype 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "gamma-lut 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1077,11 +1097,11 @@ dependencies = [ "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", - "plane-split 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "plane-split 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", - "webrender_traits 0.39.0", + "webrender_traits 0.40.0", ] [[package]] @@ -1089,15 +1109,15 @@ name = "webrender_bindings" version = "0.1.0" dependencies = [ "app_units 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "euclid 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "gleam 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "webrender 0.39.0", - "webrender_traits 0.39.0", + "webrender 0.40.0", + "webrender_traits 0.40.0", ] [[package]] name = "webrender_traits" -version = "0.39.0" +version = "0.40.0" dependencies = [ "app_units 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 1.0.0-alpha6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1105,7 +1125,7 @@ dependencies = [ "core-foundation 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "dwrote 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "euclid 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "gleam 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.9.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1162,6 +1182,7 @@ dependencies = [ "checksum dwrote 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "74114b6b49d6731835da7a28a3642651451e315f7f9b9d04e907e65a45681796" "checksum env_logger 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ed39959122ea027670b704fb70539f4286ddf4a49eefede23bf0b4b2a069ec03" "checksum euclid 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f5517462c626a893f3b027615e88d7102cc6dd3f7f1bcb90c7220fb1da4970b5" +"checksum euclid 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6083f113c422ff9cd855a1cf6cc8ec0903606c0eb43a0c6a0ced3bdc9731e4c1" "checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344" "checksum freetype 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fde23272c687e4570aefec06cb71174ec0f5284b725deac4e77ba2665d635faf" "checksum futures 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "55f0008e13fc853f79ea8fc86e931486860d4c4c156cdffb59fa5f7fa833660a" @@ -1171,6 +1192,7 @@ dependencies = [ "checksum gleam 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a86944a6a4d7f54507f8ee930192d971f18a7b1da526ff529b7a0d4043935380" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" "checksum heapsize 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "5a376f7402b85be6e0ba504243ecbc0709c48019ecc6286d0540c2e359050c88" +"checksum heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4c7593b1522161003928c959c20a2ca421c68e940d63d75573316a009e48a6d4" "checksum idna 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2233d4940b1f19f0418c158509cd7396b8d70a5db5705ce410914dc8fa603b37" "checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" @@ -1198,7 +1220,7 @@ dependencies = [ "checksum phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "6b07ffcc532ccc85e3afc45865469bf5d9e4ef5bfcf9622e3cfe80c2d275ec03" "checksum phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "07e24b0ca9643bdecd0632f2b3da6b1b89bbb0030e0b992afc1113b23a7bc2f2" "checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903" -"checksum plane-split 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8b3624c9e5e728dcc6347bde5762406b0f0707bea527d585e8f7b6ac44fdd33a" +"checksum plane-split 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f00d5b0bef85e7e218329cde2f9b75784967c62c0cc9b7faa491d81c2d35eb2a" "checksum precomputed-hash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf1fc3616b3ef726a847f2cd2388c646ef6a1f1ba4835c2629004da48184150" "checksum procedural-masquerade 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9f566249236c6ca4340f7ca78968271f0ed2b0f234007a61b66f9ecd0af09260" "checksum quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18c45c4854d6d1cf5d531db97c75880feb91c958b0720f4ec1057135fec358b3"