diff options
Diffstat (limited to 'evc')
-rw-r--r-- | evc/.gitignore | 3 | ||||
-rw-r--r-- | evc/Cargo.lock | 4 | ||||
-rw-r--r-- | evc/spec.md | 8 | ||||
-rw-r--r-- | evc/src/bin/decode.rs | 11 | ||||
-rw-r--r-- | evc/src/bin/encode.rs | 21 | ||||
-rw-r--r-- | evc/src/block.rs | 57 | ||||
-rw-r--r-- | evc/src/codec/decode.rs | 32 | ||||
-rw-r--r-- | evc/src/codec/encode.rs | 177 | ||||
-rw-r--r-- | evc/src/format/ser.rs | 22 | ||||
-rw-r--r-- | evc/src/helpers/pixel.rs | 22 | ||||
-rw-r--r-- | evc/src/helpers/vector.rs | 3 | ||||
-rw-r--r-- | evc/src/refsampler.rs | 2 |
12 files changed, 244 insertions, 118 deletions
diff --git a/evc/.gitignore b/evc/.gitignore index 870bad6..b88f3af 100644 --- a/evc/.gitignore +++ b/evc/.gitignore @@ -1,3 +1,6 @@ /target /samples /reports +/flamegraph.svg +/perf.data* + diff --git a/evc/Cargo.lock b/evc/Cargo.lock index e804305..22ef8be 100644 --- a/evc/Cargo.lock +++ b/evc/Cargo.lock @@ -259,9 +259,9 @@ checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] name = "rustix" -version = "0.36.4" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb93e85278e08bb5788653183213d3a60fc242b10cb9be96586f5a73dcb67c23" +checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588" dependencies = [ "bitflags", "errno", diff --git a/evc/spec.md b/evc/spec.md index 9d4cc62..77f6b7d 100644 --- a/evc/spec.md +++ b/evc/spec.md @@ -8,13 +8,13 @@ - frames (repeated [frame count]-times) - block type - block - - **Literal-Block** (pixels saved) + - 0 **Literal-Block** (pixels saved) - pixels: _`[[u8; 3]]`_ - - **Split-Block** (delegated to 2 sub-blocks split on the longest axis) + - 1 **Split-Block** (delegated to 2 sub-blocks split on the longest axis) - sub-blocks: _`[block; 2]` (see above)_ - - **Reference-Block** (reuses previous frame in some way) + - 2 **Reference-Block** (reuses previous frame in some way) - translation: _`i8, i8`_ - - **Advanced-Reference-Block** (reuses previous frame in some way) + - 3 **Advanced-Reference-Block** (reuses previous frame in some way) - translation: _`s8, s8`_ (translation encoded as _floats_) - transform: _`s8, s8, s8, s8`_ (2x2-matrix of _floats_ applied before sampling) diff --git a/evc/src/bin/decode.rs b/evc/src/bin/decode.rs index 5cbd29e..c2b3b4d 100644 --- a/evc/src/bin/decode.rs +++ b/evc/src/bin/decode.rs @@ -3,7 +3,7 @@ use anyhow::Context; use clap::Parser; use evc::{ block::Block, - codec::decode::decode_block, + codec::decode::{decode_block, DecodeConfig}, debug::draw_debug, format::{header::Header, ser::Source}, frame::Frame, @@ -16,6 +16,9 @@ use std::io::{BufReader, BufWriter}; pub struct DecodeArgs { #[arg(long)] debug: bool, + + #[arg(short, long, default_value = "8")] + jobs: usize, } fn main() -> anyhow::Result<()> { @@ -29,6 +32,10 @@ fn main() -> anyhow::Result<()> { info!("{header:?}"); let size = header.resolution; + let config = DecodeConfig { + max_threads: args.jobs, + }; + let mut prev = Frame::new(size); for i in 0.. { info!("decode frame {i}"); @@ -36,7 +43,7 @@ fn main() -> anyhow::Result<()> { let block = Block::read(&mut input, size).context("reading encoded frame")?; let mut frame = Frame::new(size); - decode_block(&block, frame.view_mut(), prev.view()); + decode_block(&block, frame.view_mut(), prev.view(), &config); if args.debug { let mut f2 = frame.clone(); diff --git a/evc/src/bin/encode.rs b/evc/src/bin/encode.rs index fe03023..d27074e 100644 --- a/evc/src/bin/encode.rs +++ b/evc/src/bin/encode.rs @@ -20,7 +20,7 @@ pub struct EncodeArgs { #[arg(short = 'H', long)] height: usize, - #[arg(short, long, default_value = "advanced")] + #[arg(short, long, default_value = "default")] mode: EncodeMode, #[arg(short, long, default_value = "8")] @@ -29,10 +29,8 @@ pub struct EncodeArgs { #[arg(short, long, default_value = "8")] min_block_size: isize, - #[arg(short = 't', long, default_value = "100")] + #[arg(short = 't', long, default_value = "200")] ref_thres: f64, - #[arg(short = 'T', long)] - no_translation: bool, } fn main() -> anyhow::Result<()> { @@ -45,9 +43,12 @@ fn main() -> anyhow::Result<()> { let config = EncodeConfig { mode: args.mode, ref_thres: args.ref_thres, + weight_factor: 50.0, max_diff_area: 10_000, min_block_size: args.min_block_size, max_threads: args.jobs, + do_matrix_transform: false, + do_value_scale: false, }; let size = Vec2 { @@ -72,9 +73,17 @@ fn main() -> anyhow::Result<()> { let v2 = prev_frame.view(); let root = encode_block(v1, v2, &config); - root.write(&mut output).context("writing encoded frame")?; + root.write(&mut output, size) + .context("writing encoded frame")?; - decode_block(&root, frame.view_mut(), prev_frame.view()); + decode_block( + &root, + frame.view_mut(), + prev_frame.view(), + &evc::codec::decode::DecodeConfig { + max_threads: config.max_threads, + }, + ); prev_frame = frame; } diff --git a/evc/src/block.rs b/evc/src/block.rs index 24691e8..04ee3d2 100644 --- a/evc/src/block.rs +++ b/evc/src/block.rs @@ -1,10 +1,9 @@ -use anyhow::bail; - use crate::{ - format::ser::{Ser, Sink, Small, Source}, + format::ser::{ConstSizeSerExt, Sink, Small, Source}, helpers::vector::Vec2, helpers::{matrix::Mat2, pixel::Pixel}, }; +use anyhow::bail; #[derive(Clone, Debug)] pub enum Block { @@ -22,16 +21,21 @@ pub struct AdvancedReference { } impl Block { - pub fn write(&self, sink: &mut impl std::io::Write) -> anyhow::Result<()> { + pub const REFZERO: Block = Block::Reference { + translation: Vec2::<isize>::ZERO, + }; + + pub fn write(&self, sink: &mut impl std::io::Write, size: Vec2<isize>) -> anyhow::Result<()> { match &self { Block::Literal(pixels) => { sink.put(0u8)?; - pixels.write(sink)?; + pixels.write_const_size(sink, size.area() as usize)?; } Block::Split(box [a, b]) => { sink.put(1u8)?; - a.write(sink)?; - b.write(sink)?; + let [asize, bsize] = split_size(size); + a.write(sink, asize)?; + b.write(sink, bsize)?; } Block::Reference { translation } => { sink.put(2u8)?; @@ -51,20 +55,9 @@ impl Block { pub fn read(source: &mut impl std::io::Read, size: Vec2<isize>) -> anyhow::Result<Self> { Ok(match source.get::<u8>()? { - 0 => Block::Literal(source.get()?), + 0 => Block::Literal(Vec::read_const_size(source, size.area() as usize)?), 1 => Block::Split(Box::new({ - let vert = size.x > size.y; - let asize = if vert { - (size.x / 2, size.y).into() - } else { - (size.x, size.y / 2).into() - }; - let bsize = if vert { - (size.x - size.x / 2, size.y).into() - } else { - (size.x, size.y - size.y / 2).into() - }; - + let [asize, bsize] = split_size(size); let a = Block::read(source, asize)?; let b = Block::read(source, bsize)?; [a, b] @@ -82,10 +75,34 @@ impl Block { } } +pub fn split_size(size: Vec2<isize>) -> [Vec2<isize>; 2] { + let vert = size.x > size.y; + [ + if vert { + (size.x / 2, size.y).into() + } else { + (size.x, size.y / 2).into() + }, + if vert { + (size.x - size.x / 2, size.y).into() + } else { + (size.x, size.y - size.y / 2).into() + }, + ] +} + impl Block { pub fn is_literal(&self) -> bool { matches!(self, Block::Literal(..)) } + pub fn is_ref_without_translation(&self) -> bool { + matches!( + self, + Block::Reference { + translation: Vec2::<isize>::ZERO + } + ) + } } impl Default for AdvancedReference { diff --git a/evc/src/codec/decode.rs b/evc/src/codec/decode.rs index b7ab8c7..197028c 100644 --- a/evc/src/codec/decode.rs +++ b/evc/src/codec/decode.rs @@ -1,13 +1,33 @@ -use crate::{block::Block, frame::Frame, refsampler::Sampler, view::View}; +use crate::{ + block::Block, frame::Frame, helpers::threading::both_par, refsampler::Sampler, view::View, +}; -pub fn decode_block(block: &Block, mut target: View<&mut Frame>, prev: View<&Frame>) { +pub struct DecodeConfig { + pub max_threads: usize, +} + +pub fn decode_block( + block: &Block, + mut target: View<&mut Frame>, + prev: View<&Frame>, + config: &DecodeConfig, +) { match &block { Block::Literal(pixels) => target.set_pixels(pixels), Block::Split(box [a, b]) => { - let [at, bt] = target.split_mut_unsafe(); - let [ap, bp] = prev.split(); - decode_block(a, at, ap); - decode_block(b, bt, bp); + let [a, b] = unsafe { std::mem::transmute::<_, [&'static Block; 2]>([a, b]) }; + let [at, bt] = unsafe { + std::mem::transmute::<_, [View<&'static mut Frame>; 2]>(target.split_mut_unsafe()) + }; + let [ap, bp] = + unsafe { std::mem::transmute::<_, [View<&'static Frame>; 2]>(prev.split()) }; + let config = unsafe { std::mem::transmute::<_, &'static DecodeConfig>(config) }; + + both_par( + move || decode_block(a, at, ap, config), + move || decode_block(b, bt, bp, config), + config.max_threads, + ); } Block::Reference { translation } => target.copy_from(&prev.offset(*translation)), Block::AdvancedReference(r) => target.copy_from_sampler(&Sampler::from_refblock(prev, r)), diff --git a/evc/src/codec/encode.rs b/evc/src/codec/encode.rs index 4eb86b5..6b6f8a3 100644 --- a/evc/src/codec/encode.rs +++ b/evc/src/codec/encode.rs @@ -6,7 +6,6 @@ use crate::{ view::View, }; use clap::ValueEnum; -use log::debug; #[derive(Debug, Clone)] pub struct EncodeConfig { @@ -15,15 +14,38 @@ pub struct EncodeConfig { pub max_diff_area: isize, pub min_block_size: isize, pub max_threads: usize, + pub weight_factor: f64, + pub do_value_scale: bool, + pub do_matrix_transform: bool, } #[derive(Debug, Clone, ValueEnum)] pub enum EncodeMode { Trivial, + Fast, Default, Advanced, } +#[inline] +pub fn pk<F: FnMut(&mut AdvancedReference) -> ()>( + view: &View<&Frame>, + prev: &View<&Frame>, + diff: &mut f64, + params: &mut AdvancedReference, + initial_params: &AdvancedReference, + mut f: F, +) { + let mut p = initial_params.clone(); + f(&mut p); + let sampler = Sampler::from_refblock(prev.clone(), &p); + let d = View::diff_sampler(view, &sampler); + if d < *diff { + *diff = d; + *params = p; + } +} + pub fn encode_block(view: View<&Frame>, prev: View<&Frame>, config: &EncodeConfig) -> Block { let (diff, refblock) = if view.area() > config.max_diff_area { ( @@ -33,7 +55,9 @@ pub fn encode_block(view: View<&Frame>, prev: View<&Frame>, config: &EncodeConfi }, ) } else { - match config.mode { + let weight = importance(&view).max(0.5); + let irrelevance = config.weight_factor / weight; + let (diff, refblock) = match config.mode { EncodeMode::Trivial => ( View::diff(&view, &prev) / view.area() as f64, Block::Reference { @@ -41,87 +65,95 @@ pub fn encode_block(view: View<&Frame>, prev: View<&Frame>, config: &EncodeConfi }, ), EncodeMode::Default => { - let mut best_diff = f64::INFINITY; - let mut best_translation = Vec2::<isize>::ZERO; + let mut diff = f64::INFINITY; + let mut translation = Vec2::<isize>::ZERO; const OFFSETS: &[isize] = &[-64, -32, -16, -8, -4, -2, -1, 0, 1, 2, 4, 8, 16, 32, 64]; for x in OFFSETS { for y in OFFSETS { - let translation = Vec2 { x: *x, y: *y }; - let diff = View::diff(&view, &prev.offset(translation)); - if diff < best_diff { - best_translation = translation; - best_diff = diff; + let t = Vec2 { x: *x, y: *y }; + let d = View::diff(&view, &prev.offset(t)); + if d < diff { + translation = t; + diff = d; } } } - ( - best_diff, - Block::Reference { - translation: best_translation, - }, - ) + (diff, Block::Reference { translation }) } - EncodeMode::Advanced => { - let mut params = AdvancedReference::default(); - let sampler = Sampler::from_refblock(prev.clone(), ¶ms); + EncodeMode::Fast => { + let mut pm = AdvancedReference::default(); + let sampler = Sampler::from_refblock(prev.clone(), &pm); let mut diff = View::diff_sampler(&view, &sampler); - - loop { - pub fn pk<F: FnMut(&mut AdvancedReference) -> ()>( - view: &View<&Frame>, - prev: &View<&Frame>, - diff: &mut f64, - params: &mut AdvancedReference, - mut f: F, - ) { - let mut p = params.clone(); - f(&mut p); - let d = View::diff_sampler(view, &Sampler::from_refblock(prev.clone(), &p)); - if d < *diff { - *diff = d; - *params = p; + if diff - irrelevance < config.ref_thres { + (diff, Block::REFZERO) + } else { + loop { + let (mut d, mut p) = (diff, pm.clone()); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 2); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 2); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 2); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 2); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 1); + if d >= diff { + break (diff, Block::AdvancedReference(pm)); } + diff = d; + pm = p; } + } + } + EncodeMode::Advanced => { + let mut pm = AdvancedReference::default(); + let sampler = Sampler::from_refblock(prev.clone(), &pm); + let mut diff = View::diff_sampler(&view, &sampler); + if diff - irrelevance < config.ref_thres { + (diff, Block::REFZERO) + } else { + loop { + let (mut d, mut p) = (diff, pm.clone()); - let (mut d, mut p) = (diff, params.clone()); - - pk(&view, &prev, &mut d, &mut p, |p| p.translation.x += 4); - pk(&view, &prev, &mut d, &mut p, |p| p.translation.x -= 4); - pk(&view, &prev, &mut d, &mut p, |p| p.translation.y += 4); - pk(&view, &prev, &mut d, &mut p, |p| p.translation.y -= 4); - pk(&view, &prev, &mut d, &mut p, |p| p.translation.x += 2); - pk(&view, &prev, &mut d, &mut p, |p| p.translation.x -= 2); - pk(&view, &prev, &mut d, &mut p, |p| p.translation.y += 2); - pk(&view, &prev, &mut d, &mut p, |p| p.translation.y -= 2); - pk(&view, &prev, &mut d, &mut p, |p| p.translation.x += 1); - pk(&view, &prev, &mut d, &mut p, |p| p.translation.x -= 1); - pk(&view, &prev, &mut d, &mut p, |p| p.translation.y += 1); - pk(&view, &prev, &mut d, &mut p, |p| p.translation.y -= 1); - - pk(&view, &prev, &mut d, &mut p, |p| p.value_scale += 1); - pk(&view, &prev, &mut d, &mut p, |p| p.value_scale -= 1); - - pk(&view, &prev, &mut d, &mut p, |p| p.transform.a -= 1); - pk(&view, &prev, &mut d, &mut p, |p| p.transform.a += 1); - pk(&view, &prev, &mut d, &mut p, |p| p.transform.b -= 1); - pk(&view, &prev, &mut d, &mut p, |p| p.transform.b += 1); - pk(&view, &prev, &mut d, &mut p, |p| p.transform.c -= 1); - pk(&view, &prev, &mut d, &mut p, |p| p.transform.c += 1); - pk(&view, &prev, &mut d, &mut p, |p| p.transform.d -= 1); - pk(&view, &prev, &mut d, &mut p, |p| p.transform.d += 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 4); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 4); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 4); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 4); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 2); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 2); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 2); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 2); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 1); + if config.do_value_scale { + // pk(&view, &prev, &mut d, &mut p, &pm, |p| p.value_scale += 1); + // pk(&view, &prev, &mut d, &mut p, &pm, |p| p.value_scale -= 1); + } + if config.do_matrix_transform { + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.a -= 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.a += 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.b -= 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.b += 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.c -= 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.c += 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.d -= 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.d += 1); + } - debug!("{diff} -> {d}"); + if d >= diff { + break (diff, Block::AdvancedReference(pm)); + } - if d >= diff { - break (diff, Block::AdvancedReference(params)); + diff = d; + pm = p; } - - diff = d; - params = p; } } - } + }; + (diff - irrelevance, refblock) }; if diff < config.ref_thres { refblock @@ -136,18 +168,23 @@ pub fn encode_block(view: View<&Frame>, prev: View<&Frame>, config: &EncodeConfi let config = unsafe { std::mem::transmute::<_, &'static EncodeConfig>(config) }; // only bother to do multithreading, when the block is big. - let (a, b) = if view.size.x > 64 { + let (a, b) = // if view.size.x > 64 { both_par( || encode_block(av, ap, config), || encode_block(bv, bp, config), config.max_threads, ) - } else { - (encode_block(av, ap, config), encode_block(bv, bp, config)) - }; + // } else { + // (encode_block(av, ap, config), encode_block(bv, bp, config)) + // } + ; if a.is_literal() && b.is_literal() { Block::Literal(view.pixels()) + } else if a.is_ref_without_translation() && b.is_ref_without_translation() { + Block::Reference { + translation: Vec2::<isize>::ZERO, + } } else { Block::Split(Box::new([a, b])) } diff --git a/evc/src/format/ser.rs b/evc/src/format/ser.rs index 817cafe..995ca75 100644 --- a/evc/src/format/ser.rs +++ b/evc/src/format/ser.rs @@ -103,6 +103,28 @@ impl<T: Ser> Ser for Vec<T> { } } +pub trait ConstSizeSerExt: Sized { + fn write_const_size(&self, sink: &mut impl Write, size: usize) -> anyhow::Result<()>; + fn read_const_size(source: &mut impl Read, size: usize) -> anyhow::Result<Self>; +} +impl<T: Ser> ConstSizeSerExt for Vec<T> { + fn write_const_size(&self, sink: &mut impl Write, size: usize) -> anyhow::Result<()> { + assert_eq!(self.len(), size); + for e in self { + e.write(sink).context("some const-size vec")?; + } + Ok(()) + } + + fn read_const_size(source: &mut impl Read, size: usize) -> anyhow::Result<Self> { + let mut v = vec![]; + for _ in 0..size { + v.push(T::read(source)?) + } + Ok(v) + } +} + impl Ser for u8 { fn write(&self, sink: &mut impl Write) -> anyhow::Result<()> { Ok(sink.write_all(&[*self]).context("write u8")?) diff --git a/evc/src/helpers/pixel.rs b/evc/src/helpers/pixel.rs index 964040d..816d7dc 100644 --- a/evc/src/helpers/pixel.rs +++ b/evc/src/helpers/pixel.rs @@ -27,17 +27,16 @@ impl Pixel { a.r.abs_diff(b.r) as usize, a.r.abs_diff(b.r) as usize, ); + // fast_sqrt(rd * rd + gd * gd + bd * bd) SQRT[rd + gd + bd] } - + #[inline] pub fn average(a: Pixel, b: Pixel) -> Pixel { - //? this functions is broken - // TODO dont loose accuracy Pixel { - r: (a.r >> 1) + (b.r >> 1), - g: (a.g >> 1) + (b.g >> 1), - b: (a.b >> 1) + (b.b >> 1), + r: ((a.r as u16 + b.r as u16) >> 1) as u8, + g: ((a.g as u16 + b.g as u16) >> 1) as u8, + b: ((a.b as u16 + b.b as u16) >> 1) as u8, } } @@ -51,7 +50,7 @@ impl Pixel { } } -const SQRT: [usize; 256 * 3] = gen_sqrt_lookup(); +pub const SQRT: [usize; 256 * 3] = gen_sqrt_lookup(); const fn gen_sqrt_lookup<const N: usize>() -> [usize; N] { let mut arr = [0; N]; @@ -70,3 +69,12 @@ const fn sqrt(x: f32) -> f32 { let a = (a + x / a) * 0.5; a } + +pub fn fast_sqrt(x: usize) -> usize { + let a = 1; + let a = (a + x / (a + 1)) / 2; + let a = (a + x / (a + 1)) / 2; + // let a = (a + x / (a + 1)) / 2; + // let a = (a + x / (a + 1)) / 2; + a +} diff --git a/evc/src/helpers/vector.rs b/evc/src/helpers/vector.rs index f411832..7cb180a 100644 --- a/evc/src/helpers/vector.rs +++ b/evc/src/helpers/vector.rs @@ -31,6 +31,9 @@ impl<T: std::ops::Mul<Output = T> + Copy> Vec2<T> { y: self.y * f, } } + pub fn area(&self) -> T { + self.x * self.y + } } impl Vec2<isize> { diff --git a/evc/src/refsampler.rs b/evc/src/refsampler.rs index d6f2bb1..a9a6d29 100644 --- a/evc/src/refsampler.rs +++ b/evc/src/refsampler.rs @@ -22,7 +22,7 @@ impl<'a> Sampler<'a> { pub fn sample(&self, p: Vec2<f32>) -> Pixel { self.view .sample(self.translation + self.transform.transform(p + self.halfsize) - self.halfsize) - .scale(self.value_scale) + // .scale(self.value_scale) } pub fn from_refblock( view: View<&'a Frame>, |