android: compat webviewexecutor to support arraybuffer

This commit is contained in:
pengfei.zhou 2021-12-31 16:55:38 +08:00 committed by osborn
parent c67803b717
commit 8c57e5e500
19 changed files with 381 additions and 177 deletions

View File

@ -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;
}
}

View File

@ -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());
}
// }
}
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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());

View File

@ -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);
}
}
}

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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
}

View File

@ -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;
}

File diff suppressed because one or more lines are too long