1use dotloom_geometry::{
15 Aabb, Arc, Circle, HAlign, Path, PathEl, Point, PointShape, Polygon, Polyline, Rect, Segment, Shape, Text, VAlign,
16 Vector,
17};
18use thiserror::Error;
19
20use crate::{Primitive, SceneDelta, SceneItem, Stroke};
21
22pub const MAGIC: &[u8; 4] = b"DLSC";
24pub const SCENE_FORMAT_VERSION: u16 = 1;
26
27#[derive(Debug, Clone, PartialEq, Eq, Error)]
29pub enum DecodeError {
30 #[error("unexpected end of scene data")]
32 Truncated,
33 #[error("not a Dotloom scene delta")]
35 BadMagic,
36 #[error("unsupported scene format version {0}")]
38 Version(u16),
39 #[error("invalid tag {0}")]
41 Tag(u8),
42 #[error("count {0} exceeds remaining input")]
44 Count(u32),
45 #[error("invalid utf-8 text")]
47 Utf8,
48 #[error("trailing bytes")]
50 Trailing,
51}
52
53struct W(Vec<u8>);
54
55impl W {
56 fn u8(&mut self, v: u8) {
57 self.0.push(v);
58 }
59 fn u16(&mut self, v: u16) {
60 self.0.extend_from_slice(&v.to_le_bytes());
61 }
62 fn u32(&mut self, v: u32) {
63 self.0.extend_from_slice(&v.to_le_bytes());
64 }
65 fn u64(&mut self, v: u64) {
66 self.0.extend_from_slice(&v.to_le_bytes());
67 }
68 fn f32(&mut self, v: f32) {
69 self.0.extend_from_slice(&v.to_le_bytes());
70 }
71 fn f64(&mut self, v: f64) {
72 self.0.extend_from_slice(&v.to_le_bytes());
73 }
74 fn len(&mut self, n: usize) {
75 self.u32(u32::try_from(n).unwrap_or(u32::MAX));
76 }
77 fn pt(&mut self, p: Point) {
78 self.f64(p.x);
79 self.f64(p.y);
80 }
81 fn str(&mut self, s: &str) {
82 self.len(s.len());
83 self.0.extend_from_slice(s.as_bytes());
84 }
85 fn text(&mut self, t: &Text) {
86 self.pt(t.position);
87 self.f64(t.height);
88 self.f64(t.rotation);
89 self.u8(match t.halign {
90 HAlign::Left => 0,
91 HAlign::Center => 1,
92 HAlign::Right => 2,
93 });
94 self.u8(match t.valign {
95 VAlign::Baseline => 0,
96 VAlign::Middle => 1,
97 VAlign::Top => 2,
98 VAlign::Bottom => 3,
99 });
100 self.str(&t.content);
101 }
102 fn points(&mut self, pts: &[Point]) {
103 self.len(pts.len());
104 for p in pts {
105 self.pt(*p);
106 }
107 }
108 fn shape(&mut self, s: &Shape) {
109 match s {
110 Shape::Point(p) => {
111 self.u8(0);
112 self.pt(p.at);
113 }
114 Shape::Line(l) => {
115 self.u8(1);
116 self.pt(l.a);
117 self.pt(l.b);
118 }
119 Shape::Polyline(p) => {
120 self.u8(2);
121 self.u8(u8::from(p.closed));
122 self.points(&p.points);
123 self.len(p.bulges.len());
124 for b in &p.bulges {
125 self.f64(*b);
126 }
127 }
128 Shape::Rect(r) => {
129 self.u8(3);
130 self.pt(r.origin);
131 self.f64(r.width);
132 self.f64(r.height);
133 }
134 Shape::Circle(c) => {
135 self.u8(4);
136 self.pt(c.center);
137 self.f64(c.radius);
138 }
139 Shape::Arc(a) => {
140 self.u8(5);
141 self.pt(a.center);
142 self.f64(a.radius);
143 self.f64(a.start);
144 self.f64(a.sweep);
145 }
146 Shape::Path(p) => {
147 self.u8(6);
148 self.len(p.elements.len());
149 for el in &p.elements {
150 match *el {
151 PathEl::MoveTo(a) => {
152 self.u8(0);
153 self.pt(a);
154 }
155 PathEl::LineTo(a) => {
156 self.u8(1);
157 self.pt(a);
158 }
159 PathEl::QuadTo(a, b) => {
160 self.u8(2);
161 self.pt(a);
162 self.pt(b);
163 }
164 PathEl::CubicTo(a, b, c) => {
165 self.u8(3);
166 self.pt(a);
167 self.pt(b);
168 self.pt(c);
169 }
170 PathEl::Close => self.u8(4),
171 }
172 }
173 }
174 Shape::Polygon(p) => {
175 self.u8(7);
176 self.len(1 + p.holes.len());
177 self.points(&p.outer);
178 for h in &p.holes {
179 self.points(h);
180 }
181 }
182 Shape::Text(t) => {
183 self.u8(8);
184 self.text(t);
185 }
186 }
187 }
188}
189
190struct R<'a> {
191 b: &'a [u8],
192 i: usize,
193}
194
195impl<'a> R<'a> {
196 fn take(&mut self, n: usize) -> Result<&'a [u8], DecodeError> {
197 let end = self.i.checked_add(n).ok_or(DecodeError::Truncated)?;
198 let s = self.b.get(self.i..end).ok_or(DecodeError::Truncated)?;
199 self.i = end;
200 Ok(s)
201 }
202 fn arr<const N: usize>(&mut self) -> Result<[u8; N], DecodeError> {
203 let s = self.take(N)?;
204 let mut a = [0u8; N];
205 a.copy_from_slice(s);
206 Ok(a)
207 }
208 fn u8(&mut self) -> Result<u8, DecodeError> {
209 Ok(self.arr::<1>()?[0])
210 }
211 fn u16(&mut self) -> Result<u16, DecodeError> {
212 Ok(u16::from_le_bytes(self.arr()?))
213 }
214 fn u32(&mut self) -> Result<u32, DecodeError> {
215 Ok(u32::from_le_bytes(self.arr()?))
216 }
217 fn u64(&mut self) -> Result<u64, DecodeError> {
218 Ok(u64::from_le_bytes(self.arr()?))
219 }
220 fn f32(&mut self) -> Result<f32, DecodeError> {
221 Ok(f32::from_le_bytes(self.arr()?))
222 }
223 fn f64(&mut self) -> Result<f64, DecodeError> {
224 Ok(f64::from_le_bytes(self.arr()?))
225 }
226 fn remaining(&self) -> usize {
227 self.b.len().saturating_sub(self.i)
228 }
229 fn count(&mut self, min_size: usize) -> Result<usize, DecodeError> {
231 let n = self.u32()?;
232 let need = (n as usize).checked_mul(min_size.max(1)).ok_or(DecodeError::Count(n))?;
233 if need > self.remaining() {
234 return Err(DecodeError::Count(n));
235 }
236 Ok(n as usize)
237 }
238 fn pt(&mut self) -> Result<Point, DecodeError> {
239 Ok(Point::new(self.f64()?, self.f64()?))
240 }
241 fn str(&mut self) -> Result<String, DecodeError> {
242 let n = self.count(1)?;
243 let s = self.take(n)?;
244 String::from_utf8(s.to_vec()).map_err(|_| DecodeError::Utf8)
245 }
246 fn text(&mut self) -> Result<Text, DecodeError> {
247 let position = self.pt()?;
248 let height = self.f64()?;
249 let rotation = self.f64()?;
250 let halign = match self.u8()? {
251 0 => HAlign::Left,
252 1 => HAlign::Center,
253 2 => HAlign::Right,
254 t => return Err(DecodeError::Tag(t)),
255 };
256 let valign = match self.u8()? {
257 0 => VAlign::Baseline,
258 1 => VAlign::Middle,
259 2 => VAlign::Top,
260 3 => VAlign::Bottom,
261 t => return Err(DecodeError::Tag(t)),
262 };
263 let content = self.str()?;
264 Ok(Text { position, content, height, rotation, halign, valign })
265 }
266 fn points(&mut self) -> Result<Vec<Point>, DecodeError> {
267 let n = self.count(16)?;
268 (0..n).map(|_| self.pt()).collect()
269 }
270 fn shape(&mut self) -> Result<Shape, DecodeError> {
271 Ok(match self.u8()? {
272 0 => Shape::Point(PointShape { at: self.pt()? }),
273 1 => Shape::Line(Segment::new(self.pt()?, self.pt()?)),
274 2 => {
275 let closed = self.u8()? != 0;
276 let points = self.points()?;
277 let nb = self.count(8)?;
278 let bulges = (0..nb).map(|_| self.f64()).collect::<Result<_, _>>()?;
279 Shape::Polyline(Polyline { points, bulges, closed })
280 }
281 3 => Shape::Rect(Rect { origin: self.pt()?, width: self.f64()?, height: self.f64()? }),
282 4 => Shape::Circle(Circle { center: self.pt()?, radius: self.f64()? }),
283 5 => Shape::Arc(Arc { center: self.pt()?, radius: self.f64()?, start: self.f64()?, sweep: self.f64()? }),
284 6 => {
285 let n = self.count(1)?;
286 let mut elements = Vec::with_capacity(n);
287 for _ in 0..n {
288 elements.push(match self.u8()? {
289 0 => PathEl::MoveTo(self.pt()?),
290 1 => PathEl::LineTo(self.pt()?),
291 2 => PathEl::QuadTo(self.pt()?, self.pt()?),
292 3 => PathEl::CubicTo(self.pt()?, self.pt()?, self.pt()?),
293 4 => PathEl::Close,
294 t => return Err(DecodeError::Tag(t)),
295 });
296 }
297 Shape::Path(Path { elements })
298 }
299 7 => {
300 let rings = self.count(4)?;
301 if rings == 0 {
302 return Err(DecodeError::Count(0));
303 }
304 let outer = self.points()?;
305 let holes = (1..rings).map(|_| self.points()).collect::<Result<_, _>>()?;
306 Shape::Polygon(Polygon { outer, holes })
307 }
308 8 => Shape::Text(self.text()?),
309 t => return Err(DecodeError::Tag(t)),
310 })
311 }
312}
313
314impl SceneDelta {
315 #[must_use]
317 pub fn encode(&self) -> Vec<u8> {
318 let mut w = W(Vec::with_capacity(64 + self.upserts.len() * 96));
319 w.0.extend_from_slice(MAGIC);
320 w.u16(SCENE_FORMAT_VERSION);
321 let flags = u16::from(self.preview) | (u16::from(self.reset) << 1) | (u16::from(self.order.is_some()) << 2);
322 w.u16(flags);
323 w.u64(self.revision);
324 w.len(self.upserts.len());
325 w.len(self.removals.len());
326 if let Some(o) = &self.order {
327 w.len(o.len());
328 }
329 for r in &self.removals {
330 w.u64(*r);
331 }
332 if let Some(o) = &self.order {
333 for id in o {
334 w.u64(*id);
335 }
336 }
337 for item in &self.upserts {
338 w.u64(item.id);
339 w.u32(item.layer);
340 w.u8(item.flags);
341 w.pt(item.bbox.min);
342 w.pt(item.bbox.max);
343 w.len(item.prims.len());
344 for p in &item.prims {
345 match p {
346 Primitive::Shape { shape, stroke, fill } => {
347 w.u8(0);
348 w.u8(u8::from(stroke.is_some()) | (u8::from(fill.is_some()) << 1));
349 if let Some(s) = stroke {
350 w.u32(s.color);
351 w.f32(s.width);
352 w.len(s.dash.len());
353 for d in &s.dash {
354 w.f32(*d);
355 }
356 }
357 if let Some(f) = fill {
358 w.u32(*f);
359 }
360 w.shape(shape);
361 }
362 Primitive::Text { text, color } => {
363 w.u8(1);
364 w.text(text);
365 w.u32(*color);
366 }
367 Primitive::Arrow { tip, direction, size, color } => {
368 w.u8(2);
369 w.pt(*tip);
370 w.f64(direction.x);
371 w.f64(direction.y);
372 w.f64(*size);
373 w.u32(*color);
374 }
375 }
376 }
377 }
378 w.0
379 }
380
381 pub fn decode(bytes: &[u8]) -> Result<Self, DecodeError> {
383 let mut r = R { b: bytes, i: 0 };
384 if r.take(4)? != MAGIC {
385 return Err(DecodeError::BadMagic);
386 }
387 let version = r.u16()?;
388 if version != SCENE_FORMAT_VERSION {
389 return Err(DecodeError::Version(version));
390 }
391 let flags = r.u16()?;
392 let revision = r.u64()?;
393 let n_up = r.count(4)?;
394 let n_rm = r.count(8)?;
395 let n_order = if flags & 4 != 0 { Some(r.count(8)?) } else { None };
396 let removals = (0..n_rm).map(|_| r.u64()).collect::<Result<_, _>>()?;
397 let order = match n_order {
398 Some(n) => Some((0..n).map(|_| r.u64()).collect::<Result<_, _>>()?),
399 None => None,
400 };
401 let mut upserts = Vec::with_capacity(n_up);
402 for _ in 0..n_up {
403 let id = r.u64()?;
404 let layer = r.u32()?;
405 let item_flags = r.u8()?;
406 let bbox = Aabb { min: r.pt()?, max: r.pt()? };
407 let np = r.count(1)?;
408 let mut prims = Vec::with_capacity(np);
409 for _ in 0..np {
410 prims.push(match r.u8()? {
411 0 => {
412 let f = r.u8()?;
413 let stroke = if f & 1 != 0 {
414 let color = r.u32()?;
415 let width = r.f32()?;
416 let nd = r.count(4)?;
417 let dash = (0..nd).map(|_| r.f32()).collect::<Result<_, _>>()?;
418 Some(Stroke { color, width, dash })
419 } else {
420 None
421 };
422 let fill = if f & 2 != 0 { Some(r.u32()?) } else { None };
423 Primitive::Shape { shape: r.shape()?, stroke, fill }
424 }
425 1 => Primitive::Text { text: r.text()?, color: r.u32()? },
426 2 => Primitive::Arrow {
427 tip: r.pt()?,
428 direction: Vector::new(r.f64()?, r.f64()?),
429 size: r.f64()?,
430 color: r.u32()?,
431 },
432 t => return Err(DecodeError::Tag(t)),
433 });
434 }
435 upserts.push(SceneItem { id, layer, bbox, flags: item_flags, prims });
436 }
437 if r.remaining() != 0 {
438 return Err(DecodeError::Trailing);
439 }
440 Ok(Self { revision, preview: flags & 1 != 0, reset: flags & 2 != 0, upserts, removals, order })
441 }
442}