Skip to content

Commit

Permalink
feat: support maxWidth in fillText and strokeText
Browse files Browse the repository at this point in the history
  • Loading branch information
Brooooooklyn committed Jul 13, 2021
1 parent 9e94279 commit ccf33f3
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 41 deletions.
11 changes: 11 additions & 0 deletions __test__/draw.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,17 @@ test('fillText', async (t) => {
await snapshotImage(t, { canvas, ctx }, 'png', 3.2)
})

test('fillText-maxWidth', async (t) => {
const { ctx, canvas } = t.context
ctx.fillStyle = 'white'
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.fillStyle = 'black'
ctx.font = '24px Iosevka Slab'
ctx.fillText('Hello world', 50, 90, 90)
ctx.fillText('Hello world', 160, 90)
await snapshotImage(t, { canvas, ctx }, 'png', 0.8)
})

test('fillText-AA', async (t) => {
GlobalFonts.registerFromPath(fontOSRSPath)
const { ctx, canvas } = t.context
Expand Down
Binary file added __test__/snapshots/fillText-maxWidth.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Canvas</title>
</head>

<body>
<canvas width="1024" height="768" id="canvas"></canvas>

<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
ctx.font = '50px serif';
ctx.fillText('Hello world', 50, 90, 140);

ctx.fillText('Hello world', 250, 90);
</script>
</body>

</html>
47 changes: 28 additions & 19 deletions skia-c/skia_c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#define FONT_METRICS_CAST reinterpret_cast<SkFontMetrics *>(c_font_metrics)
#define TYPEFACE_CAST reinterpret_cast<SkTypeface *>(c_typeface)

#define MAX_LAYOUT_WIDTH 100000

extern "C"
{

Expand Down Expand Up @@ -287,10 +289,12 @@ extern "C"
size_t text_len,
float x,
float y,
float max_width,
int weight,
int width,
int slant,
skiac_typeface_font_provider *c_typeface_font_provider,
skiac_font_mgr *c_font_mgr,
float font_size,
const char *font_family,
float baseline_offset,
Expand All @@ -300,44 +304,49 @@ extern "C"
{
auto font_collection = sk_make_sp<FontCollection>();
auto font_provider = sp_from_const(reinterpret_cast<TypefaceFontProvider *>(c_typeface_font_provider));
auto default_font_mgr = SkFontMgr_New_Custom_Empty();
auto default_font_mgr = sp_from_const(reinterpret_cast<SkFontMgr *>(c_font_mgr));
font_collection->setDefaultFontManager(default_font_mgr);
font_collection->setAssetFontManager(font_provider);
font_collection->enableFontFallback();
TextStyle text_style;
auto font_style = SkFontStyle(weight, width, (SkFontStyle::Slant)slant);
const std::vector<SkString> families = {SkString(font_family)};
auto typefaces = font_collection->findTypefaces(families, font_style);
auto typeface = typefaces.front();
text_style.setFontFamilies(families);
text_style.setFontSize(font_size);
text_style.setForegroundColor(*PAINT_CAST);
text_style.setWordSpacing(0);
text_style.setHeight(1);
text_style.setFontStyle(font_style);
text_style.setTypeface(typeface);

auto struct_style = StrutStyle();
struct_style.setFontStyle(font_style);
struct_style.setFontSize(font_size);
struct_style.setFontFamilies(families);
auto paragraph_style = new ParagraphStyle();
paragraph_style->turnHintingOff();
paragraph_style->setTextStyle(text_style);
paragraph_style->setStrutStyle(struct_style);
paragraph_style->setTextAlign((TextAlign)align);
auto builder = ParagraphBuilderImpl::make(*paragraph_style, font_collection).release();

ParagraphStyle paragraph_style;
paragraph_style.turnHintingOff();
paragraph_style.setTextStyle(text_style);
paragraph_style.setTextAlign((TextAlign)align);
auto builder = ParagraphBuilderImpl::make(paragraph_style, font_collection).release();
builder->pushStyle(text_style);
builder->addText(text, text_len);

auto paragraph = builder->Build().release();
auto paragraph = reinterpret_cast<ParagraphImpl *>(builder->Build().release());
auto alphabetic_baseline = paragraph->getAlphabeticBaseline();

auto paragraph_width = 100000;
auto paint_x = x + paragraph_width * align_factor;
paragraph->layout(paragraph_width);
auto paint_x = x + max_width * align_factor;
paragraph->layout(MAX_LAYOUT_WIDTH);
auto metrics = std::vector<LineMetrics>();
paragraph->getLineMetrics(metrics);
auto line_metric = metrics[0];
auto line_width = line_metric.fWidth;
auto need_scale = line_width > max_width;
if (need_scale)
{
CANVAS_CAST->save();
CANVAS_CAST->scale(max_width / line_width, 1.0);
}
auto paint_y = y + baseline_offset - paragraph->getHeight() - alphabetic_baseline;
paragraph->paint(CANVAS_CAST, paint_x, paint_y);
if (need_scale)
{
CANVAS_CAST->restore();
}
delete paragraph;
}

Expand Down
3 changes: 3 additions & 0 deletions skia-c/skia_c.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <modules/skparagraph/include/Paragraph.h>
#include <modules/skparagraph/include/ParagraphBuilder.h>
#include <modules/skparagraph/src/ParagraphBuilderImpl.h>
#include <modules/skparagraph/src/ParagraphImpl.h>
#include <modules/skparagraph/include/TypefaceFontProvider.h>
#include <modules/svg/include/SkSVGDOM.h>
#include <src/ports/SkFontMgr_custom.h>
Expand Down Expand Up @@ -163,10 +164,12 @@ extern "C"
size_t text_len,
float x,
float y,
float max_width,
int weight,
int width,
int slant,
skiac_typeface_font_provider *c_typeface_font_provider,
skiac_font_mgr *c_font_mgr,
float font_size,
const char *font_family,
float baseline_offset,
Expand Down
39 changes: 33 additions & 6 deletions src/ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ impl From<SkError> for Error {
}
}

const MAX_TEXT_WIDTH: f32 = 100_000.0;

pub struct Context {
pub(crate) surface: Surface,
path: Path,
Expand Down Expand Up @@ -200,9 +202,15 @@ impl Context {
}

#[inline(always)]
pub fn stroke_text(&mut self, text: &str, x: f32, y: f32) -> result::Result<(), SkError> {
pub fn stroke_text(
&mut self,
text: &str,
x: f32,
y: f32,
max_width: f32,
) -> result::Result<(), SkError> {
let stroke_paint = self.stroke_paint()?;
self.draw_text(text, x, y, &stroke_paint)?;
self.draw_text(text, x, y, max_width, &stroke_paint)?;
Ok(())
}

Expand All @@ -228,9 +236,15 @@ impl Context {
}

#[inline(always)]
pub fn fill_text(&mut self, text: &str, x: f32, y: f32) -> result::Result<(), SkError> {
pub fn fill_text(
&mut self,
text: &str,
x: f32,
y: f32,
max_width: f32,
) -> result::Result<(), SkError> {
let fill_paint = self.fill_paint()?;
self.draw_text(text, x, y, &fill_paint)?;
self.draw_text(text, x, y, max_width, &fill_paint)?;
Ok(())
}

Expand Down Expand Up @@ -466,6 +480,7 @@ impl Context {
text: &str,
x: f32,
y: f32,
max_width: f32,
paint: &Paint,
) -> result::Result<(), SkError> {
let state = self.states.last().unwrap();
Expand All @@ -480,6 +495,7 @@ impl Context {
text,
x,
y,
max_width,
weight,
stretch as u32,
slant,
Expand All @@ -497,6 +513,7 @@ impl Context {
text,
x,
y,
max_width,
weight,
stretch as u32,
slant,
Expand Down Expand Up @@ -1064,10 +1081,15 @@ fn stroke_text(ctx: CallContext) -> Result<JsUndefined> {
let text = ctx.get::<JsString>(0)?.into_utf8()?;
let x: f64 = ctx.get::<JsNumber>(1)?.try_into()?;
let y: f64 = ctx.get::<JsNumber>(2)?.try_into()?;
let max_width = if ctx.length == 3 {
MAX_TEXT_WIDTH
} else {
ctx.get::<JsNumber>(3)?.get_double()? as f32
};

let this = ctx.this_unchecked::<JsObject>();
let context_2d = ctx.env.unwrap::<Context>(&this)?;
context_2d.stroke_text(text.as_str()?, x as f32, y as f32)?;
context_2d.stroke_text(text.as_str()?, x as f32, y as f32, max_width)?;

ctx.env.get_undefined()
}
Expand All @@ -1092,10 +1114,15 @@ fn fill_text(ctx: CallContext) -> Result<JsUndefined> {
let text = ctx.get::<JsString>(0)?.into_utf8()?;
let x: f64 = ctx.get::<JsNumber>(1)?.try_into()?;
let y: f64 = ctx.get::<JsNumber>(2)?.try_into()?;
let max_width = if ctx.length == 3 {
MAX_TEXT_WIDTH
} else {
ctx.get::<JsNumber>(3)?.get_double()? as f32
};

let this = ctx.this_unchecked::<JsObject>();
let context_2d = ctx.env.unwrap::<Context>(&this)?;
context_2d.fill_text(text.as_str()?, x as f32, y as f32)?;
context_2d.fill_text(text.as_str()?, x as f32, y as f32, max_width)?;

ctx.env.get_undefined()
}
Expand Down
21 changes: 5 additions & 16 deletions src/sk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,10 +303,12 @@ mod ffi {
text_len: usize,
x: f32,
y: f32,
max_width: f32,
weight: i32,
width: i32,
slant: i32,
c_typeface_font_provider: *mut skiac_typeface_font_provider,
c_font_mgr: *mut skiac_font_mgr,
font_size: f32,
font_family: *const ::std::os::raw::c_char,
baseline_offset: f32,
Expand Down Expand Up @@ -661,12 +663,6 @@ mod ffi {
// FontMgr
pub fn skiac_font_mgr_ref_default() -> *mut skiac_font_mgr;

pub fn skiac_font_mgr_make_from_data(
data: *mut *mut u8,
len: *mut *mut usize,
number: i32,
) -> *mut skiac_font_mgr;

pub fn skiac_font_mgr_get_default_fonts_count(c_font_mgr: *mut skiac_font_mgr) -> u32;

pub fn skiac_font_mgr_get_family(
Expand Down Expand Up @@ -1607,6 +1603,7 @@ impl Canvas {
text: &str,
x: f32,
y: f32,
max_width: f32,
weight: u32,
width: u32,
slant: FontStyle,
Expand Down Expand Up @@ -1636,10 +1633,12 @@ impl Canvas {
text.len(),
x,
y,
max_width,
weight as i32,
width as i32,
slant as i32,
typeface_font_provider.0,
typeface_font_provider.1,
font_size,
c_font_family.as_ptr(),
baseline_offset,
Expand Down Expand Up @@ -2872,16 +2871,6 @@ impl Drop for FontMetrics {
}
}

#[derive(Debug, Clone)]
pub struct FontMgr(*mut ffi::skiac_font_mgr);

impl FontMgr {
#[inline]
pub fn new() -> Self {
Self(unsafe { ffi::skiac_font_mgr_ref_default() })
}
}

#[inline(always)]
fn radians_to_degrees(rad: f32) -> f32 {
(rad / PI) * 180.0
Expand Down

1 comment on commit ccf33f3

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Benchmark suite Current: ccf33f3 Previous: 9e94279 Ratio
Draw house#skia-canvas 28 ops/sec (±0.06%) 20.6 ops/sec (±0.46%) 0.74
Draw house#node-canvas 21 ops/sec (±0.21%) 21.2 ops/sec (±0.58%) 1.01
Draw house#@napi-rs/skia 25 ops/sec (±0.31%) 22 ops/sec (±0.6%) 0.88
Draw gradient#skia-canvas 27 ops/sec (±0.42%) 19.6 ops/sec (±0.57%) 0.73
Draw gradient#node-canvas 20 ops/sec (±0.16%) 20.6 ops/sec (±0.76%) 1.03
Draw gradient#@napi-rs/skia 25 ops/sec (±0.04%) 20.9 ops/sec (±0.56%) 0.84

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.