This is a stylus-enabled demo of a custom APK for the DPT-RP1.
- Stylus handwriting as fast as Sony
- Eraser feature, but not stroke-based (it clears a bitmap around the eraser circle rather than detecting intersection between strokes and the eraser like Sony does)
- Stylus pressure, because yes it works <-- removed for now, I left the test class and the screenshot
There are 3 ways to program handwriting for the Stylus on the DPT:
- Capture MotionEvent events
- Store the point/lines/whatever
- Invalidate the view, or a dirty rectangle
- Pray this doesn't redraw too late.
The performance are horrendous, this won't work
Sony has a special rewrite of the View class that can invalidate a dirty rectangle in a preferential mode for E-Ink, a lot like what the Onyx lib does.
- Write a static cache to capture the Sony method at Runtime
- Run this method instead of View.Invalidate on your custom view, with a parameter to indicate Direct Update
public class ViewOverride implements SonyOverride<View> {
private static Method invalidateRect;
static {
try {
invalidateRect = DrawableView.class.getMethod("invalidate", Rect.class, int.class);
invalidateRect.setAccessible(true); // Small acceleration
} catch (Exception ignored) {}
}
private final View view;
public ViewOverride(final View view) {
this.view = view;
}
public void invalidate(Rect rect, int updateMode) {
invalidate(view, rect, updateMode);
}
public static void invalidate(View view, Rect rect, int updateMode) {
try {
invalidateRect.invoke(view, rect, updateMode);
} catch (Exception ignored) { }
}
}
// Then use it in a View inheritor:
@Override
public void invalidate(Rect dirty) {
ViewOverride.invalidate(view, dirty, UPDATE_MODE_NOWAIT_NOCONVERT_DU_SP1_IGNORE);
}
This works well, but there's a 200 to 500ms delay, compared to the official app, which is interestingly A LOT like all the early critics of the device said. Sony made an update around 2018, to "make it 30% faster"
It turns out, there's a special .so library in /lib/libSystemUtil.so, which can draw a fast squiggle on the framebuffer, and that... makes it 30% faster. We can now store/draw to a bitmap slowly while pre-showing the squiggle (it's around 20px long ?) as a temporary preview.
public class SystemUtil {
private static SystemUtil.EpdUtil epdUtil;
private static final SystemUtil systemUtil;
static {
System.loadLibrary("SystemUtil");
systemUtil = new SystemUtil();
}
private SystemUtil() {
}
public native int getScreenShot(byte[] content);
public native int nativeAddDhwArea(int left, int top, int right, int bottom, int penWidth, boolean portraitOrientation);
public native int nativeChangeDhwStrokeWidth(int widthParam1, int widthParam2);
public native boolean nativeGetDhwState();
public native int nativeRemoveDhwArea(int index);
public native void nativeSetDhwState(boolean enabled);
public native int nativeWriteWaveform(byte[] waveform);
public native int setShutdownScreenFlag(boolean enabled);
public native int setShutdownScreenImage(byte[] image);
public native int setStandbyScreenImage(byte[] image);
}
// Then use it during motion detection:
public void initView() {
systemUtil.nativeAddDhwArea(
0,
0,
view.getWidth(),
view.getHeight(),
strokeWidth,
0
);
}
public boolean onTouchEvent(MotionEvent event) {
int action = event.getActionMasked();
switch(action) {
case MotionEvent.ACTION_DOWN:
systemUtil.nativeSetDhwState(true);
// ...
break;
case MotionEvent.ACTION_MOVE:
// ...
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
// ...
systemUtil.nativeSetDhwState(false);
break;
}
}
It turns out that the device is able to sense pressure like most of its competition. Looking for maths/geometry geniuses who can find a less naive way to make it look and animate nicely, I don't care too much for it myself. Here is what the app looks like with pressure handling:
public boolean onTouchEvent(MotionEvent event) {
float pressure = event.getPressure();
// We renormalize the pressure: 0.5 is "normal"
float multiplier = pressure / 0.5f;
this.currentWidth = baseWidth * multiplier;
if (this.currentWidth < 1) this.currentWidth = 1;
strikeDelegate.getStrokePaint().setStrokeWidth(currentWidth);
return strikeDelegate.onTouchEvent(event);
}
As many have noted while reviewing the DPT-RP1, there's a strange anti-aliasing delay when you finishing writing. The way the official app does is that it does an async render thread in a surface view to render strokes as fast as possible. Then it stores them, wait a little and redraw the path with a Paint that as AntiAlias true.
What's interesting is that it's possible to go a lot faster with the 4 cores of the machine, and this app demonstrate a way to have real-time anti-aliasing. The technique is the same except with a special thread just for the anti-aliasing working on the bitmap at the same time.
Bear in mind Sony's application has a lot more to do, and it might be the most optimal solution for them, with their load.
- Store / Retrieve strokes or at least the bitmap with event listeners so that clients can retrieve them