spectrusty_utils/printer/
image_spooler.rs

1/*
2    Copyright (C) 2020-2022  Rafal Michalski
3
4    This file is part of SPECTRUSTY, a Rust library for building emulators.
5
6    For the full copyright notice, see the lib.rs file.
7*/
8use std::io;
9
10#[cfg(feature = "snapshot")]
11use serde::{Serialize, Deserialize};
12#[allow(unused_imports)]
13use log::{error, warn, info, debug, trace};
14
15use spectrusty::peripherals::zxprinter::{DOTS_PER_LINE, BYTES_PER_LINE, Spooler};
16use super::*;
17
18/// A simple **ZXPrinter** spooler that produces images via [DotMatrixGfx] trait.
19/// Use this type as a substitute for the `S` generic parameter of [ZxPrinterDevice] or [ZxPrinterBusDevice].
20///
21/// [ZxPrinterDevice]: spectrusty::peripherals::zxprinter::ZxPrinterDevice
22/// [ZxPrinterBusDevice]: spectrusty::peripherals::bus::zxprinter::ZxPrinterBusDevice
23#[derive(Clone, Default, Debug)]
24#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
25#[cfg_attr(feature = "snapshot", serde(default))]
26pub struct ImageSpooler {
27    spooling: bool,
28    #[cfg_attr(feature = "snapshot", serde(skip))]
29    buf: Vec<u8>
30}
31
32impl Spooler for ImageSpooler {
33    fn motor_on(&mut self) {
34        self.spooling = true;
35    }
36
37    fn push_line(&mut self, line: &[u8]) {
38        assert!(line.len() <= BYTES_PER_LINE as usize);
39        self.buf.extend_from_slice(line);
40        self.buf.resize(self.buf.len() + BYTES_PER_LINE as usize - line.len(), 0);
41        self.spooling = true;
42        debug!("printed line: {}", self.buf.len() / BYTES_PER_LINE as usize);
43    }
44
45    fn motor_off(&mut self) {
46        self.spooling = false;
47        debug!("end of printing");
48    }
49}
50
51impl DotMatrixGfx for ImageSpooler {
52    fn is_spooling(&self) -> bool {
53        self.spooling
54    }
55
56    fn lines_buffered(&self) -> usize {
57        (self.buf.len() + BYTES_PER_LINE as usize - 1) / BYTES_PER_LINE as usize
58    }
59
60    fn clear(&mut self) {
61        self.buf.clear();
62    }
63
64    fn write_svg_dot_gfx_lines(&self, description: &str, target: &mut dyn io::Write) -> io::Result<bool> {
65        let lines = self.lines_buffered();
66        if lines == 0 {
67            return Ok(false)
68        }
69        write!(target, r##"<?xml version="1.0" standalone="no"?>
70<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
71  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
72<svg width="110mm" height="{height_mm:.2}mm" version="1.1"
73     viewBox="0 0 {pixel_width} {pixel_height}" preserveAspectRatio="none"
74     xmlns="http://www.w3.org/2000/svg">
75  <desc>{description}</desc>
76  <g stroke-width="0.2" stroke="#b8b" fill="#646">
77"##,
78            description=description,
79            height_mm=0.4*lines as f32,
80            pixel_width=DOTS_PER_LINE,
81            pixel_height = lines)?;
82        for (index, mut dots) in self.buf.iter().copied().enumerate() {
83            let x = (index % BYTES_PER_LINE as usize) * 8;
84            let y = index / BYTES_PER_LINE as usize;
85            for i in 0..8 {
86                dots = dots.rotate_left(1);
87                if dots & 1 == 1 {
88                    write!(target,
89                        r##"<circle cx="{}" cy="{}" r="0.5"/>"##,
90                        x + i, y)?;
91                }
92            }
93        }
94        target.write_all(b"</g></svg>")?;
95        Ok(true)
96    }
97
98    fn write_gfx_data(&mut self, target: &mut Vec<u8>) -> Option<(u32, u32)> {
99        let height = self.lines_buffered();
100        if height == 0 {
101            return None;
102        }
103        target.reserve_exact(DOTS_PER_LINE as usize * height);
104        target.extend(self.buf.iter().copied().flat_map(|mut bits| {
105            (0..8).map(move |_| {
106                bits = bits.rotate_left(1);
107                if bits & 1 == 1 { 0 } else { !0 }
108            })
109        }));
110        Some((DOTS_PER_LINE, height as u32))
111    }
112}