更新说明:

- 支持hsp多包图片资源
- 新增putCache写入缓存接口
- 修复入参为pixelMap图片不显示问题

Signed-off-by: 明月清风 <qiufeihu1@h-partners.com>
This commit is contained in:
明月清风 2024-05-08 17:45:08 +08:00
parent dd24720c00
commit 7030f5f55a
5 changed files with 195 additions and 68 deletions

View File

@ -1,4 +1,9 @@
## 3.0.0-rc.4
- 支持hsp多包图片资源
- 新增putCache写入缓存接口
- 修复入参为pixelMap图片不显示问题
## 3.0.0-rc.3
- 将请求默认并行从64调整到8减少对taskpool execute内存消耗
- 补充option参数placeholderObjectFiterrorholderObjectFit分别支持占位图填充效果和错误图填充效果

View File

@ -23,23 +23,27 @@
- 支持使用一个或多个图片变换,如模糊,高亮等
待实现特性
- gif/webp动图显示与控制
- 内存降采样优化,节约内存的占用
- 支持自定义图片解码
注意3.x版本相对2.x版本做了重大的重构主要体现在
- 使用Image组件代替Canvas组件渲染
- 重构Dispatch分发逻辑支持控制并发请求数支持请求排队队列的优先级
- 支持通过initMemoryCache自定义策略内存缓存策略和大小
- 支持option自定义实现图片获取/网络下载
因此API及能力上目前有部分差异主要体现在
- 不支持drawLifeCycle接口通过canvas自会图片
- mainScaleTypeborder等参数新版本与系统Image保持一致
- gif/webp动图播放与控制
- 抗锯齿相关参数
## 下载安装
```
ohpm install @ohos/imageknife
@ -48,7 +52,9 @@ await ImageKnife.getInstance().initFileCache(context, 256, 256 * 1024 * 1024)
```
## 使用说明
#### 1.显示本地资源图片
```
ImageKnifeComponent({
ImageKnifeOption: {
@ -61,6 +67,7 @@ ImageKnifeComponent({
```
#### 2.显示本地context files下文件
```
ImageKnifeComponent({
ImageKnifeOption: {
@ -73,6 +80,7 @@ ImageKnifeComponent({
```
#### 3.显示网络图片
```
ImageKnifeComponent({
ImageKnifeOption: {
@ -85,6 +93,7 @@ ImageKnifeComponent({
```
#### 4.自定义下载图片
```
ImageKnifeComponent({
ImageKnifeOption: {
@ -106,6 +115,7 @@ async function custom(context: Context, src: string | PixelMap | Resource): Prom
```
#### 5.监听网络下载进度
```
ImageKnifeComponent({
ImageKnifeOption: {
@ -114,7 +124,9 @@ ImageKnifeComponent({
}
}).width(100).height(100)
```
#### 6.支持option传入border设置边框圆角
```
ImageKnifeComponent({ ImageKnifeOption:
{
@ -123,7 +135,9 @@ ImageKnifeComponent({ ImageKnifeOption:
}
}).width(100).height(100)
```
#### 7.支持option图片变换
```
ImageKnifeComponent({ ImageKnifeOption:
{
@ -134,12 +148,12 @@ ImageKnifeComponent({ ImageKnifeOption:
}).width(100).height(100)
```
## 接口说明
### ImageKnifeOption参数列表
| 参数名称 | 入参内容 | 功能简介 |
|-----------------------|---------------------------|-----------------|
|-----------------------|--------------------------------|-----------------|
| loadSrc | string、PixelMap、Resource | 主图展示 |
| placeholderSrc | PixelMap、Resource | 占位图图展示(可选) |
| errorholderSrc | PixelMap、Resource | 错误图展示(可选) |
@ -158,20 +172,24 @@ ImageKnifeComponent({ ImageKnifeOption:
| transformation | PixelMapTransformation | 图片变换(可选) |
### ImageKnife接口
| 参数名称 | 入参内容 | 功能简介 |
|------------------|---------------------------|---------------|
|------------------|-------------------------------------------------------------------------------------------------------|---------------|
| initMemoryCache | newMemoryCache: IMemoryCache | 自定义内存缓存策略 |
| initFileCache | context: Context, size: number, memory: number | 初始化文件缓存数量和大小 |
| preLoadCache | url:string | 预加载并返回文件缓存路径 |
| getCacheImage | url: string, cacheType: CacheType | 从内存或文件缓存中获取资源 |
| preLoadCache | loadSrc: string I ImageKnifeOption | 预加载并返回文件缓存路径 |
| getCacheImage | loadSrc: string, cacheType: CacheStrategy = CacheStrategy.Default, signature?: string) | 从内存或文件缓存中获取资源 |
| addHeader | key: string, value: Object | 全局添加http请求头 |
| setHeaderOptions | Array<HeaderOptions> | 全局设置http请求头 |
| deleteHeader | key: string | 全局删除http请求头 |
| setEngineKeyImpl | IEngineKey | 全局配置缓存key生成策略 |
| putCacheImage | url: string, pixelMap: PixelMap, cacheType: CacheStrategy = CacheStrategy.Default, signature?: string | 写入内存磁盘缓存 |
## 约束与限制
在下述版本验证通过:
DevEco Studio 5.0 Canary35.0.3.221--SDK:API12
## 贡献代码
使用过程中发现任何问题都可以提 [issue](https://gitee.com/openharmony-tpc/ImageKnife/issues)
@ -182,4 +200,5 @@ DevEco Studio 5.0 Canary35.0.3.221--SDK:API12
本项目基于 [Apache License 2.0](https://gitee.com/openharmony-tpc/ImageKnife/blob/master/LICENSE) ,请自由的享受和参与开源。
## 遗留问题
- 添加组件闪动问题

View File

@ -14,13 +14,14 @@
*/
import { ImageKnifeComponent,BlurTransformation } from '@ohos/imageknife';
import fs from '@ohos.file.fs';
import image from '@ohos.multimedia.image';
@Entry
@Component
struct SingleImage {
scroller: Scroller = new Scroller;
localFile: string = getContext(this).filesDir + "/icon.png"
@State pixelMap:PixelMap | undefined = undefined;
aboutToAppear(): void {
// 拷贝本地文件
let icon: Uint8Array = getContext(this).resourceManager.getMediaContentSync($r("app.media.startIcon"));
@ -28,6 +29,9 @@ struct SingleImage {
fs.writeSync(file.fd, icon.buffer);
fs.fsyncSync(file.fd);
fs.closeSync(file);
getContext().resourceManager.getMediaContentSync( $r("app.media.aaa"))
.buffer as ArrayBuffer;
}
build() {
@ -80,11 +84,34 @@ struct SingleImage {
transformation: new BlurTransformation(10)
}
}).width(100).height(100)
Text("自定义下载")
.fontSize(30)
.fontWeight(FontWeight.Bold)
ImageKnifeComponent({
imageKnifeOption: {
loadSrc: this.pixelMap,
placeholderSrc: $r("app.media.loading"),
errorholderSrc: $r("app.media.app_icon"),
objectFit: ImageFit.Contain,
}
}).width(100).height(100)
}
.width('100%')
}
.height('100%')
}
changePic(buffer: ArrayBuffer){
let imageSource: image.ImageSource = image.createImageSource(buffer);
if (imageSource) {
let decodingOptions: image.DecodingOptions = {
editable: true,
}
imageSource.createPixelMap(decodingOptions,(err,pixelMap)=>{
this.pixelMap = pixelMap;
})
}
}
}
// 自定义下载方法

