Skip to content

Commit

Permalink
dev: init gradient support
Browse files Browse the repository at this point in the history
  • Loading branch information
Myriad-Dreamin committed Oct 23, 2023
1 parent 533f6da commit 1c0b6a2
Show file tree
Hide file tree
Showing 201 changed files with 3,262 additions and 234 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 39 additions & 7 deletions core/src/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use siphasher::sip128::{Hasher128, SipHasher13};

#[cfg(feature = "rkyv")]
use rkyv::{Archive, Deserialize as rDeser, Serialize as rSer};
use typst::diag::StrResult;

/// See <https://github.com/rust-lang/rust/blob/master/compiler/rustc_hir/src/stable_hash_impls.rs#L22>
/// The fingerprint conflicts should be very rare and should be handled by the
Expand Down Expand Up @@ -35,26 +36,39 @@ impl Fingerprint {
((self.1 as u128) << 64) | self.0 as u128
}

pub fn try_from_str(s: &str) -> StrResult<Self> {
let bytes = base64::engine::general_purpose::STANDARD_NO_PAD
.decode(&s.as_bytes()[..11])
.expect("invalid base64 string");
let lo = u64::from_le_bytes(bytes.try_into().unwrap());
let mut bytes = base64::engine::general_purpose::STANDARD_NO_PAD
.decode(&s.as_bytes()[11..])
.expect("invalid base64 string");
bytes.resize(8, 0);
let hi = u64::from_le_bytes(bytes.try_into().unwrap());
Ok(Self::from_pair(lo, hi))
}

/// Create a xml id from the given prefix and the fingerprint of this
/// reference. Note that the entire html document shares namespace for
/// ids.
#[comemo::memoize]
pub fn as_svg_id(self, prefix: &'static str) -> String {
let fingerprint_hi =
let fingerprint_lo =
base64::engine::general_purpose::STANDARD_NO_PAD.encode(self.0.to_le_bytes());
if self.1 == 0 {
return [prefix, &fingerprint_hi].join("");
return [prefix, &fingerprint_lo].join("");
}

// possible the id in the lower 64 bits.
let fingerprint_lo = {
let fingerprint_hi = {
let id = self.1.to_le_bytes();
// truncate zero
let rev_zero = id.iter().rev().skip_while(|&&b| b == 0).count();
let id = &id[..rev_zero];
base64::engine::general_purpose::STANDARD_NO_PAD.encode(id)
};
[prefix, &fingerprint_hi, &fingerprint_lo].join("")
[prefix, &fingerprint_lo, &fingerprint_hi].join("")
}
}

Expand All @@ -66,18 +80,21 @@ pub trait FingerprintHasher: std::hash::Hasher {
}

