mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-21 01:05:45 +00:00
Merge mozilla-inbound to mozilla-central. a=merge
This commit is contained in:
commit
48c0fc9598
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -2814,6 +2814,11 @@ dependencies = [
|
||||
"to_shmem 0.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "svg_fmt"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "0.13.1"
|
||||
@ -3345,6 +3350,7 @@ dependencies = [
|
||||
"serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"svg_fmt 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"webrender_api 0.60.0",
|
||||
@ -3814,6 +3820,7 @@ dependencies = [
|
||||
"checksum string_cache_codegen 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eea1eee654ef80933142157fdad9dd8bc43cf7c74e999e369263496f04ff4da"
|
||||
"checksum string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc"
|
||||
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
|
||||
"checksum svg_fmt 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c666f0fed8e1e20e057af770af9077d72f3d5a33157b8537c1475dd8ffd6d32b"
|
||||
"checksum syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "91b52877572087400e83d24b9178488541e3d535259e04ff17a63df1e5ceff59"
|
||||
"checksum syn 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4e4b5274d4a0a3d2749d5c158dc64d3403e60554dc61194648787ada5212473d"
|
||||
"checksum syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)" = "66c8865bf5a7cbb662d8b011950060b3c8743dca141b054bf7195b20d314d8e2"
|
||||
|
7
gfx/wr/Cargo.lock
generated
7
gfx/wr/Cargo.lock
generated
@ -1392,6 +1392,11 @@ name = "strsim"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "svg_fmt"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "0.15.30"
|
||||
@ -1661,6 +1666,7 @@ dependencies = [
|
||||
"serde_json 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"svg_fmt 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_profiler 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"webrender_api 0.60.0",
|
||||
@ -2037,6 +2043,7 @@ dependencies = [
|
||||
"checksum smithay-client-toolkit 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "428d6c019bb92753be9670367e3f483e4fcef396180a9b59e813b69b20014881"
|
||||
"checksum stable_deref_trait 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ffbc596e092fe5f598b12ef46cc03754085ac2f4d8c739ad61c4ae266cc3b3fa"
|
||||
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
|
||||
"checksum svg_fmt 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c666f0fed8e1e20e057af770af9077d72f3d5a33157b8537c1475dd8ffd6d32b"
|
||||
"checksum syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)" = "66c8865bf5a7cbb662d8b011950060b3c8743dca141b054bf7195b20d314d8e2"
|
||||
"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
|
||||
"checksum tempfile 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "47776f63b85777d984a50ce49d6b9e58826b6a3766a449fc95bc66cd5663c15b"
|
||||
|
@ -50,6 +50,7 @@ webrender_api = { version = "0.60.0", path = "../webrender_api" }
|
||||
webrender_build = { version = "0.0.1", path = "../webrender_build" }
|
||||
wr_malloc_size_of = { version = "0.0.1", path = "../wr_malloc_size_of" }
|
||||
ws = { optional = true, version = "0.7.3" }
|
||||
svg_fmt = "0.4"
|
||||
|
||||
[dependencies.pathfinder_font_renderer]
|
||||
git = "https://github.com/pcwalton/pathfinder"
|
||||
|
@ -39,6 +39,12 @@ impl CaptureConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "capture")]
|
||||
pub fn file_path<P>(&self, name: P, ext: &str) -> PathBuf
|
||||
where P: AsRef<Path> {
|
||||
self.root.join(name).with_extension(ext)
|
||||
}
|
||||
|
||||
#[cfg(feature = "capture")]
|
||||
pub fn serialize<T, P>(&self, data: &T, name: P)
|
||||
where
|
||||
@ -49,9 +55,7 @@ impl CaptureConfig {
|
||||
|
||||
let ron = ron::ser::to_string_pretty(data, self.pretty.clone())
|
||||
.unwrap();
|
||||
let path = self.root
|
||||
.join(name)
|
||||
.with_extension("ron");
|
||||
let path = self.file_path(name, "ron");
|
||||
let mut file = File::create(path)
|
||||
.unwrap();
|
||||
write!(file, "{}\n", ron)
|
||||
|
@ -70,6 +70,7 @@ extern crate serde;
|
||||
extern crate thread_profiler;
|
||||
|
||||
extern crate wr_malloc_size_of;
|
||||
extern crate svg_fmt;
|
||||
use wr_malloc_size_of as malloc_size_of;
|
||||
|
||||
#[macro_use]
|
||||
|
@ -1644,6 +1644,7 @@ impl RenderBackend {
|
||||
) -> DebugOutput {
|
||||
use std::fs;
|
||||
use capture::CaptureConfig;
|
||||
use render_task::dump_render_tasks_as_svg;
|
||||
|
||||
debug!("capture: saving {:?}", root);
|
||||
if !root.is_dir() {
|
||||
@ -1680,6 +1681,14 @@ impl RenderBackend {
|
||||
config.serialize_tree(&doc.clip_scroll_tree, file_name);
|
||||
let file_name = format!("builder-{}-{}", id.namespace_id.0, id.id);
|
||||
config.serialize(doc.frame_builder.as_ref().unwrap(), file_name);
|
||||
let file_name = format!("render-tasks-{}-{}.svg", id.namespace_id.0, id.id);
|
||||
let mut svg_file = fs::File::create(&config.file_path(file_name, "svg"))
|
||||
.expect("Failed to open the SVG file.");
|
||||
dump_render_tasks_as_svg(
|
||||
&rendered_document.frame.render_tasks,
|
||||
&rendered_document.frame.passes,
|
||||
&mut svg_file
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
let data_stores_name = format!("data-stores-{}-{}", id.namespace_id.0, id.id);
|
||||
|
@ -34,6 +34,7 @@ use std::{ops, mem, usize, f32, i32, u32};
|
||||
use texture_cache::{TextureCache, TextureCacheHandle, Eviction};
|
||||
use tiling::{RenderPass, RenderTargetIndex};
|
||||
use tiling::{RenderTargetKind};
|
||||
use std::io;
|
||||
|
||||
|
||||
const RENDER_TASK_SIZE_SANITY_CHECK: i32 = 16000;
|
||||
@ -260,6 +261,14 @@ impl RenderTaskLocation {
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(&self) -> DeviceIntSize {
|
||||
match self {
|
||||
RenderTaskLocation::Fixed(rect) => rect.size,
|
||||
RenderTaskLocation::Dynamic(_, size) => *size,
|
||||
RenderTaskLocation::TextureCache { rect, .. } => rect.size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -425,6 +434,25 @@ pub enum RenderTaskKind {
|
||||
Gradient(GradientTask),
|
||||
}
|
||||
|
||||
impl RenderTaskKind {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match *self {
|
||||
RenderTaskKind::Picture(..) => "Picture",
|
||||
RenderTaskKind::CacheMask(..) => "CacheMask",
|
||||
RenderTaskKind::ClipRegion(..) => "ClipRegion",
|
||||
RenderTaskKind::VerticalBlur(..) => "VerticalBlur",
|
||||
RenderTaskKind::HorizontalBlur(..) => "HorizontalBlur",
|
||||
RenderTaskKind::Glyph(..) => "Glyph",
|
||||
RenderTaskKind::Readback(..) => "Readback",
|
||||
RenderTaskKind::Scaling(..) => "Scaling",
|
||||
RenderTaskKind::Blit(..) => "Blit",
|
||||
RenderTaskKind::Border(..) => "Border",
|
||||
RenderTaskKind::LineDecoration(..) => "LineDecoration",
|
||||
RenderTaskKind::Gradient(..) => "Gradient",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
@ -1470,3 +1498,185 @@ pub fn to_cache_size(size: DeviceSize) -> DeviceIntSize {
|
||||
1.max(size.height.round() as i32),
|
||||
)
|
||||
}
|
||||
|
||||
// Dump an SVG visualization of the render graph for debugging purposes
|
||||
#[allow(dead_code)]
|
||||
pub fn dump_render_tasks_as_svg(
|
||||
render_tasks: &RenderTaskTree,
|
||||
passes: &[RenderPass],
|
||||
output: &mut dyn io::Write,
|
||||
) -> io::Result<()> {
|
||||
use svg_fmt::*;
|
||||
|
||||
let node_width = 80.0;
|
||||
let node_height = 30.0;
|
||||
let vertical_spacing = 8.0;
|
||||
let horizontal_spacing = 20.0;
|
||||
let margin = 10.0;
|
||||
let text_size = 10.0;
|
||||
|
||||
let mut pass_rects = Vec::new();
|
||||
let mut nodes = vec![None; render_tasks.tasks.len()];
|
||||
|
||||
let mut x = margin;
|
||||
let mut max_y: f32 = 0.0;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Node {
|
||||
rect: Rectangle,
|
||||
label: Text,
|
||||
size: Text,
|
||||
}
|
||||
|
||||
for pass in passes {
|
||||
let mut layout = VerticalLayout::new(x, margin, node_width);
|
||||
|
||||
for task_id in &pass.tasks {
|
||||
let task_index = task_id.index as usize;
|
||||
let task = &render_tasks.tasks[task_index];
|
||||
|
||||
let rect = layout.push_rectangle(node_height);
|
||||
|
||||
let tx = rect.x + rect.w / 2.0;
|
||||
let ty = rect.y + 10.0;
|
||||
|
||||
let label = text(tx, ty, task.kind.as_str());
|
||||
let size = text(tx, ty + 12.0, format!("{}", task.location.size()));
|
||||
|
||||
nodes[task_index] = Some(Node { rect, label, size });
|
||||
|
||||
layout.advance(vertical_spacing);
|
||||
}
|
||||
|
||||
pass_rects.push(layout.total_rectangle());
|
||||
|
||||
x += node_width + horizontal_spacing;
|
||||
max_y = max_y.max(layout.y + margin);
|
||||
}
|
||||
|
||||
let mut links = Vec::new();
|
||||
for node_index in 0..nodes.len() {
|
||||
if nodes[node_index].is_none() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let task = &render_tasks.tasks[node_index];
|
||||
for dep in &task.children {
|
||||
let dep_index = dep.index as usize;
|
||||
|
||||
if let (&Some(ref node), &Some(ref dep_node)) = (&nodes[node_index], &nodes[dep_index]) {
|
||||
links.push((
|
||||
dep_node.rect.x + dep_node.rect.w,
|
||||
dep_node.rect.y + dep_node.rect.h / 2.0,
|
||||
node.rect.x,
|
||||
node.rect.y + node.rect.h / 2.0,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let svg_w = x + margin;
|
||||
let svg_h = max_y + margin;
|
||||
writeln!(output, "{}", BeginSvg { w: svg_w, h: svg_h })?;
|
||||
|
||||
// Background.
|
||||
writeln!(output,
|
||||
" {}",
|
||||
rectangle(0.0, 0.0, svg_w, svg_h)
|
||||
.inflate(1.0, 1.0)
|
||||
.fill(rgb(50, 50, 50))
|
||||
)?;
|
||||
|
||||
// Passes.
|
||||
for rect in pass_rects {
|
||||
writeln!(output,
|
||||
" {}",
|
||||
rect.inflate(3.0, 3.0)
|
||||
.border_radius(4.0)
|
||||
.opacity(0.4)
|
||||
.fill(black())
|
||||
)?;
|
||||
}
|
||||
|
||||
// Links.
|
||||
for (x1, y1, x2, y2) in links {
|
||||
dump_task_dependency_link(output, x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
// Tasks.
|
||||
for node in &nodes {
|
||||
if let Some(node) = node {
|
||||
writeln!(output,
|
||||
" {}",
|
||||
node.rect
|
||||
.clone()
|
||||
.fill(black())
|
||||
.border_radius(3.0)
|
||||
.opacity(0.5)
|
||||
.offset(0.0, 2.0)
|
||||
)?;
|
||||
writeln!(output,
|
||||
" {}",
|
||||
node.rect
|
||||
.clone()
|
||||
.fill(rgb(200, 200, 200))
|
||||
.border_radius(3.0)
|
||||
.opacity(0.8)
|
||||
)?;
|
||||
|
||||
writeln!(output,
|
||||
" {}",
|
||||
node.label
|
||||
.clone()
|
||||
.size(text_size)
|
||||
.align(Align::Center)
|
||||
.color(rgb(50, 50, 50))
|
||||
)?;
|
||||
writeln!(output,
|
||||
" {}",
|
||||
node.size
|
||||
.clone()
|
||||
.size(text_size * 0.7)
|
||||
.align(Align::Center)
|
||||
.color(rgb(50, 50, 50))
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
writeln!(output, "{}", EndSvg)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn dump_task_dependency_link(
|
||||
output: &mut io::Write,
|
||||
x1: f32, y1: f32,
|
||||
x2: f32, y2: f32,
|
||||
) {
|
||||
use svg_fmt::*;
|
||||
|
||||
// If the link is a straight horizontal line and spans over multiple passes, it
|
||||
// is likely to go straight though unrelated nodes in a way that makes it look like
|
||||
// they are connected, so we bend the line upward a bit to avoid that.
|
||||
let simple_path = (y1 - y2).abs() > 1.0 || (x2 - x1) < 45.0;
|
||||
|
||||
let mid_x = (x1 + x2) / 2.0;
|
||||
if simple_path {
|
||||
write!(output, " {}",
|
||||
path().move_to(x1, y1)
|
||||
.cubic_bezier_to(mid_x, y1, mid_x, y2, x2, y2)
|
||||
.fill(Fill::None)
|
||||
.stroke(Stroke::Color(rgb(100, 100, 100), 3.0))
|
||||
).unwrap();
|
||||
} else {
|
||||
let ctrl1_x = (mid_x + x1) / 2.0;
|
||||
let ctrl2_x = (mid_x + x2) / 2.0;
|
||||
let ctrl_y = y1 - 25.0;
|
||||
write!(output, " {}",
|
||||
path().move_to(x1, y1)
|
||||
.cubic_bezier_to(ctrl1_x, y1, ctrl1_x, ctrl_y, mid_x, ctrl_y)
|
||||
.cubic_bezier_to(ctrl2_x, ctrl_y, ctrl2_x, y2, x2, y2)
|
||||
.fill(Fill::None)
|
||||
.stroke(Stroke::Color(rgb(100, 100, 100), 3.0))
|
||||
).unwrap();
|
||||
}
|
||||
}
|
||||
|
@ -912,7 +912,7 @@ pub struct RenderPass {
|
||||
pub kind: RenderPassKind,
|
||||
/// The set of tasks to be performed in this pass, as indices into the
|
||||
/// `RenderTaskTree`.
|
||||
tasks: Vec<RenderTaskId>,
|
||||
pub tasks: Vec<RenderTaskId>,
|
||||
}
|
||||
|
||||
impl RenderPass {
|
||||
|
@ -1664,33 +1664,28 @@ class ScriptSource::LoadSourceMatcher {
|
||||
|
||||
template <typename Unit>
|
||||
bool operator()(const Compressed<Unit>&) const {
|
||||
return sourceAlreadyLoaded();
|
||||
*loaded_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Unit>
|
||||
bool operator()(const Uncompressed<Unit>&) const {
|
||||
return sourceAlreadyLoaded();
|
||||
*loaded_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Unit>
|
||||
bool operator()(const Retrievable<Unit>&) {
|
||||
// Establish the default outcome first.
|
||||
*loaded_ = false;
|
||||
|
||||
MOZ_ASSERT(ss_->sourceRetrievable(),
|
||||
"should be retrievable if Retrievable");
|
||||
|
||||
if (!cx_->runtime()->sourceHook.ref()) {
|
||||
*loaded_ = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// The argument here is just for overloading -- its value doesn't matter.
|
||||
if (!tryLoadAndSetSource(Unit('0'))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*loaded_ = true;
|
||||
return true;
|
||||
// The argument is just for overloading -- its value doesn't matter.
|
||||
return tryLoadAndSetSource(Unit('0'));
|
||||
}
|
||||
|
||||
bool operator()(const Missing&) const {
|
||||
@ -1708,31 +1703,50 @@ class ScriptSource::LoadSourceMatcher {
|
||||
}
|
||||
|
||||
private:
|
||||
bool sourceAlreadyLoaded() const {
|
||||
*loaded_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tryLoadAndSetSource(const Utf8Unit&) const {
|
||||
char* utf8Source;
|
||||
size_t length;
|
||||
return cx_->runtime()->sourceHook->load(cx_, ss_->filename(), nullptr,
|
||||
&utf8Source, &length) &&
|
||||
utf8Source &&
|
||||
ss_->setRetrievedSource(
|
||||
cx_,
|
||||
EntryUnits<Utf8Unit>(reinterpret_cast<Utf8Unit*>(utf8Source)),
|
||||
length);
|
||||
if (!cx_->runtime()->sourceHook->load(cx_, ss_->filename(), nullptr,
|
||||
&utf8Source, &length)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!utf8Source) {
|
||||
*loaded_ = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!ss_->setRetrievedSource(
|
||||
cx_,
|
||||
EntryUnits<Utf8Unit>(reinterpret_cast<Utf8Unit*>(utf8Source)),
|
||||
length)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*loaded_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tryLoadAndSetSource(const char16_t&) const {
|
||||
char16_t* utf16Source;
|
||||
size_t length;
|
||||
return cx_->runtime()->sourceHook->load(cx_, ss_->filename(), &utf16Source,
|
||||
nullptr, &length) &&
|
||||
utf16Source &&
|
||||
ss_->setRetrievedSource(cx_, EntryUnits<char16_t>(utf16Source),
|
||||
length);
|
||||
if (!cx_->runtime()->sourceHook->load(cx_, ss_->filename(), &utf16Source,
|
||||
nullptr, &length)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!utf16Source) {
|
||||
*loaded_ = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!ss_->setRetrievedSource(cx_, EntryUnits<char16_t>(utf16Source),
|
||||
length)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*loaded_ = true;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@ -2628,6 +2642,220 @@ XDRResult ScriptSource::xdrUncompressedSource<XDR_ENCODE>(
|
||||
|
||||
} // namespace js
|
||||
|
||||
template <typename Unit, XDRMode mode>
|
||||
/* static */
|
||||
XDRResult ScriptSource::codeUncompressedData(XDRState<mode>* const xdr,
|
||||
ScriptSource* const ss,
|
||||
bool retrievable) {
|
||||
static_assert(std::is_same<Unit, Utf8Unit>::value ||
|
||||
std::is_same<Unit, char16_t>::value,
|
||||
"should handle UTF-8 and UTF-16");
|
||||
|
||||
if (mode == XDR_ENCODE) {
|
||||
MOZ_ASSERT(ss->data.is<Uncompressed<Unit>>());
|
||||
}
|
||||
|
||||
MOZ_ASSERT(retrievable == ss->sourceRetrievable());
|
||||
|
||||
if (retrievable) {
|
||||
// It's unnecessary to code uncompressed data if it can just be retrieved
|
||||
// using the source hook.
|
||||
if (mode == XDR_DECODE) {
|
||||
ss->data = SourceType(Retrievable<Unit>());
|
||||
}
|
||||
return Ok();
|
||||
}
|
||||
|
||||
uint32_t uncompressedLength;
|
||||
if (mode == XDR_ENCODE) {
|
||||
uncompressedLength = ss->data.as<Uncompressed<Unit>>().length();
|
||||
}
|
||||
MOZ_TRY(xdr->codeUint32(&uncompressedLength));
|
||||
|
||||
return ss->xdrUncompressedSource(xdr, sizeof(Unit), uncompressedLength);
|
||||
}
|
||||
|
||||
template <typename Unit, XDRMode mode>
|
||||
/* static */
|
||||
XDRResult ScriptSource::codeCompressedData(XDRState<mode>* const xdr,
|
||||
ScriptSource* const ss,
|
||||
bool retrievable) {
|
||||
static_assert(std::is_same<Unit, Utf8Unit>::value ||
|
||||
std::is_same<Unit, char16_t>::value,
|
||||
"should handle UTF-8 and UTF-16");
|
||||
|
||||
if (mode == XDR_ENCODE) {
|
||||
MOZ_ASSERT(ss->data.is<Compressed<Unit>>());
|
||||
}
|
||||
|
||||
MOZ_ASSERT(retrievable == ss->sourceRetrievable());
|
||||
|
||||
if (retrievable) {
|
||||
// It's unnecessary to code compressed data if it can just be retrieved
|
||||
// using the source hook.
|
||||
if (mode == XDR_DECODE) {
|
||||
ss->data = SourceType(Retrievable<Unit>());
|
||||
}
|
||||
return Ok();
|
||||
}
|
||||
|
||||
uint32_t uncompressedLength;
|
||||
if (mode == XDR_ENCODE) {
|
||||
uncompressedLength = ss->data.as<Compressed<Unit>>().uncompressedLength;
|
||||
}
|
||||
MOZ_TRY(xdr->codeUint32(&uncompressedLength));
|
||||
|
||||
uint32_t compressedLength;
|
||||
if (mode == XDR_ENCODE) {
|
||||
compressedLength = ss->data.as<Compressed<Unit>>().raw.length();
|
||||
}
|
||||
MOZ_TRY(xdr->codeUint32(&compressedLength));
|
||||
|
||||
if (mode == XDR_DECODE) {
|
||||
// Compressed data is always single-byte chars.
|
||||
auto bytes = xdr->cx()->template make_pod_array<char>(compressedLength);
|
||||
if (!bytes) {
|
||||
return xdr->fail(JS::TranscodeResult_Throw);
|
||||
}
|
||||
MOZ_TRY(xdr->codeBytes(bytes.get(), compressedLength));
|
||||
|
||||
if (!ss->initializeWithCompressedSource<Unit>(xdr->cx(), std::move(bytes),
|
||||
compressedLength,
|
||||
uncompressedLength)) {
|
||||
return xdr->fail(JS::TranscodeResult_Throw);
|
||||
}
|
||||
} else {
|
||||
void* bytes =
|
||||
const_cast<char*>(ss->data.as<Compressed<Unit>>().raw.chars());
|
||||
MOZ_TRY(xdr->codeBytes(bytes, compressedLength));
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
template <XDRMode mode>
|
||||
/* static */
|
||||
XDRResult ScriptSource::codeBinASTData(XDRState<mode>* const xdr,
|
||||
ScriptSource* const ss) {
|
||||
#if !defined(JS_BUILD_BINAST)
|
||||
return xdr->fail(JS::TranscodeResult_Throw);
|
||||
#else
|
||||
// XDR the length of the BinAST data.
|
||||
uint32_t binASTLength;
|
||||
if (mode == XDR_ENCODE) {
|
||||
binASTLength = ss->data.as<BinAST>().string.length();
|
||||
}
|
||||
MOZ_TRY(xdr->codeUint32(&binASTLength));
|
||||
|
||||
// XDR the BinAST data.
|
||||
UniquePtr<char[], JS::FreePolicy> bytes;
|
||||
if (mode == XDR_DECODE) {
|
||||
bytes =
|
||||
xdr->cx()->template make_pod_array<char>(Max<size_t>(binASTLength, 1));
|
||||
if (!bytes) {
|
||||
return xdr->fail(JS::TranscodeResult_Throw);
|
||||
}
|
||||
MOZ_TRY(xdr->codeBytes(bytes.get(), binASTLength));
|
||||
} else {
|
||||
void* bytes = ss->binASTData();
|
||||
MOZ_TRY(xdr->codeBytes(bytes, binASTLength));
|
||||
}
|
||||
|
||||
// XDR any BinAST metadata.
|
||||
uint8_t hasMetadata;
|
||||
if (mode == XDR_ENCODE) {
|
||||
hasMetadata = ss->binASTMetadata_ != nullptr;
|
||||
}
|
||||
MOZ_TRY(xdr->codeUint8(&hasMetadata));
|
||||
|
||||
UniquePtr<frontend::BinASTSourceMetadata> freshMetadata;
|
||||
if (hasMetadata) {
|
||||
// If we're decoding, we decode into fresh metadata. If we're encoding,
|
||||
// we encode *from* the stored metadata.
|
||||
auto& binASTMetadata =
|
||||
mode == XDR_DECODE ? freshMetadata : ss->binASTMetadata_;
|
||||
|
||||
uint32_t numBinASTKinds;
|
||||
uint32_t numStrings;
|
||||
if (mode == XDR_ENCODE) {
|
||||
numBinASTKinds = binASTMetadata->numBinASTKinds();
|
||||
numStrings = binASTMetadata->numStrings();
|
||||
}
|
||||
MOZ_TRY(xdr->codeUint32(&numBinASTKinds));
|
||||
MOZ_TRY(xdr->codeUint32(&numStrings));
|
||||
|
||||
if (mode == XDR_DECODE) {
|
||||
// Use calloc, since we're storing this immediately, and filling it
|
||||
// might GC, to avoid marking bogus atoms.
|
||||
void* mem = js_calloc(frontend::BinASTSourceMetadata::totalSize(
|
||||
numBinASTKinds, numStrings));
|
||||
if (!mem) {
|
||||
return xdr->fail(JS::TranscodeResult_Throw);
|
||||
}
|
||||
|
||||
auto metadata =
|
||||
new (mem) frontend::BinASTSourceMetadata(numBinASTKinds, numStrings);
|
||||
binASTMetadata.reset(metadata);
|
||||
}
|
||||
|
||||
frontend::BinASTKind* binASTKindBase = binASTMetadata->binASTKindBase();
|
||||
for (uint32_t i = 0; i < numBinASTKinds; i++) {
|
||||
MOZ_TRY(xdr->codeEnum32(&binASTKindBase[i]));
|
||||
}
|
||||
|
||||
RootedAtom atom(xdr->cx());
|
||||
JSAtom** atomsBase = binASTMetadata->atomsBase();
|
||||
auto slices = binASTMetadata->sliceBase();
|
||||
const char* sourceBase =
|
||||
mode == XDR_ENCODE ? bytes.get() : ss->data.as<BinAST>().string.chars();
|
||||
|
||||
for (uint32_t i = 0; i < numStrings; i++) {
|
||||
uint8_t isNull;
|
||||
if (mode == XDR_ENCODE) {
|
||||
atom = binASTMetadata->getAtom(i);
|
||||
isNull = !atom;
|
||||
}
|
||||
MOZ_TRY(xdr->codeUint8(&isNull));
|
||||
if (isNull) {
|
||||
atom = nullptr;
|
||||
} else {
|
||||
MOZ_TRY(XDRAtom(xdr, &atom));
|
||||
}
|
||||
if (mode == XDR_DECODE) {
|
||||
atomsBase[i] = atom;
|
||||
}
|
||||
|
||||
uint64_t sliceOffset;
|
||||
uint32_t sliceLen;
|
||||
if (mode == XDR_ENCODE) {
|
||||
auto& slice = binASTMetadata->getSlice(i);
|
||||
sliceOffset = slice.begin() - sourceBase;
|
||||
sliceLen = slice.byteLen_;
|
||||
}
|
||||
|
||||
MOZ_TRY(xdr->codeUint64(&sliceOffset));
|
||||
MOZ_TRY(xdr->codeUint32(&sliceLen));
|
||||
|
||||
if (mode == XDR_DECODE) {
|
||||
new (&slices[i]) frontend::BinASTSourceMetadata::CharSlice(
|
||||
sourceBase + sliceOffset, sliceLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == XDR_DECODE) {
|
||||
if (!ss->initializeBinAST(xdr->cx(), std::move(bytes), binASTLength,
|
||||
std::move(freshMetadata))) {
|
||||
return xdr->fail(JS::TranscodeResult_Throw);
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(freshMetadata == nullptr);
|
||||
}
|
||||
|
||||
return Ok();
|
||||
#endif // !defined(JS_BUILD_BINAST)
|
||||
}
|
||||
|
||||
template <XDRMode mode>
|
||||
/* static */
|
||||
XDRResult ScriptSource::xdrData(XDRState<mode>* const xdr,
|
||||
@ -2703,231 +2931,18 @@ XDRResult ScriptSource::xdrData(XDRState<mode>* const xdr,
|
||||
tag = static_cast<DataType>(type);
|
||||
}
|
||||
|
||||
auto CodeCompressedData = [xdr, ss, &retrievable](auto unit) -> XDRResult {
|
||||
using Unit = decltype(unit);
|
||||
|
||||
static_assert(std::is_same<Unit, Utf8Unit>::value ||
|
||||
std::is_same<Unit, char16_t>::value,
|
||||
"should handle UTF-8 and UTF-16");
|
||||
|
||||
if (mode == XDR_ENCODE) {
|
||||
MOZ_ASSERT(ss->data.is<Compressed<Unit>>());
|
||||
}
|
||||
|
||||
if (retrievable) {
|
||||
// It's unnecessary to code compressed data if it can just be retrieved
|
||||
// using the source hook.
|
||||
if (mode == XDR_DECODE) {
|
||||
ss->data = SourceType(Retrievable<Unit>());
|
||||
}
|
||||
return Ok();
|
||||
}
|
||||
|
||||
uint32_t uncompressedLength = 0;
|
||||
if (mode == XDR_ENCODE) {
|
||||
uncompressedLength = ss->data.as<Compressed<Unit>>().uncompressedLength;
|
||||
}
|
||||
MOZ_TRY(xdr->codeUint32(&uncompressedLength));
|
||||
|
||||
uint32_t compressedLength;
|
||||
if (mode == XDR_ENCODE) {
|
||||
compressedLength = ss->data.as<Compressed<Unit>>().raw.length();
|
||||
}
|
||||
MOZ_TRY(xdr->codeUint32(&compressedLength));
|
||||
|
||||
if (mode == XDR_DECODE) {
|
||||
// Compressed data is always single-byte chars.
|
||||
auto bytes = xdr->cx()->template make_pod_array<char>(compressedLength);
|
||||
if (!bytes) {
|
||||
return xdr->fail(JS::TranscodeResult_Throw);
|
||||
}
|
||||
MOZ_TRY(xdr->codeBytes(bytes.get(), compressedLength));
|
||||
|
||||
if (!ss->initializeWithCompressedSource<Unit>(xdr->cx(), std::move(bytes),
|
||||
compressedLength,
|
||||
uncompressedLength)) {
|
||||
return xdr->fail(JS::TranscodeResult_Throw);
|
||||
}
|
||||
} else {
|
||||
void* bytes =
|
||||
const_cast<char*>(ss->data.as<Compressed<Unit>>().raw.chars());
|
||||
MOZ_TRY(xdr->codeBytes(bytes, compressedLength));
|
||||
}
|
||||
|
||||
return Ok();
|
||||
};
|
||||
|
||||
auto CodeUncompressedData = [xdr, ss, &retrievable](auto unit) -> XDRResult {
|
||||
using Unit = decltype(unit);
|
||||
|
||||
static_assert(std::is_same<Unit, Utf8Unit>::value ||
|
||||
std::is_same<Unit, char16_t>::value,
|
||||
"should handle UTF-8 and UTF-16");
|
||||
|
||||
if (mode == XDR_ENCODE) {
|
||||
MOZ_ASSERT(ss->data.is<Uncompressed<Unit>>());
|
||||
}
|
||||
|
||||
if (retrievable) {
|
||||
// It's unnecessary to code uncompressed data if it can just be retrieved
|
||||
// using the source hook.
|
||||
if (mode == XDR_DECODE) {
|
||||
ss->data = SourceType(Retrievable<Unit>());
|
||||
}
|
||||
return Ok();
|
||||
}
|
||||
|
||||
uint32_t uncompressedLength = 0;
|
||||
if (mode == XDR_ENCODE) {
|
||||
uncompressedLength = ss->data.as<Uncompressed<Unit>>().length();
|
||||
}
|
||||
MOZ_TRY(xdr->codeUint32(&uncompressedLength));
|
||||
|
||||
return ss->xdrUncompressedSource(xdr, sizeof(Unit), uncompressedLength);
|
||||
};
|
||||
|
||||
auto CodeBinASTData = [xdr
|
||||
#if defined(JS_BUILD_BINAST)
|
||||
,
|
||||
ss
|
||||
#endif
|
||||
]() -> XDRResult {
|
||||
#if !defined(JS_BUILD_BINAST)
|
||||
return xdr->fail(JS::TranscodeResult_Throw);
|
||||
#else
|
||||
// XDR the length of the BinAST data.
|
||||
uint32_t binASTLength;
|
||||
if (mode == XDR_ENCODE) {
|
||||
binASTLength = ss->data.as<BinAST>().string.length();
|
||||
}
|
||||
MOZ_TRY(xdr->codeUint32(&binASTLength));
|
||||
|
||||
// XDR the BinAST data.
|
||||
UniquePtr<char[], JS::FreePolicy> bytes;
|
||||
if (mode == XDR_DECODE) {
|
||||
bytes = xdr->cx()->template make_pod_array<char>(
|
||||
Max<size_t>(binASTLength, 1));
|
||||
if (!bytes) {
|
||||
return xdr->fail(JS::TranscodeResult_Throw);
|
||||
}
|
||||
MOZ_TRY(xdr->codeBytes(bytes.get(), binASTLength));
|
||||
} else {
|
||||
void* bytes = ss->binASTData();
|
||||
MOZ_TRY(xdr->codeBytes(bytes, binASTLength));
|
||||
}
|
||||
|
||||
// XDR any BinAST metadata.
|
||||
uint8_t hasMetadata;
|
||||
if (mode == XDR_ENCODE) {
|
||||
hasMetadata = ss->binASTMetadata_ != nullptr;
|
||||
}
|
||||
MOZ_TRY(xdr->codeUint8(&hasMetadata));
|
||||
|
||||
UniquePtr<frontend::BinASTSourceMetadata> freshMetadata;
|
||||
if (hasMetadata) {
|
||||
// If we're decoding, we decode into fresh metadata. If we're encoding,
|
||||
// we encode *from* the stored metadata.
|
||||
auto& binASTMetadata =
|
||||
mode == XDR_DECODE ? freshMetadata : ss->binASTMetadata_;
|
||||
|
||||
uint32_t numBinASTKinds;
|
||||
uint32_t numStrings;
|
||||
if (mode == XDR_ENCODE) {
|
||||
numBinASTKinds = binASTMetadata->numBinASTKinds();
|
||||
numStrings = binASTMetadata->numStrings();
|
||||
}
|
||||
MOZ_TRY(xdr->codeUint32(&numBinASTKinds));
|
||||
MOZ_TRY(xdr->codeUint32(&numStrings));
|
||||
|
||||
if (mode == XDR_DECODE) {
|
||||
// Use calloc, since we're storing this immediately, and filling it
|
||||
// might GC, to avoid marking bogus atoms.
|
||||
void* mem = js_calloc(frontend::BinASTSourceMetadata::totalSize(
|
||||
numBinASTKinds, numStrings));
|
||||
if (!mem) {
|
||||
return xdr->fail(JS::TranscodeResult_Throw);
|
||||
}
|
||||
|
||||
auto metadata = new (mem)
|
||||
frontend::BinASTSourceMetadata(numBinASTKinds, numStrings);
|
||||
binASTMetadata.reset(metadata);
|
||||
}
|
||||
|
||||
frontend::BinASTKind* binASTKindBase = binASTMetadata->binASTKindBase();
|
||||
for (uint32_t i = 0; i < numBinASTKinds; i++) {
|
||||
MOZ_TRY(xdr->codeEnum32(&binASTKindBase[i]));
|
||||
}
|
||||
|
||||
RootedAtom atom(xdr->cx());
|
||||
JSAtom** atomsBase = binASTMetadata->atomsBase();
|
||||
auto slices = binASTMetadata->sliceBase();
|
||||
const char* sourceBase = mode == XDR_ENCODE
|
||||
? bytes.get()
|
||||
: ss->data.as<BinAST>().string.chars();
|
||||
|
||||
for (uint32_t i = 0; i < numStrings; i++) {
|
||||
uint8_t isNull;
|
||||
if (mode == XDR_ENCODE) {
|
||||
atom = binASTMetadata->getAtom(i);
|
||||
isNull = !atom;
|
||||
}
|
||||
MOZ_TRY(xdr->codeUint8(&isNull));
|
||||
if (isNull) {
|
||||
atom = nullptr;
|
||||
} else {
|
||||
MOZ_TRY(XDRAtom(xdr, &atom));
|
||||
}
|
||||
if (mode == XDR_DECODE) {
|
||||
atomsBase[i] = atom;
|
||||
}
|
||||
|
||||
uint64_t sliceOffset;
|
||||
uint32_t sliceLen;
|
||||
if (mode == XDR_ENCODE) {
|
||||
auto& slice = binASTMetadata->getSlice(i);
|
||||
sliceOffset = slice.begin() - sourceBase;
|
||||
sliceLen = slice.byteLen_;
|
||||
}
|
||||
|
||||
MOZ_TRY(xdr->codeUint64(&sliceOffset));
|
||||
MOZ_TRY(xdr->codeUint32(&sliceLen));
|
||||
|
||||
if (mode == XDR_DECODE) {
|
||||
new (&slices[i]) frontend::BinASTSourceMetadata::CharSlice(
|
||||
sourceBase + sliceOffset, sliceLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == XDR_DECODE) {
|
||||
if (!ss->initializeBinAST(xdr->cx(), std::move(bytes), binASTLength,
|
||||
std::move(freshMetadata))) {
|
||||
return xdr->fail(JS::TranscodeResult_Throw);
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(freshMetadata == nullptr);
|
||||
}
|
||||
|
||||
return Ok();
|
||||
#endif // !defined(JS_BUILD_BINAST)
|
||||
};
|
||||
|
||||
switch (tag) {
|
||||
case DataType::CompressedUtf8:
|
||||
// The argument here is just for overloading -- its value doesn't matter.
|
||||
return CodeCompressedData(Utf8Unit('0'));
|
||||
return ScriptSource::codeCompressedData<Utf8Unit>(xdr, ss, retrievable);
|
||||
|
||||
case DataType::UncompressedUtf8:
|
||||
// The argument here is just for overloading -- its value doesn't matter.
|
||||
return CodeUncompressedData(Utf8Unit('0'));
|
||||
return ScriptSource::codeUncompressedData<Utf8Unit>(xdr, ss, retrievable);
|
||||
|
||||
case DataType::CompressedUtf16:
|
||||
// The argument here is just for overloading -- its value doesn't matter.
|
||||
return CodeCompressedData(char16_t('0'));
|
||||
return ScriptSource::codeCompressedData<char16_t>(xdr, ss, retrievable);
|
||||
|
||||
case DataType::UncompressedUtf16:
|
||||
// The argument here is just for overloading -- its value doesn't matter.
|
||||
return CodeUncompressedData(char16_t('0'));
|
||||
return ScriptSource::codeUncompressedData<char16_t>(xdr, ss, retrievable);
|
||||
|
||||
case DataType::Missing: {
|
||||
MOZ_ASSERT(ss->data.is<Missing>(),
|
||||
@ -2955,7 +2970,7 @@ XDRResult ScriptSource::xdrData(XDRState<mode>* const xdr,
|
||||
}
|
||||
|
||||
case DataType::BinAST:
|
||||
return CodeBinASTData();
|
||||
return codeBinASTData(xdr, ss);
|
||||
}
|
||||
|
||||
// The range-check on |type| far above ought ensure the above |switch| is
|
||||
|
@ -1146,6 +1146,20 @@ class ScriptSource {
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename Unit, XDRMode mode>
|
||||
static MOZ_MUST_USE XDRResult codeUncompressedData(XDRState<mode>* const xdr,
|
||||
ScriptSource* const ss,
|
||||
bool retrievable);
|
||||
|
||||
template <typename Unit, XDRMode mode>
|
||||
static MOZ_MUST_USE XDRResult codeCompressedData(XDRState<mode>* const xdr,
|
||||
ScriptSource* const ss,
|
||||
bool retrievable);
|
||||
|
||||
template <XDRMode mode>
|
||||
static MOZ_MUST_USE XDRResult codeBinASTData(XDRState<mode>* const xdr,
|
||||
ScriptSource* const ss);
|
||||
|
||||
template <XDRMode mode>
|
||||
static MOZ_MUST_USE XDRResult xdrData(XDRState<mode>* const xdr,
|
||||
ScriptSource* const ss);
|
||||
|
1
third_party/rust/svg_fmt/.cargo-checksum.json
vendored
Normal file
1
third_party/rust/svg_fmt/.cargo-checksum.json
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"files":{"Cargo.toml":"ceb0db86bf7dc037a5f1c60d721e011c997af4d0b3039969eebf4ec7963ef1d8","README.md":"efffe3ac4d3b72a3786c842724f78721f65047c2420a51362355b73e6c5f879d","src/layout.rs":"dc1378e2911912b59e826b31e30306ecc47cf2f12bada184c7c73a21c3a15acf","src/lib.rs":"983b5686a05b093bf405418fe42eb662f72deb9f05c30c6ea122c477683ad405","src/svg.rs":"6c04d9bcee8a2e5724c6e3ec77526b52433529270e627bd8e693224c197923e2"},"package":"c666f0fed8e1e20e057af770af9077d72f3d5a33157b8537c1475dd8ffd6d32b"}
|
24
third_party/rust/svg_fmt/Cargo.toml
vendored
Normal file
24
third_party/rust/svg_fmt/Cargo.toml
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "svg_fmt"
|
||||
version = "0.4.0"
|
||||
authors = ["Nicolas Silva <nical@fastmail.com>"]
|
||||
description = "Very simple debugging utilities to dump shapes in SVG format."
|
||||
documentation = "https://docs.rs/svg_fmt/"
|
||||
keywords = ["2d", "graphics", "svg"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/nical/rust_debug"
|
||||
|
||||
[dependencies]
|
28
third_party/rust/svg_fmt/README.md
vendored
Normal file
28
third_party/rust/svg_fmt/README.md
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
# svg_fmt
|
||||
|
||||
A set of simple types using `Display` formatters `{}` to easily write in the SVG format.
|
||||
This can be useful to dump information in a visual way when debugging.
|
||||
|
||||
The crate is very small (and has no dependency).
|
||||
|
||||
## Example
|
||||
|
||||
```rust
|
||||
use svg_fmt::*;
|
||||
|
||||
println!("{}", BeginSvg { w: 800.0, h: 600.0 });
|
||||
println!(" {}",
|
||||
rectangle(20.0, 50.0, 200.0, 100.0)
|
||||
.fill(Fill::Color(red()))
|
||||
.stroke(Stroke::Color(black(), 3.0))
|
||||
.border_radius(5.0)
|
||||
);
|
||||
println!(" {}",
|
||||
text(25.0, 100.0, "Hi!")
|
||||
.size(42.0)
|
||||
.color(white())
|
||||
);
|
||||
println!("{}", EndSvg);
|
||||
|
||||
```
|
||||
|
43
third_party/rust/svg_fmt/src/layout.rs
vendored
Normal file
43
third_party/rust/svg_fmt/src/layout.rs
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
use crate::svg::{Rectangle, rectangle};
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct VerticalLayout {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub start_y: f32,
|
||||
pub width: f32,
|
||||
}
|
||||
|
||||
impl VerticalLayout {
|
||||
pub fn new(x: f32, y: f32, width: f32) -> Self {
|
||||
VerticalLayout {
|
||||
x,
|
||||
y,
|
||||
start_y: y,
|
||||
width,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn advance(&mut self, by: f32) {
|
||||
self.y += by;
|
||||
}
|
||||
|
||||
pub fn push_rectangle(&mut self, height: f32) -> Rectangle {
|
||||
let rect = rectangle(self.x, self.y, self.width, height);
|
||||
|
||||
self.y += height;
|
||||
|
||||
rect
|
||||
}
|
||||
|
||||
pub fn total_rectangle(&self) -> Rectangle {
|
||||
rectangle(
|
||||
self.x, self.start_y,
|
||||
self.width, self.y,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn start_here(&mut self) {
|
||||
self.start_y = self.y;
|
||||
}
|
||||
}
|
5
third_party/rust/svg_fmt/src/lib.rs
vendored
Normal file
5
third_party/rust/svg_fmt/src/lib.rs
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
mod svg;
|
||||
mod layout;
|
||||
|
||||
pub use svg::*;
|
||||
pub use layout::*;
|
586
third_party/rust/svg_fmt/src/svg.rs
vendored
Normal file
586
third_party/rust/svg_fmt/src/svg.rs
vendored
Normal file
@ -0,0 +1,586 @@
|
||||
use std::fmt;
|
||||
|
||||
/// `rgb({r},{g},{b})`
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub struct Color {
|
||||
pub r: u8,
|
||||
pub g: u8,
|
||||
pub b: u8,
|
||||
}
|
||||
|
||||
impl fmt::Display for Color {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "rgb({},{},{})", self.r, self.g, self.b)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rgb(r: u8, g: u8, b: u8) -> Color { Color { r, g, b } }
|
||||
pub fn black() -> Color { rgb(0, 0, 0) }
|
||||
pub fn white() -> Color { rgb(255, 255, 255) }
|
||||
pub fn red() -> Color { rgb(255, 0, 0) }
|
||||
pub fn green() -> Color { rgb(0, 255, 0) }
|
||||
pub fn blue() -> Color { rgb(0, 0, 255) }
|
||||
|
||||
/// `fill:{self}`
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum Fill {
|
||||
Color(Color),
|
||||
None,
|
||||
}
|
||||
|
||||
/// `stroke:{self}`
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum Stroke {
|
||||
Color(Color, f32),
|
||||
None,
|
||||
}
|
||||
|
||||
/// `fill:{fill};stroke:{stroke};fill-opacity:{opacity};`
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub struct Style {
|
||||
pub fill: Fill,
|
||||
pub stroke: Stroke,
|
||||
pub opacity: f32,
|
||||
}
|
||||
|
||||
impl fmt::Display for Style {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{};{};fill-opacity:{};",
|
||||
self.fill,
|
||||
self.stroke,
|
||||
self.opacity,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Style {
|
||||
pub fn default() -> Self {
|
||||
Style {
|
||||
fill: Fill::Color(black()),
|
||||
stroke: Stroke::None,
|
||||
opacity: 1.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Fill {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Fill::Color(color) => write!(f, "fill:{}", color),
|
||||
Fill::None => write!(f, "fill:none"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Stroke {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Stroke::Color(color, radius) => write!(f, "stroke:{};stroke-width:{}", color, radius),
|
||||
Stroke::None => write!(f, "stroke:none"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Fill> for Color {
|
||||
fn into(self) -> Fill {
|
||||
Fill::Color(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Stroke> for Color {
|
||||
fn into(self) -> Stroke {
|
||||
Stroke::Color(self, 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// `<rect x="{x}" y="{y}" width="{w}" height="{h}" ... />`,
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub struct Rectangle {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub w: f32,
|
||||
pub h: f32,
|
||||
pub style: Style,
|
||||
pub border_radius: f32,
|
||||
}
|
||||
|
||||
pub fn rectangle(x: f32, y: f32, w: f32, h: f32) -> Rectangle {
|
||||
Rectangle {
|
||||
x, y, w, h,
|
||||
style: Style::default(),
|
||||
border_radius: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
impl Rectangle {
|
||||
pub fn fill<F>(mut self, fill: F) -> Self
|
||||
where F: Into<Fill> {
|
||||
self.style.fill = fill.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn stroke<S>(mut self, stroke: S) -> Self
|
||||
where S: Into<Stroke> {
|
||||
self.style.stroke = stroke.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn opacity(mut self, opacity: f32) -> Self {
|
||||
self.style.opacity = opacity;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn style(mut self, style: Style) -> Self {
|
||||
self.style = style;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn border_radius(mut self, r: f32) -> Self {
|
||||
self.border_radius = r;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn offset(mut self, dx: f32, dy: f32) -> Self {
|
||||
self.x += dx;
|
||||
self.y += dy;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn inflate(mut self, dx: f32, dy: f32) -> Self {
|
||||
self.x -= dx;
|
||||
self.y -= dy;
|
||||
self.w += 2.0 * dx;
|
||||
self.h += 2.0 * dy;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Rectangle {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f,
|
||||
r#"<rect x="{}" y="{}" width="{}" height="{}" ry="{}" style="{}" />""#,
|
||||
self.x, self.y, self.w, self.h,
|
||||
self.border_radius,
|
||||
self.style,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// `<circle cx="{x}" cy="{y}" r="{radius}" .../>`
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub struct Circle {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
pub radius: f32,
|
||||
pub style: Style,
|
||||
}
|
||||
|
||||
impl Circle {
|
||||
pub fn fill<F>(mut self, fill: F) -> Self
|
||||
where F: Into<Fill> {
|
||||
self.style.fill = fill.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn stroke<S>(mut self, stroke: S) -> Self
|
||||
where S: Into<Stroke> {
|
||||
self.style.stroke = stroke.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn style(mut self, style: Style) -> Self {
|
||||
self.style = style;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn opacity(mut self, opacity: f32) -> Self {
|
||||
self.style.opacity = opacity;
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
pub fn offset(mut self, dx: f32, dy: f32) -> Self {
|
||||
self.x += dx;
|
||||
self.y += dy;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn inflate(mut self, by: f32) -> Self {
|
||||
self.radius += by;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Circle {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f,
|
||||
r#"<circle cx="{}" cy="{}" r="{}" style="{}" />""#,
|
||||
self.x, self.y, self.radius,
|
||||
self.style,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// `<path d="..." style="..."/>`
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Polygon {
|
||||
pub points: Vec<[f32; 2]>,
|
||||
pub closed: bool,
|
||||
pub style: Style,
|
||||
}
|
||||
|
||||
impl fmt::Display for Polygon {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, r#"<path d="#)?;
|
||||
if self.points.len() > 0 {
|
||||
write!(f, "M {} {} ", self.points[0][0], self.points[0][1])?;
|
||||
for &p in &self.points[1..] {
|
||||
write!(f, "L {} {} ", p[0], p[1])?;
|
||||
}
|
||||
if self.closed {
|
||||
write!(f, "Z")?;
|
||||
}
|
||||
}
|
||||
write!(f, r#"" style="{}"/>"#, self.style)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn polygon<T: Copy + Into<[f32; 2]>>(pts: &[T]) -> Polygon {
|
||||
let mut points = Vec::with_capacity(pts.len());
|
||||
for p in pts {
|
||||
points.push((*p).into());
|
||||
}
|
||||
Polygon {
|
||||
points,
|
||||
closed: true,
|
||||
style: Style::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn triangle(x1: f32, y1: f32, x2: f32, y2: f32, x3: f32, y3: f32) -> Polygon {
|
||||
polygon(&[[x1, y1], [x2, y2], [x3, y3]])
|
||||
}
|
||||
|
||||
impl Polygon {
|
||||
pub fn open(mut self) -> Self {
|
||||
self.closed = false;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn fill<F>(mut self, fill: F) -> Self
|
||||
where F: Into<Fill> {
|
||||
self.style.fill = fill.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn stroke<S>(mut self, stroke: S) -> Self
|
||||
where S: Into<Stroke> {
|
||||
self.style.stroke = stroke.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn opacity(mut self, opacity: f32) -> Self {
|
||||
self.style.opacity = opacity;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn style(mut self, style: Style) -> Self {
|
||||
self.style = style;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// `<path d="M {x1} {y1} L {x2} {y2}" ... />`
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub struct LineSegment {
|
||||
pub x1: f32,
|
||||
pub x2: f32,
|
||||
pub y1: f32,
|
||||
pub y2: f32,
|
||||
pub color: Color,
|
||||
pub width: f32,
|
||||
}
|
||||
|
||||
impl fmt::Display for LineSegment {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f,
|
||||
r#"<path d="M {} {} L {} {}" style="stroke:{};stroke-width:{}"/>"#,
|
||||
self.x1, self.y1,
|
||||
self.x2, self.y2,
|
||||
self.color,
|
||||
self.width,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn line_segment(x1: f32, y1: f32, x2: f32, y2: f32) -> LineSegment {
|
||||
LineSegment {
|
||||
x1, y1, x2, y2,
|
||||
color: black(),
|
||||
width: 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
impl LineSegment {
|
||||
pub fn color(mut self, color: Color) -> Self {
|
||||
self.color = color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn width(mut self, width: f32) -> Self {
|
||||
self.width = width;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn offset(mut self, dx: f32, dy: f32) -> Self {
|
||||
self.x1 += dx;
|
||||
self.y1 += dy;
|
||||
self.x2 += dx;
|
||||
self.y2 += dy;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// `<path d="..." />`
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Path {
|
||||
pub ops: Vec<PathOp>,
|
||||
pub style: Style,
|
||||
}
|
||||
|
||||
/// `M {} {} L {} {} ...`
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum PathOp {
|
||||
MoveTo { x: f32, y: f32 },
|
||||
LineTo { x: f32, y: f32 },
|
||||
QuadraticTo { ctrl_x: f32, ctrl_y: f32, x: f32, y: f32 },
|
||||
CubicTo { ctrl1_x: f32, ctrl1_y: f32, ctrl2_x: f32, ctrl2_y: f32, x: f32, y: f32 },
|
||||
Close,
|
||||
}
|
||||
impl fmt::Display for PathOp {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
PathOp::MoveTo { x, y } => write!(f, "M {} {} ", x, y),
|
||||
PathOp::LineTo { x, y } => write!(f, "L {} {} ", x, y),
|
||||
PathOp::QuadraticTo { ctrl_x, ctrl_y, x, y } => write!(f, "Q {} {} {} {} ", ctrl_x, ctrl_y, x, y),
|
||||
PathOp::CubicTo { ctrl1_x, ctrl1_y, ctrl2_x, ctrl2_y, x, y } => write!(f, "C {} {} {} {} {} {} ", ctrl1_x, ctrl1_y, ctrl2_x, ctrl2_y, x, y),
|
||||
PathOp::Close => write!(f, "Z "),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Path {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, r#"<path d=""#)?;
|
||||
for op in &self.ops {
|
||||
op.fmt(f)?;
|
||||
}
|
||||
write!(f, r#"" style="{}" />"#, self.style)
|
||||
}
|
||||
}
|
||||
|
||||
impl Path {
|
||||
pub fn move_to(mut self, x: f32, y: f32) -> Self {
|
||||
self.ops.push(PathOp::MoveTo { x, y });
|
||||
self
|
||||
}
|
||||
|
||||
pub fn line_to(mut self, x: f32, y: f32) -> Self {
|
||||
self.ops.push(PathOp::LineTo { x, y });
|
||||
self
|
||||
}
|
||||
|
||||
pub fn quadratic_bezier_to(
|
||||
mut self,
|
||||
ctrl_x: f32, ctrl_y: f32,
|
||||
x: f32, y: f32,
|
||||
) -> Self {
|
||||
self.ops.push(PathOp::QuadraticTo { ctrl_x, ctrl_y, x, y });
|
||||
self
|
||||
}
|
||||
|
||||
pub fn cubic_bezier_to(
|
||||
mut self,
|
||||
ctrl1_x: f32, ctrl1_y: f32,
|
||||
ctrl2_x: f32, ctrl2_y: f32,
|
||||
x: f32, y: f32,
|
||||
) -> Self {
|
||||
self.ops.push(PathOp::CubicTo { ctrl1_x, ctrl1_y, ctrl2_x, ctrl2_y, x, y });
|
||||
self
|
||||
}
|
||||
|
||||
pub fn close(mut self) -> Self {
|
||||
self.ops.push(PathOp::Close);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn fill<F>(mut self, fill: F) -> Self
|
||||
where F: Into<Fill> {
|
||||
self.style.fill = fill.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn stroke<S>(mut self, stroke: S) -> Self
|
||||
where S: Into<Stroke> {
|
||||
self.style.stroke = stroke.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn opacity(mut self, opacity: f32) -> Self {
|
||||
self.style.opacity = opacity;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn style(mut self, style: Style) -> Self {
|
||||
self.style = style;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path() -> Path {
|
||||
Path {
|
||||
ops: Vec::new(),
|
||||
style: Style::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// `<text x="{x}" y="{y}" ... > {text} </text>`
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Text {
|
||||
pub x: f32, pub y: f32,
|
||||
pub text: String,
|
||||
pub color: Color,
|
||||
pub align: Align,
|
||||
pub size: f32,
|
||||
}
|
||||
|
||||
impl fmt::Display for Text {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f,
|
||||
r#"<text x="{}" y="{}" style="font-size:{}px;fill:{};{}"> {} </text>"#,
|
||||
self.x, self.y,
|
||||
self.size,
|
||||
self.color,
|
||||
self.align,
|
||||
self.text,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn text<T: Into<String>>(x: f32, y: f32, txt: T) -> Text {
|
||||
Text {
|
||||
x, y,
|
||||
text: txt.into(),
|
||||
color: black(),
|
||||
align: Align::Left,
|
||||
size: 10.0,
|
||||
}
|
||||
}
|
||||
|
||||
impl Text {
|
||||
pub fn color(mut self, color: Color) -> Self {
|
||||
self.color = color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn size(mut self, size: f32) -> Self {
|
||||
self.size = size;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn align(mut self, align: Align) -> Self {
|
||||
self.align = align;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn offset(mut self, dx: f32, dy: f32) -> Self {
|
||||
self.x += dx;
|
||||
self.y += dy;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// `text-align:{self}`
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum Align {
|
||||
Left, Right, Center
|
||||
}
|
||||
|
||||
impl fmt::Display for Align {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Align::Left => write!(f, "text-anchor:start;text-align:left;"),
|
||||
Align::Right => write!(f, "text-anchor:end;text-align:right;"),
|
||||
Align::Center => write!(f, "text-anchor:middle;text-align:center;"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {w} {y}">`
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub struct BeginSvg {
|
||||
pub w: f32,
|
||||
pub h: f32,
|
||||
}
|
||||
|
||||
impl fmt::Display for BeginSvg {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f,
|
||||
r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {} {}">"#,
|
||||
self.w,
|
||||
self.h,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// `</svg>`
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub struct EndSvg;
|
||||
|
||||
impl fmt::Display for EndSvg {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "</svg>")
|
||||
}
|
||||
}
|
||||
|
||||
/// `" "`
|
||||
pub struct Indentation {
|
||||
pub n: u32,
|
||||
}
|
||||
|
||||
pub fn indent(n: u32) -> Indentation {
|
||||
Indentation { n }
|
||||
}
|
||||
|
||||
impl Indentation {
|
||||
pub fn push(&mut self) {
|
||||
self.n += 1;
|
||||
}
|
||||
|
||||
pub fn pop(&mut self) {
|
||||
self.n -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Indentation {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
for _ in 0..self.n {
|
||||
write!(f, " ")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn foo() {
|
||||
println!("{}", BeginSvg { w: 800.0, h: 600.0 });
|
||||
println!(" {}",
|
||||
rectangle(20.0, 50.0, 200.0, 100.0)
|
||||
.fill(red())
|
||||
.stroke(Stroke::Color(black(), 3.0))
|
||||
.border_radius(5.0)
|
||||
);
|
||||
println!(" {}", text(25.0, 100.0, "Foo!").size(42.0).color(white()));
|
||||
println!("{}", EndSvg);
|
||||
}
|
Loading…
Reference in New Issue
Block a user