Bug 1335525 - Add top-level webrender crates to gfx/. r=gfx
authorKartikaya Gupta <kgupta@mozilla.com>
Mon, 06 Feb 2017 11:42:52 -0500
changeset 340900 bafbb19be9a460d896e0d065843e5507610b6171
parent 340899 7f1b358fb17dfd982c5e18c34d5735cd481c7f7c
child 340901 7fa68c2685b2d3fe239e7118a55aae852e56beed
push id31320
push userkgupta@mozilla.com
push dateMon, 06 Feb 2017 16:51:55 +0000
treeherdermozilla-central@12c02bf624c4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgfx
bugs1335525
milestone54.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1335525 - Add top-level webrender crates to gfx/. r=gfx MozReview-Commit-ID: BXVNHlczLh5
gfx/webrender/Cargo.toml
gfx/webrender/build.rs
gfx/webrender/res/clip_shared.glsl
gfx/webrender/res/cs_blur.fs.glsl
gfx/webrender/res/cs_blur.glsl
gfx/webrender/res/cs_blur.vs.glsl
gfx/webrender/res/cs_box_shadow.fs.glsl
gfx/webrender/res/cs_box_shadow.glsl
gfx/webrender/res/cs_box_shadow.vs.glsl
gfx/webrender/res/cs_clip_image.fs.glsl
gfx/webrender/res/cs_clip_image.glsl
gfx/webrender/res/cs_clip_image.vs.glsl
gfx/webrender/res/cs_clip_rectangle.fs.glsl
gfx/webrender/res/cs_clip_rectangle.glsl
gfx/webrender/res/cs_clip_rectangle.vs.glsl
gfx/webrender/res/cs_text_run.fs.glsl
gfx/webrender/res/cs_text_run.glsl
gfx/webrender/res/cs_text_run.vs.glsl
gfx/webrender/res/debug_color.fs.glsl
gfx/webrender/res/debug_color.vs.glsl
gfx/webrender/res/debug_font.fs.glsl
gfx/webrender/res/debug_font.vs.glsl
gfx/webrender/res/prim_shared.glsl
gfx/webrender/res/ps_angle_gradient.fs.glsl
gfx/webrender/res/ps_angle_gradient.glsl
gfx/webrender/res/ps_angle_gradient.vs.glsl
gfx/webrender/res/ps_blend.fs.glsl
gfx/webrender/res/ps_blend.glsl
gfx/webrender/res/ps_blend.vs.glsl
gfx/webrender/res/ps_border.fs.glsl
gfx/webrender/res/ps_border.glsl
gfx/webrender/res/ps_border.vs.glsl
gfx/webrender/res/ps_box_shadow.fs.glsl
gfx/webrender/res/ps_box_shadow.glsl
gfx/webrender/res/ps_box_shadow.vs.glsl
gfx/webrender/res/ps_cache_image.fs.glsl
gfx/webrender/res/ps_cache_image.glsl
gfx/webrender/res/ps_cache_image.vs.glsl
gfx/webrender/res/ps_clear.fs.glsl
gfx/webrender/res/ps_clear.glsl
gfx/webrender/res/ps_clear.vs.glsl
gfx/webrender/res/ps_composite.fs.glsl
gfx/webrender/res/ps_composite.glsl
gfx/webrender/res/ps_composite.vs.glsl
gfx/webrender/res/ps_gradient.fs.glsl
gfx/webrender/res/ps_gradient.glsl
gfx/webrender/res/ps_gradient.vs.glsl
gfx/webrender/res/ps_image.fs.glsl
gfx/webrender/res/ps_image.glsl
gfx/webrender/res/ps_image.vs.glsl
gfx/webrender/res/ps_radial_gradient.fs.glsl
gfx/webrender/res/ps_radial_gradient.glsl
gfx/webrender/res/ps_radial_gradient.vs.glsl
gfx/webrender/res/ps_rectangle.fs.glsl
gfx/webrender/res/ps_rectangle.glsl
gfx/webrender/res/ps_rectangle.vs.glsl
gfx/webrender/res/ps_text_run.fs.glsl
gfx/webrender/res/ps_text_run.glsl
gfx/webrender/res/ps_text_run.vs.glsl
gfx/webrender/res/ps_yuv_image.fs.glsl
gfx/webrender/res/ps_yuv_image.glsl
gfx/webrender/res/ps_yuv_image.vs.glsl
gfx/webrender/res/shared.glsl
gfx/webrender/res/shared_other.glsl
gfx/webrender/src/batch_builder.rs
gfx/webrender/src/debug_colors.rs
gfx/webrender/src/debug_font_data.rs
gfx/webrender/src/debug_render.rs
gfx/webrender/src/device.rs
gfx/webrender/src/frame.rs
gfx/webrender/src/freelist.rs
gfx/webrender/src/geometry.rs
gfx/webrender/src/gpu_store.rs
gfx/webrender/src/internal_types.rs
gfx/webrender/src/layer.rs
gfx/webrender/src/lib.rs
gfx/webrender/src/mask_cache.rs
gfx/webrender/src/platform/macos/font.rs
gfx/webrender/src/platform/unix/font.rs
gfx/webrender/src/platform/windows/font.rs
gfx/webrender/src/prim_store.rs
gfx/webrender/src/profiler.rs
gfx/webrender/src/record.rs
gfx/webrender/src/render_backend.rs
gfx/webrender/src/renderer.rs
gfx/webrender/src/resource_cache.rs
gfx/webrender/src/scene.rs
gfx/webrender/src/scroll_tree.rs
gfx/webrender/src/spring.rs
gfx/webrender/src/texture_cache.rs
gfx/webrender/src/tiling.rs
gfx/webrender/src/util.rs
gfx/webrender/tests/angle_shader_validation.rs
gfx/webrender/tests/bug_124.html
gfx/webrender/tests/bug_134.html
gfx/webrender/tests/bug_137.html
gfx/webrender/tests/bug_143.html
gfx/webrender/tests/bug_159.html
gfx/webrender/tests/bug_166.html
gfx/webrender/tests/bug_176.html
gfx/webrender/tests/bug_177.html
gfx/webrender/tests/bug_178.html
gfx/webrender/tests/bug_203a.html
gfx/webrender/tests/bug_203b.html
gfx/webrender/tests/bug_servo_10136.html
gfx/webrender/tests/bug_servo_10164.html
gfx/webrender/tests/bug_servo_10307.html
gfx/webrender/tests/bug_servo_11358.html
gfx/webrender/tests/bug_servo_9983a.html
gfx/webrender/tests/color_pattern_1.png
gfx/webrender/tests/color_pattern_2.png
gfx/webrender/tests/fixed-position.html
gfx/webrender/tests/mix-blend-mode-2.html
gfx/webrender/tests/mix-blend-mode.html
gfx/webrender/tests/nav-1.html
gfx/webrender/tests/nav-2.html
gfx/webrender_bindings/Cargo.toml
gfx/webrender_bindings/src/bindings.rs
gfx/webrender_bindings/src/lib.rs
gfx/webrender_traits/Cargo.toml
gfx/webrender_traits/build.rs
gfx/webrender_traits/src/api.rs
gfx/webrender_traits/src/channel.rs
gfx/webrender_traits/src/channel_ipc.rs
gfx/webrender_traits/src/channel_mpsc.rs
gfx/webrender_traits/src/display_item.rs
gfx/webrender_traits/src/display_list.rs
gfx/webrender_traits/src/lib.rs
gfx/webrender_traits/src/stacking_context.rs
gfx/webrender_traits/src/types.rs
gfx/webrender_traits/src/units.rs
gfx/webrender_traits/src/webgl.rs
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/Cargo.toml
@@ -0,0 +1,44 @@
+[package]
+name = "webrender"
+version = "0.11.1"
+authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
+license = "MPL-2.0"
+repository = "https://github.com/servo/webrender"
+build = "build.rs"
+workspace = ".."
+
+[features]
+default = ["codegen", "freetype-lib"]
+codegen = ["webrender_traits/codegen"]
+freetype-lib = ["freetype/servo-freetype-sys"]
+serde_derive = ["webrender_traits/serde_derive"]
+
+[dependencies]
+app_units = "0.3"
+bincode = "0.6"
+bit-set = "0.4"
+byteorder = "0.5"
+euclid = "0.10.3"
+fnv="1.0"
+gleam = "0.2.30"
+lazy_static = "0.2"
+log = "0.3"
+num-traits = "0.1.32"
+offscreen_gl_context = {version = "0.5", features = ["serde_serialization", "osmesa"]}
+time = "0.1"
+threadpool = "1.3.2"
+webrender_traits = {path = "../webrender_traits", default-features = false}
+bitflags = "0.7"
+
+[dev-dependencies]
+angle = {git = "https://github.com/servo/angle", branch = "servo"}
+
+[target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies]
+freetype = { version = "0.2", default-features = false }
+
+[target.'cfg(target_os = "windows")'.dependencies]
+dwrote = "0.1.5"
+
+[target.'cfg(target_os = "macos")'.dependencies]
+core-graphics = "0.5.0"
+core-text = "2.0"
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/build.rs
@@ -0,0 +1,54 @@
+/* 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 std::env;
+use std::path::{Path, PathBuf};
+use std::io::prelude::*;
+use std::fs::{canonicalize, read_dir, File};
+
+fn write_shaders(glsl_files: Vec<PathBuf>, shader_file_path: &Path) {
+    let mut shader_file = File::create(shader_file_path).unwrap();
+
+    write!(shader_file, "/// AUTO GENERATED BY build.rs\n\n").unwrap();
+    write!(shader_file, "use std::collections::HashMap;\n").unwrap();
+    write!(shader_file, "lazy_static! {{\n").unwrap();
+    write!(shader_file, "  pub static ref SHADERS: HashMap<&'static str, &'static str> = {{\n").unwrap();
+    write!(shader_file, "    let mut h = HashMap::with_capacity({});\n", glsl_files.len()).unwrap();
+    for glsl in glsl_files {
+        let shader_name = glsl.file_name().unwrap().to_str().unwrap();
+        // strip .glsl
+        let shader_name = shader_name.replace(".glsl", "");
+        let full_path = canonicalize(&glsl).unwrap();
+        let full_name = full_path.as_os_str().to_str().unwrap();
+        // if someone is building on a network share, I'm sorry.
+        let full_name = full_name.replace("\\\\?\\", "");
+        let full_name = full_name.replace("\\", "/");
+        write!(shader_file, "    h.insert(\"{}\", include_str!(\"{}\"));\n",
+               shader_name, full_name).unwrap();
+    }
+    write!(shader_file, "    h\n").unwrap(); 
+    write!(shader_file, "  }};\n").unwrap(); 
+    write!(shader_file, "}}\n").unwrap(); 
+}
+
+fn main() {
+    let out_dir = env::var("OUT_DIR").unwrap_or("out".to_owned());
+
+    let shaders_file = Path::new(&out_dir).join("shaders.rs");
+    let mut glsl_files = vec![];
+
+    println!("cargo:rerun-if-changed=res");
+    let res_dir = Path::new("res");
+    for entry in read_dir(res_dir).unwrap() {
+        let entry = entry.unwrap();
+        let path = entry.path();
+
+        if entry.file_name().to_str().unwrap().ends_with(".glsl") {
+            println!("cargo:rerun-if-changed={}", path.display());
+            glsl_files.push(path.to_owned());
+        }
+    }
+
+    write_shaders(glsl_files, &shaders_file);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/clip_shared.glsl
@@ -0,0 +1,91 @@
+#line 1
+/* 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/. */
+
+#ifdef WR_VERTEX_SHADER
+
+#define SEGMENT_ALL         0
+#define SEGMENT_CORNER_TL   1
+#define SEGMENT_CORNER_TR   2
+#define SEGMENT_CORNER_BL   3
+#define SEGMENT_CORNER_BR   4
+
+in int aClipRenderTaskIndex;
+in int aClipLayerIndex;
+in int aClipDataIndex;
+in int aClipSegmentIndex;
+
+struct CacheClipInstance {
+    int render_task_index;
+    int layer_index;
+    int data_index;
+    int segment_index;
+};
+
+CacheClipInstance fetch_clip_item(int index) {
+    CacheClipInstance cci;
+
+    cci.render_task_index = aClipRenderTaskIndex;
+    cci.layer_index = aClipLayerIndex;
+    cci.data_index = aClipDataIndex;
+    cci.segment_index = aClipSegmentIndex;
+
+    return cci;
+}
+
+// The transformed vertex function that always covers the whole clip area,
+// which is the intersection of all clip instances of a given primitive
+TransformVertexInfo write_clip_tile_vertex(vec4 local_clip_rect,
+                                           Layer layer,
+                                           ClipArea area,
+                                           int segment_index) {
+    vec2 lp0_base = local_clip_rect.xy;
+    vec2 lp1_base = local_clip_rect.xy + local_clip_rect.zw;
+
+    vec2 lp0 = clamp_rect(lp0_base, layer.local_clip_rect);
+    vec2 lp1 = clamp_rect(lp1_base, layer.local_clip_rect);
+    vec4 clipped_local_rect = vec4(lp0, lp1 - lp0);
+
+    vec2 outer_p0 = area.screen_origin_target_index.xy;
+    vec2 outer_p1 = outer_p0 + area.task_bounds.zw - area.task_bounds.xy;
+    vec2 inner_p0 = area.inner_rect.xy;
+    vec2 inner_p1 = area.inner_rect.zw;
+
+    vec2 p0, p1;
+    switch (segment_index) {
+        case SEGMENT_ALL:
+            p0 = outer_p0;
+            p1 = outer_p1;
+            break;
+        case SEGMENT_CORNER_TL:
+            p0 = outer_p0;
+            p1 = inner_p0;
+            break;
+        case SEGMENT_CORNER_BL:
+            p0 = vec2(outer_p0.x, outer_p1.y);
+            p1 = vec2(inner_p0.x, inner_p1.y);
+            break;
+        case SEGMENT_CORNER_TR:
+            p0 = vec2(outer_p1.x, outer_p1.y);
+            p1 = vec2(inner_p1.x, inner_p1.y);
+            break;
+        case SEGMENT_CORNER_BR:
+            p0 = vec2(outer_p1.x, outer_p0.y);
+            p1 = vec2(inner_p1.x, inner_p0.y);
+            break;
+    }
+
+    vec2 actual_pos = mix(p0, p1, aPosition.xy);
+
+    vec4 layer_pos = get_layer_pos(actual_pos / uDevicePixelRatio, layer);
+
+    // compute the point position in side the layer, in CSS space
+    vec2 vertex_pos = actual_pos + area.task_bounds.xy - area.screen_origin_target_index.xy;
+
+    gl_Position = uTransform * vec4(vertex_pos, 0.0, 1);
+
+    return TransformVertexInfo(layer_pos.xyw, actual_pos, clipped_local_rect);
+}
+
+#endif //WR_VERTEX_SHADER
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/cs_blur.fs.glsl
@@ -0,0 +1,40 @@
+#line 1
+/* 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/. */
+
+// TODO(gw): Write a fast path blur that handles smaller blur radii
+//           with a offset / weight uniform table and a constant
+//           loop iteration count!
+
+// TODO(gw): Make use of the bilinear sampling trick to reduce
+//           the number of texture fetches needed for a gaussian blur.
+
+float gauss(float x, float sigma) {
+    return (1.0 / sqrt(6.283185307179586 * sigma * sigma)) * exp(-(x * x) / (2.0 * sigma * sigma));
+}
+
+void main(void) {
+    vec4 cache_sample = texture(sCache, vUv);
+    vec4 color = vec4(cache_sample.rgb, 1.0) * (cache_sample.a * gauss(0.0, vSigma));
+
+    for (int i=1 ; i < vBlurRadius ; ++i) {
+        vec2 offset = vec2(float(i)) * vOffsetScale;
+
+        vec2 st0 = clamp(vUv.xy + offset, vUvRect.xy, vUvRect.zw);
+        vec4 color0 = texture(sCache, vec3(st0, vUv.z));
+
+        vec2 st1 = clamp(vUv.xy - offset, vUvRect.xy, vUvRect.zw);
+        vec4 color1 = texture(sCache, vec3(st1, vUv.z));
+
+        // Alpha must be premultiplied in order to properly blur the alpha channel.
+        float weight = gauss(float(i), vSigma);
+        color += vec4(color0.rgb * color0.a, color0.a) * weight;
+        color += vec4(color1.rgb * color1.a, color1.a) * weight;
+    }
+
+    // Unpremultiply the alpha.
+    color.rgb /= color.a;
+
+    oFragColor = color;
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/cs_blur.glsl
@@ -0,0 +1,10 @@
+#line 1
+/* 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/. */
+
+varying vec3 vUv;
+flat varying vec4 vUvRect;
+flat varying vec2 vOffsetScale;
+flat varying float vSigma;
+flat varying int vBlurRadius;
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/cs_blur.vs.glsl
@@ -0,0 +1,65 @@
+#line 1
+/* 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/. */
+
+// Applies a separable gaussian blur in one direction, as specified
+// by the dir field in the blur command.
+
+#define DIR_HORIZONTAL  0
+#define DIR_VERTICAL    1
+
+in int aBlurRenderTaskIndex;
+in int aBlurSourceTaskIndex;
+in int aBlurDirection;
+
+struct BlurCommand {
+    int task_id;
+    int src_task_id;
+    int dir;
+};
+
+BlurCommand fetch_blur() {
+    BlurCommand blur;
+
+    blur.task_id = aBlurRenderTaskIndex;
+    blur.src_task_id = aBlurSourceTaskIndex;
+    blur.dir = aBlurDirection;
+
+    return blur;
+}
+
+void main(void) {
+    BlurCommand cmd = fetch_blur();
+    RenderTaskData task = fetch_render_task(cmd.task_id);
+    RenderTaskData src_task = fetch_render_task(cmd.src_task_id);
+
+    vec4 local_rect = task.data0;
+
+    vec2 pos = mix(local_rect.xy,
+                   local_rect.xy + local_rect.zw,
+                   aPosition.xy);
+
+    vec2 texture_size = vec2(textureSize(sCache, 0).xy);
+    vUv.z = src_task.data1.x;
+    vBlurRadius = int(task.data1.y);
+    vSigma = task.data1.y * 0.5;
+
+    switch (cmd.dir) {
+        case DIR_HORIZONTAL:
+            vOffsetScale = vec2(1.0 / texture_size.x, 0.0);
+            break;
+        case DIR_VERTICAL:
+            vOffsetScale = vec2(0.0, 1.0 / texture_size.y);
+            break;
+    }
+
+    vUvRect = vec4(src_task.data0.xy, src_task.data0.xy + src_task.data0.zw);
+    vUvRect /= texture_size.xyxy;
+
+    vec2 uv0 = src_task.data0.xy / texture_size;
+    vec2 uv1 = (src_task.data0.xy + src_task.data0.zw) / texture_size;
+    vUv.xy = mix(uv0, uv1, aPosition.xy);
+
+    gl_Position = uTransform * vec4(pos, 0.0, 1.0);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/cs_box_shadow.fs.glsl
@@ -0,0 +1,148 @@
+#line 1
+/* 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/. */
+
+// See http://asciimath.org to render the equations here.
+
+// The Gaussian function used for blurring:
+//
+//     G_sigma(x) = 1/sqrt(2 pi sigma^2) e^(-x^2/(2 sigma^2))
+float gauss(float x, float sigma) {
+    float sigmaPow2 = sigma * sigma;
+    return 1.0 / sqrt(6.283185307179586 * sigmaPow2) * exp(-(x * x) / (2.0 * sigmaPow2));
+}
+
+// An approximation of the error function, which is related to the integral of the Gaussian
+// function:
+//
+//     "erf"(x) = 2/sqrt(pi) int_0^x e^(-t^2) dt
+//              ~~ 1 - 1 / (1 + a_1 x + a_2 x^2 + a_3 x^3 + a_4 x^4)^4
+//
+// where:
+//
+//     a_1 = 0.278393, a_2 = 0.230389, a_3 = 0.000972, a_4 = 0.078108
+//
+// This approximation is accurate to `5 xx 10^-4`, more than accurate enough for our purposes.
+//
+// See: https://en.wikipedia.org/wiki/Error_function#Approximation_with_elementary_functions
+float erf(float x) {
+    bool negative = x < 0.0;
+    if (negative)
+        x = -x;
+    float x2 = x * x;
+    float x3 = x2 * x;
+    float x4 = x2 * x2;
+    float denom = 1.0 + 0.278393 * x + 0.230389 * x2 + 0.000972 * x3 + 0.078108 * x4;
+    float result = 1.0 - 1.0 / (denom * denom * denom * denom);
+    return negative ? -result : result;
+}
+
+// A useful helper for calculating integrals of the Gaussian function via the error function:
+//
+//      "erf"_sigma(x) = 2 int 1/sqrt(2 pi sigma^2) e^(-x^2/(2 sigma^2)) dx
+//                     = "erf"(x/(sigma sqrt(2)))
+float erfSigma(float x, float sigma) {
+    return erf(x / (sigma * 1.4142135623730951));
+}
+
+// Returns the blurred color value from the box itself (not counting any rounded corners). `p_0` is
+// the vector distance to the top left corner of the box; `p_1` is the vector distance to its
+// bottom right corner.
+//
+//      "colorFromRect"_sigma(p_0, p_1)
+//          = int_{p_{0_y}}^{p_{1_y}} int_{p_{1_x}}^{p_{0_x}} G_sigma(y) G_sigma(x) dx dy
+//          = 1/4 ("erf"_sigma(p_{1_x}) - "erf"_sigma(p_{0_x}))
+//              ("erf"_sigma(p_{1_y}) - "erf"_sigma(p_{0_y}))
+float colorFromRect(vec2 p0, vec2 p1, float sigma) {
+    return (erfSigma(p1.x, sigma) - erfSigma(p0.x, sigma)) *
+        (erfSigma(p1.y, sigma) - erfSigma(p0.y, sigma)) / 4.0;
+}
+
+// Returns the `x` coordinate on the ellipse with the given radii for the given `y` coordinate:
+//
+//      "ellipsePoint"(y, y_0, a, b) = a sqrt(1 - ((y - y_0) / b)^2)
+float ellipsePoint(float y, float y0, vec2 radii) {
+    float bStep = (y - y0) / radii.y;
+    return radii.x * sqrt(1.0 - bStep * bStep);
+}
+
+// A helper function to compute the value that needs to be subtracted to accommodate the border
+// corners.
+//
+//     "colorCutout"_sigma(x_{0_l}, x_{0_r}, y_0, y_{min}, y_{max}, a, b)
+//          = int_{y_{min}}^{y_{max}}
+//              int_{x_{0_r} + "ellipsePoint"(y, y_0, a, b)}^{x_{0_r} + a} G_sigma(y) G_sigma(x) dx
+//              + int_{x_{0_l} - a}^{x_{0_l} - "ellipsePoint"(y, y_0, a, b)} G_sigma(y) G_sigma(x)
+//                  dx dy
+//          = int_{y_{min}}^{y_{max}} 1/2 G_sigma(y)
+//              ("erf"_sigma(x_{0_r} + a) - "erf"_sigma(x_{0_r} + "ellipsePoint"(y, y_0, a, b)) +
+//               "erf"_sigma(x_{0_l} - "ellipsePoint"(y, y_0, a, b)) - "erf"_sigma(x_{0_l} - a))
+//
+// with the outer integral evaluated numerically.
+float colorCutoutGeneral(float x0l,
+                         float x0r,
+                         float y0,
+                         float yMin,
+                         float yMax,
+                         vec2 radii,
+                         float sigma) {
+    float sum = 0.0;
+    for (float y = yMin; y <= yMax; y += 1.0) {
+        float xEllipsePoint = ellipsePoint(y, y0, radii);
+        sum += gauss(y, sigma) *
+            (erfSigma(x0r + radii.x, sigma) - erfSigma(x0r + xEllipsePoint, sigma) +
+             erfSigma(x0l - xEllipsePoint, sigma) - erfSigma(x0l - radii.x, sigma));
+    }
+    return sum / 2.0;
+}
+
+// The value that needs to be subtracted to accommodate the top border corners.
+float colorCutoutTop(float x0l, float x0r, float y0, vec2 radii, float sigma) {
+    return colorCutoutGeneral(x0l, x0r, y0, y0, y0 + radii.y, radii, sigma);
+}
+
+// The value that needs to be subtracted to accommodate the bottom border corners.
+float colorCutoutBottom(float x0l, float x0r, float y0, vec2 radii, float sigma) {
+    return colorCutoutGeneral(x0l, x0r, y0, y0 - radii.y, y0, radii, sigma);
+}
+
+// The blurred color value for the point at `pos` with the top left corner of the box at
+// `p_{0_"rect"}` and the bottom right corner of the box at `p_{1_"rect"}`.
+float color(vec2 pos, vec2 p0Rect, vec2 p1Rect, vec2 radii, float sigma) {
+    // Compute the vector distances `p_0` and `p_1`.
+    vec2 p0 = p0Rect - pos, p1 = p1Rect - pos;
+
+    // Compute the basic color `"colorFromRect"_sigma(p_0, p_1)`. This is all we have to do if
+    // the box is unrounded.
+    float cRect = colorFromRect(p0, p1, sigma);
+    if (radii.x == 0.0 || radii.y == 0.0)
+        return cRect;
+
+    // Compute the inner corners of the box, taking border radii into account: `x_{0_l}`,
+    // `y_{0_t}`, `x_{0_r}`, and `y_{0_b}`.
+    float x0l = p0.x + radii.x;
+    float y0t = p1.y - radii.y;
+    float x0r = p1.x - radii.x;
+    float y0b = p0.y + radii.y;
+
+    // Compute the final color:
+    //
+    //     "colorFromRect"_sigma(p_0, p_1) -
+    //          ("colorCutoutTop"_sigma(x_{0_l}, x_{0_r}, y_{0_t}, a, b) +
+    //           "colorCutoutBottom"_sigma(x_{0_l}, x_{0_r}, y_{0_b}, a, b))
+    float cCutoutTop = colorCutoutTop(x0l, x0r, y0t, radii, sigma);
+    float cCutoutBottom = colorCutoutBottom(x0l, x0r, y0b, radii, sigma);
+    return cRect - (cCutoutTop + cCutoutBottom);
+}
+
+void main(void) {
+    vec2 pos = vPos.xy;
+    vec2 p0Rect = vBoxShadowRect.xy, p1Rect = vBoxShadowRect.zw;
+    vec2 radii = vBorderRadii.xy;
+    float sigma = vBlurRadius / 2.0;
+    float value = color(pos, p0Rect, p1Rect, radii, sigma);
+
+    value = max(value, 0.0);
+    oFragColor = vec4(1.0, 1.0, 1.0, vInverted == 1.0 ? 1.0 - value : value);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/cs_box_shadow.glsl
@@ -0,0 +1,10 @@
+#line 1
+/* 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/. */
+
+varying vec2 vPos;
+flat varying vec2 vBorderRadii;
+flat varying float vBlurRadius;
+flat varying vec4 vBoxShadowRect;
+flat varying float vInverted;
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/cs_box_shadow.vs.glsl
@@ -0,0 +1,31 @@
+#line 1
+/* 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/. */
+
+void main(void) {
+    CachePrimitiveInstance cpi = fetch_cache_instance();
+    RenderTaskData task = fetch_render_task(cpi.render_task_index);
+    BoxShadow bs = fetch_boxshadow(cpi.specific_prim_index);
+
+    vec2 p0 = task.data0.xy;
+    vec2 p1 = p0 + task.data0.zw;
+
+    vec2 pos = mix(p0, p1, aPosition.xy);
+
+    vBorderRadii = bs.border_radius_edge_size_blur_radius_inverted.xx;
+    vBlurRadius = bs.border_radius_edge_size_blur_radius_inverted.z;
+    vInverted = bs.border_radius_edge_size_blur_radius_inverted.w;
+    vBoxShadowRect = vec4(bs.bs_rect.xy, bs.bs_rect.xy + bs.bs_rect.zw);
+
+    // The fragment shader expects logical units, beginning at where the
+    // blur radius begins.
+    // The first path of the equation gets the virtual position in
+    // logical pixels within the patch rectangle (accounting for
+    // bilinear offset). Then we add the start position of the
+    // box shadow rect and subtract the blur radius to get the
+    // virtual coordinates that the FS expects.
+    vPos = (pos - 1.0 - p0) / uDevicePixelRatio + bs.bs_rect.xy - vec2(2.0 * vBlurRadius);
+
+    gl_Position = uTransform * vec4(pos, 0.0, 1.0);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/cs_clip_image.fs.glsl
@@ -0,0 +1,16 @@
+/* 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/. */
+
+void main(void) {
+    float alpha = 1.f;
+    vec2 local_pos = init_transform_fs(vPos, vLocalRect, alpha);
+
+    bool repeat_mask = false; //TODO
+    vec2 clamped_mask_uv = repeat_mask ? fract(vClipMaskUv.xy) :
+        clamp(vClipMaskUv.xy, vec2(0.0, 0.0), vec2(1.0, 1.0));
+    vec2 source_uv = clamped_mask_uv * vClipMaskUvRect.zw + vClipMaskUvRect.xy;
+    float clip_alpha = texture(sMask, source_uv).r; //careful: texture has type A8
+
+    oFragColor = vec4(min(alpha, clip_alpha), 1.0, 1.0, 1.0);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/cs_clip_image.glsl
@@ -0,0 +1,9 @@
+#line 1
+
+/* 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/. */
+
+varying vec3 vPos;
+flat varying vec4 vLocalRect;
+flat varying vec4 vClipMaskUvRect;
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/cs_clip_image.vs.glsl
@@ -0,0 +1,39 @@
+#line 1
+/* 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/. */
+
+struct ImageMaskData {
+    vec4 uv_rect;
+    vec4 local_rect;
+};
+
+ImageMaskData fetch_mask_data(int index) {
+    ImageMaskData info;
+
+    ivec2 uv = get_fetch_uv_2(index);
+
+    info.uv_rect = texelFetchOffset(sData32, uv, 0, ivec2(0, 0));
+    info.local_rect = texelFetchOffset(sData32, uv, 0, ivec2(1, 0));
+
+    return info;
+}
+
+void main(void) {
+    CacheClipInstance cci = fetch_clip_item(gl_InstanceID);
+    ClipArea area = fetch_clip_area(cci.render_task_index);
+    Layer layer = fetch_layer(cci.layer_index);
+    ImageMaskData mask = fetch_mask_data(cci.data_index);
+    vec4 local_rect = mask.local_rect;
+
+    TransformVertexInfo vi = write_clip_tile_vertex(local_rect,
+                                                    layer,
+                                                    area,
+                                                    cci.segment_index);
+    vLocalRect = vi.clipped_local_rect;
+    vPos = vi.local_pos;
+
+    vClipMaskUv = vec3((vPos.xy / vPos.z - local_rect.xy) / local_rect.zw, 0.0);
+    vec2 texture_size = vec2(textureSize(sMask, 0));
+    vClipMaskUvRect = mask.uv_rect / texture_size.xyxy;
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/cs_clip_rectangle.fs.glsl
@@ -0,0 +1,44 @@
+/* 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/. */
+
+float rounded_rect(vec2 pos) {
+    vec2 ref_tl = vClipRect.xy + vec2( vClipRadius.x,  vClipRadius.x);
+    vec2 ref_tr = vClipRect.zy + vec2(-vClipRadius.y,  vClipRadius.y);
+    vec2 ref_br = vClipRect.zw + vec2(-vClipRadius.z, -vClipRadius.z);
+    vec2 ref_bl = vClipRect.xw + vec2( vClipRadius.w, -vClipRadius.w);
+
+    float d_tl = distance(pos, ref_tl);
+    float d_tr = distance(pos, ref_tr);
+    float d_br = distance(pos, ref_br);
+    float d_bl = distance(pos, ref_bl);
+
+    float pixels_per_fragment = length(fwidth(pos.xy));
+    float nudge = 0.5 * pixels_per_fragment;
+    vec4 distances = vec4(d_tl, d_tr, d_br, d_bl) - vClipRadius + nudge;
+
+    bvec4 is_out = bvec4(pos.x < ref_tl.x && pos.y < ref_tl.y,
+                         pos.x > ref_tr.x && pos.y < ref_tr.y,
+                         pos.x > ref_br.x && pos.y > ref_br.y,
+                         pos.x < ref_bl.x && pos.y > ref_bl.y);
+
+    float distance_from_border = dot(vec4(is_out),
+                                     max(vec4(0.0, 0.0, 0.0, 0.0), distances));
+
+    // Move the distance back into pixels.
+    distance_from_border /= pixels_per_fragment;
+    // Apply a more gradual fade out to transparent.
+    //distance_from_border -= 0.5;
+
+    return 1.0 - smoothstep(0.0, 1.0, distance_from_border);
+}
+
+
+void main(void) {
+    float alpha = 1.f;
+    vec2 local_pos = init_transform_fs(vPos, vLocalRect, alpha);
+
+    float clip_alpha = rounded_rect(local_pos);
+
+    oFragColor = vec4(min(alpha, clip_alpha), 0.0, 0.0, 1.0);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/cs_clip_rectangle.glsl
@@ -0,0 +1,10 @@
+#line 1
+
+/* 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/. */
+
+varying vec3 vPos;
+flat varying vec4 vLocalRect;
+flat varying vec4 vClipRect;
+flat varying vec4 vClipRadius;
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/cs_clip_rectangle.vs.glsl
@@ -0,0 +1,78 @@
+#line 1
+/* 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/. */
+
+struct ClipRect {
+    vec4 rect;
+    vec4 dummy;
+};
+
+ClipRect fetch_clip_rect(int index) {
+    ClipRect rect;
+
+    ivec2 uv = get_fetch_uv_2(index);
+
+    rect.rect = texelFetchOffset(sData32, uv, 0, ivec2(0, 0));
+    //rect.dummy = texelFetchOffset(sData32, uv, 0, ivec2(1, 0));
+    rect.dummy = vec4(0.0, 0.0, 0.0, 0.0);
+
+    return rect;
+}
+
+struct ClipCorner {
+    vec4 rect;
+    vec4 outer_inner_radius;
+};
+
+ClipCorner fetch_clip_corner(int index) {
+    ClipCorner corner;
+
+    ivec2 uv = get_fetch_uv_2(index);
+
+    corner.rect = texelFetchOffset(sData32, uv, 0, ivec2(0, 0));
+    corner.outer_inner_radius = texelFetchOffset(sData32, uv, 0, ivec2(1, 0));
+
+    return corner;
+}
+
+struct ClipData {
+    ClipRect rect;
+    ClipCorner top_left;
+    ClipCorner top_right;
+    ClipCorner bottom_left;
+    ClipCorner bottom_right;
+};
+
+ClipData fetch_clip(int index) {
+    ClipData clip;
+
+    clip.rect = fetch_clip_rect(index + 0);
+    clip.top_left = fetch_clip_corner(index + 1);
+    clip.top_right = fetch_clip_corner(index + 2);
+    clip.bottom_left = fetch_clip_corner(index + 3);
+    clip.bottom_right = fetch_clip_corner(index + 4);
+
+    return clip;
+}
+
+void main(void) {
+    CacheClipInstance cci = fetch_clip_item(gl_InstanceID);
+    ClipArea area = fetch_clip_area(cci.render_task_index);
+    Layer layer = fetch_layer(cci.layer_index);
+    ClipData clip = fetch_clip(cci.data_index);
+    vec4 local_rect = clip.rect.rect;
+
+    TransformVertexInfo vi = write_clip_tile_vertex(local_rect,
+                                                    layer,
+                                                    area,
+                                                    cci.segment_index);
+    vLocalRect = vi.clipped_local_rect;
+    vPos = vi.local_pos;
+
+    vClipRect = vec4(local_rect.xy, local_rect.xy + local_rect.zw);
+    vClipRadius = vec4(clip.top_left.outer_inner_radius.x,
+                       clip.top_right.outer_inner_radius.x,
+                       clip.bottom_right.outer_inner_radius.x,
+                       clip.bottom_left.outer_inner_radius.x);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/cs_text_run.fs.glsl
@@ -0,0 +1,9 @@
+#line 1
+/* 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/. */
+
+void main(void) {
+    float a = texture(sColor0, vUv).a;
+    oFragColor = vec4(vColor.rgb, vColor.a * a);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/cs_text_run.glsl
@@ -0,0 +1,7 @@
+#line 1
+/* 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/. */
+
+varying vec2 vUv;
+flat varying vec4 vColor;
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/cs_text_run.vs.glsl
@@ -0,0 +1,36 @@
+#line 1
+/* 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/. */
+
+// Draw a text run to a cache target. These are always
+// drawn un-transformed. These are used for effects such
+// as text-shadow.
+
+void main(void) {
+    CachePrimitiveInstance cpi = fetch_cache_instance();
+    RenderTaskData task = fetch_render_task(cpi.render_task_index);
+    TextRun text = fetch_text_run(cpi.specific_prim_index);
+    Glyph glyph = fetch_glyph(cpi.sub_index);
+    PrimitiveGeometry pg = fetch_prim_geometry(cpi.global_prim_index);
+    ResourceRect res = fetch_resource_rect(cpi.user_data.x);
+
+    // Glyphs size is already in device-pixels.
+    // The render task origin is in device-pixels. Offset that by
+    // the glyph offset, relative to its primitive bounding rect.
+    vec2 size = res.uv_rect.zw - res.uv_rect.xy;
+    vec2 origin = task.data0.xy + uDevicePixelRatio * (glyph.offset.xy - pg.local_rect.xy);
+    vec4 local_rect = vec4(origin, size);
+
+    vec2 texture_size = vec2(textureSize(sColor0, 0));
+    vec2 st0 = res.uv_rect.xy / texture_size;
+    vec2 st1 = res.uv_rect.zw / texture_size;
+
+    vec2 pos = mix(local_rect.xy,
+                   local_rect.xy + local_rect.zw,
+                   aPosition.xy);
+	vUv = mix(st0, st1, aPosition.xy);
+	vColor = text.color;
+
+    gl_Position = uTransform * vec4(pos, 0.0, 1.0);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/debug_color.fs.glsl
@@ -0,0 +1,10 @@
+/* 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/. */
+
+varying vec4 vColor;
+
+void main(void)
+{
+    oFragColor = vColor;
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/debug_color.vs.glsl
@@ -0,0 +1,14 @@
+/* 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/. */
+
+in vec4 aColor;
+varying vec4 vColor;
+
+void main(void)
+{
+    vColor = aColor;
+    vec4 pos = vec4(aPosition, 1.0);
+    pos.xy = floor(pos.xy * uDevicePixelRatio + 0.5) / uDevicePixelRatio;
+    gl_Position = uTransform * pos;
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/debug_font.fs.glsl
@@ -0,0 +1,16 @@
+/* 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/. */
+
+varying vec2 vColorTexCoord;
+varying vec4 vColor;
+
+void main(void)
+{
+#ifdef SERVO_ES2
+    float alpha = texture(sColor0, vColorTexCoord.xy).a;
+#else
+    float alpha = texture(sColor0, vColorTexCoord.xy).r;
+#endif
+    oFragColor = vec4(vColor.xyz, vColor.w * alpha);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/debug_font.vs.glsl
@@ -0,0 +1,18 @@
+/* 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/. */
+
+in vec4 aColor;
+in vec4 aColorTexCoord;
+
+varying vec2 vColorTexCoord;
+varying vec4 vColor;
+
+void main(void)
+{
+    vColor = aColor;
+    vColorTexCoord = aColorTexCoord.xy;
+    vec4 pos = vec4(aPosition, 1.0);
+    pos.xy = floor(pos.xy * uDevicePixelRatio + 0.5) / uDevicePixelRatio;
+    gl_Position = uTransform * pos;
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -0,0 +1,665 @@
+#line 1
+/* 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/. */
+
+#if defined(GL_ES)
+    #if GL_ES == 1
+        #ifdef GL_FRAGMENT_PRECISION_HIGH
+        precision highp sampler2DArray;
+        #else
+        precision mediump sampler2DArray;
+        #endif
+    #endif
+#endif
+
+#define PST_TOP_LEFT     0
+#define PST_TOP          1
+#define PST_TOP_RIGHT    2
+#define PST_RIGHT        3
+#define PST_BOTTOM_RIGHT 4
+#define PST_BOTTOM       5
+#define PST_BOTTOM_LEFT  6
+#define PST_LEFT         7
+
+#define BORDER_LEFT      0
+#define BORDER_TOP       1
+#define BORDER_RIGHT     2
+#define BORDER_BOTTOM    3
+
+#define UV_NORMALIZED    uint(0)
+#define UV_PIXEL         uint(1)
+
+#define MAX_STOPS_PER_ANGLE_GRADIENT 8
+#define MAX_STOPS_PER_RADIAL_GRADIENT 8
+
+uniform sampler2DArray sCache;
+
+flat varying vec4 vClipMaskUvBounds;
+varying vec3 vClipMaskUv;
+
+#ifdef WR_VERTEX_SHADER
+
+#define VECS_PER_LAYER             13
+#define VECS_PER_RENDER_TASK        3
+#define VECS_PER_PRIM_GEOM          2
+
+#define GRADIENT_HORIZONTAL     0
+#define GRADIENT_VERTICAL       1
+#define GRADIENT_ROTATED        2
+
+uniform sampler2D sLayers;
+uniform sampler2D sRenderTasks;
+uniform sampler2D sPrimGeometry;
+
+uniform sampler2D sData16;
+uniform sampler2D sData32;
+uniform sampler2D sData64;
+uniform sampler2D sData128;
+uniform sampler2D sResourceRects;
+
+// Instanced attributes
+in int aGlobalPrimId;
+in int aPrimitiveAddress;
+in int aTaskIndex;
+in int aClipTaskIndex;
+in int aLayerIndex;
+in int aElementIndex;
+in ivec2 aUserData;
+in int aZIndex;
+
+// get_fetch_uv is a macro to work around a macOS Intel driver parsing bug.
+// TODO: convert back to a function once the driver issues are resolved, if ever.
+// https://github.com/servo/webrender/pull/623
+// https://github.com/servo/servo/issues/13953
+#define get_fetch_uv(i, vpi)  ivec2(vpi * (i % (WR_MAX_VERTEX_TEXTURE_WIDTH/vpi)), i / (WR_MAX_VERTEX_TEXTURE_WIDTH/vpi))
+
+ivec2 get_fetch_uv_1(int index) {
+    return get_fetch_uv(index, 1);
+}
+
+ivec2 get_fetch_uv_2(int index) {
+    return get_fetch_uv(index, 2);
+}
+
+ivec2 get_fetch_uv_4(int index) {
+    return get_fetch_uv(index, 4);
+}
+
+ivec2 get_fetch_uv_8(int index) {
+    return get_fetch_uv(index, 8);
+}
+
+struct Layer {
+    mat4 transform;
+    mat4 inv_transform;
+    vec4 local_clip_rect;
+    vec4 screen_vertices[4];
+};
+
+Layer fetch_layer(int index) {
+    Layer layer;
+
+    // Create a UV base coord for each 8 texels.
+    // This is required because trying to use an offset
+    // of more than 8 texels doesn't work on some versions
+    // of OSX.
+    ivec2 uv = get_fetch_uv(index, VECS_PER_LAYER);
+    ivec2 uv0 = ivec2(uv.x + 0, uv.y);
+    ivec2 uv1 = ivec2(uv.x + 8, uv.y);
+
+    layer.transform[0] = texelFetchOffset(sLayers, uv0, 0, ivec2(0, 0));
+    layer.transform[1] = texelFetchOffset(sLayers, uv0, 0, ivec2(1, 0));
+    layer.transform[2] = texelFetchOffset(sLayers, uv0, 0, ivec2(2, 0));
+    layer.transform[3] = texelFetchOffset(sLayers, uv0, 0, ivec2(3, 0));
+
+    layer.inv_transform[0] = texelFetchOffset(sLayers, uv0, 0, ivec2(4, 0));
+    layer.inv_transform[1] = texelFetchOffset(sLayers, uv0, 0, ivec2(5, 0));
+    layer.inv_transform[2] = texelFetchOffset(sLayers, uv0, 0, ivec2(6, 0));
+    layer.inv_transform[3] = texelFetchOffset(sLayers, uv0, 0, ivec2(7, 0));
+
+    layer.local_clip_rect = texelFetchOffset(sLayers, uv1, 0, ivec2(0, 0));
+
+    layer.screen_vertices[0] = texelFetchOffset(sLayers, uv1, 0, ivec2(1, 0));
+    layer.screen_vertices[1] = texelFetchOffset(sLayers, uv1, 0, ivec2(2, 0));
+    layer.screen_vertices[2] = texelFetchOffset(sLayers, uv1, 0, ivec2(3, 0));
+    layer.screen_vertices[3] = texelFetchOffset(sLayers, uv1, 0, ivec2(4, 0));
+
+    return layer;
+}
+
+struct RenderTaskData {
+    vec4 data0;
+    vec4 data1;
+    vec4 data2;
+};
+
+RenderTaskData fetch_render_task(int index) {
+    RenderTaskData task;
+
+    ivec2 uv = get_fetch_uv(index, VECS_PER_RENDER_TASK);
+
+    task.data0 = texelFetchOffset(sRenderTasks, uv, 0, ivec2(0, 0));
+    task.data1 = texelFetchOffset(sRenderTasks, uv, 0, ivec2(1, 0));
+    task.data2 = texelFetchOffset(sRenderTasks, uv, 0, ivec2(2, 0));
+
+    return task;
+}
+
+struct Tile {
+    vec4 screen_origin_task_origin;
+    vec4 size_target_index;
+};
+
+Tile fetch_tile(int index) {
+    RenderTaskData task = fetch_render_task(index);
+
+    Tile tile;
+    tile.screen_origin_task_origin = task.data0;
+    tile.size_target_index = task.data1;
+
+    return tile;
+}
+
+struct ClipArea {
+    vec4 task_bounds;
+    vec4 screen_origin_target_index;
+    vec4 inner_rect;
+};
+
+ClipArea fetch_clip_area(int index) {
+    ClipArea area;
+
+    if (index == 0x7FFFFFFF) { //special sentinel task index
+        area.task_bounds = vec4(0.0, 0.0, 0.0, 0.0);
+        area.screen_origin_target_index = vec4(0.0, 0.0, 0.0, 0.0);
+        area.inner_rect = vec4(0.0);
+    } else {
+        RenderTaskData task = fetch_render_task(index);
+        area.task_bounds = task.data0;
+        area.screen_origin_target_index = task.data1;
+        area.inner_rect = task.data2;
+    }
+
+    return area;
+}
+
+struct Gradient {
+    vec4 start_end_point;
+    vec4 kind;
+};
+
+Gradient fetch_gradient(int index) {
+    Gradient gradient;
+
+    ivec2 uv = get_fetch_uv_2(index);
+
+    gradient.start_end_point = texelFetchOffset(sData32, uv, 0, ivec2(0, 0));
+    gradient.kind = texelFetchOffset(sData32, uv, 0, ivec2(1, 0));
+
+    return gradient;
+}
+
+struct GradientStop {
+    vec4 color;
+    vec4 offset;
+};
+
+GradientStop fetch_gradient_stop(int index) {
+    GradientStop stop;
+
+    ivec2 uv = get_fetch_uv_2(index);
+
+    stop.color = texelFetchOffset(sData32, uv, 0, ivec2(0, 0));
+    stop.offset = texelFetchOffset(sData32, uv, 0, ivec2(1, 0));
+
+    return stop;
+}
+
+struct RadialGradient {
+    vec4 start_end_center;
+    vec4 start_end_radius;
+};
+
+RadialGradient fetch_radial_gradient(int index) {
+    RadialGradient gradient;
+
+    ivec2 uv = get_fetch_uv_2(index);
+
+    gradient.start_end_center = texelFetchOffset(sData32, uv, 0, ivec2(0, 0));
+    gradient.start_end_radius = texelFetchOffset(sData32, uv, 0, ivec2(1, 0));
+
+    return gradient;
+}
+
+struct Glyph {
+    vec4 offset;
+};
+
+Glyph fetch_glyph(int index) {
+    Glyph glyph;
+
+    ivec2 uv = get_fetch_uv_1(index);
+
+    glyph.offset = texelFetchOffset(sData16, uv, 0, ivec2(0, 0));
+
+    return glyph;
+}
+
+vec4 fetch_instance_geometry(int index) {
+    ivec2 uv = get_fetch_uv_1(index);
+
+    vec4 rect = texelFetchOffset(sData16, uv, 0, ivec2(0, 0));
+
+    return rect;
+}
+
+struct PrimitiveGeometry {
+    vec4 local_rect;
+    vec4 local_clip_rect;
+};
+
+PrimitiveGeometry fetch_prim_geometry(int index) {
+    PrimitiveGeometry pg;
+
+    ivec2 uv = get_fetch_uv(index, VECS_PER_PRIM_GEOM);
+
+    pg.local_rect = texelFetchOffset(sPrimGeometry, uv, 0, ivec2(0, 0));
+    pg.local_clip_rect = texelFetchOffset(sPrimGeometry, uv, 0, ivec2(1, 0));
+
+    return pg;
+}
+
+struct PrimitiveInstance {
+    int global_prim_index;
+    int specific_prim_index;
+    int render_task_index;
+    int clip_task_index;
+    int layer_index;
+    int sub_index;
+    int z;
+    ivec2 user_data;
+};
+
+PrimitiveInstance fetch_prim_instance() {
+    PrimitiveInstance pi;
+
+    pi.global_prim_index = aGlobalPrimId;
+    pi.specific_prim_index = aPrimitiveAddress;
+    pi.render_task_index = aTaskIndex;
+    pi.clip_task_index = aClipTaskIndex;
+    pi.layer_index = aLayerIndex;
+    pi.sub_index = aElementIndex;
+    pi.user_data = aUserData;
+    pi.z = aZIndex;
+
+    return pi;
+}
+
+struct CachePrimitiveInstance {
+    int global_prim_index;
+    int specific_prim_index;
+    int render_task_index;
+    int sub_index;
+    ivec2 user_data;
+};
+
+CachePrimitiveInstance fetch_cache_instance() {
+    CachePrimitiveInstance cpi;
+
+    PrimitiveInstance pi = fetch_prim_instance();
+
+    cpi.global_prim_index = pi.global_prim_index;
+    cpi.specific_prim_index = pi.specific_prim_index;
+    cpi.render_task_index = pi.render_task_index;
+    cpi.sub_index = pi.sub_index;
+    cpi.user_data = pi.user_data;
+
+    return cpi;
+}
+
+struct Primitive {
+    Layer layer;
+    Tile tile;
+    ClipArea clip_area;
+    vec4 local_rect;
+    vec4 local_clip_rect;
+    int prim_index;
+    // when sending multiple primitives of the same type (e.g. border segments)
+    // this index allows the vertex shader to recognize the difference
+    int sub_index;
+    ivec2 user_data;
+    float z;
+};
+
+Primitive load_primitive_custom(PrimitiveInstance pi) {
+    Primitive prim;
+
+    prim.layer = fetch_layer(pi.layer_index);
+    prim.tile = fetch_tile(pi.render_task_index);
+    prim.clip_area = fetch_clip_area(pi.clip_task_index);
+
+    PrimitiveGeometry pg = fetch_prim_geometry(pi.global_prim_index);
+    prim.local_rect = pg.local_rect;
+    prim.local_clip_rect = pg.local_clip_rect;
+
+    prim.prim_index = pi.specific_prim_index;
+    prim.sub_index = pi.sub_index;
+    prim.user_data = pi.user_data;
+    prim.z = float(pi.z);
+
+    return prim;
+}
+
+Primitive load_primitive() {
+    PrimitiveInstance pi = fetch_prim_instance();
+
+    return load_primitive_custom(pi);
+}
+
+
+// Return the intersection of the plane (set up by "normal" and "point")
+// with the ray (set up by "ray_origin" and "ray_dir"),
+// writing the resulting scaler into "t".
+bool ray_plane(vec3 normal, vec3 point, vec3 ray_origin, vec3 ray_dir, out float t)
+{
+    float denom = dot(normal, ray_dir);
+    if (denom > 1e-6) {
+        vec3 d = point - ray_origin;
+        t = dot(d, normal) / denom;
+        return t >= 0.0;
+    }
+
+    return false;
+}
+
+// Apply the inverse transform "inv_transform"
+// to the reference point "ref" in CSS space,
+// producing a local point on a layer plane,
+// set by a base point "a" and a normal "n".
+vec4 untransform(vec2 ref, vec3 n, vec3 a, mat4 inv_transform) {
+    vec3 p = vec3(ref, -10000.0);
+    vec3 d = vec3(0, 0, 1.0);
+
+    float t = 0.0;
+    // get an intersection of the layer plane with Z axis vector,
+    // originated from the "ref" point
+    ray_plane(n, a, p, d, t);
+    float z = p.z + d.z * t; // Z of the visible point on the layer
+
+    vec4 r = inv_transform * vec4(ref, z, 1.0);
+    return r;
+}
+
+// Given a CSS space position, transform it back into the layer space.
+vec4 get_layer_pos(vec2 pos, Layer layer) {
+    // get 3 of the layer corners in CSS space
+    vec3 a = layer.screen_vertices[0].xyz / layer.screen_vertices[0].w;
+    vec3 b = layer.screen_vertices[3].xyz / layer.screen_vertices[3].w;
+    vec3 c = layer.screen_vertices[2].xyz / layer.screen_vertices[2].w;
+    // get the normal to the layer plane
+    vec3 n = normalize(cross(b-a, c-a));
+    return untransform(pos, n, a, layer.inv_transform);
+}
+
+vec2 clamp_rect(vec2 point, vec4 rect) {
+    return clamp(point, rect.xy, rect.xy + rect.zw);
+}
+
+struct Rect {
+    vec2 p0;
+    vec2 p1;
+};
+
+struct VertexInfo {
+    Rect local_rect;
+    vec2 local_clamped_pos;
+    vec2 global_clamped_pos;
+};
+
+VertexInfo write_vertex(vec4 instance_rect,
+                        vec4 local_clip_rect,
+                        float z,
+                        Layer layer,
+                        Tile tile) {
+    vec2 p0 = floor(0.5 + instance_rect.xy * uDevicePixelRatio) / uDevicePixelRatio;
+    vec2 p1 = floor(0.5 + (instance_rect.xy + instance_rect.zw) * uDevicePixelRatio) / uDevicePixelRatio;
+
+    vec2 local_pos = mix(p0, p1, aPosition.xy);
+
+    vec2 cp0 = floor(0.5 + local_clip_rect.xy * uDevicePixelRatio) / uDevicePixelRatio;
+    vec2 cp1 = floor(0.5 + (local_clip_rect.xy + local_clip_rect.zw) * uDevicePixelRatio) / uDevicePixelRatio;
+    local_pos = clamp(local_pos, cp0, cp1);
+
+    local_pos = clamp_rect(local_pos, layer.local_clip_rect);
+
+    vec4 world_pos = layer.transform * vec4(local_pos, 0.0, 1.0);
+    world_pos.xyz /= world_pos.w;
+
+    vec2 device_pos = world_pos.xy * uDevicePixelRatio;
+
+    vec2 clamped_pos = clamp(device_pos,
+                             tile.screen_origin_task_origin.xy,
+                             tile.screen_origin_task_origin.xy + tile.size_target_index.xy);
+
+    vec4 local_clamped_pos = layer.inv_transform * vec4(clamped_pos / uDevicePixelRatio, world_pos.z, 1);
+    local_clamped_pos.xyz /= local_clamped_pos.w;
+
+    vec2 final_pos = clamped_pos + tile.screen_origin_task_origin.zw - tile.screen_origin_task_origin.xy;
+
+    gl_Position = uTransform * vec4(final_pos, z, 1.0);
+
+    VertexInfo vi = VertexInfo(Rect(p0, p1), local_clamped_pos.xy, clamped_pos.xy);
+    return vi;
+}
+
+#ifdef WR_FEATURE_TRANSFORM
+
+struct TransformVertexInfo {
+    vec3 local_pos;
+    vec2 global_clamped_pos;
+    vec4 clipped_local_rect;
+};
+
+TransformVertexInfo write_transform_vertex(vec4 instance_rect,
+                                           vec4 local_clip_rect,
+                                           float z,
+                                           Layer layer,
+                                           Tile tile) {
+    vec2 lp0_base = instance_rect.xy;
+    vec2 lp1_base = instance_rect.xy + instance_rect.zw;
+
+    vec2 lp0 = clamp_rect(clamp_rect(lp0_base, local_clip_rect),
+                          layer.local_clip_rect);
+    vec2 lp1 = clamp_rect(clamp_rect(lp1_base, local_clip_rect),
+                          layer.local_clip_rect);
+
+    vec4 clipped_local_rect = vec4(lp0, lp1 - lp0);
+
+    vec2 p0 = lp0;
+    vec2 p1 = vec2(lp1.x, lp0.y);
+    vec2 p2 = vec2(lp0.x, lp1.y);
+    vec2 p3 = lp1;
+
+    vec4 t0 = layer.transform * vec4(p0, 0, 1);
+    vec4 t1 = layer.transform * vec4(p1, 0, 1);
+    vec4 t2 = layer.transform * vec4(p2, 0, 1);
+    vec4 t3 = layer.transform * vec4(p3, 0, 1);
+
+    vec2 tp0 = t0.xy / t0.w;
+    vec2 tp1 = t1.xy / t1.w;
+    vec2 tp2 = t2.xy / t2.w;
+    vec2 tp3 = t3.xy / t3.w;
+
+    // compute a CSS space aligned bounding box
+    vec2 min_pos = min(min(tp0.xy, tp1.xy), min(tp2.xy, tp3.xy));
+    vec2 max_pos = max(max(tp0.xy, tp1.xy), max(tp2.xy, tp3.xy));
+
+    // clamp to the tile boundaries, in device space
+    vec2 min_pos_clamped = clamp(min_pos * uDevicePixelRatio,
+                                 tile.screen_origin_task_origin.xy,
+                                 tile.screen_origin_task_origin.xy + tile.size_target_index.xy);
+
+    vec2 max_pos_clamped = clamp(max_pos * uDevicePixelRatio,
+                                 tile.screen_origin_task_origin.xy,
+                                 tile.screen_origin_task_origin.xy + tile.size_target_index.xy);
+
+    // compute the device space position of this vertex
+    vec2 clamped_pos = mix(min_pos_clamped,
+                           max_pos_clamped,
+                           aPosition.xy);
+
+    // compute the point position in side the layer, in CSS space
+    vec4 layer_pos = get_layer_pos(clamped_pos / uDevicePixelRatio, layer);
+
+    // apply the task offset
+    vec2 final_pos = clamped_pos + tile.screen_origin_task_origin.zw - tile.screen_origin_task_origin.xy;
+
+    gl_Position = uTransform * vec4(final_pos, z, 1.0);
+
+    return TransformVertexInfo(layer_pos.xyw, clamped_pos, clipped_local_rect);
+}
+
+#endif //WR_FEATURE_TRANSFORM
+
+struct ResourceRect {
+    vec4 uv_rect;
+};
+
+ResourceRect fetch_resource_rect(int index) {
+    ResourceRect rect;
+
+    ivec2 uv = get_fetch_uv_1(index);
+
+    rect.uv_rect = texelFetchOffset(sResourceRects, uv, 0, ivec2(0, 0));
+
+    return rect;
+}
+
+struct Rectangle {
+    vec4 color;
+};
+
+Rectangle fetch_rectangle(int index) {
+    Rectangle rect;
+
+    ivec2 uv = get_fetch_uv_1(index);
+
+    rect.color = texelFetchOffset(sData16, uv, 0, ivec2(0, 0));
+
+    return rect;
+}
+
+struct TextRun {
+    vec4 color;
+};
+
+TextRun fetch_text_run(int index) {
+    TextRun text;
+
+    ivec2 uv = get_fetch_uv_1(index);
+
+    text.color = texelFetchOffset(sData16, uv, 0, ivec2(0, 0));
+
+    return text;
+}
+
+struct Image {
+    vec4 stretch_size_and_tile_spacing;  // Size of the actual image and amount of space between
+                                         //     tiled instances of this image.
+};
+
+Image fetch_image(int index) {
+    Image image;
+
+    ivec2 uv = get_fetch_uv_1(index);
+
+    image.stretch_size_and_tile_spacing = texelFetchOffset(sData16, uv, 0, ivec2(0, 0));
+
+    return image;
+}
+
+// YUV color spaces
+#define YUV_REC601 1
+#define YUV_REC709 2
+
+struct YuvImage {
+    vec4 y_st_rect;
+    vec4 u_st_rect;
+    vec4 v_st_rect;
+    vec2 size;
+    int color_space;
+};
+
+YuvImage fetch_yuv_image(int index) {
+    YuvImage image;
+
+    ivec2 uv = get_fetch_uv_4(index);
+
+    image.y_st_rect = texelFetchOffset(sData64, uv, 0, ivec2(0, 0));
+    image.u_st_rect = texelFetchOffset(sData64, uv, 0, ivec2(1, 0));
+    image.v_st_rect = texelFetchOffset(sData64, uv, 0, ivec2(2, 0));
+    vec4 size_color_space = texelFetchOffset(sData64, uv, 0, ivec2(3, 0));
+    image.size = size_color_space.xy;
+    image.color_space = int(size_color_space.z);
+
+    return image;
+}
+
+struct BoxShadow {
+    vec4 src_rect;
+    vec4 bs_rect;
+    vec4 color;
+    vec4 border_radius_edge_size_blur_radius_inverted;
+};
+
+BoxShadow fetch_boxshadow(int index) {
+    BoxShadow bs;
+
+    ivec2 uv = get_fetch_uv_4(index);
+
+    bs.src_rect = texelFetchOffset(sData64, uv, 0, ivec2(0, 0));
+    bs.bs_rect = texelFetchOffset(sData64, uv, 0, ivec2(1, 0));
+    bs.color = texelFetchOffset(sData64, uv, 0, ivec2(2, 0));
+    bs.border_radius_edge_size_blur_radius_inverted = texelFetchOffset(sData64, uv, 0, ivec2(3, 0));
+
+    return bs;
+}
+
+void write_clip(vec2 global_pos, ClipArea area) {
+    vec2 texture_size = vec2(textureSize(sCache, 0).xy);
+    vec2 uv = global_pos + area.task_bounds.xy - area.screen_origin_target_index.xy;
+    vClipMaskUvBounds = area.task_bounds / texture_size.xyxy;
+    vClipMaskUv = vec3(uv / texture_size, area.screen_origin_target_index.z);
+}
+#endif //WR_VERTEX_SHADER
+
+#ifdef WR_FRAGMENT_SHADER
+float distance_from_rect(vec2 p, vec2 origin, vec2 size) {
+    vec2 clamped = clamp(p, origin, origin + size);
+    return distance(clamped, p);
+}
+
+vec2 init_transform_fs(vec3 local_pos, vec4 local_rect, out float fragment_alpha) {
+    fragment_alpha = 1.0;
+    vec2 pos = local_pos.xy / local_pos.z;
+
+    float border_distance = distance_from_rect(pos, local_rect.xy, local_rect.zw);
+    if (border_distance != 0.0) {
+        float delta = length(fwidth(local_pos.xy));
+        fragment_alpha = 1.0 - smoothstep(0.0, 1.0, border_distance / delta * 2.0);
+    }
+
+    return pos;
+}
+
+float do_clip() {
+    // anything outside of the mask is considered transparent
+    bvec4 inside = lessThanEqual(
+        vec4(vClipMaskUvBounds.xy, vClipMaskUv.xy),
+        vec4(vClipMaskUv.xy, vClipMaskUvBounds.zw));
+    // check for the dummy bounds, which are given to the opaque objects
+    return vClipMaskUvBounds.xy == vClipMaskUvBounds.zw ? 1.0:
+        all(inside) ? textureLod(sCache, vClipMaskUv, 0.0).r : 0.0;
+}
+#endif //WR_FRAGMENT_SHADER
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_angle_gradient.fs.glsl
@@ -0,0 +1,39 @@
+/* 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/. */
+
+float offset(int index) {
+    return vOffsets[index / 4][index % 4];
+}
+
+float linearStep(float lo, float hi, float x) {
+    float d = hi - lo;
+    float v = x - lo;
+    if (d != 0.0) {
+        v /= d;
+    }
+    return clamp(v, 0.0, 1.0);
+}
+
+void main(void) {
+    float angle = atan(-vEndPoint.y + vStartPoint.y,
+                        vEndPoint.x - vStartPoint.x);
+    float sa = sin(angle);
+    float ca = cos(angle);
+
+    float sx = vStartPoint.x * ca - vStartPoint.y * sa;
+    float ex = vEndPoint.x * ca - vEndPoint.y * sa;
+    float d = ex - sx;
+
+    float x = vPos.x * ca - vPos.y * sa;
+
+    oFragColor = mix(vColors[0],
+                     vColors[1],
+                     linearStep(sx + d * offset(0), sx + d * offset(1), x));
+
+    for (int i=1 ; i < vStopCount-1 ; ++i) {
+        oFragColor = mix(oFragColor,
+                         vColors[i+1],
+                         linearStep(sx + d * offset(i), sx + d * offset(i+1), x));
+    }
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_angle_gradient.glsl
@@ -0,0 +1,11 @@
+/* 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/. */
+
+flat varying int vStopCount;
+flat varying float vAngle;
+flat varying vec2 vStartPoint;
+flat varying vec2 vEndPoint;
+varying vec2 vPos;
+flat varying vec4 vColors[MAX_STOPS_PER_ANGLE_GRADIENT];
+flat varying vec4 vOffsets[MAX_STOPS_PER_ANGLE_GRADIENT/4];
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_angle_gradient.vs.glsl
@@ -0,0 +1,33 @@
+#line 1
+/* 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/. */
+
+void main(void) {
+    Primitive prim = load_primitive();
+    Gradient gradient = fetch_gradient(prim.prim_index);
+
+    VertexInfo vi = write_vertex(prim.local_rect,
+                                 prim.local_clip_rect,
+                                 prim.z,
+                                 prim.layer,
+                                 prim.tile);
+
+    vStopCount = int(prim.user_data.x);
+    vPos = vi.local_clamped_pos;
+
+    // Snap the start/end points to device pixel units.
+    // I'm not sure this is entirely correct, but the
+    // old render path does this, and it is needed to
+    // make the angle gradient ref tests pass. It might
+    // be better to fix this higher up in DL construction
+    // and not snap here?
+    vStartPoint = floor(0.5 + gradient.start_end_point.xy * uDevicePixelRatio) / uDevicePixelRatio;
+    vEndPoint = floor(0.5 + gradient.start_end_point.zw * uDevicePixelRatio) / uDevicePixelRatio;
+
+    for (int i=0 ; i < vStopCount ; ++i) {
+        GradientStop stop = fetch_gradient_stop(prim.sub_index + i);
+        vColors[i] = stop.color;
+        vOffsets[i/4][i%4] = stop.offset.x;
+    }
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_blend.fs.glsl
@@ -0,0 +1,138 @@
+/* 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/. */
+
+vec3 rgbToHsv(vec3 c) {
+    float value = max(max(c.r, c.g), c.b);
+
+    float chroma = value - min(min(c.r, c.g), c.b);
+    if (chroma == 0.0) {
+        return vec3(0.0);
+    }
+    float saturation = chroma / value;
+
+    float hue;
+    if (c.r == value)
+        hue = (c.g - c.b) / chroma;
+    else if (c.g == value)
+        hue = 2.0 + (c.b - c.r) / chroma;
+    else // if (c.b == value)
+        hue = 4.0 + (c.r - c.g) / chroma;
+
+    hue *= 1.0/6.0;
+    if (hue < 0.0)
+        hue += 1.0;
+    return vec3(hue, saturation, value);
+}
+
+vec3 hsvToRgb(vec3 c) {
+    if (c.s == 0.0) {
+        return vec3(c.z);
+    }
+
+    float hue = c.x * 6.0;
+    int sector = int(hue);
+    float residualHue = hue - float(sector);
+
+    vec3 pqt = c.z * vec3(1.0 - c.y, 1.0 - c.y * residualHue, 1.0 - c.y * (1.0 - residualHue));
+    switch (sector) {
+        case 0:
+            return vec3(c.z, pqt.z, pqt.x);
+        case 1:
+            return vec3(pqt.y, c.z, pqt.x);
+        case 2:
+            return vec3(pqt.x, c.z, pqt.z);
+        case 3:
+            return vec3(pqt.x, pqt.y, c.z);
+        case 4:
+            return vec3(pqt.z, pqt.x, c.z);
+        default:
+            return vec3(c.z, pqt.x, pqt.y);
+    }
+}
+
+vec4 Blur(float radius, vec2 direction) {
+    // TODO(gw): Support blur in WR2!
+    return vec4(1.0);
+}
+
+vec4 Contrast(vec4 Cs, float amount) {
+    return vec4(Cs.rgb * amount - 0.5 * amount + 0.5, 1.0);
+}
+
+vec4 Grayscale(vec4 Cs, float amount) {
+    float ia = 1.0 - amount;
+    return mat4(vec4(0.2126 + 0.7874 * ia, 0.2126 - 0.2126 * ia, 0.2126 - 0.2126 * ia, 0.0),
+                vec4(0.7152 - 0.7152 * ia, 0.7152 + 0.2848 * ia, 0.7152 - 0.7152 * ia, 0.0),
+                vec4(0.0722 - 0.0722 * ia, 0.0722 - 0.0722 * ia, 0.0722 + 0.9278 * ia, 0.0),
+                vec4(0.0, 0.0, 0.0, 1.0)) * Cs;
+}
+
+vec4 HueRotate(vec4 Cs, float amount) {
+    vec3 CsHsv = rgbToHsv(Cs.rgb);
+    CsHsv.x = mod(CsHsv.x + amount / 6.283185307179586, 1.0);
+    return vec4(hsvToRgb(CsHsv), Cs.a);
+}
+
+vec4 Invert(vec4 Cs, float amount) {
+    return mix(Cs, vec4(1.0, 1.0, 1.0, Cs.a) - vec4(Cs.rgb, 0.0), amount);
+}
+
+vec4 Saturate(vec4 Cs, float amount) {
+    return vec4(hsvToRgb(min(vec3(1.0, amount, 1.0) * rgbToHsv(Cs.rgb), vec3(1.0))), Cs.a);
+}
+
+vec4 Sepia(vec4 Cs, float amount) {
+    float ia = 1.0 - amount;
+    return mat4(vec4(0.393 + 0.607 * ia, 0.349 - 0.349 * ia, 0.272 - 0.272 * ia, 0.0),
+                vec4(0.769 - 0.769 * ia, 0.686 + 0.314 * ia, 0.534 - 0.534 * ia, 0.0),
+                vec4(0.189 - 0.189 * ia, 0.168 - 0.168 * ia, 0.131 + 0.869 * ia, 0.0),
+                vec4(0.0, 0.0, 0.0, 1.0)) * Cs;
+}
+
+vec4 Brightness(vec4 Cs, float amount) {
+    return vec4(Cs.rgb * amount, Cs.a);
+}
+
+vec4 Opacity(vec4 Cs, float amount) {
+    return vec4(Cs.rgb, Cs.a * amount);
+}
+
+void main(void) {
+    vec4 Cs = texture(sCache, vUv);
+
+    if (Cs.a == 0.0) {
+        discard;
+    }
+
+    switch (vOp) {
+        case 0:
+            // Gaussian blur is specially handled:
+            oFragColor = Cs;// Blur(vAmount, vec2(0,0));
+            break;
+        case 1:
+            oFragColor = Contrast(Cs, vAmount);
+            break;
+        case 2:
+            oFragColor = Grayscale(Cs, vAmount);
+            break;
+        case 3:
+            oFragColor = HueRotate(Cs, vAmount);
+            break;
+        case 4:
+            oFragColor = Invert(Cs, vAmount);
+            break;
+        case 5:
+            oFragColor = Saturate(Cs, vAmount);
+            break;
+        case 6:
+            oFragColor = Sepia(Cs, vAmount);
+            break;
+        case 7:
+            oFragColor = Brightness(Cs, vAmount);
+            break;
+        case 8:
+            oFragColor = Opacity(Cs, vAmount);
+            break;
+    }
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_blend.glsl
@@ -0,0 +1,7 @@
+/* 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/. */
+
+varying vec3 vUv;
+flat varying float vAmount;
+flat varying int vOp;
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_blend.vs.glsl
@@ -0,0 +1,46 @@
+#line 1
+/* 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/. */
+
+struct Blend {
+    ivec4 src_id_target_id_op_amount;
+    int z;
+};
+
+Blend fetch_blend() {
+    PrimitiveInstance pi = fetch_prim_instance();
+
+    Blend blend;
+    blend.src_id_target_id_op_amount = ivec4(pi.user_data.x,
+                                             pi.render_task_index,
+                                             pi.sub_index,
+                                             pi.user_data.y);
+    blend.z = pi.z;
+
+    return blend;
+}
+
+void main(void) {
+    Blend blend = fetch_blend();
+    Tile src = fetch_tile(blend.src_id_target_id_op_amount.x);
+    Tile dest = fetch_tile(blend.src_id_target_id_op_amount.y);
+
+    vec2 dest_origin = dest.screen_origin_task_origin.zw -
+                       dest.screen_origin_task_origin.xy +
+                       src.screen_origin_task_origin.xy;
+
+    vec2 local_pos = mix(dest_origin,
+                         dest_origin + src.size_target_index.xy,
+                         aPosition.xy);
+
+    vec2 texture_size = vec2(textureSize(sCache, 0));
+    vec2 st0 = src.screen_origin_task_origin.zw / texture_size;
+    vec2 st1 = (src.screen_origin_task_origin.zw + src.size_target_index.xy) / texture_size;
+    vUv = vec3(mix(st0, st1, aPosition.xy), src.size_target_index.z);
+
+    vOp = blend.src_id_target_id_op_amount.z;
+    vAmount = float(blend.src_id_target_id_op_amount.w) / 65535.0;
+
+    gl_Position = uTransform * vec4(local_pos, blend.z, 1.0);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_border.fs.glsl
@@ -0,0 +1,408 @@
+#line 1
+
+/* 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/. */
+
+// Border styles as defined in webrender_traits/types.rs
+#define BORDER_STYLE_NONE         0
+#define BORDER_STYLE_SOLID        1
+#define BORDER_STYLE_DOUBLE       2
+#define BORDER_STYLE_DOTTED       3
+#define BORDER_STYLE_DASHED       4
+#define BORDER_STYLE_HIDDEN       5
+#define BORDER_STYLE_GROOVE       6
+#define BORDER_STYLE_RIDGE        7
+#define BORDER_STYLE_INSET        8
+#define BORDER_STYLE_OUTSET       9
+
+void discard_pixels_in_rounded_borders(vec2 local_pos) {
+  float distanceFromRef = distance(vRefPoint, local_pos);
+  if (vRadii.x > 0.0 && (distanceFromRef > vRadii.x || distanceFromRef < vRadii.z)) {
+      discard;
+  }
+}
+
+vec4 get_fragment_color(float distanceFromMixLine, float pixelsPerFragment) {
+  // Here we are mixing between the two border colors. We need to convert
+  // distanceFromMixLine it to pixel space to properly anti-alias and then push
+  // it between the limits accepted by `mix`.
+  float colorMix = min(max(distanceFromMixLine / pixelsPerFragment, -0.5), 0.5) + 0.5;
+  return mix(vHorizontalColor, vVerticalColor, colorMix);
+}
+
+float alpha_for_solid_border(float distance_from_ref,
+                             float inner_radius,
+                             float outer_radius,
+                             float pixels_per_fragment) {
+  // We want to start anti-aliasing one pixel in from the border.
+  float nudge = pixels_per_fragment;
+  inner_radius += nudge;
+  outer_radius -= nudge;
+
+  if (distance_from_ref < outer_radius && distance_from_ref > inner_radius) {
+    return 1.0;
+  }
+
+  float distance_from_border = max(distance_from_ref - outer_radius,
+                                   inner_radius - distance_from_ref);
+
+  // Move the distance back into pixels.
+  distance_from_border /= pixels_per_fragment;
+
+  // Apply a more gradual fade out to transparent.
+  // distance_from_border -= 0.5;
+
+  return 1.0 - smoothstep(0.0, 1.0, distance_from_border);
+}
+
+float alpha_for_solid_border_corner(vec2 local_pos,
+                                    float inner_radius,
+                                    float outer_radius,
+                                    float pixels_per_fragment) {
+  float distance_from_ref = distance(vRefPoint, local_pos);
+  return alpha_for_solid_border(distance_from_ref, inner_radius, outer_radius, pixels_per_fragment);
+}
+
+vec4 draw_dotted_edge(vec2 local_pos, vec4 piece_rect, float pixels_per_fragment) {
+  // We don't use pixels_per_fragment here, since it can change along the edge
+  // of a transformed border edge. We want this calculation to be consistent
+  // across the entire edge so that the positioning of the dots stays the same.
+  float two_pixels = 2.0 * length(fwidth(vLocalPos.xy));
+
+  // Circle diameter is stroke width, minus a couple pixels to account for anti-aliasing.
+  float circle_diameter = max(piece_rect.z - two_pixels, min(piece_rect.z, two_pixels));
+
+  // We want to spread the circles across the edge, but keep one circle diameter at the end
+  // reserved for two half-circles which connect to the corners.
+  float edge_available = piece_rect.w - (circle_diameter * 2.0);
+  float number_of_circles = floor(edge_available / (circle_diameter * 2.0));
+
+  // Here we are initializing the distance from the y coordinate of the center of the circle to
+  // the closest end half-circle.
+  vec2 relative_pos = local_pos - piece_rect.xy;
+  float y_distance = min(relative_pos.y, piece_rect.w - relative_pos.y);
+
+  if (number_of_circles > 0.0) {
+    // Spread the circles throughout the edge, to distribute the extra space evenly. We want
+    // to ensure that we have at last two pixels of space for each circle so that they aren't
+    // touching.
+    float space_for_each_circle = ceil(max(edge_available / number_of_circles, two_pixels));
+
+    float first_half_circle_space = circle_diameter;
+
+    float circle_index = (relative_pos.y - first_half_circle_space) / space_for_each_circle;
+    circle_index = floor(clamp(circle_index, 0.0, number_of_circles - 1.0));
+
+    float circle_y_pos =
+      circle_index * space_for_each_circle + (space_for_each_circle / 2.0) + circle_diameter;
+    y_distance = min(abs(circle_y_pos - relative_pos.y), y_distance);
+  }
+
+  float distance_from_circle_center = length(vec2(relative_pos.x - (piece_rect.z / 2.0), y_distance));
+  float distance_from_circle_edge = distance_from_circle_center - (circle_diameter / 2.0);
+
+  // Don't anti-alias if the circle diameter is small to avoid a blur of color.
+  if (circle_diameter < two_pixels && distance_from_circle_edge > 0.0)
+    return vec4(0.0);
+
+  // Move the distance back into pixels.
+  distance_from_circle_edge /= pixels_per_fragment;
+
+  float alpha = 1.0 - smoothstep(0.0, 1.0, min(1.0, max(0.0, distance_from_circle_edge)));
+  return vHorizontalColor * vec4(1.0, 1.0, 1.0, alpha);
+}
+
+vec4 draw_dashed_edge(float position, float border_width, float pixels_per_fragment) {
+  // TODO: Investigate exactly what FF does.
+  float size = border_width * 3.0;
+  float segment = floor(position / size);
+
+  float alpha = alpha_for_solid_border(position,
+                                       segment * size,
+                                       (segment + 1.0) * size,
+                                       pixels_per_fragment);
+
+  if (mod(segment + 2.0, 2.0) == 0.0) {
+    return vHorizontalColor * vec4(1.0, 1.0, 1.0, 1.0 - alpha);
+  } else {
+    return vHorizontalColor * vec4(1.0, 1.0, 1.0, alpha);
+  }
+}
+
+void draw_dashed_or_dotted_border(vec2 local_pos, float distance_from_mix_line) {
+  // This is the conversion factor for transformations and device pixel scaling.
+  float pixels_per_fragment = length(fwidth(local_pos.xy));
+
+  switch (vBorderPart) {
+    // These are the layer tile part PrimitivePart as uploaded by the tiling.rs
+    case PST_TOP_LEFT:
+    case PST_TOP_RIGHT:
+    case PST_BOTTOM_LEFT:
+    case PST_BOTTOM_RIGHT:
+    {
+      oFragColor = get_fragment_color(distance_from_mix_line, pixels_per_fragment);
+      if (vRadii.x > 0.0) {
+        oFragColor *= vec4(1.0, 1.0, 1.0, alpha_for_solid_border_corner(local_pos,
+                                                                  vRadii.z,
+                                                                  vRadii.x,
+                                                                  pixels_per_fragment));
+      }
+
+      break;
+    }
+    case PST_BOTTOM:
+    case PST_TOP: {
+      if (vBorderStyle == BORDER_STYLE_DASHED) {
+        oFragColor = draw_dashed_edge(vLocalPos.x - vPieceRect.x,
+                                      vPieceRect.w,
+                                      pixels_per_fragment);
+      } else {
+        oFragColor = draw_dotted_edge(local_pos.yx, vPieceRect.yxwz, pixels_per_fragment);
+      }
+      break;
+    }
+    case PST_LEFT:
+    case PST_RIGHT:
+    {
+      if (vBorderStyle == BORDER_STYLE_DASHED) {
+        oFragColor = draw_dashed_edge(vLocalPos.y - vPieceRect.y,
+                                      vPieceRect.z,
+                                      pixels_per_fragment);
+      } else {
+        oFragColor = draw_dotted_edge(local_pos.xy, vPieceRect.xyzw, pixels_per_fragment);
+      }
+      break;
+    }
+  }
+}
+
+vec4 draw_double_edge(float pos,
+                      float len,
+                      float distance_from_mix_line,
+                      float pixels_per_fragment) {
+  float total_border_width = len;
+  float one_third_width = total_border_width / 3.0;
+
+  // Contribution of the outer border segment.
+  float alpha = alpha_for_solid_border(pos,
+                                       total_border_width - one_third_width,
+                                       total_border_width,
+                                       pixels_per_fragment);
+
+  // Contribution of the inner border segment.
+  alpha += alpha_for_solid_border(pos, 0.0, one_third_width, pixels_per_fragment);
+  return get_fragment_color(distance_from_mix_line, pixels_per_fragment) * vec4(1.0, 1.0, 1.0, alpha);
+}
+
+vec4 draw_double_edge_vertical(vec2 local_pos,
+                               float distance_from_mix_line,
+                               float pixels_per_fragment) {
+  // Get our position within this specific segment
+  float position = local_pos.x - vLocalRect.x;
+  return draw_double_edge(position, vLocalRect.z, distance_from_mix_line, pixels_per_fragment);
+}
+
+vec4 draw_double_edge_horizontal(vec2 local_pos,
+                                 float distance_from_mix_line,
+                                 float pixels_per_fragment) {
+  // Get our position within this specific segment
+  float position = local_pos.y - vLocalRect.y;
+  return draw_double_edge(position, vLocalRect.w, distance_from_mix_line, pixels_per_fragment);
+}
+
+vec4 draw_double_edge_corner_with_radius(vec2 local_pos,
+                                         float distance_from_mix_line,
+                                         float pixels_per_fragment) {
+  float total_border_width = vRadii.x - vRadii.z;
+  float one_third_width = total_border_width / 3.0;
+
+  // Contribution of the outer border segment.
+  float alpha = alpha_for_solid_border_corner(local_pos,
+                                              vRadii.x - one_third_width,
+                                              vRadii.x,
+                                              pixels_per_fragment);
+
+  // Contribution of the inner border segment.
+  alpha += alpha_for_solid_border_corner(local_pos,
+                                         vRadii.z,
+                                         vRadii.z + one_third_width,
+                                         pixels_per_fragment);
+  return get_fragment_color(distance_from_mix_line, pixels_per_fragment) * vec4(1.0, 1.0, 1.0, alpha);
+}
+
+vec4 draw_double_edge_corner(vec2 local_pos,
+                             float distance_from_mix_line,
+                             float pixels_per_fragment) {
+  if (vRadii.x > 0.0) {
+      return draw_double_edge_corner_with_radius(local_pos,
+                                                 distance_from_mix_line,
+                                                 pixels_per_fragment);
+  }
+
+  bool is_vertical = (vBorderPart == PST_TOP_LEFT) ? distance_from_mix_line < 0.0 :
+                                                     distance_from_mix_line >= 0.0;
+  if (is_vertical) {
+    return draw_double_edge_vertical(local_pos, distance_from_mix_line, pixels_per_fragment);
+  } else {
+    return draw_double_edge_horizontal(local_pos, distance_from_mix_line, pixels_per_fragment);
+  }
+}
+
+void draw_double_border(float distance_from_mix_line, vec2 local_pos) {
+  float pixels_per_fragment = length(fwidth(local_pos.xy));
+  switch (vBorderPart) {
+    // These are the layer tile part PrimitivePart as uploaded by the tiling.rs
+    case PST_TOP_LEFT:
+    case PST_TOP_RIGHT:
+    case PST_BOTTOM_LEFT:
+    case PST_BOTTOM_RIGHT:
+    {
+      oFragColor = draw_double_edge_corner(local_pos, distance_from_mix_line, pixels_per_fragment);
+      break;
+    }
+    case PST_BOTTOM:
+    case PST_TOP:
+    {
+      oFragColor = draw_double_edge_horizontal(local_pos,
+                                               distance_from_mix_line,
+                                               pixels_per_fragment);
+      break;
+    }
+    case PST_LEFT:
+    case PST_RIGHT:
+    {
+      oFragColor = draw_double_edge_vertical(local_pos,
+                                             distance_from_mix_line,
+                                             pixels_per_fragment);
+      break;
+    }
+  }
+}
+
+void draw_solid_border(float distanceFromMixLine, vec2 localPos) {
+  switch (vBorderPart) {
+    case PST_TOP_LEFT:
+    case PST_TOP_RIGHT:
+    case PST_BOTTOM_LEFT:
+    case PST_BOTTOM_RIGHT: {
+      // This is the conversion factor for transformations and device pixel scaling.
+      float pixelsPerFragment = length(fwidth(localPos.xy));
+      oFragColor = get_fragment_color(distanceFromMixLine, pixelsPerFragment);
+
+      if (vRadii.x > 0.0) {
+        float alpha = alpha_for_solid_border_corner(localPos, vRadii.z, vRadii.x, pixelsPerFragment);
+        oFragColor *= vec4(1.0, 1.0, 1.0, alpha);
+      }
+
+      break;
+    }
+    default:
+      oFragColor = vHorizontalColor;
+      discard_pixels_in_rounded_borders(localPos);
+  }
+}
+
+vec4 draw_mixed_edge(float distance, float border_len, vec4 color, vec2 brightness_mod) {
+  float modulator = distance / border_len > 0.5 ? brightness_mod.x : brightness_mod.y;
+  return vec4(color.xyz * modulator, color.a);
+}
+
+void draw_mixed_border(float distanceFromMixLine, float distanceFromMiddle, vec2 localPos, vec2 brightness_mod) {
+  switch (vBorderPart) {
+    case PST_TOP_LEFT:
+    case PST_TOP_RIGHT:
+    case PST_BOTTOM_LEFT:
+    case PST_BOTTOM_RIGHT: {
+      // This is the conversion factor for transformations and device pixel scaling.
+      float pixelsPerFragment = length(fwidth(localPos.xy));
+      vec4 color = get_fragment_color(distanceFromMixLine, pixelsPerFragment);
+
+      float distance = distance(vRefPoint, localPos) - vRadii.z;
+      float length = vRadii.x - vRadii.z;
+      if (distanceFromMiddle < 0.0) {
+        distance = length - distance;
+      }
+
+      oFragColor = 0.0 <= distance && distance <= length ?
+        draw_mixed_edge(distance, length, color, brightness_mod) : vec4(0.0, 0.0, 0.0, 0.0);
+      break;
+    }
+    case PST_BOTTOM:
+    case PST_TOP: {
+      oFragColor = draw_mixed_edge(localPos.y - vPieceRect.y, vPieceRect.w, vVerticalColor, brightness_mod);
+      break;
+    }
+    case PST_LEFT:
+    case PST_RIGHT: {
+      oFragColor = draw_mixed_edge(localPos.x - vPieceRect.x, vPieceRect.z, vHorizontalColor, brightness_mod);
+      break;
+    }
+  }
+}
+
+// TODO: Investigate performance of this shader and see
+//       if it's worthwhile splitting it / removing branches etc.
+void main(void) {
+#ifdef WR_FEATURE_TRANSFORM
+    float alpha = 0.0;
+    vec2 local_pos = init_transform_fs(vLocalPos, vLocalRect, alpha);
+#else
+    vec2 local_pos = vLocalPos;
+#endif
+
+#ifdef WR_FEATURE_TRANSFORM
+    // TODO(gw): Support other border styles for transformed elements.
+    float distance_from_mix_line = (local_pos.x - vPieceRect.x) * vPieceRect.w -
+                                   (local_pos.y - vPieceRect.y) * vPieceRect.z;
+    distance_from_mix_line /= vPieceRectHypotenuseLength;
+    float distance_from_middle = (local_pos.x - vLocalRect.x) +
+                                 (local_pos.y - vLocalRect.y) -
+                                 0.5 * (vLocalRect.z + vLocalRect.w);
+#else
+    float distance_from_mix_line = vDistanceFromMixLine;
+    float distance_from_middle = vDistanceFromMiddle;
+#endif
+
+    vec2 brightness_mod = vec2(0.7, 1.3);
+
+    // Note: we can't pass-through in the following cases,
+    // because Angle doesn't support it and fails to compile the shaders.
+    switch (vBorderStyle) {
+        case BORDER_STYLE_DASHED:
+          draw_dashed_or_dotted_border(local_pos, distance_from_mix_line);
+          break;
+        case BORDER_STYLE_DOTTED:
+          draw_dashed_or_dotted_border(local_pos, distance_from_mix_line);
+          break;
+        case BORDER_STYLE_DOUBLE:
+          draw_double_border(distance_from_mix_line, local_pos);
+          break;
+        case BORDER_STYLE_OUTSET:
+          draw_solid_border(distance_from_mix_line, local_pos);
+          break;
+        case BORDER_STYLE_INSET:
+          draw_solid_border(distance_from_mix_line, local_pos);
+          break;
+        case BORDER_STYLE_SOLID:
+          draw_solid_border(distance_from_mix_line, local_pos);
+          break;
+        case BORDER_STYLE_NONE:
+          draw_solid_border(distance_from_mix_line, local_pos);
+          break;
+        case BORDER_STYLE_GROOVE:
+          draw_mixed_border(distance_from_mix_line, distance_from_middle, local_pos, brightness_mod.yx);
+          break;
+        case BORDER_STYLE_RIDGE:
+          draw_mixed_border(distance_from_mix_line, distance_from_middle, local_pos, brightness_mod.xy);
+          break;
+        case BORDER_STYLE_HIDDEN:
+          discard;
+        default:
+          discard;
+    }
+
+#ifdef WR_FEATURE_TRANSFORM
+    oFragColor *= vec4(1.0, 1.0, 1.0, alpha);
+#endif
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_border.glsl
@@ -0,0 +1,33 @@
+#line 1
+
+/* 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/. */
+
+// These are not changing.
+flat varying vec4 vVerticalColor;     // The vertical color, e.g. top/bottom
+flat varying vec4 vHorizontalColor;   // The horizontal color e.g. left/right
+flat varying vec4 vRadii;             // The border radius from CSS border-radius
+flat varying vec4 vLocalRect; // The rect of the border (x, y, w, h) in local space.
+
+// for corners, this is the beginning of the corner.
+// For the lines, this is the top left of the line.
+flat varying vec2 vRefPoint;
+flat varying int vBorderStyle;
+flat varying int vBorderPart; // Which part of the border we're drawing.
+
+flat varying vec4 vPieceRect;
+
+// These are in device space
+#ifdef WR_FEATURE_TRANSFORM
+varying vec3 vLocalPos;     // The clamped position in local space.
+flat varying float vPieceRectHypotenuseLength;
+#else
+varying vec2 vLocalPos;     // The clamped position in local space.
+
+// These two are interpolated
+varying float vDistanceFromMixLine;  // This is the distance from the line where two colors
+                                     // meet in border corners.
+varying float vDistanceFromMiddle;   // This is the distance from the line between the top
+                                     // left corner and the bottom right.
+#endif
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_border.vs.glsl
@@ -0,0 +1,226 @@
+#line 1
+/* 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/. */
+
+struct Border {
+    vec4 style;
+    vec4 widths;
+    vec4 colors[4];
+    vec4 radii[2];
+};
+
+Border fetch_border(int index) {
+    Border border;
+
+    ivec2 uv = get_fetch_uv_8(index);
+
+    border.style = texelFetchOffset(sData128, uv, 0, ivec2(0, 0));
+    border.widths = texelFetchOffset(sData128, uv, 0, ivec2(1, 0));
+    border.colors[0] = texelFetchOffset(sData128, uv, 0, ivec2(2, 0));
+    border.colors[1] = texelFetchOffset(sData128, uv, 0, ivec2(3, 0));
+    border.colors[2] = texelFetchOffset(sData128, uv, 0, ivec2(4, 0));
+    border.colors[3] = texelFetchOffset(sData128, uv, 0, ivec2(5, 0));
+    border.radii[0] = texelFetchOffset(sData128, uv, 0, ivec2(6, 0));
+    border.radii[1] = texelFetchOffset(sData128, uv, 0, ivec2(7, 0));
+
+    return border;
+}
+
+void main(void) {
+    Primitive prim = load_primitive();
+    Border border = fetch_border(prim.prim_index);
+    int sub_part = prim.sub_index;
+
+    vec2 tl_outer = prim.local_rect.xy;
+    vec2 tl_inner = tl_outer + vec2(max(border.radii[0].x, border.widths.x),
+                                    max(border.radii[0].y, border.widths.y));
+
+    vec2 tr_outer = vec2(prim.local_rect.x + prim.local_rect.z,
+                         prim.local_rect.y);
+    vec2 tr_inner = tr_outer + vec2(-max(border.radii[0].z, border.widths.z),
+                                    max(border.radii[0].w, border.widths.y));
+
+    vec2 br_outer = vec2(prim.local_rect.x + prim.local_rect.z,
+                         prim.local_rect.y + prim.local_rect.w);
+    vec2 br_inner = br_outer - vec2(max(border.radii[1].x, border.widths.z),
+                                    max(border.radii[1].y, border.widths.w));
+
+    vec2 bl_outer = vec2(prim.local_rect.x,
+                         prim.local_rect.y + prim.local_rect.w);
+    vec2 bl_inner = bl_outer + vec2(max(border.radii[1].z, border.widths.x),
+                                    -max(border.radii[1].w, border.widths.w));
+
+    vec4 segment_rect;
+    switch (sub_part) {
+        case PST_TOP_LEFT:
+            segment_rect = vec4(tl_outer, tl_inner - tl_outer);
+            vBorderStyle = int(border.style.x);
+            vHorizontalColor = border.colors[BORDER_LEFT];
+            vVerticalColor = border.colors[BORDER_TOP];
+            vRadii = vec4(border.radii[0].xy,
+                          border.radii[0].xy - border.widths.xy);
+            break;
+        case PST_TOP_RIGHT:
+            segment_rect = vec4(tr_inner.x,
+                                tr_outer.y,
+                                tr_outer.x - tr_inner.x,
+                                tr_inner.y - tr_outer.y);
+            vBorderStyle = int(border.style.y);
+            vHorizontalColor = border.colors[BORDER_TOP];
+            vVerticalColor = border.colors[BORDER_RIGHT];
+            vRadii = vec4(border.radii[0].zw,
+                          border.radii[0].zw - border.widths.zy);
+            break;
+        case PST_BOTTOM_RIGHT:
+            segment_rect = vec4(br_inner, br_outer - br_inner);
+            vBorderStyle = int(border.style.z);
+            vHorizontalColor = border.colors[BORDER_BOTTOM];
+            vVerticalColor = border.colors[BORDER_RIGHT];
+            vRadii = vec4(border.radii[1].xy,
+                          border.radii[1].xy - border.widths.zw);
+            break;
+        case PST_BOTTOM_LEFT:
+            segment_rect = vec4(bl_outer.x,
+                                bl_inner.y,
+                                bl_inner.x - bl_outer.x,
+                                bl_outer.y - bl_inner.y);
+            vBorderStyle = int(border.style.w);
+            vHorizontalColor = border.colors[BORDER_BOTTOM];
+            vVerticalColor = border.colors[BORDER_LEFT];
+            vRadii = vec4(border.radii[1].zw,
+                          border.radii[1].zw - border.widths.xw);
+            break;
+        case PST_LEFT:
+            segment_rect = vec4(tl_outer.x,
+                                tl_inner.y,
+                                border.widths.x,
+                                bl_inner.y - tl_inner.y);
+            vBorderStyle = int(border.style.x);
+            vHorizontalColor = border.colors[BORDER_LEFT];
+            vVerticalColor = border.colors[BORDER_LEFT];
+            vRadii = vec4(0.0);
+            break;
+        case PST_RIGHT:
+            segment_rect = vec4(tr_outer.x - border.widths.z,
+                                tr_inner.y,
+                                border.widths.z,
+                                br_inner.y - tr_inner.y);
+            vBorderStyle = int(border.style.z);
+            vHorizontalColor = border.colors[BORDER_RIGHT];
+            vVerticalColor = border.colors[BORDER_RIGHT];
+            vRadii = vec4(0.0);
+            break;
+        case PST_BOTTOM:
+            segment_rect = vec4(bl_inner.x,
+                                bl_outer.y - border.widths.w,
+                                br_inner.x - bl_inner.x,
+                                border.widths.w);
+            vBorderStyle = int(border.style.w);
+            vHorizontalColor = border.colors[BORDER_BOTTOM];
+            vVerticalColor = border.colors[BORDER_BOTTOM];
+            vRadii = vec4(0.0);
+            break;
+        case PST_TOP:
+            segment_rect = vec4(tl_inner.x,
+                                tl_outer.y,
+                                tr_inner.x - tl_inner.x,
+                                border.widths.y);
+            vBorderStyle = int(border.style.y);
+            vHorizontalColor = border.colors[BORDER_TOP];
+            vVerticalColor = border.colors[BORDER_TOP];
+            vRadii = vec4(0.0);
+            break;
+    }
+
+#ifdef WR_FEATURE_TRANSFORM
+    TransformVertexInfo vi = write_transform_vertex(segment_rect,
+                                                    prim.local_clip_rect,
+                                                    prim.z,
+                                                    prim.layer,
+                                                    prim.tile);
+    vLocalPos = vi.local_pos;
+
+    // Local space
+    vLocalRect = vi.clipped_local_rect;
+#else
+    VertexInfo vi = write_vertex(segment_rect,
+                                 prim.local_clip_rect,
+                                 prim.z,
+                                 prim.layer,
+                                 prim.tile);
+    vLocalPos = vi.local_clamped_pos.xy;
+
+    // Local space
+    vLocalRect = prim.local_rect;
+#endif
+
+    float x0, y0, x1, y1;
+    switch (sub_part) {
+        // These are the layer tile part PrimitivePart as uploaded by the tiling.rs
+        case PST_TOP_LEFT:
+            x0 = segment_rect.x;
+            y0 = segment_rect.y;
+            // These are width / heights
+            x1 = segment_rect.x + segment_rect.z;
+            y1 = segment_rect.y + segment_rect.w;
+
+            // The radius here is the border-radius. This is 0, so vRefPoint will
+            // just be the top left (x,y) corner.
+            vRefPoint = vec2(x0, y0) + vRadii.xy;
+            break;
+        case PST_TOP_RIGHT:
+            x0 = segment_rect.x + segment_rect.z;
+            y0 = segment_rect.y;
+            x1 = segment_rect.x;
+            y1 = segment_rect.y + segment_rect.w;
+            vRefPoint = vec2(x0, y0) + vec2(-vRadii.x, vRadii.y);
+            break;
+        case PST_BOTTOM_LEFT:
+            x0 = segment_rect.x;
+            y0 = segment_rect.y + segment_rect.w;
+            x1 = segment_rect.x + segment_rect.z;
+            y1 = segment_rect.y;
+            vRefPoint = vec2(x0, y0) + vec2(vRadii.x, -vRadii.y);
+            break;
+        case PST_BOTTOM_RIGHT:
+            x0 = segment_rect.x;
+            y0 = segment_rect.y;
+            x1 = segment_rect.x + segment_rect.z;
+            y1 = segment_rect.y + segment_rect.w;
+            vRefPoint = vec2(x1, y1) + vec2(-vRadii.x, -vRadii.y);
+            break;
+        case PST_TOP:
+        case PST_LEFT:
+        case PST_BOTTOM:
+        case PST_RIGHT:
+            vRefPoint = segment_rect.xy;
+            x0 = segment_rect.x;
+            y0 = segment_rect.y;
+            x1 = segment_rect.x + segment_rect.z;
+            y1 = segment_rect.y + segment_rect.w;
+            break;
+    }
+
+    // y1 - y0 is the height of the corner / line
+    // x1 - x0 is the width of the corner / line.
+    float width = x1 - x0;
+    float height = y1 - y0;
+
+    vBorderPart = sub_part;
+    vPieceRect = vec4(x0, y0, width, height);
+
+    // The fragment shader needs to calculate the distance from the bisecting line
+    // to properly mix border colors. For transformed borders, we calculate this distance
+    // in the fragment shader itself. For non-transformed borders, we can use the
+    // interpolator.
+#ifdef WR_FEATURE_TRANSFORM
+    vPieceRectHypotenuseLength = sqrt(pow(width, 2.0) + pow(height, 2.0));
+#else
+    vDistanceFromMixLine = (vi.local_clamped_pos.x - x0) * height -
+                           (vi.local_clamped_pos.y - y0) * width;
+    vDistanceFromMiddle = (vi.local_clamped_pos.x - vLocalRect.x) +
+                          (vi.local_clamped_pos.y - vLocalRect.y) -
+                          0.5 * (vLocalRect.z + vLocalRect.w);
+#endif
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_box_shadow.fs.glsl
@@ -0,0 +1,9 @@
+/* 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/. */
+
+void main(void) {
+    vec2 uv = min(vec2(1.0), vMirrorPoint - abs(vUv.xy - vMirrorPoint));
+    uv = mix(vCacheUvRectCoords.xy, vCacheUvRectCoords.zw, uv);
+    oFragColor = vColor * texture(sCache, vec3(uv, vUv.z));
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_box_shadow.glsl
@@ -0,0 +1,9 @@
+/* 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/. */
+
+flat varying vec4 vColor;
+
+varying vec3 vUv;
+flat varying vec2 vMirrorPoint;
+flat varying vec4 vCacheUvRectCoords;
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_box_shadow.vs.glsl
@@ -0,0 +1,32 @@
+#line 1
+/* 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/. */
+
+void main(void) {
+    Primitive prim = load_primitive();
+    BoxShadow bs = fetch_boxshadow(prim.prim_index);
+    vec4 segment_rect = fetch_instance_geometry(prim.sub_index);
+
+    VertexInfo vi = write_vertex(segment_rect,
+                                 prim.local_clip_rect,
+                                 prim.z,
+                                 prim.layer,
+                                 prim.tile);
+
+    RenderTaskData child_task = fetch_render_task(prim.user_data.x);
+    vUv.z = child_task.data1.x;
+
+    // Constant offsets to inset from bilinear filtering border.
+    vec2 patch_origin = child_task.data0.xy + vec2(1.0);
+    vec2 patch_size_device_pixels = child_task.data0.zw - vec2(2.0);
+    vec2 patch_size = patch_size_device_pixels / uDevicePixelRatio;
+
+    vUv.xy = (vi.local_clamped_pos - prim.local_rect.xy) / patch_size;
+    vMirrorPoint = 0.5 * prim.local_rect.zw / patch_size;
+
+    vec2 texture_size = vec2(textureSize(sCache, 0));
+    vCacheUvRectCoords = vec4(patch_origin, patch_origin + patch_size_device_pixels) / texture_size.xyxy;
+
+    vColor = bs.color;
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_cache_image.fs.glsl
@@ -0,0 +1,7 @@
+/* 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/. */
+
+void main(void) {
+    oFragColor = texture(sCache, vUv);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_cache_image.glsl
@@ -0,0 +1,5 @@
+/* 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/. */
+
+varying vec3 vUv;
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_cache_image.vs.glsl
@@ -0,0 +1,28 @@
+#line 1
+/* 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/. */
+
+// Draw a cached primitive (e.g. a blurred text run) from the
+// target cache to the framebuffer, applying tile clip boundaries.
+
+void main(void) {
+    Primitive prim = load_primitive();
+
+    VertexInfo vi = write_vertex(prim.local_rect,
+                                 prim.local_clip_rect,
+                                 prim.z,
+                                 prim.layer,
+                                 prim.tile);
+
+    RenderTaskData child_task = fetch_render_task(prim.user_data.x);
+    vUv.z = child_task.data1.x;
+
+    vec2 texture_size = vec2(textureSize(sCache, 0));
+    vec2 uv0 = child_task.data0.xy / texture_size;
+    vec2 uv1 = (child_task.data0.xy + child_task.data0.zw) / texture_size;
+
+    vec2 f = (vi.local_clamped_pos - prim.local_rect.xy) / prim.local_rect.zw;
+
+    vUv.xy = mix(uv0, uv1, f);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_clear.fs.glsl
@@ -0,0 +1,7 @@
+/* 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/. */
+
+void main(void) {
+    oFragColor = vec4(1.0, 1.0, 1.0, 1.0);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_clear.glsl
@@ -0,0 +1,3 @@
+/* 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/. */
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_clear.vs.glsl
@@ -0,0 +1,14 @@
+#line 1
+
+/* 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/. */
+
+in ivec4 aClearRectangle;
+
+void main() {
+    vec4 rect = vec4(aClearRectangle);
+
+    vec4 pos = vec4(mix(rect.xy, rect.xy + rect.zw, aPosition.xy), 0, 1);
+    gl_Position = uTransform * pos;
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_composite.fs.glsl
@@ -0,0 +1,219 @@
+#line 1
+
+/* 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/. */
+
+float gauss(float x, float sigma) {
+    if (sigma == 0.0)
+        return 1.0;
+    return (1.0 / sqrt(6.283185307179586 * sigma * sigma)) * exp(-(x * x) / (2.0 * sigma * sigma));
+}
+
+vec3 Multiply(vec3 Cb, vec3 Cs) {
+    return Cb * Cs;
+}
+
+vec3 Screen(vec3 Cb, vec3 Cs) {
+    return Cb + Cs - (Cb * Cs);
+}
+
+vec3 HardLight(vec3 Cb, vec3 Cs) {
+    vec3 m = Multiply(Cb, 2.0 * Cs);
+    vec3 s = Screen(Cb, 2.0 * Cs - 1.0);
+    vec3 edge = vec3(0.5, 0.5, 0.5);
+    return mix(m, s, step(edge, Cs));
+}
+
+// TODO: Worth doing with mix/step? Check GLSL output.
+float ColorDodge(float Cb, float Cs) {
+    if (Cb == 0.0)
+        return 0.0;
+    else if (Cs == 1.0)
+        return 1.0;
+    else
+        return min(1.0, Cb / (1.0 - Cs));
+}
+
+// TODO: Worth doing with mix/step? Check GLSL output.
+float ColorBurn(float Cb, float Cs) {
+    if (Cb == 1.0)
+        return 1.0;
+    else if (Cs == 0.0)
+        return 0.0;
+    else
+        return 1.0 - min(1.0, (1.0 - Cb) / Cs);
+}
+
+float SoftLight(float Cb, float Cs) {
+    if (Cs <= 0.5) {
+        return Cb - (1.0 - 2.0 * Cs) * Cb * (1.0 - Cb);
+    } else {
+        float D;
+
+        if (Cb <= 0.25)
+            D = ((16.0 * Cb - 12.0) * Cb + 4.0) * Cb;
+        else
+            D = sqrt(Cb);
+
+        return Cb + (2.0 * Cs - 1.0) * (D - Cb);
+    }
+}
+
+vec3 Difference(vec3 Cb, vec3 Cs) {
+    return abs(Cb - Cs);
+}
+
+vec3 Exclusion(vec3 Cb, vec3 Cs) {
+    return Cb + Cs - 2.0 * Cb * Cs;
+}
+
+// These functions below are taken from the spec.
+// There's probably a much quicker way to implement
+// them in GLSL...
+float Sat(vec3 c) {
+    return max(c.r, max(c.g, c.b)) - min(c.r, min(c.g, c.b));
+}
+
+float Lum(vec3 c) {
+    vec3 f = vec3(0.3, 0.59, 0.11);
+    return dot(c, f);
+}
+
+vec3 ClipColor(vec3 C) {
+    float L = Lum(C);
+    float n = min(C.r, min(C.g, C.b));
+    float x = max(C.r, max(C.g, C.b));
+
+    if (n < 0.0)
+        C = L + (((C - L) * L) / (L - n));
+
+    if (x > 1.0)
+        C = L + (((C - L) * (1.0 - L)) / (x - L));
+
+    return C;
+}
+
+vec3 SetLum(vec3 C, float l) {
+    float d = l - Lum(C);
+    return ClipColor(C + d);
+}
+
+void SetSatInner(inout float Cmin, inout float Cmid, inout float Cmax, float s) {
+    if (Cmax > Cmin) {
+        Cmid = (((Cmid - Cmin) * s) / (Cmax - Cmin));
+        Cmax = s;
+    } else {
+        Cmid = 0.0;
+        Cmax = 0.0;
+    }
+    Cmin = 0.0;
+}
+
+vec3 SetSat(vec3 C, float s) {
+    if (C.r <= C.g) {
+        if (C.g <= C.b) {
+            SetSatInner(C.r, C.g, C.b, s);
+        } else {
+            if (C.r <= C.b) {
+                SetSatInner(C.r, C.b, C.g, s);
+            } else {
+                SetSatInner(C.b, C.r, C.g, s);
+            }
+        }
+    } else {
+        if (C.r <= C.b) {
+            SetSatInner(C.g, C.r, C.b, s);
+        } else {
+            if (C.g <= C.b) {
+                SetSatInner(C.g, C.b, C.r, s);
+            } else {
+                SetSatInner(C.b, C.g, C.r, s);
+            }
+        }
+    }
+    return C;
+}
+
+vec3 Hue(vec3 Cb, vec3 Cs) {
+    return SetLum(SetSat(Cs, Sat(Cb)), Lum(Cb));
+}
+
+vec3 Saturation(vec3 Cb, vec3 Cs) {
+    return SetLum(SetSat(Cb, Sat(Cs)), Lum(Cb));
+}
+
+vec3 Color(vec3 Cb, vec3 Cs) {
+    return SetLum(Cs, Lum(Cb));
+}
+
+vec3 Luminosity(vec3 Cb, vec3 Cs) {
+    return SetLum(Cb, Lum(Cs));
+}
+
+void main(void) {
+    vec4 Cb = texture(sCache, vUv0);
+
+    if (vUv1.x < vUv1Rect.x ||
+        vUv1.x > vUv1Rect.z ||
+        vUv1.y < vUv1Rect.y ||
+        vUv1.y > vUv1Rect.w) {
+        oFragColor = Cb;
+        return;
+    }
+
+    vec4 Cs = texture(sCache, vUv1);
+
+    // Return yellow if none of the branches match (shouldn't happen).
+    vec4 result = vec4(1.0, 1.0, 0.0, 1.0);
+
+    switch (vOp) {
+        case 1:
+            result.rgb = Multiply(Cb.rgb, Cs.rgb);
+            break;
+        case 2:
+            result.rgb = Screen(Cb.rgb, Cs.rgb);
+            break;
+        case 3:
+            result.rgb = HardLight(Cs.rgb, Cb.rgb);        // Overlay is inverse of Hardlight
+            break;
+        case 6:
+            result.r = ColorDodge(Cb.r, Cs.r);
+            result.g = ColorDodge(Cb.g, Cs.g);
+            result.b = ColorDodge(Cb.b, Cs.b);
+            break;
+        case 7:
+            result.r = ColorBurn(Cb.r, Cs.r);
+            result.g = ColorBurn(Cb.g, Cs.g);
+            result.b = ColorBurn(Cb.b, Cs.b);
+            break;
+        case 8:
+            result.rgb = HardLight(Cb.rgb, Cs.rgb);
+            break;
+        case 9:
+            result.r = SoftLight(Cb.r, Cs.r);
+            result.g = SoftLight(Cb.g, Cs.g);
+            result.b = SoftLight(Cb.b, Cs.b);
+            break;
+        case 10:
+            result.rgb = Difference(Cb.rgb, Cs.rgb);
+            break;
+        case 11:
+            result.rgb = Exclusion(Cb.rgb, Cs.rgb);
+            break;
+        case 12:
+            result.rgb = Hue(Cb.rgb, Cs.rgb);
+            break;
+        case 13:
+            result.rgb = Saturation(Cb.rgb, Cs.rgb);
+            break;
+        case 14:
+            result.rgb = Color(Cb.rgb, Cs.rgb);
+            break;
+        case 15:
+            result.rgb = Luminosity(Cb.rgb, Cs.rgb);
+            break;
+    }
+
+    oFragColor = result;
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_composite.glsl
@@ -0,0 +1,8 @@
+/* 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/. */
+
+varying vec3 vUv0;
+varying vec3 vUv1;
+flat varying vec4 vUv1Rect;
+flat varying int vOp;
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_composite.vs.glsl
@@ -0,0 +1,50 @@
+#line 1
+/* 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/. */
+
+struct Composite {
+    ivec4 src0_src1_target_id_op;
+    int z;
+};
+
+Composite fetch_composite() {
+    PrimitiveInstance pi = fetch_prim_instance();
+
+    Composite composite;
+    composite.src0_src1_target_id_op = ivec4(pi.user_data.xy,
+                                             pi.render_task_index,
+                                             pi.sub_index);
+    composite.z = pi.z;
+
+    return composite;
+}
+
+void main(void) {
+    Composite composite = fetch_composite();
+    Tile src0 = fetch_tile(composite.src0_src1_target_id_op.x);
+    Tile src1 = fetch_tile(composite.src0_src1_target_id_op.y);
+    Tile dest = fetch_tile(composite.src0_src1_target_id_op.z);
+
+    vec2 local_pos = mix(dest.screen_origin_task_origin.zw,
+                         dest.screen_origin_task_origin.zw + dest.size_target_index.xy,
+                         aPosition.xy);
+
+    vec2 texture_size = vec2(textureSize(sCache, 0));
+    vec2 st0 = src0.screen_origin_task_origin.zw / texture_size;
+    vec2 st1 = (src0.screen_origin_task_origin.zw + src0.size_target_index.xy) / texture_size;
+    vUv0 = vec3(mix(st0, st1, aPosition.xy), src0.size_target_index.z);
+
+    st0 = vec2(src1.screen_origin_task_origin.zw) / texture_size;
+    st1 = vec2(src1.screen_origin_task_origin.zw + src1.size_target_index.xy) / texture_size;
+    vec2 local_virtual_pos = mix(dest.screen_origin_task_origin.xy,
+                                 dest.screen_origin_task_origin.xy + dest.size_target_index.xy,
+                                 aPosition.xy);
+    vec2 f = (local_virtual_pos - src1.screen_origin_task_origin.xy) / src1.size_target_index.xy;
+    vUv1 = vec3(mix(st0, st1, f), src1.size_target_index.z);
+    vUv1Rect = vec4(st0, st1);
+
+    vOp = composite.src0_src1_target_id_op.w;
+
+    gl_Position = uTransform * vec4(local_pos, composite.z, 1.0);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_gradient.fs.glsl
@@ -0,0 +1,16 @@
+/* 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/. */
+
+void main(void) {
+#ifdef WR_FEATURE_TRANSFORM
+    float alpha = 0.0;
+    vec2 local_pos = init_transform_fs(vLocalPos, vLocalRect, alpha);
+#else
+    float alpha = 1.0;
+    vec2 local_pos = vPos;
+#endif
+
+    alpha = min(alpha, do_clip());
+    oFragColor = vColor * vec4(1.0, 1.0, 1.0, alpha);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_gradient.glsl
@@ -0,0 +1,12 @@
+/* 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/. */
+
+varying vec4 vColor;
+
+#ifdef WR_FEATURE_TRANSFORM
+varying vec3 vLocalPos;
+flat varying vec4 vLocalRect;
+#else
+varying vec2 vPos;
+#endif
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_gradient.vs.glsl
@@ -0,0 +1,72 @@
+#line 1
+/* 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/. */
+
+void main(void) {
+    Primitive prim = load_primitive();
+    Gradient gradient = fetch_gradient(prim.prim_index);
+
+    GradientStop g0 = fetch_gradient_stop(prim.sub_index + 0);
+    GradientStop g1 = fetch_gradient_stop(prim.sub_index + 1);
+
+    vec4 segment_rect;
+    switch (int(gradient.kind.x)) {
+        case GRADIENT_HORIZONTAL: {
+            float x0 = mix(gradient.start_end_point.x,
+                           gradient.start_end_point.z,
+                           g0.offset.x);
+            float x1 = mix(gradient.start_end_point.x,
+                           gradient.start_end_point.z,
+                           g1.offset.x);
+            segment_rect.yw = prim.local_rect.yw;
+            segment_rect.x = x0;
+            segment_rect.z = x1 - x0;
+            } break;
+        case GRADIENT_VERTICAL: {
+            float y0 = mix(gradient.start_end_point.y,
+                           gradient.start_end_point.w,
+                           g0.offset.x);
+            float y1 = mix(gradient.start_end_point.y,
+                           gradient.start_end_point.w,
+                           g1.offset.x);
+            segment_rect.xz = prim.local_rect.xz;
+            segment_rect.y = y0;
+            segment_rect.w = y1 - y0;
+            } break;
+    }
+
+#ifdef WR_FEATURE_TRANSFORM
+    TransformVertexInfo vi = write_transform_vertex(segment_rect,
+                                                    prim.local_clip_rect,
+                                                    prim.z,
+                                                    prim.layer,
+                                                    prim.tile);
+    vLocalRect = vi.clipped_local_rect;
+    vLocalPos = vi.local_pos;
+    vec2 f = (vi.local_pos.xy - prim.local_rect.xy) / prim.local_rect.zw;
+#else
+    VertexInfo vi = write_vertex(segment_rect,
+                                 prim.local_clip_rect,
+                                 prim.z,
+                                 prim.layer,
+                                 prim.tile);
+
+    vec2 f = (vi.local_clamped_pos - segment_rect.xy) / segment_rect.zw;
+    vPos = vi.local_clamped_pos;
+#endif
+
+    write_clip(vi.global_clamped_pos, prim.clip_area);
+
+    switch (int(gradient.kind.x)) {
+        case GRADIENT_HORIZONTAL:
+            vColor = mix(g0.color, g1.color, f.x);
+            break;
+        case GRADIENT_VERTICAL:
+            vColor = mix(g0.color, g1.color, f.y);
+            break;
+        case GRADIENT_ROTATED:
+            vColor = vec4(1.0, 0.0, 1.0, 1.0);
+            break;
+    }
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_image.fs.glsl
@@ -0,0 +1,31 @@
+#line 1
+
+/* 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/. */
+
+void main(void) {
+#ifdef WR_FEATURE_TRANSFORM
+    float alpha = 0.0;
+    vec2 pos = init_transform_fs(vLocalPos, vLocalRect, alpha);
+
+    // We clamp the texture coordinate calculation here to the local rectangle boundaries,
+    // which makes the edge of the texture stretch instead of repeat.
+    vec2 relative_pos_in_rect =
+         clamp(pos, vLocalRect.xy, vLocalRect.xy + vLocalRect.zw) - vLocalRect.xy;
+#else
+    float alpha = 1.0;
+    vec2 relative_pos_in_rect = vLocalPos;
+#endif
+
+    alpha = min(alpha, do_clip());
+
+    // We calculate the particular tile this fragment belongs to, taking into
+    // account the spacing in between tiles. We only paint if our fragment does
+    // not fall into that spacing.
+    vec2 position_in_tile = mod(relative_pos_in_rect, vStretchSize + vTileSpacing);
+    vec2 st = vTextureOffset + ((position_in_tile / vStretchSize) * vTextureSize);
+    alpha = alpha * float(all(bvec2(step(position_in_tile, vStretchSize))));
+
+    oFragColor = vec4(1.0, 1.0, 1.0, alpha) * texture(sColor0, st);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_image.glsl
@@ -0,0 +1,16 @@
+/* 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/. */
+
+flat varying vec2 vTextureOffset; // Offset of this image into the texture atlas.
+flat varying vec2 vTextureSize;   // Size of the image in the texture atlas.
+flat varying vec2 vTileSpacing;   // Amount of space between tiled instances of this image.
+
+#ifdef WR_FEATURE_TRANSFORM
+varying vec3 vLocalPos;
+flat varying vec4 vLocalRect;
+flat varying vec2 vStretchSize;
+#else
+varying vec2 vLocalPos;
+flat varying vec2 vStretchSize;
+#endif
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_image.vs.glsl
@@ -0,0 +1,39 @@
+#line 1
+/* 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/. */
+
+void main(void) {
+    Primitive prim = load_primitive();
+    Image image = fetch_image(prim.prim_index);
+    ResourceRect res = fetch_resource_rect(prim.user_data.x);
+
+#ifdef WR_FEATURE_TRANSFORM
+    TransformVertexInfo vi = write_transform_vertex(prim.local_rect,
+                                                    prim.local_clip_rect,
+                                                    prim.z,
+                                                    prim.layer,
+                                                    prim.tile);
+    vLocalRect = vi.clipped_local_rect;
+    vLocalPos = vi.local_pos;
+#else
+    VertexInfo vi = write_vertex(prim.local_rect,
+                                 prim.local_clip_rect,
+                                 prim.z,
+                                 prim.layer,
+                                 prim.tile);
+    vLocalPos = vi.local_clamped_pos - vi.local_rect.p0;
+#endif
+
+    write_clip(vi.global_clamped_pos, prim.clip_area);
+
+    // vUv will contain how many times this image has wrapped around the image size.
+    vec2 texture_size = vec2(textureSize(sColor0, 0));
+    vec2 st0 = res.uv_rect.xy / texture_size;
+    vec2 st1 = res.uv_rect.zw / texture_size;
+
+    vTextureSize = st1 - st0;
+    vTextureOffset = st0;
+    vTileSpacing = image.stretch_size_and_tile_spacing.zw;
+    vStretchSize = image.stretch_size_and_tile_spacing.xy;
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_radial_gradient.fs.glsl
@@ -0,0 +1,67 @@
+/* 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/. */
+
+float offset(int index) {
+    return vOffsets[index / 4][index % 4];
+}
+
+float linearStep(float lo, float hi, float x) {
+    float d = hi - lo;
+    float v = x - lo;
+    if (d != 0.0) {
+        v /= d;
+    }
+    return clamp(v, 0.0, 1.0);
+}
+
+void main(void) {
+    vec2 cd = vEndCenter - vStartCenter;
+    vec2 pd = vPos - vStartCenter;
+    float rd = vEndRadius - vStartRadius;
+
+    // Solve for t in length(t * cd - pd) = vStartRadius + t * rd
+    // using a quadratic equation in form of At^2 - 2Bt + C = 0
+    float A = dot(cd, cd) - rd * rd;
+    float B = dot(pd, cd) + vStartRadius * rd;
+    float C = dot(pd, pd) - vStartRadius * vStartRadius;
+
+    float x;
+    if (A == 0.0) {
+        // Since A is 0, just solve for -2Bt + C = 0
+        if (B == 0.0) {
+            discard;
+        }
+        float t = 0.5 * C / B;
+        if (vStartRadius + rd * t >= 0.0) {
+            x = t;
+        } else {
+            discard;
+        }
+    } else {
+        float discr = B * B - A * C;
+        if (discr < 0.0) {
+            discard;
+        }
+        discr = sqrt(discr);
+        float t0 = (B + discr) / A;
+        float t1 = (B - discr) / A;
+        if (vStartRadius + rd * t0 >= 0.0) {
+            x = t0;
+        } else if (vStartRadius + rd * t1 >= 0.0) {
+            x = t1;
+        } else {
+            discard;
+        }
+    }
+
+    oFragColor = mix(vColors[0],
+                     vColors[1],
+                     linearStep(offset(0), offset(1), x));
+
+    for (int i=1 ; i < vStopCount-1 ; ++i) {
+        oFragColor = mix(oFragColor,
+                         vColors[i+1],
+                         linearStep(offset(i), offset(i+1), x));
+    }
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_radial_gradient.glsl
@@ -0,0 +1,12 @@
+/* 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/. */
+
+flat varying int vStopCount;
+flat varying vec2 vStartCenter;
+flat varying vec2 vEndCenter;
+flat varying float vStartRadius;
+flat varying float vEndRadius;
+varying vec2 vPos;
+flat varying vec4 vColors[MAX_STOPS_PER_RADIAL_GRADIENT];
+flat varying vec4 vOffsets[MAX_STOPS_PER_RADIAL_GRADIENT/4];
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_radial_gradient.vs.glsl
@@ -0,0 +1,35 @@
+#line 1
+/* 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/. */
+
+void main(void) {
+    Primitive prim = load_primitive();
+    RadialGradient gradient = fetch_radial_gradient(prim.prim_index);
+
+    VertexInfo vi = write_vertex(prim.local_rect,
+                                 prim.local_clip_rect,
+                                 prim.z,
+                                 prim.layer,
+                                 prim.tile);
+
+    vStopCount = int(prim.user_data.x);
+    vPos = vi.local_clamped_pos;
+
+    // Snap the start/end points to device pixel units.
+    // I'm not sure this is entirely correct, but the
+    // old render path does this, and it is needed to
+    // make the angle gradient ref tests pass. It might
+    // be better to fix this higher up in DL construction
+    // and not snap here?
+    vStartCenter = floor(0.5 + gradient.start_end_center.xy * uDevicePixelRatio) / uDevicePixelRatio;
+    vEndCenter = floor(0.5 + gradient.start_end_center.zw * uDevicePixelRatio) / uDevicePixelRatio;
+    vStartRadius = gradient.start_end_radius.x;
+    vEndRadius = gradient.start_end_radius.y;
+
+    for (int i=0 ; i < vStopCount ; ++i) {
+        GradientStop stop = fetch_gradient_stop(prim.sub_index + i);
+        vColors[i] = stop.color;
+        vOffsets[i/4][i%4] = stop.offset.x;
+    }
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_rectangle.fs.glsl
@@ -0,0 +1,16 @@
+/* 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/. */
+
+void main(void) {
+    float alpha = 1.0;
+#ifdef WR_FEATURE_TRANSFORM
+    alpha = 0.0;
+    init_transform_fs(vLocalPos, vLocalRect, alpha);
+#endif
+
+#ifdef WR_FEATURE_CLIP
+    alpha = min(alpha, do_clip());
+#endif
+    oFragColor = vColor * vec4(1.0, 1.0, 1.0, alpha);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_rectangle.glsl
@@ -0,0 +1,10 @@
+/* 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/. */
+
+varying vec4 vColor;
+
+#ifdef WR_FEATURE_TRANSFORM
+varying vec3 vLocalPos;
+flat varying vec4 vLocalRect;
+#endif
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_rectangle.vs.glsl
@@ -0,0 +1,29 @@
+#line 1
+/* 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/. */
+
+void main(void) {
+    Primitive prim = load_primitive();
+    Rectangle rect = fetch_rectangle(prim.prim_index);
+    vColor = rect.color;
+#ifdef WR_FEATURE_TRANSFORM
+    TransformVertexInfo vi = write_transform_vertex(prim.local_rect,
+                                                    prim.local_clip_rect,
+                                                    prim.z,
+                                                    prim.layer,
+                                                    prim.tile);
+    vLocalRect = vi.clipped_local_rect;
+    vLocalPos = vi.local_pos;
+#else
+    VertexInfo vi = write_vertex(prim.local_rect,
+                                 prim.local_clip_rect,
+                                 prim.z,
+                                 prim.layer,
+                                 prim.tile);
+#endif
+
+#ifdef WR_FEATURE_CLIP
+    write_clip(vi.global_clamped_pos, prim.clip_area);
+#endif
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_text_run.fs.glsl
@@ -0,0 +1,20 @@
+/* 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/. */
+
+void main(void) {
+#ifdef WR_FEATURE_SUBPIXEL_AA
+    //note: the blend mode is not compatible with clipping
+    oFragColor = texture(sColor0, vUv);
+#else
+    float alpha = texture(sColor0, vUv).a;
+#ifdef WR_FEATURE_TRANSFORM
+    float a = 0.0;
+    init_transform_fs(vLocalPos, vLocalRect, a);
+    alpha *= a;
+#endif
+    vec4 color = vColor;
+    alpha = min(alpha, do_clip());
+    oFragColor = vec4(vColor.rgb, vColor.a * alpha);
+#endif
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_text_run.glsl
@@ -0,0 +1,11 @@
+/* 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/. */
+
+flat varying vec4 vColor;
+varying vec2 vUv;
+
+#ifdef WR_FEATURE_TRANSFORM
+varying vec3 vLocalPos;
+flat varying vec4 vLocalRect;
+#endif
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_text_run.vs.glsl
@@ -0,0 +1,40 @@
+#line 1
+/* 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/. */
+
+void main(void) {
+    Primitive prim = load_primitive();
+    TextRun text = fetch_text_run(prim.prim_index);
+    Glyph glyph = fetch_glyph(prim.sub_index);
+    ResourceRect res = fetch_resource_rect(prim.user_data.x);
+
+    vec4 local_rect = vec4(glyph.offset.xy, (res.uv_rect.zw - res.uv_rect.xy) / uDevicePixelRatio);
+
+#ifdef WR_FEATURE_TRANSFORM
+    TransformVertexInfo vi = write_transform_vertex(local_rect,
+                                                    prim.local_clip_rect,
+                                                    prim.z,
+                                                    prim.layer,
+                                                    prim.tile);
+    vLocalRect = vi.clipped_local_rect;
+    vLocalPos = vi.local_pos;
+    vec2 f = (vi.local_pos.xy / vi.local_pos.z - local_rect.xy) / local_rect.zw;
+#else
+    VertexInfo vi = write_vertex(local_rect,
+                                 prim.local_clip_rect,
+                                 prim.z,
+                                 prim.layer,
+                                 prim.tile);
+    vec2 f = (vi.local_clamped_pos - vi.local_rect.p0) / (vi.local_rect.p1 - vi.local_rect.p0);
+#endif
+
+    write_clip(vi.global_clamped_pos, prim.clip_area);
+
+    vec2 texture_size = vec2(textureSize(sColor0, 0));
+    vec2 st0 = res.uv_rect.xy / texture_size;
+    vec2 st1 = res.uv_rect.zw / texture_size;
+
+    vColor = text.color;
+    vUv = mix(st0, st1, f);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_yuv_image.fs.glsl
@@ -0,0 +1,33 @@
+#line 1
+/* 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/. */
+
+void main(void) {
+#ifdef WR_FEATURE_TRANSFORM
+    float alpha = 0.0;
+    vec2 pos = init_transform_fs(vLocalPos, vLocalRect, alpha);
+
+    // We clamp the texture coordinate calculation here to the local rectangle boundaries,
+    // which makes the edge of the texture stretch instead of repeat.
+    vec2 relative_pos_in_rect =
+         clamp(pos, vLocalRect.xy, vLocalRect.xy + vLocalRect.zw) - vLocalRect.xy;
+#else
+    float alpha = 1.0;;
+    vec2 relative_pos_in_rect = vLocalPos;
+#endif
+
+    alpha = min(alpha, do_clip());
+
+    vec2 st_y = vTextureOffsetY + relative_pos_in_rect / vStretchSize * vTextureSizeY;
+    vec2 st_u = vTextureOffsetU + relative_pos_in_rect / vStretchSize * vTextureSizeUv;
+    vec2 st_v = vTextureOffsetV + relative_pos_in_rect / vStretchSize * vTextureSizeUv;
+
+    float y = texture(sColor0, st_y).r;
+    float u = texture(sColor1, st_u).r;
+    float v = texture(sColor2, st_v).r;
+
+    // See the vertex shader for an explanation of where the constants come from.
+    vec3 rgb = vYuvColorMatrix * vec3(y - 0.06275, u - 0.50196, v - 0.50196);
+    oFragColor = vec4(rgb, alpha);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_yuv_image.glsl
@@ -0,0 +1,19 @@
+/* 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/. */
+
+flat varying vec2 vTextureOffsetY; // Offset of the y plane into the texture atlas.
+flat varying vec2 vTextureOffsetU; // Offset of the u plane into the texture atlas.
+flat varying vec2 vTextureOffsetV; // Offset of the v plane into the texture atlas.
+flat varying vec2 vTextureSizeY;   // Size of the y plane in the texture atlas.
+flat varying vec2 vTextureSizeUv;  // Size of the u and v planes in the texture atlas.
+flat varying vec2 vStretchSize;
+
+flat varying mat3 vYuvColorMatrix;
+
+#ifdef WR_FEATURE_TRANSFORM
+varying vec3 vLocalPos;
+flat varying vec4 vLocalRect;
+#else
+varying vec2 vLocalPos;
+#endif
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_yuv_image.vs.glsl
@@ -0,0 +1,77 @@
+#line 1
+/* 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/. */
+
+void main(void) {
+    Primitive prim = load_primitive();
+#ifdef WR_FEATURE_TRANSFORM
+    TransformVertexInfo vi = write_transform_vertex(prim.local_rect,
+                                                    prim.local_clip_rect,
+                                                    prim.z,
+                                                    prim.layer,
+                                                    prim.tile);
+    vLocalRect = vi.clipped_local_rect;
+    vLocalPos = vi.local_pos;
+#else
+    VertexInfo vi = write_vertex(prim.local_rect,
+                                 prim.local_clip_rect,
+                                 prim.z,
+                                 prim.layer,
+                                 prim.tile);
+    vLocalPos = vi.local_clamped_pos - vi.local_rect.p0;
+#endif
+
+    YuvImage image = fetch_yuv_image(prim.prim_index);
+
+    vec2 y_texture_size = vec2(textureSize(sColor0, 0));
+    vec2 y_st0 = image.y_st_rect.xy / y_texture_size;
+    vec2 y_st1 = image.y_st_rect.zw / y_texture_size;
+
+    vTextureSizeY = y_st1 - y_st0;
+    vTextureOffsetY = y_st0;
+
+    vec2 uv_texture_size = vec2(textureSize(sColor1, 0));
+    vec2 u_st0 = image.u_st_rect.xy / uv_texture_size;
+    vec2 u_st1 = image.u_st_rect.zw / uv_texture_size;
+
+    vec2 v_st0 = image.v_st_rect.xy / uv_texture_size;
+    vec2 v_st1 = image.v_st_rect.zw / uv_texture_size;
+
+    // This assumes the U and V surfaces have the same size.
+    vTextureSizeUv = u_st1 - u_st0;
+    vTextureOffsetU = u_st0;
+    vTextureOffsetV = v_st0;
+
+    vStretchSize = image.size;
+
+    // The constants added to the Y, U and V components are applied in the fragment shader.
+    if (image.color_space == YUV_REC601) {
+        // From Rec601:
+        // [R]   [1.1643835616438356,  0.0,                 1.5960267857142858   ]   [Y -  16]
+        // [G] = [1.1643835616438358, -0.3917622900949137, -0.8129676472377708   ] x [U - 128]
+        // [B]   [1.1643835616438356,  2.017232142857143,   8.862867620416422e-17]   [V - 128]
+        //
+        // For the range [0,1] instead of [0,255].
+        vYuvColorMatrix = mat3(
+            1.16438,  0.0,      1.59603,
+            1.16438, -0.39176, -0.81297,
+            1.16438,  2.01723,  0.0
+        );
+    } else { // if (image.color_space == YUV_REC709)
+        // From Rec709:
+        // [R]   [1.1643835616438356,  4.2781193979771426e-17, 1.7927410714285714]   [Y -  16]
+        // [G] = [1.1643835616438358, -0.21324861427372963,   -0.532909328559444 ] x [U - 128]
+        // [B]   [1.1643835616438356,  2.1124017857142854,     0.0               ]   [V - 128]
+        //
+        // For the range [0,1] instead of [0,255]:
+        vYuvColorMatrix = mat3(
+            1.16438,  0.0,      1.79274,
+            1.16438, -0.21325, -0.53291,
+            1.16438,  2.11240,  0.0
+        );
+    }
+
+    write_clip(vi.global_clamped_pos, prim.clip_area);
+
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/shared.glsl
@@ -0,0 +1,51 @@
+/* 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/. */
+
+//======================================================================================
+// Vertex shader attributes and uniforms
+//======================================================================================
+#ifdef WR_VERTEX_SHADER
+    #define varying out
+
+    // Uniform inputs
+    uniform mat4 uTransform;       // Orthographic projection
+    uniform float uDevicePixelRatio;
+
+    // Attribute inputs
+    in vec3 aPosition;
+#endif
+
+//======================================================================================
+// Fragment shader attributes and uniforms
+//======================================================================================
+#ifdef WR_FRAGMENT_SHADER
+    precision highp float;
+
+    #define varying in
+
+    // Uniform inputs
+
+    // Fragment shader outputs
+    out vec4 oFragColor;
+#endif
+
+//======================================================================================
+// Shared shader uniforms
+//======================================================================================
+uniform sampler2D sColor0;
+uniform sampler2D sColor1;
+uniform sampler2D sColor2;
+uniform sampler2D sMask;
+
+//======================================================================================
+// Interpolator definitions
+//======================================================================================
+
+//======================================================================================
+// VS only types and UBOs
+//======================================================================================
+
+//======================================================================================
+// VS only functions
+//======================================================================================
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/shared_other.glsl
@@ -0,0 +1,33 @@
+/* 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/. */
+
+//======================================================================================
+// Vertex shader attributes and uniforms
+//======================================================================================
+#ifdef WR_VERTEX_SHADER
+#endif
+
+//======================================================================================
+// Fragment shader attributes and uniforms
+//======================================================================================
+#ifdef WR_FRAGMENT_SHADER
+#endif
+
+//======================================================================================
+// Interpolator definitions
+//======================================================================================
+
+//======================================================================================
+// VS only types and UBOs
+//======================================================================================
+
+//======================================================================================
+// VS only functions
+//======================================================================================
+
+//======================================================================================
+// FS only functions
+//======================================================================================
+#ifdef WR_FRAGMENT_SHADER
+#endif
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/src/batch_builder.rs
@@ -0,0 +1,43 @@
+/* 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 std::f32;
+use webrender_traits::{ColorF, BorderStyle};
+use webrender_traits::{BorderSide};
+
+//const BORDER_DASH_SIZE: f32 = 3.0;
+
+pub trait BorderSideHelpers {
+    fn border_color(&self,
+                    scale_factor_0: f32,
+                    scale_factor_1: f32,
+                    black_color_0: f32,
+                    black_color_1: f32) -> ColorF;
+}
+
+impl BorderSideHelpers for BorderSide {
+    fn border_color(&self,
+                    scale_factor_0: f32,
+                    scale_factor_1: f32,
+                    black_color_0: f32,
+                    black_color_1: f32) -> ColorF {
+        match self.style {
+            BorderStyle::Inset => {
+                if self.color.r != 0.0 || self.color.g != 0.0 || self.color.b != 0.0 {
+                    self.color.scale_rgb(scale_factor_1)
+                } else {
+                    ColorF::new(black_color_0, black_color_0, black_color_0, self.color.a)
+                }
+            }
+            BorderStyle::Outset => {
+                if self.color.r != 0.0 || self.color.g != 0.0 || self.color.b != 0.0 {
+                    self.color.scale_rgb(scale_factor_0)
+                } else {
+                    ColorF::new(black_color_1, black_color_1, black_color_1, self.color.a)
+                }
+            }
+            _ => self.color,
+        }
+    }
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/src/debug_colors.rs
@@ -0,0 +1,158 @@
+/* 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/. */
+
+#![allow(dead_code)]
+
+use webrender_traits::ColorF;
+
+// A subset of the standard CSS colors, useful for defining GPU tag colors etc.
+
+pub const INDIGO: ColorF = ColorF { r: 0.294117647059, g: 0.0, b: 0.509803921569, a: 1.0 };
+pub const GOLD: ColorF = ColorF { r: 1.0, g: 0.843137254902, b: 0.0, a: 1.0 };
+pub const FIREBRICK: ColorF = ColorF { r: 0.698039215686, g: 0.133333333333, b: 0.133333333333, a: 1.0 };
+pub const INDIANRED: ColorF = ColorF { r: 0.803921568627, g: 0.360784313725, b: 0.360784313725, a: 1.0 };
+pub const YELLOW: ColorF = ColorF { r: 1.0, g: 1.0, b: 0.0, a: 1.0 };
+pub const DARKOLIVEGREEN: ColorF = ColorF { r: 0.333333333333, g: 0.419607843137, b: 0.18431372549, a: 1.0 };
+pub const DARKSEAGREEN: ColorF = ColorF { r: 0.560784313725, g: 0.737254901961, b: 0.560784313725, a: 1.0 };
+pub const SLATEGREY: ColorF = ColorF { r: 0.439215686275, g: 0.501960784314, b: 0.564705882353, a: 1.0 };
+pub const DARKSLATEGREY: ColorF = ColorF { r: 0.18431372549, g: 0.309803921569, b: 0.309803921569, a: 1.0 };
+pub const MEDIUMVIOLETRED: ColorF = ColorF { r: 0.780392156863, g: 0.0823529411765, b: 0.521568627451, a: 1.0 };
+pub const MEDIUMORCHID: ColorF = ColorF { r: 0.729411764706, g: 0.333333333333, b: 0.827450980392, a: 1.0 };
+pub const CHARTREUSE: ColorF = ColorF { r: 0.498039215686, g: 1.0, b: 0.0, a: 1.0 };
+pub const MEDIUMSLATEBLUE: ColorF = ColorF { r: 0.482352941176, g: 0.407843137255, b: 0.933333333333, a: 1.0 };
+pub const BLACK: ColorF = ColorF { r: 0.0, g: 0.0, b: 0.0, a: 1.0 };
+pub const SPRINGGREEN: ColorF = ColorF { r: 0.0, g: 1.0, b: 0.498039215686, a: 1.0 };
+pub const CRIMSON: ColorF = ColorF { r: 0.862745098039, g: 0.078431372549, b: 0.235294117647, a: 1.0 };
+pub const LIGHTSALMON: ColorF = ColorF { r: 1.0, g: 0.627450980392, b: 0.478431372549, a: 1.0 };
+pub const BROWN: ColorF = ColorF { r: 0.647058823529, g: 0.164705882353, b: 0.164705882353, a: 1.0 };
+pub const TURQUOISE: ColorF = ColorF { r: 0.250980392157, g: 0.878431372549, b: 0.81568627451, a: 1.0 };
+pub const OLIVEDRAB: ColorF = ColorF { r: 0.419607843137, g: 0.556862745098, b: 0.137254901961, a: 1.0 };
+pub const CYAN: ColorF = ColorF { r: 0.0, g: 1.0, b: 1.0, a: 1.0 };
+pub const SILVER: ColorF = ColorF { r: 0.752941176471, g: 0.752941176471, b: 0.752941176471, a: 1.0 };
+pub const SKYBLUE: ColorF = ColorF { r: 0.529411764706, g: 0.807843137255, b: 0.921568627451, a: 1.0 };
+pub const GRAY: ColorF = ColorF { r: 0.501960784314, g: 0.501960784314, b: 0.501960784314, a: 1.0 };
+pub const DARKTURQUOISE: ColorF = ColorF { r: 0.0, g: 0.807843137255, b: 0.819607843137, a: 1.0 };
+pub const GOLDENROD: ColorF = ColorF { r: 0.854901960784, g: 0.647058823529, b: 0.125490196078, a: 1.0 };
+pub const DARKGREEN: ColorF = ColorF { r: 0.0, g: 0.392156862745, b: 0.0, a: 1.0 };
+pub const DARKVIOLET: ColorF = ColorF { r: 0.580392156863, g: 0.0, b: 0.827450980392, a: 1.0 };
+pub const DARKGRAY: ColorF = ColorF { r: 0.662745098039, g: 0.662745098039, b: 0.662745098039, a: 1.0 };
+pub const LIGHTPINK: ColorF = ColorF { r: 1.0, g: 0.713725490196, b: 0.756862745098, a: 1.0 };
+pub const TEAL: ColorF = ColorF { r: 0.0, g: 0.501960784314, b: 0.501960784314, a: 1.0 };
+pub const DARKMAGENTA: ColorF = ColorF { r: 0.545098039216, g: 0.0, b: 0.545098039216, a: 1.0 };
+pub const LIGHTGOLDENRODYELLOW: ColorF = ColorF { r: 0.980392156863, g: 0.980392156863, b: 0.823529411765, a: 1.0 };
+pub const LAVENDER: ColorF = ColorF { r: 0.901960784314, g: 0.901960784314, b: 0.980392156863, a: 1.0 };
+pub const YELLOWGREEN: ColorF = ColorF { r: 0.603921568627, g: 0.803921568627, b: 0.196078431373, a: 1.0 };
+pub const THISTLE: ColorF = ColorF { r: 0.847058823529, g: 0.749019607843, b: 0.847058823529, a: 1.0 };
+pub const VIOLET: ColorF = ColorF { r: 0.933333333333, g: 0.509803921569, b: 0.933333333333, a: 1.0 };
+pub const NAVY: ColorF = ColorF { r: 0.0, g: 0.0, b: 0.501960784314, a: 1.0 };
+pub const DIMGREY: ColorF = ColorF { r: 0.411764705882, g: 0.411764705882, b: 0.411764705882, a: 1.0 };
+pub const ORCHID: ColorF = ColorF { r: 0.854901960784, g: 0.439215686275, b: 0.839215686275, a: 1.0 };
+pub const BLUE: ColorF = ColorF { r: 0.0, g: 0.0, b: 1.0, a: 1.0 };
+pub const GHOSTWHITE: ColorF = ColorF { r: 0.972549019608, g: 0.972549019608, b: 1.0, a: 1.0 };
+pub const HONEYDEW: ColorF = ColorF { r: 0.941176470588, g: 1.0, b: 0.941176470588, a: 1.0 };
+pub const CORNFLOWERBLUE: ColorF = ColorF { r: 0.392156862745, g: 0.58431372549, b: 0.929411764706, a: 1.0 };
+pub const DARKBLUE: ColorF = ColorF { r: 0.0, g: 0.0, b: 0.545098039216, a: 1.0 };
+pub const DARKKHAKI: ColorF = ColorF { r: 0.741176470588, g: 0.717647058824, b: 0.419607843137, a: 1.0 };
+pub const MEDIUMPURPLE: ColorF = ColorF { r: 0.576470588235, g: 0.439215686275, b: 0.858823529412, a: 1.0 };
+pub const CORNSILK: ColorF = ColorF { r: 1.0, g: 0.972549019608, b: 0.862745098039, a: 1.0 };
+pub const RED: ColorF = ColorF { r: 1.0, g: 0.0, b: 0.0, a: 1.0 };
+pub const BISQUE: ColorF = ColorF { r: 1.0, g: 0.894117647059, b: 0.76862745098, a: 1.0 };
+pub const SLATEGRAY: ColorF = ColorF { r: 0.439215686275, g: 0.501960784314, b: 0.564705882353, a: 1.0 };
+pub const DARKCYAN: ColorF = ColorF { r: 0.0, g: 0.545098039216, b: 0.545098039216, a: 1.0 };
+pub const KHAKI: ColorF = ColorF { r: 0.941176470588, g: 0.901960784314, b: 0.549019607843, a: 1.0 };
+pub const WHEAT: ColorF = ColorF { r: 0.960784313725, g: 0.870588235294, b: 0.701960784314, a: 1.0 };
+pub const DEEPSKYBLUE: ColorF = ColorF { r: 0.0, g: 0.749019607843, b: 1.0, a: 1.0 };
+pub const REBECCAPURPLE: ColorF = ColorF { r: 0.4, g: 0.2, b: 0.6, a: 1.0 };
+pub const DARKRED: ColorF = ColorF { r: 0.545098039216, g: 0.0, b: 0.0, a: 1.0 };
+pub const STEELBLUE: ColorF = ColorF { r: 0.274509803922, g: 0.509803921569, b: 0.705882352941, a: 1.0 };
+pub const ALICEBLUE: ColorF = ColorF { r: 0.941176470588, g: 0.972549019608, b: 1.0, a: 1.0 };
+pub const LIGHTSLATEGREY: ColorF = ColorF { r: 0.466666666667, g: 0.533333333333, b: 0.6, a: 1.0 };
+pub const GAINSBORO: ColorF = ColorF { r: 0.862745098039, g: 0.862745098039, b: 0.862745098039, a: 1.0 };
+pub const MEDIUMTURQUOISE: ColorF = ColorF { r: 0.282352941176, g: 0.819607843137, b: 0.8, a: 1.0 };
+pub const FLORALWHITE: ColorF = ColorF { r: 1.0, g: 0.980392156863, b: 0.941176470588, a: 1.0 };
+pub const CORAL: ColorF = ColorF { r: 1.0, g: 0.498039215686, b: 0.313725490196, a: 1.0 };
+pub const PURPLE: ColorF = ColorF { r: 0.501960784314, g: 0.0, b: 0.501960784314, a: 1.0 };
+pub const LIGHTGREY: ColorF = ColorF { r: 0.827450980392, g: 0.827450980392, b: 0.827450980392, a: 1.0 };
+pub const LIGHTCYAN: ColorF = ColorF { r: 0.878431372549, g: 1.0, b: 1.0, a: 1.0 };
+pub const DARKSALMON: ColorF = ColorF { r: 0.913725490196, g: 0.588235294118, b: 0.478431372549, a: 1.0 };
+pub const BEIGE: ColorF = ColorF { r: 0.960784313725, g: 0.960784313725, b: 0.862745098039, a: 1.0 };
+pub const AZURE: ColorF = ColorF { r: 0.941176470588, g: 1.0, b: 1.0, a: 1.0 };
+pub const LIGHTSTEELBLUE: ColorF = ColorF { r: 0.690196078431, g: 0.76862745098, b: 0.870588235294, a: 1.0 };
+pub const OLDLACE: ColorF = ColorF { r: 0.992156862745, g: 0.960784313725, b: 0.901960784314, a: 1.0 };
+pub const GREENYELLOW: ColorF = ColorF { r: 0.678431372549, g: 1.0, b: 0.18431372549, a: 1.0 };
+pub const ROYALBLUE: ColorF = ColorF { r: 0.254901960784, g: 0.411764705882, b: 0.882352941176, a: 1.0 };
+pub const LIGHTSEAGREEN: ColorF = ColorF { r: 0.125490196078, g: 0.698039215686, b: 0.666666666667, a: 1.0 };
+pub const MISTYROSE: ColorF = ColorF { r: 1.0, g: 0.894117647059, b: 0.882352941176, a: 1.0 };
+pub const SIENNA: ColorF = ColorF { r: 0.627450980392, g: 0.321568627451, b: 0.176470588235, a: 1.0 };
+pub const LIGHTCORAL: ColorF = ColorF { r: 0.941176470588, g: 0.501960784314, b: 0.501960784314, a: 1.0 };
+pub const ORANGERED: ColorF = ColorF { r: 1.0, g: 0.270588235294, b: 0.0, a: 1.0 };
+pub const NAVAJOWHITE: ColorF = ColorF { r: 1.0, g: 0.870588235294, b: 0.678431372549, a: 1.0 };
+pub const LIME: ColorF = ColorF { r: 0.0, g: 1.0, b: 0.0, a: 1.0 };
+pub const PALEGREEN: ColorF = ColorF { r: 0.596078431373, g: 0.98431372549, b: 0.596078431373, a: 1.0 };
+pub const BURLYWOOD: ColorF = ColorF { r: 0.870588235294, g: 0.721568627451, b: 0.529411764706, a: 1.0 };
+pub const SEASHELL: ColorF = ColorF { r: 1.0, g: 0.960784313725, b: 0.933333333333, a: 1.0 };
+pub const MEDIUMSPRINGGREEN: ColorF = ColorF { r: 0.0, g: 0.980392156863, b: 0.603921568627, a: 1.0 };
+pub const FUCHSIA: ColorF = ColorF { r: 1.0, g: 0.0, b: 1.0, a: 1.0 };
+pub const PAPAYAWHIP: ColorF = ColorF { r: 1.0, g: 0.937254901961, b: 0.835294117647, a: 1.0 };
+pub const BLANCHEDALMOND: ColorF = ColorF { r: 1.0, g: 0.921568627451, b: 0.803921568627, a: 1.0 };
+pub const PERU: ColorF = ColorF { r: 0.803921568627, g: 0.521568627451, b: 0.247058823529, a: 1.0 };
+pub const AQUAMARINE: ColorF = ColorF { r: 0.498039215686, g: 1.0, b: 0.83137254902, a: 1.0 };
+pub const WHITE: ColorF = ColorF { r: 1.0, g: 1.0, b: 1.0, a: 1.0 };
+pub const DARKSLATEGRAY: ColorF = ColorF { r: 0.18431372549, g: 0.309803921569, b: 0.309803921569, a: 1.0 };
+pub const TOMATO: ColorF = ColorF { r: 1.0, g: 0.388235294118, b: 0.278431372549, a: 1.0 };
+pub const IVORY: ColorF = ColorF { r: 1.0, g: 1.0, b: 0.941176470588, a: 1.0 };
+pub const DODGERBLUE: ColorF = ColorF { r: 0.117647058824, g: 0.564705882353, b: 1.0, a: 1.0 };
+pub const LEMONCHIFFON: ColorF = ColorF { r: 1.0, g: 0.980392156863, b: 0.803921568627, a: 1.0 };
+pub const CHOCOLATE: ColorF = ColorF { r: 0.823529411765, g: 0.411764705882, b: 0.117647058824, a: 1.0 };
+pub const ORANGE: ColorF = ColorF { r: 1.0, g: 0.647058823529, b: 0.0, a: 1.0 };
+pub const FORESTGREEN: ColorF = ColorF { r: 0.133333333333, g: 0.545098039216, b: 0.133333333333, a: 1.0 };
+pub const DARKGREY: ColorF = ColorF { r: 0.662745098039, g: 0.662745098039, b: 0.662745098039, a: 1.0 };
+pub const OLIVE: ColorF = ColorF { r: 0.501960784314, g: 0.501960784314, b: 0.0, a: 1.0 };
+pub const MINTCREAM: ColorF = ColorF { r: 0.960784313725, g: 1.0, b: 0.980392156863, a: 1.0 };
+pub const ANTIQUEWHITE: ColorF = ColorF { r: 0.980392156863, g: 0.921568627451, b: 0.843137254902, a: 1.0 };
+pub const DARKORANGE: ColorF = ColorF { r: 1.0, g: 0.549019607843, b: 0.0, a: 1.0 };
+pub const CADETBLUE: ColorF = ColorF { r: 0.372549019608, g: 0.619607843137, b: 0.627450980392, a: 1.0 };
+pub const MOCCASIN: ColorF = ColorF { r: 1.0, g: 0.894117647059, b: 0.709803921569, a: 1.0 };
+pub const LIMEGREEN: ColorF = ColorF { r: 0.196078431373, g: 0.803921568627, b: 0.196078431373, a: 1.0 };
+pub const SADDLEBROWN: ColorF = ColorF { r: 0.545098039216, g: 0.270588235294, b: 0.0745098039216, a: 1.0 };
+pub const GREY: ColorF = ColorF { r: 0.501960784314, g: 0.501960784314, b: 0.501960784314, a: 1.0 };
+pub const DARKSLATEBLUE: ColorF = ColorF { r: 0.282352941176, g: 0.239215686275, b: 0.545098039216, a: 1.0 };
+pub const LIGHTSKYBLUE: ColorF = ColorF { r: 0.529411764706, g: 0.807843137255, b: 0.980392156863, a: 1.0 };
+pub const DEEPPINK: ColorF = ColorF { r: 1.0, g: 0.078431372549, b: 0.576470588235, a: 1.0 };
+pub const PLUM: ColorF = ColorF { r: 0.866666666667, g: 0.627450980392, b: 0.866666666667, a: 1.0 };
+pub const AQUA: ColorF = ColorF { r: 0.0, g: 1.0, b: 1.0, a: 1.0 };
+pub const DARKGOLDENROD: ColorF = ColorF { r: 0.721568627451, g: 0.525490196078, b: 0.043137254902, a: 1.0 };
+pub const MAROON: ColorF = ColorF { r: 0.501960784314, g: 0.0, b: 0.0, a: 1.0 };
+pub const SANDYBROWN: ColorF = ColorF { r: 0.956862745098, g: 0.643137254902, b: 0.376470588235, a: 1.0 };
+pub const MAGENTA: ColorF = ColorF { r: 1.0, g: 0.0, b: 1.0, a: 1.0 };
+pub const TAN: ColorF = ColorF { r: 0.823529411765, g: 0.705882352941, b: 0.549019607843, a: 1.0 };
+pub const ROSYBROWN: ColorF = ColorF { r: 0.737254901961, g: 0.560784313725, b: 0.560784313725, a: 1.0 };
+pub const PINK: ColorF = ColorF { r: 1.0, g: 0.752941176471, b: 0.796078431373, a: 1.0 };
+pub const LIGHTBLUE: ColorF = ColorF { r: 0.678431372549, g: 0.847058823529, b: 0.901960784314, a: 1.0 };
+pub const PALEVIOLETRED: ColorF = ColorF { r: 0.858823529412, g: 0.439215686275, b: 0.576470588235, a: 1.0 };
+pub const MEDIUMSEAGREEN: ColorF = ColorF { r: 0.235294117647, g: 0.701960784314, b: 0.443137254902, a: 1.0 };
+pub const SLATEBLUE: ColorF = ColorF { r: 0.41568627451, g: 0.352941176471, b: 0.803921568627, a: 1.0 };
+pub const DIMGRAY: ColorF = ColorF { r: 0.411764705882, g: 0.411764705882, b: 0.411764705882, a: 1.0 };
+pub const POWDERBLUE: ColorF = ColorF { r: 0.690196078431, g: 0.878431372549, b: 0.901960784314, a: 1.0 };
+pub const SEAGREEN: ColorF = ColorF { r: 0.180392156863, g: 0.545098039216, b: 0.341176470588, a: 1.0 };
+pub const SNOW: ColorF = ColorF { r: 1.0, g: 0.980392156863, b: 0.980392156863, a: 1.0 };
+pub const MEDIUMBLUE: ColorF = ColorF { r: 0.0, g: 0.0, b: 0.803921568627, a: 1.0 };
+pub const MIDNIGHTBLUE: ColorF = ColorF { r: 0.0980392156863, g: 0.0980392156863, b: 0.439215686275, a: 1.0 };
+pub const PALETURQUOISE: ColorF = ColorF { r: 0.686274509804, g: 0.933333333333, b: 0.933333333333, a: 1.0 };
+pub const PALEGOLDENROD: ColorF = ColorF { r: 0.933333333333, g: 0.909803921569, b: 0.666666666667, a: 1.0 };
+pub const WHITESMOKE: ColorF = ColorF { r: 0.960784313725, g: 0.960784313725, b: 0.960784313725, a: 1.0 };
+pub const DARKORCHID: ColorF = ColorF { r: 0.6, g: 0.196078431373, b: 0.8, a: 1.0 };
+pub const SALMON: ColorF = ColorF { r: 0.980392156863, g: 0.501960784314, b: 0.447058823529, a: 1.0 };
+pub const LIGHTSLATEGRAY: ColorF = ColorF { r: 0.466666666667, g: 0.533333333333, b: 0.6, a: 1.0 };
+pub const LAWNGREEN: ColorF = ColorF { r: 0.486274509804, g: 0.988235294118, b: 0.0, a: 1.0 };
+pub const LIGHTGREEN: ColorF = ColorF { r: 0.564705882353, g: 0.933333333333, b: 0.564705882353, a: 1.0 };
+pub const LIGHTGRAY: ColorF = ColorF { r: 0.827450980392, g: 0.827450980392, b: 0.827450980392, a: 1.0 };
+pub const HOTPINK: ColorF = ColorF { r: 1.0, g: 0.411764705882, b: 0.705882352941, a: 1.0 };
+pub const LIGHTYELLOW: ColorF = ColorF { r: 1.0, g: 1.0, b: 0.878431372549, a: 1.0 };
+pub const LAVENDERBLUSH: ColorF = ColorF { r: 1.0, g: 0.941176470588, b: 0.960784313725, a: 1.0 };
+pub const LINEN: ColorF = ColorF { r: 0.980392156863, g: 0.941176470588, b: 0.901960784314, a: 1.0 };
+pub const MEDIUMAQUAMARINE: ColorF = ColorF { r: 0.4, g: 0.803921568627, b: 0.666666666667, a: 1.0 };
+pub const GREEN: ColorF = ColorF { r: 0.0, g: 0.501960784314, b: 0.0, a: 1.0 };
+pub const BLUEVIOLET: ColorF = ColorF { r: 0.541176470588, g: 0.16862745098, b: 0.886274509804, a: 1.0 };
+pub const PEACHPUFF: ColorF = ColorF { r: 1.0, g: 0.854901960784, b: 0.725490196078, a: 1.0 };
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/src/debug_font_data.rs
@@ -0,0 +1,1914 @@
+/* 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/. */
+
+#[derive(Debug)]
+pub struct BakedGlyph {
+    pub x0: u32,
+    pub y0: u32,
+    pub x1: u32,
+    pub y1: u32,
+    pub xo: f32,
+    pub yo: f32,
+    pub xa: f32,
+}
+
+pub const FIRST_GLYPH_INDEX: u32 = 32;
+pub const BMP_WIDTH: u32 = 128;
+pub const BMP_HEIGHT: u32 = 128;
+pub const FONT_SIZE: u32 = 19;
+
+pub const GLYPHS: [BakedGlyph; 96] = [
+    BakedGlyph {
+        x0: 1,
+        y0: 1,
+        x1: 1,
+        y1: 1,
+        xo: 0.000000,
+        yo: 0.000000,
+        xa: 3.864407,
+    },
+    BakedGlyph {
+        x0: 2,
+        y0: 1,
+        x1: 5,
+        y1: 14,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 4.644068,
+    },
+    BakedGlyph {
+        x0: 6,
+        y0: 1,
+        x1: 11,
+        y1: 6,
+        xo: 1.000000,
+        yo: -13.000000,
+        xa: 6.644068,
+    },
+    BakedGlyph {
+        x0: 12,
+        y0: 1,
+        x1: 23,
+        y1: 13,
+        xo: 0.000000,
+        yo: -12.000000,
+        xa: 11.067797,
+    },
+    BakedGlyph {
+        x0: 24,
+        y0: 1,
+        x1: 32,
+        y1: 17,
+        xo: 1.000000,
+        yo: -14.000000,
+        xa: 9.559322,
+    },
+    BakedGlyph {
+        x0: 33,
+        y0: 1,
+        x1: 46,
+        y1: 14,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 14.084745,
+    },
+    BakedGlyph {
+        x0: 47,
+        y0: 1,
+        x1: 58,
+        y1: 14,
+        xo: 0.000000,
+        yo: -12.000000,
+        xa: 10.983051,
+    },
+    BakedGlyph {
+        x0: 59,
+        y0: 1,
+        x1: 61,
+        y1: 6,
+        xo: 1.000000,
+        yo: -13.000000,
+        xa: 4.067797,
+    },
+    BakedGlyph {
+        x0: 62,
+        y0: 1,
+        x1: 67,
+        y1: 19,
+        xo: 1.000000,
+        yo: -14.000000,
+        xa: 5.254237,
+    },
+    BakedGlyph {
+        x0: 68,
+        y0: 1,
+        x1: 72,
+        y1: 19,
+        xo: 0.000000,
+        yo: -14.000000,
+        xa: 5.254237,
+    },
+    BakedGlyph {
+        x0: 73,
+        y0: 1,
+        x1: 81,
+        y1: 8,
+        xo: 0.000000,
+        yo: -12.000000,
+        xa: 8.000000,
+    },
+    BakedGlyph {
+        x0: 82,
+        y0: 1,
+        x1: 91,
+        y1: 11,
+        xo: 0.000000,
+        yo: -10.000000,
+        xa: 9.559322,
+    },
+    BakedGlyph {
+        x0: 92,
+        y0: 1,
+        x1: 95,
+        y1: 6,
+        xo: 0.000000,
+        yo: -2.000000,
+        xa: 4.169492,
+    },
+    BakedGlyph {
+        x0: 96,
+        y0: 1,
+        x1: 101,
+        y1: 3,
+        xo: 0.000000,
+        yo: -6.000000,
+        xa: 4.779661,
+    },
+    BakedGlyph {
+        x0: 102,
+        y0: 1,
+        x1: 105,
+        y1: 4,
+        xo: 1.000000,
+        yo: -2.000000,
+        xa: 4.169492,
+    },
+    BakedGlyph {
+        x0: 106,
+        y0: 1,
+        x1: 114,
+        y1: 19,
+        xo: -1.000000,
+        yo: -14.000000,
+        xa: 6.084746,
+    },
+    BakedGlyph {
+        x0: 115,
+        y0: 1,
+        x1: 123,
+        y1: 14,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 9.559322,
+    },
+    BakedGlyph {
+        x0: 1,
+        y0: 20,
+        x1: 6,
+        y1: 32,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 9.559322,
+    },
+    BakedGlyph {
+        x0: 7,
+        y0: 20,
+        x1: 15,
+        y1: 32,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 9.559322,
+    },
+    BakedGlyph {
+        x0: 16,
+        y0: 20,
+        x1: 24,
+        y1: 33,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 9.559322,
+    },
+    BakedGlyph {
+        x0: 25,
+        y0: 20,
+        x1: 34,
+        y1: 32,
+        xo: 0.000000,
+        yo: -12.000000,
+        xa: 9.559322,
+    },
+    BakedGlyph {
+        x0: 35,
+        y0: 20,
+        x1: 43,
+        y1: 33,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 9.559322,
+    },
+    BakedGlyph {
+        x0: 44,
+        y0: 20,
+        x1: 52,
+        y1: 33,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 9.559322,
+    },
+    BakedGlyph {
+        x0: 53,
+        y0: 20,
+        x1: 61,
+        y1: 32,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 9.559322,
+    },
+    BakedGlyph {
+        x0: 62,
+        y0: 20,
+        x1: 70,
+        y1: 33,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 9.559322,
+    },
+    BakedGlyph {
+        x0: 71,
+        y0: 20,
+        x1: 79,
+        y1: 33,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 9.559322,
+    },
+    BakedGlyph {
+        x0: 80,
+        y0: 20,
+        x1: 83,
+        y1: 30,
+        xo: 1.000000,
+        yo: -9.000000,
+        xa: 4.169492,
+    },
+    BakedGlyph {
+        x0: 84,
+        y0: 20,
+        x1: 88,
+        y1: 32,
+        xo: 0.000000,
+        yo: -9.000000,
+        xa: 4.169492,
+    },
+    BakedGlyph {
+        x0: 89,
+        y0: 20,
+        x1: 98,
+        y1: 28,
+        xo: 0.000000,
+        yo: -9.000000,
+        xa: 9.559322,
+    },
+    BakedGlyph {
+        x0: 99,
+        y0: 20,
+        x1: 108,
+        y1: 26,
+        xo: 0.000000,
+        yo: -8.000000,
+        xa: 9.559322,
+    },
+    BakedGlyph {
+        x0: 109,
+        y0: 20,
+        x1: 118,
+        y1: 28,
+        xo: 0.000000,
+        yo: -9.000000,
+        xa: 9.559322,
+    },
+    BakedGlyph {
+        x0: 119,
+        y0: 20,
+        x1: 125,
+        y1: 33,
+        xo: 0.000000,
+        yo: -12.000000,
+        xa: 6.440678,
+    },
+    BakedGlyph {
+        x0: 1,
+        y0: 34,
+        x1: 15,
+        y1: 49,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 15.932203,
+    },
+    BakedGlyph {
+        x0: 16,
+        y0: 34,
+        x1: 27,
+        y1: 46,
+        xo: 0.000000,
+        yo: -12.000000,
+        xa: 10.864407,
+    },
+    BakedGlyph {
+        x0: 28,
+        y0: 34,
+        x1: 37,
+        y1: 47,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 10.677966,
+    },
+    BakedGlyph {
+        x0: 38,
+        y0: 34,
+        x1: 47,
+        y1: 47,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 10.322034,
+    },
+    BakedGlyph {
+        x0: 48,
+        y0: 34,
+        x1: 58,
+        y1: 47,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 11.898305,
+    },
+    BakedGlyph {
+        x0: 59,
+        y0: 34,
+        x1: 67,
+        y1: 46,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 9.406779,
+    },
+    BakedGlyph {
+        x0: 68,
+        y0: 34,
+        x1: 76,
+        y1: 46,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 8.813560,
+    },
+    BakedGlyph {
+        x0: 77,
+        y0: 34,
+        x1: 86,
+        y1: 47,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 11.152542,
+    },
+    BakedGlyph {
+        x0: 87,
+        y0: 34,
+        x1: 97,
+        y1: 46,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 11.728813,
+    },
+    BakedGlyph {
+        x0: 98,
+        y0: 34,
+        x1: 100,
+        y1: 46,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 4.203390,
+    },
+    BakedGlyph {
+        x0: 101,
+        y0: 34,
+        x1: 108,
+        y1: 47,
+        xo: 0.000000,
+        yo: -12.000000,
+        xa: 8.254237,
+    },
+    BakedGlyph {
+        x0: 109,
+        y0: 34,
+        x1: 118,
+        y1: 46,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 10.152542,
+    },
+    BakedGlyph {
+        x0: 1,
+        y0: 50,
+        x1: 9,
+        y1: 62,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 8.508474,
+    },
+    BakedGlyph {
+        x0: 10,
+        y0: 50,
+        x1: 23,
+        y1: 62,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 14.661017,
+    },
+    BakedGlyph {
+        x0: 24,
+        y0: 50,
+        x1: 34,
+        y1: 62,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 12.016949,
+    },
+    BakedGlyph {
+        x0: 35,
+        y0: 50,
+        x1: 47,
+        y1: 63,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 13.118644,
+    },
+    BakedGlyph {
+        x0: 48,
+        y0: 50,
+        x1: 57,
+        y1: 62,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 10.033898,
+    },
+    BakedGlyph {
+        x0: 58,
+        y0: 50,
+        x1: 70,
+        y1: 66,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 13.118644,
+    },
+    BakedGlyph {
+        x0: 71,
+        y0: 50,
+        x1: 81,
+        y1: 62,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 10.474576,
+    },
+    BakedGlyph {
+        x0: 82,
+        y0: 50,
+        x1: 91,
+        y1: 63,
+        xo: 0.000000,
+        yo: -12.000000,
+        xa: 8.762712,
+    },
+    BakedGlyph {
+        x0: 92,
+        y0: 50,
+        x1: 101,
+        y1: 62,
+        xo: 0.000000,
+        yo: -12.000000,
+        xa: 9.288136,
+    },
+    BakedGlyph {
+        x0: 102,
+        y0: 50,
+        x1: 112,
+        y1: 63,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 11.525424,
+    },
+    BakedGlyph {
+        x0: 113,
+        y0: 50,
+        x1: 124,
+        y1: 62,
+        xo: 0.000000,
+        yo: -12.000000,
+        xa: 10.576271,
+    },
+    BakedGlyph {
+        x0: 1,
+        y0: 67,
+        x1: 16,
+        y1: 79,
+        xo: 0.000000,
+        yo: -12.000000,
+        xa: 15.610169,
+    },
+    BakedGlyph {
+        x0: 17,
+        y0: 67,
+        x1: 27,
+        y1: 79,
+        xo: 0.000000,
+        yo: -12.000000,
+        xa: 10.305085,
+    },
+    BakedGlyph {
+        x0: 28,
+        y0: 67,
+        x1: 38,
+        y1: 79,
+        xo: 0.000000,
+        yo: -12.000000,
+        xa: 9.644068,
+    },
+    BakedGlyph {
+        x0: 39,
+        y0: 67,
+        x1: 48,
+        y1: 79,
+        xo: 0.000000,
+        yo: -12.000000,
+        xa: 9.491526,
+    },
+    BakedGlyph {
+        x0: 49,
+        y0: 67,
+        x1: 54,
+        y1: 85,
+        xo: 1.000000,
+        yo: -14.000000,
+        xa: 5.254237,
+    },
+    BakedGlyph {
+        x0: 55,
+        y0: 67,
+        x1: 63,
+        y1: 85,
+        xo: -1.000000,
+        yo: -14.000000,
+        xa: 6.084746,
+    },
+    BakedGlyph {
+        x0: 64,
+        y0: 67,
+        x1: 68,
+        y1: 85,
+        xo: 0.000000,
+        yo: -14.000000,
+        xa: 5.254237,
+    },
+    BakedGlyph {
+        x0: 69,
+        y0: 67,
+        x1: 77,
+        y1: 74,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 9.559322,
+    },
+    BakedGlyph {
+        x0: 78,
+        y0: 67,
+        x1: 88,
+        y1: 69,
+        xo: -1.000000,
+        yo: 2.000000,
+        xa: 8.305085,
+    },
+    BakedGlyph {
+        x0: 89,
+        y0: 67,
+        x1: 93,
+        y1: 72,
+        xo: 1.000000,
+        yo: -14.000000,
+        xa: 6.372881,
+    },
+    BakedGlyph {
+        x0: 94,
+        y0: 67,
+        x1: 102,
+        y1: 77,
+        xo: 0.000000,
+        yo: -9.000000,
+        xa: 8.627119,
+    },
+    BakedGlyph {
+        x0: 103,
+        y0: 67,
+        x1: 111,
+        y1: 82,
+        xo: 1.000000,
+        yo: -14.000000,
+        xa: 9.881356,
+    },
+    BakedGlyph {
+        x0: 112,
+        y0: 67,
+        x1: 120,
+        y1: 77,
+        xo: 0.000000,
+        yo: -9.000000,
+        xa: 7.796610,
+    },
+    BakedGlyph {
+        x0: 1,
+        y0: 86,
+        x1: 10,
+        y1: 101,
+        xo: 0.000000,
+        yo: -14.000000,
+        xa: 9.881356,
+    },
+    BakedGlyph {
+        x0: 11,
+        y0: 86,
+        x1: 20,
+        y1: 96,
+        xo: 0.000000,
+        yo: -9.000000,
+        xa: 9.288136,
+    },
+    BakedGlyph {
+        x0: 21,
+        y0: 86,
+        x1: 27,
+        y1: 100,
+        xo: 1.000000,
+        yo: -14.000000,
+        xa: 6.372881,
+    },
+    BakedGlyph {
+        x0: 28,
+        y0: 86,
+        x1: 37,
+        y1: 99,
+        xo: 0.000000,
+        yo: -9.000000,
+        xa: 9.711864,
+    },
+    BakedGlyph {
+        x0: 38,
+        y0: 86,
+        x1: 46,
+        y1: 100,
+        xo: 1.000000,
+        yo: -14.000000,
+        xa: 9.644068,
+    },
+    BakedGlyph {
+        x0: 47,
+        y0: 86,
+        x1: 49,
+        y1: 99,
+        xo: 1.000000,
+        yo: -13.000000,
+        xa: 4.016949,
+    },
+    BakedGlyph {
+        x0: 50,
+        y0: 86,
+        x1: 55,
+        y1: 103,
+        xo: -2.000000,
+        yo: -13.000000,
+        xa: 4.016949,
+    },
+    BakedGlyph {
+        x0: 56,
+        y0: 86,
+        x1: 64,
+        y1: 100,
+        xo: 1.000000,
+        yo: -14.000000,
+        xa: 8.389831,
+    },
+    BakedGlyph {
+        x0: 65,
+        y0: 86,
+        x1: 68,
+        y1: 101,
+        xo: 1.000000,
+        yo: -14.000000,
+        xa: 4.322034,
+    },
+    BakedGlyph {
+        x0: 69,
+        y0: 86,
+        x1: 82,
+        y1: 95,
+        xo: 1.000000,
+        yo: -9.000000,
+        xa: 14.627119,
+    },
+    BakedGlyph {
+        x0: 83,
+        y0: 86,
+        x1: 91,
+        y1: 95,
+        xo: 1.000000,
+        yo: -9.000000,
+        xa: 9.644068,
+    },
+    BakedGlyph {
+        x0: 92,
+        y0: 86,
+        x1: 101,
+        y1: 96,
+        xo: 0.000000,
+        yo: -9.000000,
+        xa: 9.864407,
+    },
+    BakedGlyph {
+        x0: 102,
+        y0: 86,
+        x1: 110,
+        y1: 99,
+        xo: 1.000000,
+        yo: -9.000000,
+        xa: 9.881356,
+    },
+    BakedGlyph {
+        x0: 111,
+        y0: 86,
+        x1: 120,
+        y1: 99,
+        xo: 0.000000,
+        yo: -9.000000,
+        xa: 9.881356,
+    },
+    BakedGlyph {
+        x0: 1,
+        y0: 104,
+        x1: 7,
+        y1: 113,
+        xo: 1.000000,
+        yo: -9.000000,
+        xa: 6.338983,
+    },
+    BakedGlyph {
+        x0: 8,
+        y0: 104,
+        x1: 15,
+        y1: 114,
+        xo: 0.000000,
+        yo: -9.000000,
+        xa: 7.254237,
+    },
+    BakedGlyph {
+        x0: 16,
+        y0: 104,
+        x1: 22,
+        y1: 117,
+        xo: 1.000000,
+        yo: -12.000000,
+        xa: 6.559322,
+    },
+    BakedGlyph {
+        x0: 23,
+        y0: 104,
+        x1: 31,
+        y1: 114,
+        xo: 1.000000,
+        yo: -9.000000,
+        xa: 9.644068,
+    },
+    BakedGlyph {
+        x0: 32,
+        y0: 104,
+        x1: 40,
+        y1: 113,
+        xo: 0.000000,
+        yo: -9.000000,
+        xa: 8.135593,
+    },
+    BakedGlyph {
+        x0: 41,
+        y0: 104,
+        x1: 54,
+        y1: 113,
+        xo: 0.000000,
+        yo: -9.000000,
+        xa: 13.135593,
+    },
+    BakedGlyph {
+        x0: 55,
+        y0: 104,
+        x1: 63,
+        y1: 113,
+        xo: 0.000000,
+        yo: -9.000000,
+        xa: 8.457627,
+    },
+    BakedGlyph {
+        x0: 64,
+        y0: 104,
+        x1: 72,
+        y1: 117,
+        xo: 0.000000,
+        yo: -9.000000,
+        xa: 8.033898,
+    },
+    BakedGlyph {
+        x0: 73,
+        y0: 104,
+        x1: 81,
+        y1: 113,
+        xo: 0.000000,
+        yo: -9.000000,
+        xa: 7.711864,
+    },
+    BakedGlyph {
+        x0: 82,
+        y0: 104,
+        x1: 88,
+        y1: 122,
+        xo: 0.000000,
+        yo: -14.000000,
+        xa: 5.406780,
+    },
+    BakedGlyph {
+        x0: 89,
+        y0: 104,
+        x1: 91,
+        y1: 122,
+        xo: 1.000000,
+        yo: -14.000000,
+        xa: 4.440678,
+    },
+    BakedGlyph {
+        x0: 92,
+        y0: 104,
+        x1: 97,
+        y1: 122,
+        xo: 0.000000,
+        yo: -14.000000,
+        xa: 5.406780,
+    },
+    BakedGlyph {
+        x0: 98,
+        y0: 104,
+        x1: 107,
+        y1: 108,
+        xo: 0.000000,
+        yo: -7.000000,
+        xa: 9.559322,
+    },
+    BakedGlyph {
+        x0: 108,
+        y0: 104,
+        x1: 116,
+        y1: 117,
+        xo: 0.000000,
+        yo: -13.000000,
+        xa: 8.474576,
+    },
+];
+
+pub const FONT_BITMAP: [u8; 16384] = [
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x34,0xae,0x00,0x00,0x81,0x90,0x00,0xe0,0x31,0x00,0x00,0x00,0x00,0x06,
+    0xba,0x05,0x00,0x00,0xa2,0x23,0x00,0x00,0x00,0x00,0x00,0x1a,0x0d,0x00,0x00,0x00,
+    0x00,0x16,0xb2,0xec,0xbc,0x1f,0x00,0x00,0x00,0x49,0x8c,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x0d,0x9d,0xeb,0xe5,0x89,0x03,0x00,0x00,0x00,0x00,0x81,0x90,0x00,0x00,0x00,
+    0x00,0x13,0x00,0x00,0x11,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x6c,0x6b,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x09,0x04,0x00,0x00,0x00,0x00,0x00,0x4a,0x76,0x00,
+    0x3b,0x70,0x70,0x70,0x22,0x00,0x56,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x2d,0x1e,0x00,0x00,0x18,0xac,0xea,0xd2,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x45,0xe9,0x00,0x00,0x8a,0x99,0x00,0xf6,0x2d,0x00,0x00,0x00,0x00,0x2f,
+    0xd8,0x00,0x00,0x08,0xf7,0x09,0x00,0x00,0x00,0x00,0x00,0xad,0x52,0x00,0x00,0x00,
+    0x00,0xa5,0x8f,0x0c,0x7d,0xba,0x00,0x00,0x06,0xda,0x3d,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x92,0xb9,0x20,0x28,0xd7,0x64,0x00,0x00,0x00,0x00,0x8a,0x99,0x00,0x00,0x00,
+    0x28,0xdd,0x03,0x00,0xa5,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x83,0x7f,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x45,0x00,0x00,0x00,0x00,0x00,0x80,0xaa,0x00,
+    0x54,0xa0,0xa0,0xa0,0x31,0x00,0xbb,0xe4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
+    0xe0,0x34,0x00,0x0b,0xd7,0x95,0x1d,0x40,0xe1,0x6d,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x45,0xe9,0x00,0x00,0x7a,0x88,0x00,0xe6,0x1c,0x00,0x00,0x00,0x00,0x60,
+    0xa8,0x00,0x00,0x34,0xd3,0x00,0x00,0x00,0x00,0x00,0x00,0xb2,0x6b,0x0b,0x00,0x00,
+    0x00,0xea,0x22,0x00,0x10,0xf7,0x05,0x00,0x73,0xab,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0xd6,0x50,0x00,0x00,0x81,0x98,0x00,0x00,0x00,0x00,0x7a,0x88,0x00,0x00,0x00,
+    0xb4,0x6a,0x00,0x00,0x2e,0xe4,0x0d,0x00,0x00,0x24,0xcd,0x61,0x65,0x62,0x61,0xcd,
+    0x24,0x00,0x00,0x00,0x00,0x00,0xb6,0x45,0x00,0x00,0x00,0x00,0x00,0x9b,0x83,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x09,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3c,
+    0xda,0x00,0x00,0x5e,0xd0,0x03,0x00,0x00,0x49,0xe6,0x04,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x44,0xe9,0x00,0x00,0x5a,0x67,0x00,0xbf,0x02,0x00,0x1b,0x86,0x86,0xc4,
+    0xc5,0x86,0x86,0xb0,0xd9,0x86,0x24,0x00,0x00,0x57,0xc9,0xea,0xd3,0xef,0x85,0x00,
+    0x00,0xed,0x1e,0x00,0x0d,0xf7,0x07,0x15,0xe8,0x21,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0xbf,0x65,0x00,0x00,0xc6,0x6a,0x00,0x00,0x00,0x00,0x5a,0x67,0x00,0x00,0x47,
+    0xd7,0x04,0x00,0x00,0x00,0x9d,0x85,0x00,0x00,0x0d,0x55,0x8d,0xcc,0xcc,0x8c,0x54,
+    0x0d,0x00,0x00,0x00,0x00,0x00,0xb6,0x45,0x00,0x00,0x00,0x00,0x01,0xe0,0x2e,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x96,
+    0x81,0x00,0x00,0xb7,0x80,0x00,0x00,0x00,0x04,0xf1,0x45,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x3d,0xe3,0x00,0x00,0x0f,0x12,0x00,0x21,0x00,0x00,0x13,0x5f,0x5f,0xe0,
+    0x86,0x5f,0x5f,0xc2,0x9f,0x5f,0x1a,0x00,0x16,0xf9,0x3d,0x01,0x00,0x03,0x13,0x00,
+    0x00,0xae,0x80,0x04,0x6c,0xc4,0x00,0x96,0x87,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x56,0xe2,0x1f,0x8e,0xc2,0x06,0x00,0x00,0x00,0x00,0x0f,0x12,0x00,0x00,0xc7,
+    0x4f,0x00,0x00,0x00,0x00,0x1a,0xee,0x0d,0x00,0x00,0x00,0x3b,0xb4,0xb5,0x37,0x00,
+    0x00,0x00,0x05,0x5f,0x5f,0x5f,0xd1,0x8a,0x5f,0x5f,0x3a,0x00,0x0b,0x76,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0xea,
+    0x27,0x00,0x00,0xd8,0x4f,0x00,0x00,0x00,0x00,0xc2,0x67,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x35,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0xf2,
+    0x16,0x00,0x00,0xc6,0x3f,0x00,0x00,0x00,0x65,0xc3,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x1f,0xc7,0xef,0xd1,0x29,0x2c,0xe4,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x0d,0xd1,0xfa,0x8f,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0xf6,
+    0x0a,0x00,0x00,0x00,0x00,0x00,0xca,0x3b,0x00,0x00,0x23,0xec,0x3c,0x3d,0xea,0x21,
+    0x00,0x00,0x07,0x93,0x93,0x93,0xe0,0xb0,0x93,0x93,0x5a,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x4a,0xcd,
+    0x00,0x00,0x00,0xf0,0x3e,0x00,0x00,0x00,0x00,0xb0,0x7f,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x25,0xc5,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x25,0xe4,
+    0x00,0x00,0x03,0xf4,0x0f,0x00,0x00,0x00,0x4d,0xec,0x20,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x06,0x00,0x00,0xba,0x63,0x07,0x82,0xc2,0x8d,0x0c,0x00,0x00,0x00,
+    0x24,0xda,0x7b,0x97,0xc2,0x0b,0x00,0x01,0xa2,0x03,0x00,0x00,0x00,0x00,0x2a,0xe1,
+    0x00,0x00,0x00,0x00,0x00,0x00,0xa3,0x69,0x00,0x00,0x0a,0x5b,0x00,0x00,0x5d,0x0a,
+    0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x45,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xa4,0x74,
+    0x00,0x00,0x00,0xf6,0x3a,0x00,0x00,0x00,0x00,0xab,0x85,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x09,0xaa,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1e,0x67,0xbe,
+    0x1e,0x1e,0x40,0xe4,0x1e,0x1e,0x08,0x00,0x01,0x96,0xf2,0x91,0x2b,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x4c,0xce,0x02,0x8c,0xb1,0x37,0xa3,0xa2,0x00,0x00,0x00,
+    0xc6,0x75,0x00,0x01,0x9c,0xbe,0x0a,0x28,0xd1,0x00,0x00,0x00,0x00,0x00,0x57,0xbc,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x7d,0x96,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x45,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0xf1,0x1c,
+    0x00,0x00,0x00,0xde,0x49,0x00,0x00,0x00,0x00,0xbc,0x6d,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x37,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x29,0xcb,0xea,0xe2,
+    0xcb,0xcb,0xe1,0xeb,0xcb,0xcb,0x37,0x00,0x00,0x00,0x2d,0x93,0xee,0xb4,0x1f,0x00,
+    0x00,0x00,0x00,0x00,0x05,0xd8,0x40,0x00,0xe2,0x2e,0x00,0x19,0xf5,0x02,0x00,0x13,
+    0xff,0x1d,0x00,0x00,0x02,0xa1,0xbb,0x94,0x7b,0x00,0x00,0x00,0x00,0x00,0x84,0x96,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x56,0xc4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x45,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x59,0xc0,0x00,
+    0x00,0x00,0x00,0xc4,0x6e,0x00,0x00,0x00,0x00,0xe2,0x53,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,0x52,
+    0x00,0x00,0x8b,0x7d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xa7,0xd7,0x04,
+    0x00,0x00,0x00,0x00,0x70,0xad,0x00,0x00,0xf2,0x15,0x00,0x06,0xf7,0x0a,0x00,0x10,
+    0xff,0x23,0x00,0x00,0x00,0x03,0xb2,0xfb,0x13,0x00,0x00,0x00,0x00,0x00,0x7e,0x9b,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x5b,0xbe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x31,0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb3,0x67,0x00,
+    0x00,0x00,0x00,0x77,0xb5,0x00,0x00,0x00,0x2b,0xf5,0x0f,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x33,0x8c,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe8,0x20,
+    0x00,0x00,0xbd,0x4b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0xfc,0x2c,
+    0x00,0x00,0x00,0x13,0xe7,0x23,0x00,0x00,0xc1,0x5c,0x00,0x47,0xd7,0x00,0x00,0x00,
+    0xb5,0xaf,0x0d,0x00,0x0a,0x60,0xe1,0xbd,0xa5,0x01,0x00,0x00,0x00,0x00,0x51,0xc1,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x82,0x90,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x13,0xf4,0x13,0x00,
+    0x00,0x00,0x00,0x19,0xf3,0x57,0x00,0x0b,0xc0,0x98,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x7f,0xfe,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1b,0xec,0x00,
+    0x00,0x01,0xee,0x19,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x15,0xef,0x0c,
+    0x00,0x00,0x00,0x94,0x8a,0x00,0x00,0x00,0x35,0xe8,0xc4,0xed,0x44,0x00,0x00,0x00,
+    0x15,0xa9,0xf1,0xdc,0xea,0xaf,0x1e,0x07,0xc7,0x73,0x00,0x00,0x00,0x00,0x24,0xe6,
+    0x00,0x00,0x00,0x00,0x00,0x00,0xa8,0x62,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x67,0xb3,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x3c,0xea,0xdb,0xf2,0xae,0x07,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x02,0x19,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x85,0x9b,0x61,0x52,0x6c,0xd1,0x89,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x34,0x10,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x03,0x1b,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0xf4,
+    0x0d,0x00,0x00,0x00,0x00,0x00,0xcf,0x35,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc1,0x5a,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x02,0x2c,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x15,0x69,0x8f,0xe2,0xa5,0x28,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xba,
+    0x5e,0x00,0x00,0x00,0x00,0x26,0xe9,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1d,0xf2,0x0b,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xad,0x52,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x35,
+    0xe1,0x0a,0x00,0x00,0x00,0xaf,0x71,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x76,0xa6,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x93,0x46,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0xa1,0x7d,0x00,0x00,0x3e,0xd9,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcf,0x4d,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x1a,0xd2,0x03,0x00,0xa0,0x4f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2a,0xed,0x06,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x05,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x1a,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x28,0xb6,0x00,0x14,0x93,0xe1,0xe8,0x99,0x42,0x00,0x00,0x00,
+    0x2b,0xaa,0xea,0xf3,0xca,0x4e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2c,0xbd,
+    0x30,0x00,0x00,0x00,0x63,0xbe,0xbe,0xbe,0xbe,0x94,0x00,0x00,0x00,0x00,0x00,0x0d,
+    0x5a,0x8c,0x92,0x00,0x00,0x9e,0xbe,0xbe,0xbe,0xbe,0xbe,0xbe,0x3a,0x00,0x00,0x39,
+    0xbe,0xed,0xde,0x94,0x0f,0x00,0x00,0x00,0x47,0xc5,0xf0,0xce,0x62,0x00,0x00,0x00,
+    0x6a,0x88,0x00,0x00,0x00,0x6a,0x88,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x2a,0x29,0x00,0x02,0x27,0x27,0x27,0x27,0x27,0x27,0x27,0x18,0x00,0x00,0x4a,0x0a,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x55,0xd0,0xf3,0xe5,0xa3,0x17,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x36,0xe8,0xfb,0x00,0x98,0x8a,0x21,0x16,0x68,0xfb,0x1d,0x00,0x00,
+    0x3a,0x6a,0x22,0x11,0x62,0xf8,0x36,0x00,0x00,0x00,0x00,0x00,0x00,0x1a,0xd7,0xed,
+    0x41,0x00,0x00,0x00,0x8e,0x8e,0x38,0x38,0x38,0x2c,0x00,0x00,0x00,0x00,0x42,0xdd,
+    0xa9,0x5c,0x2b,0x00,0x00,0x39,0x45,0x45,0x45,0x45,0x5a,0xf1,0x24,0x00,0x25,0xef,
+    0x54,0x05,0x17,0xad,0xb3,0x00,0x00,0x3e,0xe9,0x46,0x08,0x39,0xe1,0x65,0x00,0x00,
+    0xaf,0xd8,0x00,0x00,0x00,0xaf,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x50,0xbc,
+    0xe2,0x4f,0x00,0x0a,0xcb,0xcb,0xcb,0xcb,0xcb,0xcb,0xcb,0x7c,0x00,0x02,0xaa,0xe9,
+    0x8b,0x21,0x00,0x00,0x00,0x00,0x00,0x32,0x49,0x0e,0x16,0xae,0xb1,0x00,0x00,0x00,
+    0x00,0x0d,0xad,0xdf,0x61,0xfb,0x00,0x02,0x00,0x00,0x00,0x00,0xba,0x7a,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0xac,0x86,0x00,0x00,0x00,0x00,0x00,0x05,0xc6,0x50,0xd4,
+    0x41,0x00,0x00,0x00,0x9b,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x43,0xf1,0x36,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xae,0x71,0x00,0x00,0x7d,0xa3,
+    0x00,0x00,0x00,0x18,0xff,0x13,0x00,0xb6,0x78,0x00,0x00,0x00,0x4b,0xe0,0x04,0x00,
+    0x02,0x05,0x00,0x00,0x00,0x02,0x05,0x00,0x00,0x00,0x00,0x13,0x76,0xdf,0xbe,0x53,
+    0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1d,
+    0x84,0xe6,0xb1,0x45,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x46,0xeb,0x00,0x00,0x00,
+    0x00,0x01,0x58,0x08,0x1e,0xfb,0x00,0x00,0x00,0x00,0x00,0x00,0x84,0xa8,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0xaa,0x70,0x00,0x00,0x00,0x00,0x00,0x87,0x8a,0x00,0xd4,
+    0x41,0x00,0x00,0x00,0xad,0x4e,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xd0,0x54,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0xd4,0x02,0x00,0x00,0x8b,0x93,
+    0x00,0x00,0x00,0x1e,0xff,0x18,0x00,0xe2,0x4c,0x00,0x00,0x00,0x06,0xfb,0x24,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x9d,0xe9,0x94,0x2a,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x06,0x5b,0xc5,0xd6,0x4e,0x00,0x00,0x00,0x00,0x00,0x75,0xbd,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x1e,0xfb,0x00,0x00,0x00,0x00,0x00,0x00,0xc8,0x69,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x3e,0xe4,0x1c,0x00,0x00,0x00,0x00,0x39,0xd0,0x07,0x00,0xd4,
+    0x41,0x00,0x00,0x00,0xc5,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x4a,0xeb,0x04,0x01,
+    0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xcc,0x58,0x00,0x00,0x00,0x45,0xe5,
+    0x1a,0x00,0x00,0x74,0xc5,0x00,0x00,0xd2,0x80,0x00,0x00,0x00,0x00,0xe9,0x41,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0xd8,0xcb,0x58,0x06,0x00,0x00,
+    0x00,0x00,0x00,0x08,0x97,0x97,0x97,0x97,0x97,0x97,0x97,0x5c,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x21,0x89,0xef,0x73,0x00,0x00,0x00,0x00,0x2b,0xe6,0x32,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x1e,0xfb,0x00,0x00,0x00,0x00,0x00,0x70,0xd1,0x06,0x00,0x00,
+    0x00,0x00,0x82,0xd2,0xed,0x2e,0x00,0x00,0x00,0x00,0x03,0xcd,0x3d,0x00,0x00,0xd4,
+    0x41,0x00,0x00,0x00,0xc5,0xf6,0xe8,0xb5,0x3e,0x00,0x00,0x00,0x71,0xdc,0xc6,0xef,
+    0xf3,0xc1,0x30,0x00,0x00,0x00,0x00,0x00,0x48,0xd8,0x02,0x00,0x00,0x00,0x00,0x5c,
+    0xe9,0x8e,0x7d,0xd8,0x20,0x00,0x00,0x82,0xd4,0x1c,0x00,0x00,0x16,0xed,0x39,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x46,0xb3,0xe4,0x81,0x1b,
+    0x00,0x00,0x00,0x05,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x37,0x00,0x00,0x00,0x01,
+    0x47,0xb2,0xe5,0x81,0x1a,0x00,0x00,0x00,0x00,0x1d,0xdf,0x49,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x1e,0xfb,0x00,0x00,0x00,0x00,0x67,0xe2,0x1f,0x00,0x00,0x00,
+    0x00,0x00,0x25,0x4b,0x95,0xea,0x3f,0x00,0x00,0x00,0x63,0xac,0x00,0x00,0x00,0xd4,
+    0x41,0x00,0x00,0x00,0x00,0x00,0x17,0x6c,0xee,0x5c,0x00,0x00,0x94,0xb6,0x29,0x02,
+    0x1d,0x8a,0xef,0x11,0x00,0x00,0x00,0x00,0xaf,0x78,0x00,0x00,0x00,0x00,0x03,0x9e,
+    0xc5,0x6f,0xc8,0xc2,0x19,0x00,0x00,0x0c,0xab,0xfd,0xc6,0xcd,0xdb,0xff,0x17,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x22,0x8d,0xea,
+    0xab,0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x71,0xda,
+    0xc7,0x5b,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0xaa,0x76,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x1e,0xfb,0x00,0x00,0x00,0x6f,0xe1,0x24,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x6a,0xe0,0x01,0x00,0x02,0xd9,0x45,0x16,0x16,0x16,0xd7,
+    0x51,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0xed,0x01,0x00,0xa1,0x81,0x00,0x00,
+    0x00,0x06,0xf1,0x65,0x00,0x00,0x00,0x0d,0xf6,0x20,0x00,0x00,0x00,0x00,0x76,0xc8,
+    0x07,0x00,0x00,0x63,0xe6,0x0a,0x00,0x00,0x00,0x16,0x3b,0x26,0x3e,0xf2,0x00,0x00,
+    0x56,0x70,0x00,0x00,0x00,0x4a,0x76,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,
+    0x66,0x47,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x83,0x35,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xea,0x11,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x1e,0xfb,0x00,0x00,0x59,0xe6,0x23,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x18,0xff,0x1c,0x00,0x1f,0xcf,0xcf,0xcf,0xcf,0xcf,0xf7,
+    0xdc,0x94,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0xff,0x20,0x00,0x64,0x92,0x00,0x00,
+    0x00,0x00,0xb4,0x81,0x00,0x00,0x00,0x5a,0xc7,0x00,0x00,0x00,0x00,0x00,0xca,0x72,
+    0x00,0x00,0x00,0x00,0xdc,0x4d,0x00,0x00,0x00,0x00,0x00,0x00,0x8d,0x9b,0x00,0x00,
+    0xbb,0xe4,0x00,0x00,0x00,0x80,0xaa,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x59,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x1e,0xfb,0x00,0x13,0xec,0x3a,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x4c,0xfc,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd4,
+    0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x22,0xfe,0x0d,0x00,0x21,0xcc,0x00,0x00,
+    0x00,0x00,0xca,0x63,0x00,0x00,0x00,0x9b,0x8e,0x00,0x00,0x00,0x00,0x00,0xca,0x5e,
+    0x00,0x00,0x00,0x00,0xce,0x54,0x00,0x00,0x00,0x00,0x00,0x41,0xed,0x18,0x00,0x00,
+    0x09,0x11,0x00,0x00,0x00,0x9b,0x83,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x1e,0xfb,0x00,0x6d,0xb6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x25,0x04,0x00,0x01,0x32,0xc1,0xa8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd4,
+    0x41,0x00,0x00,0x22,0x06,0x00,0x00,0x10,0xb0,0xac,0x00,0x00,0x00,0xdd,0x5b,0x00,
+    0x00,0x4b,0xec,0x10,0x00,0x00,0x00,0xc6,0x63,0x00,0x00,0x00,0x00,0x00,0x7a,0xcb,
+    0x11,0x00,0x00,0x52,0xeb,0x14,0x00,0x00,0x03,0x22,0x88,0xda,0x4b,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x01,0xe0,0x2e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x8e,0x2b,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x1e,0xfb,0x00,0x9b,0xfb,0xf6,0xf6,0xf6,0xf6,0xf6,0x21,0x00,
+    0xa4,0xf0,0xcc,0xe7,0xfd,0x9d,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd4,
+    0x41,0x00,0x00,0x8f,0xf2,0xce,0xd2,0xf4,0xa2,0x13,0x00,0x00,0x00,0x33,0xe0,0xd8,
+    0xcd,0xe0,0x3a,0x00,0x00,0x00,0x01,0xf1,0x38,0x00,0x00,0x00,0x00,0x00,0x04,0x91,
+    0xea,0xbe,0xcf,0xdc,0x3f,0x00,0x00,0x52,0xe1,0xbf,0x89,0x10,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x0b,0x76,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x31,0xfe,0x70,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x17,0x39,0x2e,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x12,0x37,0x30,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x24,
+    0x28,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x0e,0x34,0x24,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1a,0x01,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x0e,0x7d,0xb2,0xdc,0xe5,0xb8,0x84,0x14,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x30,0xbd,0x15,0x00,0x00,0x00,0x00,0x00,0x45,0xb2,0xce,0xd4,
+    0xc2,0x9a,0x42,0x00,0x00,0x00,0x00,0x00,0x12,0x90,0xce,0xf7,0xea,0xb6,0x4c,0x00,
+    0x45,0xb1,0xcb,0xd0,0xb0,0x8b,0x34,0x00,0x00,0x00,0x00,0x5a,0xbe,0xbe,0xbe,0xbe,
+    0xbe,0xbe,0x3a,0x00,0x5a,0xbe,0xbe,0xbe,0xbe,0xbe,0xbe,0x2a,0x00,0x00,0x00,0x11,
+    0x8e,0xcb,0xf4,0xdf,0xbb,0x5f,0x00,0x5a,0x81,0x00,0x00,0x00,0x00,0x00,0x00,0xb4,
+    0x27,0x00,0x5a,0x81,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x94,0x00,0x5a,0x81,0x00,
+    0x00,0x00,0x00,0x21,0xb7,0x2f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x54,0xe4,0x9c,0x4a,0x1d,0x13,0x45,0x8c,0xe7,0x5c,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0xa6,0xec,0x7f,0x00,0x00,0x00,0x00,0x00,0x79,0xc0,0x2c,0x27,
+    0x39,0x74,0xeb,0x79,0x00,0x00,0x00,0x37,0xe5,0x9f,0x43,0x16,0x29,0x63,0x62,0x00,
+    0x79,0xc1,0x31,0x2d,0x4f,0x7b,0xe7,0xa6,0x0f,0x00,0x00,0x79,0xc2,0x41,0x41,0x41,
+    0x41,0x41,0x14,0x00,0x79,0xc2,0x41,0x41,0x41,0x41,0x41,0x0e,0x00,0x00,0x37,0xe4,
+    0x9e,0x3b,0x13,0x28,0x5b,0x83,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0xf2,
+    0x34,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x5f,0xc7,0x00,0x79,0xad,0x00,
+    0x00,0x00,0x1b,0xda,0x69,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x39,0xeb,0x42,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0xe5,0x44,0x00,0x00,
+    0x00,0x00,0x00,0x1f,0xed,0x45,0xea,0x09,0x00,0x00,0x00,0x00,0x79,0xad,0x00,0x00,
+    0x00,0x00,0x42,0xf4,0x06,0x00,0x08,0xd9,0x73,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x79,0xad,0x00,0x00,0x00,0x00,0x10,0xb6,0xa5,0x00,0x00,0x79,0xad,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0xda,0x73,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0xf2,
+    0x34,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x5f,0xc7,0x00,0x79,0xad,0x00,
+    0x00,0x1b,0xd9,0x6b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x07,0xd6,0x67,0x00,0x00,0x19,0x63,0x86,0x70,0x36,0x00,0x53,0xe2,0x0e,0x00,
+    0x00,0x00,0x00,0x89,0x93,0x00,0xcb,0x61,0x00,0x00,0x00,0x00,0x79,0xad,0x00,0x00,
+    0x00,0x00,0x1b,0xfd,0x13,0x00,0x72,0xd4,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x19,0xf0,0x4d,0x00,0x79,0xad,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x72,0xd1,0x03,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0xf2,
+    0x34,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x5f,0xc7,0x00,0x79,0xad,0x00,
+    0x1d,0xd9,0x67,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x4f,0xd4,0x02,0x00,0x3f,0xe7,0x94,0x64,0x8d,0xe1,0x00,0x00,0xc8,0x50,0x00,
+    0x00,0x00,0x08,0xea,0x29,0x00,0x60,0xc9,0x00,0x00,0x00,0x00,0x79,0xad,0x00,0x00,
+    0x00,0x00,0x80,0xc2,0x00,0x00,0xac,0x7c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0xa0,0x8d,0x00,0x79,0xad,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xac,0x81,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0xf2,
+    0x34,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x5f,0xc7,0x00,0x79,0xad,0x22,
+    0xda,0x5f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x7b,0x99,0x00,0x05,0xdc,0x5a,0x00,0x00,0x34,0xe1,0x00,0x00,0x91,0x82,0x00,
+    0x00,0x00,0x5d,0xc5,0x00,0x00,0x0b,0xf1,0x32,0x00,0x00,0x00,0x79,0xdf,0x9c,0x9c,
+    0xa6,0xd5,0xcc,0x12,0x00,0x00,0xd7,0x5a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0xbb,0x00,0x79,0xe3,0xa9,0xa9,0xa9,
+    0xa9,0x75,0x00,0x00,0x79,0xdf,0x9c,0x9c,0x9c,0x9c,0x5c,0x00,0x00,0xd7,0x5c,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x79,0xe2,0xa4,0xa4,0xa4,0xa4,0xa4,0xa4,0xfa,
+    0x34,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x5f,0xc7,0x00,0x79,0xcf,0xe6,
+    0x5b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0xa7,0x70,0x00,0x2b,0xf3,0x01,0x00,0x00,0x34,0xe1,0x00,0x00,0x6c,0xab,0x00,
+    0x00,0x00,0xc1,0x64,0x00,0x00,0x00,0x99,0x97,0x00,0x00,0x00,0x79,0xcc,0x5f,0x5f,
+    0x66,0x80,0xd4,0xc1,0x0f,0x00,0xe2,0x52,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x6f,0xc6,0x00,0x79,0xc7,0x52,0x52,0x52,
+    0x52,0x39,0x00,0x00,0x79,0xcd,0x63,0x63,0x63,0x63,0x3b,0x00,0x00,0xe2,0x52,0x00,
+    0x00,0x00,0x00,0x00,0x16,0x8f,0x00,0x79,0xca,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0xf7,
+    0x34,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x5f,0xc7,0x00,0x79,0xc5,0xba,
+    0xd2,0x25,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0xb9,0x60,0x00,0x3d,0xe4,0x00,0x00,0x00,0x34,0xe1,0x00,0x00,0x79,0x98,0x00,
+    0x00,0x24,0xfb,0x41,0x34,0x34,0x34,0x68,0xf0,0x0a,0x00,0x00,0x79,0xad,0x00,0x00,
+    0x00,0x00,0x08,0xce,0x80,0x00,0xbb,0x72,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x95,0x98,0x00,0x79,0xad,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xbb,0x72,0x00,
+    0x00,0x00,0x00,0x00,0x27,0xff,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0xf2,
+    0x34,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x5f,0xc7,0x00,0x79,0xad,0x01,
+    0x74,0xea,0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x8f,0x87,0x00,0x16,0xfd,0x15,0x00,0x00,0x34,0xe1,0x00,0x00,0xb4,0x6d,0x00,
+    0x00,0x85,0xe3,0xc7,0xc7,0xc7,0xc7,0xc7,0xee,0x5e,0x00,0x00,0x79,0xad,0x00,0x00,
+    0x00,0x00,0x00,0x89,0xb2,0x00,0x90,0xae,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x07,0xdb,0x66,0x00,0x79,0xad,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x90,0xaf,0x00,
+    0x00,0x00,0x00,0x00,0x27,0xff,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0xf2,
+    0x34,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x76,0xc0,0x00,0x79,0xad,0x00,
+    0x00,0x50,0xec,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x64,0xb2,0x00,0x00,0xa8,0xae,0x0e,0x00,0x3d,0xfb,0x16,0x4b,0xec,0x0f,0x00,
+    0x02,0xe2,0x46,0x00,0x00,0x00,0x00,0x00,0x7c,0xbf,0x00,0x00,0x79,0xad,0x00,0x00,
+    0x00,0x00,0x00,0xb4,0x90,0x00,0x21,0xf5,0x34,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x86,0xce,0x06,0x00,0x79,0xad,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x22,0xf6,0x32,
+    0x00,0x00,0x00,0x00,0x27,0xff,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0xf2,
+    0x34,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0xb8,0xa5,0x00,0x79,0xad,0x00,
+    0x00,0x00,0x52,0xee,0x31,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x2a,0xf3,0x24,0x00,0x0f,0x96,0xe4,0xe3,0xc4,0xa4,0xf0,0xcd,0x33,0x00,0x00,
+    0x45,0xe5,0x03,0x00,0x00,0x00,0x00,0x00,0x21,0xfd,0x22,0x00,0x79,0xad,0x00,0x00,
+    0x00,0x21,0x8d,0xed,0x2a,0x00,0x00,0x74,0xec,0x50,0x04,0x00,0x00,0x11,0x33,0x00,
+    0x79,0xad,0x00,0x00,0x0d,0x37,0xb5,0xdf,0x27,0x00,0x00,0x79,0xad,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0xec,
+    0x53,0x05,0x00,0x00,0x28,0xff,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0xf2,
+    0x34,0x00,0x79,0xad,0x00,0x4f,0x28,0x00,0x04,0x48,0xf5,0x51,0x00,0x79,0xad,0x00,
+    0x00,0x00,0x00,0x6e,0xe1,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x8a,0xbf,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0xa4,0x8d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc2,0x81,0x00,0x65,0xf3,0xeb,0xe4,
+    0xef,0xe5,0x97,0x29,0x00,0x00,0x00,0x00,0x4c,0xdd,0xf5,0xd1,0xe2,0xf7,0x95,0x00,
+    0x65,0xf3,0xef,0xeb,0xf0,0xcc,0x74,0x07,0x00,0x00,0x00,0x79,0xff,0xff,0xff,0xff,
+    0xff,0xff,0xbe,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x4b,
+    0xd9,0xf7,0xd1,0xd4,0xf2,0xd1,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0xf2,
+    0x34,0x00,0x79,0xad,0x00,0x75,0xf0,0xd8,0xe9,0xfd,0x86,0x02,0x00,0x79,0xad,0x00,
+    0x00,0x00,0x00,0x00,0xad,0xa9,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x08,0xcf,0xbd,0x26,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x11,0x15,
+    0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x15,0x3a,0x30,0x0a,0x00,0x00,
+    0x00,0x02,0x10,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x10,0x34,0x24,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x30,0x33,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x07,0x78,0xeb,0xba,0x91,0x72,0x7c,0x72,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x12,0x41,0x68,0x84,0x76,0x44,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x5a,0x81,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb7,0x37,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x6e,0x77,0x00,0x00,0x5a,0x8e,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x7a,0x5d,0x00,0x00,0x00,0x13,0x93,0xcd,0xf3,0xd3,0x9e,0x1e,0x00,0x00,0x00,0x00,
+    0x44,0xaf,0xca,0xd3,0xbc,0x9b,0x39,0x00,0x00,0x00,0x00,0x00,0x13,0x93,0xcd,0xf3,
+    0xd3,0x9e,0x1e,0x00,0x00,0x00,0x00,0x44,0xaf,0xca,0xd3,0xbc,0x99,0x36,0x00,0x00,
+    0x00,0x00,0x00,0x09,0x63,0xab,0xee,0xed,0xb8,0x4b,0x00,0x00,0x84,0xbe,0xbe,0xbe,
+    0xbe,0xbe,0xbe,0xbe,0xbb,0x00,0x64,0x77,0x00,0x00,0x00,0x00,0x00,0x13,0xbe,0x0a,
+    0x00,0x85,0x6f,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0xb1,0x34,0x00,0x00,0x00,0x00,
+    0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0xff,0xc4,0x01,0x00,0x00,
+    0x00,0x00,0x00,0x1a,0xf4,0xb2,0x00,0x00,0x79,0xff,0x78,0x00,0x00,0x00,0x00,0x00,
+    0xa4,0x7d,0x00,0x00,0x3c,0xe8,0x9c,0x3f,0x16,0x39,0x8c,0xee,0x4f,0x00,0x00,0x00,
+    0x79,0xc2,0x34,0x2b,0x3d,0x7d,0xed,0x83,0x00,0x00,0x00,0x3c,0xe8,0x9c,0x3f,0x16,
+    0x39,0x8c,0xee,0x4f,0x00,0x00,0x00,0x79,0xc2,0x35,0x2b,0x3c,0x7b,0xed,0x78,0x00,
+    0x00,0x00,0x00,0x6a,0xc2,0x30,0x08,0x1e,0x5e,0x58,0x00,0x00,0x2d,0x41,0x41,0x41,
+    0xf2,0x6b,0x41,0x41,0x40,0x00,0x86,0xa0,0x00,0x00,0x00,0x00,0x00,0x1a,0xff,0x0d,
+    0x00,0x63,0xe1,0x01,0x00,0x00,0x00,0x00,0x00,0x3d,0xed,0x07,0x00,0x00,0x00,0x00,
+    0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x21,0xef,0xdd,0x4e,0x00,0x00,
+    0x00,0x00,0x00,0x98,0xcd,0xc8,0x00,0x00,0x79,0xc4,0xe6,0x4b,0x00,0x00,0x00,0x00,
+    0xa4,0x7d,0x00,0x09,0xdd,0x71,0x00,0x00,0x00,0x00,0x00,0x53,0xed,0x16,0x00,0x00,
+    0x79,0xad,0x00,0x00,0x00,0x00,0x32,0xfe,0x27,0x00,0x09,0xdd,0x71,0x00,0x00,0x00,
+    0x00,0x00,0x53,0xed,0x16,0x00,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x3b,0xfd,0x1a,
+    0x00,0x00,0x02,0xd8,0x2b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0xee,0x38,0x00,0x00,0x00,0x00,0x86,0xa0,0x00,0x00,0x00,0x00,0x00,0x1a,0xff,0x0d,
+    0x00,0x0f,0xf7,0x3a,0x00,0x00,0x00,0x00,0x00,0x95,0x98,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x37,0xd8,0x61,0xcb,0x01,0x00,
+    0x00,0x00,0x1f,0xea,0x4a,0xdd,0x00,0x00,0x79,0xa9,0x3d,0xe9,0x1f,0x00,0x00,0x00,
+    0xa4,0x7d,0x00,0x76,0xd4,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0xb9,0x94,0x00,0x00,
+    0x79,0xad,0x00,0x00,0x00,0x00,0x00,0xdc,0x5b,0x00,0x76,0xd4,0x03,0x00,0x00,0x00,
+    0x00,0x00,0x00,0xb9,0x94,0x00,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0xeb,0x49,
+    0x00,0x00,0x09,0xfc,0x2f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0xee,0x38,0x00,0x00,0x00,0x00,0x86,0xa0,0x00,0x00,0x00,0x00,0x00,0x1a,0xff,0x0d,
+    0x00,0x00,0xac,0x93,0x00,0x00,0x00,0x00,0x04,0xe9,0x3c,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x4e,0xc4,0x05,0xdf,0x4a,0x00,
+    0x00,0x00,0x97,0x84,0x27,0xf3,0x00,0x00,0x79,0xa9,0x00,0x79,0xc8,0x06,0x00,0x00,
+    0xa4,0x7d,0x00,0xad,0x81,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x62,0xcb,0x00,0x00,
+    0x79,0xad,0x00,0x00,0x00,0x00,0x01,0xed,0x47,0x00,0xad,0x81,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x62,0xcb,0x00,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x09,0xf9,0x1f,
+    0x00,0x00,0x00,0xaf,0xc5,0x1d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0xee,0x38,0x00,0x00,0x00,0x00,0x86,0xa0,0x00,0x00,0x00,0x00,0x00,0x1a,0xff,0x0d,
+    0x00,0x00,0x4f,0xea,0x05,0x00,0x00,0x00,0x49,0xdc,0x01,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0xb1,0x00,0x6a,0xc2,0x00,
+    0x00,0x18,0xed,0x15,0x14,0xff,0x09,0x00,0x79,0xa9,0x00,0x02,0xbc,0x88,0x00,0x00,
+    0xa4,0x7d,0x00,0xd8,0x5c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3e,0xf4,0x02,0x00,
+    0x79,0xad,0x00,0x00,0x00,0x00,0x75,0xeb,0x10,0x00,0xd8,0x5c,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x3e,0xf4,0x02,0x00,0x79,0xad,0x00,0x00,0x00,0x08,0x95,0xc4,0x00,
+    0x00,0x00,0x00,0x0e,0x9f,0xf5,0x9e,0x2f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0xee,0x38,0x00,0x00,0x00,0x00,0x86,0xa0,0x00,0x00,0x00,0x00,0x00,0x1a,0xff,0x0d,
+    0x00,0x00,0x05,0xe8,0x4a,0x00,0x00,0x00,0xa4,0x7c,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x75,0x9d,0x00,0x09,0xe8,0x3a,
+    0x00,0x88,0x92,0x00,0x03,0xfd,0x1b,0x00,0x79,0xa9,0x00,0x00,0x1b,0xea,0x3c,0x00,
+    0xa4,0x7d,0x00,0xe1,0x54,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x35,0xfa,0x05,0x00,
+    0x79,0xd9,0x8a,0x90,0xa1,0xdb,0xda,0x38,0x00,0x00,0xe2,0x54,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x35,0xfc,0x06,0x00,0x79,0xe9,0xba,0xbd,0xc9,0xf2,0x90,0x0f,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x25,0x92,0xf2,0xa9,0x12,0x00,0x00,0x00,0x00,0x00,0x00,
+    0xee,0x38,0x00,0x00,0x00,0x00,0x86,0xa0,0x00,0x00,0x00,0x00,0x00,0x1a,0xff,0x0d,
+    0x00,0x00,0x00,0x8c,0xa9,0x00,0x00,0x0f,0xf3,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x83,0x90,0x00,0x00,0x79,0xb0,
+    0x0d,0xea,0x1f,0x00,0x00,0xf2,0x29,0x00,0x79,0xa9,0x00,0x00,0x00,0x57,0xdc,0x0b,
+    0xa4,0x7d,0x00,0xb8,0x77,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x59,0xd6,0x00,0x00,
+    0x79,0xd2,0x75,0x6f,0x5a,0x37,0x01,0x00,0x00,0x00,0xb9,0x77,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x59,0xdc,0x00,0x00,0x79,0xc2,0x41,0x40,0x4a,0xf0,0x40,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x17,0xc5,0xb4,0x00,0x00,0x00,0x00,0x00,0x00,
+    0xee,0x38,0x00,0x00,0x00,0x00,0x84,0xa4,0x00,0x00,0x00,0x00,0x00,0x1f,0xff,0x0b,
+    0x00,0x00,0x00,0x29,0xf7,0x12,0x00,0x66,0xb8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x91,0x85,0x00,0x00,0x10,0xf0,
+    0x96,0xa0,0x00,0x00,0x00,0xe7,0x38,0x00,0x79,0xa9,0x00,0x00,0x00,0x00,0xab,0x8a,
+    0xa4,0x7d,0x00,0x8b,0xb8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x9a,0xa9,0x00,0x00,
+    0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x8b,0xb8,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x9a,0xab,0x00,0x00,0x79,0xad,0x00,0x00,0x00,0x62,0xe3,0x12,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x34,0xfd,0x0c,0x00,0x00,0x00,0x00,0x00,
+    0xee,0x38,0x00,0x00,0x00,0x00,0x6b,0xc2,0x00,0x00,0x00,0x00,0x00,0x3e,0xef,0x00,
+    0x00,0x00,0x00,0x00,0xbe,0x6e,0x00,0xca,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x9f,0x7a,0x00,0x00,0x00,0x8e,
+    0xff,0x2d,0x00,0x00,0x00,0xdb,0x47,0x00,0x79,0xa9,0x00,0x00,0x00,0x00,0x16,0xe8,
+    0xcf,0x7d,0x00,0x1d,0xf3,0x43,0x00,0x00,0x00,0x00,0x00,0x29,0xf7,0x31,0x00,0x00,
+    0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1d,0xf4,0x43,0x00,0x00,0x00,
+    0x00,0x00,0x29,0xf9,0x32,0x00,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0xb1,0xa4,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3a,0xe9,0x09,0x00,0x00,0x00,0x00,0x00,
+    0xee,0x38,0x00,0x00,0x00,0x00,0x3e,0xf3,0x16,0x00,0x00,0x00,0x00,0x86,0xc0,0x00,
+    0x00,0x00,0x00,0x00,0x55,0xd7,0x35,0xe4,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xad,0x6f,0x00,0x00,0x00,0x15,
+    0x46,0x00,0x00,0x00,0x00,0xd0,0x56,0x00,0x79,0xa9,0x00,0x00,0x00,0x00,0x00,0x61,
+    0xff,0x7d,0x00,0x00,0x6b,0xf1,0x5c,0x05,0x00,0x03,0x4c,0xe9,0x84,0x00,0x00,0x00,
+    0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0xf1,0x5c,0x05,0x00,
+    0x03,0x4c,0xe9,0x84,0x00,0x00,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x1b,0xf0,0x44,
+    0x00,0x00,0x15,0x47,0x02,0x00,0x00,0x15,0xb7,0x7b,0x00,0x00,0x00,0x00,0x00,0x00,
+    0xee,0x38,0x00,0x00,0x00,0x00,0x00,0xc4,0xb7,0x14,0x00,0x00,0x4f,0xf5,0x4a,0x00,
+    0x00,0x00,0x00,0x00,0x04,0xe1,0xd3,0x7b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x79,0xff,0xff,0xff,0xff,0xff,0xff,0x34,0x00,0xbb,0x64,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0xc4,0x65,0x00,0x79,0xa9,0x00,0x00,0x00,0x00,0x00,0x01,
+    0xc4,0x7d,0x00,0x00,0x00,0x41,0xd4,0xf7,0xd4,0xf4,0xde,0x52,0x00,0x00,0x00,0x00,
+    0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x43,0xd4,0xf7,0xd4,
+    0xf4,0xd5,0x5f,0x00,0x00,0x00,0x00,0x79,0xad,0x00,0x00,0x00,0x00,0x00,0x75,0xcc,
+    0x01,0x00,0x33,0xda,0xee,0xc9,0xd9,0xdc,0x99,0x10,0x00,0x00,0x00,0x00,0x00,0x00,
+    0xee,0x38,0x00,0x00,0x00,0x00,0x00,0x13,0xaf,0xf6,0xd5,0xe7,0xeb,0x58,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x75,0xf7,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x35,0x15,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x13,0x94,
+    0xbe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x1b,0x38,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x32,0x1f,0x01,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,
+    0xd6,0xa9,0x37,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x0a,0x75,0xce,0xf0,0x16,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x3e,0xa4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x28,0xb1,
+    0x00,0x18,0xba,0x29,0x00,0x00,0x00,0x00,0x07,0xac,0x40,0x00,0x77,0x85,0x00,0x00,
+    0x00,0x00,0x00,0x07,0xb2,0x32,0x00,0x06,0xbe,0xbe,0xbe,0xbe,0xbe,0xbe,0xbe,0x7a,
+    0x00,0x15,0x45,0x45,0x45,0x08,0x00,0x18,0x33,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x3c,0x45,0x45,0x27,0x00,0x00,0x00,0x07,0xb1,0x65,0x00,0x00,0x00,0x00,0x12,0xcf,
+    0xcf,0xcf,0xcf,0xcf,0xcf,0xcf,0xcf,0x51,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x1c,
+    0xc9,0xea,0xe5,0xad,0x20,0x00,0x00,0x02,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x1e,0xa5,0xde,0xea,0xc5,0x1a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x2c,0xfc,0x09,0x00,0x00,0x00,0x00,0x39,0x1d,0x00,0x00,0x00,0x00,0x62,0xc5,
+    0x00,0x00,0x81,0xc9,0x04,0x00,0x00,0x00,0x87,0xbb,0x01,0x00,0x28,0xf8,0x29,0x00,
+    0x00,0x00,0x00,0x6f,0xc4,0x01,0x00,0x02,0x41,0x41,0x41,0x41,0x41,0x49,0xe8,0x72,
+    0x00,0x4e,0xe4,0x93,0x93,0x11,0x00,0x20,0xee,0x07,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x7f,0x93,0xc8,0x8f,0x00,0x00,0x00,0x75,0xae,0xe3,0x14,0x00,0x00,0x00,0x03,0x23,
+    0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x0d,0x00,0xae,0x6d,0x00,0x00,0x00,0x00,0x09,
+    0x35,0x12,0x1b,0x8f,0xd4,0x03,0x00,0x89,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x1f,0xeb,0xb4,0x47,0x13,0x3e,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x05,0xf9,0x33,0x00,0x00,0x00,0x05,0xec,0x96,0x00,0x00,0x00,0x00,0x94,0x96,
+    0x00,0x00,0x05,0xcd,0x78,0x00,0x00,0x37,0xec,0x1d,0x00,0x00,0x00,0x95,0xa9,0x00,
+    0x00,0x00,0x0c,0xe7,0x35,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xa4,0xaa,0x01,
+    0x00,0x4e,0xbe,0x00,0x00,0x00,0x00,0x00,0xc4,0x51,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x7d,0x8f,0x00,0x00,0x14,0xe3,0x19,0x7d,0x93,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x32,0xe0,0x76,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x05,0xef,0x2b,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0xa3,0xb3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0xd2,0x62,0x00,0x00,0x00,0x47,0xee,0xe7,0x02,0x00,0x00,0x00,0xc7,0x66,
+    0x00,0x00,0x00,0x2c,0xf1,0x2b,0x08,0xd6,0x5d,0x00,0x00,0x00,0x00,0x15,0xee,0x35,
+    0x00,0x00,0x81,0xa2,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0xd9,0x0d,0x00,
+    0x00,0x4e,0xbe,0x00,0x00,0x00,0x00,0x00,0x6b,0xab,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x7d,0x8f,0x00,0x00,0x92,0x7e,0x00,0x0a,0xdf,0x27,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x16,0xc2,0x3d,0x00,0x00,0x00,
+    0x03,0x27,0x3b,0x24,0xd7,0x44,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0xe6,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x9f,0x96,0x00,0x00,0x00,0x9c,0x7a,0xe1,0x3e,0x00,0x00,0x04,0xf5,0x31,
+    0x00,0x00,0x00,0x00,0x75,0xcb,0x8e,0xad,0x00,0x00,0x00,0x00,0x00,0x00,0x69,0xcd,
+    0x03,0x23,0xe7,0x17,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2b,0xee,0x2f,0x00,0x00,
+    0x00,0x4e,0xbe,0x00,0x00,0x00,0x00,0x00,0x16,0xf2,0x0e,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x7d,0x8f,0x00,0x26,0xe0,0x0b,0x00,0x00,0x61,0xb0,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x45,
+    0xde,0xd0,0xb7,0xce,0xfc,0x45,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x08,0xfe,0x2b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x6a,0xca,0x00,0x00,0x05,0xec,0x22,0x8d,0x92,0x00,0x00,0x2d,0xf5,0x05,
+    0x00,0x00,0x00,0x00,0x03,0xcb,0xf2,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0xc7,
+    0x6e,0xb5,0x69,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xd0,0x6d,0x00,0x00,0x00,
+    0x00,0x4e,0xbe,0x00,0x00,0x00,0x00,0x00,0x00,0xb7,0x60,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x7d,0x8f,0x00,0xaf,0x62,0x00,0x00,0x00,0x02,0xd0,0x3f,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0xf0,
+    0x54,0x00,0x00,0x00,0xd4,0x45,0x00,0x8a,0xb6,0xa9,0xdd,0xd7,0x9c,0x15,0x00,0x00,
+    0x00,0xf0,0x34,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x34,0xf9,0x09,0x00,0x48,0xc6,0x00,0x33,0xe4,0x03,0x00,0x66,0xc3,0x00,
+    0x00,0x00,0x00,0x00,0x1e,0xeb,0xe9,0x52,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2e,
+    0xf4,0xc9,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x87,0xb9,0x01,0x00,0x00,0x00,
+    0x00,0x4e,0xbe,0x00,0x00,0x00,0x00,0x00,0x00,0x5e,0xba,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x7d,0x8f,0x00,0x31,0x03,0x00,0x00,0x00,0x00,0x26,0x0d,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0xfc,
+    0x04,0x00,0x00,0x00,0xd4,0x45,0x00,0x8a,0xdb,0x56,0x19,0x2e,0x95,0xe3,0x11,0x00,
+    0x00,0xbf,0x79,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x04,0xf0,0x41,0x00,0xa7,0x6a,0x00,0x00,0xd6,0x4a,0x00,0xa5,0x84,0x00,
+    0x00,0x00,0x00,0x02,0xbd,0x79,0x49,0xe9,0x17,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0xc0,0x69,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x35,0xec,0x1c,0x00,0x00,0x00,0x00,
+    0x00,0x4e,0xbe,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0xf3,0x18,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x7d,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0xf1,
+    0x58,0x00,0x00,0x00,0xd4,0x45,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0xb4,0x83,0x00,
+    0x00,0x42,0xef,0x4c,0x01,0x00,0x0b,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0xb2,0x7f,0x12,0xef,0x12,0x00,0x00,0x76,0xaf,0x00,0xe4,0x41,0x00,
+    0x00,0x00,0x00,0x6f,0xc7,0x03,0x00,0x9b,0xb0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0xbe,0x68,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0xcf,0x66,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x4e,0xbe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xaa,0x6e,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x7d,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x4b,
+    0xe4,0xdf,0xce,0xdb,0xf0,0x39,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x5b,0xc7,0x00,
+    0x00,0x00,0x50,0xe1,0xf1,0xda,0xee,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x6d,0xc7,0x6c,0xa2,0x00,0x00,0x00,0x17,0xf6,0x3e,0xf4,0x08,0x00,
+    0x00,0x00,0x1c,0xed,0x29,0x00,0x00,0x0f,0xe4,0x52,0x00,0x00,0x00,0x00,0x00,0x00,
+    0xbe,0x68,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0xc3,0x01,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x4e,0xbe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0xc8,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x7d,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x03,0x1f,0x2c,0x1a,0x01,0x00,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x44,0xe8,0x00,
+    0x00,0x00,0x00,0x00,0x15,0x23,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x1c,0xfb,0xd9,0x3a,0x00,0x00,0x00,0x00,0xaa,0xd7,0xad,0x00,0x00,
+    0x00,0x00,0xa8,0x82,0x00,0x00,0x00,0x00,0x53,0xdf,0x09,0x00,0x00,0x00,0x00,0x00,
+    0xbe,0x68,0x00,0x00,0x00,0x00,0x00,0x12,0xec,0x32,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x4e,0xbe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xef,0x23,0x00,0x00,0x00,
+    0x00,0x00,0x7d,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x80,0xcd,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0xc6,0xd0,0x00,0x00,0x00,0x00,0x00,0x3e,0xff,0x5c,0x00,0x00,
+    0x00,0x40,0xe4,0x0a,0x00,0x00,0x00,0x00,0x00,0xbd,0x80,0x00,0x00,0x00,0x00,0x00,
+    0xbe,0x68,0x00,0x00,0x00,0x00,0x00,0x59,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xcf,
+    0x00,0x4e,0xbe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x9e,0x7d,0x00,0x00,0x00,
+    0x00,0x00,0x7d,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0xd3,0x96,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x4e,0xbe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x44,0xd6,0x00,0x00,0x00,
+    0x00,0x00,0x7d,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x8a,0x8f,0x00,0x00,0x26,0x8e,0xf0,0x19,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x4e,0xbe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xe7,0x31,0x00,0x00,
+    0x00,0x00,0x7d,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6e,0xf4,0xdc,0xd5,0xfd,0xc8,0x2d,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x4e,0xbe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x91,0x8b,0x00,0x00,
+    0x00,0x00,0x7d,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x1d,0x2d,0x0f,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x4e,0xbe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x37,0xe3,0x02,0x00,
+    0x00,0x00,0x7d,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x4e,0xec,0xb6,0xb6,0x16,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xdc,0x40,0x00,
+    0x9d,0xb6,0xda,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x0b,0x23,0x23,0x23,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x17,0x10,0x00,
+    0x1e,0x23,0x23,0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x0c,0x00,0x00,0x00,0x24,0xae,0xe4,
+    0xa9,0x40,0x01,0x00,0x00,0x00,0x00,0x00,0x14,0x11,0x00,0x00,0x00,0x00,0x17,0x98,
+    0xd1,0xe4,0xce,0xa5,0x20,0x00,0x02,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x14,
+    0x14,0x00,0x00,0x00,0x00,0x14,0x14,0x00,0x02,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x02,0x0e,0x00,0x00,0x48,0xb3,0xd3,0xe5,0xa4,0x3e,0x2f,0xa4,0xdd,0xdb,0xa5,
+    0x17,0x00,0x00,0x48,0xb1,0xd2,0xe6,0xcf,0x8e,0x09,0x00,0x00,0x00,0x00,0x1b,0xa3,
+    0xdf,0xd8,0x94,0x0e,0x00,0x00,0x49,0xb7,0xd7,0xe7,0xc5,0x80,0x08,0x00,0x00,0x00,
+    0x00,0x11,0x8f,0xcb,0xe7,0xd4,0xb1,0x37,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xab,0x6c,0x00,0x00,0x1f,0xed,0x72,0x18,
+    0x55,0xd2,0x8a,0x00,0x00,0x00,0x56,0xbc,0xe7,0xec,0x4e,0x00,0x00,0x1d,0xe9,0x93,
+    0x30,0x18,0x2f,0xe3,0x41,0x00,0x89,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb6,
+    0xb6,0x00,0x00,0x00,0x00,0xb6,0xb6,0x00,0x89,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x8d,0x8a,0x00,0x00,0x8a,0xab,0x24,0x18,0x43,0xe0,0xe3,0x5f,0x20,0x29,0xa4,
+    0xc8,0x00,0x00,0x8a,0xac,0x25,0x17,0x35,0xbd,0xab,0x00,0x00,0x00,0x1b,0xea,0xb6,
+    0x3e,0x4d,0xc7,0xd6,0x09,0x00,0x8a,0xac,0x25,0x1c,0x6c,0xd3,0xcf,0x07,0x00,0x00,
+    0x15,0xe3,0xc8,0x60,0x19,0x29,0xc3,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xad,0x6c,0x00,0x00,0xa5,0x8c,0x00,0x00,
+    0x00,0x46,0xf7,0x0d,0x00,0x0a,0xf1,0x45,0x03,0x01,0x04,0x00,0x00,0xa4,0xa1,0x00,
+    0x00,0x00,0x00,0xd8,0x41,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,
+    0x33,0x00,0x00,0x00,0x00,0x33,0x33,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x8f,0x8a,0x00,0x00,0x8a,0x8f,0x00,0x00,0x00,0x68,0xbf,0x00,0x00,0x00,0x11,
+    0xfb,0x1f,0x00,0x8a,0x8f,0x00,0x00,0x00,0x1d,0xfa,0x18,0x00,0x00,0x9f,0xb8,0x00,
+    0x00,0x00,0x00,0xdb,0x78,0x00,0x8a,0x8f,0x00,0x00,0x00,0x03,0xe8,0x78,0x00,0x00,
+    0x9b,0xcd,0x00,0x00,0x00,0x00,0xad,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xad,0x6c,0x00,0x00,0xe3,0x4b,0x16,0x16,
+    0x16,0x1c,0xf5,0x2f,0x00,0x4d,0xc5,0x00,0x00,0x00,0x00,0x00,0x00,0xe9,0x3e,0x00,
+    0x00,0x00,0x00,0xd8,0x41,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x8f,0x8a,0x00,0x00,0x8a,0x8f,0x00,0x00,0x00,0x3d,0xe0,0x00,0x00,0x00,0x00,
+    0xe2,0x3a,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0xe4,0x3a,0x00,0x00,0xe4,0x6e,0x00,
+    0x00,0x00,0x00,0x90,0xbf,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x98,0xc1,0x00,0x00,
+    0xe2,0x79,0x00,0x00,0x00,0x00,0xad,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xad,0x6c,0x00,0x08,0xfe,0xdd,0xd8,0xd8,
+    0xd8,0xd8,0xd8,0x3a,0x00,0x87,0x92,0x00,0x00,0x00,0x00,0x00,0x07,0xfe,0x24,0x00,
+    0x00,0x00,0x00,0xd8,0x41,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x69,
+    0x6d,0x00,0x00,0x00,0x00,0x69,0x6d,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x8f,0x8a,0x00,0x00,0x8a,0x8f,0x00,0x00,0x00,0x31,0xe9,0x00,0x00,0x00,0x00,
+    0xd5,0x45,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0xd2,0x49,0x00,0x08,0xfe,0x2e,0x00,
+    0x00,0x00,0x00,0x50,0xe3,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x4b,0xe7,0x00,0x08,
+    0xfe,0x2d,0x00,0x00,0x00,0x00,0xad,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x22,0xa8,0xdd,0xd8,0x9d,0xc7,0x6c,0x00,0x00,0xf3,0x4f,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x8a,0xe4,0xc2,0xc2,0xc2,0x10,0x00,0x00,0xe3,0x45,0x00,
+    0x00,0x00,0x00,0xd8,0x41,0x00,0x8a,0xcf,0xc2,0xe4,0xce,0x8e,0x0a,0x00,0x00,0x8a,
+    0x8f,0x00,0x00,0x00,0x00,0x8a,0x8f,0x00,0x8a,0x8f,0x00,0x00,0x0d,0xad,0x43,0x00,
+    0x00,0x8f,0x8a,0x00,0x00,0x8a,0x8f,0x00,0x00,0x00,0x30,0xe9,0x00,0x00,0x00,0x00,
+    0xd4,0x45,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0xcf,0x49,0x00,0x00,0xee,0x5b,0x00,
+    0x00,0x00,0x00,0x7e,0xca,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x50,0xd2,0x00,0x00,
+    0xf2,0x32,0x00,0x00,0x00,0x00,0xad,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x24,0xed,0x7f,0x28,0x1d,0x62,0xe8,0x6c,0x00,0x00,0xc3,0xa2,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x8a,0xa2,0x2b,0x2b,0x2b,0x04,0x00,0x00,0xa3,0xaf,0x00,
+    0x00,0x00,0x00,0xd9,0x41,0x00,0x8a,0xba,0x37,0x18,0x35,0xbd,0xae,0x00,0x00,0x8a,
+    0x8f,0x00,0x00,0x00,0x00,0x8a,0x8f,0x00,0x8a,0x8f,0x00,0x09,0xbd,0x7e,0x00,0x00,
+    0x00,0x8f,0x8a,0x00,0x00,0x8a,0x8f,0x00,0x00,0x00,0x30,0xe9,0x00,0x00,0x00,0x00,
+    0xd4,0x45,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0xcf,0x49,0x00,0x00,0xba,0xa5,0x00,
+    0x00,0x00,0x00,0xc8,0x94,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x93,0xa0,0x00,0x00,
+    0xc2,0x74,0x00,0x00,0x00,0x00,0xad,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0xa5,0x95,0x00,0x00,0x00,0x00,0xad,0x6c,0x00,0x00,0x46,0xf1,0x75,0x1b,
+    0x00,0x03,0x18,0x00,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0xe1,0xb6,
+    0x62,0x6e,0xb6,0xff,0x41,0x00,0x8a,0x8f,0x00,0x00,0x00,0x1d,0xfb,0x18,0x00,0x8a,
+    0x8f,0x00,0x00,0x00,0x00,0x8a,0x8f,0x00,0x8a,0x8f,0x08,0xb8,0x85,0x00,0x00,0x00,
+    0x00,0x8f,0x8a,0x00,0x00,0x8a,0x8f,0x00,0x00,0x00,0x30,0xe9,0x00,0x00,0x00,0x00,
+    0xd4,0x45,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0xcf,0x49,0x00,0x00,0x36,0xf2,0x77,
+    0x0f,0x18,0x87,0xf1,0x1a,0x00,0x8a,0xbf,0x18,0x00,0x00,0x4f,0xf2,0x28,0x00,0x00,
+    0x44,0xeb,0x39,0x00,0x00,0x22,0xd4,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0xe8,0x3d,0x00,0x00,0x00,0x00,0xad,0x6c,0x00,0x00,0x00,0x4d,0xdd,0xfc,
+    0xdb,0xf2,0x7a,0x00,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x6f,
+    0x9e,0x9c,0x55,0xde,0x3d,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0xe4,0x3a,0x00,0x8a,
+    0x8f,0x00,0x00,0x00,0x00,0x8a,0x8f,0x00,0x8a,0x97,0xb6,0x88,0x00,0x00,0x00,0x00,
+    0x00,0x8f,0x8a,0x00,0x00,0x8a,0x8f,0x00,0x00,0x00,0x30,0xe9,0x00,0x00,0x00,0x00,
+    0xd4,0x45,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0xcf,0x49,0x00,0x00,0x00,0x48,0xe3,
+    0xf0,0xf7,0xd4,0x32,0x00,0x00,0x8a,0xd3,0xe7,0xd5,0xea,0xdd,0x40,0x00,0x00,0x00,
+    0x00,0x56,0xe8,0xe4,0xd9,0xdf,0xdc,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x08,0xff,0x25,0x00,0x00,0x00,0x00,0xad,0x6c,0x00,0x00,0x00,0x00,0x00,0x10,
+    0x25,0x09,0x00,0x00,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x27,0xff,0x1c,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0xd2,0x49,0x00,0x8a,
+    0x8f,0x00,0x00,0x00,0x00,0x8a,0x8f,0x00,0x8a,0xdf,0xf4,0x38,0x00,0x00,0x00,0x00,
+    0x00,0x8f,0x8a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x1e,0x17,0x00,0x00,0x00,0x00,0x8a,0x8f,0x02,0x20,0x19,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x1f,0x1b,0x01,0xad,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0xed,0x62,0x00,0x00,0x00,0x00,0xad,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x11,0x09,0x00,
+    0x00,0x1f,0x9e,0xcc,0x01,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0xcf,0x49,0x00,0x8a,
+    0x8f,0x00,0x00,0x00,0x00,0x8a,0x8f,0x00,0x8a,0x8f,0x4f,0xed,0x48,0x00,0x00,0x00,
+    0x00,0x8f,0x8a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0xad,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0xb8,0xb5,0x00,0x00,0x00,0x00,0xad,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x63,0xf2,0xd8,
+    0xdc,0xd3,0x8e,0x22,0x00,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0xcf,0x49,0x00,0x8a,
+    0x8f,0x00,0x00,0x00,0x00,0x8a,0x8f,0x00,0x8a,0x8f,0x00,0x3e,0xed,0x3b,0x00,0x00,
+    0x00,0x8e,0x8b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0xad,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x31,0xf6,0x7f,0x1c,0x00,0x00,0xae,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x22,
+    0x18,0x00,0x00,0x00,0x00,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0xcf,0x49,0x00,0x8a,
+    0x8f,0x00,0x00,0x00,0x00,0x8a,0x8e,0x00,0x8a,0x8f,0x00,0x00,0x52,0xe7,0x1d,0x00,
+    0x00,0x7b,0xab,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x13,0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x17,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x3f,0xd8,0xfc,0xd2,0xe0,0xf1,0x55,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0xcf,0x49,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0xa6,0x69,0x00,0x8a,0x8f,0x00,0x00,0x00,0x92,0xbc,0x01,
+    0x00,0x1f,0xdf,0xa9,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x13,0x2d,0x1a,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x1c,0xe0,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x03,0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x03,0xd1,0xe1,0x86,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x1e,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x3d,0xac,0xd3,0xe8,0xd1,0x0b,0x00,0x00,0x2a,0x91,0xdc,0xe5,0xbd,0x0a,0x00,
+    0x3e,0x3e,0x00,0x00,0x00,0x00,0x00,0x7d,0x59,0x00,0x00,0x00,0x00,0xb2,0x24,0x00,
+    0x6b,0x76,0x00,0x00,0x00,0x00,0x4f,0x86,0x00,0x62,0x7e,0x00,0x00,0x00,0x01,0xb6,
+    0x24,0x00,0x00,0x00,0x54,0x7c,0x00,0x17,0xbc,0x26,0x00,0x00,0x00,0x92,0x57,0x00,
+    0x6b,0x77,0x00,0x00,0x00,0x00,0x6a,0x6a,0x00,0x14,0xc2,0xc2,0xc2,0xc2,0xc2,0xaf,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x07,0x00,0x15,0x34,0x00,0x0d,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x15,0x1a,0x00,0x00,0x00,0x01,0x00,0x00,0x1c,0xb6,0xb6,0xb6,
+    0xb6,0xb6,0xb6,0x72,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x8a,0xab,0x1f,0x13,0x2a,0x00,0x00,0x00,0xc2,0x66,0x0a,0x16,0x44,0x00,0x00,
+    0x9c,0x7d,0x00,0x00,0x00,0x00,0x00,0xa4,0x75,0x00,0x00,0x00,0x00,0xe9,0x30,0x00,
+    0x4d,0xd4,0x00,0x00,0x00,0x00,0xa3,0x6f,0x00,0x41,0xda,0x00,0x00,0x00,0x27,0xff,
+    0x5f,0x00,0x00,0x00,0xa5,0x63,0x00,0x00,0x7b,0xc6,0x03,0x00,0x5c,0xd0,0x06,0x00,
+    0x4d,0xd8,0x00,0x00,0x00,0x00,0xc1,0x51,0x00,0x04,0x2b,0x2b,0x2b,0x2d,0xcd,0x96,
+    0x00,0x00,0x00,0x00,0x11,0xba,0xd8,0x35,0x00,0x4e,0xbe,0x00,0xb0,0xdc,0x48,0x00,
+    0x00,0x00,0x00,0x71,0xdc,0xde,0xb6,0x21,0x00,0x60,0x8a,0x00,0x27,0xbc,0x23,0x23,
+    0x23,0x23,0x53,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x00,0x1c,0xfa,0x04,0x00,0x00,0x00,0x00,0x00,
+    0x9c,0x7d,0x00,0x00,0x00,0x00,0x00,0xa4,0x75,0x00,0x00,0x00,0x00,0xe9,0x30,0x00,
+    0x0b,0xf6,0x1c,0x00,0x00,0x02,0xe8,0x25,0x00,0x06,0xf2,0x1d,0x00,0x00,0x6a,0xe9,
+    0x9d,0x00,0x00,0x01,0xe5,0x1b,0x00,0x00,0x03,0xc6,0x76,0x1c,0xe7,0x2d,0x00,0x00,
+    0x0b,0xf5,0x26,0x00,0x00,0x07,0xf4,0x11,0x00,0x00,0x00,0x00,0x00,0x7e,0xc1,0x05,
+    0x00,0x00,0x00,0x00,0x78,0xaa,0x00,0x00,0x00,0x4e,0xbe,0x00,0x00,0x46,0xdb,0x00,
+    0x00,0x00,0x0a,0xdb,0x0c,0x02,0x69,0xe5,0xc0,0xdb,0x21,0x00,0x27,0xb1,0x00,0x00,
+    0x00,0x00,0x38,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x00,0x02,0xd4,0xa4,0x1b,0x00,0x00,0x00,0x00,
+    0x9c,0xe0,0xc2,0xc2,0xbf,0x00,0x00,0xa4,0x75,0x00,0x00,0x00,0x00,0xe9,0x30,0x00,
+    0x00,0xad,0x6b,0x00,0x00,0x3c,0xce,0x00,0x00,0x00,0xb1,0x67,0x00,0x00,0xb1,0x67,
+    0xe4,0x01,0x00,0x31,0xd1,0x00,0x00,0x00,0x00,0x25,0xec,0xc9,0x75,0x00,0x00,0x00,
+    0x00,0xa8,0x7b,0x00,0x00,0x3e,0xcc,0x00,0x00,0x00,0x00,0x00,0x41,0xe4,0x19,0x00,
+    0x00,0x00,0x00,0x00,0x9e,0x70,0x00,0x00,0x00,0x4e,0xbe,0x00,0x00,0x08,0xfe,0x06,
+    0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x08,0x2e,0x0a,0x00,0x00,0x27,0xb1,0x00,0x00,
+    0x00,0x00,0x38,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x16,0x99,0xf2,0xa7,0x26,0x00,0x00,
+    0x9c,0x93,0x2b,0x2b,0x2a,0x00,0x00,0xa4,0x75,0x00,0x00,0x00,0x00,0xe9,0x30,0x00,
+    0x00,0x56,0xc1,0x00,0x00,0x91,0x75,0x00,0x00,0x00,0x6a,0xb5,0x00,0x0a,0xe7,0x08,
+    0xd7,0x32,0x00,0x7d,0x88,0x00,0x00,0x00,0x00,0x00,0xb2,0xfc,0x24,0x00,0x00,0x00,
+    0x00,0x4d,0xd9,0x01,0x00,0x81,0x8a,0x00,0x00,0x00,0x00,0x13,0xe4,0x44,0x00,0x00,
+    0x00,0x00,0x00,0x00,0xa4,0x69,0x00,0x00,0x00,0x4e,0xbe,0x00,0x00,0x01,0xff,0x0d,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x27,0xb1,0x00,0x00,
+    0x00,0x00,0x38,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x15,0x84,0xf1,0x2b,0x00,
+    0x9c,0x7d,0x00,0x00,0x00,0x00,0x00,0x9b,0x84,0x00,0x00,0x00,0x00,0xe9,0x30,0x00,
+    0x00,0x08,0xec,0x22,0x05,0xe4,0x19,0x00,0x00,0x00,0x17,0xf4,0x11,0x51,0xa7,0x00,
+    0x84,0x84,0x00,0xd1,0x31,0x00,0x00,0x00,0x00,0x50,0xd4,0x80,0xc8,0x04,0x00,0x00,
+    0x00,0x04,0xe1,0x40,0x00,0xc9,0x3c,0x00,0x00,0x00,0x00,0xab,0x8e,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0xa4,0x68,0x00,0x00,0x00,0x4e,0xbe,0x00,0x00,0x00,0xff,0x0d,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x27,0xb1,0x00,0x00,
+    0x00,0x00,0x38,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xa6,0x7f,0x00,
+    0x9c,0x7d,0x00,0x00,0x00,0x00,0x00,0x7f,0xab,0x00,0x00,0x00,0x00,0xe9,0x30,0x00,
+    0x00,0x00,0x8f,0x86,0x52,0xad,0x00,0x00,0x00,0x00,0x00,0xad,0x66,0xa8,0x50,0x00,
+    0x2d,0xdc,0x2c,0xcd,0x00,0x00,0x00,0x00,0x16,0xe4,0x2f,0x02,0xc3,0x7d,0x00,0x00,
+    0x00,0x00,0x77,0xad,0x1a,0xdd,0x01,0x00,0x00,0x00,0x57,0xda,0x08,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0xa4,0x64,0x00,0x00,0x00,0x4e,0xbe,0x00,0x00,0x00,0xfc,0x0d,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x27,0xb1,0x00,0x00,
+    0x00,0x00,0x38,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x00,0x05,0x14,0x00,0x00,0x10,0xe4,0x39,0x00,
+    0x9c,0x7d,0x00,0x00,0x00,0x00,0x00,0x27,0xf7,0x3f,0x00,0x00,0x02,0xeb,0x30,0x00,
+    0x00,0x00,0x25,0xea,0xc8,0x42,0x00,0x00,0x00,0x00,0x00,0x49,0xd3,0xe2,0x06,0x00,
+    0x00,0xce,0xc6,0x6a,0x00,0x00,0x00,0x00,0x9f,0x81,0x00,0x00,0x24,0xef,0x27,0x00,
+    0x00,0x00,0x10,0xed,0x92,0x86,0x00,0x00,0x00,0x0c,0xe3,0x45,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0xc0,0x49,0x00,0x00,0x00,0x4e,0xbe,0x00,0x00,0x00,0xe3,0x26,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x27,0xb1,0x00,0x00,
+    0x00,0x00,0x38,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x8a,0x8f,0x00,0x00,0x00,0x00,0x00,0x2d,0xf2,0xd7,0xd5,0xdf,0x8c,0x00,0x00,
+    0x9b,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x5b,0xeb,0xe6,0xdc,0xf1,0xda,0x22,0x00,
+    0x00,0x00,0x00,0xae,0xcf,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xe1,0x8d,0x00,0x00,
+    0x00,0x68,0xf6,0x10,0x00,0x00,0x00,0x3b,0xdc,0x08,0x00,0x00,0x00,0x7e,0xb3,0x00,
+    0x00,0x00,0x00,0x7f,0xff,0x2c,0x00,0x00,0x00,0x58,0xfe,0xee,0xee,0xee,0xee,0xee,
+    0x0c,0x00,0x17,0x8c,0xd4,0x09,0x00,0x00,0x00,0x4e,0xbe,0x00,0x00,0x00,0x76,0xc9,
+    0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x27,0xb1,0x00,0x00,
+    0x00,0x00,0x38,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x20,0x21,0x00,0x00,0x00,0x00,
+    0x8a,0x8e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1c,0x1d,0x08,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x53,0xc7,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x26,0xc3,0xaf,0x02,0x00,0x00,0x00,0x4e,0xbe,0x00,0x00,0x00,0x4e,0xe4,
+    0x67,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x27,0xb1,0x00,0x00,
+    0x00,0x00,0x38,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x5c,0xd5,0x0b,0x00,0x0f,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x17,0xd9,0x43,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x02,0xd3,0x3d,0x00,0x00,0x00,0x4e,0xbe,0x00,0x00,0x00,0xd7,0x3b,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x27,0xb1,0x00,0x00,
+    0x00,0x00,0x38,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x08,0xb5,0xef,0xd9,0xec,0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0xc2,0xc7,0xe5,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0xa5,0x60,0x00,0x00,0x00,0x4e,0xbe,0x00,0x00,0x00,0xf9,0x0e,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x27,0xb1,0x00,0x00,
+    0x00,0x00,0x38,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x14,0x24,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x14,0x25,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0xa4,0x68,0x00,0x00,0x00,0x4e,0xbe,0x00,0x00,0x00,0xff,0x0d,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x27,0xf3,0xd8,0xd8,
+    0xd8,0xd8,0xe1,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0xa4,0x68,0x00,0x00,0x00,0x4e,0xbe,0x00,0x00,0x00,0xff,0x0d,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0xa1,0x6d,0x00,0x00,0x00,0x4e,0xbe,0x00,0x00,0x05,0xff,0x09,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x87,0x8f,0x00,0x00,0x00,0x4e,0xbe,0x00,0x00,0x2a,0xeb,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x24,0xe1,0xa3,0x26,0x00,0x4e,0xbe,0x00,0x80,0xd9,0x71,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x0c,0x3c,0x16,0x00,0x0b,0x1a,0x00,0x3e,0x20,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+];
+
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/src/debug_render.rs
@@ -0,0 +1,219 @@
+/* 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 debug_font_data;
+use device::{Device, GpuMarker, ProgramId, VAOId, TextureId, VertexFormat};
+use device::{TextureFilter, VertexUsageHint, TextureTarget};
+use euclid::{Matrix4D, Point2D, Size2D, Rect};
+use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, TextureSampler};
+use internal_types::{DebugFontVertex, DebugColorVertex, RenderTargetMode, PackedColor};
+use std::f32;
+use webrender_traits::{ColorF, ImageFormat, DeviceUintSize};
+
+pub struct DebugRenderer {
+    font_vertices: Vec<DebugFontVertex>,
+    font_indices: Vec<u32>,
+    font_program_id: ProgramId,
+    font_vao: VAOId,
+    font_texture_id: TextureId,
+
+    tri_vertices: Vec<DebugColorVertex>,
+    tri_indices: Vec<u32>,
+    tri_vao: VAOId,
+    line_vertices: Vec<DebugColorVertex>,
+    line_vao: VAOId,
+    color_program_id: ProgramId,
+}
+
+impl DebugRenderer {
+    pub fn new(device: &mut Device) -> DebugRenderer {
+        let font_program_id = device.create_program("debug_font", "shared_other");
+        let color_program_id = device.create_program("debug_color", "shared_other");
+
+        let font_vao = device.create_vao(VertexFormat::DebugFont, 32);
+        let line_vao = device.create_vao(VertexFormat::DebugColor, 32);
+        let tri_vao = device.create_vao(VertexFormat::DebugColor, 32);
+
+        let font_texture_id = device.create_texture_ids(1, TextureTarget::Default)[0];
+        device.init_texture(font_texture_id,
+                            debug_font_data::BMP_WIDTH,
+                            debug_font_data::BMP_HEIGHT,
+                            ImageFormat::A8,
+                            TextureFilter::Linear,
+                            RenderTargetMode::None,
+                            Some(&debug_font_data::FONT_BITMAP));
+
+        DebugRenderer {
+            font_vertices: Vec::new(),
+            font_indices: Vec::new(),
+            line_vertices: Vec::new(),
+            tri_vao: tri_vao,
+            tri_vertices: Vec::new(),
+            tri_indices: Vec::new(),
+            font_program_id: font_program_id,
+            color_program_id: color_program_id,
+            font_vao: font_vao,
+            line_vao: line_vao,
+            font_texture_id: font_texture_id,
+        }
+    }
+
+    pub fn line_height(&self) -> f32 {
+        debug_font_data::FONT_SIZE as f32 * 1.1
+    }
+
+    pub fn add_text(&mut self,
+                    x: f32,
+                    y: f32,
+                    text: &str,
+                    color: &ColorF) -> Rect<f32> {
+        let mut x_start = x;
+        let ipw = 1.0 / debug_font_data::BMP_WIDTH as f32;
+        let iph = 1.0 / debug_font_data::BMP_HEIGHT as f32;
+        let color = PackedColor::from_color(color);
+
+        let mut min_x = f32::MAX;
+        let mut max_x = -f32::MAX;
+        let mut min_y = f32::MAX;
+        let mut max_y = -f32::MAX;
+
+        for c in text.chars() {
+            let c = c as usize - debug_font_data::FIRST_GLYPH_INDEX as usize;
+            if c < debug_font_data::GLYPHS.len() {
+                let glyph = &debug_font_data::GLYPHS[c];
+
+                let x0 = (x_start + glyph.xo + 0.5).floor();
+                let y0 = (y + glyph.yo + 0.5).floor();
+
+                let x1 = x0 + glyph.x1 as f32 - glyph.x0 as f32;
+                let y1 = y0 + glyph.y1 as f32 - glyph.y0 as f32;
+
+                let s0 = glyph.x0 as f32 * ipw;
+                let t0 = glyph.y0 as f32 * iph;
+                let s1 = glyph.x1 as f32 * ipw;
+                let t1 = glyph.y1 as f32 * iph;
+
+                x_start += glyph.xa;
+
+                let vertex_count = self.font_vertices.len() as u32;
+
+                self.font_vertices.push(DebugFontVertex::new(x0, y0, s0, t0, color));
+                self.font_vertices.push(DebugFontVertex::new(x1, y0, s1, t0, color));
+                self.font_vertices.push(DebugFontVertex::new(x0, y1, s0, t1, color));
+                self.font_vertices.push(DebugFontVertex::new(x1, y1, s1, t1, color));
+
+                self.font_indices.push(vertex_count + 0);
+                self.font_indices.push(vertex_count + 1);
+                self.font_indices.push(vertex_count + 2);
+                self.font_indices.push(vertex_count + 2);
+                self.font_indices.push(vertex_count + 1);
+                self.font_indices.push(vertex_count + 3);
+
+                min_x = min_x.min(x0);
+                max_x = max_x.max(x1);
+                min_y = min_y.min(y0);
+                max_y = max_y.max(y1);
+            }
+        }
+
+        Rect::new(Point2D::new(min_x, min_y), Size2D::new(max_x-min_x, max_y-min_y))
+    }
+
+    pub fn add_quad(&mut self,
+                    x0: f32,
+                    y0: f32,
+                    x1: f32,
+                    y1: f32,
+                    color_top: &ColorF,
+                    color_bottom: &ColorF) {
+        let color_top = PackedColor::from_color(color_top);
+        let color_bottom = PackedColor::from_color(color_bottom);
+        let vertex_count = self.tri_vertices.len() as u32;
+
+        self.tri_vertices.push(DebugColorVertex::new(x0, y0, color_top));
+        self.tri_vertices.push(DebugColorVertex::new(x1, y0, color_top));
+        self.tri_vertices.push(DebugColorVertex::new(x0, y1, color_bottom));
+        self.tri_vertices.push(DebugColorVertex::new(x1, y1, color_bottom));
+
+        self.tri_indices.push(vertex_count + 0);
+        self.tri_indices.push(vertex_count + 1);
+        self.tri_indices.push(vertex_count + 2);
+        self.tri_indices.push(vertex_count + 2);
+        self.tri_indices.push(vertex_count + 1);
+        self.tri_indices.push(vertex_count + 3);
+    }
+
+    #[allow(dead_code)]
+    pub fn add_line(&mut self,
+                    x0: i32,
+                    y0: i32,
+                    color0: &ColorF,
+                    x1: i32,
+                    y1: i32,
+                    color1: &ColorF) {
+        let color0 = PackedColor::from_color(color0);
+        let color1 = PackedColor::from_color(color1);
+        self.line_vertices.push(DebugColorVertex::new(x0 as f32, y0 as f32, color0));
+        self.line_vertices.push(DebugColorVertex::new(x1 as f32, y1 as f32, color1));
+    }
+
+    pub fn render(&mut self,
+                  device: &mut Device,
+                  viewport_size: &DeviceUintSize) {
+        let _gm = GpuMarker::new("debug");
+        device.disable_depth();
+        device.set_blend(true);
+        device.set_blend_mode_alpha();
+
+        let projection = Matrix4D::ortho(0.0,
+                                         viewport_size.width as f32,
+                                         viewport_size.height as f32,
+                                         0.0,
+                                         ORTHO_NEAR_PLANE,
+                                         ORTHO_FAR_PLANE);
+
+        // Triangles
+        if !self.tri_vertices.is_empty() {
+            device.bind_program(self.color_program_id, &projection);
+            device.bind_vao(self.tri_vao);
+            device.update_vao_indices(self.tri_vao,
+                                      &self.tri_indices,
+                                      VertexUsageHint::Dynamic);
+            device.update_vao_main_vertices(self.tri_vao,
+                                            &self.tri_vertices,
+                                            VertexUsageHint::Dynamic);
+            device.draw_triangles_u32(0, self.tri_indices.len() as i32);
+        }
+
+        // Lines
+        if !self.line_vertices.is_empty() {
+            device.bind_program(self.color_program_id, &projection);
+            device.bind_vao(self.line_vao);
+            device.update_vao_main_vertices(self.line_vao,
+                                            &self.line_vertices,
+                                            VertexUsageHint::Dynamic);
+            device.draw_nonindexed_lines(0, self.line_vertices.len() as i32);
+        }
+
+        // Glyph
+        if !self.font_indices.is_empty() {
+            device.bind_program(self.font_program_id, &projection);
+            device.bind_texture(TextureSampler::Color0, self.font_texture_id);
+            device.bind_vao(self.font_vao);
+            device.update_vao_indices(self.font_vao,
+                                      &self.font_indices,
+                                      VertexUsageHint::Dynamic);
+            device.update_vao_main_vertices(self.font_vao,
+                                            &self.font_vertices,
+                                            VertexUsageHint::Dynamic);
+            device.draw_triangles_u32(0, self.font_indices.len() as i32);
+        }
+
+        self.font_indices.clear();
+        self.font_vertices.clear();
+        self.line_vertices.clear();
+        self.tri_vertices.clear();
+        self.tri_indices.clear();
+    }
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/src/device.rs
@@ -0,0 +1,1965 @@
+/* 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 euclid::Matrix4D;
+use fnv::FnvHasher;
+use gleam::gl;
+use internal_types::{PackedVertex, RenderTargetMode, TextureSampler, DEFAULT_TEXTURE};
+use internal_types::{BlurAttribute, ClearAttribute, ClipAttribute, VertexAttribute};
+use internal_types::{DebugFontVertex, DebugColorVertex};
+//use notify::{self, Watcher};
+use super::shader_source;
+use std::collections::HashMap;
+use std::fs::File;
+use std::hash::BuildHasherDefault;
+use std::io::Read;
+use std::mem;
+use std::path::PathBuf;
+//use std::sync::mpsc::{channel, Sender};
+//use std::thread;
+use webrender_traits::{ColorF, ImageFormat};
+use webrender_traits::{DeviceIntPoint, DeviceIntRect, DeviceIntSize};
+
+#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
+const GL_FORMAT_A: gl::GLuint = gl::RED;
+
+#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
+const GL_FORMAT_A: gl::GLuint = gl::ALPHA;
+
+#[cfg(any(target_os = "windows", all(unix, not(target_os = "android"))))]
+const GL_FORMAT_BGRA: gl::GLuint = gl::BGRA;
+
+#[cfg(target_os = "android")]
+const GL_FORMAT_BGRA: gl::GLuint = gl::BGRA_EXT;
+
+#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
+const SHADER_VERSION: &'static str = "#version 150\n";
+
+#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
+const SHADER_VERSION: &'static str = "#version 300 es\n";
+
+static SHADER_PREAMBLE: &'static str = "shared";
+
+pub type ViewportDimensions = [u32; 2];
+
+lazy_static! {
+    pub static ref MAX_TEXTURE_SIZE: gl::GLint = {
+        gl::get_integer_v(gl::MAX_TEXTURE_SIZE)
+    };
+}
+
+#[repr(u32)]
+pub enum DepthFunction {
+    Less = gl::LESS,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum TextureTarget {
+    Default,
+    Array,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum TextureFilter {
+    Nearest,
+    Linear,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum VertexFormat {
+    Triangles,
+    Rectangles,
+    DebugFont,
+    DebugColor,
+    Clear,
+    Blur,
+    Clip,
+}
+
+enum FBOTarget {
+    Read,
+    Draw,
+}
+
+fn get_optional_shader_source(shader_name: &str, base_path: &Option<PathBuf>) -> Option<String> {
+    if let Some(ref base) = *base_path {
+        let shader_path = base.join(&format!("{}.glsl", shader_name));
+        if shader_path.exists() {
+            let mut source = String::new();
+            File::open(&shader_path).unwrap().read_to_string(&mut source).unwrap();
+            return Some(source);
+        }
+    }
+
+    shader_source::SHADERS.get(shader_name).and_then(|s| Some((*s).to_owned()))
+}
+
+fn get_shader_source(shader_name: &str, base_path: &Option<PathBuf>) -> String {
+    get_optional_shader_source(shader_name, base_path)
+        .expect(&format!("Couldn't get required shader: {}", shader_name))
+}
+
+pub trait FileWatcherHandler : Send {
+    fn file_changed(&self, path: PathBuf);
+}
+
+impl VertexFormat {
+    fn bind(&self, main: VBOId, instance: VBOId, offset: gl::GLuint, instance_stride: gl::GLint) {
+        main.bind();
+
+        match *self {
+            VertexFormat::DebugFont => {
+                gl::enable_vertex_attrib_array(VertexAttribute::Position as gl::GLuint);
+                gl::enable_vertex_attrib_array(VertexAttribute::Color as gl::GLuint);
+                gl::enable_vertex_attrib_array(VertexAttribute::ColorTexCoord as gl::GLuint);
+
+                gl::vertex_attrib_divisor(VertexAttribute::Position as gl::GLuint, 0);
+                gl::vertex_attrib_divisor(VertexAttribute::Color as gl::GLuint, 0);
+                gl::vertex_attrib_divisor(VertexAttribute::ColorTexCoord as gl::GLuint, 0);
+
+                let vertex_stride = mem::size_of::<DebugFontVertex>() as gl::GLuint;
+
+                gl::vertex_attrib_pointer(VertexAttribute::Position as gl::GLuint,
+                                          2,
+                                          gl::FLOAT,
+                                          false,
+                                          vertex_stride as gl::GLint,
+                                          0 + vertex_stride * offset);
+                gl::vertex_attrib_pointer(VertexAttribute::Color as gl::GLuint,
+                                          4,
+                                          gl::UNSIGNED_BYTE,
+                                          true,
+                                          vertex_stride as gl::GLint,
+                                          8 + vertex_stride * offset);
+                gl::vertex_attrib_pointer(VertexAttribute::ColorTexCoord as gl::GLuint,
+                                          2,
+                                          gl::FLOAT,
+                                          false,
+                                          vertex_stride as gl::GLint,
+                                          12 + vertex_stride * offset);
+            }
+            VertexFormat::DebugColor => {
+                gl::enable_vertex_attrib_array(VertexAttribute::Position as gl::GLuint);
+                gl::enable_vertex_attrib_array(VertexAttribute::Color as gl::GLuint);
+
+                gl::vertex_attrib_divisor(VertexAttribute::Position as gl::GLuint, 0);
+                gl::vertex_attrib_divisor(VertexAttribute::Color as gl::GLuint, 0);
+
+                let vertex_stride = mem::size_of::<DebugColorVertex>() as gl::GLuint;
+
+                gl::vertex_attrib_pointer(VertexAttribute::Position as gl::GLuint,
+                                          2,
+                                          gl::FLOAT,
+                                          false,
+                                          vertex_stride as gl::GLint,
+                                          0 + vertex_stride * offset);
+                gl::vertex_attrib_pointer(VertexAttribute::Color as gl::GLuint,
+                                          4,
+                                          gl::UNSIGNED_BYTE,
+                                          true,
+                                          vertex_stride as gl::GLint,
+                                          8 + vertex_stride * offset);
+            }
+            VertexFormat::Rectangles |
+            VertexFormat::Triangles => {
+                let vertex_stride = mem::size_of::<PackedVertex>() as gl::GLuint;
+                gl::enable_vertex_attrib_array(VertexAttribute::Position as gl::GLuint);
+                gl::vertex_attrib_divisor(VertexAttribute::Position as gl::GLuint, 0);
+
+                gl::vertex_attrib_pointer(VertexAttribute::Position as gl::GLuint,
+                                          2,
+                                          gl::FLOAT,
+                                          false,
+                                          vertex_stride as gl::GLint,
+                                          0);
+
+                instance.bind();
+                let mut offset = 0;
+
+                for &attrib in [VertexAttribute::GlobalPrimId,
+                                VertexAttribute::PrimitiveAddress,
+                                VertexAttribute::TaskIndex,
+                                VertexAttribute::ClipTaskIndex,
+                                VertexAttribute::LayerIndex,
+                                VertexAttribute::ElementIndex,
+                                VertexAttribute::ZIndex,
+                               ].into_iter() {
+                    gl::enable_vertex_attrib_array(attrib as gl::GLuint);
+                    gl::vertex_attrib_divisor(attrib as gl::GLuint, 1);
+                    gl::vertex_attrib_i_pointer(attrib as gl::GLuint,
+                                                1,
+                                                gl::INT,
+                                                instance_stride,
+                                                offset);
+                    offset += 4;
+                }
+
+                gl::enable_vertex_attrib_array(VertexAttribute::UserData as gl::GLuint);
+                gl::vertex_attrib_divisor(VertexAttribute::UserData as gl::GLuint, 1);
+                gl::vertex_attrib_i_pointer(VertexAttribute::UserData as gl::GLuint,
+                                            2,
+                                            gl::INT,
+                                            instance_stride,
+                                            offset);
+            }
+            VertexFormat::Clear => {
+                let vertex_stride = mem::size_of::<PackedVertex>() as gl::GLuint;
+                gl::enable_vertex_attrib_array(ClearAttribute::Position as gl::GLuint);
+                gl::vertex_attrib_divisor(ClearAttribute::Position as gl::GLuint, 0);
+
+                gl::vertex_attrib_pointer(ClearAttribute::Position as gl::GLuint,
+                                          2,
+                                          gl::FLOAT,
+                                          false,
+                                          vertex_stride as gl::GLint,
+                                          0);
+
+                instance.bind();
+
+                gl::enable_vertex_attrib_array(ClearAttribute::Rectangle as gl::GLuint);
+                gl::vertex_attrib_divisor(ClearAttribute::Rectangle as gl::GLuint, 1);
+                gl::vertex_attrib_i_pointer(ClearAttribute::Rectangle as gl::GLuint,
+                                            4,
+                                            gl::INT,
+                                            instance_stride,
+                                            0);
+            }
+            VertexFormat::Blur => {
+                let vertex_stride = mem::size_of::<PackedVertex>() as gl::GLuint;
+                gl::enable_vertex_attrib_array(BlurAttribute::Position as gl::GLuint);
+                gl::vertex_attrib_divisor(BlurAttribute::Position as gl::GLuint, 0);
+
+                gl::vertex_attrib_pointer(BlurAttribute::Position as gl::GLuint,
+                                          2,
+                                          gl::FLOAT,
+                                          false,
+                                          vertex_stride as gl::GLint,
+                                          0);
+
+                instance.bind();
+
+                for (i, &attrib) in [BlurAttribute::RenderTaskIndex,
+                                     BlurAttribute::SourceTaskIndex,
+                                     BlurAttribute::Direction,
+                                    ].into_iter().enumerate() {
+                    gl::enable_vertex_attrib_array(attrib as gl::GLuint);
+                    gl::vertex_attrib_divisor(attrib as gl::GLuint, 1);
+                    gl::vertex_attrib_i_pointer(attrib as gl::GLuint,
+                                                1,
+                                                gl::INT,
+                                                instance_stride,
+                                                (i * 4) as gl::GLuint);
+                }
+            }
+            VertexFormat::Clip => {
+                let vertex_stride = mem::size_of::<PackedVertex>() as gl::GLuint;
+                gl::enable_vertex_attrib_array(ClipAttribute::Position as gl::GLuint);
+                gl::vertex_attrib_divisor(ClipAttribute::Position as gl::GLuint, 0);
+
+                gl::vertex_attrib_pointer(ClipAttribute::Position as gl::GLuint,
+                                          2,
+                                          gl::FLOAT,
+                                          false,
+                                          vertex_stride as gl::GLint,
+                                          0);
+
+                instance.bind();
+
+                for (i, &attrib) in [ClipAttribute::RenderTaskIndex,
+                                     ClipAttribute::LayerIndex,
+                                     ClipAttribute::DataIndex,
+                                     ClipAttribute::SegmentIndex,
+                                    ].into_iter().enumerate() {
+                    gl::enable_vertex_attrib_array(attrib as gl::GLuint);
+                    gl::vertex_attrib_divisor(attrib as gl::GLuint, 1);
+                    gl::vertex_attrib_i_pointer(attrib as gl::GLuint,
+                                                1,
+                                                gl::INT,
+                                                instance_stride,
+                                                (i * 4) as gl::GLuint);
+                }
+            }
+        }
+    }
+}
+
+impl TextureId {
+    pub fn bind(&self) {
+        gl::bind_texture(self.target, self.name);
+    }
+
+    pub fn new(name: gl::GLuint) -> TextureId {
+        TextureId {
+            name: name,
+            target: gl::TEXTURE_2D,
+        }
+    }
+
+    pub fn invalid() -> TextureId {
+        TextureId {
+            name: 0,
+            target: gl::TEXTURE_2D,
+        }
+    }
+
+    pub fn is_valid(&self) -> bool { *self != TextureId::invalid() }
+}
+
+impl ProgramId {
+    fn bind(&self) {
+        gl::use_program(self.0);
+    }
+}
+
+impl VBOId {
+    fn bind(&self) {
+        gl::bind_buffer(gl::ARRAY_BUFFER, self.0);
+    }
+}
+
+impl IBOId {
+    fn bind(&self) {
+        gl::bind_buffer(gl::ELEMENT_ARRAY_BUFFER, self.0);
+    }
+}
+
+impl UBOId {
+    fn _bind(&self) {
+        gl::bind_buffer(gl::UNIFORM_BUFFER, self.0);
+    }
+}
+
+impl FBOId {
+    fn bind(&self, target: FBOTarget) {
+        let target = match target {
+            FBOTarget::Read => gl::READ_FRAMEBUFFER,
+            FBOTarget::Draw => gl::DRAW_FRAMEBUFFER,
+        };
+        gl::bind_framebuffer(target, self.0);
+    }
+}
+
+struct Texture {
+    id: gl::GLuint,
+    format: ImageFormat,
+    width: u32,
+    height: u32,
+    filter: TextureFilter,
+    mode: RenderTargetMode,
+    fbo_ids: Vec<FBOId>,
+}
+
+impl Drop for Texture {
+    fn drop(&mut self) {
+        if !self.fbo_ids.is_empty() {
+            let fbo_ids: Vec<_> = self.fbo_ids.iter().map(|&FBOId(fbo_id)| fbo_id).collect();
+            gl::delete_framebuffers(&fbo_ids[..]);
+        }
+        gl::delete_textures(&[self.id]);
+    }
+}
+
+struct Program {
+    id: gl::GLuint,
+    u_transform: gl::GLint,
+    u_device_pixel_ratio: gl::GLint,
+    name: String,
+    vs_source: String,
+    fs_source: String,
+    prefix: Option<String>,
+    vs_id: Option<gl::GLuint>,
+    fs_id: Option<gl::GLuint>,
+}
+
+impl Program {
+    fn attach_and_bind_shaders(&mut self,
+                               vs_id: gl::GLuint,
+                               fs_id: gl::GLuint,
+                               panic_on_fail: bool) -> bool {
+        gl::attach_shader(self.id, vs_id);
+        gl::attach_shader(self.id, fs_id);
+
+        gl::bind_attrib_location(self.id, VertexAttribute::Position as gl::GLuint, "aPosition");
+        gl::bind_attrib_location(self.id, VertexAttribute::Color as gl::GLuint, "aColor");
+        gl::bind_attrib_location(self.id, VertexAttribute::ColorTexCoord as gl::GLuint, "aColorTexCoord");
+
+        gl::bind_attrib_location(self.id, VertexAttribute::GlobalPrimId as gl::GLuint, "aGlobalPrimId");
+        gl::bind_attrib_location(self.id, VertexAttribute::PrimitiveAddress as gl::GLuint, "aPrimitiveAddress");
+        gl::bind_attrib_location(self.id, VertexAttribute::TaskIndex as gl::GLuint, "aTaskIndex");
+        gl::bind_attrib_location(self.id, VertexAttribute::ClipTaskIndex as gl::GLuint, "aClipTaskIndex");
+        gl::bind_attrib_location(self.id, VertexAttribute::LayerIndex as gl::GLuint, "aLayerIndex");
+        gl::bind_attrib_location(self.id, VertexAttribute::ElementIndex as gl::GLuint, "aElementIndex");
+        gl::bind_attrib_location(self.id, VertexAttribute::UserData as gl::GLuint, "aUserData");
+        gl::bind_attrib_location(self.id, VertexAttribute::ZIndex as gl::GLuint, "aZIndex");
+
+        gl::bind_attrib_location(self.id, ClearAttribute::Rectangle as gl::GLuint, "aClearRectangle");
+
+        gl::bind_attrib_location(self.id, BlurAttribute::RenderTaskIndex as gl::GLuint, "aBlurRenderTaskIndex");
+        gl::bind_attrib_location(self.id, BlurAttribute::SourceTaskIndex as gl::GLuint, "aBlurSourceTaskIndex");
+        gl::bind_attrib_location(self.id, BlurAttribute::Direction as gl::GLuint, "aBlurDirection");
+
+        gl::bind_attrib_location(self.id, ClipAttribute::RenderTaskIndex as gl::GLuint, "aClipRenderTaskIndex");
+        gl::bind_attrib_location(self.id, ClipAttribute::LayerIndex as gl::GLuint, "aClipLayerIndex");
+        gl::bind_attrib_location(self.id, ClipAttribute::DataIndex as gl::GLuint, "aClipDataIndex");
+        gl::bind_attrib_location(self.id, ClipAttribute::SegmentIndex as gl::GLuint, "aClipSegmentIndex");
+
+        gl::link_program(self.id);
+        if gl::get_program_iv(self.id, gl::LINK_STATUS) == (0 as gl::GLint) {
+            println!("Failed to link shader program: {}", gl::get_program_info_log(self.id));
+            gl::detach_shader(self.id, vs_id);
+            gl::detach_shader(self.id, fs_id);
+            if panic_on_fail {
+                panic!("-- Program link failed - exiting --");
+            }
+            false
+        } else {
+            //println!("{}", gl::get_program_info_log(self.id));
+            true
+        }
+    }
+}
+
+impl Drop for Program {
+    fn drop(&mut self) {
+        gl::delete_program(self.id);
+    }
+}
+
+struct VAO {
+    id: gl::GLuint,
+    ibo_id: IBOId,
+    main_vbo_id: VBOId,
+    instance_vbo_id: VBOId,
+    instance_stride: gl::GLint,
+    owns_indices: bool,
+    owns_vertices: bool,
+    owns_instances: bool,
+}
+
+impl Drop for VAO {
+    fn drop(&mut self) {
+        gl::delete_vertex_arrays(&[self.id]);
+
+        if self.owns_indices {
+            // todo(gw): maybe make these their own type with hashmap?
+            gl::delete_buffers(&[self.ibo_id.0]);
+        }
+        if self.owns_vertices {
+            gl::delete_buffers(&[self.main_vbo_id.0]);
+        }
+        if self.owns_instances {
+            gl::delete_buffers(&[self.instance_vbo_id.0])
+        }
+    }
+}
+
+#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Copy, Clone)]
+pub struct TextureId {
+    name: gl::GLuint,
+    target: gl::GLuint,
+}
+
+#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
+pub struct ProgramId(pub gl::GLuint);
+
+#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
+pub struct VAOId(gl::GLuint);
+
+#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
+pub struct FBOId(gl::GLuint);
+
+#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
+pub struct VBOId(gl::GLuint);
+
+#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
+struct IBOId(gl::GLuint);
+
+#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
+pub struct UBOId(gl::GLuint);
+
+const MAX_EVENTS_PER_FRAME: usize = 256;
+const MAX_PROFILE_FRAMES: usize = 4;
+
+pub trait NamedTag {
+    fn get_label(&self) -> &str;
+}
+
+#[derive(Debug, Clone)]
+pub struct GpuSample<T> {
+    pub tag: T,
+    pub time_ns: u64,
+}
+
+pub struct GpuFrameProfile<T> {
+    queries: Vec<gl::GLuint>,
+    samples: Vec<GpuSample<T>>,
+    next_query: usize,
+    pending_query: gl::GLuint,
+}
+
+impl<T> GpuFrameProfile<T> {
+    #[cfg(not(target_os = "android"))]
+    fn new() -> GpuFrameProfile<T> {
+        let queries = gl::gen_queries(MAX_EVENTS_PER_FRAME as gl::GLint);
+
+        GpuFrameProfile {
+            queries: queries,
+            samples: Vec::new(),
+            next_query: 0,
+            pending_query: 0,
+        }
+    }
+
+    #[cfg(target_os = "android")]
+    fn new() -> GpuFrameProfile<T> {
+        GpuFrameProfile {
+            queries: Vec::new(),
+            samples: Vec::new(),
+            next_query: 0,
+            pending_query: 0,
+        }
+    }
+
+    fn begin_frame(&mut self) {
+        self.next_query = 0;
+        self.pending_query = 0;
+        self.samples.clear();
+    }
+
+    #[cfg(not(target_os = "android"))]
+    fn end_frame(&mut self) {
+        if self.pending_query != 0 {
+            gl::end_query(gl::TIME_ELAPSED);
+        }
+    }
+
+    #[cfg(target_os = "android")]
+    fn end_frame(&mut self) {
+    }
+
+    #[cfg(not(target_os = "android"))]
+    fn add_marker(&mut self, tag: T) -> GpuMarker
+    where T: NamedTag {
+        if self.pending_query != 0 {
+            gl::end_query(gl::TIME_ELAPSED);
+        }
+
+        let marker = GpuMarker::new(tag.get_label());
+
+        if self.next_query < MAX_EVENTS_PER_FRAME {
+            self.pending_query = self.queries[self.next_query];
+            gl::begin_query(gl::TIME_ELAPSED, self.pending_query);
+            self.samples.push(GpuSample {
+                tag: tag,
+                time_ns: 0,
+            });
+        } else {
+            self.pending_query = 0;
+        }
+
+        self.next_query += 1;
+        marker
+    }
+
+    #[cfg(target_os = "android")]
+    fn add_marker(&mut self, tag: T) {
+        self.samples.push(GpuSample {
+            tag: tag,
+            time_ns: 0,
+        });
+    }
+
+    fn is_valid(&self) -> bool {
+        self.next_query <= MAX_EVENTS_PER_FRAME
+    }
+
+    #[cfg(not(target_os = "android"))]
+    fn build_samples(&mut self) -> Vec<GpuSample<T>> {
+        for (index, sample) in self.samples.iter_mut().enumerate() {
+            sample.time_ns = gl::get_query_object_ui64v(self.queries[index], gl::QUERY_RESULT)
+        }
+
+        mem::replace(&mut self.samples, Vec::new())
+    }
+
+    #[cfg(target_os = "android")]
+    fn build_samples(&mut self) -> Vec<GpuSample<T>> {
+        mem::replace(&mut self.samples, Vec::new())
+    }
+}
+
+impl<T> Drop for GpuFrameProfile<T> {
+    #[cfg(not(target_os = "android"))]
+    fn drop(&mut self) {
+        gl::delete_queries(&self.queries);
+    }
+
+    #[cfg(target_os = "android")]
+    fn drop(&mut self) {
+    }
+}
+
+pub struct GpuProfiler<T> {
+    frames: [GpuFrameProfile<T>; MAX_PROFILE_FRAMES],
+    next_frame: usize,
+}
+
+impl<T> GpuProfiler<T> {
+    pub fn new() -> GpuProfiler<T> {
+        GpuProfiler {
+            next_frame: 0,
+            frames: [
+                      GpuFrameProfile::new(),
+                      GpuFrameProfile::new(),
+                      GpuFrameProfile::new(),
+                      GpuFrameProfile::new(),
+                    ],
+        }
+    }
+
+    pub fn build_samples(&mut self) -> Option<Vec<GpuSample<T>>> {
+        let frame = &mut self.frames[self.next_frame];
+        if frame.is_valid() {
+            Some(frame.build_samples())
+        } else {
+            None
+        }
+    }
+
+    pub fn begin_frame(&mut self) {
+        let frame = &mut self.frames[self.next_frame];
+        frame.begin_frame();
+    }
+
+    pub fn end_frame(&mut self) {
+        let frame = &mut self.frames[self.next_frame];
+        frame.end_frame();
+        self.next_frame = (self.next_frame + 1) % MAX_PROFILE_FRAMES;
+    }
+
+    #[cfg(not(target_os = "android"))]
+    pub fn add_marker(&mut self, tag: T) -> GpuMarker
+    where T: NamedTag {
+        self.frames[self.next_frame].add_marker(tag)
+    }
+
+    #[cfg(target_os = "android")]
+    pub fn add_marker(&mut self, tag: T) {
+        self.frames[self.next_frame].add_marker(tag)
+    }
+}
+
+#[must_use]
+pub struct GpuMarker(());
+
+#[cfg(any(target_arch="arm", target_arch="aarch64"))]
+impl GpuMarker {
+    pub fn new(_: &str) -> GpuMarker {
+        GpuMarker(())
+    }
+
+    pub fn fire(_: &str) {}
+}
+
+
+#[cfg(not(any(target_arch="arm", target_arch="aarch64")))]
+impl GpuMarker {
+    pub fn new(message: &str) -> GpuMarker {
+       gl::push_group_marker_ext(message);
+       GpuMarker(())
+    }
+
+    pub fn fire(message: &str) {
+        gl::insert_event_marker_ext(message);
+    }
+}
+
+#[cfg(not(any(target_arch="arm", target_arch="aarch64")))]
+impl Drop for GpuMarker {
+    fn drop(&mut self) {
+        gl::pop_group_marker_ext();
+    }
+}
+
+#[derive(Debug, Copy, Clone)]
+pub enum VertexUsageHint {
+    Static,
+    Dynamic,
+    Stream,
+}
+
+impl VertexUsageHint {
+    fn to_gl(&self) -> gl::GLuint {
+        match *self {
+            VertexUsageHint::Static => gl::STATIC_DRAW,
+            VertexUsageHint::Dynamic => gl::DYNAMIC_DRAW,
+            VertexUsageHint::Stream => gl::STREAM_DRAW,
+        }
+    }
+}
+
+#[derive(Copy, Clone, Debug)]
+pub struct UniformLocation(gl::GLint);
+
+impl UniformLocation {
+    pub fn invalid() -> UniformLocation {
+        UniformLocation(-1)
+    }
+}
+
+// TODO(gw): Fix up notify cargo deps and re-enable this!
+/*
+enum FileWatcherCmd {
+    AddWatch(PathBuf),
+    Exit,
+}
+
+struct FileWatcherThread {
+    api_tx: Sender<FileWatcherCmd>,
+}
+
+impl FileWatcherThread {
+    fn new(handler: Box<FileWatcherHandler>) -> FileWatcherThread {
+        let (api_tx, api_rx) = channel();
+
+        thread::spawn(move || {
+
+            let (watch_tx, watch_rx) = channel();
+
+            enum Request {
+                Watcher(notify::Event),
+                Command(FileWatcherCmd),
+            }
+
+            let mut file_watcher: notify::RecommendedWatcher = notify::Watcher::new(watch_tx).unwrap();
+
+            loop {
+                let request = {
+                    let receiver_from_api = &api_rx;
+                    let receiver_from_watcher = &watch_rx;
+                    select! {
+                        msg = receiver_from_api.recv() => Request::Command(msg.unwrap()),
+                        msg = receiver_from_watcher.recv() => Request::Watcher(msg.unwrap())
+                    }
+                };
+
+                match request {
+                    Request::Watcher(event) => {
+                        handler.file_changed(event.path.unwrap());
+                    }
+                    Request::Command(cmd) => {
+                        match cmd {
+                            FileWatcherCmd::AddWatch(path) => {
+                                file_watcher.watch(path).ok();
+                            }
+                            FileWatcherCmd::Exit => {
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+        });
+
+        FileWatcherThread {
+            api_tx: api_tx,
+        }
+    }
+
+    fn exit(&self) {
+        self.api_tx.send(FileWatcherCmd::Exit).ok();
+    }
+
+    fn add_watch(&self, path: PathBuf) {
+        self.api_tx.send(FileWatcherCmd::AddWatch(path)).ok();
+    }
+}
+*/
+
+pub struct Capabilities {
+    pub max_ubo_size: usize,
+    pub supports_multisampling: bool,
+}
+
+pub struct Device {
+    // device state
+    bound_textures: [TextureId; 16],
+    bound_program: ProgramId,
+    bound_vao: VAOId,
+    bound_read_fbo: FBOId,
+    bound_draw_fbo: FBOId,
+    default_read_fbo: gl::GLuint,
+    default_draw_fbo: gl::GLuint,
+    device_pixel_ratio: f32,
+
+    // HW or API capabilties
+    capabilities: Capabilities,
+
+    // debug
+    inside_frame: bool,
+
+    // resources
+    resource_override_path: Option<PathBuf>,
+    textures: HashMap<TextureId, Texture, BuildHasherDefault<FnvHasher>>,
+    programs: HashMap<ProgramId, Program, BuildHasherDefault<FnvHasher>>,
+    vaos: HashMap<VAOId, VAO, BuildHasherDefault<FnvHasher>>,
+
+    // misc.
+    shader_preamble: String,
+    //file_watcher: FileWatcherThread,
+
+    // Used on android only
+    #[allow(dead_code)]
+    next_vao_id: gl::GLuint,
+}
+
+impl Device {
+    pub fn new(resource_override_path: Option<PathBuf>,
+               _file_changed_handler: Box<FileWatcherHandler>) -> Device {
+        //let file_watcher = FileWatcherThread::new(file_changed_handler);
+
+        let shader_preamble = get_shader_source(SHADER_PREAMBLE, &resource_override_path);
+        //file_watcher.add_watch(resource_path);
+
+        Device {
+            resource_override_path: resource_override_path,
+            // This is initialized to 1 by default, but it is set
+            // every frame by the call to begin_frame().
+            device_pixel_ratio: 1.0,
+            inside_frame: false,
+
+            capabilities: Capabilities {
+                max_ubo_size: gl::get_integer_v(gl::MAX_UNIFORM_BLOCK_SIZE) as usize,
+                supports_multisampling: false, //TODO
+            },
+
+            bound_textures: [ TextureId::invalid(); 16 ],
+            bound_program: ProgramId(0),
+            bound_vao: VAOId(0),
+            bound_read_fbo: FBOId(0),
+            bound_draw_fbo: FBOId(0),
+            default_read_fbo: 0,
+            default_draw_fbo: 0,
+
+            textures: HashMap::with_hasher(Default::default()),
+            programs: HashMap::with_hasher(Default::default()),
+            vaos: HashMap::with_hasher(Default::default()),
+
+            shader_preamble: shader_preamble,
+
+            next_vao_id: 1,
+            //file_watcher: file_watcher,
+        }
+    }
+
+    pub fn get_capabilities(&self) -> &Capabilities {
+        &self.capabilities
+    }
+
+    pub fn compile_shader(name: &str,
+                          source_str: &str,
+                          shader_type: gl::GLenum,
+                          shader_preamble: &[String],
+                          panic_on_fail: bool)
+                          -> Option<gl::GLuint> {
+        debug!("compile {:?}", name);
+
+        let mut s = String::new();
+        s.push_str(SHADER_VERSION);
+        for prefix in shader_preamble {
+            s.push_str(&prefix);
+        }
+        s.push_str(source_str);
+
+        let id = gl::create_shader(shader_type);
+        let mut source = Vec::new();
+        source.extend_from_slice(s.as_bytes());
+        gl::shader_source(id, &[&source[..]]);
+        gl::compile_shader(id);
+        let log = gl::get_shader_info_log(id);
+        if gl::get_shader_iv(id, gl::COMPILE_STATUS) == (0 as gl::GLint) {
+            println!("Failed to compile shader: {:?}\n{}", name, log);
+            if panic_on_fail {
+                panic!("-- Shader compile failed - exiting --");
+            }
+
+            None
+        } else {
+            if !log.is_empty() {
+                println!("Warnings detected on shader: {:?}\n{}", name, log);
+            }
+            Some(id)
+        }
+    }
+
+    pub fn begin_frame(&mut self, device_pixel_ratio: f32) {
+        debug_assert!(!self.inside_frame);
+        self.inside_frame = true;
+        self.device_pixel_ratio = device_pixel_ratio;
+
+        // Retrive the currently set FBO.
+        let default_read_fbo = gl::get_integer_v(gl::READ_FRAMEBUFFER_BINDING);
+        self.default_read_fbo = default_read_fbo as gl::GLuint;
+        let default_draw_fbo = gl::get_integer_v(gl::DRAW_FRAMEBUFFER_BINDING);
+        self.default_draw_fbo = default_draw_fbo as gl::GLuint;
+
+        // Texture state
+        for i in 0..self.bound_textures.len() {
+            self.bound_textures[i] = TextureId::invalid();
+            gl::active_texture(gl::TEXTURE0 + i as gl::GLuint);
+            gl::bind_texture(gl::TEXTURE_2D, 0);
+        }
+
+        // Shader state
+        self.bound_program = ProgramId(0);
+        gl::use_program(0);
+
+        // Vertex state
+        self.bound_vao = VAOId(0);
+        self.clear_vertex_array();
+
+        // FBO state
+        self.bound_read_fbo = FBOId(self.default_read_fbo);
+        self.bound_draw_fbo = FBOId(self.default_draw_fbo);
+
+        // Pixel op state
+        gl::pixel_store_i(gl::UNPACK_ALIGNMENT, 1);
+
+        // Default is sampler 0, always
+        gl::active_texture(gl::TEXTURE0);
+    }
+
+    pub fn bind_texture(&mut self,
+                        sampler: TextureSampler,
+                        texture_id: TextureId) {
+        debug_assert!(self.inside_frame);
+
+        let sampler_index = sampler as usize;
+        if self.bound_textures[sampler_index] != texture_id {
+            self.bound_textures[sampler_index] = texture_id;
+            gl::active_texture(gl::TEXTURE0 + sampler_index as gl::GLuint);
+            texture_id.bind();
+            gl::active_texture(gl::TEXTURE0);
+        }
+    }
+
+    pub fn bind_read_target(&mut self, texture_id: Option<(TextureId, i32)>) {
+        debug_assert!(self.inside_frame);
+
+        let fbo_id = texture_id.map_or(FBOId(self.default_read_fbo), |texture_id| {
+            self.textures.get(&texture_id.0).unwrap().fbo_ids[texture_id.1 as usize]
+        });
+
+        if self.bound_read_fbo != fbo_id {
+            self.bound_read_fbo = fbo_id;
+            fbo_id.bind(FBOTarget::Read);
+        }
+    }
+
+    pub fn bind_draw_target(&mut self,
+                            texture_id: Option<(TextureId, i32)>,
+                            dimensions: Option<ViewportDimensions>) {
+        debug_assert!(self.inside_frame);
+
+        let fbo_id = texture_id.map_or(FBOId(self.default_draw_fbo), |texture_id| {
+            self.textures.get(&texture_id.0).unwrap().fbo_ids[texture_id.1 as usize]
+        });
+
+        if self.bound_draw_fbo != fbo_id {
+            self.bound_draw_fbo = fbo_id;
+            fbo_id.bind(FBOTarget::Draw);
+        }
+
+        if let Some(dimensions) = dimensions {
+            gl::viewport(0, 0, dimensions[0] as gl::GLint, dimensions[1] as gl::GLint);
+        }
+    }
+
+    pub fn bind_program(&mut self,
+                        program_id: ProgramId,
+                        projection: &Matrix4D<f32>) {
+        debug_assert!(self.inside_frame);
+
+        if self.bound_program != program_id {
+            self.bound_program = program_id;
+            program_id.bind();
+        }
+
+        let program = self.programs.get(&program_id).unwrap();
+        self.set_uniforms(program,
+                          projection,
+                          self.device_pixel_ratio);
+    }
+
+    pub fn create_texture_ids(&mut self,
+                              count: i32,
+                              target: TextureTarget) -> Vec<TextureId> {
+        let id_list = gl::gen_textures(count);
+        let mut texture_ids = Vec::new();
+
+        let target = match target {
+            TextureTarget::Default => gl::TEXTURE_2D,
+            TextureTarget::Array => gl::TEXTURE_2D_ARRAY,
+        };
+
+        for id in id_list {
+            let texture_id = TextureId {
+                name: id,
+                target: target,
+            };
+
+            let texture = Texture {
+                id: id,
+                width: 0,
+                height: 0,
+                format: ImageFormat::Invalid,
+                filter: TextureFilter::Nearest,
+                mode: RenderTargetMode::None,
+                fbo_ids: vec![],
+            };
+
+            debug_assert!(self.textures.contains_key(&texture_id) == false);
+            self.textures.insert(texture_id, texture);
+
+            texture_ids.push(texture_id);
+        }
+
+        texture_ids
+    }
+
+    pub fn get_texture_dimensions(&self, texture_id: TextureId) -> (u32, u32) {
+        let texture = &self.textures[&texture_id];
+        (texture.width, texture.height)
+    }
+
+    fn set_texture_parameters(&mut self, target: gl::GLuint, filter: TextureFilter) {
+        let filter = match filter {
+            TextureFilter::Nearest => {
+                gl::NEAREST
+            }
+            TextureFilter::Linear => {
+                gl::LINEAR
+            }
+        };
+
+        gl::tex_parameter_i(target, gl::TEXTURE_MAG_FILTER, filter as gl::GLint);
+        gl::tex_parameter_i(target, gl::TEXTURE_MIN_FILTER, filter as gl::GLint);
+
+        gl::tex_parameter_i(target, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as gl::GLint);
+        gl::tex_parameter_i(target, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as gl::GLint);
+    }
+
+    fn upload_texture_image(&mut self,
+                            target: gl::GLuint,
+                            width: u32,
+                            height: u32,
+                            internal_format: u32,
+                            format: u32,
+                            type_: u32,
+                            pixels: Option<&[u8]>) {
+        gl::tex_image_2d(target,
+                         0,
+                         internal_format as gl::GLint,
+                         width as gl::GLint, height as gl::GLint,
+                         0,
+                         format,
+                         type_,
+                         pixels);
+    }
+
+    pub fn init_texture(&mut self,
+                        texture_id: TextureId,
+                        width: u32,
+                        height: u32,
+                        format: ImageFormat,
+                        filter: TextureFilter,
+                        mode: RenderTargetMode,
+                        pixels: Option<&[u8]>) {
+        debug_assert!(self.inside_frame);
+
+        {
+            let texture = self.textures.get_mut(&texture_id).expect("Didn't find texture!");
+            texture.format = format;
+            texture.width = width;
+            texture.height = height;
+            texture.filter = filter;
+            texture.mode = mode;
+        }
+
+        let (internal_format, gl_format) = gl_texture_formats_for_image_format(format);
+        let type_ = gl_type_for_texture_format(format);
+
+        match mode {
+            RenderTargetMode::SimpleRenderTarget => {
+                self.bind_texture(DEFAULT_TEXTURE, texture_id);
+                self.set_texture_parameters(texture_id.target, filter);
+                self.upload_texture_image(texture_id.target,
+                                          width,
+                                          height,
+                                          internal_format as u32,
+                                          gl_format,
+                                          type_,
+                                          None);
+                self.create_fbo_for_texture_if_necessary(texture_id, None);
+            }
+            RenderTargetMode::LayerRenderTarget(layer_count) => {
+                self.bind_texture(DEFAULT_TEXTURE, texture_id);
+                self.set_texture_parameters(texture_id.target, filter);
+                self.create_fbo_for_texture_if_necessary(texture_id, Some(layer_count));
+            }
+            RenderTargetMode::None => {
+                self.bind_texture(DEFAULT_TEXTURE, texture_id);
+                self.set_texture_parameters(texture_id.target, filter);
+                self.upload_texture_image(texture_id.target,
+                                          width,
+                                          height,
+                                          internal_format as u32,
+                                          gl_format,
+                                          type_,
+                                          pixels);
+            }
+        }
+    }
+
+    pub fn get_render_target_layer_count(&self, texture_id: TextureId) -> usize {
+        self.textures[&texture_id].fbo_ids.len()
+    }
+
+    pub fn create_fbo_for_texture_if_necessary(&mut self,
+                                               texture_id: TextureId,
+                                               layer_count: Option<i32>) {
+        let texture = self.textures.get_mut(&texture_id).unwrap();
+
+        match layer_count {
+            Some(layer_count) => {
+                debug_assert!(layer_count > 0);
+
+                // If we have enough layers allocated already, just use them.
+                // TODO(gw): Probably worth removing some after a while if
+                //           there is a surplus?
+                let current_layer_count = texture.fbo_ids.len() as i32;
+                if current_layer_count >= layer_count {
+                    return;
+                }
+
+                let (internal_format, gl_format) = gl_texture_formats_for_image_format(texture.format);
+                let type_ = gl_type_for_texture_format(texture.format);
+
+                gl::tex_image_3d(texture_id.target,
+                                 0,
+                                 internal_format as gl::GLint,
+                                 texture.width as gl::GLint,
+                                 texture.height as gl::GLint,
+                                 layer_count,
+                                 0,
+                                 gl_format,
+                                 type_,
+                                 None);
+
+                let needed_layer_count = layer_count - current_layer_count;
+                let new_fbos = gl::gen_framebuffers(needed_layer_count);
+                texture.fbo_ids.extend(new_fbos.into_iter().map(|id| FBOId(id)));
+
+                for (fbo_index, fbo_id) in texture.fbo_ids.iter().enumerate() {
+                    gl::bind_framebuffer(gl::FRAMEBUFFER, fbo_id.0);
+                    gl::framebuffer_texture_layer(gl::FRAMEBUFFER,
+                                                  gl::COLOR_ATTACHMENT0,
+                                                  texture_id.name,
+                                                  0,
+                                                  fbo_index as gl::GLint);
+
+                    // TODO(gw): Share depth render buffer between FBOs to
+                    //           save memory!
+                    // TODO(gw): Free these renderbuffers on exit!
+                    let renderbuffer_ids = gl::gen_renderbuffers(1);
+                    let depth_rb = renderbuffer_ids[0];
+                    gl::bind_renderbuffer(gl::RENDERBUFFER, depth_rb);
+                    gl::renderbuffer_storage(gl::RENDERBUFFER,
+                                             gl::DEPTH_COMPONENT24,
+                                             texture.width as gl::GLsizei,
+                                             texture.height as gl::GLsizei);
+                    gl::framebuffer_renderbuffer(gl::FRAMEBUFFER,
+                                                 gl::DEPTH_ATTACHMENT,
+                                                 gl::RENDERBUFFER,
+                                                 depth_rb);
+                }
+            }
+            None => {
+                debug_assert!(texture.fbo_ids.len() == 0 || texture.fbo_ids.len() == 1);
+                if texture.fbo_ids.is_empty() {
+                    let new_fbo = gl::gen_framebuffers(1)[0];
+
+                    gl::bind_framebuffer(gl::FRAMEBUFFER, new_fbo);
+
+                    gl::framebuffer_texture_2d(gl::FRAMEBUFFER,
+                                               gl::COLOR_ATTACHMENT0,
+                                               texture_id.target,
+                                               texture_id.name,
+                                               0);
+
+                    texture.fbo_ids.push(FBOId(new_fbo));
+                }
+            }
+        }
+
+        // TODO(gw): Hack! Modify the code above to use the normal binding interfaces the device exposes.
+        gl::bind_framebuffer(gl::READ_FRAMEBUFFER, self.bound_read_fbo.0);
+        gl::bind_framebuffer(gl::DRAW_FRAMEBUFFER, self.bound_draw_fbo.0);
+    }
+
+    pub fn blit_render_target(&mut self,
+                              src_texture: Option<(TextureId, i32)>,
+                              src_rect: Option<DeviceIntRect>,
+                              dest_rect: DeviceIntRect) {
+        debug_assert!(self.inside_frame);
+
+        let src_rect = src_rect.unwrap_or_else(|| {
+            let texture = self.textures.get(&src_texture.unwrap().0).expect("unknown texture id!");
+            DeviceIntRect::new(DeviceIntPoint::zero(),
+                               DeviceIntSize::new(texture.width as gl::GLint,
+                                                  texture.height as gl::GLint))
+        });
+
+        self.bind_read_target(src_texture);
+
+        gl::blit_framebuffer(src_rect.origin.x,
+                             src_rect.origin.y,
+                             src_rect.origin.x + src_rect.size.width,
+                             src_rect.origin.y + src_rect.size.height,
+                             dest_rect.origin.x,
+                             dest_rect.origin.y,
+                             dest_rect.origin.x + dest_rect.size.width,
+                             dest_rect.origin.y + dest_rect.size.height,
+                             gl::COLOR_BUFFER_BIT,
+                             gl::LINEAR);
+    }
+
+    pub fn resize_texture(&mut self,
+                          texture_id: TextureId,
+                          new_width: u32,
+                          new_height: u32,
+                          format: ImageFormat,
+                          filter: TextureFilter,
+                          mode: RenderTargetMode) {
+        debug_assert!(self.inside_frame);
+
+        let (old_width, old_height) = self.get_texture_dimensions(texture_id);
+
+        let temp_texture_id = self.create_texture_ids(1, TextureTarget::Default)[0];
+        self.init_texture(temp_texture_id, old_width, old_height, format, filter, mode, None);
+        self.create_fbo_for_texture_if_necessary(temp_texture_id, None);
+
+        self.bind_read_target(Some((texture_id, 0)));
+        self.bind_texture(DEFAULT_TEXTURE, temp_texture_id);
+
+        gl::copy_tex_sub_image_2d(temp_texture_id.target,
+                                  0,
+                                  0,
+                                  0,
+                                  0,
+                                  0,
+                                  old_width as i32,
+                                  old_height as i32);
+
+        self.deinit_texture(texture_id);
+        self.init_texture(texture_id, new_width, new_height, format, filter, mode, None);
+        self.create_fbo_for_texture_if_necessary(texture_id, None);
+        self.bind_read_target(Some((temp_texture_id, 0)));
+        self.bind_texture(DEFAULT_TEXTURE, texture_id);
+
+        gl::copy_tex_sub_image_2d(texture_id.target,
+                                  0,
+                                  0,
+                                  0,
+                                  0,
+                                  0,
+                                  old_width as i32,
+                                  old_height as i32);
+
+        self.bind_read_target(None);
+        self.deinit_texture(temp_texture_id);
+    }
+
+    pub fn deinit_texture(&mut self, texture_id: TextureId) {
+        debug_assert!(self.inside_frame);
+
+        self.bind_texture(DEFAULT_TEXTURE, texture_id);
+
+        let texture = self.textures.get_mut(&texture_id).unwrap();
+        let (internal_format, gl_format) = gl_texture_formats_for_image_format(texture.format);
+        let type_ = gl_type_for_texture_format(texture.format);
+
+        gl::tex_image_2d(texture_id.target,
+                         0,
+                         internal_format,
+                         0,
+                         0,
+                         0,
+                         gl_format,
+                         type_,
+                         None);
+
+        if !texture.fbo_ids.is_empty() {
+            let fbo_ids: Vec<_> = texture.fbo_ids.iter().map(|&FBOId(fbo_id)| fbo_id).collect();
+            gl::delete_framebuffers(&fbo_ids[..]);
+        }
+
+        texture.format = ImageFormat::Invalid;
+        texture.width = 0;
+        texture.height = 0;
+        texture.fbo_ids.clear();
+    }
+
+    pub fn create_program(&mut self,
+                          base_filename: &str,
+                          include_filename: &str) -> ProgramId {
+        self.create_program_with_prefix(base_filename, &[include_filename], None)
+    }
+
+    pub fn create_program_with_prefix(&mut self,
+                                      base_filename: &str,
+                                      include_filenames: &[&str],
+                                      prefix: Option<String>) -> ProgramId {
+        debug_assert!(self.inside_frame);
+
+        let pid = gl::create_program();
+
+        let mut vs_name = String::from(base_filename);
+        vs_name.push_str(".vs");
+        let mut fs_name = String::from(base_filename);
+        fs_name.push_str(".fs");
+
+        let mut include = format!("// Base shader: {}\n", base_filename);
+        for inc_filename in include_filenames {
+            let src = get_shader_source(inc_filename, &self.resource_override_path);
+            include.push_str(&src);
+        }
+
+        if let Some(shared_src) = get_optional_shader_source(base_filename, &self.resource_override_path) {
+            include.push_str(&shared_src);
+        }
+
+        let program = Program {
+            name: base_filename.to_owned(),
+            id: pid,
+            u_transform: -1,
+            u_device_pixel_ratio: -1,
+            vs_source: get_shader_source(&vs_name, &self.resource_override_path),
+            fs_source: get_shader_source(&fs_name, &self.resource_override_path),
+            prefix: prefix,
+            vs_id: None,
+            fs_id: None,
+        };
+
+        let program_id = ProgramId(pid);
+
+        debug_assert!(self.programs.contains_key(&program_id) == false);
+        self.programs.insert(program_id, program);
+
+        self.load_program(program_id, include, true);
+
+        program_id
+    }
+
+    fn load_program(&mut self,
+                    program_id: ProgramId,
+                    include: String,
+                    panic_on_fail: bool) {
+        debug_assert!(self.inside_frame);
+
+        let program = self.programs.get_mut(&program_id).unwrap();
+
+        let mut vs_preamble = Vec::new();
+        let mut fs_preamble = Vec::new();
+
+        vs_preamble.push("#define WR_VERTEX_SHADER\n".to_owned());
+        fs_preamble.push("#define WR_FRAGMENT_SHADER\n".to_owned());
+
+        if let Some(ref prefix) = program.prefix {
+            vs_preamble.push(prefix.clone());
+            fs_preamble.push(prefix.clone());
+        }
+
+        vs_preamble.push(self.shader_preamble.to_owned());
+        fs_preamble.push(self.shader_preamble.to_owned());
+
+        vs_preamble.push(include.clone());
+        fs_preamble.push(include);
+
+        // todo(gw): store shader ids so they can be freed!
+        let vs_id = Device::compile_shader(&program.name,
+                                           &program.vs_source,
+                                           gl::VERTEX_SHADER,
+                                           &vs_preamble,
+                                           panic_on_fail);
+        let fs_id = Device::compile_shader(&program.name,
+                                           &program.fs_source,
+                                           gl::FRAGMENT_SHADER,
+                                           &fs_preamble,
+                                           panic_on_fail);
+
+        match (vs_id, fs_id) {
+            (Some(vs_id), None) => {
+                println!("FAILED to load fs - falling back to previous!");
+                gl::delete_shader(vs_id);
+            }
+            (None, Some(fs_id)) => {
+                println!("FAILED to load vs - falling back to previous!");
+                gl::delete_shader(fs_id);
+            }
+            (None, None) => {
+                println!("FAILED to load vs/fs - falling back to previous!");
+            }
+            (Some(vs_id), Some(fs_id)) => {
+                if let Some(vs_id) = program.vs_id {
+                    gl::detach_shader(program.id, vs_id);
+                }
+
+                if let Some(fs_id) = program.fs_id {
+                    gl::detach_shader(program.id, fs_id);
+                }
+
+                if program.attach_and_bind_shaders(vs_id, fs_id, panic_on_fail) {
+                    if let Some(vs_id) = program.vs_id {
+                        gl::delete_shader(vs_id);
+                    }
+
+                    if let Some(fs_id) = program.fs_id {
+                        gl::delete_shader(fs_id);
+                    }
+
+                    program.vs_id = Some(vs_id);
+                    program.fs_id = Some(fs_id);
+                } else {
+                    let vs_id = program.vs_id.unwrap();
+                    let fs_id = program.fs_id.unwrap();
+                    program.attach_and_bind_shaders(vs_id, fs_id, true);
+                }
+
+                program.u_transform = gl::get_uniform_location(program.id, "uTransform");
+                program.u_device_pixel_ratio = gl::get_uniform_location(program.id, "uDevicePixelRatio");
+
+                program_id.bind();
+                let u_color_0 = gl::get_uniform_location(program.id, "sColor0");
+                if u_color_0 != -1 {
+                    gl::uniform_1i(u_color_0, TextureSampler::Color0 as i32);
+                }
+                let u_color1 = gl::get_uniform_location(program.id, "sColor1");
+                if u_color1 != -1 {
+                    gl::uniform_1i(u_color1, TextureSampler::Color1 as i32);
+                }
+                let u_color_2 = gl::get_uniform_location(program.id, "sColor2");
+                if u_color_2 != -1 {
+                    gl::uniform_1i(u_color_2, TextureSampler::Color2 as i32);
+                }
+                let u_mask = gl::get_uniform_location(program.id, "sMask");
+                if u_mask != -1 {
+                    gl::uniform_1i(u_mask, TextureSampler::Mask as i32);
+                }
+
+                let u_cache = gl::get_uniform_location(program.id, "sCache");
+                if u_cache != -1 {
+                    gl::uniform_1i(u_cache, TextureSampler::Cache as i32);
+                }
+
+                let u_layers = gl::get_uniform_location(program.id, "sLayers");
+                if u_layers != -1 {
+                    gl::uniform_1i(u_layers, TextureSampler::Layers as i32);
+                }
+
+                let u_tasks = gl::get_uniform_location(program.id, "sRenderTasks");
+                if u_tasks != -1 {
+                    gl::uniform_1i(u_tasks, TextureSampler::RenderTasks as i32);
+                }
+
+                let u_prim_geom = gl::get_uniform_location(program.id, "sPrimGeometry");
+                if u_prim_geom != -1 {
+                    gl::uniform_1i(u_prim_geom, TextureSampler::Geometry as i32);
+                }
+
+                let u_data16 = gl::get_uniform_location(program.id, "sData16");
+                if u_data16 != -1 {
+                    gl::uniform_1i(u_data16, TextureSampler::Data16 as i32);
+                }
+
+                let u_data32 = gl::get_uniform_location(program.id, "sData32");
+                if u_data32 != -1 {
+                    gl::uniform_1i(u_data32, TextureSampler::Data32 as i32);
+                }
+
+                let u_data64 = gl::get_uniform_location(program.id, "sData64");
+                if u_data64 != -1 {
+                    gl::uniform_1i(u_data64, TextureSampler::Data64 as i32);
+                }
+
+                let u_data128 = gl::get_uniform_location(program.id, "sData128");
+                if u_data128 != -1 {
+                    gl::uniform_1i(u_data128, TextureSampler::Data128    as i32);
+                }
+
+                let u_resource_rects = gl::get_uniform_location(program.id, "sResourceRects");
+                if u_resource_rects != -1 {
+                    gl::uniform_1i(u_resource_rects, TextureSampler::ResourceRects as i32);
+                }
+            }
+        }
+    }
+
+/*
+    pub fn refresh_shader(&mut self, path: PathBuf) {
+        let mut vs_preamble_path = self.resource_path.clone();
+        vs_preamble_path.push(VERTEX_SHADER_PREAMBLE);
+
+        let mut fs_preamble_path = self.resource_path.clone();
+        fs_preamble_path.push(FRAGMENT_SHADER_PREAMBLE);
+
+        let mut refresh_all = false;
+
+        if path == vs_preamble_path {
+            let mut f = File::open(&vs_preamble_path).unwrap();
+            self.vertex_shader_preamble = String::new();
+            f.read_to_string(&mut self.vertex_shader_preamble).unwrap();
+            refresh_all = true;
+        }
+
+        if path == fs_preamble_path {
+            let mut f = File::open(&fs_preamble_path).unwrap();
+            self.fragment_shader_preamble = String::new();
+            f.read_to_string(&mut self.fragment_shader_preamble).unwrap();
+            refresh_all = true;
+        }
+
+        let mut programs_to_update = Vec::new();
+
+        for (program_id, program) in &mut self.programs {
+            if refresh_all || program.vs_path == path || program.fs_path == path {
+                programs_to_update.push(*program_id)
+            }
+        }
+
+        for program_id in programs_to_update {
+            self.load_program(program_id, false);
+        }
+    }*/
+
+    pub fn get_uniform_location(&self, program_id: ProgramId, name: &str) -> UniformLocation {
+        let ProgramId(program_id) = program_id;
+        UniformLocation(gl::get_uniform_location(program_id, name))
+    }
+
+    pub fn set_uniform_2f(&self, uniform: UniformLocation, x: f32, y: f32) {
+        debug_assert!(self.inside_frame);
+        let UniformLocation(location) = uniform;
+        gl::uniform_2f(location, x, y);
+    }
+
+    fn set_uniforms(&self,
+                    program: &Program,
+                    transform: &Matrix4D<f32>,
+                    device_pixel_ratio: f32) {
+        debug_assert!(self.inside_frame);
+        gl::uniform_matrix_4fv(program.u_transform,
+                               false,
+                               &transform.to_row_major_array());
+        gl::uniform_1f(program.u_device_pixel_ratio, device_pixel_ratio);
+    }
+
+    fn update_image_for_2d_texture(&mut self,
+                                   target: gl::GLuint,
+                                   x0: gl::GLint,
+                                   y0: gl::GLint,
+                                   width: gl::GLint,
+                                   height: gl::GLint,
+                                   format: gl::GLuint,
+                                   data: &[u8]) {
+        gl::tex_sub_image_2d(target,
+                             0,
+                             x0, y0,
+                             width, height,
+                             format,
+                             gl::UNSIGNED_BYTE,
+                             data);
+    }
+
+    pub fn update_texture(&mut self,
+                          texture_id: TextureId,
+                          x0: u32,
+                          y0: u32,
+                          width: u32,
+                          height: u32,
+                          stride: Option<u32>,
+                          data: &[u8]) {
+        debug_assert!(self.inside_frame);
+
+        let mut expanded_data = Vec::new();
+
+        let (gl_format, bpp, data) = match self.textures.get(&texture_id).unwrap().format {
+            ImageFormat::A8 => {
+                if cfg!(any(target_arch="arm", target_arch="aarch64")) {
+                    for byte in data {
+                        expanded_data.push(*byte);
+                        expanded_data.push(*byte);
+                        expanded_data.push(*byte);
+                        expanded_data.push(*byte);
+                    }
+                    (GL_FORMAT_BGRA, 4, expanded_data.as_slice())
+                } else {
+                    (GL_FORMAT_A, 1, data)
+                }
+            }
+            ImageFormat::RGB8 => (gl::RGB, 3, data),
+            ImageFormat::RGBA8 => (GL_FORMAT_BGRA, 4, data),
+            ImageFormat::Invalid | ImageFormat::RGBAF32 => unreachable!(),
+        };
+
+        let row_length = match stride {
+            Some(value) => value / bpp,
+            None => width,
+        };
+
+        assert!(data.len() as u32 == bpp * row_length * height);
+
+        if let Some(..) = stride {
+            gl::pixel_store_i(gl::UNPACK_ROW_LENGTH, row_length as gl::GLint);
+        }
+
+        self.bind_texture(DEFAULT_TEXTURE, texture_id);
+        self.update_image_for_2d_texture(texture_id.target,
+                                         x0 as gl::GLint,
+                                         y0 as gl::GLint,
+                                         width as gl::GLint,
+                                         height as gl::GLint,
+                                         gl_format,
+                                         data);
+
+        // Reset row length to 0, otherwise the stride would apply to all texture uploads.
+        if let Some(..) = stride {
+            gl::pixel_store_i(gl::UNPACK_ROW_LENGTH, 0 as gl::GLint);
+        }
+    }
+
+    fn clear_vertex_array(&mut self) {
+        debug_assert!(self.inside_frame);
+        gl::bind_vertex_array(0);
+    }
+
+    pub fn bind_vao(&mut self, vao_id: VAOId) {
+        debug_assert!(self.inside_frame);
+
+        if self.bound_vao != vao_id {
+            self.bound_vao = vao_id;
+
+            let VAOId(id) = vao_id;
+            gl::bind_vertex_array(id);
+        }
+    }
+
+    fn create_vao_with_vbos(&mut self,
+                            format: VertexFormat,
+                            main_vbo_id: VBOId,
+                            instance_vbo_id: VBOId,
+                            ibo_id: IBOId,
+                            vertex_offset: gl::GLuint,
+                            instance_stride: gl::GLint,
+                            owns_vertices: bool,
+                            owns_instances: bool,
+                            owns_indices: bool)
+                            -> VAOId {
+        debug_assert!(self.inside_frame);
+
+        let vao_ids = gl::gen_vertex_arrays(1);
+        let vao_id = vao_ids[0];
+
+        gl::bind_vertex_array(vao_id);
+
+        format.bind(main_vbo_id, instance_vbo_id, vertex_offset, instance_stride);
+        ibo_id.bind(); // force it to be a part of VAO
+
+        let vao = VAO {
+            id: vao_id,
+            ibo_id: ibo_id,
+            main_vbo_id: main_vbo_id,
+            instance_vbo_id: instance_vbo_id,
+            instance_stride: instance_stride,
+            owns_indices: owns_indices,
+            owns_vertices: owns_vertices,
+            owns_instances: owns_instances,
+        };
+
+        gl::bind_vertex_array(0);
+
+        let vao_id = VAOId(vao_id);
+
+        debug_assert!(!self.vaos.contains_key(&vao_id));
+        self.vaos.insert(vao_id, vao);
+
+        vao_id
+    }
+
+    pub fn create_vao(&mut self, format: VertexFormat, inst_stride: gl::GLint) -> VAOId {
+        debug_assert!(self.inside_frame);
+
+        let buffer_ids = gl::gen_buffers(3);
+        let ibo_id = IBOId(buffer_ids[0]);
+        let main_vbo_id = VBOId(buffer_ids[1]);
+        let intance_vbo_id = VBOId(buffer_ids[2]);
+
+        self.create_vao_with_vbos(format, main_vbo_id, intance_vbo_id, ibo_id, 0, inst_stride, true, true, true)
+    }
+
+    pub fn create_vao_with_new_instances(&mut self, format: VertexFormat, inst_stride: gl::GLint,
+                                         base_vao: VAOId) -> VAOId {
+        debug_assert!(self.inside_frame);
+
+        let buffer_ids = gl::gen_buffers(1);
+        let intance_vbo_id = VBOId(buffer_ids[0]);
+        let (main_vbo_id, ibo_id) = {
+            let vao = self.vaos.get(&base_vao).unwrap();
+            (vao.main_vbo_id, vao.ibo_id)
+        };
+
+        self.create_vao_with_vbos(format, main_vbo_id, intance_vbo_id, ibo_id, 0, inst_stride, false, true, false)
+    }
+
+    pub fn update_vao_main_vertices<V>(&mut self,
+                                       vao_id: VAOId,
+                                       vertices: &[V],
+                                       usage_hint: VertexUsageHint) {
+        debug_assert!(self.inside_frame);
+
+        let vao = self.vaos.get(&vao_id).unwrap();
+        debug_assert_eq!(self.bound_vao, vao_id);
+
+        vao.main_vbo_id.bind();
+        gl::buffer_data(gl::ARRAY_BUFFER, &vertices, usage_hint.to_gl());
+    }
+
+    pub fn update_vao_instances<V>(&mut self,
+                                   vao_id: VAOId,
+                                   instances: &[V],
+                                   usage_hint: VertexUsageHint) {
+        debug_assert!(self.inside_frame);
+
+        let vao = self.vaos.get(&vao_id).unwrap();
+        debug_assert_eq!(self.bound_vao, vao_id);
+        debug_assert_eq!(vao.instance_stride as usize, mem::size_of::<V>());
+
+        vao.instance_vbo_id.bind();
+        gl::buffer_data(gl::ARRAY_BUFFER, &instances, usage_hint.to_gl());
+    }
+
+    pub fn update_vao_indices<I>(&mut self,
+                                 vao_id: VAOId,
+                                 indices: &[I],
+                                 usage_hint: VertexUsageHint) {
+        debug_assert!(self.inside_frame);
+
+        let vao = self.vaos.get(&vao_id).unwrap();
+        debug_assert_eq!(self.bound_vao, vao_id);
+
+        vao.ibo_id.bind();
+        gl::buffer_data(gl::ELEMENT_ARRAY_BUFFER, &indices, usage_hint.to_gl());
+    }
+
+    pub fn draw_triangles_u16(&mut self, first_vertex: i32, index_count: i32) {
+        debug_assert!(self.inside_frame);
+        gl::draw_elements(gl::TRIANGLES,
+                          index_count,
+                          gl::UNSIGNED_SHORT,
+                          first_vertex as u32 * 2);
+    }
+
+    pub fn draw_triangles_u32(&mut self, first_vertex: i32, index_count: i32) {
+        debug_assert!(self.inside_frame);
+        gl::draw_elements(gl::TRIANGLES,
+                          index_count,
+                          gl::UNSIGNED_INT,
+                          first_vertex as u32 * 4);
+    }
+
+    pub fn draw_nonindexed_lines(&mut self, first_vertex: i32, vertex_count: i32) {
+        debug_assert!(self.inside_frame);
+        gl::draw_arrays(gl::LINES,
+                          first_vertex,
+                          vertex_count);
+    }
+
+    pub fn draw_indexed_triangles_instanced_u16(&mut self,
+                                                index_count: i32,
+                                                instance_count: i32) {
+        debug_assert!(self.inside_frame);
+        gl::draw_elements_instanced(gl::TRIANGLES, index_count, gl::UNSIGNED_SHORT, 0, instance_count);
+    }
+
+    pub fn end_frame(&mut self) {
+        self.bind_draw_target(None, None);
+        self.bind_read_target(None);
+
+        debug_assert!(self.inside_frame);
+        self.inside_frame = false;
+
+        gl::bind_texture(gl::TEXTURE_2D, 0);
+        gl::use_program(0);
+
+        for i in 0..self.bound_textures.len() {
+            gl::active_texture(gl::TEXTURE0 + i as gl::GLuint);
+            gl::bind_texture(gl::TEXTURE_2D, 0);
+        }
+
+        gl::active_texture(gl::TEXTURE0);
+    }
+
+    pub fn assign_ubo_binding(&self, program_id: ProgramId, name: &str, value: u32) -> u32 {
+        let index = gl::get_uniform_block_index(program_id.0, name);
+        gl::uniform_block_binding(program_id.0, index, value);
+        index
+    }
+
+    pub fn create_ubo<T>(&self, data: &[T], binding: u32) -> UBOId {
+        let ubo = gl::gen_buffers(1)[0];
+        gl::bind_buffer(gl::UNIFORM_BUFFER, ubo);
+        gl::buffer_data(gl::UNIFORM_BUFFER, data, gl::STATIC_DRAW);
+        gl::bind_buffer_base(gl::UNIFORM_BUFFER, binding, ubo);
+        UBOId(ubo)
+    }
+
+    pub fn reset_ubo(&self, binding: u32) {
+        gl::bind_buffer(gl::UNIFORM_BUFFER, 0);
+        gl::bind_buffer_base(gl::UNIFORM_BUFFER, binding, 0);
+    }
+
+    pub fn delete_buffer(&self, buffer: UBOId) {
+        gl::delete_buffers(&[buffer.0]);
+    }
+
+    #[cfg(target_os = "android")]
+    pub fn set_multisample(&self, enable: bool) {
+    }
+
+    #[cfg(not(target_os = "android"))]
+    pub fn set_multisample(&self, enable: bool) {
+        if self.capabilities.supports_multisampling {
+            if enable {
+                gl::enable(gl::MULTISAMPLE);
+            } else {
+                gl::disable(gl::MULTISAMPLE);
+            }
+        }
+    }
+
+    pub fn clear_target(&self,
+                        color: Option<[f32; 4]>,
+                        depth: Option<f32>) {
+        let mut clear_bits = 0;
+
+        if let Some(color) = color {
+            gl::clear_color(color[0], color[1], color[2], color[3]);
+            clear_bits |= gl::COLOR_BUFFER_BIT;
+        }
+
+        if let Some(depth) = depth {
+            gl::clear_depth(depth as f64);
+            clear_bits |= gl::DEPTH_BUFFER_BIT;
+        }
+
+        if clear_bits != 0 {
+            gl::clear(clear_bits);
+        }
+    }
+
+    pub fn enable_depth(&self) {
+        gl::enable(gl::DEPTH_TEST);
+    }
+
+    pub fn disable_depth(&self) {
+        gl::disable(gl::DEPTH_TEST);
+    }
+
+    pub fn set_depth_func(&self, depth_func: DepthFunction) {
+        gl::depth_func(depth_func as gl::GLuint);
+    }
+
+    pub fn enable_depth_write(&self) {
+        gl::depth_mask(true);
+    }
+
+    pub fn disable_depth_write(&self) {
+        gl::depth_mask(false);
+    }
+
+    pub fn disable_stencil(&self) {
+        gl::disable(gl::STENCIL_TEST);
+    }
+
+    pub fn disable_scissor(&self) {
+        gl::disable(gl::SCISSOR_TEST);
+    }
+
+    pub fn set_blend(&self, enable: bool) {
+        if enable {
+            gl::enable(gl::BLEND);
+        } else {
+            gl::disable(gl::BLEND);
+        }
+    }
+
+    pub fn set_blend_mode_premultiplied_alpha(&self) {
+        gl::blend_func(gl::SRC_ALPHA, gl::ZERO);
+        gl::blend_equation(gl::FUNC_ADD);
+    }
+
+    pub fn set_blend_mode_alpha(&self) {
+        //gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
+        gl::blend_func_separate(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA,
+                                gl::ONE, gl::ONE);
+        gl::blend_equation(gl::FUNC_ADD);
+    }
+
+    pub fn set_blend_mode_subpixel(&self, color: ColorF) {
+        gl::blend_color(color.r, color.g, color.b, color.a);
+        gl::blend_func(gl::CONSTANT_COLOR, gl::ONE_MINUS_SRC_COLOR);
+    }
+
+    pub fn set_blend_mode_multiply(&self) {
+        gl::blend_func_separate(gl::ZERO, gl::SRC_COLOR,
+                                gl::ZERO, gl::SRC_ALPHA);
+        gl::blend_equation(gl::FUNC_ADD);
+    }
+}
+
+impl Drop for Device {
+    fn drop(&mut self) {
+        //self.file_watcher.exit();
+    }
+}
+
+fn gl_texture_formats_for_image_format(format: ImageFormat) -> (gl::GLint, gl::GLuint) {
+    match format {
+        ImageFormat::A8 => {
+            if cfg!(any(target_arch="arm", target_arch="aarch64")) {
+                (GL_FORMAT_BGRA as gl::GLint, GL_FORMAT_BGRA)
+            } else {
+                (GL_FORMAT_A as gl::GLint, GL_FORMAT_A)
+            }
+        },
+        ImageFormat::RGB8 => (gl::RGB as gl::GLint, gl::RGB),
+        ImageFormat::RGBA8 => {
+            if cfg!(any(target_arch="arm", target_arch="aarch64")) {
+                (GL_FORMAT_BGRA as gl::GLint, GL_FORMAT_BGRA)
+            } else {
+                (gl::RGBA as gl::GLint, GL_FORMAT_BGRA)
+            }
+        }
+        ImageFormat::RGBAF32 => (gl::RGBA32F as gl::GLint, gl::RGBA),
+        ImageFormat::Invalid => unreachable!(),
+    }
+}
+
+fn gl_type_for_texture_format(format: ImageFormat) -> gl::GLuint {
+    match format {
+        ImageFormat::RGBAF32 => gl::FLOAT,
+        _ => gl::UNSIGNED_BYTE,
+    }
+}
+
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/src/frame.rs
@@ -0,0 +1,660 @@
+/* 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 fnv::FnvHasher;
+use internal_types::{ANGLE_FLOAT_TO_FIXED, AxisDirection};
+use internal_types::{CompositionOp};
+use internal_types::{LowLevelFilterOp};
+use internal_types::{RendererFrame};
+use layer::Layer;
+use resource_cache::ResourceCache;
+use scene::Scene;
+use scroll_tree::{ScrollTree, ScrollStates};
+use std::collections::HashMap;
+use std::hash::BuildHasherDefault;
+use tiling::{AuxiliaryListsMap, FrameBuilder, FrameBuilderConfig, PrimitiveFlags};
+use webrender_traits::{AuxiliaryLists, ClipRegion, ColorF, DisplayItem, Epoch, FilterOp};
+use webrender_traits::{LayerPoint, LayerRect, LayerSize, LayerToScrollTransform};
+use webrender_traits::{MixBlendMode, PipelineId, ScrollEventPhase, ScrollLayerId, ScrollLayerState};
+use webrender_traits::{ScrollLocation, ScrollPolicy, ServoScrollRootId, SpecificDisplayItem};
+use webrender_traits::{StackingContext, WorldPoint};
+
+#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
+pub struct FrameId(pub u32);
+
+static DEFAULT_SCROLLBAR_COLOR: ColorF = ColorF { r: 0.3, g: 0.3, b: 0.3, a: 0.6 };
+
+struct FlattenContext<'a> {
+    scene: &'a Scene,
+    pipeline_sizes: &'a mut HashMap<PipelineId, LayerSize>,
+    builder: &'a mut FrameBuilder,
+}
+
+// TODO: doc
+pub struct Frame {
+    pub scroll_tree: ScrollTree,
+    pub pipeline_epoch_map: HashMap<PipelineId, Epoch, BuildHasherDefault<FnvHasher>>,
+    pub pipeline_auxiliary_lists: AuxiliaryListsMap,
+    id: FrameId,
+    debug: bool,
+    frame_builder_config: FrameBuilderConfig,
+    frame_builder: Option<FrameBuilder>,
+}
+
+trait DisplayListHelpers {
+    fn starting_stacking_context<'a>(&'a self) -> Option<(&'a StackingContext, &'a ClipRegion)>;
+}
+
+impl DisplayListHelpers for Vec<DisplayItem> {
+    fn starting_stacking_context<'a>(&'a self) -> Option<(&'a StackingContext, &'a ClipRegion)> {
+        self.first().and_then(|item| match item.item {
+            SpecificDisplayItem::PushStackingContext(ref specific_item) => {
+                Some((&specific_item.stacking_context, &item.clip))
+            },
+            _ => None,
+        })
+    }
+}
+
+trait StackingContextHelpers {
+    fn needs_composition_operation_for_mix_blend_mode(&self) -> bool;
+    fn composition_operations(&self, auxiliary_lists: &AuxiliaryLists) -> Vec<CompositionOp>;
+}
+
+impl StackingContextHelpers for StackingContext {
+    fn needs_composition_operation_for_mix_blend_mode(&self) -> bool {
+        match self.mix_blend_mode {
+            MixBlendMode::Normal => false,
+            MixBlendMode::Multiply |
+            MixBlendMode::Screen |
+            MixBlendMode::Overlay |
+            MixBlendMode::Darken |
+            MixBlendMode::Lighten |
+            MixBlendMode::ColorDodge |
+            MixBlendMode::ColorBurn |
+            MixBlendMode::HardLight |
+            MixBlendMode::SoftLight |
+            MixBlendMode::Difference |
+            MixBlendMode::Exclusion |
+            MixBlendMode::Hue |
+            MixBlendMode::Saturation |
+            MixBlendMode::Color |
+            MixBlendMode::Luminosity => true,
+        }
+    }
+
+    fn composition_operations(&self, auxiliary_lists: &AuxiliaryLists) -> Vec<CompositionOp> {
+        let mut composition_operations = vec![];
+        if self.needs_composition_operation_for_mix_blend_mode() {
+            composition_operations.push(CompositionOp::MixBlend(self.mix_blend_mode));
+        }
+        for filter in auxiliary_lists.filters(&self.filters) {
+            match *filter {
+                FilterOp::Blur(radius) => {
+                    composition_operations.push(CompositionOp::Filter(LowLevelFilterOp::Blur(
+                        radius,
+                        AxisDirection::Horizontal)));
+                    composition_operations.push(CompositionOp::Filter(LowLevelFilterOp::Blur(
+                        radius,
+                        AxisDirection::Vertical)));
+                }
+                FilterOp::Brightness(amount) => {
+                    composition_operations.push(CompositionOp::Filter(
+                            LowLevelFilterOp::Brightness(Au::from_f32_px(amount))));
+                }
+                FilterOp::Contrast(amount) => {
+                    composition_operations.push(CompositionOp::Filter(
+                            LowLevelFilterOp::Contrast(Au::from_f32_px(amount))));
+                }
+                FilterOp::Grayscale(amount) => {
+                    composition_operations.push(CompositionOp::Filter(
+                            LowLevelFilterOp::Grayscale(Au::from_f32_px(amount))));
+                }
+                FilterOp::HueRotate(angle) => {
+                    composition_operations.push(CompositionOp::Filter(
+                            LowLevelFilterOp::HueRotate(f32::round(
+                                    angle * ANGLE_FLOAT_TO_FIXED) as i32)));
+                }
+                FilterOp::Invert(amount) => {
+                    composition_operations.push(CompositionOp::Filter(
+                            LowLevelFilterOp::Invert(Au::from_f32_px(amount))));
+                }
+                FilterOp::Opacity(amount) => {
+                    composition_operations.push(CompositionOp::Filter(
+                            LowLevelFilterOp::Opacity(Au::from_f32_px(amount))));
+                }
+                FilterOp::Saturate(amount) => {
+                    composition_operations.push(CompositionOp::Filter(
+                            LowLevelFilterOp::Saturate(Au::from_f32_px(amount))));
+                }
+                FilterOp::Sepia(amount) => {
+                    composition_operations.push(CompositionOp::Filter(
+                            LowLevelFilterOp::Sepia(Au::from_f32_px(amount))));
+                }
+            }
+        }
+
+        composition_operations
+    }
+}
+
+struct DisplayListTraversal<'a> {
+    pub display_list: &'a [DisplayItem],
+    pub next_item_index: usize,
+}
+
+impl<'a> DisplayListTraversal<'a> {
+    pub fn new_skipping_first(display_list: &'a Vec<DisplayItem>) -> DisplayListTraversal {
+        DisplayListTraversal {
+            display_list: display_list,
+            next_item_index: 1,
+        }
+    }
+
+    pub fn skip_current_stacking_context(&mut self) {
+        for item in self {
+            if item.item == SpecificDisplayItem::PopStackingContext {
+                return;
+            }
+        }
+    }
+
+    pub fn current_stacking_context_empty(&self) -> bool {
+        match self.peek() {
+            Some(item) => item.item == SpecificDisplayItem::PopStackingContext,
+            None => true,
+        }
+    }
+
+    fn peek(&self) -> Option<&'a DisplayItem> {
+        if self.next_item_index >= self.display_list.len() {
+            return None
+        }
+        Some(&self.display_list[self.next_item_index])
+    }
+}
+
+impl<'a> Iterator for DisplayListTraversal<'a> {
+    type Item = &'a DisplayItem;
+
+    fn next(&mut self) -> Option<&'a DisplayItem> {
+        if self.next_item_index >= self.display_list.len() {
+            return None
+        }
+
+        let item = &self.display_list[self.next_item_index];
+        self.next_item_index += 1;
+        Some(item)
+    }
+}
+
+impl Frame {
+    pub fn new(debug: bool, config: FrameBuilderConfig) -> Frame {
+        Frame {
+            pipeline_epoch_map: HashMap::with_hasher(Default::default()),
+            pipeline_auxiliary_lists: HashMap::with_hasher(Default::default()),
+            scroll_tree: ScrollTree::new(),
+            id: FrameId(0),
+            debug: debug,
+            frame_builder: None,
+            frame_builder_config: config,
+        }
+    }
+
+    pub fn reset(&mut self) -> ScrollStates {
+        self.pipeline_epoch_map.clear();
+
+        // Advance to the next frame.
+        self.id.0 += 1;
+
+        self.scroll_tree.drain()
+    }
+
+    pub fn get_scroll_layer_state(&self) -> Vec<ScrollLayerState> {
+        self.scroll_tree.get_scroll_layer_state()
+    }
+
+    /// Returns true if any layers actually changed position or false otherwise.
+    pub fn scroll_layers(&mut self,
+                         origin: LayerPoint,
+                         pipeline_id: PipelineId,
+                         scroll_root_id: ServoScrollRootId)
+                          -> bool {
+        self.scroll_tree.scroll_layers(origin, pipeline_id, scroll_root_id)
+    }
+
+    /// Returns true if any layers actually changed position or false otherwise.
+    pub fn scroll(&mut self,
+                  scroll_location: ScrollLocation,
+                  cursor: WorldPoint,
+                  phase: ScrollEventPhase)
+                  -> bool {
+        self.scroll_tree.scroll(scroll_location, cursor, phase,)
+    }
+
+    pub fn tick_scrolling_bounce_animations(&mut self) {
+        self.scroll_tree.tick_scrolling_bounce_animations();
+    }
+
+    pub fn create(&mut self,
+                  scene: &Scene,
+                  pipeline_sizes: &mut HashMap<PipelineId, LayerSize>) {
+        let root_pipeline_id = match scene.root_pipeline_id {
+            Some(root_pipeline_id) => root_pipeline_id,
+            None => return,
+        };
+
+        let root_pipeline = match scene.pipeline_map.get(&root_pipeline_id) {
+            Some(root_pipeline) => root_pipeline,
+            None => return,
+        };
+
+        let display_list = scene.display_lists.get(&root_pipeline_id);
+        let display_list = match display_list {
+            Some(display_list) => display_list,
+            None => return,
+        };
+
+        let old_scrolling_states = self.reset();
+        self.pipeline_auxiliary_lists = scene.pipeline_auxiliary_lists.clone();
+
+        self.pipeline_epoch_map.insert(root_pipeline_id, root_pipeline.epoch);
+
+        let (root_stacking_context, root_clip) = match display_list.starting_stacking_context() {
+            Some(some) => some,
+            None => {
+                warn!("Pipeline display list does not start with a stacking context.");
+                return;
+            }
+        };
+
+        // Insert global position: fixed elements layer
+        debug_assert!(self.scroll_tree.layers.is_empty());
+        let root_scroll_layer_id = ScrollLayerId::root(root_pipeline_id);
+        let root_fixed_layer_id = ScrollLayerId::create_fixed(root_pipeline_id);
+        let root_viewport = LayerRect::new(LayerPoint::zero(), root_pipeline.viewport_size);
+        let layer = Layer::new(&root_viewport,
+                               root_clip.main.size,
+                               &LayerToScrollTransform::identity(),
+                               root_pipeline_id);
+        self.scroll_tree.add_layer(layer.clone(), root_fixed_layer_id, None);
+        self.scroll_tree.add_layer(layer, root_scroll_layer_id, None);
+        self.scroll_tree.root_scroll_layer_id = Some(root_scroll_layer_id);