/// A fingerprint hasher that uses the [`SipHasher13`] algorithm.
struct FingerprintSipHasher {
#[derive(Default)]
pub struct FingerprintSipHasher {
/// The underlying data passed to the hasher.
data: Vec<u8>,
}

pub type FingerprintSipHasherBase = SipHasher13;

impl std::hash::Hasher for FingerprintSipHasher {
fn write(&mut self, bytes: &[u8]) {
self.data.extend_from_slice(bytes);
}

fn finish(&self) -> u64 {
let mut inner = SipHasher13::new();
let mut inner = FingerprintSipHasherBase::new();
self.data.hash(&mut inner);
inner.finish()
}
Expand All @@ -86,7 +103,7 @@ impl std::hash::Hasher for FingerprintSipHasher {
impl FingerprintHasher for FingerprintSipHasher {
fn finish_fingerprint(&self) -> (Fingerprint, Vec<u8>) {
let buffer = self.data.clone();
let mut inner = SipHasher13::new();
let mut inner = FingerprintSipHasherBase::new();
buffer.hash(&mut inner);
let hash = inner.finish128();
(Fingerprint(hash.h1, hash.h2), buffer)
Expand Down Expand Up @@ -157,3 +174,18 @@ pub fn item_hash128<T: Hash + 'static>(item: &T) -> u128 {
pub fn hash128<T: std::hash::Hash>(t: &T) -> u128 {
typst::util::hash128(t)
}

#[test]
fn test_fingerprint() {
let t = Fingerprint::from_pair(0, 1);
assert_eq!(Fingerprint::try_from_str(&t.as_svg_id("")).unwrap(), t);

let t = Fingerprint::from_pair(1, 1);
assert_eq!(Fingerprint::try_from_str(&t.as_svg_id("")).unwrap(), t);

let t = Fingerprint::from_pair(1, 0);
assert_eq!(Fingerprint::try_from_str(&t.as_svg_id("")).unwrap(), t);

let t = Fingerprint::from_pair(0, 0);
assert_eq!(Fingerprint::try_from_str(&t.as_svg_id("")).unwrap(), t);
}
26 changes: 20 additions & 6 deletions core/src/vector/bbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use comemo::Prehashed;
use typst::font::Font;

use super::ir::{FontIndice, FontRef, GlyphPackBuilder, GlyphRef};
use super::vm::RenderState;
use super::{
flat_ir::{self, Module},
flat_vm::{FlatGroupContext, FlatIncrGroupContext, FlatIncrRenderVm, FlatRenderVm},
Expand Down Expand Up @@ -205,8 +206,14 @@ impl<C> TransformContext<C> for BBoxBuilder {

/// See [`GroupContext`].
impl<C: BuildGlyph + RenderVm<Resultant = BBox>> GroupContext<C> for BBoxBuilder {
fn render_item_at(&mut self, ctx: &mut C, pos: ir::Point, item: &ir::SvgItem) {
let bbox = ctx.render_item(item);
fn render_item_at(
&mut self,
state: RenderState,
ctx: &mut C,
pos: ir::Point,
item: &ir::SvgItem,
) {
let bbox = ctx.render_item(state, item);
self.inner.push((pos, bbox));
}

Expand All @@ -215,7 +222,7 @@ impl<C: BuildGlyph + RenderVm<Resultant = BBox>> GroupContext<C> for BBoxBuilder
self.render_glyph_ref_inner(pos, &glyph_ref, glyph)
}

fn render_path(&mut self, _ctx: &mut C, path: &ir::PathItem) {
fn render_path(&mut self, _ctx: &mut C, path: &ir::PathItem, _abs_ref: &Fingerprint) {
let path = PathRepr::from_item(path).unwrap();
self.inner.push((
ir::Point::default(),
Expand Down Expand Up @@ -407,7 +414,7 @@ fn convert_path(path_data: &str) -> Option<tiny_skia_path::Path> {

#[cfg(test)]
mod tests {
use tests::ir::PathItem;
use tests::ir::{PathItem, Size};

use crate::vector::path2d::SvgPath2DBuilder;

Expand Down Expand Up @@ -438,6 +445,7 @@ mod tests {
let d = d.0.into();
let path = PathItem {
d,
size: None,
styles: Default::default(),
};

Expand All @@ -450,7 +458,10 @@ mod tests {
let mut task = t.get();

let rect = get_rect_item(1., 2., 10., 20.);
let bbox = task.render_item(&rect);
let bbox = task.render_item(
RenderState::new_size(Size::new(Scalar(10.), Scalar(20.))),
&rect,
);

println!("{:?}", bbox.realize(Transform::identity()));
}
Expand All @@ -461,7 +472,10 @@ mod tests {
let mut task = t.get();

let rect = get_rect_item(1., 2., 10., 20.);
let bbox = task.render_item(&rect);
let bbox = task.render_item(
RenderState::new_size(Size::new(Scalar(10.), Scalar(20.))),
&rect,
);

let ts = sk::Transform::from_translate(10., 20.);
println!("{:?}", bbox.realize(ts.into()));
Expand Down
7 changes: 4 additions & 3 deletions core/src/vector/flat_ir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ use crate::{
use super::{
geom::{Abs, Point, Size},
ir::{
DefId, FontItem, FontRef, GlyphItem, GlyphRef, ImageGlyphItem, ImageItem, ImmutStr,
LinkItem, OutlineGlyphItem, PathItem, SpanId, TextShape, TransformItem,
DefId, FontItem, FontRef, GlyphItem, GlyphRef, GradientItem, ImageGlyphItem, ImageItem,
ImmutStr, LinkItem, OutlineGlyphItem, PathItem, SpanId, TextShape, TransformItem,
},
};

Expand All @@ -67,7 +67,8 @@ pub enum FlatSvgItem {
Path(PathItem),
Text(FlatTextItem),
Item(TransformedRef),
Group(GroupRef),
Group(GroupRef, Option<Size>),
Gradient(GradientItem),
}

/// Flatten text item.
Expand Down
6 changes: 4 additions & 2 deletions core/src/vector/flat_ir/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ impl<const ENABLE_REF_CNT: bool> ModuleBuilderImpl<ENABLE_REF_CNT> {

FlatSvgItem::Path(path)
}
SvgItem::Gradient(g) => FlatSvgItem::Gradient(g),
SvgItem::Link(link) => FlatSvgItem::Link(link),
SvgItem::Text(text) => {
let font = self.build_font(&text.font);
Expand Down Expand Up @@ -288,7 +289,7 @@ impl<const ENABLE_REF_CNT: bool> ModuleBuilderImpl<ENABLE_REF_CNT> {

FlatSvgItem::Item(TransformedRef(transform, item_id))
}
SvgItem::Group(group) => {
SvgItem::Group(group, size) => {
let t = if self.should_attach_debug_info {
Some(self.source_mapping_buffer.len())
} else {
Expand All @@ -312,7 +313,8 @@ impl<const ENABLE_REF_CNT: bool> ModuleBuilderImpl<ENABLE_REF_CNT> {
.push(SourceMappingNode::Group(sm_range.collect()));
self.source_mapping_buffer.push(sm_id);
}
FlatSvgItem::Group(GroupRef(items.into()))

FlatSvgItem::Group(GroupRef(items.into()), size)
}
};

Expand Down
41 changes: 29 additions & 12 deletions core/src/vector/flat_vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::hash::Fingerprint;

use super::flat_ir as ir;
use super::ir::{FontIndice, GlyphRef};
use super::vm::RenderState;
use super::{
ir::{Point, Scalar},
vm::{GroupContext, TransformContext},
Expand All @@ -30,7 +31,13 @@ pub trait FlatGroupContext<C>: Sized {
fn with_frame(self, _ctx: &mut C, _group: &ir::GroupRef) -> Self {
self
}
fn with_text(self, _ctx: &mut C, _text: &ir::FlatTextItem) -> Self {
fn with_text(
self,
_ctx: &mut C,
_text: &ir::FlatTextItem,
_fill_key: &Fingerprint,
_state: RenderState,
) -> Self {
self
}
fn with_reuse(self, _ctx: &mut C, _v: &Fingerprint) -> Self {
Expand All @@ -56,21 +63,28 @@ pub trait FlatRenderVm<'m>: Sized + FontIndice<'m> {
self.start_flat_group(value)
}

fn start_flat_text(&mut self, value: &Fingerprint, _text: &ir::FlatTextItem) -> Self::Group {
fn start_flat_text(
&mut self,
_state: RenderState,
value: &Fingerprint,
_text: &ir::FlatTextItem,
) -> Self::Group {
self.start_flat_group(value)
}

#[doc(hidden)]
/// Default implemenetion to render an item into the a `<g/>` element.
fn _render_flat_item(&mut self, abs_ref: &Fingerprint) -> Self::Resultant {
// todo state
let state = RenderState::default();
let item: &'m ir::FlatSvgItem = self.get_item(abs_ref).unwrap();
match &item {
ir::FlatSvgItem::Group(group) => self.render_group_ref(abs_ref, group),
ir::FlatSvgItem::Group(group, _) => self.render_group_ref(abs_ref, group),
ir::FlatSvgItem::Item(transformed) => self.render_transformed_ref(abs_ref, transformed),
ir::FlatSvgItem::Text(text) => self.render_flat_text(abs_ref, text),
ir::FlatSvgItem::Text(text) => self.render_flat_text(state, abs_ref, text),
ir::FlatSvgItem::Path(path) => {
let mut g = self.start_flat_group(abs_ref);
g.render_path(self, path);
g.render_path(self, path, abs_ref);
g.into()
}
ir::FlatSvgItem::Link(link) => {
Expand All @@ -83,7 +97,7 @@ pub trait FlatRenderVm<'m>: Sized + FontIndice<'m> {
g.render_image(self, image);
g.into()
}
ir::FlatSvgItem::None => {
ir::FlatSvgItem::Gradient(..) | ir::FlatSvgItem::None => {
panic!("FlatRenderVm.RenderFrame.UnknownItem {:?}", item)
}
}
Expand Down Expand Up @@ -125,10 +139,11 @@ pub trait FlatRenderVm<'m>: Sized + FontIndice<'m> {
/// Render a text into the underlying context.
fn render_flat_text(
&mut self,
state: RenderState,
abs_ref: &Fingerprint,
text: &ir::FlatTextItem,
) -> Self::Resultant {
let group_ctx = self.start_flat_text(abs_ref, text);
let group_ctx = self.start_flat_text(state, abs_ref, text);

let font = self.get_font(&text.font).unwrap();

Expand Down Expand Up @@ -184,13 +199,15 @@ where
next_abs_ref: &Fingerprint,
prev_abs_ref: &Fingerprint,
) -> Self::Resultant {
// todo state
let state = RenderState::default();
let next_item: &'m ir::FlatSvgItem = self.get_item(next_abs_ref).unwrap();
let prev_item = self.get_item(prev_abs_ref);

let mut group_ctx = self.start_flat_group(next_abs_ref);

match &next_item {
ir::FlatSvgItem::Group(group) => {
ir::FlatSvgItem::Group(group, _) => {
let mut group_ctx = group_ctx
.with_reuse(self, prev_abs_ref)
.with_frame(self, group);
Expand All @@ -205,11 +222,11 @@ where
group_ctx
}
ir::FlatSvgItem::Text(text) => {
let group_ctx = group_ctx.with_text(self, text);
let group_ctx = group_ctx.with_text(self, text, next_abs_ref, state);
self.render_diff_flat_text(group_ctx, text)
}
ir::FlatSvgItem::Path(path) => {
group_ctx.render_path(self, path);
group_ctx.render_path(self, path, next_abs_ref);
group_ctx
}
ir::FlatSvgItem::Link(link) => {
Expand All @@ -220,7 +237,7 @@ where
group_ctx.render_image(self, image);
group_ctx
}
ir::FlatSvgItem::None => {
ir::FlatSvgItem::Gradient(..) | ir::FlatSvgItem::None => {
panic!("FlatRenderVm.RenderFrame.UnknownItem {:?}", next_item)
}
}
Expand All @@ -243,7 +260,7 @@ where
prev_item_: Option<&ir::FlatSvgItem>,
next: &ir::GroupRef,
) {
if let Some(ir::FlatSvgItem::Group(prev_group)) = prev_item_ {
if let Some(ir::FlatSvgItem::Group(prev_group, _)) = prev_item_ {
let mut unused_prev: BTreeMap<usize, Fingerprint> =
prev_group.0.iter().map(|v| v.1).enumerate().collect();
let reusable: HashSet<Fingerprint, RandomState> =
Expand Down
Loading

0 comments on commit 1c0b6a2

Please sign in to comment.