diff --git a/CHANGELOG.md b/CHANGELOG.md
index f0042ce2..fc69c5b2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,39 @@
+# 0.93.1 - Aug 11, 2021
+
+Added:
+
+- ShapingOptions
+ - Groups together FontMgr, FontFeature[], leftToRight
+ - Adds approximateSpaces and approximatePunctuation
+- FontMgrRunIterator(String, Font)
+- Shaper::shapeLine(String, Font)
+
+Changed:
+
+- AnimationDisposalMethod -> AnimationDisposalMode
+
+Signature updated from:
+
+ - TextLine::make(String, Font, FontFeature[], boolean)
+ - FontMgrRunIterator(ManagedString, boolean, Font, FontMgr)
+ - FontMgrRunIterator(String, Font, FontMgr)
+ - Shaper::shape(String, Font, boolean, float, Point)
+ - Shaper::shape(String, Font, FontMgr, FontFeature[], boolean, float, RunHandler)
+ - Shaper::shape(String, Iterator, Iterator, Iterator, Iterator, FontFeature[], float width, RunHandler)
+ - Shaper::shape(ManagedString, Iterator, Iterator, Iterator, Iterator, FontFeature[], float width, RunHandler)
+ - Shaper::shapeLine(String, Font, FontFeature[], boolean)
+
+to:
+
+ - TextLine::make(String, Font, ShapingOptions)
+ - FontMgrRunIterator(ManagedString, boolean, Font, ShapingOptions)
+ - FontMgrRunIterator(String, Font, ShapingOptions)
+ - Shaper::shape(String, Font, ShapingOptions, float, Point)
+ - Shaper::shape(String, Font, FontMgr, ShapingOptions, float, RunHandler)
+ - Shaper::shape(String, Iterator, Iterator, Iterator, Iterator, ShapingOptions, float width, RunHandler)
+ - Shaper::shape(ManagedString, Iterator, Iterator, Iterator, Iterator, ShapingOptions, float width, RunHandler)
+ - Shaper::shapeLine(String, Font, ShapingOptions)
+
# 0.93.0 - Aug 10, 2021
Changed:
diff --git a/examples/jwm/script/run.py b/examples/jwm/script/run.py
index 6cb22067..168897e4 100755
--- a/examples/jwm/script/run.py
+++ b/examples/jwm/script/run.py
@@ -8,7 +8,7 @@
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--skija-version')
- parser.add_argument('--jwm-version', default='0.1.3')
+ parser.add_argument('--jwm-version', default='0.1.170')
parser.add_argument('--jwm-dir', default=None)
(args, _) = parser.parse_known_args()
@@ -46,7 +46,7 @@ def main():
os.chdir(common.root + '/examples/jwm')
sources = common.glob('src', '*.java') + common.glob('../scenes/src', '*.java')
- common.javac(sources, 'target/classes', classpath = classpath)
+ common.javac(sources, 'target/classes', classpath = classpath, release = '15', opts = ['--enable-preview'])
# Java
common.check_call([
@@ -54,6 +54,7 @@ def main():
'--class-path', common.classpath_separator.join(['target/classes'] + classpath)]
+ (['-XstartOnFirstThread'] if 'macos' == common.system else [])
+ ['-Djava.awt.headless=true',
+ '--enable-preview',
'-enableassertions',
'-enablesystemassertions',
'-Xcheck:jni',
diff --git a/examples/jwm/src/Main.java b/examples/jwm/src/Main.java
index 53a2be48..5a461dae 100644
--- a/examples/jwm/src/Main.java
+++ b/examples/jwm/src/Main.java
@@ -23,15 +23,17 @@ public class Main implements Consumer {
public Main() {
Stats.enabled = true;
- _window = App.makeWindow();
- _window.setEventListener(this);
- changeLayer();
- var scale = _window.getScale();
- _window.resize((int) (1440 * scale), (int) (810 * scale));
- _window.move((int) (240 * scale), (int) (135 * scale));
- _window.show();
- accept(EventReconfigure.INSTANCE);
- _window.requestFrame();
+ App.makeWindow((window) -> {
+ _window = window;
+ _window.setEventListener(this);
+ changeLayer();
+ var scale = _window.getScale();
+ _window.resize((int) (1440 * scale), (int) (810 * scale));
+ _window.move((int) (240 * scale), (int) (135 * scale));
+ _window.show();
+ accept(EventReconfigure.INSTANCE);
+ _window.requestFrame();
+ });
}
public void paint() {
@@ -78,28 +80,35 @@ public void accept(Event e) {
} else if (e instanceof EventMouseMove) {
_xpos = (int) (((EventMouseMove) e).getX() / _window.getScale());
_ypos = (int) (((EventMouseMove) e).getY() / _window.getScale());
- } else if (e instanceof EventKeyboard) {
- EventKeyboard eventKeyboard = (EventKeyboard) e;
+ } else if (e instanceof EventKeyboard eventKeyboard) {
if (eventKeyboard.isPressed() == true) {
- if (eventKeyboard.getKeyCode() == 1) { // s
- Scenes.stats = !Scenes.stats;
- Stats.enabled = Scenes.stats;
- } else if (eventKeyboard.getKeyCode() == 5) { // g
- System.out.println("Before GC " + Stats.allocated);
- System.gc();
- } else if (eventKeyboard.getKeyCode() == 37) { // l
- _layerIdx = (_layerIdx + _layers.length - 1) % _layers.length;
- changeLayer();
- } else if (eventKeyboard.getKeyCode() == 123) { // ←
- Scenes.prevScene();
- } else if (eventKeyboard.getKeyCode() == 124) { // →
- Scenes.nextScene();
- } else if (eventKeyboard.getKeyCode() == 125) { // ↓
- Scenes.currentScene().changeVariant(1);
- } else if (eventKeyboard.getKeyCode() == 126) { // ↑
- Scenes.currentScene().changeVariant(-1);
- } else
- System.out.println("Key pressed: " + eventKeyboard.getKeyCode());
+ switch (eventKeyboard.getKey()) {
+ case S -> {
+ Scenes.stats = !Scenes.stats;
+ Stats.enabled = Scenes.stats;
+ }
+ case G -> {
+ System.out.println("Before GC " + Stats.allocated);
+ System.gc();
+ }
+ case L -> {
+ _layerIdx = (_layerIdx + _layers.length - 1) % _layers.length;
+ changeLayer();
+ }
+ case LEFT ->
+ Scenes.prevScene();
+
+ case RIGHT ->
+ Scenes.nextScene();
+
+ case DOWN ->
+ Scenes.currentScene().changeVariant(1);
+
+ case UP ->
+ Scenes.currentScene().changeVariant(-1);
+ default ->
+ System.out.println("Key pressed: " + eventKeyboard.getKey());
+ }
}
} else if (e instanceof EventFrame) {
paint();
diff --git a/examples/scenes/src/RunHandlerScene.java b/examples/scenes/src/RunHandlerScene.java
index 9ec7673d..d89b2d6a 100644
--- a/examples/scenes/src/RunHandlerScene.java
+++ b/examples/scenes/src/RunHandlerScene.java
@@ -15,6 +15,13 @@ public class RunHandlerScene extends Scene {
public RunHandlerScene() {
lato36 = new Font(Typeface.makeFromFile(file("fonts/Lato-Regular.ttf")), 36);
inter9 = new Font(inter, 9);
+
+ _variants = new String[] {
+ "Approximate All",
+ "Approximate Spaces",
+ "Approximate Punctuation",
+ "Approximate None",
+ };
}
@Override
@@ -26,15 +33,22 @@ public void draw(Canvas canvas, int width, int height, float dpi, int xpos, int
var tbHandler = new TextBlobBuilderRunHandler(text, new Point(0, 0));
var handler = new DebugTextBlobHandler().withRuns();)
{
+ var opts = switch (_variants[_variantIdx]) {
+ case "Approximate Spaces" -> ShapingOptions.DEFAULT.withApproximatePunctuation(false);
+ case "Approximate Punctuation" -> ShapingOptions.DEFAULT.withApproximateSpaces(false);
+ case "Approximate None" -> ShapingOptions.DEFAULT.withApproximateSpaces(false).withApproximatePunctuation(false);
+ default -> ShapingOptions.DEFAULT;
+ };
+
// TextBlobBuilderRunHandler
- shaper.shape(text, lato36, FontMgr.getDefault(), null, true, width - 40, tbHandler);
+ shaper.shape(text, lato36, opts, width - 40, tbHandler);
try (var blob = tbHandler.makeBlob()) {
canvas.drawTextBlob(blob, 0, 0, textFill);
canvas.translate(0, blob.getBounds().getBottom() + 20);
}
// DebugTextBlobHandler
- shaper.shape(text, lato36, FontMgr.getDefault(), null, true, width - 40, handler);
+ shaper.shape(text, lato36, opts, width - 40, handler);
try (var blob = handler.makeBlob()) {
canvas.drawTextBlob(blob, 0, 0, textFill);
@@ -69,7 +83,6 @@ public void draw(Canvas canvas, int width, int height, float dpi, int xpos, int
builder.appendRun(inter9, "clusters " + Arrays.toString(run.getClusters()), 0, yPos + lh * 7);
try (var detailsBlob = builder.build(); ) {
-
// try to fit in
var detailsWidth = detailsBlob.getBounds().getWidth();
var detailsHeight = detailsBlob.getBounds().getHeight();
diff --git a/examples/scenes/src/RunIteratorScene.java b/examples/scenes/src/RunIteratorScene.java
index de7712cd..46a5c6bf 100644
--- a/examples/scenes/src/RunIteratorScene.java
+++ b/examples/scenes/src/RunIteratorScene.java
@@ -42,17 +42,17 @@ public void draw(Canvas canvas, int width, int height, float dpi, int xpos, int
var bidiIter = new TrivialBidiRunIterator(text, Bidi.DIRECTION_LEFT_TO_RIGHT);
var scriptIter = new TrivialScriptRunIterator(text, "latn");
var langIter = new TrivialLanguageRunIterator(text, "en-US");
- shaper.shape(text, fontIter, bidiIter, scriptIter, langIter, null, width - 40, handler);
+ shaper.shape(text, fontIter, bidiIter, scriptIter, langIter, ShapingOptions.DEFAULT, width - 40, handler);
drawBlob(canvas, handler, "All trivial");
}
try (var handler = new DebugTextBlobHandler().withRuns();
- var fontIter = new FontMgrRunIterator(text, lato36, null);)
+ var fontIter = new FontMgrRunIterator(text, lato36);)
{
var bidiIter = new TrivialBidiRunIterator(text, Bidi.DIRECTION_LEFT_TO_RIGHT);
var scriptIter = new TrivialScriptRunIterator(text, "latn");
var langIter = new TrivialLanguageRunIterator(text, "en-US");
- shaper.shape(text, fontIter, bidiIter, scriptIter, langIter, null, width - 40, handler);
+ shaper.shape(text, fontIter, bidiIter, scriptIter, langIter, ShapingOptions.DEFAULT, width - 40, handler);
drawBlob(canvas, handler, "FontMgrRunIterator");
}
@@ -69,7 +69,7 @@ public void draw(Canvas canvas, int width, int height, float dpi, int xpos, int
var bidiIter = new TrivialBidiRunIterator(text, Bidi.DIRECTION_LEFT_TO_RIGHT);
var scriptIter = new TrivialScriptRunIterator(text, "latn");
var langIter = new TrivialLanguageRunIterator(text, "en-US");
- shaper.shape(text, fontIter, bidiIter, scriptIter, langIter, null, width - 40, handler);
+ shaper.shape(text, fontIter, bidiIter, scriptIter, langIter, ShapingOptions.DEFAULT, width - 40, handler);
drawBlob(canvas, handler, "CustomFontRunIterator");
}
@@ -79,7 +79,7 @@ public void draw(Canvas canvas, int width, int height, float dpi, int xpos, int
var fontIter = new TrivialFontRunIterator(text, lato36);
var scriptIter = new TrivialScriptRunIterator(text, "latn");
var langIter = new TrivialLanguageRunIterator(text, "en-US");
- shaper.shape(text, fontIter, bidiIter, scriptIter, langIter, null, width - 40, handler);
+ shaper.shape(text, fontIter, bidiIter, scriptIter, langIter, ShapingOptions.DEFAULT, width - 40, handler);
drawBlob(canvas, handler, "IcuBidiRunIterator");
}
@@ -89,7 +89,7 @@ public void draw(Canvas canvas, int width, int height, float dpi, int xpos, int
var bidiIter = new JavaTextBidiRunIterator(text);
var scriptIter = new TrivialScriptRunIterator(text, "latn");
var langIter = new TrivialLanguageRunIterator(text, "en-US");
- shaper.shape(text, fontIter, bidiIter, scriptIter, langIter, null, width - 40, handler);
+ shaper.shape(text, fontIter, bidiIter, scriptIter, langIter, ShapingOptions.DEFAULT, width - 40, handler);
drawBlob(canvas, handler, "JavaTextBidiRunIterator");
}
@@ -99,17 +99,17 @@ public void draw(Canvas canvas, int width, int height, float dpi, int xpos, int
var fontIter = new TrivialFontRunIterator(text, lato36);
var bidiIter = new TrivialBidiRunIterator(text, Bidi.DIRECTION_LEFT_TO_RIGHT);
var langIter = new TrivialLanguageRunIterator(text, "en-US");
- shaper.shape(text, fontIter, bidiIter, scriptIter, langIter, null, width - 40, handler);
+ shaper.shape(text, fontIter, bidiIter, scriptIter, langIter, ShapingOptions.DEFAULT, width - 40, handler);
drawBlob(canvas, handler, "HbIcuScriptRunIterator");
}
try (var handler = new DebugTextBlobHandler().withRuns();
- var fontIter = new FontMgrRunIterator(text, lato36, null);
+ var fontIter = new FontMgrRunIterator(text, lato36);
var bidiIter = new IcuBidiRunIterator(text, Bidi.DIRECTION_LEFT_TO_RIGHT);
var scriptIter = new HbIcuScriptRunIterator(text);)
{
var langIter = new TrivialLanguageRunIterator(text, "en-US");
- shaper.shape(text, fontIter, bidiIter, scriptIter, langIter, null, width - 40, handler);
+ shaper.shape(text, fontIter, bidiIter, scriptIter, langIter, ShapingOptions.DEFAULT, width - 40, handler);
drawBlob(canvas, handler, "All native");
}
}
diff --git a/examples/scenes/src/Scenes.java b/examples/scenes/src/Scenes.java
index 32ee174c..fa695f42 100644
--- a/examples/scenes/src/Scenes.java
+++ b/examples/scenes/src/Scenes.java
@@ -6,7 +6,7 @@
public class Scenes {
public static TreeMap scenes;
- public static String currentScene = "Runtime Effect";
+ public static String currentScene = "Run Handler";
public static HUD hud = new HUD();
public static boolean stats = true;
diff --git a/examples/scenes/src/ShapersScene.java b/examples/scenes/src/ShapersScene.java
index 9ebcf09b..6dd121ad 100644
--- a/examples/scenes/src/ShapersScene.java
+++ b/examples/scenes/src/ShapersScene.java
@@ -17,31 +17,31 @@ public void drawWithShaper(Canvas canvas, float maxWidth, int color, String name
fill.setColor(color);
String text = "Incomprehensibilities word word word word ararar123456 +++ <-> *** c̝̣̱̲͈̝ͨ͐̈ͪͨ̃ͥͅh̙̬̿̂a̯͎͍̜͐͌͂̚o̬s͉̰͊̀ " + name;
- try (var blob = shaper.shape(text, firaCode11, null, true, maxWidth, new Point(0, 0)); ) {
+ try (var blob = shaper.shape(text, firaCode11, ShapingOptions.DEFAULT, maxWidth, new Point(0, 0)); ) {
canvas.drawTextBlob(blob, 0, 0, fill);
canvas.translate(0, blob.getBounds().getHeight());
}
- try (var blob = shaper.shape(text + " RTL", firaCode11, null, false, maxWidth, new Point(0, 0)); ) {
+ try (var blob = shaper.shape(text + " RTL", firaCode11, ShapingOptions.DEFAULT.withLeftToRight(false), maxWidth, new Point(0, 0)); ) {
canvas.drawTextBlob(blob, 0, 0, fill);
canvas.translate(0, blob.getBounds().getHeight());
}
String features = "ss01 cv01 onum -calt";
- try (var blob = shaper.shape(text + " " + features, firaCode11, FontFeature.parse(features), true, maxWidth, new Point(0, 0)); ) {
+ try (var blob = shaper.shape(text + " " + features, firaCode11, ShapingOptions.DEFAULT.withFeatures(features), maxWidth, new Point(0, 0)); ) {
canvas.drawTextBlob(blob, 0, 0, fill);
canvas.translate(0, blob.getBounds().getHeight());
}
features = "ss01[42:45] cv01[42:45] onum[48:51] calt[59:60]=0";
- try (var blob = shaper.shape(text + " " + features, firaCode11, FontFeature.parse(features), true, maxWidth, new Point(0, 0)); ) {
+ try (var blob = shaper.shape(text + " " + features, firaCode11, ShapingOptions.DEFAULT.withFeatures(features), maxWidth, new Point(0, 0)); ) {
canvas.drawTextBlob(blob, 0, 0, fill);
canvas.translate(0, blob.getBounds().getHeight());
}
float height = 0;
- try (var blob = shaper.shape(text, firaCode11, null, true, 75, new Point(0, 0)); ) {
+ try (var blob = shaper.shape(text, firaCode11, ShapingOptions.DEFAULT, 75, new Point(0, 0)); ) {
canvas.drawTextBlob(blob, 0, 0, fill);
Rect bounds = blob.getBounds();
canvas.drawRect(Rect.makeXYWH(0, 0, 75, bounds.getHeight()), stroke);
diff --git a/examples/scenes/src/TextShapeBenchScene.java b/examples/scenes/src/TextShapeBenchScene.java
index f55bd204..b36cd988 100644
--- a/examples/scenes/src/TextShapeBenchScene.java
+++ b/examples/scenes/src/TextShapeBenchScene.java
@@ -18,11 +18,11 @@ public TextShapeBenchScene() {
_variants = new String[] {
"Tabs Paragraph", "Tabs Paragraph No-Cache", "Tabs TextLine",
"Emoji Paragraph", "Emoji Paragraph No-Cache", "Emoji TextLine",
- "Greek Paragraph", "Greek Paragraph No-Cache", "Greek TextLine",
+ "Greek Paragraph", "Greek Paragraph No-Cache", "Greek TextLine", "Greek TextLine No-Approx",
"Notdef Paragraph", "Notdef Paragraph No-Cache", "Notdef TextLine",
"English Paragraph", "English Paragraph No-Cache", "English TextLine",
};
- _variantIdx = 5;
+ _variantIdx = 9;
font = new Font(jbMono, fontSize).setSubpixel(true);
metrics = font.getMetrics();
@@ -67,12 +67,15 @@ else if ("English".equals(variant[0]))
}
}
}
- } else {
+ } else { // TextLine
try (var shaper = Shaper.makeShapeDontWrapOrReorder();) {
for (int i = 1; true; ++i) {
float y = i * padding;
if (y > height - padding) break;
- try (var line = shaper.shapeLine(i + " [" + text + "]", font, null, true);) {
+ try (var line = variant.length > 2 // No-Approx
+ ? shaper.shapeLine(i + " [" + text + "]", font, ShapingOptions.DEFAULT.withApproximateSpaces(false).withApproximatePunctuation(false))
+ : shaper.shapeLine(i + " [" + text + "]", font))
+ {
canvas.drawTextLine(line, padding, y - metrics.getAscent(), blackFill);
canvas.drawRect(Rect.makeXYWH(padding, y, line.getWidth(), metrics.getHeight()), redStroke);
for (float x: TextLine._nGetRunPositions(line._ptr))
diff --git a/examples/scenes/src/WallOfTextScene.java b/examples/scenes/src/WallOfTextScene.java
index 6723ddb0..190194e6 100644
--- a/examples/scenes/src/WallOfTextScene.java
+++ b/examples/scenes/src/WallOfTextScene.java
@@ -92,7 +92,7 @@ void drawParagraph(Canvas canvas, float fontSize, float padding, float textWidth
TextBlob makeBlob(String text, Shaper shaper, float textWidth) {
if (_variants[_variantIdx].startsWith("JVM RunHandler")) {
try (var handler = new DebugTextBlobHandler();) {
- shaper.shape(text, font, FontMgr.getDefault(), null, true, textWidth, handler);
+ shaper.shape(text, font, ShapingOptions.DEFAULT, textWidth, handler);
return handler.makeBlob();
}
} else
diff --git a/platform/cc/interop.cc b/platform/cc/interop.cc
index 4b76bd74..e61b2757 100644
--- a/platform/cc/interop.cc
+++ b/platform/cc/interop.cc
@@ -296,6 +296,19 @@ namespace skija {
}
}
+ namespace FontMgr {
+ jclass cls;
+
+ void onLoad(JNIEnv* env) {
+ jclass local = env->FindClass("org/jetbrains/skija/FontMgr");
+ cls = static_cast(env->NewGlobalRef(local));
+ }
+
+ void onUnload(JNIEnv* env) {
+ env->DeleteGlobalRef(cls);
+ }
+ }
+
namespace FontStyle {
SkFontStyle fromJava(jint style) {
return SkFontStyle(style & 0xFFFF, (style >> 16) & 0xFF, static_cast((style >> 24) & 0xFF));
diff --git a/platform/cc/interop.hh b/platform/cc/interop.hh
index e4e8e5b9..cc47d9cb 100644
--- a/platform/cc/interop.hh
+++ b/platform/cc/interop.hh
@@ -152,6 +152,12 @@ namespace skija {
jobject toJava(JNIEnv* env, const SkFontMetrics& m);
}
+ namespace FontMgr {
+ extern jclass cls;
+ void onLoad(JNIEnv* env);
+ void onUnload(JNIEnv* env);
+ }
+
namespace FontStyle {
SkFontStyle fromJava(jint style);
jint toJava(const SkFontStyle& fs);
diff --git a/platform/cc/shaper/FontMgrRunIterator.cc b/platform/cc/shaper/FontMgrRunIterator.cc
index f1609dc1..a4ee3a6a 100644
--- a/platform/cc/shaper/FontMgrRunIterator.cc
+++ b/platform/cc/shaper/FontMgrRunIterator.cc
@@ -6,15 +6,25 @@
#include "SkShaper.h"
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_shaper_FontMgrRunIterator__1nMake
- (JNIEnv* env, jclass jclass, jlong textPtr, jlong fontPtr, jlong fontMgrPtr) {
+ (JNIEnv* env, jclass jclass, jlong textPtr, jlong fontPtr, jobject opts) {
SkString* text = reinterpret_cast(static_cast(textPtr));
SkFont* font = reinterpret_cast(static_cast(fontPtr));
- sk_sp fontMgr = fontMgrPtr == 0 ? SkFontMgr::RefDefault() : sk_ref_sp(reinterpret_cast(static_cast(fontMgrPtr)));
-
+ jobject fontMgrObj = env->GetObjectField(opts, skija::shaper::ShapingOptions::_fontMgr);
+ sk_sp fontMgr = fontMgrObj == nullptr
+ ? SkFontMgr::RefDefault()
+ : sk_ref_sp(reinterpret_cast(skija::impl::Native::fromJava(env, fontMgrObj, skija::FontMgr::cls)));
std::shared_ptr graphemeIter = skija::shaper::graphemeBreakIterator(*text);
if (!graphemeIter) return 0;
- auto instance = new FontRunIterator(text->c_str(), text->size(), *font, fontMgr, graphemeIter);
+ auto instance = new FontRunIterator(
+ text->c_str(),
+ text->size(),
+ *font,
+ fontMgr,
+ graphemeIter,
+ env->GetBooleanField(opts, skija::shaper::ShapingOptions::_approximateSpaces),
+ env->GetBooleanField(opts, skija::shaper::ShapingOptions::_approximatePunctuation)
+ );
return reinterpret_cast(instance);
}
diff --git a/platform/cc/shaper/FontRunIterator.cc b/platform/cc/shaper/FontRunIterator.cc
index 9d29076c..c6aac066 100644
--- a/platform/cc/shaper/FontRunIterator.cc
+++ b/platform/cc/shaper/FontRunIterator.cc
@@ -60,7 +60,11 @@ void FontRunIterator::consume() {
SkUnichar u = utf8_next(&ptr, clusterEnd);
// Do not switch font on control, whitespace or punct
- if (ptr == clusterEnd && fCurrentFont->getTypeface()->unicharToGlyph(u) != 0 && (u_iscntrl(u) || u_isWhitespace(u) || u_ispunct(u)))
+ if (ptr == clusterEnd
+ && fCurrentFont->getTypeface()->unicharToGlyph(u) != 0
+ && (u_iscntrl(u)
+ || (fApproximateSpaces && u_isWhitespace(u))
+ || (fApproximatePunctuation && u_ispunct(u))))
continue;
// End run if not using initial typeface and initial typeface has this character.
diff --git a/platform/cc/shaper/FontRunIterator.hh b/platform/cc/shaper/FontRunIterator.hh
index e2104439..e9bb60d4 100644
--- a/platform/cc/shaper/FontRunIterator.hh
+++ b/platform/cc/shaper/FontRunIterator.hh
@@ -12,7 +12,9 @@ public:
const char* requestName,
SkFontStyle requestStyle,
const SkShaper::LanguageRunIterator* lang,
- std::shared_ptr graphemeIter)
+ std::shared_ptr graphemeIter,
+ bool approximateSpaces,
+ bool approximatePunctuation)
: fCurrent(utf8)
, fBegin(utf8)
, fEnd(fCurrent + utf8Bytes)
@@ -24,13 +26,30 @@ public:
, fRequestStyle(requestStyle)
, fLanguage(lang)
, fGraphemeIter(graphemeIter)
+ , fApproximateSpaces(approximateSpaces)
+ , fApproximatePunctuation(approximatePunctuation)
{
fFont.setTypeface(font.refTypefaceOrDefault());
fFallbackFont.setTypeface(nullptr);
}
- FontRunIterator(const char* utf8, size_t utf8Bytes, const SkFont& font, sk_sp fallbackMgr, std::shared_ptr graphemeIter)
- : FontRunIterator(utf8, utf8Bytes, font, std::move(fallbackMgr), nullptr, font.refTypefaceOrDefault()->fontStyle(), nullptr, graphemeIter) {
+ FontRunIterator(const char* utf8,
+ size_t utf8Bytes,
+ const SkFont& font,
+ sk_sp fallbackMgr,
+ std::shared_ptr graphemeIter,
+ bool approximateSpaces,
+ bool approximatePunctuation)
+ : FontRunIterator(utf8,
+ utf8Bytes,
+ font,
+ std::move(fallbackMgr),
+ nullptr,
+ font.refTypefaceOrDefault()->fontStyle(),
+ nullptr,
+ graphemeIter,
+ approximateSpaces,
+ approximatePunctuation) {
}
~FontRunIterator() {
@@ -62,4 +81,6 @@ private:
SkFontStyle const fRequestStyle;
SkShaper::LanguageRunIterator const * const fLanguage;
std::shared_ptr fGraphemeIter;
+ bool fApproximateSpaces;
+ bool fApproximatePunctuation;
};
\ No newline at end of file
diff --git a/platform/cc/shaper/Shaper.cc b/platform/cc/shaper/Shaper.cc
index 69e7b46d..830b7b2c 100644
--- a/platform/cc/shaper/Shaper.cc
+++ b/platform/cc/shaper/Shaper.cc
@@ -56,18 +56,26 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_shaper_Shaper__1nMak
}
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_shaper_Shaper__1nShapeBlob
- (JNIEnv* env, jclass jclass, jlong ptr, jstring textObj, jlong fontPtr, jobjectArray featuresArr, jboolean leftToRight, jfloat width, jfloat offsetX, jfloat offsetY) {
+ (JNIEnv* env, jclass jclass, jlong ptr, jstring textObj, jlong fontPtr, jobject opts, jfloat width, jfloat offsetX, jfloat offsetY) {
SkShaper* instance = reinterpret_cast(static_cast(ptr));
SkString text = skString(env, textObj);
std::shared_ptr graphemeIter = skija::shaper::graphemeBreakIterator(text);
if (!graphemeIter) return 0;
SkFont* font = reinterpret_cast(static_cast(fontPtr));
- std::vector features = skija::FontFeature::fromJavaArray(env, featuresArr);
-
- std::unique_ptr fontRunIter(new FontRunIterator(text.c_str(), text.size(), *font, SkFontMgr::RefDefault(), graphemeIter));
+ std::vector features = skija::shaper::ShapingOptions::getFeatures(env, opts);
+
+ std::unique_ptr fontRunIter(new FontRunIterator(
+ text.c_str(),
+ text.size(),
+ *font,
+ SkFontMgr::RefDefault(),
+ graphemeIter,
+ env->GetBooleanField(opts, skija::shaper::ShapingOptions::_approximateSpaces),
+ env->GetBooleanField(opts, skija::shaper::ShapingOptions::_approximatePunctuation)
+ ));
if (!fontRunIter) return 0;
- uint8_t defaultBiDiLevel = leftToRight ? UBIDI_DEFAULT_LTR : UBIDI_DEFAULT_RTL;
+ uint8_t defaultBiDiLevel = env->GetBooleanField(opts, skija::shaper::ShapingOptions::_leftToRight) ? UBIDI_DEFAULT_LTR : UBIDI_DEFAULT_RTL;
std::unique_ptr bidiRunIter(SkShaper::MakeBiDiRunIterator(text.c_str(), text.size(), defaultBiDiLevel));
if (!bidiRunIter) return 0;
@@ -85,7 +93,7 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_shaper_Shaper__1nSha
}
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_shaper_Shaper__1nShapeLine
- (JNIEnv* env, jclass jclass, jlong ptr, jstring textObj, jlong fontPtr, jobjectArray featuresArr, jboolean leftToRight) {
+ (JNIEnv* env, jclass jclass, jlong ptr, jstring textObj, jlong fontPtr, jobject opts) {
SkShaper* instance = reinterpret_cast(static_cast(ptr));
SkString text = skString(env, textObj);
@@ -93,12 +101,19 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_shaper_Shaper__1nSha
if (!graphemeIter) return 0;
SkFont* font = reinterpret_cast(static_cast(fontPtr));
- std::vector features = skija::FontFeature::fromJavaArray(env, featuresArr);
-
- std::unique_ptr fontRunIter(new FontRunIterator(text.c_str(), text.size(), *font, SkFontMgr::RefDefault(), graphemeIter));
+ std::vector features = skija::shaper::ShapingOptions::getFeatures(env, opts);
+
+ std::unique_ptr fontRunIter(new FontRunIterator(
+ text.c_str(),
+ text.size(),
+ *font,
+ SkFontMgr::RefDefault(),
+ graphemeIter,
+ env->GetBooleanField(opts, skija::shaper::ShapingOptions::_approximateSpaces),
+ env->GetBooleanField(opts, skija::shaper::ShapingOptions::_approximatePunctuation)));
if (!fontRunIter) return 0;
- uint8_t defaultBiDiLevel = leftToRight ? UBIDI_DEFAULT_LTR : UBIDI_DEFAULT_RTL;
+ uint8_t defaultBiDiLevel = env->GetBooleanField(opts, skija::shaper::ShapingOptions::_leftToRight) ? UBIDI_DEFAULT_LTR : UBIDI_DEFAULT_RTL;
std::unique_ptr bidiRunIter(SkShaper::MakeBiDiRunIterator(text.c_str(), text.size(), defaultBiDiLevel));
if (!bidiRunIter) return 0;
@@ -314,8 +329,7 @@ class SkijaRunHandler: public SkShaper::RunHandler {
};
extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skija_shaper_Shaper__1nShape
- (JNIEnv* env, jclass jclass, jlong ptr, jlong textPtr, jobject fontRunIterObj, jobject bidiRunIterObj, jobject scriptRunIterObj, jobject languageRunIterObj,
- jobjectArray featuresArr, jfloat width, jobject runHandlerObj)
+ (JNIEnv* env, jclass jclass, jlong ptr, jlong textPtr, jobject fontRunIterObj, jobject bidiRunIterObj, jobject scriptRunIterObj, jobject languageRunIterObj, jobject opts, jfloat width, jobject runHandlerObj)
{
SkShaper* instance = reinterpret_cast(static_cast(ptr));
SkString* text = reinterpret_cast(static_cast(textPtr));
@@ -337,7 +351,7 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skija_shaper_Shaper__1nShap
auto languageRunIter = SkijaLanguageRunIterator(env, languageRunIterObj, *text);
- std::vector features = skija::FontFeature::fromJavaArray(env, featuresArr);
+ std::vector features = skija::shaper::ShapingOptions::getFeatures(env, opts);
auto nativeRunHandler = (SkShaper::RunHandler*) skija::impl::Native::fromJava(env, runHandlerObj, skija::shaper::TextBlobBuilderRunHandler::cls);
std::unique_ptr localRunHandler;
diff --git a/platform/cc/shaper/interop.cc b/platform/cc/shaper/interop.cc
index 671d8f53..8cdc8b9f 100644
--- a/platform/cc/shaper/interop.cc
+++ b/platform/cc/shaper/interop.cc
@@ -153,6 +153,27 @@ namespace skija {
}
}
+ namespace ShapingOptions {
+ jfieldID _fontMgr;
+ jfieldID _features;
+ jfieldID _leftToRight;
+ jfieldID _approximateSpaces;
+ jfieldID _approximatePunctuation;
+
+ void onLoad(JNIEnv* env) {
+ jclass cls = env->FindClass("org/jetbrains/skija/shaper/ShapingOptions");
+ _fontMgr = env->GetFieldID(cls, "_fontMgr", "Lorg/jetbrains/skija/FontMgr;");
+ _features = env->GetFieldID(cls, "_features", "[Lorg/jetbrains/skija/FontFeature;");
+ _leftToRight = env->GetFieldID(cls, "_leftToRight", "Z");
+ _approximateSpaces = env->GetFieldID(cls, "_approximateSpaces", "Z");
+ _approximatePunctuation = env->GetFieldID(cls, "_approximatePunctuation", "Z");
+ }
+
+ std::vector getFeatures(JNIEnv* env, jobject opts) {
+ return skija::FontFeature::fromJavaArray(env, (jobjectArray) env->GetObjectField(opts, _features));
+ }
+ }
+
namespace TextBlobBuilderRunHandler {
jclass cls;
@@ -176,6 +197,7 @@ namespace skija {
RunHandler::onLoad(env);
RunInfo::onLoad(env);
ScriptRun::onLoad(env);
+ ShapingOptions::onLoad(env);
TextBlobBuilderRunHandler::onLoad(env);
SkLoadICU();
diff --git a/platform/cc/shaper/interop.hh b/platform/cc/shaper/interop.hh
index 97b1f32d..db10248e 100644
--- a/platform/cc/shaper/interop.hh
+++ b/platform/cc/shaper/interop.hh
@@ -80,6 +80,17 @@ namespace skija {
void onLoad(JNIEnv* env);
}
+ namespace ShapingOptions {
+ extern jfieldID _fontMgr;
+ extern jfieldID _features;
+ extern jfieldID _leftToRight;
+ extern jfieldID _approximateSpaces;
+ extern jfieldID _approximatePunctuation;
+
+ void onLoad(JNIEnv* env);
+ std::vector getFeatures(JNIEnv* env, jobject opts);
+ }
+
namespace TextBlobBuilderRunHandler {
extern jclass cls;
diff --git a/script/clean.py b/script/clean.py
index b2078723..e60de0f8 100755
--- a/script/clean.py
+++ b/script/clean.py
@@ -7,6 +7,11 @@ def main():
shutil.rmtree('platform/build', ignore_errors = True)
shutil.rmtree('platform/target', ignore_errors = True)
shutil.rmtree('tests/target', ignore_errors = True)
+ shutil.rmtree('examples/lwjgl/target', ignore_errors = True)
+ shutil.rmtree('examples/kwinit/target', ignore_errors = True)
+ shutil.rmtree('examples/jwm/target', ignore_errors = True)
+ shutil.rmtree('examples/swt/target', ignore_errors = True)
+
return 0
if __name__ == '__main__':
diff --git a/script/common.py b/script/common.py
index d8b9f620..1ff16d48 100755
--- a/script/common.py
+++ b/script/common.py
@@ -52,7 +52,7 @@ def deps():
fetch_maven('org.jetbrains', 'annotations', '20.1.0'),
]
-def javac(sources, target, classpath = [], modulepath = [], add_modules = [], release = '11'):
+def javac(sources, target, classpath = [], modulepath = [], add_modules = [], release = '11', opts = []):
classes = {path.stem: path.stat().st_mtime for path in pathlib.Path(target).rglob('*.class') if '$' not in path.stem}
newer = lambda path: path.stem not in classes or path.stat().st_mtime > classes.get(path.stem)
new_sources = [path for path in sources if newer(pathlib.Path(path))]
@@ -61,7 +61,7 @@ def javac(sources, target, classpath = [], modulepath = [], add_modules = [], re
check_call([
'javac',
'-encoding', 'UTF8',
- '--release', release,
+ '--release', release] + opts + [
# '-J--illegal-access=permit',
# '-Xlint:deprecation',
# '-Xlint:unchecked',
diff --git a/shared/java/AnimationDisposalMethod.java b/shared/java/AnimationDisposalMode.java
similarity index 87%
rename from shared/java/AnimationDisposalMethod.java
rename to shared/java/AnimationDisposalMode.java
index efb7c6a6..e337fcb5 100644
--- a/shared/java/AnimationDisposalMethod.java
+++ b/shared/java/AnimationDisposalMode.java
@@ -8,7 +8,7 @@
*
* Names are based on the GIF 89a spec.
*/
-public enum AnimationDisposalMethod {
+public enum AnimationDisposalMode {
@ApiStatus.Internal
_UNUSED,
@@ -34,5 +34,5 @@ public enum AnimationDisposalMethod {
*/
RESTORE_PREVIOUS;
- @ApiStatus.Internal public static final AnimationDisposalMethod[] _values = values();
+ @ApiStatus.Internal public static final AnimationDisposalMode[] _values = values();
}
\ No newline at end of file
diff --git a/shared/java/AnimationFrameInfo.java b/shared/java/AnimationFrameInfo.java
index ed42a4a3..520072c8 100644
--- a/shared/java/AnimationFrameInfo.java
+++ b/shared/java/AnimationFrameInfo.java
@@ -10,7 +10,7 @@
public class AnimationFrameInfo {
@ApiStatus.Internal
public AnimationFrameInfo(int requiredFrame, int duration, boolean fullyReceived, int alphaTypeOrdinal, boolean hasAlphaWithinBounds, int disposalMethodOrdinal, int blendModeOrdinal, IRect frameRect) {
- this(requiredFrame, duration, fullyReceived, ColorAlphaType._values[alphaTypeOrdinal], hasAlphaWithinBounds, AnimationDisposalMethod._values[disposalMethodOrdinal], BlendMode._values[blendModeOrdinal], frameRect);
+ this(requiredFrame, duration, fullyReceived, ColorAlphaType._values[alphaTypeOrdinal], hasAlphaWithinBounds, AnimationDisposalMode._values[disposalMethodOrdinal], BlendMode._values[blendModeOrdinal], frameRect);
}
/**
@@ -20,7 +20,7 @@ public AnimationFrameInfo(int requiredFrame, int duration, boolean fullyReceived
*
* Note that this is the *earliest* frame that can be used
* for blending. Any frame from [_requiredFrame, i) can be
- * used, unless its getDisposalMethod() is {@link AnimationDisposalMethod#RESTORE_PREVIOUS}.
+ * used, unless its getDisposalMethod() is {@link AnimationDisposalMode#RESTORE_PREVIOUS}.
*/
@ApiStatus.Internal
public int _requiredFrame;
@@ -62,7 +62,7 @@ public AnimationFrameInfo(int requiredFrame, int duration, boolean fullyReceived
* How this frame should be modified before decoding the next one.
*/
@ApiStatus.Internal
- public AnimationDisposalMethod _disposalMethod;
+ public AnimationDisposalMode _disposalMethod;
/**
* How this frame should blend with the prior frame.
diff --git a/shared/java/TextLine.java b/shared/java/TextLine.java
index caab8fcb..790ba617 100644
--- a/shared/java/TextLine.java
+++ b/shared/java/TextLine.java
@@ -20,13 +20,13 @@ public static class _FinalizerHolder {
@NotNull @Contract("_, _ -> new")
public static TextLine make(String text, Font font) {
- return make(text, font, null, true);
+ return make(text, font, ShapingOptions.DEFAULT);
}
@NotNull @Contract("_, _, _, _ -> new")
- public static TextLine make(String text, Font font, @Nullable FontFeature[] features, boolean leftToRight) {
+ public static TextLine make(String text, Font font, ShapingOptions opts) {
try (Shaper shaper = Shaper.makeShapeDontWrapOrReorder();) {
- return shaper.shapeLine(text, font, features, leftToRight);
+ return shaper.shapeLine(text, font, opts);
}
}
diff --git a/shared/java/shaper/FontMgrRunIterator.java b/shared/java/shaper/FontMgrRunIterator.java
index 525273f5..03cd598a 100644
--- a/shared/java/shaper/FontMgrRunIterator.java
+++ b/shared/java/shaper/FontMgrRunIterator.java
@@ -8,16 +8,20 @@
public class FontMgrRunIterator extends ManagedRunIterator {
static { Library.staticLoad(); }
- public FontMgrRunIterator(ManagedString text, boolean manageText, Font font, FontMgr fontMgr) {
- super(_nMake(Native.getPtr(text), Native.getPtr(font), Native.getPtr(fontMgr)), text, manageText);
+ public FontMgrRunIterator(ManagedString text, boolean manageText, Font font, @NotNull ShapingOptions opts) {
+ super(_nMake(Native.getPtr(text), Native.getPtr(font), opts), text, manageText);
Stats.onNativeCall();
Reference.reachabilityFence(text);
Reference.reachabilityFence(font);
- Reference.reachabilityFence(fontMgr);
+ Reference.reachabilityFence(opts);
}
- public FontMgrRunIterator(String text, Font font, @Nullable FontMgr fontMgr) {
- this(new ManagedString(text), true, font, fontMgr);
+ public FontMgrRunIterator(String text, Font font, @NotNull ShapingOptions opts) {
+ this(new ManagedString(text), true, font, opts);
+ }
+
+ public FontMgrRunIterator(String text, Font font) {
+ this(new ManagedString(text), true, font, ShapingOptions.DEFAULT);
}
@Override
@@ -30,6 +34,6 @@ public FontRun next() {
}
}
- @ApiStatus.Internal public static native long _nMake(long textPtr, long fontPtr, long fontMgrPtr);
+ @ApiStatus.Internal public static native long _nMake(long textPtr, long fontPtr, ShapingOptions opts);
@ApiStatus.Internal public static native long _nGetCurrentFont(long ptr);
}
\ No newline at end of file
diff --git a/shared/java/shaper/Shaper.java b/shared/java/shaper/Shaper.java
index 042cbbf1..d73d74da 100644
--- a/shared/java/shaper/Shaper.java
+++ b/shared/java/shaper/Shaper.java
@@ -97,29 +97,25 @@ public static Shaper make(@Nullable FontMgr fontMgr) {
@Nullable @Contract("_, _ -> new")
public TextBlob shape(String text, Font font) {
- return shape(text, font, null, true, Float.POSITIVE_INFINITY, Point.ZERO);
+ return shape(text, font, ShapingOptions.DEFAULT, Float.POSITIVE_INFINITY, Point.ZERO);
}
@Nullable @Contract("_, _, _ -> new")
public TextBlob shape(String text, Font font, float width) {
- return shape(text, font, null, true, width, Point.ZERO);
+ return shape(text, font, ShapingOptions.DEFAULT, width, Point.ZERO);
}
@Nullable @Contract("_, _, _, _ -> new")
public TextBlob shape(String text, Font font, float width, @NotNull Point offset) {
- return shape(text, font, null, true, width, offset);
+ return shape(text, font, ShapingOptions.DEFAULT, width, offset);
}
@Nullable @Contract("_, _, _, _, _ -> new")
- public TextBlob shape(String text, Font font, boolean leftToRight, float width, @NotNull Point offset) {
- return shape(text, font, null, leftToRight, width, offset);
- }
-
- @Nullable @Contract("_, _, _, _, _, _ -> new")
- public TextBlob shape(String text, Font font, @Nullable FontFeature[] features, boolean leftToRight, float width, @NotNull Point offset) {
+ public TextBlob shape(String text, Font font, @NotNull ShapingOptions opts, float width, @NotNull Point offset) {
try {
+ assert opts != null : "Can’t Shaper::shape with opts == null";
Stats.onNativeCall();
- long ptr = _nShapeBlob(_ptr, text, Native.getPtr(font), features, leftToRight, width, offset._x, offset._y);
+ long ptr = _nShapeBlob(_ptr, text, Native.getPtr(font), opts, width, offset._x, offset._y);
return 0 == ptr ? null : new TextBlob(ptr);
} finally {
Reference.reachabilityFence(this);
@@ -127,22 +123,20 @@ public TextBlob shape(String text, Font font, @Nullable FontFeature[] features,
}
}
- @NotNull @Contract("_, _, _, _, _, _, _ -> this")
+ @NotNull @Contract("_, _, _, _, _ -> this")
public Shaper shape(String text,
Font font,
- @Nullable FontMgr fontMgr,
- @Nullable FontFeature[] features,
- boolean leftToRight,
+ @NotNull ShapingOptions opts,
float width,
RunHandler runHandler)
{
try (ManagedString textUtf8 = new ManagedString(text);
- FontMgrRunIterator fontIter = new FontMgrRunIterator(textUtf8, false, font, fontMgr);
- IcuBidiRunIterator bidiIter = new IcuBidiRunIterator(textUtf8, false, leftToRight ? java.text.Bidi.DIRECTION_LEFT_TO_RIGHT : java.text.Bidi.DIRECTION_RIGHT_TO_LEFT);
+ FontMgrRunIterator fontIter = new FontMgrRunIterator(textUtf8, false, font, opts);
+ IcuBidiRunIterator bidiIter = new IcuBidiRunIterator(textUtf8, false, opts._leftToRight ? java.text.Bidi.DIRECTION_LEFT_TO_RIGHT : java.text.Bidi.DIRECTION_RIGHT_TO_LEFT);
HbIcuScriptRunIterator scriptIter = new HbIcuScriptRunIterator(textUtf8, false);)
{
Iterator langIter = new TrivialLanguageRunIterator(text, Locale.getDefault().toLanguageTag());
- return shape(textUtf8, fontIter, bidiIter, scriptIter, langIter, features, width, runHandler);
+ return shape(textUtf8, fontIter, bidiIter, scriptIter, langIter, opts, width, runHandler);
}
}
@@ -152,12 +146,12 @@ public Shaper shape(@NotNull String text,
@NotNull Iterator bidiIter,
@NotNull Iterator scriptIter,
@NotNull Iterator langIter,
- @Nullable FontFeature[] features,
+ @NotNull ShapingOptions opts,
float width,
@NotNull RunHandler runHandler)
{
try (ManagedString textUtf8 = new ManagedString(text);) {
- return shape(textUtf8, fontIter, bidiIter, scriptIter, langIter, features, width, runHandler);
+ return shape(textUtf8, fontIter, bidiIter, scriptIter, langIter, opts, width, runHandler);
}
}
@@ -167,7 +161,7 @@ public Shaper shape(@NotNull ManagedString textUtf8,
@NotNull Iterator bidiIter,
@NotNull Iterator scriptIter,
@NotNull Iterator langIter,
- @Nullable FontFeature[] features,
+ @NotNull ShapingOptions opts,
float width,
@NotNull RunHandler runHandler)
{
@@ -175,22 +169,29 @@ public Shaper shape(@NotNull ManagedString textUtf8,
assert bidiIter != null : "BidiRunIterator == null";
assert scriptIter != null : "ScriptRunIterator == null";
assert langIter != null : "LanguageRunIterator == null";
+ assert opts != null : "Can’t Shaper::shape with opts == null";
Stats.onNativeCall();
- _nShape(_ptr, Native.getPtr(textUtf8), fontIter, bidiIter, scriptIter, langIter, features, width, runHandler);
+ _nShape(_ptr, Native.getPtr(textUtf8), fontIter, bidiIter, scriptIter, langIter, opts, width, runHandler);
return this;
}
- @NotNull @Contract("_, _, _, _, _, _ -> new")
- public TextLine shapeLine(String text, Font font, @Nullable FontFeature[] features, boolean leftToRight) {
+ @NotNull @Contract("_, _, _ -> new")
+ public TextLine shapeLine(String text, Font font, @NotNull ShapingOptions opts) {
try {
+ assert opts != null : "Can’t Shaper::shapeLine with opts == null";
Stats.onNativeCall();
- return new TextLine(_nShapeLine(_ptr, text, Native.getPtr(font), features, leftToRight));
+ return new TextLine(_nShapeLine(_ptr, text, Native.getPtr(font), opts));
} finally {
Reference.reachabilityFence(this);
Reference.reachabilityFence(font);
}
}
+ @NotNull @Contract("_, _, _ -> new")
+ public TextLine shapeLine(String text, Font font) {
+ return shapeLine(text, font, ShapingOptions.DEFAULT);
+ }
+
@ApiStatus.Internal
public Shaper(long ptr) {
super(ptr, _FinalizerHolder.PTR);
@@ -208,8 +209,7 @@ public static class _FinalizerHolder {
public static native long _nMakeShapeDontWrapOrReorder(long fontMgrPtr);
public static native long _nMakeCoreText();
public static native long _nMake(long fontMgrPtr);
- public static native long _nShapeBlob(long ptr, String text, long fontPtr, FontFeature[] features, boolean leftToRight, float width, float offsetX, float offsetY);
- public static native long _nShapeLine(long ptr, String text, long fontPtr, FontFeature[] features, boolean leftToRight);
- public static native void _nShape(long ptr, long textPtr, Iterator fontIter, Iterator bidiIter, Iterator scriptIter, Iterator langIter,
- FontFeature[] features, float width, RunHandler runHandler);
+ public static native long _nShapeBlob(long ptr, String text, long fontPtr, ShapingOptions opts, float width, float offsetX, float offsetY);
+ public static native long _nShapeLine(long ptr, String text, long fontPtr, ShapingOptions opts);
+ public static native void _nShape(long ptr, long textPtr, Iterator fontIter, Iterator bidiIter, Iterator scriptIter, Iterator langIter, ShapingOptions opts, float width, RunHandler runHandler);
}
diff --git a/shared/java/shaper/ShapingOptions.java b/shared/java/shaper/ShapingOptions.java
new file mode 100644
index 00000000..52f807f0
--- /dev/null
+++ b/shared/java/shaper/ShapingOptions.java
@@ -0,0 +1,42 @@
+package org.jetbrains.skija.shaper;
+
+import lombok.*;
+import lombok.Data;
+import org.jetbrains.annotations.*;
+import org.jetbrains.skija.*;
+
+@Data @With
+public class ShapingOptions {
+ public static final ShapingOptions DEFAULT = new ShapingOptions(null, null, true, true, true);
+
+ @ApiStatus.Internal @Nullable
+ public final FontMgr _fontMgr;
+
+ @ApiStatus.Internal @Nullable
+ public final FontFeature[] _features;
+
+ @ApiStatus.Internal
+ public final boolean _leftToRight;
+
+ /**
+ * If enabled, fallback font runs will not be broken by whitespace from original font
+ */
+ @ApiStatus.Internal
+ public final boolean _approximateSpaces;
+
+ /**
+ * If enabled, fallback font runs will not be broken by punctuation from original font
+ */
+ @ApiStatus.Internal
+ public final boolean _approximatePunctuation;
+
+ @NotNull
+ public ShapingOptions withFeatures(@Nullable FontFeature[] features) {
+ return new ShapingOptions(_fontMgr, features, _leftToRight, _approximateSpaces, _approximatePunctuation);
+ }
+
+ @NotNull
+ public ShapingOptions withFeatures(@Nullable String featuresString) {
+ return featuresString == null ? withFeatures((FontFeature[]) null) : withFeatures(FontFeature.parse(featuresString));
+ }
+}