Skip to content

Commit

Permalink
Add support for colored fonts and SVGs
Browse files Browse the repository at this point in the history
  • Loading branch information
dzhou121 authored and Zoxc committed Jan 4, 2024
1 parent 74e6ea1 commit e182e68
Show file tree
Hide file tree
Showing 7 changed files with 344 additions and 46 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ wgpu = "0.18.0"
euclid = "0.22.7"
fontdue = "0.8.0"
rect_packer = "0.2.1"
cosmic-text = { git = "https://github.com/lapce/cosmic-text", rev = "fdc5165f79bb24c76bfeb335dbfa133094636e25" }
# cosmic-text = { path = "../cosmic-text" }

[dev-dependencies]
png = "0.17.6"
Expand Down
45 changes: 39 additions & 6 deletions src/atlas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@ struct ImageData {
data: Vec<u8>,
}

pub enum AtlasContent {
Mask,
Color,
}

pub struct Atlas {
packer: Packer,
new_data: Vec<ImageData>,
pub atlas_texture: wgpu::Texture,
area_used: i32,
did_clear: bool,
content: AtlasContent,
}

impl Atlas {
Expand Down Expand Up @@ -50,15 +56,37 @@ impl Atlas {
}
}

pub fn new(device: &wgpu::Device) -> Self {
let atlas_texture = device.create_texture(&Self::get_texture_desc());
pub fn new(device: &wgpu::Device, content: AtlasContent) -> Self {
let texture_size = wgpu::Extent3d {
width: Atlas::ATLAS_SIZE,
height: Atlas::ATLAS_SIZE,
depth_or_array_layers: 1,
};
let format = match content {
AtlasContent::Mask => wgpu::TextureFormat::R8Unorm,
AtlasContent::Color => wgpu::TextureFormat::Rgba8Unorm,
};
let desc = wgpu::TextureDescriptor {
size: texture_size,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format,
usage: wgpu::TextureUsages::COPY_SRC
| wgpu::TextureUsages::COPY_DST
| wgpu::TextureUsages::TEXTURE_BINDING,
label: Some("atlas_texture"),
view_formats: &[format],
};
let atlas_texture = device.create_texture(&desc);

Self {
packer: Packer::new(Atlas::get_packer_config()),
new_data: vec![],
atlas_texture,
area_used: 0,
did_clear: false,
content,
}
}

Expand Down Expand Up @@ -102,7 +130,7 @@ impl Atlas {
buffer: &buffer,
layout: wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: Some(sz as u32),
bytes_per_row: Some((sz * 4) as u32),
rows_per_image: None,
},
},
Expand All @@ -121,14 +149,19 @@ impl Atlas {
for data in &self.new_data {
// Pad data to wgpu::COPY_BYTES_PER_ROW_ALIGNMENT
let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as i32;
let padding = (align - data.rect.width % align) % align;
let padded_width = data.rect.width + padding;
let pixels = match self.content {
AtlasContent::Mask => 1,
AtlasContent::Color => 4,
};
let width = data.rect.width * pixels;
let padding = (align - width % align) % align;
let padded_width = width + padding;
let mut padded_data = vec![];
padded_data.reserve((padded_width * data.rect.height) as usize);

let mut i = 0;
for _ in 0..data.rect.height {
for _ in 0..data.rect.width {
for _ in 0..width {
padded_data.push(data.data[i]);
i += 1;
}
Expand Down
117 changes: 105 additions & 12 deletions src/glyphs.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::atlas::Atlas;
use crate::atlas::{Atlas, AtlasContent};
use cosmic_text::{SubpixelBin, SwashContent, SwashImage};
use rect_packer::Rect;
use std::collections::HashMap;

Expand All @@ -8,10 +9,29 @@ pub struct GlyphInfo {
pub metrics: fontdue::Metrics,
}

#[derive(Copy, Clone, Debug)]
pub struct AtlasInfo {
pub rect: Option<Rect>,
pub left: i32,
pub top: i32,
pub colored: bool,
}

pub struct GlyphCache {
pub atlas: Atlas,
pub mask_atlas: Atlas,
pub color_atlas: Atlas,
pub font: fontdue::Font,
info: HashMap<(char, u32), GlyphInfo>,
atlas_infos: HashMap<
(
cosmic_text::fontdb::ID,
u16,
u32,
(SubpixelBin, SubpixelBin),
),
AtlasInfo,
>,
svg_infos: HashMap<Vec<u8>, HashMap<(u32, u32), AtlasInfo>>,
}

impl GlyphCache {
Expand All @@ -22,10 +42,82 @@ impl GlyphCache {
let font = include_bytes!("fonts/Anodina-Regular.ttf") as &[u8];

Self {
atlas: Atlas::new(device),
mask_atlas: Atlas::new(device, AtlasContent::Mask),
color_atlas: Atlas::new(device, AtlasContent::Color),
font: fontdue::Font::from_bytes(font, settings).unwrap(),
info: HashMap::new(),
atlas_infos: HashMap::new(),
svg_infos: HashMap::new(),
}
}

pub fn get_svg_mask(
&mut self,
hash: &[u8],
width: u32,
height: u32,
image: impl FnOnce() -> Vec<u8>,
) -> AtlasInfo {
if !self.svg_infos.contains_key(hash) {
self.svg_infos.insert(hash.to_vec(), HashMap::new());
}

{
let svg_infos = self.svg_infos.get(hash).unwrap();
if let Some(info) = svg_infos.get(&(width, height)) {
return info.clone();
}
}

let data = image();
let rect = self.color_atlas.add_region(&data, width, height);
let info = AtlasInfo {
rect,
left: 0,
top: 0,
colored: true,
};

let svg_infos = self.svg_infos.get_mut(hash).unwrap();
svg_infos.insert((width, height), info.clone());

info
}

pub fn get_glyph_mask<'a>(
&mut self,
font_id: cosmic_text::fontdb::ID,
glyph_id: u16,
size: u32,
subpx: (SubpixelBin, SubpixelBin),
image: impl FnOnce() -> SwashImage,
) -> AtlasInfo {
let key = (font_id, glyph_id, size, subpx);
if let Some(rect) = self.atlas_infos.get(&key) {
return *rect;
}

let image = image();
let rect = match image.content {
SwashContent::Mask => self.mask_atlas.add_region(
&image.data,
image.placement.width,
image.placement.height,
),
SwashContent::SubpixelMask | SwashContent::Color => self.color_atlas.add_region(
&image.data,
image.placement.width,
image.placement.height,
),
};
let info = AtlasInfo {
rect,
left: image.placement.left,
top: image.placement.top,
colored: image.content != SwashContent::Mask,
};
self.atlas_infos.insert(key, info);
info
}

pub fn get_glyph(&mut self, c: char, size: f32) -> GlyphInfo {
Expand All @@ -52,7 +144,7 @@ impl GlyphCache {
*/

let rect =
self.atlas
self.mask_atlas
.add_region(&data, metrics.width as u32, metrics.height as u32);

let info = GlyphInfo { rect, metrics };
Expand All @@ -64,19 +156,20 @@ impl GlyphCache {
}

pub fn update(&mut self, device: &wgpu::Device, encoder: &mut wgpu::CommandEncoder) {
self.atlas.update(device, encoder);
self.mask_atlas.update(device, encoder);
self.color_atlas.update(device, encoder);
}

pub fn create_view(&self) -> wgpu::TextureView {
self.atlas.create_view()
}

pub fn usage(&self) -> f32 {
self.atlas.usage()
pub fn check_usage(&mut self) {
if self.mask_atlas.usage() > 0.7 || self.color_atlas.usage() > 0.7 {
self.clear();
}
}

pub fn clear(&mut self) {
self.info.clear();
self.atlas.clear();
self.mask_atlas.clear();
self.color_atlas.clear();
self.atlas_infos.clear();
}
}
Loading

0 comments on commit e182e68

Please sign in to comment.