#[cfg(not(feature = "std"))]
use alloc::string::ToString;
use alloc::{boxed::Box, collections::btree_map::BTreeMap, string::String, vec::Vec};
use core::{
fmt,
hash::{Hash, Hasher},
sync::atomic::{AtomicU32, AtomicUsize, Ordering as AtomicOrdering},
};
use azul_css::{
format_rust_code::GetHash,
props::basic::{
pixel::DEFAULT_FONT_SIZE, ColorU, FloatValue, FontRef, LayoutRect, LayoutSize,
StyleFontFamily, StyleFontFamilyVec, StyleFontSize,
},
system::SystemStyle,
AzString, F32Vec, LayoutDebugMessage, OptionI32, StringVec, U16Vec, U32Vec, U8Vec,
};
use rust_fontconfig::FcFontCache;
pub use crate::callbacks::{
CoreImageCallback, CoreRenderImageCallback, CoreRenderImageCallbackType,
};
use crate::{
callbacks::IFrameCallback,
dom::{DomId, NodeData, NodeType},
geom::{LogicalPosition, LogicalRect, LogicalSize},
gl::{OptionGlContextPtr, Texture},
hit_test::DocumentId,
id::NodeId,
prop_cache::CssPropertyCache,
refany::RefAny,
styled_dom::{
NodeHierarchyItemId, StyleFontFamiliesHash, StyleFontFamilyHash, StyledDom, StyledNodeState,
},
ui_solver::GlyphInstance,
window::OptionChar,
FastBTreeSet, FastHashMap,
};
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub struct DpiScaleFactor {
pub inner: FloatValue,
}
impl DpiScaleFactor {
pub fn new(f: f32) -> Self {
Self {
inner: FloatValue::new(f),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub enum AppTerminationBehavior {
ReturnToMain,
RunForever,
EndProcess,
}
impl Default for AppTerminationBehavior {
fn default() -> Self {
AppTerminationBehavior::EndProcess
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub struct NamedFont {
pub name: AzString,
pub bytes: U8Vec,
}
impl_option!(
NamedFont,
OptionNamedFont,
copy = false,
[Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
);
impl NamedFont {
pub fn new(name: AzString, bytes: U8Vec) -> Self {
Self { name, bytes }
}
}
impl_vec!(NamedFont, NamedFontVec, NamedFontVecDestructor, NamedFontVecDestructorType, NamedFontVecSlice, OptionNamedFont);
impl_vec_mut!(NamedFont, NamedFontVec);
impl_vec_debug!(NamedFont, NamedFontVec);
impl_vec_partialeq!(NamedFont, NamedFontVec);
impl_vec_eq!(NamedFont, NamedFontVec);
impl_vec_partialord!(NamedFont, NamedFontVec);
impl_vec_ord!(NamedFont, NamedFontVec);
impl_vec_hash!(NamedFont, NamedFontVec);
impl_vec_clone!(NamedFont, NamedFontVec, NamedFontVecDestructor);
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C, u8)]
pub enum FontLoadingConfig {
LoadAllSystemFonts,
LoadOnlyFamilies(StringVec),
BundledFontsOnly,
}
impl Default for FontLoadingConfig {
fn default() -> Self {
FontLoadingConfig::LoadAllSystemFonts
}
}
#[derive(Debug, Clone, Default)]
#[repr(C)]
pub struct CssMockEnvironment {
pub os: azul_css::dynamic_selector::OptionOsCondition,
pub os_version: azul_css::dynamic_selector::OptionOsVersion,
pub desktop_env: azul_css::dynamic_selector::OptionLinuxDesktopEnv,
pub theme: azul_css::dynamic_selector::OptionThemeCondition,
pub language: azul_css::OptionString,
pub prefers_reduced_motion: azul_css::OptionBool,
pub prefers_high_contrast: azul_css::OptionBool,
pub viewport_width: azul_css::OptionF32,
pub viewport_height: azul_css::OptionF32,
}
impl CssMockEnvironment {
pub fn linux() -> Self {
Self {
os: azul_css::dynamic_selector::OptionOsCondition::Some(azul_css::dynamic_selector::OsCondition::Linux),
..Default::default()
}
}
pub fn windows() -> Self {
Self {
os: azul_css::dynamic_selector::OptionOsCondition::Some(azul_css::dynamic_selector::OsCondition::Windows),
..Default::default()
}
}
pub fn macos() -> Self {
Self {
os: azul_css::dynamic_selector::OptionOsCondition::Some(azul_css::dynamic_selector::OsCondition::MacOS),
..Default::default()
}
}
pub fn dark_theme() -> Self {
Self {
theme: azul_css::dynamic_selector::OptionThemeCondition::Some(azul_css::dynamic_selector::ThemeCondition::Dark),
..Default::default()
}
}
pub fn light_theme() -> Self {
Self {
theme: azul_css::dynamic_selector::OptionThemeCondition::Some(azul_css::dynamic_selector::ThemeCondition::Light),
..Default::default()
}
}
pub fn apply_to(&self, ctx: &mut azul_css::dynamic_selector::DynamicSelectorContext) {
if let azul_css::dynamic_selector::OptionOsCondition::Some(os) = self.os {
ctx.os = os;
}
if let azul_css::dynamic_selector::OptionOsVersion::Some(os_version) = self.os_version {
ctx.os_version = os_version;
}
if let azul_css::dynamic_selector::OptionLinuxDesktopEnv::Some(de) = self.desktop_env {
ctx.desktop_env = azul_css::dynamic_selector::OptionLinuxDesktopEnv::Some(de);
}
if let azul_css::dynamic_selector::OptionThemeCondition::Some(ref theme) = self.theme {
ctx.theme = theme.clone();
}
if let azul_css::OptionString::Some(ref lang) = self.language {
ctx.language = lang.clone();
}
if let azul_css::OptionBool::Some(reduced) = self.prefers_reduced_motion {
ctx.prefers_reduced_motion = if reduced {
azul_css::dynamic_selector::BoolCondition::True
} else {
azul_css::dynamic_selector::BoolCondition::False
};
}
if let azul_css::OptionBool::Some(high_contrast) = self.prefers_high_contrast {
ctx.prefers_high_contrast = if high_contrast {
azul_css::dynamic_selector::BoolCondition::True
} else {
azul_css::dynamic_selector::BoolCondition::False
};
}
if let azul_css::OptionF32::Some(w) = self.viewport_width {
ctx.viewport_width = w;
}
if let azul_css::OptionF32::Some(h) = self.viewport_height {
ctx.viewport_height = h;
}
}
}
impl_option!(
CssMockEnvironment,
OptionCssMockEnvironment,
copy = false,
[Debug, Clone]
);
#[derive(Debug, Clone)]
#[repr(C)]
pub struct AppConfig {
pub log_level: AppLogLevel,
pub enable_visual_panic_hook: bool,
pub enable_logging_on_panic: bool,
pub enable_tab_navigation: bool,
pub termination_behavior: AppTerminationBehavior,
pub icon_provider: crate::icon::IconProviderHandle,
pub bundled_fonts: NamedFontVec,
pub font_loading: FontLoadingConfig,
pub mock_css_environment: OptionCssMockEnvironment,
pub system_style: SystemStyle,
}
impl AppConfig {
pub fn create() -> Self {
let log_level = AppLogLevel::Error;
let icon_provider = crate::icon::IconProviderHandle::new();
let bundled_fonts = NamedFontVec::from_const_slice(&[]);
let font_loading = FontLoadingConfig::default();
let system_style = SystemStyle::detect();
Self {
log_level,
enable_visual_panic_hook: false,
enable_logging_on_panic: true,
enable_tab_navigation: true,
termination_behavior: AppTerminationBehavior::default(),
icon_provider,
bundled_fonts,
font_loading,
mock_css_environment: OptionCssMockEnvironment::None,
system_style,
}
}
pub fn with_mock_environment(mut self, env: CssMockEnvironment) -> Self {
self.mock_css_environment = OptionCssMockEnvironment::Some(env);
self
}
}
impl Default for AppConfig {
fn default() -> Self {
Self::create()
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub enum AppLogLevel {
Off,
Error,
Warn,
Info,
Debug,
Trace,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PrimitiveFlags {
pub is_backface_visible: bool,
pub is_scrollbar_container: bool,
pub is_scrollbar_thumb: bool,
pub prefer_compositor_surface: bool,
pub supports_external_compositor_surface: bool,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub struct ImageDescriptor {
pub format: RawImageFormat,
pub width: usize,
pub height: usize,
pub stride: OptionI32,
pub offset: i32,
pub flags: ImageDescriptorFlags,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub struct ImageDescriptorFlags {
pub is_opaque: bool,
pub allow_mipmaps: bool,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct IdNamespace(pub u32);
impl ::core::fmt::Display for IdNamespace {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "IdNamespace({})", self.0)
}
}
impl ::core::fmt::Debug for IdNamespace {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub enum RawImageFormat {
R8,
RG8,
RGB8,
RGBA8,
R16,
RG16,
RGB16,
RGBA16,
BGR8,
BGRA8,
RGBF32,
RGBAF32,
}
static IMAGE_KEY: AtomicU32 = AtomicU32::new(1);
static FONT_KEY: AtomicU32 = AtomicU32::new(0);
static FONT_INSTANCE_KEY: AtomicU32 = AtomicU32::new(0);
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ImageKey {
pub namespace: IdNamespace,
pub key: u32,
}
impl ImageKey {
pub const DUMMY: Self = Self {
namespace: IdNamespace(0),
key: 0,
};
pub fn unique(render_api_namespace: IdNamespace) -> Self {
Self {
namespace: render_api_namespace,
key: IMAGE_KEY.fetch_add(1, AtomicOrdering::SeqCst),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FontKey {
pub namespace: IdNamespace,
pub key: u32,
}
impl FontKey {
pub fn unique(render_api_namespace: IdNamespace) -> Self {
Self {
namespace: render_api_namespace,
key: FONT_KEY.fetch_add(1, AtomicOrdering::SeqCst),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FontInstanceKey {
pub namespace: IdNamespace,
pub key: u32,
}
impl FontInstanceKey {
pub fn unique(render_api_namespace: IdNamespace) -> Self {
Self {
namespace: render_api_namespace,
key: FONT_INSTANCE_KEY.fetch_add(1, AtomicOrdering::SeqCst),
}
}
}
#[derive(Debug)]
pub enum DecodedImage {
NullImage {
width: usize,
height: usize,
format: RawImageFormat,
tag: Vec<u8>,
},
Gl(Texture),
Raw((ImageDescriptor, ImageData)),
Callback(CoreImageCallback),
}
#[derive(Debug)]
#[repr(C)]
pub struct ImageRef {
pub data: *const DecodedImage,
pub copies: *const AtomicUsize,
pub run_destructor: bool,
}
impl ImageRef {
pub fn get_hash(&self) -> ImageRefHash {
image_ref_get_hash(self)
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Hash, Ord, Eq)]
#[repr(C)]
pub struct ImageRefHash {
pub inner: usize,
}
impl_option!(
ImageRef,
OptionImageRef,
copy = false,
[Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
);
impl ImageRef {
pub fn into_inner(self) -> Option<DecodedImage> {
unsafe {
if self.copies.as_ref().map(|m| m.load(AtomicOrdering::SeqCst)) == Some(1) {
let data = Box::from_raw(self.data as *mut DecodedImage);
let _ = Box::from_raw(self.copies as *mut AtomicUsize);
core::mem::forget(self); Some(*data)
} else {
None
}
}
}
pub fn get_data<'a>(&'a self) -> &'a DecodedImage {
unsafe { &*self.data }
}
pub fn get_image_callback<'a>(&'a self) -> Option<&'a CoreImageCallback> {
if unsafe { self.copies.as_ref().map(|m| m.load(AtomicOrdering::SeqCst)) != Some(1) } {
return None; }
match unsafe { &*self.data } {
DecodedImage::Callback(gl_texture_callback) => Some(gl_texture_callback),
_ => None,
}
}
pub fn get_image_callback_mut<'a>(&'a mut self) -> Option<&'a mut CoreImageCallback> {
if unsafe { self.copies.as_ref().map(|m| m.load(AtomicOrdering::SeqCst)) != Some(1) } {
return None; }
match unsafe { &mut *(self.data as *mut DecodedImage) } {
DecodedImage::Callback(gl_texture_callback) => Some(gl_texture_callback),
_ => None,
}
}
pub fn deep_copy(&self) -> Self {
let new_data = match self.get_data() {
DecodedImage::NullImage {
width,
height,
format,
tag,
} => DecodedImage::NullImage {
width: *width,
height: *height,
format: *format,
tag: tag.clone(),
},
DecodedImage::Gl(tex) => DecodedImage::NullImage {
width: tex.size.width as usize,
height: tex.size.height as usize,
format: tex.format,
tag: Vec::new(),
},
DecodedImage::Raw((descriptor, data)) => {
DecodedImage::Raw((descriptor.clone(), data.clone()))
}
DecodedImage::Callback(cb) => DecodedImage::Callback(cb.clone()),
};
Self::new(new_data)
}
pub fn is_null_image(&self) -> bool {
match self.get_data() {
DecodedImage::NullImage { .. } => true,
_ => false,
}
}
pub fn is_gl_texture(&self) -> bool {
match self.get_data() {
DecodedImage::Gl(_) => true,
_ => false,
}
}
pub fn is_raw_image(&self) -> bool {
match self.get_data() {
DecodedImage::Raw((_, _)) => true,
_ => false,
}
}
pub fn is_callback(&self) -> bool {
match self.get_data() {
DecodedImage::Callback(_) => true,
_ => false,
}
}
pub fn get_rawimage(&self) -> Option<RawImage> {
match self.get_data() {
DecodedImage::Raw((image_descriptor, image_data)) => Some(RawImage {
pixels: match image_data {
ImageData::Raw(shared_data) => {
let data_clone = shared_data.clone();
if let Some(u8vec) = data_clone.into_inner() {
RawImageData::U8(u8vec)
} else {
RawImageData::U8(shared_data.as_ref().to_vec().into())
}
}
ImageData::External(_) => return None,
},
width: image_descriptor.width,
height: image_descriptor.height,
premultiplied_alpha: true,
data_format: image_descriptor.format,
tag: Vec::new().into(),
}),
_ => None,
}
}
pub fn get_bytes(&self) -> Option<&[u8]> {
match self.get_data() {
DecodedImage::Raw((_, image_data)) => match image_data {
ImageData::Raw(shared_data) => Some(shared_data.as_ref()),
ImageData::External(_) => None,
},
_ => None,
}
}
pub fn get_bytes_ptr(&self) -> *const u8 {
match self.get_data() {
DecodedImage::Raw((_, image_data)) => match image_data {
ImageData::Raw(shared_data) => shared_data.as_ptr(),
ImageData::External(_) => core::ptr::null(),
},
_ => core::ptr::null(),
}
}
pub fn get_size(&self) -> LogicalSize {
match self.get_data() {
DecodedImage::NullImage { width, height, .. } => {
LogicalSize::new(*width as f32, *height as f32)
}
DecodedImage::Gl(tex) => {
LogicalSize::new(tex.size.width as f32, tex.size.height as f32)
}
DecodedImage::Raw((image_descriptor, _)) => LogicalSize::new(
image_descriptor.width as f32,
image_descriptor.height as f32,
),
DecodedImage::Callback(_) => LogicalSize::new(0.0, 0.0),
}
}
pub fn null_image(width: usize, height: usize, format: RawImageFormat, tag: Vec<u8>) -> Self {
Self::new(DecodedImage::NullImage {
width,
height,
format,
tag,
})
}
pub fn callback<C: Into<CoreRenderImageCallback>>(callback: C, data: RefAny) -> Self {
Self::new(DecodedImage::Callback(CoreImageCallback {
callback: callback.into(),
refany: data,
}))
}
pub fn new_rawimage(image_data: RawImage) -> Option<Self> {
let (image_data, image_descriptor) = image_data.into_loaded_image_source()?;
Some(Self::new(DecodedImage::Raw((image_descriptor, image_data))))
}
pub fn new_gltexture(texture: Texture) -> Self {
Self::new(DecodedImage::Gl(texture))
}
fn new(data: DecodedImage) -> Self {
Self {
data: Box::into_raw(Box::new(data)),
copies: Box::into_raw(Box::new(AtomicUsize::new(1))),
run_destructor: true,
}
}
}
unsafe impl Send for ImageRef {}
unsafe impl Sync for ImageRef {}
impl PartialEq for ImageRef {
fn eq(&self, rhs: &Self) -> bool {
self.data as usize == rhs.data as usize
}
}
impl PartialOrd for ImageRef {
fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> {
Some((self.data as usize).cmp(&(other.data as usize)))
}
}
impl Ord for ImageRef {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
let self_data = self.data as usize;
let other_data = other.data as usize;
self_data.cmp(&other_data)
}
}
impl Eq for ImageRef {}
impl Hash for ImageRef {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
let self_data = self.data as usize;
self_data.hash(state)
}
}
impl Clone for ImageRef {
fn clone(&self) -> Self {
unsafe {
self.copies
.as_ref()
.map(|m| m.fetch_add(1, AtomicOrdering::SeqCst));
}
Self {
data: self.data, copies: self.copies, run_destructor: true,
}
}
}
impl Drop for ImageRef {
fn drop(&mut self) {
self.run_destructor = false;
unsafe {
let copies = unsafe { (*self.copies).fetch_sub(1, AtomicOrdering::SeqCst) };
if copies == 1 {
let _ = Box::from_raw(self.data as *mut DecodedImage);
let _ = Box::from_raw(self.copies as *mut AtomicUsize);
}
}
}
}
pub fn image_ref_get_hash(ir: &ImageRef) -> ImageRefHash {
ImageRefHash {
inner: ir.data as usize,
}
}
pub fn image_ref_hash_to_image_key(hash: ImageRefHash, namespace: IdNamespace) -> ImageKey {
ImageKey {
namespace,
key: hash.inner as u32,
}
}
pub fn font_ref_get_hash(fr: &FontRef) -> u64 {
fr.get_hash()
}
#[derive(Debug)]
pub struct ImageCache {
pub image_id_map: FastHashMap<AzString, ImageRef>,
}
impl Default for ImageCache {
fn default() -> Self {
Self {
image_id_map: FastHashMap::default(),
}
}
}
impl ImageCache {
pub fn new() -> Self {
Self::default()
}
pub fn add_css_image_id(&mut self, css_id: AzString, image: ImageRef) {
self.image_id_map.insert(css_id, image);
}
pub fn get_css_image_id(&self, css_id: &AzString) -> Option<&ImageRef> {
self.image_id_map.get(css_id)
}
pub fn delete_css_image_id(&mut self, css_id: &AzString) {
self.image_id_map.remove(css_id);
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ImageType {
Background,
Content,
ClipMask,
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct ResolvedImage {
pub key: ImageKey,
pub descriptor: ImageDescriptor,
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct TextExclusionArea {
pub rect: LogicalRect,
pub side: ExclusionSide,
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum ExclusionSide {
Left,
Right,
Both,
None,
}
pub trait RendererResourcesTrait: core::fmt::Debug {
fn get_font_family(
&self,
style_font_families_hash: &StyleFontFamiliesHash,
) -> Option<&StyleFontFamilyHash>;
fn get_font_key(&self, style_font_family_hash: &StyleFontFamilyHash) -> Option<&FontKey>;
fn get_registered_font(
&self,
font_key: &FontKey,
) -> Option<&(FontRef, FastHashMap<(Au, DpiScaleFactor), FontInstanceKey>)>;
fn get_image(&self, hash: &ImageRefHash) -> Option<&ResolvedImage>;
fn update_image(
&mut self,
image_ref_hash: &ImageRefHash,
descriptor: crate::resources::ImageDescriptor,
);
}
impl RendererResourcesTrait for RendererResources {
fn get_font_family(
&self,
style_font_families_hash: &StyleFontFamiliesHash,
) -> Option<&StyleFontFamilyHash> {
self.font_families_map.get(style_font_families_hash)
}
fn get_font_key(&self, style_font_family_hash: &StyleFontFamilyHash) -> Option<&FontKey> {
self.font_id_map.get(style_font_family_hash)
}
fn get_registered_font(
&self,
font_key: &FontKey,
) -> Option<&(FontRef, FastHashMap<(Au, DpiScaleFactor), FontInstanceKey>)> {
self.currently_registered_fonts.get(font_key)
}
fn get_image(&self, hash: &ImageRefHash) -> Option<&ResolvedImage> {
self.currently_registered_images.get(hash)
}
fn update_image(
&mut self,
image_ref_hash: &ImageRefHash,
descriptor: crate::resources::ImageDescriptor,
) {
if let Some(s) = self.currently_registered_images.get_mut(image_ref_hash) {
s.descriptor = descriptor;
}
}
}
pub struct RendererResources {
pub currently_registered_images: FastHashMap<ImageRefHash, ResolvedImage>,
pub image_key_map: FastHashMap<ImageKey, ImageRefHash>,
pub currently_registered_fonts:
FastHashMap<FontKey, (FontRef, FastHashMap<(Au, DpiScaleFactor), FontInstanceKey>)>,
pub last_frame_registered_fonts:
FastHashMap<FontKey, FastHashMap<(Au, DpiScaleFactor), FontInstanceKey>>,
pub font_families_map: FastHashMap<StyleFontFamiliesHash, StyleFontFamilyHash>,
pub font_id_map: FastHashMap<StyleFontFamilyHash, FontKey>,
pub font_hash_map: FastHashMap<u64, FontKey>,
}
impl fmt::Debug for RendererResources {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"RendererResources {{
currently_registered_images: {:#?},
currently_registered_fonts: {:#?},
font_families_map: {:#?},
font_id_map: {:#?},
}}",
self.currently_registered_images.keys().collect::<Vec<_>>(),
self.currently_registered_fonts.keys().collect::<Vec<_>>(),
self.font_families_map.keys().collect::<Vec<_>>(),
self.font_id_map.keys().collect::<Vec<_>>(),
)
}
}
impl Default for RendererResources {
fn default() -> Self {
Self {
currently_registered_images: FastHashMap::default(),
image_key_map: FastHashMap::default(),
currently_registered_fonts: FastHashMap::default(),
last_frame_registered_fonts: FastHashMap::default(),
font_families_map: FastHashMap::default(),
font_id_map: FastHashMap::default(),
font_hash_map: FastHashMap::default(),
}
}
}
impl RendererResources {
pub fn get_renderable_font_data(
&self,
font_instance_key: &FontInstanceKey,
) -> Option<(&FontRef, Au, DpiScaleFactor)> {
self.currently_registered_fonts
.iter()
.find_map(|(font_key, (font_ref, instances))| {
instances.iter().find_map(|((au, dpi), instance_key)| {
if *instance_key == *font_instance_key {
Some((font_ref, *au, *dpi))
} else {
None
}
})
})
}
pub fn get_image(&self, hash: &ImageRefHash) -> Option<&ResolvedImage> {
self.currently_registered_images.get(hash)
}
pub fn get_font_family(
&self,
style_font_families_hash: &StyleFontFamiliesHash,
) -> Option<&StyleFontFamilyHash> {
self.font_families_map.get(style_font_families_hash)
}
pub fn get_font_key(&self, style_font_family_hash: &StyleFontFamilyHash) -> Option<&FontKey> {
self.font_id_map.get(style_font_family_hash)
}
pub fn get_registered_font(
&self,
font_key: &FontKey,
) -> Option<&(FontRef, FastHashMap<(Au, DpiScaleFactor), FontInstanceKey>)> {
self.currently_registered_fonts.get(font_key)
}
pub fn update_image(&mut self, image_ref_hash: &ImageRefHash, descriptor: ImageDescriptor) {
if let Some(s) = self.currently_registered_images.get_mut(image_ref_hash) {
s.descriptor = descriptor; }
}
pub fn get_font_instance_key_for_text(
&self,
font_size_px: f32,
css_property_cache: &CssPropertyCache,
node_data: &NodeData,
node_id: &NodeId,
styled_node_state: &StyledNodeState,
dpi_scale: f32,
) -> Option<FontInstanceKey> {
let font_size = StyleFontSize {
inner: azul_css::props::basic::PixelValue::const_px(font_size_px as isize),
};
let font_size_au = font_size_to_au(font_size);
let dpi_scale_factor = DpiScaleFactor {
inner: FloatValue::new(dpi_scale),
};
let font_family =
css_property_cache.get_font_id_or_default(node_data, node_id, styled_node_state);
let font_families_hash = StyleFontFamiliesHash::new(font_family.as_ref());
self.get_font_instance_key(&font_families_hash, font_size_au, dpi_scale_factor)
}
pub fn get_font_instance_key(
&self,
font_families_hash: &StyleFontFamiliesHash,
font_size_au: Au,
dpi_scale: DpiScaleFactor,
) -> Option<FontInstanceKey> {
let font_family_hash = self.get_font_family(font_families_hash)?;
let font_key = self.get_font_key(font_family_hash)?;
let (_, instances) = self.get_registered_font(font_key)?;
instances.get(&(font_size_au, dpi_scale)).copied()
}
fn remove_font_families_with_zero_references(&mut self) {
let font_family_to_delete = self
.font_id_map
.iter()
.filter_map(|(font_family, font_key)| {
if !self.currently_registered_fonts.contains_key(font_key) {
Some(font_family.clone())
} else {
None
}
})
.collect::<Vec<_>>();
for f in font_family_to_delete {
self.font_id_map.remove(&f); }
let font_families_to_delete = self
.font_families_map
.iter()
.filter_map(|(font_families, font_family)| {
if !self.font_id_map.contains_key(font_family) {
Some(font_families.clone())
} else {
None
}
})
.collect::<Vec<_>>();
for f in font_families_to_delete {
self.font_families_map.remove(&f); }
}
}
#[derive(Debug, Clone)]
pub struct UpdateImageResult {
pub key_to_update: ImageKey,
pub new_descriptor: ImageDescriptor,
pub new_image_data: ImageData,
}
#[derive(Debug, Default)]
pub struct GlTextureCache {
pub solved_textures:
BTreeMap<DomId, BTreeMap<NodeId, (ImageKey, ImageDescriptor, ExternalImageId)>>,
pub hashes: BTreeMap<(DomId, NodeId, ImageRefHash), ImageRefHash>,
}
unsafe impl Send for GlTextureCache {}
impl GlTextureCache {
pub fn empty() -> Self {
Self {
solved_textures: BTreeMap::new(),
hashes: BTreeMap::new(),
}
}
pub fn update_texture(
&mut self,
dom_id: DomId,
node_id: NodeId,
document_id: DocumentId,
epoch: Epoch,
new_texture: Texture,
insert_into_active_gl_textures_fn: &GlStoreImageFn,
) -> Option<ExternalImageId> {
let new_descriptor = new_texture.get_descriptor();
let di_map = self.solved_textures.get_mut(&dom_id)?;
let entry = di_map.get_mut(&node_id)?;
entry.1 = new_descriptor;
let external_image_id =
(insert_into_active_gl_textures_fn)(document_id, epoch, new_texture);
entry.2 = external_image_id;
Some(external_image_id)
}
}
macro_rules! unique_id {
($struct_name:ident, $counter_name:ident) => {
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
#[repr(C)]
pub struct $struct_name {
pub id: usize,
}
impl $struct_name {
pub fn unique() -> Self {
Self {
id: $counter_name.fetch_add(1, AtomicOrdering::SeqCst),
}
}
}
};
}
static PROPERTY_KEY_COUNTER: AtomicUsize = AtomicUsize::new(0);
unique_id!(TransformKey, PROPERTY_KEY_COUNTER);
unique_id!(ColorKey, PROPERTY_KEY_COUNTER);
unique_id!(OpacityKey, PROPERTY_KEY_COUNTER);
static IMAGE_ID_COUNTER: AtomicUsize = AtomicUsize::new(0);
unique_id!(ImageId, IMAGE_ID_COUNTER);
static FONT_ID_COUNTER: AtomicUsize = AtomicUsize::new(0);
unique_id!(FontId, FONT_ID_COUNTER);
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[repr(C)]
pub struct ImageMask {
pub image: ImageRef,
pub rect: LogicalRect,
pub repeat: bool,
}
impl_option!(
ImageMask,
OptionImageMask,
copy = false,
[Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash]
);
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ImmediateFontId {
Resolved((StyleFontFamilyHash, FontKey)),
Unresolved(StyleFontFamilyVec),
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[repr(C, u8)]
pub enum RawImageData {
U8(U8Vec),
U16(U16Vec),
F32(F32Vec),
}
impl RawImageData {
pub fn get_u8_vec_ref(&self) -> Option<&U8Vec> {
match self {
RawImageData::U8(v) => Some(v),
_ => None,
}
}
pub fn get_u16_vec_ref(&self) -> Option<&U16Vec> {
match self {
RawImageData::U16(v) => Some(v),
_ => None,
}
}
pub fn get_f32_vec_ref(&self) -> Option<&F32Vec> {
match self {
RawImageData::F32(v) => Some(v),
_ => None,
}
}
fn get_u8_vec(self) -> Option<U8Vec> {
match self {
RawImageData::U8(v) => Some(v),
_ => None,
}
}
fn get_u16_vec(self) -> Option<U16Vec> {
match self {
RawImageData::U16(v) => Some(v),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct RawImage {
pub pixels: RawImageData,
pub width: usize,
pub height: usize,
pub premultiplied_alpha: bool,
pub data_format: RawImageFormat,
pub tag: U8Vec,
}
impl RawImage {
pub fn null_image() -> Self {
Self {
pixels: RawImageData::U8(Vec::new().into()),
width: 0,
height: 0,
premultiplied_alpha: true,
data_format: RawImageFormat::BGRA8,
tag: Vec::new().into(),
}
}
pub fn allocate_mask(size: LayoutSize) -> Self {
Self {
pixels: RawImageData::U8(
vec![0; size.width.max(0) as usize * size.height.max(0) as usize].into(),
),
width: size.width as usize,
height: size.height as usize,
premultiplied_alpha: true,
data_format: RawImageFormat::R8,
tag: Vec::new().into(),
}
}
pub fn into_loaded_image_source(self) -> Option<(ImageData, ImageDescriptor)> {
#[inline(always)]
fn premultiply_alpha(array: &mut [u8]) {
if array.len() != 4 {
return;
}
let a = u32::from(array[3]);
array[0] = (((array[0] as u32 * a) + 128) / 255) as u8;
array[1] = (((array[1] as u32 * a) + 128) / 255) as u8;
array[2] = (((array[2] as u32 * a) + 128) / 255) as u8;
}
#[inline(always)]
fn normalize_u16(i: u16) -> u8 {
((core::u16::MAX as f32 / i as f32) * core::u8::MAX as f32) as u8
}
let RawImage {
width,
height,
pixels,
mut data_format,
premultiplied_alpha,
tag,
} = self;
const FOUR_BPP: usize = 4;
const TWO_CHANNELS: usize = 2;
const THREE_CHANNELS: usize = 3;
const FOUR_CHANNELS: usize = 4;
let mut is_opaque = true;
let expected_len = width * height;
let bytes: U8Vec = match data_format {
RawImageFormat::R8 => {
let pixels = pixels.get_u8_vec()?;
if pixels.len() != expected_len {
return None;
}
let pixels_ref = pixels.as_ref();
let mut px = vec![0; pixels_ref.len() * 4];
for (i, r) in pixels_ref.iter().enumerate() {
px[i * 4 + 0] = *r;
px[i * 4 + 1] = *r;
px[i * 4 + 2] = *r;
px[i * 4 + 3] = 0xff;
}
data_format = RawImageFormat::BGRA8;
px.into()
}
RawImageFormat::RG8 => {
let pixels = pixels.get_u8_vec()?;
if pixels.len() != expected_len * TWO_CHANNELS {
return None;
}
let mut px = vec![0; expected_len * FOUR_BPP];
for (pixel_index, greyalpha) in
pixels.as_ref().chunks_exact(TWO_CHANNELS).enumerate()
{
let grey = greyalpha[0];
let alpha = greyalpha[1];
if alpha != 255 {
is_opaque = false;
}
px[pixel_index * FOUR_BPP] = grey;
px[(pixel_index * FOUR_BPP) + 1] = grey;
px[(pixel_index * FOUR_BPP) + 2] = grey;
px[(pixel_index * FOUR_BPP) + 3] = alpha;
}
data_format = RawImageFormat::BGRA8;
px.into()
}
RawImageFormat::RGB8 => {
let pixels = pixels.get_u8_vec()?;
if pixels.len() != expected_len * THREE_CHANNELS {
return None;
}
let mut px = vec![0; expected_len * FOUR_BPP];
for (pixel_index, rgb) in pixels.as_ref().chunks_exact(THREE_CHANNELS).enumerate() {
let red = rgb[0];
let green = rgb[1];
let blue = rgb[2];
px[pixel_index * FOUR_BPP] = blue;
px[(pixel_index * FOUR_BPP) + 1] = green;
px[(pixel_index * FOUR_BPP) + 2] = red;
px[(pixel_index * FOUR_BPP) + 3] = 0xff;
}
data_format = RawImageFormat::BGRA8;
px.into()
}
RawImageFormat::RGBA8 => {
let mut pixels: Vec<u8> = pixels.get_u8_vec()?.into_library_owned_vec();
if pixels.len() != expected_len * FOUR_CHANNELS {
return None;
}
if premultiplied_alpha {
for rgba in pixels.chunks_exact_mut(4) {
let (r, gba) = rgba.split_first_mut()?;
core::mem::swap(r, gba.get_mut(1)?);
let a = rgba.get_mut(3)?;
if *a != 255 {
is_opaque = false;
}
}
} else {
for rgba in pixels.chunks_exact_mut(4) {
let (r, gba) = rgba.split_first_mut()?;
core::mem::swap(r, gba.get_mut(1)?);
let a = rgba.get_mut(3)?;
if *a != 255 {
is_opaque = false;
}
premultiply_alpha(rgba); }
}
data_format = RawImageFormat::BGRA8;
pixels.into()
}
RawImageFormat::R16 => {
let pixels = pixels.get_u16_vec()?;
if pixels.len() != expected_len {
return None;
}
let mut px = vec![0; expected_len * FOUR_BPP];
for (pixel_index, grey_u16) in pixels.as_ref().iter().enumerate() {
let grey_u8 = normalize_u16(*grey_u16);
px[pixel_index * FOUR_BPP] = grey_u8;
px[(pixel_index * FOUR_BPP) + 1] = grey_u8;
px[(pixel_index * FOUR_BPP) + 2] = grey_u8;
px[(pixel_index * FOUR_BPP) + 3] = 0xff;
}
data_format = RawImageFormat::BGRA8;
px.into()
}
RawImageFormat::RG16 => {
let pixels = pixels.get_u16_vec()?;
if pixels.len() != expected_len * TWO_CHANNELS {
return None;
}
let mut px = vec![0; expected_len * FOUR_BPP];
for (pixel_index, greyalpha) in
pixels.as_ref().chunks_exact(TWO_CHANNELS).enumerate()
{
let grey_u8 = normalize_u16(greyalpha[0]);
let alpha_u8 = normalize_u16(greyalpha[1]);
if alpha_u8 != 255 {
is_opaque = false;
}
px[pixel_index * FOUR_BPP] = grey_u8;
px[(pixel_index * FOUR_BPP) + 1] = grey_u8;
px[(pixel_index * FOUR_BPP) + 2] = grey_u8;
px[(pixel_index * FOUR_BPP) + 3] = alpha_u8;
}
data_format = RawImageFormat::BGRA8;
px.into()
}
RawImageFormat::RGB16 => {
let pixels = pixels.get_u16_vec()?;
if pixels.len() != expected_len * THREE_CHANNELS {
return None;
}
let mut px = vec![0; expected_len * FOUR_BPP];
for (pixel_index, rgb) in pixels.as_ref().chunks_exact(THREE_CHANNELS).enumerate() {
let red_u8 = normalize_u16(rgb[0]);
let green_u8 = normalize_u16(rgb[1]);
let blue_u8 = normalize_u16(rgb[2]);
px[pixel_index * FOUR_BPP] = blue_u8;
px[(pixel_index * FOUR_BPP) + 1] = green_u8;
px[(pixel_index * FOUR_BPP) + 2] = red_u8;
px[(pixel_index * FOUR_BPP) + 3] = 0xff;
}
data_format = RawImageFormat::BGRA8;
px.into()
}
RawImageFormat::RGBA16 => {
let pixels = pixels.get_u16_vec()?;
if pixels.len() != expected_len * FOUR_CHANNELS {
return None;
}
let mut px = vec![0; expected_len * FOUR_BPP];
if premultiplied_alpha {
for (pixel_index, rgba) in
pixels.as_ref().chunks_exact(FOUR_CHANNELS).enumerate()
{
let red_u8 = normalize_u16(rgba[0]);
let green_u8 = normalize_u16(rgba[1]);
let blue_u8 = normalize_u16(rgba[2]);
let alpha_u8 = normalize_u16(rgba[3]);
if alpha_u8 != 255 {
is_opaque = false;
}
px[pixel_index * FOUR_BPP] = blue_u8;
px[(pixel_index * FOUR_BPP) + 1] = green_u8;
px[(pixel_index * FOUR_BPP) + 2] = red_u8;
px[(pixel_index * FOUR_BPP) + 3] = alpha_u8;
}
} else {
for (pixel_index, rgba) in
pixels.as_ref().chunks_exact(FOUR_CHANNELS).enumerate()
{
let red_u8 = normalize_u16(rgba[0]);
let green_u8 = normalize_u16(rgba[1]);
let blue_u8 = normalize_u16(rgba[2]);
let alpha_u8 = normalize_u16(rgba[3]);
if alpha_u8 != 255 {
is_opaque = false;
}
px[pixel_index * FOUR_BPP] = blue_u8;
px[(pixel_index * FOUR_BPP) + 1] = green_u8;
px[(pixel_index * FOUR_BPP) + 2] = red_u8;
px[(pixel_index * FOUR_BPP) + 3] = alpha_u8;
premultiply_alpha(
&mut px
[(pixel_index * FOUR_BPP)..((pixel_index * FOUR_BPP) + FOUR_BPP)],
);
}
}
data_format = RawImageFormat::BGRA8;
px.into()
}
RawImageFormat::BGR8 => {
let pixels = pixels.get_u8_vec()?;
if pixels.len() != expected_len * THREE_CHANNELS {
return None;
}
let mut px = vec![0; expected_len * FOUR_BPP];
for (pixel_index, bgr) in pixels.as_ref().chunks_exact(THREE_CHANNELS).enumerate() {
let blue = bgr[0];
let green = bgr[1];
let red = bgr[2];
px[pixel_index * FOUR_BPP] = blue;
px[(pixel_index * FOUR_BPP) + 1] = green;
px[(pixel_index * FOUR_BPP) + 2] = red;
px[(pixel_index * FOUR_BPP) + 3] = 0xff;
}
data_format = RawImageFormat::BGRA8;
px.into()
}
RawImageFormat::BGRA8 => {
if premultiplied_alpha {
let pixels = pixels.get_u8_vec()?;
is_opaque = pixels
.as_ref()
.chunks_exact(FOUR_CHANNELS)
.all(|bgra| bgra[3] == 255);
pixels
} else {
let mut pixels: Vec<u8> = pixels.get_u8_vec()?.into_library_owned_vec();
if pixels.len() != expected_len * FOUR_BPP {
return None;
}
for bgra in pixels.chunks_exact_mut(FOUR_CHANNELS) {
if bgra[3] != 255 {
is_opaque = false;
}
premultiply_alpha(bgra);
}
data_format = RawImageFormat::BGRA8;
pixels.into()
}
}
RawImageFormat::RGBF32 => {
let pixels = pixels.get_f32_vec_ref()?;
if pixels.len() != expected_len * THREE_CHANNELS {
return None;
}
let mut px = vec![0; expected_len * FOUR_BPP];
for (pixel_index, rgb) in pixels.as_ref().chunks_exact(THREE_CHANNELS).enumerate() {
let red_u8 = (rgb[0] * 255.0) as u8;
let green_u8 = (rgb[1] * 255.0) as u8;
let blue_u8 = (rgb[2] * 255.0) as u8;
px[pixel_index * FOUR_BPP] = blue_u8;
px[(pixel_index * FOUR_BPP) + 1] = green_u8;
px[(pixel_index * FOUR_BPP) + 2] = red_u8;
px[(pixel_index * FOUR_BPP) + 3] = 0xff;
}
data_format = RawImageFormat::BGRA8;
px.into()
}
RawImageFormat::RGBAF32 => {
let pixels = pixels.get_f32_vec_ref()?;
if pixels.len() != expected_len * FOUR_CHANNELS {
return None;
}
let mut px = vec![0; expected_len * FOUR_BPP];
if premultiplied_alpha {
for (pixel_index, rgba) in
pixels.as_ref().chunks_exact(FOUR_CHANNELS).enumerate()
{
let red_u8 = (rgba[0] * 255.0) as u8;
let green_u8 = (rgba[1] * 255.0) as u8;
let blue_u8 = (rgba[2] * 255.0) as u8;
let alpha_u8 = (rgba[3] * 255.0) as u8;
if alpha_u8 != 255 {
is_opaque = false;
}
px[pixel_index * FOUR_BPP] = blue_u8;
px[(pixel_index * FOUR_BPP) + 1] = green_u8;
px[(pixel_index * FOUR_BPP) + 2] = red_u8;
px[(pixel_index * FOUR_BPP) + 3] = alpha_u8;
}
} else {
for (pixel_index, rgba) in
pixels.as_ref().chunks_exact(FOUR_CHANNELS).enumerate()
{
let red_u8 = (rgba[0] * 255.0) as u8;
let green_u8 = (rgba[1] * 255.0) as u8;
let blue_u8 = (rgba[2] * 255.0) as u8;
let alpha_u8 = (rgba[3] * 255.0) as u8;
if alpha_u8 != 255 {
is_opaque = false;
}
px[pixel_index * FOUR_BPP] = blue_u8;
px[(pixel_index * FOUR_BPP) + 1] = green_u8;
px[(pixel_index * FOUR_BPP) + 2] = red_u8;
px[(pixel_index * FOUR_BPP) + 3] = alpha_u8;
premultiply_alpha(
&mut px
[(pixel_index * FOUR_BPP)..((pixel_index * FOUR_BPP) + FOUR_BPP)],
);
}
}
data_format = RawImageFormat::BGRA8;
px.into()
}
};
let image_data = ImageData::Raw(SharedRawImageData::new(bytes));
let image_descriptor = ImageDescriptor {
format: data_format,
width,
height,
offset: 0,
stride: None.into(),
flags: ImageDescriptorFlags {
is_opaque,
allow_mipmaps: true,
},
};
Some((image_data, image_descriptor))
}
}
impl_option!(
RawImage,
OptionRawImage,
copy = false,
[Debug, Clone, PartialEq, PartialOrd]
);
pub fn font_size_to_au(font_size: StyleFontSize) -> Au {
Au::from_px(font_size.inner.to_pixels_internal(0.0, DEFAULT_FONT_SIZE))
}
pub type FontInstanceFlags = u32;
pub const FONT_INSTANCE_FLAG_SYNTHETIC_BOLD: u32 = 1 << 1;
pub const FONT_INSTANCE_FLAG_EMBEDDED_BITMAPS: u32 = 1 << 2;
pub const FONT_INSTANCE_FLAG_SUBPIXEL_BGR: u32 = 1 << 3;
pub const FONT_INSTANCE_FLAG_TRANSPOSE: u32 = 1 << 4;
pub const FONT_INSTANCE_FLAG_FLIP_X: u32 = 1 << 5;
pub const FONT_INSTANCE_FLAG_FLIP_Y: u32 = 1 << 6;
pub const FONT_INSTANCE_FLAG_SUBPIXEL_POSITION: u32 = 1 << 7;
pub const FONT_INSTANCE_FLAG_FORCE_GDI: u32 = 1 << 16;
pub const FONT_INSTANCE_FLAG_FONT_SMOOTHING: u32 = 1 << 16;
pub const FONT_INSTANCE_FLAG_FORCE_AUTOHINT: u32 = 1 << 16;
pub const FONT_INSTANCE_FLAG_NO_AUTOHINT: u32 = 1 << 17;
pub const FONT_INSTANCE_FLAG_VERTICAL_LAYOUT: u32 = 1 << 18;
pub const FONT_INSTANCE_FLAG_LCD_VERTICAL: u32 = 1 << 19;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct GlyphOptions {
pub render_mode: FontRenderMode,
pub flags: FontInstanceFlags,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub enum FontRenderMode {
Mono,
Alpha,
Subpixel,
}
#[cfg(target_arch = "wasm32")]
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct FontInstancePlatformOptions {
}
#[cfg(target_os = "windows")]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct FontInstancePlatformOptions {
pub gamma: u16,
pub contrast: u8,
pub cleartype_level: u8,
}
#[cfg(target_os = "macos")]
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct FontInstancePlatformOptions {
pub unused: u32,
}
#[cfg(target_os = "linux")]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct FontInstancePlatformOptions {
pub lcd_filter: FontLCDFilter,
pub hinting: FontHinting,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub enum FontHinting {
None,
Mono,
Light,
Normal,
LCD,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub enum FontLCDFilter {
None,
Default,
Light,
Legacy,
}
impl Default for FontLCDFilter {
fn default() -> Self {
FontLCDFilter::Default
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct FontInstanceOptions {
pub render_mode: FontRenderMode,
pub flags: FontInstanceFlags,
pub bg_color: ColorU,
pub synthetic_italics: SyntheticItalics,
}
impl Default for FontInstanceOptions {
fn default() -> FontInstanceOptions {
FontInstanceOptions {
render_mode: FontRenderMode::Subpixel,
flags: 0,
bg_color: ColorU::TRANSPARENT,
synthetic_italics: SyntheticItalics::default(),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct SyntheticItalics {
pub angle: i16,
}
impl Default for SyntheticItalics {
fn default() -> Self {
Self { angle: 0 }
}
}
#[derive(Debug)]
#[repr(C)]
pub struct SharedRawImageData {
pub data: *const U8Vec,
pub copies: *const AtomicUsize,
pub run_destructor: bool,
}
impl SharedRawImageData {
pub fn new(data: U8Vec) -> Self {
Self {
data: Box::into_raw(Box::new(data)),
copies: Box::into_raw(Box::new(AtomicUsize::new(1))),
run_destructor: true,
}
}
pub fn as_ref(&self) -> &[u8] {
unsafe { (*self.data).as_ref() }
}
pub fn get_bytes(&self) -> &[u8] {
self.as_ref()
}
pub fn as_ptr(&self) -> *const u8 {
unsafe { (*self.data).as_ref().as_ptr() }
}
pub fn len(&self) -> usize {
unsafe { (*self.data).len() }
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn into_inner(self) -> Option<U8Vec> {
unsafe {
if self.copies.as_ref().map(|m| m.load(AtomicOrdering::SeqCst)) == Some(1) {
let data = Box::from_raw(self.data as *mut U8Vec);
let _ = Box::from_raw(self.copies as *mut AtomicUsize);
core::mem::forget(self); Some(*data)
} else {
None
}
}
}
}
unsafe impl Send for SharedRawImageData {}
unsafe impl Sync for SharedRawImageData {}
impl Clone for SharedRawImageData {
fn clone(&self) -> Self {
unsafe {
self.copies
.as_ref()
.map(|m| m.fetch_add(1, AtomicOrdering::SeqCst));
}
Self {
data: self.data,
copies: self.copies,
run_destructor: true,
}
}
}
impl Drop for SharedRawImageData {
fn drop(&mut self) {
self.run_destructor = false;
unsafe {
let copies = (*self.copies).fetch_sub(1, AtomicOrdering::SeqCst);
if copies == 1 {
let _ = Box::from_raw(self.data as *mut U8Vec);
let _ = Box::from_raw(self.copies as *mut AtomicUsize);
}
}
}
}
impl PartialEq for SharedRawImageData {
fn eq(&self, rhs: &Self) -> bool {
self.data as usize == rhs.data as usize
}
}
impl Eq for SharedRawImageData {}
impl PartialOrd for SharedRawImageData {
fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for SharedRawImageData {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
(self.data as usize).cmp(&(other.data as usize))
}
}
impl Hash for SharedRawImageData {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
(self.data as usize).hash(state)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
#[repr(C, u8)]
pub enum ImageData {
Raw(SharedRawImageData),
External(ExternalImageData),
}
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
#[repr(C, u8)]
pub enum ExternalImageType {
TextureHandle(ImageBufferKind),
Buffer,
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
pub struct ExternalImageId {
pub inner: u64,
}
static LAST_EXTERNAL_IMAGE_ID: AtomicUsize = AtomicUsize::new(0);
impl ExternalImageId {
pub fn new() -> Self {
Self {
inner: LAST_EXTERNAL_IMAGE_ID.fetch_add(1, AtomicOrdering::SeqCst) as u64,
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[repr(C, u8)]
pub enum GlyphOutlineOperation {
MoveTo(OutlineMoveTo),
LineTo(OutlineLineTo),
QuadraticCurveTo(OutlineQuadTo),
CubicCurveTo(OutlineCubicTo),
ClosePath,
}
impl_option!(
GlyphOutlineOperation,
OptionGlyphOutlineOperation,
copy = false,
[Debug, Clone, PartialEq, PartialOrd]
);
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct OutlineMoveTo {
pub x: i16,
pub y: i16,
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct OutlineLineTo {
pub x: i16,
pub y: i16,
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct OutlineQuadTo {
pub ctrl_1_x: i16,
pub ctrl_1_y: i16,
pub end_x: i16,
pub end_y: i16,
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct OutlineCubicTo {
pub ctrl_1_x: i16,
pub ctrl_1_y: i16,
pub ctrl_2_x: i16,
pub ctrl_2_y: i16,
pub end_x: i16,
pub end_y: i16,
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct GlyphOutline {
pub operations: GlyphOutlineOperationVec,
}
azul_css::impl_vec!(GlyphOutlineOperation, GlyphOutlineOperationVec, GlyphOutlineOperationVecDestructor, GlyphOutlineOperationVecDestructorType, GlyphOutlineOperationVecSlice, OptionGlyphOutlineOperation);
azul_css::impl_vec_clone!(
GlyphOutlineOperation,
GlyphOutlineOperationVec,
GlyphOutlineOperationVecDestructor
);
azul_css::impl_vec_debug!(GlyphOutlineOperation, GlyphOutlineOperationVec);
azul_css::impl_vec_partialord!(GlyphOutlineOperation, GlyphOutlineOperationVec);
azul_css::impl_vec_partialeq!(GlyphOutlineOperation, GlyphOutlineOperationVec);
#[derive(Debug, Clone)]
#[repr(C)]
pub struct OwnedGlyphBoundingBox {
pub max_x: i16,
pub max_y: i16,
pub min_x: i16,
pub min_y: i16,
}
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
#[repr(C)]
pub enum ImageBufferKind {
Texture2D = 0,
TextureRect = 1,
TextureExternal = 2,
}
#[repr(C)]
#[derive(Debug, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct ExternalImageData {
pub id: ExternalImageId,
pub channel_index: u8,
pub image_type: ExternalImageType,
}
pub type TileSize = u16;
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
pub enum ImageDirtyRect {
All,
Partial(LayoutRect),
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum ResourceUpdate {
AddFont(AddFont),
DeleteFont(FontKey),
AddFontInstance(AddFontInstance),
DeleteFontInstance(FontInstanceKey),
AddImage(AddImage),
UpdateImage(UpdateImage),
DeleteImage(ImageKey),
}
#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct AddImage {
pub key: ImageKey,
pub descriptor: ImageDescriptor,
pub data: ImageData,
pub tiling: Option<TileSize>,
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct UpdateImage {
pub key: ImageKey,
pub descriptor: ImageDescriptor,
pub data: ImageData,
pub dirty_rect: ImageDirtyRect,
}
#[derive(Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct AddFont {
pub key: FontKey,
pub font: azul_css::props::basic::FontRef,
}
impl fmt::Debug for AddFont {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"AddFont {{ key: {:?}, font: {:?} }}",
self.key, self.font
)
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct AddFontInstance {
pub key: FontInstanceKey,
pub font_key: FontKey,
pub glyph_size: (Au, DpiScaleFactor),
pub options: Option<FontInstanceOptions>,
pub platform_options: Option<FontInstancePlatformOptions>,
pub variations: Vec<FontVariation>,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialOrd, PartialEq)]
pub struct FontVariation {
pub tag: u32,
pub value: f32,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Epoch {
inner: u32,
}
impl fmt::Display for Epoch {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.inner)
}
}
impl Epoch {
pub const fn new() -> Self {
Self { inner: 0 }
}
pub const fn from(i: u32) -> Self {
Self { inner: i }
}
pub const fn into_u32(&self) -> u32 {
self.inner
}
pub fn increment(&mut self) {
use core::u32;
const MAX_ID: u32 = u32::MAX - 1;
*self = match self.inner {
MAX_ID => Epoch { inner: 0 },
other => Epoch {
inner: other.saturating_add(1),
},
};
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, PartialOrd, Eq, Ord)]
pub struct Au(pub i32);
pub const AU_PER_PX: i32 = 60;
pub const MAX_AU: i32 = (1 << 30) - 1;
pub const MIN_AU: i32 = -(1 << 30) - 1;
impl Au {
pub fn from_px(px: f32) -> Self {
let target_app_units = (px * AU_PER_PX as f32) as i32;
Au(target_app_units.min(MAX_AU).max(MIN_AU))
}
pub fn into_px(&self) -> f32 {
self.0 as f32 / AU_PER_PX as f32
}
}
#[derive(Debug)]
pub enum AddFontMsg {
Font(FontKey, StyleFontFamilyHash, FontRef),
Instance(AddFontInstance, (Au, DpiScaleFactor)),
}
impl AddFontMsg {
pub fn into_resource_update(&self) -> ResourceUpdate {
use self::AddFontMsg::*;
match self {
Font(font_key, _, font_ref) => ResourceUpdate::AddFont(AddFont {
key: *font_key,
font: font_ref.clone(),
}),
Instance(fi, _) => ResourceUpdate::AddFontInstance(fi.clone()),
}
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub enum DeleteFontMsg {
Font(FontKey),
Instance(FontInstanceKey, (Au, DpiScaleFactor)),
}
impl DeleteFontMsg {
pub fn into_resource_update(&self) -> ResourceUpdate {
use self::DeleteFontMsg::*;
match self {
Font(f) => ResourceUpdate::DeleteFont(*f),
Instance(fi, _) => ResourceUpdate::DeleteFontInstance(*fi),
}
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct AddImageMsg(pub AddImage);
impl AddImageMsg {
pub fn into_resource_update(&self) -> ResourceUpdate {
ResourceUpdate::AddImage(self.0.clone())
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct DeleteImageMsg(ImageKey);
impl DeleteImageMsg {
pub fn into_resource_update(&self) -> ResourceUpdate {
ResourceUpdate::DeleteImage(self.0.clone())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct LoadedFontSource {
pub data: U8Vec,
pub index: u32,
pub load_outlines: bool,
}
pub type LoadFontFn = fn(&StyleFontFamily, &FcFontCache) -> Option<LoadedFontSource>;
pub type ParseFontFn = fn(LoadedFontSource) -> Option<FontRef>;
pub type GlStoreImageFn = fn(DocumentId, Epoch, Texture) -> ExternalImageId;
pub fn build_add_font_resource_updates(
renderer_resources: &mut RendererResources,
dpi: DpiScaleFactor,
fc_cache: &FcFontCache,
id_namespace: IdNamespace,
fonts_in_dom: &FastHashMap<ImmediateFontId, FastBTreeSet<Au>>,
font_source_load_fn: LoadFontFn,
parse_font_fn: ParseFontFn,
) -> Vec<(StyleFontFamilyHash, AddFontMsg)> {
let mut resource_updates = alloc::vec::Vec::new();
let mut font_instances_added_this_frame = FastBTreeSet::new();
'outer: for (im_font_id, font_sizes) in fonts_in_dom {
macro_rules! insert_font_instances {
($font_family_hash:expr, $font_key:expr, $font_size:expr) => {{
let font_instance_key_exists = renderer_resources
.currently_registered_fonts
.get(&$font_key)
.and_then(|(_, font_instances)| font_instances.get(&($font_size, dpi)))
.is_some()
|| font_instances_added_this_frame.contains(&($font_key, ($font_size, dpi)));
if !font_instance_key_exists {
let font_instance_key = FontInstanceKey::unique(id_namespace);
#[cfg(target_os = "windows")]
let platform_options = FontInstancePlatformOptions {
gamma: 300,
contrast: 100,
cleartype_level: 100,
};
#[cfg(target_os = "linux")]
let platform_options = FontInstancePlatformOptions {
lcd_filter: FontLCDFilter::Default,
hinting: FontHinting::Normal,
};
#[cfg(target_os = "macos")]
let platform_options = FontInstancePlatformOptions::default();
#[cfg(target_arch = "wasm32")]
let platform_options = FontInstancePlatformOptions::default();
let options = FontInstanceOptions {
render_mode: FontRenderMode::Subpixel,
flags: 0 | FONT_INSTANCE_FLAG_NO_AUTOHINT,
..Default::default()
};
font_instances_added_this_frame.insert(($font_key, ($font_size, dpi)));
resource_updates.push((
$font_family_hash,
AddFontMsg::Instance(
AddFontInstance {
key: font_instance_key,
font_key: $font_key,
glyph_size: ($font_size, dpi),
options: Some(options),
platform_options: Some(platform_options),
variations: alloc::vec::Vec::new(),
},
($font_size, dpi),
),
));
}
}};
}
match im_font_id {
ImmediateFontId::Resolved((font_family_hash, font_id)) => {
for font_size in font_sizes.iter() {
insert_font_instances!(*font_family_hash, *font_id, *font_size);
}
}
ImmediateFontId::Unresolved(style_font_families) => {
let mut font_family_hash = None;
let font_families_hash = StyleFontFamiliesHash::new(style_font_families.as_ref());
'inner: for family in style_font_families.as_ref().iter() {
let current_family_hash = StyleFontFamilyHash::new(&family);
if let Some(font_id) = renderer_resources.font_id_map.get(¤t_family_hash)
{
for font_size in font_sizes {
insert_font_instances!(current_family_hash, *font_id, *font_size);
}
continue 'outer;
}
let font_ref = match family {
StyleFontFamily::Ref(r) => r.clone(), other => {
let font_data = match (font_source_load_fn)(&other, fc_cache) {
Some(s) => s,
None => continue 'inner,
};
let font_ref = match (parse_font_fn)(font_data) {
Some(s) => s,
None => continue 'inner,
};
font_ref
}
};
font_family_hash = Some((current_family_hash, font_ref));
break 'inner;
}
let (font_family_hash, font_ref) = match font_family_hash {
None => continue 'outer, Some(s) => s,
};
let font_key = FontKey::unique(id_namespace);
let add_font_msg = AddFontMsg::Font(font_key, font_family_hash, font_ref);
renderer_resources
.font_id_map
.insert(font_family_hash, font_key);
renderer_resources
.font_families_map
.insert(font_families_hash, font_family_hash);
resource_updates.push((font_family_hash, add_font_msg));
for font_size in font_sizes {
insert_font_instances!(font_family_hash, font_key, *font_size);
}
}
}
}
resource_updates
}
#[allow(unused_variables)]
pub fn build_add_image_resource_updates(
renderer_resources: &RendererResources,
id_namespace: IdNamespace,
epoch: Epoch,
document_id: &DocumentId,
images_in_dom: &FastBTreeSet<ImageRef>,
insert_into_active_gl_textures: GlStoreImageFn,
) -> Vec<(ImageRefHash, AddImageMsg)> {
images_in_dom
.iter()
.filter_map(|image_ref| {
let image_ref_hash = image_ref_get_hash(&image_ref);
if renderer_resources
.currently_registered_images
.contains_key(&image_ref_hash)
{
return None;
}
match image_ref.get_data() {
DecodedImage::Gl(texture) => {
let descriptor = texture.get_descriptor();
let key = image_ref_hash_to_image_key(image_ref_hash, id_namespace);
let external_image_id =
(insert_into_active_gl_textures)(*document_id, epoch, texture.clone());
Some((
image_ref_hash,
AddImageMsg(AddImage {
key,
data: ImageData::External(ExternalImageData {
id: external_image_id,
channel_index: 0,
image_type: ExternalImageType::TextureHandle(
ImageBufferKind::Texture2D,
),
}),
descriptor,
tiling: None,
}),
))
}
DecodedImage::Raw((descriptor, data)) => {
let key = image_ref_hash_to_image_key(image_ref_hash, id_namespace);
Some((
image_ref_hash,
AddImageMsg(AddImage {
key,
data: data.clone(), descriptor: descriptor.clone(),
tiling: None,
}),
))
}
DecodedImage::NullImage {
width: _,
height: _,
format: _,
tag: _,
} => None,
DecodedImage::Callback(_) => None,
}
})
.collect()
}
fn add_gl_resources(
renderer_resources: &mut RendererResources,
all_resource_updates: &mut Vec<ResourceUpdate>,
add_image_resources: Vec<(ImageRefHash, ImageRefHash, AddImageMsg)>,
) {
let add_image_resources = add_image_resources
.into_iter()
.map(|(_, k, v)| (k, v))
.collect::<Vec<_>>();
add_resources(
renderer_resources,
all_resource_updates,
Vec::new(),
add_image_resources,
);
}
pub fn add_resources(
renderer_resources: &mut RendererResources,
all_resource_updates: &mut Vec<ResourceUpdate>,
add_font_resources: Vec<(StyleFontFamilyHash, AddFontMsg)>,
add_image_resources: Vec<(ImageRefHash, AddImageMsg)>,
) {
all_resource_updates.extend(
add_font_resources
.iter()
.map(|(_, f)| f.into_resource_update()),
);
all_resource_updates.extend(
add_image_resources
.iter()
.map(|(_, i)| i.into_resource_update()),
);
for (image_ref_hash, add_image_msg) in add_image_resources.iter() {
renderer_resources.currently_registered_images.insert(
*image_ref_hash,
ResolvedImage {
key: add_image_msg.0.key,
descriptor: add_image_msg.0.descriptor,
},
);
}
for (_, add_font_msg) in add_font_resources {
use self::AddFontMsg::*;
match add_font_msg {
Font(fk, font_family_hash, font_ref) => {
renderer_resources
.currently_registered_fonts
.entry(fk)
.or_insert_with(|| (font_ref.clone(), FastHashMap::default()));
renderer_resources
.font_hash_map
.insert(font_ref.get_hash(), fk);
}
Instance(fi, size) => {
if let Some((_, instances)) = renderer_resources
.currently_registered_fonts
.get_mut(&fi.font_key)
{
instances.insert(size, fi.key);
}
}
}
}
}