feat:Image add pixel API,pass pixels directly to Image

This commit is contained in:
pengfei.zhou 2021-11-19 14:30:24 +08:00 committed by osborn
parent 62cb618923
commit 190eb4afd7
16 changed files with 378 additions and 161 deletions

View File

@ -20,7 +20,6 @@ import com.github.pengfeizhou.jscore.JSObject;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -51,12 +50,19 @@ public class DoricResourceManager {
String identifier = resource.getProperty("identifier").asString().value(); String identifier = resource.getProperty("identifier").asString().value();
DoricResource doricResource = cachedResources.get(resId); DoricResource doricResource = cachedResources.get(resId);
if (doricResource == null) { if (doricResource == null) {
if ("arrayBuffer".equals(type)) {
doricResource = new DoricArrayBufferResource(
doricContext,
resource.getProperty("data").asArrayBuffer().value()
);
} else {
DoricResourceLoader loader = mResourceLoaders.get(type); DoricResourceLoader loader = mResourceLoaders.get(type);
if (loader != null) { if (loader != null) {
doricResource = loader.load(doricContext, identifier); doricResource = loader.load(doricContext, identifier);
cachedResources.put(resId, doricResource); cachedResources.put(resId, doricResource);
} }
} }
}
return doricResource; return doricResource;
} }
} }

View File

@ -55,10 +55,14 @@ import com.facebook.yoga.YogaNode;
import com.github.pengfeizhou.jscore.JSONBuilder; import com.github.pengfeizhou.jscore.JSONBuilder;
import com.github.pengfeizhou.jscore.JSObject; import com.github.pengfeizhou.jscore.JSObject;
import com.github.pengfeizhou.jscore.JSValue; import com.github.pengfeizhou.jscore.JSValue;
import com.github.pengfeizhou.jscore.JavaValue;
import org.json.JSONObject;
import java.io.File; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.nio.ByteBuffer;
import androidx.vectordrawable.graphics.drawable.Animatable2Compat; import androidx.vectordrawable.graphics.drawable.Animatable2Compat;
import pub.doric.DoricContext; import pub.doric.DoricContext;
@ -514,6 +518,17 @@ public class ImageNode extends ViewNode<ImageView> {
callJSResponse(functionId); callJSResponse(functionId);
} }
}; };
case "imagePixels":
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);
break; break;
default: default:
super.blend(view, name, prop); super.blend(view, name, prop);
@ -561,4 +576,34 @@ public class ImageNode extends ViewNode<ImageView> {
errorImageBase64 = null; errorImageBase64 = null;
imageScale = DoricUtils.getScreenScale(); imageScale = DoricUtils.getScreenScale();
} }
@DoricMethod
public JSONObject getImageInfo() {
Drawable drawable = mView.getDrawable();
if (drawable instanceof BitmapDrawable) {
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
return new JSONBuilder()
.put("width", bitmap.getWidth())
.put("height", bitmap.getHeight())
.toJSONObject();
} else {
return new JSONBuilder()
.put("width", drawable.getIntrinsicWidth())
.put("height", drawable.getIntrinsicHeight())
.toJSONObject();
}
}
@DoricMethod
public JavaValue getImagePixels() {
Drawable drawable = mView.getDrawable();
if (drawable instanceof BitmapDrawable) {
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
ByteBuffer byteBuffer = ByteBuffer.allocate(bitmap.getByteCount());
bitmap.copyPixelsToBuffer(byteBuffer);
return new JavaValue(byteBuffer.array());
} else {
return new JavaValue();
}
}
} }

View File

