Skip to content

Commit

Permalink
TextBlob.getTightBounds
Browse files Browse the repository at this point in the history
  • Loading branch information
tonsky committed Dec 16, 2020
1 parent 470462a commit c100dc6
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -356,11 +356,12 @@ private void loop() {
scenes.put("Squares", new SquaresScene());
scenes.put("SVG", new SVGScene());
scenes.put("Swing", new SwingScene());
scenes.put("Text Blob", new TextBlobScene());
scenes.put("Text Style", new TextStyleScene());
scenes.put("TextBlob", new TextBlobScene());
scenes.put("TextBlob Bounds", new TextBlobBoundsScene());
scenes.put("Wall of Text", new WallOfTextScene());
scenes.put("Watches", new WatchesScene());
currentScene = "Paragraph";
currentScene = "TextBlob Bounds";
t0 = System.nanoTime();

while (!glfwWindowShouldClose(window)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package org.jetbrains.skija.examples.lwjgl;

import java.util.*;
import org.jetbrains.skija.*;
import org.jetbrains.skija.shaper.*;

public class TextBlobBoundsScene extends Scene {
private Paint fill = new Paint().setColor(0xFFCC3333);
private Paint stroke = new Paint().setColor(0x803333CC).setMode(PaintMode.STROKE).setStrokeWidth(1);
private Font inter18 = new Font(inter, 18);
private Font zapfino18 = new Font(FontMgr.getDefault().matchFamilyStyle("Zapfino", FontStyle.NORMAL), 18);

public TextBlobBoundsScene() {
_variants = new String[] { "Tight", "Loose" };
}

public void drawLine(Canvas canvas, String text, Font font) {
drawLine(canvas, text, font, Float.POSITIVE_INFINITY);
}

public void drawLine(Canvas canvas, String text, Font font, float width) {
drawLine(canvas, new String[] { text }, font, width);
}

public void drawLine(Canvas canvas, String[] texts, Font font) {
drawLine(canvas, texts, font, Float.POSITIVE_INFINITY);
}

public void drawLine(Canvas canvas, String[] texts, Font font, float width) {
canvas.save();
float bottom = 0;
for (var text: texts) {
try (var shaper = Shaper.makeShapeThenWrap();
var blob = shaper.shape(text, font, width);)
{
var bounds = "Tight".equals(variantTitle()) ? blob.getTightBounds() : blob.getBounds();
canvas.drawCircle(0, 0, 3, stroke);
canvas.drawRect(bounds, stroke);
canvas.drawTextBlob(blob, 0, 0, font, fill);
canvas.translate(bounds.getRight() + 20, 0);
bottom = Math.max(bottom, bounds.getBottom());
}
}
canvas.restore();
canvas.translate(0, bottom + 20);
}

@Override
public void draw(Canvas canvas, int width, int height, float dpi, int xpos, int ypos) {
canvas.translate(20, 20);
drawLine(canvas, "onerun", inter18);
drawLine(canvas, "xyfx", inter18);
drawLine(canvas, new String[] { "one", "два", "three", "one два three" }, inter18);
drawLine(canvas, "multiple different lines", inter18, 50);
drawLine(canvas, new String[] { "ace", "الخطوط", "🧛", "ace الخطوط 🧛" }, inter18);
drawLine(canvas, "واحد اثنين ثلاثة", inter18);
drawLine(canvas, "fiz", zapfino18);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ public class TextBlobScene extends Scene {
private float gap = 40;

public TextBlobScene() {
var face = Typeface.makeFromFile("fonts/InterHinted-Regular.ttf");
font = new Font(face, 24);
font = new Font(inter, 24);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public class WallOfTextScene extends Scene {
private String[] words;
private String text;
private Paint fill = new Paint();
private Paint boundsStroke = new Paint().setColor(0x803333CC).setMode(PaintMode.STROKE).setStrokeWidth(1);
private int[] colors;
private FontCollection fc = null;

Expand All @@ -36,8 +37,9 @@ public WallOfTextScene() {
"Paragraph with cache",
"Paragraph no cache"
};
_variantIdx = 8;
_variantIdx = 1;
colors = new int[] {
0xFF000000,
0xFFf94144,
0xFFf3722c,
0xFFf8961e,
Expand Down Expand Up @@ -99,16 +101,20 @@ TextBlob makeBlob(String text, Shaper shaper, float textWidth) {
void drawByWords(Shaper shaper, Canvas canvas, float padding, float textWidth) {
var x = padding;
var y = padding;
var space = font.measureTextWidth(" ");
var lineHeight = font.getMetrics().getHeight();
for (int i = 0; i < words.length; ++i) {
try (var blob = makeBlob(words[i], shaper, Float.POSITIVE_INFINITY);) {
var bounds = blob.getBounds();
var wordWidth = bounds.getRight();
var bounds = blob.getTightBounds();
var wordWidth = bounds.getWidth();
if (x + wordWidth > textWidth) {
x = padding;
y += bounds.getHeight();
y += lineHeight;
}
fill.setColor(colors[i % colors.length]);
canvas.drawRect(bounds.translate(x, y), boundsStroke);
canvas.drawTextBlob(blob, x, y, font, fill);
x += wordWidth;
x += wordWidth + space;
}
}
}
Expand Down
33 changes: 33 additions & 0 deletions native/src/TextBlob.cc
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_TextBlob__1nMakeFrom
}

// Must match SkTextBlobPriv.h
//
// Extended Textblob runs have more data after the Pos[] array:
//
// -------------------------------------------------------------------------
// ... | RunRecord | Glyphs[] | Pos[] | TextSize | Clusters[] | Text[] | ...
// -------------------------------------------------------------------------
class RunRecordClone {
public:
SkFont fFont;
Expand Down Expand Up @@ -215,3 +221,30 @@ extern "C" JNIEXPORT jintArray JNICALL Java_org_jetbrains_skija_TextBlob__1nGetC
}
return javaIntArray(env, clusters);
}

extern "C" JNIEXPORT jobject JNICALL Java_org_jetbrains_skija_TextBlob__1nGetTightBounds
(JNIEnv* env, jclass jclass, jlong ptr) {
SkTextBlob* instance = reinterpret_cast<SkTextBlob*>(static_cast<uintptr_t>(ptr));
SkTextBlob::Iter iter(*instance);
SkTextBlob::Iter::Run run;
auto bounds = SkRect::MakeEmpty();
SkRect tmpBounds;
SkGlyphID glyph;
SkPoint pos;
bool first = false;
while (iter.next(&run)) {
// run.fGlyphIndices points directly to runRecord.glyphBuffer(), which comes directly after RunRecord itself
auto runRecord = reinterpret_cast<const RunRecordClone*>(run.fGlyphIndices) - 1;
if (runRecord->positioning() != 2) // kFull_Positioning
return nullptr;

runRecord->fFont.measureText(runRecord->glyphBuffer(), runRecord->fCount * sizeof(uint16_t), SkTextEncoding::kGlyphID, &tmpBounds, nullptr);
tmpBounds.offset(runRecord->posBuffer()[0], runRecord->posBuffer()[1]);
if (first) {
bounds.setLTRB(tmpBounds.fLeft, tmpBounds.fTop, tmpBounds.fRight, tmpBounds.fBottom);
first = false;
} else
bounds.join(tmpBounds);
}
return skija::Rect::fromSkRect(env, bounds);
}
22 changes: 21 additions & 1 deletion shared/src/main/java/org/jetbrains/skija/TextBlob.java
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,25 @@ public int[] getClusters() {
}
}

/**
* Only works on TextBlobs that come from TextBlobBuilderRunHandler/Shaper default handler.
*
* @return tight bounds around all the glyphs in the TextBlob
* @throws IllegalArgumentException if TextBlob doesn’t have this information
*/
@NotNull
public Rect getTightBounds() {
try {
Stats.onNativeCall();
Rect res = _nGetTightBounds(_ptr);
if (res == null)
throw new IllegalArgumentException();
return res;
} finally {
Reference.reachabilityFence(this);
}
}

@ApiStatus.Internal
public static class _FinalizerHolder {
public static final long PTR = _nGetFinalizer();
Expand All @@ -220,5 +239,6 @@ public static class _FinalizerHolder {
@ApiStatus.Internal public static native long _nMakeFromData(long dataPtr /*, SkDeserialProcs */);
@ApiStatus.Internal public static native short[] _nGetGlyphs(long ptr);
@ApiStatus.Internal public static native float[] _nGetPositions(long ptr);
@ApiStatus.Internal public static native int[] _nGetClusters(long ptr);
@ApiStatus.Internal public static native int[] _nGetClusters(long ptr);
@ApiStatus.Internal public static native Rect _nGetTightBounds(long ptr);
}

0 comments on commit c100dc6

Please sign in to comment.