Draw pen hover position into image
This is useful to point out parts of your drawing when streaming to a video conference.
This commit is contained in:
parent
57dcd7ca27
commit
66e98b1fee
970
Cargo.lock
generated
970
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -8,3 +8,4 @@ edition = "2018"
|
||||
anyhow = "1.0"
|
||||
lz-fear = "0.1"
|
||||
clap = "3.0.0-beta.2"
|
||||
libremarkable = "0.4.3"
|
||||
|
12
reStream.sh
12
reStream.sh
@ -3,6 +3,7 @@
|
||||
# default values for arguments
|
||||
remarkable="10.11.99.1" # remarkable connected through USB
|
||||
landscape=true # rotate 90 degrees to the right
|
||||
cursor=false # show a cursor where the pen is hovering
|
||||
output_path=- # display output through ffplay
|
||||
format=- # automatic output format
|
||||
webcam=false # not to a webcam
|
||||
@ -18,6 +19,10 @@ while [ $# -gt 0 ]; do
|
||||
landscape=false
|
||||
shift
|
||||
;;
|
||||
-c | --cursor)
|
||||
cursor=true
|
||||
shift
|
||||
;;
|
||||
-s | --source)
|
||||
remarkable="$2"
|
||||
shift
|
||||
@ -68,10 +73,11 @@ while [ $# -gt 0 ]; do
|
||||
shift
|
||||
;;
|
||||
-h | --help | *)
|
||||
echo "Usage: $0 [-p] [-u] [-s <source>] [-o <output>] [-f <format>] [-t <title>]"
|
||||
echo "Usage: $0 [-p] [-c] [-u] [-s <source>] [-o <output>] [-f <format>] [-t <title>]"
|
||||
echo "Examples:"
|
||||
echo " $0 # live view in landscape"
|
||||
echo " $0 -p # live view in portrait"
|
||||
echo " $0 -c # live view with cursor"
|
||||
echo " $0 -s 192.168.0.10 # connect to different IP"
|
||||
echo " $0 -o remarkable.mp4 # record to a file"
|
||||
echo " $0 -o udp://dest:1234 -f mpegts # record to a stream"
|
||||
@ -199,6 +205,10 @@ set -e # stop if an error occurs
|
||||
|
||||
restream_options="-h $height -w $width -b $bytes_per_pixel -f $fb_file"
|
||||
|
||||
if "$cursor"; then
|
||||
restream_options="$restream_options -c"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2089
|
||||
restream_rs="PATH=\"\$PATH:/opt/bin/:.\" restream $restream_options"
|
||||
if $unsecure_connection; then
|
||||
|
151
src/main.rs
151
src/main.rs
@ -6,6 +6,9 @@ use anyhow::{Context, Result};
|
||||
use clap::{crate_authors, crate_version, Clap};
|
||||
use lz_fear::CompressionSettings;
|
||||
|
||||
use libremarkable::cgmath;
|
||||
use libremarkable::input::{ev::EvDevContext, wacom::WacomEvent, wacom::WacomPen, InputDevice, InputEvent};
|
||||
|
||||
use std::default::Default;
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader, Read, Seek, SeekFrom, Write};
|
||||
@ -13,7 +16,7 @@ use std::net::{TcpStream, TcpListener};
|
||||
use std::process::Command;
|
||||
use std::time::Duration;
|
||||
use std::thread;
|
||||
use std::sync::mpsc::channel;
|
||||
use std::sync::mpsc::{channel, Receiver};
|
||||
|
||||
#[derive(Clap)]
|
||||
#[clap(version = crate_version!(), author = crate_authors!())]
|
||||
@ -58,6 +61,14 @@ pub struct Opts {
|
||||
)]
|
||||
file: String,
|
||||
|
||||
#[clap(
|
||||
long,
|
||||
name = "cursor",
|
||||
short = 'c',
|
||||
about = "Show a cursor where the pen is hovering."
|
||||
)]
|
||||
show_cursor: bool,
|
||||
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
@ -72,7 +83,7 @@ fn main() -> Result<()> {
|
||||
(opts.file.to_owned(), 0)
|
||||
};
|
||||
|
||||
let streamer = ReStreamer::init(&file, offset, opts.width, opts.height, opts.bytes_per_pixel)?;
|
||||
let streamer = ReStreamer::init(&file, offset, opts.width, opts.height, opts.bytes_per_pixel, opts.show_cursor)?;
|
||||
|
||||
let stdout = std::io::stdout();
|
||||
let data_target: Box<dyn Write> = if let Some(port) = opts.listen {
|
||||
@ -145,6 +156,14 @@ pub struct ReStreamer {
|
||||
start: u64,
|
||||
cursor: usize,
|
||||
size: usize,
|
||||
width: usize,
|
||||
height: usize,
|
||||
bytes_per_pixel: usize,
|
||||
|
||||
show_cursor: bool,
|
||||
input_rx: Receiver<InputEvent>,
|
||||
pen_pos: Option<(usize, usize)>,
|
||||
drawing: bool,
|
||||
}
|
||||
|
||||
impl ReStreamer {
|
||||
@ -154,16 +173,30 @@ impl ReStreamer {
|
||||
width: usize,
|
||||
height: usize,
|
||||
bytes_per_pixel: usize,
|
||||
show_cursor: bool,
|
||||
) -> Result<ReStreamer> {
|
||||
let start = offset as u64;
|
||||
let size = width * height * bytes_per_pixel;
|
||||
let cursor = 0;
|
||||
let file = File::open(path)?;
|
||||
|
||||
let (input_tx, input_rx) = channel::<InputEvent>();
|
||||
if show_cursor {
|
||||
EvDevContext::new(InputDevice::Wacom, input_tx).start();
|
||||
}
|
||||
|
||||
let mut streamer = ReStreamer {
|
||||
file,
|
||||
start: start,
|
||||
cursor,
|
||||
size,
|
||||
width,
|
||||
height,
|
||||
bytes_per_pixel,
|
||||
show_cursor,
|
||||
input_rx,
|
||||
pen_pos: None,
|
||||
drawing: false,
|
||||
};
|
||||
streamer.next_frame()?;
|
||||
Ok(streamer)
|
||||
@ -172,10 +205,119 @@ impl ReStreamer {
|
||||
pub fn next_frame(&mut self) -> std::io::Result<()> {
|
||||
self.file.seek(SeekFrom::Start(self.start))?;
|
||||
self.cursor = 0;
|
||||
if self.show_cursor {
|
||||
self.read_input();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read input events to figure out pen state and position.
|
||||
fn read_input(&mut self) {
|
||||
let mut down = self.pen_pos.is_some();
|
||||
let mut pos = self.pen_pos.unwrap_or((0, 0));
|
||||
while let Ok(event) = self.input_rx.try_recv() {
|
||||
match event {
|
||||
InputEvent::WacomEvent { event: e } =>
|
||||
match e {
|
||||
WacomEvent::InstrumentChange { pen: WacomPen::ToolPen, state: s } => {
|
||||
down = s;
|
||||
},
|
||||
WacomEvent::Hover { position: cgmath::Point2 { x, y }, .. } => {
|
||||
pos = (x as usize, y as usize);
|
||||
self.drawing = false;
|
||||
},
|
||||
WacomEvent::Draw { .. } => {
|
||||
// no need to show position while drawing
|
||||
self.drawing = true;
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
self.pen_pos = if down {
|
||||
Some(pos)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
|
||||
/// Draw pen position into fb data, if necessary (in hover range and not drawing).
|
||||
fn draw_pen_position(&mut self, buf: &mut [u8]) {
|
||||
if let (false, Some((y, x))) = (self.drawing, self.pen_pos) {
|
||||
// we need negative numbers to calculate offsets correctly
|
||||
let width = self.width as isize;
|
||||
let height = self.height as isize;
|
||||
let bpp = self.bytes_per_pixel as isize;
|
||||
let cursor = self.cursor as isize;
|
||||
for (i, (yoff, no)) in PEN_IMAGE.iter().enumerate() {
|
||||
// we draw vertically (lines along y)
|
||||
let xoff = i as isize - (PEN_IMAGE.len() as isize / 2);
|
||||
let xstart = x as isize + xoff;
|
||||
// line outside of canvas?
|
||||
if xstart < 0 || xstart >= width {
|
||||
continue
|
||||
}
|
||||
let mut ystart = (height - y as isize) + yoff;
|
||||
let mut no = *no;
|
||||
// cut-off at sides
|
||||
if ystart < 0 {
|
||||
no += ystart;
|
||||
ystart = 0;
|
||||
}
|
||||
if ystart + no > height {
|
||||
no = height - ystart;
|
||||
}
|
||||
if no <= 0 {
|
||||
continue
|
||||
}
|
||||
// translate to buf indexes, check bounds and draw
|
||||
let mut px_start = (xstart * height + ystart) * bpp;
|
||||
let mut px_end = px_start + no * bpp;
|
||||
// outside current buf?
|
||||
if px_end < cursor || px_start >= cursor + buf.len() as isize {
|
||||
continue
|
||||
}
|
||||
// truncate if partially outside
|
||||
if px_start < cursor {
|
||||
px_start = cursor;
|
||||
}
|
||||
if px_end > cursor + buf.len() as isize {
|
||||
px_end = cursor + buf.len() as isize;
|
||||
}
|
||||
// invert pixel (on RM2)
|
||||
// TODO: Do something sensible on RM1
|
||||
for b in buf[(px_start-cursor) as usize..(px_end-cursor) as usize].iter_mut() {
|
||||
*b = 255 - *b;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Image of pen, given as (offset from pen position, number of pixels)
|
||||
static PEN_IMAGE: [(isize, isize); 17] = [
|
||||
(0, 1), // 00000000100000000
|
||||
(0, 1), // 00000000100000000
|
||||
(0, 1), // 00000000100000000
|
||||
(-1, 3), // 00000001110000000
|
||||
(-3, 7), // 00000111111100000
|
||||
(-4, 9), // 00001111111110000
|
||||
(-4, 9), // 00001111111110000
|
||||
(-5, 11), // 00011111111111000
|
||||
(-8, 17), // 11111111111111111
|
||||
(-5, 11), // 00011111111111000
|
||||
(-4, 9), // 00001111111110000
|
||||
(-4, 9), // 00001111111110000
|
||||
(-3, 7), // 00000111111100000
|
||||
(-1, 3), // 00000001110000000
|
||||
(0, 1), // 00000000100000000
|
||||
(0, 1), // 00000000100000000
|
||||
(0, 1), // 00000000100000000
|
||||
];
|
||||
|
||||
impl Read for ReStreamer {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
let requested = buf.len();
|
||||
@ -185,6 +327,11 @@ impl Read for ReStreamer {
|
||||
let rest = self.size - self.cursor;
|
||||
self.file.read(&mut buf[0..rest])?
|
||||
};
|
||||
|
||||
if self.show_cursor {
|
||||
self.draw_pen_position(&mut buf[0..bytes_read]);
|
||||
}
|
||||
|
||||
self.cursor += bytes_read;
|
||||
if self.cursor == self.size {
|
||||
self.next_frame()?;
|
||||
|
Loading…
x
Reference in New Issue
Block a user