@ -44,141 +44,141 @@ class ImageDemo extends Panel {
heightSpec: LayoutSpec.FIT, heightSpec: LayoutSpec.FIT,
}, },
}), }),
// image({ image({
// imageBase64: button, imageBase64: button,
// scaleType: ScaleType.ScaleToFill, scaleType: ScaleType.ScaleToFill,
// layoutConfig: { layoutConfig: {
// widthSpec: LayoutSpec.JUST, widthSpec: LayoutSpec.JUST,
// heightSpec: LayoutSpec.JUST, heightSpec: LayoutSpec.JUST,
// }, },
// width: 200, width: 200,
// height: 150 / 2.75, height: 150 / 2.75,
// stretchInset: { stretchInset: {
// left: 100, left: 100,
// top: 0, top: 0,
// right: 100, right: 100,
// bottom: 0 bottom: 0
// }, },
// imageScale: 2.75, imageScale: 2.75,
// }), }),
// image({ image({
// imageBase64: button, imageBase64: button,
// scaleType: ScaleType.ScaleToFill, scaleType: ScaleType.ScaleToFill,
// layoutConfig: { layoutConfig: {
// widthSpec: LayoutSpec.JUST, widthSpec: LayoutSpec.JUST,
// heightSpec: LayoutSpec.JUST, heightSpec: LayoutSpec.JUST,
// }, },
// width: 200, width: 200,
// height: 75, height: 75,
// stretchInset: { stretchInset: {
// left: 100, left: 100,
// top: 0, top: 0,
// right: 100, right: 100,
// bottom: 0 bottom: 0
// }, },
// imageScale: 2, imageScale: 2,
// }), }),
// label('Gif '), label('Gif '),
// image({ image({
// imageUrl: "https://www.w3.org/People/mimasa/test/imgformat/img/w3c_home_animation.gif", imageUrl: "https://www.w3.org/People/mimasa/test/imgformat/img/w3c_home_animation.gif",
// scaleType: ScaleType.ScaleToFill, scaleType: ScaleType.ScaleToFill,
// imageScale: 3, imageScale: 3,
// }), }),
// label('APNG'), label('APNG'),
// image({ image({
// imageUrl: "https://upload.wikimedia.org/wikipedia/commons/1/14/Animated_PNG_example_bouncing_beach_ball.png", imageUrl: "https://upload.wikimedia.org/wikipedia/commons/1/14/Animated_PNG_example_bouncing_beach_ball.png",
// }), }),
// label('Animated WebP'), label('Animated WebP'),
// image({ image({
// imageUrl: "https://p.upyun.com/demo/webp/webp/animated-gif-0.webp", imageUrl: "https://p.upyun.com/demo/webp/webp/animated-gif-0.webp",
// }), }),
// label('WebP'), label('WebP'),
// imageView = image({ imageView = image({
// imageUrl: "https://p.upyun.com/demo/webp/webp/jpg-0.webp", imageUrl: "https://p.upyun.com/demo/webp/webp/jpg-0.webp",
// layoutConfig: layoutConfig().just(), layoutConfig: layoutConfig().just(),
// width: 200, width: 200,
// height: 200, height: 200,
// loadCallback: (ret) => { loadCallback: (ret) => {
// if (ret) { if (ret) {
// imageView.width = ret.width imageView.width = ret.width
// imageView.height = ret.height imageView.height = ret.height
// } }
// } }
// }), }),
// label('ScaleToFill'), label('ScaleToFill'),
// image({ image({
// imageUrl, imageUrl,
// width: 300, width: 300,
// height: 300, height: 300,
// isBlur: true, isBlur: true,
// border: { border: {
// width: 2, width: 2,
// color: Color.GRAY, color: Color.GRAY,
// }, },
// scaleType: ScaleType.ScaleToFill, scaleType: ScaleType.ScaleToFill,
// layoutConfig: layoutConfig().just(), layoutConfig: layoutConfig().just(),
// loadCallback: (ret) => { loadCallback: (ret) => {
// } }
// }), }),
// label('ScaleAspectFit'), label('ScaleAspectFit'),
// image({ image({
// imageUrl, imageUrl,
// width: 300, width: 300,
// height: 300, height: 300,
// border: { border: {
// width: 2, width: 2,
// color: Color.GRAY, color: Color.GRAY,
// }, },
// scaleType: ScaleType.ScaleAspectFit, scaleType: ScaleType.ScaleAspectFit,
// layoutConfig: layoutConfig().just(), layoutConfig: layoutConfig().just(),
// }), }),
// label('ScaleAspectFill'), label('ScaleAspectFill'),
// image({ image({
// imageUrl, imageUrl,
// width: 300, width: 300,
// height: 300, height: 300,
// border: { border: {
// width: 2, width: 2,
// color: Color.GRAY, color: Color.GRAY,
// }, },
// scaleType: ScaleType.ScaleAspectFill, scaleType: ScaleType.ScaleAspectFill,
// layoutConfig: layoutConfig().just(), layoutConfig: layoutConfig().just(),
// }), }),
// label('ImageBase64'), label('ImageBase64'),
// image({ image({
// imageBase64: img_base64[0], imageBase64: img_base64[0],
// width: 300, width: 300,
// height: 300, height: 300,
// border: { border: {
// width: 2, width: 2,
// color: Color.GRAY, color: Color.GRAY,
// }, },
// scaleType: ScaleType.ScaleAspectFill, scaleType: ScaleType.ScaleAspectFill,
// layoutConfig: layoutConfig().just(), layoutConfig: layoutConfig().just(),
// }), }),
// label('StretchInset'), label('StretchInset'),
// image({ image({
// imageBase64: img_base64[1], imageBase64: img_base64[1],
// height: 60, height: 60,
// width: 134, width: 134,
// scaleType: ScaleType.ScaleAspectFill, scaleType: ScaleType.ScaleAspectFill,
// layoutConfig: layoutConfig().just(), layoutConfig: layoutConfig().just(),
// }), }),
// image({ image({
// imageBase64: img_base64[1], imageBase64: img_base64[1],
// height: 60, height: 60,
// width: 294, width: 294,
// scaleType: ScaleType.ScaleToFill, scaleType: ScaleType.ScaleToFill,
// layoutConfig: layoutConfig().just(), layoutConfig: layoutConfig().just(),
// stretchInset: { stretchInset: {
// left: 0.85, left: 0.85,
// top: 0, top: 0,
// right: 0.149, right: 0.149,
// bottom: 0 bottom: 0
// } }
// }), }),
], ],
{ {
layoutConfig: layoutConfig().most().configHeight(LayoutSpec.FIT), layoutConfig: layoutConfig().most().configHeight(LayoutSpec.FIT),

View File

@ -13,8 +13,6 @@ import {
VLayout, VLayout,
Text, Text,
Gravity, Gravity,
resourceLoader,
imageDecoder,
createRef, createRef,
loge, loge,
} from "doric"; } from "doric";
@ -25,6 +23,21 @@ import { img_base64 } from "./image_base64";
export class ResourceDemo extends Panel { export class ResourceDemo extends Panel {
build(root: Group): void { build(root: Group): void {
const iv = createRef<Image>(); const iv = createRef<Image>();
async function click() {
const imageInfo = await iv.current.getImageInfo(context);
loge(imageInfo);
const pixels = await iv.current.getImagePixels(context);
loge(pixels.byteLength);
const data = new Uint8Array(pixels);
for (let i = 0; i < data.length - 4; i += 4) {
data[i + 3] = 12;
}
iv.current.imagePixels = {
width: imageInfo.width,
height: imageInfo.height,
pixels: pixels,
};
}
<Scroller parent={root} layoutConfig={layoutConfig().most()}> <Scroller parent={root} layoutConfig={layoutConfig().most()}>
<VLayout <VLayout
layoutConfig={layoutConfig().mostWidth().fitHeight()} layoutConfig={layoutConfig().mostWidth().fitHeight()}
@ -39,7 +52,7 @@ export class ResourceDemo extends Panel {
textAlignment={Gravity.Center} textAlignment={Gravity.Center}
height={50} height={50}
> >
Image Demo Resource Demo
</Text> </Text>
{label("Button")} {label("Button")}
<Image <Image
@ -50,30 +63,15 @@ export class ResourceDemo extends Panel {
} }
/> />
<Image <Image
ref={iv}
image={ image={
new RemoteResource("https://p.upyun.com/demo/webp/webp/jpg-0.webp") new RemoteResource("https://p.upyun.com/demo/webp/webp/jpg-0.webp")
} }
/> />
<Image <Image
image={new Base64Resource(img_base64[0])} image={new Base64Resource(img_base64[0])}
ref={iv}
onClick={async () => { onClick={async () => {
const resource = new RemoteResource( await click();
"https://p.upyun.com/demo/webp/webp/jpg-0.webp"
);
const rawData = await resourceLoader(context).load(resource);
loge(rawData.byteLength);
const imageInfo = await imageDecoder(context).getImageInfo(
resource
);
loge(imageInfo);
const pixels = await imageDecoder(context).decodeToPixels(resource);
const unit8Array = new Uint8Array(pixels);
for (let i = 0; i < unit8Array.length; i += 4) {
unit8Array[i] -= 20;
}
loge(pixels.byteLength);
iv.current.image = resource;
}} }}
/> />
</VLayout> </VLayout>

View File

@ -2248,10 +2248,20 @@ var BundleResource = /** @class */ (function (_super) {
======= =======
}(Resource)); }(Resource));
var ArrayBufferResource = /** @class */ (function (_super) { var ArrayBufferResource = /** @class */ (function (_super) {
__extends$e(ArrayBufferResource, _super); __extends$f(ArrayBufferResource, _super);
function ArrayBufferResource(data) { function ArrayBufferResource(data) {
return _super.call(this, "arrayBuffer", "") || this; var _this = _super.call(this, "arrayBuffer", "") || this;
_this.data = data;
return _this;
} }
ArrayBufferResource.prototype.toModel = function () {
return {
data: this.data,
resId: this.resId,
type: this.type,
identifier: this.identifier,
};
};
return ArrayBufferResource; return ArrayBufferResource;
}(Resource)); }(Resource));
>>>>>>> f476a5b0... feat:android support ArrayBuffer Resource >>>>>>> f476a5b0... feat:android support ArrayBuffer Resource
@ -2300,6 +2310,16 @@ var Image = /** @class */ (function (_super) {
Image.prototype.stopAnimating = function (context) { Image.prototype.stopAnimating = function (context) {
return this.nativeChannel(context, "stopAnimating")(); return this.nativeChannel(context, "stopAnimating")();
}; };
Image.prototype.getImageInfo = function (context) {
return this.nativeChannel(context, "getImageInfo")();
};
Image.prototype.getImagePixels = function (context) {
return this.nativeChannel(context, "getImagePixels")();
};
__decorate$b([
Property,
__metadata$b("design:type", Object)
], Image.prototype, "imagePixels", void 0);
__decorate$b([ __decorate$b([
Property, Property,
__metadata$b("design:type", Resource) __metadata$b("design:type", Resource)

View File

@ -1683,6 +1683,15 @@ class BundleResource extends iOSResource {
class ArrayBufferResource extends Resource { class ArrayBufferResource extends Resource {
constructor(data) { constructor(data) {
super("arrayBuffer", ""); super("arrayBuffer", "");
this.data = data;
}
toModel() {
return {
data: this.data,
resId: this.resId,
type: this.type,
identifier: this.identifier,
};
} }
} }
@ -1711,7 +1720,17 @@ class Image extends View {
stopAnimating(context) { stopAnimating(context) {
return this.nativeChannel(context, "stopAnimating")(); return this.nativeChannel(context, "stopAnimating")();
} }
getImageInfo(context) {
return this.nativeChannel(context, "getImageInfo")();
} }
getImagePixels(context) {
return this.nativeChannel(context, "getImagePixels")();
}
}
__decorate$b([
Property,
__metadata$b("design:type", Object)
], Image.prototype, "imagePixels", void 0);
__decorate$b([ __decorate$b([
Property, Property,
__metadata$b("design:type", Resource) __metadata$b("design:type", Resource)

View File

@ -3211,6 +3211,15 @@ class BundleResource extends iOSResource {
class ArrayBufferResource extends Resource { class ArrayBufferResource extends Resource {
constructor(data) { constructor(data) {
super("arrayBuffer", ""); super("arrayBuffer", "");
this.data = data;
}
toModel() {
return {
data: this.data,
resId: this.resId,
type: this.type,
identifier: this.identifier,
};
} }
} }
@ -3239,7 +3248,17 @@ class Image extends View {
stopAnimating(context) { stopAnimating(context) {
return this.nativeChannel(context, "stopAnimating")(); return this.nativeChannel(context, "stopAnimating")();
} }
getImageInfo(context) {
return this.nativeChannel(context, "getImageInfo")();
} }
getImagePixels(context) {
return this.nativeChannel(context, "getImagePixels")();
}
}
__decorate$b([
Property,
__metadata$b("design:type", Object)
], Image.prototype, "imagePixels", void 0);
__decorate$b([ __decorate$b([
Property, Property,
__metadata$b("design:type", Resource) __metadata$b("design:type", Resource)

21
doric-js/index.d.ts vendored
View File

@ -626,6 +626,14 @@ declare module 'doric/lib/src/widget/image' {
ScaleAspectFill = 2 ScaleAspectFill = 2
} }
export class Image extends View { export class Image extends View {
/**
* Set pixels for image directly
*/
imagePixels?: {
width: number;
height: number;
pixels: ArrayBuffer;
};
/** /**
* This could be loaded by customized resource loader * This could be loaded by customized resource loader
*/ */
@ -699,6 +707,12 @@ declare module 'doric/lib/src/widget/image' {
isAnimating(context: BridgeContext): Promise<boolean>; isAnimating(context: BridgeContext): Promise<boolean>;
startAnimating(context: BridgeContext): Promise<any>; startAnimating(context: BridgeContext): Promise<any>;
stopAnimating(context: BridgeContext): Promise<any>; stopAnimating(context: BridgeContext): Promise<any>;
getImageInfo(context: BridgeContext): Promise<{
width: number;
height: number;
mimeType: string;
}>;
getImagePixels(context: BridgeContext): Promise<ArrayBuffer>;
} }
export function image(config: Partial<Image>): Image; export function image(config: Partial<Image>): Image;
} }
@ -1751,7 +1765,14 @@ declare module 'doric/lib/src/util/resource' {
constructor(bundleName: string, fileName: string); constructor(bundleName: string, fileName: string);
} }
export class ArrayBufferResource extends Resource { export class ArrayBufferResource extends Resource {
data: ArrayBuffer;
constructor(data: ArrayBuffer); constructor(data: ArrayBuffer);
toModel(): {
data: ArrayBuffer;
resId: string;
type: string;
identifier: string;
};
} }
} }

View File

@ -51,5 +51,12 @@ export declare class BundleResource extends iOSResource {
constructor(bundleName: string, fileName: string); constructor(bundleName: string, fileName: string);
} }
export declare class ArrayBufferResource extends Resource { export declare class ArrayBufferResource extends Resource {
data: ArrayBuffer;
constructor(data: ArrayBuffer); constructor(data: ArrayBuffer);
toModel(): {
data: ArrayBuffer;
resId: string;
type: string;
identifier: string;
};
} }

View File

@ -74,5 +74,14 @@ export class BundleResource extends iOSResource {
export class ArrayBufferResource extends Resource { export class ArrayBufferResource extends Resource {
constructor(data) { constructor(data) {
super("arrayBuffer", ""); super("arrayBuffer", "");
this.data = data;
}
toModel() {
return {
data: this.data,
resId: this.resId,
type: this.type,
identifier: this.identifier,
};
} }
} }

View File

@ -8,6 +8,14 @@ export declare enum ScaleType {
ScaleAspectFill = 2 ScaleAspectFill = 2
} }
export declare class Image extends View { export declare class Image extends View {
/**
* Set pixels for image directly
*/
imagePixels?: {
width: number;
height: number;
pixels: ArrayBuffer;
};
/** /**
* This could be loaded by customized resource loader * This could be loaded by customized resource loader
*/ */
@ -81,5 +89,11 @@ export declare class Image extends View {
isAnimating(context: BridgeContext): Promise<boolean>; isAnimating(context: BridgeContext): Promise<boolean>;
startAnimating(context: BridgeContext): Promise<any>; startAnimating(context: BridgeContext): Promise<any>;
stopAnimating(context: BridgeContext): Promise<any>; stopAnimating(context: BridgeContext): Promise<any>;
getImageInfo(context: BridgeContext): Promise<{
width: number;
height: number;
mimeType: string;
}>;
getImagePixels(context: BridgeContext): Promise<ArrayBuffer>;
} }
export declare function image(config: Partial<Image>): Image; export declare function image(config: Partial<Image>): Image;

View File

@ -42,7 +42,17 @@ export class Image extends View {
stopAnimating(context) { stopAnimating(context) {
return this.nativeChannel(context, "stopAnimating")(); return this.nativeChannel(context, "stopAnimating")();
} }
getImageInfo(context) {
return this.nativeChannel(context, "getImageInfo")();
} }
getImagePixels(context) {
return this.nativeChannel(context, "getImagePixels")();
}
}
__decorate([
Property,
__metadata("design:type", Object)
], Image.prototype, "imagePixels", void 0);
__decorate([ __decorate([
Property, Property,
__metadata("design:type", Resource) __metadata("design:type", Resource)

View File

@ -85,8 +85,17 @@ export class BundleResource extends iOSResource {
} }
export class ArrayBufferResource extends Resource { export class ArrayBufferResource extends Resource {
data: ArrayBuffer
constructor(data: ArrayBuffer) { constructor(data: ArrayBuffer) {
super("arrayBuffer", "") super("arrayBuffer", "")
this.data = data
}
toModel() {
return {
data: this.data,
resId: this.resId,
type: this.type,
identifier: this.identifier,
}
} }
} }

View File

@ -26,6 +26,11 @@ export enum ScaleType {
} }
export class Image extends View { export class Image extends View {
/**
* Set pixels for image directly
*/
@Property
imagePixels?: { width: number, height: number, pixels: ArrayBuffer }
/** /**
* This could be loaded by customized resource loader * This could be loaded by customized resource loader
*/ */
@ -132,6 +137,18 @@ export class Image extends View {
stopAnimating(context: BridgeContext) { stopAnimating(context: BridgeContext) {
return this.nativeChannel(context, "stopAnimating")() return this.nativeChannel(context, "stopAnimating")()
} }
getImageInfo(context: BridgeContext): Promise<{
width: number,
height: number,
mimeType: string,
}> {
return this.nativeChannel(context, "getImageInfo")()
}
getImagePixels(context: BridgeContext): Promise<ArrayBuffer> {
return this.nativeChannel(context, "getImagePixels")()
}
} }
export function image(config: Partial<Image>) { export function image(config: Partial<Image>) {

View File

@ -3285,6 +3285,15 @@ class BundleResource extends iOSResource {
class ArrayBufferResource extends Resource { class ArrayBufferResource extends Resource {
constructor(data) { constructor(data) {
super("arrayBuffer", ""); super("arrayBuffer", "");
this.data = data;
}
toModel() {
return {
data: this.data,
resId: this.resId,
type: this.type,
identifier: this.identifier,
};
} }
} }
@ -3313,7 +3322,17 @@ class Image extends View {
stopAnimating(context) { stopAnimating(context) {
return this.nativeChannel(context, "stopAnimating")(); return this.nativeChannel(context, "stopAnimating")();
} }
getImageInfo(context) {
return this.nativeChannel(context, "getImageInfo")();
} }
getImagePixels(context) {
return this.nativeChannel(context, "getImagePixels")();
}
}
__decorate$b([
Property,
__metadata$b("design:type", Object)
], Image.prototype, "imagePixels", void 0);
__decorate$b([ __decorate$b([
Property, Property,
__metadata$b("design:type", Resource) __metadata$b("design:type", Resource)

File diff suppressed because one or more lines are too long