Merge mozilla-inbound to mozilla-central. a=merge

This commit is contained in:
Sebastian Hengst 2019-04-30 13:33:07 +02:00
commit 48c0fc9598
16 changed files with 1206 additions and 251 deletions

7
Cargo.lock generated
View File

@ -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
View File

@ -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"

View File

@ -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"

View File

@ -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)

View File

@ -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]

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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 {

View File

@ -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(
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);
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

View File

@ -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);

View 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
View 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
View 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
View 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
View 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
View 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);
}