mirror of
https://gitlab.gnome.org/World/Authenticator.git
synced 2025-03-04 00:34:40 +01:00
qrcode_paintable: Use an upscaled texture
This is more efficient and draws without gaps in betweek black squares.
This commit is contained in:
parent
7fd6186886
commit
fab729c6be
1 changed files with 38 additions and 40 deletions
|
@ -1,35 +1,22 @@
|
|||
use gtk::{gdk, glib, graphene, prelude::*, subclass::prelude::*};
|
||||
use gtk::{gdk, glib, graphene, gsk, prelude::*, subclass::prelude::*};
|
||||
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub struct QRCodeData {
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
pub items: Vec<Vec<bool>>,
|
||||
pub size: i32,
|
||||
pub items: Vec<bool>,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> From<B> for QRCodeData {
|
||||
fn from(data: B) -> Self {
|
||||
let code = qrencode::QrCode::new(data).unwrap();
|
||||
let items = code
|
||||
.render::<char>()
|
||||
.quiet_zone(false)
|
||||
.module_dimensions(1, 1)
|
||||
.build()
|
||||
.split('\n')
|
||||
.map(|line| {
|
||||
line.chars()
|
||||
.map(|c| !c.is_whitespace())
|
||||
.collect::<Vec<bool>>()
|
||||
})
|
||||
.collect::<Vec<Vec<bool>>>();
|
||||
.to_colors()
|
||||
.iter()
|
||||
.map(|color| matches!(color, qrencode::types::Color::Dark))
|
||||
.collect::<Vec<bool>>();
|
||||
|
||||
let width = items.first().unwrap().len() as i32;
|
||||
let height = items.len() as i32;
|
||||
Self {
|
||||
width,
|
||||
height,
|
||||
items,
|
||||
}
|
||||
let size = code.width() as i32;
|
||||
Self { size, items }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,30 +42,23 @@ mod imp {
|
|||
impl PaintableImpl for QRCodePaintable {
|
||||
fn snapshot(&self, snapshot: &gdk::Snapshot, width: f64, height: f64) {
|
||||
if let Some(ref qrcode) = *self.qrcode.borrow() {
|
||||
let padding_squares = 3.max(qrcode.height / 10);
|
||||
let square_height = height as f32 / (qrcode.height + 2 * padding_squares) as f32;
|
||||
let square_width = width as f32 / (qrcode.width + 2 * padding_squares) as f32;
|
||||
let padding_squares = 3.max(qrcode.size / 10);
|
||||
let square_height = height as f32 / (qrcode.size + 2 * padding_squares) as f32;
|
||||
let square_width = width as f32 / (qrcode.size + 2 * padding_squares) as f32;
|
||||
let padding = square_height * padding_squares as f32;
|
||||
|
||||
let rect = graphene::Rect::new(0.0, 0.0, width as f32, height as f32);
|
||||
snapshot.append_color(&gdk::RGBA::WHITE, &rect);
|
||||
qrcode.items.iter().enumerate().for_each(|(y, line)| {
|
||||
line.iter().enumerate().for_each(|(x, is_dark)| {
|
||||
if *is_dark {
|
||||
let mut black = gdk::RGBA::BLACK;
|
||||
black.set_alpha(0.85);
|
||||
|
||||
let position = graphene::Rect::new(
|
||||
(x as f32) * square_width + padding,
|
||||
(y as f32) * square_height + padding,
|
||||
square_width,
|
||||
square_height,
|
||||
);
|
||||
let inner_rect = graphene::Rect::new(
|
||||
padding,
|
||||
padding,
|
||||
square_width * qrcode.size as f32,
|
||||
square_height * qrcode.size as f32,
|
||||
);
|
||||
|
||||
snapshot.append_color(&black, &position);
|
||||
};
|
||||
});
|
||||
});
|
||||
let texture = texture(qrcode);
|
||||
snapshot.append_scaled_texture(&texture, gsk::ScalingFilter::Nearest, &inner_rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -101,3 +81,21 @@ impl Default for QRCodePaintable {
|
|||
glib::Object::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn texture(qrcode: &QRCodeData) -> gdk::Texture {
|
||||
let size = qrcode.size;
|
||||
|
||||
const G8_SIZE: usize = 1;
|
||||
const WHITE: u8 = 0xff; // #ffffff
|
||||
const BLACK: u8 = 0x24; // #242424
|
||||
|
||||
let bytes: Vec<u8> = qrcode
|
||||
.items
|
||||
.iter()
|
||||
.map(|is_black| if *is_black { BLACK } else { WHITE })
|
||||
.collect();
|
||||
let bytes = glib::Bytes::from_owned(bytes);
|
||||
let stride = G8_SIZE * size as usize;
|
||||
|
||||
gdk::MemoryTexture::new(size, size, gdk::MemoryFormat::G8, &bytes, stride).upcast()
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue