feat: Image add setImagePixels API,iOS receive ArrayBuffer directly
This commit is contained in:
parent
aa837b807a
commit
9bd4ba8722
@ -71,6 +71,7 @@ import pub.doric.DoricContext;
|
||||
import pub.doric.async.AsyncResult;
|
||||
import pub.doric.extension.bridge.DoricMethod;
|
||||
import pub.doric.extension.bridge.DoricPlugin;
|
||||
import pub.doric.extension.bridge.DoricPromise;
|
||||
import pub.doric.resource.DoricResource;
|
||||
import pub.doric.shader.flex.FlexNode;
|
||||
import pub.doric.utils.DoricLog;
|
||||
@ -527,43 +528,11 @@ public class ImageNode extends ViewNode<ImageView> {
|
||||
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);
|
||||
@ -641,4 +610,17 @@ public class ImageNode extends ViewNode<ImageView> {
|
||||
return new JavaValue();
|
||||
}
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void setImagePixels(JSObject imagePixels, DoricPromise promise) {
|
||||
final int width = imagePixels.asObject().getProperty("width").asNumber().toInt();
|
||||
final int height = imagePixels.asObject().getProperty("height").asNumber().toInt();
|
||||
JSValue pixelsValue = imagePixels.asObject().getProperty("pixels");
|
||||
byte[] pixels = imagePixels.asObject().getProperty("pixels").asArrayBuffer().value();
|
||||
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(pixels);
|
||||
bitmap.copyPixelsFromBuffer(byteBuffer);
|
||||
mView.setImageBitmap(bitmap);
|
||||
promise.resolve();
|
||||
}
|
||||
}
|
||||
|
@ -44,6 +44,8 @@ export class ImageProcessorDemo extends Panel {
|
||||
build(root: Group): void {
|
||||
const iv = createRef<Image>();
|
||||
const imageUrl = "https://doric.pub/about/The_Parthenon_in_Athens.jpg";
|
||||
let imageInfo: { width: number; height: number };
|
||||
let pixels: ArrayBuffer;
|
||||
<Scroller parent={root} layoutConfig={layoutConfig().most()}>
|
||||
<VLayout
|
||||
layoutConfig={layoutConfig().mostWidth().fitHeight()}
|
||||
@ -64,6 +66,10 @@ export class ImageProcessorDemo extends Panel {
|
||||
layoutConfig={layoutConfig().justWidth().fitHeight()}
|
||||
width={(Environment.screenWidth / 5) * 4}
|
||||
imageUrl={imageUrl}
|
||||
loadCallback={async () => {
|
||||
imageInfo = await iv.current.getImageInfo(context);
|
||||
pixels = (await iv.current.getImagePixels(context)).slice(0);
|
||||
}}
|
||||
/>
|
||||
<VLayout
|
||||
layoutConfig={layoutConfig().mostWidth().fitHeight()}
|
||||
@ -91,13 +97,8 @@ export class ImageProcessorDemo extends Panel {
|
||||
const spinnerRef = createRef<Stack>();
|
||||
const containerRef = createRef<GestureContainer>();
|
||||
this.addOnRenderFinishedCallback(async () => {
|
||||
const imageInfo = await iv.current.getImageInfo(context);
|
||||
const pixels = (await iv.current.getImagePixels(context)).slice(
|
||||
0
|
||||
);
|
||||
const data = new Uint8Array(pixels);
|
||||
|
||||
async function changeAlpha(alpha: number) {
|
||||
const data = new Uint8Array(pixels);
|
||||
for (let i = 0; i < data.length - 4; i += 4) {
|
||||
data[i + 3] = alpha;
|
||||
}
|
||||
@ -147,21 +148,25 @@ export class ImageProcessorDemo extends Panel {
|
||||
const spinnerRef = createRef<Stack>();
|
||||
const containerRef = createRef<GestureContainer>();
|
||||
this.addOnRenderFinishedCallback(async () => {
|
||||
const imageInfo = await iv.current.getImageInfo(context);
|
||||
const pixels = (await iv.current.getImagePixels(context)).slice(
|
||||
0
|
||||
);
|
||||
const data = new Uint32Array(pixels);
|
||||
let data: Uint32Array | undefined = undefined;
|
||||
async function binarizationImage(t: number) {
|
||||
if (!!!data) {
|
||||
data = new Uint32Array(pixels);
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
let { r, g, b } = pixelToRGBA(data[i]);
|
||||
data[i] = Math.floor(r * 0.2989 + g * 0.587 + b * 0.114);
|
||||
}
|
||||
async function binarizationImage(t: number) {
|
||||
}
|
||||
const arrayBuffer = pixels.slice(0);
|
||||
const ret = new Uint32Array(arrayBuffer);
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
ret[i] = data[i] < t ? 0xff000000 : 0xffffffff;
|
||||
}
|
||||
iv.current.setImagePixels(context, {
|
||||
width: imageInfo.width,
|
||||
height: imageInfo.height,
|
||||
pixels: arrayBuffer,
|
||||
});
|
||||
iv.current.imagePixels = {
|
||||
width: imageInfo.width,
|
||||
height: imageInfo.height,
|
||||
@ -208,13 +213,9 @@ export class ImageProcessorDemo extends Panel {
|
||||
const spinnerRef = createRef<Stack>();
|
||||
const containerRef = createRef<GestureContainer>();
|
||||
this.addOnRenderFinishedCallback(async () => {
|
||||
const imageInfo = await iv.current.getImageInfo(context);
|
||||
const rawPixels = (
|
||||
await iv.current.getImagePixels(context)
|
||||
).slice(0);
|
||||
async function blurEffect(radius: number) {
|
||||
radius = Math.round(radius);
|
||||
const buffer = rawPixels.slice(0);
|
||||
const buffer = pixels.slice(0);
|
||||
const data = new Uint32Array(buffer);
|
||||
fastBlur(data, imageInfo.width, imageInfo.height, radius);
|
||||
iv.current.imagePixels = {
|
||||
|
@ -6,9 +6,5 @@
|
||||
#import <JavaScriptCore/JavaScriptCore.h>
|
||||
|
||||
@interface JSValue (Doric)
|
||||
- (BOOL)isArrayBuffer;
|
||||
|
||||
- (NSData *)toArrayBuffer;
|
||||
|
||||
- (id)toObjectWithArrayBuffer;
|
||||
@end
|
@ -159,29 +159,6 @@ id valueToObject(JSContext *context, JSValueRef value) {
|
||||
return containerValueToObject([context JSGlobalContextRef], result);
|
||||
}
|
||||
|
||||
- (BOOL)isArrayBuffer {
|
||||
JSContextRef ctx = self.context.JSGlobalContextRef;
|
||||
JSValueRef jsValueRef = self.JSValueRef;
|
||||
if (self.isObject) {
|
||||
JSTypedArrayType type = JSValueGetTypedArrayType(ctx, jsValueRef, NULL);
|
||||
return type == kJSTypedArrayTypeArrayBuffer;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSData *)toArrayBuffer {
|
||||
if (!self.isArrayBuffer) {
|
||||
return nil;
|
||||
}
|
||||
JSContextRef ctx = self.context.JSGlobalContextRef;
|
||||
JSValueRef jsValueRef = self.JSValueRef;
|
||||
JSObjectRef ref = JSValueToObject(ctx, jsValueRef, NULL);
|
||||
size_t size = JSObjectGetArrayBufferByteLength(ctx, ref, NULL);
|
||||
void *ptr = JSObjectGetArrayBufferBytesPtr(ctx, ref, NULL);
|
||||
|
||||
return [[NSData alloc] initWithBytesNoCopy:ptr length:size freeWhenDone:NO];
|
||||
}
|
||||
|
||||
- (id)toObjectWithArrayBuffer {
|
||||
return valueToObject(self.context, self.JSValueRef);
|
||||
}
|
||||
|
@ -54,6 +54,7 @@ - (void)displayLayer:(CALayer *)layer {
|
||||
#elif DORIC_USE_SDWEBIMAGE
|
||||
|
||||
#import <SDWebImage/SDWebImage.h>
|
||||
#import <DoricPromise.h>
|
||||
|
||||
@interface DoricImageView : SDAnimatedImageView
|
||||
@end
|
||||
@ -582,15 +583,9 @@ - (void)blendView:(UIImageView *)view forPropName:(NSString *)name propValue:(id
|
||||
NSDictionary *imagePixels = prop;
|
||||
NSUInteger width = [imagePixels[@"width"] unsignedIntValue];
|
||||
NSUInteger height = [imagePixels[@"height"] unsignedIntValue];
|
||||
NSString *pixelsCallbackId = imagePixels[@"pixels"];
|
||||
[[self callJSResponse:pixelsCallbackId, nil] setResultCallback:^(JSValue *pixelsValue) {
|
||||
if (![pixelsValue isArrayBuffer]) {
|
||||
return;
|
||||
}
|
||||
NSData *data = [pixelsValue toArrayBuffer];
|
||||
[self.doricContext.driver ensureSyncInMainQueue:^{
|
||||
NSData *pixels = imagePixels[@"pixels"];
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGContextRef context = CGBitmapContextCreate((void *) data.bytes,
|
||||
CGContextRef context = CGBitmapContextCreate((void *) pixels.bytes,
|
||||
width,
|
||||
height,
|
||||
8,
|
||||
@ -602,9 +597,7 @@ - (void)blendView:(UIImageView *)view forPropName:(NSString *)name propValue:(id
|
||||
CGImageRelease(imageRef);
|
||||
CGContextRelease(context);
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
self.view.image = image;
|
||||
}];
|
||||
}];
|
||||
view.image = image;
|
||||
} else {
|
||||
[super blendView:view forPropName:name propValue:prop];
|
||||
}
|
||||
@ -741,6 +734,9 @@ - (NSDictionary *)getImageInfo {
|
||||
}
|
||||
|
||||
- (NSData *)getImagePixels {
|
||||
if (!self.view.image) {
|
||||
return nil;
|
||||
}
|
||||
CGImageRef imageRef = [self.view.image CGImage];
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
size_t width = CGImageGetWidth(imageRef);
|
||||
@ -760,10 +756,30 @@ - (NSData *)getImagePixels {
|
||||
CGContextDrawImage(contextRef, CGRectMake(0, 0, width, height), imageRef);
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
CGContextRelease(contextRef);
|
||||
|
||||
return [[NSData alloc] initWithBytesNoCopy:imageData length:width * height * bytesPerPixel freeWhenDone:YES];
|
||||
}
|
||||
|
||||
- (void)setImagePixels:(NSDictionary *)imagePixels withPromise:(DoricPromise *)promise {
|
||||
NSUInteger width = [imagePixels[@"width"] unsignedIntValue];
|
||||
NSUInteger height = [imagePixels[@"height"] unsignedIntValue];
|
||||
NSData *pixels = imagePixels[@"pixels"];
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGContextRef context = CGBitmapContextCreate((void *) pixels.bytes,
|
||||
width,
|
||||
height,
|
||||
8,
|
||||
width * 4,
|
||||
colorSpace,
|
||||
kCGImageAlphaPremultipliedLast);
|
||||
CGImageRef imageRef = CGBitmapContextCreateImage(context);
|
||||
UIImage *image = [[UIImage alloc] initWithCGImage:imageRef scale:UIScreen.mainScreen.scale orientation:UIImageOrientationUp];
|
||||
CGImageRelease(imageRef);
|
||||
CGContextRelease(context);
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
self.view.image = image;
|
||||
[promise resolve:nil];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if (self.animationEndCallbackId) {
|
||||
#if DORIC_USE_YYWEBIMAGE
|
||||
|
@ -2295,14 +2295,8 @@ var Image = /** @class */ (function (_super) {
|
||||
Image.prototype.getImagePixels = function (context) {
|
||||
return this.nativeChannel(context, "getImagePixels")();
|
||||
};
|
||||
Image.prototype.toModel = function () {
|
||||
var ret = _super.prototype.toModel.call(this);
|
||||
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;
|
||||
Image.prototype.setImagePixels = function (context, image) {
|
||||
return this.nativeChannel(context, "setImagePixels")(image);
|
||||
};
|
||||
__decorate$b([
|
||||
Property,
|
||||
|
@ -1712,14 +1712,8 @@ class Image extends View {
|
||||
getImagePixels(context) {
|
||||
return this.nativeChannel(context, "getImagePixels")();
|
||||
}
|
||||
toModel() {
|
||||
const ret = super.toModel();
|
||||
if (Reflect.has(ret.props, "imagePixels")) {
|
||||
const imagePixels = Reflect.get(ret.props, "imagePixels");
|
||||
const pixels = imagePixels.pixels;
|
||||
imagePixels.pixels = this.callback2Id(() => pixels);
|
||||
}
|
||||
return ret;
|
||||
setImagePixels(context, image) {
|
||||
return this.nativeChannel(context, "setImagePixels")(image);
|
||||
}
|
||||
}
|
||||
__decorate$b([
|
||||
|
@ -3240,14 +3240,8 @@ class Image extends View {
|
||||
getImagePixels(context) {
|
||||
return this.nativeChannel(context, "getImagePixels")();
|
||||
}
|
||||
toModel() {
|
||||
const ret = super.toModel();
|
||||
if (Reflect.has(ret.props, "imagePixels")) {
|
||||
const imagePixels = Reflect.get(ret.props, "imagePixels");
|
||||
const pixels = imagePixels.pixels;
|
||||
imagePixels.pixels = this.callback2Id(() => pixels);
|
||||
}
|
||||
return ret;
|
||||
setImagePixels(context, image) {
|
||||
return this.nativeChannel(context, "setImagePixels")(image);
|
||||
}
|
||||
}
|
||||
__decorate$b([
|
||||
|
8
doric-js/index.d.ts
vendored
8
doric-js/index.d.ts
vendored
@ -618,7 +618,7 @@ declare module 'doric/lib/src/widget/text' {
|
||||
}
|
||||
|
||||
declare module 'doric/lib/src/widget/image' {
|
||||
import { View, NativeViewModel } from "doric/lib/src/ui/view";
|
||||
import { View } from "doric/lib/src/ui/view";
|
||||
import { Color } from "doric/lib/src/util/color";
|
||||
import { BridgeContext } from "doric/lib/src/runtime/global";
|
||||
import { Resource } from "doric/lib/src/util/resource";
|
||||
@ -715,7 +715,11 @@ declare module 'doric/lib/src/widget/image' {
|
||||
mimeType: string;
|
||||
}>;
|
||||
getImagePixels(context: BridgeContext): Promise<ArrayBuffer>;
|
||||
toModel(): NativeViewModel;
|
||||
setImagePixels(context: BridgeContext, image: {
|
||||
width: number;
|
||||
height: number;
|
||||
pixels: ArrayBuffer;
|
||||
}): Promise<void>;
|
||||
}
|
||||
export function image(config: Partial<Image>): Image;
|
||||
}
|
||||
|
8
doric-js/lib/src/widget/image.d.ts
vendored
8
doric-js/lib/src/widget/image.d.ts
vendored
@ -1,4 +1,4 @@
|
||||
import { View, NativeViewModel } from "../ui/view";
|
||||
import { View } from "../ui/view";
|
||||
import { Color } from "../util/color";
|
||||
import { BridgeContext } from "../runtime/global";
|
||||
import { Resource } from "../util/resource";
|
||||
@ -95,6 +95,10 @@ export declare class Image extends View {
|
||||
mimeType: string;
|
||||
}>;
|
||||
getImagePixels(context: BridgeContext): Promise<ArrayBuffer>;
|
||||
toModel(): NativeViewModel;
|
||||
setImagePixels(context: BridgeContext, image: {
|
||||
width: number;
|
||||
height: number;
|
||||
pixels: ArrayBuffer;
|
||||
}): Promise<void>;
|
||||
}
|
||||
export declare function image(config: Partial<Image>): Image;
|
||||
|
@ -48,14 +48,8 @@ export class Image extends View {
|
||||
getImagePixels(context) {
|
||||
return this.nativeChannel(context, "getImagePixels")();
|
||||
}
|
||||
toModel() {
|
||||
const ret = super.toModel();
|
||||
if (Reflect.has(ret.props, "imagePixels")) {
|
||||
const imagePixels = Reflect.get(ret.props, "imagePixels");
|
||||
const pixels = imagePixels.pixels;
|
||||
imagePixels.pixels = this.callback2Id(() => pixels);
|
||||
}
|
||||
return ret;
|
||||
setImagePixels(context, image) {
|
||||
return this.nativeChannel(context, "setImagePixels")(image);
|
||||
}
|
||||
}
|
||||
__decorate([
|
||||
|
@ -150,14 +150,12 @@ export class Image extends View {
|
||||
return this.nativeChannel(context, "getImagePixels")()
|
||||
}
|
||||
|
||||
toModel(): NativeViewModel {
|
||||
const ret = super.toModel()
|
||||
if (Reflect.has(ret.props, "imagePixels")) {
|
||||
const imagePixels = Reflect.get(ret.props, "imagePixels")
|
||||
const pixels = imagePixels.pixels
|
||||
imagePixels.pixels = this.callback2Id(() => pixels)
|
||||
}
|
||||
return ret
|
||||
setImagePixels(context: BridgeContext, image: {
|
||||
width: number,
|
||||
height: number,
|
||||
pixels: ArrayBuffer
|
||||
}): Promise<void> {
|
||||
return this.nativeChannel(context, "setImagePixels",)(image)
|
||||
}
|
||||
}
|
||||
|
||||
|
10
doric-web/dist/index.js
vendored
10
doric-web/dist/index.js
vendored
@ -3314,14 +3314,8 @@ class Image extends View {
|
||||
getImagePixels(context) {
|
||||
return this.nativeChannel(context, "getImagePixels")();
|
||||
}
|
||||
toModel() {
|
||||
const ret = super.toModel();
|
||||
if (Reflect.has(ret.props, "imagePixels")) {
|
||||
const imagePixels = Reflect.get(ret.props, "imagePixels");
|
||||
const pixels = imagePixels.pixels;
|
||||
imagePixels.pixels = this.callback2Id(() => pixels);
|
||||
}
|
||||
return ret;
|
||||
setImagePixels(context, image) {
|
||||
return this.nativeChannel(context, "setImagePixels")(image);
|
||||
}
|
||||
}
|
||||
__decorate$b([
|
||||
|
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