Skip to content
Snippets Groups Projects
Unverified Commit 6753953b authored by John's avatar John Committed by GitHub
Browse files

Merge pull request #181 from StarArawn/optional-subpixel

Added settings to turn on/off subpixel rendering.
parents aa0af014 70c82151
No related branches found
No related tags found
No related merge requests found
...@@ -6,7 +6,13 @@ fn startup( ...@@ -6,7 +6,13 @@ fn startup(
mut font_mapping: ResMut<FontMapping>, mut font_mapping: ResMut<FontMapping>,
asset_server: Res<AssetServer>, asset_server: Res<AssetServer>,
) { ) {
font_mapping.set_default(asset_server.load("roboto.kayak_font")); let font_asset = asset_server.load("roboto.kayak_font");
font_mapping.set_default(font_asset.clone());
// You can force the entire font to use subpixel rendering.
// Note: The subpixel settings on the text widget or render command
// will be ignored if this setting is used.
font_mapping.force_subpixel(&font_asset);
// Camera 2D forces a clear pass in bevy. // Camera 2D forces a clear pass in bevy.
// We do this because our scene is not rendering anything else. // We do this because our scene is not rendering anything else.
......
...@@ -21,6 +21,7 @@ fn my_widget_1_render( ...@@ -21,6 +21,7 @@ fn my_widget_1_render(
content: format!("My number is: {}", my_widget.foo), content: format!("My number is: {}", my_widget.foo),
alignment: Alignment::Start, alignment: Alignment::Start,
word_wrap: false, word_wrap: false,
subpixel: false,
}), }),
..Default::default() ..Default::default()
} }
......
use bevy::prelude::*; use bevy::prelude::*;
use kayak_ui::prelude::{widgets::*, KStyle, *}; use kayak_ui::prelude::{widgets::*, *};
#[derive(Component, Default, PartialEq, Eq, Clone)] #[derive(Component, Default, PartialEq, Eq, Clone)]
pub struct MyWidgetProps {} pub struct MyWidgetProps {}
......
...@@ -20,15 +20,17 @@ pub fn extract_texts( ...@@ -20,15 +20,17 @@ pub fn extract_texts(
_dpi: f32, _dpi: f32,
) -> Vec<ExtractQuadBundle> { ) -> Vec<ExtractQuadBundle> {
let mut extracted_texts = Vec::new(); let mut extracted_texts = Vec::new();
let (background_color, text_layout, layout, font, properties) = match render_primitive { let (background_color, text_layout, layout, font, properties, subpixel) = match render_primitive
{
RenderPrimitive::Text { RenderPrimitive::Text {
color, color,
text_layout, text_layout,
layout, layout,
font, font,
properties, properties,
subpixel,
.. ..
} => (color, text_layout, layout, font, *properties), } => (color, text_layout, layout, font, *properties, subpixel),
_ => panic!(""), _ => panic!(""),
}; };
...@@ -40,6 +42,8 @@ pub fn extract_texts( ...@@ -40,6 +42,8 @@ pub fn extract_texts(
} }
}; };
let forced = font_mapping.get_subpixel_forced(&font_handle);
let base_position = Vec2::new(layout.posx, layout.posy + properties.font_size); let base_position = Vec2::new(layout.posx, layout.posy + properties.font_size);
for glyph_rect in text_layout.glyphs() { for glyph_rect in text_layout.glyphs() {
...@@ -60,7 +64,11 @@ pub fn extract_texts( ...@@ -60,7 +64,11 @@ pub fn extract_texts(
vertex_index: 0, vertex_index: 0,
char_id: font.get_char_id(glyph_rect.content).unwrap(), char_id: font.get_char_id(glyph_rect.content).unwrap(),
z_index: layout.z_index, z_index: layout.z_index,
quad_type: UIQuadType::Text, quad_type: if *subpixel || forced {
UIQuadType::TextSubpixel
} else {
UIQuadType::Text
},
type_index: 0, type_index: 0,
border_radius: Corner::default(), border_radius: Corner::default(),
image: None, image: None,
......
use bevy::{ use bevy::{
prelude::{Handle, Resource}, prelude::{Handle, Resource},
utils::HashMap, utils::{HashMap, HashSet},
}; };
use kayak_font::KayakFont; use kayak_font::KayakFont;
// use crate::context::Context; // use crate::context::Context;
/// A resource used to manage fonts for use in a `KayakContext` /// A resource used to manage fonts for use in a `KayakContext`
/// ///
/// # Example /// # Example
/// ///
/// ``` /// ```
/// use bevy::prelude::*; /// use bevy::prelude::*;
/// use bevy_kayak_ui::FontMapping; /// use bevy_kayak_ui::FontMapping;
/// ///
/// fn setup_ui( /// fn setup_ui(
/// # mut commands: Commands, /// # mut commands: Commands,
/// asset_server: Res<AssetServer>, /// asset_server: Res<AssetServer>,
/// mut font_mapping: ResMut<FontMapping> /// mut font_mapping: ResMut<FontMapping>
/// ) { /// ) {
/// # commands.spawn_bundle(UICameraBundle::new()); /// # commands.spawn_bundle(UICameraBundle::new());
/// # /// #
/// font_mapping.set_default(asset_server.load("roboto.kayak_font")); /// font_mapping.set_default(asset_server.load("roboto.kayak_font"));
/// // ... /// // ...
/// # /// #
/// # let context = BevyContext::new(|context| { /// # let context = BevyContext::new(|context| {
/// # render! { /// # render! {
/// # <App> /// # <App>
/// # <Text content={"Hello World!".to_string()} /> /// # <Text content={"Hello World!".to_string()} />
/// # </App> /// # </App>
/// # } /// # }
/// # }); /// # });
/// # /// #
/// # commands.insert_resource(context); /// # commands.insert_resource(context);
/// } /// }
/// ``` /// ```
#[derive(Resource, Default)] #[derive(Resource, Default)]
pub struct FontMapping { pub struct FontMapping {
font_ids: HashMap<Handle<KayakFont>, String>, font_ids: HashMap<Handle<KayakFont>, String>,
font_handles: HashMap<String, Handle<KayakFont>>, font_handles: HashMap<String, Handle<KayakFont>>,
new_fonts: Vec<String>, new_fonts: Vec<String>,
} subpixel: HashSet<Handle<KayakFont>>,
}
impl FontMapping {
/// Add a `KayakFont` to be tracked impl FontMapping {
pub fn add(&mut self, key: impl Into<String>, handle: Handle<KayakFont>) { /// Add a `KayakFont` to be tracked
let key = key.into(); pub fn add(&mut self, key: impl Into<String>, handle: Handle<KayakFont>) {
if !self.font_ids.contains_key(&handle) { let key = key.into();
self.font_ids.insert(handle.clone(), key.clone()); if !self.font_ids.contains_key(&handle) {
self.new_fonts.push(key.clone()); self.font_ids.insert(handle.clone(), key.clone());
self.font_handles.insert(key, handle); self.new_fonts.push(key.clone());
} self.font_handles.insert(key, handle);
} }
}
/// Set a default `KayakFont`
pub fn set_default(&mut self, handle: Handle<KayakFont>) { /// Set a default `KayakFont`
self.add(crate::DEFAULT_FONT, handle); pub fn set_default(&mut self, handle: Handle<KayakFont>) {
} self.add(crate::DEFAULT_FONT, handle);
}
pub(crate) fn mark_all_as_new(&mut self) {
self.new_fonts.extend(self.font_handles.keys().cloned()); pub(crate) fn mark_all_as_new(&mut self) {
} self.new_fonts.extend(self.font_handles.keys().cloned());
}
/// Get the handle for the given font name
pub fn get_handle(&self, id: String) -> Option<Handle<KayakFont>> { /// Get the handle for the given font name
self.font_handles.get(&id).cloned() pub fn get_handle(&self, id: String) -> Option<Handle<KayakFont>> {
} self.font_handles.get(&id).cloned()
}
/// Get the font name for the given handle
pub fn get(&self, font: &Handle<KayakFont>) -> Option<String> { /// Get the font name for the given handle
self.font_ids.get(font).cloned() pub fn get(&self, font: &Handle<KayakFont>) -> Option<String> {
} self.font_ids.get(font).cloned()
}
// pub(crate) fn add_loaded_to_kayak(
// &mut self, /// Forces any text render commands to use subpixel font rendering for this specific font asset.
// fonts: &Res<Assets<KayakFont>>, pub fn force_subpixel(&mut self, font: &Handle<KayakFont>) {
// context: &Context, self.subpixel.insert(font.clone_weak());
// ) { }
// if let Ok(mut kayak_context) = context.kayak_context.write() {
// let new_fonts = self.new_fonts.drain(..).collect::<Vec<_>>(); /// Turns off the forced subpixel rendering mode for this font.
// for font_key in new_fonts { pub fn disable_subpixel(&mut self, font: &Handle<KayakFont>) {
// let font_handle = self.font_handles.get(&font_key).unwrap(); self.subpixel.remove(font);
// if let Some(font) = fonts.get(font_handle) { }
// kayak_context.set_asset(font_key, font.clone());
// } else { pub fn get_subpixel_forced(&self, font: &Handle<KayakFont>) -> bool {
// self.new_fonts.push(font_key); self.subpixel.contains(font)
// } }
// }
// } // pub(crate) fn add_loaded_to_kayak(
// } // &mut self,
} // fonts: &Res<Assets<KayakFont>>,
// context: &Context,
// ) {
// if let Ok(mut kayak_context) = context.kayak_context.write() {
// let new_fonts = self.new_fonts.drain(..).collect::<Vec<_>>();
// for font_key in new_fonts {
// let font_handle = self.font_handles.get(&font_key).unwrap();
// if let Some(font) = fonts.get(font_handle) {
// kayak_context.set_asset(font_key, font.clone());
// } else {
// self.new_fonts.push(font_key);
// }
// }
// }
// }
}
...@@ -333,6 +333,7 @@ pub struct ExtractQuadBundle { ...@@ -333,6 +333,7 @@ pub struct ExtractQuadBundle {
pub enum UIQuadType { pub enum UIQuadType {
Quad, Quad,
Text, Text,
TextSubpixel,
Image, Image,
Clip, Clip,
} }
...@@ -416,18 +417,24 @@ pub fn prepare_quads( ...@@ -416,18 +417,24 @@ pub fn prepare_quads(
_padding_2: 0, _padding_2: 0,
_padding_3: 0, _padding_3: 0,
}); });
let text_type_offset = sprite_meta.types_buffer.push(QuadType { let text_sub_pixel_type_offset = sprite_meta.types_buffer.push(QuadType {
t: 1, t: 1,
_padding_1: 0, _padding_1: 0,
_padding_2: 0, _padding_2: 0,
_padding_3: 0, _padding_3: 0,
}); });
let image_type_offset = sprite_meta.types_buffer.push(QuadType { let text_type_offset = sprite_meta.types_buffer.push(QuadType {
t: 2, t: 2,
_padding_1: 0, _padding_1: 0,
_padding_2: 0, _padding_2: 0,
_padding_3: 0, _padding_3: 0,
}); });
let image_type_offset = sprite_meta.types_buffer.push(QuadType {
t: 3,
_padding_1: 0,
_padding_2: 0,
_padding_3: 0,
});
sprite_meta sprite_meta
.types_buffer .types_buffer
...@@ -450,6 +457,7 @@ pub fn prepare_quads( ...@@ -450,6 +457,7 @@ pub fn prepare_quads(
match extracted_sprite.quad_type { match extracted_sprite.quad_type {
UIQuadType::Quad => extracted_sprite.type_index = quad_type_offset, UIQuadType::Quad => extracted_sprite.type_index = quad_type_offset,
UIQuadType::Text => extracted_sprite.type_index = text_type_offset, UIQuadType::Text => extracted_sprite.type_index = text_type_offset,
UIQuadType::TextSubpixel => extracted_sprite.type_index = text_sub_pixel_type_offset,
UIQuadType::Image => extracted_sprite.type_index = image_type_offset, UIQuadType::Image => extracted_sprite.type_index = image_type_offset,
UIQuadType::Clip => {} UIQuadType::Clip => {}
}; };
......
...@@ -110,6 +110,16 @@ fn fragment(in: VertexOutput) -> @location(0) vec4<f32> { ...@@ -110,6 +110,16 @@ fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
return vec4(red * in.color.r, green * in.color.g, blue * in.color.b, alpha); return vec4(red * in.color.r, green * in.color.g, blue * in.color.b, alpha);
} }
if quad_type.t == 2 { if quad_type.t == 2 {
var px_range = 2.5;
var tex_dimensions = textureDimensions(font_texture);
var msdf_unit = vec2<f32>(px_range, px_range) / vec2<f32>(f32(tex_dimensions.x), f32(tex_dimensions.y));
var x = textureSample(font_texture, font_sampler, vec2<f32>(in.uv.x, 1.0 - in.uv.y), i32(in.uv.z));
var v = max(min(x.r, x.g), min(max(x.r, x.g), x.b));
var sig_dist = (v - 0.5) * dot(msdf_unit, 0.5 / fwidth(vec2<f32>(in.uv.x, 1.0 - in.uv.y)));
var a = clamp(sig_dist + 0.5, 0.0, 1.0);
return vec4<f32>(in.color.rgb, a);
}
if quad_type.t == 3 {
var bs = min(in.border_radius, min(in.size.x, in.size.y)); var bs = min(in.border_radius, min(in.size.x, in.size.y));
var mask = sdRoundBox( var mask = sdRoundBox(
in.pos.xy * 2.0 - (in.size.xy), in.pos.xy * 2.0 - (in.size.xy),
......
...@@ -29,6 +29,7 @@ pub enum RenderPrimitive { ...@@ -29,6 +29,7 @@ pub enum RenderPrimitive {
layout: Rect, layout: Rect,
properties: TextProperties, properties: TextProperties,
word_wrap: bool, word_wrap: bool,
subpixel: bool,
}, },
Image { Image {
border_radius: Corner<f32>, border_radius: Corner<f32>,
...@@ -111,6 +112,7 @@ impl From<&KStyle> for RenderPrimitive { ...@@ -111,6 +112,7 @@ impl From<&KStyle> for RenderPrimitive {
content, content,
alignment, alignment,
word_wrap, word_wrap,
subpixel,
} => Self::Text { } => Self::Text {
color: style.color.resolve(), color: style.color.resolve(),
content, content,
...@@ -124,6 +126,7 @@ impl From<&KStyle> for RenderPrimitive { ...@@ -124,6 +126,7 @@ impl From<&KStyle> for RenderPrimitive {
..Default::default() ..Default::default()
}, },
word_wrap, word_wrap,
subpixel,
}, },
RenderCommand::Image { handle } => Self::Image { RenderCommand::Image { handle } => Self::Image {
border_radius: style.border_radius.resolve(), border_radius: style.border_radius.resolve(),
......
...@@ -17,6 +17,7 @@ pub enum RenderCommand { ...@@ -17,6 +17,7 @@ pub enum RenderCommand {
content: String, content: String,
alignment: Alignment, alignment: Alignment,
word_wrap: bool, word_wrap: bool,
subpixel: bool,
}, },
Image { Image {
handle: Handle<Image>, handle: Handle<Image>,
......
...@@ -31,6 +31,8 @@ pub struct TextProps { ...@@ -31,6 +31,8 @@ pub struct TextProps {
/// Basic word wrapping. /// Basic word wrapping.
/// Defautls to true /// Defautls to true
pub word_wrap: bool, pub word_wrap: bool,
/// Enables subpixel rendering of text. This is useful on smaller low-dpi screens.
pub subpixel: bool,
} }
impl Default for TextProps { impl Default for TextProps {
...@@ -43,6 +45,7 @@ impl Default for TextProps { ...@@ -43,6 +45,7 @@ impl Default for TextProps {
size: -1.0, size: -1.0,
alignment: Alignment::Start, alignment: Alignment::Start,
word_wrap: true, word_wrap: true,
subpixel: false,
} }
} }
} }
...@@ -82,6 +85,7 @@ pub fn text_render( ...@@ -82,6 +85,7 @@ pub fn text_render(
content: text.content.clone(), content: text.content.clone(),
alignment: text.alignment, alignment: text.alignment,
word_wrap: text.word_wrap, word_wrap: text.word_wrap,
subpixel: text.subpixel,
}), }),
font: if let Some(ref font) = text.font { font: if let Some(ref font) = text.font {
StyleProp::Value(font.clone()) StyleProp::Value(font.clone())
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment