android: compat webviewexecutor to support arraybuffer
This commit is contained in:
parent
c67803b717
commit
8c57e5e500
@ -17,6 +17,7 @@ package pub.doric.engine;
|
||||
|
||||
import com.github.pengfeizhou.jscore.ArchiveException;
|
||||
import com.github.pengfeizhou.jscore.JSArray;
|
||||
import com.github.pengfeizhou.jscore.JSArrayBuffer;
|
||||
import com.github.pengfeizhou.jscore.JSBoolean;
|
||||
import com.github.pengfeizhou.jscore.JSDecoder;
|
||||
import com.github.pengfeizhou.jscore.JSNull;
|
||||
@ -127,6 +128,9 @@ public class DoricJSDecoder extends JSDecoder {
|
||||
}
|
||||
return jsArray;
|
||||
}
|
||||
if (this.value instanceof byte[]) {
|
||||
return new JSArrayBuffer((byte[]) this.value);
|
||||
}
|
||||
return JS_NULL;
|
||||
}
|
||||
}
|
||||
|
@ -109,14 +109,14 @@ public class DoricJSEngine implements Handler.Callback, DoricTimerExtension.Time
|
||||
e.printStackTrace(new PrintWriter(stringWriter));
|
||||
mDoricRegistry.onLog(Log.ERROR, stringWriter.toString());
|
||||
//In case some unexpected errors happened
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
mDoricRegistry.onLog(Log.WARN, "Use DoricWebViewJSExecutor");
|
||||
mDoricJSE = new DoricWebViewJSExecutor(Doric.application());
|
||||
loadBuiltinJS("doric-web.js");
|
||||
} else {
|
||||
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
// mDoricRegistry.onLog(Log.WARN, "Use DoricWebViewJSExecutor");
|
||||
// mDoricJSE = new DoricWebViewJSExecutor(Doric.application());
|
||||
// loadBuiltinJS("doric-web.js");
|
||||
// } else {
|
||||
mDoricRegistry.onLog(Log.WARN, "Use DoricWebShellJSExecutor");
|
||||
mDoricJSE = new DoricWebShellJSExecutor(Doric.application());
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@ import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.webkit.ConsoleMessage;
|
||||
import android.webkit.JavascriptInterface;
|
||||
import android.webkit.JsResult;
|
||||
@ -43,6 +44,7 @@ import org.json.JSONObject;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
@ -66,6 +68,8 @@ public class DoricWebShellJSExecutor implements IDoricJSE {
|
||||
private WebView webView;
|
||||
private final Map<String, JavaFunction> globalFunctions = new HashMap<>();
|
||||
private final Handler handler;
|
||||
private static final Map<String, SoftReference<byte[]>> arrayBuffers = new HashMap<>();
|
||||
private static final AtomicInteger arrayBufferId = new AtomicInteger();
|
||||
|
||||
private static Object unwrapJSObject(JSONObject jsonObject) {
|
||||
String type = jsonObject.optString("type");
|
||||
@ -90,6 +94,9 @@ public class DoricWebShellJSExecutor implements IDoricJSE {
|
||||
e.printStackTrace();
|
||||
return JSONObject.NULL;
|
||||
}
|
||||
case "arrayBuffer":
|
||||
String base64 = jsonObject.optString("value");
|
||||
return Base64.decode(base64, Base64.NO_WRAP);
|
||||
default:
|
||||
return JSONObject.NULL;
|
||||
}
|
||||
@ -127,6 +134,12 @@ public class DoricWebShellJSExecutor implements IDoricJSE {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (javaValue.getType() == 6) {
|
||||
byte[] data = javaValue.getByteData();
|
||||
String id = String.valueOf(arrayBufferId.incrementAndGet());
|
||||
arrayBuffers.put(id, new SoftReference<>(data));
|
||||
return new JSONBuilder().put("type", "arrayBuffer").put("value", id).toJSONObject();
|
||||
}
|
||||
return WRAPPED_NULL;
|
||||
}
|
||||
|
||||
@ -142,6 +155,16 @@ public class DoricWebShellJSExecutor implements IDoricJSE {
|
||||
readyFuture.set(true);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public String fetchArrayBuffer(String arrayBufferId) {
|
||||
SoftReference<byte[]> ref = arrayBuffers.remove(arrayBufferId);
|
||||
if (ref != null && ref.get() != null) {
|
||||
byte[] data = ref.get();
|
||||
return Base64.encodeToString(data, Base64.NO_WRAP);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public void log(String message) {
|
||||
DoricLog.d(message);
|
||||
|
@ -22,6 +22,7 @@ import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.webkit.ConsoleMessage;
|
||||
import android.webkit.JavascriptInterface;
|
||||
import android.webkit.JsResult;
|
||||
@ -39,8 +40,10 @@ import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import pub.doric.BuildConfig;
|
||||
import pub.doric.async.SettableFuture;
|
||||
@ -57,6 +60,8 @@ public class DoricWebViewJSExecutor implements IDoricJSE {
|
||||
private WebView webView;
|
||||
private final Map<String, JavaFunction> globalFunctions = new HashMap<>();
|
||||
private final Handler handler;
|
||||
private static final Map<String, SoftReference<byte[]>> arrayBuffers = new HashMap<>();
|
||||
private static final AtomicInteger arrayBufferId = new AtomicInteger();
|
||||
|
||||
private static Object unwrapJSObject(JSONObject jsonObject) {
|
||||
String type = jsonObject.optString("type");
|
||||
@ -81,6 +86,9 @@ public class DoricWebViewJSExecutor implements IDoricJSE {
|
||||
e.printStackTrace();
|
||||
return JSONObject.NULL;
|
||||
}
|
||||
case "arrayBuffer":
|
||||
String base64 = jsonObject.optString("value");
|
||||
return Base64.decode(base64, Base64.NO_WRAP);
|
||||
default:
|
||||
return JSONObject.NULL;
|
||||
}
|
||||
@ -118,6 +126,12 @@ public class DoricWebViewJSExecutor implements IDoricJSE {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (javaValue.getType() == 6) {
|
||||
byte[] data = javaValue.getByteData();
|
||||
String id = String.valueOf(arrayBufferId.incrementAndGet());
|
||||
arrayBuffers.put(id, new SoftReference<>(data));
|
||||
return new JSONBuilder().put("type", "arrayBuffer").put("value", id).toJSONObject();
|
||||
}
|
||||
return WRAPPED_NULL;
|
||||
}
|
||||
|
||||
@ -132,9 +146,19 @@ public class DoricWebViewJSExecutor implements IDoricJSE {
|
||||
DoricLog.d(message);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public String fetchArrayBuffer(String arrayBufferId) {
|
||||
SoftReference<byte[]> ref = arrayBuffers.remove(arrayBufferId);
|
||||
if (ref != null && ref.get() != null) {
|
||||
byte[] data = ref.get();
|
||||
return Base64.encodeToString(data, Base64.NO_WRAP);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public void returnNative(String result) {
|
||||
DoricLog.d("return Native" + result);
|
||||
DoricLog.d("return Native " + result);
|
||||
if (returnFuture != null) {
|
||||
returnFuture.set(result);
|
||||
}
|
||||
|
@ -52,6 +52,8 @@ import com.bumptech.glide.request.target.DrawableImageViewTarget;
|
||||
import com.bumptech.glide.request.target.SizeReadyCallback;
|
||||
import com.bumptech.glide.request.target.Target;
|
||||
import com.facebook.yoga.YogaNode;
|
||||
import com.github.pengfeizhou.jscore.ArchiveException;
|
||||
import com.github.pengfeizhou.jscore.JSDecoder;
|
||||
import com.github.pengfeizhou.jscore.JSONBuilder;
|
||||
import com.github.pengfeizhou.jscore.JSObject;
|
||||
import com.github.pengfeizhou.jscore.JSValue;
|
||||
@ -394,7 +396,7 @@ public class ImageNode extends ViewNode<ImageView> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void blend(ImageView view, String name, JSValue prop) {
|
||||
protected void blend(final ImageView view, String name, JSValue prop) {
|
||||
switch (name) {
|
||||
case "image":
|
||||
if (!prop.isObject()) {
|
||||
@ -522,13 +524,46 @@ public class ImageNode extends ViewNode<ImageView> {
|
||||
if (!prop.isObject()) {
|
||||
return;
|
||||
}
|
||||
int width = prop.asObject().getProperty("width").asNumber().toInt();
|
||||
int height = prop.asObject().getProperty("height").asNumber().toInt();
|
||||
byte[] pixels = prop.asObject().getProperty("pixels").asArrayBuffer().value();
|
||||
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(pixels);
|
||||
bitmap.copyPixelsFromBuffer(byteBuffer);
|
||||
view.setImageBitmap(bitmap);
|
||||
final int width = prop.asObject().getProperty("width").asNumber().toInt();
|
||||
final int height = prop.asObject().getProperty("height").asNumber().toInt();
|
||||
JSValue pixelsValue = prop.asObject().getProperty("pixels");
|
||||
if (pixelsValue.isArrayBuffer()) {
|
||||
byte[] pixels = prop.asObject().getProperty("pixels").asArrayBuffer().value();
|
||||
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(pixels);
|
||||
bitmap.copyPixelsFromBuffer(byteBuffer);
|
||||
view.setImageBitmap(bitmap);
|
||||
} else if (pixelsValue.isString()) {
|
||||
String pixelsCallbackId = pixelsValue.asString().value();
|
||||
callJSResponse(pixelsCallbackId).setCallback(new AsyncResult.Callback<JSDecoder>() {
|
||||
@Override
|
||||
public void onResult(JSDecoder result) {
|
||||
try {
|
||||
JSValue value = result.decode();
|
||||
if (value.isArrayBuffer()) {
|
||||
byte[] pixels = value.asArrayBuffer().value();
|
||||
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(pixels);
|
||||
bitmap.copyPixelsFromBuffer(byteBuffer);
|
||||
view.setImageBitmap(bitmap);
|
||||
}
|
||||
} catch (ArchiveException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
default:
|
||||
super.blend(view, name, prop);
|
||||
|
@ -0,0 +1,23 @@
|
||||
package com.github.pengfeizhou.jscore;
|
||||
|
||||
public class JSArrayBuffer extends JSValue {
|
||||
private final byte[] mVal;
|
||||
|
||||
public JSArrayBuffer(byte[] val) {
|
||||
this.mVal = val;
|
||||
}
|
||||
|
||||
public int getByteLength() {
|
||||
return mVal.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSType getJSType() {
|
||||
return JSType.ArrayBuffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] value() {
|
||||
return mVal;
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@ public abstract class JSValue {
|
||||
String,
|
||||
Object,
|
||||
Array,
|
||||
ArrayBuffer,
|
||||
}
|
||||
|
||||
public abstract JSType getJSType();
|
||||
@ -55,6 +56,10 @@ public abstract class JSValue {
|
||||
return getJSType() == JSType.Array;
|
||||
}
|
||||
|
||||
public boolean isArrayBuffer() {
|
||||
return getJSType() == JSType.ArrayBuffer;
|
||||
}
|
||||
|
||||
public JSNull asNull() {
|
||||
return (JSNull) this;
|
||||
}
|
||||
@ -79,6 +84,10 @@ public abstract class JSValue {
|
||||
return (JSArray) this;
|
||||
}
|
||||
|
||||
public JSArrayBuffer asArrayBuffer() {
|
||||
return (JSArrayBuffer) this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.valueOf(value());
|
||||
|
@ -25,10 +25,14 @@ public class JavaValue {
|
||||
protected static final int TYPE_STRING = 3;
|
||||
protected static final int TYPE_OBJECT = 4;
|
||||
protected static final int TYPE_ARRAY = 5;
|
||||
protected static final int TYPE_ARRAYBUFFER = 6;
|
||||
|
||||
protected JavaFunction[] functions = null;
|
||||
protected String[] functionNames = null;
|
||||
protected int type;
|
||||
protected String value = "";
|
||||
protected byte[] data = null;
|
||||
protected MemoryReleaser memoryReleaser = null;
|
||||
|
||||
public JavaValue() {
|
||||
this.type = TYPE_NULL;
|
||||
@ -75,6 +79,17 @@ public class JavaValue {
|
||||
this.value = jsonArray.toString();
|
||||
}
|
||||
|
||||
public JavaValue(byte[] data) {
|
||||
this.type = TYPE_ARRAYBUFFER;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public JavaValue(byte[] data, MemoryReleaser memoryReleaser) {
|
||||
this.type = TYPE_ARRAYBUFFER;
|
||||
this.data = data;
|
||||
this.memoryReleaser = memoryReleaser;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
@ -82,4 +97,24 @@ public class JavaValue {
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public byte[] getByteData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public interface MemoryReleaser {
|
||||
/**
|
||||
* Called when JS deallocated the arraybuffer
|
||||
*/
|
||||
void deallocate(byte[] data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by JNI
|
||||
*/
|
||||
private void onDeallocated() {
|
||||
if (this.memoryReleaser != null) {
|
||||
this.memoryReleaser.deallocate(this.data);
|
||||
}
|
||||
}
|
||||
}
|
@ -2314,12 +2314,10 @@ var Image = /** @class */ (function (_super) {
|
||||
};
|
||||
Image.prototype.toModel = function () {
|
||||
var ret = _super.prototype.toModel.call(this);
|
||||
if (Environment.platform === 'iOS') {
|
||||
if (Reflect.has(ret.props, "imagePixels")) {
|
||||
var imagePixels = Reflect.get(ret.props, "imagePixels");
|
||||
var pixels_1 = imagePixels.pixels;
|
||||
imagePixels.pixels = this.callback2Id(function () { return pixels_1; });
|
||||
}
|
||||
if (Reflect.has(ret.props, "imagePixels")) {
|
||||
var imagePixels = Reflect.get(ret.props, "imagePixels");
|
||||
var pixels_1 = imagePixels.pixels;
|
||||
imagePixels.pixels = this.callback2Id(function () { return pixels_1; });
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
@ -1728,12 +1728,10 @@ class Image extends View {
|
||||
}
|
||||
toModel() {
|
||||
const ret = super.toModel();
|
||||
if (Environment.platform === 'iOS') {
|
||||
if (Reflect.has(ret.props, "imagePixels")) {
|
||||
const imagePixels = Reflect.get(ret.props, "imagePixels");
|
||||
const pixels = imagePixels.pixels;
|
||||
imagePixels.pixels = this.callback2Id(() => pixels);
|
||||
}
|
||||
if (Reflect.has(ret.props, "imagePixels")) {
|
||||
const imagePixels = Reflect.get(ret.props, "imagePixels");
|
||||
const pixels = imagePixels.pixels;
|
||||
imagePixels.pixels = this.callback2Id(() => pixels);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -3256,12 +3256,10 @@ class Image extends View {
|
||||
}
|
||||
toModel() {
|
||||
const ret = super.toModel();
|
||||
if (Environment.platform === 'iOS') {
|
||||
if (Reflect.has(ret.props, "imagePixels")) {
|
||||
const imagePixels = Reflect.get(ret.props, "imagePixels");
|
||||
const pixels = imagePixels.pixels;
|
||||
imagePixels.pixels = this.callback2Id(() => pixels);
|
||||
}
|
||||
if (Reflect.has(ret.props, "imagePixels")) {
|
||||
const imagePixels = Reflect.get(ret.props, "imagePixels");
|
||||
const pixels = imagePixels.pixels;
|
||||
imagePixels.pixels = this.callback2Id(() => pixels);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -1,42 +1,53 @@
|
||||
'use strict';
|
||||
|
||||
"use strict";
|
||||
function _binaryValue(v) {
|
||||
switch (typeof v) {
|
||||
case "number":
|
||||
return {
|
||||
type: "number",
|
||||
value: v
|
||||
};
|
||||
case "string":
|
||||
return {
|
||||
type: "string",
|
||||
value: v
|
||||
};
|
||||
case "boolean":
|
||||
return {
|
||||
type: "boolean",
|
||||
value: v
|
||||
};
|
||||
case "object":
|
||||
if (v instanceof Array) {
|
||||
return {
|
||||
type: "array",
|
||||
value: JSON.stringify(v)
|
||||
};
|
||||
}
|
||||
else {
|
||||
return {
|
||||
type: "object",
|
||||
value: JSON.stringify(v)
|
||||
};
|
||||
}
|
||||
default:
|
||||
return {
|
||||
type: "null",
|
||||
value: undefined
|
||||
};
|
||||
function _arrayBufferToBase64(arrayBuffer) {
|
||||
let base64 = '';
|
||||
const encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
||||
const bytes = new Uint8Array(arrayBuffer);
|
||||
const byteLength = bytes.byteLength;
|
||||
const byteRemainder = byteLength % 3;
|
||||
const mainLength = byteLength - byteRemainder;
|
||||
let a, b, c, d;
|
||||
let chunk;
|
||||
// Main loop deals with bytes in chunks of 3
|
||||
for (var i = 0; i < mainLength; i = i + 3) {
|
||||
// Combine the three bytes into a single integer
|
||||
chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
|
||||
// Use bitmasks to extract 6-bit segments from the triplet
|
||||
a = (chunk & 16515072) >> 18; // 16515072 = (2^6 - 1) << 18
|
||||
b = (chunk & 258048) >> 12; // 258048 = (2^6 - 1) << 12
|
||||
c = (chunk & 4032) >> 6; // 4032 = (2^6 - 1) << 6
|
||||
d = chunk & 63; // 63 = 2^6 - 1
|
||||
// Convert the raw binary segments to the appropriate ASCII encoding
|
||||
base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d];
|
||||
}
|
||||
// Deal with the remaining bytes and padding
|
||||
if (byteRemainder == 1) {
|
||||
chunk = bytes[mainLength];
|
||||
a = (chunk & 252) >> 2; // 252 = (2^6 - 1) << 2
|
||||
// Set the 4 least significant bits to zero
|
||||
b = (chunk & 3) << 4; // 3 = 2^2 - 1
|
||||
base64 += encodings[a] + encodings[b] + '==';
|
||||
}
|
||||
else if (byteRemainder == 2) {
|
||||
chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1];
|
||||
a = (chunk & 64512) >> 10; // 64512 = (2^6 - 1) << 10
|
||||
b = (chunk & 1008) >> 4; // 1008 = (2^6 - 1) << 4
|
||||
// Set the 2 least significant bits to zero
|
||||
c = (chunk & 15) << 2; // 15 = 2^4 - 1
|
||||
base64 += encodings[a] + encodings[b] + encodings[c] + '=';
|
||||
}
|
||||
return base64;
|
||||
}
|
||||
function _base64ToArrayBuffer(v) {
|
||||
const binary_string = window.atob(v);
|
||||
const len = binary_string.length;
|
||||
const bytes = new Uint8Array(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
bytes[i] = binary_string.charCodeAt(i);
|
||||
}
|
||||
return bytes.buffer;
|
||||
}
|
||||
function _wrappedValue(v) {
|
||||
switch (typeof v) {
|
||||
@ -56,7 +67,13 @@ function _wrappedValue(v) {
|
||||
value: v
|
||||
};
|
||||
case "object":
|
||||
if (v instanceof Array) {
|
||||
if (v instanceof ArrayBuffer) {
|
||||
return {
|
||||
type: "arrayBuffer",
|
||||
value: _arrayBufferToBase64(v)
|
||||
};
|
||||
}
|
||||
else if (v instanceof Array) {
|
||||
return {
|
||||
type: "array",
|
||||
value: JSON.stringify(v)
|
||||
@ -89,6 +106,10 @@ function _rawValue(v) {
|
||||
return JSON.parse(v.value);
|
||||
}
|
||||
return v.value;
|
||||
case "arrayBuffer":
|
||||
const data = NativeClient.fetchArrayBuffer(v.value);
|
||||
return _base64ToArrayBuffer(data);
|
||||
;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
|
@ -2,51 +2,74 @@ declare module NativeClient {
|
||||
function log(message: string): void
|
||||
function returnNative(ret: string): void
|
||||
function callNative(name: string, args: string): string
|
||||
function fetchArrayBuffer(id: string): string
|
||||
}
|
||||
|
||||
type RawValue = number | string | boolean | object | undefined
|
||||
type RawValue = number | string | boolean | object | undefined | ArrayBuffer
|
||||
|
||||
type WrappedValue = {
|
||||
type: "number" | "string" | "boolean" | "object" | "array" | "null",
|
||||
type: "number" | "string" | "boolean" | "object" | "array" | "null" | "arrayBuffer",
|
||||
value: RawValue,
|
||||
}
|
||||
function _binaryValue(v: RawValue) {
|
||||
switch (typeof v) {
|
||||
case "number":
|
||||
return {
|
||||
type: "number",
|
||||
value: v
|
||||
};
|
||||
case "string":
|
||||
return {
|
||||
type: "string",
|
||||
value: v
|
||||
};
|
||||
case "boolean":
|
||||
return {
|
||||
type: "boolean",
|
||||
value: v
|
||||
};
|
||||
case "object":
|
||||
if (v instanceof Array) {
|
||||
return {
|
||||
type: "array",
|
||||
value: JSON.stringify(v)
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
type: "object",
|
||||
value: JSON.stringify(v)
|
||||
};
|
||||
}
|
||||
default:
|
||||
return {
|
||||
type: "null",
|
||||
value: undefined
|
||||
};
|
||||
|
||||
function _arrayBufferToBase64(arrayBuffer: ArrayBuffer) {
|
||||
let base64 = ''
|
||||
const encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
|
||||
|
||||
const bytes = new Uint8Array(arrayBuffer)
|
||||
const byteLength = bytes.byteLength
|
||||
const byteRemainder = byteLength % 3
|
||||
const mainLength = byteLength - byteRemainder
|
||||
|
||||
let a, b, c, d
|
||||
let chunk
|
||||
|
||||
// Main loop deals with bytes in chunks of 3
|
||||
for (var i = 0; i < mainLength; i = i + 3) {
|
||||
// Combine the three bytes into a single integer
|
||||
chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]
|
||||
// Use bitmasks to extract 6-bit segments from the triplet
|
||||
a = (chunk & 16515072) >> 18 // 16515072 = (2^6 - 1) << 18
|
||||
b = (chunk & 258048) >> 12 // 258048 = (2^6 - 1) << 12
|
||||
c = (chunk & 4032) >> 6 // 4032 = (2^6 - 1) << 6
|
||||
d = chunk & 63 // 63 = 2^6 - 1
|
||||
// Convert the raw binary segments to the appropriate ASCII encoding
|
||||
base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d]
|
||||
}
|
||||
|
||||
// Deal with the remaining bytes and padding
|
||||
if (byteRemainder == 1) {
|
||||
chunk = bytes[mainLength]
|
||||
|
||||
a = (chunk & 252) >> 2 // 252 = (2^6 - 1) << 2
|
||||
|
||||
// Set the 4 least significant bits to zero
|
||||
b = (chunk & 3) << 4 // 3 = 2^2 - 1
|
||||
|
||||
base64 += encodings[a] + encodings[b] + '=='
|
||||
} else if (byteRemainder == 2) {
|
||||
chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1]
|
||||
|
||||
a = (chunk & 64512) >> 10 // 64512 = (2^6 - 1) << 10
|
||||
b = (chunk & 1008) >> 4 // 1008 = (2^6 - 1) << 4
|
||||
|
||||
// Set the 2 least significant bits to zero
|
||||
c = (chunk & 15) << 2 // 15 = 2^4 - 1
|
||||
|
||||
base64 += encodings[a] + encodings[b] + encodings[c] + '='
|
||||
}
|
||||
return base64
|
||||
}
|
||||
function _base64ToArrayBuffer(v: string) {
|
||||
const binary_string = window.atob(v);
|
||||
const len = binary_string.length;
|
||||
const bytes = new Uint8Array(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
bytes[i] = binary_string.charCodeAt(i);
|
||||
}
|
||||
return bytes.buffer;
|
||||
}
|
||||
|
||||
function _wrappedValue(v: RawValue): WrappedValue {
|
||||
switch (typeof v) {
|
||||
case "number":
|
||||
@ -65,7 +88,12 @@ function _wrappedValue(v: RawValue): WrappedValue {
|
||||
value: v
|
||||
};
|
||||
case "object":
|
||||
if (v instanceof Array) {
|
||||
if (v instanceof ArrayBuffer) {
|
||||
return {
|
||||
type: "arrayBuffer",
|
||||
value: _arrayBufferToBase64(v)
|
||||
};
|
||||
} else if (v instanceof Array) {
|
||||
return {
|
||||
type: "array",
|
||||
value: JSON.stringify(v)
|
||||
@ -98,6 +126,9 @@ function _rawValue(v: WrappedValue): RawValue {
|
||||
return JSON.parse(v.value)
|
||||
}
|
||||
return v.value;
|
||||
case "arrayBuffer":
|
||||
const data = NativeClient.fetchArrayBuffer(v.value as string);
|
||||
return _base64ToArrayBuffer(data);;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
|
20
doric-js/lib/index.web.d.ts
vendored
20
doric-js/lib/index.web.d.ts
vendored
@ -2,25 +2,15 @@ declare module NativeClient {
|
||||
function log(message: string): void;
|
||||
function returnNative(ret: string): void;
|
||||
function callNative(name: string, args: string): string;
|
||||
function fetchArrayBuffer(id: string): string;
|
||||
}
|
||||
declare type RawValue = number | string | boolean | object | undefined;
|
||||
declare type RawValue = number | string | boolean | object | undefined | ArrayBuffer;
|
||||
declare type WrappedValue = {
|
||||
type: "number" | "string" | "boolean" | "object" | "array" | "null";
|
||||
type: "number" | "string" | "boolean" | "object" | "array" | "null" | "arrayBuffer";
|
||||
value: RawValue;
|
||||
};
|
||||
declare function _binaryValue(v: RawValue): {
|
||||
type: string;
|
||||
value: number;
|
||||
} | {
|
||||
type: string;
|
||||
value: string;
|
||||
} | {
|
||||
type: string;
|
||||
value: boolean;
|
||||
} | {
|
||||
type: string;
|
||||
value: undefined;
|
||||
};
|
||||
declare function _arrayBufferToBase64(arrayBuffer: ArrayBuffer): string;
|
||||
declare function _base64ToArrayBuffer(v: string): ArrayBufferLike;
|
||||
declare function _wrappedValue(v: RawValue): WrappedValue;
|
||||
declare function _rawValue(v: WrappedValue): RawValue;
|
||||
declare function __injectGlobalObject(name: string, args: string): void;
|
||||
|
@ -1,40 +1,51 @@
|
||||
"use strict";
|
||||
function _binaryValue(v) {
|
||||
switch (typeof v) {
|
||||
case "number":
|
||||
return {
|
||||
type: "number",
|
||||
value: v
|
||||
};
|
||||
case "string":
|
||||
return {
|
||||
type: "string",
|
||||
value: v
|
||||
};
|
||||
case "boolean":
|
||||
return {
|
||||
type: "boolean",
|
||||
value: v
|
||||
};
|
||||
case "object":
|
||||
if (v instanceof Array) {
|
||||
return {
|
||||
type: "array",
|
||||
value: JSON.stringify(v)
|
||||
};
|
||||
}
|
||||
else {
|
||||
return {
|
||||
type: "object",
|
||||
value: JSON.stringify(v)
|
||||
};
|
||||
}
|
||||
default:
|
||||
return {
|
||||
type: "null",
|
||||
value: undefined
|
||||
};
|
||||
function _arrayBufferToBase64(arrayBuffer) {
|
||||
let base64 = '';
|
||||
const encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
||||
const bytes = new Uint8Array(arrayBuffer);
|
||||
const byteLength = bytes.byteLength;
|
||||
const byteRemainder = byteLength % 3;
|
||||
const mainLength = byteLength - byteRemainder;
|
||||
let a, b, c, d;
|
||||
let chunk;
|
||||
// Main loop deals with bytes in chunks of 3
|
||||
for (var i = 0; i < mainLength; i = i + 3) {
|
||||
// Combine the three bytes into a single integer
|
||||
chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
|
||||
// Use bitmasks to extract 6-bit segments from the triplet
|
||||
a = (chunk & 16515072) >> 18; // 16515072 = (2^6 - 1) << 18
|
||||
b = (chunk & 258048) >> 12; // 258048 = (2^6 - 1) << 12
|
||||
c = (chunk & 4032) >> 6; // 4032 = (2^6 - 1) << 6
|
||||
d = chunk & 63; // 63 = 2^6 - 1
|
||||
// Convert the raw binary segments to the appropriate ASCII encoding
|
||||
base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d];
|
||||
}
|
||||
// Deal with the remaining bytes and padding
|
||||
if (byteRemainder == 1) {
|
||||
chunk = bytes[mainLength];
|
||||
a = (chunk & 252) >> 2; // 252 = (2^6 - 1) << 2
|
||||
// Set the 4 least significant bits to zero
|
||||
b = (chunk & 3) << 4; // 3 = 2^2 - 1
|
||||
base64 += encodings[a] + encodings[b] + '==';
|
||||
}
|
||||
else if (byteRemainder == 2) {
|
||||
chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1];
|
||||
a = (chunk & 64512) >> 10; // 64512 = (2^6 - 1) << 10
|
||||
b = (chunk & 1008) >> 4; // 1008 = (2^6 - 1) << 4
|
||||
// Set the 2 least significant bits to zero
|
||||
c = (chunk & 15) << 2; // 15 = 2^4 - 1
|
||||
base64 += encodings[a] + encodings[b] + encodings[c] + '=';
|
||||
}
|
||||
return base64;
|
||||
}
|
||||
function _base64ToArrayBuffer(v) {
|
||||
const binary_string = window.atob(v);
|
||||
const len = binary_string.length;
|
||||
const bytes = new Uint8Array(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
bytes[i] = binary_string.charCodeAt(i);
|
||||
}
|
||||
return bytes.buffer;
|
||||
}
|
||||
function _wrappedValue(v) {
|
||||
switch (typeof v) {
|
||||
@ -54,7 +65,13 @@ function _wrappedValue(v) {
|
||||
value: v
|
||||
};
|
||||
case "object":
|
||||
if (v instanceof Array) {
|
||||
if (v instanceof ArrayBuffer) {
|
||||
return {
|
||||
type: "arrayBuffer",
|
||||
value: _arrayBufferToBase64(v)
|
||||
};
|
||||
}
|
||||
else if (v instanceof Array) {
|
||||
return {
|
||||
type: "array",
|
||||
value: JSON.stringify(v)
|
||||
@ -87,6 +104,10 @@ function _rawValue(v) {
|
||||
return JSON.parse(v.value);
|
||||
}
|
||||
return v.value;
|
||||
case "arrayBuffer":
|
||||
const data = NativeClient.fetchArrayBuffer(v.value);
|
||||
return _base64ToArrayBuffer(data);
|
||||
;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
|
@ -50,12 +50,10 @@ export class Image extends View {
|
||||
}
|
||||
toModel() {
|
||||
const ret = super.toModel();
|
||||
if (Environment.platform === 'iOS') {
|
||||
if (Reflect.has(ret.props, "imagePixels")) {
|
||||
const imagePixels = Reflect.get(ret.props, "imagePixels");
|
||||
const pixels = imagePixels.pixels;
|
||||
imagePixels.pixels = this.callback2Id(() => pixels);
|
||||
}
|
||||
if (Reflect.has(ret.props, "imagePixels")) {
|
||||
const imagePixels = Reflect.get(ret.props, "imagePixels");
|
||||
const pixels = imagePixels.pixels;
|
||||
imagePixels.pixels = this.callback2Id(() => pixels);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -150,14 +150,12 @@ export class Image extends View {
|
||||
return this.nativeChannel(context, "getImagePixels")()
|
||||
}
|
||||
|
||||
toModel() {
|
||||
toModel(): NativeViewModel {
|
||||
const ret = super.toModel()
|
||||
if (Environment.platform === 'iOS') {
|
||||
if (Reflect.has(ret.props, "imagePixels")) {
|
||||
const imagePixels = Reflect.get(ret.props, "imagePixels")
|
||||
const pixels = imagePixels.pixels
|
||||
imagePixels.pixels = this.callback2Id(() => pixels)
|
||||
}
|
||||
if (Reflect.has(ret.props, "imagePixels")) {
|
||||
const imagePixels = Reflect.get(ret.props, "imagePixels")
|
||||
const pixels = imagePixels.pixels
|
||||
imagePixels.pixels = this.callback2Id(() => pixels)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
10
doric-web/dist/index.js
vendored
10
doric-web/dist/index.js
vendored
@ -3330,12 +3330,10 @@ class Image extends View {
|
||||
}
|
||||
toModel() {
|
||||
const ret = super.toModel();
|
||||
if (Environment.platform === 'iOS') {
|
||||
if (Reflect.has(ret.props, "imagePixels")) {
|
||||
const imagePixels = Reflect.get(ret.props, "imagePixels");
|
||||
const pixels = imagePixels.pixels;
|
||||
imagePixels.pixels = this.callback2Id(() => pixels);
|
||||
}
|
||||
if (Reflect.has(ret.props, "imagePixels")) {
|
||||
const imagePixels = Reflect.get(ret.props, "imagePixels");
|
||||
const pixels = imagePixels.pixels;
|
||||
imagePixels.pixels = this.callback2Id(() => pixels);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
2
doric-web/dist/index.js.map
vendored
2
doric-web/dist/index.js.map
vendored
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user