View File

@ -73,6 +73,7 @@ export class ImageKnife {
addHeader(key: string, value: Object) {
this.headerMap.set(key, value);
}
/**
* 全局设置请求头调用方法
* @param options 请求头数组
@ -82,6 +83,7 @@ export class ImageKnife {
this.headerMap.set(value.key, value.value)
})
}
/**
* 删除单个请求头属性
* @param key 请求头属性
@ -89,6 +91,7 @@ export class ImageKnife {
deleteHeader(key: string) {
this.headerMap.delete(key);
}
/**
* 设置自定义的内存缓存
* @param newMemoryCache 自定义内存缓存
@ -128,17 +131,21 @@ export class ImageKnife {
getFileCache(): FileCache {
return this.fileCache as FileCache
}
/**
* 预加载到文件缓存
* @param loadSrc 图片地址url
* @returns 返回文件缓存路径
*/
preLoadCache(loadSrc: string): Promise<string> {
preLoadCache(loadSrc: string | ImageKnifeOption): Promise<string> {
return new Promise((resolve, reject) => {
let imageKnifeOption = new ImageKnifeOption()
imageKnifeOption.loadSrc = loadSrc
let engineKeyImpl: IEngineKey = new DefaultEngineKey()
let fileKey = engineKeyImpl.generateFileKey(loadSrc)
if (loadSrc instanceof ImageKnifeOption) {
imageKnifeOption = loadSrc
} else {
imageKnifeOption.loadSrc = loadSrc;
}
let fileKey = this.getEngineKeyImpl().generateFileKey(imageKnifeOption.loadSrc, imageKnifeOption.signature)
let cachePath = ImageKnife.getInstance().getFileCache().getFileToPath(fileKey)
if (cachePath == null || cachePath == "" || cachePath == undefined) {
let request = new ImageKnifeRequest(
@ -159,18 +166,21 @@ export class ImageKnife {
}
})
}
/**
* 从内存或文件缓存中获取图片数据
* @param url 图片地址url
* @param cacheType 缓存策略
* @returns 图片数据
* @param signature key自定义信息
*/
getCacheImage(loadSrc: string,
cacheType: CacheStrategy = CacheStrategy.Default): Promise<ImageKnifeData | undefined> {
cacheType: CacheStrategy = CacheStrategy.Default, signature?: string): Promise<ImageKnifeData | undefined> {
let option: ImageKnifeOption = {
loadSrc: loadSrc
loadSrc: loadSrc,
signature:signature
}
let engineKeyImpl: IEngineKey = new DefaultEngineKey()
let engineKeyImpl: IEngineKey = this.getEngineKeyImpl();
return new Promise((resolve, reject) => {
if (cacheType == CacheStrategy.Memory) {
@ -184,6 +194,40 @@ export class ImageKnife {
})
}
/**
*
* @param url 图片地址url
* @param pixelMap 图片
* @param cacheType 缓存策略
* @param signature key自定义信息
*/
putCacheImage(url: string, pixelMap: PixelMap, cacheType: CacheStrategy = CacheStrategy.Default, signature?: string) {
let memoryKey = this.getEngineKeyImpl()
.generateMemoryKey(url, ImageKnifeRequestSource.SRC, { loadSrc: url, signature: signature });
let fileKey = this.getEngineKeyImpl().generateFileKey(url, signature);
let imageKnifeData: ImageKnifeData = { source: pixelMap, imageWidth: 0, imageHeight: 0 };
switch (cacheType) {
case CacheStrategy.Default:
this.saveMemoryCache(memoryKey, imageKnifeData);
this.saveFileCache(fileKey, this.pixelMapToArrayBuffer(pixelMap));
break;
case CacheStrategy.File:
this.saveFileCache(fileKey, this.pixelMapToArrayBuffer(pixelMap));
break
case CacheStrategy.Memory:
this.saveMemoryCache(memoryKey, imageKnifeData);
break
}
}
private pixelMapToArrayBuffer(pixelMap: PixelMap): ArrayBuffer {
let imageInfo = pixelMap.getImageInfoSync();
let readBuffer: ArrayBuffer = new ArrayBuffer(imageInfo.size.height * imageInfo.size.width * 4);
pixelMap.readPixelsToBufferSync(readBuffer);
return readBuffer
}
private readMemoryCache(loadSrc: string, option: ImageKnifeOption, engineKey: IEngineKey): ImageKnifeData | undefined {
let memoryKey = engineKey.generateMemoryKey(loadSrc, ImageKnifeRequestSource.SRC, option)
return ImageKnife.getInstance()
@ -225,6 +269,7 @@ export class ImageKnife {
onComplete(undefined)
}
}
/**
* 清除所有文件缓存
* @returns
@ -268,4 +313,7 @@ export class ImageKnife {
this.dispatcher.setEngineKeyImpl(impl);
}
getEngineKeyImpl(): IEngineKey {
return this.dispatcher.getEngineKeyImpl();
}
}

View File

@ -32,7 +32,12 @@ import { FileTypeUtil } from './utils/FileTypeUtil';
import util from '@ohos.util';
import { IEngineKey } from './key/IEngineKey';
import { DefaultEngineKey } from './key/DefaultEngineKey';
import { ImageKnifeRequestWithSource , ImageKnifeRequestSource , RequestJobResult , RequestJobRequest } from './model/ImageKnifeData'
import {
ImageKnifeRequestWithSource,
ImageKnifeRequestSource,
RequestJobResult,
RequestJobRequest
} from './model/ImageKnifeData'
export class ImageKnifeDispatcher {
// 最大并发
@ -45,8 +50,19 @@ export class ImageKnifeDispatcher {
private engineKey: IEngineKey = new DefaultEngineKey();
showFromMemomry(request: ImageKnifeRequest, imageSrc: string | PixelMap | Resource, requestSource: ImageKnifeRequestSource): boolean {
let memoryCache: ImageKnifeData | undefined = ImageKnife.getInstance()
.loadFromMemoryCache(this.engineKey.generateMemoryKey(imageSrc, requestSource, request.imageKnifeOption))
let memoryCache: ImageKnifeData | undefined;
if ((typeof (request.imageKnifeOption.loadSrc as image.PixelMap).isEditable) == 'boolean') {
memoryCache = {
source: request.imageKnifeOption.loadSrc as image.PixelMap,
imageWidth: 0,
imageHeight: 0,
}
} else {
memoryCache = ImageKnife.getInstance()
.loadFromMemoryCache(this.engineKey.generateMemoryKey(imageSrc, requestSource, request.imageKnifeOption));
}
if (memoryCache !== undefined) {
// 画主图
if (request.requestState === ImageKnifeRequestState.PROGRESS) {
@ -114,7 +130,8 @@ export class ImageKnifeDispatcher {
customGetImage: currentRequest.imageKnifeOption.customGetImage,
onlyRetrieveFromCache: currentRequest.imageKnifeOption.onlyRetrieveFromCache,
transformation: currentRequest.imageKnifeOption.transformation,
writeCacheStrategy: ImageKnife.getInstance().isFileCacheInit() ? currentRequest.imageKnifeOption.writeCacheStrategy : CacheStrategy.Memory, // 未初始化文件缓存时,不写文件缓存
writeCacheStrategy: ImageKnife.getInstance()
.isFileCacheInit() ? currentRequest.imageKnifeOption.writeCacheStrategy : CacheStrategy.Memory, // 未初始化文件缓存时,不写文件缓存
engineKey: this.engineKey,
signature: currentRequest.imageKnifeOption.signature,
requestSource
@ -215,6 +232,10 @@ export class ImageKnifeDispatcher {
setEngineKeyImpl(impl: IEngineKey): void {
this.engineKey = impl;
}
getEngineKeyImpl(): IEngineKey {
return this.engineKey;
}
}
/**
@ -321,10 +342,17 @@ async function requestJob(request: RequestJobRequest): Promise<RequestJobResult
}
}
} else if ((request.src as Resource).id !== undefined) { //从资源文件获取
let res = request.src as Resource;
if (resBuf == undefined && request.onlyRetrieveFromCache != true && request.requestSource == ImageKnifeRequestSource.SRC) {
resBuf = request.context.resourceManager.getMediaContentSync((request.src as Resource).id).buffer as ArrayBuffer
resBuf = request.context.createModuleContext(res.moduleName)
.resourceManager
.getMediaContentSync(res.id)
.buffer as ArrayBuffer;
} else if (resBuf == undefined && request.requestSource != ImageKnifeRequestSource.SRC) {
resBuf = request.context.resourceManager.getMediaContentSync((request.src as Resource).id).buffer as ArrayBuffer
resBuf = request.context.createModuleContext(res.moduleName)
.resourceManager
.getMediaContentSync(res.id)
.buffer as ArrayBuffer;
}
}
}