feat: Image add setImagePixels API,iOS receive ArrayBuffer directly
This commit is contained in:
		| @@ -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