diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2a69d5e..2c5d7cc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,35 +1,107 @@
-## 2.3.0-rc.2
-- 加载优先级Priority类型改为taskpool.Priority类型
+## 3.0.1-rc.2
+- 修复自定义下载失败无失败回调
+- 增加全局配置自定义下载接口
+- 修复主图相同,错误图不同导致只显示一个错误图
+- heic格式图片文件魔数从第五位开始匹配
+
+## 3.0.1-rc.1
+- 新增ImageKnifeAnimatorComponent控制动图组件
- 修复部分heif图无法解码
-## 2.3.0-rc.1
-- 修复file://格式图片无法显示
-- 修改uuid的生成方式,使用资源loadSrc,宽高以及转换效果的拼接字符串作为入参, 修复同资源图形转换错乱的问题
+## 3.0.1-rc.0
+- 文件缓存设置最大缓存数量改为无限制
-## 2.3.0-rc.0
-- 增加gif图duration的默认值,以及默认播放次数
+## 3.0.0
+- 修复图形变换的闪退问题
+- 自定义下载customGetImage改为仅主图支持
+- 修改网络请求requestInStream配置优先返回arraybuffer
+- 新增ColorFilter属性
-## 2.2.0
-- 修复错误图绘制完后变成占位图
-- 提供图片加载成功/失败的事件
-- 修复懒加载在多次点击出现卡死的问题
+## 3.0.0-rc.9
+- 修复Resource类型$r(变量无法)加载
+- 成功回调增加图片格式
+- Image组件增加onComplete回调
+- 修复404链接无返回错误信息
+- onLoadListener增加请求取消回调
+
+## 3.0.0-rc.8
+- svg解码单位改为px
+- 修复预加载接口preLoadCache传ImageKnifeOption失效
+- 文件缓存初始化接口新增目录参数
+- 占位图从内存获取提前到判断队列前面
+- 图片改为不可拖拽
+- 修复getCacheImage默认内存获取后不返回数据
+- 成功回调返回GIF图宽高
+
+## 3.0.0-rc.7
+- 修复成功回调获取不到宽高
+- 新增svg图片解码
+- 新增媒体图片file://格式
+- 修复头像超过设备高度图片闪动问题-消息列表底部头像闪动问题
+
+## 3.0.0-rc.6
- 支持多种组合变换
+- 支持全局配置是否在子线程请求加载图片,默认在子线程
+- 文件缓存初始化增加默认值
+- 预加载接口新增返回加载错误信息
+- 加载队列改为使用堆Stack
+- fileType图片格式新增heic格式
+
+## 3.0.0-rc.5
+- 图片加载事件增加请求开始的回调,以及修复有缓存时,没有回调的bug
+- 修复对已销毁组件不再下发请求的逻辑
+- 加载图片流程添加日志
+- 子线程写入文件缓存获取buffer优化
+- 成功回调增加返回图片分辨率宽高
+- 内存缓存时将pixelMap进行release释放
- 提供清理缓存能力
-- 修复preLoad接口失效
-- 修复多线程图片加载出现空白问题
+
+## 3.0.0-rc.4
+- 支持hsp多包图片资源
+- 新增putCache写入缓存接口
+- 修复入参为pixelMap图片不显示问题
+- 网络请求减少拼接操作,修复网络加载速度慢
+- 提供图片加载成功/失败的事件
+
+## 3.0.0-rc.3
+- 将请求默认并行从64调整到8,减少对taskpool execute内存消耗
+- 补充option参数:placeholderObjectFit,errorholderObjectFit分别支持占位图填充效果和错误图填充效果
+
+## 3.0.0-rc.2
+- 新增支持使用一个或多个图片变换,如模糊,高亮等
+
+## 3.0.0-rc.1
+- 新增从内存或文件缓存获取图片数据接口getCacheImage
+- 新增图片预加载preLoadCache并返回文件缓存路径
+- ImageKnifeOption新增writeCacheStrategy存入策略(只存入内存或文件缓存)
+- ImageKnifeOption新增onlyRetrieveFromCache仅用缓存加载
+- 新增单个和全局请求头
+- 补齐自定key特性
- 获取组件宽高改用onSizeChange (需要API12)
-- svg解码宽高单位给位px
-- 修复复用场景下从内存获取图片后又清空了画布导致图片不显示
-- 修复复用场景主图比占位图绘制快后下次不显示占位图问题
+
+## 3.0.0-rc.0
+- 使用Image组件替换Canvas组件渲染,并重构大部分的实现逻辑,提升渲染性能
+
+较2.x版本增强点:
+- 使用Image组件代替Canvas组件渲染
+- 重构Dispatch分发逻辑,支持控制并发请求数,支持请求排队队列的优先级
+- 支持通过initMemoryCache自定义策略内存缓存策略和大小。
+- 支持option自定义实现图片获取/网络下载
+- 继承Image的能力,支持option传入border,设置边框,圆角
+- 继承Image的能力,支持option传入objectFit设置图片缩放
+- 修复发送消息时最近的两条消息头像闪动的问题
+
+缺失特性
+- 不支持drawLifeCycle接口,通过canvas自会图片
+- mainScaleType,border等参数,新版本与系统Image保持一致
+- gif/webp动图播放与控制
+- signature自定义key的实现
+- 支持进行图片变换: 支持图像像素源图片变换效果。
+- 抗锯齿相关参数
## 2.2.0-rc.2
-- ImageKnife支持下采样
- ImageKnife支持heic图片修改demo,按钮控制组件是否展示
-- 修复通过磁盘链接加载图片无法显示
- ImageKnife控制可视化区域图片
-- 修复占位图、错误图、重试图从内存获取之后进入子线程导致内存泄露
-- ImageKnifeComponent组件key属性改为id属性
-- 修改header图的存储标志位
## 2.2.0-rc.1
- 修改ImageKnife跳过网络,点击默认,图片没有传入宽高,无显示bug
@@ -283,12 +355,12 @@
新增
- - 1.onClick事件属性
+ - 1.onClick事件属性
- 删除
+ 删除
- - 1.size(设置大小)
- - 2.sizeAnimated 显式动画
+ - 1.size(设置大小)
+ - 2.sizeAnimated 显式动画
- 3.backgroundColor背景色
- 4.margin 组件外间距 等属性,删除的属性将由通用属性提供支持,可支持在ImageKnifeComponent自定义组件上链式调用
## 1.0.4
diff --git a/README.md b/README.md
index f17bd4b..3f50768 100644
--- a/README.md
+++ b/README.md
@@ -4,630 +4,355 @@
## 简介
-本项目基于开源库 [Glide](https://github.com/bumptech/glide) 进行OpenHarmony的自研版本:
+本项目参考开源库 [Glide](https://github.com/bumptech/glide) 进行OpenHarmony的自研版本:
-- 支持内存缓存,使用LRUCache算法,对图片数据进行内存缓存。
-- 支持磁盘缓存,对于下载图片会保存一份至磁盘当中。
-- 支持进行图片变换: 支持图像像素源图片变换效果。
-- 支持用户配置参数使用:(
- 例如:配置是否开启一级内存缓存,配置磁盘缓存策略,配置仅使用缓存加载数据,配置图片变换效果,配置占位图,配置加载失败占位图等)。
-- 推荐使用ImageKnifeComponent组件配合ImageKnifeOption参数来实现功能。
-- 支持用户自定义配置实现能力参考ImageKnifeComponent组件中对于入参ImageKnifeOption的处理。
+- 支持自定义内存缓存策略,支持设置内存缓存的大小(默认LRU策略)。
+- 支持磁盘二级缓存,对于下载图片会保存一份至磁盘当中。
+- 支持自定义实现图片获取/网络下载
+- 支持监听网络下载回调进度
+- 继承Image的能力,支持option传入border,设置边框,圆角
+- 继承Image的能力,支持option传入objectFit设置图片缩放,包括objectFit为auto时根据图片自适应高度
+- 支持通过设置transform缩放图片
+- 并发请求数量,支持请求排队队列的优先级
+- 支持生命周期已销毁的图片,不再发起请求
+- 自定义缓存key
+- 自定义http网络请求头
+- 支持writeCacheStrategy控制缓存的存入策略(只存入内存或文件缓存)
+- 支持preLoadCache预加载图片
+- 支持onlyRetrieveFromCache仅用缓存加载
+- 支持使用一个或多个图片变换,如模糊,高亮等
-
+待实现特性
+
+- gif/webp动图显示与控制
+- 内存降采样优化,节约内存的占用
+- 支持自定义图片解码
+
+注意:3.x版本相对2.x版本做了重大的重构,主要体现在:
+
+- 使用Image组件代替Canvas组件渲染
+- 重构Dispatch分发逻辑,支持控制并发请求数,支持请求排队队列的优先级
+- 支持通过initMemoryCache自定义策略内存缓存策略和大小
+- 支持option自定义实现图片获取/网络下载
+
+因此API及能力上,目前有部分差异,主要体现在:
+
+- 不支持drawLifeCycle接口,通过canvas自会图片
+- mainScaleType,border等参数,新版本与系统Image保持一致
+- gif/webp动图播放与控制
+- 抗锯齿相关参数
## 下载安装
```
ohpm install @ohos/imageknife
+
+// 如果需要用文件缓存,需要提前初始化文件缓存
+await ImageKnife.getInstance().initFileCache(context, 256, 256 * 1024 * 1024)
```
-## X86模拟器配置
+## 使用说明
-[使用模拟器运行应用/服务](https://developer.huawei.com/consumer/cn/deveco-developer-suite/enabling/kit?currentPage=1&pageSize=100)
+#### 1.显示本地资源图片
-
-## 使用说明
-
-### 1.依赖配置
-在entry\src\main\ets\entryability\EntryAbility.ts中做如下配置初始化全局ImageKnife实例:
-
-```typescript
-import UIAbility from '@ohos.app.ability.UIAbility';
-import window from '@ohos.window';
-import { ImageKnife } from '@ohos/imageknife'
-
-export default class EntryAbility extends UIAbility {
- onWindowStageCreate(windowStage: window.WindowStage) {
- windowStage.loadContent('pages/Index', (err, data) => {
- });
- // 初始化全局ImageKnife
- ImageKnife.with(this.context);
- // 后续访问ImageKnife请通过:ImageKnifeGlobal.getInstance().getImageKnife()方式
+```
+ImageKnifeComponent({
+ ImageKnifeOption: {
+ loadSrc: $r("app.media.app_icon"),
+ placeholderSrc: $r("app.media.loading"),
+ errorholderSrc: $r("app.media.app_icon"),
+ objectFit: ImageFit.Auto
}
-}
+}).width(100).height(100)
```
-### 2.加载普通图片
+#### 2.显示本地context files下文件
-接下来我们来写个简单实例看看:
-
-```extendtypescript
-import { ImageKnifeComponent, ImageKnifeOption } from '@ohos/imageknife'
-
-@Entry
-@Component
-struct Index {
- @State message: string = 'Hello World'
- @State option: ImageKnifeOption = {
- loadSrc: $r('app.media.icon')
+```
+ImageKnifeComponent({
+ ImageKnifeOption: {
+ loadSrc: this.localFile,
+ placeholderSrc: $r("app.media.loading"),
+ errorholderSrc: $r("app.media.app_icon"),
+ objectFit: ImageFit.Auto
}
+}).width(100).height(100)
+```
- build() {
- Row() {
- Column() {
- Text(this.message)
- .fontSize(50)
- .fontWeight(FontWeight.Bold)
- ImageKnifeComponent({ imageKnifeOption: this.option })
- .width(300)
- .height(300)
- }.width('100%')
- }.height('100%')
+#### 3.显示网络图片
+
+```
+ImageKnifeComponent({
+ ImageKnifeOption: {
+ loadSrc:"https://www.openharmony.cn/_nuxt/img/logo.dcf95b3.png",
+ placeholderSrc: $r("app.media.loading"),
+ errorholderSrc: $r("app.media.app_icon"),
+ objectFit: ImageFit.Auto
}
-}
+}).width(100).height(100)
```
-非常简单,仅需定义一个ImageKnifeOption数据对象,然后在你需要的UI位置,加入ImageKnifeComponent自定义组件就可以加载出一张图像了。
+#### 4.自定义下载图片
-### 3.加载SVG图片
-
-加载svg其实和普通流程没有区别,只要将 `loadSrc: $r('app.media.jpgSample'),` `改成一张 loadSrc: $r('app.media.svgSample'),`
-svg类型图片即可。
-
-注:SVG文件需添加xml声明,应以" {
- let result: DataFetchResult = new DataFetchResult();
- result.data = arraybuffer; //此处替换成自己网络获取的ArrayBuffer的逻辑
- return result;
+async function custom(context: Context, src: string | PixelMap | Resource): Promise {
+ console.info("ImageKnife:: custom download:" + src)
+ // 举例写死从本地文件读取,也可以自己请求网络图片
+ return context.resourceManager.getMediaContentSync($r("app.media.bb").id).buffer as ArrayBuffer
}
```
-7.2 自定义网络栈加载全部图片
-```
-1.先执行ImageKnifeGlobal.getInstance().getImageKnife()?.replaceDataFetch(new CustomDataFetchClient()); 替换网络栈
-2.在CustomDataFetchClient这个类中也通过下面的逻辑替换成自己网络获取的ArrayBuffer的逻辑
- let result: DataFetchResult = new DataFetchResult();
- result.data = arraybuffer; //此处替换成自己网络获取的ArrayBuffer的逻辑
- return result;
-```
-7.3 取消自定义网络栈加载全部图片
-```
-如果用户执行了自定义网络加载全部图片,后面又不想自定义网络栈加载全部图片了,可以通过下面的方式恢复
-ImageKnifeGlobal.getInstance().getImageKnife()?.replaceDataFetch(new CustomDataFetchClient());
-```
-### 8.监听图片加载成功与失败
+#### 5.监听网络下载进度
+
```
-ImageKnifeNextComponent({
- imageKnifeNextOption: {
- loadSrc: 'http://e.hiphotos.baidu.com/image/pic/item/4e4a20a4462309f7e41f5cfe760e0cf3d6cad6ee.jpg',
- onLoadListener: {
- console.log('Load Successful: ' + data);
- return data;
- },
- onLoadFailed: (err) => {
- console.error('Load Failed Reason: ' + err);
- }
+ImageKnifeComponent({
+ ImageKnifeOption: {
+ loadSrc:"https://www.openharmony.cn/_nuxt/img/logo.dcf95b3.png",
+ progressListener:(progress:number)=>{console.info("ImageKinfe:: call back progress = " + progress)}
}
- }
}).width(100).height(100)
```
-## 进阶使用
+#### 6.支持option传入border,设置边框,圆角
-如果简单的加载一张图像无法满足需求,我们可以看看ImageKnifeOption这个类提供了哪些扩展能力。
+```
+ImageKnifeComponent({ ImageKnifeOption:
+{
+ loadSrc: $r("app.media.rabbit"),
+ border: {radius:50}
+ }
+}).width(100).height(100)
+```
+
+#### 7.支持option图片变换
+
+```
+ImageKnifeComponent({ ImageKnifeOption:
+{
+ loadSrc: $r("app.media.rabbit"),
+ border: {radius:50},
+ transformation: new BlurTransformation(3)
+ }
+}).width(100).height(100)
+```
+多种组合变换用法
+
+```
+let transformations: collections.Array = new collections.Array();
+transformations.push(new BlurTransformation(5));
+transformations.push(new BrightnessTransformation(0.2));
+ImageKnifeComponent({
+ {
+ loadSrc: $r('app.media.pngSample'),
+ placeholderSrc: $r("app.media.loading"),
+ errorholderSrc: $r("app.media.app_icon"),
+ objectFit: ImageFit.Contain,
+ border: { radius: { topLeft: 50, bottomRight: 50 } }, // 圆角设置
+ transformation: transformations.length > 0 ? new MultiTransTransformation(transformations) : undefined // 图形变换组
+}
+}).width(300)
+ .height(300)
+ .rotate({ angle: 90 }) // 旋转90度
+ .contrast(12) // 对比度滤波器
+```
+其他变换相关属性,可叠加实现组合变换效果
+
+圆形裁剪变换示例
+
+```
+ImageKnifeComponent({ ImageKnifeOption:
+ {
+ loadSrc: $r('app.media.pngSample'),
+ objectFit: ImageFit.Cover,
+ border: { radius: 150 }
+}
+}).width(300)
+ .height(300)
+```
+
+圆形裁剪带边框变换示例
+
+```
+ImageKnifeComponent({ ImageKnifeOption:
+ {
+ loadSrc: $r('app.media.pngSample'),
+ objectFit: ImageFit.Cover,
+ border: { radius: 150, color: Color.Red, width: 5 }
+}
+}).width(300)
+ .height(300)
+```
+
+对比度滤波变换示例
+
+```
+ImageKnifeComponent({
+ imageKnifeOption: {
+ loadSrc: $r('app.media.pngSample')
+ }
+}).width(300)
+ .height(300)
+ .contrast(12)
+```
+
+旋转变换示例
+
+```
+ImageKnifeComponent({
+ imageKnifeOption: {
+ loadSrc: $r('app.media.pngSample')
+ }
+}).width(300)
+ .height(300)
+ .rotate({angle:90})
+ .backgroundColor(Color.Pink)
+```
+
+#### 8.监听图片加载成功与失败
+
+```
+ImageKnifeComponent({ ImageKnifeOption:
+{
+ loadSrc: $r("app.media.rabbit"),
+ onLoadListener:{
+ onLoadStart:()=>{
+ this.starTime = new Date().getTime()
+ console.info("Load start: ");
+ },
+ onLoadFailed: (err) => {
+ console.error("Load Failed Reason: " + err + " cost " + (new Date().getTime() - this.starTime) + " milliseconds");
+ },
+ onLoadSuccess: (data, imageData) => {
+ console.info("Load Successful: cost " + (new Date().getTime() - this.starTime) + " milliseconds");
+ return data;
+ },
+ onLoadCancel(err){
+ console.info(err)
+ }
+ }
+ }
+}).width(100).height(100)
+```
+#### 9.ImageKnifeComponent - syncLoad
+设置是否同步加载图片,默认是异步加载。建议加载尺寸较小的本地图片时将syncLoad设为true,因为耗时较短,在主线程上执行即可
+```
+ImageKnifeComponent({
+ imageKnifeOption:{
+ loadSrc:$r("app.media.pngSample"),
+ placeholderSrc:$r("app.media.loading")
+ },syncLoad:true
+ })
+```
+#### 10.ImageKnifeAnimatorComponent 示例
+```
+ImageKnifeAnimatorComponent({
+ imageKnifeOption:{
+ loadSrc:"https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658",
+ placeholderSrc:$r('app.media.loading'),
+ errorholderSrc:$r('app.media.failed')
+ },animatorOption:this.animatorOption
+ }).width(300).height(300).backgroundColor(Color.Orange).margin({top:30})
+```
+## 接口说明
+### ImageKnife组件
+| 组件名称 | 入参内容 | 功能简介 |
+|-----------------------------|---------------------------------|--------|
+| ImageKnifeComponent | ImageKnifeOption | 图片显示组件 |
+| ImageKnifeAnimatorComponent | ImageKnifeOption、AnimatorOption | 动图控制组件 |
+
+### AnimatorOption参数列表
+| 参数名称 | 入参内容 | 功能简介 |
+|-----------------------|-------------------------------------------------------|----------|
+| state | AnimationStatus | 播放状态(可选) |
+| iterations | number | 播放次数(可选) |
+| reverse | boolean | 播放顺序(可选) |
### ImageKnifeOption参数列表
-| 参数名称 | 入参内容 | 功能简介 |
-|------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------|
-| loadSrc | string\ | PixelMap\ |Resource | 图片数据源 |
-| mainScaleType | ScaleType | 设置主图展示样式(可选) |
-| strategy | DiskStrategy | 设置磁盘缓存策略(可选) |
-| dontAnimateFlag | boolean | gif加载展示一帧(可选) |
-| placeholderSrc | string\ (占位图不支持gif图且不支持网络下载功能) | PixelMap\ |Resource | 占位图数据源 |
-| placeholderScaleType | ScaleType | 设置占位图展示样式(可选) |
-| errorholderSrc | PixelMap\ | Resource | 错误占位图数据源 |
-| errorholderSrcScaleType | ScaleType | 设置失败占位图展示样式(可选) |
-| retryholderSrc | PixelMap\ | Resource | 重试占位图数据源 |
-| retryholderScaleType | ScaleType | 设置重试占位图展示样式(可选) |
-| fallbackSrc | PixelMap\ | Resource | 后备回调符数据源 |
-| thumbSizeMultiplier | number 范围(0,1] | 设置缩略图占比(可选) |
-| thumbSizeDelay | number | 设置缩略图展示时间(可选) |
-| thumbSizeMultiplierScaleType | ScaleType | 设置缩略图展示样式(可选) |
-| displayProgress | boolean | 设置是否展示下载进度条(可选) |
-| canRetryClick | boolean | 设置重试图层是否点击重试(可选) |
-| onlyRetrieveFromCache | boolean | 仅使用缓存加载数据(可选) |
-| isCacheable | boolean | 是否开启一级内存缓存(可选) |
-| gif | {
// 返回一周期动画gif消耗的时间
loopFinish?: (loopTime?) => void
// gif播放速率相关
speedFactory?: number
// 直接展示gif第几帧数据
seekTo?: number
playTimes?: number
} | GIF播放控制能力(可选) |
-| transformation | BaseTransform | 单个变换(可选) |
-| transformations | Array> | 多个变换(可选) |
-| allCacheInfoCallback | IAllCacheInfoCallback | 输出缓存相关内容和信息(可选) |
-| signature | ObjectKey | 自定key(可选) |
-| **drawLifeCycle** | **IDrawLifeCycle** | **用户自定义实现绘制方案(可选)** |
-| imageSmoothingEnabled | boolean | 抗锯齿是否开启属性配置,设置为false时,imageSmoothingQuality失效 |
-| imageSmoothingQuality | AntiAliasing | 抗锯齿属性配置 |
-| autoPlay | boolean | GIF播放暂停控制(可选) |
-| customGetImage | (Context, string) | 设置是否使用应用自定义的方式加载图片(可选) |
-| onLoadListener | onLoadSuccess: (data: string | PixelMap | Resource | undefined) => void;onLoadFailed: (err: string) => void; | 监听图片加载成功/失败 |
+| 参数名称 | 入参内容 | 功能简介 |
+|-----------------------|-------------------------------------------------------|-----------------|
+| loadSrc | string、PixelMap、Resource | 主图展示 |
+| placeholderSrc | PixelMap、Resource | 占位图图展示(可选) |
+| errorholderSrc | PixelMap、Resource | 错误图展示(可选) |
+| objectFit | ImageFit | 主图填充效果(可选) |
+| placeholderObjectFit | ImageFit | 占位图填充效果(可选) |
+| errorholderObjectFit | ImageFit | 错误图填充效果(可选) |
+| writeCacheStrategy | CacheStrategyType | 写入缓存策略(可选) |
+| onlyRetrieveFromCache | boolean | 是否跳过网络和本地请求(可选) |
+| customGetImage | (context: Context, src: string | 自定义下载图片(可选) | | Resource | 错误占位图数据源 |
+| border | BorderOptions | 边框圆角(可选) |
+| priority | taskpool.Priority | 加载优先级(可选) |
+| context | common.UIAbilityContext | 上下文(可选) |
+| progressListener | (progress: number)=>void | 进度(可选) |
+| signature | String | 自定义缓存关键字(可选) |
+| headerOption | Array | 设置请求头(可选) |
+| transformation | PixelMapTransformation | 图片变换(可选) |
+| drawingColorFilter | ColorFilter | drawing.ColorFilter | 图片变换(可选) |
+| onComplete | (event:EventImage | undefined) => voi | 颜色滤镜效果(可选) |
+| onLoadListener | onLoadStart: () => void、onLoadSuccess: (data: string | PixelMap | undefined) => void、onLoadFailed: (err: string) => void| 监听图片加载成功与失败 |
-其他参数只需要在ImageKnifeOption对象上按需添加即可。
+### ImageKnife接口
-这里我们着重讲一下**自定义实现绘制方案**。为了增强绘制扩展能力,目前ImageKnifeComponent使用了Canvas的渲染能力作为基础。在此之上为了抽象组件绘制表达。我将图像的状态使用了
-**IDrawLifeCycle绘制生命周期进行表达**,
+| 参数名称 | 入参内容 | 功能简介 |
+|------------------|-------------------------------------------------------------------------------------------------------|---------------|
+| initMemoryCache | newMemoryCache: IMemoryCache | 自定义内存缓存策略 |
+| initFileCache | context: Context, size: number, memory: number | 初始化文件缓存数量和大小 |
+| preLoadCache | loadSrc: string I ImageKnifeOption | 预加载并返回文件缓存路径 |
+| getCacheImage | loadSrc: string, cacheType: CacheStrategy = CacheStrategy.Default, signature?: string) | 从内存或文件缓存中获取资源 |
+| addHeader | key: string, value: Object | 全局添加http请求头 |
+| setHeaderOptions | Array | 全局设置http请求头 |
+| deleteHeader | key: string | 全局删除http请求头 |
+| setCustomGetImage | customGetImage?: (context: Context, src: string | PixelMap | Resource) => Promise | 全局设置自定义下载 |
+| setEngineKeyImpl | IEngineKey | 全局配置缓存key生成策略 |
+| putCacheImage | url: string, pixelMap: PixelMap, cacheType: CacheStrategy = CacheStrategy.Default, signature?: string | 写入内存磁盘缓存 |
+| removeMemoryCache| url: string | ImageKnifeOption | 清理指定内存缓存 |
+| removeFileCache | url: string | ImageKnifeOption | 清理指定磁盘缓存 |
+### 图形变换类型(需要为GPUImage添加依赖项)
-大致流程 展示占位图->展示网络加载进度->展示缩略图->展示主图->展示重试图层->展示失败占位图
+| 类型 | 相关描述 |
+| ---------------------------------- | ----------------------------- |
+| BlurTransformation | 模糊处理 |
+| BrightnessTransformation | 亮度滤波器 |
+| CropSquareTransformation | 正方形剪裁 |
+| CropTransformation | 自定义矩形剪裁 |
+| GrayScaleTransformation | 灰度级滤波器 |
+| InvertTransformation | 反转滤波器 |
+| KuwaharaTransformation | 桑原滤波器(使用GPUIImage) |
+| MaskTransformation | 遮罩 |
+| PixelationTransformation | 像素化滤波器(使用GPUIImage) |
+| SepiaTransformation | 乌墨色滤波器(使用GPUIImage) |
+| SketchTransformation | 素描滤波器(使用GPUIImage) |
+| SwirlTransformation | 扭曲滤波器(使用GPUIImage) |
+| ToonTransformation | 动画滤波器(使用GPUIImage) |
+| VignetterTransformation | 装饰滤波器(使用GPUIImage) |
-
-
-ImageKnifeComponent内部,责任链实现。 用户参数设置->全局参数设置->自定义组件内部设置
-
-采用责任链的好处是,用户可以通过自定义绘制,重新绘制图层。如果不想绘制也可以通过预制回调获取绘制流程信息。
-
-
-
-### 场景1:默认的展示不满足需求,需要加个圆角效果。
-
-代码如下:
-
-```typescript
-import { ImageKnifeComponent } from '@ohos/imageknife'
-import { ImageKnifeOption } from '@ohos/imageknife'
-import { ImageKnifeDrawFactory } from '@ohos/imageknife'
-
-@Entry
-@Component
-struct Index {
- @State imageKnifeOption1: ImageKnifeOption = {
- // 加载一张本地的jpg资源(必选)
- loadSrc: $r('app.media.jpgSample'),
- // 占位图使用本地资源icon_loading(可选)
- placeholderSrc: $r('app.media.icon_loading'),
- // 失败占位图使用本地资源icon_failed(可选)
- errorholderSrc: $r('app.media.icon_failed'),
- // 绘制圆角30,边框5,边框"#ff00ff".用户自定义绘制(可选)
- drawLifeCycle:ImageKnifeDrawFactory.createRoundLifeCycle(5,"#ff00ff",30)
- };
-
- build(){
- Scroll() {
- Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
- ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption1 })
- .width(300)
- .height(300)
- }
- }
- .width('100%')
- .height('100%')
+## 下载安装GPUImage依赖
+方法一:在Terminal窗口中,执行如下命令安装三方包,DevEco Studio会自动在工程的oh-package.json5中自动添加三方包依赖。
+```
+ ohpm install @ohos/gpu_transform
+```
+方法二: 在工程的oh-package.json5中设置三方包依赖,配置示例如下:
+```
+ "dependencies": {
+ "@ohos/gpu_transform": "^1.0.2"
}
-}
```
-
-`ImageKnifeDrawFactory.createRoundLifeCycle(5,"#ff00ff",30)`
-我们深入查看源码可以发现,实际上是对IDrawLifeCycle接口的部分实现,这里我介绍一下IDrawLifeCycle。
-
-*
-*IDrawLifeCycle的返回值代表事件是否被消费,如果被消费接下来组件内部就不会处理,如果没被消费就会传递到下一个使用者。目前消费流程(用户自定义->
-全局配置定义->组件内部默认定义)**
-
-所以我们在当数据是一张PixelMap的时候(目前jpg png bmp webp
-svg返回的都是PixelMap,gif返回GIFFrame数组),我们返回了true。消费了事件,代表这个绘制流程用户自定义完成。
-
-
-
-由于IDrawLifeCycle实现较为冗长,我们封装了ImageKnifeDrawFactory工厂,提供了网络下载百分比效果、圆角、椭圆添加边框等能力。下面我们就再看看使用工厂封装之后的场景代码。
-
-### 场景2: 网络下载百分比效果展示
-
-当进行加载网络图片时,可能需要展示网络下载百分比动画。但是默认的动画又不能满足需求,这个时候我们就需要自定义网络下载百分比效果。代码如下:
-
-```typescript
-import UIAbility from '@ohos.app.ability.UIAbility';
-import window from '@ohos.window';
-import { ImageKnifeGlobal,ImageKnife,ImageKnifeDrawFactory,LogUtil } from '@ohos/imageknife'
-import abilityAccessCtrl,{Permissions} from '@ohos.abilityAccessCtrl';
-export default class EntryAbility extends UIAbility {
- onWindowStageCreate(windowStage: window.WindowStage) {
- //.. 删除不必要代码
- windowStage.loadContent('pages/index', (err, data) => {
- });
- // 初始化ImageKnifeGlobal和ImageKnife
- ImageKnife.with(this.context);
- // 全局配置网络加载进度条 使用ImageKnifeGlobal.getInstance().getImageKnife()访问ImageKnife
-ImageKnifeGlobal.getInstance().getImageKnife().setDefaultLifeCycle(ImageKnifeDrawFactory.createProgressLifeCycle("#10a5ff", 0.5))
- }
-}
-```
-
-这里大家可能会问,为什么会将这个IDrawLifeCycle放在AbilityStage里面实现?
-
-这是因为网络下载百分比进度很多时候都是全局通用,如果有需要全局配置的自定义展示方案。推荐在AbilityStage里面,往ImageKnife的setDefaultLifeCycle函数中注入,即可将ImageKnifeComponent中的默认绘制方案替换。
-
-在这里我们实现的效果如下图所示。
-
-
-
-## 高级用法
-
-以上简单使用和进阶使用都是经过一层自定义组件封装之后形成的,RequestOption封装成了ImageKnifeOption,绘制部分封装成了自定义组件ImageKnifeComponent。
-
-如果用户其实并不关心绘制部分,或者说想用自己的通用方案对自定义组件ImageKnifeComponent重构都是可以的。
-
-下面我们会着重指导用户如何复用图片加载逻辑,重构自定义组件ImageKnifeComponent。
-
-首先我们先看看RequestOption构建的内容,如下所示:
-
-### 数据加载
-
-#### RequestOption构建:
-
-请查阅下文接口内容:[RequestOption接口方法](#requestoption用户配置参数)
-
-了解了RequestOption的参数内容后,我们可以参考ImageKnifeComponent组件代码进行分析。
-
-**从`imageKnifeExecute()`函数入口,首先我们需要构建一个RequestOption对象,`let request = new RequestOption()`,
-接下来就是按需配置request对象的内容,最后使用 `ImageKnifeGlobal.getInstance().getImageKnife()?.call(request)`发送request执行任务即可。**
-
-是不是很简单,而其实最重要的内容是就是: **按需配置request对象的内容** 为了更好理解,我举例说明一下:
-
-#### 场景一: 简单加载一张图片
-
-```
-let request = new RequestOption();
-// (必传)
-request.load("图片url")
- // (可选 整个request监听回调)
- .addListener({callback:(err:BusinessError|string, data:ImageKnifeData) => {
- // data 是ImageKnifeData对象
- if(data.isPixelMap()){
- // 这样就获取到了目标PixelMap
- let pixelmap = data.drawPixleMap.imagePixelMap;
- }
- return false;
- })
-
- let compSize:Size = {
- width: this.currentWidth,
- height:this.currentHeight
- }
- // (必传)这里setImageViewSize函数必传组件大小,因为涉及到图片变换效果都需要适配图像源和组件大小
- request.setImageViewSize(compSize)
- // 最后使用ImageKnife的call函数调用request即可
- let imageKnife:ImageKnife|undefined = ImageKnifeGlobal.getInstance().getImageKnife();
- if(imageKnife != undefined){
- imageKnife.call(request)
- }
-
-```
-
-**其他场景,可以按需加载**
-
-比如我需要配置 **占位图** 只需要 在request对象创建好之后,调用 **placeholder** 函数即可
-
-```
-request.placeholder(this.imageKnifeOption.placeholderSrc, (data) => {
- console.log('request.placeholder callback')
- this.displayPlaceholder(data)
-})
-```
-
-再比如 我对缓存配置有要求,我要禁用内存缓存,调用 **skipMemoryCache** 函数即可
-
-```
-request.skipMemoryCache(true)
-```
-
-这里只是简单介绍部分使用,更多的内容请参考 **按需加载** 原则,并且可以参考ImageKnifeComponent源码或者根据文档自行探索实现。
-
-## 接口说明
-
-### RequestOption用户配置参数
-
-| 方法名 | 入参 | 接口描述 |
-| ------------------------------------------------------------ | ------------------------------------------------------------ | -------------------------------------------------------- |
-| load(src: string \| PixelMap \|Resource) | src:string\|PixelMap\|Resource | 用户加载图片源 |
-| setImageViewSize(imageSize: { width: number, height: number }) | imageSize:{width: number, height: number } | 传入显示图片组件的大小,变换的时候需要作为参考 |
-| diskCacheStrategy(strategy: DiskStrategy) | strategy:DiskStrategy | 配置磁盘缓存策略 NONE SOURCE RESULT ALL AUTOMATIC |
-| placeholder(src: string \| PixelMap\|Resource, func?: AsyncSuccess) | src: string\|PixelMap\|Resource, func?: AsyncSuccess | 占位图,占位图回调数据ImageKnifeData |
-| errorholder(src: PixelMap\|Resource, func?: AsyncSuccess) | src: PixelMap\|Resource, func?: AsyncSuccess | 错误占位图,错误占位图回调数据ImageKnifeData |
-| retryholder(src: PixelMap\|Resource, func?: AsyncSuccess) | src: PixelMap\|Resource, func?: AsyncSuccess | 重试占位图,重试占位图回调数据ImageKnifeData |
-| fallback(src: PixelMap\|Resource, func?: AsyncSuccess) | src: PixelMap\|Resource, func?: AsyncSuccess | 重试占位图,重试占位图回调数据ImageKnifeData |
-| addListener(func: AsyncCallback) | func: AsyncCallback | 配置整个监听回调,数据正常加载返回,加载失败返回错误信息 |
-| thumbnail(sizeMultiplier:number, func?: AsyncSuccess) | sizeMultiplier:number, func?: AsyncSuccess | 设置缩略图比例,缩略图返回后,加载并展示缩略图 |
-| addProgressListener(func?: AsyncSuccess) | func?: AsyncSuccess | 设置网络下载百分比监听,返回数据加载百分比数值 |
-| addAllCacheInfoCallback(func: IAllCacheInfoCallback) | func: IAllCacheInfoCallback | 设置获取所有缓存信息监听 |
-| skipMemoryCache(skip: boolean) | skip: boolean | 配置是否跳过内存缓存 |
-| retrieveDataFromCache(flag: boolean) | flag: boolean | 配置仅从缓存中加载数据 |
-| signature | ObjectKey | 自定义key |
-
-同时支持[图片变换相关](#图片变换相关)接口。
-
-### ImageKnife 启动器/门面类
-
-| 方法名 | 入参 | 接口描述 |
-|----------------------------------| ---------------------- | ------------------------------------------------------------ |
-| call(request: RequestOption) | request: RequestOption | 根据用户配置参数具体执行加载流程 |
-| preload(request: RequestOption) | request: RequestOption | 根据用户配置参数具体执行预加载流程 |
-| pauseRequests() | | 全局暂停请求 |
-| resumeRequests() | | 全局恢复暂停 |
-| isUrlExist(url, cacheType, size) | url, CacheType, Size | 判断图片是否在 缓存和磁盘中存在,如果入参是缓存,需要传入值图片大小,参数 CacheType, Size(可选) |
-| setMaxRequests(count: number) | count | 设置请求的最大并发数量 |
-| removeAllMemoryCache() | | 清除全部内存缓存 |
-| removeAllFileCache() | | 清除全部磁盘缓存 |
-| removeMemoryCache(url: string) | url: string | 清除指定内存缓存 |
-| removeFileCache(url: string) | url: string | 清除指定磁盘缓存 |
-| getMemoryCacheKey(url: string) | url: string | 获取指定图片内存缓存key |
-| getDiskCacheKey(url: string) | url: string | 获取指定图片磁盘缓存key |
-
-### 缓存策略相关
-
-| 使用方法 | 类型 | 策略描述 |
-|--------------------------------------------|-----------|----------------------|
-| request.diskCacheStrategy(new ALL()) | ALL | 表示既缓存原始图片,也缓存转换过后的图片 |
-| request.diskCacheStrategy(new AUTOMATIC()) | AUTOMATIC | 表示尝试对本地和远程图片使用适合的策略 |
-| request.diskCacheStrategy(new DATA()) | DATA | 表示只缓存原始图片 |
-| request.diskCacheStrategy(new NONE()) | NONE | 表示不缓存任何内容 |
-| request.diskCacheStrategy(new RESOURCE()) | RESOURCE | 表示只缓存转换过后的图片 |
-
-### AntiAliasing类型展示效果
-
-| 使用方法 | 类型 | 策略描述 |
-|-------------------------|--------|-------------|
-| AntiAliasing.FIT_HIGH | String | 图像抗锯齿设置为高画质 |
-| AntiAliasing.FIT_MEDIUM | String | 图像抗锯齿设置为中画质 |
-| AntiAliasing.FIT_LOW | String | 图像抗锯齿设置为低画质 |
-
-### CacheType类型展示效果
-
-| 使用方法 | 类型 | 策略描述 |
-|-------------------------|-----|-----------------------------------|
-| CacheType.Default| int | 默认值,先从内存获取,无值则从磁盘获取 |
-| CacheType.Cache| int | 缓存 |
-| CacheType.Disk | int | 磁盘 |
-
-### ScaleType类型展示效果
-
-| 使用方法 | 类型 | 策略描述 |
-|-------------------------|-----|-----------------------------------|
-| ScaleType.FIT_START | int | 图像位于用户设置组件左上角显示,图像会缩放至全部展示 |
-| ScaleType.FIT_END | int | 图像位于用户设置组件右下角显示,图像会缩放至全部展示 |
-| ScaleType.FIT_CENTER | int | 图像位于用户设置组件居中,图像会缩放至全部展示 |
-| ScaleType.CENTER | int | 图像居中展示,不缩放 |
-| ScaleType.CENTER_CROP | int | 图像的宽高长度,短的部分缩放至组件大小,超出的全部裁剪 |
-| ScaleType.FIT_XY | int | 图像拉伸至组件大小 |
-| ScaleType.CENTER_INSIDE | int | 如果图像大于组件则执行FIT_CENTER,小于组件则CENTER |
-| ScaleType.NONE | int | 如果不想适配,直接展示原图大小 |
-| ScaleType.AUTO_HEIGHT | int | 设置宽的时候,图片高度自适应 |
-| ScaleType.AUTO_WIDTH | int | 设置高的时候,图片宽度自适应 |
-| ScaleType.AUTO | int | 没有设置宽和高,图片按照自身宽高显示 |
-
-### 图片变换相关
-
-| 使用方法 | 类型 | 相关描述 |
-|--------------------------------|------------------------------------|--------------------------------|
-| request.centerCrop() | CenterCrop | 可以根据图片文件,目标显示大小,进行对应centerCrop |
-| request.centerInside() | CenterInside | 可以根据图片文件,目标显示大小,进行对应centerInside |
-| request.fitCenter() | FitCenter | 可以根据图片文件,目标显示大小,进行对应fitCenter |
-| request.blur() | BlurTransformation | 模糊处理(图片分辨率较大建议传递第二个参数将图片进行缩小) |
-| request.brightnessFilter() | BrightnessFilterTransformation | 亮度滤波器 |
-| request.contrastFilter() | ContrastFilterTransformation | 对比度滤波器 |
-| request.cropCircle() | CropCircleTransformation | 圆形剪裁显示 |
-| request.cropCircleWithBorder() | CropCircleWithBorderTransformation | 圆环展示 |
-| request.cropSquare() | CropSquareTransformation | 正方形剪裁 |
-| request.crop() | CropTransformation | 自定义矩形剪裁 |
-| request.grayscale() | GrayscaleTransformation | 灰度级转换 |
-| request.invertFilter() | InvertFilterTransformation | 反转滤波器 |
-| request.pixelationFilter() | PixelationFilterTransformation | 像素化滤波器 |
-| request.rotateImage() | RotateImageTransformation | 图片旋转 |
-| request.roundedCorners() | RoundedCornersTransformation | 圆角剪裁 |
-| request.sepiaFilter() | SepiaFilterTransformation | 乌墨色滤波器 |
-| request.sketchFilter() | SketchFilterTransformation | 素描滤波器 |
-| request.mask() | MaskTransformation | 遮罩 |
-| request.swirlFilter() | SwirlFilterTransformation | 扭曲滤波器 |
-| request.kuwaharaFilter() | KuwaharaFilterTransform | 桑原滤波器 |
-| request.toonFilter() | ToonFilterTransform | 动画滤波器 |
-| request.vignetteFilter() | VignetteFilterTransform | 装饰滤波器 |
-| request.downsampleOf() | BaseDownsampling | 下采样 |
-
-
-
-### setLruCacheSize
-
-setLruCacheSize(size: number,memory:number): void
-
-设置图片文件缓存的大小上限,size单位为张数,memory单位为字节,提升再次加载同源图片的加载速度,特别是对网络图源会有较明显提升。
-如果不设置则默认为100张,100MB。缓存采用内置的LRU策略。
-size为0则代表不限制缓存张数,memory为0则代表不限制缓存大小。
-建议根据应用实际需求,设置合理缓存上限,数字过大可能导致内存占用过高,可能导致OOM异常。
-
-**参数:**
-
-| 参数名 | 类型 | 必填 | 说明 |
-| -------- | -------- | -------- |-------------------------|
-| size | number | 是 | 图片文件的缓存张数,单位为张。只支持正整数,0 |
-| memory | number | 是 | 图片文件的缓存大小,单位为字节。只支持正数,0 |
-
-**示例:**
-```ts
-//EntryAbility.ets
-import { InitImageKnife } from '...imageknife'
-export default class EntryAbility extends UIAbility {
- onWindowStageCreate(windowStage: window.WindowStage) {
- InitImageKnife.init(this.context);
- let imageKnife: ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife()
- if (imageKnife != undefined) {
- //设置全局内存缓存大小张数
- imageKnife.setLruCacheSize(100, 100 * 1204 * 1024)
- }
- }
-}
-```
-
-### Queue
-
-| 方法名 | 入参 | 接口描述 |
-| ----------------------------------- | ----------------------- | ------------------------------ |
-| getQueueLength(): number | | 获取队列总长度 |
-| add(request: RequestOption) | request:RequestOption | 在队列尾部插入元素 |
-| pop(): RequestOption | undefined | | 删除队列头元素并返回该删除元素 |
-
## 约束与限制
-在下述版本验证通过:
-DevEco Studio 4.1(4.1.3.520)--SDK:API11( 4.1.0.63)
-DevEco Studio 4.1(4.1.3.418)--SDK:API11( 4.1.0.56)
-DevEco Studio 4.1(4.1.3.322)--SDK:API11( 4.1.0.36)
-DevEco Studio 4.0(4.0.3.700)--SDK:API10( 4.0.10.15)
-
-HSP场景适配:
-
-在使用ImageKnifeComponent进行加载图片时, 提供的ImageKnifeOption配置类新增了可选参数context, 在HSP场景下需要传入正确的context, 才能保证三方库后续正确获取Resource资源。
-
-在使用RquestOption进行加载图片时, 提供的RquestOption配置类新增了接口`setModuleContext(moduleCtx:common.UIAbilityContext)`, 在HSP场景下需要传入正确的context, 才能保证三方库后续正确获取Resource资源。
-
-非HSP场景不影响原功能, ImageKnifeOption配置类新增的可选参数context可以不传, RquestOption配置类新增的接口可以不调用。
-
-注意:
-
-基于性能优化的原因,2.1.2-rc.13及以后版本引用了API12 Sendable接口,至此以后的版本只支持API12。
-
-## 目录结构
-
-```
-/library/src/
-- main/ets/components
- - cache # 缓存相关内容
- - diskstrategy # 缓存策略
- - key # 缓存key生成策略
- - Base64.ets # Base64算法
- - CustomMap.ets # 自定义Map封装
- - DiskCacheEntry.ets # 磁盘缓存entry
- - DiskLruCache.ets # 磁盘LRU缓存策略
- - FileReader.ets # 文件读取相关
- - FileUtils.ets # 文件工具类
- - LruCache.ets # 内存LRU缓存策略
- - Md5.ets # MD5算法
-
- - imageknife # imageknife主要内容
- - compress # 压缩相关
- - constants # 常量相关
- - Downsampling # 下采样相关
- - entry # 部分数据结构
- - holder # 占位图相关解析
- - interface # 接口相关
- - networkmanage # 网络相关
- - pngj # pngj相关
- - requestmanage # imageknife请求相关
- - resourcemanage # 本地资源解析相关
- - transform # 图片变换相关
- - utils # 工具类相关
- - ImageKnife.ets # imageknife门面,app持久化类
- - ImageKnifeData.ets # 数据封装
- - ImageKnifeComponent.ets # 自定义控件封装
- - ImageKnifeDrawFactory.ets # IDrawLifeCycle用户自定义实现
- - ImageKnifeOption.ets # 用户传参数封装
- - RequestOption.ets # 用户设置参数封装
-
-/entry/src/
-- main/ets
- - entryability
- - CustomEngineKeyImpl.ets
- - EntryAbility.ts
- - pages # 测试page页面列表
- - basicTestFeatureAbilityPage.ets # 测试列表加载
- - basicTestFileIOPage.ets # 测试fileio
- - basicTestMediaImage.ets # 测试媒体image
- - basicTestResourceManagerPage.ets # 测试本地资源解析
- - compressPage.ets # 压缩页面
- - cropImagePage2.ets # 手势裁剪页面
- - DownsamplingPage.ets # 图片下采样测试
- - frescoImageTestCasePage.ets # 测试属性动画组件切换
- - frescoRetryTestCasePage.ets # 测试ImageKnifeComponent加载失败重试
- - svgTestCasePage.ets # 测试svg解析页面
- - imageknifeTestCaseIndex.ets # 测试用例页面入口
- - index.ets # 程序入口页面
- - loadNetworkTestCasePage.ets # 网络加载测试
- - loadResourceTestCasePage.ets # 本地加载测试
- - showErrorholderTestCasePage.ets # 加载失败占位图测试
- - SignatureTestPage.ets # 自定义key测试
- - storageTestDiskLruCache.ets # 磁盘缓存测试
- - storageTestLruCache.ets # 内存缓存测试
- - testAllCacheInfoPage.ets # 所有缓存信息获取测试
- - testCustomDataFetchClientWithPage # 测试图片下载使用自定义的网络栈
- - testImageKnifeAutoHeightPage.ets # 图片高度自适应测试
- - testImageKnifeAutoWidthPage.ets # 图片宽度自适应测试
- - testImageKnifeAutoPage.ets # 图片宽高自适应测试
- - testImageKnifeOptionChangedPage.ets # 数据切换测试
- - testImageKnifeOptionChangedPage2.ets # 数据切换测试,部分变换
- - testImageKnifeOptionChangedPage3.ets # 数据切换测试,组件动画
- - testImageKnifeOptionChangedPage4.ets # 数据切换测试,内容动画
- - testImageKnifeOptionChangedPage5.ets # 数据切换测试,ImageKnifeDrawFactory封装圆角圆环边框等
- - testPreloadPage.ets # 预加载测试
- - transformPixelMapPage.ets # 所有类型变换测试
- - testSingleFrameGifPage.ets # 单帧gif加载测试
- - TestStopPlayingGifPage # gif播放暂停测试
- - TestImageKnifeNetPlaceholder # 缓存获取string类型占位图以及后备回调符测试
-
- - OptionTestPage.ets # 图片缓存测试
- - testManyGifLoadWithPage # 测试gif加载页面
- - testImageKnifeCache # 测试图片是否在缓存或者磁盘中
- -workers
- - upngWorkerTestCase.ets # png子线程解析
- - upngWorkerDepend.ts # png子线程解析具体执行
-```
+在下述版本验证通过:
+DevEco Studio 5.0 Canary3(5.0.3.221)--SDK:API12
## 贡献代码
@@ -640,47 +365,5 @@ HSP场景适配:
## 遗留问题
-1.目前svg和gif动图不支持变换效果。
-
-## 补充说明
-### SVG标签说明
-从API version 10开始支持SVG标签, 使用版本为(SVG)1.1,SVG文件需添加xml声明,应以"();
+
+ public static getContext(): GlobalContext {
+ if (!GlobalContext.instance) {
+ GlobalContext.instance = new GlobalContext();
+ }
+ return GlobalContext.instance;
+ }
+
+ getObject(value: string): Object | undefined {
+ return this._objects.get(value);
+ }
+
+ setObject(key: string, objectClass: Object): void {
+ this._objects.set(key, objectClass);
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/entryability/EntryAbility.ets b/entry/src/main/ets/entryability/EntryAbility.ets
index c460bb3..488dcce 100644
--- a/entry/src/main/ets/entryability/EntryAbility.ets
+++ b/entry/src/main/ets/entryability/EntryAbility.ets
@@ -1,27 +1,37 @@
/*
- * Copyright (C) 2023 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the "License");
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
+ * distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import UIAbility from '@ohos.app.ability.UIAbility';
+import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import hilog from '@ohos.hilog';
+import UIAbility from '@ohos.app.ability.UIAbility';
+import Want from '@ohos.app.ability.Want';
import window from '@ohos.window';
-import { InitImageKnife, ImageKnifeGlobal, ImageKnife, ImageKnifeDrawFactory, LogUtil } from '@ohos/libraryimageknife'
-import { CustomEngineKeyImpl } from './CustomEngineKeyImpl'
+import { ImageKnife, InitImageKnife, LogUtil } from '@ohos/libraryimageknife';
+import { CustomEngineKeyImpl } from '../common/CustomEngineKeyImpl';
import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl';
import { BusinessError } from '@ohos.base'
export default class EntryAbility extends UIAbility {
- onWindowStageCreate(windowStage: window.WindowStage) {
+ onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
+ }
+
+ onDestroy(): void {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
+ }
+
+ async onWindowStageCreate(windowStage: window.WindowStage): Promise {
// Main window is created, set main page for this ability
// let list: Array = ['ohos.permission.READ_IMAGEVIDEO', 'ohos.permission.WRITE_IMAGEVIDEO'];
// let permissionRequestResult: Object;
@@ -35,25 +45,37 @@ export default class EntryAbility extends UIAbility {
// }
// })
- windowStage.loadContent('pages/index', (err: BusinessError, data: void) => {
- });
+ hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
- InitImageKnife.init(this.context);
-
- let imageKnife: ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife()
- if (imageKnife != undefined) {
- // 全局配置网络加载进度条
- imageKnife
- .setDefaultLifeCycle(ImageKnifeDrawFactory.createProgressLifeCycle("#10a5ff", 0.5))
- // 全局配置缓存key
- imageKnife.setEngineKeyImpl(new CustomEngineKeyImpl())
- // 设置全局内存缓存大小张数
- imageKnife.setLruCacheSize(100, 100 * 1204 * 1024)
- // 全局配置请求头
- imageKnife.addHeader('refer', "http://1.94.37.200:7070/AntiTheftChain/downloadImage");
- imageKnife.deleteHeader('refer');
- }
- // 开启ImageKnife所有级别日志开关
LogUtil.mLogLevel = LogUtil.ALL
+ // 初始化ImageKnife的文件缓存
+ await InitImageKnife.init(this.context)
+ ImageKnife.getInstance().setEngineKeyImpl(new CustomEngineKeyImpl())
+ // 全局配置请求头
+ ImageKnife.getInstance().addHeader('refer', "http://1.94.37.200:7070/AntiTheftChain/downloadImage");
+ ImageKnife.getInstance().deleteHeader('refer');
+
+ windowStage.loadContent('pages/Index', (err, data) => {
+ if (err.code) {
+ hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
+ return;
+ }
+ hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
+ });
+ }
+
+ onWindowStageDestroy(): void {
+ // Main window is destroyed, release UI related resources
+ hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
+ }
+
+ onForeground(): void {
+ // Ability has brought to foreground
+ hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
+ }
+
+ onBackground(): void {
+ // Ability has back to background
+ hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
}
}
diff --git a/entry/src/main/ets/pages/ImageAnimatorPage.ets b/entry/src/main/ets/pages/ImageAnimatorPage.ets
new file mode 100644
index 0000000..bde0508
--- /dev/null
+++ b/entry/src/main/ets/pages/ImageAnimatorPage.ets
@@ -0,0 +1,41 @@
+import { AnimatorOption, ImageKnifeAnimatorComponent } from "@ohos/libraryimageknife"
+
+@Entry
+@Component
+struct ImageAnimatorPage {
+ @State animatorOption: AnimatorOption = {
+ state: AnimationStatus.Running,
+ iterations: -1
+ }
+ build() {
+ Column(){
+ Flex(){
+ Button("播放").onClick(()=>{
+ this.animatorOption.state = AnimationStatus.Running
+ })
+ Button("暂停").onClick(()=>{
+ this.animatorOption.state = AnimationStatus.Paused
+ })
+ Button("停止").onClick(()=>{
+ this.animatorOption.state = AnimationStatus.Stopped
+ })
+ Button("无限循环").onClick(()=>{
+ this.animatorOption.iterations = -1
+ })
+ Button("播放一次").onClick(()=>{
+ this.animatorOption.iterations = 1
+ })
+ Button("播放两次").onClick(()=>{
+ this.animatorOption.iterations = 2
+ })
+ }
+ ImageKnifeAnimatorComponent({
+ imageKnifeOption:{
+ loadSrc:"https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658",
+ placeholderSrc:$r('app.media.loading'),
+ errorholderSrc:$r('app.media.failed')
+ },animatorOption:this.animatorOption
+ }).width(300).height(300).backgroundColor(Color.Orange).margin({top:30})
+ }.width("100%").height("100%")
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/pages/ImageTransformation.ets b/entry/src/main/ets/pages/ImageTransformation.ets
new file mode 100644
index 0000000..844ed62
--- /dev/null
+++ b/entry/src/main/ets/pages/ImageTransformation.ets
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {
+ BlurTransformation,
+ BrightnessTransformation,
+ CropSquareTransformation,
+ CropTransformation,
+ GrayScaleTransformation,
+ ImageKnifeComponent,
+ ImageKnifeOption,
+ InvertTransformation,
+ KuwaharaTransformation,
+ MaskTransformation,
+ MultiTransTransformation,
+ PixelationTransformation,
+ PixelMapTransformation,
+ SepiaTransformation,
+ SketchTransformation,
+ SwirlTransformation,
+ ToonTransformation,
+ VignetterTransformation
+} from '@ohos/libraryimageknife';
+import { collections } from '@kit.ArkTS'
+
+@Entry
+@Component
+struct ImageTransformation {
+ @State imageKnifeOption: ImageKnifeOption = {
+ loadSrc: $r('app.media.pngSample'),
+ placeholderSrc: $r("app.media.loading"),
+ errorholderSrc: $r("app.media.app_icon"),
+ objectFit: ImageFit.Contain
+ }
+ @State isRound: boolean = false;
+ @State isContrast: boolean = false;
+ @State isRotate: boolean = false;
+ isBlur: boolean = false
+ isBrightness: boolean = false
+ isGrayScale: boolean = false;
+ isInvert: boolean = false;
+ isToon: boolean = false;
+ isCropCircle: boolean = false;
+ isCropCircleWithBorder: boolean = false;
+ isKuwahara: boolean = false;
+ isPixelation: boolean = false;
+ isSketch: boolean = false;
+ isSwirl: boolean = false;
+ isVignetter: boolean = false;
+ isCropSquare: boolean = false;
+ isCropTop: boolean = false;
+ isCropCenter: boolean = false;
+ isCropBottom: boolean = false;
+ isMask: boolean = false;
+ isSepia: boolean = false;
+
+ build() {
+ Scroll() {
+ Column() {
+ Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
+ Checkbox({ name: 'checkbox1', group: 'checkboxGroup' })
+ .selectedColor(0x39a2db)
+ .shape(CheckBoxShape.ROUNDED_SQUARE)
+ .onChange((value: boolean) => {
+ this.isBlur = value;
+ this.updateImageKnifeOption();
+ })
+ .width(30)
+ .height(30)
+ Text('模糊效果').fontSize(20)
+ }
+
+ Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
+ Checkbox({ name: 'checkbox2', group: 'checkboxGroup' })
+ .selectedColor(0x39a2db)
+ .shape(CheckBoxShape.ROUNDED_SQUARE)
+ .onChange((value: boolean) => {
+ this.isBrightness = value;
+ this.updateImageKnifeOption();
+ })
+ .width(30)
+ .height(30)
+ Text('高亮效果').fontSize(20)
+ }
+
+ Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
+ Checkbox({ name: 'checkbox3', group: 'checkboxGroup' })
+ .selectedColor(0x39a2db)
+ .shape(CheckBoxShape.ROUNDED_SQUARE)
+ .onChange((value: boolean) => {
+ this.isGrayScale = value;
+ this.updateImageKnifeOption();
+ })
+ .width(30)
+ .height(30)
+ Text('灰化效果').fontSize(20)
+ }
+
+ Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
+ Checkbox({ name: 'checkbox4', group: 'checkboxGroup' })
+ .selectedColor(0x39a2db)
+ .shape(CheckBoxShape.ROUNDED_SQUARE)
+ .onChange((value: boolean) => {
+ this.isInvert = value;
+ this.updateImageKnifeOption();
+ })
+ .width(30)
+ .height(30)
+ Text('反转效果').fontSize(20)
+ }
+
+ Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
+ Checkbox({ name: 'checkbox5', group: 'checkboxGroup' })
+ .selectedColor(0x39a2db)
+ .shape(CheckBoxShape.ROUNDED_SQUARE)
+ .onChange((value: boolean) => {
+ this.isToon = value;
+ this.updateImageKnifeOption();
+ })
+ .width(30)
+ .height(30)
+ Text('动画滤镜效果').fontSize(20)
+ }
+
+ Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
+ Checkbox({ name: 'checkbox6', group: 'checkboxGroup' })
+ .selectedColor(0x39a2db)
+ .shape(CheckBoxShape.ROUNDED_SQUARE)
+ .onChange((value: boolean) => {
+ this.isCropCircle = value;
+ this.updateImageKnifeOption();
+ })
+ .width(30)
+ .height(30)
+ Text('裁剪圆形效果').fontSize(20)
+ }
+
+ Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
+ Checkbox({ name: 'checkbox7', group: 'checkboxGroup' })
+ .selectedColor(0x39a2db)
+ .shape(CheckBoxShape.ROUNDED_SQUARE)
+ .onChange((value: boolean) => {
+ this.isCropCircleWithBorder = value;
+ this.updateImageKnifeOption();
+ })
+ .width(30)
+ .height(30)
+ Text('裁剪圆形带边框效果').fontSize(20)
+ }
+
+ Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
+ Checkbox({ name: 'checkbox8', group: 'checkboxGroup' })
+ .selectedColor(0x39a2db)
+ .shape(CheckBoxShape.ROUNDED_SQUARE)
+ .onChange((value: boolean) => {
+ this.isContrast = value;
+ })
+ .width(30)
+ .height(30)
+ Text('对比度效果').fontSize(20)
+ }
+
+ Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
+ Checkbox({ name: 'checkbox9', group: 'checkboxGroup' })
+ .selectedColor(0x39a2db)
+ .shape(CheckBoxShape.ROUNDED_SQUARE)
+ .onChange((value: boolean) => {
+ this.isSepia = value;
+ this.updateImageKnifeOption();
+ })
+ .width(30)
+ .height(30)
+ Text('乌墨色滤波效果').fontSize(20)
+ }
+
+ Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
+ Checkbox({ name: 'checkbox10', group: 'checkboxGroup' })
+ .selectedColor(0x39a2db)
+ .shape(CheckBoxShape.ROUNDED_SQUARE)
+ .onChange((value: boolean) => {
+ this.isRotate = value;
+ })
+ .width(30)
+ .height(30)
+ Text('旋转效果').fontSize(20)
+ }
+
+ Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
+ Checkbox({ name: 'checkbox11', group: 'checkboxGroup' })
+ .selectedColor(0x39a2db)
+ .shape(CheckBoxShape.ROUNDED_SQUARE)
+ .onChange((value: boolean) => {
+ this.isRound = value;
+ this.updateImageKnifeOption();
+ })
+ .width(30)
+ .height(30)
+ Text('圆角效果').fontSize(20)
+ }
+
+ Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
+ Checkbox({ name: 'checkbox12', group: 'checkboxGroup' })
+ .selectedColor(0x39a2db)
+ .shape(CheckBoxShape.ROUNDED_SQUARE)
+ .onChange((value: boolean) => {
+ this.isKuwahara = value;
+ this.updateImageKnifeOption();
+ })
+ .width(30)
+ .height(30)
+ Text('桑原滤波效果').fontSize(20)
+ }
+
+ Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
+ Checkbox({ name: 'checkbox13', group: 'checkboxGroup' })
+ .selectedColor(0x39a2db)
+ .shape(CheckBoxShape.ROUNDED_SQUARE)
+ .onChange((value: boolean) => {
+ this.isPixelation = value;
+ this.updateImageKnifeOption();
+ })
+ .width(30)
+ .height(30)
+ Text('像素化滤波效果').fontSize(20)
+ }
+
+ Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
+ Checkbox({ name: 'checkbox14', group: 'checkboxGroup' })
+ .selectedColor(0x39a2db)
+ .shape(CheckBoxShape.ROUNDED_SQUARE)
+ .onChange((value: boolean) => {
+ this.isSketch = value;
+ this.updateImageKnifeOption();
+ })
+ .width(30)
+ .height(30)
+ Text('素描滤波效果').fontSize(20)
+ }
+
+ Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
+ Checkbox({ name: 'checkbox15', group: 'checkboxGroup' })
+ .selectedColor(0x39a2db)
+ .shape(CheckBoxShape.ROUNDED_SQUARE)
+ .onChange((value: boolean) => {
+ this.isSwirl = value;
+ this.updateImageKnifeOption();
+ })
+ .width(30)
+ .height(30)
+ Text('扭曲滤波效果').fontSize(20)
+ }
+
+ Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
+ Checkbox({ name: 'checkbox16', group: 'checkboxGroup' })
+ .selectedColor(0x39a2db)
+ .shape(CheckBoxShape.ROUNDED_SQUARE)
+ .onChange((value: boolean) => {
+ this.isVignetter = value;
+ this.updateImageKnifeOption();
+ })
+ .width(30)
+ .height(30)
+ Text('装饰滤波效果').fontSize(20)
+ }
+
+ Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
+ Checkbox({ name: 'checkbox17', group: 'checkboxGroup' })
+ .selectedColor(0x39a2db)
+ .shape(CheckBoxShape.ROUNDED_SQUARE)
+ .onChange((value: boolean) => {
+ this.isCropSquare = value;
+ this.updateImageKnifeOption();
+ })
+ .width(30)
+ .height(30)
+ Text('正方形裁剪效果').fontSize(20)
+ }
+
+ Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
+ Checkbox({ name: 'checkbox18', group: 'checkboxGroup' })
+ .selectedColor(0x39a2db)
+ .shape(CheckBoxShape.ROUNDED_SQUARE)
+ .onChange((value: boolean) => {
+ this.isCropTop = value;
+ this.updateImageKnifeOption();
+ })
+ .width(30)
+ .height(30)
+ Text('上方裁剪效果').fontSize(20)
+ }
+
+ Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
+ Checkbox({ name: 'checkbox19', group: 'checkboxGroup' })
+ .selectedColor(0x39a2db)
+ .shape(CheckBoxShape.ROUNDED_SQUARE)
+ .onChange((value: boolean) => {
+ this.isCropCenter = value;
+ this.updateImageKnifeOption();
+ })
+ .width(30)
+ .height(30)
+ Text('中间裁剪效果').fontSize(20)
+ }
+
+ Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
+ Checkbox({ name: 'checkbox20', group: 'checkboxGroup' })
+ .selectedColor(0x39a2db)
+ .shape(CheckBoxShape.ROUNDED_SQUARE)
+ .onChange((value: boolean) => {
+ this.isCropBottom = value;
+ this.updateImageKnifeOption();
+ })
+ .width(30)
+ .height(30)
+ Text('底下裁剪效果').fontSize(20)
+ }
+
+ Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
+ Checkbox({ name: 'checkbox21', group: 'checkboxGroup' })
+ .selectedColor(0x39a2db)
+ .shape(CheckBoxShape.ROUNDED_SQUARE)
+ .onChange((value: boolean) => {
+ this.isMask = value;
+ this.updateImageKnifeOption();
+ })
+ .width(30)
+ .height(30)
+ Text('遮罩效果').fontSize(20)
+ }
+
+ if (this.isContrast) {
+ ImageKnifeComponent({
+ imageKnifeOption: this.imageKnifeOption
+ })
+ .width(300)
+ .height(300)
+ .rotate({ angle: this.isRotate ? 90 : 0 })
+ .contrast(12)
+ .backgroundColor(Color.Pink)
+ } else {
+ ImageKnifeComponent({
+ imageKnifeOption: this.imageKnifeOption
+ }).width(300)
+ .height(300)
+ .rotate({ angle: this.isRotate ? 90 : 0 })
+ .backgroundColor(Color.Pink)
+ }
+ }
+ }
+ .height('100%')
+ .width('100%')
+ }
+
+ updateImageKnifeOption() {
+ let transformations: collections.Array = new collections.Array()
+ if (this.isBlur) {
+ transformations.push(new BlurTransformation(5));
+ }
+ if (this.isBrightness) {
+ transformations.push(new BrightnessTransformation(0.2));
+ }
+ if (this.isGrayScale) {
+ transformations.push(new GrayScaleTransformation());
+ }
+ if (this.isInvert) {
+ transformations.push(new InvertTransformation());
+ }
+ if (this.isToon) {
+ transformations.push(new ToonTransformation(0.3, 10.0));
+ }
+ if (this.isKuwahara) {
+ transformations.push(new KuwaharaTransformation(10));
+ }
+ if (this.isPixelation) {
+ transformations.push(new PixelationTransformation(5.0));
+ }
+ if (this.isSketch) {
+ transformations.push(new SketchTransformation());
+ }
+ if (this.isSwirl) {
+ transformations.push(new SwirlTransformation(200, 1.0, [0.5, 0.5]));
+ }
+ if (this.isVignetter) {
+ transformations.push(new VignetterTransformation([0.5, 0.5], [0.0, 0.0, 0.0], [0.3, 0.75]));
+ }
+ if (this.isCropSquare) {
+ transformations.push(new CropSquareTransformation());
+ }
+ if (this.isCropTop) {
+ transformations.push(new CropTransformation(25, 25, 0));
+ }
+ if (this.isCropCenter) {
+ transformations.push(new CropTransformation(25, 25, 1));
+ }
+ if (this.isCropBottom) {
+ transformations.push(new CropTransformation(25, 25, 2));
+ }
+ if (this.isSepia) {
+ transformations.push(new SepiaTransformation());
+ }
+ if (this.isMask) {
+ transformations.push(new MaskTransformation($r('app.media.mask_starfish')));
+ }
+ this.imageKnifeOption = {
+ loadSrc: $r('app.media.pngSample'),
+ placeholderSrc: $r("app.media.loading"),
+ errorholderSrc: $r("app.media.app_icon"),
+ objectFit: ImageFit.Contain,
+ border: { radius: this.isRound ? { topLeft: 50, bottomRight: 50 } : 0 },
+ transformation: transformations.length > 0 ? new MultiTransTransformation(transformations) : undefined
+ }
+ if (this.isCropCircle) {
+ this.imageKnifeOption.objectFit = ImageFit.Cover;
+ this.imageKnifeOption.border = { radius: 150 };
+ }
+ if (this.isCropCircleWithBorder) {
+ this.imageKnifeOption.objectFit = ImageFit.Cover;
+ this.imageKnifeOption.border = { radius: 150, color: Color.Red, width: 5 };
+ }
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/pages/ListPage.ets b/entry/src/main/ets/pages/ListPage.ets
new file mode 100644
index 0000000..c604d97
--- /dev/null
+++ b/entry/src/main/ets/pages/ListPage.ets
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { ImageKnifeComponent, ImageKnifeOption } from '@ohos/libraryimageknife';
+
+@Entry
+@Component
+struct ListPage {
+
+ private data: string[] = []
+ @State ImageKnifeOption: ImageKnifeOption = { loadSrc: $r('app.media.startIcon')}
+
+
+ aboutToAppear(): void {
+ for (let i = 0; i < 1000; i++) {
+ this.data.push(i.toString())
+ }
+ }
+
+ build() {
+ Row() {
+ List({ space: 10 }) {
+ ForEach(this.data, (item: string) => {
+ ImageKnifeComponent({ imageKnifeOption: this.ImageKnifeOption }).height(200).width(200)
+ }, (item: string) => item)
+ }
+ .width('100%')
+ }
+ .height('100%')
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/pages/LoadStatePage.ets b/entry/src/main/ets/pages/LoadStatePage.ets
new file mode 100644
index 0000000..80c41a3
--- /dev/null
+++ b/entry/src/main/ets/pages/LoadStatePage.ets
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { ImageKnifeComponent, ImageKnifeOption } from "@ohos/libraryimageknife"
+import matrix4 from '@ohos.matrix4'
+
+@Entry
+@Component
+struct LoadStatePage {
+
+ starTime:number = new Date().getTime()
+
+ @State ImageKnifeOption: ImageKnifeOption = {
+ loadSrc: $r("app.media.rabbit"),
+ placeholderSrc: $r("app.media.loading"),
+ errorholderSrc: $r("app.media.app_icon"),
+ objectFit: ImageFit.Contain,
+ onLoadListener: {
+ onLoadFailed: (err) => {
+ console.error("Load Failed Reason: " + err);
+ },
+ onLoadSuccess: (data) => {
+ return data;
+ },
+ },
+ border: { radius: 50 }
+ }
+ @State imageKnifeOption1: ImageKnifeOption = {
+ loadSrc: $r('app.media.startIcon')
+ }
+ @State message: string = ""
+ @State currentWidth: number = 200
+ @State currentHeight: number = 200
+ @State typeValue: string = ""
+ build() {
+ Column() {
+ Text('测试失败场景请先关闭网络,并保证本地没有此网络图片的缓存')
+ .margin({ top: 20 })
+ Row() {
+ Button('测试失败/成功场景')
+ .onClick(() => {
+ this.ImageKnifeOption = {
+ loadSrc: "https://www.openharmony.cn/_nuxt/img/logo.dcf95b3.png",
+ placeholderSrc: $r("app.media.loading"),
+ errorholderSrc: $r("app.media.app_icon"),
+ objectFit: ImageFit.Contain,
+ onLoadListener: {
+ onLoadStart:()=>{
+ this.starTime = new Date().getTime()
+ console.info("Load start: ");
+ },
+ onLoadFailed: (err) => {
+ console.error("Load Failed Reason: " + err + " cost " + (new Date().getTime() - this.starTime) + " milliseconds");
+ },
+ onLoadSuccess: (data,imageData) => {
+ console.info("Load Successful: cost " + (new Date().getTime() - this.starTime) + " milliseconds");
+ this.currentWidth = imageData.imageWidth!
+ this.currentHeight = imageData.imageHeight!
+ this.typeValue = imageData.type!
+ return data;
+ },
+ },
+ border: { radius: 50 },
+ onComplete:(event)=>{
+ console.error("Load onComplete width:"+event?.width , " height:"+event?.height , " componentWidth:"+event?.componentWidth," componentHeight:" + event?.componentHeight);
+ }
+ }
+ })
+ }
+ .margin({ top: 20 })
+ Text(this.typeValue)
+ ImageKnifeComponent({ imageKnifeOption: this.ImageKnifeOption }).height(this.currentHeight).width(this.currentWidth)
+ .margin({ top: 20 })
+ Button("自定义下载失败").onClick(()=>{
+ this.imageKnifeOption1 = {
+ loadSrc: "abc",
+ placeholderSrc:$r('app.media.loading'),
+ errorholderSrc:$r('app.media.failed'),
+ customGetImage:custom,
+ onLoadListener: {
+ onLoadFailed:(err)=>{
+ this.message = "err:" + err
+ }
+ }
+ }
+ }).margin({ top: 20 })
+ Text(this.message).fontSize(20).margin({ top: 20 })
+ ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption1 }).height(this.currentHeight).width(this.currentWidth)
+ .margin({ top: 20 })
+ }
+
+ .width('100%')
+ .height('100%')
+ }
+
+}
+// 自定义下载方法
+@Concurrent
+async function custom(context: Context, src: string | PixelMap | Resource): Promise {
+ console.info("ImageKnife:: custom download:" + src)
+ // 举例写死从本地文件读取,也可以自己请求网络图片
+ return undefined
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/pages/LongImagePage.ets b/entry/src/main/ets/pages/LongImagePage.ets
new file mode 100644
index 0000000..c8f6e1f
--- /dev/null
+++ b/entry/src/main/ets/pages/LongImagePage.ets
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { ImageKnifeComponent } from '@ohos/libraryimageknife'
+
+@Entry
+@Component
+struct LongImagePage {
+
+ build() {
+ Scroll() {
+
+ // Image("https://wx2.sinaimg.cn/mw690/006HyQKGgy1hnqp08dw09j30u04twu0x.jpg").objectFit(ImageFit.Auto).height(300)
+
+ // Image($r("app.media.aaa")).objectFit(ImageFit.Auto).width(200)
+ ImageKnifeComponent({
+ imageKnifeOption: {
+ loadSrc:"https://wx2.sinaimg.cn/mw690/006HyQKGgy1hnqp08dw09j30u04twu0x.jpg",
+ //src:$r("app.media.aaa"),
+ placeholderSrc: $r("app.media.loading"),
+ errorholderSrc: $r("app.media.failed"),
+ objectFit: ImageFit.Auto
+ }
+ })
+ }
+ .height('100%') .width('100%')
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/pages/ObjectFitPage.ets b/entry/src/main/ets/pages/ObjectFitPage.ets
new file mode 100644
index 0000000..c86415f
--- /dev/null
+++ b/entry/src/main/ets/pages/ObjectFitPage.ets
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { ImageKnife, ImageKnifeComponent, ImageKnifeOption } from '@ohos/libraryimageknife'
+
+@Entry
+@Component
+struct ObjectFitPage {
+ @State imageKnifeOption: ImageKnifeOption = {
+ loadSrc: $r("app.media.app_icon"),
+ placeholderSrc: $r("app.media.loading"),
+ errorholderSrc: $r("app.media.app_icon"),
+ objectFit: ImageFit.Fill
+ }
+
+ build() {
+ Column() {
+
+ Button("主图Fill拉伸填充").onClick(()=>{
+ this.imageKnifeOption = {
+ loadSrc: $r("app.media.app_icon"),
+ placeholderSrc: $r("app.media.loading"),
+ errorholderSrc: $r("app.media.app_icon"),
+ objectFit: ImageFit.Fill
+ }
+ })
+
+ Button("占位图Contain保持比例填充").margin({top:10}).onClick(async () => {
+ ImageKnife.getInstance().removeAllMemoryCache()
+ await ImageKnife.getInstance().removeAllFileCache()
+
+ this.imageKnifeOption = {
+ loadSrc: "https://wx2.sinaimg.cn/mw690/006HyQKGgy1hnqp08dw09j30u04twu0x.jpg",
+ placeholderSrc: $r("app.media.app_icon"),
+ errorholderSrc: $r("app.media.app_icon"),
+ objectFit: ImageFit.Fill,
+ placeholderObjectFit: ImageFit.Contain
+ }
+ })
+
+
+ Button("错误图None不变化").margin({top:10}).onClick(() => {
+ this.imageKnifeOption = {
+ loadSrc: "http://xxxxx",
+ placeholderSrc: $r("app.media.loading"),
+ errorholderSrc: $r("app.media.app_icon"),
+ objectFit: ImageFit.Fill,
+ errorholderObjectFit: ImageFit.None
+ }
+ })
+
+ ImageKnifeComponent({
+ imageKnifeOption: this.imageKnifeOption
+ }).width(300).height(200).border({width:1}).margin({top:50})
+
+ }
+ .height('100%')
+ .width('100%')
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/pages/SignatureTestPage.ets b/entry/src/main/ets/pages/SignatureTestPage.ets
index 5d26d49..42f49e4 100644
--- a/entry/src/main/ets/pages/SignatureTestPage.ets
+++ b/entry/src/main/ets/pages/SignatureTestPage.ets
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 Huawei Device Co., Ltd.
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
@@ -12,9 +12,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+import { ImageKnifeComponent, ImageKnifeOption } from '@ohos/libraryimageknife';
-import { ImageKnifeComponent, ImageKnifeOption, NONE, DiskStrategy } from '@ohos/libraryimageknife'
-import { ObjectKey } from '@ohos/libraryimageknife';
@Entry
@Component
@@ -22,43 +21,39 @@ struct SignatureTestPage {
@State imageKnifeOption1: ImageKnifeOption =
{
loadSrc: $r('app.media.icon'),
- placeholderSrc: $r('app.media.icon_loading'),
- errorholderSrc: $r('app.media.icon_failed'),
+ placeholderSrc:$r("app.media.loading"),
};
@State imageKnifeOption2: ImageKnifeOption =
{
loadSrc: $r('app.media.icon'),
- placeholderSrc: $r('app.media.icon_loading'),
- errorholderSrc: $r('app.media.icon_failed')
+ placeholderSrc:$r("app.media.loading"),
};
build() {
Scroll() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
- Text("Signature固定为 1").fontSize(15)
+ Text("key固定为 1").fontSize(15)
Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Button("加载")
.onClick(() => {
this.imageKnifeOption1 = {
loadSrc: 'https://img-blog.csdn.net/20140514114029140',
- placeholderSrc: $r('app.media.icon_loading'),
- errorholderSrc: $r('app.media.icon_failed'),
- signature: new ObjectKey("1")
+ placeholderSrc:$r("app.media.loading"),
+ signature: "1"
}
}).margin({ top: 5, left: 3 })
ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption1 }).width(300).height(300)
}.width('100%').backgroundColor(Color.Pink)
- Text("设置Signature,每次为时间戳").fontSize(15)
+ Text("key每次变化:时间戳").fontSize(15)
Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Button("加载")
.onClick(() => {
this.imageKnifeOption2 = {
loadSrc: 'https://img-blog.csdn.net/20140514114029140',
- placeholderSrc: $r('app.media.icon_loading'),
- errorholderSrc: $r('app.media.icon_failed'),
- signature: new ObjectKey(new Date().getTime().toString())
+ placeholderSrc:$r("app.media.loading"),
+ signature: new Date().getTime().toString()
}
}).margin({ top: 5, left: 3 })
ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption2 }).width(300).height(300)
@@ -71,7 +66,7 @@ struct SignatureTestPage {
}
aboutToAppear() {
- console.log("唯一标识页面:" + new ObjectKey(new Date().getTime().toString()).getKey())
+ console.log("唯一标识页面:" + new Date().getTime().toString())
}
}
\ No newline at end of file
diff --git a/entry/src/main/ets/pages/SingleImage.ets b/entry/src/main/ets/pages/SingleImage.ets
new file mode 100644
index 0000000..b7fc9b3
--- /dev/null
+++ b/entry/src/main/ets/pages/SingleImage.ets
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { ImageKnifeComponent,BlurTransformation } from '@ohos/libraryimageknife';
+import fs from '@ohos.file.fs';
+import image from '@ohos.multimedia.image';
+import { common2D, drawing } from '@kit.ArkGraphics2D';
+
+@Entry
+@Component
+struct SingleImage {
+ resource: string = "app.media.svgSample"
+ scroller: Scroller = new Scroller;
+ localFile: string = getContext(this).filesDir + "/icon.png"
+ @State pixelMap:PixelMap | undefined = undefined;
+ @State DrawingColorFilter: ColorFilter | undefined = undefined
+ private color: common2D.Color = { alpha: 255, red: 255, green: 0, blue: 0 };
+ aboutToAppear(): void {
+ // 拷贝本地文件
+ let icon: Uint8Array = getContext(this).resourceManager.getMediaContentSync($r("app.media.startIcon"));
+ let file = fs.openSync(this.localFile, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE);
+ fs.writeSync(file.fd, icon.buffer);
+ fs.fsyncSync(file.fd);
+ fs.closeSync(file);
+ this.changePic(getContext().resourceManager.getMediaContentSync( $r("app.media.aaa"))
+ .buffer as ArrayBuffer);
+
+
+ }
+
+ build() {
+ Scroll(this.scroller) {
+ Column() {
+ Text("本地资源svg图片")
+ .fontSize(30)
+ .fontWeight(FontWeight.Bold)
+ ImageKnifeComponent({
+ imageKnifeOption: {
+ loadSrc: $r("app.media.svgSample"),
+ placeholderSrc: $r("app.media.loading"),
+ errorholderSrc: $r("app.media.failed"),
+ objectFit: ImageFit.Contain
+ }
+ }).width(100).height(100)
+ .onClick(()=>{
+ this.DrawingColorFilter = drawing.ColorFilter.createBlendModeColorFilter(this.color, drawing.BlendMode.SRC_IN);
+ })
+ Text("本地context files下文件")
+ .fontSize(30)
+ .fontWeight(FontWeight.Bold)
+ ImageKnifeComponent({
+ imageKnifeOption: {
+ loadSrc: this.localFile,
+ placeholderSrc: $r("app.media.loading"),
+ errorholderSrc: $r("app.media.failed"),
+ objectFit: ImageFit.Contain
+ }
+ }).width(100).height(100)
+ Text("网络图片")
+ .fontSize(30)
+ .fontWeight(FontWeight.Bold)
+ ImageKnifeComponent({
+ imageKnifeOption: {
+ loadSrc:"https://www.openharmony.cn/_nuxt/img/logo.dcf95b3.png",
+ placeholderSrc: $r("app.media.loading"),
+ errorholderSrc: $r("app.media.failed"),
+ objectFit: ImageFit.Contain,
+ progressListener:(progress:number)=>{console.info("ImageKnife:: call back progress = " + progress)}
+ }
+ }).width(100).height(100)
+ Text("自定义下载")
+ .fontSize(30)
+ .fontWeight(FontWeight.Bold)
+ ImageKnifeComponent({
+ imageKnifeOption: {
+ loadSrc: "https://file.atomgit.com/uploads/user/1704857786989_8994.jpeg",
+ placeholderSrc: $r("app.media.loading"),
+ errorholderSrc: $r("app.media.failed"),
+ objectFit: ImageFit.Contain,
+ customGetImage: custom,
+ transformation: new BlurTransformation(10)
+ }
+ }).width(100).height(100)
+ Text("pixelMap加载图片")
+ .fontSize(30)
+ .fontWeight(FontWeight.Bold)
+ ImageKnifeComponent({
+ imageKnifeOption: {
+ loadSrc: this.pixelMap!,
+ placeholderSrc: $r("app.media.loading"),
+ errorholderSrc: $r("app.media.failed"),
+ 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;
+ })
+ }
+ }
+}
+
+// 自定义下载方法
+@Concurrent
+async function custom(context: Context, src: string | PixelMap | Resource): Promise {
+ console.info("ImageKnife:: custom download:" + src)
+ // 举例写死从本地文件读取,也可以自己请求网络图片
+ return context.resourceManager.getMediaContentSync($r("app.media.startIcon").id).buffer as ArrayBuffer
+}
+
diff --git a/entry/src/main/ets/pages/TestCommonImage.ets b/entry/src/main/ets/pages/TestCommonImage.ets
new file mode 100644
index 0000000..669220c
--- /dev/null
+++ b/entry/src/main/ets/pages/TestCommonImage.ets
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { ImageKnifeComponent } from '@ohos/libraryimageknife';
+
+@Entry
+@Component
+struct TestCommonImage {
+ private data: Array = []
+ aboutToAppear(): void {
+ for (let index = 0; index < 30; index++) {
+ this.data.push(`https://img-blog.csdn.net/20140514114029140?${index}`)
+ }
+ }
+ build() {
+ Column() {
+ WaterFlow() {
+ ForEach(this.data,(item: string)=>{
+ FlowItem() {
+ Column(){
+ ImageKnifeComponent({
+ imageKnifeOption: {
+ loadSrc: item,
+ placeholderSrc: $r("app.media.loading"),
+ errorholderSrc: $r("app.media.failed"),
+ objectFit: ImageFit.Contain,
+ signature: "aaa"
+ }
+ }).width("50%").height(200)
+ }
+ }.height(200)
+ .backgroundColor("#95efd2")
+ },(item: string) => item)
+ }.columnsTemplate("1fr 1fr")
+ .columnsGap(10)
+ .rowsGap(5)
+ .backgroundColor(0xFAEEE0)
+ .width("100%").height("100%")
+ }
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/pages/TestErrorHolderPage.ets b/entry/src/main/ets/pages/TestErrorHolderPage.ets
new file mode 100644
index 0000000..f49ab6c
--- /dev/null
+++ b/entry/src/main/ets/pages/TestErrorHolderPage.ets
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { ImageKnifeComponent } from '@ohos/libraryimageknife'
+
+@Entry
+@Component
+struct TestErrorHolderPage {
+
+ build() {
+ Column() {
+ Text("ImageKnifeComponent1").fontSize(20)
+ ImageKnifeComponent({
+ imageKnifeOption: {
+ loadSrc: "abc",
+ errorholderSrc:$r('app.media.failed')
+ }
+ }).width(200).height(200)
+ Text("ImageKnifeComponent2").fontSize(20)
+ ImageKnifeComponent({
+ imageKnifeOption: {
+ loadSrc: "abc",
+ errorholderSrc:$r('app.media.startIcon')
+ }
+ }).width(200).height(200)
+ Text("ImageKnifeComponent2").fontSize(20)
+ ImageKnifeComponent({
+ imageKnifeOption: {
+ loadSrc: "abc",
+ errorholderSrc:$r('app.media.mask_starfish')
+ }
+ }).width(200).height(200)
+ }
+ .height('100%') .width('100%')
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/pages/TestHeader.ets b/entry/src/main/ets/pages/TestHeader.ets
new file mode 100644
index 0000000..9be198e
--- /dev/null
+++ b/entry/src/main/ets/pages/TestHeader.ets
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { ImageKnifeComponent,ImageKnifeOption } from '@ohos/libraryimageknife'
+
+@Entry
+@Component
+struct TestPrefetchToFileCachePage {
+ @State imageKnifeOption: ImageKnifeOption = {
+ loadSrc:"https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658",
+ placeholderSrc:$r('app.media.loading'),
+ headerOption:[
+ {
+ key:"abc",
+ value:"单个"
+ }
+ ]
+ }
+
+ build() {
+ Column() {
+ ImageKnifeComponent({
+ imageKnifeOption: this.imageKnifeOption
+ }).width(300).height(300)
+ }
+ .height('100%') .width('100%')
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/pages/TestHspPreLoadImage.ets b/entry/src/main/ets/pages/TestHspPreLoadImage.ets
new file mode 100644
index 0000000..16ff464
--- /dev/null
+++ b/entry/src/main/ets/pages/TestHspPreLoadImage.ets
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { IndexComponent } from "@ohos/libraryimageknife"
+
+@Entry
+@Component
+struct TestHspPreLoadImage {
+ build() {
+ Column() {
+ IndexComponent()
+ }.width("100%").height('100%')
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/pages/TestImageFlash.ets b/entry/src/main/ets/pages/TestImageFlash.ets
new file mode 100644
index 0000000..75144e0
--- /dev/null
+++ b/entry/src/main/ets/pages/TestImageFlash.ets
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { ImageKnifeComponent } from '@ohos/libraryimageknife'
+
+@Observed
+export class MsgModel {
+ id: string
+ cId: string
+ body: string
+ status: number
+
+ constructor(id: string, body: string, cId?: string) {
+ this.id = id
+ this.body = body
+ this.status = -1
+ this.cId = cId || ''
+ }
+}
+
+
+@Reusable
+@Component
+export struct MsgItem {
+ count: number = 0
+ private data: Array = [
+ "http://e.hiphotos.baidu.com/image/pic/item/a1ec08fa513d2697e542494057fbb2fb4316d81e.jpg",
+ "http://c.hiphotos.baidu.com/image/pic/item/30adcbef76094b36de8a2fe5a1cc7cd98d109d99.jpg",
+ "http://h.hiphotos.baidu.com/image/pic/item/7c1ed21b0ef41bd5f2c2a9e953da81cb39db3d1d.jpg",
+ "http://g.hiphotos.baidu.com/image/pic/item/55e736d12f2eb938d5277fd5d0628535e5dd6f4a.jpg",
+ "http://e.hiphotos.baidu.com/image/pic/item/4e4a20a4462309f7e41f5cfe760e0cf3d6cad6ee.jpg",
+ "http://b.hiphotos.baidu.com/image/pic/item/9d82d158ccbf6c81b94575cfb93eb13533fa40a2.jpg",
+ "http://e.hiphotos.baidu.com/image/pic/item/4bed2e738bd4b31c1badd5a685d6277f9e2ff81e.jpg",
+ "http://g.hiphotos.baidu.com/image/pic/item/0d338744ebf81a4c87a3add4d52a6059252da61e.jpg",
+ "http://a.hiphotos.baidu.com/image/pic/item/f2deb48f8c5494ee5080c8142ff5e0fe99257e19.jpg",
+ "http://f.hiphotos.baidu.com/image/pic/item/4034970a304e251f503521f5a586c9177e3e53f9.jpg",
+ "http://b.hiphotos.baidu.com/image/pic/item/279759ee3d6d55fbb3586c0168224f4a20a4dd7e.jpg",
+ "http://img2.xkhouse.com/bbs/hfhouse/data/attachment/forum/corebbs/2009-11/2009113011534566298.jpg",
+ "http://a.hiphotos.baidu.com/image/pic/item/e824b899a9014c087eb617650e7b02087af4f464.jpg",
+ "http://c.hiphotos.baidu.com/image/pic/item/9c16fdfaaf51f3de1e296fa390eef01f3b29795a.jpg",
+ "http://d.hiphotos.baidu.com/image/pic/item/b58f8c5494eef01f119945cbe2fe9925bc317d2a.jpg",
+ "http://h.hiphotos.baidu.com/image/pic/item/902397dda144ad340668b847d4a20cf430ad851e.jpg",
+ "http://b.hiphotos.baidu.com/image/pic/item/359b033b5bb5c9ea5c0e3c23d139b6003bf3b374.jpg",
+ "http://a.hiphotos.baidu.com/image/pic/item/8d5494eef01f3a292d2472199d25bc315d607c7c.jpg",
+ "http://b.hiphotos.baidu.com/image/pic/item/e824b899a9014c08878b2c4c0e7b02087af4f4a3.jpg",
+ "http://g.hiphotos.baidu.com/image/pic/item/6d81800a19d8bc3e770bd00d868ba61ea9d345f2.jpg",
+ ]
+ build(){
+ if (this.count % 2 == 0 && this.count <6){
+ ImageKnifeComponent({
+ imageKnifeOption:{
+ loadSrc:$r("app.media.startIcon"),
+ placeholderSrc:$r("app.media.loading")
+ },syncLoad:true
+ })
+ }else if (this.count > 6 && this.count - 6 < this.data.length){
+ ImageKnifeComponent({
+ imageKnifeOption:{
+ loadSrc:this.data[this.count - 6],
+ placeholderSrc:$r("app.media.loading")
+ },syncLoad:true
+ })
+ }else {
+ ImageKnifeComponent({
+ imageKnifeOption:{
+ loadSrc:$r("app.media.pngSample"),
+ placeholderSrc:$r("app.media.loading")
+ },syncLoad:true
+ })
+ }
+ }
+}
+
+@Entry
+@Component
+struct ImageTestPage {
+ count : number = 0
+ rCount: number = 0
+ scroller: Scroller = new Scroller()
+ @State list: MsgModel[] = []
+ @State imageSize: number =100
+ handAdd(){
+ this.count++
+ const msgItem = new MsgModel('add_id'+this.count, 'addBody'+this.count,'cId'+ this.count)
+ this.list.push(msgItem)
+ setTimeout(()=> {
+ msgItem.status = 1
+ },3000)
+ this.scroller.scrollEdge(Edge.Bottom)
+ }
+
+ build(){
+ Column(){
+ Row(){
+ Button("addItem").onClick(()=> {
+ this.handAdd()
+ })
+ Button("remove").onClick(()=> {
+ this.list.splice(0,1)
+ })
+ }
+ Row(){
+ Text("点击尺寸加50")
+ .onClick(()=> {
+ this.imageSize = this.imageSize + 50
+ })
+ .width('50%').backgroundColor(0x88ff0000).textAlign(TextAlign.Center).height(50)
+ Text("点击尺寸减50")
+ .onClick(()=> {
+ this.imageSize = Math.max(this.imageSize - 50, 0)
+ })
+ .width('50%').backgroundColor(0x88ff0000).textAlign(TextAlign.Center).height(50)
+ }.height(50).width('100%')
+
+ List({space: 20, scroller: this.scroller }) {
+ ForEach(this.list, (item: MsgModel)=> {
+ ListItem(){
+ MsgItem({count : this.count}).width(this.imageSize).height(this.imageSize);
+ }
+ },(item:MsgModel)=> item.id)
+ }.width('100%').height('auto').layoutWeight(1)
+ }
+ .width('100%')
+ .height('100%')
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/pages/TestIsUrlExist.ets b/entry/src/main/ets/pages/TestIsUrlExist.ets
new file mode 100644
index 0000000..1225edc
--- /dev/null
+++ b/entry/src/main/ets/pages/TestIsUrlExist.ets
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { ImageKnifeComponent, ImageKnife, ImageKnifeOption, CacheStrategy } from '@ohos/libraryimageknife'
+
+@Entry
+@Component
+struct TestIsUrlExist {
+ @State imageKnifeOption: ImageKnifeOption = {
+ loadSrc: $r('app.media.startIcon'),
+ placeholderSrc: $r('app.media.loading'),
+ errorholderSrc:$r('app.media.failed')
+ }
+ @State source: PixelMap | string | Resource = $r("app.media.startIcon")
+ @State source1: PixelMap | string | Resource = $r("app.media.startIcon")
+
+ build() {
+ Column() {
+ Flex() {
+ Button("预加载gif图").onClick(() => {
+ this.imageKnifeOption.loadSrc =
+ "https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658"
+ })
+ Button("内存缓存获取gif").onClick(() => {
+ ImageKnife.getInstance()
+ .getCacheImage("https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658",
+ CacheStrategy.Memory)
+ .then((data) => {
+ this.source = data !== undefined ? data.source : $r("app.media.startIcon")
+ })
+ })
+ Button("文件缓存获取gif").onClick(() => {
+ ImageKnife.getInstance()
+ .getCacheImage("https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658",
+ CacheStrategy.File)
+ .then((data) => {
+ this.source1 = data !== undefined ? data.source : $r("app.media.startIcon")
+ })
+ })
+ }
+
+ Flex() {
+ Button("预加载静态图").onClick(() => {
+ this.imageKnifeOption.loadSrc =
+ 'https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp'
+ })
+ Button("内存缓存获取").onClick(() => {
+ ImageKnife.getInstance()
+ .getCacheImage('https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp',
+ CacheStrategy.Memory)
+ .then((data) => {
+ this.source = data!.source
+ })
+ })
+ Button("文件缓存获取").onClick(() => {
+ ImageKnife.getInstance()
+ .getCacheImage('https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp',
+ CacheStrategy.File)
+ .then((data) => {
+ this.source1 = data!.source
+ })
+ })
+ }.margin({ top: 10 })
+
+ ImageKnifeComponent({
+ imageKnifeOption: this.imageKnifeOption
+ }).width(200).height(200).margin({ top: 10 })
+ Image(this.source).margin({ top: 10 })
+ .width(200).height(200)
+ Image(this.source1).margin({ top: 10 })
+ .width(200).height(200)
+ }
+ .height('100%').width('100%')
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/pages/TestPrefetchToFileCache.ets b/entry/src/main/ets/pages/TestPrefetchToFileCache.ets
new file mode 100644
index 0000000..eb735a7
--- /dev/null
+++ b/entry/src/main/ets/pages/TestPrefetchToFileCache.ets
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { ImageKnifeComponent,ImageKnife,ImageKnifeOption } from '@ohos/libraryimageknife'
+
+@Entry
+@Component
+struct TestPrefetchToFileCachePage {
+ @State imageKnifeOption: ImageKnifeOption = {
+ loadSrc:$r('app.media.startIcon'),
+ placeholderSrc:$r('app.media.loading'),
+ errorholderSrc:$r('app.media.failed')
+ }
+ async preload(url:string) {
+ let fileCachePath = await ImageKnife.getInstance().preLoadCache(url)
+ console.log("preload-fileCachePath=="+ fileCachePath)
+ }
+ async preload1(url:string) {
+ let fileCachePath = await ImageKnife.getInstance().preLoadCache({ loadSrc: url })
+ console.log("preload-fileCachePath1=="+ fileCachePath)
+ }
+ build() {
+ Column() {
+ Button("url预加载图片到文件缓存").margin({top:10}).onClick(async ()=>{
+ await this.preload("https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658")
+ })
+ Button("option预加载图片到文件缓存").margin({top:10}).onClick(async ()=>{
+ await this.preload1("https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658")
+ })
+ Button("加载图片(预加载后可断网加载)").margin({top:10}).onClick(()=>{
+ this.imageKnifeOption.loadSrc = "https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658"
+ })
+ ImageKnifeComponent({
+ imageKnifeOption: this.imageKnifeOption
+ }).width(300).height(300).margin({top:30})
+ }
+ .height('100%') .width('100%')
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/pages/TestRemoveCache.ets b/entry/src/main/ets/pages/TestRemoveCache.ets
new file mode 100644
index 0000000..13e7def
--- /dev/null
+++ b/entry/src/main/ets/pages/TestRemoveCache.ets
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { ImageKnifeComponent, ImageKnife, ImageKnifeOption, CacheStrategy } from '@ohos/libraryimageknife'
+
+@Entry
+@Component
+struct TestRemoveCache {
+ @State imageKnifeOption: ImageKnifeOption = {
+ loadSrc: $r('app.media.startIcon'),
+ placeholderSrc: $r('app.media.loading'),
+ errorholderSrc:$r('app.media.failed')
+ }
+ @State source: PixelMap | string | Resource = $r("app.media.startIcon");
+ @State source1: PixelMap | string | Resource = $r("app.media.startIcon");
+ @State url: string = '';
+
+ build() {
+ Column() {
+ Flex() {
+ Button("预加载gif图").onClick(() => {
+ this.imageKnifeOption.loadSrc = "https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658";
+ this.url = "https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658";
+ })
+ .margin({left:10})
+ Button("内存缓存获取gif").onClick(() => {
+ ImageKnife.getInstance()
+ .getCacheImage("https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658",
+ CacheStrategy.Memory)
+ .then((data) => {
+ this.source = data !== undefined ? data.source : $r("app.media.startIcon");
+ })
+ })
+ .margin({left:10})
+ Button("文件缓存获取gif").onClick(() => {
+ ImageKnife.getInstance()
+ .getCacheImage("https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658",
+ CacheStrategy.File)
+ .then((data) => {
+ this.source1 = data !== undefined ? data.source : $r("app.media.startIcon");
+ })
+ })
+ .margin({left:10})
+ }
+
+ Flex() {
+ Button("预加载静态图").onClick(() => {
+ this.imageKnifeOption.loadSrc = 'https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp';
+ this.url = 'https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp';
+ })
+ .margin({left:10})
+ Button("内存缓存获取").onClick(() => {
+ ImageKnife.getInstance()
+ .getCacheImage('https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp',
+ CacheStrategy.Memory)
+ .then((data) => {
+ this.source = data!.source;
+ })
+ })
+ .margin({left:10})
+ Button("文件缓存获取").onClick(() => {
+ ImageKnife.getInstance()
+ .getCacheImage('https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp',
+ CacheStrategy.File)
+ .then((data) => {
+ this.source1 = data!.source;
+ })
+ })
+ .margin({left:10})
+ }.margin({ top: 10 })
+
+ Flex() {
+ Button("删除全部缓存").onClick(() => {
+ ImageKnife.getInstance()
+ .removeAllMemoryCache()
+ ImageKnife.getInstance()
+ .removeAllFileCache()
+ })
+ .margin({left:5})
+ Button("删除全部内存缓存").onClick(() => {
+ ImageKnife.getInstance()
+ .removeAllMemoryCache()
+ })
+ .margin({left:5})
+ Button("删除全部文件缓存").onClick(() => {
+ ImageKnife.getInstance()
+ .removeAllFileCache()
+ })
+ .margin({left:5})
+ }.margin({ top: 10 })
+
+ Flex() {
+ Button("删除自定义内存缓存").onClick(() => {
+ ImageKnife.getInstance()
+ .removeMemoryCache(this.url)
+ })
+ .margin({left:20})
+ Button("删除自定义文件缓存").onClick(() => {
+ ImageKnife.getInstance()
+ .removeFileCache(this.url)
+ })
+ .margin({left:20})
+ }.margin({ top: 10 })
+
+ ImageKnifeComponent({
+ imageKnifeOption: this.imageKnifeOption
+ }).width(200).height(200).margin({ top: 10 })
+ Image(this.source).margin({ top: 10 })
+ .width(200).height(200)
+ Image(this.source1).margin({ top: 10 })
+ .width(200).height(200)
+ }
+ .height('100%').width('100%')
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/pages/TestSetCustomImagePage.ets b/entry/src/main/ets/pages/TestSetCustomImagePage.ets
new file mode 100644
index 0000000..809769f
--- /dev/null
+++ b/entry/src/main/ets/pages/TestSetCustomImagePage.ets
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { ImageKnifeComponent, ImageKnife, ImageKnifeOption } from '@ohos/libraryimageknife'
+
+@Entry
+@Component
+struct TestSetCustomImagePage {
+ @State imageKnifeOption: ImageKnifeOption = {
+ loadSrc: $r('app.media.startIcon'),
+ placeholderSrc: $r('app.media.loading')
+ }
+ aboutToAppear(): void {
+ ImageKnife.getInstance().setCustomGetImage(custom)
+ }
+ aboutToDisappear(): void {
+ ImageKnife.getInstance().setCustomGetImage()
+ }
+ build() {
+ Column() {
+ Button("自定义下载a").onClick(()=>{
+ this.imageKnifeOption = {
+ loadSrc: "aaa",
+ placeholderSrc: $r('app.media.loading')
+ }
+ })
+ Button("自定义下载b").onClick(()=>{
+ this.imageKnifeOption = {
+ loadSrc: "bbb",
+ placeholderSrc: $r('app.media.loading')
+ }
+ })
+ Button("自定义下载c").onClick(()=>{
+ this.imageKnifeOption = {
+ loadSrc: "ccc",
+ placeholderSrc: $r('app.media.loading')
+ }
+ })
+ ImageKnifeComponent({
+ imageKnifeOption: this.imageKnifeOption
+ }).width(300)
+ .height(300)
+ }
+ .width("100%")
+ .height("100%")
+ }
+}
+@Concurrent
+async function custom(context: Context, src: string | PixelMap | Resource): Promise {
+ console.info("ImageKnife:: custom download:" + src)
+ // 举例写死从本地文件读取,也可以自己请求网络图片
+ return context.resourceManager.getMediaContentSync($r("app.media.pngSample").id).buffer as ArrayBuffer
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/pages/TestWriteCacheStage.ets b/entry/src/main/ets/pages/TestWriteCacheStage.ets
new file mode 100644
index 0000000..6b0b203
--- /dev/null
+++ b/entry/src/main/ets/pages/TestWriteCacheStage.ets
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { ImageKnifeComponent,CacheStrategy,ImageKnifeOption } from '@ohos/libraryimageknife'
+
+@Entry
+@Component
+struct TestWriteCacheStage {
+ @State imageKnifeOption1: ImageKnifeOption = {
+ loadSrc:$r('app.media.startIcon'),
+ placeholderSrc:$r('app.media.loading'),
+ errorholderSrc:$r('app.media.failed')
+ }
+ @State imageKnifeOption2: ImageKnifeOption = {
+ loadSrc:$r('app.media.startIcon'),
+ placeholderSrc:$r('app.media.loading'),
+ errorholderSrc:$r('app.media.failed')
+ }
+ @State imageKnifeOption3: ImageKnifeOption = {
+ loadSrc:$r('app.media.startIcon'),
+ placeholderSrc:$r('app.media.loading'),
+ errorholderSrc:$r('app.media.failed')
+ }
+
+ build() {
+ Column() {
+ Button("写入内存文件缓存").margin({top:10}).onClick(async ()=>{
+ this.imageKnifeOption1 = {
+ loadSrc:'https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp',
+ placeholderSrc:$r('app.media.loading'),
+ errorholderSrc:$r('app.media.failed'),
+ writeCacheStrategy:CacheStrategy.Default
+ }
+ })
+ ImageKnifeComponent({
+ imageKnifeOption: this.imageKnifeOption1
+ }).width(200).height(200).margin({top:10})
+ Button("写入内存缓存").margin({top:10}).onClick(async ()=>{
+ this.imageKnifeOption2 = {
+ loadSrc:"https://hbimg.huabanimg.com/cc6af25f8d782d3cf3122bef4e61571378271145735e9-vEVggB",
+ placeholderSrc:$r('app.media.loading'),
+ errorholderSrc:$r('app.media.failed'),
+ writeCacheStrategy:CacheStrategy.Memory
+ }
+ })
+ ImageKnifeComponent({
+ imageKnifeOption: this.imageKnifeOption2
+ }).width(200).height(200).margin({top:10})
+ Button("写入文件缓存").margin({top:10}).onClick(async ()=>{
+ this.imageKnifeOption3 = {
+ loadSrc:'https://img-blog.csdn.net/20140514114029140',
+ placeholderSrc:$r('app.media.loading'),
+ errorholderSrc:$r('app.media.failed'),
+ writeCacheStrategy:CacheStrategy.File
+ }
+ })
+ ImageKnifeComponent({
+ imageKnifeOption: this.imageKnifeOption3
+ }).width(200).height(200).margin({top:10})
+ }
+ .height('100%') .width('100%')
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/pages/TransformPage.ets b/entry/src/main/ets/pages/TransformPage.ets
new file mode 100644
index 0000000..7674156
--- /dev/null
+++ b/entry/src/main/ets/pages/TransformPage.ets
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { ImageKnifeComponent, ImageKnifeOption } from "@ohos/libraryimageknife"
+import matrix4 from '@ohos.matrix4'
+
+
+@Entry
+@Component
+struct TransformPage {
+ private custom_scale:number = 1
+ @State matrix1:object = matrix4.identity().scale({ x: 1, y: 1 })
+ @State ImageKnifeOption: ImageKnifeOption = {
+ loadSrc: $r("app.media.rabbit"),
+ placeholderSrc: $r("app.media.loading"),
+ errorholderSrc: $r("app.media.app_icon"),
+ objectFit: ImageFit.Contain,
+ border: { radius: 50 }
+ }
+
+ build() {
+ Column() {
+ ImageKnifeComponent({ imageKnifeOption: this.ImageKnifeOption }).height(200).width(200)
+ .transform(this.matrix1)
+ // Image($r('app.media.rabbit')).objectFit(ImageFit.Contain).height(200).width(200).transform(this.matrix1)
+ Button("放大").onClick(()=>{
+ this.custom_scale = this.custom_scale * 2
+ this.matrix1 = matrix4.identity().scale({ x: this.custom_scale, y: this.custom_scale })
+ })
+
+ Button("缩小").onClick(()=>{
+ this.custom_scale = this.custom_scale / 2
+ this.matrix1 = matrix4.identity().scale({ x: this.custom_scale, y: this.custom_scale })
+ })
+ }
+ .width('100%')
+
+ .height('100%')
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/pages/User.ets b/entry/src/main/ets/pages/User.ets
new file mode 100644
index 0000000..1e3e582
--- /dev/null
+++ b/entry/src/main/ets/pages/User.ets
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { ImageKnifeComponent, ImageKnifeOption } from '@ohos/libraryimageknife'
+
+// const logger = new imUtils.logger.IMLogger('Avatar')
+
+class MyImageOption extends ImageKnifeOption {
+ account?: string
+}
+
+@Component
+export struct UserAvatar {
+ @Prop @Watch('userInfoUpdate') userInfo: string = ""
+ // @Prop userInfo: string = ""
+ imgSize: number = 100
+ radius: number = 12
+ borderSize: number = 0
+ imgSizes: number = 1
+ @State ImageKnifeOption: ImageKnifeOption = new ImageKnifeOption()
+ @StorageProp('WeLink_Mob_fontSize_multiple') @Watch('updateImgSize') WeLink_Mob_fontSize_multiple: number = 0
+ scalable: boolean = true;
+ @State calcImgSize: number = 100
+
+ aboutToAppear(): void {
+ this.userInfoUpdate()
+ this.setImageSize()
+ }
+
+ setImageSize() {
+ if (!this.scalable) {
+ this.calcImgSize = this.imgSize
+ } else if (this.WeLink_Mob_fontSize_multiple < 0.9) {
+ this.calcImgSize = this.imgSize * 0.9
+ } else if (this.WeLink_Mob_fontSize_multiple > 1.6) {
+ this.calcImgSize = this.imgSize * 1.6
+ } else {
+ this.calcImgSize = this.imgSize * this.WeLink_Mob_fontSize_multiple
+ }
+ }
+
+ updateImgSize() {
+ this.setImageSize()
+ }
+
+ aboutToReuse(param: ESObject) {
+ this.userInfoUpdate()
+ }
+
+ userInfoUpdate() {
+ // if (uri === 'userInfo' && this.imageKnifeOption.account !== this.userInfo.contactId) return;
+ // // logger.info(`userInfoUpdate uri=${uri} oldAcc=${this.imageKnifeOption.loadSrc} nowAcc=${this.userInfo.externalHeadUrl}`)
+ // if (this.userInfo.externalHeadUrl === this.imageKnifeOption.loadSrc && this.userInfo.infoUpdateTime.getTime()
+ // .toString() === this.imageKnifeOption?.signature?.getKey()) return;
+ this.ImageKnifeOption = {
+ //TODO:写死loadSRC,场景:变更组件大小,所有图片不显示
+ loadSrc: this.userInfo,
+ placeholderSrc: $r('app.media.loading'),
+ errorholderSrc: $r('app.media.failed'),
+ border: { radius:20,width:5,color:$r('app.color.start_window_background') },
+ objectFit:ImageFit.Contain
+ // signature: new ObjectKey(this.userInfo.infoUpdateTime.getTime().toString())
+ }
+ }
+
+ build() {
+ Row() {
+ // Image(this.imageKnifeOption.loadSrc)
+
+ ImageKnifeComponent({ imageKnifeOption: this.ImageKnifeOption })
+ .borderRadius(this.radius)
+ .clip(true)
+ .width(this.calcImgSize)
+ .height(this.calcImgSize)
+ .backgroundColor(Color.Pink)
+
+
+
+ // Image(this.userInfo)
+ // Text((this.imageKnifeOption.loadSrc as string).split('/')[8])
+ // .position({ x: 0, y: 0 })
+ // .zIndex(9)
+ // .fontSize(12)
+ // .fontColor('#ff0000')
+ }
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/pages/UserPage.ets b/entry/src/main/ets/pages/UserPage.ets
new file mode 100644
index 0000000..ee210bc
--- /dev/null
+++ b/entry/src/main/ets/pages/UserPage.ets
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { UserAvatar } from './User'
+
+class CommonDataSource implements IDataSource {
+ private dataArray: T[] = []
+ private listeners: DataChangeListener[] = []
+
+ constructor(element: []) {
+ this.dataArray = element
+ }
+
+ public getData(index: number) {
+ return this.dataArray[index]
+ }
+
+ public totalCount(): number {
+ return this.dataArray.length
+ }
+
+ public addData(index: number, data: T[]): void {
+ this.dataArray = this.dataArray.concat(data)
+ this.notifyDataAdd(index)
+ }
+
+ unregisterDataChangeListener(listener: DataChangeListener): void {
+ const pos = this.listeners.indexOf(listener);
+ if (pos >= 0) {
+ this.listeners.splice(pos, 1);
+ }
+ }
+
+ registerDataChangeListener(listener: DataChangeListener): void {
+ if (this.listeners.indexOf(listener) < 0) {
+ this.listeners.push(listener)
+ }
+ }
+
+ notifyDataAdd(index: number): void {
+ this.listeners.forEach((listener: DataChangeListener) => {
+ listener.onDataAdd(index)
+ })
+ }
+}
+@Entry
+@Component
+struct Index {
+ @State hotCommendList:CommonDataSource = new CommonDataSource([])
+ private data:string[] = [
+ "http://e.hiphotos.baidu.com/image/pic/item/a1ec08fa513d2697e542494057fbb2fb4316d81e.jpg",
+ "http://c.hiphotos.baidu.com/image/pic/item/30adcbef76094b36de8a2fe5a1cc7cd98d109d99.jpg",
+ "http://h.hiphotos.baidu.com/image/pic/item/7c1ed21b0ef41bd5f2c2a9e953da81cb39db3d1d.jpg",
+ "http://g.hiphotos.baidu.com/image/pic/item/55e736d12f2eb938d5277fd5d0628535e5dd6f4a.jpg",
+ "http://e.hiphotos.baidu.com/image/pic/item/4e4a20a4462309f7e41f5cfe760e0cf3d6cad6ee.jpg",
+ "http://b.hiphotos.baidu.com/image/pic/item/9d82d158ccbf6c81b94575cfb93eb13533fa40a2.jpg",
+ "http://e.hiphotos.baidu.com/image/pic/item/4bed2e738bd4b31c1badd5a685d6277f9e2ff81e.jpg",
+ "http://g.hiphotos.baidu.com/image/pic/item/0d338744ebf81a4c87a3add4d52a6059252da61e.jpg",
+ "http://a.hiphotos.baidu.com/image/pic/item/f2deb48f8c5494ee5080c8142ff5e0fe99257e19.jpg",
+ "http://f.hiphotos.baidu.com/image/pic/item/4034970a304e251f503521f5a586c9177e3e53f9.jpg",
+ "http://b.hiphotos.baidu.com/image/pic/item/279759ee3d6d55fbb3586c0168224f4a20a4dd7e.jpg",
+ "http://img2.xkhouse.com/bbs/hfhouse/data/attachment/forum/corebbs/2009-11/2009113011534566298.jpg",
+ "http://a.hiphotos.baidu.com/image/pic/item/e824b899a9014c087eb617650e7b02087af4f464.jpg",
+ "http://c.hiphotos.baidu.com/image/pic/item/9c16fdfaaf51f3de1e296fa390eef01f3b29795a.jpg",
+ "http://d.hiphotos.baidu.com/image/pic/item/b58f8c5494eef01f119945cbe2fe9925bc317d2a.jpg",
+ "http://h.hiphotos.baidu.com/image/pic/item/902397dda144ad340668b847d4a20cf430ad851e.jpg",
+ "http://b.hiphotos.baidu.com/image/pic/item/359b033b5bb5c9ea5c0e3c23d139b6003bf3b374.jpg",
+ "http://a.hiphotos.baidu.com/image/pic/item/8d5494eef01f3a292d2472199d25bc315d607c7c.jpg",
+ "http://b.hiphotos.baidu.com/image/pic/item/e824b899a9014c08878b2c4c0e7b02087af4f4a3.jpg",
+ "http://g.hiphotos.baidu.com/image/pic/item/6d81800a19d8bc3e770bd00d868ba61ea9d345f2.jpg",
+ "https://hbimg.huabanimg.com/cc6af25f8d782d3cf3122bef4e61571378271145735e9-vEVggB",
+ 'https://img-blog.csdnimg.cn/20191215043500229.png',
+ 'https://img-blog.csdn.net/20140514114029140',
+ 'https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp',
+ ]
+ aboutToAppear(): void {
+ this.hotCommendList.addData(this.hotCommendList.totalCount(),this.data)
+ AppStorage.set("WeLink_Mob_fontSize_multiple",1)
+ }
+
+ build() {
+ Column() {
+ Button("bigger").onClick(()=>{
+ AppStorage.set("WeLink_Mob_fontSize_multiple",1.6)
+ })
+ Button("small").onClick(()=>{
+ AppStorage.set("WeLink_Mob_fontSize_multiple",0.8)
+ })
+ List(){
+ LazyForEach(this.hotCommendList,(item:string)=>{
+ ListItem(){
+ ReuseImage({
+ userInfo:item
+ }).width("100%").height("100%").backgroundColor(Color.Yellow)
+ }.width(200).height(200).margin({bottom:5})
+ })
+ }
+ // .cachedCount(20)
+ .width("100%")
+ .height("100%")
+ .backgroundColor(0xFAEEE0)
+ }.width('100%').height("100%")
+ }
+}
+
+
+@Reusable
+@Component
+struct ReuseImage {
+ @State userInfo:string = ""
+ aboutToReuse(params: ESObject): void {
+ this.userInfo = params.userInfo
+ }
+
+ build() {
+ Column(){
+ UserAvatar({
+ userInfo:this.userInfo
+ })
+ }.width("100%").height("100%")
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/pages/dataShareUriLoadPage.ets b/entry/src/main/ets/pages/dataShareUriLoadPage.ets
index b3fe696..bb71ac2 100644
--- a/entry/src/main/ets/pages/dataShareUriLoadPage.ets
+++ b/entry/src/main/ets/pages/dataShareUriLoadPage.ets
@@ -24,8 +24,8 @@ struct DataShareUriLoadPage {
{
loadSrc: $r('app.media.icon'),
- placeholderSrc: $r('app.media.icon_loading'),
- errorholderSrc: $r('app.media.icon_failed')
+ placeholderSrc: $r('app.media.loading'),
+ errorholderSrc: $r('app.media.failed')
};
@@ -45,7 +45,7 @@ struct DataShareUriLoadPage {
uris = photoSelectResult.photoUris;
this.imageKnifeOption1 = {
loadSrc: uris[0],
- placeholderSrc:$r('app.media.icon_loading')
+ placeholderSrc:$r('app.media.loading')
}
}).margin({ top: 5, left: 3 })
ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption1 }).width(300).height(300)
diff --git a/entry/src/main/ets/pages/index.ets b/entry/src/main/ets/pages/index.ets
deleted file mode 100644
index 63f783a..0000000
--- a/entry/src/main/ets/pages/index.ets
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2022 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import router from '@ohos.router';
-import {
- ImageKnifeComponent,
- ImageKnifeOption,
- ImageKnifeGlobal,
- ImageKnife,
- HeaderOptions
-} from '@ohos/libraryimageknife'
-
-import { ObjectKey } from '@ohos/libraryimageknife';
-
-@Entry
-@Component
-struct IndexFunctionDemo {
- @State headerOptions1: HeaderOptions = {
- key: "refer",
- value: "http://1.94.37.200:7070/AntiTheftChain/downloadImage"
- };
- @State imageKnifeOption1: ImageKnifeOption =
- {
- loadSrc: $r('app.media.icon'),
- placeholderSrc: $r('app.media.icon_loading'),
- errorholderSrc: $r('app.media.icon_failed'),
- headerOption: [this.headerOptions1]
- };
- @State imageKnifeOption2: ImageKnifeOption =
- {
- loadSrc: $r('app.media.icon'),
- placeholderSrc: $r('app.media.icon_loading'),
- errorholderSrc: $r('app.media.icon_failed'),
- headerOption: [this.headerOptions1]
- };
- build() {
- Scroll() {
- Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
- Text("简单示例1:加载一张本地png图片").fontSize(15)
- Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
- Button("加载PNG")
- .onClick(() => {
- this.imageKnifeOption1 = {
- loadSrc: $r('app.media.pngSample'),
- placeholderSrc: $r('app.media.icon_loading'),
- errorholderSrc: $r('app.media.icon_failed'),
- signature: new ObjectKey('ccccccc')
- }
- }).margin({ top: 5, left: 3 })
- ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption1 }).width(300).height(300)
- }.width('100%').backgroundColor(Color.Pink)
-
- Text("简单示例2:加载一张网络gif图片").fontSize(15)
- Text("gif解析在子线程,请在页面构建后创建worker,注入imageknife").fontSize(15)
- Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
- Button("加载GIF")
- .onClick(() => {
- this.imageKnifeOption2 = {
- loadSrc: 'https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658',
- placeholderSrc: $r('app.media.icon_loading'),
- errorholderSrc: $r('app.media.icon_failed'),
- displayProgress:true,
- }
- }).margin({ top: 5, left: 3 })
- ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption2 }).width(300).height(300)
- }.width('100%').backgroundColor(Color.Pink)
-
- Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
- Button("ImageKnife测试目录页面")
- .onClick(() => {
- console.log("pages/imageknifeTestCaseIndex 页面跳转")
- router.pushUrl({ url: "pages/imageknifeTestCaseIndex" });
- }).margin({ top: 15 })
- }.width('100%').height(60).backgroundColor(Color.Pink)
- }
- }
- .width('100%')
- .height('100%')
- }
-
- aboutToAppear() {
-
- }
- aboutToDisappear(){
-
- }
-
- onBackPress() {
-
- }
-}
\ No newline at end of file
diff --git a/entry/src/main/ets/pages/manyPhotoShowPage.ets b/entry/src/main/ets/pages/manyPhotoShowPage.ets
deleted file mode 100644
index dffbb0a..0000000
--- a/entry/src/main/ets/pages/manyPhotoShowPage.ets
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2021 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the 'License');
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an 'AS IS' BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import {ImageKnifeComponent, ScaleType} from '@ohos/libraryimageknife'
-import {ImageKnifeOption} from '@ohos/libraryimageknife'
-import {ImageKnifeGlobal} from '@ohos/libraryimageknife'
-import {RotateImageTransformation} from '@ohos/libraryimageknife'
-import {Material} from './model/Material'
-import {TestDataSource} from './model/TestDataSource'
-import {DiskLruCache} from '@ohos/disklrucache'
-import ArkWorker from '@ohos.worker'
-import Prompt from '@system.prompt'
-@Entry
-@Component
-struct ManyPhotoShowPage {
- private data: TestDataSource = new TestDataSource();
-
- private elementScroller: Scroller = new Scroller()
-
- build() {
- Column() {
-
- List({ space: 20, scroller: this.elementScroller }) {
- LazyForEach(this.data, (item: Material, index) => {
- ListItem() {
- Column() {
- Stack({ alignContent: Alignment.BottomEnd }) {
- // 滤镜图片
- ImageKnifeComponent({ imageKnifeOption: {
- loadSrc: item.thumbnail,
- mainScaleType: ScaleType.FIT_XY,
- } })
- }
- .width(56).height(56)
- //滤镜标题
- Text(item.name)
- .fontSize(10)
- .maxLines(1)
- .fontColor(Color.White)
- .textAlign(TextAlign.Center)
- .layoutWeight(1)
- .width('100%')
- .backgroundColor(Color.Orange)
- }
- .width(56)
- .height(72)
- .clip(true)
- .borderRadius(4)
- }
- }, (item: Material) => item.material_id)
- }
- .listDirection(Axis.Horizontal)
- .width('100%')
- .height(72)
-
- }
- }
-
-
-
-}
-
-
diff --git a/entry/src/main/module.json5 b/entry/src/main/module.json5
index c83bbef..9036eb0 100644
--- a/entry/src/main/module.json5
+++ b/entry/src/main/module.json5
@@ -2,33 +2,26 @@
"module": {
"name": "entry",
"type": "entry",
-
- "description": "$string:entry_desc",
- "mainElement": "MainAbility",
+ "description": "$string:module_desc",
+ "mainElement": "EntryAbility",
"deviceTypes": [
- "default",
- "tablet"
+ "phone",
+ "tablet",
+ "2in1"
],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:main_pages",
- "metadata": [
- {
- "name": "ArkTSPartialUpdate",
- "value": "true"
- }
- ],
- "uiSyntax": "ets",
"abilities": [
{
- "name": "MainAbility",
+ "name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets",
- "description": "$string:MainAbility_desc",
+ "description": "$string:EntryAbility_desc",
"icon": "$media:icon",
- "label": "$string:MainAbility_label",
- "startWindowIcon": "$media:icon",
- "startWindowBackground": "$color:test_color",
- "visible": true,
+ "label": "$string:EntryAbility_label",
+ "startWindowIcon": "$media:startIcon",
+ "startWindowBackground": "$color:start_window_background",
+ "exported": true,
"skills": [
{
"entities": [
diff --git a/entry/src/main/resources/base/element/color.json b/entry/src/main/resources/base/element/color.json
index 6146fec..3c71296 100644
--- a/entry/src/main/resources/base/element/color.json
+++ b/entry/src/main/resources/base/element/color.json
@@ -1,8 +1,8 @@
{
"color": [
{
- "name": "test_color",
- "value": "#3d2564"
+ "name": "start_window_background",
+ "value": "#FFFFFF"
}
]
}
\ No newline at end of file
diff --git a/entry/src/main/resources/base/element/string.json b/entry/src/main/resources/base/element/string.json
index 8d7b7cf..13c1fec 100644
--- a/entry/src/main/resources/base/element/string.json
+++ b/entry/src/main/resources/base/element/string.json
@@ -1,125 +1,24 @@
{
"string": [
{
- "name": "entry_desc",
+ "name": "module_desc",
+ "value": "module description"
+ },
+ {
+ "name": "EntryAbility_desc",
"value": "description"
},
{
- "name": "MainAbility_desc",
- "value": "description"
- },
- {
- "name": "MainAbility_label",
- "value": "ImageKnife"
- },
-
- {
- "name": "ImageKnife_OHOS",
- "value": "ImageKnife_OHOS"
- },
- {
- "name": "mainability_description",
- "value": "ETS_Empty Ability"
- },
- {
- "name": "left_top_corner",
- "value": "左上角"
- },
- {
- "name": "r_top_corner",
- "value": "右上角"
- },
- {
- "name": "left_bottom_corner",
- "value": "左下角"
- },
- {
- "name": "r_bottom_corner",
- "value": "右下角"
- },
- {
- "name": "trans_circle",
- "value": "裁剪-圆"
- },
- {
- "name": "trans_circle_border",
- "value": "裁剪-圆环"
- },
- {
- "name": "trans_rotate",
- "value": "旋转"
- },
- {
- "name": "trans_square",
- "value": "正方形裁剪"
- },
- {
- "name": "trans_clip_top",
- "value": "上方裁剪"
- },
- {
- "name": "trans_clip_center",
- "value": "中间裁剪"
- },
- {
- "name": "trans_clip_bottom",
- "value": "底下裁剪"
- },
- {
- "name": "resource_image_compress",
- "value": "资源图片压缩"
- },
- {
- "name": "file_image_compress",
- "value": "本地文件图片压缩"
- },
- {
- "name": "image_transform",
- "value": "图片变换"
- },
- {
- "name": "image_compress",
- "value": "图片压缩"
- },
- {
- "name": "image_grayscale",
- "value": "灰度处理"
- },
- {
- "name": "image_Brightness",
- "value": "亮度处理"
- },
- {
- "name": "image_Contrast",
- "value": "对比度处理"
- },
- {
- "name": "image_Invert",
- "value": "反转处理"
- },
- {
- "name": "image_Sepia",
- "value": "黑褐色处理"
- },
- {
- "name": "image_Sketch",
- "value": "素描处理"
- },
- {
- "name": "image_blur",
- "value": "模糊处理"
- },
- {
- "name": "image_pixel",
- "value": "马赛克处理"
- },
- {
- "name": "app_permission_READ_IMAGEVIDEO",
- "value": "获取读媒体资源权限"
+ "name": "EntryAbility_label",
+ "value": "label"
},
{
"name": "app_permission_WRITE_IMAGEVIDEO",
"value": "获取写入媒体资源权限"
+ },
+ {
+ "name": "app_permission_READ_IMAGEVIDEO",
+ "value": "获取读媒体资源权限"
}
]
}
\ No newline at end of file
diff --git a/entry/src/main/resources/base/media/aaa.jpg b/entry/src/main/resources/base/media/aaa.jpg
new file mode 100644
index 0000000..b4212c2
Binary files /dev/null and b/entry/src/main/resources/base/media/aaa.jpg differ
diff --git a/entry/src/main/resources/base/media/failed.png b/entry/src/main/resources/base/media/failed.png
new file mode 100644
index 0000000..94c63eb
Binary files /dev/null and b/entry/src/main/resources/base/media/failed.png differ
diff --git a/entry/src/main/resources/base/media/icon.png b/entry/src/main/resources/base/media/icon.png
index ce307a8..cd45acc 100644
Binary files a/entry/src/main/resources/base/media/icon.png and b/entry/src/main/resources/base/media/icon.png differ
diff --git a/entry/src/main/resources/base/media/loading.png b/entry/src/main/resources/base/media/loading.png
new file mode 100644
index 0000000..e7ddd2e
Binary files /dev/null and b/entry/src/main/resources/base/media/loading.png differ
diff --git a/entry/src/main/resources/base/media/pngSample.png b/entry/src/main/resources/base/media/pngSample.png
index ebe1711..df4a140 100644
Binary files a/entry/src/main/resources/base/media/pngSample.png and b/entry/src/main/resources/base/media/pngSample.png differ
diff --git a/entry/src/main/resources/base/media/rabbit.gif b/entry/src/main/resources/base/media/rabbit.gif
new file mode 100644
index 0000000..c2c1402
Binary files /dev/null and b/entry/src/main/resources/base/media/rabbit.gif differ
diff --git a/entry/src/main/resources/base/media/startIcon.png b/entry/src/main/resources/base/media/startIcon.png
new file mode 100644
index 0000000..366f764
Binary files /dev/null and b/entry/src/main/resources/base/media/startIcon.png differ
diff --git a/entry/src/main/resources/base/profile/main_pages.json b/entry/src/main/resources/base/profile/main_pages.json
index ae140a8..42b571c 100644
--- a/entry/src/main/resources/base/profile/main_pages.json
+++ b/entry/src/main/resources/base/profile/main_pages.json
@@ -1,67 +1,27 @@
{
"src": [
- "pages/index",
- "pages/CacheRuleChangedPage",
- "pages/frescoRetryTestCasePage",
- "pages/basicTestFeatureAbilityPage",
- "pages/basicTestFileIOPage",
- "pages/basicTestMediaImage",
- "pages/basicTestResourceManagerPage",
- "pages/storageTestLruCache",
- "pages/pngjTestCasePage",
- "pages/showErrorholderTestCasePage",
- "pages/transformPixelMapPage",
- "pages/transformsPage",
- "pages/testPreloadPage",
- "pages/testDiskPreLoadPage",
- "pages/testImageKnifeOptionChangedPage",
- "pages/testImageKnifeOptionChangedPage2",
- "pages/testImageKnifeOptionChangedPage3",
- "pages/testImageKnifeOptionChangedPage4",
- "pages/testImageKnifeOptionChangedPage5",
- "pages/testGifPlayTimesPage",
- "pages/compressPage",
- "pages/testAllCacheInfoPage",
- "pages/cropImagePage2",
- "pages/svgTestCasePage",
- "pages/imageknifeTestCaseIndex",
- "pages/dataShareUriLoadPage",
- "pages/manyPhotoShowPage",
- "pages/photosPausedResumedPage",
- "pages/photosPausedResumedPage2",
- "pages/tempUrlTestPage",
- "pages/drawFactoryTestPage",
- "pages/testSingleFrameGifPage",
- "pages/OptionTestPage",
+ "pages/Index",
+ "pages/ListPage",
+ "pages/SingleImage",
+ "pages/ManyPhotoShowPage",
+ "pages/LongImagePage",
+ "pages/TransformPage",
+ "pages/UserPage",
+ "pages/TestImageFlash",
"pages/SignatureTestPage",
- "pages/hspCacheTestPage",
- "pages/multiHspTestPage",
- "pages/testManyNetImageLoadWithPage",
- "pages/testManyNetImageLoadWithPage2",
- "pages/testManyGifLoadWithPage",
- "pages/testImageAntiAliasingWithPage",
- "pages/testImageKnifeRouter1",
- "pages/testImageKnifeRouter2",
- "pages/RequestOptionLoadImage",
- "pages/testImageKnifeHttpRequestHeader",
- "pages/testImageKnifeHttpRequestHeader1",
- "pages/testImageKnifeAutoPage",
- "pages/testImageKnifeAutoWidthPage",
- "pages/testImageKnifeAutoHeightPage",
- "pages/testPriorityComponent",
- "pages/testVisiblePage",
- "pages/testReusePhotoPage",
- "pages/testImageKnifeCache",
- "pages/webpImageTestPage",
- "pages/testStopPlayingGifPage",
- "pages/testImageKnifeDataFetch",
- "pages/testImageKnifeHeic",
- "pages/testImageKnifeNetPlaceholder",
- "pages/testCustomDataFetchClientWithPage",
- "pages/testReuseAblePages",
- "pages/downsamplingPage",
- "pages/testImageKnifeLoadState",
- "pages/testImageKnifeRemoveCache",
- "pages/TestDurationAndPlayTimesPage"
+ "pages/TestPrefetchToFileCache",
+ "pages/TestIsUrlExist",
+ "pages/TestHeader",
+ "pages/ImageTransformation",
+ "pages/ObjectFitPage",
+ "pages/TestWriteCacheStage",
+ "pages/LoadStatePage",
+ "pages/TestHspPreLoadImage",
+ "pages/TestRemoveCache",
+ "pages/dataShareUriLoadPage",
+ "pages/TestCommonImage",
+ "pages/ImageAnimatorPage",
+ "pages/TestSetCustomImagePage",
+ "pages/TestErrorHolderPage"
]
}
\ No newline at end of file
diff --git a/entry/src/main/resources/en_US/element/string.json b/entry/src/main/resources/en_US/element/string.json
new file mode 100644
index 0000000..13c1fec
--- /dev/null
+++ b/entry/src/main/resources/en_US/element/string.json
@@ -0,0 +1,24 @@
+{
+ "string": [
+ {
+ "name": "module_desc",
+ "value": "module description"
+ },
+ {
+ "name": "EntryAbility_desc",
+ "value": "description"
+ },
+ {
+ "name": "EntryAbility_label",
+ "value": "label"
+ },
+ {
+ "name": "app_permission_WRITE_IMAGEVIDEO",
+ "value": "获取写入媒体资源权限"
+ },
+ {
+ "name": "app_permission_READ_IMAGEVIDEO",
+ "value": "获取读媒体资源权限"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/entry/src/main/resources/zh_CN/element/string.json b/entry/src/main/resources/zh_CN/element/string.json
new file mode 100644
index 0000000..6250115
--- /dev/null
+++ b/entry/src/main/resources/zh_CN/element/string.json
@@ -0,0 +1,24 @@
+{
+ "string": [
+ {
+ "name": "module_desc",
+ "value": "模块描述"
+ },
+ {
+ "name": "EntryAbility_desc",
+ "value": "description"
+ },
+ {
+ "name": "EntryAbility_label",
+ "value": "label"
+ },
+ {
+ "name": "app_permission_WRITE_IMAGEVIDEO",
+ "value": "获取写入媒体资源权限"
+ },
+ {
+ "name": "app_permission_READ_IMAGEVIDEO",
+ "value": "获取读媒体资源权限"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/entry/src/ohosTest/ets/test/DefaultJobQueueTest.test.ets b/entry/src/ohosTest/ets/test/DefaultJobQueueTest.test.ets
index 3212de0..eb13a71 100644
--- a/entry/src/ohosTest/ets/test/DefaultJobQueueTest.test.ets
+++ b/entry/src/ohosTest/ets/test/DefaultJobQueueTest.test.ets
@@ -12,60 +12,76 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { describe, it, expect } from '@ohos/hypium';
-import { ImageKnife, ImageKnifeGlobal, RequestOption } from '@ohos/imageknife/Index';
-import { DefaultJobQueue } from '@ohos/imageknife/src/main/ets/components/imageknife/utils/DefaultJobQueue';
-import { IJobQueue } from '@ohos/imageknife/src/main/ets/components/imageknife/utils/IJobQueue';
+import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
+import { ImageKnifeOption, ImageKnifeRequest } from '@ohos/imageknife/Index';
+import { DefaultJobQueue } from '@ohos/imageknife/src/main/ets/utils/DefaultJobQueue';
+import { IJobQueue } from '@ohos/imageknife/src/main/ets/utils/IJobQueue';
import taskpool from '@ohos.taskpool';
import common from '@ohos.app.ability.common';
-import { fitter} from '@ohos/imageknife';
+
+
export default function DefaultJobQueueTest() {
describe('DefaultJobQueueTest', () => {
- it('testJob', 0, () => {
- let job: IJobQueue = new DefaultJobQueue();
- job.add(makeRequest("medium1", getContext() as common.UIAbilityContext));
- job.add(makeRequest("medium2", getContext() as common.UIAbilityContext, taskpool.Priority.MEDIUM));
- job.add(makeRequest("medium3", getContext() as common.UIAbilityContext));
- job.add(makeRequest("low1", getContext() as common.UIAbilityContext, taskpool.Priority.LOW));
- job.add(makeRequest("high1", getContext() as common.UIAbilityContext, taskpool.Priority.HIGH));
- job.add(makeRequest("low2", getContext() as common.UIAbilityContext, taskpool.Priority.LOW));
- job.add(makeRequest("medium4", getContext() as common.UIAbilityContext));
-
- expect(job.getQueueLength()).assertEqual(7);
- expect(job.pop()!.loadSrc).assertEqual("high1");
- expect(job.pop()!.loadSrc).assertEqual("medium1");
- expect(job.pop()!.loadSrc).assertEqual("medium2");
- expect(job.pop()!.loadSrc).assertEqual("medium3");
- expect(job.pop()!.loadSrc).assertEqual("medium4");
- expect(job.pop()!.loadSrc).assertEqual("low1");
- expect(job.pop()!.loadSrc).assertEqual("low2");
- expect(job.pop()).assertEqual(undefined);
- expect(job.getQueueLength()).assertEqual(0);
+ // Defines a test suite. Two parameters are supported: test suite name and test suite function.
+ beforeAll(() => {
+ // Presets an action, which is performed only once before all test cases of the test suite start.
+ // This API supports only one parameter: preset action function.
+ });
+ beforeEach(() => {
+ // Presets an action, which is performed before each unit test case starts.
+ // The number of execution times is the same as the number of test cases defined by **it**.
+ // This API supports only one parameter: preset action function.
+ });
+ afterEach(() => {
+ // Presets a clear action, which is performed after each unit test case ends.
+ // The number of execution times is the same as the number of test cases defined by **it**.
+ // This API supports only one parameter: clear action function.
+ });
+ afterAll(() => {
+ // Presets a clear action, which is performed after all test cases of the test suite end.
+ // This API supports only one parameter: clear action function.
+ });
+
+ it('testJob', 0, async () => {
+ let job: IJobQueue = new DefaultJobQueue()
+ job.add(makeRequest("medium1", getContext() as common.UIAbilityContext))
+ job.add(makeRequest("medium2", getContext() as common.UIAbilityContext, taskpool.Priority.MEDIUM))
+ job.add(makeRequest("medium3", getContext() as common.UIAbilityContext))
+ job.add(makeRequest("low1", getContext() as common.UIAbilityContext, taskpool.Priority.LOW))
+ job.add(makeRequest("high1", getContext() as common.UIAbilityContext, taskpool.Priority.HIGH))
+ job.add(makeRequest("low2", getContext() as common.UIAbilityContext, taskpool.Priority.LOW))
+ job.add(makeRequest("medium4", getContext() as common.UIAbilityContext))
+
+ expect(job.getQueueLength()).assertEqual(7)
+ expect(job.pop()!.imageKnifeOption.loadSrc).assertEqual("high1")
+ expect(job.pop()!.imageKnifeOption.loadSrc).assertEqual("medium4")
+ expect(job.pop()!.imageKnifeOption.loadSrc).assertEqual("medium3")
+ expect(job.pop()!.imageKnifeOption.loadSrc).assertEqual("medium2")
+ expect(job.pop()!.imageKnifeOption.loadSrc).assertEqual("medium1")
+ expect(job.pop()!.imageKnifeOption.loadSrc).assertEqual("low2")
+ expect(job.pop()!.imageKnifeOption.loadSrc).assertEqual("low1")
+ expect(job.pop()).assertEqual(undefined)
+ expect(job.getQueueLength()).assertEqual(0)
+
});
- it("testMaxRequests", 1, () => {
- let imageKnife: ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife();
- if (imageKnife) {
- expect(imageKnife.maxRequests).assertEqual(64);
- imageKnife.setMaxRequests(10);
- expect(imageKnife.maxRequests).assertEqual(10);
- };
- })
- it("downsampleOf", 1, () => {
- let request: RequestOption = new RequestOption()
- if (request) {
- let requests = request.downsampleOf(new fitter())
- expect(requests.downsampType.getName()=='FitCenter').assertTrue()
- };
- })
});
}
-function makeRequest(src: string, context: common.UIAbilityContext, priority: taskpool.Priority = taskpool.Priority.MEDIUM): RequestOption {
- let request: RequestOption = new RequestOption()
- request.loadSrc = src;
- request.priority = priority;
- request.moduleContext = context;
- return request;
+function makeRequest(src: string, context: common.UIAbilityContext, priority?: taskpool.Priority): ImageKnifeRequest {
+ let option: ImageKnifeOption = {
+ loadSrc: src,
+ priority: priority
+ }
+ return new ImageKnifeRequest(
+ option,
+ context,
+ 0,
+ 0,
+ 0,
+ {
+ showPixelMap: async (version: number,pixelMap: PixelMap | string) => {
+ }
+ })
}
diff --git a/entry/src/ohosTest/ets/test/FileLruCache.test.ets b/entry/src/ohosTest/ets/test/FileLruCache.test.ets
new file mode 100644
index 0000000..2dd76f3
--- /dev/null
+++ b/entry/src/ohosTest/ets/test/FileLruCache.test.ets
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
+
+import Constants from '../../../main/ets/common/Constants';
+import taskpool from '@ohos.taskpool';
+import { GlobalContext } from '../../../main/ets/common/GlobalContext';
+import { FileCache } from '@ohos/imageknife/src/main/ets/utils/FileCache';
+import { IEngineKey, ImageKnifeOption } from '@ohos/imageknife';
+import { DefaultEngineKey } from '@ohos/imageknife/src/main/ets/key/DefaultEngineKey';
+
+
+export default function FileLruCacheTest() {
+
+ describe('FileLruCacheTest', () => {
+ // Defines a test suite. Two parameters are supported: test suite name and test suite function.
+ beforeAll(() => {
+ // Presets an action, which is performed only once before all test cases of the test suite start.
+ // This API supports only one parameter: preset action function.
+ });
+ beforeEach(() => {
+ // Presets an action, which is performed before each unit test case starts.
+ // The number of execution times is the same as the number of test cases defined by **it**.
+ // This API supports only one parameter: preset action function.
+ });
+ afterEach(() => {
+ // Presets a clear action, which is performed after each unit test case ends.
+ // The number of execution times is the same as the number of test cases defined by **it**.
+ // This API supports only one parameter: clear action function.
+ });
+ afterAll(() => {
+ // Presets a clear action, which is performed after all test cases of the test suite end.
+ // This API supports only one parameter: clear action function.
+ });
+
+ // 测试基础put,get以及size功能
+ it('assertFileSizeAllInMainThread', 0, async () => {
+ const buf: ArrayBuffer = new ArrayBuffer(1024 * 1024);
+ console.info(Constants.TAG + buf.byteLength)
+ console.info(Constants.TAG + GlobalContext.getContext().getObject("cacheDir"))
+
+ let fileCache: FileCache = new FileCache(GlobalContext.getContext()
+ .getObject("context") as Context, 5, 3 * 1024 * 1024)
+ await fileCache.initFileCache()
+
+ fileCache.put("aaa", buf)
+ await sleep(1000)
+ fileCache.put("bbb", buf)
+ await sleep(1000)
+ fileCache.put("ccc", buf)
+ expect(fileCache.size()).assertEqual(3)
+ console.info(Constants.TAG + fileCache.currentMemory + "")
+ fileCache.get("aaa")
+ await sleep(1000)
+ fileCache.put("ddd", buf)
+ await sleep(1000)
+ console.info(Constants.TAG + fileCache.currentMemory + "")
+ expect(fileCache.size()).assertEqual(3)
+ console.info(Constants.TAG + fileCache.get("aaa")?.byteLength)
+ expect(fileCache.get("ddd")?.byteLength).assertEqual(buf.byteLength)
+ await sleep(1000)
+ expect(fileCache.get("aaa")?.byteLength).assertEqual(buf.byteLength)
+ await sleep(1000)
+ expect(fileCache.get("ccc")?.byteLength).assertEqual(buf.byteLength)
+ await sleep(1000)
+ expect(fileCache.get("bbb")).assertUndefined()
+
+
+ // 模拟第二次启动后重新加载缓存
+ let fileCache2: FileCache = new FileCache(GlobalContext.getContext()
+ .getObject("context") as Context, 5, 3 * 1024 * 1024)
+ await fileCache2.initFileCache()
+ expect(fileCache2.size()).assertEqual(3)
+ expect(fileCache2.get("ddd")?.byteLength).assertEqual(buf.byteLength)
+ expect(fileCache2.get("aaa")?.byteLength).assertEqual(buf.byteLength)
+ expect(fileCache2.get("ccc")?.byteLength).assertEqual(buf.byteLength)
+
+ });
+
+ it('assertFileSizeAllInSubThread', 0, async () => {
+ const buf: ArrayBuffer = new ArrayBuffer(1024 * 1024);
+ console.info(Constants.TAG + buf.byteLength)
+ console.info(Constants.TAG + GlobalContext.getContext().getObject("cacheDir"))
+
+ let fileCache: FileCache = new FileCache(GlobalContext.getContext()
+ .getObject("context") as Context, 5, 3 * 1024 * 1024)
+ await fileCache.initFileCache()
+
+ fileCache.put("aaa", buf)
+
+ let task: taskpool.Task = new taskpool.Task(getFile, "bbb", GlobalContext.getContext()
+ .getObject("context") as Context);
+ let res: ArrayBuffer | undefined = await taskpool.execute(task) as (ArrayBuffer | undefined)
+ if (res !== undefined) {
+ fileCache.putWithoutWriteFile("bbb", res)
+ }
+
+ task = new taskpool.Task(getFile, "aaa", GlobalContext.getContext()
+ .getObject("context") as Context);
+ res = await taskpool.execute(task) as (ArrayBuffer | undefined)
+ if (res !== undefined) {
+ fileCache.putWithoutWriteFile("aaa", res)
+ }
+
+ fileCache.put("ddd", buf)
+ expect(fileCache.size()).assertEqual(3)
+ expect(fileCache.currentMemory).assertEqual(3 * 1024 * 1024)
+ expect(fileCache.get("bbb")?.byteLength).assertEqual(1024 * 1024)
+ expect(fileCache.get("aaa")?.byteLength).assertEqual(1024 * 1024)
+ expect(fileCache.get("ddd")?.byteLength).assertEqual(1024 * 1024)
+
+
+ task = new taskpool.Task(getFile, "eee", GlobalContext.getContext()
+ .getObject("context") as Context);
+
+ res = await taskpool.execute(task) as (ArrayBuffer | undefined)
+ if (res !== undefined) {
+ fileCache.putWithoutWriteFile("eee", res)
+ }
+ expect(fileCache.size()).assertEqual(3)
+ expect(fileCache.currentMemory).assertEqual(3 * 1024 * 1024)
+ expect(fileCache.get("bbb")).assertEqual(undefined)
+ expect(fileCache.get("aaa")?.byteLength).assertEqual(1024 * 1024)
+ expect(fileCache.get("ddd")?.byteLength).assertEqual(1024 * 1024)
+
+ });
+
+
+ it('test', 0, async () => {
+ const buf: ArrayBuffer = new ArrayBuffer(1024 * 1024);
+ console.info(Constants.TAG + buf.byteLength)
+ console.info(Constants.TAG + GlobalContext.getContext().getObject("cacheDir"))
+
+ let fileCache: FileCache = new FileCache(GlobalContext.getContext()
+ .getObject("context") as Context, 5, 3 * 1024 * 1024)
+ await fileCache.initFileCache()
+
+ console.info(Constants.TAG + JSON.stringify("xxxxx"))
+ fileCache.put(JSON.stringify("xxxxx"),buf)
+ expect(fileCache.get(JSON.stringify("xxxxx"))?.byteLength).assertEqual(1024 * 1024)
+ });
+ it('fileCacheEngineKey', 0, () => {
+ let engineKey: IEngineKey = new DefaultEngineKey()
+ let imageKnifeOption: ImageKnifeOption = {
+ loadSrc:"abc"
+ }
+ let imageKey = engineKey.generateFileKey(imageKnifeOption.loadSrc,"")
+ let imageAnimatorKey = engineKey.generateFileKey(imageKnifeOption.loadSrc,"",true)
+ expect(imageKey == imageAnimatorKey).assertFalse()
+ });
+ });
+}
+
+function sleep(ms: number): Promise {
+ return new Promise(resolve => setTimeout(resolve, ms));
+}
+
+@Concurrent
+async function getFile(key: string, context: Context): Promise {
+ // 读取文件缓存
+ let buf = FileCache.getFileCacheByFile(context, key)
+
+ if (buf !== undefined) {
+ return buf
+ }
+
+ buf = new ArrayBuffer(1024 * 1024)
+ // 写文件缓存
+ FileCache.saveFileCacheOnlyFile(context, key, buf)
+ return buf;
+}
\ No newline at end of file
diff --git a/entry/src/ohosTest/ets/test/List.test.ets b/entry/src/ohosTest/ets/test/List.test.ets
index ad551ac..8c04309 100644
--- a/entry/src/ohosTest/ets/test/List.test.ets
+++ b/entry/src/ohosTest/ets/test/List.test.ets
@@ -1,39 +1,29 @@
/*
- * Copyright (C) 2023 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the "License");
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
+ * distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import abilityTest from './Ability.test'
-import lruCacheTest from './lrucache.test'
-import CustomDataFetchClientTest from './customdatafetchclient.test'
-import LogUtilTest from './logutil.test'
-import Transfrom from './transfrom.test'
-import RequestOptionTest from './requestoption.test'
-import ImageKnifeTest from './imageknife.test'
-import DiskLruCacheTest from './diskLruCache.test'
-import SendableDataTest from './SendableData.test'
import DefaultJobQueueTest from './DefaultJobQueueTest.test';
-import ImageKnifeOptionTest from './imageknifeOption.test';
+import FileLruCacheTest from './FileLruCache.test';
+import ImageKnifeOptionTest from './ImageKnifeOption.test';
+import MemoryLruCacheTest from './MemoryLruCache.test';
+import ImageKnifeTest from './ImageKnife.test';
+import Transform from './transform.test';
export default function testsuite() {
- abilityTest()
- lruCacheTest()
- DiskLruCacheTest()
- LogUtilTest()
- Transfrom()
- RequestOptionTest()
- ImageKnifeTest();
- SendableDataTest();
+ MemoryLruCacheTest();
+ FileLruCacheTest();
DefaultJobQueueTest();
- CustomDataFetchClientTest();
ImageKnifeOptionTest();
+ ImageKnifeTest();
+ Transform();
}
\ No newline at end of file
diff --git a/entry/src/ohosTest/ets/test/MemoryLruCache.test.ets b/entry/src/ohosTest/ets/test/MemoryLruCache.test.ets
new file mode 100644
index 0000000..98f2514
--- /dev/null
+++ b/entry/src/ohosTest/ets/test/MemoryLruCache.test.ets
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
+import image from '@ohos.multimedia.image';
+
+
+import Constants from '../../../main/ets/common/Constants';
+import { MemoryLruCache } from '@ohos/imageknife/src/main/ets/utils/MemoryLruCache';
+import { ImageKnifeData } from '@ohos/imageknife/src/main/ets/model/ImageKnifeData';
+import { IEngineKey, ImageKnifeOption,ImageKnifeRequestSource } from '@ohos/imageknife';
+import { DefaultEngineKey } from '@ohos/imageknife/src/main/ets/key/DefaultEngineKey';
+
+
+export default function MemoryLruCacheTest() {
+
+ describe('MemoryLruCacheTest', () => {
+ // Defines a test suite. Two parameters are supported: test suite name and test suite function.
+ beforeAll(() => {
+ // Presets an action, which is performed only once before all test cases of the test suite start.
+ // This API supports only one parameter: preset action function.
+ });
+ beforeEach(() => {
+ // Presets an action, which is performed before each unit test case starts.
+ // The number of execution times is the same as the number of test cases defined by **it**.
+ // This API supports only one parameter: preset action function.
+ });
+ afterEach(() => {
+ // Presets a clear action, which is performed after each unit test case ends.
+ // The number of execution times is the same as the number of test cases defined by **it**.
+ // This API supports only one parameter: clear action function.
+ });
+ afterAll(() => {
+ // Presets a clear action, which is performed after all test cases of the test suite end.
+ // This API supports only one parameter: clear action function.
+ });
+
+ // 测试基础put,get以及size功能
+ it('assertBasicFunction', 0, async () => {
+ let memoryCache: MemoryLruCache = new MemoryLruCache(3, 3 * 1024 * 1024);
+
+ let data: ImageKnifeData = await getNewImageKnifeData(96)
+ memoryCache.put("aaa", data)
+ memoryCache.put("bbb", data)
+ memoryCache.put("ccc", data)
+ expect(memoryCache.size()).assertEqual(3)
+ expect(memoryCache.get("aaa")).assertEqual(data)
+ expect(memoryCache.get("bbb")).assertEqual(data)
+ expect(memoryCache.get("ccc")).assertEqual(data)
+ expect(memoryCache.size()).assertEqual(3)
+
+ memoryCache.remove("ccc")
+ memoryCache.remove("ddd")
+ expect(memoryCache.size()).assertEqual(2)
+
+ memoryCache.removeAll()
+ expect(memoryCache.size()).assertEqual(0)
+ });
+
+
+ // 测试内存缓存size的lru功能
+ it('assertSizeLruFuction', 0, async () => {
+ let memoryCache: MemoryLruCache = new MemoryLruCache(3, 3 * 1024 * 1024);
+
+ let data1: ImageKnifeData = await getNewImageKnifeData(96)
+ let data2: ImageKnifeData = await getNewImageKnifeData(106)
+ let data3: ImageKnifeData = await getNewImageKnifeData(116)
+ let data4: ImageKnifeData = await getNewImageKnifeData(126)
+ let data5: ImageKnifeData = await getNewImageKnifeData(136)
+ memoryCache.put("aaa", data1)
+ memoryCache.put("bbb", data2)
+ memoryCache.put("ccc", data3)
+ memoryCache.put("ddd", data4)
+
+ expect(memoryCache.get("aaa")).assertUndefined()
+ expect(memoryCache.get("bbb")).assertEqual(data2)
+ memoryCache.put("eee", data5)
+ expect(memoryCache.get("ccc")).assertUndefined()
+ expect(memoryCache.get("bbb")).assertEqual(data2)
+ expect(memoryCache.get("ddd")).assertEqual(data4)
+ expect(memoryCache.get("eee")).assertEqual(data5)
+ });
+
+ // 测试内存缓存memorySize的lru功能
+ it('assertMemorySizeLruFuction', 0, async () => {
+ let memoryCache: MemoryLruCache = new MemoryLruCache(3, 2 * 1024 * 1024);
+
+ const color: ArrayBuffer = new ArrayBuffer(1024 * 1024); //96为需要创建的像素buffer大小,取值为:height * width *4
+ let opts: image.InitializationOptions = {
+ editable: true, pixelFormat: 3, size: {
+ height: 512, width: 512
+ }
+ }
+ let pixelmap: PixelMap = await image.createPixelMap(color, opts)
+ console.info(Constants.TAG + pixelmap.getPixelBytesNumber())
+ let data: ImageKnifeData = {
+ source: pixelmap,
+ imageWidth: 5,
+ imageHeight: 5
+ }
+
+ memoryCache.put("aaa", data)
+ memoryCache.put("bbb", data)
+ memoryCache.get("aaa")
+ memoryCache.put("ccc", data)
+ expect(memoryCache.size()).assertEqual(2)
+ expect(memoryCache.get("bbb")).assertUndefined()
+ expect(memoryCache.get("aaa")).assertEqual(data)
+ expect(memoryCache.get("ccc")).assertEqual(data)
+ });
+
+ it('memoryCacheEngineKey', 0, () => {
+ let engineKey: IEngineKey = new DefaultEngineKey()
+ let imageKnifeOption: ImageKnifeOption = {
+ loadSrc:"abc"
+ }
+ let imageKey = engineKey.generateMemoryKey(imageKnifeOption.loadSrc,ImageKnifeRequestSource.SRC,imageKnifeOption)
+ let imageAnimatorKey = engineKey.generateMemoryKey(imageKnifeOption.loadSrc,ImageKnifeRequestSource.SRC,imageKnifeOption,true)
+ expect(imageKey == imageAnimatorKey).assertFalse()
+ });
+
+ });
+}
+
+async function getNewImageKnifeData(num:number): Promise {
+ const color: ArrayBuffer = new ArrayBuffer(num); //96为需要创建的像素buffer大小,取值为:height * width *4
+ let opts: image.InitializationOptions = {
+ editable: true, pixelFormat: 3, size: {
+ height: 4, width: 6
+ }
+ }
+ let pixelmap: PixelMap = await image.createPixelMap(color, opts)
+
+ let data: ImageKnifeData = {
+ source: pixelmap,
+ imageWidth: 5,
+ imageHeight: 5
+ }
+ return data
+}
\ No newline at end of file
diff --git a/entry/src/ohosTest/ets/test/imageknife.test.ets b/entry/src/ohosTest/ets/test/imageknife.test.ets
deleted file mode 100644
index 5f3b429..0000000
--- a/entry/src/ohosTest/ets/test/imageknife.test.ets
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2023 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import hilog from '@ohos.hilog';
-import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'
-import { DiskLruCache, ImageKnife,ImageKnifeDrawFactory,ImageKnifeGlobal,LruCache} from '@ohos/imageknife';
-import { common } from '@kit.AbilityKit';
-import { GlobalContext } from '../testability/GlobalContext';
-
-const BASE_COUNT: number = 2000;
-
-export default function ImageKnifeTest() {
- describe('ImageKnifeTest', ()=> {
- // Defines a test suite. Two parameters are supported: test suite name and test suite function.
- beforeAll( ()=> {
- // Presets an action, which is performed only once before all test cases of the test suite start.
- // This API supports only one parameter: preset action function.
- })
- beforeEach( ()=> {
- // Presets an action, which is performed before each unit test case starts.
- // The number of execution times is the same as the number of test cases defined by **it**.
- // This API supports only one parameter: preset action function.
- })
- afterEach( ()=> {
- // Presets a clear action, which is performed after each unit test case ends.
- // The number of execution times is the same as the number of test cases defined by **it**.
- // This API supports only one parameter: clear action function.
- })
- afterAll( ()=> {
- // Presets a clear action, which is performed after all test cases of the test suite end.
- // This API supports only one parameter: clear action function.
- })
-
-
- it('TestGlobalImageKnife',0, ()=> {
- let context:Object|undefined = ImageKnifeGlobal.getInstance().getHapContext();
- if(context != undefined) {
- let startTime = new Date().getTime();
- for (let index = 0; index < BASE_COUNT; index++) {
- ImageKnife.with(context);
- }
- endTime(startTime, 'TestGlobalImageKnife');
- let global: ImageKnifeGlobal = ImageKnife.with(context)
- expect(global.getImageKnife()).not().assertUndefined()
- }
- })
-
-
- it('TestGlobalDefaultLifeCycle',1, ()=> {
- let imageKnife:ImageKnife|undefined = ImageKnifeGlobal.getInstance().getImageKnife()
- if(imageKnife != undefined){
- let startTime = new Date().getTime();
- for (let index = 0; index < BASE_COUNT; index++) {
- imageKnife.setDefaultLifeCycle(ImageKnifeDrawFactory.createProgressLifeCycle("#10a5ff", 0.5))
- }
- endTime(startTime, 'setDefaultLifeCycle');
- let startTime1 = new Date().getTime();
- for (let index = 0; index < BASE_COUNT; index++) {
- imageKnife.getDefaultLifeCycle();
- }
- endTime(startTime1, 'getDefaultLifeCycle');
- imageKnife.setDefaultLifeCycle(ImageKnifeDrawFactory.createProgressLifeCycle("#10a5ff", 0.5))
- let globalLifeCycle = imageKnife.getDefaultLifeCycle();
- expect(globalLifeCycle).not().assertUndefined()
- }
-
- })
- it('TestRemoveAllMemoryCache',2,()=>{
- let imageKnife:ImageKnife|undefined = ImageKnifeGlobal.getInstance().getImageKnife();
- imageKnife?.removeAllMemoryCache();
- let a = imageKnife?.getMemoryCache();
- expect(a).assertEqual(undefined);
- })
- it('TestRemoveAllFileCache',3,()=>{
- let imageKnife:ImageKnife|undefined = ImageKnifeGlobal.getInstance().getImageKnife();
- imageKnife?.removeAllFileCache();
- let a = imageKnife?.getDiskMemoryCache();
- expect(a).assertEqual(undefined);
- })
- it('TestRemoveMemoryCache',4,()=>{
- let memoryCache = new LruCache(5);
- memoryCache.put("1","1");
- memoryCache.remove("1");
- let result = memoryCache.get("1");
- expect(result).assertEqual(undefined);
- })
- it('TestRemoveFileCache',5,()=>{
- let context: object | undefined = GlobalContext.getInstance().getObject("hapContext");
- let disLruCache: DiskLruCache = DiskLruCache.create(context as common.UIAbilityContext, 1024);
- disLruCache.set('test', "Hello World Simple Example.");
- disLruCache.deleteCacheDataByKey('test');
- let a = disLruCache.get('test');
- expect(a).assertEqual(undefined);
- })
- })
-}
-
-function endTime(startTime: number, tag: string) {
- let endTime: number = new Date().getTime();
- let averageTime = ((endTime - startTime) * 1000 / BASE_COUNT)
- console.info(tag + " startTime: " + endTime)
- console.info(tag + " endTime: " + endTime)
- console.log(tag + " averageTime: " + averageTime + "μs");
-}
\ No newline at end of file
diff --git a/entry/src/ohosTest/ets/test/transform.test.ets b/entry/src/ohosTest/ets/test/transform.test.ets
new file mode 100644
index 0000000..cec2fe7
--- /dev/null
+++ b/entry/src/ohosTest/ets/test/transform.test.ets
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'
+import {
+ BlurTransformation,
+ BrightnessTransformation,
+ CropCircleTransformation,
+ CropCircleWithBorderTransformation,
+ CropSquareTransformation,
+ CropTransformation,
+ GrayScaleTransformation,
+ InvertTransformation,
+ KuwaharaTransformation,
+ MaskTransformation,
+ PixelationTransformation,
+ SepiaTransformation,
+ SketchTransformation,
+ SwirlTransformation,
+ ToonTransformation,
+ VignetterTransformation
+} from '@ohos/imageknife'
+
+const BASE_COUNT: number = 1000;
+
+export default function Transform() {
+ describe('Transform', () => {
+ // Defines a test suite. Two parameters are supported: test suite name and test suite function.
+ beforeAll(() => {
+ // Presets an action, which is performed only once before all test cases of the test suite start.
+ // This API supports only one parameter: preset action function.
+
+ })
+ beforeEach(() => {
+ // Presets an action, which is performed before each unit test case starts.
+ // The number of execution times is the same as the number of test cases defined by **it**.
+ // This API supports only one parameter: preset action function.
+ })
+ afterEach(() => {
+ // Presets a clear action, which is performed after each unit test case ends.
+ // The number of execution times is the same as the number of test cases defined by **it**.
+ // This API supports only one parameter: clear action function.
+ })
+ afterAll(() => {
+ // Presets a clear action, which is performed after all test cases of the test suite end.
+ // This API supports only one parameter: clear action function.
+ })
+ it('TestBlurTransformation', 0, () => {
+ let startTime = new Date().getTime();
+ for (let index = 0; index < BASE_COUNT; index++) {
+ new BlurTransformation(15);
+ }
+ endTime(startTime, 'TestBlurTransformation');
+ let blur = new BlurTransformation(15);
+ expect(blur.getName()).assertEqual('BlurTransformation;radius:15');
+ })
+ it('TestBrightnessTransformation', 1, () => {
+ let startTime = new Date().getTime();
+ for (let index = 0; index < BASE_COUNT; index++) {
+ new BrightnessTransformation(20);
+ }
+ endTime(startTime, 'BrightnessTransformation');
+ let bright = new BrightnessTransformation(20);
+ expect(bright.getName()).assertEqual("BrightnessTransformation;bright:20");
+ })
+ it('TestCropCircleTransformation', 3, () => {
+ let startTime = new Date().getTime();
+ for (let index = 0; index < BASE_COUNT; index++) {
+ new CropCircleTransformation();
+ }
+ endTime(startTime, 'TestCropCircleTransformation');
+ let cropCircle = new CropCircleTransformation();
+ expect(cropCircle.getName()).assertContain("CropCircleTransformation");
+ expect(cropCircle.getName()).assertContain(";mCenterX:");
+ expect(cropCircle.getName()).assertContain(";mCenterY:");
+ expect(cropCircle.getName()).assertContain(";mRadius:");
+ })
+ it('TestCropCircleWithBorderTransformation', 4, () => {
+ let startTime = new Date().getTime();
+ for (let index = 0; index < BASE_COUNT; index++) {
+ new CropCircleWithBorderTransformation(10, {
+ r_color: 100, g_color: 100, b_color: 100
+ });
+ }
+ endTime(startTime, 'TestCropCircleWithBorderTransformation');
+ let CropCircleWithBorder = new CropCircleWithBorderTransformation(10, {
+ r_color: 100, g_color: 100, b_color: 100
+ });
+ expect(CropCircleWithBorder.getName()).assertContain("CropCircleWithBorderTransformation")
+ expect(CropCircleWithBorder.getName()).assertContain(";mCenterX:");
+ expect(CropCircleWithBorder.getName()).assertContain(";mCenterY:");
+ expect(CropCircleWithBorder.getName()).assertContain(";mRadius:");
+ expect(CropCircleWithBorder.getName()).assertContain(";mBorderSize:");
+ expect(CropCircleWithBorder.getName()).assertContain(";mRColor:");
+ expect(CropCircleWithBorder.getName()).assertContain(";mGColor:");
+ expect(CropCircleWithBorder.getName()).assertContain(";mBColor:");
+ })
+ it('TestCropSquareTransformation', 5, () => {
+ let startTime = new Date().getTime();
+ for (let index = 0; index < BASE_COUNT; index++) {
+ new CropSquareTransformation();
+ }
+ endTime(startTime, 'TestCropSquareTransformation');
+ let CropSquare = new CropSquareTransformation();
+ expect(CropSquare.getName()).assertContain("CropSquareTransformation");
+ })
+ it('TestCropTransformation', 6, () => {
+ let startTime = new Date().getTime();
+ for (let index = 0; index < BASE_COUNT; index++) {
+ new CropTransformation(10, 10, 1);
+ }
+ endTime(startTime, 'TestCropTransformation');
+ let crop = new CropTransformation(10, 10, 1);
+ expect(crop.getName()).assertContain("CropTransformation" + ";mWidth:10" + ";mHeight:10" + ";mCropType:1")
+ })
+ it('TestGrayScaleTransformation', 7, () => {
+ let startTime = new Date().getTime();
+ for (let index = 0; index < BASE_COUNT; index++) {
+ new GrayScaleTransformation();
+ }
+ endTime(startTime, 'GrayScaleTransformation');
+ let grayscale = new GrayScaleTransformation();
+ expect(grayscale.getName()).assertContain("GrayScaleTransformation")
+ })
+ it('TestInvertTransformation', 8, () => {
+ let startTime = new Date().getTime();
+ for (let index = 0; index < BASE_COUNT; index++) {
+ new InvertTransformation();
+ }
+ endTime(startTime, 'TestInvertFilterTransformation');
+ let invert = new InvertTransformation();
+ expect(invert.getName()).assertContain("InvertTransformation");
+ })
+ it('TestPixelationTransformation', 9, () => {
+ let startTime = new Date().getTime();
+ for (let index = 0; index < BASE_COUNT; index++) {
+ new PixelationTransformation();
+ }
+ endTime(startTime, 'TestPixelationTransformation');
+ let pixelation = new PixelationTransformation();
+ expect(pixelation.getName()).assertContain("PixelationTransformation");
+ })
+ it('TestSepiaTransformation', 12, () => {
+ let startTime = new Date().getTime();
+ for (let index = 0; index < BASE_COUNT; index++) {
+ new SepiaTransformation();
+ }
+ endTime(startTime, 'SepiaTransformation');
+ let speia = new SepiaTransformation();
+ expect(speia.getName()).assertContain("SepiaTransformation");
+ })
+ it('TestSketchTransformation', 13, () => {
+ let startTime = new Date().getTime();
+ for (let index = 0; index < BASE_COUNT; index++) {
+ new SketchTransformation();
+ }
+ endTime(startTime, 'TestSketchTransformation');
+ let Sketch = new SketchTransformation();
+ expect(Sketch.getName()).assertContain("SketchTransformation");
+ })
+ it('TestMaskTransformation', 14, () => {
+ let startTime = new Date().getTime();
+ for (let index = 0; index < BASE_COUNT; index++) {
+ new MaskTransformation($r('app.media.icon'));
+ }
+ endTime(startTime, 'TestMaskTransformation');
+ let mask = new MaskTransformation($r('app.media.icon'));
+ expect(mask.getName()).assertContain("MaskTransformation");
+ })
+ it('TestSwirlTransformation', 15, () => {
+ let startTime = new Date().getTime();
+ for (let index = 0; index < BASE_COUNT; index++) {
+ new SwirlTransformation(10, 180, [10, 10]);
+ }
+ endTime(startTime, 'TestSwirlTransformation');
+ let swirl = new SwirlTransformation(10, 180, [10, 10]);
+ expect(swirl.getName()).assertContain("SwirlTransformation");
+ })
+ it('TestKuwaharaTransformation', 16, () => {
+ let startTime = new Date().getTime();
+ for (let index = 0; index < BASE_COUNT; index++) {
+ new KuwaharaTransformation(10);
+ }
+ endTime(startTime, 'TestKuwaharaTransformation');
+ let kuwahara = new KuwaharaTransformation(10);
+ expect(kuwahara.getName()).assertContain("KuwaharaTransformation;radius:10");
+ })
+ it('TestToonTransformation', 17, () => {
+ let startTime = new Date().getTime();
+ for (let index = 0; index < BASE_COUNT; index++) {
+ new ToonTransformation(10);
+ }
+ endTime(startTime, 'TestToonTransformation');
+ let toon = new ToonTransformation(10);
+ expect(toon.getName()).assertContain("ToonTransformation;threshold:10");
+ })
+ it('TestVignetterTransformation', 18, () => {
+ let startTime = new Date().getTime();
+ for (let index = 0; index < BASE_COUNT; index++) {
+ new VignetterTransformation([0.5, 0.5], [0.0, 0.0, 0.0], [0.3, 0.75]);
+ }
+ endTime(startTime, 'TestVignetterTransformation');
+ let vignette = new VignetterTransformation([0.5, 0.5], [0.0, 0.0, 0.0], [0.3, 0.75]);
+ expect(vignette.getName()).assertContain("VignetterTransformation");
+ })
+ })
+}
+
+function endTime(startTime: number, tag: string) {
+ let endTime: number = new Date().getTime();
+ let averageTime = ((endTime - startTime) * 1000 / BASE_COUNT);
+ console.info(tag + " startTime: " + endTime);
+ console.info(tag + " endTime: " + endTime);
+ console.log(tag + " averageTime: " + averageTime + "μs");
+}
\ No newline at end of file
diff --git a/entry/src/ohosTest/ets/testability/TestAbility.ets b/entry/src/ohosTest/ets/testability/TestAbility.ets
index 1860105..628af01 100644
--- a/entry/src/ohosTest/ets/testability/TestAbility.ets
+++ b/entry/src/ohosTest/ets/testability/TestAbility.ets
@@ -1,13 +1,13 @@
/*
- * Copyright (C) 2023 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the "License");
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
+ * distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
@@ -18,46 +18,50 @@ import hilog from '@ohos.hilog';
import { Hypium } from '@ohos/hypium';
import testsuite from '../test/List.test';
import window from '@ohos.window';
-import {ImageKnife,ImageKnifeDrawFactory,ImageKnifeGlobal} from '@ohos/libraryimageknife'
-import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import Want from '@ohos.app.ability.Want';
-import { BusinessError } from '@ohos.base'
-import {GlobalContext } from './GlobalContext'
+import AbilityConstant from '@ohos.app.ability.AbilityConstant';
+import { GlobalContext } from '../../../main/ets/common/GlobalContext';
+
export default class TestAbility extends UIAbility {
- onCreate(want: Want, param: AbilityConstant.LaunchParam) {
+ onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onCreate');
+ hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? '');
+ hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:' + JSON.stringify(launchParam) ?? '');
+ let abilityDelegator: AbilityDelegatorRegistry.AbilityDelegator;
+ abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator();
+ let abilityDelegatorArguments: AbilityDelegatorRegistry.AbilityDelegatorArgs;
+ abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments();
+ hilog.info(0x0000, 'testTag', '%{public}s', 'start run testcase!!!');
+ Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite);
+ }
- let abilityDelegator: AbilityDelegatorRegistry.AbilityDelegator
- abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
- let abilityDelegatorArguments: AbilityDelegatorRegistry.AbilityDelegatorArgs
- abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments()
+ onDestroy() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onDestroy');
+ }
- Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite)
- // 初始化xts的ImageKnife
- ImageKnife.with(this.context.createModuleContext("entry_test"));
- GlobalContext.getInstance().setObject("hapContext",this.context.createModuleContext("entry_test"));
- GlobalContext.getInstance().setObject("filesDir",this.context.createModuleContext("entry_test").filesDir);
- }
+ onWindowStageCreate(windowStage: window.WindowStage) {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageCreate');
+ GlobalContext.getContext().setObject("context", this.context);
+ GlobalContext.getContext().setObject("cacheDir", this.context.cacheDir);
+ windowStage.loadContent('testability/pages/Index', (err, data) => {
+ if (err.code) {
+ hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
+ return;
+ }
+ hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s',
+ JSON.stringify(data) ?? '');
+ });
+ }
- onDestroy() {
+ onWindowStageDestroy() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageDestroy');
+ }
- }
+ onForeground() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onForeground');
+ }
- onWindowStageCreate(windowStage: window.WindowStage) {
-
- windowStage.loadContent('testability/pages/Index', (err:BusinessError, data:void) => {
-
- });
- }
-
- onWindowStageDestroy() {
- hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageDestroy');
- }
-
- onForeground() {
- hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onForeground');
- }
-
- onBackground() {
- hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onBackground');
- }
+ onBackground() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onBackground');
+ }
}
\ No newline at end of file
diff --git a/entry/src/ohosTest/ets/testability/pages/Index.ets b/entry/src/ohosTest/ets/testability/pages/Index.ets
index 19ddc97..98be4ce 100644
--- a/entry/src/ohosTest/ets/testability/pages/Index.ets
+++ b/entry/src/ohosTest/ets/testability/pages/Index.ets
@@ -1,50 +1,31 @@
/*
- * Copyright (C) 2023 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the "License");
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
+ * distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import hilog from '@ohos.hilog';
-
@Entry
@Component
struct Index {
+ @State message: string = 'Hello World';
- aboutToAppear() {
- hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility index aboutToAppear');
+ build() {
+ Row() {
+ Column() {
+ Text(this.message)
+ .fontSize(50)
+ .fontWeight(FontWeight.Bold)
+ }
+ .width('100%')
+ }
+ .height('100%')
}
-
- @State message: string = 'Hello World'
- build() {
- Row() {
- Column() {
- Text(this.message)
- .fontSize(50)
- .fontWeight(FontWeight.Bold)
- Button() {
- Text('next page')
- .fontSize(20)
- .fontWeight(FontWeight.Bold)
- }.type(ButtonType.Capsule)
- .margin({
- top: 20
- })
- .backgroundColor('#0D9FFB')
- .width('35%')
- .height('5%')
- .onClick(()=>{
- })
- }
- .width('100%')
- }
- .height('100%')
- }
- }
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ets b/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ets
new file mode 100644
index 0000000..9d981ea
--- /dev/null
+++ b/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ets
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import hilog from '@ohos.hilog';
+import TestRunner from '@ohos.application.testRunner';
+import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';
+import Want from '@ohos.app.ability.Want';
+
+let abilityDelegator: AbilityDelegatorRegistry.AbilityDelegator | undefined = undefined
+let abilityDelegatorArguments: AbilityDelegatorRegistry.AbilityDelegatorArgs | undefined = undefined
+
+async function onAbilityCreateCallback() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'onAbilityCreateCallback');
+}
+
+async function addAbilityMonitorCallback(err : Error) {
+ hilog.info(0x0000, 'testTag', 'addAbilityMonitorCallback : %{public}s', JSON.stringify(err) ?? '');
+}
+
+export default class OpenHarmonyTestRunner implements TestRunner {
+ constructor() {
+ }
+
+ onPrepare() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner OnPrepare ');
+ }
+
+ async onRun() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun run');
+ abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments()
+ abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
+ const bundleName = abilityDelegatorArguments.bundleName;
+ const testAbilityName = 'TestAbility';
+ const moduleName = abilityDelegatorArguments.parameters['-m'];
+ let lMonitor: AbilityDelegatorRegistry.AbilityMonitor = {
+ abilityName: testAbilityName,
+ onAbilityCreate: onAbilityCreateCallback,
+ moduleName: moduleName
+ };
+ abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback)
+ const want: Want = {
+ bundleName: bundleName,
+ abilityName: testAbilityName,
+ moduleName: moduleName
+ };
+ abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator();
+ abilityDelegator.startAbility(want, (err, data) => {
+ hilog.info(0x0000, 'testTag', 'startAbility : err : %{public}s', JSON.stringify(err) ?? '');
+ hilog.info(0x0000, 'testTag', 'startAbility : data : %{public}s',JSON.stringify(data) ?? '');
+ })
+ hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun end');
+ }
+}
\ No newline at end of file
diff --git a/entry/src/ohosTest/module.json5 b/entry/src/ohosTest/module.json5
index 3054622..d0dc5d5 100644
--- a/entry/src/ohosTest/module.json5
+++ b/entry/src/ohosTest/module.json5
@@ -2,19 +2,16 @@
"module": {
"name": "entry_test",
"type": "feature",
- "srcEntry": "./ets/testability/TestAbility.ets",
"description": "$string:module_test_desc",
"mainElement": "TestAbility",
- "deviceTypes": ["default"],
+ "deviceTypes": [
+ "phone",
+ "tablet",
+ "2in1"
+ ],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:test_pages",
- "metadata": [
- {
- "name": "ArkTSPartialUpdate",
- "value": "true"
- }
- ],
"abilities": [
{
"name": "TestAbility",
diff --git a/entry/src/ohosTest/resources/base/media/icon.png b/entry/src/ohosTest/resources/base/media/icon.png
index ce307a8..cd45acc 100644
Binary files a/entry/src/ohosTest/resources/base/media/icon.png and b/entry/src/ohosTest/resources/base/media/icon.png differ
diff --git a/entry/src/ohosTest/resources/base/profile/test_pages.json b/entry/src/ohosTest/resources/base/profile/test_pages.json
index 2d90c65..b7e7343 100644
--- a/entry/src/ohosTest/resources/base/profile/test_pages.json
+++ b/entry/src/ohosTest/resources/base/profile/test_pages.json
@@ -1,5 +1,5 @@
{
"src": [
- "testability/pages/index"
+ "testability/pages/Index"
]
}
diff --git a/library/.gitignore b/library/.gitignore
index f8fba9f..e2713a2 100644
--- a/library/.gitignore
+++ b/library/.gitignore
@@ -1,5 +1,6 @@
/node_modules
+/oh_modules
/.preview
/build
-/oh_modules/
-/oh-package-lock.json5
+/.cxx
+/.test
\ No newline at end of file
diff --git a/library/BuildProfile.ets b/library/BuildProfile.ets
new file mode 100644
index 0000000..774bfdf
--- /dev/null
+++ b/library/BuildProfile.ets
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+export default class BuildProfile {
+ static readonly HAR_VERSION = '3.0.0-rc.0';
+ static readonly BUILD_MODE_NAME = 'debug';
+ static readonly DEBUG = true;
+}
\ No newline at end of file
diff --git a/library/build-profile.json5 b/library/build-profile.json5
index 35dff6d..312d38e 100644
--- a/library/build-profile.json5
+++ b/library/build-profile.json5
@@ -1,5 +1,28 @@
{
"apiType": "stageMode",
"buildOption": {
- }
+ },
+ "buildOptionSet": [
+ {
+ "name": "release",
+ "arkOptions": {
+ "obfuscation": {
+ "ruleOptions": {
+ "enable": true,
+ "files": [
+ "./obfuscation-rules.txt"
+ ]
+ },
+ "consumerFiles": [
+ "./consumer-rules.txt"
+ ]
+ }
+ },
+ },
+ ],
+ "targets": [
+ {
+ "name": "default"
+ }
+ ]
}
diff --git a/library/consumer-rules.txt b/library/consumer-rules.txt
new file mode 100644
index 0000000..e69de29
diff --git a/library/hvigorfile.ts b/library/hvigorfile.ts
index 42ed4b4..4218707 100644
--- a/library/hvigorfile.ts
+++ b/library/hvigorfile.ts
@@ -1,3 +1,6 @@
-// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently.
-module.exports = require('@ohos/hvigor-ohos-plugin').harTasks
+import { harTasks } from '@ohos/hvigor-ohos-plugin';
+export default {
+ system: harTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
+ plugins:[] /* Custom plugin to extend the functionality of Hvigor. */
+}
diff --git a/library/index.ets b/library/index.ets
index ca65555..4576c08 100644
--- a/library/index.ets
+++ b/library/index.ets
@@ -1,10 +1,10 @@
/*
- * Copyright (C) 2021 Huawei Device Co., Ltd.
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http:// www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS,
@@ -12,127 +12,58 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+export { ImageKnifeComponent } from './src/main/ets/components/ImageKnifeComponent'
+export { ImageKnifeAnimatorComponent } from './src/main/ets/components/ImageKnifeAnimatorComponent'
-/**
- * cache
- */
+export { ImageKnife } from './src/main/ets/ImageKnife'
-export { FileUtils } from './src/main/ets/components/cache/FileUtils'
-export { Base64 } from './src/main/ets/components/cache/Base64'
-export { LruCache } from './src/main/ets/components/cache/LruCache'
-export { DiskLruCache } from './src/main/ets/components/cache/DiskLruCache'
-export { DiskCacheEntry } from './src/main/ets/components/cache/DiskCacheEntry'
-export { DiskStrategy } from './src/main/ets/components/cache/diskstrategy/DiskStrategy'
-export { ALL } from './src/main/ets/components/cache/diskstrategy/enum/ALL'
-export { AUTOMATIC } from './src/main/ets/components/cache/diskstrategy/enum/AUTOMATIC'
-export { DATA } from './src/main/ets/components/cache/diskstrategy/enum/DATA'
-export { NONE } from './src/main/ets/components/cache/diskstrategy/enum/NONE'
-export { RESOURCE } from './src/main/ets/components/cache/diskstrategy/enum/RESOURCE'
-export { EngineKeyInterface } from './src/main/ets/components/cache/key/EngineKeyInterface'
-export { EngineKeyFactories } from './src/main/ets/components/cache/key/EngineKeyFactories'
-export { DataFetchResult } from './src/main/ets/components/imageknife/networkmanage/DataFetchResult'
+export { ImageKnifeOption , AnimatorOption } from './src/main/ets/ImageKnifeOption'
-/**
- * compress
- */
-export { CompressBuilder } from './src/main/ets/components/imageknife/compress/CompressBuilder'
-export { OnCompressListener } from './src/main/ets/components/imageknife/compress/listener/OnCompressListener'
-export { OnRenameListener } from './src/main/ets/components/imageknife/compress/listener/OnRenameListener'
-export { CompressDataListener } from './src/main/ets/components/imageknife/compress/listener/CompressDataListener'
-export { CompressionPredicate } from './src/main/ets/components/imageknife/compress/listener/CompressionPredicate'
-export { CompressAdapter } from './src/main/ets/components/imageknife/compress/provider/CompressAdapter'
-export { CompressProvider } from './src/main/ets/components/imageknife/compress/provider/CompressProvider'
-export { DataStringPathProvider } from './src/main/ets/components/imageknife/compress/provider/DataStringPathProvider'
-export { RecourseProvider } from './src/main/ets/components/imageknife/compress/provider/RecourseProvider'
+export { ImageKnifeRequest } from './src/main/ets/ImageKnifeRequest'
-/**
- * crop
- */
+export { FileUtils } from './src/main/ets/utils/FileUtils'
-export { CropImage } from './src/main/ets/components/imageknife/crop/CropImage'
-export { CropOptions } from './src/main/ets/components/imageknife/crop/CropOptions'
-export { PixelMapCrop,Options } from './src/main/ets/components/imageknife/crop/PixelMapCrop'
-export { CropCallback } from './src/main/ets/components/imageknife/crop/CropCallback'
+export { LogUtil } from './src/main/ets/utils/LogUtil'
-/**
- * transform
- */
-export { BaseTransform } from './src/main/ets/components/imageknife/transform/BaseTransform'
-export { BlurTransformation } from './src/main/ets/components/imageknife/transform/BlurTransformation'
-export { BrightnessFilterTransformation } from './src/main/ets/components/imageknife/transform/BrightnessFilterTransformation'
-export { ContrastFilterTransformation } from './src/main/ets/components/imageknife/transform/ContrastFilterTransformation'
-export { CropCircleTransformation } from './src/main/ets/components/imageknife/transform/CropCircleTransformation'
-export { CropCircleWithBorderTransformation,rgbColor } from './src/main/ets/components/imageknife/transform/CropCircleWithBorderTransformation'
-export { CropSquareTransformation } from './src/main/ets/components/imageknife/transform/CropSquareTransformation'
-export { CropTransformation,CropType } from './src/main/ets/components/imageknife/transform/CropTransformation'
-export { GrayscaleTransformation } from './src/main/ets/components/imageknife/transform/GrayscaleTransformation'
-export { InvertFilterTransformation } from './src/main/ets/components/imageknife/transform/InvertFilterTransformation'
-export { PixelationFilterTransformation } from './src/main/ets/components/imageknife/transform/PixelationFilterTransformation'
-export { RotateImageTransformation } from './src/main/ets/components/imageknife/transform/RotateImageTransformation'
-export { RoundedCornersTransformation,RoundCorner } from './src/main/ets/components/imageknife/transform/RoundedCornersTransformation'
-export { SepiaFilterTransformation } from './src/main/ets/components/imageknife/transform/SepiaFilterTransformation'
-export { SketchFilterTransformation } from './src/main/ets/components/imageknife/transform/SketchFilterTransformation'
-export { MaskTransformation } from './src/main/ets/components/imageknife/transform/MaskTransformation'
-export { SwirlFilterTransformation } from './src/main/ets/components/imageknife/transform/SwirlFilterTransformation'
-export { KuwaharaFilterTransform } from './src/main/ets/components/imageknife/transform/KuwaharaFilterTransform'
-export { ToonFilterTransform } from './src/main/ets/components/imageknife/transform/ToonFilterTransform'
-export { VignetteFilterTransform } from './src/main/ets/components/imageknife/transform/VignetteFilterTransform'
-export { TransformUtils } from './src/main/ets/components/imageknife/transform/TransformUtils'
-export { TransformType } from './src/main/ets/components/imageknife/transform/TransformType'
-export { CenterCrop } from './src/main/ets/components/imageknife/transform/pixelmap/CenterCrop'
-export { CenterInside } from './src/main/ets/components/imageknife/transform/pixelmap/CenterInside'
-export { FitCenter } from './src/main/ets/components/imageknife/transform/pixelmap/FitCenter'
+export { IEngineKey } from './src/main/ets/key/IEngineKey'
-/**
- * pngj
- */
-export { Pngj } from './src/main/ets/components/imageknife/pngj/Pngj'
-export {handler} from './PngWork'
-export { UPNG } from './src/main/ets/components/3rd_party/upng/UPNG'
+export { ImageKnifeData , CacheStrategy , ImageKnifeRequestSource} from "./src/main/ets/model/ImageKnifeData"
+export { PixelMapTransformation } from './src/main/ets/transform/PixelMapTransformation'
+export { MultiTransTransformation } from './src/main/ets/transform/MultiTransTransformation'
-/**
- * ImageKnife
- */
-export { ImageKnife } from './src/main/ets/components/imageknife/ImageKnife'
-export { ImageKnifeGlobal } from './src/main/ets/components/imageknife/ImageKnifeGlobal'
-export { ObjectKey } from './src/main/ets/components/imageknife/ObjectKey'
-export {RequestOption,Size,DetachFromLayout,CacheType} from './src/main/ets/components/imageknife/RequestOption'
-export { ImageKnifeComponent, ScaleType, ScaleTypeHelper, AntiAliasing} from './src/main/ets/components/imageknife/ImageKnifeComponent'
-export { ImageKnifeDrawFactory } from './src/main/ets/components/imageknife/ImageKnifeDrawFactory'
-export {ImageKnifeOption,CropCircleWithBorder,Crop,GifOptions,TransformOptions,HeaderOptions} from './src/main/ets/components/imageknife/ImageKnifeOption'
-export { ImageKnifeData } from './src/main/ets/components/imageknife/ImageKnifeData'
-export {IAllCacheInfoCallback,AllCacheInfo,ResourceCacheInfo,MemoryCacheInfo,DataCacheInfo} from './src/main/ets/components/imageknife/interface/IAllCacheInfoCallback'
-export {IParseImage} from './src/main/ets/components/imageknife/interface/IParseImage'
-export {IDataFetch} from './src/main/ets/components/imageknife/networkmanage/IDataFetch'
-export {ICache} from './src/main/ets/components/imageknife/requestmanage/ICache'
-export { FileTypeUtil } from './src/main/ets/components/imageknife/utils/FileTypeUtil'
-export { ParseImageUtil } from './src/main/ets/components/imageknife/utils/ParseImageUtil'
-export { DownloadClient } from './src/main/ets/components/imageknife/networkmanage/DownloadClient';
-export { CustomDataFetchClient } from './src/main/ets/components/imageknife/networkmanage/CustomDataFetchClient';
+export { BrightnessTransformation } from './src/main/ets/transform/BrightnessTransformation'
-/**
- * svg parse
- */
-export { SVGParseImpl } from './src/main/ets/components/imageknife/utils/svg/SVGParseImpl'
+export { BlurTransformation } from './src/main/ets/transform/BlurTransformation'
-/**
- * gif parse
- */
-export { GIFParseImpl } from './src/main/ets/components/imageknife/utils/gif/GIFParseImpl'
-export { GIFFrame } from './src/main/ets/components/imageknife/utils/gif/GIFFrame'
+export { SparkMD5 } from "./src/main/ets/3rd_party/sparkmd5/spark-md5"
+export { GrayScaleTransformation } from './src/main/ets/transform/GrayScaleTransformation'
-// 自定义组件新增
-// 自定义组件绘制生命周期
-export { IDrawLifeCycle } from './src/main/ets/components/imageknife/interface/IDrawLifeCycle'
+export { InvertTransformation } from './src/main/ets/transform/InvertTransformation'
-// 日志管理
-export { LogUtil } from './src/main/ets/components/imageknife/utils/LogUtil'
+export { ToonTransformation } from './src/main/ets/transform/ToonTransformation'
-/*下采样*/
-export {Downsampler} from './src/main/ets/components/imageknife/downsampling/Downsampler'
+export { CropCircleTransformation } from './src/main/ets/transform/CropCircleTransformation'
-export {DownsampleNone as sampleNone, FitCenter as fitter} from './src/main/ets/components/imageknife/downsampling/DownsampleStartegy'
\ No newline at end of file
+export { CropCircleWithBorderTransformation } from './src/main/ets/transform/CropCircleWithBorderTransformation'
+
+export { KuwaharaTransformation } from './src/main/ets/transform/KuwaharaTransformation'
+
+export { PixelationTransformation } from './src/main/ets/transform/PixelationTransformation'
+
+export { SketchTransformation } from './src/main/ets/transform/SketchTransformation'
+
+export { SwirlTransformation } from './src/main/ets/transform/SwirlTransformation'
+
+export { VignetterTransformation } from './src/main/ets/transform/VignetterTransformation'
+
+export { CropSquareTransformation } from './src/main/ets/transform/CropSquareTransformation'
+
+export { CropTransformation } from './src/main/ets/transform/CropTransformation'
+
+export { MaskTransformation } from './src/main/ets/transform/MaskTransformation'
+
+export { SepiaTransformation } from './src/main/ets/transform/SepiaTransformation'
\ No newline at end of file
diff --git a/library/obfuscation-rules.txt b/library/obfuscation-rules.txt
new file mode 100644
index 0000000..985b2ae
--- /dev/null
+++ b/library/obfuscation-rules.txt
@@ -0,0 +1,18 @@
+# Define project specific obfuscation rules here.
+# You can include the obfuscation configuration files in the current module's build-profile.json5.
+#
+# For more details, see
+# https://gitee.com/openharmony/arkcompiler_ets_frontend/blob/master/arkguard/README.md
+
+# Obfuscation options:
+# -disable-obfuscation: disable all obfuscations
+# -enable-property-obfuscation: obfuscate the property names
+# -enable-toplevel-obfuscation: obfuscate the names in the global scope
+# -compact: remove unnecessary blank spaces and all line feeds
+# -remove-log: remove all console.* statements
+# -print-namecache: print the name cache that contains the mapping from the old names to new names
+# -apply-namecache: reuse the given cache file
+
+# Keep options:
+# -keep-property-name: specifies property names that you want to keep
+# -keep-global-name: specifies names that you want to keep in the global scope
\ No newline at end of file
diff --git a/library/oh-package.json5 b/library/oh-package.json5
index dce5ea3..67af1d6 100644
--- a/library/oh-package.json5
+++ b/library/oh-package.json5
@@ -14,10 +14,9 @@
"main": "index.ets",
"repository": "https://gitee.com/openharmony-tpc/ImageKnife",
"type": "module",
- "version": "2.3.0-rc.2",
+ "version": "3.0.1-rc.2",
"dependencies": {
- "pako": "^2.1.0",
- "@ohos/gpu_transform": "^1.0.0"
+ "@ohos/gpu_transform": "^1.0.2"
},
"tags": [
"ImageCache",
diff --git a/library/src/main/ets/3rd_party/sparkmd5/spark-md5.js b/library/src/main/ets/3rd_party/sparkmd5/spark-md5.js
new file mode 100644
index 0000000..9a3e5d0
--- /dev/null
+++ b/library/src/main/ets/3rd_party/sparkmd5/spark-md5.js
@@ -0,0 +1,741 @@
+import buffer from '@ohos.buffer';
+
+ /*
+ * Fastest md5 implementation around (JKM md5).
+ * Credits: Joseph Myers
+ *
+ * @see http://www.myersdaily.org/joseph/javascript/md5-text.html
+ * @see http://jsperf.com/md5-shootout/7
+ */
+
+ /* this function is much faster,
+ so if possible we use it. Some IEs
+ are the only ones I know of that
+ need the idiotic second function,
+ generated by an if clause. */
+ var add32 = function (a, b) {
+ return (a + b) & 0xFFFFFFFF;
+ },
+ hex_chr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
+
+
+ function cmn(q, a, b, x, s, t) {
+ a = add32(add32(a, q), add32(x, t));
+ return add32((a << s) | (a >>> (32 - s)), b);
+ }
+
+ function md5cycle(x, k) {
+ var a = x[0],
+ b = x[1],
+ c = x[2],
+ d = x[3];
+
+ a += (b & c | ~b & d) + k[0] - 680876936 | 0;
+ a = (a << 7 | a >>> 25) + b | 0;
+ d += (a & b | ~a & c) + k[1] - 389564586 | 0;
+ d = (d << 12 | d >>> 20) + a | 0;
+ c += (d & a | ~d & b) + k[2] + 606105819 | 0;
+ c = (c << 17 | c >>> 15) + d | 0;
+ b += (c & d | ~c & a) + k[3] - 1044525330 | 0;
+ b = (b << 22 | b >>> 10) + c | 0;
+ a += (b & c | ~b & d) + k[4] - 176418897 | 0;
+ a = (a << 7 | a >>> 25) + b | 0;
+ d += (a & b | ~a & c) + k[5] + 1200080426 | 0;
+ d = (d << 12 | d >>> 20) + a | 0;
+ c += (d & a | ~d & b) + k[6] - 1473231341 | 0;
+ c = (c << 17 | c >>> 15) + d | 0;
+ b += (c & d | ~c & a) + k[7] - 45705983 | 0;
+ b = (b << 22 | b >>> 10) + c | 0;
+ a += (b & c | ~b & d) + k[8] + 1770035416 | 0;
+ a = (a << 7 | a >>> 25) + b | 0;
+ d += (a & b | ~a & c) + k[9] - 1958414417 | 0;
+ d = (d << 12 | d >>> 20) + a | 0;
+ c += (d & a | ~d & b) + k[10] - 42063 | 0;
+ c = (c << 17 | c >>> 15) + d | 0;
+ b += (c & d | ~c & a) + k[11] - 1990404162 | 0;
+ b = (b << 22 | b >>> 10) + c | 0;
+ a += (b & c | ~b & d) + k[12] + 1804603682 | 0;
+ a = (a << 7 | a >>> 25) + b | 0;
+ d += (a & b | ~a & c) + k[13] - 40341101 | 0;
+ d = (d << 12 | d >>> 20) + a | 0;
+ c += (d & a | ~d & b) + k[14] - 1502002290 | 0;
+ c = (c << 17 | c >>> 15) + d | 0;
+ b += (c & d | ~c & a) + k[15] + 1236535329 | 0;
+ b = (b << 22 | b >>> 10) + c | 0;
+
+ a += (b & d | c & ~d) + k[1] - 165796510 | 0;
+ a = (a << 5 | a >>> 27) + b | 0;
+ d += (a & c | b & ~c) + k[6] - 1069501632 | 0;
+ d = (d << 9 | d >>> 23) + a | 0;
+ c += (d & b | a & ~b) + k[11] + 643717713 | 0;
+ c = (c << 14 | c >>> 18) + d | 0;
+ b += (c & a | d & ~a) + k[0] - 373897302 | 0;
+ b = (b << 20 | b >>> 12) + c | 0;
+ a += (b & d | c & ~d) + k[5] - 701558691 | 0;
+ a = (a << 5 | a >>> 27) + b | 0;
+ d += (a & c | b & ~c) + k[10] + 38016083 | 0;
+ d = (d << 9 | d >>> 23) + a | 0;
+ c += (d & b | a & ~b) + k[15] - 660478335 | 0;
+ c = (c << 14 | c >>> 18) + d | 0;
+ b += (c & a | d & ~a) + k[4] - 405537848 | 0;
+ b = (b << 20 | b >>> 12) + c | 0;
+ a += (b & d | c & ~d) + k[9] + 568446438 | 0;
+ a = (a << 5 | a >>> 27) + b | 0;
+ d += (a & c | b & ~c) + k[14] - 1019803690 | 0;
+ d = (d << 9 | d >>> 23) + a | 0;
+ c += (d & b | a & ~b) + k[3] - 187363961 | 0;
+ c = (c << 14 | c >>> 18) + d | 0;
+ b += (c & a | d & ~a) + k[8] + 1163531501 | 0;
+ b = (b << 20 | b >>> 12) + c | 0;
+ a += (b & d | c & ~d) + k[13] - 1444681467 | 0;
+ a = (a << 5 | a >>> 27) + b | 0;
+ d += (a & c | b & ~c) + k[2] - 51403784 | 0;
+ d = (d << 9 | d >>> 23) + a | 0;
+ c += (d & b | a & ~b) + k[7] + 1735328473 | 0;
+ c = (c << 14 | c >>> 18) + d | 0;
+ b += (c & a | d & ~a) + k[12] - 1926607734 | 0;
+ b = (b << 20 | b >>> 12) + c | 0;
+
+ a += (b ^ c ^ d) + k[5] - 378558 | 0;
+ a = (a << 4 | a >>> 28) + b | 0;
+ d += (a ^ b ^ c) + k[8] - 2022574463 | 0;
+ d = (d << 11 | d >>> 21) + a | 0;
+ c += (d ^ a ^ b) + k[11] + 1839030562 | 0;
+ c = (c << 16 | c >>> 16) + d | 0;
+ b += (c ^ d ^ a) + k[14] - 35309556 | 0;
+ b = (b << 23 | b >>> 9) + c | 0;
+ a += (b ^ c ^ d) + k[1] - 1530992060 | 0;
+ a = (a << 4 | a >>> 28) + b | 0;
+ d += (a ^ b ^ c) + k[4] + 1272893353 | 0;
+ d = (d << 11 | d >>> 21) + a | 0;
+ c += (d ^ a ^ b) + k[7] - 155497632 | 0;
+ c = (c << 16 | c >>> 16) + d | 0;
+ b += (c ^ d ^ a) + k[10] - 1094730640 | 0;
+ b = (b << 23 | b >>> 9) + c | 0;
+ a += (b ^ c ^ d) + k[13] + 681279174 | 0;
+ a = (a << 4 | a >>> 28) + b | 0;
+ d += (a ^ b ^ c) + k[0] - 358537222 | 0;
+ d = (d << 11 | d >>> 21) + a | 0;
+ c += (d ^ a ^ b) + k[3] - 722521979 | 0;
+ c = (c << 16 | c >>> 16) + d | 0;
+ b += (c ^ d ^ a) + k[6] + 76029189 | 0;
+ b = (b << 23 | b >>> 9) + c | 0;
+ a += (b ^ c ^ d) + k[9] - 640364487 | 0;
+ a = (a << 4 | a >>> 28) + b | 0;
+ d += (a ^ b ^ c) + k[12] - 421815835 | 0;
+ d = (d << 11 | d >>> 21) + a | 0;
+ c += (d ^ a ^ b) + k[15] + 530742520 | 0;
+ c = (c << 16 | c >>> 16) + d | 0;
+ b += (c ^ d ^ a) + k[2] - 995338651 | 0;
+ b = (b << 23 | b >>> 9) + c | 0;
+
+ a += (c ^ (b | ~d)) + k[0] - 198630844 | 0;
+ a = (a << 6 | a >>> 26) + b | 0;
+ d += (b ^ (a | ~c)) + k[7] + 1126891415 | 0;
+ d = (d << 10 | d >>> 22) + a | 0;
+ c += (a ^ (d | ~b)) + k[14] - 1416354905 | 0;
+ c = (c << 15 | c >>> 17) + d | 0;
+ b += (d ^ (c | ~a)) + k[5] - 57434055 | 0;
+ b = (b << 21 | b >>> 11) + c | 0;
+ a += (c ^ (b | ~d)) + k[12] + 1700485571 | 0;
+ a = (a << 6 | a >>> 26) + b | 0;
+ d += (b ^ (a | ~c)) + k[3] - 1894986606 | 0;
+ d = (d << 10 | d >>> 22) + a | 0;
+ c += (a ^ (d | ~b)) + k[10] - 1051523 | 0;
+ c = (c << 15 | c >>> 17) + d | 0;
+ b += (d ^ (c | ~a)) + k[1] - 2054922799 | 0;
+ b = (b << 21 | b >>> 11) + c | 0;
+ a += (c ^ (b | ~d)) + k[8] + 1873313359 | 0;
+ a = (a << 6 | a >>> 26) + b | 0;
+ d += (b ^ (a | ~c)) + k[15] - 30611744 | 0;
+ d = (d << 10 | d >>> 22) + a | 0;
+ c += (a ^ (d | ~b)) + k[6] - 1560198380 | 0;
+ c = (c << 15 | c >>> 17) + d | 0;
+ b += (d ^ (c | ~a)) + k[13] + 1309151649 | 0;
+ b = (b << 21 | b >>> 11) + c | 0;
+ a += (c ^ (b | ~d)) + k[4] - 145523070 | 0;
+ a = (a << 6 | a >>> 26) + b | 0;
+ d += (b ^ (a | ~c)) + k[11] - 1120210379 | 0;
+ d = (d << 10 | d >>> 22) + a | 0;
+ c += (a ^ (d | ~b)) + k[2] + 718787259 | 0;
+ c = (c << 15 | c >>> 17) + d | 0;
+ b += (d ^ (c | ~a)) + k[9] - 343485551 | 0;
+ b = (b << 21 | b >>> 11) + c | 0;
+
+ x[0] = a + x[0] | 0;
+ x[1] = b + x[1] | 0;
+ x[2] = c + x[2] | 0;
+ x[3] = d + x[3] | 0;
+ }
+
+ function md5blk(s) {
+ var md5blks = [],
+ i; /* Andy King said do it this way. */
+
+ for (i = 0; i < 64; i += 4) {
+ md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);
+ }
+ return md5blks;
+ }
+
+ function md5blk_array(a) {
+ var md5blks = [],
+ i; /* Andy King said do it this way. */
+
+ for (i = 0; i < 64; i += 4) {
+ md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24);
+ }
+ return md5blks;
+ }
+
+ function md51(s) {
+ var n = s.length,
+ state = [1732584193, -271733879, -1732584194, 271733878],
+ i,
+ length,
+ tail,
+ tmp,
+ lo,
+ hi;
+
+ for (i = 64; i <= n; i += 64) {
+ md5cycle(state, md5blk(s.substring(i - 64, i)));
+ }
+ s = s.substring(i - 64);
+ length = s.length;
+ tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+ for (i = 0; i < length; i += 1) {
+ tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3);
+ }
+ tail[i >> 2] |= 0x80 << ((i % 4) << 3);
+ if (i > 55) {
+ md5cycle(state, tail);
+ for (i = 0; i < 16; i += 1) {
+ tail[i] = 0;
+ }
+ }
+
+ // Beware that the final length might not fit in 32 bits so we take care of that
+ tmp = n * 8;
+ tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
+ lo = parseInt(tmp[2], 16);
+ hi = parseInt(tmp[1], 16) || 0;
+
+ tail[14] = lo;
+ tail[15] = hi;
+
+ md5cycle(state, tail);
+ return state;
+ }
+
+ function md51_array(a) {
+ var n = a.length,
+ state = [1732584193, -271733879, -1732584194, 271733878],
+ i,
+ length,
+ tail,
+ tmp,
+ lo,
+ hi;
+
+ for (i = 64; i <= n; i += 64) {
+ md5cycle(state, md5blk_array(a.subarray(i - 64, i)));
+ }
+
+ // Not sure if it is a bug, however IE10 will always produce a sub array of length 1
+ // containing the last element of the parent array if the sub array specified starts
+ // beyond the length of the parent array - weird.
+ // https://connect.microsoft.com/IE/feedback/details/771452/typed-array-subarray-issue
+ a = (i - 64) < n ? a.subarray(i - 64) : new Uint8Array(0);
+
+ length = a.length;
+ tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+ for (i = 0; i < length; i += 1) {
+ tail[i >> 2] |= a[i] << ((i % 4) << 3);
+ }
+
+ tail[i >> 2] |= 0x80 << ((i % 4) << 3);
+ if (i > 55) {
+ md5cycle(state, tail);
+ for (i = 0; i < 16; i += 1) {
+ tail[i] = 0;
+ }
+ }
+
+ // Beware that the final length might not fit in 32 bits so we take care of that
+ tmp = n * 8;
+ tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
+ lo = parseInt(tmp[2], 16);
+ hi = parseInt(tmp[1], 16) || 0;
+
+ tail[14] = lo;
+ tail[15] = hi;
+
+ md5cycle(state, tail);
+
+ return state;
+ }
+
+ function rhex(n) {
+ var s = '',
+ j;
+ for (j = 0; j < 4; j += 1) {
+ s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F];
+ }
+ return s;
+ }
+
+ function hex(x) {
+ var i;
+ for (i = 0; i < x.length; i += 1) {
+ x[i] = rhex(x[i]);
+ }
+ return x.join('');
+ }
+
+ // In some cases the fast add32 function cannot be used..
+ if (hex(md51('hello')) !== '5d41402abc4b2a76b9719d911017c592') {
+ add32 = function (x, y) {
+ var lsw = (x & 0xFFFF) + (y & 0xFFFF),
+ msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+ return (msw << 16) | (lsw & 0xFFFF);
+ };
+ }
+
+ // ---------------------------------------------------
+
+ /**
+ * ArrayBuffer slice polyfill.
+ *
+ * @see https://github.com/ttaubert/node-arraybuffer-slice
+ */
+
+ if (typeof ArrayBuffer !== 'undefined' && !ArrayBuffer.prototype.slice) {
+ (function () {
+
+ function clamp(val, length) {
+ val = (val | 0) || 0;
+
+ if (val < 0) {
+ return Math.max(val + length, 0);
+ }
+
+ return Math.min(val, length);
+ }
+
+ ArrayBuffer.prototype.slice = function (from, to) {
+ var length = this.byteLength,
+ begin = clamp(from, length),
+ end = length,
+ num,
+ target,
+ targetArray,
+ sourceArray;
+
+ if (to !== undefined) {
+ end = clamp(to, length);
+ }
+
+ if (begin > end) {
+ return new ArrayBuffer(0);
+ }
+
+ num = end - begin;
+ target = new ArrayBuffer(num);
+ targetArray = new Uint8Array(target);
+
+ sourceArray = new Uint8Array(this, begin, num);
+ targetArray.set(sourceArray);
+
+ return target;
+ };
+ })();
+ }
+
+ // ---------------------------------------------------
+
+ /**
+ * Helpers.
+ */
+
+ function toUtf8(str) {
+ if (/[\u0080-\uFFFF]/.test(str)) {
+ // 源码是str = unescape(encodeURIComponent(str));这里的API并不对等
+ buffer.from(str).toString("utf-8")
+ }
+ return str;
+ }
+
+ function utf8Str2ArrayBuffer(str, returnUInt8Array) {
+ var length = str.length,
+ buff = new ArrayBuffer(length),
+ arr = new Uint8Array(buff),
+ i;
+
+ for (i = 0; i < length; i += 1) {
+ arr[i] = str.charCodeAt(i);
+ }
+
+ return returnUInt8Array ? arr : buff;
+ }
+
+ function arrayBuffer2Utf8Str(buff) {
+ return String.fromCharCode.apply(null, new Uint8Array(buff));
+ }
+
+ function concatenateArrayBuffers(first, second, returnUInt8Array) {
+ var result = new Uint8Array(first.byteLength + second.byteLength);
+
+ result.set(new Uint8Array(first));
+ result.set(new Uint8Array(second), first.byteLength);
+
+ return returnUInt8Array ? result : result.buffer;
+ }
+
+ function hexToBinaryString(hex) {
+ var bytes = [],
+ length = hex.length,
+ x;
+
+ for (x = 0; x < length - 1; x += 2) {
+ bytes.push(parseInt(hex.substr(x, 2), 16));
+ }
+
+ return String.fromCharCode.apply(String, bytes);
+ }
+
+ // ---------------------------------------------------
+
+ /**
+ * SparkMD5 OOP implementation.
+ *
+ * Use this class to perform an incremental md5, otherwise use the
+ * static methods instead.
+ */
+
+ function SparkMD5() {
+ // call reset to init the instance
+ this.reset();
+ }
+
+ /**
+ * Appends a string.
+ * A conversion will be applied if an utf8 string is detected.
+ *
+ * @param {String} str The string to be appended
+ *
+ * @return {SparkMD5} The instance itself
+ */
+ SparkMD5.prototype.append = function (str) {
+ // Converts the string to utf8 bytes if necessary
+ // Then append as binary
+ this.appendBinary(toUtf8(str));
+
+ return this;
+ };
+
+ /**
+ * Appends a binary string.
+ *
+ * @param {String} contents The binary string to be appended
+ *
+ * @return {SparkMD5} The instance itself
+ */
+ SparkMD5.prototype.appendBinary = function (contents) {
+ this._buff += contents;
+ this._length += contents.length;
+
+ var length = this._buff.length,
+ i;
+
+ for (i = 64; i <= length; i += 64) {
+ md5cycle(this._hash, md5blk(this._buff.substring(i - 64, i)));
+ }
+
+ this._buff = this._buff.substring(i - 64);
+
+ return this;
+ };
+
+ /**
+ * Finishes the incremental computation, reseting the internal state and
+ * returning the result.
+ *
+ * @param {Boolean} raw True to get the raw string, false to get the hex string
+ *
+ * @return {String} The result
+ */
+ SparkMD5.prototype.end = function (raw) {
+ var buff = this._buff,
+ length = buff.length,
+ i,
+ tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ ret;
+
+ for (i = 0; i < length; i += 1) {
+ tail[i >> 2] |= buff.charCodeAt(i) << ((i % 4) << 3);
+ }
+
+ this._finish(tail, length);
+ ret = hex(this._hash);
+
+ if (raw) {
+ ret = hexToBinaryString(ret);
+ }
+
+ this.reset();
+
+ return ret;
+ };
+
+ /**
+ * Resets the internal state of the computation.
+ *
+ * @return {SparkMD5} The instance itself
+ */
+ SparkMD5.prototype.reset = function () {
+ this._buff = '';
+ this._length = 0;
+ this._hash = [1732584193, -271733879, -1732584194, 271733878];
+
+ return this;
+ };
+
+ /**
+ * Gets the internal state of the computation.
+ *
+ * @return {Object} The state
+ */
+ SparkMD5.prototype.getState = function () {
+ return {
+ buff: this._buff,
+ length: this._length,
+ hash: this._hash.slice()
+ };
+ };
+
+ /**
+ * Gets the internal state of the computation.
+ *
+ * @param {Object} state The state
+ *
+ * @return {SparkMD5} The instance itself
+ */
+ SparkMD5.prototype.setState = function (state) {
+ this._buff = state.buff;
+ this._length = state.length;
+ this._hash = state.hash;
+
+ return this;
+ };
+
+ /**
+ * Releases memory used by the incremental buffer and other additional
+ * resources. If you plan to use the instance again, use reset instead.
+ */
+ SparkMD5.prototype.destroy = function () {
+ delete this._hash;
+ delete this._buff;
+ delete this._length;
+ };
+
+ /**
+ * Finish the final calculation based on the tail.
+ *
+ * @param {Array} tail The tail (will be modified)
+ * @param {Number} length The length of the remaining buffer
+ */
+ SparkMD5.prototype._finish = function (tail, length) {
+ var i = length,
+ tmp,
+ lo,
+ hi;
+
+ tail[i >> 2] |= 0x80 << ((i % 4) << 3);
+ if (i > 55) {
+ md5cycle(this._hash, tail);
+ for (i = 0; i < 16; i += 1) {
+ tail[i] = 0;
+ }
+ }
+
+ // Do the final computation based on the tail and length
+ // Beware that the final length may not fit in 32 bits so we take care of that
+ tmp = this._length * 8;
+ tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
+ lo = parseInt(tmp[2], 16);
+ hi = parseInt(tmp[1], 16) || 0;
+
+ tail[14] = lo;
+ tail[15] = hi;
+ md5cycle(this._hash, tail);
+ };
+
+ /**
+ * Performs the md5 hash on a string.
+ * A conversion will be applied if utf8 string is detected.
+ *
+ * @param {String} str The string
+ * @param {Boolean} [raw] True to get the raw string, false to get the hex string
+ *
+ * @return {String} The result
+ */
+ SparkMD5.hash = function (str, raw) {
+ // Converts the string to utf8 bytes if necessary
+ // Then compute it using the binary function
+ return SparkMD5.hashBinary(toUtf8(str), raw);
+ };
+
+ /**
+ * Performs the md5 hash on a binary string.
+ *
+ * @param {String} content The binary string
+ * @param {Boolean} [raw] True to get the raw string, false to get the hex string
+ *
+ * @return {String} The result
+ */
+ SparkMD5.hashBinary = function (content, raw) {
+ var hash = md51(content),
+ ret = hex(hash);
+
+ return raw ? hexToBinaryString(ret) : ret;
+ };
+
+ // ---------------------------------------------------
+
+ /**
+ * SparkMD5 OOP implementation for array buffers.
+ *
+ * Use this class to perform an incremental md5 ONLY for array buffers.
+ */
+ SparkMD5.ArrayBuffer = function () {
+ // call reset to init the instance
+ this.reset();
+ };
+
+ /**
+ * Appends an array buffer.
+ *
+ * @param {ArrayBuffer} arr The array to be appended
+ *
+ * @return {SparkMD5.ArrayBuffer} The instance itself
+ */
+ SparkMD5.ArrayBuffer.prototype.append = function (arr) {
+ // @ts-ignore
+ var buff = concatenateArrayBuffers(this._buff.buffer, arr, true),
+ // @ts-ignore
+ length = buff.length,
+ i;
+
+ // @ts-ignore
+ this._length += arr.byteLength;
+
+ for (i = 64; i <= length; i += 64) {
+ // @ts-ignore
+ md5cycle(this._hash, md5blk_array(buff.subarray(i - 64, i)));
+ }
+
+ // @ts-ignore
+ this._buff = (i - 64) < length ? new Uint8Array(buff.buffer.slice(i - 64)) : new Uint8Array(0);
+
+ return this;
+ };
+
+ /**
+ * Finishes the incremental computation, reseting the internal state and
+ * returning the result.
+ *
+ * @param {Boolean} raw True to get the raw string, false to get the hex string
+ *
+ * @return {String} The result
+ */
+ SparkMD5.ArrayBuffer.prototype.end = function (raw) {
+ // @ts-ignore
+ var buff = this._buff,
+ length = buff.length,
+ tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ i,
+ ret;
+
+ for (i = 0; i < length; i += 1) {
+ tail[i >> 2] |= buff[i] << ((i % 4) << 3);
+ }
+
+ this._finish(tail, length);
+ // @ts-ignore
+ ret = hex(this._hash);
+
+ if (raw) {
+ ret = hexToBinaryString(ret);
+ }
+
+ this.reset();
+
+ return ret;
+ };
+
+ /**
+ * Resets the internal state of the computation.
+ *
+ * @return {SparkMD5.ArrayBuffer} The instance itself
+ */
+ SparkMD5.ArrayBuffer.prototype.reset = function () {
+ // @ts-ignore
+ this._buff = new Uint8Array(0);
+ // @ts-ignore
+ this._length = 0;
+ // @ts-ignore
+ this._hash = [1732584193, -271733879, -1732584194, 271733878];
+
+ return this;
+ };
+
+ /**
+ * Gets the internal state of the computation.
+ *
+ * @return {Object} The state
+ */
+ SparkMD5.ArrayBuffer.prototype.getState = function () {
+ var state = SparkMD5.prototype.getState.call(this);
+
+ // Convert buffer to a string
+ state.buff = arrayBuffer2Utf8Str(state.buff);
+
+ return state;
+ };
+
+ /**
+ * Gets the internal state of the computation.
+ *
+ * @param {Object} state The state
+ *
+ * @return {SparkMD5.ArrayBuffer} The instance itself
+ */
+ SparkMD5.ArrayBuffer.prototype.setState = function (state) {
+ // Convert string to buffer
+ state.buff = utf8Str2ArrayBuffer(state.buff, true);
+
+ return SparkMD5.prototype.setState.call(this, state);
+ };
+
+ SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy;
+
+ SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish;
+
+ /**
+ * Performs the md5 hash on an array buffer.
+ *
+ * @param {ArrayBuffer} arr The array buffer
+ * @param {Boolean} [raw] True to get the raw string, false to get the hex one
+ *
+ * @return {String} The result
+ */
+ SparkMD5.ArrayBuffer.hash = function (arr, raw) {
+ var hash = md51_array(new Uint8Array(arr)),
+ ret = hex(hash);
+
+ return raw ? hexToBinaryString(ret) : ret;
+ };
+
+export { SparkMD5 }
+
diff --git a/library/src/main/ets/ImageKnife.ets b/library/src/main/ets/ImageKnife.ets
new file mode 100644
index 0000000..d168038
--- /dev/null
+++ b/library/src/main/ets/ImageKnife.ets
@@ -0,0 +1,393 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { ImageKnifeRequest } from './ImageKnifeRequest';
+import { CacheStrategy, ImageKnifeData, ImageKnifeRequestSource } from './model/ImageKnifeData';
+import { MemoryLruCache } from './utils/MemoryLruCache';
+import { IMemoryCache } from './utils/IMemoryCache'
+import { FileCache } from './utils/FileCache';
+import { ImageKnifeDispatcher } from './ImageKnifeDispatcher';
+import { IEngineKey } from './key/IEngineKey';
+import { HeaderOptions, ImageKnifeOption } from './ImageKnifeOption';
+import { FileTypeUtil } from './utils/FileTypeUtil';
+import { util } from '@kit.ArkTS';
+import { image } from '@kit.ImageKit';
+import { common } from '@kit.AbilityKit';
+import { LogUtil } from './utils/LogUtil';
+
+
+export class ImageKnife {
+ private static instance: ImageKnife;
+ // 内存缓存
+ private memoryCache: IMemoryCache = new MemoryLruCache(256, 128 * 1024 * 1024);
+ // 文件缓存
+ private fileCache?: FileCache
+ private dispatcher: ImageKnifeDispatcher = new ImageKnifeDispatcher()
+ // 配置全局是否在子线程加载图片请求
+ private _isRequestInSubThread: boolean = true;
+ //定义全局网络请求header map
+ headerMap: Map = new Map();
+ customGetImage: ((context: Context, src: string | PixelMap | Resource) => Promise) | undefined = undefined
+ public static getInstance(): ImageKnife {
+ if (!ImageKnife.instance) {
+ ImageKnife.instance = new ImageKnife();
+ }
+ return ImageKnife.instance;
+ }
+
+ private constructor() {
+ }
+
+ public set isRequestInSubThread(value: boolean) {
+ this._isRequestInSubThread = value;
+ }
+
+ public get isRequestInSubThread(): boolean {
+ return this._isRequestInSubThread;
+ }
+
+ /**
+ * 初始化文件缓存个数,大小,以及路径
+ * @param context 上下文
+ * @param size 缓存数量
+ * @param memory 内存大小
+ * @param path 文件目录
+ */
+ async initFileCache(context: Context, size: number = 256, memory: number = 256 * 1024 * 1024,path?: string) {
+ this.fileCache = new FileCache(context, size, memory)
+ if ( path != undefined ) {
+ await this.fileCache.initFileCache(path)
+ } else {
+ await this.fileCache.initFileCache()
+ }
+ }
+
+ /**
+ * 判断文件缓存是否已完成初始化
+ * @returns 是否初始化
+ */
+ public isFileCacheInit(): boolean {
+ return this.fileCache === undefined ? false : this.fileCache.isFileCacheInit()
+ }
+
+ /**
+ * 全局添加单个请求头header
+ * @param key 请求头属性
+ * @param value 请求头值
+ */
+ addHeader(key: string, value: Object) {
+ this.headerMap.set(key, value)
+ }
+
+ /**
+ * 全局设置请求头header
+ * @param options 请求头数组
+ */
+ serHeaderOptions(options: Array) {
+ options.forEach((value) => {
+ this.headerMap.set(value.key, value.value)
+ })
+ }
+
+ /**
+ * 删除单个请求头header
+ * @param key 请求头属性
+ */
+ deleteHeader(key: string) {
+ this.headerMap.delete(key)
+ }
+
+ /**
+ * 设置自定义的内存缓存
+ * @param newMemoryCache 自定义内存缓存
+ */
+ initMemoryCache(newMemoryCache: IMemoryCache): void {
+ this.memoryCache = newMemoryCache
+ }
+
+ /**
+ * 清除所有内存缓存
+ */
+ removeAllMemoryCache(): void {
+ this.memoryCache.removeAll()
+ }
+
+ /**
+ * 清除指定内存缓存
+ * @param url 待清除的url路径或ImageKnifeOption
+ */
+ removeMemoryCache(url: string | ImageKnifeOption) {
+ let imageKnifeOption = new ImageKnifeOption();
+ if (typeof url == 'string') {
+ imageKnifeOption.loadSrc = url;
+ } else {
+ imageKnifeOption = url;
+ }
+ let key = this.getEngineKeyImpl().generateMemoryKey(imageKnifeOption.loadSrc, ImageKnifeRequestSource.SRC, imageKnifeOption);
+ this.memoryCache.remove(key);
+ }
+
+
+ /**
+ * 预加载图片到文件缓存
+ * @param loadSrc 图片地址url
+ * @returns 返回文件缓存路径
+ */
+ preLoadCache(loadSrc: string | ImageKnifeOption): Promise {
+ return new Promise((resolve, reject) => {
+ let imageKnifeOption = new ImageKnifeOption()
+ if (typeof loadSrc == "string") {
+ imageKnifeOption.loadSrc = loadSrc
+ } else {
+ imageKnifeOption = loadSrc;
+ }
+ LogUtil.log("ImageKnife_DataTime_preLoadCache-imageKnifeOption:"+loadSrc)
+ let fileKey = this.getEngineKeyImpl().generateFileKey(imageKnifeOption.loadSrc, imageKnifeOption.signature)
+ let cachePath = ImageKnife.getInstance().getFileCache().getFileToPath(fileKey)
+ if (cachePath == null || cachePath == "" || cachePath == undefined) {
+ imageKnifeOption.onLoadListener = {
+ onLoadSuccess(){
+ resolve(ImageKnife.getInstance().getFileCache().getFileToPath(fileKey))
+ },
+ onLoadFailed(err) {
+ reject(err)
+ }
+ }
+ let request = new ImageKnifeRequest(
+ imageKnifeOption,
+ imageKnifeOption.context !== undefined ? imageKnifeOption.context : getContext(this) as common.UIAbilityContext,
+ 0,
+ 0,
+ 0,
+ {
+ showPixelMap(version: number, pixelMap: PixelMap | string) {
+ }
+ }
+ )
+ this.execute(request)
+ } else {
+ resolve(cachePath)
+ }
+ })
+ }
+
+ /**
+ * 从内存或文件缓存中获取图片数据
+ * @param url 图片地址url
+ * @param cacheType 缓存策略
+ * @returns 图片数据
+ * @param signature key自定义信息
+ */
+ getCacheImage(loadSrc: string,
+ cacheType: CacheStrategy = CacheStrategy.Default, signature?: string): Promise {
+ let option: ImageKnifeOption = {
+ loadSrc: loadSrc,
+ signature:signature
+ }
+ let engineKeyImpl: IEngineKey = this.getEngineKeyImpl();
+
+ return new Promise((resolve, reject) => {
+ if (cacheType == CacheStrategy.Memory) {
+ resolve(this.readMemoryCache(loadSrc, option, engineKeyImpl))
+ } else if (cacheType == CacheStrategy.File) {
+ this.readFileCache(loadSrc, engineKeyImpl, resolve)
+ } else {
+ let data = this.readMemoryCache(loadSrc, option, engineKeyImpl)
+ data == undefined ? this.readFileCache(loadSrc, engineKeyImpl, resolve) : resolve(data)
+ }
+ })
+ }
+
+ /**
+ * 预加载缓存(用于外部已获取pixelmap,需要加入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
+ }
+ }
+
+ /**
+ * 清除所有文件缓存
+ * @returns
+ */
+ async removeAllFileCache(): Promise {
+ if (this.fileCache !== undefined) {
+ await this.fileCache.removeAll()
+ }
+ }
+ /*
+ * 清除指定文件缓存
+ * */
+ removeFileCache(url: string | ImageKnifeOption) {
+ let imageKnifeOption: ImageKnifeOption;
+ if (url instanceof ImageKnifeOption) {
+ imageKnifeOption = url;
+ } else {
+ imageKnifeOption = {
+ loadSrc: url
+ };
+ }
+ let key = this.getEngineKeyImpl().generateFileKey(imageKnifeOption.loadSrc, imageKnifeOption.signature);
+ if (this.fileCache !== undefined) {
+ this.fileCache.remove(key);
+ }
+ }
+
+ /**
+ * 设置taskpool默认并发数量
+ * @param concurrency 默认并发数量,默认为8
+ */
+ setMaxRequests(concurrency: number): void {
+ this.dispatcher.setMaxRequests(concurrency)
+ }
+
+ getFileCacheByFile(context: Context, key: string): ArrayBuffer | undefined {
+ if (this.fileCache !== undefined) {
+ return FileCache.getFileCacheByFile(context, key)
+ }
+ return undefined
+ }
+
+ loadFromMemoryCache(key: string): ImageKnifeData | undefined {
+ if (key !== "") {
+ return this.memoryCache.get(key)
+ }
+ return undefined
+ }
+
+ saveMemoryCache(key: string, data: ImageKnifeData): void {
+ if (key !== "") {
+ this.memoryCache.put(key, data)
+ }
+ }
+
+ loadFromFileCache(key: string): ArrayBuffer | undefined {
+ return this.fileCache?.get(key)
+ }
+
+ saveFileCache(key: string, data: ArrayBuffer): void {
+ this.fileCache?.put(key, data)
+ }
+
+ getFileCache(): FileCache {
+ return this.fileCache as FileCache
+ }
+
+
+ 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()
+ .loadFromMemoryCache(memoryKey)
+ }
+
+ private readFileCache(loadSrc: string, engineKey: IEngineKey, onComplete: (data: ImageKnifeData | undefined) => void) {
+ let keys = engineKey.generateFileKey(loadSrc)
+ let buffer = ImageKnife.getInstance().loadFromFileCache(keys)
+ if (buffer != undefined) {
+ let fileTypeUtil = new FileTypeUtil();
+ let typeValue = fileTypeUtil.getFileType(buffer);
+ if (typeValue === 'gif' || typeValue === 'webp') {
+ let base64Help = new util.Base64Helper()
+
+ let base64str = "data:image/" + typeValue + ";base64," + base64Help.encodeToStringSync(new Uint8Array(buffer))
+ onComplete({
+ source: base64str,
+ imageWidth: 0,
+ imageHeight: 0
+ })
+ }
+
+ let imageSource: image.ImageSource = image.createImageSource(buffer);
+ let decodingOptions: image.DecodingOptions = {
+ editable: true,
+ }
+
+ imageSource.createPixelMap(decodingOptions)
+ .then((pixelmap: PixelMap) => {
+ onComplete({
+ source: pixelmap,
+ imageWidth: 0,
+ imageHeight: 0
+ })
+ imageSource.release()
+ })
+ } else {
+ onComplete(undefined)
+ }
+ }
+
+ saveWithoutWriteFile(key: string, bufferSize: number): void {
+ this.fileCache?.putWithoutWriteFile(key, bufferSize)
+ }
+
+ saveFileCacheOnlyFile(context: Context, key: string, value: ArrayBuffer): boolean {
+ if (this.fileCache !== undefined) {
+ return FileCache.saveFileCacheOnlyFile(context, key, value)
+ }
+ return false
+ }
+
+ async execute(request: ImageKnifeRequest,isAnimator?: boolean): Promise {
+ LogUtil.log("ImageKnife_DataTime_execute.start:"+request.imageKnifeOption.loadSrc)
+ if (this.headerMap.size > 0) {
+ request.addHeaderMap(this.headerMap)
+ }
+ this.dispatcher.enqueue(request,isAnimator)
+ LogUtil.log("ImageKnife_DataTime_execute.end:"+request.imageKnifeOption.loadSrc)
+ }
+
+ setEngineKeyImpl(impl: IEngineKey): void {
+ this.dispatcher.setEngineKeyImpl(impl);
+ }
+
+ getEngineKeyImpl(): IEngineKey {
+ return this.dispatcher.getEngineKeyImpl();
+ }
+ /**
+ * 全局设置自定义下载
+ * @param customGetImage 自定义请求函数
+ */
+ setCustomGetImage(customGetImage?: (context: Context, src: string | PixelMap | Resource) => Promise) {
+ this.customGetImage = customGetImage
+ }
+ getCustomGetImage(): undefined | ((context: Context, src: string | PixelMap | Resource) => Promise){
+ return this.customGetImage
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/ets/ImageKnifeDispatcher.ets b/library/src/main/ets/ImageKnifeDispatcher.ets
new file mode 100644
index 0000000..795d709
--- /dev/null
+++ b/library/src/main/ets/ImageKnifeDispatcher.ets
@@ -0,0 +1,668 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { ImageKnifeRequest, ImageKnifeRequestState } from './ImageKnifeRequest'
+import { DefaultJobQueue } from './utils/DefaultJobQueue'
+import { IJobQueue } from './utils/IJobQueue'
+import List from '@ohos.util.List';
+import LightWeightMap from '@ohos.util.LightWeightMap';
+import { LogUtil } from './utils/LogUtil';
+import buffer from '@ohos.buffer';
+import { FileCache } from './utils/FileCache';
+import fs from '@ohos.file.fs';
+import { ImageKnife } from './ImageKnife';
+import { ImageKnifeData, CacheStrategy } from './model/ImageKnifeData';
+import http from '@ohos.net.http';
+import image from '@ohos.multimedia.image';
+import emitter from '@ohos.events.emitter';
+import { Constants } from './utils/Constants';
+import taskpool from '@ohos.taskpool';
+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 { combineArrayBuffers } from './model/utils';
+import { BusinessError } from '@kit.BasicServicesKit';
+
+export class ImageKnifeDispatcher {
+ // 最大并发
+ private maxRequests: number = 8
+ // 排队队列
+ private jobQueue: IJobQueue = new DefaultJobQueue()
+ // 执行中的请求
+ executingJobMap: LightWeightMap> = new LightWeightMap();
+ // 开发者可配置全局缓存
+ private engineKey: IEngineKey = new DefaultEngineKey();
+
+ showFromMemomry(request: ImageKnifeRequest, imageSrc: string | PixelMap | Resource, requestSource: ImageKnifeRequestSource,isAnimator?: boolean): boolean {
+ LogUtil.log("ImageKnife_DataTime_showFromMemomry.start:" + request.imageKnifeOption.loadSrc)
+ 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,isAnimator));
+ }
+
+
+ if (memoryCache !== undefined) {
+ // 画主图
+ if (request.requestState === ImageKnifeRequestState.PROGRESS) {
+ // 回调请求开始
+ if (requestSource === ImageKnifeRequestSource.SRC && request.imageKnifeOption.onLoadListener?.onLoadStart !== undefined) {
+ request.imageKnifeOption.onLoadListener?.onLoadStart()
+ LogUtil.log("ImageKnife_DataTime_MemoryCache_onLoadStart:" + request.imageKnifeOption.loadSrc)
+ }
+ LogUtil.log("ImageKnife_DataTime_MemoryCache_showPixelMap.start:" + request.imageKnifeOption.loadSrc)
+ request.ImageKnifeRequestCallback?.showPixelMap(request.componentVersion, memoryCache.source, requestSource,memoryCache.imageAnimator)
+ LogUtil.log("ImageKnife_DataTime_MemoryCache_showPixelMap.end:" + request.imageKnifeOption.loadSrc)
+
+ if (requestSource == ImageKnifeRequestSource.SRC) {
+ request.requestState = ImageKnifeRequestState.COMPLETE
+ // 回调请求开结束
+ if (request.imageKnifeOption.onLoadListener?.onLoadSuccess !== undefined) {
+ request.imageKnifeOption.onLoadListener?.onLoadSuccess(memoryCache.source,memoryCache)
+ LogUtil.log("ImageKnife_DataTime_MemoryCache_onLoadSuccess:" + request.imageKnifeOption.loadSrc)
+ }
+ } else if (requestSource == ImageKnifeRequestSource.ERROR_HOLDER) {
+ request.requestState = ImageKnifeRequestState.ERROR
+ }
+ }
+ LogUtil.log("ImageKnife_DataTime_showFromMemomry.end_true:" + request.imageKnifeOption.loadSrc)
+ return true
+ }
+ LogUtil.log("ImageKnife_DataTime_showFromMemomry.end_false:" + request.imageKnifeOption.loadSrc)
+ return false
+ }
+
+
+ enqueue(request: ImageKnifeRequest,isAnimator?: boolean): void {
+
+ //1.内存有的话直接渲染
+ if (this.showFromMemomry(request, request.imageKnifeOption.loadSrc, ImageKnifeRequestSource.SRC,isAnimator)) {
+ return
+ }
+ // 2.内存获取占位图
+ if (request.imageKnifeOption.placeholderSrc !== undefined) {
+ if (this.showFromMemomry(request, request.imageKnifeOption.placeholderSrc, ImageKnifeRequestSource.PLACE_HOLDER)) {
+ request.drawPlayHolderSuccess = true
+ }
+ }
+ //3.判断是否要排队
+ if (this.executingJobMap.length > this.maxRequests) {
+ this.jobQueue.add(request)
+ return
+ }
+ this.executeJob(request,isAnimator)
+ }
+
+ executeJob(request: ImageKnifeRequest,isAnimator?: boolean): void {
+ LogUtil.log("ImageKnife_DataTime_executeJob.start:" + request.imageKnifeOption.loadSrc)
+ // 加载占位符
+ if (request.imageKnifeOption.placeholderSrc !== undefined && request.drawPlayHolderSuccess == false) {
+ this.getAndShowImage(request, request.imageKnifeOption.placeholderSrc, ImageKnifeRequestSource.PLACE_HOLDER)
+ }
+
+ // 加载主图
+ this.getAndShowImage(request, request.imageKnifeOption.loadSrc, ImageKnifeRequestSource.SRC,isAnimator)
+ LogUtil.log("ImageKnife_DataTime_executeJob.end:" + request.imageKnifeOption.loadSrc)
+ }
+
+ /**
+ * 获取和显示图片
+ */
+ getAndShowImage(currentRequest: ImageKnifeRequest, imageSrc: string | PixelMap | Resource, requestSource: ImageKnifeRequestSource,isAnimator?: boolean): void {
+ LogUtil.log("ImageKnife_DataTime_getAndShowImage.start:" + currentRequest.imageKnifeOption.loadSrc)
+ let memoryKey: string = this.engineKey.generateMemoryKey(imageSrc, requestSource, currentRequest.imageKnifeOption,isAnimator)
+ let requestList: List | undefined = this.executingJobMap.get(memoryKey)
+ if (requestList == undefined) {
+ requestList = new List()
+ requestList.add({ request: currentRequest, source: requestSource })
+ this.executingJobMap.set(memoryKey, requestList)
+ } else {
+ requestList.add({ request: currentRequest, source: requestSource })
+ return
+ }
+
+ let isWatchProgress : boolean = false
+
+ // 回调请求开始
+ requestList.forEach((requestWithSource: ImageKnifeRequestWithSource) => {
+ if (requestWithSource.source === ImageKnifeRequestSource.SRC && requestWithSource.request.imageKnifeOption.onLoadListener?.onLoadStart !== undefined) {
+ requestWithSource.request.imageKnifeOption.onLoadListener?.onLoadStart()
+ LogUtil.log("ImageKnife_DataTime_getAndShowImage_onLoadStart:" + currentRequest.imageKnifeOption.loadSrc)
+ }
+ if (requestWithSource.request.imageKnifeOption.progressListener !== undefined && requestWithSource.source === ImageKnifeRequestSource.SRC) {
+ isWatchProgress = true
+ }
+ });
+
+ let request: RequestJobRequest = {
+ context: currentRequest.context,
+ src: imageSrc,
+ headers: currentRequest.imageKnifeOption.headerOption,
+ allHeaders: currentRequest.headers,
+ componentWidth:currentRequest.componentWidth,
+ componentHeight:currentRequest.componentHeight,
+ customGetImage: currentRequest.imageKnifeOption.customGetImage,
+ onlyRetrieveFromCache: currentRequest.imageKnifeOption.onlyRetrieveFromCache,
+ transformation: currentRequest.imageKnifeOption.transformation,
+ writeCacheStrategy: ImageKnife.getInstance()
+ .isFileCacheInit() ? currentRequest.imageKnifeOption.writeCacheStrategy : CacheStrategy.Memory, // 未初始化文件缓存时,不写文件缓存
+ engineKey: this.engineKey,
+ signature: currentRequest.imageKnifeOption.signature,
+ requestSource: requestSource,
+ isWatchProgress: isWatchProgress,
+ memoryKey: memoryKey,
+ fileCacheFolder: ImageKnife.getInstance().getFileCache().getCacheFolder(),
+ isAnimator:isAnimator
+ }
+
+ if(request.customGetImage == undefined) {
+ request.customGetImage = ImageKnife.getInstance().getCustomGetImage()
+ }
+ if (ImageKnife.getInstance().isRequestInSubThread){
+ // 启动线程下载和解码主图
+ LogUtil.log("ImageKnife_DataTime_getAndShowImage_Task.start:" + currentRequest.imageKnifeOption.loadSrc)
+ let task = new taskpool.Task(requestJob, request)
+ LogUtil.log("ImageKnife_DataTime_getAndShowImage_Task.end:" + currentRequest.imageKnifeOption.loadSrc)
+ if (isWatchProgress){
+ emitter.on(Constants.PROGRESS_EMITTER + memoryKey, (data) => {
+ this.progressCallBack(requestList! , data?.data?.value as number)
+ });
+ }
+
+ LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.start:" + currentRequest.imageKnifeOption.loadSrc)
+ taskpool.execute(task).then((res: Object) => {
+ this.doTaskCallback(res as RequestJobResult | undefined, requestList!, currentRequest, memoryKey, imageSrc, requestSource,isAnimator);
+ if (isWatchProgress){
+ emitter.off(Constants.PROGRESS_EMITTER + memoryKey)
+ }
+ LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.end:"+currentRequest.imageKnifeOption.loadSrc)
+ LogUtil.log("ImageKnife_DataTime_getAndShowImage.end:"+currentRequest.imageKnifeOption.loadSrc)
+ }).catch((err:BusinessError)=>{
+ LogUtil.error("Fail to execute in sub thread src=" + imageSrc + " err=" + err)
+ if (isWatchProgress){
+ emitter.off(Constants.PROGRESS_EMITTER + memoryKey)
+ }
+ this.executingJobMap.remove(memoryKey);
+ this.dispatchNextJob();
+ })
+ } else { //主线程请求
+ requestJob(request, requestList).then((res: RequestJobResult | undefined) => {
+ this.doTaskCallback(res, requestList!, currentRequest, memoryKey, imageSrc, requestSource,isAnimator);
+ LogUtil.log("ImageKnife_DataTime_getAndShowImage_execute.end:"+currentRequest.imageKnifeOption.loadSrc)
+ LogUtil.log("ImageKnife_DataTime_getAndShowImage.end:"+currentRequest.imageKnifeOption.loadSrc)
+ }).catch((err:BusinessError)=>{
+ LogUtil.error("Fail to execute in main thread src=" + imageSrc + " err=" + err)
+ this.executingJobMap.remove(memoryKey);
+ this.dispatchNextJob();
+ })
+ }
+ }
+
+ /**
+ * 回调下载进度
+ * @param requestList 请求列表
+ * @param data 进度
+ */
+ private progressCallBack(requestList:List, data: number) {
+ for (let i = 0; i < requestList.length; i++) {
+ let requestWithSource:ImageKnifeRequestWithSource = requestList[i]
+ if (requestWithSource.request.imageKnifeOption.progressListener !== undefined && requestWithSource.source === ImageKnifeRequestSource.SRC) {
+ requestWithSource.request.imageKnifeOption.progressListener(data)
+ }
+ }
+ }
+
+ private doTaskCallback(requestJobResult: RequestJobResult | undefined, requestList: List ,
+ currentRequest: ImageKnifeRequest, memoryKey: string, imageSrc: string | PixelMap | Resource, requestSource: ImageKnifeRequestSource,isAnimator?: boolean):void {
+ LogUtil.log("ImageKnife_DataTime_getAndShowImage_CallBack.start:"+currentRequest.imageKnifeOption.loadSrc)
+ if (requestJobResult === undefined){
+ return
+ }
+ let pixelmap = requestJobResult.pixelMap;
+ if (pixelmap === undefined) {
+ requestList.forEach((requestWithSource: ImageKnifeRequestWithSource) => {
+ // 回调请求失败
+ if (requestWithSource.source === ImageKnifeRequestSource.SRC &&
+ requestWithSource.request.imageKnifeOption.onLoadListener?.onLoadFailed !== undefined &&
+ requestJobResult.loadFail) {
+ requestWithSource.request.imageKnifeOption.onLoadListener.onLoadFailed(requestJobResult.loadFail);
+ LogUtil.log("ImageKnife_DataTime_getAndShowImage_onLoadFailed:"+currentRequest.imageKnifeOption.loadSrc)
+ }
+ if (requestWithSource.source === ImageKnifeRequestSource.SRC &&
+ requestWithSource.request.imageKnifeOption.errorholderSrc !== undefined) {
+
+ if (this.showFromMemomry(requestWithSource.request, requestWithSource.request.imageKnifeOption.errorholderSrc,
+ ImageKnifeRequestSource.ERROR_HOLDER) === false) {
+ this.getAndShowImage(requestWithSource.request, requestWithSource.request.imageKnifeOption.errorholderSrc,
+ ImageKnifeRequestSource.ERROR_HOLDER);
+ }
+ }
+ });
+ this.executingJobMap.remove(memoryKey);
+ return;
+ }
+ // 保存文件缓存
+ if (requestJobResult.bufferSize > 0 && currentRequest.imageKnifeOption.writeCacheStrategy !== CacheStrategy.Memory) {
+ LogUtil.log("ImageKnife_DataTime_getAndShowImage_saveWithoutWriteFile.start:"+currentRequest.imageKnifeOption.loadSrc)
+ ImageKnife.getInstance().saveWithoutWriteFile(requestJobResult.fileKey, requestJobResult.bufferSize);
+ LogUtil.log("ImageKnife_DataTime_getAndShowImage_saveWithoutWriteFile.end:"+currentRequest.imageKnifeOption.loadSrc)
+ }
+
+ let ImageKnifeData: ImageKnifeData = {
+ source: pixelmap!,
+ imageWidth: requestJobResult.size == undefined ? 0 : requestJobResult.size.width,
+ imageHeight: requestJobResult.size == undefined ? 0 : requestJobResult.size.height,
+ type:requestJobResult.type
+ };
+ if(requestJobResult.pixelMapList != undefined) {
+ let imageAnimator: Array = []
+ requestJobResult.pixelMapList.forEach((item,index)=>{
+ imageAnimator.push({
+ src:requestJobResult.pixelMapList![index],
+ duration:requestJobResult.delayList![index]
+ })
+ })
+ ImageKnifeData.imageAnimator = imageAnimator
+ }
+ // 保存内存缓存
+ if (currentRequest.imageKnifeOption.writeCacheStrategy !== CacheStrategy.File) {
+ LogUtil.log("ImageKnife_DataTime_getAndShowImage_saveMemoryCache.start:"+currentRequest.imageKnifeOption.loadSrc)
+ ImageKnife.getInstance()
+ .saveMemoryCache(this.engineKey.generateMemoryKey(imageSrc, requestSource, currentRequest.imageKnifeOption,isAnimator),
+ ImageKnifeData);
+ LogUtil.log("ImageKnife_DataTime_getAndShowImage_saveMemoryCache.end:"+currentRequest.imageKnifeOption.loadSrc)
+ }
+ if (requestList !== undefined) {
+
+ // todo 判断request生命周期,已销毁的不需要再绘制
+ // key相同的request,一起绘制
+ requestList.forEach((requestWithSource: ImageKnifeRequestWithSource) => {
+ if (requestWithSource.request.requestState !== ImageKnifeRequestState.DESTROY) {
+ // 画主图
+ if (requestWithSource.source === ImageKnifeRequestSource.SRC ||
+ requestWithSource.source === ImageKnifeRequestSource.ERROR_HOLDER
+ || (requestWithSource.source === ImageKnifeRequestSource.PLACE_HOLDER &&
+ requestWithSource.request.requestState === ImageKnifeRequestState.PROGRESS)) {
+ LogUtil.log("ImageKnife_DataTime_getAndShowImage_showPixelMap.start:"+currentRequest.imageKnifeOption.loadSrc)
+ requestWithSource.request.ImageKnifeRequestCallback.showPixelMap(requestWithSource.request.componentVersion,
+ ImageKnifeData.source, requestWithSource.source,ImageKnifeData.imageAnimator);
+ LogUtil.log("ImageKnife_DataTime_getAndShowImage_showPixelMap.end:"+currentRequest.imageKnifeOption.loadSrc)
+ }
+
+ if (requestWithSource.source == ImageKnifeRequestSource.SRC) {
+ requestWithSource.request.requestState = ImageKnifeRequestState.COMPLETE;
+ if (requestWithSource.request.imageKnifeOption.onLoadListener &&
+ requestWithSource.request.imageKnifeOption.onLoadListener.onLoadSuccess) {
+ // 回调请求成功
+ requestWithSource.request.imageKnifeOption.onLoadListener.onLoadSuccess(ImageKnifeData.source,ImageKnifeData);
+ LogUtil.log("ImageKnife_DataTime_getAndShowImage_onLoadSuccess:"+currentRequest.imageKnifeOption.loadSrc)
+ }
+ } else if (requestWithSource.source == ImageKnifeRequestSource.ERROR_HOLDER) {
+ requestWithSource.request.requestState = ImageKnifeRequestState.ERROR;
+ }
+ } else {
+ if (requestWithSource.source == ImageKnifeRequestSource.SRC && requestWithSource.request.imageKnifeOption.onLoadListener?.onLoadCancel) {
+ // 回调请求成功
+ requestWithSource.request.imageKnifeOption.onLoadListener.onLoadCancel("component has destroyed")
+ }
+ }
+ });
+
+ this.executingJobMap.remove(memoryKey);
+ this.dispatchNextJob();
+ } else {
+ LogUtil.log("error: no requestlist need to draw for key = " + memoryKey);
+ }
+ LogUtil.log("ImageKnife_DataTime_getAndShowImage_CallBack.end:"+currentRequest.imageKnifeOption.loadSrc)
+ }
+
+
+ dispatchNextJob() {
+ LogUtil.log("ImageKnife_DataTime_dispatchNextJob.start")
+ while (true) {
+ let request = this.jobQueue.pop()
+ if (request === undefined) {
+ break // 队列已无任务
+ }
+ else if (request.requestState === ImageKnifeRequestState.PROGRESS) {
+ this.executeJob(request)
+ LogUtil.log("ImageKnife_DataTime_dispatchNextJob.end:" + request.imageKnifeOption.loadSrc)
+ break
+ }else if (request.requestState == ImageKnifeRequestState.DESTROY && request.imageKnifeOption.onLoadListener?.onLoadCancel) {
+ request.imageKnifeOption.onLoadListener.onLoadCancel("component has destroyed")
+ }
+ }
+ }
+
+ setMaxRequests(concurrency: number): void {
+ if (concurrency > 0) {
+ this.maxRequests = concurrency
+ }
+ }
+
+ setEngineKeyImpl(impl: IEngineKey): void {
+ this.engineKey = impl;
+ }
+
+ getEngineKeyImpl(): IEngineKey {
+ return this.engineKey;
+ }
+}
+
+/**
+ * 通过taskpool 二级缓存,下载/读取本地文件,编解码
+ * @param context
+ * @param src
+ * @returns
+ */
+@Concurrent
+async function requestJob(request: RequestJobRequest, requestList?: List): Promise {
+ LogUtil.log("ImageKnife_DataTime_requestJob.start:" + request.src)
+ let resBuf: ArrayBuffer | undefined
+ let bufferSize: number = 0
+ let loadError: string = '';
+
+ class RequestData {
+ receiveSize: number = 2000
+ totalSize: number = 2000
+ }
+
+ // 生成文件key
+ let fileKey = request.engineKey.generateFileKey(request.src, request.signature,request.isAnimator)
+
+ // 判断自定义下载
+ if (request.customGetImage !== undefined && request.requestSource == ImageKnifeRequestSource.SRC) {
+ // 先从文件缓存获取
+ resBuf = FileCache.getFileCacheByFile(request.context, fileKey , request.fileCacheFolder)
+ if (resBuf === undefined) {
+ LogUtil.log("customGetImage customGetImage");
+ resBuf = await request.customGetImage(request.context, request.src)
+ loadError = resBuf == undefined ? "customGetImage loadFile" : loadError
+ // 保存文件缓存
+ if (resBuf !== undefined && request.writeCacheStrategy !== CacheStrategy.Memory) {
+ let copyBuf = buffer.concat([buffer.from(resBuf)]).buffer; // IDE有bug,不能直接获取resBuf.byteLength
+ bufferSize = copyBuf.byteLength
+ FileCache.saveFileCacheOnlyFile(request.context, fileKey, resBuf , request.fileCacheFolder)
+ }
+ }
+ }
+ else {
+ if (typeof request.src === 'string') {
+ if (request.src.indexOf("http://") == 0 || request.src.indexOf("https://") == 0) { //从网络下载
+ // 先从文件缓存获取
+ resBuf = FileCache.getFileCacheByFile(request.context, fileKey , request.fileCacheFolder)
+ if (resBuf === undefined && request.onlyRetrieveFromCache != true) {
+ LogUtil.log("ImageKnife_DataTime_requestJob_httpRequest.start:"+request.src)
+ let httpRequest = http.createHttp();
+ let progress: number = 0
+ let arrayBuffers = new Array()
+ const headerObj: Record = {}
+ if (request.headers != undefined) {
+ request.headers.forEach((value) => {
+ headerObj[value.key] = value.value
+ })
+ } else if (request.allHeaders.size > 0) {
+ request.allHeaders.forEach((value, key) => {
+ headerObj[key] = value
+ })
+ }
+ httpRequest.on("dataReceive", (data: ArrayBuffer) => {
+ arrayBuffers.push(data)
+ });
+
+ if (request.isWatchProgress) {
+ httpRequest.on('dataReceiveProgress', (data: RequestData) => {
+ // 下载进度
+ if (data != undefined && (typeof data.receiveSize == 'number') && (typeof data.totalSize == 'number')) {
+ let percent = Math.round(((data.receiveSize * 1.0) / (data.totalSize * 1.0)) * 100)
+ if (progress !== percent) {
+ progress = percent
+ if (requestList === undefined) {
+ // 子线程
+ emitter.emit(Constants.PROGRESS_EMITTER + request.memoryKey, { data: { "value": progress } })
+ }else {
+ // 主线程请求
+ requestList!.forEach((requestWithSource: ImageKnifeRequestWithSource) => {
+ if (requestWithSource.request.imageKnifeOption.progressListener !== undefined && requestWithSource.source === ImageKnifeRequestSource.SRC) {
+ requestWithSource.request.imageKnifeOption.progressListener(progress)
+ }
+ })
+ }
+ }
+ }
+ })
+ }
+ let promise = httpRequest.requestInStream(request.src, {
+ header: headerObj,
+ method: http.RequestMethod.GET,
+ expectDataType: http.HttpDataType.ARRAY_BUFFER,
+ connectTimeout: 6000,
+ readTimeout: 6000,
+ // usingProtocol:http.HttpProtocol.HTTP1_1
+ // header: new Header('application/json')
+ });
+
+ await promise.then((data: number) => {
+ if (data == 200) {
+ resBuf = combineArrayBuffers(arrayBuffers)
+ } else {
+ loadError = "HttpDownloadClient has error, http code =" + JSON.stringify(data)
+ }
+ }).catch((err: Error) => {
+ loadError = err.message;
+ LogUtil.error("requestInStream ERROR : err = " + JSON.stringify(err));
+ });
+ LogUtil.log("ImageKnife_DataTime_requestJob_httpRequest.end:"+request.src)
+ // 保存文件缓存
+ if (resBuf !== undefined && request.writeCacheStrategy !== CacheStrategy.Memory) {
+ LogUtil.log("ImageKnife_DataTime_requestJob_saveFileCacheOnlyFile.start:"+request.src)
+ let copyBuf = combineArrayBuffers(arrayBuffers); // IDE有bug,不能直接获取resBuf.byteLength
+ bufferSize = copyBuf.byteLength
+ FileCache.saveFileCacheOnlyFile(request.context, fileKey, resBuf , request.fileCacheFolder)
+ LogUtil.log("ImageKnife_DataTime_requestJob_saveFileCacheOnlyFile.end:"+request.src)
+ }
+ }
+ else {
+ LogUtil.log("success get image from filecache for key = " + fileKey);
+ loadError = "success get image from filecache for key = " + fileKey;
+ }
+ } else if (request.src.startsWith('datashare://') || request.src.startsWith('file://')) {
+ await fs.open(request.src, fs.OpenMode.READ_ONLY).then(async (file) => {
+ await fs.stat(file.fd).then(async (stat) =>{
+ let buf = new ArrayBuffer(stat.size);
+ await fs.read(file.fd, buf).then((readLen) => {
+ resBuf = buf;
+ fs.close(file.fd);
+ }).catch((err:BusinessError) => {
+ loadError = 'LoadDataShareFileClient fs.read err happened uri=' + request.src + " err.msg=" + err?.message + " err.code=" + err?.code;
+ })
+ }).catch((err:BusinessError) => {
+ loadError = 'LoadDataShareFileClient fs.stat err happened uri=' + request.src + " err.msg=" + err?.message + " err.code=" + err?.code;
+ })
+ }).catch((err:BusinessError) => {
+ loadError ='LoadDataShareFileClient fs.open err happened uri=' + request.src + " err.msg=" + err?.message + " err.code=" + err?.code;
+ })
+ } else { //从本地文件获取
+ try {
+ let stat = fs.statSync(request.src);
+ if (stat.size > 0) {
+ let file = fs.openSync(request.src, fs.OpenMode.READ_ONLY);
+ resBuf = new ArrayBuffer(stat.size);
+ fs.readSync(file.fd, resBuf);
+ fs.closeSync(file);
+ }
+ } catch (err) {
+ if (typeof err == 'string') {
+ loadError = err;
+ } else {
+ loadError = err.message;
+ }
+ }
+ }
+ } else if ((request.src as Resource).id !== undefined) { //从资源文件获取
+ let res = request.src as Resource;
+ let manager = request.context.createModuleContext(res.moduleName).resourceManager
+ if (resBuf == undefined && request.onlyRetrieveFromCache != true && request.requestSource == ImageKnifeRequestSource.SRC) {
+ if(res.id == -1) {
+ let resName = (res.params![0] as string)
+ resBuf = (await manager.getMediaByName(resName.substring(10))).buffer as ArrayBuffer
+ } else {
+ resBuf = manager.getMediaContentSync(res.id).buffer as ArrayBuffer
+ }
+ } else if (resBuf == undefined && request.requestSource != ImageKnifeRequestSource.SRC) {
+ if(res.id == -1) {
+ let resName = (res.params![0] as string)
+ resBuf = (await manager.getMediaByName(resName.substring(10))).buffer as ArrayBuffer
+ } else {
+ resBuf = manager.getMediaContentSync(res.id).buffer as ArrayBuffer
+ }
+ }
+ }
+ }
+
+
+ if (resBuf == undefined) {
+ LogUtil.log("ImageKnife_DataTime_requestJob.end_undefined:"+request.src)
+ return {
+ pixelMap: undefined,
+ bufferSize: 0,
+ fileKey: '',
+ loadFail: loadError,
+ }
+ }
+ LogUtil.log("ImageKnife_DataTime_requestJob_createPixelMap.start:"+request.src)
+ let fileTypeUtil = new FileTypeUtil();
+ let typeValue = fileTypeUtil.getFileType(resBuf);
+ if(typeValue == null) {
+ return {
+ pixelMap: undefined,
+ bufferSize: 0,
+ fileKey: '',
+ loadFail: "request is not a valid image source",
+ }
+ }
+ let imageSource: image.ImageSource = image.createImageSource(resBuf);
+ let decodingOptions: image.DecodingOptions = {
+ editable: true,
+ }
+ if(request.isAnimator) {
+ if (typeValue === 'gif' || typeValue === 'webp') {
+ let pixelMapList: Array = []
+ let delayList: Array = []
+ await imageSource.createPixelMapList(decodingOptions).then(async (pixelList: Array) => {
+ //sdk的api接口发生变更:从.getDelayTime() 变为.getDelayTimeList()
+ await imageSource.getDelayTimeList().then(delayTimes => {
+ if (pixelList.length > 0) {
+ for (let i = 0; i < pixelList.length; i++) {
+ pixelMapList.push(pixelList[i]);
+ if (i < delayTimes.length) {
+ delayList.push(delayTimes[i]);
+ } else {
+ delayList.push(delayTimes[delayTimes.length - 1])
+ }
+ }
+ imageSource.release();
+ }
+ })
+ })
+ return {
+ pixelMap: "",
+ bufferSize: bufferSize,
+ fileKey: fileKey,
+ type: typeValue,
+ pixelMapList,
+ delayList
+ }
+ } else {
+ return {
+ pixelMap: undefined,
+ bufferSize: 0,
+ fileKey: '',
+ loadFail: "ImageKnifeAnimatorComponent组件仅支持动态图",
+ }
+ }
+ }
+ let resPixelmap: PixelMap | undefined = undefined
+ if (typeValue === 'gif' || typeValue === 'webp') {
+ let size = (await imageSource.getImageInfo()).size
+ let base64Help = new util.Base64Helper()
+
+ let base64str = "data:image/" + typeValue + ";base64," + base64Help.encodeToStringSync(new Uint8Array(resBuf))
+ LogUtil.log("ImageKnife_DataTime_requestJob_createPixelMap.end_GIF:"+request.src)
+ LogUtil.log("ImageKnife_DataTime_requestJob.end_GIF:"+request.src)
+ return {
+ pixelMap: base64str,
+ bufferSize: bufferSize,
+ fileKey: fileKey,
+ size:size,
+ type:typeValue
+ };
+ } else if(typeValue == "svg") {
+ let hValue = Math.round(request.componentHeight);
+ let wValue = Math.round(request.componentWidth);
+ let defaultSize: image.Size = {
+ height: vp2px(hValue),
+ width: vp2px(wValue)
+ };
+ let opts: image.DecodingOptions = {
+ editable: true,
+ desiredSize: defaultSize
+ };
+ await imageSource.createPixelMap(opts)
+ .then((pixelmap: PixelMap) => {
+ resPixelmap = pixelmap
+ imageSource.release()
+ })
+ return {
+ pixelMap: resPixelmap,
+ bufferSize: bufferSize,
+ fileKey: fileKey,
+ type:typeValue
+ };
+ }
+ let size = (await imageSource.getImageInfo()).size
+ await imageSource.createPixelMap(decodingOptions)
+ .then((pixelmap: PixelMap) => {
+ resPixelmap = pixelmap
+ imageSource.release()
+ })
+
+ // 图形变化
+ if (request.requestSource === ImageKnifeRequestSource.SRC && request.transformation !== undefined) {
+ resPixelmap = await request.transformation?.transform(request.context, resPixelmap!, request.componentWidth, request.componentHeight);
+ }
+ LogUtil.log("ImageKnife_DataTime_requestJob_createPixelMap.end:"+request.src)
+ LogUtil.log("ImageKnife_DataTime_requestJob.end:"+request.src)
+ return {
+ pixelMap: resPixelmap,
+ bufferSize: bufferSize,
+ fileKey: fileKey,
+ size:size,
+ type:typeValue
+ };
+}
+
diff --git a/library/src/main/ets/ImageKnifeOption.ets b/library/src/main/ets/ImageKnifeOption.ets
new file mode 100644
index 0000000..438849e
--- /dev/null
+++ b/library/src/main/ets/ImageKnifeOption.ets
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import taskpool from '@ohos.taskpool';
+import common from '@ohos.app.ability.common'
+import { CacheStrategy, ImageKnifeData,EventImage } from './model/ImageKnifeData';
+import { PixelMapTransformation } from './transform/PixelMapTransformation';
+import { drawing } from '@kit.ArkGraphics2D';
+
+export interface HeaderOptions {
+ key: string;
+ value: Object;
+}
+
+@Observed
+export class AnimatorOption {
+ @Track
+ state?: AnimationStatus = AnimationStatus.Running
+ @Track
+ iterations?: number = -1
+ @Track
+ reverse?: boolean = false
+}
+
+@Observed
+export class ImageKnifeOption {
+ // 主图资源
+ loadSrc: string | PixelMap | Resource = "";
+ // 占位图
+ placeholderSrc?: string | PixelMap | Resource;
+ // 失败占位图
+ errorholderSrc?: string | PixelMap | Resource;
+ headerOption?: Array;
+ // 自定义缓存关键字
+ signature?: string;
+ // 主图填充效果
+ objectFit?: ImageFit
+ // 占位图填充效果
+ placeholderObjectFit?: ImageFit
+ // 错误图填充效果
+ errorholderObjectFit?: ImageFit
+ customGetImage?: (context: Context, src: string | PixelMap | Resource) => Promise
+ border?: BorderOptions
+ // 缓存策略
+ writeCacheStrategy?: CacheStrategy
+ // 仅使用缓存加载数据
+ onlyRetrieveFromCache?: boolean = false;
+ priority?: taskpool.Priority = taskpool.Priority.LOW
+ context?: common.UIAbilityContext;
+ progressListener?: (progress: number) => void;
+ transformation?: PixelMapTransformation
+ onLoadListener?: OnLoadCallBack | undefined;
+ onComplete?:(event:EventImage | undefined) => void
+ drawingColorFilter?: ColorFilter | drawing.ColorFilter
+ constructor() {
+
+ }
+}
+
+/**
+ * 请求回调
+ */
+export interface OnLoadCallBack {
+ // 请求开始
+ onLoadStart?: () => void;
+
+ // 请求成功
+ onLoadSuccess?: (data: string | PixelMap | undefined, imageKnifeData: ImageKnifeData) => void;
+
+ // 请求结束
+ onLoadFailed?: (err: string) => void;
+ // 请求取消
+ onLoadCancel?: (reason: string) => void;
+}
\ No newline at end of file
diff --git a/library/src/main/ets/ImageKnifeRequest.ets b/library/src/main/ets/ImageKnifeRequest.ets
new file mode 100644
index 0000000..cb2115b
--- /dev/null
+++ b/library/src/main/ets/ImageKnifeRequest.ets
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { ImageKnifeOption } from './ImageKnifeOption';
+import common from '@ohos.app.ability.common';
+import { ImageKnifeRequestSource } from './model/ImageKnifeData';
+
+
+export class ImageKnifeRequest {
+ requestState: ImageKnifeRequestState = ImageKnifeRequestState.PROGRESS
+ componentWidth: number = 0
+ componentHeight: number = 0
+ drawPlayHolderSuccess: boolean = false
+ imageKnifeOption: ImageKnifeOption
+ context: common.UIAbilityContext
+ ImageKnifeRequestCallback: ImageKnifeRequestCallback
+ componentVersion: number = 0
+ headers: Map = new Map()
+ constructor(option: ImageKnifeOption,
+ uIAbilityContext: common.UIAbilityContext,
+ width: number,
+ height: number,
+ version: number,
+ ImageKnifeRequestCallback: ImageKnifeRequestCallback) {
+ this.imageKnifeOption = option
+ this.context = uIAbilityContext
+ this.componentWidth = width
+ this.componentHeight = height
+ this.componentVersion = version
+ this.ImageKnifeRequestCallback = ImageKnifeRequestCallback
+ }
+ // RequestOption调用header对于的方法
+ addHeader(key: string, value: Object) {
+ this.headers.set(key, value);
+ }
+
+ // 全局调用header对应的方法,包含RequestOption的形式
+ addHeaderMap(map: Map) {
+ map.forEach((value, key) => {
+ if (!this.headers.has(key)) {
+ this.addHeader(key, value);
+ }
+ })
+ }
+}
+
+export enum ImageKnifeRequestState {
+ PROGRESS,
+ COMPLETE,
+ ERROR,
+ DESTROY
+}
+
+
+export interface ImageKnifeRequestCallback {
+ showPixelMap: (version: number, pixelMap: PixelMap | string , requestSource: ImageKnifeRequestSource,imageAnimator?: Array) => void;
+}
diff --git a/library/src/main/ets/components/ImageKnifeAnimatorComponent.ets b/library/src/main/ets/components/ImageKnifeAnimatorComponent.ets
new file mode 100644
index 0000000..2e1671f
--- /dev/null
+++ b/library/src/main/ets/components/ImageKnifeAnimatorComponent.ets
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { AnimatorOption, ImageKnifeOption } from '../ImageKnifeOption';
+import { ImageKnifeRequest, ImageKnifeRequestState } from '../ImageKnifeRequest';
+import common from '@ohos.app.ability.common';
+import { ImageKnife } from '../ImageKnife';
+import { LogUtil } from '../utils/LogUtil';
+import { ImageKnifeRequestSource } from '../model/ImageKnifeData';
+
+@Component
+export struct ImageKnifeAnimatorComponent {
+ @Watch('watchImageKnifeOption') @ObjectLink imageKnifeOption: ImageKnifeOption;
+ @Watch('watchAnimatorOption') @State animatorOption: AnimatorOption = new AnimatorOption();
+ @State pixelMap: PixelMap | string | undefined = undefined
+ @State imageAnimator: Array | undefined = undefined
+ @State state: AnimationStatus = AnimationStatus.Running
+ @State iterations: number = -1
+ @State reverse: boolean = false
+ @State adaptiveWidth: Length = '100%'
+ @State adaptiveHeight: Length = '100%'
+ @State objectFit: ImageFit = ImageFit.Contain
+ private request: ImageKnifeRequest | undefined
+ private lastWidth: number = 0
+ private lastHeight: number = 0
+ private currentWidth: number = 0
+ private currentHeight: number = 0
+ private componentVersion: number = 0
+ private currentContext: common.UIAbilityContext | undefined = undefined
+
+ aboutToAppear(): void {
+ this.objectFit = this.imageKnifeOption.objectFit === undefined ? ImageFit.Contain : this.imageKnifeOption.objectFit
+ }
+
+ aboutToDisappear(): void {
+ if (this.request !== undefined) {
+ this.request.requestState = ImageKnifeRequestState.DESTROY
+ this.request = undefined
+ }
+ }
+
+ aboutToRecycle() {
+ if (this.request !== undefined) {
+ this.request.requestState = ImageKnifeRequestState.DESTROY
+ this.request = undefined
+ }
+ this.objectFit = this.imageKnifeOption.objectFit === undefined ? ImageFit.Contain : this.imageKnifeOption.objectFit
+ }
+
+ build() {
+ ImageAnimator()
+ .images(this.imageAnimator)
+ .width(this.adaptiveWidth)
+ .height(this.adaptiveHeight)
+ .border(this.imageKnifeOption.border)
+ .state(this.state)
+ .iterations(this.iterations)
+ .reverse(this.reverse)
+ .onSizeChange((oldValue:SizeOptions, newValue:SizeOptions) => {
+ this.currentWidth = newValue.width as number
+ this.currentHeight = newValue.height as number
+ this.lastWidth = oldValue.width as number
+ this.lastHeight = oldValue.height as number
+ if (this.currentWidth <= 0 || this.currentHeight <= 0) {
+ // 存在宽或者高为0,此次重回无意义,无需进行request请求
+ } else {
+ // 前提:宽高值均有效,值>0. 条件1:当前宽高与上一次宽高不同 条件2:当前是第一次绘制
+ if (this.currentHeight != this.lastHeight || this.currentWidth != this.lastWidth) {
+ LogUtil.log("execute request:width=" + this.currentWidth + " height= " + this.currentHeight)
+ ImageKnife.getInstance().execute(this.getRequest(this.currentWidth, this.currentHeight),true)
+ }
+ }
+ })
+ }
+
+ watchAnimatorOption(){
+ if(this.animatorOption.state != undefined) {
+ this.state = this.animatorOption.state
+ }
+ if(this.animatorOption.iterations != undefined) {
+ this.iterations = this.animatorOption.iterations
+ }
+ if(this.animatorOption.reverse != undefined) {
+ this.reverse = this.animatorOption.reverse
+ }
+ }
+
+ watchImageKnifeOption() {
+ if (this.request !== undefined) {
+ this.request.requestState = ImageKnifeRequestState.DESTROY
+ }
+ this.request = undefined
+ this.componentVersion++
+ ImageKnife.getInstance().execute(this.getRequest(this.currentWidth, this.currentHeight),true)
+ }
+
+ getCurrentContext(): common.UIAbilityContext {
+ if (this.currentContext == undefined) {
+ this.currentContext = getContext(this) as common.UIAbilityContext
+ }
+ return this.currentContext
+ }
+
+ getRequest(width: number, height: number): ImageKnifeRequest {
+ if (this.request == undefined) {
+ this.request = new ImageKnifeRequest(
+ this.imageKnifeOption,
+ this.imageKnifeOption.context !== undefined ? this.imageKnifeOption.context : this.getCurrentContext(),
+ width,
+ height,
+ this.componentVersion,
+ {
+ showPixelMap: async (version: number, pixelMap: PixelMap | string, requestSource: ImageKnifeRequestSource,imageAnimator?: Array) => {
+ if (version !== this.componentVersion) {
+ return //针对reuse场景,不显示历史图片
+ }
+ if (imageAnimator != undefined) {
+ this.imageAnimator = imageAnimator
+ } else {
+ this.imageAnimator = [
+ {
+ src: pixelMap
+ }
+ ]
+ }
+
+ if (requestSource == ImageKnifeRequestSource.SRC) {
+ this.objectFit =
+ this.imageKnifeOption.objectFit === undefined ? ImageFit.Contain : this.imageKnifeOption.objectFit
+ } else if (requestSource == ImageKnifeRequestSource.PLACE_HOLDER) {
+ this.objectFit =
+ this.imageKnifeOption.placeholderObjectFit === undefined ? (this.imageKnifeOption.objectFit === undefined ? ImageFit.Contain : this.imageKnifeOption.objectFit) : this.imageKnifeOption.placeholderObjectFit
+ } else {
+ this.objectFit =
+ this.imageKnifeOption.errorholderObjectFit === undefined ? (this.imageKnifeOption.objectFit === undefined ? ImageFit.Contain : this.imageKnifeOption.objectFit) : this.imageKnifeOption.errorholderObjectFit
+ }
+ }
+ })
+ }
+
+ return this.request
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/ets/components/ImageKnifeComponent.ets b/library/src/main/ets/components/ImageKnifeComponent.ets
new file mode 100644
index 0000000..57e7432
--- /dev/null
+++ b/library/src/main/ets/components/ImageKnifeComponent.ets
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { ImageKnifeOption } from '../ImageKnifeOption';
+import { ImageKnifeRequest, ImageKnifeRequestState } from '../ImageKnifeRequest';
+import common from '@ohos.app.ability.common';
+import { ImageKnife } from '../ImageKnife';
+import { LogUtil } from '../utils/LogUtil';
+import { ImageKnifeData, ImageKnifeRequestSource } from '../model/ImageKnifeData';
+import { IEngineKey } from '../key/IEngineKey';
+import { DefaultEngineKey } from '../key/DefaultEngineKey';
+
+@Component
+export struct ImageKnifeComponent {
+ @Watch('watchImageKnifeOption') @ObjectLink imageKnifeOption: ImageKnifeOption;
+ @State pixelMap: PixelMap | string | undefined = undefined
+ @State syncLoad: boolean = false
+ @State adaptiveWidth: Length = '100%'
+ @State adaptiveHeight: Length = '100%'
+ @State objectFit: ImageFit = ImageFit.Contain
+ private request: ImageKnifeRequest | undefined
+ private lastWidth: number = 0
+ private lastHeight: number = 0
+ private currentWidth: number = 0
+ private currentHeight: number = 0
+ private componentVersion: number = 0
+ private currentContext: common.UIAbilityContext | undefined = undefined
+
+ aboutToAppear(): void {
+ //闪动问题失效,注释相应代码后续修复
+ if(this.syncLoad) {
+ let engineKey: IEngineKey = new DefaultEngineKey();
+ let memoryCacheSrc: ImageKnifeData | undefined = ImageKnife.getInstance()
+ .loadFromMemoryCache(engineKey.generateMemoryKey(this.imageKnifeOption.loadSrc,ImageKnifeRequestSource.SRC,this.imageKnifeOption))
+ if (memoryCacheSrc !== undefined){
+ LogUtil.log("aboutToAppear load from memory cache for key = "+ engineKey.generateMemoryKey(this.imageKnifeOption.loadSrc,ImageKnifeRequestSource.SRC,this.imageKnifeOption))
+ //画主图
+ this.pixelMap = memoryCacheSrc.source;
+ }else {
+ let memoryCachePlace: ImageKnifeData | undefined = ImageKnife.getInstance()
+ .loadFromMemoryCache(engineKey.generateMemoryKey(this.imageKnifeOption.placeholderSrc!,ImageKnifeRequestSource.PLACE_HOLDER,this.imageKnifeOption))
+ if (memoryCachePlace !== undefined){
+ LogUtil.log("aboutToAppear load from memory cache for key = "+ engineKey.generateMemoryKey(this.imageKnifeOption.loadSrc,ImageKnifeRequestSource.SRC,this.imageKnifeOption))
+ //画主图
+ this.pixelMap = memoryCachePlace.source;
+ }
+ }
+ }
+ this.objectFit = this.imageKnifeOption.objectFit === undefined ? ImageFit.Contain : this.imageKnifeOption.objectFit
+ }
+
+ aboutToDisappear(): void {
+ if (this.request !== undefined) {
+ this.request.requestState = ImageKnifeRequestState.DESTROY
+ this.request = undefined
+ }
+ }
+
+ aboutToRecycle() {
+ if (this.request !== undefined) {
+ this.request.requestState = ImageKnifeRequestState.DESTROY
+ this.request = undefined
+ }
+ this.objectFit = this.imageKnifeOption.objectFit === undefined ? ImageFit.Contain : this.imageKnifeOption.objectFit
+ }
+
+ build() {
+ Image(this.pixelMap)
+ .colorFilter(this.imageKnifeOption.drawingColorFilter)
+ .objectFit(this.objectFit)
+ .width(this.adaptiveWidth)
+ .height(this.adaptiveHeight)
+ .border(this.imageKnifeOption.border)
+ .syncLoad(this.syncLoad)
+ .draggable(false)
+ .onComplete(this.imageKnifeOption.onComplete)
+ .onSizeChange((oldValue:SizeOptions, newValue:SizeOptions) => {
+ this.currentWidth = newValue.width as number
+ this.currentHeight = newValue.height as number
+ this.lastWidth = oldValue.width as number
+ this.lastHeight = oldValue.height as number
+ if (this.currentWidth <= 0 || this.currentHeight <= 0) {
+ // 存在宽或者高为0,此次重回无意义,无需进行request请求
+ } else {
+ // 前提:宽高值均有效,值>0. 条件1:当前宽高与上一次宽高不同 条件2:当前是第一次绘制
+ if (this.currentHeight != this.lastHeight || this.currentWidth != this.lastWidth) {
+ LogUtil.log("execute request:width=" + this.currentWidth + " height= " + this.currentHeight)
+ ImageKnife.getInstance().execute(this.getRequest(this.currentWidth, this.currentHeight))
+ }
+ }
+ })
+ }
+
+ watchImageKnifeOption() {
+ if (this.request !== undefined) {
+ this.request.requestState = ImageKnifeRequestState.DESTROY
+ }
+ this.request = undefined
+ this.componentVersion++
+ ImageKnife.getInstance().execute(this.getRequest(this.currentWidth, this.currentHeight))
+ }
+
+ getCurrentContext(): common.UIAbilityContext {
+ if (this.currentContext == undefined) {
+ this.currentContext = getContext(this) as common.UIAbilityContext
+ }
+ return this.currentContext
+ }
+
+ getRequest(width: number, height: number): ImageKnifeRequest {
+ if (this.request == undefined) {
+ this.request = new ImageKnifeRequest(
+ this.imageKnifeOption,
+ this.imageKnifeOption.context !== undefined ? this.imageKnifeOption.context : this.getCurrentContext(),
+ width,
+ height,
+ this.componentVersion,
+ {
+ showPixelMap: async (version: number, pixelMap: PixelMap | string, requestSource: ImageKnifeRequestSource) => {
+ if (version !== this.componentVersion) {
+ return //针对reuse场景,不显示历史图片
+ }
+ this.pixelMap = pixelMap
+ if (typeof this.pixelMap !== 'string') {
+ if (this.imageKnifeOption.objectFit === ImageFit.Auto) {
+ let info = await this.pixelMap.getImageInfo()
+
+ this.adaptiveWidth = this.currentWidth
+ this.adaptiveHeight = info.size.height * this.currentWidth / info.size.width
+
+ // if (this.currentWidth / this.currentHeight > info.size.width / info.size.height) {
+ // this.adaptiveWidth = this.currentWidth
+ // this.adaptiveHeight = info.size.height * this.currentWidth / this.currentHeight
+ // }
+ // else {
+ // this.adaptiveWidth = info.size.width * this.currentWidth / this.currentHeight
+ // this.adaptiveHeight = this.currentHeight
+ // }
+ }
+ } else {
+ //console.info("KKKKKKKKKKK:" + pixelMap)
+ }
+
+ if (requestSource == ImageKnifeRequestSource.SRC) {
+ this.objectFit =
+ this.imageKnifeOption.objectFit === undefined ? ImageFit.Contain : this.imageKnifeOption.objectFit
+ } else if (requestSource == ImageKnifeRequestSource.PLACE_HOLDER) {
+ this.objectFit =
+ this.imageKnifeOption.placeholderObjectFit === undefined ? (this.imageKnifeOption.objectFit === undefined ? ImageFit.Contain : this.imageKnifeOption.objectFit) : this.imageKnifeOption.placeholderObjectFit
+ } else {
+ this.objectFit =
+ this.imageKnifeOption.errorholderObjectFit === undefined ? (this.imageKnifeOption.objectFit === undefined ? ImageFit.Contain : this.imageKnifeOption.objectFit) : this.imageKnifeOption.errorholderObjectFit
+ }
+ }
+ })
+ }
+
+ return this.request
+ }
+}
+
+interface KeyCanvas {
+ keyId: string
+}
\ No newline at end of file
diff --git a/library/src/main/ets/key/DefaultEngineKey.ets b/library/src/main/ets/key/DefaultEngineKey.ets
new file mode 100644
index 0000000..6d43be1
--- /dev/null
+++ b/library/src/main/ets/key/DefaultEngineKey.ets
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { SparkMD5 } from '../3rd_party/sparkmd5/spark-md5';
+import { ImageKnifeOption } from '../ImageKnifeOption';
+import { IEngineKey } from './IEngineKey';
+import { PixelMapTransformation } from '../transform/PixelMapTransformation';
+import { ImageKnifeRequestSource } from '../model/ImageKnifeData';
+
+@Sendable
+export class DefaultEngineKey implements IEngineKey {
+ // 生成内存缓存key
+ generateMemoryKey(loadSrc: string | PixelMap | Resource, requestSource: ImageKnifeRequestSource,
+ imageKnifeOption: ImageKnifeOption,isAnimator?: boolean, width?: number, height?: number): string {
+ let key = (isAnimator == true ? "Animator=" : "loadSrc==") + (typeof loadSrc == "string" ? loadSrc : JSON.stringify(loadSrc)) + ";"
+ if (requestSource === ImageKnifeRequestSource.SRC) {
+ if (imageKnifeOption.signature !== undefined && imageKnifeOption.signature !== "") {
+ key += "signature=" + imageKnifeOption.signature + ";"
+ }
+ if (imageKnifeOption.transformation) {
+ key += "transformation=" + this.getTransformation(imageKnifeOption.transformation) + ";"
+ }
+ }
+ return key
+ }
+
+ // 生成文件缓存key
+ generateFileKey(loadSrc: string | PixelMap | Resource, signature?: string,isAnimator?: boolean): string {
+ let src = (isAnimator == true ? "Animator=" : "loadSrc==") + (typeof loadSrc == "string" ? loadSrc : JSON.stringify(loadSrc)) + ";"
+ if (signature !== undefined && signature !== "") {
+ src += "signature=" + signature + ";"
+ }
+ return SparkMD5.hashBinary(src)
+ }
+
+ private getTransformation(transformation: PixelMapTransformation): string {
+ return transformation.getName()
+ }
+}
+
+
+
diff --git a/library/src/main/ets/key/IEngineKey.ets b/library/src/main/ets/key/IEngineKey.ets
new file mode 100644
index 0000000..54be5bb
--- /dev/null
+++ b/library/src/main/ets/key/IEngineKey.ets
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { ImageKnifeOption } from '../ImageKnifeOption'
+import { ImageKnifeRequestSource } from '../model/ImageKnifeData'
+
+export interface IEngineKey {
+ // 生成内存缓存key
+ generateMemoryKey(loadSrc: string | PixelMap | Resource, requestSource: ImageKnifeRequestSource,
+ imageKnifeOption: ImageKnifeOption,isAnimator?: boolean, width?: number, height?: number): string
+
+ // 生成文件缓存key
+ generateFileKey(loadSrc: string | PixelMap | Resource, signature?: string,isAnimator?: boolean): string
+}
+
+
+
diff --git a/library/src/main/ets/model/ImageKnifeData.ets b/library/src/main/ets/model/ImageKnifeData.ets
new file mode 100644
index 0000000..79fe5d0
--- /dev/null
+++ b/library/src/main/ets/model/ImageKnifeData.ets
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { HeaderOptions } from '../ImageKnifeOption'
+import { ImageKnifeRequest } from '../ImageKnifeRequest'
+import { IEngineKey } from '../key/IEngineKey'
+import { PixelMapTransformation } from '../transform/PixelMapTransformation'
+import common from '@ohos.app.ability.common';
+import { Size } from '@kit.ArkUI'
+
+export interface ImageKnifeData {
+ source: PixelMap | string,
+ imageWidth: number,
+ imageHeight: number,
+ type?:string,
+ imageAnimator?: Array
+}
+/**
+ * onComplete成功回调
+ */
+export interface EventImage {
+ width: number;
+ height: number;
+ componentWidth: number;
+ componentHeight: number;
+ loadingStatus: number;
+ contentWidth: number;
+ contentHeight: number;
+ contentOffsetX: number;
+ contentOffsetY: number;
+}
+/**
+ * 缓存策略
+ */
+export enum CacheStrategy {
+ // 默认-写入/读取内存和文件缓存
+ Default = 0,
+ // 只写入/读取内存缓存
+ Memory = 1,
+ // 只写入/读取文件缓存
+ File = 2
+}
+
+/**
+ * 区分是src,placehodler,还是error_holder
+ */
+export enum ImageKnifeRequestSource {
+ SRC,
+ PLACE_HOLDER,
+ ERROR_HOLDER
+}
+
+
+export interface ImageKnifeRequestWithSource {
+ request: ImageKnifeRequest
+ source: ImageKnifeRequestSource
+}
+
+/**
+ * request子线程处理时的返回
+ */
+export interface RequestJobResult {
+ pixelMap: PixelMap | string | undefined
+ bufferSize: number
+ fileKey: string
+ loadFail?: string,
+ size?:Size,
+ type?: string,
+ pixelMapList?:Array,
+ delayList?: Array
+}
+
+/**
+ * request子线程处理时的请求参数
+ */
+export interface RequestJobRequest {
+ context: common.UIAbilityContext,
+ src: string | PixelMap | Resource,
+ headers?: Array,
+ allHeaders: Map,
+ componentWidth: number,
+ componentHeight: number,
+ customGetImage?: (context: Context, src: string | PixelMap | Resource) => Promise,
+ onlyRetrieveFromCache?: boolean
+ requestSource: ImageKnifeRequestSource
+ transformation?: PixelMapTransformation
+ writeCacheStrategy?: CacheStrategy
+ signature?: string
+ engineKey: IEngineKey
+ isWatchProgress: boolean
+ memoryKey: string
+ fileCacheFolder: string,
+ isAnimator?: boolean
+}
+
diff --git a/library/src/main/ets/model/utils.ets b/library/src/main/ets/model/utils.ets
new file mode 100644
index 0000000..d5d4269
--- /dev/null
+++ b/library/src/main/ets/model/utils.ets
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export function combineArrayBuffers(arrayBuffers: ArrayBuffer[]): ArrayBuffer {
+ // 计算多个ArrayBuffer的总字节大小
+ let totalByteLength = 0;
+ for (const arrayBuffer of arrayBuffers) {
+ totalByteLength += arrayBuffer.byteLength;
+ }
+
+ // 创建一个新的ArrayBuffer
+ const combinedArrayBuffer = new ArrayBuffer(totalByteLength);
+
+ // 创建一个Uint8Array来操作新的ArrayBuffer
+ const combinedUint8Array = new Uint8Array(combinedArrayBuffer);
+
+ // 依次复制每个ArrayBuffer的内容到新的ArrayBuffer中
+ let offset = 0;
+ for (const arrayBuffer of arrayBuffers) {
+ const sourceUint8Array = new Uint8Array(arrayBuffer);
+ combinedUint8Array.set(sourceUint8Array, offset);
+ offset += sourceUint8Array.length;
+ }
+
+ return combinedArrayBuffer;
+}
\ No newline at end of file
diff --git a/library/src/main/ets/transform/BaseTransformation.ets b/library/src/main/ets/transform/BaseTransformation.ets
new file mode 100644
index 0000000..ced2f06
--- /dev/null
+++ b/library/src/main/ets/transform/BaseTransformation.ets
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * 图片变换接口
+ */
+export interface BaseTransformation {
+
+ transform(context: Context, toTransform: T, width: number, height: number): Promise;
+
+ getName(): string
+}
\ No newline at end of file
diff --git a/library/src/main/ets/transform/BlurTransformation.ets b/library/src/main/ets/transform/BlurTransformation.ets
new file mode 100644
index 0000000..10af8cd
--- /dev/null
+++ b/library/src/main/ets/transform/BlurTransformation.ets
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { PixelMapTransformation } from './PixelMapTransformation';
+import effectKit from '@ohos.effectKit';
+
+/**
+ * 图片变换:模糊效果
+ */
+@Sendable
+export class BlurTransformation extends PixelMapTransformation {
+ private radius: number // 模糊半径,单位是像素。模糊效果与所设置的值成正比,值越大效果越明显。
+
+ constructor(radius: number) {
+ super()
+ this.radius = radius
+ }
+
+ getName(): string {
+ return this.constructor.name + ';radius:' + this.radius;
+ }
+
+ async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise {
+ let headFilter = effectKit.createEffect(toTransform);
+ if (headFilter != null) {
+ return await headFilter.blur(this.radius).getEffectPixelMap()
+ }
+ return toTransform
+ }
+}
+
diff --git a/library/src/main/ets/transform/BrightnessTransformation.ets b/library/src/main/ets/transform/BrightnessTransformation.ets
new file mode 100644
index 0000000..a263ec9
--- /dev/null
+++ b/library/src/main/ets/transform/BrightnessTransformation.ets
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { PixelMapTransformation } from './PixelMapTransformation';
+import effectKit from '@ohos.effectKit';
+
+/**
+ * 图片变换:高亮效果
+ */
+@Sendable
+export class BrightnessTransformation extends PixelMapTransformation {
+ private bright: number // 高亮程度,取值范围在0-1之间,取值为0时图像保持不变。
+
+ constructor(bright: number) {
+ super()
+ this.bright = bright
+ }
+
+ getName(): string {
+ return this.constructor.name + ';bright:' + this.bright;
+ }
+
+ async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise {
+ let headFilter = effectKit.createEffect(toTransform);
+ if (headFilter != null) {
+ return await headFilter.brightness(this.bright).getEffectPixelMap()
+ }
+ return toTransform
+ }
+}
+
diff --git a/library/src/main/ets/transform/CropCircleTransformation.ets b/library/src/main/ets/transform/CropCircleTransformation.ets
new file mode 100644
index 0000000..707ecbf
--- /dev/null
+++ b/library/src/main/ets/transform/CropCircleTransformation.ets
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { PixelMapTransformation } from './PixelMapTransformation';
+import { Size } from '@kit.ArkUI';
+import { image } from '@kit.ImageKit';
+
+/**
+ * 图片变换:圆形裁剪效果
+ */
+@Sendable
+export class CropCircleTransformation extends PixelMapTransformation {
+ private mCenterX: number = 0;
+ private mCenterY: number = 0;
+ private mRadius: number = 0;
+
+ constructor() {
+ super();
+ }
+
+ getName(): string {
+ return this.constructor.name + ';mCenterX:' + this.mCenterX + ';mCenterY:' + this.mCenterY + ';mRadius:' + this.mRadius;
+ }
+
+ async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise {
+ return await this.transformCircle(toTransform);
+ }
+
+ private async transformCircle(data: PixelMap): Promise {
+ let imageInfo: image.ImageInfo = await data.getImageInfo();
+ let size: Size = {
+ width: imageInfo.size.width,
+ height: imageInfo.size.height
+ };
+ if (!size) {
+ console.error("CropCircleTransformation The image size does not exist.");
+ return data;
+ }
+ let height: number = size.height;
+ let width: number = size.width;
+ this.mRadius = 0;
+ if (width > height) {
+ this.mRadius = height / 2;
+ } else {
+ this.mRadius = width / 2;
+ }
+ this.mCenterX = width / 2;
+ this.mCenterY = height / 2;
+
+ let bufferData: ArrayBuffer = new ArrayBuffer(data.getPixelBytesNumber());
+ await data.readPixelsToBuffer(bufferData);
+
+ let dataArray = new Uint8Array(bufferData);
+
+ for (let h = 0; h <= height; h++) {
+ for (let w = 0; w <= width; w++) {
+ if (this.isContainsCircle(w, h)) {
+ continue;
+ }
+ // 针对的点
+ let index = (h * width + w) * 4;
+ dataArray[index] = 0;
+ dataArray[index+1] = 0;
+ dataArray[index+2] = 0;
+ dataArray[index+3] = 0;
+ }
+ }
+ await data.writeBufferToPixels(bufferData);
+ return data;
+ }
+
+ isContainsCircle(x: number, y: number): boolean {
+ let a = Math.pow((this.mCenterX - x), 2);
+ let b = Math.pow((this.mCenterY - y), 2);
+ let c = Math.sqrt((a + b));
+ return c <= this.mRadius;
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/ets/transform/CropCircleWithBorderTransformation.ets b/library/src/main/ets/transform/CropCircleWithBorderTransformation.ets
new file mode 100644
index 0000000..bad7336
--- /dev/null
+++ b/library/src/main/ets/transform/CropCircleWithBorderTransformation.ets
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { PixelMapTransformation } from './PixelMapTransformation';
+import { Size } from '@kit.ArkUI';
+import { image } from '@kit.ImageKit';
+
+export interface rgbColor {
+ r_color: number,
+ g_color: number,
+ b_color: number,
+}
+
+/**
+ * 图片变换:圆环裁剪效果
+ */
+@Sendable
+export class CropCircleWithBorderTransformation extends PixelMapTransformation {
+ private mBorderSize: number = 5;
+ private mCenterX: number = 0;
+ private mCenterY: number = 0;
+ private mRadius: number = 0;
+ private mRColor: number = 0;
+ private mGColor: number = 0;
+ private mBColor: number = 0;
+
+ constructor(borderSize: number, value: rgbColor) {
+ super();
+ this.mRColor = value.g_color;
+ this.mGColor = value.g_color;
+ this.mBColor = value.b_color;
+ this.mBorderSize = borderSize;
+ }
+
+ getConstructorParams() {
+ return JSON.stringify([this.mBorderSize, {
+ r_color: this.mRColor,
+ g_color: this.mGColor,
+ b_color: this.mBColor
+ }]);
+ }
+
+ getName(): string {
+ return this.constructor.name + ';mBorderSize:' + this.mBorderSize + ';mCenterX:' + this.mCenterX + ';mCenterY:'
+ + this.mCenterY + ';mRadius:' + this.mRadius + ';mRColor:' + this.mRColor + ';mGColor:' + this.mGColor + ';mBColor:' + this.mBColor;
+ }
+
+ async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise {
+ return await this.transformPixelMap(toTransform);
+ }
+
+ private async transformPixelMap(pixelMap: PixelMap): Promise {
+ let imageInfo: image.ImageInfo = await pixelMap.getImageInfo();
+ let size: Size = {
+ width: imageInfo.size.width,
+ height: imageInfo.size.height
+ };
+ if (!size) {
+ console.error("CropCircleWithBorderTransformation The image size does not exist.");
+ return pixelMap;
+ }
+ let height: number = size.height;
+ let width: number = size.width;
+ this.mRadius = 0;
+ if (width > height) {
+ this.mRadius = height / 2;
+ } else {
+ this.mRadius = width / 2;
+ }
+ this.mCenterX = width / 2;
+ this.mCenterY = height / 2;
+
+
+ let bufferData = new ArrayBuffer(pixelMap.getPixelBytesNumber());
+ await pixelMap.readPixelsToBuffer(bufferData);
+
+ let dataArray = new Uint8Array(bufferData);
+
+ for (let h = 0; h <= height; h++) {
+ for (let w = 0; w <= width; w++) {
+ // 不在大圆之内的设置透明
+ // 在大圆与小圆之间的 设置rgb值
+ // 小圆之内的不变
+ let isSmallCircle: boolean = this.isContainsSmallCircle(w, h);
+ let isBigCircle: boolean = this.isContainsCircle(w, h);
+ if (isSmallCircle) {
+ continue;
+ }
+
+ let index = (h * width + w) * 4;
+ if (!isBigCircle) {
+ // 设置透明
+ dataArray[index] = 0;
+ dataArray[index+1] = 0;
+ dataArray[index+2] = 0;
+ dataArray[index+3] = 0;
+ } else {
+ // 设置broke
+ dataArray[index] = this.mRColor;
+ dataArray[index+1] = this.mGColor;
+ dataArray[index+2] = this.mBColor;
+ }
+ }
+ }
+ await pixelMap.writeBufferToPixels(bufferData);
+ return pixelMap;
+ }
+
+ isContainsCircle(x: number, y: number): boolean {
+ let a: number = Math.pow((this.mCenterX - x), 2);
+ let b: number = Math.pow((this.mCenterY - y), 2);
+ let c: number = Math.sqrt((a + b));
+ return c <= this.mRadius;
+ }
+
+ isContainsSmallCircle(x: number, y: number): boolean {
+ let a: number = Math.pow((this.mCenterX - x), 2);
+ let b: number = Math.pow((this.mCenterY - y), 2);
+ let c: number = Math.sqrt((a + b));
+ return c <= (this.mRadius - this.mBorderSize);
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/ets/transform/CropSquareTransformation.ets b/library/src/main/ets/transform/CropSquareTransformation.ets
new file mode 100644
index 0000000..f939845
--- /dev/null
+++ b/library/src/main/ets/transform/CropSquareTransformation.ets
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { PixelMapTransformation } from './PixelMapTransformation';
+import { Size } from '@kit.ArkUI';
+import { image } from '@kit.ImageKit';
+
+/**
+ * 图片变换:正方形裁剪效果
+ */
+@Sendable
+export class CropSquareTransformation extends PixelMapTransformation {
+ constructor() {
+ super();
+ }
+
+ async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise {
+ let imageInfo: image.ImageInfo = await toTransform.getImageInfo();
+ let size: Size = {
+ width: imageInfo.size.width,
+ height: imageInfo.size.height
+ };
+ if (!size) {
+ console.error("CropSquareTransformation The image size does not exist.");
+ return toTransform;
+ }
+ let pixelMapWidth: number = size.width;
+ let pixelMapHeight: number = size.height;
+ let targetSize: number = pixelMapWidth > pixelMapHeight ? pixelMapHeight : pixelMapWidth;
+ let region: image.Region = {
+ size: { width: targetSize, height: targetSize },
+ x: pixelMapWidth / 2 - targetSize / 2,
+ y: pixelMapHeight / 2 - targetSize / 2
+ };
+ await toTransform.crop(region);
+ return toTransform;
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/ets/transform/CropTransformation.ets b/library/src/main/ets/transform/CropTransformation.ets
new file mode 100644
index 0000000..1d63651
--- /dev/null
+++ b/library/src/main/ets/transform/CropTransformation.ets
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { PixelMapTransformation } from './PixelMapTransformation';
+import { Size } from '@kit.ArkUI';
+import { image } from '@kit.ImageKit';
+
+/**
+ * 图片变换:自定义裁剪效果
+ */
+@Sendable
+export class CropTransformation extends PixelMapTransformation {
+ private mWidth: number = 0;
+ private mHeight: number = 0;
+ private mCropType: number = 0;
+
+ constructor(width: number, height: number, cropType: number) {
+ super();
+ this.mWidth = width;
+ this.mHeight = height;
+ this.mCropType = cropType;
+ }
+
+ getName(): string {
+ return this.constructor.name + ";mWidth:" + this.mWidth + ";mHeight:" + this.mHeight + ";mCropType:" + this.mCropType;
+ }
+
+ async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise {
+ let imageInfo: image.ImageInfo = await toTransform.getImageInfo();
+ let size: Size = {
+ width: imageInfo.size.width,
+ height: imageInfo.size.height
+ };
+ if (!size) {
+ console.error("CropTransformation The image size does not exist.");
+ return toTransform;
+ }
+ let pixelMapWidth: number = size.width;
+ let pixelMapHeight: number = size.height;
+ this.mWidth = this.mWidth == 0 ? pixelMapWidth : this.mWidth;
+ this.mHeight = this.mHeight == 0 ? pixelMapHeight : this.mHeight;
+ let scaleX: number = this.mWidth / pixelMapWidth;
+ let scaleY: number = this.mHeight / pixelMapHeight;
+ let scale: number = Math.max(scaleX, scaleY);
+ let scaledWidth: number = scale * pixelMapWidth;
+ let scaledHeight: number = scale * pixelMapHeight;
+ let left: number = (this.mWidth - scaledWidth) / 2;
+ let top: number = Math.abs(this.getTop(pixelMapHeight));
+ let region: image.Region = {
+ size: {
+ width: scaledWidth > pixelMapWidth ? pixelMapWidth : scaledWidth,
+ height: scaledHeight > pixelMapHeight ? pixelMapHeight : scaledHeight
+ },
+ x: left < 0 ? 0 : left,
+ y: top < 0 ? 0 : top
+ };
+ toTransform.cropSync(region);
+ return toTransform;
+ }
+
+ private getTop(scaledHeight: number): number {
+ switch (this.mCropType) {
+ case 0:
+ return 0;
+ case 1:
+ return (this.mHeight - scaledHeight) / 2;
+ case 2:
+ return this.mHeight - scaledHeight;
+ default:
+ return 0;
+ }
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/ets/transform/GrayScaleTransformation.ets b/library/src/main/ets/transform/GrayScaleTransformation.ets
new file mode 100644
index 0000000..eb13e48
--- /dev/null
+++ b/library/src/main/ets/transform/GrayScaleTransformation.ets
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { PixelMapTransformation } from './PixelMapTransformation';
+import { effectKit } from '@kit.ArkGraphics2D';
+
+/**
+ * 图片变换:灰化效果
+ */
+@Sendable
+export class GrayScaleTransformation extends PixelMapTransformation {
+ constructor() {
+ super();
+ }
+
+ async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise {
+ let headFilter = effectKit.createEffect(toTransform);
+ if (headFilter != null) {
+ return await headFilter.grayscale().getEffectPixelMap();
+ }
+ return toTransform;
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/ets/transform/InvertTransformation.ets b/library/src/main/ets/transform/InvertTransformation.ets
new file mode 100644
index 0000000..40e5e32
--- /dev/null
+++ b/library/src/main/ets/transform/InvertTransformation.ets
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { PixelMapTransformation } from './PixelMapTransformation';
+import { effectKit } from '@kit.ArkGraphics2D';
+
+/**
+ * 图片变换:反转效果
+ */
+@Sendable
+export class InvertTransformation extends PixelMapTransformation {
+ constructor() {
+ super();
+ }
+
+ async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise {
+ let headFilter = effectKit.createEffect(toTransform);
+ if (headFilter != null) {
+ return await headFilter.invert().getEffectPixelMap();
+ }
+ return toTransform;
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/ets/transform/KuwaharaTransformation.ets b/library/src/main/ets/transform/KuwaharaTransformation.ets
new file mode 100644
index 0000000..4baf79b
--- /dev/null
+++ b/library/src/main/ets/transform/KuwaharaTransformation.ets
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { GPUImageKuwaharaFilter } from '@ohos/gpu_transform';
+import { PixelMapTransformation } from './PixelMapTransformation';
+import { image } from '@kit.ImageKit';
+
+/**
+ * 图片变换:桑原滤波效果
+ */
+@Sendable
+export class KuwaharaTransformation extends PixelMapTransformation {
+ private radius: number;
+
+ constructor(radius: number) {
+ super();
+ this.radius = radius;
+ }
+
+ getName(): string {
+ return this.constructor.name + ';radius:' + this.radius;
+ }
+
+ async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise {
+ let imageInfo: image.ImageInfo = await toTransform.getImageInfo();
+ if (!imageInfo.size) {
+ console.error("KuwaharaTransformation The image size does not exist.");
+ return toTransform;
+ }
+ return await this.kuwaharaGpu(toTransform, imageInfo.size.width, imageInfo.size.height);
+ }
+
+ private async kuwaharaGpu(bitmap: PixelMap, targetWidth: number, targetHeight: number): Promise {
+ let bufferData = new ArrayBuffer(bitmap.getPixelBytesNumber());
+ await bitmap.readPixelsToBuffer(bufferData);
+ let filter = new GPUImageKuwaharaFilter();
+ filter.setImageData(bufferData, targetWidth, targetHeight);
+ filter.setRadius(this.radius);
+ let buf = await filter.getPixelMapBuf(0, 0, targetWidth, targetHeight);
+ await bitmap.writeBufferToPixels(buf);
+ return bitmap;
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/ets/transform/MaskTransformation.ets b/library/src/main/ets/transform/MaskTransformation.ets
new file mode 100644
index 0000000..a715cf1
--- /dev/null
+++ b/library/src/main/ets/transform/MaskTransformation.ets
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { PixelMapTransformation } from './PixelMapTransformation';
+import { Size } from '@kit.ArkUI';
+import { image } from '@kit.ImageKit';
+import { resourceManager } from '@kit.LocalizationKit';
+import { CalculatePixelUtils } from '../utils/CalculatePixelUtils';
+import { ColorUtils } from '../utils/ColorUtils';
+import { PixelEntry } from './entry/PixelEntry';
+
+/**
+ * 图片变换:遮罩效果
+ */
+@Sendable
+export class MaskTransformation extends PixelMapTransformation {
+ private mResourceId: number;
+ private mResourceModuleName: string;
+
+ constructor(resource: Resource) {
+ super();
+ this.mResourceId = resource.id;
+ this.mResourceModuleName = resource.moduleName;
+ }
+
+ getName(): string {
+ return this.constructor.name + ';resourceId:' + this.mResourceId + ';resourceModuleName:' + this.mResourceModuleName;
+ }
+
+ async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise {
+ let imageInfo = await toTransform.getImageInfo();
+ let size: Size = {
+ width: imageInfo.size.width,
+ height: imageInfo.size.height
+ };
+ if (!size) {
+ console.error("MaskTransformation The image size does not exist.");
+ return toTransform;
+ }
+ let pixelMapWidth: number = size.width;
+ let pixelMapHeight: number = size.height;
+ let targetWidth: number = width;
+ let targetHeight: number = height;
+ if (pixelMapWidth > targetWidth && pixelMapHeight > targetHeight) {
+ let scale = Math.max(targetWidth / pixelMapWidth, targetHeight / pixelMapHeight);
+ await toTransform.scale(scale, scale);
+ return await this.openInternal(context, toTransform, scale * pixelMapWidth, scale * pixelMapHeight);
+ }
+ return await this.openInternal(context, toTransform, size.width, size.height);
+ }
+
+ private async openInternal(context: Context, bitmap: PixelMap, width: number, height: number): Promise {
+ if (context == undefined) {
+ console.error("MaskTransformation openInternal the context is undefined.");
+ return bitmap;
+ }
+ let moduleContext = context.createModuleContext(this.mResourceModuleName);
+ if (moduleContext == undefined) {
+ console.error("MaskTransformation openInternal the moduleContext is undefined.");
+ return bitmap;
+ }
+ let resourceManager = moduleContext.resourceManager as resourceManager.ResourceManager;
+ if (resourceManager == undefined) {
+ console.error("MaskTransformation openInternal the resourceManager is undefined.");
+ return bitmap;
+ }
+ let array: Uint8Array = await resourceManager.getMediaContent(this.mResourceId);
+ let buffer = array.buffer.slice(array.byteOffset, array.byteLength + array.byteOffset);
+ let imageSource: image.ImageSource = image.createImageSource(buffer);
+ let options: image.DecodingOptions = {
+ editable: true,
+ desiredSize: {
+ width: width,
+ height: height
+ }
+ };
+ let maskBitmap: PixelMap = await imageSource.createPixelMap(options);
+ return await this.mask(bitmap, maskBitmap);
+ }
+
+ async mask(bitmap: PixelMap, maskBitmap: PixelMap): Promise {
+ let imageInfo = await bitmap.getImageInfo();
+ let size: Size = {
+ width: imageInfo.size.width,
+ height: imageInfo.size.height
+ };
+ if (!size) {
+ console.error("MaskTransformation mask the image size does not exist.");
+ return bitmap;
+ }
+ let width = size.width;
+ let height = size.height;
+ let rgbData = CalculatePixelUtils.createInt2DArray(height, width);
+ let bufferData = new ArrayBuffer(bitmap.getPixelBytesNumber());
+ await bitmap.readPixelsToBuffer(bufferData);
+ let dataArray = new Uint8Array(bufferData);
+ let ph = 0;
+ let pw = 0;
+ for (let index = 0; index < dataArray.length; index += 4) {
+ const r = dataArray[index];
+ const g = dataArray[index+1];
+ const b = dataArray[index+2];
+ const f = dataArray[index+3];
+ let entry = new PixelEntry();
+ entry.a = 0;
+ entry.b = b;
+ entry.g = g;
+ entry.r = r;
+ entry.f = f;
+ entry.pixel = ColorUtils.rgb(entry.r, entry.g, entry.b);
+ rgbData[ph][pw] = ColorUtils.rgb(entry.r, entry.g, entry.b);
+ if (pw == width - 1) {
+ pw = 0;
+ ph++;
+ } else {
+ pw++;
+ }
+ }
+ let imageInfoMask = await maskBitmap.getImageInfo();
+ let sizeMask: Size = {
+ width: imageInfoMask.size.width,
+ height: imageInfoMask.size.height
+ };
+ if (!sizeMask) {
+ console.error("MaskTransformation mask the sizeMask size does not exist.");
+ return bitmap;
+ }
+ let widthMask = sizeMask.width;
+ let heightMask = sizeMask.height;
+ let rgbDataMask = CalculatePixelUtils.createInt2DArray(heightMask, widthMask);
+ let pixEntry: Array = new Array();
+ let bufferDataM = new ArrayBuffer(maskBitmap.getPixelBytesNumber());
+ await maskBitmap.readPixelsToBuffer(bufferDataM);
+ let dataArrayM = new Uint8Array(bufferDataM);
+ let phM = 0;
+ let pwM = 0;
+ for (let index = 0; index < dataArrayM.length; index += 4) {
+ const r = dataArrayM[index];
+ const g = dataArrayM[index+1];
+ const b = dataArrayM[index+2];
+ const f = dataArrayM[index+3];
+ let entry = new PixelEntry();
+ entry.a = 0;
+ entry.b = b;
+ entry.g = g;
+ entry.r = r;
+ entry.f = f;
+ entry.pixel = ColorUtils.rgb(entry.r, entry.g, entry.b);
+ pixEntry.push(entry);
+ if (entry.r == 0 && entry.g == 0 && entry.b == 0) {
+ rgbDataMask[phM][pwM] = rgbData[phM][pwM];
+ } else {
+ rgbDataMask[phM][pwM] = ColorUtils.rgb(entry.r, entry.g, entry.b);
+ }
+ if (pwM == widthMask - 1) {
+ pwM = 0;
+ phM++;
+ } else {
+ pwM++;
+ }
+ }
+ let bufferNewData = new ArrayBuffer(maskBitmap.getPixelBytesNumber());
+ let dataNewArray = new Uint8Array(bufferNewData);
+ let index = 0;
+ let mh = 0;
+ let nw = 0;
+ for (let i = 0; i < dataNewArray.length; i += 4) {
+ let pixel1 = rgbDataMask[mh][nw];
+ if (nw == widthMask - 1) {
+ nw = 0;
+ mh++;
+ } else {
+ nw++;
+ }
+ let pR = ColorUtils.red(pixel1);
+ let pG = ColorUtils.green(pixel1);
+ let pB = ColorUtils.blue(pixel1);
+ dataNewArray[i] = pR;
+ dataNewArray[i+1] = pG;
+ dataNewArray[i+2] = pB;
+ dataNewArray[i+3] = pixEntry[index].f;
+ index++;
+ }
+ await maskBitmap.writeBufferToPixels(bufferNewData);
+ return maskBitmap;
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/ets/transform/MultiTransTransformation.ets b/library/src/main/ets/transform/MultiTransTransformation.ets
new file mode 100644
index 0000000..459afbc
--- /dev/null
+++ b/library/src/main/ets/transform/MultiTransTransformation.ets
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { PixelMapTransformation } from './PixelMapTransformation';
+import { collections } from '@kit.ArkTS';
+
+/**
+ * 多个图片变换
+ */
+@Sendable
+export class MultiTransTransformation extends PixelMapTransformation {
+ private transformations: collections.Array
+
+ constructor(transformations: collections.Array) {
+ super()
+ this.transformations = transformations
+ }
+
+ async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise {
+ let res = toTransform
+ for (let i = 0; i < this.transformations.length; i++) {
+ res = await this.transformations[i].transform(context, res, width, height)
+ }
+ return res
+ }
+
+ getName(): string {
+ let res: string = ""
+ this.transformations.forEach((transformation) => {
+ res += transformation.getName() + "&"
+ })
+ return res
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/ets/transform/PixelMapTransformation.ets b/library/src/main/ets/transform/PixelMapTransformation.ets
new file mode 100644
index 0000000..ca002a6
--- /dev/null
+++ b/library/src/main/ets/transform/PixelMapTransformation.ets
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { BaseTransformation } from './BaseTransformation';
+
+/**
+ * 基于PixelMap的图片变换
+ */
+@Sendable
+export abstract class PixelMapTransformation implements BaseTransformation{
+ transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise {
+ throw new Error('Method not implemented.');
+ }
+
+ getName(): string {
+ return this.constructor.name
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/ets/transform/PixelationTransformation.ets b/library/src/main/ets/transform/PixelationTransformation.ets
new file mode 100644
index 0000000..2a32d78
--- /dev/null
+++ b/library/src/main/ets/transform/PixelationTransformation.ets
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { GPUImagePixelationFilter } from '@ohos/gpu_transform';
+import { PixelMapTransformation } from './PixelMapTransformation';
+import { image } from '@kit.ImageKit';
+
+/**
+ * Applies a Pixelation effect to the image.
+ * The pixel with a default of 10.0.
+ */
+@Sendable
+export class PixelationTransformation extends PixelMapTransformation {
+ private mPixel: number = 10.0;
+
+ constructor(pixel?: number) {
+ super();
+ if (pixel) {
+ this.mPixel = pixel;
+ }
+ }
+
+ getName(): string {
+ return this.constructor.name + ';pixel:' + this.mPixel;
+ }
+
+
+ async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise {
+ let imageInfo: image.ImageInfo = await toTransform.getImageInfo();
+ if (!imageInfo.size) {
+ console.error("PixelationTransformation The image size does not exist.");
+ return toTransform;
+ }
+ return await this.pixelGPU(toTransform, imageInfo.size.width, imageInfo.size.height);
+ }
+
+ private async pixelGPU(bitmap: PixelMap, targetWidth: number, targetHeight: number): Promise {
+ let bufferData = new ArrayBuffer(bitmap.getPixelBytesNumber());
+ await bitmap.readPixelsToBuffer(bufferData);
+ let filter = new GPUImagePixelationFilter();
+ filter.setImageData(bufferData, targetWidth, targetHeight);
+ filter.setPixel(this.mPixel);
+ let buf = await filter.getPixelMapBuf(0, 0, targetWidth, targetHeight);
+ await bitmap.writeBufferToPixels(buf);
+ return bitmap;
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/ets/transform/SepiaTransformation.ets b/library/src/main/ets/transform/SepiaTransformation.ets
new file mode 100644
index 0000000..153d69d
--- /dev/null
+++ b/library/src/main/ets/transform/SepiaTransformation.ets
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { GPUImageSepiaToneFilter } from '@ohos/gpu_transform';
+import { PixelMapTransformation } from './PixelMapTransformation';
+import { image } from '@kit.ImageKit';
+
+/**
+ * 图片变换:乌墨色滤波效果
+ */
+@Sendable
+export class SepiaTransformation extends PixelMapTransformation {
+ constructor() {
+ super();
+ }
+
+ async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise {
+ let imageInfo: image.ImageInfo = await toTransform.getImageInfo();
+ if (!imageInfo.size) {
+ console.error("SepiaTransformation The image size does not exist.");
+ return toTransform;
+ }
+ return await this.sepiaGPU(toTransform, imageInfo.size.width, imageInfo.size.height);
+ }
+
+ private async sepiaGPU(bitmap: PixelMap, targetWidth: number, targetHeight: number): Promise {
+ let bufferData = new ArrayBuffer(bitmap.getPixelBytesNumber());
+ await bitmap.readPixelsToBuffer(bufferData);
+ let filter = new GPUImageSepiaToneFilter();
+ filter.setImageData(bufferData, targetWidth, targetHeight);
+ let buf = await filter.getPixelMapBuf(0, 0, targetWidth, targetHeight);
+ await bitmap.writeBufferToPixels(buf);
+ return bitmap;
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/ets/transform/SketchTransformation.ets b/library/src/main/ets/transform/SketchTransformation.ets
new file mode 100644
index 0000000..2f8933d
--- /dev/null
+++ b/library/src/main/ets/transform/SketchTransformation.ets
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { GPUImageSketchFilter } from '@ohos/gpu_transform';
+import { PixelMapTransformation } from './PixelMapTransformation';
+import { image } from '@kit.ImageKit';
+
+/**
+ * 图片变换:素描效果
+ */
+@Sendable
+export class SketchTransformation extends PixelMapTransformation {
+ constructor() {
+ super();
+ }
+
+ async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise {
+ let imageInfo: image.ImageInfo = await toTransform.getImageInfo();
+ if (!imageInfo.size) {
+ console.error("SketchTransformation The image size does not exist.");
+ return toTransform;
+ }
+ return await this.sketchGPU(toTransform, imageInfo.size.width, imageInfo.size.height);
+ }
+
+ private async sketchGPU(bitmap: PixelMap, targetWidth: number, targetHeight: number): Promise {
+ let bufferData = new ArrayBuffer(bitmap.getPixelBytesNumber());
+ await bitmap.readPixelsToBuffer(bufferData);
+ let filter = new GPUImageSketchFilter();
+ filter.setImageData(bufferData, targetWidth, targetHeight);
+ let buf = await filter.getPixelMapBuf(0, 0, targetWidth, targetHeight);
+ await bitmap.writeBufferToPixels(buf);
+ return bitmap;
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/ets/transform/SwirlTransformation.ets b/library/src/main/ets/transform/SwirlTransformation.ets
new file mode 100644
index 0000000..1cebc74
--- /dev/null
+++ b/library/src/main/ets/transform/SwirlTransformation.ets
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { GPUImageSwirlFilter } from '@ohos/gpu_transform';
+import { PixelMapTransformation } from './PixelMapTransformation';
+import { image } from '@kit.ImageKit';
+
+/**
+ * 图片变换:扭曲效果
+ */
+@Sendable
+export class SwirlTransformation extends PixelMapTransformation {
+ // degree:值越大范围越大
+ private mDegree: number = 0;
+ // 取值范围 0.0 ~ 1.0
+ private mAngle: number = 0.9;
+ // 在图中的位置 取值范围 0.0 ~ 1.0
+ private mXCenter: number = 0.5;
+ // 在图中的位置 取值范围 0.0 ~ 1.0
+ private mYCenter: number = 0.5;
+
+ constructor(degree: number, angle?: number, centerPoint?: Array) {
+ super();
+ this.mDegree = degree;
+ if (angle) {
+ this.mAngle = angle;
+ }
+ if (centerPoint && centerPoint.length === 2) {
+ this.mXCenter = centerPoint[0];
+ this.mYCenter = centerPoint[1];
+ }
+ }
+
+ getName(): string {
+ return this.constructor.name + ';degree:' + this.mDegree + ';angle:' + this.mAngle + ';XCenter:' + this.mXCenter
+ + ';YCenter:' + this.mYCenter;
+ }
+
+ async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise {
+ let imageInfo: image.ImageInfo = await toTransform.getImageInfo();
+ if (!imageInfo.size) {
+ console.error("SwirlTransformation The image size does not exist.");
+ return toTransform;
+ }
+ return await this.swirlGPU(toTransform, imageInfo.size.width, imageInfo.size.height);
+ }
+
+ private async swirlGPU(bitmap: image.PixelMap, targetWidth: number, targetHeight: number): Promise {
+ let bufferData = new ArrayBuffer(bitmap.getPixelBytesNumber());
+ await bitmap.readPixelsToBuffer(bufferData);
+ let filter = new GPUImageSwirlFilter();
+ filter.setImageData(bufferData, targetWidth, targetHeight);
+ filter.setRadius(this.mDegree);
+ filter.setAngle(this.mAngle);
+ filter.setCenter(this.mXCenter, this.mYCenter);
+ let buf = await filter.getPixelMapBuf(0, 0, targetWidth, targetHeight);
+ await bitmap.writeBufferToPixels(buf);
+ return bitmap;
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/ets/transform/ToonTransformation.ets b/library/src/main/ets/transform/ToonTransformation.ets
new file mode 100644
index 0000000..8b73343
--- /dev/null
+++ b/library/src/main/ets/transform/ToonTransformation.ets
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { GPUImageToonFilter } from '@ohos/gpu_transform';
+import { PixelMapTransformation } from './PixelMapTransformation';
+import { image } from '@kit.ImageKit';
+
+/**
+ * 图片变换:动画滤镜效果
+ */
+@Sendable
+export class ToonTransformation extends PixelMapTransformation {
+ private threshold: number = 0.2;
+ private quantizationLevels: number = 10.0;
+
+ constructor(threshold?: number, quantizationLevels?: number) {
+ super();
+ if (threshold) {
+ this.threshold = threshold;
+ }
+ if (quantizationLevels) {
+ this.quantizationLevels = quantizationLevels;
+ }
+ }
+
+ getName(): string {
+ return this.constructor.name + ';threshold:' + this.threshold + ';quantizationLevels:' + this.quantizationLevels;
+ }
+
+ async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise {
+ let imageInfo: image.ImageInfo = await toTransform.getImageInfo();
+ if (!imageInfo.size) {
+ console.error("ToonTransformation The image size does not exist.");
+ return toTransform;
+ }
+ return await this.toonGPU(toTransform, imageInfo.size.width, imageInfo.size.height);
+ }
+
+ private async toonGPU(bitmap: image.PixelMap, targetWidth: number, targetHeight: number) {
+ let bufferData = new ArrayBuffer(bitmap.getPixelBytesNumber());
+ await bitmap.readPixelsToBuffer(bufferData);
+ let filter = new GPUImageToonFilter();
+ filter.setImageData(bufferData, targetWidth, targetHeight);
+ filter.setThreshold(this.threshold);
+ filter.setQuantizationLevels(this.quantizationLevels);
+ let buf = await filter.getPixelMapBuf(0, 0, targetWidth, targetHeight);
+ await bitmap.writeBufferToPixels(buf);
+ return bitmap;
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/ets/transform/VignetterTransformation.ets b/library/src/main/ets/transform/VignetterTransformation.ets
new file mode 100644
index 0000000..a8c7691
--- /dev/null
+++ b/library/src/main/ets/transform/VignetterTransformation.ets
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { GPUImageVignetterFilter } from '@ohos/gpu_transform';
+import { PixelMapTransformation } from './PixelMapTransformation';
+import { image } from '@kit.ImageKit';
+
+/**
+ * 图片变换:装饰效果
+ */
+@Sendable
+export class VignetterTransformation extends PixelMapTransformation {
+ private mXCenter: number = 0.5;
+ private mYCenter: number = 0.5;
+ private mRed: number = 0.0;
+ private mGreen: number = 0.0;
+ private mBlue: number = 0.0;
+ private mStart: number = 0.3;
+ private mEnd: number = 0.75;
+
+ constructor(centerPoint: Array, vignetteColor: Array, vignetteSpace: Array) {
+ super();
+ if (centerPoint.length === 2) {
+ this.mXCenter = centerPoint[0];
+ this.mYCenter = centerPoint[1];
+ }
+ if (vignetteColor.length === 3) {
+ this.mRed = vignetteColor[0];
+ this.mGreen = vignetteColor[1];
+ this.mBlue = vignetteColor[2];
+ }
+ if (vignetteSpace.length === 2) {
+ this.mStart = vignetteSpace[0];
+ this.mEnd = vignetteSpace[1];
+ }
+ }
+
+ getName(): string {
+ return this.constructor.name + ';XCenter:' + this.mXCenter + ';YCenter:' + this.mYCenter + ';Red:'
+ + this.mRed + ';Green:' + this.mGreen + ';Blue:' + this.mBlue + ';Start:' + this.mStart + ';End:' + this.mEnd;
+ }
+
+ async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise {
+ let imageInfo: image.ImageInfo = await toTransform.getImageInfo();
+ if (!imageInfo.size) {
+ console.error("VignetterTransformation The image size does not exist.");
+ return toTransform;
+ }
+ return await this.swirlGPU(toTransform, imageInfo.size.width, imageInfo.size.height);
+ }
+
+ private async swirlGPU(bitmap: image.PixelMap, targetWidth: number, targetHeight: number) {
+ let bufferData = new ArrayBuffer(bitmap.getPixelBytesNumber());
+ await bitmap.readPixelsToBuffer(bufferData);
+ let filter = new GPUImageVignetterFilter();
+ filter.setImageData(bufferData, targetWidth, targetHeight);
+ filter.setVignetteCenter([this.mXCenter, this.mYCenter]);
+ filter.setVignetteColor([this.mRed, this.mGreen, this.mBlue]);
+ filter.setVignetteStart(this.mStart);
+ filter.setVignetteEnd(this.mEnd);
+ let buf = await filter.getPixelMapBuf(0, 0, targetWidth, targetHeight);
+ await bitmap.writeBufferToPixels(buf);
+ return bitmap;
+ }
+}
\ No newline at end of file
diff --git a/sharedlibrary2/src/main/ets/pages/GetRes2.ets b/library/src/main/ets/transform/entry/PixelEntry.ets
similarity index 64%
rename from sharedlibrary2/src/main/ets/pages/GetRes2.ets
rename to library/src/main/ets/transform/entry/PixelEntry.ets
index 36bfdef..4a0205a 100644
--- a/sharedlibrary2/src/main/ets/pages/GetRes2.ets
+++ b/library/src/main/ets/transform/entry/PixelEntry.ets
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 Huawei Device Co., Ltd.
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
@@ -12,10 +12,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import common from '@ohos.app.ability.common'
+export class PixelEntry {
+ a: number = 0;
+ b: number = 0;
+ r: number = 0;
+ g: number = 0;
+ f: number = 0;
+ pixel: number = 0;
-export class GetRes2{
- static getSample2():Resource {
- return $r('app.media.icon_loading');
+ public toString(): string {
+ return "PixelEntry a:" + this.a + ";b:" + this.b + ";r:" + this.r + ";g:" + this.g + ";f:" + this.f;
}
}
\ No newline at end of file
diff --git a/library/src/main/ets/utils/CalculatePixelUtils.ets b/library/src/main/ets/utils/CalculatePixelUtils.ets
new file mode 100644
index 0000000..3bf1568
--- /dev/null
+++ b/library/src/main/ets/utils/CalculatePixelUtils.ets
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+export namespace CalculatePixelUtils {
+
+ export function createInt2DArray(first_len: number, second_len: number): Array> {
+ let array = new Array>();
+ for (let f = 0; f < first_len; f++) {
+ let s1 = new Array();
+ for (let s = 0; s < second_len; s++) {
+ s1.push(0);
+ }
+ array.push(s1);
+ }
+ return array;
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/ets/utils/ColorUtils.ets b/library/src/main/ets/utils/ColorUtils.ets
new file mode 100644
index 0000000..5c1ee2e
--- /dev/null
+++ b/library/src/main/ets/utils/ColorUtils.ets
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+export namespace ColorUtils {
+
+ export function red(color: number): number {
+ return (color >> 16) & 0xFF;
+ }
+
+ export function green(color: number): number {
+ return (color >> 8) & 0xFF;
+ }
+
+ export function blue(color: number): number {
+ return color & 0xFF;
+ }
+
+ export function alpha(color: number): number {
+ return color >>> 24;
+ }
+
+ export function rgb(red: number, green: number, blue: number): number {
+ return 0xff000000 | (red << 16) | (green << 8) | blue;
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/ets/utils/Constants.ets b/library/src/main/ets/utils/Constants.ets
new file mode 100644
index 0000000..d187451
--- /dev/null
+++ b/library/src/main/ets/utils/Constants.ets
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+export class Constants {
+ public static PROGRESS_EMITTER: string = "progressEmitter"
+}
\ No newline at end of file
diff --git a/library/src/main/ets/utils/DefaultJobQueue.ets b/library/src/main/ets/utils/DefaultJobQueue.ets
new file mode 100644
index 0000000..ac31956
--- /dev/null
+++ b/library/src/main/ets/utils/DefaultJobQueue.ets
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { ImageKnifeRequest } from '../ImageKnifeRequest';
+import { IJobQueue } from './IJobQueue'
+import Queue from '@ohos.util.Queue';
+import { taskpool,Stack } from '@kit.ArkTS';
+
+export class DefaultJobQueue implements IJobQueue {
+ highQueue: Stack = new Stack();
+ normalQueue: Stack = new Stack();
+ lowQueue: Stack = new Stack();
+
+ getQueueLength(): number {
+ return this.highQueue.length + this.normalQueue.length + this.lowQueue.length
+ }
+
+ add(request: ImageKnifeRequest): void {
+ if (request.imageKnifeOption.priority === undefined || request.imageKnifeOption.priority === taskpool.Priority.MEDIUM) {
+ this.normalQueue.push(request)
+ } else if (request.imageKnifeOption.priority === taskpool.Priority.HIGH) {
+ this.highQueue.push(request)
+ } else {
+ this.lowQueue.push(request)
+ }
+ }
+
+ pop(): ImageKnifeRequest | undefined {
+ if (this.highQueue.length > 0) {
+ return this.highQueue.pop()
+ } else if (this.normalQueue.length > 0) {
+ return this.normalQueue.pop()
+ } else {
+ return this.lowQueue.pop()
+ }
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/ets/utils/FileCache.ets b/library/src/main/ets/utils/FileCache.ets
new file mode 100644
index 0000000..8c47562
--- /dev/null
+++ b/library/src/main/ets/utils/FileCache.ets
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import util from '@ohos.util';
+import { FileUtils } from './FileUtils';
+import fs from '@ohos.file.fs';
+import { LogUtil } from './LogUtil';
+import { SparkMD5 } from '../3rd_party/sparkmd5/spark-md5';
+
+
+/**
+ * 二级文件缓存
+ * 主线程通过lruCache管理缓存大小
+ * 子线程直接读写文件
+ */
+export class FileCache {
+ static CACHE_FOLDER: string = "ImageKnife" // context cacheDir 的缓存文件目录
+ maxMemory: number = 0
+ currentMemory: number = 0
+ maxSize: number = 0
+ path: string = ""
+ private lruCache: util.LRUCache
+ private isInited: boolean = false
+ private context?: Context
+ readonly defaultMaxSize: number = 512;
+ readonly defaultSize: number = 128;
+ readonly defaultMaxMemorySize: number = 512 * 1024 * 1024;
+ readonly defaultMemorySize: number = 128 * 1024 * 1024;
+
+ constructor(context: Context, size: number, memory: number) {
+ if (size <= 0) {
+ size = this.defaultSize
+ }
+ if (memory <= 0 || memory > this.defaultMaxMemorySize) {
+ memory = this.defaultMemorySize
+ }
+
+ this.lruCache = new util.LRUCache(size);
+ this.maxMemory = memory
+ this.currentMemory = 0;
+ this.maxSize = size
+ this.context = context
+ }
+
+ /**
+ * 遍历缓存文件目录,初始化缓存
+ */
+ public async initFileCache(path: string = FileCache.CACHE_FOLDER) {
+ if (this.isInited) {
+ return
+ }
+ if (this.context && path.startsWith(this.context.cacheDir) === true) {
+ this.path = path
+ } else {
+ FileCache.CACHE_FOLDER = path
+ this.path = this.context?.cacheDir + FileUtils.SEPARATOR + FileCache.CACHE_FOLDER + FileUtils.SEPARATOR
+ }
+ await FileUtils.getInstance().createFolder(this.path)
+ // 遍历缓存目录下的文件,按照时间顺序加入缓存
+ let filenames: string[] = await FileUtils.getInstance().ListFile(this.path)
+
+ interface CacheFileInfo {
+ file: string;
+ ctime: number;
+ }
+
+ // 按照上次访问该文件的时间排序
+ let cachefiles: CacheFileInfo[] = []
+ for (let i = 0; i < filenames.length; i++) {
+ let stat: fs.Stat | undefined = await FileUtils.getInstance().Stat(this.path + filenames[i])
+ cachefiles.push({
+ file: filenames[i],
+ ctime: stat === undefined ? 0 : stat.ctime
+ })
+ }
+ let sortedCachefiles: CacheFileInfo[] = cachefiles.sort((a, b) => a.ctime - b.ctime)
+
+ for (let i = 0; i < sortedCachefiles.length; i++) {
+ let buf: ArrayBuffer | undefined = await FileUtils.getInstance().readFile(this.path + sortedCachefiles[i].file)
+ if (buf !== undefined) {
+ // 处理数量超过size的场景,移除即将排除的文件
+ if (this.lruCache.length == this.maxSize && !this.lruCache.contains(sortedCachefiles[i].file)) {
+ let remove: number | undefined = this.lruCache.remove(this.lruCache.keys()[0])
+ if (remove !== undefined) {
+ FileUtils.getInstance().deleteFile(this.path + this.lruCache.keys()[0])
+ this.removeMemorySize(buf)
+ }
+ }
+
+ this.lruCache.put(sortedCachefiles[i].file, buf.byteLength)
+ this.addMemorySize(buf)
+ }
+ }
+
+ this.trimToSize();
+ this.isInited = true
+ }
+
+ public isFileCacheInit():boolean {
+ return this.isInited
+ }
+
+ public getCacheFolder(): string {
+ return FileCache.CACHE_FOLDER
+ }
+
+ // 添加缓存键值对,同时写文件
+ put(key: string, value: ArrayBuffer): void {
+ if (key == null || value == null) {
+ throw new Error('key or value is invalid ');
+ }
+ if (!this.isInited) {
+ return
+ }
+
+ // 如果size满了的话,需要按照LRU的方式删除第一个
+ if (this.lruCache.length == this.maxSize && !this.lruCache.contains(key)) {
+ this.remove(this.lruCache.keys()[0])
+ } else if (this.lruCache.contains(key)) {
+ this.remove(key)
+ }
+
+ let pre = this.lruCache.put(key, value.byteLength)
+ FileUtils.getInstance().writeDataSync(this.path + key, value)
+ if (pre !== undefined) {
+ this.addMemorySize(value)
+ }
+ this.trimToSize()
+ }
+
+ // 添加缓存键值对,但不写文件(用于子线程已经写文件的场景)
+ putWithoutWriteFile(key: string, value: ArrayBuffer | number): void {
+ if (key == null || value == null) {
+ throw new Error('key or value is invalid ');
+ }
+ if (!this.isInited) {
+ return
+ }
+
+ // 如果size满了的话,需要按照LRU的方式删除第一个
+ if (this.lruCache.length == this.maxSize && !this.lruCache.contains(key)) {
+ this.remove(this.lruCache.keys()[0])
+ } else if (this.lruCache.contains(key)) {
+ this.lruCache.remove(key)
+ this.lruCache.put(key, typeof value == "number" ? value : value.byteLength)
+ return
+ }
+
+ this.lruCache.put(key, typeof value == "number" ? value : value.byteLength)
+ this.addMemorySize(value)
+ this.trimToSize()
+ }
+
+ get(key: string): ArrayBuffer | undefined {
+ if (!this.isInited) {
+ return
+ }
+
+ if (this.lruCache.get(key) !== undefined) {
+ // TODO 如何才能修改文件访问时间呢
+ return FileUtils.getInstance().readFileSync(this.path + key)
+ }
+ return undefined
+ }
+
+ // 移除键为key的缓存
+ remove(key: string): void {
+ if (key == null) {
+ throw new Error('key is null,checking the parameter');
+ }
+ if (!this.isInited) {
+ return
+ }
+
+ let remove: number | undefined = this.lruCache.remove(key)
+ if (remove !== undefined) {
+ FileUtils.getInstance().deleteFile(this.path + key)
+ this.removeMemorySize(remove)
+ }
+ }
+
+ async removeAll(): Promise {
+ if (!this.isInited) {
+ return
+ }
+ this.isInited = false
+ this.lruCache.clear()
+ this.currentMemory = 0;
+
+ let filenames: string[] = await FileUtils.getInstance().ListFile(this.path)
+ for (let i = 0; i < filenames.length; i++) {
+ await FileUtils.getInstance().deleteFile(this.path + filenames[i])
+ }
+
+ this.isInited = true
+ }
+
+ size(): number {
+ return this.lruCache.length
+ }
+
+ // 移除较少使用的缓存数据
+ private trimToSize(): void {
+ while (true) {
+ if (this.currentMemory <= this.maxMemory || this.lruCache.isEmpty()) {
+ break
+ }
+ let delkey = this.lruCache.keys()[0]
+ let remove: number | undefined = this.lruCache.remove(delkey)
+ if (remove !== undefined) {
+ FileUtils.getInstance().deleteFile(this.path + delkey)
+ this.removeMemorySize(remove)
+ }
+ this.lruCache.remove(delkey)
+ }
+ }
+
+ private removeMemorySize(value: ArrayBuffer | number): void {
+ if (typeof value == "number") {
+ this.currentMemory -= value
+ }
+ else if (value != undefined) {
+ this.currentMemory -= value.byteLength
+ LogUtil.info("FileCache removeMemorySize: " + value.byteLength + " currentMemory:" + this.currentMemory)
+ }
+ }
+
+ private addMemorySize(value: ArrayBuffer | number): void {
+ if (typeof value == "number") {
+ this.currentMemory += value
+ LogUtil.info("FileCache addMemorySize: " + value + " currentMemory:" + this.currentMemory)
+ }
+ else if (value != undefined) {
+ this.currentMemory += value.byteLength
+ LogUtil.info("FileCache addMemorySize: " + value.byteLength + " currentMemory:" + this.currentMemory)
+ }
+ }
+
+ /**
+ * 子线程里只写入缓存文件
+ * @param context
+ * @param key
+ * @param value
+ */
+ static saveFileCacheOnlyFile(context: Context, key: string, value: ArrayBuffer, folder: string = FileCache.CACHE_FOLDER): boolean {
+ // 写文件
+ FileUtils.getInstance()
+ .writeFileSync(context.cacheDir + FileUtils.SEPARATOR + folder + FileUtils.SEPARATOR + key, value)
+ return true
+ }
+
+ /**
+ * 子线程中,通过文件名,直接查找是否有文件缓存
+ * @param context
+ * @param key
+ * @returns
+ */
+ static getFileCacheByFile(context: Context, key: string, folder: string = FileCache.CACHE_FOLDER): ArrayBuffer | undefined {
+ // 从文件获取查看是否有缓存
+ return FileUtils.getInstance()
+ .readFileSync(context.cacheDir + FileUtils.SEPARATOR + folder + FileUtils.SEPARATOR + key)
+ }
+
+ /**
+ * 获取key缓存数据绝对路径
+ *
+ * @params key 数值
+ */
+ getFileToPath(key: string): string {
+ if(!!!key) {
+ throw new Error("key is null,checking the parameter")
+ }
+ let path = this.path + key
+ if(FileUtils.getInstance().exist(path)) {
+ return path
+ } else {
+ return ""
+ }
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/ets/utils/FileTypeUtil.ets b/library/src/main/ets/utils/FileTypeUtil.ets
new file mode 100644
index 0000000..7627162
--- /dev/null
+++ b/library/src/main/ets/utils/FileTypeUtil.ets
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+export class FileTypeUtil {
+ private fileSignatureMap: Record> = {
+ // 添加文件类型和对应的文件头部特征
+ 'jpg': [new Uint8Array([0xFF, 0xD8])],
+ 'png': [new Uint8Array([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A])],
+ 'gif': [new Uint8Array([0x47, 0x49, 0x46, 0x38, 0x39, 0x61]),new Uint8Array([0x47, 0x49, 0x46, 0x38])],
+ 'bmp': [new Uint8Array([0x42, 0x4D])],
+ 'svg': [new Uint8Array([0x3C, 0x3F, 0x78, 0x6D, 0x6C]),new Uint8Array([0x3C, 0x73, 0x76, 0x67, 0x20])],
+ 'webp': [new Uint8Array([0x52, 0x49, 0x46, 0x46])],
+ 'tiff': [new Uint8Array([0x49, 0x20, 0x49]), new Uint8Array([0x49, 0x49, 0x2A, 0x00]), new Uint8Array([0x4D, 0x4D, 0x00, 0x2A]), new Uint8Array([0x4D, 0x4D, 0x00, 0x2B])],
+ // 添加更多的文件类型和特征
+ 'heic': [new Uint8Array([0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x68, 0x65, 0x69, 0x63, 0x00, 0x00, 0x00, 0x00]),new Uint8Array([0x00, 0x00, 0x00, 0x1C, 0x66, 0x74, 0x79, 0x70, 0x68, 0x65, 0x69, 0x63, 0x00, 0x00, 0x00, 0x00])],
+ };
+
+
+ constructor() {
+ }
+
+ isImage(arraybuffer: ArrayBuffer) {
+ let value = this.getFileType(arraybuffer);
+ if (
+ value == SupportFormat.jpg ||
+ value == SupportFormat.png ||
+ value == SupportFormat.tiff ||
+ value == SupportFormat.webp ||
+ value == SupportFormat.bmp ||
+ value == SupportFormat.gif ||
+ value == SupportFormat.svg ||
+ value == SupportFormat.heic
+ ) {
+ return true;
+ }
+ return false;
+ }
+
+ getFileType(file: ArrayBuffer): string | null {
+ const fileData = new Uint8Array(file);
+ let hasMatched = false;
+ let matchedFileType = ''
+ Object.keys(this.fileSignatureMap).map((fileType)=>{
+ if(!hasMatched) {
+ const bufferList = this.fileSignatureMap[fileType];
+ for (let i = 0; i < bufferList.length; i++) {
+ let signature = bufferList[i];
+ if (this.matchesSignature(fileData, signature,fileType)) {
+ hasMatched = true;
+ matchedFileType = fileType;
+ break
+ }
+ }
+ }else{
+ // 由于map函数会输出所有的keys, 所以匹配之后不再需要执行任何逻辑
+ }
+ })
+ if(hasMatched){
+ return matchedFileType;
+ }
+ return null; // 若无法识别文件类型,返回null
+ }
+
+
+ matchesSignature(fileData: Uint8Array, signature: Uint8Array,fileType:string): boolean {
+ if (fileData.length < signature.length) {
+ return false; // 文件长度不足,无法匹配魔数
+ }
+
+ for (let i = fileType == "heic" ? 4 : 0; i < signature.length; i++) {
+ if (fileData[i] !== signature[i]) {
+ return false; // 魔数不匹配
+ }
+ }
+ return true; // 文件头部魔数匹配
+ }
+}
+
+
+export enum PhotoFormat {
+ jpg = 'jpg,0,FFD8',
+ png = 'png,0,89504E470D0A1A0A',
+ bmp = 'bmp,0,424D',
+ gif = 'gif,0,474946383961',
+ svg = 'svg,0,3C3F786D6C',
+ webp = 'webp,0,52494646',
+ tiff = 'tiff,0,492049|49492A00|4D4D002A|4D4D002B'
+}
+
+export enum SupportFormat {
+ jpg = 'jpg',
+ png = 'png',
+ webp = 'webp',
+ bmp = 'bmp',
+ gif = 'gif',
+ svg = 'svg',
+ tiff = 'tiff',
+ heic = 'heic'
+}
diff --git a/library/src/main/ets/utils/FileUtils.ets b/library/src/main/ets/utils/FileUtils.ets
new file mode 100644
index 0000000..5f41dd1
--- /dev/null
+++ b/library/src/main/ets/utils/FileUtils.ets
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import fs from '@ohos.file.fs';
+import { BusinessError } from '@ohos.base';
+import { LogUtil } from './LogUtil'
+
+
+export class FileUtils {
+ static readonly SEPARATOR: string = '/'
+ private static sInstance: FileUtils
+
+ private constructor() {
+ }
+
+ /**
+ * 单例实现FileUtils类
+ */
+ public static getInstance(): FileUtils {
+ if (!FileUtils.sInstance) {
+ FileUtils.sInstance = new FileUtils();
+ }
+ return FileUtils.sInstance;
+ }
+
+ /**
+ * 删除文件
+ *
+ * @param path 文件绝对路径及文件名
+ */
+ deleteFileSync(path: string): boolean {
+ try {
+ let fileExist = fs.accessSync(path);
+ if (fileExist) {
+ fs.unlinkSync(path);
+ }
+ return true
+ } catch (err) {
+ LogUtil.error("FileUtils deleteFileSync failed: err msg=" + err.message + " err code=" + err.code);
+ return false
+ }
+ }
+
+ /**
+ * 异步删除文件
+ * @param path
+ * @returns
+ */
+ async deleteFile(path: string): Promise {
+ // const isExist: boolean = await fs.access(path)
+ // if (isExist) {
+ try {
+ await fs.unlink(path)
+ } catch (err) {
+ LogUtil.error("FileUtils deleteFile failed: err msg=" + err.message + " err code=" + err.code);
+ }
+ }
+
+ /**
+ * 向path写入数据
+ *
+ * @param path 文件绝对路径
+ * @param content 文件内容
+ */
+ writeDataSync(path: string, content: ArrayBuffer | string): boolean {
+ try {
+ let fd = fs.openSync(path, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE | fs.OpenMode.TRUNC).fd
+ let stat = fs.statSync(path)
+ fs.writeSync(fd, content, { offset: stat.size })
+ fs.closeSync(fd)
+ return true
+ }
+ catch (err) {
+ LogUtil.error("FileUtils writeDataSync failed: err msg=" + err.message + " err code=" + err.code);
+ return false
+ }
+ }
+
+ /**
+ * 异步向path写入数据
+ *
+ * @param path 文件绝对路径
+ * @param content 文件内容
+ */
+ async writeData(path: string, content: ArrayBuffer | string): Promise {
+ try {
+ let fd = (await fs.open(path, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)).fd
+ let stat = await fs.stat(path)
+ await fs.write(fd, content, { offset: stat.size })
+ await fs.close(fd)
+ return true
+ }
+ catch (err) {
+ LogUtil.error("FileUtils writeData failed: err msg=" + err.message + " err code=" + err.code);
+ return false
+ }
+ }
+
+ /**
+ * 判断path文件是否存在
+ *
+ * @param path 文件绝对路径
+ */
+ exist(path: string): boolean {
+ try {
+ if (fs.accessSync(path)) {
+ let stat = fs.statSync(path)
+ return stat.isFile()
+ } else {
+ return false
+ }
+ } catch (error) {
+ let err: BusinessError = error as BusinessError;
+ LogUtil.error("FileUtils exist failed with error message: " + err.message + ", error code: " + err.code);
+ }
+ return false
+ }
+
+ /**
+ * 获取path的文件大小
+ *
+ * @param path 文件绝对路径
+ */
+ getFileSizeSync(path: string): number {
+ try {
+ let stat = fs.statSync(path)
+ return stat.size
+ } catch (e) {
+ LogUtil.error("FileUtils getFileSize e " + e)
+ return -1
+ }
+ }
+
+ /**
+ * 读取路径path的文件
+ *
+ * @param path 文件绝对路径
+ */
+ readFileSync(path: string): ArrayBuffer | undefined {
+ try {
+ if (fs.accessSync(path)) {
+ let fd = fs.openSync(path, fs.OpenMode.READ_ONLY).fd;
+ let length = fs.statSync(path).size
+ let buf = new ArrayBuffer(length);
+ fs.readSync(fd, buf)
+ fs.closeSync(fd)
+ return buf
+ }
+ } catch (error) {
+ let err: BusinessError = error as BusinessError;
+ LogUtil.error("FileUtils readFileSync failed with error message: " + err.message + ", error code: " + err.code);
+ }
+ return undefined
+ }
+
+ /**
+ * 读取路径path的文件
+ *
+ * @param path 文件绝对路径
+ */
+ async readFile(path: string): Promise {
+ try {
+ let stat = await fs.stat(path);
+ let fd = (await fs.open(path, fs.OpenMode.READ_ONLY)).fd;
+ let length = stat.size;
+ let buf = new ArrayBuffer(length);
+ await fs.read(fd, buf);
+ return buf
+ } catch (error) {
+ let err: BusinessError = error as BusinessError;
+ LogUtil.error("FileUtils readFile failed with error message: " + err.message + ", error code: " + err.code);
+ }
+ return undefined
+ }
+
+ /**
+ * 创建文件夹
+ *
+ * @param path 文件夹绝对路径,只有是权限范围内的路径,可以生成
+ * @param recursive
+ */
+ createFolderSync(path: string, recursive?: boolean) {
+ try {
+ if (recursive) {
+ if (!this.existFolder(path)) {
+ let lastInterval = path.lastIndexOf(FileUtils.SEPARATOR)
+ if (lastInterval == 0) {
+ return
+ }
+ let newPath = path.substring(0, lastInterval)
+ this.createFolderSync(newPath, true)
+ if (!this.existFolder(path)) {
+ fs.mkdirSync(path)
+ }
+ }
+ } else {
+ if (!this.existFolder(path)) {
+ fs.mkdirSync(path)
+ }
+ }
+ } catch (e) {
+ LogUtil.log("FileUtils createFolder err : " + e)
+ }
+ }
+
+ async createFolder(path: string): Promise {
+ try {
+ let isExist: boolean = await fs.access(path)
+ if (!isExist) {
+ await fs.mkdir(path)
+
+ }
+ return true
+ } catch (error) {
+ let err: BusinessError = error as BusinessError;
+ LogUtil.error("FileUtils createFolder failed with error message: " + err.message + ", error code: " + err.code);
+ }
+ return false
+ }
+
+ /**
+ * 判断文件夹是否存在
+ *
+ * @param path 文件夹绝对路径
+ */
+ existFolder(path: string): boolean {
+ try {
+ if (fs.accessSync(path)) {
+ let stat = fs.statSync(path)
+ return stat.isDirectory()
+ } else {
+ return false
+ }
+ }
+ catch (error) {
+ let err: BusinessError = error as BusinessError;
+ LogUtil.error("FileUtils existFolder failed with error message: " + err.message + ", error code: " + err.code);
+ }
+ return false
+ }
+
+ writeFileSync(path: string, content: ArrayBuffer | string): boolean {
+ try {
+ let fd = fs.openSync(path, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE).fd
+ fs.truncateSync(fd)
+ fs.writeSync(fd, content)
+ fs.closeSync(fd)
+ return true
+ } catch (err) {
+ LogUtil.error("FileUtils writeFileSync failed with error message: " + err.message + ", error code: " + err.code);
+ }
+ return false
+ }
+
+ ListFileSync(path: string): string[] {
+ try {
+ return fs.listFileSync(path)
+ } catch (err) {
+ LogUtil.error("FileUtils ListFileSync failed with error message: " + err.message + ", error code: " + err.code);
+ }
+ return []
+ }
+
+ async ListFile(path: string): Promise {
+ try {
+ return fs.listFile(path)
+ } catch (err) {
+ LogUtil.error("FileUtils ListFile failed with error message: " + err.message + ", error code: " + err.code);
+ }
+ return []
+ }
+
+ StatSync(path: string): fs.Stat | undefined {
+ try {
+ return fs.statSync(path)
+ } catch (err) {
+ LogUtil.error("FileUtils StatSync failed with error message: " + err.message + ", error code: " + err.code);
+ }
+ return undefined
+ }
+
+ async Stat(path: string): Promise {
+ try {
+ return fs.stat(path)
+ } catch (err) {
+ LogUtil.error("FileUtils Stat failed with error message: " + err.message + ", error code: " + err.code);
+ }
+ return undefined
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/ets/utils/IJobQueue.ets b/library/src/main/ets/utils/IJobQueue.ets
new file mode 100644
index 0000000..0a51ff0
--- /dev/null
+++ b/library/src/main/ets/utils/IJobQueue.ets
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { ImageKnifeRequest } from '../ImageKnifeRequest'
+
+export interface IJobQueue {
+
+ /**
+ * 获取队列长度
+ * @returns
+ */
+ getQueueLength(): number
+
+ /**
+ * 往队列中添加元素
+ * @param request
+ */
+ add(request: ImageKnifeRequest): void
+
+
+ /**
+ * 移除并返回队列头部的元素
+ * @returns
+ */
+ pop(): ImageKnifeRequest | undefined
+}
\ No newline at end of file
diff --git a/library/src/main/ets/utils/IMemoryCache.ets b/library/src/main/ets/utils/IMemoryCache.ets
new file mode 100644
index 0000000..8e35c62
--- /dev/null
+++ b/library/src/main/ets/utils/IMemoryCache.ets
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { ImageKnifeData } from '../model/ImageKnifeData'
+
+export interface IMemoryCache {
+
+ put(key: string, value: ImageKnifeData): void
+
+ get(key: string): ImageKnifeData | undefined
+
+ remove(key: string): ImageKnifeData | undefined
+
+ removeAll(): void
+
+ size(): number
+}
\ No newline at end of file
diff --git a/library/src/main/ets/utils/LogUtil.ets b/library/src/main/ets/utils/LogUtil.ets
new file mode 100644
index 0000000..02c2d39
--- /dev/null
+++ b/library/src/main/ets/utils/LogUtil.ets
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+export class LogUtil {
+ public static OFF: number = 1
+ public static LOG: number = 2
+ public static DEBUG: number = 3
+ public static INFO: number = 4
+ public static WARN: number = 5
+ public static ERROR: number = 6
+ public static ALL: number = 7
+ public static mLogLevel: number = LogUtil.OFF;
+ public static TAG: string = "ImageKnife:: ";
+
+ public static debug(message: string, ...args: Object[]) {
+ if (LogUtil.mLogLevel >= LogUtil.DEBUG) {
+ console.debug(LogUtil.TAG + message, args)
+ }
+ }
+
+ public static info(message: string, ...args: Object[]) {
+ if (LogUtil.mLogLevel >= LogUtil.INFO) {
+ console.info(LogUtil.TAG + message, args)
+ }
+ }
+
+ public static log(message: string, ...args: Object[]) {
+ if (LogUtil.mLogLevel >= LogUtil.LOG) {
+ console.log(LogUtil.TAG + message, args)
+ }
+ }
+
+ public static warn(message: string, ...args: Object[]) {
+ if (LogUtil.mLogLevel >= LogUtil.WARN) {
+ console.warn(LogUtil.TAG + message, args)
+ }
+ }
+
+ public static error(message: string, ...args: Object[]) {
+ if (LogUtil.mLogLevel >= LogUtil.ERROR) {
+ console.error(LogUtil.TAG + message, args)
+ }
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/ets/utils/MemoryLruCache.ets b/library/src/main/ets/utils/MemoryLruCache.ets
new file mode 100644
index 0000000..493fc11
--- /dev/null
+++ b/library/src/main/ets/utils/MemoryLruCache.ets
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { ImageKnifeData } from '../model/ImageKnifeData';
+import util from '@ohos.util';
+import { IMemoryCache } from './IMemoryCache';
+
+export class MemoryLruCache implements IMemoryCache {
+ maxMemory: number = 0
+ currentMemory: number = 0
+ maxSize: number = 0
+ private lruCache: util.LRUCache
+ readonly defaultMaxSize: number = 4096
+ readonly defaultSize: number = 512
+ readonly defaultMaxMemorySize: number = 1024 * 1024 * 1024
+ readonly defaultMemorySize: number = 128 * 1024 * 1024
+
+ constructor(size: number, memory: number) {
+ if (size <= 0 || size > this.defaultMaxSize) {
+ size = this.defaultSize
+ }
+ if (memory <= 0 || memory > this.defaultMaxMemorySize) {
+ memory = this.defaultMemorySize
+ }
+
+ this.lruCache = new util.LRUCache(size);
+ this.maxMemory = memory
+ this.maxSize = size
+ this.currentMemory = 0
+ }
+
+ // 添加缓存键值对
+ put(key: string, value: ImageKnifeData): void {
+ if (key == null || value == null) {
+ throw new Error('key or value is invalid ');
+ }
+
+ // 如果size满了的话,需要按照LRU的方式删除第一个
+ if (this.lruCache.length == this.maxSize && !this.lruCache.contains(key)) {
+ this.remove(this.lruCache.keys()[0])
+ } else if (this.lruCache.contains(key)) {
+ this.remove(key)
+ }
+
+ let pre: ImageKnifeData = this.lruCache.put(key, value)
+ this.addMemorySize(value)
+ // if (pre !== undefined) { // 当前返回不是key的之前value
+ // this.removeMemorySize(pre)
+ // }
+ this.trimToSize();
+ }
+
+ get(key: string): ImageKnifeData | undefined {
+ return this.lruCache.get(key)
+ }
+
+ // 移除键为key的缓存
+ remove(key: string): ImageKnifeData | undefined {
+ if (key == null) {
+ throw new Error('key is null,checking the parameter');
+ }
+
+ let remove: ImageKnifeData | undefined = this.lruCache.remove(key)
+ if (remove !== undefined) {
+ this.removeMemorySize(remove)
+ }
+ return remove
+ }
+
+ removeAll(): void {
+ this.lruCache.clear()
+ this.currentMemory = 0;
+ }
+
+ size(): number {
+ return this.lruCache.length
+ }
+
+ // 移除较少使用的缓存数据
+ trimToSize(): void {
+ while (true) {
+ if (this.currentMemory <= this.maxMemory || this.lruCache.isEmpty()) {
+ break
+ }
+ let delkey = this.lruCache.keys()[0]
+ let data: ImageKnifeData | undefined = this.lruCache.get(delkey)
+ if (data != undefined) {
+ this.removeMemorySize(data)
+ }
+ this.lruCache.remove(delkey)
+ }
+ }
+
+ private removeMemorySize(value: ImageKnifeData): void {
+ if (value.source != undefined) {
+ if (typeof value.source === 'string' && value.source != "") {
+ this.currentMemory -= value.source.length
+ } else if (value.source == "") {
+ for (let index = 0;index < value.imageAnimator!.length;index++) {
+ let pixelMap = value.imageAnimator![index].src as PixelMap
+ this.currentMemory -= pixelMap.getPixelBytesNumber()
+ }
+ } else {
+ this.currentMemory -= value.source.getPixelBytesNumber();
+ value.source.release()
+ }
+ // LogUtil.info("MemoryCache removeMemorySize: " + value.source.getPixelBytesNumber() + " currentMemory:" + this.currentMemory)
+ }
+ }
+
+ private addMemorySize(value: ImageKnifeData): void {
+ if (value.source != undefined) {
+ if (typeof value.source === 'string' && value.source != "") {
+ this.currentMemory += value.source.length
+ } else if (value.source == "") {
+ for (let index = 0;index < value.imageAnimator!.length;index++) {
+ let pixelMap = value.imageAnimator![index].src as PixelMap
+ this.currentMemory += pixelMap.getPixelBytesNumber()
+ }
+ } else {
+ this.currentMemory += value.source.getPixelBytesNumber();
+ }
+ //LogUtil.log("MemoryCache addMemorySize: " + value.source.getPixelBytesNumber() + " currentMemory:" + this.currentMemory)
+ }
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/ets/utils/Tools.ets b/library/src/main/ets/utils/Tools.ets
new file mode 100644
index 0000000..a51faa8
--- /dev/null
+++ b/library/src/main/ets/utils/Tools.ets
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { SparkMD5 } from '../3rd_party/sparkmd5/spark-md5'
+import util from '@ohos.util'
+
+export class Tools {
+ private static keyCache: util.LRUCache = new util.LRUCache(1024)
+ public static generateMemoryKey(key: string | PixelMap | Resource): string{
+ return typeof key == "string"? key : JSON.stringify(key)
+ }
+ // 生成唯一的key
+ public static generateKey(key: string | PixelMap | Resource): string{
+ let keyCache = typeof key == "string"? key : JSON.stringify(key)
+ let result = Tools.keyCache.get(keyCache)
+ if(result != undefined) {
+ return result
+ } else {
+ result = SparkMD5.hashBinary(keyCache)
+ Tools.keyCache.put(keyCache,result)
+ return result
+ }
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/module.json5 b/library/src/main/module.json5
index 7274415..dd30f8e 100644
--- a/library/src/main/module.json5
+++ b/library/src/main/module.json5
@@ -4,8 +4,8 @@
"type": "har",
"deviceTypes": [
"default",
- "tablet"
- ],
- "uiSyntax": "ets"
+ "tablet",
+ "2in1"
+ ]
}
-}
\ No newline at end of file
+}
diff --git a/library/src/main/resources/base/element/string.json b/library/src/main/resources/base/element/string.json
index 1e76de0..f51a9c8 100644
--- a/library/src/main/resources/base/element/string.json
+++ b/library/src/main/resources/base/element/string.json
@@ -2,7 +2,7 @@
"string": [
{
"name": "page_show",
- "value": "page from npm package"
+ "value": "page from package"
}
]
}
diff --git a/library/src/main/resources/base/media/rabbit.gif b/library/src/main/resources/base/media/rabbit.gif
new file mode 100644
index 0000000..c2c1402
Binary files /dev/null and b/library/src/main/resources/base/media/rabbit.gif differ
diff --git a/library/src/main/resources/base/media/startIcon.png b/library/src/main/resources/base/media/startIcon.png
new file mode 100644
index 0000000..366f764
Binary files /dev/null and b/library/src/main/resources/base/media/startIcon.png differ
diff --git a/library/src/main/resources/en_US/element/string.json b/library/src/main/resources/en_US/element/string.json
new file mode 100644
index 0000000..f51a9c8
--- /dev/null
+++ b/library/src/main/resources/en_US/element/string.json
@@ -0,0 +1,8 @@
+{
+ "string": [
+ {
+ "name": "page_show",
+ "value": "page from package"
+ }
+ ]
+}
diff --git a/library/src/main/resources/zh_CN/element/string.json b/library/src/main/resources/zh_CN/element/string.json
new file mode 100644
index 0000000..f51a9c8
--- /dev/null
+++ b/library/src/main/resources/zh_CN/element/string.json
@@ -0,0 +1,8 @@
+{
+ "string": [
+ {
+ "name": "page_show",
+ "value": "page from package"
+ }
+ ]
+}
diff --git a/library/src/test/List.test.ets b/library/src/test/List.test.ets
new file mode 100644
index 0000000..f43d80b
--- /dev/null
+++ b/library/src/test/List.test.ets
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import localUnitTest from './LocalUnit.test';
+
+export default function testsuite() {
+ localUnitTest();
+}
\ No newline at end of file
diff --git a/library/src/test/LocalUnit.test.ets b/library/src/test/LocalUnit.test.ets
new file mode 100644
index 0000000..dacc818
--- /dev/null
+++ b/library/src/test/LocalUnit.test.ets
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
+
+export default function localUnitTest() {
+ describe('localUnitTest',() => {
+ // Defines a test suite. Two parameters are supported: test suite name and test suite function.
+ beforeAll(() => {
+ // Presets an action, which is performed only once before all test cases of the test suite start.
+ // This API supports only one parameter: preset action function.
+ });
+ beforeEach(() => {
+ // Presets an action, which is performed before each unit test case starts.
+ // The number of execution times is the same as the number of test cases defined by **it**.
+ // This API supports only one parameter: preset action function.
+ });
+ afterEach(() => {
+ // Presets a clear action, which is performed after each unit test case ends.
+ // The number of execution times is the same as the number of test cases defined by **it**.
+ // This API supports only one parameter: clear action function.
+ });
+ afterAll(() => {
+ // Presets a clear action, which is performed after all test cases of the test suite end.
+ // This API supports only one parameter: clear action function.
+ });
+ it('assertContain', 0, () => {
+ // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
+ let a = 'abc';
+ let b = 'b';
+ // Defines a variety of assertion methods, which are used to declare expected boolean conditions.
+ expect(a).assertContain(b);
+ expect(a).assertEqual(a);
+ });
+ });
+}
\ No newline at end of file
diff --git a/sharedlibrary/Index.ets b/sharedlibrary/Index.ets
new file mode 100644
index 0000000..5d74d4b
--- /dev/null
+++ b/sharedlibrary/Index.ets
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+export { add } from './src/main/ets/utils/Calc'
+
+export { InitImageKnife } from "./src/main/ets/pages/InitImageKnife"
+
+export { IndexComponent } from "./src/main/ets/pages/Index"
+
+export { ImageKnifeComponent,ImageKnifeAnimatorComponent } from '@ohos/imageknife'
+
+export { ImageKnife } from '@ohos/imageknife'
+
+export { ImageKnifeOption,AnimatorOption } from '@ohos/imageknife'
+
+export { ImageKnifeRequest } from '@ohos/imageknife'
+
+export { FileUtils } from '@ohos/imageknife'
+
+export { LogUtil } from '@ohos/imageknife'
+
+export { IEngineKey } from '@ohos/imageknife'
+
+export { ImageKnifeData , CacheStrategy} from "@ohos/imageknife"
+
+export { PixelMapTransformation } from '@ohos/imageknife'
+
+export { MultiTransTransformation } from '@ohos/imageknife'
+
+export { BrightnessTransformation } from '@ohos/imageknife'
+
+export { BlurTransformation } from '@ohos/imageknife'
+
+export { SparkMD5 } from "@ohos/imageknife"
+
+export { ImageKnifeRequestSource } from "@ohos/imageknife"
+
+export { CropCircleTransformation } from '@ohos/imageknife'
+
+export { CropCircleWithBorderTransformation } from '@ohos/imageknife'
+
+export { CropSquareTransformation } from '@ohos/imageknife'
+
+export { CropTransformation } from '@ohos/imageknife'
+
+export { GrayScaleTransformation } from '@ohos/imageknife'
+
+export { InvertTransformation } from '@ohos/imageknife'
+
+export { KuwaharaTransformation } from '@ohos/imageknife'
+
+export { MaskTransformation } from '@ohos/imageknife'
+
+export { PixelationTransformation } from '@ohos/imageknife'
+
+export { SepiaTransformation } from '@ohos/imageknife'
+
+export { SketchTransformation } from '@ohos/imageknife'
+
+export { SwirlTransformation } from '@ohos/imageknife'
+
+export { ToonTransformation } from '@ohos/imageknife'
+
+export { VignetterTransformation } from '@ohos/imageknife'
\ No newline at end of file
diff --git a/sharedlibrary/build-profile.json5 b/sharedlibrary/build-profile.json5
index befa101..170d980 100644
--- a/sharedlibrary/build-profile.json5
+++ b/sharedlibrary/build-profile.json5
@@ -1,10 +1,28 @@
{
- "apiType": 'stageMode',
+ "apiType": "stageMode",
"buildOption": {
},
+ "buildOptionSet": [
+ {
+ "name": "release",
+ "arkOptions": {
+ "obfuscation": {
+ "ruleOptions": {
+ "enable": true,
+ "files": [
+ "./obfuscation-rules.txt"
+ ]
+ }
+ }
+ },
+ },
+ ],
"targets": [
{
"name": "default"
+ },
+ {
+ "name": "ohosTest"
}
]
}
\ No newline at end of file
diff --git a/sharedlibrary/hvigorfile.ts b/sharedlibrary/hvigorfile.ts
index 0e65ea8..d993120 100644
--- a/sharedlibrary/hvigorfile.ts
+++ b/sharedlibrary/hvigorfile.ts
@@ -1,2 +1,6 @@
-// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently.
-module.exports = require('@ohos/hvigor-ohos-plugin').hspTasks
+import { hspTasks } from '@ohos/hvigor-ohos-plugin';
+
+export default {
+ system: hspTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
+ plugins:[] /* Custom plugin to extend the functionality of Hvigor. */
+}
diff --git a/sharedlibrary/obfuscation-rules.txt b/sharedlibrary/obfuscation-rules.txt
new file mode 100644
index 0000000..985b2ae
--- /dev/null
+++ b/sharedlibrary/obfuscation-rules.txt
@@ -0,0 +1,18 @@
+# Define project specific obfuscation rules here.
+# You can include the obfuscation configuration files in the current module's build-profile.json5.
+#
+# For more details, see
+# https://gitee.com/openharmony/arkcompiler_ets_frontend/blob/master/arkguard/README.md
+
+# Obfuscation options:
+# -disable-obfuscation: disable all obfuscations
+# -enable-property-obfuscation: obfuscate the property names
+# -enable-toplevel-obfuscation: obfuscate the names in the global scope
+# -compact: remove unnecessary blank spaces and all line feeds
+# -remove-log: remove all console.* statements
+# -print-namecache: print the name cache that contains the mapping from the old names to new names
+# -apply-namecache: reuse the given cache file
+
+# Keep options:
+# -keep-property-name: specifies property names that you want to keep
+# -keep-global-name: specifies names that you want to keep in the global scope
\ No newline at end of file
diff --git a/sharedlibrary/oh-package.json5 b/sharedlibrary/oh-package.json5
index 1d879aa..65115c7 100644
--- a/sharedlibrary/oh-package.json5
+++ b/sharedlibrary/oh-package.json5
@@ -1,12 +1,12 @@
{
- "license": "Apache-2.0",
- "devDependencies": {},
- "author": "",
"name": "sharedlibrary",
- "description": "Please describe the basic information.",
- "main": "./src/main/ets/Index.ets",
"version": "1.0.0",
+ "description": "Please describe the basic information.",
+ "main": "Index.ets",
+ "author": "",
+ "license": "Apache-2.0",
+ "packageType": "InterfaceHar",
"dependencies": {
"@ohos/imageknife": "file:../library"
}
-}
+}
\ No newline at end of file
diff --git a/sharedlibrary/src/main/ets/pages/Index.ets b/sharedlibrary/src/main/ets/pages/Index.ets
index d2b98af..ad35007 100644
--- a/sharedlibrary/src/main/ets/pages/Index.ets
+++ b/sharedlibrary/src/main/ets/pages/Index.ets
@@ -1,77 +1,37 @@
/*
- * Copyright (C) 2023 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the "License");
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
+ * distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import {ImageKnifeGlobal, ImageKnifeComponent, ImageKnifeOption, FileUtils } from '@ohos/imageknife'
-import common from '@ohos.app.ability.common'
+import { ImageKnife , ImageKnifeComponent ,ImageKnifeOption } from "@ohos/imageknife"
-@Entry
@Component
-struct Index {
-
-
- @State imageOption1:ImageKnifeOption = {
- loadSrc: $r('app.media.icon'),
- context: getContext(this).createModuleContext('sharedlibrary') as common.UIAbilityContext
+export struct IndexComponent {
+ @State imageKnifeOption: ImageKnifeOption = {
+ loadSrc: $r('app.media.startIcon')
}
- @State imageOption2:ImageKnifeOption = {
- loadSrc: $r('app.media.icon'),
- context: getContext(this).createModuleContext('sharedlibrary') as common.UIAbilityContext
- }
- @State imageOption3:ImageKnifeOption = {
- loadSrc: $r('app.media.icon'),
- context: getContext(this).createModuleContext('sharedlibrary') as common.UIAbilityContext
- }
-
build() {
- Scroll() {
- Column() {
- Button('点击加载Resource').onClick(()=>{
- this.imageOption1 = {
- loadSrc: $r('app.media.setting'),
- // 只要涉及resource加载 在HSP中都要带上context属性
- context: getContext(this).createModuleContext('sharedlibrary') as common.UIAbilityContext
- }
- })
- ImageKnifeComponent({imageKnifeOption:this.imageOption1}).width(300).height(300).backgroundColor(Color.Pink)
- Button('点击加载网络图片').onClick(()=>{
- this.imageOption2 = {
- loadSrc: 'https://hbimg.huabanimg.com/cc6af25f8d782d3cf3122bef4e61571378271145735e9-vEVggB',
- context: getContext(this).createModuleContext('sharedlibrary') as common.UIAbilityContext
- }
- })
- ImageKnifeComponent({imageKnifeOption:this.imageOption2}).width(300).height(300).backgroundColor(Color.Pink)
-
- Button('点击加载本地文件').onClick(()=>{
- getContext(this).createModuleContext('sharedlibrary').resourceManager.getMediaContent($r('app.media.setting').id).then((data:Uint8Array)=>{
- let ctx = getContext(this).createModuleContext('sharedlibrary') as common.UIAbilityContext;
- let path = ctx.filesDir+"/set.jpeg";
- FileUtils.getInstance().writeFile(path,data.buffer)
- FileUtils.getInstance().readFilePicAsync(path).then(buffer=>{
- this.imageOption3 = {
- loadSrc: path,
- context: getContext(this).createModuleContext('sharedlibrary') as common.UIAbilityContext
- }
- })
+ Column() {
+ Button("预加载").onClick((event: ClickEvent) => {
+ ImageKnife.getInstance()
+ .preLoadCache('https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp')
+ .then((data) => {
+ console.log("preLoadImage_FileCache:" + data)
+ this.imageKnifeOption.loadSrc = data
})
-
- })
- ImageKnifeComponent({imageKnifeOption:this.imageOption3}).width(300).height(300).backgroundColor(Color.Pink)
-
-
-
- }.width('100%')
-
+ })
+ ImageKnifeComponent({
+ imageKnifeOption:this.imageKnifeOption
+ }).width(300).height(300)
}
.width('100%')
.height('100%')
diff --git a/sharedlibrary2/src/main/ets/pages/Index.ets b/sharedlibrary/src/main/ets/pages/IndexPage.ets
similarity index 66%
rename from sharedlibrary2/src/main/ets/pages/Index.ets
rename to sharedlibrary/src/main/ets/pages/IndexPage.ets
index f2894ad..86424e3 100644
--- a/sharedlibrary2/src/main/ets/pages/Index.ets
+++ b/sharedlibrary/src/main/ets/pages/IndexPage.ets
@@ -1,27 +1,21 @@
/*
- * Copyright (C) 2023 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the "License");
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
+ * distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
@Entry
@Component
-struct Index {
-
-
+export struct IndexPage {
build() {
- Column(){
- Text('Hello world')
- }
}
}
\ No newline at end of file
diff --git a/sharedlibrary/src/main/ets/pages/InitImageKnife.ets b/sharedlibrary/src/main/ets/pages/InitImageKnife.ets
index 05fc782..7bf492c 100644
--- a/sharedlibrary/src/main/ets/pages/InitImageKnife.ets
+++ b/sharedlibrary/src/main/ets/pages/InitImageKnife.ets
@@ -1,22 +1,21 @@
/*
- * Copyright (C) 2023 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the "License");
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
+ * distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+import { ImageKnife } from '@ohos/imageknife'
import common from '@ohos.app.ability.common'
-import { ImageKnifeComponent, ImageKnifeOption, FileUtils } from '@ohos/imageknife'
-import { ImageKnifeGlobal,ImageKnife,ImageKnifeDrawFactory,LogUtil } from '@ohos/imageknife'
export class InitImageKnife{
- static init(entryContext:common.UIAbilityContext){
- ImageKnife.with(entryContext);
+ static async init(entryContext:common.UIAbilityContext){
+ await ImageKnife.getInstance().initFileCache(entryContext, 256, 256 * 1024 * 1024,"ImageKnifeCache")
}
}
\ No newline at end of file
diff --git a/sharedlibrary2/src/main/ets/Index.ets b/sharedlibrary/src/main/ets/utils/Calc.ets
similarity index 64%
rename from sharedlibrary2/src/main/ets/Index.ets
rename to sharedlibrary/src/main/ets/utils/Calc.ets
index aeba7a3..b2ed92e 100644
--- a/sharedlibrary2/src/main/ets/Index.ets
+++ b/sharedlibrary/src/main/ets/utils/Calc.ets
@@ -1,16 +1,17 @@
/*
- * Copyright (C) 2023 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the "License");
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
+ * distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-export {GetRes2} from '../ets/pages/GetRes2'
\ No newline at end of file
+export function add(a:number, b:number) {
+ return a + b;
+}
\ No newline at end of file
diff --git a/sharedlibrary/src/main/module.json5 b/sharedlibrary/src/main/module.json5
index 7e37699..5ff0ab4 100644
--- a/sharedlibrary/src/main/module.json5
+++ b/sharedlibrary/src/main/module.json5
@@ -4,8 +4,9 @@
"type": "shared",
"description": "$string:shared_desc",
"deviceTypes": [
- "default",
- "tablet"
+ "phone",
+ "tablet",
+ "2in1"
],
"deliveryWithInstall": true,
"pages": "$profile:main_pages"
diff --git a/sharedlibrary/src/main/resources/base/profile/main_pages.json b/sharedlibrary/src/main/resources/base/profile/main_pages.json
index 1898d94..3187f74 100644
--- a/sharedlibrary/src/main/resources/base/profile/main_pages.json
+++ b/sharedlibrary/src/main/resources/base/profile/main_pages.json
@@ -1,5 +1,5 @@
{
"src": [
- "pages/Index"
+ "pages/IndexPage"
]
-}
+}
\ No newline at end of file
diff --git a/sharedlibrary/src/mock/mock-config.json5 b/sharedlibrary/src/mock/mock-config.json5
new file mode 100644
index 0000000..7a73a41
--- /dev/null
+++ b/sharedlibrary/src/mock/mock-config.json5
@@ -0,0 +1,2 @@
+{
+}
\ No newline at end of file
diff --git a/entry/src/ohosTest/ets/test/imageknifeOption.test.ets b/sharedlibrary/src/ohosTest/ets/test/Ability.test.ets
similarity index 56%
rename from entry/src/ohosTest/ets/test/imageknifeOption.test.ets
rename to sharedlibrary/src/ohosTest/ets/test/Ability.test.ets
index 21a0f19..3417975 100644
--- a/entry/src/ohosTest/ets/test/imageknifeOption.test.ets
+++ b/sharedlibrary/src/ohosTest/ets/test/Ability.test.ets
@@ -1,64 +1,49 @@
/*
* Copyright (C) 2024 Huawei Device Co., Ltd.
- * Licensed under the Apache License, Version 2.0 (the "License");
+ * Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
+ * distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'
-import { ImageKnifeOption } from '@ohos/imageknife'
+import { hilog } from '@kit.PerformanceAnalysisKit';
+import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
-export default function ImageKnifeOptionTest() {
- describe('ImageKnifeOptionTest', ()=> {
+export default function abilityTest() {
+ describe('ActsAbilityTest', () => {
// Defines a test suite. Two parameters are supported: test suite name and test suite function.
- beforeAll( ()=> {
+ beforeAll(() => {
// Presets an action, which is performed only once before all test cases of the test suite start.
// This API supports only one parameter: preset action function.
})
- beforeEach( ()=> {
+ beforeEach(() => {
// Presets an action, which is performed before each unit test case starts.
// The number of execution times is the same as the number of test cases defined by **it**.
// This API supports only one parameter: preset action function.
})
- afterEach( ()=> {
+ afterEach(() => {
// Presets a clear action, which is performed after each unit test case ends.
// The number of execution times is the same as the number of test cases defined by **it**.
// This API supports only one parameter: clear action function.
})
- afterAll( ()=> {
+ afterAll(() => {
// Presets a clear action, which is performed after all test cases of the test suite end.
// This API supports only one parameter: clear action function.
})
-
- it('onLoadListener',0, ()=> {
+ it('assertContain', 0, () => {
+ // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
+ hilog.info(0x0000, 'testTag', '%{public}s', 'it begin');
let a = 'abc';
- let b: string = '';
- let imageKnifeOption: ImageKnifeOption = {
- loadSrc: 'https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658',
- onLoadListener: {
- onLoadSuccess: (data) => {
- if (typeof data == 'string') {
- b = data;
- }
- return data;
- },
- onLoadFailed: (err) => {
- console.log('Load Failed Reason: '+err);
- }
- }
- }
- if(imageKnifeOption.onLoadListener && imageKnifeOption.onLoadListener.onLoadSuccess && imageKnifeOption.onLoadListener.onLoadFailed) {
- imageKnifeOption.onLoadListener.onLoadSuccess(a);
- imageKnifeOption.onLoadListener.onLoadFailed(a);
- }
- expect(a).assertEqual(b);
+ let b = 'b';
+ // Defines a variety of assertion methods, which are used to declare expected boolean conditions.
+ expect(a).assertContain(b);
+ expect(a).assertEqual(a);
})
})
}
\ No newline at end of file
diff --git a/sharedlibrary/src/ohosTest/ets/test/List.test.ets b/sharedlibrary/src/ohosTest/ets/test/List.test.ets
new file mode 100644
index 0000000..b80e94c
--- /dev/null
+++ b/sharedlibrary/src/ohosTest/ets/test/List.test.ets
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import abilityTest from './Ability.test';
+
+export default function testsuite() {
+ abilityTest();
+}
\ No newline at end of file
diff --git a/sharedlibrary/src/ohosTest/ets/testability/TestAbility.ets b/sharedlibrary/src/ohosTest/ets/testability/TestAbility.ets
new file mode 100644
index 0000000..64fb765
--- /dev/null
+++ b/sharedlibrary/src/ohosTest/ets/testability/TestAbility.ets
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
+import { abilityDelegatorRegistry } from '@kit.TestKit';
+import { hilog } from '@kit.PerformanceAnalysisKit';
+import { window } from '@kit.ArkUI';
+import { Hypium } from '@ohos/hypium';
+import testsuite from '../test/List.test';
+
+export default class TestAbility extends UIAbility {
+ onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onCreate');
+ hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? '');
+ hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:' + JSON.stringify(launchParam) ?? '');
+ let abilityDelegator: abilityDelegatorRegistry.AbilityDelegator;
+ abilityDelegator = abilityDelegatorRegistry.getAbilityDelegator();
+ let abilityDelegatorArguments: abilityDelegatorRegistry.AbilityDelegatorArgs;
+ abilityDelegatorArguments = abilityDelegatorRegistry.getArguments();
+ hilog.info(0x0000, 'testTag', '%{public}s', 'start run testcase!!!');
+ Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite);
+ }
+
+ onDestroy() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onDestroy');
+ }
+
+ onWindowStageCreate(windowStage: window.WindowStage) {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageCreate');
+ windowStage.loadContent('testability/pages/Index', (err) => {
+ if (err.code) {
+ hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
+ return;
+ }
+ hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.');
+ });
+ }
+
+ onWindowStageDestroy() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageDestroy');
+ }
+
+ onForeground() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onForeground');
+ }
+
+ onBackground() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onBackground');
+ }
+}
\ No newline at end of file
diff --git a/sharedlibrary/src/ohosTest/ets/testability/pages/Index.ets b/sharedlibrary/src/ohosTest/ets/testability/pages/Index.ets
new file mode 100644
index 0000000..98be4ce
--- /dev/null
+++ b/sharedlibrary/src/ohosTest/ets/testability/pages/Index.ets
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@Entry
+@Component
+struct Index {
+ @State message: string = 'Hello World';
+
+ build() {
+ Row() {
+ Column() {
+ Text(this.message)
+ .fontSize(50)
+ .fontWeight(FontWeight.Bold)
+ }
+ .width('100%')
+ }
+ .height('100%')
+ }
+}
\ No newline at end of file
diff --git a/sharedlibrary/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ets b/sharedlibrary/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ets
new file mode 100644
index 0000000..a5c1586
--- /dev/null
+++ b/sharedlibrary/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ets
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { abilityDelegatorRegistry, TestRunner } from '@kit.TestKit';
+import { UIAbility, Want } from '@kit.AbilityKit';
+import { BusinessError } from '@kit.BasicServicesKit';
+import { hilog } from '@kit.PerformanceAnalysisKit';
+import { resourceManager } from '@kit.LocalizationKit';
+import { util } from '@kit.ArkTS';
+
+let abilityDelegator: abilityDelegatorRegistry.AbilityDelegator;
+let abilityDelegatorArguments: abilityDelegatorRegistry.AbilityDelegatorArgs;
+let jsonPath: string = 'mock/mock-config.json';
+let tag: string = 'testTag';
+
+async function onAbilityCreateCallback(data: UIAbility) {
+ hilog.info(0x0000, 'testTag', 'onAbilityCreateCallback, data: ${}', JSON.stringify(data));
+}
+
+async function addAbilityMonitorCallback(err: BusinessError) {
+ hilog.info(0x0000, 'testTag', 'addAbilityMonitorCallback : %{public}s', JSON.stringify(err) ?? '');
+}
+
+export default class OpenHarmonyTestRunner implements TestRunner {
+ constructor() {
+ }
+
+ onPrepare() {
+ hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner OnPrepare');
+ }
+
+ async onRun() {
+ let tag = 'testTag';
+ hilog.info(0x0000, tag, '%{public}s', 'OpenHarmonyTestRunner onRun run');
+ abilityDelegatorArguments = abilityDelegatorRegistry.getArguments()
+ abilityDelegator = abilityDelegatorRegistry.getAbilityDelegator()
+ let moduleName = abilityDelegatorArguments.parameters['-m'];
+ let context = abilityDelegator.getAppContext().getApplicationContext().createModuleContext(moduleName);
+ let mResourceManager = context.resourceManager;
+ await checkMock(abilityDelegator, mResourceManager);
+ const bundleName = abilityDelegatorArguments.bundleName;
+ const testAbilityName: string = 'TestAbility';
+ let lMonitor: abilityDelegatorRegistry.AbilityMonitor = {
+ abilityName: testAbilityName,
+ onAbilityCreate: onAbilityCreateCallback,
+ moduleName: moduleName
+ };
+ abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback)
+ const want: Want = {
+ bundleName: bundleName,
+ abilityName: testAbilityName,
+ moduleName: moduleName
+ };
+ abilityDelegator.startAbility(want, (err: BusinessError, data: void) => {
+ hilog.info(0x0000, tag, 'startAbility : err : %{public}s', JSON.stringify(err) ?? '');
+ hilog.info(0x0000, tag, 'startAbility : data : %{public}s', JSON.stringify(data) ?? '');
+ })
+ hilog.info(0x0000, tag, '%{public}s', 'OpenHarmonyTestRunner onRun end');
+ }
+}
+
+async function checkMock(abilityDelegator: abilityDelegatorRegistry.AbilityDelegator, resourceManager: resourceManager.ResourceManager) {
+ let rawFile: Uint8Array;
+ try {
+ rawFile = resourceManager.getRawFileContentSync(jsonPath);
+ hilog.info(0x0000, tag, 'MockList file exists');
+ let mockStr: string = util.TextDecoder.create('utf-8', { ignoreBOM: true }).decodeWithStream(rawFile);
+ let mockMap: Record = getMockList(mockStr);
+ try {
+ abilityDelegator.setMockList(mockMap)
+ } catch (error) {
+ let code = (error as BusinessError).code;
+ let message = (error as BusinessError).message;
+ hilog.error(0x0000, tag, `abilityDelegator.setMockList failed, error code: ${code}, message: ${message}.`);
+ }
+ } catch (error) {
+ let code = (error as BusinessError).code;
+ let message = (error as BusinessError).message;
+ hilog.error(0x0000, tag, `ResourceManager:callback getRawFileContent failed, error code: ${code}, message: ${message}.`);
+ }
+}
+
+function getMockList(jsonStr: string) {
+ let jsonObj: Record = JSON.parse(jsonStr);
+ let map: Map = new Map(Object.entries(jsonObj));
+ let mockList: Record = {};
+ map.forEach((value: object, key: string) => {
+ let realValue: string = value['source'].toString();
+ mockList[key] = realValue;
+ });
+ hilog.info(0x0000, tag, '%{public}s', 'mock-json value:' + JSON.stringify(mockList) ?? '');
+ return mockList;
+}
\ No newline at end of file
diff --git a/sharedlibrary/src/ohosTest/module.json5 b/sharedlibrary/src/ohosTest/module.json5
new file mode 100644
index 0000000..4cca4a0
--- /dev/null
+++ b/sharedlibrary/src/ohosTest/module.json5
@@ -0,0 +1,38 @@
+{
+ "module": {
+ "name": "sharedlibrary_test",
+ "type": "feature",
+ "description": "$string:module_test_desc",
+ "mainElement": "TestAbility",
+ "deviceTypes": [
+ "phone",
+ "tablet",
+ "2in1"
+ ],
+ "deliveryWithInstall": true,
+ "installationFree": false,
+ "pages": "$profile:test_pages",
+ "abilities": [
+ {
+ "name": "TestAbility",
+ "srcEntry": "./ets/testability/TestAbility.ets",
+ "description": "$string:TestAbility_desc",
+ "icon": "$media:icon",
+ "label": "$string:TestAbility_label",
+ "exported": true,
+ "startWindowIcon": "$media:icon",
+ "startWindowBackground": "$color:start_window_background",
+ "skills": [
+ {
+ "actions": [
+ "action.system.home"
+ ],
+ "entities": [
+ "entity.system.home"
+ ]
+ }
+ ]
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/sharedlibrary2/src/main/resources/base/element/color.json b/sharedlibrary/src/ohosTest/resources/base/element/color.json
similarity index 58%
rename from sharedlibrary2/src/main/resources/base/element/color.json
rename to sharedlibrary/src/ohosTest/resources/base/element/color.json
index 1bbc9aa..3c71296 100644
--- a/sharedlibrary2/src/main/resources/base/element/color.json
+++ b/sharedlibrary/src/ohosTest/resources/base/element/color.json
@@ -1,7 +1,7 @@
{
"color": [
{
- "name": "white",
+ "name": "start_window_background",
"value": "#FFFFFF"
}
]
diff --git a/sharedlibrary/src/ohosTest/resources/base/element/string.json b/sharedlibrary/src/ohosTest/resources/base/element/string.json
new file mode 100644
index 0000000..65d8fa5
--- /dev/null
+++ b/sharedlibrary/src/ohosTest/resources/base/element/string.json
@@ -0,0 +1,16 @@
+{
+ "string": [
+ {
+ "name": "module_test_desc",
+ "value": "test ability description"
+ },
+ {
+ "name": "TestAbility_desc",
+ "value": "the test ability"
+ },
+ {
+ "name": "TestAbility_label",
+ "value": "test label"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/sharedlibrary/src/ohosTest/resources/base/media/icon.png b/sharedlibrary/src/ohosTest/resources/base/media/icon.png
new file mode 100644
index 0000000..a39445d
Binary files /dev/null and b/sharedlibrary/src/ohosTest/resources/base/media/icon.png differ
diff --git a/sharedlibrary/src/ohosTest/resources/base/profile/test_pages.json b/sharedlibrary/src/ohosTest/resources/base/profile/test_pages.json
new file mode 100644
index 0000000..b7e7343
--- /dev/null
+++ b/sharedlibrary/src/ohosTest/resources/base/profile/test_pages.json
@@ -0,0 +1,5 @@
+{
+ "src": [
+ "testability/pages/Index"
+ ]
+}
diff --git a/sharedlibrary/src/test/List.test.ets b/sharedlibrary/src/test/List.test.ets
new file mode 100644
index 0000000..f43d80b
--- /dev/null
+++ b/sharedlibrary/src/test/List.test.ets
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import localUnitTest from './LocalUnit.test';
+
+export default function testsuite() {
+ localUnitTest();
+}
\ No newline at end of file
diff --git a/sharedlibrary/src/test/LocalUnit.test.ets b/sharedlibrary/src/test/LocalUnit.test.ets
new file mode 100644
index 0000000..9ec8a77
--- /dev/null
+++ b/sharedlibrary/src/test/LocalUnit.test.ets
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
+
+export default function localUnitTest() {
+ describe('localUnitTest', () => {
+ // Defines a test suite. Two parameters are supported: test suite name and test suite function.
+ beforeAll(() => {
+ // Presets an action, which is performed only once before all test cases of the test suite start.
+ // This API supports only one parameter: preset action function.
+ });
+ beforeEach(() => {
+ // Presets an action, which is performed before each unit test case starts.
+ // The number of execution times is the same as the number of test cases defined by **it**.
+ // This API supports only one parameter: preset action function.
+ });
+ afterEach(() => {
+ // Presets a clear action, which is performed after each unit test case ends.
+ // The number of execution times is the same as the number of test cases defined by **it**.
+ // This API supports only one parameter: clear action function.
+ });
+ afterAll(() => {
+ // Presets a clear action, which is performed after all test cases of the test suite end.
+ // This API supports only one parameter: clear action function.
+ });
+ it('assertContain', 0, () => {
+ // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
+ let a = 'abc';
+ let b = 'b';
+ // Defines a variety of assertion methods, which are used to declare expected boolean conditions.
+ expect(a).assertContain(b);
+ expect(a).assertEqual(a);
+ });
+ });
+}
\ No newline at end of file
diff --git a/sharedlibrary2/.gitignore b/sharedlibrary2/.gitignore
deleted file mode 100644
index e2713a2..0000000
--- a/sharedlibrary2/.gitignore
+++ /dev/null
@@ -1,6 +0,0 @@
-/node_modules
-/oh_modules
-/.preview
-/build
-/.cxx
-/.test
\ No newline at end of file
diff --git a/sharedlibrary2/build-profile.json5 b/sharedlibrary2/build-profile.json5
deleted file mode 100644
index befa101..0000000
--- a/sharedlibrary2/build-profile.json5
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "apiType": 'stageMode',
- "buildOption": {
- },
- "targets": [
- {
- "name": "default"
- }
- ]
-}
\ No newline at end of file
diff --git a/sharedlibrary2/hvigorfile.ts b/sharedlibrary2/hvigorfile.ts
deleted file mode 100644
index 0e65ea8..0000000
--- a/sharedlibrary2/hvigorfile.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently.
-module.exports = require('@ohos/hvigor-ohos-plugin').hspTasks
diff --git a/sharedlibrary2/oh-package.json5 b/sharedlibrary2/oh-package.json5
deleted file mode 100644
index f6d8e3a..0000000
--- a/sharedlibrary2/oh-package.json5
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "license": "Apache-2.0",
- "devDependencies": {},
- "author": "",
- "name": "sharedlibrary2",
- "description": "Please describe the basic information.",
- "main": "./src/main/ets/Index.ets",
- "version": "1.0.0",
- "dependencies": {
- }
-}
diff --git a/sharedlibrary2/src/main/module.json5 b/sharedlibrary2/src/main/module.json5
deleted file mode 100644
index ff37b1a..0000000
--- a/sharedlibrary2/src/main/module.json5
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "module": {
- "name": "sharedlibrary2",
- "type": "shared",
- "description": "$string:shared_desc",
- "deviceTypes": [
- "default",
- "tablet"
- ],
- "deliveryWithInstall": true,
- "pages": "$profile:main_pages"
- }
-}
\ No newline at end of file
diff --git a/sharedlibrary2/src/main/resources/base/element/string.json b/sharedlibrary2/src/main/resources/base/element/string.json
deleted file mode 100644
index 98e1d8a..0000000
--- a/sharedlibrary2/src/main/resources/base/element/string.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "string": [
- {
- "name": "shared_desc",
- "value": "description"
- }
- ]
-}
\ No newline at end of file
diff --git a/sharedlibrary2/src/main/resources/base/media/icon_loading.png b/sharedlibrary2/src/main/resources/base/media/icon_loading.png
deleted file mode 100644
index 592d50b..0000000
Binary files a/sharedlibrary2/src/main/resources/base/media/icon_loading.png and /dev/null differ
diff --git a/sharedlibrary2/src/main/resources/base/profile/main_pages.json b/sharedlibrary2/src/main/resources/base/profile/main_pages.json
deleted file mode 100644
index 1898d94..0000000
--- a/sharedlibrary2/src/main/resources/base/profile/main_pages.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "src": [
- "pages/Index"
- ]
-}