Skip to content

Commit caf1f32

Browse files
Support openStream functionality in Typeface (#82)
1 parent fb7ee46 commit caf1f32

File tree

8 files changed

+413
-1
lines changed

8 files changed

+413
-1
lines changed

platform/cc/StreamAsset.cc

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#include <jni.h>
2+
#include "interop.hh"
3+
#include "SkStream.h"
4+
5+
static void deleteStreamAsset(SkStreamAsset* stream) {
6+
delete stream;
7+
}
8+
9+
extern "C" JNIEXPORT jlong JNICALL Java_io_github_humbleui_skija_StreamAsset__1nGetFinalizer(JNIEnv* env, jclass clazz) {
10+
return reinterpret_cast<jlong>(&deleteStreamAsset);
11+
}
12+
13+
extern "C" JNIEXPORT jboolean JNICALL Java_io_github_humbleui_skija_StreamAsset__1nIsAtEnd(JNIEnv* env, jclass clazz, jlong ptr) {
14+
SkStreamAsset* stream = reinterpret_cast<SkStreamAsset*>(static_cast<uintptr_t>(ptr));
15+
return stream->isAtEnd();
16+
}
17+
18+
extern "C" JNIEXPORT jint JNICALL Java_io_github_humbleui_skija_StreamAsset__1nRead(JNIEnv* env, jclass clazz, jlong ptr, jbyteArray buffer, jint size) {
19+
SkStreamAsset* stream = reinterpret_cast<SkStreamAsset*>(static_cast<uintptr_t>(ptr));
20+
jbyte* buf = env->GetByteArrayElements(buffer, nullptr);
21+
size_t bytesRead = stream->read(buf, size);
22+
env->ReleaseByteArrayElements(buffer, buf, 0);
23+
return static_cast<jint>(bytesRead);
24+
}
25+
26+
extern "C" JNIEXPORT jint JNICALL Java_io_github_humbleui_skija_StreamAsset__1nPeek(JNIEnv* env, jclass clazz, jlong ptr, jbyteArray buffer, jint size) {
27+
SkStreamAsset* stream = reinterpret_cast<SkStreamAsset*>(static_cast<uintptr_t>(ptr));
28+
jbyte* buf = env->GetByteArrayElements(buffer, nullptr);
29+
size_t bytesPeeked = stream->peek(buf, size);
30+
env->ReleaseByteArrayElements(buffer, buf, 0);
31+
return static_cast<jint>(bytesPeeked);
32+
}
33+
34+
extern "C" JNIEXPORT jint JNICALL Java_io_github_humbleui_skija_StreamAsset__1nSkip(JNIEnv* env, jclass clazz, jlong ptr, jint size) {
35+
SkStreamAsset* stream = reinterpret_cast<SkStreamAsset*>(static_cast<uintptr_t>(ptr));
36+
return static_cast<jint>(stream->skip(size));
37+
}
38+
39+
extern "C" JNIEXPORT jboolean JNICALL Java_io_github_humbleui_skija_StreamAsset__1nRewind(JNIEnv* env, jclass clazz, jlong ptr) {
40+
SkStreamAsset* stream = reinterpret_cast<SkStreamAsset*>(static_cast<uintptr_t>(ptr));
41+
return stream->rewind();
42+
}
43+
44+
extern "C" JNIEXPORT jlong JNICALL Java_io_github_humbleui_skija_StreamAsset__1nDuplicate(JNIEnv* env, jclass clazz, jlong ptr) {
45+
SkStreamAsset* stream = reinterpret_cast<SkStreamAsset*>(static_cast<uintptr_t>(ptr));
46+
std::unique_ptr<SkStreamAsset> duplicate = stream->duplicate();
47+
return reinterpret_cast<jlong>(duplicate.release());
48+
}
49+
50+
extern "C" JNIEXPORT jlong JNICALL Java_io_github_humbleui_skija_StreamAsset__1nFork(JNIEnv* env, jclass clazz, jlong ptr) {
51+
SkStreamAsset* stream = reinterpret_cast<SkStreamAsset*>(static_cast<uintptr_t>(ptr));
52+
std::unique_ptr<SkStreamAsset> fork = stream->fork();
53+
return reinterpret_cast<jlong>(fork.release());
54+
}
55+
56+
extern "C" JNIEXPORT jboolean JNICALL Java_io_github_humbleui_skija_StreamAsset__1nHasPosition(JNIEnv* env, jclass clazz, jlong ptr) {
57+
SkStreamAsset* stream = reinterpret_cast<SkStreamAsset*>(static_cast<uintptr_t>(ptr));
58+
return stream->hasPosition();
59+
}
60+
61+
extern "C" JNIEXPORT jint JNICALL Java_io_github_humbleui_skija_StreamAsset__1nGetPosition(JNIEnv* env, jclass clazz, jlong ptr) {
62+
SkStreamAsset* stream = reinterpret_cast<SkStreamAsset*>(static_cast<uintptr_t>(ptr));
63+
return static_cast<jint>(stream->getPosition());
64+
}
65+
66+
extern "C" JNIEXPORT jboolean JNICALL Java_io_github_humbleui_skija_StreamAsset__1nSeek(JNIEnv* env, jclass clazz, jlong ptr, jint position) {
67+
SkStreamAsset* stream = reinterpret_cast<SkStreamAsset*>(static_cast<uintptr_t>(ptr));
68+
return stream->seek(position);
69+
}
70+
71+
extern "C" JNIEXPORT jboolean JNICALL Java_io_github_humbleui_skija_StreamAsset__1nMove(JNIEnv* env, jclass clazz, jlong ptr, jint offset) {
72+
SkStreamAsset* stream = reinterpret_cast<SkStreamAsset*>(static_cast<uintptr_t>(ptr));
73+
return stream->move(offset);
74+
}
75+
76+
extern "C" JNIEXPORT jboolean JNICALL Java_io_github_humbleui_skija_StreamAsset__1nHasLength(JNIEnv* env, jclass clazz, jlong ptr) {
77+
SkStreamAsset* stream = reinterpret_cast<SkStreamAsset*>(static_cast<uintptr_t>(ptr));
78+
return stream->hasLength();
79+
}
80+
81+
extern "C" JNIEXPORT jint JNICALL Java_io_github_humbleui_skija_StreamAsset__1nGetLength(JNIEnv* env, jclass clazz, jlong ptr) {
82+
SkStreamAsset* stream = reinterpret_cast<SkStreamAsset*>(static_cast<uintptr_t>(ptr));
83+
return static_cast<jint>(stream->getLength());
84+
}
85+
86+
extern "C" JNIEXPORT jlong JNICALL Java_io_github_humbleui_skija_StreamAsset__1nGetMemoryBase(JNIEnv* env, jclass clazz, jlong ptr) {
87+
SkStreamAsset* stream = reinterpret_cast<SkStreamAsset*>(static_cast<uintptr_t>(ptr));
88+
return reinterpret_cast<jlong>(stream->getMemoryBase());
89+
}

platform/cc/Typeface.cc

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include <iostream>
22
#include <jni.h>
33
#include "SkData.h"
4+
#include "SkStream.h"
45
#include "SkTypeface.h"
56
#include "interop.hh"
67

@@ -241,4 +242,18 @@ extern "C" JNIEXPORT jobject JNICALL Java_io_github_humbleui_skija_Typeface__1nG
241242
(JNIEnv* env, jclass jclass, jlong ptr) {
242243
SkTypeface* instance = reinterpret_cast<SkTypeface*>(static_cast<uintptr_t>(ptr));
243244
return types::Rect::fromSkRect(env, instance->getBounds());
244-
}
245+
}
246+
247+
extern "C" JNIEXPORT jlong JNICALL Java_io_github_humbleui_skija_Typeface__1nOpenStream
248+
(JNIEnv* env, jclass jclass, jlong ptr, jintArray ttcIndexArray) {
249+
SkTypeface* instance = reinterpret_cast<SkTypeface*>(static_cast<uintptr_t>(ptr));
250+
int ttcIndex = 0;
251+
std::unique_ptr<SkStreamAsset> streamPtr = instance->openStream(&ttcIndex);
252+
SkStreamAsset* stream = streamPtr.release();
253+
254+
if (ttcIndexArray != nullptr && env->GetArrayLength(ttcIndexArray) > 0) {
255+
env->SetIntArrayRegion(ttcIndexArray, 0, 1, &ttcIndex);
256+
}
257+
258+
return reinterpret_cast<jlong>(stream);
259+
}

