Send mesh libraries to the client in raw binary instead of Base64 and JSON

This commit is contained in:
Patrick Walton 2017-10-02 16:31:54 -07:00
parent 0bbcf8f1a0
commit 7bbd02ed85
5 changed files with 65 additions and 47 deletions

View File

@ -346,3 +346,11 @@ function toFourCC(buffer: ArrayBuffer, position: number): string {
result += String.fromCharCode(byte);
return result;
}
export function parseServerTiming(headers: Headers): number {
if (!headers.has('Server-Timing'))
return 0.0;
const timing = headers.get('Server-Timing')!;
const matches = /^Partitioning\s*=\s*([0-9.]+)$/.exec(timing);
return matches != null ? parseFloat(matches[1]) / 1000.0 : 0.0;
}

View File

@ -11,7 +11,8 @@
import * as glmatrix from 'gl-matrix';
import * as _ from 'lodash';
import { AntialiasingStrategy, AntialiasingStrategyName, NoAAStrategy, SubpixelAAType } from "./aa-strategy";
import {AntialiasingStrategy, AntialiasingStrategyName, NoAAStrategy} from "./aa-strategy";
import {SubpixelAAType} from "./aa-strategy";
import {DemoAppController} from './app-controller';
import PathfinderBufferTexture from "./buffer-texture";
import {OrthographicCamera} from "./camera";

View File

@ -13,7 +13,7 @@ import * as glmatrix from 'gl-matrix';
import * as _ from 'lodash';
import 'path-data-polyfill.js';
import {PathfinderMeshData} from "./meshes";
import {parseServerTiming, PathfinderMeshData} from "./meshes";
import {panic, unwrapNull} from "./utils";
export const BUILTIN_SVG_URI: string = "/svg/demo";
@ -70,17 +70,15 @@ export class SVGLoader {
partition(pathIndex?: number | undefined): Promise<PathfinderMeshData> {
// Make the request.
const paths = pathIndex == null ? this.paths : [this.paths[pathIndex]];
let time = 0;
return window.fetch(PARTITION_SVG_PATHS_ENDPOINT_URL, {
body: JSON.stringify({ paths: paths }),
headers: {'Content-Type': 'application/json'},
method: 'POST',
}).then(response => response.text()).then(responseText => {
const response = JSON.parse(responseText);
if (!('Ok' in response))
panic("Failed to partition the font!");
const meshes = base64js.toByteArray(response.Ok.pathData);
return new PathfinderMeshData(meshes.buffer as ArrayBuffer);
});
}).then(response => {
time = parseServerTiming(response.headers);
return response.arrayBuffer();
}).then(buffer => new PathfinderMeshData(buffer));
}
private attachSVG(svgElement: SVGSVGElement) {

View File

@ -14,7 +14,7 @@ import * as _ from 'lodash';
import * as opentype from "opentype.js";
import {Metrics} from 'opentype.js';
import {B_QUAD_SIZE, PathfinderMeshData} from "./meshes";
import {B_QUAD_SIZE, parseServerTiming, PathfinderMeshData} from "./meshes";
import {assert, lerp, panic, UINT32_MAX, UINT32_SIZE, unwrapNull} from "./utils";
export const BUILTIN_FONT_URI: string = "/otf/demo";
@ -227,18 +227,18 @@ export class GlyphStore {
};
// Make the request.
let time = 0;
return window.fetch(PARTITION_FONT_ENDPOINT_URI, {
body: JSON.stringify(request),
headers: {'Content-Type': 'application/json'},
method: 'POST',
}).then(response => response.text()).then(responseText => {
const response = JSON.parse(responseText);
if (!('Ok' in response))
panic(`Failed to partition the font: ${response.Err}`);
const meshes = base64js.toByteArray(response.Ok.pathData);
}).then(response => {
time = parseServerTiming(response.headers);
return response.arrayBuffer();
}).then(buffer => {
return {
meshes: new PathfinderMeshData(meshes.buffer as ArrayBuffer),
time: response.Ok.time,
meshes: new PathfinderMeshData(buffer),
time: time,
};
});
}

View File

