- As close to Skia API as possible. Exceptions:
- Methods that don’t make sense (C++ specific, etc)
- Fix minor inconsistencies in naming
- Methods that would clash with the Java ones (e.g.
SkPath#close
would clash withManaged#close
, so it becamePath#closePath
in Skija)
- Skia modules -> org.jetbrains.skija.* subpackages
- camelCase in Java & C++.
- UPPER_CASE for enum values.
- Field/method prefixes:
_n...
for native methods._...
for “private” fields.get...
/is...
for getters.set...
/with...
for setters.make...
for static named constructors._FLAG_<TYPE>_<NAME>
for bit flags....Mask
for bit masks.
Some common dictionary:
- ptr (paintPtr, canvasPtr, ...)
- finalizer
- lerp
- count (instead of size/length/...)
- ...Style instead of ...Config/...Options/...Spec for appearance of something
- ...Mode instead of ...Type/...Style/...Op/...Kind
- serializeToData/..Bytes/makeFromData/..Bytes
- Java/CC method order must match Skia’s *.h method order
- If no reference order is present, sort alphabetically (e.g. java imports)
- No inner classes
- Remove prefixes/suffixes. SkCanvas.h -> Canvas.java, kLine_SkPathSegmentMask -> LINE.
- Getter methods ALWAYS start with
get...
/is...
(lineNumber
->getLineNumber()
,accessible
->isAccessible()
). - Constructors/static builders ALWAYS start with
make...
. - Setters/updaters that normally return
void
in Skia must returnthis
in Skija.
- All fields/methods
public
. - Fields/methods/inner classes not for public consumption:
- also
public
, but prefixed with_
(startIndex
->_startIndex
). - annotated with @ApiStatus.Internal
- also
Why public?
- We cannot anticipate what specific needs our clients might have. Sometimes the difference between totally possible and completely impossible might be as small as a single field made public.
- It’s strictly better to have implementation details accessible and not use them than just have them inaccessible.
- Following Skia, we want to split Skija into multiple packages (core, shaper, paragraph), impossible even with protected visibility.
- We are all grown ups. If clients use fields starting with
_
, we assume they know what they are doing, and that the alternatives were worse. - As with private APIs, we give no guarantees with regards to
_
fields/methods. They are considered effectively private, but if you really, really need them, they are here for you.
- public final fields prefixed with
_
. - public getters/setters following javaBeans convention (get/is/set/with).
- setters return this.
- flags/bit masks are not exposed as getters, instead, individual check for each flag value is done.
Why getters/setters?
- If fields goes away if future version, still can be emulated with getter (or getter might throw).
- Computable fields need getters (e.g. height = bottom - top), would be strange to have them as methods but others as fields.
- Some fields need to call native instance, again, getter can hide that.
class LineMetrics {
public:
size_t fStartIndex = 0;
size_t fEndIndex = 0;
size_t fEndExcludingWhitespaces = 0;
size_t fEndIncludingNewline = 0;
bool fHardBreak = false;
int fFlags = 0;
enum Flags {
kIsBold_Flag = 1,
kIsItalic_Flag = 2,
};
}
↓
@Data
public class LineMetrics {
public final long _startIndex;
public final long _endIndex;
public final long _endExcludingWhitespaces;
public final long _endIncludingNewline;
public final boolean _hardBreak;
@Getter(AccessLevel.NONE)
public final int _flags;
public static final int _FLAG_IS_BOLD = 0b0001;
public static final int _FLAG_IS_ITALIC = 0b0010;
public boolean isBold() { return (_flags | _FLAG_IS_BOLD) != 0; }
public boolean isItalic() { return (_flags | _FLAG_IS_ITALIC) != 0; }
}
- Native methods are
public static
, name starts with_n...
+ java method name.- Exceptions are overloaded Skia methods. Can’t overload in JNI.
- Enum values are passed as ints (for this to work, enums must have same elements in the same order).
- Avoid arrays and objects when possible.
- Unroll objects and small fixed-size arrays when possible (SkRect -> jfloat, jfloat, jfloat, jfloat, int[2] -> jint, jint).
- But return data objects, e.g. IRect instead of int[].
- Pointers are passed as longs.
- Two-integers are returned as single long.
- Strings are passed as Strings (converted to UTF-8/UTF-16 on C++ side).
- When a managed object is returned, its ref count should be bumped (normally Skia follows that as well, no extra action needed).
- Prefer
Native.ptr(obj)
toobj._ptr
because some Skia APIs are fine with acceptingnullptr
as some arguments, so obj might be null. - Assert known constraint on Java side (e.g.
assert matrix.length == 9
).
Types correspondence:
- string indices -> jint
- size_t -> jlong
- SkColor -> jint
- SkScalar -> jfloat
class SK_API SkCanvas {
public:
enum PointMode {
kPoints_PointMode,
kLines_PointMode,
kPolygon_PointMode,
};
void drawRRect(const SkRRect& rrect, const SkPaint& paint);
}
↓
public class Canvas extends Managed {
public enum PointMode { POINTS, LINES, POLYGON }
public Canvas drawRRect(RRect rr, Paint paint) {
Native.onNativeCall();
_nDrawRRect(ptr, rr.left, rr.top, rr.right, rr.bottom, rr.radii, Native.ptr(paint));
return this;
}
public static native void _nDrawRRect(long ptr, float left, float top, float right, float bottom, float[] radii, long paintPtr);
}