shared/java/StreamAsset.java

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
package io.github.humbleui.skija;
2+
3+
import org.jetbrains.annotations.*;
4+
import io.github.humbleui.skija.impl.*;
5+
6+
public class StreamAsset extends Managed {
7+
static { Library.staticLoad(); }
8+
9+
@ApiStatus.Internal
10+
public StreamAsset(long ptr) {
11+
super(ptr, _FinalizerHolder.PTR);
12+
}
13+
14+
/**
15+
* Check if the stream has reached the end
16+
* @return true if at end of stream
17+
*/
18+
public boolean isAtEnd() {
19+
try {
20+
Stats.onNativeCall();
21+
return _nIsAtEnd(_ptr);
22+
} finally {
23+
ReferenceUtil.reachabilityFence(this);
24+
}
25+
}
26+
27+
/**
28+
* Read data from the stream
29+
* @param buffer byte array to read into
30+
* @param size number of bytes to read
31+
* @return actual number of bytes read
32+
*/
33+
public int read(byte[] buffer, int size) {
34+
try {
35+
Stats.onNativeCall();
36+
return _nRead(_ptr, buffer, size);
37+
} finally {
38+
ReferenceUtil.reachabilityFence(this);
39+
}
40+
}
41+
42+
/**
43+
* Peek at data without advancing the stream position
44+
* @param buffer byte array to peek into
45+
* @param size number of bytes to peek
46+
* @return actual number of bytes peeked
47+
*/
48+
public int peek(byte[] buffer, int size) {
49+
try {
50+
Stats.onNativeCall();
51+
return _nPeek(_ptr, buffer, size);
52+
} finally {
53+
ReferenceUtil.reachabilityFence(this);
54+
}
55+
}
56+
57+
/**
58+
* Skip bytes in the stream
59+
* @param size number of bytes to skip
60+
* @return actual number of bytes skipped
61+
*/
62+
public int skip(int size) {
63+
try {
64+
Stats.onNativeCall();
65+
return _nSkip(_ptr, size);
66+
} finally {
67+
ReferenceUtil.reachabilityFence(this);
68+
}
69+
}
70+
71+
/**
72+
* Rewind the stream to the beginning
73+
* @return true if rewind was successful
74+
*/
75+
public boolean rewind() {
76+
try {
77+
Stats.onNativeCall();
78+
return _nRewind(_ptr);
79+
} finally {
80+
ReferenceUtil.reachabilityFence(this);
81+
}
82+
}
83+
84+
/**
85+
* Create a duplicate of this stream
86+
* @return new StreamAsset duplicate, or null if not supported
87+
*/
88+
@Nullable
89+
public StreamAsset duplicate() {
90+
try {
91+
Stats.onNativeCall();
92+
long ptr = _nDuplicate(_ptr);
93+
return ptr == 0 ? null : new StreamAsset(ptr);
94+
} finally {
95+
ReferenceUtil.reachabilityFence(this);
96+
}
97+
}
98+
99+
/**
100+
* Create a fork of this stream
101+
* @return new StreamAsset fork, or null if not supported
102+
*/
103+
@Nullable
104+
public StreamAsset fork() {
105+
try {
106+
Stats.onNativeCall();
107+
long ptr = _nFork(_ptr);
108+
return ptr == 0 ? null : new StreamAsset(ptr);
109+
} finally {
110+
ReferenceUtil.reachabilityFence(this);
111+
}
112+
}
113+
114+
/**
115+
* Check if the stream supports position operations
116+
* @return true if position operations are supported
117+
*/
118+
public boolean hasPosition() {
119+
try {
120+
Stats.onNativeCall();
121+
return _nHasPosition(_ptr);
122+
} finally {
123+
ReferenceUtil.reachabilityFence(this);
124+
}
125+
}
126+
127+
/**
128+
* Get current position in the stream
129+
* @return current position, or -1 if not supported
130+
*/
131+
public int getPosition() {
132+
try {
133+
Stats.onNativeCall();
134+
return _nGetPosition(_ptr);
135+
} finally {
136+
ReferenceUtil.reachabilityFence(this);
137+
}
138+
}
139+
140+
/**
141+
* Seek to a specific position in the stream
142+
* @param position position to seek to
143+
* @return true if seek was successful
144+
*/
145+
public boolean seek(int position) {
146+
try {
147+
Stats.onNativeCall();
148+
return _nSeek(_ptr, position);
149+
} finally {
150+
ReferenceUtil.reachabilityFence(this);
151+
}
152+
}
153+
154+
/**
155+
* Move by an offset from current position
156+
* @param offset offset to move by
157+
* @return true if move was successful
158+
*/
159+
public boolean move(int offset) {
160+
try {
161+
Stats.onNativeCall();
162+
return _nMove(_ptr, offset);
163+
} finally {
164+
ReferenceUtil.reachabilityFence(this);
165+
}
166+
}
167+
168+
/**
169+
* Check if the stream has a known length
170+
* @return true if length is available
171+
*/
172+
public boolean hasLength() {
173+
try {
174+
Stats.onNativeCall();
175+
return _nHasLength(_ptr);
176+
} finally {
177+
ReferenceUtil.reachabilityFence(this);
178+
}
179+
}
180+
181+
/**
182+
* Get the length of the stream
183+
* @return stream length, or -1 if not available
184+
*/
185+
public int getLength() {
186+
try {
187+
Stats.onNativeCall();
188+
return _nGetLength(_ptr);
189+
} finally {
190+
ReferenceUtil.reachabilityFence(this);
191+
}
192+
}
193+
194+
/**
195+
* Get the memory base address (if stream is memory-based)
196+
* @return memory base address, or 0 if not memory-based
197+
*/
198+
public long getMemoryBase() {
199+
try {
200+
Stats.onNativeCall();
201+
return _nGetMemoryBase(_ptr);
202+
} finally {
203+
ReferenceUtil.reachabilityFence(this);
204+
}
205+
}
206+
207+
// Native methods
208+
@ApiStatus.Internal public static native long _nGetFinalizer();
209+
@ApiStatus.Internal public static native boolean _nIsAtEnd(long ptr);
210+
@ApiStatus.Internal public static native int _nRead(long ptr, byte[] buffer, int size);
211+
@ApiStatus.Internal public static native int _nPeek(long ptr, byte[] buffer, int size);
212+
@ApiStatus.Internal public static native int _nSkip(long ptr, int size);
213+
@ApiStatus.Internal public static native boolean _nRewind(long ptr);
214+
@ApiStatus.Internal public static native long _nDuplicate(long ptr);
215+
@ApiStatus.Internal public static native long _nFork(long ptr);
216+
@ApiStatus.Internal public static native boolean _nHasPosition(long ptr);
217+
@ApiStatus.Internal public static native int _nGetPosition(long ptr);
218+
@ApiStatus.Internal public static native boolean _nSeek(long ptr, int position);
219+
@ApiStatus.Internal public static native boolean _nMove(long ptr, int offset);
220+
@ApiStatus.Internal public static native boolean _nHasLength(long ptr);
221+
@ApiStatus.Internal public static native int _nGetLength(long ptr);
222+
@ApiStatus.Internal public static native long _nGetMemoryBase(long ptr);
223+
224+
@ApiStatus.Internal
225+
static class _FinalizerHolder {
226+
static final long PTR = _nGetFinalizer();
227+
}
228+
}