@ -34,7 +34,7 @@ use pathfinder_path_utils::cubic::CubicCurve;
use pathfinder_path_utils::monotonic::MonotonicPathSegmentStream;
use pathfinder_path_utils::stroke;
use pathfinder_path_utils::{PathBuffer, PathBufferStream, PathSegment, Transform2DPathStream};
use rocket::http::{ContentType, Status};
use rocket::http::{ContentType, Header, Status};
use rocket::request::Request;
use rocket::response::{NamedFile, Redirect, Responder, Response};
use rocket_contrib::json::Json;
@ -112,10 +112,9 @@ struct PartitionGlyph {
struct PartitionFontResponse {
#[serde(rename = "pathData")]
path_data: String,
time: f64,
}
#[derive(Clone, Copy, Serialize, Deserialize)]
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
enum PartitionFontError {
UnknownBuiltinFont,
Base64DecodingFailed,
@ -124,7 +123,7 @@ enum PartitionFontError {
Unimplemented,
}
#[derive(Clone, Copy, Serialize, Deserialize)]
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
enum PartitionSvgPathsError {
UnknownSvgPathSegmentType,
Unimplemented,
@ -154,14 +153,8 @@ struct PartitionSvgPathSegment {
values: Vec<f64>,
}
#[derive(Clone, Serialize, Deserialize)]
struct PartitionSvgPathsResponse {
#[serde(rename = "pathData")]
path_data: String,
}
struct PathPartitioningResult {
encoded_data: String,
encoded_data: Vec<u8>,
time: Duration,
}
@ -182,18 +175,36 @@ impl PathPartitioningResult {
let mut data_buffer = Cursor::new(vec![]);
drop(partitioner.library().serialize_into(&mut data_buffer));
let data_string = base64::encode(data_buffer.get_ref());
PathPartitioningResult {
encoded_data: data_string,
encoded_data: data_buffer.into_inner(),
time: time_elapsed,
}
}
fn elapsed_ms(&self) -> f64 {
self.time.as_secs() as f64 * 1000.0 + self.time.subsec_nanos() as f64 * 1e-6
}
}
struct PartitionResponder {
data: Vec<u8>,
time: f64,
}
impl<'r> Responder<'r> for PartitionResponder {
fn respond_to(self, _: &Request) -> Result<Response<'r>, Status> {
let mut builder = Response::build();
builder.header(ContentType::new("application", "vnd.mozilla.pfml"));
builder.header(Header::new("Server-Timing", format!("Partitioning={}", self.time)));
builder.sized_body(Cursor::new(self.data));
builder.ok()
}
}
#[post("/partition-font", format = "application/json", data = "<request>")]
fn partition_font(request: Json<PartitionFontRequest>)
-> Json<Result<PartitionFontResponse, PartitionFontError>> {
-> Result<PartitionResponder, PartitionFontError> {
// Fetch the OTF data.
let otf_data = match request.face {
PartitionFontRequestFace::Builtin(ref builtin_font_name) => {
@ -206,20 +217,20 @@ fn partition_font(request: Json<PartitionFontRequest>)
.expect("Couldn't read builtin font!");
data
}
None => return Json(Err(PartitionFontError::UnknownBuiltinFont)),
None => return Err(PartitionFontError::UnknownBuiltinFont),
}
}
PartitionFontRequestFace::Custom(ref encoded_data) => {
// Decode Base64-encoded OTF data.
let unsafe_otf_data = match base64::decode(encoded_data) {
Ok(unsafe_otf_data) => unsafe_otf_data,
Err(_) => return Json(Err(PartitionFontError::Base64DecodingFailed)),
Err(_) => return Err(PartitionFontError::Base64DecodingFailed),
};
// Sanitize.
match fontsan::process(&unsafe_otf_data) {
Ok(otf_data) => otf_data,
Err(_) => return Json(Err(PartitionFontError::FontSanitizationFailed)),
Err(_) => return Err(PartitionFontError::FontSanitizationFailed),
}
}
};
@ -232,7 +243,7 @@ fn partition_font(request: Json<PartitionFontRequest>)
};
let mut font_context = FontContext::new();
if font_context.add_font_from_memory(&font_key, otf_data, request.font_index).is_err() {
return Json(Err(PartitionFontError::FontLoadingFailed))
return Err(PartitionFontError::FontLoadingFailed)
}
// Read glyph info.
@ -263,19 +274,17 @@ fn partition_font(request: Json<PartitionFontRequest>)
let path_partitioning_result = PathPartitioningResult::compute(&mut partitioner,
&subpath_indices);
let time = path_partitioning_result.time.as_secs() as f64 +
path_partitioning_result.time.subsec_nanos() as f64 * 1e-9;
// Return the response.
Json(Ok(PartitionFontResponse {
path_data: path_partitioning_result.encoded_data,
time: time,
}))
let elapsed_ms = path_partitioning_result.elapsed_ms();
Ok(PartitionResponder {
data: path_partitioning_result.encoded_data,
time: elapsed_ms,
})
}
#[post("/partition-svg-paths", format = "application/json", data = "<request>")]
fn partition_svg_paths(request: Json<PartitionSvgPathsRequest>)
-> Json<Result<PartitionSvgPathsResponse, PartitionSvgPathsError>> {
-> Result<PartitionResponder, PartitionSvgPathsError> {
// Parse the SVG path.
//
// The client has already normalized it, so we only have to handle `M`, `L`, `C`, and `Z`
@ -316,7 +325,7 @@ fn partition_svg_paths(request: Json<PartitionSvgPathsRequest>)
.map(|curve| curve.to_path_segment()));
}
'Z' => stream.push(PathSegment::ClosePath),
_ => return Json(Err(PartitionSvgPathsError::UnknownSvgPathSegmentType)),
_ => return Err(PartitionSvgPathsError::UnknownSvgPathSegmentType),
}
}
@ -348,9 +357,11 @@ fn partition_svg_paths(request: Json<PartitionSvgPathsRequest>)
let path_partitioning_result = PathPartitioningResult::compute(&mut partitioner, &paths);
// Return the response.
Json(Ok(PartitionSvgPathsResponse {
path_data: path_partitioning_result.encoded_data,
}))
let elapsed_ms = path_partitioning_result.elapsed_ms();
Ok(PartitionResponder {
data: path_partitioning_result.encoded_data,
time: elapsed_ms,
})
}
// Static files