Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: resize canvas should clear the context #564

Merged
merged 2 commits into from
Sep 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions src/ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use cssparser::{Color as CSSColor, Parser, ParserInput, RGBA};
use libavif::AvifData;
use napi::{bindgen_prelude::*, JsBuffer, JsString, NapiRaw, NapiValue};

use crate::global_fonts::get_font;
use crate::{
avif::Config,
error::SkError,
Expand Down Expand Up @@ -666,6 +667,7 @@ impl Context {
let surface = &mut self.surface;
surface.save();
Self::apply_shadow_offset_matrix(surface, state.shadow_offset_x, state.shadow_offset_y)?;
let font = get_font()?;
surface.canvas.draw_text(
text,
x,
Expand All @@ -675,17 +677,18 @@ impl Context {
weight,
stretch as i32,
slant,
&*crate::global_fonts::GLOBAL_FONT_COLLECTION,
&*font,
state.font_style.size,
&state.font_style.family,
state.text_baseline,
state.text_align,
state.text_direction,
&shadow_paint,
)?;
mem::drop(font);
surface.restore();
}

let font = get_font()?;
self.surface.canvas.draw_text(
text,
x,
Expand All @@ -695,7 +698,7 @@ impl Context {
weight,
stretch as i32,
slant,
&*crate::global_fonts::GLOBAL_FONT_COLLECTION,
&*font,
state.font_style.size,
&state.font_style.family,
state.text_baseline,
Expand All @@ -712,9 +715,10 @@ impl Context {
let weight = state.font_style.weight;
let stretch = state.font_style.stretch;
let slant = state.font_style.style;
let font = get_font()?;
let line_metrics = LineMetrics(self.surface.canvas.get_line_metrics(
text,
&*crate::global_fonts::GLOBAL_FONT_COLLECTION,
&*font,
state.font_style.size,
weight,
stretch as i32,
Expand Down
6 changes: 6 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,9 @@ impl From<NulError> for SkError {
Self::NulError
}
}

impl<T> From<std::sync::PoisonError<T>> for SkError {
fn from(err: std::sync::PoisonError<T>) -> Self {
Self::Generic(format!("PoisonError {}", err))
}
}
55 changes: 36 additions & 19 deletions src/global_fonts.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::fs::read_dir;
use std::path;
use std::sync::{LockResult, Mutex, MutexGuard, PoisonError};

use once_cell::sync::{Lazy, OnceCell};

Expand All @@ -14,60 +15,75 @@ const FONT_PATH: &str = "/usr/share/fonts/";
#[cfg(target_os = "android")]
const FONT_PATH: &str = "/system/fonts";

static FONT_DIR: OnceCell<u32> = OnceCell::new();
static FONT_DIR: OnceCell<napi::Result<u32>> = OnceCell::new();

pub(crate) static GLOBAL_FONT_COLLECTION: Lazy<FontCollection> = Lazy::new(FontCollection::new);
pub(crate) static GLOBAL_FONT_COLLECTION: Lazy<Mutex<FontCollection>> =
Lazy::new(|| Mutex::new(FontCollection::new()));

#[inline]
pub(crate) fn get_font<'a>() -> LockResult<MutexGuard<'a, FontCollection>> {
GLOBAL_FONT_COLLECTION.lock()
}

#[inline]
fn into_napi_error(err: PoisonError<MutexGuard<'_, FontCollection>>) -> napi::Error {
napi::Error::new(napi::Status::GenericFailure, format!("{err}"))
}

#[napi]
#[allow(non_snake_case)]
pub mod GlobalFonts {
use napi::bindgen_prelude::*;

use super::{FONT_DIR, FONT_PATH, GLOBAL_FONT_COLLECTION};
use super::{get_font, into_napi_error, FONT_DIR, FONT_PATH};

#[napi]
pub fn register(font_data: Buffer, name_alias: Option<String>) -> bool {
pub fn register(font_data: Buffer, name_alias: Option<String>) -> Result<bool> {
let maybe_name_alias = name_alias.and_then(|s| if s.is_empty() { None } else { Some(s) });
GLOBAL_FONT_COLLECTION.register(font_data.as_ref(), maybe_name_alias)
let font = get_font().map_err(into_napi_error)?;
Ok(font.register(font_data.as_ref(), maybe_name_alias))
}

#[napi]
pub fn register_from_path(font_path: String, name_alias: Option<String>) -> bool {
pub fn register_from_path(font_path: String, name_alias: Option<String>) -> Result<bool> {
let maybe_name_alias = name_alias.and_then(|s| if s.is_empty() { None } else { Some(s) });
GLOBAL_FONT_COLLECTION.register_from_path(font_path.as_str(), maybe_name_alias)
let font = get_font().map_err(into_napi_error)?;
Ok(font.register_from_path(font_path.as_str(), maybe_name_alias))
}

#[napi]
pub fn get_families() -> Result<String> {
Ok(serde_json::to_string(
&GLOBAL_FONT_COLLECTION.get_families(),
)?)
let font = get_font().map_err(into_napi_error)?;
Ok(serde_json::to_string(&font.get_families())?)
}

#[napi]
pub fn load_system_fonts() -> u32 {
*FONT_DIR.get_or_init(move || super::load_fonts_from_dir(FONT_PATH))
pub fn load_system_fonts() -> Result<u32> {
FONT_DIR
.get_or_init(move || super::load_fonts_from_dir(FONT_PATH))
.clone()
}

#[napi]
pub fn load_fonts_from_dir(dir: String) -> u32 {
pub fn load_fonts_from_dir(dir: String) -> Result<u32> {
super::load_fonts_from_dir(dir.as_str())
}

#[napi]
pub fn set_alias(font_name: String, alias: String) {
GLOBAL_FONT_COLLECTION.set_alias(font_name.as_str(), alias.as_str());
pub fn set_alias(font_name: String, alias: String) -> Result<()> {
let font = get_font().map_err(into_napi_error)?;
font.set_alias(font_name.as_str(), alias.as_str());
Ok(())
}
}

fn load_fonts_from_dir<P: AsRef<path::Path>>(dir: P) -> u32 {
fn load_fonts_from_dir<P: AsRef<path::Path>>(dir: P) -> napi::Result<u32> {
let mut count = 0u32;
let font_collection = &*GLOBAL_FONT_COLLECTION;
if let Ok(dir) = read_dir(dir) {
for f in dir.flatten() {
if let Ok(meta) = f.metadata() {
if meta.is_dir() {
load_fonts_from_dir(f.path());
load_fonts_from_dir(f.path())?;
} else {
let p = f.path();
let ext = p.extension().and_then(|s| s.to_str());
Expand All @@ -76,6 +92,7 @@ fn load_fonts_from_dir<P: AsRef<path::Path>>(dir: P) -> u32 {
Some("ttf") | Some("ttc") | Some("otf") | Some("pfb") | Some("woff2")
| Some("woff") => {
if let Some(p) = p.into_os_string().to_str() {
let font_collection = get_font().map_err(into_napi_error)?;
if font_collection.register_from_path::<String>(p, None) {
count += 1;
}
Expand All @@ -87,5 +104,5 @@ fn load_fonts_from_dir<P: AsRef<path::Path>>(dir: P) -> u32 {
}
}
}
count
Ok(count)
}
51 changes: 47 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,18 @@ pub struct CanvasRenderingContext2DAttributes {

#[napi]
pub struct CanvasElement {
pub width: u32,
pub height: u32,
pub(crate) width: u32,
pub(crate) height: u32,
pub(crate) ctx: ClassInstance<CanvasRenderingContext2D>,
}

#[napi]
impl CanvasElement {
#[napi(constructor)]
pub fn new(mut env: Env, mut this: This, width: u32, height: u32) -> Result<Self> {
fn create_context(
mut env: Env,
width: u32,
height: u32,
) -> Result<ClassInstance<CanvasRenderingContext2D>> {
let ctx = CanvasRenderingContext2D::into_instance(
CanvasRenderingContext2D {
context: Context::new(width, height, ColorSpace::default())?,
Expand All @@ -96,12 +99,52 @@ impl CanvasElement {
.with_property_attributes(PropertyAttributes::Writable | PropertyAttributes::Configurable),
])?;
env.adjust_external_memory((width * height * 4) as i64)?;
Ok(ctx)
}

#[napi(constructor)]
pub fn new(env: Env, mut this: This, width: u32, height: u32) -> Result<Self> {
let ctx = Self::create_context(env, width, height)?;
this.define_properties(&[Property::new("ctx")?
.with_value(&ctx)
.with_property_attributes(PropertyAttributes::Default)])?;
Ok(Self { width, height, ctx })
}

#[napi(setter)]
pub fn set_width(&mut self, mut env: Env, width: u32) -> Result<()> {
self.width = width;
let height = self.height;
let old_ctx = mem::replace(
&mut self.ctx.context,
Context::new(width, height, ColorSpace::default())?,
);
env.adjust_external_memory((width as i64 - old_ctx.width as i64) * 4)?;
Ok(())
}

#[napi(getter)]
pub fn get_width(&self) -> u32 {
self.width
}

#[napi(setter)]
pub fn set_height(&mut self, mut env: Env, height: u32) -> Result<()> {
self.height = height;
let width = self.width;
let old_ctx = mem::replace(
&mut self.ctx.context,
Context::new(width, height, ColorSpace::default())?,
);
env.adjust_external_memory((height as i64 - old_ctx.height as i64) * 4)?;
Ok(())
}

#[napi(getter)]
pub fn get_height(&self) -> u32 {
self.height
}

#[napi]
pub fn get_context(
&mut self,
Expand Down
28 changes: 13 additions & 15 deletions src/svg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,25 @@ use std::mem;

use napi::{bindgen_prelude::*, JsBuffer};

use crate::sk::sk_svg_text_to_path;
use crate::{error::SkError, global_fonts::get_font, sk::sk_svg_text_to_path};

#[napi(js_name = "convertSVGTextToPath")]
pub fn convert_svg_text_to_path(
env: Env,
input: Either3<Buffer, String, Unknown>,
) -> Result<JsBuffer> {
sk_svg_text_to_path(
input.as_bytes()?,
&*crate::global_fonts::GLOBAL_FONT_COLLECTION,
)
.ok_or_else(|| {
Error::new(
Status::InvalidArg,
"Convert svg text to path failed".to_owned(),
)
})
.and_then(|v| unsafe {
env.create_buffer_with_borrowed_data(v.0.ptr, v.0.size, v, |d, _| mem::drop(d))
})
.map(|b| b.into_raw())
let font = get_font().map_err(SkError::from)?;
sk_svg_text_to_path(input.as_bytes()?, &*font)
.ok_or_else(|| {
Error::new(
Status::InvalidArg,
"Convert svg text to path failed".to_owned(),
)
})
.and_then(|v| unsafe {
env.create_buffer_with_borrowed_data(v.0.ptr, v.0.size, v, |d, _| mem::drop(d))
})
.map(|b| b.into_raw())
}

trait AsBytes {
Expand Down