shared/java/Typeface.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,29 @@ public Rect getBounds() {
389389
}
390390
}
391391

392+
/**
393+
* Opens a stream for reading the data of this typeface.
394+
* @return a StreamAsset for the typeface data, or null if not available
395+
*/
396+
public StreamAsset openStream(){
397+
return openStream(null);
398+
}
399+
400+
/**
401+
* Opens a stream for reading the data of this typeface.
402+
* @param ttcIndex if not null, will be filled with the TTC index (for TrueType Collections)
403+
* @return a StreamAsset for the typeface data, or null if not available
404+
*/
405+
public StreamAsset openStream(int[] ttcIndex) {
406+
try {
407+
Stats.onNativeCall();
408+
long ptr = _nOpenStream(_ptr, ttcIndex);
409+
return ptr == 0 ? null : new StreamAsset(ptr);
410+
} finally {
411+
ReferenceUtil.reachabilityFence(this);
412+
}
413+
}
414+
392415
@ApiStatus.Internal
393416
public Typeface(long ptr) {
394417
super(ptr);
@@ -417,4 +440,5 @@ public Typeface(long ptr) {
417440
@ApiStatus.Internal public static native FontFamilyName[] _nGetFamilyNames(long ptr);
418441
@ApiStatus.Internal public static native String _nGetFamilyName(long ptr);
419442
@ApiStatus.Internal public static native Rect _nGetBounds(long ptr);
443+
@ApiStatus.Internal public static native long _nOpenStream(long ptr, int[] ttcIndex);
420444
}

0 commit comments

Comments
 (0)