diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e4d6c5..bab7ab2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,70 @@ +## 2.2.0-rc.1 +- 修改ImageKnife跳过网络,点击默认,图片没有传入宽高,无显示bug +- ImageKnife支持根据自定义key获取已缓存的图片 +- ImageKnife加载图片支持自定义网络栈和图片加载组件 +- 适配复用场景触发懒加载onDataReloaded +- ImageKnife控制重要图片请求加载优先级 + +## 2.2.0-rc.0 +- 修复自定义DataFetch接口实现不生效问题 +- 修改磁盘缓存到子线程 +- 更新SDK到API12 +- 适配Sendable内存共享优化 +- 修改全局请求头覆盖request请求头 +- imageKnife支持heic测试demo独立页面展示 +- drawLifeCycle支持gif图 + +## 2.1.2 +- 修改ImageKnife跳过网络,从内存中获取图片 cacheType参数未使用bug +- 新增WEBP图片解析能力。 +- 新增gif图片支持暂停播放功能 + +## 2.1.2-rc.12 +- 新增gif播放次数功能 +- 新增磁盘预加载返回文件路径接口prefetchToDiskCache +- 新增跳过网络判断缓存或者磁盘中是否存在图片接口isUrlExist +- 删除多余操作磁盘记录读写 +- 清除定时器改为Gif图时清除 +- uuid改为util.generateRandomUUID() + +## 2.1.2-rc.11 +- 修复设置磁盘容量最大值出现闪退 +- 修复概率出现jscrash问题 +- 修复进度条问题 +- 修复单帧gif图片加载失败 +- removeRunning删除running队列log设置开关 +- ImageKnife新增图片宽高自适应功能 +- 修复onlyRetrieveFromCache属性(仅磁盘和内存获取资源)失效 +- 修改拼写错误 +- 新增多线程优先级 +- 修复复用场景下图片闪动以及概率错位 +- 获取组件宽高改为使用CanvasRenderingContext2D对象获取宽高,并修复改变字体大小导致部分图片消失 +- 修复获取不到磁盘缓存文件问题 +- 修复获取不到网络请求错误回调问题 + +## 2.1.2-rc.10 +- 修复部分gif图片识别成静态图 +- 修复同一张图片发送多次请求 +- 复用场景缓存到树aboutToRecycle清理定时器 + +## 2.1.2-rc.9 +- 使用taskpool实现多线程加载图片资源 +- 修复部分gif图片识别成静态图 +- 修复同一张图片发送多次请求 +- disAppear生命周期清空定时器只在gif图片时执行 + +## 2.1.2-rc.8 +- onAreaChange绘制图片改为component Util绘制 + +## 2.1.2-rc.7 +- 修复图片圆角图形变换导致抗锯齿、ScaleType失效 +- 修复使用模糊化出现图片变模糊和变形 + ## 2.1.2-rc.6 - 修复手机调节显示大小时图片消失 +- imageKnife防盗链,header请求头属性设置 +- pngWorker线程改为taskpool +- 修复正方形裁剪有些图片裁剪创建pixelMap失败 ## 2.1.2-rc.5 - moduleContext新增缓存策略,缓存上限5,缓存策略Lru diff --git a/OAT.xml b/OAT.xml index 10e5eb1..db4b883 100644 --- a/OAT.xml +++ b/OAT.xml @@ -1,4 +1,21 @@ + @@ -32,6 +49,7 @@ + diff --git a/README.OpenSource b/README.OpenSource index ac553d7..4737674 100644 --- a/README.OpenSource +++ b/README.OpenSource @@ -2,9 +2,9 @@ { "Name": "glide", "License": "Apache License 2.0", - "License File": "https://github.com/bumptech/glide/blob/master/LICENSE", + "License File": "LICENSE", "Version Number": "4.13.1", - "Owner" : "bumptech", + "Owner" : "xiafeng@huawei.com", "Upstream URL": "https://github.com/bumptech/glide", "Description": "An image loading and caching library focused on smooth scrolling" }, @@ -12,9 +12,9 @@ { "Name": "glide-transformations", "License": "Apache License 2.0", - "License File": "https://github.com/wasabeef/glide-transformations/blob/main/LICENSE", + "License File": "LICENSE", "Version Number": "4.3.0", - "Owner" : "wasabeef", + "Owner" : "xiafeng@huawei.com", "Upstream URL": "https://github.com/wasabeef/glide-transformations", "Description": " An transformation library providing a variety of image transformations for Glide. " }, @@ -22,9 +22,9 @@ { "Name": "fresco", "License": "MIT License", - "License File": "https://github.com/facebook/fresco/blob/main/LICENSE", + "License File": "LICENSE", "Version Number": "2.6.0", - "Owner" : "facebook", + "Owner" : "xiafeng@huawei.com", "Upstream URL": "https://github.com/facebook/fresco", "Description": " An library for managing images and the memory they use. " }, @@ -32,9 +32,9 @@ { "Name": "UPNG.js", "License": "MIT License", - "License File": "https://github.com/photopea/UPNG.js/blob/master/LICENSE", + "License File": "LICENSE", "Version Number": "1.0.0", - "Owner" : "photopea", + "Owner" : "xiafeng@huawei.com", "Upstream URL": "https://github.com/photopea/UPNG.js", "Description": " Fast and advanced PNG (APNG) decoder and encoder (lossy / lossless) " }, @@ -42,9 +42,9 @@ { "Name": "Luban", "License": "Apache License 2.0", - "License File": "https://github.com/Curzibn/Luban/blob/master/LICENSE", + "License File": "LICENSE", "Version Number": "1.1.8", - "Owner" : " Curzibn", + "Owner" : " xiafeng@huawei.com", "Upstream URL": "https://github.com/Curzibn/Luban", "Description": " Luban(鲁班)—Image compression with efficiency very close to WeChat Moments/可能是最接近微信朋友圈的图片压缩算法 " }, @@ -54,16 +54,16 @@ "License": "Apache License 2.0", "License File": "https://github.com/Yalantis/uCrop/blob/develop/README.md", "Version Number": "2.2.8", - "Owner" : " Yalantis", + "Owner" : " xiafeng@huawei.com", "Upstream URL": "https://github.com/Yalantis/uCrop", "Description": " Image Cropping Library " }, { "Name": "js-spark-md5", "License": "MIT", - "License File": "https://github.com/satazor/js-spark-md5/blob/master/LICENSE", + "License File": "LICENSE", "Version Number": "v3.0.2", - "Owner" : "satazor", + "Owner" : "xiafeng@huawei.com", "Upstream URL": "https://github.com/satazor/js-spark-md5", "Description": "Lightning fast normal and incremental md5 for javascript" } diff --git a/README.md b/README.md index df64d43..5cd4314 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,10 @@ svg类型图片即可。 加载GIF其实和普通流程也没有区别只要将 `loadSrc: $r('app.media.jpgSample'),` `改成一张 loadSrc: $r('app.media.gifSample'),` GIF图片即可。 +#### 4.1加载GIF图片 + +更改ImageKnifeOption对象的autoPlay(可选autoPlay = true为开始播放,autoPlay = false为暂停播放) + ### 5.自定义Key 因为通常改变标识符比较困难或者根本不可能,所以ImageKnife也提供了 签名 API 来混合(你可以控制的)额外数据到你的缓存键中。 签名(signature)适用于媒体内容,也适用于你可以自行维护的一些版本元数据。 @@ -101,39 +105,44 @@ imageKnifeOption = { 代码示例 +### 6.自定义请求头规格 +设置全局header并且设置request的header时,当key不同时全局和request并行,当key相同时request的header覆盖全局的header + ## 进阶使用 如果简单的加载一张图像无法满足需求,我们可以看看ImageKnifeOption这个类提供了哪些扩展能力。 ### ImageKnifeOption参数列表 -| 参数名称 | 入参内容 | 功能简介 | -| ---------------------------- | ------------------------------------------------------------ |-----------------------------------------------| -| loadSrc | string\| PixelMap\ |Resource | 图片数据源 | -| mainScaleType | ScaleType | 设置主图展示样式(可选) | -| strategy | DiskStrategy | 设置磁盘缓存策略(可选) | -| dontAnimateFlag | boolean | gif加载展示一帧(可选) | -| placeholderSrc | PixelMap\| Resource | 占位图数据源 | -| placeholderScaleType | ScaleType | 设置占位图展示样式(可选) | -| errorholderSrc | PixelMap\| Resource | 错误占位图数据源 | -| errorholderSrcScaleType | ScaleType | 设置失败占位图展示样式(可选) | -| retryholderSrc | PixelMap\| Resource | 重试占位图数据源 | -| retryholderScaleType | ScaleType | 设置重试占位图展示样式(可选) | -| 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
} | GIF播放控制能力(可选) | -| transformation | BaseTransform | 单个变换(可选) | -| transformations | Array> | 多个变换,目前仅支持单个变换(可选) | -| allCacheInfoCallback | IAllCacheInfoCallback | 输出缓存相关内容和信息(可选) | -| signature | ObjectKey | 自定key(可选) | -| **drawLifeCycle** | **IDrawLifeCycle** | **用户自定义实现绘制方案(可选)** | -| imageSmoothingEnabled | boolean | 抗锯齿是否开启属性配置,设置为false时,imageSmoothingQuality失效 | -| imageSmoothingQuality | AntiAliasing | 抗锯齿属性配置 | +| 参数名称 | 入参内容 | 功能简介 | +|------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------| +| 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播放暂停控制(可选) | 其他参数只需要在ImageKnifeOption对象上按需添加即可。 @@ -312,9 +321,10 @@ request.skipMemoryCache(true) | 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: PixelMap\|Resource, func?: AsyncSuccess) | src: PixelMap\|Resource, func?: AsyncSuccess | 占位图,占位图回调数据ImageKnifeData | +| 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 | 设置网络下载百分比监听,返回数据加载百分比数值 | @@ -327,12 +337,14 @@ request.skipMemoryCache(true) ### ImageKnife 启动器/门面类 -| 方法名 | 入参 | 接口描述 | -| ------------------------------- | ---------------------- | ---------------------------------- | -| call(request: RequestOption) | request: RequestOption | 根据用户配置参数具体执行加载流程 | -| preload(request: RequestOption) | request: RequestOption | 根据用户配置参数具体执行预加载流程 | -| pauseRequests() | | 全局暂停请求 | -| resumeRequests() | | 全局恢复暂停 | +| 方法名 | 入参 | 接口描述 | +|----------------------------------| ---------------------- | ------------------------------------------------------------ | +| 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 | 设置请求的最大并发数量 | ### 缓存策略相关 @@ -352,6 +364,14 @@ request.skipMemoryCache(true) | AntiAliasing.FIT_MEDIUM | String | 图像抗锯齿设置为中画质 | | AntiAliasing.FIT_LOW | String | 图像抗锯齿设置为低画质 | +### CacheType类型展示效果 + +| 使用方法 | 类型 | 策略描述 | +|-------------------------|-----|-----------------------------------| +| CacheType.Default| int | 默认值,先从内存获取,无值则从磁盘获取 | +| CacheType.Cache| int | 缓存 | +| CacheType.Disk | int | 磁盘 | + ### ScaleType类型展示效果 | 使用方法 | 类型 | 策略描述 | @@ -364,33 +384,36 @@ request.skipMemoryCache(true) | 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.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.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 | 装饰滤波器 | @@ -426,10 +449,18 @@ export default class EntryAbility extends UIAbility { } ``` +### 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) @@ -442,6 +473,10 @@ HSP场景适配: 非HSP场景不影响原功能, ImageKnifeOption配置类新增的可选参数context可以不传, RquestOption配置类新增的接口可以不调用。 +注意: + +基于性能优化的原因,2.1.2-rc.13及以后版本引用了API12 Sendable接口,至此以后的版本只支持API12。 + ## 目录结构 ``` @@ -502,6 +537,9 @@ HSP场景适配: - storageTestDiskLruCache.ets # 磁盘缓存测试 - storageTestLruCache.ets # 内存缓存测试 - testAllCacheInfoPage.ets # 所有缓存信息获取测试 + - testImageKnifeAutoHeightPage.ets # 图片高度自适应测试 + - testImageKnifeAutoWidthPage.ets # 图片宽度自适应测试 + - testImageKnifeAutoPage.ets # 图片宽高自适应测试 - testImageKnifeOptionChangedPage.ets # 数据切换测试 - testImageKnifeOptionChangedPage2.ets # 数据切换测试,部分变换 - testImageKnifeOptionChangedPage3.ets # 数据切换测试,组件动画 @@ -510,9 +548,12 @@ HSP场景适配: - testPreloadPage.ets # 预加载测试 - transformPixelMapPage.ets # 所有类型变换测试 - testSingleFrameGifPage.ets # 单帧gif加载测试 + - TestStopPlayingGifPage # gif播放暂停测试 + - TestImageKnifeNetPlaceholder # 缓存获取string类型占位图以及后备回调符测试 - OptionTestPage.ets # 图片缓存测试 - testManyGifLoadWithPage # 测试gif加载页面 + - testImageKnifeCache # 测试图片是否在缓存或者磁盘中 -workers - upngWorkerTestCase.ets # png子线程解析 - upngWorkerDepend.ts # png子线程解析具体执行 diff --git a/build-profile.json5 b/build-profile.json5 index aa57008..79fdb01 100644 --- a/build-profile.json5 +++ b/build-profile.json5 @@ -4,8 +4,8 @@ { "name": "default", "signingConfig": "default", - "compileSdkVersion": 10, - "compatibleSdkVersion": 10 + "compileSdkVersion": 12, + "compatibleSdkVersion": 12 } ], "buildModeSet": [ diff --git a/entry/oh-package.json5 b/entry/oh-package.json5 index bec89dd..113d186 100644 --- a/entry/oh-package.json5 +++ b/entry/oh-package.json5 @@ -4,7 +4,7 @@ "name": "entry", "description": "example description", "repository": {}, - "version": "2.1.2-rc.6", + "version": "2.1.2-rc.12", "dependencies": { "@ohos/libraryimageknife": "file:../sharedlibrary", "@ohos/sharedlibrary2": "file:../sharedlibrary2", diff --git a/entry/src/main/ets/entryability/EntryAbility.ets b/entry/src/main/ets/entryability/EntryAbility.ets index d28ff9b..97ad9c5 100644 --- a/entry/src/main/ets/entryability/EntryAbility.ets +++ b/entry/src/main/ets/entryability/EntryAbility.ets @@ -49,6 +49,9 @@ export default class EntryAbility extends UIAbility { 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 diff --git a/entry/src/main/ets/pages/RequestOptionLoadImage.ets b/entry/src/main/ets/pages/RequestOptionLoadImage.ets index 4b7fb99..66ce87b 100644 --- a/entry/src/main/ets/pages/RequestOptionLoadImage.ets +++ b/entry/src/main/ets/pages/RequestOptionLoadImage.ets @@ -19,7 +19,8 @@ import { Size, ImageKnife, ImageKnifeGlobal, - ImageKnifeComponent + ImageKnifeComponent, + ObjectKey } from '@ohos/libraryimageknife' import { BusinessError } from '@ohos.base' @@ -34,6 +35,10 @@ struct RequestOptionLoadImage { load(src: string | image.PixelMap | Resource) { clearTimeout(timeId) let request = new RequestOption() + //*requestOption调用* + request.addHeader('refer', 'http://1.94.37.200:7070/AntiTheftChain/downloadImage'); + //通过时间戳去清除缓存 + request.signature = new ObjectKey(new Date().getTime().toString()) request.load(src) .addListener({ callback: (err: BusinessError | string, data: ImageKnifeData) => { if (data.isPixelMap()) { diff --git a/entry/src/main/ets/pages/basicTestFileIOPage.ets b/entry/src/main/ets/pages/basicTestFileIOPage.ets index 7488325..eb987e2 100644 --- a/entry/src/main/ets/pages/basicTestFileIOPage.ets +++ b/entry/src/main/ets/pages/basicTestFileIOPage.ets @@ -69,10 +69,14 @@ struct basicTestFileIOPage { return } console.log('files目录创建Folder1和Folder2 验证statSync mkdirSync') - FileUtils.getInstance() - .createFolder(this.appFilePath + '/Folder1'); - FileUtils.getInstance() - .createFolder(this.appFilePath + '/Folder2'); + try { + FileUtils.getInstance() + .createFolder(this.appFilePath + '/Folder1'); + FileUtils.getInstance() + .createFolder(this.appFilePath + '/Folder2'); + } catch (e) { + console.log('appFilePath未取到值,请按顺序从上往下,从左往右依次测试"'+JSON.stringify(e)); + } }) Button('将media资源存入Folder1 验证writeSync mkdirSync createStreamSync') .margin({ top: 10 }) @@ -105,13 +109,18 @@ struct basicTestFileIOPage { this.imageHint2 = 'appFilePath未取到值,请按顺序从上往下,从左往右依次测试' return } - let filePath1 = this.appFilePath + '/Folder1/jpgSample.gif'; - let filePath2 = this.appFilePath + '/Folder2/jpgSample.gif'; - FileUtils.getInstance().createFolder(this.appFilePath + '/Folder1') - FileUtils.getInstance().createFolder(this.appFilePath + '/Folder2') - FileUtils.getInstance().copyFile(filePath1, filePath2); - this.imageFile = 'file://' + this.appFilePath + '/Folder2/jpgSample.gif' - console.log('Folder2 imaeFile =' + this.imageFile) + try { + let filePath1 = this.appFilePath + '/Folder1/jpgSample.gif'; + let filePath2 = this.appFilePath + '/Folder2/jpgSample.gif'; + FileUtils.getInstance().createFolder(this.appFilePath + '/Folder1') + FileUtils.getInstance().createFolder(this.appFilePath + '/Folder2') + FileUtils.getInstance().copyFile(filePath1, filePath2); + this.imageFile = 'file://' + this.appFilePath + '/Folder2/jpgSample.gif' + console.log('Folder2 imaeFile =' + this.imageFile) + } catch (e) { + console.log('appFilePath未取到值,请按顺序从上往下,从左往右依次测试:'+JSON.stringify(e)) + } + }) Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { Button('显示空PixelMap') diff --git a/entry/src/main/ets/pages/imageknifeTestCaseIndex.ets b/entry/src/main/ets/pages/imageknifeTestCaseIndex.ets index 0161792..a951647 100644 --- a/entry/src/main/ets/pages/imageknifeTestCaseIndex.ets +++ b/entry/src/main/ets/pages/imageknifeTestCaseIndex.ets @@ -15,7 +15,6 @@ import router from '@ohos.router'; - @Entry @Component struct IndexFunctionDemo { @@ -36,8 +35,13 @@ struct IndexFunctionDemo { console.log("测试文件子系统") router.pushUrl({ url: "pages/basicTestFileIOPage" }); }).margin({ top: 5, left: 3 }) - + Button("优先级加载") + .onClick(() => { + console.log("优先级加载") + router.pushUrl({ url: "pages/testPriorityComponent" }); + }).margin({ top: 5, left: 3 }) }.width('100%').height(60).backgroundColor(Color.Pink) + Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { Button("测试全球化") .onClick(() => { @@ -53,8 +57,6 @@ struct IndexFunctionDemo { }.width('100%').height(60).backgroundColor(Color.Pink) - - Text("测试图片切换功能点").fontSize(15) Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { @@ -63,8 +65,32 @@ struct IndexFunctionDemo { console.log("测试ImageKnifeComponent所有图片切换") router.pushUrl({ url: "pages/testImageKnifeOptionChangedPage" }); }).margin({ top: 5, left: 3 }) + Button("测试图片高度自适应") + .onClick(() => { + console.log("测试ImageKnifeComponent图片高度自适应") + router.pushUrl({ url: "pages/testImageKnifeAutoHeightPage" }); + }).margin({ top: 5, left: 3 }) + Button("测试gif播放次数") + .onClick(() => { + router.pushUrl({ url: "pages/testGifPlayTimesPage" }); + }).margin({ top: 15 }) + }.width('100%').height(60).backgroundColor(Color.Pink) + + Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + + Button("测试图片宽度自适应") + .onClick(() => { + console.log("测试ImageKnifeComponent图片宽度自适应") + router.pushUrl({ url: "pages/testImageKnifeAutoWidthPage" }); + }).margin({ top: 5, left: 3 }) + Button("测试图片宽高自适应") + .onClick(() => { + console.log("测试ImageKnifeComponent图片宽高自适应") + router.pushUrl({ url: "pages/testImageKnifeAutoPage" }); + }).margin({ top: 5, left: 3 }) }.width('100%').height(60).backgroundColor(Color.Pink) + Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { Button("测试thumbnail") .onClick(() => { @@ -94,7 +120,20 @@ struct IndexFunctionDemo { }.width('100%') .height(60).backgroundColor(Color.Pink) - + Text("测试复用场景").fontSize(15) + Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Button("懒加载复用列表") + .onClick(() => { + console.log("测试一级内存缓存") + router.pushUrl({ url: "pages/testReusePhotoPage" }); + }).margin({ top: 5, left: 3 }) + Button("适配复用场景") + .onClick(() => { + console.log("测试一级内存缓存") + router.pushUrl({ url: "pages/testReuseAblePages" }); + }).margin({ top: 5, left: 3 }) + }.width('100%') + .height(60).backgroundColor(Color.Pink) Text("测试占位图 失败占位图 功能点").fontSize(15) Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { Button("测试失败占位图") @@ -111,6 +150,11 @@ struct IndexFunctionDemo { console.log("测试预加载") router.pushUrl({ url: "pages/testPreloadPage" }); }).margin({ top: 5, left: 3 }) + Button("测试磁盘预加载返回string") + .onClick(() => { + console.log("测试预加载测试磁盘预加载返回string") + router.pushUrl({ url: "pages/testDiskPreLoadPage" }); + }).margin({ top: 5, left: 3 }) }.width('100%').height(60).backgroundColor(Color.Pink) @@ -190,6 +234,7 @@ struct IndexFunctionDemo { }).margin({ top: 15 }) }.width('100%').height(60).backgroundColor(Color.Pink) + Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { Button("组件显式动画") @@ -211,7 +256,6 @@ struct IndexFunctionDemo { }).margin({ top: 15 }) - }.width('100%').height(60).backgroundColor(Color.Pink) Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { @@ -253,6 +297,10 @@ struct IndexFunctionDemo { .onClick(() => { router.pushUrl({ url: "pages/testSingleFrameGifPage" }); }).margin({ top: 5, left: 3 }) + Button("webp测试") + .onClick(() => { + router.pushUrl({ url: "pages/webpImageTestPage" }); + }).margin({ top: 5, left: 3 }) }.width('100%').height(60).backgroundColor(Color.Pink) Text("worker测试").fontSize(15) @@ -262,13 +310,14 @@ struct IndexFunctionDemo { }).margin({ top: 5, left: 3 }) }.width('100%').height(60).backgroundColor(Color.Pink) + Text("HSP相关测试").fontSize(15) Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { Button("进入HSP的library共享包") .onClick(() => { - router.pushUrl({url:'@bundle:com.openharmony.imageknife/sharedlibrary/ets/pages/Index'}) - .then(()=>{ + router.pushUrl({ url: '@bundle:com.openharmony.imageknife/sharedlibrary/ets/pages/Index' }) + .then(() => { console.log('push page suceess') }) }).margin({ top: 15 }) @@ -278,23 +327,29 @@ struct IndexFunctionDemo { router.pushUrl({ url: "pages/hspCacheTestPage" }); }).margin({ top: 15 }) Button("不同的hsp资源加载") - .onClick(()=>{ + .onClick(() => { console.log("pages/multiHspTestPage 页面跳转") router.pushUrl({ url: "pages/multiHspTestPage" }); }) }.width('100%').height(60).backgroundColor(Color.Pink) + Text("测试图片加载稳定").fontSize(15) Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { Button("测试多张网络图片加载速度") .onClick(() => { router.pushUrl({ url: "pages/testManyNetImageLoadWithPage" }); }).margin({ top: 5, left: 3 }) + Button("多线程加载大量网络图片加载速度") + .onClick(() => { + router.pushUrl({ url: "pages/testManyNetImageLoadWithPage2" }); + }).margin({ top: 5, left: 3 }) Button("测试多张gif加载位置") .onClick(() => { router.pushUrl({ url: "pages/testManyGifLoadWithPage" }); }).margin({ top: 5, left: 3 }) }.width('100%').height(60).backgroundColor(Color.Pink) + Text('测试图片抗锯齿').fontSize(15) Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { Button('测试图片抗锯齿') @@ -306,6 +361,62 @@ struct IndexFunctionDemo { router.pushUrl({ url: 'pages/testImageKnifeRouter1' }); }).margin({ top: 5, left: 3 }) }.width('100%').height(60).backgroundColor(Color.Pink) + + Text('测试图片header属性').fontSize(15) + Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Button('图片header属性设置') + .onClick(() => { + router.pushUrl({ url: 'pages/testImageKnifeHttpRequestHeader' }); + }).margin({ top: 5, left: 3 }) + Button('测试自定义DataFetch属性') + .onClick(() => { + router.pushUrl({ url: 'pages/testImageKnifeDataFetch' }); + }).margin({ top: 5, left: 3 }) + Button('全局header和request的header') + .onClick(() => { + router.pushUrl({ url: 'pages/testImageKnifeHttpRequestHeader1' }); + }).margin({ top: 5, left: 3 }) + }.width('100%').height(60).backgroundColor(Color.Pink) + + Text('测试图片缓存内存').fontSize(15) + Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Button('测试图片缓存内存') + .onClick(() => { + router.pushUrl({ url: 'pages/testImageKnifeCache' }); + }).margin({ top: 5, left: 3 }) + }.width('100%').height(60).backgroundColor(Color.Pink) + + Text('测试gif暂停播放属性').fontSize(15) + Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Button('测试gif暂停播放属性') + .onClick(() => { + router.pushUrl({ url: 'pages/testStopPlayingGifPage' }); + }).margin({ top: 5, left: 3 }) + }.width('100%').height(60).backgroundColor(Color.Pink) + + Text('测试heic图片加载').fontSize(15) + Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Button('测试heic') + .onClick(() => { + router.pushUrl({ url: 'pages/testImageKnifeHeic' }); + }).margin({ top: 5, left: 3 }) + }.width('100%').height(60).backgroundColor(Color.Pink) + + Text('测试string类型占位图').fontSize(15) + Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Button('测试string类型占位图以及后备回调符') + .onClick(() => { + router.pushUrl({ url: 'pages/testImageKnifeNetPlaceholder' }); + }).margin({ top: 5, left: 3 }) + }.width('100%').height(60).backgroundColor(Color.Pink) + + Text('测试加载图片自定义网络栈').fontSize(15) + Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Button('加载图片自定义网络栈') + .onClick(() => { + router.pushUrl({ url: 'pages/testCustomDataFetchClientWithPage' }); + }).margin({ top: 5, left: 3 }) + }.width('100%').height(60).backgroundColor(Color.Pink) } } .width('100%') diff --git a/entry/src/main/ets/pages/index.ets b/entry/src/main/ets/pages/index.ets index 879f5f5..63f783a 100644 --- a/entry/src/main/ets/pages/index.ets +++ b/entry/src/main/ets/pages/index.ets @@ -17,7 +17,8 @@ import { ImageKnifeComponent, ImageKnifeOption, ImageKnifeGlobal, - ImageKnife + ImageKnife, + HeaderOptions } from '@ohos/libraryimageknife' import { ObjectKey } from '@ohos/libraryimageknife'; @@ -25,23 +26,24 @@ 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') + 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') + errorholderSrc: $r('app.media.icon_failed'), + headerOption: [this.headerOptions1] }; - build() { Scroll() { Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { @@ -51,7 +53,6 @@ struct IndexFunctionDemo { .onClick(() => { this.imageKnifeOption1 = { loadSrc: $r('app.media.pngSample'), - placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), signature: new ObjectKey('ccccccc') diff --git a/entry/src/main/ets/pages/photosPausedResumedPage.ets b/entry/src/main/ets/pages/photosPausedResumedPage.ets index 4105d79..d82f1fd 100644 --- a/entry/src/main/ets/pages/photosPausedResumedPage.ets +++ b/entry/src/main/ets/pages/photosPausedResumedPage.ets @@ -18,9 +18,8 @@ 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 photosPausedResumedPage { diff --git a/entry/src/main/ets/pages/pngjTestCasePage.ets b/entry/src/main/ets/pages/pngjTestCasePage.ets index 19680cf..3969de7 100644 --- a/entry/src/main/ets/pages/pngjTestCasePage.ets +++ b/entry/src/main/ets/pages/pngjTestCasePage.ets @@ -12,15 +12,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import router from '@system.router'; import { Pngj } from '@ohos/libraryimageknife' import resourceManager from '@ohos.resourceManager'; import { FileUtils,ImageKnifeGlobal } from '@ohos/libraryimageknife' -import featureability from '@ohos.ability.featureAbility' -import ArkWorker from '@ohos.worker' -import worker from '@ohos.worker'; import { BusinessError } from '@ohos.base' import common from '@ohos.app.ability.common'; +interface WorkerType { + type: string + name: string +} @Entry @Component struct PngjTestCasePage { @@ -115,18 +115,17 @@ struct PngjTestCasePage { if (!this.pngdecodeRun2) { this.pngdecodeRun2 = true; let pngj = new Pngj(); - // let png_worker = new worker.ThreadWorker('entry/ets/workers/upngWorkerTestCase.ets', ) - let obj ={ - type: 'classic', - name: 'readPngImageAsync' - } - pngj.readPngImageAsync(obj, this.pngSource2!, {pngCallback: (sender:ArrayBuffer, value:Record) => { - this.pngSource2 = sender + let png_worker: WorkerType = { + type: 'classic', + name: 'readPngImageAsync' + } + pngj.readPngImageAsync(png_worker, this.pngSource2!, (value:ESObject) => { this.hint8 = '重新获取buffer才能测试' this.hint2 = 'img with=' + value.width + ' img height=' + value.height + ' img depth=' + value.depth + ' img ctype=' + value.ctype this.pngdecodeRun2 = false; - }}) + }) + } else { this.hint8 = '已经在执行了,请稍等' } @@ -161,12 +160,11 @@ struct PngjTestCasePage { if (!this.pngdecodeRun3) { this.pngdecodeRun3 = true; let pngj = new Pngj(); - let png_worker = new worker.ThreadWorker('entry/ets/workers/upngWorkerTestCase.ets', { + let png_worker: WorkerType = { type: 'classic', name: 'writePngWithStringAsync' - }) - pngj.writePngWithStringAsync(png_worker, 'hello world', this.pngSource3, {pngCallback: (sender:ArrayBuffer, value:ArrayBuffer) => { - this.pngSource3 = sender + } + pngj.writePngWithStringAsync(png_worker, this.pngSource3, ( value:ESObject) => { FileUtils.getInstance().createFileProcess( this.rootFolder + '/pngj', this.rootFolder + '/pngj/newPng.png', @@ -174,7 +172,7 @@ struct PngjTestCasePage { let png1 = new Uint8Array(value) this.hint3 = 'png写入后长度' + png1.byteLength + '目录文件:' + this.rootFolder + '/pngj/newPng.png' + '保存后使用2进制查看数据是否新增' this.pngdecodeRun3 = false; - }}) + },'hello world') } else { this.hint9 = '已经在执行了,请稍等' } @@ -210,12 +208,11 @@ struct PngjTestCasePage { if (!this.pngdecodeRun4) { this.pngdecodeRun4 = true; let pngj = new Pngj(); - let png_worker = new worker.ThreadWorker('entry/ets/workers/upngWorkerTestCase.ets', { + let png_worker: WorkerType = { type: 'classic', name: 'writePngAsync' - }) - pngj.writePngAsync(png_worker, this.pngSource4,{pngCallback: (sender:ArrayBuffer, value:ArrayBuffer) => { - this.pngSource4 = sender + } + pngj.writePngAsync(png_worker, this.pngSource4, ( value:ESObject) => { FileUtils.getInstance().createFileProcess( this.rootFolder + '/pngj', this.rootFolder + '/pngj/newPng2.png', @@ -223,7 +220,7 @@ struct PngjTestCasePage { let png2 = new Uint8Array(value) this.hint4 = 'png2未写入长度' + png2.byteLength + '目录文件:' + this.rootFolder + '/pngj/newPng2.png' + '保存后使用2进制查看数据是否新增' this.pngdecodeRun4 = false; - }}) + }) } else { this.hint10 = '已经在执行了,请稍等' } diff --git a/entry/src/main/ets/pages/testCustomDataFetchClientWithPage.ets b/entry/src/main/ets/pages/testCustomDataFetchClientWithPage.ets new file mode 100644 index 0000000..c2a82f0 --- /dev/null +++ b/entry/src/main/ets/pages/testCustomDataFetchClientWithPage.ets @@ -0,0 +1,237 @@ +/* + * 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 { + CustomDataFetchClient, + DataFetchResult, + DownloadClient, + ImageKnifeComponent, + ImageKnifeGlobal, + ImageKnifeOption, + LogUtil, + ScaleType +} from '@ohos/libraryimageknife'; + +import http from '@ohos.net.http'; + +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 TestCustomDataFetchClientWithPage { + @State hotCommendList: CommonDataSource = new CommonDataSource([]) + @State singleImageKnifeOption: ImageKnifeOption = + { + loadSrc: $r('app.media.icon'), + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + }; + @State isSingleImageVisible: boolean = true; + @State isAllImageVisible: boolean = false; + @State isCustom: boolean = false; + private data: Array = [ + "http://img2.xkhouse.com/bbs/hfhouse/data/attachment/forum/corebbs/2009-11/2009113011534566298.jpg", + "http://a.hiphotos.baidu.com/image/pic/item/e824b899a9014c087eb617650e7b02087af4f464.jpg" + ] + private addData: Array = [ + "http://c.hiphotos.baidu.com/image/pic/item/9c16fdfaaf51f3de1e296fa390eef01f3b29795a.jpg", + "http://h.hiphotos.baidu.com/image/pic/item/902397dda144ad340668b847d4a20cf430ad851e.jpg" + + ] + private cancelData: Array = [ + "http://b.hiphotos.baidu.com/image/pic/item/359b033b5bb5c9ea5c0e3c23d139b6003bf3b374.jpg", + "http://a.hiphotos.baidu.com/image/pic/item/8d5494eef01f3a292d2472199d25bc315d607c7c.jpg" + ] + + aboutToAppear(): void { + this.hotCommendList.addData(this.hotCommendList.totalCount(), this.data) + LogUtil.log('TestCustomDataFetch about to appear.') + } + + build() { + Scroll() { + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + + Button("单个图片").margin(16).onClick(() => { + LogUtil.log('TestCustomDataFetch click single.'); + this.isSingleImageVisible = true; + this.isAllImageVisible = false; + ImageKnifeGlobal.getInstance().getImageKnife()?.replaceDataFetch(new CustomDataFetchClient()); + + this.singleImageKnifeOption = { + loadSrc: 'http://e.hiphotos.baidu.com/image/pic/item/4e4a20a4462309f7e41f5cfe760e0cf3d6cad6ee.jpg', + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + customGetImage: custom + } + }) + Button("全部图片").margin(16).onClick(() => { + LogUtil.log('TestCustomDataFetch click all.'); + this.isSingleImageVisible = false; + this.isAllImageVisible = true; + + ImageKnifeGlobal.getInstance().getImageKnife()?.replaceDataFetch(new CustomDataFetchClient()); + this.hotCommendList.addData(this.hotCommendList.totalCount(), this.addData) + + }) + + Button("取消自定义全部图片").margin(16).onClick(() => { + LogUtil.log('TestCustomDataFetch click cancel.'); + this.isSingleImageVisible = false; + this.isAllImageVisible = true; + + ImageKnifeGlobal.getInstance().getImageKnife()?.replaceDataFetch(new DownloadClient()); + this.hotCommendList.addData(this.hotCommendList.totalCount(), this.cancelData) + }) + } + + // 单个图片使用自定义网络栈 + ImageKnifeComponent({ imageKnifeOption: this.singleImageKnifeOption }) + .width(200) + .height(200) + .margin({ top: 50 }) + .visibility(this.isSingleImageVisible ? Visibility.Visible : Visibility.None) + + // 全部图片使用自定义网络栈 + Column() { + Grid() { + LazyForEach(this.hotCommendList, (item: string) => { + GridItem() { + ImageKnifeComponent({ + imageKnifeOption: { + loadSrc: item, + placeholderSrc: $r('app.media.icon'), + errorholderSrc: $r('app.media.icon_failed'), + placeholderScaleType: ScaleType.CENTER_CROP, + mainScaleType: ScaleType.CENTER_CROP, + } + }).width('100%').height('100%') + }.width('40%').height(200) + }, (item: string) => JSON.stringify(item)) + } + .columnsTemplate('1fr 1fr') + .columnsGap(8) + .rowsGap(10) + .width('100%') + .hitTestBehavior(HitTestMode.None) + .maxCount(10) + }.margin({ top: 5 }) + .visibility(this.isAllImageVisible ? Visibility.Visible : Visibility.None) + } + } + .width('100%') + .height('100%') + } +} + +@Concurrent +async function custom(context: Context, loadSrc: string): Promise { + let result: DataFetchResult = new DataFetchResult(); + try { + let arrayBuffers = new Array(); + let httpRequest = http.createHttp() + httpRequest.on('headersReceive', (header: Object) => { + // 跟服务器连接成功准备下载 + }) + httpRequest.on('dataReceive', (data: ArrayBuffer) => { + // 下载数据流多次返回 + arrayBuffers.push(data); + }) + httpRequest.on('dataEnd', () => { + // 下载完毕 + }) + + const resultCode = await httpRequest.requestInStream(loadSrc as string, + { + method: http.RequestMethod.GET, + expectDataType: http.HttpDataType.ARRAY_BUFFER, + connectTimeout: 60000, // 可选 默认60000ms + readTimeout: 0, // 可选, 默认为60000ms + usingProtocol: http.HttpProtocol.HTTP1_1, // 可选,协议类型默认值由系统自动指定 + usingCache: false + }).catch((err: Error) => { + result.error = 'TestCustomDataFetchClientWithPage requestInStream error.' + JSON.stringify(err); + }) + if (resultCode == 200) { + //let combineArray = this.combineArrayBuffers(arrayBuffers); + // 计算多个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; + } + result.data = combinedArrayBuffer; + } else { + result.error = 'TestCustomDataFetchClientWithPage error. resultCode = ' + resultCode; + } + console.log('TestCustomDataFetch single onComplete, code = ' + resultCode + ',length = ' + result.data?.byteLength); + } catch (error) { + result.error = 'TestCustomDataFetchClientWithPage error' + error.stack; + } + return result; +} + + diff --git a/entry/src/main/ets/pages/testDiskPreLoadPage.ets b/entry/src/main/ets/pages/testDiskPreLoadPage.ets new file mode 100644 index 0000000..fb52d2d --- /dev/null +++ b/entry/src/main/ets/pages/testDiskPreLoadPage.ets @@ -0,0 +1,61 @@ +/* + * 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 { ImageKnife,ImageKnifeGlobal,ImageKnifeOption,ImageKnifeComponent} from '@ohos/libraryimageknife' + +@Entry +@Component +struct DiskPreLoadPage { + + @State imageKnifeOption2: ImageKnifeOption = + { + loadSrc: $r('app.media.icon'), + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + }; + + build() { + Row() { + Column() { + Button("预加载图片").onClick(()=>{ + let imageKnife: ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife(); + if(imageKnife != undefined){ + imageKnife.prefetchToDiskCache("https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658") + .then((resolve)=>{ + console.log("成功回调 : " + resolve) + }) + .catch((reject: ESObject) => { + console.log("失败回调 : " + reject) + }) + } + }) + 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) + } + .width('100%') + } + .height('100%') + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/testGifPlayTimesPage.ets b/entry/src/main/ets/pages/testGifPlayTimesPage.ets new file mode 100644 index 0000000..38ced1a --- /dev/null +++ b/entry/src/main/ets/pages/testGifPlayTimesPage.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, ImageKnifeOption } from '@ohos/libraryimageknife' + +@Entry +@Component +struct TestGifPlayTimesPage { + @State ImageKnifeOption1: ImageKnifeOption = { + loadSrc: $r('app.media.icon'), + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed') + } + + build() { + Scroll() { + Column() { + Text("测试gif播放次数").fontSize(25) + Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Button("加载网络gif").onClick(() => { + this.ImageKnifeOption1 = { + loadSrc: "https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658", + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed') + } + }) + Button("设置gif播放1次").onClick(() => { + this.ImageKnifeOption1 = { + loadSrc: "https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658", + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + gif: { + playTimes: 1 + } + } + }) + Button("设置gif播放2次").onClick(() => { + this.ImageKnifeOption1 = { + loadSrc: "https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658", + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + gif: { + playTimes: 2 + } + } + }) + } + + ImageKnifeComponent({ imageKnifeOption: this.ImageKnifeOption1 }).width(300).height(300).borderWidth(3) + } + } + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/testImageKnifeAutoHeightPage.ets b/entry/src/main/ets/pages/testImageKnifeAutoHeightPage.ets new file mode 100644 index 0000000..701b868 --- /dev/null +++ b/entry/src/main/ets/pages/testImageKnifeAutoHeightPage.ets @@ -0,0 +1,71 @@ +/* + * 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 { ComponentUtils } from '@ohos.arkui.UIContext' +import display from '@ohos.display'; +import { ImageKnifeComponent, + ScaleType } from '@ohos/libraryimageknife' +import componentUtils from '@ohos.arkui.componentUtils'; + +@Entry +@Component + +struct testImageKnifeAutoHeightPage { + + + private currentWidth: number =0 + private currentHeight: number =0 + + @State value : componentUtils.ComponentInfo = componentUtils.getRectangleById("ImageKnifeCanvas"); + + aboutToAppear(){ + let displayClas : ESObject =null + try { + displayClas = display.getDefaultDisplaySync() + console.info('........width'+ displayClas.width) + console.info('........height'+ displayClas.height) + + }catch (e){ + + console.error('error' + e) + } + + + } + build(){ + Scroll(){ + Column(){ + ImageKnifeComponent({ + imageKnifeOption:{ + loadSrc :$r('app.media.pngSample'), + mainScaleType:ScaleType.AUTO_HEIGHT, + } + }) + .width("100%") + Button(){ + Text('getRectangleById').fontSize(40).fontWeight(FontWeight.Bold); + } + .onClick(()=> { + this.value = componentUtils.getRectangleById("ImageKnifeCanvas") + this.currentWidth = px2vp(this.value.size.width) + this.currentHeight = px2vp(this.value.size.height) + console.log('currentWidth'+this.currentWidth) + console.log('currentHeight'+this.currentHeight) + }) + } + } + + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/testImageKnifeAutoPage.ets b/entry/src/main/ets/pages/testImageKnifeAutoPage.ets new file mode 100644 index 0000000..1251d76 --- /dev/null +++ b/entry/src/main/ets/pages/testImageKnifeAutoPage.ets @@ -0,0 +1,71 @@ +/* + * 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 { ComponentUtils } from '@ohos.arkui.UIContext' +import display from '@ohos.display'; +import { ImageKnifeComponent, + ScaleType } from '@ohos/libraryimageknife' +import componentUtils from '@ohos.arkui.componentUtils'; + +@Entry +@Component + +struct testImageKnifeAutoPage { + + + private currentWidth: number =0 + private currentHeight: number =0 + + @State value : componentUtils.ComponentInfo = componentUtils.getRectangleById("ImageKnifeCanvas"); + + aboutToAppear(){ + let displayClas : ESObject =null + try { + displayClas = display.getDefaultDisplaySync() + console.info('........width'+ displayClas.width) + console.info('........height'+ displayClas.height) + + }catch (e){ + + console.error('error' + e) + } + + + } + build(){ + Scroll(){ + Column(){ + ImageKnifeComponent({ + imageKnifeOption:{ + loadSrc :$r('app.media.pngSample'), + mainScaleType:ScaleType.AUTO, + } + }) + + Button(){ + Text('getRectangleById').fontSize(40).fontWeight(FontWeight.Bold); + } + .onClick(()=> { + this.value = componentUtils.getRectangleById("ImageKnifeCanvas") + this.currentWidth = px2vp(this.value.size.width) + this.currentHeight = px2vp(this.value.size.height) + console.log('currentWidth'+this.currentWidth) + console.log('currentHeight'+this.currentHeight) + }) + } + } + + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/testImageKnifeAutoWidthPage.ets b/entry/src/main/ets/pages/testImageKnifeAutoWidthPage.ets new file mode 100644 index 0000000..256f29c --- /dev/null +++ b/entry/src/main/ets/pages/testImageKnifeAutoWidthPage.ets @@ -0,0 +1,72 @@ +/* + * 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 { ComponentUtils } from '@ohos.arkui.UIContext' +import display from '@ohos.display'; +import { ImageKnifeComponent, + ScaleType } from '@ohos/libraryimageknife' +import componentUtils from '@ohos.arkui.componentUtils'; + +@Entry +@Component + +struct testImageKnifeAutoWidthPage { + + + private currentWidth: number =0 + private currentHeight: number =0 + + @State value : componentUtils.ComponentInfo = componentUtils.getRectangleById("ImageKnifeCanvas"); + + aboutToAppear(){ + let displayClas : ESObject =null + try { + displayClas = display.getDefaultDisplaySync() + console.info('........width'+ displayClas.width) + console.info('........height'+ displayClas.height) + + }catch (e){ + + console.error('error' + e) + } + + + } + build(){ + Scroll(){ + Column(){ + ImageKnifeComponent({ + imageKnifeOption:{ + loadSrc :$r('app.media.pngSample'), + mainScaleType:ScaleType.AUTO_WIDTH, + } + }) + .height(400) + + Button(){ + Text('getRectangleById').fontSize(40).fontWeight(FontWeight.Bold); + } + .onClick(()=> { + this.value = componentUtils.getRectangleById("ImageKnifeCanvas") + this.currentWidth = px2vp(this.value.size.width) + this.currentHeight = px2vp(this.value.size.height) + console.log('currentWidth'+this.currentWidth) + console.log('currentHeight'+this.currentHeight) + }) + } + } + + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/testImageKnifeCache.ets b/entry/src/main/ets/pages/testImageKnifeCache.ets new file mode 100644 index 0000000..8a787ea --- /dev/null +++ b/entry/src/main/ets/pages/testImageKnifeCache.ets @@ -0,0 +1,239 @@ +/* + * 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 { + ImageKnifeComponent, + ImageKnifeOption, + ImageKnifeGlobal, + ImageKnife, + ImageKnifeData, + RequestOption, + Size, + CacheType +} from '@ohos/libraryimageknife' +import image from '@ohos.multimedia.image'; +import { BusinessError } from '@ohos.base'; + +let imageKnife: ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife(); + +@Entry +@Component +struct testImageKnifeCache { + @State url: string = ''; + @State urlGif: string = 'https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658'; + @State urlPng: string = 'https://img-blog.csdnimg.cn/20191215043500229.png'; + @State urlJpg: string = 'https://hbimg.huabanimg.com/cc6af25f8d782d3cf3122bef4e61571378271145735e9-vEVggB'; + @State urlBmp: string = 'https://img-blog.csdn.net/20140514114029140'; + @State urlWebp: string = 'https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp'; + @State imagePixelMap: PixelMap | undefined = undefined; + @State imagePixelMap_: PixelMap | undefined = undefined; + private index_: number = -1; + private tempSize: number = 200; + private timeId = -1; + private comSize: Size = { + width: this.tempSize, + height: this.tempSize, + } + @State imageKnifeOption: ImageKnifeOption = + { + loadSrc: $r('app.media.icon'), + + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed') + }; + loadSuccess = (data: ImageKnifeData) => { + clearTimeout(this.timeId); + if (data.isPixelMap()) { + if (data.drawPixelMap) { + let pixelmap = data.drawPixelMap.imagePixelMap + if (pixelmap) { + if (this.index_ == 1) { + this.imagePixelMap = pixelmap; + } else if (this.index_ == 2) { + this.imagePixelMap_ = pixelmap; + } + } + } + } + if (data.isGIFFrame()) { + let index: number = 0 + if (data.drawGIFFrame) { + if (data.drawGIFFrame.imageGIFFrames) { + let renderGif = () => { + if (data.drawGIFFrame) { + if (data.drawGIFFrame.imageGIFFrames) { + let pixelmap = data.drawGIFFrame.imageGIFFrames[index].drawPixelMap + let delay = data.drawGIFFrame.imageGIFFrames[index].delay + if (pixelmap) { + if (this.index_ == 1) { + this.imagePixelMap = pixelmap; + } else if (this.index_ == 2) { + this.imagePixelMap_ = pixelmap; + } + } + index++; + if (index == data.drawGIFFrame.imageGIFFrames.length - 1) { + index = 0 + } + this.timeId = setTimeout(renderGif, data!.drawGIFFrame!.imageGIFFrames![index].delay) + } + } + } + renderGif() + } + } + } + } + loadError = (err: BusinessError) => { + } + + build() { + Scroll() { + Column() { + + Text('图片内存和磁盘读取').fontSize(30); + Text('加载的缓存时候关闭掉网络').fontSize(15); + + Row() { + Button('png') + .onClick(() => { + this.index_ = 0; + this.url = this.urlPng; + this.imageKnifeOption = + { + loadSrc: this.url, + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed') + } + }) + + Button('bmp') + .onClick(() => { + this.index_ = 0; + this.url = this.urlBmp; + this.imageKnifeOption = + { + loadSrc: this.url, + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed') + } + }) + + Button('webp') + .onClick(() => { + this.index_ = 0; + this.url = this.urlWebp; + this.imageKnifeOption = + { + loadSrc: this.url, + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed') + } + }) + + Button('jpg') + .onClick(() => { + this.index_ = 0; + this.url = this.urlJpg; + this.imageKnifeOption = + { + loadSrc: this.url, + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed') + } + }) + + Button('gif') + .onClick(() => { + this.index_ = 0; + this.url = this.urlGif; + this.imageKnifeOption = + { + loadSrc: this.url, + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed') + } + }) + } + + Row() { + Button('缓存图片') + .onClick(() => { + this.index_ = 1; + imageKnife?.isUrlExist(this.url, CacheType.Cache, this.comSize).then(this.loadSuccess) + .catch(this.loadError); + }) + + Button('磁盘图片') + .onClick(() => { + this.index_ = 2; + imageKnife?.isUrlExist(this.url, CacheType.Disk, this.comSize).then(this.loadSuccess) + .catch(this.loadError); + }) + + Button('默认') + .onClick(() => { + this.index_ = 2; + imageKnife?.isUrlExist(this.url, CacheType.Default, this.comSize).then(this.loadSuccess) + .catch(this.loadError); + }) + } + + Text('网络图').fontSize(15); + ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption }) + .width(200) + .height(200) + .backgroundColor(Color.Orange) + Text('缓存图').fontSize(15); + + ImageKnifeComponent({ + imageKnifeOption: { + loadSrc: this.imagePixelMap as image.PixelMap + } + }) + .width(200) + .height(200) + .backgroundColor(Color.Orange) + Text('磁盘图').fontSize(15); + ImageKnifeComponent({ + imageKnifeOption: { + loadSrc: this.imagePixelMap_ as image.PixelMap + } + }) + .width(200) + .height(200) + .backgroundColor(Color.Orange) + + } + .alignItems(HorizontalAlign.Center) + .width('100%') + + } + .width('100%') + .height('100%') + } + + aboutToAppear() { + + } + + aboutToDisappear() { + + } + + onBackPress() { + + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/testImageKnifeDataFetch.ets b/entry/src/main/ets/pages/testImageKnifeDataFetch.ets new file mode 100644 index 0000000..c097e9d --- /dev/null +++ b/entry/src/main/ets/pages/testImageKnifeDataFetch.ets @@ -0,0 +1,83 @@ +/* + * 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 { + Base64, + IDataFetch, + ImageKnife, + ImageKnifeComponent, + ImageKnifeGlobal, + ImageKnifeOption, + DataFetchResult, + RequestOption} from '@ohos/libraryimageknife' +import common from '@ohos.app.ability.common'; +import resourceManager from '@ohos.resourceManager'; +import { BusinessError } from '@ohos.base' + +@Entry +@Component +struct TestImageKnifeDataFetch { + + @State imageKnifeOption1: ImageKnifeOption = + { + loadSrc: $r('app.media.icon'), + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + }; + + build() { + Column(){ + Text("简单示例1:加载一张本地png图片").fontSize(15) + Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Button("加载PNG") + .onClick(() => { + let dataFetch:MyDataFetch = new MyDataFetch(); + let imageKnife:ImageKnife|undefined = ImageKnifeGlobal.getInstance().getImageKnife() + if(imageKnife != undefined) { + imageKnife.replaceDataFetch(dataFetch) + } + this.imageKnifeOption1 = { + loadSrc: "", + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + isCacheable: false, + } + }).margin({ top: 5, left: 3 }) + ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption1 }).width(300).height(300) + }.width('100%').backgroundColor(Color.Pink) + } + } + +} + +@Sendable +class MyDataFetch implements IDataFetch{ + loadData(option: RequestOption) { + let result:DataFetchResult = new DataFetchResult(); + ((ImageKnifeGlobal.getInstance().getHapContext() as common.UIAbilityContext).resourceManager as resourceManager.ResourceManager) + .getMediaContentBase64($r('app.media.pngSample').id) + .then(data => { + let matchReg = ';base64,'; + let firstIndex = data.indexOf(matchReg); + data = data.substring(firstIndex + matchReg.length, data.length) + console.log('MyDataFetch - 本地加载资源 解析后数据剔除非必要数据后data= ' + data) + let arrayBuffer = Base64.getInstance() + .decode(data); + result.data = arrayBuffer; + }).catch((err:BusinessError) => { + result.error = 'MyDataFetch - 本地加载资源err' + JSON.stringify(err); + }) + return result; + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/testImageKnifeHeic.ets b/entry/src/main/ets/pages/testImageKnifeHeic.ets new file mode 100644 index 0000000..e5f72ff --- /dev/null +++ b/entry/src/main/ets/pages/testImageKnifeHeic.ets @@ -0,0 +1,44 @@ +/* + * 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 { ImageKnifeComponent, ImageKnifeOption } from '@ohos/libraryimageknife' + +@Entry +@Component +struct testImageKnifeHeic { + @State imageKnifeOption1: ImageKnifeOption = + { + loadSrc: $r('app.media.yunHeic'), + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + }; + + @State flag: boolean = true; + + build() { + Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Button("heic图片测试") + .onClick(() => { + this.flag = !this.flag + if (this.flag) { + this.imageKnifeOption1.errorholderSrc = $r('app.media.icon_loading') + } else { + this.imageKnifeOption1.errorholderSrc = $r('app.media.icon_failed') + } + }).margin({ top: 15 }) + ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption1 }).width(300).height(300) + }.width('100%').height(300).backgroundColor(Color.Pink) + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/testImageKnifeHttpRequestHeader.ets b/entry/src/main/ets/pages/testImageKnifeHttpRequestHeader.ets new file mode 100644 index 0000000..411a411 --- /dev/null +++ b/entry/src/main/ets/pages/testImageKnifeHttpRequestHeader.ets @@ -0,0 +1,256 @@ +/* + * 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,ImageKnife,ImageKnifeComponent,ImageKnifeData,ImageKnifeGlobal,RequestOption,ImageKnifeOption,ObjectKey +} from '@ohos/libraryimageknife' +import image from '@ohos.multimedia.image' +import { BusinessError } from '@ohos.base' + +const TAG = "TEST-" +let timeId = -1 + +@Entry +@Component +struct testImageKnifeHttpRequestHeader { + @State pixelMap: PixelMap | undefined = undefined; + @State pixelMap1: PixelMap | undefined = undefined; + @State pixelMap2: PixelMap | undefined = undefined; + @State pixelMap3: PixelMap | undefined = undefined; + @State domeType1: boolean = false; + @State domeType2: boolean = false; + @State domeType3: boolean = false; + @State domeType4: boolean = false; + @State domeType5: boolean = false; + @State domeType6: boolean = false; + @State successHeader: string = "requestOption调用成功"; + @State errorHeader: string = "requestOption调用失败"; + @State successHeader1: string = "全局调用imageKnife成功"; + @State errorHeader1: string = "全局调用imageKnife失败"; + @State successHeader2: string = "单个imageKnife组件调用成功"; + @State errorHeader2: string = "单个imageKnife组件调用失败"; + + @State message: string = "图片header属性测试"; + @State headerOptions1: HeaderOptions = { + key: "refer", + value: "http://1.94.37.200:7070/AntiTheftChain/downloadImage" + }; + @State headerOptions2: HeaderOptions = { + key: "xx", + value: "http://1.94.37.200:7070/AntiTheftChain/downloadImage" + }; + @State imageKnifeOption1: ImageKnifeOption = + { + loadSrc: 'http://1.94.37.200:7070/AntiTheftChain/downloadImage', + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + headerOption: [this.headerOptions1] + }; + @State imageKnifeOption2: ImageKnifeOption = + { + loadSrc: 'http://1.94.37.200:7070/AntiTheftChain/downloadImage', + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + headerOption: [this.headerOptions2] + }; + + // RequestOption调用 + load(src: string | image.PixelMap | Resource, type: string, num: number) { + clearTimeout(timeId) + let request = new RequestOption() + if (type == 'error') { + request.addHeader('xx', src) + } else { + request.addHeader('refer', src) + } + //清理缓存 + request.signature = new ObjectKey(new Date().getTime().toString()) + request.load(src) + .addListener({ callback: (err: BusinessError | string, data: ImageKnifeData) => { + if (data.isPixelMap()) { + if (data.drawPixelMap) { + let pixelmap = data.drawPixelMap.imagePixelMap + if (pixelmap) { + if (num == 1) { + this.pixelMap = pixelmap + } else if (num == 2) { + this.pixelMap1 = pixelmap + } else if (num == 3) { + this.pixelMap2 = pixelmap + } else if (num == 4) { + this.pixelMap3 = pixelmap + } + } + } + } + if (data.isGIFFrame()) { + let index: number = 0 + if (data.drawGIFFrame) { + if (data.drawGIFFrame.imageGIFFrames) { + let renderGif = () => { + if (data.drawGIFFrame) { + if (data.drawGIFFrame.imageGIFFrames) { + let pixelmap = data.drawGIFFrame.imageGIFFrames[index].drawPixelMap + let delay = data.drawGIFFrame.imageGIFFrames[index].delay + if (pixelmap) { + if (num == 1) { + this.pixelMap = pixelmap + } else if (num == 2) { + this.pixelMap1 = pixelmap + } else if (num == 3) { + this.pixelMap2 = pixelmap + } else if (num == 4) { + this.pixelMap3 = pixelmap + } + } + index++; + if (index == data.drawGIFFrame.imageGIFFrames.length - 1) { + index = 0 + } + timeId = setTimeout(renderGif, data!.drawGIFFrame!.imageGIFFrames![index].delay) + } + } + } + renderGif() + } + } + } + if (err) { + console.log(TAG + "error:" + JSON.stringify(err)); + } + return false + } + }) + let imageknife: ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife() + if (imageknife != undefined) { + imageknife.call(request) + } + } + + @Builder + setHeader() { + ImageKnifeComponent({ + imageKnifeOption: { + loadSrc: this.pixelMap as image.PixelMap + } + }).width(200).height(200).borderWidth(1) + } + @Builder + setHeader3() { + ImageKnifeComponent({ + imageKnifeOption: { + loadSrc: this.pixelMap1 as image.PixelMap + } + }).width(200).height(200).borderWidth(1) + } + @Builder + setHeader1() { + ImageKnifeComponent({ + imageKnifeOption: { + loadSrc: this.pixelMap2 as image.PixelMap + } + }).width(200).height(200).borderWidth(1) + } + @Builder + setHeader4() { + ImageKnifeComponent({ + imageKnifeOption: { + loadSrc: this.pixelMap3 as image.PixelMap + } + }).width(200).height(200).borderWidth(1) + } + @Builder + setHeader2() { + ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption1 }) + .margin(16) + .width(200) + .height(200) + } + @Builder + setHeader5() { + ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption2 }) + .margin(16) + .width(200) + .height(200) + } + + build() { + Scroll() { + Column() { + Text(`${this.message}`) + .width("300vp") + .height("60vp") + .textAlign(TextAlign.Center) + .fontSize("30fp") + .fontWeight(FontWeight.Bold) + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) { + Button(this.successHeader) + .margin(16) + .onClick(() => { + this.domeType1 = !this.domeType1 + this.load('http://1.94.37.200:7070/AntiTheftChain/downloadImage', 'success', 1) + }) + if (this.domeType1) { + this.setHeader() + } + Button(this.errorHeader) + .margin(16) + .onClick(() => { + this.domeType2 = !this.domeType2 + this.load('http://1.94.37.200:7070/AntiTheftChain/downloadImage', 'error', 2) + }) + if (this.domeType2) { + this.setHeader3() + } + Button(this.successHeader1) + .margin(16) + .onClick(() => { + this.domeType3 = !this.domeType3 + this.load('http://1.94.37.200:7070/AntiTheftChain/downloadImage', 'success', 3) + }) + if (this.domeType3) { + this.setHeader1() + } + Button(this.errorHeader1) + .margin(16) + .onClick(() => { + this.domeType4 = !this.domeType4 + this.load('http://1.94.37.200:7070/AntiTheftChain/downloadImage', 'error', 4) + }) + if (this.domeType4) { + this.setHeader4() + } + Button(this.successHeader2) + .margin(16) + .onClick(() => { + this.domeType5 = !this.domeType5 + }) + if (this.domeType5) { + this.setHeader2() + } + Button(this.errorHeader2) + .margin(16) + .onClick(() => { + this.domeType6 = !this.domeType6 + }) + if (this.domeType6) { + this.setHeader5() + } + } + }.width("100%") + .justifyContent(FlexAlign.Start) + } + } +} diff --git a/entry/src/main/ets/pages/testImageKnifeHttpRequestHeader1.ets b/entry/src/main/ets/pages/testImageKnifeHttpRequestHeader1.ets new file mode 100644 index 0000000..5d93309 --- /dev/null +++ b/entry/src/main/ets/pages/testImageKnifeHttpRequestHeader1.ets @@ -0,0 +1,172 @@ +/* + * 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 { + HeaderOptions,ImageKnife,ImageKnifeComponent,ImageKnifeData,ImageKnifeGlobal,RequestOption,ImageKnifeOption,ObjectKey +} from '@ohos/libraryimageknife' +import image from '@ohos.multimedia.image' +import { BusinessError } from '@ohos.base' + +const TAG = "TEST-" +let timeId = -1 + +@Entry +@Component +struct testImageKnifeHttpRequestHeader1 { + @State pixelMap: PixelMap | undefined = undefined; + @State pixelMap1: PixelMap | undefined = undefined; + @State domeType1: boolean = false; + @State domeType2: boolean = false; + @State successHeader: string = "requestOption调用成功"; + @State errorHeader: string = "requestOption调用失败"; + @State allKeySame: string = "全局添加header和request成功的键相同值不同"; + @State allKeyNoSame: string = "全局添加header和request成功的键不同"; + @State clearnAllHeader: string = "清空全局header"; + + @State message: string = "图片header属性测试"; + + imageKnife: ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife() + // RequestOption调用 + load(src: string | image.PixelMap | Resource, type: string, num: number) { + clearTimeout(timeId) + let request = new RequestOption() + if (type == 'error') { + request.addHeader('xx', src) + } else { + request.addHeader('refer', src) + } + //清理缓存 + request.signature = new ObjectKey(new Date().getTime().toString()) + request.load(src) + .addListener({ callback: (err: BusinessError | string, data: ImageKnifeData) => { + if (data.isPixelMap()) { + if (data.drawPixelMap) { + let pixelmap = data.drawPixelMap.imagePixelMap + if (pixelmap) { + if (num == 1) { + this.pixelMap = pixelmap + } else if (num == 2) { + this.pixelMap1 = pixelmap + } + } + } + } + if (data.isGIFFrame()) { + let index: number = 0 + if (data.drawGIFFrame) { + if (data.drawGIFFrame.imageGIFFrames) { + let renderGif = () => { + if (data.drawGIFFrame) { + if (data.drawGIFFrame.imageGIFFrames) { + let pixelmap = data.drawGIFFrame.imageGIFFrames[index].drawPixelMap + let delay = data.drawGIFFrame.imageGIFFrames[index].delay + if (pixelmap) { + if (num == 1) { + this.pixelMap = pixelmap + } else if (num == 2) { + this.pixelMap1 = pixelmap + } + } + index++; + if (index == data.drawGIFFrame.imageGIFFrames.length - 1) { + index = 0 + } + timeId = setTimeout(renderGif, data!.drawGIFFrame!.imageGIFFrames![index].delay) + } + } + } + renderGif() + } + } + } + if (err) { + console.log(TAG + "error:" + JSON.stringify(err)); + } + return false + } + }) + let imageknife: ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife() + if (imageknife != undefined) { + imageknife.call(request) + } + } + + @Builder + setHeader() { + ImageKnifeComponent({ + imageKnifeOption: { + loadSrc: this.pixelMap as image.PixelMap + } + }).width(200).height(200).borderWidth(1) + } + @Builder + setHeader1() { + ImageKnifeComponent({ + imageKnifeOption: { + loadSrc: this.pixelMap1 as image.PixelMap + } + }).width(200).height(200).borderWidth(1) + } + + + build() { + Scroll() { + Column() { + Text(`${this.message}`) + .width("300vp") + .height("60vp") + .textAlign(TextAlign.Center) + .fontSize("30fp") + .fontWeight(FontWeight.Bold) + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) { + Button(this.successHeader) + .margin(16) + .onClick(() => { + this.domeType1 = !this.domeType1 + this.load('http://1.94.37.200:7070/AntiTheftChain/downloadImage', 'success', 1) + }) + if (this.domeType1) { + this.setHeader() + } + Button(this.errorHeader) + .margin(16) + .onClick(() => { + this.domeType2 = !this.domeType2 + this.load('http://1.94.37.200:7070/AntiTheftChain/downloadImage', 'error', 2) + }) + if (this.domeType2) { + this.setHeader1() + } + Button(this.allKeySame) + .margin(16) + .onClick(() => { + this.imageKnife?.addHeader('refer','test') + }) + Button(this.allKeyNoSame) + .margin(16) + .onClick(() => { + this.imageKnife?.addHeader('ceshi','http://1.94.37.200:7070/AntiTheftChain/downloadImage') + }) + Button(this.clearnAllHeader) + .margin(16) + .onClick(() => { + this.imageKnife?.deleteHeader('refer'); + this.imageKnife?.deleteHeader('ceshi'); + }) + } + }.width("100%") + .justifyContent(FlexAlign.Start) + } + } +} diff --git a/entry/src/main/ets/pages/testImageKnifeNetPlaceholder.ets b/entry/src/main/ets/pages/testImageKnifeNetPlaceholder.ets new file mode 100644 index 0000000..ca43d5e --- /dev/null +++ b/entry/src/main/ets/pages/testImageKnifeNetPlaceholder.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 { ImageKnifeComponent, ImageKnifeOption } from '@ohos/libraryimageknife' + +@Entry +@Component +struct testImageKnifeNetPlaceholder { + @State imageKnifeOption: ImageKnifeOption = + { + loadSrc: $r('app.media.icon'), + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + }; + @State imageKnifeOption1: ImageKnifeOption = + { + loadSrc: $r('app.media.icon'), + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + }; + @State imageKnifeOption2: ImageKnifeOption = + { + loadSrc: $r('app.media.icon'), + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + }; + + build() { + Scroll() { + Column() { + Text('string类型占位图支持内存和磁盘读取').fontSize(30); + Text('验证磁盘有无图片可以先主图缓存一张图片,再退出应用').fontSize(15); + Text('仅第一次打开应用加载主图可以看到效果,因主图第二次加载速度很快').fontSize(15); + + Row() { + Button('缓存一张图片作为占位图 图一') + .onClick(() => { + this.imageKnifeOption = + { + loadSrc: 'https://img-blog.csdn.net/20140514114029140', + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + } + }) + } + + Text('当图一没有展示,会从磁盘中拿占位图').fontSize(15); + Text('当图一展示时,会从内存中拿占位图,不会走磁盘逻辑').fontSize(15); + + Text('下图展示的为后备回调符的图片').fontSize(15); + ImageKnifeComponent({ imageKnifeOption: {loadSrc:$r('app.media.mask_starfish')} }) + .width(200) + .height(200) + .backgroundColor(Color.Orange) + + Text('下图展示的主图为图二string类型的占位图').fontSize(15); + Text('图一:').fontSize(20); + ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption }) + .width(200) + .height(200) + .backgroundColor(Color.Orange) + + Row() { + Button('展示已缓存的占位图') + .onClick(() => { + this.imageKnifeOption1 = + { + loadSrc : $r('app.media.pngSample'), + placeholderSrc: 'https://img-blog.csdn.net/20140514114029140', + fallbackSrc: $r('app.media.mask_starfish'), + errorholderSrc: $r('app.media.icon_failed'), + } + }) + + Button('当占位图未缓存时展示后备回调符') + .onClick(() => { + this.imageKnifeOption2 = + { + loadSrc : $r('app.media.gifSample'), + placeholderSrc: 'https://img0.baidu.com/it/u=2794536113,2700219306&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500', + fallbackSrc: $r('app.media.mask_starfish'), + errorholderSrc: $r('app.media.icon_failed'), + } + }) + } + + Text('展示已缓存的占位图,如图一未加载过则会显示后备回调符').fontSize(15); + Text('图二:').fontSize(20); + ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption1 }) + .width(200) + .height(200) + .backgroundColor(Color.Orange) + + Text('占位图未缓存展示后备回调符,如图三').fontSize(15); + Text('图三:').fontSize(20); + ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption2 }) + .width(200) + .height(200) + .backgroundColor(Color.Orange) + + } + .alignItems(HorizontalAlign.Center) + .width('100%') + + } + .width('100%') + .height('100%') + } + + aboutToAppear() { + + } + + aboutToDisappear() { + + } + + onBackPress() { + + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/testImageKnifeOptionChangedPage5.ets b/entry/src/main/ets/pages/testImageKnifeOptionChangedPage5.ets index 47c1119..d331111 100644 --- a/entry/src/main/ets/pages/testImageKnifeOptionChangedPage5.ets +++ b/entry/src/main/ets/pages/testImageKnifeOptionChangedPage5.ets @@ -27,6 +27,9 @@ import { ImageKnifeDrawFactory } from '@ohos/libraryimageknife' import worker from '@ohos.worker'; + +let gifUrl: string = 'https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658'; + @Entry @Component struct TestImageKnifeOptionChangedPage5 { @@ -83,6 +86,64 @@ struct TestImageKnifeOptionChangedPage5 { }; }).margin({ left: 5 }).backgroundColor(Color.Blue) }.margin({ top: 15 }) + Flex({ direction: FlexDirection.Row }) { + Button("本地gif") + .onClick(() => { + this.imageKnifeOption1 = { + loadSrc: $r('app.media.gifSample'), + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + }; + }).margin({ left: 5 }).backgroundColor(Color.Blue) + Button("网络gif") + .onClick(() => { + this.imageKnifeOption1 = { + loadSrc: gifUrl, + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + }; + }).margin({ left: 5 }).backgroundColor(Color.Blue) + }.margin({ top: 15 }) + Flex({ direction: FlexDirection.Row }) { + Button("网络gif - 圆角小") + .onClick(() => { + this.imageKnifeOption1 = { + loadSrc: gifUrl, + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + drawLifeCycle: ImageKnifeDrawFactory.createRoundLifeCycle(3, "#ff0000", 30) + }; + }).margin({ left: 5 }).backgroundColor(Color.Blue) + Button("本地gif - 圆角大") + .onClick(() => { + this.imageKnifeOption1 = { + loadSrc: $r('app.media.gifSample'), + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + drawLifeCycle: ImageKnifeDrawFactory.createRoundLifeCycle(5, "#ffff00", 300) + }; + }).margin({ left: 5 }).backgroundColor(Color.Blue) + }.margin({ top: 15 }) + Flex({ direction: FlexDirection.Row }) { + Button("网络gif-椭圆长方形资源") + .onClick(() => { + this.imageKnifeOption1 = { + loadSrc: gifUrl, + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + drawLifeCycle: ImageKnifeDrawFactory.createOvalLifeCycle(8, "#88ee00") + }; + }).margin({ left: 5 }).backgroundColor(Color.Blue) + Button("本地gif-椭圆正方形资源") + .onClick(() => { + this.imageKnifeOption1 = { + loadSrc: $r('app.media.gifSample'), + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + drawLifeCycle: ImageKnifeDrawFactory.createOvalLifeCycle(5, "#ff00ff") + }; + }).margin({ left: 5 }).backgroundColor(Color.Blue) + }.margin({ top: 15 }) Text("下面为展示图片区域").margin({ top: 5 }) Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { diff --git a/entry/src/main/ets/pages/testManyNetImageLoadWithPage2.ets b/entry/src/main/ets/pages/testManyNetImageLoadWithPage2.ets new file mode 100644 index 0000000..ad927d8 --- /dev/null +++ b/entry/src/main/ets/pages/testManyNetImageLoadWithPage2.ets @@ -0,0 +1,65 @@ +/* + * 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 { ScaleType, ImageKnifeComponent, NONE, DiskStrategy } from "@ohos/libraryimageknife" +import { Material } from './model/Material'; +import { TestDataSource } from './model/TestDataSource'; + + +@Entry +@Component +struct TestManyNetImageLoadWithPage2 { + private data: TestDataSource = new TestDataSource(); + private setting: DiskStrategy = new NONE(); + private scroller: Scroller = new Scroller() + + build() { + Scroll() { + Column() { + Grid(this.scroller) { + LazyForEach(this.data, (item: Material, index) => { + GridItem() { + Stack({ alignContent: Alignment.BottomEnd }) { + ImageKnifeComponent({ + imageKnifeOption: { + loadSrc: item.thumbnail, + placeholderSrc: $r('app.media.icon_loading'), + mainScaleType: ScaleType.CENTER_CROP, + placeholderScaleType: ScaleType.CENTER_CROP, + isCacheable: false, + strategy: this.setting + } + }).width('100%').height('100%') + Text(index + "." + item.name) + .fontSize(10) + .maxLines(1) + .fontColor(Color.White) + .textAlign(TextAlign.Center) + .layoutWeight(1) + .width('100%') + .backgroundColor(Color.Orange) + } + }.width('45%').height(200) + }, (item: Material,index:number) => item.material_id + index.toString()) + } + .columnsTemplate('1fr 1fr') + .columnsGap(8) + .rowsGap(10) + .width('100%') + .cachedCount(3) + .height('100%') + }.margin({ top: 5 }) + } + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/testPreloadPage.ets b/entry/src/main/ets/pages/testPreloadPage.ets index 62a16be..acb3f61 100644 --- a/entry/src/main/ets/pages/testPreloadPage.ets +++ b/entry/src/main/ets/pages/testPreloadPage.ets @@ -30,7 +30,7 @@ struct TestPreloadPage { placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), - + }; @State imageKnifeOption: ImageKnifeOption = { @@ -38,7 +38,7 @@ struct TestPreloadPage { placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), - + }; @State imageKnifeOption3: ImageKnifeOption = { @@ -46,7 +46,7 @@ struct TestPreloadPage { placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), - + }; @State imageKnifeOption4: ImageKnifeOption = @@ -55,7 +55,7 @@ struct TestPreloadPage { placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), - + }; @State imageKnifeOption5: ImageKnifeOption = { @@ -63,7 +63,7 @@ struct TestPreloadPage { placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), - + }; @State imageKnifeOption6: ImageKnifeOption = { @@ -71,7 +71,7 @@ struct TestPreloadPage { placeholderSrc: $r('app.media.icon_loading'), errorholderSrc: $r('app.media.icon_failed'), - + }; build() { @@ -88,59 +88,59 @@ struct TestPreloadPage { if (err) { console.log('预加载本地资源gif 出现错误! err=' + err) } else { - console.log('预加载本地资源gif成功! imageKnifedata=' + JSON.stringify(data)) - } - return false; - }}) - ImageKnifeGlobal.getInstance().getImageKnife()?.preload(request); - }) + console.log('预加载本地资源gif成功! imageKnifedata=' + JSON.stringify(data)) + } + return false; + }}) + ImageKnifeGlobal.getInstance().getImageKnife()?.preload(request); + }) .margin({ left: 15 }) .backgroundColor(Color.Grey) Button('本地资源gif') .onClick(() => { - this.imageKnifeOption1 = { - loadSrc: $r('app.media.gifSample'), + this.imageKnifeOption1 = { + loadSrc: $r('app.media.gifSample'), - placeholderSrc: $r('app.media.icon_loading'), - errorholderSrc: $r('app.media.icon_failed'), - - } + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), - }) + } + + }) .margin({ left: 15 }) .backgroundColor(Color.Grey) Button('预加载本地资源gif静态') .onClick(() => { - let request = new RequestOption(); - request.load($r('app.media.gifSample')) - .setImageViewSize({ width: 300, height: 300 }) - .dontAnimate() - .addListener({callback:(err:BusinessError|string, data:ImageKnifeData) => { - if (err ) { - console.log('预加载本地资源gif静态 出现错误! err=' + err) - } else { - console.log('预加载本地资源gif静态成功! imageKnifedata=' + JSON.stringify(data)) - } - return false; - }}) - ImageKnifeGlobal.getInstance().getImageKnife()?.preload(request); - }) + let request = new RequestOption(); + request.load($r('app.media.gifSample')) + .setImageViewSize({ width: 300, height: 300 }) + .dontAnimate() + .addListener({callback:(err:BusinessError|string, data:ImageKnifeData) => { + if (err ) { + console.log('预加载本地资源gif静态 出现错误! err=' + err) + } else { + console.log('预加载本地资源gif静态成功! imageKnifedata=' + JSON.stringify(data)) + } + return false; + }}) + ImageKnifeGlobal.getInstance().getImageKnife()?.preload(request); + }) .margin({ left: 15 }) .backgroundColor(Color.Grey) Button('本地资源gif静态') .onClick(() => { - this.imageKnifeOption1 = { - loadSrc: $r('app.media.gifSample'), - placeholderSrc: $r('app.media.icon_loading'), - errorholderSrc: $r('app.media.icon_failed'), - dontAnimateFlag: true - } + this.imageKnifeOption1 = { + loadSrc: $r('app.media.gifSample'), + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + dontAnimateFlag: true + } - }) + }) .margin({ left: 15 }) .backgroundColor(Color.Grey) } @@ -150,64 +150,64 @@ struct TestPreloadPage { Button('预加载网络资源gif') .onClick(() => { - let request = new RequestOption(); - request.load('https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658') - .setImageViewSize({ width: 300, height: 300 }) - .addListener({callback:(err:BusinessError|string, data:ImageKnifeData) => { - if (err) { - console.log('预加载网络资源gif 出现错误! err=' + err) - } else { - console.log('预加载网络资源gif成功! imageKnifedata=' + JSON.stringify(data)) - } - return false; - }}) - ImageKnifeGlobal.getInstance().getImageKnife()?.preload(request); - }) + let request = new RequestOption(); + request.load('https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658') + .setImageViewSize({ width: 300, height: 300 }) + .addListener({callback:(err:BusinessError|string, data:ImageKnifeData) => { + if (err) { + console.log('预加载网络资源gif 出现错误! err=' + err) + } else { + console.log('预加载网络资源gif成功! imageKnifedata=' + JSON.stringify(data)) + } + return false; + }}) + ImageKnifeGlobal.getInstance().getImageKnife()?.preload(request); + }) .margin({ left: 15 }) .backgroundColor(Color.Grey) Button('网络资源gif') .onClick(() => { - this.imageKnifeOption1 = { - loadSrc: 'https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658', + this.imageKnifeOption1 = { + loadSrc: 'https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658', - placeholderSrc: $r('app.media.icon_loading'), - errorholderSrc: $r('app.media.icon_failed'), - - }; - }) + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + + }; + }) .margin({ left: 15 }) .backgroundColor(Color.Grey) Button('预加载网络资源gif静态') .onClick(() => { - let request = new RequestOption(); - request.load('https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658') - .setImageViewSize({ width: 300, height: 300 }) - .dontAnimate() - .addListener({callback:(err:BusinessError|string, data:ImageKnifeData) => { - if (err) { - console.log('预加载网络资源gif静态 出现错误! err=' + err) - } else { - console.log('预加载网络资源gif静态成功! imageKnifedata=' + JSON.stringify(data)) - } - return false; - }}) - ImageKnifeGlobal.getInstance().getImageKnife()?.preload(request); - }) + let request = new RequestOption(); + request.load('https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658') + .setImageViewSize({ width: 300, height: 300 }) + .dontAnimate() + .addListener({callback:(err:BusinessError|string, data:ImageKnifeData) => { + if (err) { + console.log('预加载网络资源gif静态 出现错误! err=' + err) + } else { + console.log('预加载网络资源gif静态成功! imageKnifedata=' + JSON.stringify(data)) + } + return false; + }}) + ImageKnifeGlobal.getInstance().getImageKnife()?.preload(request); + }) .margin({ left: 15 }) .backgroundColor(Color.Grey) Button('网络资源gif静态') .onClick(() => { - this.imageKnifeOption1 = { - loadSrc: 'https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658', + this.imageKnifeOption1 = { + loadSrc: 'https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658', - placeholderSrc: $r('app.media.icon_loading'), - errorholderSrc: $r('app.media.icon_failed'), - dontAnimateFlag: true - }; - }) + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + dontAnimateFlag: true + }; + }) .margin({ left: 15 }) .backgroundColor(Color.Grey) } @@ -228,27 +228,27 @@ struct TestPreloadPage { if (err ) { console.log('预加载本地资源svg 出现错误! err=' + err) } else { - console.log('预加载本地资源svg成功! imageKnifedata=' + JSON.stringify(data)) - } - return false; - }}) - ImageKnifeGlobal.getInstance().getImageKnife()?.preload(request); - }) + console.log('预加载本地资源svg成功! imageKnifedata=' + JSON.stringify(data)) + } + return false; + }}) + ImageKnifeGlobal.getInstance().getImageKnife()?.preload(request); + }) .margin({ left: 15 }) .backgroundColor(Color.Grey) Button('本地资源svg') .onClick(() => { - this.imageKnifeOption = { - loadSrc: $r('app.media.svgSample'), + this.imageKnifeOption = { + loadSrc: $r('app.media.svgSample'), - placeholderSrc: $r('app.media.icon_loading'), - errorholderSrc: $r('app.media.icon_failed'), - - } + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), - }) + } + + }) .margin({ left: 15 }) .backgroundColor(Color.Grey) @@ -275,32 +275,32 @@ struct TestPreloadPage { Button('预加载网络资源svg') .onClick(() => { - let request = new RequestOption(); - request.load(this.svgUrl) - .setImageViewSize({ width: 300, height: 300 }) - .addListener({callback:(err:BusinessError|string, data:ImageKnifeData) => { - if (err) { - console.log('预加载网络资源gif 出现错误! err=' + err) - } else { - console.log('预加载网络资源gif成功! imageKnifedata=' + JSON.stringify(data)) - } - return false; - }}) - ImageKnifeGlobal.getInstance().getImageKnife()?.preload(request); - }) + let request = new RequestOption(); + request.load(this.svgUrl) + .setImageViewSize({ width: 300, height: 300 }) + .addListener({callback:(err:BusinessError|string, data:ImageKnifeData) => { + if (err) { + console.log('预加载网络资源gif 出现错误! err=' + err) + } else { + console.log('预加载网络资源gif成功! imageKnifedata=' + JSON.stringify(data)) + } + return false; + }}) + ImageKnifeGlobal.getInstance().getImageKnife()?.preload(request); + }) .margin({ left: 15 }) .backgroundColor(Color.Grey) Button('网络资源svg') .onClick(() => { - this.imageKnifeOption = { - loadSrc: this.svgUrl, + this.imageKnifeOption = { + loadSrc: this.svgUrl, - placeholderSrc: $r('app.media.icon_loading'), - errorholderSrc: $r('app.media.icon_failed'), - - }; - }) + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + + }; + }) .margin({ left: 15 }) .backgroundColor(Color.Grey) @@ -323,27 +323,27 @@ struct TestPreloadPage { if (err) { console.log('预加载本地资源webp 出现错误! err=' + err) } else { - console.log('预加载本地资源webp成功! imageKnifedata=' + JSON.stringify(data)) - } - return false; - }}) - ImageKnifeGlobal.getInstance().getImageKnife()?.preload(request); - }) + console.log('预加载本地资源webp成功! imageKnifedata=' + JSON.stringify(data)) + } + return false; + }}) + ImageKnifeGlobal.getInstance().getImageKnife()?.preload(request); + }) .margin({ left: 15 }) .backgroundColor(Color.Grey) Button('本地资源webp') .onClick(() => { - this.imageKnifeOption3 = { - loadSrc: $r('app.media.jpgSample'), + this.imageKnifeOption3 = { + loadSrc: $r('app.media.jpgSample'), - placeholderSrc: $r('app.media.icon_loading'), - errorholderSrc: $r('app.media.icon_failed'), - - } + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), - }) + } + + }) .margin({ left: 15 }) .backgroundColor(Color.Grey) @@ -355,32 +355,32 @@ struct TestPreloadPage { Button('预加载网络资源webp') .onClick(() => { - let request = new RequestOption(); - request.load('https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp') - .setImageViewSize({ width: 300, height: 300 }) - .addListener({callback:(err:BusinessError|string, data:ImageKnifeData) => { - if (err) { - console.log('预加载网络资源webp 出现错误! err=' + err) - } else { - console.log('预加载网络资源webp成功! imageKnifedata=' + JSON.stringify(data)) - } - return false; - }}) - ImageKnifeGlobal.getInstance().getImageKnife()?.preload(request); - }) + let request = new RequestOption(); + request.load('https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp') + .setImageViewSize({ width: 300, height: 300 }) + .addListener({callback:(err:BusinessError|string, data:ImageKnifeData) => { + if (err) { + console.log('预加载网络资源webp 出现错误! err=' + err) + } else { + console.log('预加载网络资源webp成功! imageKnifedata=' + JSON.stringify(data)) + } + return false; + }}) + ImageKnifeGlobal.getInstance().getImageKnife()?.preload(request); + }) .margin({ left: 15 }) .backgroundColor(Color.Grey) Button('网络资源webp') .onClick(() => { - this.imageKnifeOption3 = { - loadSrc: 'https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp', + this.imageKnifeOption3 = { + loadSrc: 'https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp', - placeholderSrc: $r('app.media.icon_loading'), - errorholderSrc: $r('app.media.icon_failed'), - - }; - }) + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + + }; + }) .margin({ left: 15 }) .backgroundColor(Color.Grey) } @@ -401,27 +401,27 @@ struct TestPreloadPage { if (err) { console.log('预加载本地资源bmp 出现错误! err=' + err) } else { - console.log('预加载本地资源bmp成功! imageKnifedata=' + JSON.stringify(data)) - } - return false; - }}) - ImageKnifeGlobal.getInstance().getImageKnife()?.preload(request); - }) + console.log('预加载本地资源bmp成功! imageKnifedata=' + JSON.stringify(data)) + } + return false; + }}) + ImageKnifeGlobal.getInstance().getImageKnife()?.preload(request); + }) .margin({ left: 15 }) .backgroundColor(Color.Grey) Button('本地资源bmp') .onClick(() => { - this.imageKnifeOption4 = { - loadSrc: $r('app.media.bmpSample'), + this.imageKnifeOption4 = { + loadSrc: $r('app.media.bmpSample'), - placeholderSrc: $r('app.media.icon_loading'), - errorholderSrc: $r('app.media.icon_failed'), - - } + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), - }) + } + + }) .margin({ left: 15 }) .backgroundColor(Color.Grey) @@ -433,32 +433,32 @@ struct TestPreloadPage { Button('预加载网络资源bmp') .onClick(() => { - let request = new RequestOption(); - request.load('https://img-blog.csdn.net/20140514114029140') - .setImageViewSize({ width: 300, height: 300 }) - .addListener({callback:(err:BusinessError|string, data:ImageKnifeData) => { - if (err) { - console.log('预加载网络资源bmp 出现错误! err=' + err) - } else { - console.log('预加载网络资源bmp成功! imageKnifedata=' + JSON.stringify(data)) - } - return false; - }}) - ImageKnifeGlobal.getInstance().getImageKnife()?.preload(request); - }) + let request = new RequestOption(); + request.load('https://img-blog.csdn.net/20140514114029140') + .setImageViewSize({ width: 300, height: 300 }) + .addListener({callback:(err:BusinessError|string, data:ImageKnifeData) => { + if (err) { + console.log('预加载网络资源bmp 出现错误! err=' + err) + } else { + console.log('预加载网络资源bmp成功! imageKnifedata=' + JSON.stringify(data)) + } + return false; + }}) + ImageKnifeGlobal.getInstance().getImageKnife()?.preload(request); + }) .margin({ left: 15 }) .backgroundColor(Color.Grey) Button('网络资源bmp') .onClick(() => { - this.imageKnifeOption4 = { - loadSrc: 'https://img-blog.csdn.net/20140514114029140', + this.imageKnifeOption4 = { + loadSrc: 'https://img-blog.csdn.net/20140514114029140', - placeholderSrc: $r('app.media.icon_loading'), - errorholderSrc: $r('app.media.icon_failed'), - - }; - }) + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + + }; + }) .margin({ left: 15 }) .backgroundColor(Color.Grey) } @@ -479,27 +479,27 @@ struct TestPreloadPage { if (err) { console.log('预加载本地资源png 出现错误! err=' + err) } else { - console.log('预加载本地资源png成功! imageKnifedata=' + JSON.stringify(data)) - } - return false; - }}) - ImageKnifeGlobal.getInstance().getImageKnife()?.preload(request); - }) + console.log('预加载本地资源png成功! imageKnifedata=' + JSON.stringify(data)) + } + return false; + }}) + ImageKnifeGlobal.getInstance().getImageKnife()?.preload(request); + }) .margin({ left: 15 }) .backgroundColor(Color.Grey) Button('本地资源png') .onClick(() => { - this.imageKnifeOption5 = { - loadSrc: $r('app.media.pngSample'), + this.imageKnifeOption5 = { + loadSrc: $r('app.media.pngSample'), - placeholderSrc: $r('app.media.icon_loading'), - errorholderSrc: $r('app.media.icon_failed'), - - } + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), - }) + } + + }) .margin({ left: 15 }) .backgroundColor(Color.Grey) @@ -511,32 +511,32 @@ struct TestPreloadPage { Button('预加载网络资源png') .onClick(() => { - let request = new RequestOption(); - request.load('https://img-blog.csdnimg.cn/20191215043500229.png') - .setImageViewSize({ width: 300, height: 300 }) - .addListener({callback:(err:BusinessError|string, data:ImageKnifeData) => { - if (err) { - console.log('预加载网络资源bmp 出现错误! err=' + err) - } else { - console.log('预加载网络资源bmp成功! imageKnifedata=' + JSON.stringify(data)) - } - return false; - }}) - ImageKnifeGlobal.getInstance().getImageKnife()?.preload(request); - }) + let request = new RequestOption(); + request.load('https://img-blog.csdnimg.cn/20191215043500229.png') + .setImageViewSize({ width: 300, height: 300 }) + .addListener({callback:(err:BusinessError|string, data:ImageKnifeData) => { + if (err) { + console.log('预加载网络资源bmp 出现错误! err=' + err) + } else { + console.log('预加载网络资源bmp成功! imageKnifedata=' + JSON.stringify(data)) + } + return false; + }}) + ImageKnifeGlobal.getInstance().getImageKnife()?.preload(request); + }) .margin({ left: 15 }) .backgroundColor(Color.Grey) Button('网络资源png') .onClick(() => { - this.imageKnifeOption5 = { - loadSrc: 'https://img-blog.csdnimg.cn/20191215043500229.png', + this.imageKnifeOption5 = { + loadSrc: 'https://img-blog.csdnimg.cn/20191215043500229.png', - placeholderSrc: $r('app.media.icon_loading'), - errorholderSrc: $r('app.media.icon_failed'), - - }; - }) + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + + }; + }) .margin({ left: 15 }) .backgroundColor(Color.Grey) } @@ -557,27 +557,27 @@ struct TestPreloadPage { if (err) { console.log('预加载本地资源jpg 出现错误! err=' + err) } else { - console.log('预加载本地资源jpg成功! imageKnifedata=' + JSON.stringify(data)) - } - return false; - }}) - ImageKnifeGlobal.getInstance().getImageKnife()?.preload(request); - }) + console.log('预加载本地资源jpg成功! imageKnifedata=' + JSON.stringify(data)) + } + return false; + }}) + ImageKnifeGlobal.getInstance().getImageKnife()?.preload(request); + }) .margin({ left: 15 }) .backgroundColor(Color.Grey) Button('本地资源jpg') .onClick(() => { - this.imageKnifeOption6 = { - loadSrc: $r('app.media.jpgSample'), + this.imageKnifeOption6 = { + loadSrc: $r('app.media.jpgSample'), - placeholderSrc: $r('app.media.icon_loading'), - errorholderSrc: $r('app.media.icon_failed'), - - } + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), - }) + } + + }) .margin({ left: 15 }) .backgroundColor(Color.Grey) @@ -589,32 +589,32 @@ struct TestPreloadPage { Button('预加载网络资源jpg') .onClick(() => { - let request = new RequestOption(); - request.load('https://hbimg.huabanimg.com/cc6af25f8d782d3cf3122bef4e61571378271145735e9-vEVggB') - .setImageViewSize({ width: 300, height: 300 }) - .addListener({callback:(err:BusinessError|string, data:ImageKnifeData) => { - if (err) { - console.log('预加载网络资源jpg 出现错误! err=' + err) - } else { - console.log('预加载网络资源jpg成功! imageknifedata=' + JSON.stringify(data)) - } - return false; - }}) - ImageKnifeGlobal.getInstance().getImageKnife()?.preload(request); - }) + let request = new RequestOption(); + request.load('https://hbimg.huabanimg.com/cc6af25f8d782d3cf3122bef4e61571378271145735e9-vEVggB') + .setImageViewSize({ width: 300, height: 300 }) + .addListener({callback:(err:BusinessError|string, data:ImageKnifeData) => { + if (err) { + console.log('预加载网络资源jpg 出现错误! err=' + err) + } else { + console.log('预加载网络资源jpg成功! imageknifedata=' + JSON.stringify(data)) + } + return false; + }}) + ImageKnifeGlobal.getInstance().getImageKnife()?.preload(request); + }) .margin({ left: 15 }) .backgroundColor(Color.Grey) Button('网络资源jpg') .onClick(() => { - this.imageKnifeOption6 = { - loadSrc: 'https://hbimg.huabanimg.com/cc6af25f8d782d3cf3122bef4e61571378271145735e9-vEVggB', + this.imageKnifeOption6 = { + loadSrc: 'https://hbimg.huabanimg.com/cc6af25f8d782d3cf3122bef4e61571378271145735e9-vEVggB', - placeholderSrc: $r('app.media.icon_loading'), - errorholderSrc: $r('app.media.icon_failed'), - - }; - }) + placeholderSrc: $r('app.media.icon_loading'), + errorholderSrc: $r('app.media.icon_failed'), + + }; + }) .margin({ left: 15 }) .backgroundColor(Color.Grey) } diff --git a/entry/src/main/ets/pages/testPriorityComponent.ets b/entry/src/main/ets/pages/testPriorityComponent.ets new file mode 100644 index 0000000..e496e8c --- /dev/null +++ b/entry/src/main/ets/pages/testPriorityComponent.ets @@ -0,0 +1,188 @@ +/* + * 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, Priority, NONE, ImageKnifeGlobal } from '@ohos/libraryimageknife'; +const dataBak: ImageKnifeOption[] = [ + { + loadSrc: "http://b.hiphotos.baidu.com/image/pic/item/9d82d158ccbf6c81b94575cfb93eb13533fa40a2.jpg", + priority: Priority.HIGH + }, + { + loadSrc: "http://e.hiphotos.baidu.com/image/pic/item/4bed2e738bd4b31c1badd5a685d6277f9e2ff81e.jpg", + priority: Priority.HIGH + }, + { + loadSrc: "http://g.hiphotos.baidu.com/image/pic/item/0d338744ebf81a4c87a3add4d52a6059252da61e.jpg", + priority: Priority.HIGH + }, + { + loadSrc: "http://a.hiphotos.baidu.com/image/pic/item/f2deb48f8c5494ee5080c8142ff5e0fe99257e19.jpg", + priority: Priority.HIGH + }, + { + loadSrc: "http://f.hiphotos.baidu.com/image/pic/item/4034970a304e251f503521f5a586c9177e3e53f9.jpg", + priority: Priority.HIGH + }, + { + loadSrc: "https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658", + priority: Priority.MEDIUM + }, + { + loadSrc: "http://h.hiphotos.baidu.com/image/pic/item/7c1ed21b0ef41bd5f2c2a9e953da81cb39db3d1d.jpg", + priority: Priority.MEDIUM + }, + { + loadSrc: "http://a.hiphotos.baidu.com/image/pic/item/8d5494eef01f3a292d2472199d25bc315d607c7c.jpg", + priority: Priority.MEDIUM + }, + { + loadSrc: "https://img-blog.csdn.net/20140514114029140", + priority: Priority.MEDIUM + }, + { + loadSrc: "http://g.hiphotos.baidu.com/image/pic/item/6d81800a19d8bc3e770bd00d868ba61ea9d345f2.jpg", + priority: Priority.MEDIUM + }, + { + loadSrc: "http://b.hiphotos.baidu.com/image/pic/item/e824b899a9014c08878b2c4c0e7b02087af4f4a3.jpg", + priority: Priority.LOW + }, + { + loadSrc: "http://b.hiphotos.baidu.com/image/pic/item/359b033b5bb5c9ea5c0e3c23d139b6003bf3b374.jpg", + priority: Priority.LOW + }, + { + loadSrc: "http://h.hiphotos.baidu.com/image/pic/item/902397dda144ad340668b847d4a20cf430ad851e.jpg", + priority: Priority.LOW + }, + { + loadSrc: "http://d.hiphotos.baidu.com/image/pic/item/b58f8c5494eef01f119945cbe2fe9925bc317d2a.jpg", + priority: Priority.LOW + }, + { + loadSrc:"http://img2.xkhouse.com/bbs/hfhouse/data/attachment/forum/corebbs/2009-11/2009113011534566298.jpg", + priority: Priority.LOW + } +]; + +@Entry +@Component +struct TestPriorityComponent { + @State data: ImageKnifeOption[] = [ + { + loadSrc: "https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658", + priority: Priority.HIGH + }, + { + loadSrc: "http://c.hiphotos.baidu.com/image/pic/item/30adcbef76094b36de8a2fe5a1cc7cd98d109d99.jpg", + priority: Priority.HIGH + }, + { + loadSrc: "http://h.hiphotos.baidu.com/image/pic/item/7c1ed21b0ef41bd5f2c2a9e953da81cb39db3d1d.jpg", + priority: Priority.HIGH + }, + { + loadSrc: "http://g.hiphotos.baidu.com/image/pic/item/55e736d12f2eb938d5277fd5d0628535e5dd6f4a.jpg", + priority: Priority.HIGH + }, + { + loadSrc: "http://e.hiphotos.baidu.com/image/pic/item/4e4a20a4462309f7e41f5cfe760e0cf3d6cad6ee.jpg", + priority: Priority.HIGH + }, + { + loadSrc: "http://b.hiphotos.baidu.com/image/pic/item/279759ee3d6d55fbb3586c0168224f4a20a4dd7e.jpg", + priority: Priority.MEDIUM + }, + { + loadSrc: "http://img2.xkhouse.com/bbs/hfhouse/data/attachment/forum/corebbs/2009-11/2009113011534566298.jpg", + priority: Priority.MEDIUM + }, + { + loadSrc: "http://a.hiphotos.baidu.com/image/pic/item/e824b899a9014c087eb617650e7b02087af4f464.jpg", + priority: Priority.MEDIUM + }, + { + loadSrc: "http://c.hiphotos.baidu.com/image/pic/item/9c16fdfaaf51f3de1e296fa390eef01f3b29795a.jpg", + priority: Priority.MEDIUM + }, + { + loadSrc: "http://d.hiphotos.baidu.com/image/pic/item/b58f8c5494eef01f119945cbe2fe9925bc317d2a.jpg", + priority: Priority.MEDIUM + }, + { + loadSrc: "https://hbimg.huabanimg.com/cc6af25f8d782d3cf3122bef4e61571378271145735e9-vEVggB", + priority: Priority.LOW + }, + { + loadSrc: "https://img-blog.csdnimg.cn/20191215043500229.png", + priority: Priority.LOW + }, + { + loadSrc: "https://img-blog.csdn.net/20140514114029140", + priority: Priority.LOW + }, + { + loadSrc: "https://hbimg.huabanimg.com/95a6d37a39aa0b70d48fa18dc7df8309e2e0e8e85571e-x4hhks_fw658/format/webp", + priority: Priority.LOW + }, + { + loadSrc: "http://g.hiphotos.baidu.com/image/pic/item/6d81800a19d8bc3e770bd00d868ba61ea9d345f2.jpg", + priority: Priority.LOW + } + ]; + @State maxRequests: number | undefined = ImageKnifeGlobal?.getInstance()?.getImageKnife()?.maxRequests; + + build() { + Scroll() { + Column() { + Text('最大并发数: ' + this.maxRequests).width('50%').height(30) + Button('修改最大并发数为1').width('50%').height(30) + .onClick(() => { + if (ImageKnifeGlobal?.getInstance()?.getImageKnife()) { + ImageKnifeGlobal?.getInstance()?.getImageKnife()?.setMaxRequests(1); + this.maxRequests = ImageKnifeGlobal?.getInstance()?.getImageKnife()?.maxRequests; + this.data = [...dataBak, ...dataBak, ...dataBak]; + } + }) + Grid() { + ForEach(this.data, (item: ImageKnifeOption, index: number) => { + GridItem() { + Flex() { + Text(item?.priority?.toString() || '') + ImageKnifeComponent({ + imageKnifeOption: { + loadSrc: item.loadSrc, + placeholderSrc: $r('app.media.icon_loading'), + strategy: new NONE(), + isCacheable: false, + priority: item.priority + } + }) + }.height(60) + } + }) + } + .width("100%") + .height("100%") + .columnsTemplate("1fr 1fr 1fr 1fr 1fr") + .columnsGap(10) + .rowsGap(10) + .backgroundColor(0xFAEEE0) + .maxCount(10) + } + .backgroundColor(Color.Pink) + .margin({ top: 5 }) + } + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/testReuseAblePages.ets b/entry/src/main/ets/pages/testReuseAblePages.ets new file mode 100644 index 0000000..7144c87 --- /dev/null +++ b/entry/src/main/ets/pages/testReuseAblePages.ets @@ -0,0 +1,204 @@ +/* + * 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' + +class BasicDataSource implements IDataSource { + private listeners: DataChangeListener[] = []; + private originDataArray: StringData[] = []; + + public totalCount(): number { + return 0; + } + + public getData(index: number): StringData { + return this.originDataArray[index]; + } + + registerDataChangeListener(listener: DataChangeListener): void { + if (this.listeners.indexOf(listener) < 0) { + console.info('add listener'); + this.listeners.push(listener); + } + } + + unregisterDataChangeListener(listener: DataChangeListener): void { + const pos = this.listeners.indexOf(listener); + if (pos >= 0) { + console.info('remove listener'); + this.listeners.splice(pos, 1); + } + } + + notifyDataReload(): void { + this.listeners.forEach(listener => { + listener.onDataReloaded(); + }) + } + + notifyDataAdd(index: number): void { + this.listeners.forEach(listener => { + listener.onDataAdd(index); + }) + } + + notifyDataChange(index: number): void { + this.listeners.forEach(listener => { + listener.onDataChange(index); + }) + } + + notifyDataDelete(index: number): void { + this.listeners.forEach(listener => { + listener.onDataDelete(index); + }) + } + + notifyDataMove(from: number, to: number): void { + this.listeners.forEach(listener => { + listener.onDataMove(from, to); + }) + } +} + +class MyDataSource extends BasicDataSource { + private dataArray: StringData[] = []; + + public totalCount(): number { + return this.dataArray.length; + } + + public getData(index: number): StringData { + return this.dataArray[index]; + } + + public addData(index: number, data: StringData): void { + this.dataArray.splice(index, 0, data); + this.notifyDataAdd(index); + } + + public pushData(data: StringData): void { + this.dataArray.push(data); + this.notifyDataAdd(this.dataArray.length - 1); + } + + public reloadData(): void { + this.notifyDataReload(); + } +} + +class StringData { + message: string; + imgSrc: string; + + constructor(message: string, imgSrc: string) { + this.message = message; + this.imgSrc = imgSrc; + } +} + +@Entry +@Component +struct TestReuseAbleComponent { + @State dataArray: 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", + "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' + ] + private data: MyDataSource = new MyDataSource(); + private index: number = 0 + private timeId: number = -1 + + aboutToAppear() { + for (let i = 0; i <= 4; i++) { + this.data.pushData(new StringData(`Hello ${i}`, this.dataArray[i])); + } + } + + build() { + List({ space: 3 }) { + LazyForEach(this.data, (item: StringData, index: number) => { + ListItem() { + Column() { + Text(item.message).fontSize(50) + .onAppear(() => { + console.info("appear:" + item.message) + }) + ReusableComponent({ url: item.imgSrc }) + .width(500) + .height(200) + }.margin({ left: 10, right: 10 }) + } + .onClick(() => { + item.message += '00'; + this.data.reloadData(); + }) + }, (item: StringData, index: number) => JSON.stringify(item)) + }.cachedCount(5) + .onReachEnd(()=>{ + clearTimeout(this.timeId) + this.timeId = setTimeout(()=>{ + this.data.reloadData() + if(this.data.totalCount() <= this.dataArray.length) { + this.index = this.data.totalCount() + for (let index = this.index; index <= this.index + 4; index++) { + this.data.addData(index,new StringData(`Hello ${index}`, this.dataArray[index])); + } + } + },1500) + }) + } +} + +@Reusable +@Component +struct ReusableComponent { + @State url: string = "" + + aboutToReuse(params: Record) { + this.url = params.url + } + + build() { + Column() { + ImageKnifeComponent({ + imageKnifeOption: { + loadSrc: this.url, + placeholderSrc: $r('app.media.icon_loading') + } + }) + }.width("100%").height("100%") + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/testReusePhotoPage.ets b/entry/src/main/ets/pages/testReusePhotoPage.ets new file mode 100644 index 0000000..52abc1e --- /dev/null +++ b/entry/src/main/ets/pages/testReusePhotoPage.ets @@ -0,0 +1,203 @@ +/* + * 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, ScaleType } from '@ohos/libraryimageknife' + +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 TestReusePhotoPage { + @State hotCommendList: CommonDataSource = new CommonDataSource([]) + 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", + "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() { + this.hotCommendList.addData(this.hotCommendList.totalCount(), this.data) + AppStorage.set("image_size",1) + } + + build() { + Column() { + Button("up").onClick(()=>{ + AppStorage.set("image_size",1.6) + }) + Button("down").onClick(()=>{ + AppStorage.set("image_size",0.9) + }) + List() { + LazyForEach(this.hotCommendList, (item: string) => { + ListItem() { + ReuseComponent({ url: item }).width("100%").height("100%") + }.width(200).height(200).backgroundColor(Color.Orange) + }) + } + .width("100%") + .height("100%") + .backgroundColor(0xFAEEE0) + }.width("100%").height("100%") + } +} + +@Reusable +@Component +struct ReuseComponent { + @Prop url: string = "" + imgSize: number = 100 + @StorageProp("image_size") @Watch("updateImageSize") image_size: number = 0 + scaleAble: boolean = true + @State calcImgSize: number = 100 + aboutToReuse(params: Record) { + this.url = params.url + } + aboutToAppear(){ + this.setImageSize() + } + updateImageSize() { + this.setImageSize() + } + + setImageSize() { + if (!this.scaleAble) { + this.calcImgSize = this.imgSize + } else if (this.image_size < 0.9) { + this.calcImgSize = this.imgSize * 0.9 + } else if (this.image_size > 1.6) { + this.calcImgSize = this.imgSize * 1.6 + } else { + this.calcImgSize = this.imgSize * this.image_size + } + } + + build() { + Column() { + ImageKnifeComponent({ + imageKnifeOption:{ + loadSrc: this.url, + mainScaleType: ScaleType.FIT_XY, + placeholderSrc: $r('app.media.icon_loading'), + placeholderScaleType: ScaleType.FIT_XY, + errorholderSrc: $r('app.media.icon_failed'), + errorholderSrcScaleType: ScaleType.FIT_XY + } + }).width(this.calcImgSize).height(this.calcImgSize) + }.width("100%").height("100%") + } +} + +@Component +struct PhotoComponent { + @Watch("updateTheUrl") @Prop url: string = "" + imgSize: number = 100 + @State imageKnifeOption: ImageKnifeOption = new ImageKnifeOption() + @StorageProp("image_size") @Watch("updateImageSize") image_size: number = 0 + scaleAble: boolean = true + @State calcImgSize: number = 100 + + updateImageSize() { + this.setImageSize() + } + + setImageSize() { + if (!this.scaleAble) { + this.calcImgSize = this.imgSize + } else if (this.image_size < 0.9) { + this.calcImgSize = this.imgSize * 0.9 + } else if (this.image_size > 1.6) { + this.calcImgSize = this.imgSize * 1.6 + } else { + this.calcImgSize = this.imgSize * this.image_size + } + } + + updateTheUrl() { + this.imageKnifeOption = { + loadSrc: this.url, + mainScaleType: ScaleType.FIT_XY, + placeholderSrc: $r('app.media.icon_loading'), + placeholderScaleType: ScaleType.FIT_XY, + errorholderSrc: $r('app.media.icon_failed'), + errorholderSrcScaleType: ScaleType.FIT_XY + } + } + + build() { + Column() { + ImageKnifeComponent({ imageKnifeOption: this.imageKnifeOption }) + }.width(this.calcImgSize).height(this.calcImgSize) + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/testStopPlayingGifPage.ets b/entry/src/main/ets/pages/testStopPlayingGifPage.ets new file mode 100644 index 0000000..346c8ae --- /dev/null +++ b/entry/src/main/ets/pages/testStopPlayingGifPage.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. + */ +import { ImageKnifeComponent } from '@ohos/libraryimageknife' +import { ImageKnifeOption } from '@ohos/libraryimageknife' +import display from '@ohos.display'; + +@Entry +@Component +struct TestStopPlayingGifPage { + @State message: string = 'gif暂停播放测试' + @State eventType: string = ''; + @State options: ImageKnifeOption = { + loadSrc: $r('app.media.app_icon'), + } + + build() { + Column() { + Column() { + Text(`${this.message}`) + .width("300vp") + .height("60vp") + .textAlign(TextAlign.Center) + .fontSize("30fp") + .fontWeight(FontWeight.Bold) + Button("加载gif图") + .margin(16) + .onClick(() => { + console.log("加载多帧gif") + this.options = { + loadSrc: $r('app.media.gifSample'), + placeholderSrc:$r('app.media.icon_loading'), + } + }) + Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { + Text("控制") + Button("开启") + .onClick(() => { + this.options.autoPlay = true; + }).margin({ left: 3 }) + + Button("暂停") + .onClick(() => { + this.options.autoPlay = false; + }).margin({ left: 3 }) + }.width('100%') + .height(40).backgroundColor(Color.Pink).margin({bottom:5}) + ImageKnifeComponent({ imageKnifeOption: this.options }) + .margin(16) + .width(300) + .height(300) + } + .width("100%") + .height("100%") + .justifyContent(FlexAlign.Center) + } + .width("100%") + .height("100%") + } + aboutToAppear() { + console.log('CTT:TestManyNetImageLoadWithPage display Height: '+ display.getDefaultDisplaySync().height) + } +} + diff --git a/entry/src/main/ets/pages/transformPixelMapPage.ets b/entry/src/main/ets/pages/transformPixelMapPage.ets index 745d1af..152ea0e 100644 --- a/entry/src/main/ets/pages/transformPixelMapPage.ets +++ b/entry/src/main/ets/pages/transformPixelMapPage.ets @@ -406,7 +406,7 @@ struct TransformPixelMapPage { .width(120) .margin({ top: 10 }) .onClick(() => { - this.blurHandlePixelMap(20); + this.blurHandlePixelMap(20,3); }); Image(this.mBlurPixelMap==undefined?'':this.mBlurPixelMap!) .objectFit(ImageFit.Fill) @@ -847,9 +847,9 @@ struct TransformPixelMapPage { /** *模糊 */ - blurHandlePixelMap(radius: number) { + blurHandlePixelMap(radius: number,sampling: number) { let imageKnifeOption = new RequestOption(); - let transformation = new BlurTransformation(radius); + let transformation = new BlurTransformation(radius,sampling); imageKnifeOption.load(mUrl) .addListener({callback:(err:BusinessError|string, data:ImageKnifeData) => { @@ -860,7 +860,7 @@ struct TransformPixelMapPage { .setImageViewSize({ width: vp2px(200), height: vp2px(200) }) .skipMemoryCache(true) .enableGPU() - .blur(radius) + .blur(radius,sampling) ImageKnife?.call(imageKnifeOption); } diff --git a/entry/src/main/ets/pages/webpImageTestPage.ets b/entry/src/main/ets/pages/webpImageTestPage.ets new file mode 100644 index 0000000..fc79bf5 --- /dev/null +++ b/entry/src/main/ets/pages/webpImageTestPage.ets @@ -0,0 +1,69 @@ +/* + * 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 } from '@ohos/libraryimageknife' +import { ImageKnifeOption,ScaleType } from '@ohos/libraryimageknife' + +@Entry +@Component +struct webpImageTestPage { + @State message: string = 'webp图片' + @State options: ImageKnifeOption = { + loadSrc: $r('app.media.app_icon') + } + + build() { + Column() { + Column() { + Text(`${this.message}`) + .width("300vp") + .height("60vp") + .textAlign(TextAlign.Center) + .fontSize("50fp") + .fontWeight(FontWeight.Bold) + Button("加载单帧webp") + .margin(16) + .onClick(() => { + console.log("加载单帧webp") + this.options = { + loadSrc: $r('app.media.webpSample'), + placeholderSrc:$r('app.media.icon_loading'), + mainScaleType:ScaleType.FIT_XY + } + }) + Button("加载多帧webp") + .margin(16) + .onClick(() => { + console.log("加载多帧webp") + this.options = { + loadSrc: $r('app.media.webpAtanta'), + placeholderSrc:$r('app.media.icon_loading'), + mainScaleType:ScaleType.FIT_XY + } + }) + ImageKnifeComponent({ imageKnifeOption: this.options }) + .margin(16) + .width(200) + .height(100) + .clip(true) + .borderRadius(50) + } + .width("100%") + .height("100%") + .justifyContent(FlexAlign.Center) + } + .width("100%") + .height("100%") + } +} \ No newline at end of file diff --git a/entry/src/main/module.json5 b/entry/src/main/module.json5 index 9a567c5..d5bfffb 100644 --- a/entry/src/main/module.json5 +++ b/entry/src/main/module.json5 @@ -22,7 +22,7 @@ "abilities": [ { "name": "MainAbility", - "srcEntrance": "./ets/entryability/EntryAbility.ets", + "srcEntry": "./ets/entryability/EntryAbility.ets", "description": "$string:MainAbility_desc", "icon": "$media:icon", "label": "$string:MainAbility_label", @@ -53,6 +53,7 @@ }, { "name": "ohos.permission.MEDIA_LOCATION", + "reason": "$string:app_permission_MEDIA_LOCATION", "usedScene": { "abilities": [ "EntryAbility" @@ -62,6 +63,7 @@ }, { "name": "ohos.permission.READ_MEDIA", + "reason": "$string:app_permission_READ_MEDIA", "usedScene": { "abilities": [ "EntryAbility" diff --git a/entry/src/main/resources/base/element/string.json b/entry/src/main/resources/base/element/string.json index a4cd007..c1c0806 100644 --- a/entry/src/main/resources/base/element/string.json +++ b/entry/src/main/resources/base/element/string.json @@ -112,6 +112,14 @@ { "name": "image_pixel", "value": "马赛克处理" + }, + { + "name": "app_permission_MEDIA_LOCATION", + "value": "获取媒体图片" + }, + { + "name": "app_permission_READ_MEDIA", + "value": "读媒体图片" } ] } \ No newline at end of file diff --git a/entry/src/main/resources/base/media/webpAtanta.webp b/entry/src/main/resources/base/media/webpAtanta.webp new file mode 100644 index 0000000..affe2a5 Binary files /dev/null and b/entry/src/main/resources/base/media/webpAtanta.webp differ diff --git a/entry/src/main/resources/base/media/yunHeic.heic b/entry/src/main/resources/base/media/yunHeic.heic new file mode 100644 index 0000000..161e3ca Binary files /dev/null and b/entry/src/main/resources/base/media/yunHeic.heic differ diff --git a/entry/src/main/resources/base/profile/main_pages.json b/entry/src/main/resources/base/profile/main_pages.json index a119187..3be410f 100644 --- a/entry/src/main/resources/base/profile/main_pages.json +++ b/entry/src/main/resources/base/profile/main_pages.json @@ -12,11 +12,13 @@ "pages/showErrorholderTestCasePage", "pages/transformPixelMapPage", "pages/testPreloadPage", + "pages/testDiskPreLoadPage", "pages/testImageKnifeOptionChangedPage", "pages/testImageKnifeOptionChangedPage2", "pages/testImageKnifeOptionChangedPage3", "pages/testImageKnifeOptionChangedPage4", "pages/testImageKnifeOptionChangedPage5", + "pages/testGifPlayTimesPage", "pages/compressPage", "pages/testAllCacheInfoPage", "pages/cropImagePage2", @@ -34,10 +36,26 @@ "pages/hspCacheTestPage", "pages/multiHspTestPage", "pages/testManyNetImageLoadWithPage", + "pages/testManyNetImageLoadWithPage2", "pages/testManyGifLoadWithPage", "pages/testImageAntiAliasingWithPage", "pages/testImageKnifeRouter1", "pages/testImageKnifeRouter2", - "pages/RequestOptionLoadImage" + "pages/RequestOptionLoadImage", + "pages/testImageKnifeHttpRequestHeader", + "pages/testImageKnifeHttpRequestHeader1", + "pages/testImageKnifeAutoPage", + "pages/testImageKnifeAutoWidthPage", + "pages/testImageKnifeAutoHeightPage", + "pages/testPriorityComponent", + "pages/testReusePhotoPage", + "pages/testImageKnifeCache", + "pages/webpImageTestPage", + "pages/testStopPlayingGifPage", + "pages/testImageKnifeDataFetch", + "pages/testImageKnifeHeic", + "pages/testImageKnifeNetPlaceholder", + "pages/testCustomDataFetchClientWithPage", + "pages/testReuseAblePages" ] } \ 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 new file mode 100644 index 0000000..e1c1401 --- /dev/null +++ b/entry/src/ohosTest/ets/test/DefaultJobQueueTest.test.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 { 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 taskpool from '@ohos.taskpool'; +import common from '@ohos.app.ability.common'; + +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); + }); + + 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); + }; + }) + }); +} + +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; +} diff --git a/entry/src/ohosTest/ets/test/List.test.ets b/entry/src/ohosTest/ets/test/List.test.ets index d42ebce..9d986c8 100644 --- a/entry/src/ohosTest/ets/test/List.test.ets +++ b/entry/src/ohosTest/ets/test/List.test.ets @@ -15,15 +15,21 @@ import abilityTest from './Ability.test' import lruCacheTest from './lrucache.test' import LogUtilTest from './logutil.test' -import Transform from './transfrom.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'; export default function testsuite() { abilityTest() lruCacheTest() + DiskLruCacheTest() LogUtilTest() - Transform() + Transfrom() RequestOptionTest() ImageKnifeTest(); + SendableDataTest(); + DefaultJobQueueTest(); } \ No newline at end of file diff --git a/entry/src/ohosTest/ets/test/SendableData.test.ets b/entry/src/ohosTest/ets/test/SendableData.test.ets new file mode 100644 index 0000000..e98f65d --- /dev/null +++ b/entry/src/ohosTest/ets/test/SendableData.test.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 { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium' +import { SendableData } from '@ohos/imageknife/src/main/ets/components/imageknife/SendableData' + +export default function SendableDataTest() { + describe('SendableDataTest', ()=> { + // 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('TestPlaceHolderCacheKey', 0, () => { + let value: string = "placeholderRegisterCacheKey"; + let data: SendableData = new SendableData(); + data.setPlaceHolderRegisterCacheKey(value); + expect(data.getPlaceHolderRegisterCacheKey()).assertEqual(value); + }) + it('TestPlaceHolderMemoryCacheKey', 1, () => { + let value: string = "placeholderRegisterMemoryCacheKey"; + let data: SendableData = new SendableData(); + data.setPlaceHolderRegisterMemoryCacheKey(value); + expect(data.getPlaceHolderRegisterMemoryCacheKey()).assertEqual(value); + }) + }) +} \ No newline at end of file diff --git a/entry/src/ohosTest/ets/test/diskLruCache.test.ets b/entry/src/ohosTest/ets/test/diskLruCache.test.ets new file mode 100644 index 0000000..a4b5fb4 --- /dev/null +++ b/entry/src/ohosTest/ets/test/diskLruCache.test.ets @@ -0,0 +1,162 @@ +/* + * 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 { DiskLruCache, DiskCacheEntry } from '@ohos/imageknife' +import { common } from '@kit.AbilityKit'; +import fs from '@ohos.file.fs'; +import { GlobalContext } from '../testability/GlobalContext' + +const BASE_COUNT: number = 2000; + +export default function DiskLruCacheTest() { + + describe('DiskLruCacheTest', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + //ImageKnife.with(this.context.createModuleContext("entry_test")); + // 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('testSetCahe', 0, () => { + let context: object | undefined = GlobalContext.getInstance().getObject("hapContext"); + let disLruCache: DiskLruCache = DiskLruCache.create(context as common.UIAbilityContext, 1024); + let startTime = new Date().getTime(); + disLruCache.set('test', "Hello World Simple Example."); + disLruCache.set('testABC', "Hello World ABC"); + disLruCache.set('testDE', "Hello World Simple DE"); + expect(String.fromCharCode(...new Uint8Array(disLruCache.get('test') as ArrayBufferLike)) == "Hello World Simple Example.") + .assertTrue() + expect(String.fromCharCode(...new Uint8Array(disLruCache.get('testABC') as ArrayBufferLike)) == "Hello World ABC") + .assertTrue() + expect(String.fromCharCode(...new Uint8Array(disLruCache.get('testDE') as ArrayBufferLike)) == "Hello World Simple DE") + .assertTrue() + let str: string = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; + for (let index = 0; index < 100; index++) { + str += index; + disLruCache.set('test' + index, str); + } + expect(disLruCache.getSize() <= 1024).assertTrue() + endTime(startTime, 'testSetCahe'); + disLruCache.cleanCacheData(); + + }) + it('testGetCacheAsync', 1, () => { + let context: object | undefined = GlobalContext.getInstance().getObject("hapContext"); + let filesDir: object | undefined = GlobalContext.getInstance().getObject("filesDir"); + let cache: DiskLruCache = DiskLruCache.create(context as common.UIAbilityContext, 1024); + let startTime = new Date().getTime(); + let path = filesDir + '/testFile.txt'; + let file = fs.openSync(path, 0o102); + fs.writeSync(file.fd, "hello, world!"); + let length = fs.statSync(path).size; + let dataArr = new ArrayBuffer(length); + fs.readSync(file.fd, dataArr); + cache.set('testFile', dataArr); + expect(cache.get('testFile')?.byteLength == 13).assertTrue() + endTime(startTime, 'testGetCacheAsync'); + cache.cleanCacheData(); + }) + it('testDeleteCacheDataByKey', 2, () => { + let context: object | undefined = GlobalContext.getInstance().getObject("hapContext"); + let cache: DiskLruCache = DiskLruCache.create(context as common.UIAbilityContext, 1024); + let startTime = new Date().getTime(); + cache.set('test', "Hello World Simple Example."); + cache.set('testABC', "Hello World ABC"); + expect(String.fromCharCode(...new Uint8Array(cache.get('test') as ArrayBufferLike))) + .assertEqual("Hello World Simple Example."); + expect(String.fromCharCode(...new Uint8Array(cache.get('testABC') as ArrayBufferLike))) + .assertEqual("Hello World ABC"); + cache.deleteCacheDataByKey('test'); + cache.deleteCacheDataByKey('testABC'); + expect(String.fromCharCode(...new Uint8Array(cache.get('test') as ArrayBufferLike))).assertEqual(''); + expect(String.fromCharCode(...new Uint8Array(cache.get('testABC') as ArrayBufferLike))).assertEqual(''); + endTime(startTime, 'testDeleteCacheDataByKey'); + cache.cleanCacheData(); + }) + it('testGetPath', 3, () => { + let context: object | undefined = GlobalContext.getInstance().getObject("hapContext"); + let cache: DiskLruCache = DiskLruCache.create(context as common.UIAbilityContext, 1024); + let startTime = new Date().getTime(); + cache.set('test', "Hello World Simple Example."); + let path: string = cache.getFileToPath('test'); + expect(String.fromCharCode(...new Uint8Array(cache.get('test') as ArrayBufferLike)) == "Hello World Simple Example.") + .assertTrue() + expect(cache.getFileToPath('test') == path).assertTrue() + expect(cache.getPath() + cache.getCacheMap().getFirstKey() == path).assertTrue() + endTime(startTime, 'testGetPath'); + cache.cleanCacheData(); + }) + it('testGetCacheMap', 6, () => { + let context: object | undefined = GlobalContext.getInstance().getObject("hapContext"); + let cache: DiskLruCache = DiskLruCache.create(context as common.UIAbilityContext, 1024); + let startTime = new Date().getTime(); + cache.set('test', "Hello World Simple Example."); + expect(cache.getCacheMap().getFirstKey() == '098f6bcd4621d373cade4e832627b4f6').assertTrue() + expect(cache.getCacheMap().hasKey('098f6bcd4621d373cade4e832627b4f6') == true).assertTrue() + endTime(startTime, 'testGetCacheMap'); + cache.cleanCacheData(); + + }) + + it('testGetSize', 7, () => { + let context: object | undefined = GlobalContext.getInstance().getObject("hapContext"); + let cache: DiskLruCache = DiskLruCache.create(context as common.UIAbilityContext, 1024); + let startTime = new Date().getTime(); + cache.set('test', "Hello World Simple Example."); + expect(String.fromCharCode(...new Uint8Array(cache.get('test') as ArrayBufferLike)) == "Hello World Simple Example.") + .assertTrue() + expect(cache.getSize() == 27).assertTrue() + endTime(startTime, 'testGetSize'); + cache.cleanCacheData(); + + }) + + it('testDiskCacheEntry', 8, () => { + let startTime = new Date().getTime(); + let dentry = new DiskCacheEntry('test', 30 * 1024 * 1024) + expect(dentry.getKey() == 'test').assertTrue() + dentry.setKey('newtest') + expect(dentry.getKey() == 'newtest').assertTrue() + expect(dentry.getLength() == 30 * 1024 * 1024).assertTrue() + dentry.setLength(12 * 1024 * 1024) + expect(dentry.getLength() == 12 * 1024 * 1024).assertTrue() + expect(dentry.toString() == dentry.getKey() + ' - ' + dentry.getLength()).assertTrue() + endTime(startTime, 'testDiskCacheEntry'); + }) + }) +} + +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/requestoption.test.ets b/entry/src/ohosTest/ets/test/requestoption.test.ets index 52becb3..5a219e2 100644 --- a/entry/src/ohosTest/ets/test/requestoption.test.ets +++ b/entry/src/ohosTest/ets/test/requestoption.test.ets @@ -91,6 +91,18 @@ export default function RequestOptionTest() { expect(option.loadThumbnailReady).assertFalse() }) + it('TestPlaceHolder', 4, () => { + let url: string = "https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658"; + let option = new RequestOption(); + option.placeholder(url); + expect(option.placeholderSrc).assertEqual(url); + }) + it('TestFallBack', 5, () => { + let url: PixelMap | Resource = $r('app.media.icon_loading'); + let option = new RequestOption(); + option.fallback(url); + expect(JSON.stringify(option.fallbackSrc)).assertEqual(JSON.stringify(url)); + }) }) } diff --git a/entry/src/ohosTest/ets/test/transfrom.test.ets b/entry/src/ohosTest/ets/test/transfrom.test.ets index 8db4339..a029651 100644 --- a/entry/src/ohosTest/ets/test/transfrom.test.ets +++ b/entry/src/ohosTest/ets/test/transfrom.test.ets @@ -64,11 +64,11 @@ export default function Transform() { it('TestBlurTransformation', 0, ()=>{ let startTime = new Date().getTime(); for (let index = 0; index < BASE_COUNT; index++) { - new BlurTransformation(15); + new BlurTransformation(15,3); } endTime(startTime, 'TestBlurTransformation'); - let blur = new BlurTransformation(15); - expect(blur.getName()).assertEqual('BlurTransformation _mRadius:15') + let blur = new BlurTransformation(15,3); + expect(blur.getName()).assertEqual('BlurTransformation _mRadius:15==BlurTransformation sampling:3') }) it('TestBrightnessFilterTransformation', 1, ()=>{ let startTime = new Date().getTime(); @@ -85,8 +85,8 @@ export default function Transform() { new ContrastFilterTransformation(30); } endTime(startTime, 'TestContrastFilterTransformation'); - let constrast = new ContrastFilterTransformation(30); - expect(constrast.getName()).assertEqual("ContrastFilterTransformation:30") + let contrast = new ContrastFilterTransformation(30); + expect(contrast.getName()).assertEqual("ContrastFilterTransformation:30") }) it('TestCropCircleTransformation', 3, ()=>{ let startTime = new Date().getTime(); diff --git a/entry/src/ohosTest/ets/testability/GlobalContext.ets b/entry/src/ohosTest/ets/testability/GlobalContext.ets new file mode 100644 index 0000000..8671492 --- /dev/null +++ b/entry/src/ohosTest/ets/testability/GlobalContext.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. + */ + +export class GlobalContext { + private constructor() { + } + + private static instance: GlobalContext; + private _objects = new Map(); + + public static getInstance(): 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/ohosTest/ets/testability/TestAbility.ets b/entry/src/ohosTest/ets/testability/TestAbility.ets index 140dbda..1860105 100644 --- a/entry/src/ohosTest/ets/testability/TestAbility.ets +++ b/entry/src/ohosTest/ets/testability/TestAbility.ets @@ -22,6 +22,7 @@ import {ImageKnife,ImageKnifeDrawFactory,ImageKnifeGlobal} from '@ohos/libraryim import AbilityConstant from '@ohos.app.ability.AbilityConstant'; import Want from '@ohos.app.ability.Want'; import { BusinessError } from '@ohos.base' +import {GlobalContext } from './GlobalContext' export default class TestAbility extends UIAbility { onCreate(want: Want, param: AbilityConstant.LaunchParam) { @@ -33,6 +34,8 @@ export default class TestAbility extends UIAbility { 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); } onDestroy() { diff --git a/gpu_transform/CHANGELOG.md b/gpu_transform/CHANGELOG.md index 01ee816..32abf4a 100644 --- a/gpu_transform/CHANGELOG.md +++ b/gpu_transform/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.0.3 +- 安全编译开启Strip和Ftrapv + +## 1.0.2 +- 支持x86编译 + ## 1.0.1 - 更新libc++sheared.so版本 diff --git a/gpu_transform/README.md b/gpu_transform/README.md index df4a84b..6ef3b6f 100644 --- a/gpu_transform/README.md +++ b/gpu_transform/README.md @@ -68,6 +68,8 @@ 在下述版本验证通过: +- DevEco Studio: (5.0.3.122), SDK: API12 (5.0.0.17) + - DevEco Studio: (4.1.3.414), SDK: API11 (4.1.0.56) ## 贡献代码 diff --git a/gpu_transform/build-profile.json5 b/gpu_transform/build-profile.json5 index 85011b1..f4b0a30 100644 --- a/gpu_transform/build-profile.json5 +++ b/gpu_transform/build-profile.json5 @@ -3,10 +3,11 @@ "buildOption": { "externalNativeOptions": { "path": "./src/main/cpp/CMakeLists.txt", - "arguments": "", + "arguments": " -DCMAKE_BUILD_TYPE=Release", "abiFilters": [ "armeabi-v7a", - "arm64-v8a" + "arm64-v8a", + "x86_64" ], "cppFlags": "" }, diff --git a/gpu_transform/oh-package.json5 b/gpu_transform/oh-package.json5 index af5c636..1eb6bf3 100644 --- a/gpu_transform/oh-package.json5 +++ b/gpu_transform/oh-package.json5 @@ -14,7 +14,7 @@ "main": "index.ets", "repository": "https://gitee.com/openharmony-tpc/ImageKnife", "type": "module", - "version": "1.0.1", + "version": "1.0.3", "tags": [ "Tool" ], diff --git a/gpu_transform/src/main/cpp/CMakeLists.txt b/gpu_transform/src/main/cpp/CMakeLists.txt index 6407f87..cf783b1 100644 --- a/gpu_transform/src/main/cpp/CMakeLists.txt +++ b/gpu_transform/src/main/cpp/CMakeLists.txt @@ -30,4 +30,4 @@ find_library ( GLES-lib GLESv3 ) -target_link_libraries(nativeGpu PUBLIC ${hilog-lib} ${EGL-lib} ${GLES-lib} libace_napi.z.so libc++.a) \ No newline at end of file +target_link_libraries(nativeGpu PUBLIC ${hilog-lib} ${EGL-lib} ${GLES-lib} libace_napi.z.so libc++.a -s -ftrapv) \ No newline at end of file diff --git a/library/PngWork.ets b/library/PngWork.ets new file mode 100644 index 0000000..d6492b8 --- /dev/null +++ b/library/PngWork.ets @@ -0,0 +1,61 @@ +/* + * 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 taskpool from '@ohos.taskpool'; +import { UPNG } from './src/main/ets/components/3rd_party/upng/UPNG' +import { WorkerType } from "./src/main/ets/components/imageknife/pngj/PngCallback" + +export function handler(e: WorkerType, source: ArrayBuffer, pngCallback: (value:ESObject) => void, info?: string) { + let task: taskpool.Task | undefined = undefined + if (info == undefined) { + task = new taskpool.Task(printArgs, e) + } else { + task = new taskpool.Task(printArgs, e, source) + } + let val1: ESObject = taskpool.execute(task) + pngCallback(val1) + try { + taskpool.cancel(task) + } catch (e) { + console.error("taskpool.cancel occur error:" + e) + } +} + +@Concurrent +function printArgs(e: WorkerType, pngSource: ArrayBuffer, info?: string): Object | ArrayBufferLike | void { + let a: Object | ArrayBufferLike | undefined = undefined + let png = UPNG.decode(pngSource) + switch (e.name) { + case 'readPngImageAsync': + let array: Uint8Array = png.data; + let arrayData = array.buffer.slice(array.byteOffset, array.byteLength + array.byteOffset) + png.data = arrayData; + a = png + break; + case 'writePngWithStringAsync': + let addInfo: string | undefined = info; + let newPng = UPNG.encodeWithString(addInfo, UPNG.toRGBA8(png), png.width, png.height, 0) + a = newPng + break; + case 'writePngAsync': + let newPng3 = UPNG.encode(UPNG.toRGBA8(png), png.width, png.height, 0) + a = newPng3 + break; + default: + break + } + if(a != undefined) { return a } +} + + diff --git a/library/PngWork.js b/library/PngWork.js deleted file mode 100644 index 1a86729..0000000 --- a/library/PngWork.js +++ /dev/null @@ -1,47 +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 arkWorker from '@ohos.worker'; -import {UPNG} from './src/main/ets/components/3rd_party/upng/UPNG' - -export function handler (e) { - var data = e.data; - switch (data.type) { - case 'readPngImageAsync': - var png = UPNG.decode(data.data); - let array = png.data; - let arrayData = array.buffer.slice(array.byteOffset, array.byteLength + array.byteOffset) - png.data = arrayData; - let dataObj = { type: 'readPngImageAsync', data: png, receiver: data.data} - arkWorker.parentPort.postMessage(dataObj, [png.data, data.data]); - break; - case 'writePngWithStringAsync': - let addInfo = data.info; - let pngDecode = UPNG.decode(data.data); - let newPng = UPNG.encodeWithString(addInfo, UPNG.toRGBA8(pngDecode), pngDecode.width, pngDecode.height, 0) - let dataObj2 = { type: 'writePngWithStringAsync', data: newPng, receiver: data.data} - arkWorker.parentPort.postMessage(dataObj2, [newPng, data.data]); - break; - case 'writePngAsync': - let pngDecode3 = UPNG.decode(data.data); - let newPng3 = UPNG.encode(UPNG.toRGBA8(pngDecode3), pngDecode3.width, pngDecode3.height, 0) - let dataObj3 = { type: 'writePngAsync', data: newPng3, receiver: data.data} - arkWorker.parentPort.postMessage(dataObj3, [newPng3, data.data]); - break; - default: - break - } -} - - diff --git a/library/index.ets b/library/index.ets index 942b184..5e8d3ca 100644 --- a/library/index.ets +++ b/library/index.ets @@ -21,6 +21,8 @@ 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' @@ -29,6 +31,7 @@ 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' /** * compress @@ -96,10 +99,10 @@ export { UPNG } from './src/main/ets/components/3rd_party/upng/UPNG' 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} from './src/main/ets/components/imageknife/RequestOption' +export {RequestOption,Size,DetachFromLayout,Priority,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} from './src/main/ets/components/imageknife/ImageKnifeOption' +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' @@ -107,6 +110,8 @@ export {IDataFetch} from './src/main/ets/components/imageknife/networkmanage/IDa 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'; /** * svg parse diff --git a/library/oh-package.json5 b/library/oh-package.json5 index ebc85ba..1b8fc01 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.1.2-rc.6", + "version": "2.1.2-rc.12", "dependencies": { "pako": "^2.1.0", - "@ohos/disklrucache": "^2.0.2-rc.0", "@ohos/gpu_transform": "^1.0.0" }, "tags": [ diff --git a/library/src/main/ets/components/cache/CustomSendableMap.ets b/library/src/main/ets/components/cache/CustomSendableMap.ets new file mode 100644 index 0000000..bb33b65 --- /dev/null +++ b/library/src/main/ets/components/cache/CustomSendableMap.ets @@ -0,0 +1,88 @@ +/* + * 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 { collections } from '@kit.ArkTS' +import { DiskCacheEntry } from './DiskCacheEntry'; + +@Sendable +export class CustomSendableMap { + map: collections.Map = new collections.Map() + + // 获取键对应的值 + get(key: string): DiskCacheEntry | undefined{ + if (key == null) { + throw new Error('key is null,checking the parameter'); + } + return this.map.get(key) + } + + /** + * 是否含有key的缓存 + */ + hasKey(key: string):boolean { + if (key == null) { + throw new Error('key is null,checking the parameter'); + } + return this.map.has(key) + } + + // 添加键值对 + put(key: string, value: DiskCacheEntry): DiskCacheEntry | undefined{ + if (key == null || value == null) { + throw new Error('key or value is invalid,checking the parameter'); + } + let pre = this.map.get(key) as DiskCacheEntry | undefined; + if (this.hasKey(key)) { + this.map.delete(key) + } + this.map.set(key, value); + return pre + } + + // 去除键值,(去除键数据中的键名及对应的值) + remove(key: string): boolean { + if (key == null) { + throw new Error('key is null,checking the parameter'); + } + return this.map.delete(key) + } + + /** + * 获取最先存储的数据的key + */ + getFirstKey(): string{ // keys()可以遍历后需要优化put()方法,暂时仅获取index=0的key + return this.map.keys().next().value + } + + // 判断键值元素是否为空 + isEmpty(): boolean{ + return this.map.size == 0; + } + // 获取键值元素大小 + size(): number{ + return this.map.size; + } + // 遍历Map,执行处理函数. 回调函数 function(key,value,index){..} + each(fn: (value: DiskCacheEntry, key: string, map: collections.Map) => void) { + this.map.forEach(fn) + } + // 清除键值对 + clear() { + this.map.clear() + } + // 遍历key + keys(): IterableIterator{ + return this.map.keys() + } +} \ No newline at end of file diff --git a/library/src/main/ets/components/cache/DiskCacheEntry.ets b/library/src/main/ets/components/cache/DiskCacheEntry.ets new file mode 100644 index 0000000..c52cd4f --- /dev/null +++ b/library/src/main/ets/components/cache/DiskCacheEntry.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. + */ +@Sendable +export class DiskCacheEntry { + // 缓存的key + key: string = '' + + // 缓存文件大小 + length: number = 0 + + constructor(key: string, length?: number) { + this.key = key + this.length = length as number + } + + setKey(key: string) { + this.key = key + } + + getKey(): string { + return this.key + } + + setLength(length: number) { + this.length = length + } + + getLength(): number { + return this.length + } + + toString(): string { + return this.key + ' - ' + this.length + } +} \ No newline at end of file diff --git a/library/src/main/ets/components/cache/DiskLruCache.ets b/library/src/main/ets/components/cache/DiskLruCache.ets new file mode 100644 index 0000000..198bc5f --- /dev/null +++ b/library/src/main/ets/components/cache/DiskLruCache.ets @@ -0,0 +1,388 @@ +/* + * 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 { FileUtils } from './FileUtils' +import { DiskCacheEntry } from './DiskCacheEntry' +import { SparkMD5 } from '../3rd_party/sparkmd5/spark-md5' +import common from '@ohos.app.ability.common' +import { CustomSendableMap } from './CustomSendableMap' + +@Sendable +export class DiskLruCache { + // 默认缓存数据最大值 + private static readonly DEFAULT_MAX_SIZE: number = 300 * 1024 * 1024 + // 默认缓存文件名 + private static readonly DEFAULT_NAME: string = 'diskLruCache' + // 缓存文件路径地址 + private path: string = '' + + // 缓存数据最大值 + private maxSize: number = DiskLruCache.DEFAULT_MAX_SIZE + // 当前缓存数据值 + private size: number = 0 + // 缓存数据集合 + private cacheMap: CustomSendableMap = new CustomSendableMap() + + constructor(path: string, maxSize: number) { + this.path = path + this.maxSize = maxSize + } + + /** + * 打开context获取的cache路径中的缓存,如果不存在缓存,则创建新缓存 + * + * @param context 上下文 + * @param maxSize 缓存数据最大值,默认值为300M + */ + public static create(context: common.UIAbilityContext, maxSize?: number): DiskLruCache { + if (!!!context) { + throw new Error('DiskLruCache create context is empty, checking the parameter'); + } + if (!!!maxSize) { + maxSize = DiskLruCache.DEFAULT_MAX_SIZE + } + if (maxSize <= 0) { + throw new Error("DiskLruCache create maxSize <= 0, checking the parameter"); + } + + // 使用默认应用在内部存储上的缓存路径,作为存储地址 + let path = context.cacheDir + FileUtils.SEPARATOR + DiskLruCache.DEFAULT_NAME + if (!FileUtils.getInstance().existFolder(path)) { + FileUtils.getInstance().createFolder(path) + } + if (path.endsWith(FileUtils.SEPARATOR)) { + path = path + } else { + path = path + FileUtils.SEPARATOR + } + return new DiskLruCache(path, maxSize) + } + + /** + * 设置disk缓存最大数据值 + * + * @param max 缓存数据最大值 + */ + setMaxSize(max: number) { + if (max <= 0 || max > DiskLruCache.DEFAULT_MAX_SIZE) { + throw new Error('setMaxSize error, checking the parameter'); + } + this.maxSize = max + this.trimToSize() + } + + /** + * 存储disk缓存数据 + * + * @param key 键值 + * @param content 文件内容 + */ + set(key: string, content: ArrayBuffer | string) { + if (!!!key) { + throw new Error('key is null, checking the parameter') + } + let fileSize = 0; + if (content instanceof ArrayBuffer) { + if (content == null || content.byteLength == 0) { + throw new Error('content is null. checking the parameter') + } + fileSize = content.byteLength + } else { + if (!!!content) { + throw new Error('content is null, checking the parameter') + } + fileSize = content.length; + } + if (this.fileSizeMoreThenMaxSize(fileSize)) { + throw new Error('content must be less then DiskLruCache Size, checking the parameter') + return + } + key = SparkMD5.hashBinary(key) + this.size = this.size + fileSize + this.putCacheMap(key, fileSize) + this.trimToSize() + let tempPath = this.path + key + FileUtils.getInstance().writeNewFile(tempPath, content) + } + + /** + * 异步存储disk缓存数据 + * + * @param key 键值 + * @param content 文件内容 + */ + async setAsync(key: string, content: ArrayBuffer | string): Promise { + if (!!!key) { + throw new Error('key is null, checking the parameter') + } + let fileSize = 0; + if (content instanceof ArrayBuffer) { + if (content == null || content.byteLength == 0) { + throw new Error('content is null. checking the parameter') + } + fileSize = content.byteLength + } else { + if (!!!content) { + throw new Error('content is null, checking the parameter') + } + fileSize = content.length; + } + if (this.fileSizeMoreThenMaxSize(fileSize)) { + throw new Error('content must be less then DiskLruCache Size, checking the parameter') + return + } + key = SparkMD5.hashBinary(key) + this.size = this.size + fileSize + this.putCacheMap(key, fileSize) + this.trimToSize() + let tempPath = this.path + key + await FileUtils.getInstance().writeNewFileAsync(tempPath, content) + } + + /** + * 获取key缓存数据 + * + * @param key key 键值 + */ + get(key: string): ArrayBuffer | undefined { + if (!!!key) { + throw new Error('key is null,checking the parameter'); + } + key = SparkMD5.hashBinary(key) + let path = this.path + key; + if (FileUtils.getInstance().exist(path)) { + let ab: ArrayBuffer = FileUtils.getInstance().readFile(path) + this.putCacheMap(key, ab.byteLength) + return ab + } else { + return undefined; + } + } + + /** + * 异步获取key缓存数据 + * + * @param key 键值 + */ + async getAsync(key: string): Promise { + if (!!!key) { + throw new Error('key is null,checking the parameter'); + } + key = SparkMD5.hashBinary(key) + let path = this.path + key; + if (FileUtils.getInstance().exist(path)) { + let ab: ArrayBuffer = await FileUtils.getInstance().readFileAsync(path) + this.putCacheMap(key, ab.byteLength) + return ab + } else { + return undefined; + } + } + + /** + * 获取key缓存数据绝对路径 + * + * @param key 键值 + */ + getFileToPath(key: string): string { + if (!!!key) { + throw new Error('key is null,checking the parameter'); + } + key = SparkMD5.hashBinary(key); + let path = this.path + key; + if (FileUtils.getInstance().exist(path)) { + return path + } else { + return ""; + } + } + + /** + * 异步获取key缓存数据绝对路径 + * + * @param key 键值 + */ + async getFileToPathAsync(key: string): Promise { + if (!!!key) { + throw new Error('key is null,checking the parameter'); + } + key = SparkMD5.hashBinary(key); + let path = this.path + key; + if (FileUtils.getInstance().exist(path)) { + return path + } else { + return "" + } + } + + /** + * 获取缓存路径 + */ + getPath(): string { + return this.path; + } + + /** + * 删除key缓存数据 + * + * @param key 键值 + */ + deleteCacheDataByKey(key: string): DiskCacheEntry { + if (!!!key) { + throw new Error('key is null,checking the parameter'); + } + key = SparkMD5.hashBinary(key) + let path = this.path + key; + if (FileUtils.getInstance().exist(path)) { + let ab = FileUtils.getInstance().readFile(path) + this.size = this.size - ab.byteLength + this.cacheMap.remove(key) + FileUtils.getInstance().deleteFile(path) + } + return this.cacheMap.get(key) as DiskCacheEntry; + } + + /** + *遍历当前的磁盘缓存数据 + * + * @param fn 遍历后方法回调 + */ + foreachDiskLruCache(fn: Function) { + this.cacheMap.each(fn()) + } + + /** + * 清除所有disk缓存数据 + */ + cleanCacheData() { + this.cacheMap.each((value, key) => { + FileUtils.getInstance().deleteFile(this.path + key) + }) + this.cacheMap.clear() + this.size = 0 + } + + getCacheMap() { + return this.cacheMap; + } + + /** + * 返回当前DiskLruCache的size大小 + */ + getSize() { + return this.size; + } + + /** + * 缓存数据map集合 + * + * @param key 键值 + * @param length 缓存文件大小 + */ + private putCacheMap(key: string, length?: number) { + if (length && length > 0) { + this.cacheMap.put(key, new DiskCacheEntry(key, length)) + } else { + this.cacheMap.put(key, new DiskCacheEntry(key)) + } + } + + /** + * 根据LRU算法删除多余缓存数据 + */ + private trimToSize() { + while (this.size > this.maxSize) { + let tempKey: string = this.cacheMap.getFirstKey() + let fileSize = FileUtils.getInstance().getFileSize(this.path + tempKey) + if (fileSize > 0) { + this.size = this.size - fileSize + } + FileUtils.getInstance().deleteFile(this.path + tempKey) + this.cacheMap.remove(tempKey) + } + } + + /** + * 图片文件过大 直接超过DiskLruCache上限 + */ + private fileSizeMoreThenMaxSize(fileSize: number): boolean { + if (fileSize > this.maxSize) { + return true; + } + return false; + } + + /** + * 子线程里只写入缓存文件 + * @param context + * @param key + * @param value + */ + static saveFileCacheOnlyFile(path: string, key: string, value: ArrayBuffer): boolean { + // 写文件 + if (!!!key) { + throw new Error('key is null, checking the parameter') + } + key = SparkMD5.hashBinary(key); + FileUtils.getInstance().writeNewFile(path + key, value); + return true + } + + /** + * 子线程中,通过文件名,直接查找是否有文件缓存 + * @param context + * @param key + * @returns + */ + static getFileCacheByFile(path: string, key: string): ArrayBuffer | undefined { + // 从文件获取查看是否有缓存 + if (!!!key) { + throw new Error('key is null,checking the parameter'); + } + key = SparkMD5.hashBinary(key) + let filepath = path + key; + if (FileUtils.getInstance().exist(filepath)) { + let ab: ArrayBuffer = FileUtils.getInstance().readFile(filepath) + return ab + } else { + return undefined; + } + } + + // 添加缓存键值对,但不写文件(用于子线程已经写文件的场景) + setCacheMapAndSize(key: string, content: ArrayBuffer | string): void { + if (!!!key) { + throw new Error('key is null, checking the parameter') + } + let fileSize = 0; + if (content instanceof ArrayBuffer) { + if (content == null || content.byteLength == 0) { + throw new Error('content is null. checking the parameter') + } + fileSize = content.byteLength + } else { + if (!!!content) { + throw new Error('content is null, checking the parameter') + } + fileSize = content.length; + } + if (this.fileSizeMoreThenMaxSize(fileSize)) { + throw new Error('content must be less then DiskLruCache Size, checking the parameter') + } + key = SparkMD5.hashBinary(key); + this.size = this.size + fileSize; + this.putCacheMap(key, fileSize); + this.trimToSize(); + } +} \ No newline at end of file diff --git a/library/src/main/ets/components/cache/FileReader.ets b/library/src/main/ets/components/cache/FileReader.ets new file mode 100644 index 0000000..5c00198 --- /dev/null +++ b/library/src/main/ets/components/cache/FileReader.ets @@ -0,0 +1,94 @@ +/* + * 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 { RequestOption } from '../imageknife/RequestOption'; + +export class FileReader { + // 换行符 + static readonly LF: string = '\n' + // CR符 + static readonly CR: string = '\r' + // 文件大小 + fileLength: number = 0 + // 读取的长度 + length: number = 0 + // 读写stream + stream: fs.Stream + // 缓存buf + buf: ArrayBuffer = new ArrayBuffer(1) + + /** + * 读取文件行 + * + * @param path 文件路径 + */ + constructor(path: string) { + if (!!!path) { + throw new Error('FileReader constructor path is null, checking the parameter') + return; + } + this.stream = fs.createStreamSync(path, 'r+'); + let stat = fs.statSync(path) + this.fileLength = stat.size + } + + /** + * 循环读取文件数据 + */ + readLine(): string { + let line = '' + while (this.stream && this.length < this.fileLength) { + this.stream?.readSync(this.buf, { offset: this.length }) + this.length++ + let temp = String.fromCharCode(...new Uint8Array(this.buf)); + line = line + temp + // check CRLF + if (temp == FileReader.CR) { + // 边界判断 首先拿到下一个字符判断是否是LF 如果是CRLF需要再向后挪一位 + if (this.length < this.fileLength) { + let nextBuf = new ArrayBuffer(1) + this.stream?.readSync(nextBuf, { offset: this.length }) + let nextTemp = String.fromCharCode(...new Uint8Array(nextBuf)); + // 如果是CRLF 需要给当前length+1 向后挪一位 + if (nextTemp == FileReader.LF) { + this.length++ + } + } + // 如果不是CRLF 只有一个CR结尾length不用变 + return line; + } else { + // 判断LF情况 + if (temp == FileReader.LF) { + return line + } + } + } + return line + } + + /** + * 判断文件是否结束 + */ + isEnd() { + return this.fileLength <= 0 || this.length == this.fileLength + } + + /** + * 关闭stream + */ + close() { + this.stream?.closeSync() + } +} \ No newline at end of file diff --git a/library/src/main/ets/components/cache/FileUtils.ets b/library/src/main/ets/components/cache/FileUtils.ets index 8f2ae0e..8e9d926 100644 --- a/library/src/main/ets/components/cache/FileUtils.ets +++ b/library/src/main/ets/components/cache/FileUtils.ets @@ -16,6 +16,7 @@ import fs from '@ohos.file.fs'; import { BusinessError } from '@ohos.base' export class FileUtils { + static readonly SEPARATOR: string = '/' base64Str: string = '' private static sInstance: FileUtils; @@ -32,13 +33,21 @@ export class FileUtils { /** * 新建文件 + * * @param path 文件绝对路径及文件名 * @return number 文件句柄id */ createFile(path: string): number { - return fs.openSync(path, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE).fd + let num = -1; + try { + num = fs.openSync(path, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE).fd + } catch (e) { + console.log("createFile err :" + e) + } + return num; } + /** * 删除文件 * @param path 文件绝对路径及文件名 @@ -143,16 +152,20 @@ export class FileUtils { /** * 判断path文件是否存在 + * + * @param path 文件绝对路径 */ exist(path: string): boolean { try { - let stat = fs.statSync(path) - return stat.isFile() - } catch (e) { - console.debug("FileUtils - fileutils exsit e" + e) - console.log("path=>" + path) - return false + if (fs.accessSync(path)) { + let stat = fs.statSync(path) + return stat.isFile() + } + } catch (error) { + let err: BusinessError = error as BusinessError; + console.error("accessSync failed with error message: " + err.message + ", error code: " + err.code); } + return false } /** @@ -285,7 +298,7 @@ export class FileUtils { let stat = fs.statSync(path) return stat.isDirectory() } catch (e) { - console.debug("fileutils folder exsit error=" + e) + console.debug("fileutils folder exist error=" + e) return false } } @@ -318,9 +331,84 @@ export class FileUtils { return tmpUint8Array } + /** + * 异步向path写入数据 + * + * @param path 文件绝对路径 + * @param content 文件内容 + */ + async writeDataAsync(path: string, content: ArrayBuffer | string): Promise { + 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) + } + + /** + * 异步拷贝文件 + * + * @param src 文件绝对路径及文件名 + * @param dest 拷贝到对应的路径 + */ + async copyFileAsync(src: string, dest: string): Promise { + await fs.copyFile(src, dest); + } + + /** + * 读取路径path的文件 + * + * @param path 文件绝对路径 + */ + async readFileAsync(path: string): Promise { + let stat = await fs.stat(path); + let fd = (await fs.open(path, fs.OpenMode.READ_WRITE)).fd; + let length = stat.size; + let buf = new ArrayBuffer(length); + await fs.read(fd, buf); + return buf + } + + /** + * 向path写入数据 + * + * @param path 文件绝对路径 + * @param data 文件内容 + */ + async writeNewFileAsync(path: string, data: ArrayBuffer | string): Promise { + let fd = (await fs.open(path, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)).fd + await fs.truncate(fd) + await fs.write(fd, data) + await fs.fsync(fd) + await fs.close(fd) + } uint8ArrayToBuffer(array: Uint8Array): ArrayBuffer { return array.buffer.slice(array.byteOffset, array.byteLength + array.byteOffset) } + + /** + * 向path写入数据 + * + * @param path 文件绝对路径 + * @param data 文件内容 + */ + writeNewFile(path: string, data: ArrayBuffer | string) { + this.createFile(path) + this.writeFile(path, data) + } + + /** + * 读取路径path的文件 + * + * @param path 文件绝对路径 + */ + readFile(path: string): ArrayBuffer { + let fd = fs.openSync(path, fs.OpenMode.READ_WRITE).fd; + let length = fs.statSync(path).size + let buf = new ArrayBuffer(length); + fs.readSync(fd, buf) + return buf + } + } diff --git a/library/src/main/ets/components/imageknife/Downsampling/BaseDownsampling.ets b/library/src/main/ets/components/imageknife/Downsampling/BaseDownsampling.ets new file mode 100644 index 0000000..317587f --- /dev/null +++ b/library/src/main/ets/components/imageknife/Downsampling/BaseDownsampling.ets @@ -0,0 +1,2 @@ +/* + * asdfasdfasdfasdfasdf*/ \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/Downsampling/DownsampleStartegy.ets b/library/src/main/ets/components/imageknife/Downsampling/DownsampleStartegy.ets new file mode 100644 index 0000000..87de8a4 --- /dev/null +++ b/library/src/main/ets/components/imageknife/Downsampling/DownsampleStartegy.ets @@ -0,0 +1,2 @@ +/* + * dsasdafasdfasdf*/ \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/Downsampling/Downsampler.ets b/library/src/main/ets/components/imageknife/Downsampling/Downsampler.ets new file mode 100644 index 0000000..317587f --- /dev/null +++ b/library/src/main/ets/components/imageknife/Downsampling/Downsampler.ets @@ -0,0 +1,2 @@ +/* + * asdfasdfasdfasdfasdf*/ \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/ImageKnife.ets b/library/src/main/ets/components/imageknife/ImageKnife.ets index 6447ff4..3e784a1 100644 --- a/library/src/main/ets/components/imageknife/ImageKnife.ets +++ b/library/src/main/ets/components/imageknife/ImageKnife.ets @@ -13,10 +13,10 @@ * limitations under the License. */ -import { DiskLruCache } from "@ohos/disklrucache" +import { DiskLruCache } from "../cache/DiskLruCache" import { EngineKeyFactories } from "../cache/key/EngineKeyFactories" import { EngineKeyInterface } from "../cache/key/EngineKeyInterface" -import { RequestOption } from "../imageknife/RequestOption" +import { CacheType, RequestOption, Size } from "../imageknife/RequestOption" import { AsyncCallback } from "../imageknife/interface/AsyncCallback" import { PlaceHolderManager } from "../imageknife/holder/PlaceHolderManager" import { RetryHolderManager } from "../imageknife/holder/RetryHolderManager" @@ -31,30 +31,46 @@ import { IResourceFetch } from '../imageknife/resourcemanage/IResourceFetch' import { ImageKnifeData, ImageKnifeType } from '../imageknife/ImageKnifeData' import { ImageKnifeGlobal } from '../imageknife/ImageKnifeGlobal' import image from "@ohos.multimedia.image" +import http from '@ohos.net.http' import { CompressBuilder } from "../imageknife/compress/CompressBuilder" import { IDrawLifeCycle } from '../imageknife/interface/IDrawLifeCycle' import { LogUtil } from '../imageknife/utils/LogUtil' -import { EasyLinkedHashMap } from './utils/base/EasyLinkedHashMap' import { MethodMutex } from './utils/base/MethodMutex' import worker from '@ohos.worker' import common from '@ohos.app.ability.common' -import HashMap from '@ohos.util.HashMap' -import LinkedList from '@ohos.util.LinkedList' import { MemoryLruCache } from '../cache/MemoryLruCache' +import { BusinessError } from '@ohos.base' +import taskpool from '@ohos.taskpool' +import { DefaultJobQueue } from './utils/DefaultJobQueue'; +import { IJobQueue } from './utils/IJobQueue'; +import LightWeightMap from '@ohos.util.LightWeightMap'; +import List from '@ohos.util.List'; +import { GIFFrame } from './utils/gif/GIFFrame' +import emitter from '@ohos.events.emitter'; + +import { MemoryCacheProxy } from './requestmanage/MemoryCacheProxy' +import { ObjectKey } from './ObjectKey' +import { Constants } from './constants/Constants' +import { TransformUtils } from './transform/TransformUtils' +import { GIFParseImpl } from './utils/gif/GIFParseImpl' +import { IParseImage } from './interface/IParseImage' +import { ParseImageUtil } from './utils/ParseImageUtil' +import { SVGParseImpl } from './utils/svg/SVGParseImpl' +import { SendableData } from './SendableData' +import { collections } from '@kit.ArkTS' +import { TaskParams } from './TaskParams' +import { MResource } from './utils/MResource' export class ImageKnife { static readonly SEPARATOR: string = '/' memoryCache: MemoryLruCache; - diskMemoryCache: DiskLruCache; dataFetch: IDataFetch; resourceFetch: IResourceFetch; filesPath: string = ""; // data/data/包名/files目录 - - + diskMemoryCache: DiskLruCache; + memoryCacheProxy: MemoryCacheProxy = new MemoryCacheProxy(new MemoryLruCache(100, 100 * 1024 * 1024)); + headerMap: Map = new Map(); //定义全局map placeholderCache: string = "placeholderCache" - runningMaps: EasyLinkedHashMap; - pendingMaps: EasyLinkedHashMap; - pausedMaps: EasyLinkedHashMap; isPaused: boolean = false; mutex: MethodMutex = new MethodMutex(); fileTypeUtil: FileTypeUtil; // 通用文件格式辨别 @@ -70,18 +86,19 @@ export class ImageKnife { defaultLifeCycle: IDrawLifeCycle | undefined = undefined; // 开发者可配置全局缓存 engineKeyImpl: EngineKeyInterface; + private mParseImageUtil: IParseImage; + // 最大并发 + maxRequests: number = 64; + // 排队队列 + private jobQueue: IJobQueue = new DefaultJobQueue(); + // 执行中的请求 + private executingJobMap: LightWeightMap> = new LightWeightMap(); private constructor() { - - this.runningMaps = new EasyLinkedHashMap(); - this.pendingMaps = new EasyLinkedHashMap(); - this.pausedMaps = new EasyLinkedHashMap(); + this.mParseImageUtil = new ParseImageUtil(); // 构造方法传入size 为保存文件个数 - this.memoryCache = new MemoryLruCache(100,100*1024*1024); - - // 创建disk缓存 传入的size 为多少比特 比如20KB 传入20*1024 - this.diskMemoryCache = DiskLruCache.create(ImageKnifeGlobal.getInstance().getHapContext()); + this.memoryCache = new MemoryLruCache(100, 100 * 1024 * 1024); // 创建网络下载能力 this.dataFetch = new DownloadClient(); @@ -92,18 +109,33 @@ export class ImageKnife { // 初始化本地 文件保存 this.filesPath = (ImageKnifeGlobal.getInstance().getHapContext() as common.UIAbilityContext).filesDir as string; + this.diskMemoryCache = DiskLruCache.create(ImageKnifeGlobal.getInstance() + .getHapContext() as common.UIAbilityContext); + // 通用文件格式识别初始化 this.fileTypeUtil = new FileTypeUtil(); this.engineKeyImpl = new EngineKeyFactories(); + } + //全局设置请求头调用方法 + addHeader(key: string, value: Object) { + this.headerMap.set(key, value); + } + + deleteHeader(key: string) { + this.headerMap.delete(key); } getMemoryCache(): MemoryLruCache { return this.memoryCache; } + getMemoryCacheProxy(): MemoryCacheProxy { + return this.memoryCacheProxy; + } + public static with(context: Object): ImageKnifeGlobal { // 存入hapContext; let global: ImageKnifeGlobal = ImageKnifeGlobal.getInstance(); @@ -174,13 +206,12 @@ export class ImageKnife { return new CompressBuilder(); } - // 设置缓存张数,缓存大小,单位字节 - public setLruCacheSize(size: number,memory:number) { + public setLruCacheSize(size: number, memory: number) { if (this.memoryCache.map.size() <= 0) { - this.memoryCache = new MemoryLruCache(size,memory); + this.memoryCache = new MemoryLruCache(size, memory); } else { - let newLruCache = new MemoryLruCache(size,memory); + let newLruCache = new MemoryLruCache(size, memory); this.memoryCache.foreachLruCache((value: ImageKnifeData, key: string, map: Object) => { newLruCache.put(key, value); }) @@ -192,17 +223,19 @@ export class ImageKnife { this.dataFetch = fetch; } + // 替代原来的DiskLruCache public replaceDiskLruCache(size: number) { - if (this.diskMemoryCache.getCacheMap().size() <= 0) { - this.diskMemoryCache = DiskLruCache.create(ImageKnifeGlobal.getInstance().getHapContext(), size); - } else { - let newDiskLruCache = DiskLruCache.create(ImageKnifeGlobal.getInstance().getHapContext(), size); - this.diskMemoryCache.foreachDiskLruCache((value: string | ArrayBuffer, key: string, map: Object) => { - newDiskLruCache.set(key, value); - }) - this.diskMemoryCache = newDiskLruCache; - } + this.diskMemoryCache.setMaxSize(size) + // if (this.diskMemoryCache.getCacheMap().size() <= 0) { + // this.diskMemoryCache = DiskLruCache.create(ImageKnifeGlobal.getInstance().getHapContext(), size); + // } else { + // let newDiskLruCache = DiskLruCache.create(ImageKnifeGlobal.getInstance().getHapContext(), size); + // this.diskMemoryCache.foreachDiskLruCache((value: string | ArrayBuffer, key: string, map: Object) => { + // newDiskLruCache.set(key, value); + // }) + // this.diskMemoryCache = newDiskLruCache; + // } } // 预加载 resource资源一级缓存,string资源实现二级缓存 @@ -216,28 +249,6 @@ export class ImageKnife { async pauseRequests(): Promise { await this.mutex.lock(async () => { this.isPaused = true; - - // 将未删除的所有request [run pend] 放入 [pause] - LogUtil.log('dodo pauseRequests start1 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size()) - - // 将run存入pause - let runNode = this.runningMaps.getHead(); - while (runNode) { - let request = runNode.value; - this.pausedMaps.put(request.uuid, request) - runNode = runNode.next; - } - this.runningMaps.clear(); - LogUtil.log('dodo pauseRequests start2 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size()) - - let pendNode = this.pendingMaps.getHead(); - while (pendNode) { - let request = pendNode.value; - this.pausedMaps.put(request.uuid, request) - pendNode = pendNode.next - } - this.pendingMaps.clear() - LogUtil.log('dodo pauseRequests start3 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size()) }) } @@ -245,28 +256,19 @@ export class ImageKnife { async resumeRequests(): Promise { await this.mutex.lock(async () => { this.isPaused = false; - LogUtil.log('dodo resumeRequest start1 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size()) - // 重启了之后需要把paused 里面的所有request重新发送 - let headNode = this.pausedMaps.getHead(); - while (headNode) { - let request = headNode.value - this.loadCacheManager(request) - headNode = headNode.next + if (this.executingJobMap.length > 0) { + this.executingJobMap.forEach((list: List) => { + this.taskpoolLoadResource(list.get(0), Constants.MAIN_HOLDER); + }) + } else { + this.dispatchNextJob(); } - this.pausedMaps.clear() - LogUtil.log('dodo resumeRequest start1 pausedMaps size=' + this.pausedMaps.size() + ' runMaps Size=' + this.runningMaps.size() + ' pendMaps Size=' + this.pendingMaps.size()) }) } // 删除 请求 remove(uuid: string) { - if (this.isPaused) { - this.pausedMaps.remove(uuid) - } else { - // TODO 哪些请求可以删除 - this.pendingMaps.remove(uuid) - this.runningMaps.remove(uuid) - } + this.executingJobMap.remove(uuid); } // 正常加载 @@ -279,27 +281,132 @@ export class ImageKnife { // 每个request 公共信息补充 request.setFilesPath(this.filesPath); + if (this.headerMap.size > 0) { + request.addHeaderMap(this.headerMap) + } + + this.generateDataCacheKey(request) // 首先执行占位图 解析任务 if (request.placeholderSrc) { - PlaceHolderManager.execute(request) + this.taskpoolLoadResource(request, Constants.PLACE_HOLDER); } // 其次执行重试占位图 解析任务 if (request.retryholderSrc) { - RetryHolderManager.execute(request) + this.taskpoolLoadResource(request, Constants.RETRY_HOLDER); } // 最后解析错误占位图 if (request.errorholderSrc) { - ErrorHolderManager.execute(request) + this.taskpoolLoadResource(request, Constants.ERROR_HOLDER); } return this.parseSource(request); } - loadResources(request: RequestOption) { + + public isUrlExist(url: string, cacheType: CacheType = CacheType.Default, size: Size = { + width: 0, + height: 0 + }): Promise { + + return new Promise((resolve, reject) => { + let request = new RequestOption(); + request.load(url) + .setImageViewSize(size) + this.generateDataCacheKey(request); + let loadComplete = (imageKnifeData: ImageKnifeData) => { + resolve(imageKnifeData); + } + let loadError = (err ?: BusinessError | string) => { + if (cacheType == CacheType.Default) { + this.loadMemoryDiskIsUrl(request, loadComplete, loadError); + } + reject(err); + } + + if (cacheType == CacheType.Cache) { + this.loadMemoryCacheIsUrl(request, loadComplete, loadError); + + } else if (cacheType == CacheType.Disk) { + this.loadMemoryDiskIsUrl(request, loadComplete, loadError); + } + else if (cacheType == CacheType.Default) { + this.loadMemoryCacheIsUrl(request, loadComplete, loadError); + } + }) + } + + + loadMemoryCacheIsUrl(request: RequestOption, onComplete: (imageKnifeData: ImageKnifeData) => void | PromiseLike, onError: (err?: BusinessError | string) => void) { + let cache = this.memoryCacheProxy.loadMemoryCache(request.generateCacheKey, true); + if (cache == null || typeof cache == 'undefined') { + onError("No data in cache!") + } else { + cache.waitSaveDisk = false; + //2.网络缓存有数据,返回 + onComplete(cache); + } + } + + loadMemoryDiskIsUrl(request: RequestOption, onComplete: (imageKnifeData: ImageKnifeData) => void | PromiseLike, onError: (err?: BusinessError | string) => void) { + let cached = this.diskMemoryCache.get(request.generateDataKey); + if (cached != null) { + let filetype: string | null = this.fileTypeUtil.getFileType(cached); + + if (filetype == null) { + onError('请检查数据源'); + return; + } + + if (!this.fileTypeUtil.isImage(cached)) { + onError('暂不支持的类型!类型=' + filetype); + } + + if ((ImageKnifeData.GIF == filetype || ImageKnifeData.WEBP == filetype) && !request.dontAnimateFlag) { + let gifParseImpl = new GIFParseImpl() + gifParseImpl.parseGifs(cached, (data?: GIFFrame[], err?: BusinessError | string) => { + if (err) { + onError(err) + } + LogUtil.log("gifProcess data is null:" + (data == null)); + if (!!data) { + let imageKnifeData = ImageKnifeData.createImageGIFFrame(ImageKnifeType.GIFFRAME, data); + LogUtil.log('gifProcess 生成gif 返回数据类型') + onComplete(imageKnifeData) + } else { + onError('Parse GIF callback data is null, you need check callback data!') + } + }) + + } else if (ImageKnifeData.SVG == filetype) { + let svgParseImpl = new SVGParseImpl() + let success = (value: PixelMap) => { + let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, value); + onComplete(imageKnifeData); + } + svgParseImpl.parseSvg(request, cached, success, onError) + } else { + //5.磁盘有数据,解析错误返回onError + let success = (value: PixelMap) => { + let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, value); + onComplete(imageKnifeData); + } + this.mParseImageUtil.parseImage(cached, success, onError) + } + + } else { + //6.磁盘无数据,返回onError + onError("No data in disk cache!") + } + + } + + + generateDataCacheKey(request: RequestOption) { let factories: EngineKeyInterface; let cacheKey: string; let transferKey: string; let dataKey: string; + //设置全局缓存key if (this.engineKeyImpl) { factories = this.engineKeyImpl; } else { @@ -331,133 +438,265 @@ export class ImageKnife { let signature = request.signature; - cacheKey = factories.generateMemoryCacheKey(loadKey,size,transformed,dontAnimateFlag,signature); + cacheKey = factories.generateMemoryCacheKey(loadKey, size, transformed, dontAnimateFlag, signature); // 生成磁盘缓存变换后数据key 变换后数据保存在磁盘 - transferKey = factories.generateTransformedDiskCacheKey(loadKey,size,transformed,dontAnimateFlag,signature); + transferKey = factories.generateTransformedDiskCacheKey(loadKey, size, transformed, dontAnimateFlag, signature); // 生成磁盘缓存源数据key 原始数据保存在磁盘 - dataKey = factories.generateOriginalDiskCacheKey(loadKey,signature); - + dataKey = factories.generateOriginalDiskCacheKey(loadKey, signature); + if (request.placeholderSrc) { + let placeholderLoadKey = ''; + if (typeof request.placeholderSrc == 'string') { + placeholderLoadKey = request.placeholderSrc; + // string类型占位图生成内存缓存源数据key + request.placeholderRegisterMemoryCacheKey = factories.generateMemoryCacheKey(placeholderLoadKey, size, transformed, dontAnimateFlag, signature); + // string类型占位图生成磁盘缓存源数据key 原始数据保存在磁盘 + request.placeholderRegisterCacheKey = factories.generateOriginalDiskCacheKey(placeholderLoadKey, signature); + } else { + placeholderLoadKey = JSON.stringify(request.placeholderSrc); + request.placeholderCacheKey = this.generateCacheKey(placeholderLoadKey, size, dontAnimateFlag, signature); + } + } + if (request.retryholderSrc) { + let retryholderLoadKey = ''; + if (typeof request.retryholderSrc == 'string') { + retryholderLoadKey = request.retryholderSrc; + } else { + retryholderLoadKey = JSON.stringify(request.retryholderSrc); + } + request.retryholderCacheKey = this.generateCacheKey(retryholderLoadKey, size, dontAnimateFlag, signature) + } + if (request.errorholderSrc) { + let errorholderLoadKey = ''; + if (typeof request.errorholderSrc == 'string') { + errorholderLoadKey = request.errorholderSrc; + } else { + errorholderLoadKey = JSON.stringify(request.errorholderSrc); + } + request.errorholderCacheKey = this.generateCacheKey(errorholderLoadKey, size, dontAnimateFlag, signature) + } request.generateCacheKey = cacheKey; request.generateResourceKey = transferKey; request.generateDataKey = dataKey; + } - this.loadCacheManager(request); + private generateCacheKey(loadkey: string, size: string, dontAnimateFlag: boolean, signature?: ObjectKey) { + let factories: EngineKeyInterface; + + //设置全局缓存key + if (this.engineKeyImpl) { + factories = this.engineKeyImpl; + } else { + factories = new EngineKeyFactories(); + } + return factories.generateMemoryCacheKey(loadkey, size, '', dontAnimateFlag, signature); } // 删除执行结束的running removeRunning(request: RequestOption) { - if (this.isPaused) { - - } else { - this.runningMaps.remove(request.uuid); - console.log('dodo runningMaps length =' + this.runningMaps.size()) - let previousRequest = request; - this.loadNextPending(previousRequest); - } - } - - // 执行相同key的pending队列请求 - private keyEqualPendingToRun(nextPending: RequestOption) { - - - - - this.pendingMaps.remove(nextPending.uuid) - this.runningMaps.put(nextPending.uuid, nextPending); - - RequestManager.execute((nextPending as RequestOption), this.memoryCache, this.diskMemoryCache, this.dataFetch, this.resourceFetch) - - } - - private searchNextKeyToRun() { - // 其次则寻找pending中第一个和running不重复的requestOrKey - let pendingTailNode = this.pendingMaps.getTail(); - while (pendingTailNode) { - let pendingRequest = pendingTailNode.value; - let hasEqual = false; - let runningTailNode = this.runningMaps.getTail(); - while (runningTailNode) { - let runningRequest = runningTailNode.value; - if (this.requestOrKeyEqual(pendingRequest, runningRequest)) { - hasEqual = true; - break - } - runningTailNode = runningTailNode.prev; - } - - if (hasEqual) { - break; - } - pendingTailNode = pendingTailNode.prev; - } - - if (pendingTailNode != null && pendingTailNode.value != null) { - let nextPending = pendingTailNode.value; - this.runningMaps.put(nextPending.uuid, nextPending) - this.pendingMaps.remove(nextPending.uuid) - RequestManager.execute((nextPending as RequestOption), this.memoryCache, this.diskMemoryCache, this.dataFetch, this.resourceFetch) - } - } - - - // 加载下一个key的请求 - private loadNextPending(request: RequestOption) { - let hasEqualRunning = false; - let tailNode = this.pendingMaps.getTail(); - while (tailNode) { - if (this.requestOrKeyEqual(request, tailNode.value)) { - hasEqualRunning = true; - break; - } - tailNode = tailNode.prev; - } - - if (hasEqualRunning) { - if(tailNode != null && tailNode.value != null) { - this.keyEqualPendingToRun(tailNode.value); - } - } else { - this.searchNextKeyToRun(); + if (!this.isPaused) { + //不暂停则继续加载 + this.executingJobMap.remove(request.uuid); + this.dispatchNextJob(); } } // 启动新线程 去磁盘取 去网络取 private loadCacheManager(request: RequestOption) { - if (this.isPaused) { - // 将当前request存入pausedMaps - this.pausedMaps.put(request.uuid, request); - } else { - // 正常逻辑 - if (this.keyNotEmpty(request)) { - let hasRunningRequest = false; - // 遍历双向链表 从尾巴到头 - let tailNode = this.runningMaps.getTail(); - while (tailNode) { - if (this.requestOrKeyEqual(request, tailNode.value)) { - hasRunningRequest = true; - break; - } - tailNode = tailNode.prev - } - - if (hasRunningRequest) { - this.pendingMaps.put(request.uuid, request); - - - } else { - this.runningMaps.put(request.uuid, request) - - - // 不存在相同key的 任务可以并行 - RequestManager.execute(request, this.memoryCache, this.diskMemoryCache, this.dataFetch, this.resourceFetch) - } + if (this.keyNotEmpty(request)) { + if (this.executingJobMap.length > this.maxRequests) { + this.jobQueue.add(request); + return } - else { - LogUtil.log("key没有生成无法进入存取!") + let requestList: List | undefined = this.executingJobMap.get(request.uuid); + if (requestList == undefined) { + requestList = new List(); + requestList.add(request); + this.executingJobMap.set(request.uuid, requestList); + if (!this.isPaused) { + LogUtil.log("loadCacheManager start uuid : " + request.uuid + " url : " + request.loadSrc); + //暂停则不开启加载 + this.taskpoolLoadResource(request, Constants.MAIN_HOLDER); + } + } else { + requestList.add(request); + this.executingJobMap.set(request.uuid, requestList); + LogUtil.log("loadCacheManager reuse uuid : " + request.uuid + " url : " + request.loadSrc); + return; + } + } else { + LogUtil.log("key没有生成无法进入存取!"); + } + } + + private assembleSendableData(request: RequestOption, usageType: string){ + //图片变换方法无法直接传递到子线程,这里先把对象名和构造参数传递到子线程,然后在子线程中实例化变换方法 + + let transformations: collections.Array> = new collections.Array>(); + + if (usageType == Constants.MAIN_HOLDER) { + for (let i = 0; i < request.transformations.length; i++) { + let item = collections.Array.from([request.transformations[i].getClassName(), request.transformations[i].getConstructorParams()]) as collections.Array; + transformations.push(item) } } + let displayProgress = request.progressFunc ? true : false; + + let data: SendableData = new SendableData(); + data.setUsageType(usageType) + data.setDisplayProgress(displayProgress) + data.setUuid(request.uuid) + data.setDontAnimateFlag(request.dontAnimateFlag) + data.setThumbSizeMultiplier(request.thumbSizeMultiplier) + data.setThumbDelayTime(request.thumbDelayTime) + data.setOnlyRetrieveFromCache(request.onlyRetrieveFromCache) + data.setIsCacheable(request.isCacheable) + data.setGpuEnabled(request.gpuEnabled) + data.setGenerateCacheKey(request.generateCacheKey) + data.setGenerateResourceKey(request.generateResourceKey) + data.setGenerateDataKey(request.generateDataKey) + data.setSignature(request.signature) + data.setTransformations(transformations) + data.setDiskMemoryCache(this.diskMemoryCache) + data.setDataFetch(this.dataFetch) + data.setDiskMemoryCachePath(this.diskMemoryCache.getPath()) + data.setPlaceHolderRegisterCacheKey(request.placeholderRegisterCacheKey); + data.setPlaceHolderRegisterMemoryCacheKey(request.placeholderRegisterMemoryCacheKey); + return data; + } + + private assembleSTaskData(request: RequestOption){ + let data: TaskParams = { + headers: request.headers, + moduleContext: request.moduleContext, + priority: request.priority, + size: request.size, + loadSrc: this.transformResource(request.loadSrc) as string | PixelMap | MResource, + placeholderSrc: this.transformResource(request.placeholderSrc) as string | PixelMap | MResource | undefined, + errorholderSrc: this.transformResource(request.errorholderSrc) as PixelMap | MResource | undefined, + retryholderSrc:this.transformResource(request.retryholderSrc) as PixelMap | MResource | undefined, + fallbackSrc: this.transformResource(request.fallbackSrc) as PixelMap | MResource | undefined, + customGetImage: request.customGetImage + } + return data; + } + + private transformResource(loadSrc:string | PixelMap | Resource | undefined):string | PixelMap | MResource | undefined{ + let res = loadSrc as Resource + if (res && res.id && res.type) { + return { + bundleName: res.bundleName, + moduleName: res.moduleName, + id: res.id, + params: res.params, + type: res.type + } as MResource; + } + return loadSrc as string | PixelMap | undefined; + + } + + //多线程请求加载资源 + private taskpoolLoadResource(request: RequestOption, usageType: string) { + // string类型占位图实现从缓存中获取图片 + if (request.placeholderSrc == 'string') { + request.placeholderCacheKey = request.placeholderRegisterMemoryCacheKey; + } + let mainCache = this.memoryCacheProxy.loadMemoryCache(request.generateCacheKey, request.isCacheable); + let placeholderCache = this.memoryCacheProxy.loadMemoryCache(request.placeholderCacheKey, request.isCacheable); + let retryholderCache = this.memoryCacheProxy.loadMemoryCache(request.retryholderCacheKey, request.isCacheable); + let errorholderCacheKey = this.memoryCacheProxy.loadMemoryCache(request.errorholderCacheKey, request.isCacheable); + + if (usageType == Constants.PLACE_HOLDER && placeholderCache && !mainCache && !retryholderCache && !errorholderCacheKey) { + LogUtil.info("imageknife load placeholder from MemoryCache") + request.placeholderOnComplete(placeholderCache); + return; + } else if (usageType == Constants.RETRY_HOLDER && retryholderCache && !mainCache && !errorholderCacheKey) { + LogUtil.info("imageknife load retryholder from MemoryCache") + request.retryholderOnComplete(retryholderCache); + return; + } else if (usageType == Constants.ERROR_HOLDER && errorholderCacheKey && !mainCache) { + LogUtil.info("imageknife load errorholder from MemoryCache") + request.errorholderOnComplete(errorholderCacheKey); + return; + } else if (usageType == Constants.MAIN_HOLDER && mainCache) { + LogUtil.info("imageknife load mainsource from MemoryCache") + mainCache.waitSaveDisk = false; + let requestList: List | undefined = this.executingJobMap.get(request.uuid); + if(requestList != undefined) { + requestList.forEach((requestOption: RequestOption)=>{ + requestOption.loadComplete(mainCache as ImageKnifeData); + }) + } + return; + } + //组装可以通过sendable传递的数据 + let sendData:SendableData = this.assembleSendableData(request, usageType); + //其他不能sendable发送的数据 + let taskData:TaskParams = this.assembleSTaskData(request); + + //使用taskpool多线程执行资源下载 + let task: ESObject = new taskpool.Task(taskExecute,sendData,taskData) + if (request.progressFunc){ + emitter.on(Constants.PROGRESS_EMITTER as ESObject, (data: ESObject) => { + if (request.progressFunc && data?.data?.value) { + let percent = data.data.value as number; + request.progressFunc.asyncSuccess(percent); + } + }); + } + taskpool.execute(task,request.priority).then((data: ESObject) => { + if (usageType == Constants.PLACE_HOLDER) { + if ((typeof (data as PixelMap).isEditable) == 'boolean') { + let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, data as PixelMap); + request.placeholderOnComplete(imageKnifeData) + this.memoryCacheProxy.putValue(request.placeholderCacheKey,imageKnifeData) + } else { + request.placeholderOnError("request placeholder error") + } + } else if (usageType == Constants.RETRY_HOLDER) { + if ((typeof (data as PixelMap).isEditable) == 'boolean') { + let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, data as PixelMap); + request.retryholderOnComplete(imageKnifeData) + this.memoryCacheProxy.putValue(request.retryholderCacheKey,imageKnifeData) + } else { + request.retryholderOnError("request retryholder error") + } + } else if (usageType == Constants.ERROR_HOLDER) { + if ((typeof (data as PixelMap).isEditable) == 'boolean') { + let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, data as PixelMap); + request.errorholderOnComplete(imageKnifeData) + this.memoryCacheProxy.putValue(request.errorholderCacheKey,imageKnifeData) + } else { + request.errorholderOnError("request errorholder error") + } + } else { + if ((typeof (data as PixelMap).isEditable) == 'boolean') { + let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, data as PixelMap); + let requestList: List | undefined = this.executingJobMap.get(request.uuid) + if(requestList != undefined) { + requestList.forEach((requestOption: RequestOption)=>{ + requestOption.loadComplete(imageKnifeData); + }) + } + this.memoryCacheProxy.putValue(request.generateCacheKey, imageKnifeData); + } else if ((data as GIFFrame[]).length > 0) { + let imageKnifeData = ImageKnifeData.createImageGIFFrame(ImageKnifeType.GIFFRAME, data as GIFFrame[]); + let requestList: List | undefined = this.executingJobMap.get(request.uuid); + if(requestList != undefined) { + requestList.forEach((requestOption: RequestOption)=>{ + requestOption.loadComplete(imageKnifeData); + }) + } + this.memoryCacheProxy.putValue(request.generateCacheKey, imageKnifeData); + } else { + request.loadError("request resources error") + } + } + }).catch((err: BusinessError | string) => { + request.loadError(err) + }) } private keyNotEmpty(request: RequestOption): boolean { @@ -471,61 +710,162 @@ export class ImageKnife { return false; } - private keyEqual(request1: RequestOption, request2: RequestOption): boolean { - // key 完全相等的情况 - if ( - request1.generateCacheKey == request2.generateCacheKey && - request1.generateResourceKey == request2.generateResourceKey && - request1.generateDataKey == request2.generateDataKey - ) { - return true; - } - - return false; - } - - // 非严格校验模式,如果所有key相等我们认为一定相等, 如果请求类型是string 网络请求url或者uri相等 我们也认为该请求应该只发送一个即可,后续请求会去缓存或者磁盘读取 - private requestOrKeyEqual(request1: RequestOption, request2: RequestOption): boolean { - // key 完全相等的情况 - if ( - request1.generateCacheKey == request2.generateCacheKey && - request1.generateResourceKey == request2.generateResourceKey && - request1.generateDataKey == request2.generateDataKey - ) { - return true; - } - - // 如果加载的是网络url或者是本地文件uri读取,那么loadSrc相同就认为是同一个请求 - if ( - typeof request1.loadSrc == 'string' && typeof request2.loadSrc == 'string' && request1.loadSrc == request2.loadSrc - ) { - return true; - } - - return false; - } - private parseSource(request: RequestOption): void { if ((typeof (request.loadSrc as image.PixelMap).isEditable) == 'boolean') { let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, request.loadSrc as PixelMap) request.loadComplete(imageKnifeData); - } else - if (typeof request.loadSrc == 'string') { - // 进入三级缓存模型 - return this.loadResources(request); - } else { + } else if (typeof request.loadSrc == 'string') { + // 进入三级缓存模型 + return this.loadCacheManager(request); + } else { let res = request.loadSrc as Resource; if (typeof res.id != 'undefined' && typeof res.type != 'undefined') { // 进入三级缓存模型 本地资源不参与磁盘缓存 let none = new NONE(); request.diskCacheStrategy(none); - this.loadResources(request); + this.loadCacheManager(request); } else { LogUtil.error("输入参数有问题!") } } } + + prefetchToDiskCache(url: string): Promise { + return new Promise((resolve, reject) => { + let key = this.engineKeyImpl.generateOriginalDiskCacheKey(url, undefined); + let cachedPath = this.getDiskMemoryCache().getFileToPath(key); + if (cachedPath == null || cachedPath == "" || cachedPath == undefined) { + let request = new RequestOption(); + request.load(url) + .addListener({ callback: (err: BusinessError | string, data: ImageKnifeData) => { + if (err) { + reject(err) + } else { + let cachedPath = this.getDiskMemoryCache().getFileToPath(key); + resolve(cachedPath); + } + return false; + } + }) + this.call(request); + } else { + resolve(cachedPath); + } + + }) + } + + // 分发下一个任务 + dispatchNextJob() { + let request: RequestOption | undefined = this.jobQueue.pop(); + if (request != undefined) { + this.loadCacheManager(request); + } + } + + // 设置请求的最大并发数量 + setMaxRequests(count: number) { + if (count > 0) { + this.maxRequests = count; + } + } } +/** + * 加载资源子线程包含流程:网络请求资源->下载资源到本地->解码成ixelMap | GIFFrame[]->缓存到内存和磁盘 + * @param taskParams:任务参数,JSON字符串类型 + * @param headers:请求头 + * @param moduleContext:模块上下文 + * @returns + */ +@Concurrent +async function taskExecute(sendData:SendableData,taskData:TaskParams): Promise { + + //sendable里面加载http模块有问题,需要在@Concurrent先使用下,临时规避方法,待Sendable解决后删除 + let unused_arg0 = http.CertType; + + let emitProgressPercent = (percentValue: number) => { + let eventData: emitter.EventData = { + data: { + "value": percentValue, + } + }; + emitter.emit(Constants.PROGRESS_EMITTER as ESObject, eventData) + } + + let transformations = sendData.getTransformations(); + let usageType = sendData.getUsageType(); + let displayProgress = sendData.getDisplayProgress(); + //子线程构造RequestOption对象 + let newRequestOption = new RequestOption(); + newRequestOption.priority = taskData.priority; + newRequestOption.uuid = sendData.getUuid(); + newRequestOption.loadSrc = taskData.loadSrc as string | PixelMap | Resource; + newRequestOption.dontAnimateFlag = sendData.getDontAnimateFlag(); + newRequestOption.generateCacheKey = sendData.getGenerateCacheKey(); + newRequestOption.generateResourceKey = sendData.getGenerateResourceKey(); + newRequestOption.generateDataKey = sendData.getGenerateDataKey(); + newRequestOption.thumbSizeMultiplier = sendData.getThumbSizeMultiplier(); + newRequestOption.thumbDelayTime = sendData.getThumbDelayTime(); + newRequestOption.size = taskData.size; + newRequestOption.placeholderRegisterCacheKey = sendData.getPlaceHolderRegisterCacheKey(); + newRequestOption.placeholderRegisterMemoryCacheKey = sendData.getPlaceHolderRegisterMemoryCacheKey(); + + if(taskData.placeholderSrc){ + newRequestOption.placeholderSrc = taskData.placeholderSrc as string | PixelMap | Resource | undefined; + } + if(taskData.fallbackSrc){ + newRequestOption.fallbackSrc = taskData.fallbackSrc as PixelMap | Resource | undefined; + } + if(taskData.errorholderSrc){ + newRequestOption.errorholderSrc = taskData.errorholderSrc as PixelMap | Resource | undefined; + } + if(taskData.retryholderSrc){ + newRequestOption.retryholderSrc = taskData.retryholderSrc as PixelMap | Resource | undefined; + } + newRequestOption.onlyRetrieveFromCache = sendData.getOnlyRetrieveFromCache(); + newRequestOption.gpuEnabled = sendData.getGpuEnabled(); + newRequestOption.headers = taskData.headers; + newRequestOption.signature = sendData.getSignature(); + ImageKnifeGlobal.getInstance().setHapContext(taskData.moduleContext as common.UIAbilityContext); + newRequestOption.moduleContext = taskData.moduleContext as common.UIAbilityContext; + newRequestOption.isCacheable = sendData.getIsCacheable(); + //设置磁盘缓存路径 + newRequestOption.diskMemoryCachePath = sendData.getDiskMemoryCachePath(); + newRequestOption.customGetImage = taskData.customGetImage; + if (displayProgress) { + newRequestOption.addProgressListener({ + asyncSuccess: (percentValue: number) => { + // 如果进度条百分比 未展示大小,展示其动画 + emitProgressPercent(percentValue) + } + }) + } + //如果是本地图片不作磁盘缓存 + if (typeof newRequestOption.loadSrc !== 'string') { + let none = new NONE(); + newRequestOption.diskCacheStrategy(none); + } + + if (usageType == Constants.PLACE_HOLDER) { + let manager = new PlaceHolderManager(newRequestOption); + return await new Promise(manager.process); + } else if (usageType == Constants.RETRY_HOLDER) { + let manager = new RetryHolderManager(newRequestOption); + return await new Promise(manager.process); + } else if (usageType == Constants.ERROR_HOLDER) { + let manager = new ErrorHolderManager(newRequestOption); + return await new Promise(manager.process); + } else { + if (transformations) { + newRequestOption.setTransformations(TransformUtils.addTransformations(transformations)) + } + let diskMemoryCache = sendData.getDiskMemoryCache(); + let newDataFetch = sendData.getDataFetch(); + let newResourceFetch = new ParseResClient(); + let manager = new RequestManager(newRequestOption, diskMemoryCache as DiskLruCache, newDataFetch, newResourceFetch); + return await new Promise(manager.process); + } +} diff --git a/library/src/main/ets/components/imageknife/ImageKnifeComponent.ets b/library/src/main/ets/components/imageknife/ImageKnifeComponent.ets index b265792..11d288a 100644 --- a/library/src/main/ets/components/imageknife/ImageKnifeComponent.ets +++ b/library/src/main/ets/components/imageknife/ImageKnifeComponent.ets @@ -23,27 +23,41 @@ import { IDrawLifeCycle } from '../imageknife/interface/IDrawLifeCycle' import { LogUtil } from '../imageknife/utils/LogUtil' import { BusinessError } from '@ohos.base' import common from '@ohos.app.ability.common' +import { ObjectKey } from './ObjectKey' +import componentUtils from '@ohos.arkui.componentUtils' +import inspector from '@ohos.arkui.inspector' +import util from '@ohos.util' +import { ImageKnifeDrawFactory } from './ImageKnifeDrawFactory' +interface KeyCanvas { + keyId:string +} @Component export struct ImageKnifeComponent { @Watch('watchImageKnifeOption') @ObjectLink imageKnifeOption: ImageKnifeOption; - autoPlay: boolean = true private settings: RenderingContextSettings = new RenderingContextSettings(true) private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings) private hasDisplayRetryholder = false; private lastWidth: number = 0 private lastHeight: number = 0 - private currentWidth: number = 0 - private currentHeight: number = 0 + // 当前帧数位置 + private renderFrames_index = 0; // 定时器id private gifTimerId: number = -1 // 完整gif播放时间 private gifLoopDuration: number = 0 private startGifLoopTime: number = 0 private endGifLoopTime: number = 0 + // gif 播放次数 + private playTimes: number = 0 // 抗锯齿属性 private imageSmoothingQuality: ImageSmoothingQuality = 'low'; private imageSmoothingEnabled: boolean = true; + // 是否是gif图片 + private isGif: boolean = false + @State keyCanvas: KeyCanvas = { + keyId: util.generateRandomUUID() + } defaultLifeCycle: IDrawLifeCycle = { // 展示占位图 @@ -91,33 +105,20 @@ export struct ImageKnifeComponent { private detachFromLayoutGIF :DetachFromLayout|undefined = undefined; private detachFromLayoutPixelMap :DetachFromLayout|undefined = undefined; + private lastSrc: string | Resource | PixelMap = "" + listener: inspector.ComponentObserver = inspector.createComponentObserver(this.keyCanvas.keyId) + @State currentSize : Size = { + width: 0.01, + height: 0.01 + } build() { Canvas(this.context) - .width('100%') - .height('100%') + .key(this.keyCanvas.keyId) + .width((this.imageKnifeOption!=undefined && this.imageKnifeOption.mainScaleType!= undefined && this.imageKnifeOption.mainScaleType == ScaleType.AUTO_WIDTH )? this.currentSize.width:'100%') + .height((this.imageKnifeOption!=undefined && this.imageKnifeOption.mainScaleType!= undefined && this.imageKnifeOption.mainScaleType == ScaleType.AUTO_HEIGHT )? this.currentSize.height:'100%') .renderFit(RenderFit.RESIZE_FILL) - .onAreaChange((oldValue: Area, newValue: Area) => { - if(newValue != undefined && newValue.width != undefined && newValue.height != undefined) { - 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) || this.firstDrawFlag) { - this.firstDrawFlag = false; - LogUtil.log('ImageKnifeComponent onAreaChange And Next To Execute. Canvas currentWidth =' + this.currentWidth + ' currentHeight=' + this.currentHeight) - this.imageKnifeExecute() - } - } - }else{ - LogUtil.log('ImageKnifeComponent onAreaChange Error newValue is undefined') - } - }) .onReady(() => { let ctx = this.context; ctx.imageSmoothingEnabled = this.imageSmoothingEnabled; @@ -140,9 +141,24 @@ export struct ImageKnifeComponent { } }) } + onLayoutComplete:()=>void = ():void=>{ + if (this.context.width <= 0 || this.context.height <= 0) { + // 存在宽或者高为0,此次重回无意义,无需进行request请求 + } else { + // 前提:宽高值均有效,值>0. 条件1:当前宽高与上一次宽高不同 条件2:当前是第一次绘制 + if ((this.context.height != this.lastHeight || this.context.width != this.lastWidth) || this.firstDrawFlag) { + this.firstDrawFlag = false; + LogUtil.log('ImageKnifeComponent onAreaChange And Next To Execute. Canvas currentWidth =' + this.context.width + ' currentHeight=' + this.context.height) + this.lastWidth = this.context.width + this.lastHeight = this.context.height + this.imageKnifeExecute() + } + } + } watchImageKnifeOption() { LogUtil.log('ImageKnifeComponent watchImageKnifeOption is happened!') + this.lastSrc = this.imageKnifeOption.loadSrc this.whetherWaitSize(); } @@ -152,7 +168,7 @@ export struct ImageKnifeComponent { * @param drawFirst 是否是aboutToAppear第一次绘制 */ whetherWaitSize(drawFirst?: boolean) { - if (this.currentHeight <= 0 || this.currentWidth <= 0) { + if (this.context.height <= 0 || this.context.width <= 0) { // 宽或者高没有高度,需要等待canvas组件初始化完成 if (drawFirst) { this.firstDrawFlag = true; @@ -188,11 +204,18 @@ export struct ImageKnifeComponent { request.load(this.imageKnifeOption.loadSrc) .addListener({ callback: (err:BusinessError|string, data:ImageKnifeData) => { LogUtil.log('ImageKnifeComponent request.load callback') - this.runNextFunction(this.displayMainSource,data); + if(data.isGIFFrame()) { + this.isGif = true + } else { + this.isGif = false + } + if(this.lastSrc !== request.loadSrc && this.lastSrc !== ""){} + else { + this.runNextFunction(this.displayMainSource,data); + } return false; } }) - let realSize:Size = { width: this.context.width, height: this.context.height @@ -222,25 +245,42 @@ export struct ImageKnifeComponent { } configDisplay(request: RequestOption) { + //单个image组件多个请求头调用 + if (this.imageKnifeOption.headerOption != undefined && this.imageKnifeOption.headerOption?.length > 0) { + for (let i = 0; i < this.imageKnifeOption.headerOption.length; i++) { + let headerOptions = this.imageKnifeOption.headerOption[i]; + request.addHeader(headerOptions.key, headerOptions.value); + request.signature = new ObjectKey(new Date().getTime().toString()) + } + } + if( this.imageKnifeOption.priority != undefined) { + request.setPriority(this.imageKnifeOption.priority) + } if (this.imageKnifeOption.placeholderSrc) { request.placeholder(this.imageKnifeOption.placeholderSrc, {asyncSuccess:(data:ImageKnifeData) => { - LogUtil.log('ImageKnifeComponent request.placeholder callback') + LogUtil.log('ImageKnife ImageKnifeComponent request.placeholder callback') this.runNextFunction(this.displayPlaceholder,data) } }) } if (this.imageKnifeOption.thumbSizeMultiplier) { request.thumbnail(this.imageKnifeOption.thumbSizeMultiplier, {asyncSuccess:(data:ImageKnifeData) => { - LogUtil.log('ImageKnifeComponent request.thumbnail callback') + LogUtil.log('ImageKnife ImageKnifeComponent request.thumbnail callback') this.runNextFunction(this.displayThumbSizeMultiplier,data) }}, this.imageKnifeOption.thumbSizeDelay) } if (this.imageKnifeOption.errorholderSrc) { request.errorholder(this.imageKnifeOption.errorholderSrc, {asyncSuccess:(data:ImageKnifeData) => { - LogUtil.log('ImageKnifeComponent request.errorholder callback') + LogUtil.log('ImageKnife ImageKnifeComponent request.errorholder callback') this.runNextFunction(this.displayErrorholder,data) }}) } + if (this.imageKnifeOption.fallbackSrc) { + request.fallback(this.imageKnifeOption.fallbackSrc, {asyncSuccess:(data:ImageKnifeData) => { + LogUtil.log('ImageKnife ImageKnifeComponent request.fallback callback'); + this.runNextFunction(this.displayPlaceholder,data); + }}) + } if (this.imageKnifeOption.transform) { this.requestAddTransform(request) @@ -259,18 +299,21 @@ export struct ImageKnifeComponent { if (this.imageKnifeOption.displayProgress) { request.addProgressListener({asyncSuccess:(percentValue: number) => { // 如果进度条百分比 未展示大小,展示其动画 - LogUtil.log('ImageKnifeComponent request.addProgressListener callback') + LogUtil.log('ImageKnife ImageKnifeComponent request.addProgressListener callback') this.runNextFunction(this.displayProgress,percentValue) }}) } if (this.imageKnifeOption.retryholderSrc) { request.retryholder(this.imageKnifeOption.retryholderSrc,{asyncSuccess: (data:ImageKnifeData) => { - LogUtil.log('ImageKnifeComponent request.retryholder callback') + LogUtil.log('ImageKnife ImageKnifeComponent request.retryholder callback') this.hasDisplayRetryholder = true this.runNextFunction(this.displayRetryholder,data) }}) } + if (this.imageKnifeOption.customGetImage) { + request.customGetImage = this.imageKnifeOption.customGetImage; + } } configHspContext(request: RequestOption){ @@ -296,7 +339,7 @@ export struct ImageKnifeComponent { this.resetGifData() if (this.canvasHasReady) { // 如果canvas已经初始化好了,清空原有的canvas内容 - this.context.clearRect(0, 0, this.currentWidth, this.currentHeight) + this.context.clearRect(0, 0, this.context.width, this.context.height) } return } @@ -306,7 +349,7 @@ export struct ImageKnifeComponent { this.configNecessary(request); this.configCacheStrategy(request); this.configDisplay(request); - this.configHspContext(request) + this.configHspContext(request); this.configRenderGpu(request); if(ImageKnifeGlobal.getInstance().getImageKnife()!=undefined) { ImageKnifeGlobal.getInstance().getImageKnife()?.call(request); @@ -318,15 +361,15 @@ export struct ImageKnifeComponent { return } if (!this.drawLifeCycleHasConsumed( 'displayPlaceholder', this.context, data, this.imageKnifeOption, - this.currentWidth, this.currentHeight, (gifTimeId) => { + this.context.width, this.context.height, (gifTimeId) => { this.setGifTimeId(gifTimeId) },this.imageKnifeOption.drawLifeCycle)) { if (!this.drawLifeCycleHasConsumed( 'displayPlaceholder', this.context, data, this.imageKnifeOption, - this.currentWidth, this.currentHeight, (gifTimeId) => { + this.context.width, this.context.height, (gifTimeId) => { this.setGifTimeId(gifTimeId) },(ImageKnifeGlobal.getInstance().getImageKnife())?.getDefaultLifeCycle())) { if(this.defaultLifeCycle.displayPlaceholder != undefined) { - this.defaultLifeCycle.displayPlaceholder(this.context, data, this.imageKnifeOption, this.currentWidth, this.currentHeight, (gifTimeId) => { + this.defaultLifeCycle.displayPlaceholder(this.context, data, this.imageKnifeOption, this.context.width, this.context.height, (gifTimeId) => { this.setGifTimeId(gifTimeId) }) } @@ -341,15 +384,15 @@ export struct ImageKnifeComponent { return } if (!this.drawLifeCycleHasConsumed( 'displayProgress', this.context, percent, this.imageKnifeOption, - this.currentWidth, this.currentHeight, (gifTimeId) => { + this.context.width, this.context.height, (gifTimeId) => { this.setGifTimeId(gifTimeId) },this.imageKnifeOption.drawLifeCycle)) { if (!this.drawLifeCycleHasConsumed( 'displayProgress', this.context, percent, this.imageKnifeOption, - this.currentWidth, this.currentHeight, (gifTimeId) => { + this.context.width, this.context.height, (gifTimeId) => { this.setGifTimeId(gifTimeId) },(ImageKnifeGlobal.getInstance().getImageKnife())?.getDefaultLifeCycle())) { if(this.defaultLifeCycle.displayProgress != undefined) { - this.defaultLifeCycle.displayProgress(this.context, percent, this.imageKnifeOption, this.currentWidth, this.currentHeight, (gifTimeId) => { + this.defaultLifeCycle.displayProgress(this.context, percent, this.imageKnifeOption, this.context.width, this.context.height, (gifTimeId) => { this.setGifTimeId(gifTimeId) }) } @@ -364,15 +407,15 @@ export struct ImageKnifeComponent { return } if (!this.drawLifeCycleHasConsumed( 'displayThumbSizeMultiplier', this.context, data, this.imageKnifeOption, - this.currentWidth, this.currentHeight, (gifTimeId) => { + this.context.width, this.context.height, (gifTimeId) => { this.setGifTimeId(gifTimeId) },this.imageKnifeOption.drawLifeCycle)) { if (!this.drawLifeCycleHasConsumed( 'displayThumbSizeMultiplier', this.context, data, this.imageKnifeOption, - this.currentWidth, this.currentHeight, (gifTimeId) => { + this.context.width, this.context.height, (gifTimeId) => { this.setGifTimeId(gifTimeId) },(ImageKnifeGlobal.getInstance().getImageKnife())?.getDefaultLifeCycle())) { if(this.defaultLifeCycle.displayThumbSizeMultiplier != undefined) { - this.defaultLifeCycle.displayThumbSizeMultiplier(this.context, data, this.imageKnifeOption, this.currentWidth, this.currentHeight, (gifTimeId) => { + this.defaultLifeCycle.displayThumbSizeMultiplier(this.context, data, this.imageKnifeOption, this.context.width, this.context.height, (gifTimeId) => { this.setGifTimeId(gifTimeId) }) } @@ -381,19 +424,20 @@ export struct ImageKnifeComponent { } displayMainSource = (data: ImageKnifeData|number|undefined)=> { + ImageKnifeDrawFactory.type = undefined; if(data == undefined || typeof data == 'number'){ return } if (!this.drawLifeCycleHasConsumed( 'displayMainSource', this.context, data, this.imageKnifeOption, - this.currentWidth, this.currentHeight, (gifTimeId) => { + this.context.width, this.context.height, (gifTimeId) => { this.setGifTimeId(gifTimeId) },this.imageKnifeOption.drawLifeCycle)) { if (!this.drawLifeCycleHasConsumed( 'displayMainSource', this.context, data, this.imageKnifeOption, - this.currentWidth, this.currentHeight, (gifTimeId) => { + this.context.width, this.context.height, (gifTimeId) => { this.setGifTimeId(gifTimeId) },(ImageKnifeGlobal.getInstance().getImageKnife())?.getDefaultLifeCycle())) { if(this.defaultLifeCycle.displayMainSource != undefined) { - this.defaultLifeCycle.displayMainSource(this.context, data, this.imageKnifeOption, this.currentWidth, this.currentHeight, (gifTimeId) => { + this.defaultLifeCycle.displayMainSource(this.context, data, this.imageKnifeOption, this.context.width, this.context.height, (gifTimeId) => { this.setGifTimeId(gifTimeId) }) } @@ -408,15 +452,15 @@ export struct ImageKnifeComponent { return } if (!this.drawLifeCycleHasConsumed( 'displayRetryholder', this.context, data, this.imageKnifeOption, - this.currentWidth, this.currentHeight, (gifTimeId) => { + this.context.width, this.context.height, (gifTimeId) => { this.setGifTimeId(gifTimeId) },this.imageKnifeOption.drawLifeCycle)) { if (!this.drawLifeCycleHasConsumed( 'displayRetryholder', this.context, data, this.imageKnifeOption, - this.currentWidth, this.currentHeight, (gifTimeId) => { + this.context.width, this.context.height, (gifTimeId) => { this.setGifTimeId(gifTimeId) },(ImageKnifeGlobal.getInstance().getImageKnife())?.getDefaultLifeCycle())) { if( this.defaultLifeCycle.displayRetryholder != undefined) { - this.defaultLifeCycle.displayRetryholder(this.context, data, this.imageKnifeOption, this.currentWidth, this.currentHeight, (gifTimeId) => { + this.defaultLifeCycle.displayRetryholder(this.context, data, this.imageKnifeOption, this.context.width, this.context.height, (gifTimeId) => { this.setGifTimeId(gifTimeId) }) } @@ -430,15 +474,15 @@ export struct ImageKnifeComponent { return } if (!this.drawLifeCycleHasConsumed( 'displayErrorholder', this.context, data, this.imageKnifeOption, - this.currentWidth, this.currentHeight, (gifTimeId) => { + this.context.width, this.context.height, (gifTimeId) => { this.setGifTimeId(gifTimeId) },this.imageKnifeOption.drawLifeCycle)) { if (!this.drawLifeCycleHasConsumed( 'displayErrorholder', this.context, data, this.imageKnifeOption, - this.currentWidth, this.currentHeight, (gifTimeId) => { + this.context.width, this.context.height, (gifTimeId) => { this.setGifTimeId(gifTimeId) },(ImageKnifeGlobal.getInstance().getImageKnife())?.getDefaultLifeCycle())) { if(this.defaultLifeCycle.displayErrorholder != undefined) { - this.defaultLifeCycle.displayErrorholder(this.context, data, this.imageKnifeOption, this.currentWidth, this.currentHeight, (gifTimeId) => { + this.defaultLifeCycle.displayErrorholder(this.context, data, this.imageKnifeOption, this.context.width, this.context.height, (gifTimeId) => { this.setGifTimeId(gifTimeId) }) } @@ -450,7 +494,18 @@ export struct ImageKnifeComponent { drawPlaceholder(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) { LogUtil.log('ImageKnifeComponent default drawPlaceholder start!') - + // API12 getImageInfoSync同步 + // if(data.drawPixelMap?.imagePixelMap != undefined) { + // let imageInfo = data.drawPixelMap?.imagePixelMap.getImageInfoSync() + // LogUtil.log('ImageKnifeComponent imageinfo width =' + imageInfo.size.width + ' height=' + imageInfo.size.height) + // let scaleType = (typeof imageKnifeOption.placeholderScaleType == 'number') ? imageKnifeOption.placeholderScaleType : ScaleType.FIT_CENTER + // context.save(); + // context.clearRect(0, 0, compWidth, compHeight) + // ScaleTypeHelper.drawImageWithScaleType(context, scaleType, data.drawPixelMap?.imagePixelMap, px2vp(imageInfo.size.width), px2vp(imageInfo.size.height), compWidth, compHeight, 0, 0) + // context.restore(); + // LogUtil.log('ImageKnifeComponent default drawPlaceholder end!') + // } + // getImageInfo异步 data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => { LogUtil.log('ImageKnifeComponent imageinfo width =' + imageInfo.size.width + ' height=' + imageInfo.size.height) let scaleType = (typeof imageKnifeOption.placeholderScaleType == 'number') ? imageKnifeOption.placeholderScaleType : ScaleType.FIT_CENTER @@ -505,7 +560,18 @@ export struct ImageKnifeComponent { drawThumbSizeMultiplier(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) { LogUtil.log('ImageKnifeComponent default drawThumbSizeMultiplier start!') - + // API12 getImageInfoSync同步 + // if(data.drawPixelMap?.imagePixelMap != undefined) { + // let imageInfo = data.drawPixelMap?.imagePixelMap.getImageInfoSync() + // LogUtil.log('ImageKnifeComponent imageinfo width =' + imageInfo.size.width + ' height=' + imageInfo.size.height) + // let scaleType = (typeof imageKnifeOption.thumbSizeMultiplierScaleType == 'number') ? imageKnifeOption.thumbSizeMultiplierScaleType : ScaleType.FIT_CENTER + // context.save(); + // context.clearRect(0, 0, compWidth, compHeight) + // ScaleTypeHelper.drawImageWithScaleType(context, scaleType, data.drawPixelMap?.imagePixelMap, px2vp(imageInfo.size.width), px2vp(imageInfo.size.height), compWidth, compHeight, 0, 0) + // context.restore(); + // LogUtil.log('ImageKnifeComponent default drawThumbSizeMultiplier end!') + // } + // getImageInfo异步 data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => { LogUtil.log('ImageKnifeComponent imageinfo width =' + imageInfo.size.width + ' height=' + imageInfo.size.height) let scaleType = (typeof imageKnifeOption.thumbSizeMultiplierScaleType == 'number') ? imageKnifeOption.thumbSizeMultiplierScaleType : ScaleType.FIT_CENTER @@ -520,11 +586,33 @@ export struct ImageKnifeComponent { drawMainSource(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) { LogUtil.log('ImageKnifeComponent default drawMainSource start!') if (data.isPixelMap()) { + // API12 getImageInfoSync同步 + // if(data.drawPixelMap?.imagePixelMap != undefined) { + // let imageInfo = data.drawPixelMap?.imagePixelMap.getImageInfoSync() + // let scaleType = (typeof imageKnifeOption.mainScaleType == 'number') ? imageKnifeOption.mainScaleType : ScaleType.FIT_CENTER + // LogUtil.log('ImageKnifeComponent imageinfo width =' + imageInfo.size.width + ' height=' + imageInfo.size.height + 'scaleType=' + scaleType) + // context.save(); + // context.clearRect(0, 0, compWidth, compHeight) + // ScaleTypeHelper.drawImageWithScaleType(context, scaleType, data.drawPixelMap?.imagePixelMap, px2vp(imageInfo.size.width), px2vp(imageInfo.size.height), compWidth, compHeight, 0, 0) + // context.restore(); + // LogUtil.log('ImageKnifeComponent default drawMainSource end!') + // } + // getImageInfo异步 data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => { let scaleType = (typeof imageKnifeOption.mainScaleType == 'number') ? imageKnifeOption.mainScaleType : ScaleType.FIT_CENTER LogUtil.log('ImageKnifeComponent imageinfo width =' + imageInfo.size.width + ' height=' + imageInfo.size.height + 'scaleType=' + scaleType) context.save(); context.clearRect(0, 0, compWidth, compHeight) + let scaleHeight = imageInfo.size.height/imageInfo.size.width + let scaleWidth = imageInfo.size.width/imageInfo.size.height + if (this.imageKnifeOption.mainScaleType == ScaleType.AUTO_WIDTH){ + this.currentSize.width=this.context.height*scaleWidth + }else if (this.imageKnifeOption.mainScaleType == ScaleType.AUTO_HEIGHT){ + this.currentSize.height=this.context.width*scaleHeight + }else if (this.imageKnifeOption.mainScaleType == ScaleType.AUTO){ + this.currentSize.height=imageInfo.size.height + this.currentSize.width =imageInfo.size.width + } ScaleTypeHelper.drawImageWithScaleType(context, scaleType, data.drawPixelMap?.imagePixelMap, px2vp(imageInfo.size.width), px2vp(imageInfo.size.height), compWidth, compHeight, 0, 0) context.restore(); LogUtil.log('ImageKnifeComponent default drawMainSource end!') @@ -544,7 +632,18 @@ export struct ImageKnifeComponent { drawRetryholder(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) { LogUtil.log('ImageKnifeComponent default drawRetryholder start!') - + // API12 getImageInfoSync同步 + // if(data.drawPixelMap?.imagePixelMap != undefined) { + // let imageInfo = data.drawPixelMap?.imagePixelMap.getImageInfoSync() + // LogUtil.log('ImageKnifeComponent imageinfo width =' + imageInfo.size.width + ' height=' + imageInfo.size.height) + // let scaleType = (typeof imageKnifeOption.retryholderScaleType == 'number') ? imageKnifeOption.retryholderScaleType : ScaleType.FIT_CENTER + // context.save(); + // context.clearRect(0, 0, compWidth, compHeight) + // ScaleTypeHelper.drawImageWithScaleType(context, scaleType, data.drawPixelMap?.imagePixelMap, px2vp(imageInfo.size.width), px2vp(imageInfo.size.height), compWidth, compHeight, 0, 0) + // context.restore(); + // LogUtil.log('ImageKnifeComponent default drawRetryholder end!') + // } + // getImageInfo异步 data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => { LogUtil.log('ImageKnifeComponent imageinfo width =' + imageInfo.size.width + ' height=' + imageInfo.size.height) let scaleType = (typeof imageKnifeOption.retryholderScaleType == 'number') ? imageKnifeOption.retryholderScaleType : ScaleType.FIT_CENTER @@ -558,7 +657,18 @@ export struct ImageKnifeComponent { drawErrorholder(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) { LogUtil.log('ImageKnifeComponent default drawErrorholder start!') - + // API12 getImageInfoSync同步 + // if(data.drawPixelMap?.imagePixelMap != undefined) { + // let imageInfo = data.drawPixelMap?.imagePixelMap.getImageInfoSync() + // LogUtil.log('ImageKnifeComponent imageinfo width =' + imageInfo.size.width + ' height=' + imageInfo.size.height) + // let scaleType = (typeof imageKnifeOption.errorholderSrcScaleType == 'number') ? imageKnifeOption.errorholderSrcScaleType : ScaleType.FIT_CENTER + // context.save(); + // context.clearRect(0, 0, compWidth, compHeight) + // ScaleTypeHelper.drawImageWithScaleType(context, scaleType, data.drawPixelMap?.imagePixelMap, px2vp(imageInfo.size.width), px2vp(imageInfo.size.height), compWidth, compHeight, 0, 0) + // context.restore(); + // LogUtil.log('ImageKnifeComponent default drawErrorholder end!') + // } + // getImageInfo异步 data.drawPixelMap?.imagePixelMap?.getImageInfo().then((imageInfo) => { LogUtil.log('ImageKnifeComponent imageinfo width =' + imageInfo.size.width + ' height=' + imageInfo.size.height) let scaleType = (typeof imageKnifeOption.errorholderSrcScaleType == 'number') ? imageKnifeOption.errorholderSrcScaleType : ScaleType.FIT_CENTER @@ -572,7 +682,7 @@ export struct ImageKnifeComponent { requestAddTransform(request: RequestOption) { if (TransformType.BlurTransformation == this.imageKnifeOption.transform?.transformType) { - request.blur(this.imageKnifeOption.transform?.blur) + request.blur(this.imageKnifeOption.transform?.blur?.radius,this.imageKnifeOption.transform?.blur?.sampling) } else if (TransformType.BrightnessFilterTransformation == this.imageKnifeOption.transform?.transformType) { request.brightnessFilter(this.imageKnifeOption.transform?.brightnessFilter) } else if (TransformType.ContrastFilterTransformation == this.imageKnifeOption.transform?.transformType) { @@ -611,11 +721,18 @@ export struct ImageKnifeComponent { request.fitCenter() } } - + aboutToRecycle(){ + this.resetGifData() + } + aboutToReuse(params: ESObject) { + this.context.clearRect(0,0,this.context.width,this.context.height) + } aboutToAppear() { LogUtil.log('ImageKnifeComponent aboutToAppear happened!') this.canvasHasReady = false; this.whetherWaitSize(true); + + this.listener.on("layout",this.onLayoutComplete) } aboutToDisappear() { @@ -629,7 +746,10 @@ export struct ImageKnifeComponent { if (this.detachFromLayoutPixelMap != undefined) { this.detachFromLayoutPixelMap.detach(); } - this.resetGifData(); + if(this.isGif){ + this.resetGifData(); + } + this.listener.off("layout",this.onLayoutComplete) } onPageShow() { @@ -658,15 +778,15 @@ export struct ImageKnifeComponent { let frames = data.drawGIFFrame?.imageGIFFrames as GIFFrame[] LogUtil.log('ImageKnifeComponent gifFrameLength =' + frames.length); if (imageKnifeOption.gif && (typeof (imageKnifeOption.gif.seekTo) == 'number') && imageKnifeOption.gif.seekTo >= 0) { - this.autoPlay = false; context.clearRect(0, 0, compWidth, compHeight) this.drawSeekToFrame(frames, context, compWidth, compHeight) } else { - this.autoPlay = true context.clearRect(0, 0, compWidth, compHeight) this.renderFrames_frames = frames - this.renderFrames_index = 0 + if (this.imageKnifeOption.autoPlay == undefined) { + this.renderFrames_index = 0; + } this.renderFrames_context = context this.renderFrames_compWidth = compWidth this.renderFrames_compHeight = compHeight @@ -676,10 +796,12 @@ export struct ImageKnifeComponent { } private resetGifData() { - clearTimeout(this.gifTimerId) - this.gifLoopDuration = 0; - this.startGifLoopTime = 0; - this.endGifLoopTime = 0; + if(this.isGif) { + clearTimeout(this.gifTimerId) + this.gifLoopDuration = 0; + this.startGifLoopTime = 0; + this.endGifLoopTime = 0; + } } /** @@ -688,13 +810,13 @@ export struct ImageKnifeComponent { private drawSeekToFrame(frames: GIFFrame[], context: CanvasRenderingContext2D, compWidth: number, compHeight: number) { if(this.imageKnifeOption.gif != undefined && this.imageKnifeOption.gif.seekTo != undefined) { for (let i = 0; i < this.imageKnifeOption.gif.seekTo; i++) { + this.renderFrames_index = i; this.drawFrame(frames, i, context, compWidth, compHeight); } } } renderFrames_frames: GIFFrame[] | undefined = undefined - renderFrames_index: number = 0; renderFrames_context: CanvasRenderingContext2D | undefined = undefined; renderFrames_compWidth: number = 0; renderFrames_compHeight: number = 0 @@ -708,6 +830,14 @@ export struct ImageKnifeComponent { } // draw Frame this.drawFrame(this.renderFrames_frames, this.renderFrames_index, this.renderFrames_context, this.renderFrames_compWidth, this.renderFrames_compHeight); + // gif播放次数 + if (this.renderFrames_frames != undefined && this.renderFrames_index === (this.renderFrames_frames.length - 1) && this.imageKnifeOption.gif != undefined ) { + this.playTimes++ + if (this.imageKnifeOption.gif.playTimes == this.playTimes){ + this.imageKnifeOption.autoPlay = false; + this.playTimes = 0 + } + } //如果gif动图只有一帧的情况下,不进行后面代码的逐帧绘制循环 if (this.renderFrames_frames != undefined && this.renderFrames_frames.length <= 1) { return @@ -717,7 +847,7 @@ export struct ImageKnifeComponent { let end = new Date().getTime(); let diff = end - start - if (this.autoPlay) { + if ((this.imageKnifeOption.autoPlay == undefined) || (this.imageKnifeOption.autoPlay)) { // 理论上该帧在屏幕上保留的时间 let stayTime:number= 0 @@ -782,10 +912,58 @@ export struct ImageKnifeComponent { ScaleTypeHelper.drawImageWithScaleType(context, scaleType, pixelmap, px2vp(frameW), px2vp(frameH), compWidth, compHeight, px2vp(frame.dims.left), px2vp(frame.dims.top)) // tips:worker如果不是在展示页面中创建,使用子线程回来的数据创建的图片,会导致canvas绘制不出来 context.restore(); - LogUtil.log('ImageKnifeComponent default drawMainSource end!') + if (ImageKnifeDrawFactory.type === undefined) { + return; + } + // 通过 destination-in 裁剪出圆角 + if (ImageKnifeDrawFactory?.type === DrawType.Round || ImageKnifeDrawFactory?.type === DrawType.Oval) { + context.save(); + context.globalCompositeOperation = 'destination-in'; + if (ImageKnifeDrawFactory.type === DrawType.Round) { + ImageKnifeDrawFactory.setRect(context, scaleType, pixelmap, px2vp(frameW), px2vp(frameH), compWidth, + compHeight, 0, 0, ImageKnifeDrawFactory.borderWidth, ImageKnifeDrawFactory.connerRadius); + } else { + context.beginPath(); + ImageKnifeDrawFactory.setOval(context, scaleType, pixelmap, px2vp(frameW), px2vp(frameH), compWidth, + compHeight, 0, 0, ImageKnifeDrawFactory.borderWidth); + context.closePath(); + } + context.fill(); + context.restore(); + if (ImageKnifeDrawFactory.borderWidth > 0) { + // 为圆角添加边框 + context.save(); + context.strokeStyle = ImageKnifeDrawFactory.colorString; + context.lineWidth = ImageKnifeDrawFactory.borderWidth; + context.globalCompositeOperation = 'source-over'; + if (ImageKnifeDrawFactory.type === DrawType.Round) { + ImageKnifeDrawFactory.setRect(context, scaleType, pixelmap, px2vp(frameW), px2vp(frameH), compWidth, + compHeight, 0, 0, ImageKnifeDrawFactory.borderWidth, ImageKnifeDrawFactory.connerRadius); + } else { + context.beginPath(); + ImageKnifeDrawFactory.setOval(context, scaleType, pixelmap, px2vp(frameW), px2vp(frameH), compWidth, + compHeight, 0, 0, ImageKnifeDrawFactory.borderWidth); + context.closePath(); + } + context.stroke(); + context.restore(); + } + context.restore(); + } + LogUtil.log('ImageKnifeComponent canvasDrawPixelMap end!') } } +export enum DrawType{ + + Normal = 0, + + Oval = 1, + + Round = 2 + +} + export enum FrameDisposalType { // 0 - No disposal specified. The decoder is not required to take any action. // 不使用处置方法 @@ -825,7 +1003,13 @@ export enum ScaleType { // 如果图像大于组件则执行FIT_CENTER,小于组件则CENTER CENTER_INSIDE = 7, // 如果不想适配,直接展示原图大小 - NONE = 8 + NONE = 8, + // 设置宽的时候,图片高度自适应 + AUTO_HEIGHT =9, + // 设置高的时候,图片宽度自适应 + AUTO_WIDTH =10, + //没有设置宽和高,图片按照自身宽高显示 + AUTO =11 } @@ -862,7 +1046,7 @@ export class ScaleTypeHelper { ScaleTypeHelper.drawNone(context, source, imageWidth, imageHeight, imageOffsetX, imageOffsetY) break; default: - ScaleTypeHelper.drawNone(context, source, imageWidth, imageHeight, imageOffsetX, imageOffsetY) + ScaleTypeHelper.drawFitCenter(context, source, minScale, imageWidth, imageHeight, compWidth, compHeight, imageOffsetX, imageOffsetY) break } diff --git a/library/src/main/ets/components/imageknife/ImageKnifeDrawFactory.ets b/library/src/main/ets/components/imageknife/ImageKnifeDrawFactory.ets index f67bfad..5080f1f 100644 --- a/library/src/main/ets/components/imageknife/ImageKnifeDrawFactory.ets +++ b/library/src/main/ets/components/imageknife/ImageKnifeDrawFactory.ets @@ -16,9 +16,13 @@ import { ImageKnifeOption } from '../imageknife/ImageKnifeOption' import { ImageKnifeData } from '../imageknife/ImageKnifeData' import { GIFFrame } from '../imageknife/utils/gif/GIFFrame' import { IDrawLifeCycle } from '../imageknife/interface/IDrawLifeCycle' -import { ScaleTypeHelper,ScaleType } from '../imageknife/ImageKnifeComponent' +import { ScaleTypeHelper,ScaleType, DrawType } from '../imageknife/ImageKnifeComponent' export class ImageKnifeDrawFactory{ + public static borderWidth: number; + public static type: DrawType | undefined = undefined; + public static colorString: string; + public static connerRadius: number; /** * 绘制PixelMap内容情况下,主图的椭圆裁剪,可添加边框 @@ -81,6 +85,11 @@ export class ImageKnifeDrawFactory{ }) return true; } + if (data.isGIFFrame()) { + ImageKnifeDrawFactory.type = DrawType.Oval; + ImageKnifeDrawFactory.borderWidth = borderWidth; + ImageKnifeDrawFactory.colorString = colorString; + } return false; }, // 展示重试图层 @@ -108,7 +117,7 @@ export class ImageKnifeDrawFactory{ * @param imageOffsetY * @param borderWidth */ - private static setOval(context: CanvasRenderingContext2D, scaleType: ScaleType, source: PixelMap | undefined, imageWidth: number, imageHeight: number, compWidth: number, compHeight: number, imageOffsetX:number,imageOffsetY:number + public static setOval(context: CanvasRenderingContext2D, scaleType: ScaleType, source: PixelMap | undefined, imageWidth: number, imageHeight: number, compWidth: number, compHeight: number, imageOffsetX:number,imageOffsetY:number ,borderWidth:number) { let scaleW = compWidth / imageWidth let scaleH = compHeight / imageHeight @@ -240,6 +249,12 @@ export class ImageKnifeDrawFactory{ }) return true; } + if (data.isGIFFrame()) { + ImageKnifeDrawFactory.type = DrawType.Round; + ImageKnifeDrawFactory.borderWidth = borderWidth; + ImageKnifeDrawFactory.colorString = colorString; + ImageKnifeDrawFactory.connerRadius = connerRadius; + } return false; }, // 展示重试图层 @@ -268,7 +283,7 @@ export class ImageKnifeDrawFactory{ * @param borderWidth * @param cornerRadius */ - private static setRect(context: CanvasRenderingContext2D, scaleType: ScaleType, source: PixelMap | undefined, imageWidth: number, imageHeight: number, compWidth: number, compHeight: number, imageOffsetX:number,imageOffsetY:number + public static setRect(context: CanvasRenderingContext2D, scaleType: ScaleType, source: PixelMap | undefined, imageWidth: number, imageHeight: number, compWidth: number, compHeight: number, imageOffsetX:number,imageOffsetY:number ,borderWidth:number,cornerRadius:number) { let scaleW = compWidth / imageWidth let scaleH = compHeight / imageHeight diff --git a/library/src/main/ets/components/imageknife/ImageKnifeOption.ets b/library/src/main/ets/components/imageknife/ImageKnifeOption.ets index cab4c2c..724b166 100644 --- a/library/src/main/ets/components/imageknife/ImageKnifeOption.ets +++ b/library/src/main/ets/components/imageknife/ImageKnifeOption.ets @@ -26,6 +26,8 @@ import { rgbColor } from './transform/CropCircleWithBorderTransformation' import { RoundCorner } from './transform/RoundedCornersTransformation' import { ObjectKey } from './ObjectKey' import common from '@ohos.app.ability.common' +import { Priority } from './RequestOption' +import { DataFetchResult } from './networkmanage/DataFetchResult' export interface CropCircleWithBorder{ border: number, @@ -37,15 +39,19 @@ export interface Crop{ height: number, cropType: CropType } - +export interface BlurType { + radius: number, + sampling: number +} export interface GifOptions{ loopFinish?: (loopTime?:number) => void speedFactory?: number seekTo?: number + playTimes?: number } export interface TransformOptions{ transformType: number, - blur?: number, + blur?: BlurType, roundedCorners?: RoundCorner cropCircleWithBorder?: CropCircleWithBorder crop?:Crop @@ -57,13 +63,22 @@ export interface TransformOptions{ rotateImage?: number } +export interface HeaderOptions { + key: string; + value: string; +} + @Observed export class ImageKnifeOption { - + //控制gif开关 + autoPlay?: boolean = true; + // header请求列表 + headerOption?: Array; // 主图资源 loadSrc: string | PixelMap | Resource = ''; mainScaleType?: ScaleType = ScaleType.FIT_CENTER - + // 优先级 + priority?: Priority = Priority.MEDIUM enableGpu?:boolean = true; // 磁盘缓存策略 @@ -73,9 +88,11 @@ export class ImageKnifeOption { dontAnimateFlag? = false; // 占位图 - placeholderSrc?: PixelMap | Resource; + placeholderSrc?: string | PixelMap | Resource; placeholderScaleType?: ScaleType = ScaleType.FIT_CENTER + // 后备回调符 + fallbackSrc?: PixelMap | Resource; // 失败占位图 errorholderSrc?: PixelMap | Resource; errorholderSrcScaleType?: ScaleType = ScaleType.FIT_CENTER @@ -129,6 +146,9 @@ export class ImageKnifeOption { context?: common.UIAbilityContext; // sizeAnimate?: AnimateParam 由业务自定义不再支持 + // 设置是否使用应用自定义的方式加载图片 + customGetImage?: (context: Context, src: string) => Promise; + constructor() { } diff --git a/library/src/main/ets/components/imageknife/ObjectKey.ets b/library/src/main/ets/components/imageknife/ObjectKey.ets index 9a121ae..18108f7 100644 --- a/library/src/main/ets/components/imageknife/ObjectKey.ets +++ b/library/src/main/ets/components/imageknife/ObjectKey.ets @@ -12,6 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@Sendable export class ObjectKey{ private objectKey: string; diff --git a/library/src/main/ets/components/imageknife/RequestOption.ets b/library/src/main/ets/components/imageknife/RequestOption.ets index a9b79d3..0d2b0f9 100644 --- a/library/src/main/ets/components/imageknife/RequestOption.ets +++ b/library/src/main/ets/components/imageknife/RequestOption.ets @@ -12,15 +12,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import {ImageKnife} from './ImageKnife' +import { ImageKnife } from './ImageKnife' import { DiskStrategy } from "../cache/diskstrategy/DiskStrategy" -import { AsyncCallback } from "../imageknife/interface/AsyncCallback" -import { AsyncSuccess } from "../imageknife/interface/AsyncSuccess" -import { IAllCacheInfoCallback } from "../imageknife/interface/IAllCacheInfoCallback" +import { AsyncCallback } from "../imageknife/interface/AsyncCallback" +import { AsyncSuccess } from "../imageknife/interface/AsyncSuccess" +import { AllCacheInfo, IAllCacheInfoCallback } from "../imageknife/interface/IAllCacheInfoCallback" import { AUTOMATIC } from "../cache/diskstrategy/enum/AUTOMATIC" import { BaseTransform } from "../imageknife/transform/BaseTransform" import { RotateImageTransformation } from "../imageknife/transform/RotateImageTransformation" -import { ImageKnifeData } from "../imageknife/ImageKnifeData" +import { ImageKnifeData, ImageKnifeType } from "../imageknife/ImageKnifeData" import { CenterCrop } from '../imageknife/transform/pixelmap/CenterCrop' import { CenterInside } from '../imageknife/transform/pixelmap/CenterInside' import { FitCenter } from '../imageknife/transform/pixelmap/FitCenter' @@ -28,7 +28,10 @@ import { RoundedCornersTransformation, RoundCorner } from '../imageknife/transfo import { CropCircleTransformation } from '../imageknife/transform/CropCircleTransformation' -import { CropCircleWithBorderTransformation, rgbColor } from '../imageknife/transform/CropCircleWithBorderTransformation' +import { + CropCircleWithBorderTransformation, + rgbColor +} from '../imageknife/transform/CropCircleWithBorderTransformation' import { CropSquareTransformation } from '../imageknife/transform/CropSquareTransformation' import { CropTransformation } from '../imageknife/transform/CropTransformation' import { CropType } from '../imageknife/transform/CropTransformation' @@ -50,46 +53,85 @@ import { ImageKnifeGlobal } from './ImageKnifeGlobal' import { BusinessError } from '@ohos.base' import { ObjectKey } from './ObjectKey' import common from '@ohos.app.ability.common' +import { GIFFrame } from './utils/gif/GIFFrame' +import { DiskCacheProxy } from './requestmanage/DiskCacheProxy' +import { DiskLruCache } from '../cache/DiskLruCache' +import { SparkMD5 } from '../3rd_party/sparkmd5/spark-md5' +import { FileUtils } from '../cache/FileUtils' +import util from '@ohos.util' +import { DataFetchResult } from './networkmanage/DataFetchResult' export interface Size { width: number, height: number } -export interface DetachFromLayout{ - detach:()=>void + +export enum CacheType { + Default, + //缓存 + Cache, + //磁盘 + Disk } +export interface DetachFromLayout { + detach: () => void +} + +export enum Priority { + HIGH = 0, + MEDIUM = 1, + LOW = 2 +} export class RequestOption { - uuid:string ='' // 唯一标识 + // 遍历添加图片http请求头 + headers: Map = new Map(); + + // 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); + } + }) + } + + // 优先级 + priority: Priority = Priority.MEDIUM; + uuid: string = '' // 唯一标识 loadSrc: string | PixelMap | Resource = ''; strategy: DiskStrategy = new AUTOMATIC(); dontAnimateFlag = false; - placeholderSrc: PixelMap | Resource | undefined = undefined; + placeholderSrc: string | PixelMap | Resource | undefined = undefined; placeholderFunc: AsyncSuccess | undefined = undefined; errorholderSrc: PixelMap | Resource | undefined = undefined; errorholderFunc: AsyncSuccess | undefined = undefined; - errorholderData: ImageKnifeData | undefined = undefined;; + errorholderData: ImageKnifeData | undefined = undefined; thumbSizeMultiplier: number = 0; - // 如果存在缩略图,则主图延时1s加载 thumbDelayTime: number = 1000 thumbHolderFunc: AsyncSuccess | undefined = undefined; requestListeners: Array> | undefined = undefined; - // 进度条 progressFunc: AsyncSuccess | undefined = undefined; - + // 后备回调符 + fallbackSrc: PixelMap | Resource | undefined = undefined; + fallbackFunc: AsyncSuccess | undefined = undefined; + fallbackData: ImageKnifeData | undefined = undefined; // 重试图层 retryholderSrc: PixelMap | Resource | undefined = undefined; retryholderFunc: AsyncSuccess | undefined = undefined; retryholderData: ImageKnifeData | undefined = undefined; - size:Size= { width: -1, height: -1 }; - + size: Size = { width: -1, height: -1 }; // 网络下载数据回调 allCacheInfoCallback: IAllCacheInfoCallback | undefined = undefined; onlyRetrieveFromCache: boolean = false; isCacheable: boolean = true; - // 开启GPU变换绘制 gpuEnabled: boolean = false; // 变换相关 @@ -99,73 +141,76 @@ export class RequestOption { generateDataKey: string = ""; filesPath: string = ""; // data/data/包名/files目录 cachesPath: string = ""; // 网络下载默认存储在data/data/包名/cache/ImageKnifeNetworkFolder/目标md5.img下面 - + placeholderCacheKey: string = ""; + retryholderCacheKey: string = ""; + errorholderCacheKey: string = ""; + // string类型占位图相关缓存key值 + placeholderRegisterCacheKey: string = ""; + placeholderRegisterMemoryCacheKey: string = ""; + fallbackCacheKey: string = ""; // 自定义缓存关键字 signature?: ObjectKey; - + // 设置是否使用应用自定义的方式加载图片 + customGetImage?: (context: Context, src: string) => Promise; // 下载原始文件地址 downloadFilePath: string = ""; - + //磁盘缓存文件路径 + diskMemoryCachePath: string =""; // 网络文件下载统一存放 networkCacheFolder: string = "ImageKnifeNetworkFolder" - - // 主线图片 状态变化 是否加载完成 // 主图未加载成功 显示占位图 主图加载成功不展示占位图 loadMainReady = false; - // 失败占位图展示状态 当true 表示主图加载失败需要展示失败占位图 loadErrorReady = false; - // 重试占位图展示状态 当true 表示主图加载失败需要展示失败占位图 loadRetryReady = false; - + // 后备回调符展示状态 当true 表占位图加载失败需要展示后备回调符 + loadFallBackReady = false; // 缩略图展示 loadThumbnailReady = false; - - detachFromLayout:DetachFromLayout = { - detach: ()=>{ - let imageKnife:ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife(); - if(imageKnife != undefined) { + detachFromLayout: DetachFromLayout = { + detach: () => { + let imageKnife: ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife(); + if (imageKnife != undefined) { imageKnife.remove(this.uuid); } } } // module资源的需要当前的module context - moduleContext?:common.UIAbilityContext = undefined; + moduleContext?: common.UIAbilityContext = undefined; + constructor() { // 初始化全局监听 this.requestListeners = new Array(); - // 初始化唯一标识,可以用这个标识找到ImageKnife中的对象 - this.uuid = this.generateUUID(); - - let ctx = ImageKnifeGlobal.getInstance().getHapContext() as common.UIAbilityContext - if(ctx != undefined){ + let ctx = ImageKnifeGlobal.getInstance().getHapContext() as common.UIAbilityContext + if (ctx != undefined) { this.moduleContext = ctx; } } - - generateUUID(): string { - let d = new Date().getTime(); - const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace( new RegExp("[xy]","g"), (c) => { - const r = (d + Math.random() * 16) % 16 | 0; - d = Math.floor(d / 16); - return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); - }); - return uuid; + setPriority(priority: Priority) { + this.priority = priority } - setModuleContext(moduleCtx:common.UIAbilityContext){ + setTransformations( array:Array>){ + this.transformations = array; + } + generateUUID(): string { + return SparkMD5.hashBinary(JSON.stringify(this.loadSrc)) as string; + } + + setModuleContext(moduleCtx: common.UIAbilityContext) { this.moduleContext = moduleCtx; } - getModuleContext():common.UIAbilityContext | undefined{ + getModuleContext(): common.UIAbilityContext | undefined { return this.moduleContext; } + /** * set image Component size */ - setImageViewSize(imageSize:Size) { + setImageViewSize(imageSize: Size) { this.size.width = imageSize.width; this.size.height = imageSize.height; return this; @@ -189,6 +234,8 @@ export class RequestOption { load(src: string | PixelMap | Resource) { this.loadSrc = src; + // 初始化唯一标识,可以用这个标识找到ImageKnife中的对象 + this.uuid = this.generateUUID(); return this; } @@ -203,7 +250,7 @@ export class RequestOption { } // 仅支持 本地图片 - placeholder(src: PixelMap | Resource, func?: AsyncSuccess) { + placeholder(src: string | PixelMap | Resource, func?: AsyncSuccess) { this.placeholderSrc = src; this.placeholderFunc = func; return this; @@ -215,6 +262,12 @@ export class RequestOption { return this; } + fallback(src: PixelMap | Resource, func?: AsyncSuccess) { + this.fallbackSrc = src; + this.fallbackFunc = func; + return this; + } + retryholder(src: PixelMap | Resource, func?: AsyncSuccess) { this.retryholderSrc = src; this.retryholderFunc = func; @@ -236,7 +289,7 @@ export class RequestOption { } addListener(func: AsyncCallback) { - if(this.requestListeners != undefined) { + if (this.requestListeners != undefined) { this.requestListeners?.push(func); } return this; @@ -256,8 +309,8 @@ export class RequestOption { this.onlyRetrieveFromCache = flag; } - rotateImage(degreesToRotate: number|undefined) { - if(degreesToRotate == undefined){ + rotateImage(degreesToRotate: number | undefined) { + if (degreesToRotate == undefined) { return } let rotateImage = new RotateImageTransformation(degreesToRotate); @@ -280,8 +333,8 @@ export class RequestOption { return this; } - roundedCorners(obj: RoundCorner|undefined) { - if(obj == undefined){ + roundedCorners(obj: RoundCorner | undefined) { + if (obj == undefined) { return } let transformation = new RoundedCornersTransformation({ @@ -300,8 +353,8 @@ export class RequestOption { return this; } - cropCircleWithBorder(border: number|undefined, obj: rgbColor|undefined) { - if(border == undefined || obj == undefined){ + cropCircleWithBorder(border: number | undefined, obj: rgbColor | undefined) { + if (border == undefined || obj == undefined) { return } let transformation = new CropCircleWithBorderTransformation(border, obj) @@ -315,8 +368,8 @@ export class RequestOption { return this; } - crop(width: number|undefined, height: number|undefined, cropType: CropType|undefined) { - if(width == undefined || height == undefined || cropType == undefined){ + crop(width: number | undefined, height: number | undefined, cropType: CropType | undefined) { + if (width == undefined || height == undefined || cropType == undefined) { return } let transformation = new CropTransformation(width, height, cropType) @@ -330,8 +383,8 @@ export class RequestOption { return this; } - brightnessFilter(brightness: number|undefined) { - if(brightness == undefined){ + brightnessFilter(brightness: number | undefined) { + if (brightness == undefined) { return } let transformation = new BrightnessFilterTransformation(brightness) @@ -339,8 +392,8 @@ export class RequestOption { return this; } - contrastFilter(contrast: number|undefined) { - if(contrast == undefined){ + contrastFilter(contrast: number | undefined) { + if (contrast == undefined) { return } let transformation = new ContrastFilterTransformation(contrast) @@ -366,17 +419,22 @@ export class RequestOption { return this; } - blur(radius: number|undefined) { - if(radius == undefined){ + blur(radius: number | undefined, sampling?: number) { + if (radius == undefined) { return } - let transformation = new BlurTransformation(radius) - this.transformations.push(transformation); + if (sampling == undefined) { + let transformation = new BlurTransformation(radius) + this.transformations.push(transformation); + } else { + let transformation = new BlurTransformation(radius, sampling) + this.transformations.push(transformation); + } return this; } - pixelationFilter(pixel: number|undefined) { - if(pixel == undefined){ + pixelationFilter(pixel: number | undefined) { + if (pixel == undefined) { return } let transformation = new PixelationFilterTransformation(pixel) @@ -384,8 +442,8 @@ export class RequestOption { return this; } - swirlFilter(degree: number|undefined) { - if(degree == undefined){ + swirlFilter(degree: number | undefined) { + if (degree == undefined) { return } let transformation = new SwirlFilterTransformation(degree) @@ -393,8 +451,8 @@ export class RequestOption { return this; } - mask(maskResource: Resource|undefined) { - if(maskResource == undefined){ + mask(maskResource: Resource | undefined) { + if (maskResource == undefined) { return } let transformation = new MaskTransformation(maskResource) @@ -402,8 +460,8 @@ export class RequestOption { return this; } - kuwaharaFilter(radius: number|undefined) { - if(radius == undefined){ + kuwaharaFilter(radius: number | undefined) { + if (radius == undefined) { return } let transformation = new KuwaharaFilterTransform(radius); @@ -411,8 +469,8 @@ export class RequestOption { return this; } - toonFilter(threshold: number|undefined, quantizationLevels: number|undefined) { - if(threshold == undefined || quantizationLevels == undefined){ + toonFilter(threshold: number | undefined, quantizationLevels: number | undefined) { + if (threshold == undefined || quantizationLevels == undefined) { return } let transformation = new ToonFilterTransform(threshold, quantizationLevels); @@ -420,8 +478,8 @@ export class RequestOption { return this; } - vignetteFilter(centerPoint: Array|undefined, vignetteColor: Array|undefined, vignetteSpace: Array|undefined) { - if(centerPoint == undefined || vignetteColor == undefined || vignetteSpace == undefined){ + vignetteFilter(centerPoint: Array | undefined, vignetteColor: Array | undefined, vignetteSpace: Array | undefined) { + if (centerPoint == undefined || vignetteColor == undefined || vignetteSpace == undefined) { return } let transformation = new VignetteFilterTransform(centerPoint, vignetteColor, vignetteSpace); @@ -438,6 +496,7 @@ export class RequestOption { this.transformations = inputs; return this; } + // 开启GPU变换绘制 enableGPU() { this.gpuEnabled = true; @@ -445,72 +504,82 @@ export class RequestOption { } // 占位图解析成功 - placeholderOnComplete = (imageKnifeData: ImageKnifeData)=> { + placeholderOnComplete = (imageKnifeData:ImageKnifeData) => { LogUtil.log("placeholderOnComplete has called!"); LogUtil.log("Main Image is Ready:" + this.loadMainReady); if (!this.loadMainReady && !(this.loadErrorReady || this.loadRetryReady) && !this.loadThumbnailReady) { // 主图未加载成功,并且未加载失败 显示占位图 主图加载成功或者加载失败后=>不展示占位图 - if(this.placeholderSrc != undefined) { + if (this.placeholderSrc != undefined) { this.placeholderFunc?.asyncSuccess(imageKnifeData) } } } - // 占位图解析失败 - placeholderOnError = (error:BusinessError|string)=>{ + placeholderOnError = (error: BusinessError | string) => { LogUtil.log("占位图解析失败 error =" + error) } - - // 缩略图解析成功 - thumbholderOnComplete = (imageKnifeData: ImageKnifeData)=> { + thumbholderOnComplete = (value: PixelMap | GIFFrame[]) => { + let imageKnifeData = new ImageKnifeData(); + if ((typeof (value as PixelMap).isEditable) == 'boolean') { + imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, value as PixelMap); + } else { + imageKnifeData = ImageKnifeData.createImageGIFFrame(ImageKnifeType.GIFFRAME, value as GIFFrame[]); + } if (!this.loadMainReady && !(this.loadErrorReady || this.loadRetryReady)) { // 主图未加载成功,并且未加载失败 显示占位图 主图加载成功或者加载失败后=>不展示占位图 - if(this.thumbHolderFunc != undefined) { + if (this.thumbHolderFunc != undefined) { this.thumbHolderFunc?.asyncSuccess(imageKnifeData) } } } - // 缩略图解析失败 - thumbholderOnError=(error? :BusinessError|string)=>{ + thumbholderOnError = (error?: BusinessError | string) => { LogUtil.log("缩略图解析失败 error =" + error) } - // 加载失败 占位图解析成功 - errorholderOnComplete = (imageKnifeData: ImageKnifeData)=> { + errorholderOnComplete = (imageKnifeData:ImageKnifeData) => { // 如果有错误占位图 先解析并保存在RequestOption中 等到加载失败时候进行调用 this.errorholderData = imageKnifeData; if (this.loadErrorReady) { - if(this.errorholderFunc != undefined) { + if (this.errorholderFunc != undefined) { this.errorholderFunc.asyncSuccess(imageKnifeData) } } } - // 加载失败 占位图解析失败 - errorholderOnError = (error:BusinessError|string)=> { + errorholderOnError = (error: BusinessError | string) => { LogUtil.log("失败占位图解析失败 error =" + error) } - - retryholderOnComplete = (imageKnifeData: ImageKnifeData)=>{ + retryholderOnComplete = (imageKnifeData:ImageKnifeData) => { this.retryholderData = imageKnifeData; if (this.loadRetryReady) { - if(this.retryholderFunc != undefined) { + if (this.retryholderFunc != undefined) { this.retryholderFunc?.asyncSuccess(imageKnifeData) } } } - - retryholderOnError = (error:BusinessError|string)=>{ + retryholderOnError = (error: BusinessError | string) => { LogUtil.log("重试占位图解析失败 error =" + error) } - - loadComplete = (imageKnifeData: ImageKnifeData)=>{ + //占位图加载失败 后备回调符解析成功 + fallbackOnComplete = (imageKnifeData:ImageKnifeData) => { + this.fallbackData = imageKnifeData; + if (this.loadFallBackReady) { + if (this.fallbackFunc != undefined) { + this.fallbackFunc?.asyncSuccess(imageKnifeData); + } + } + } + // 后备回调符解析失败 + fallbackOnError = (error: BusinessError | string) => { + LogUtil.error("失败占位图解析失败 error =" + JSON.stringify(error)); + } + loadComplete = (imageKnifeData: ImageKnifeData) => { this.loadMainReady = true; // 三级缓存数据加载成功 - if(this.requestListeners != undefined) { - for (let i = 0;i < this.requestListeners.length; i++) { + if (this.requestListeners != undefined) { + for (let i = 0; i < this.requestListeners.length; i++) { let requestListener = this.requestListeners[i]; let boolInterception = requestListener.callback("", imageKnifeData); if (boolInterception) { @@ -518,32 +587,52 @@ export class RequestOption { } } } + //输出缓存相关内容和信息 + if (this.allCacheInfoCallback) { + // 内存缓存 + let allCacheInfo: AllCacheInfo = { + memoryCacheInfo: { key: '', data: new ImageKnifeData() }, + resourceCacheInfo: { key: '', path: '' }, + dataCacheInfo: { key: '', path: '' } + }; + allCacheInfo.memoryCacheInfo = { + key: this.generateCacheKey, + data: imageKnifeData + } + let mDiskCacheProxy = new DiskCacheProxy(DiskLruCache.create(ImageKnifeGlobal.getInstance() + .getHapContext() as common.UIAbilityContext)) + // 变换后缓存 + allCacheInfo.resourceCacheInfo = { + key: SparkMD5.hashBinary(this.generateResourceKey) as string, + path: (mDiskCacheProxy.getCachePath() + SparkMD5.hashBinary(this.generateResourceKey)) as string + }; - if(imageKnifeData.waitSaveDisk){ + // 原图缓存 + allCacheInfo.dataCacheInfo = { + key: SparkMD5.hashBinary(this.generateDataKey) as string, + path: (mDiskCacheProxy.getCachePath() + SparkMD5.hashBinary(this.generateDataKey)) as string + } + this.allCacheInfoCallback.callback(allCacheInfo); + } + + if (imageKnifeData.waitSaveDisk) { // 等落盘结束后主动调用#removeCurrentAndSearchNext方法 - }else{ + } else { // 非落盘情况,直接进行寻找下一个加载 - let imageKnife:ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife(); - if(imageKnife != undefined) { + let imageKnife: ImageKnife | undefined = ImageKnifeGlobal.getInstance().getImageKnife(); + if (imageKnife != undefined) { imageKnife.removeRunning(this); } } - - - - - - } // 图片文件落盘之后会自动去寻找下一个数据加载 - removeCurrentAndSearchNext =()=>{ - if(ImageKnifeGlobal.getInstance().getImageKnife() != undefined) { + removeCurrentAndSearchNext = () => { + if (ImageKnifeGlobal.getInstance().getImageKnife() != undefined) { (ImageKnifeGlobal.getInstance().getImageKnife())?.removeRunning(this); } } - - loadError = (err:BusinessError|string)=>{ + loadError = (err: BusinessError | string) => { LogUtil.log("loadError:" + err); // 失败占位图展示规则 if (this.retryholderFunc) { @@ -552,17 +641,32 @@ export class RequestOption { if (this.retryholderData != null) { this.retryholderFunc.asyncSuccess(this.retryholderData) } + } else if (!this.retryholderFunc && !this.placeholderFunc && this.fallbackFunc) { + this.loadFallBackReady = true; + if (this.fallbackData != null) { + this.fallbackFunc.asyncSuccess(this.fallbackData); + } } else { // 失败图层标记,如果已经有数据直接展示失败图层 this.loadErrorReady = true; if (this.errorholderData != null) { - if(this.errorholderFunc != undefined) { + if (this.errorholderFunc != undefined) { this.errorholderFunc.asyncSuccess(this.errorholderData) } } } + + if (this.requestListeners != undefined) { + for (let i = 0; i < this.requestListeners.length; i++) { + let requestListener = this.requestListeners[i]; + let boolInterception = requestListener.callback(err as string, new ImageKnifeData()); + if (boolInterception) { + break; + } + } + } // 加载失败之后 - if(ImageKnifeGlobal.getInstance().getImageKnife() != undefined) { + if (ImageKnifeGlobal.getInstance().getImageKnife() != undefined) { (ImageKnifeGlobal.getInstance().getImageKnife())?.removeRunning(this); } } diff --git a/library/src/main/ets/components/imageknife/SendableData.ets b/library/src/main/ets/components/imageknife/SendableData.ets new file mode 100644 index 0000000..c910e86 --- /dev/null +++ b/library/src/main/ets/components/imageknife/SendableData.ets @@ -0,0 +1,197 @@ +/* + * 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 { ObjectKey } from './ObjectKey'; +import common from '@ohos.app.ability.common' +import { collections } from '@kit.ArkTS'; +import { IDataFetch } from './networkmanage/IDataFetch'; +import { DiskLruCache } from '../cache/DiskLruCache'; +import { DownloadClient } from './networkmanage/DownloadClient'; + +@Sendable +export class SendableData{ + + private usageType: string = ''; + private displayProgress: boolean = false + private uuid: string = '' // 唯一标识 + private dontAnimateFlag: boolean = false; + private thumbSizeMultiplier: number = 0; + private thumbDelayTime: number = 1000; + private onlyRetrieveFromCache: boolean = false; + private isCacheable: boolean = true; + private gpuEnabled: boolean = false; + private generateCacheKey: string = ""; + private generateResourceKey: string = ""; + private generateDataKey: string = ""; + private signature?: ObjectKey; + private transformations: collections.Array> = new collections.Array>(); + private diskMemoryCachePath: string = ''; + private diskMemoryCache?: DiskLruCache; + private dataFetch: IDataFetch = new DownloadClient(); + private placeholderRegisterCacheKey: string = ""; + private placeholderRegisterMemoryCacheKey: string = ""; + + public setDataFetch(value: IDataFetch) { + this.dataFetch = value; + } + + public getDataFetch(): IDataFetch{ + return this.dataFetch; + } + + public setDiskMemoryCachePath(value: string) { + this.diskMemoryCachePath = value; + } + + public getDiskMemoryCachePath(): string{ + return this.diskMemoryCachePath; + } + + + public setDiskMemoryCache(value: DiskLruCache) { + this.diskMemoryCache = value; + } + + public getDiskMemoryCache(): DiskLruCache | undefined{ + return this.diskMemoryCache; + } + + public setTransformations(value: collections.Array>) { + this.transformations = value; + } + + public getTransformations(): collections.Array>{ + return this.transformations; + } + + public setSignature(value: ObjectKey | undefined) { + this.signature = value; + } + + public getSignature(): ObjectKey | undefined{ + return this.signature; + } + + public setGenerateDataKey(value: string) { + this.generateDataKey = value; + } + + public getGenerateDataKey(): string { + return this.generateDataKey; + } + + public setGenerateResourceKey(value: string) { + this.generateResourceKey = value; + } + + public getGenerateResourceKey(): string { + return this.generateResourceKey; + } + + public setGenerateCacheKey(value: string) { + this.generateCacheKey = value; + } + + public getGenerateCacheKey(): string { + return this.generateCacheKey; + } + + public setGpuEnabled(value: boolean) { + this.gpuEnabled = value; + } + + public getGpuEnabled(): boolean { + return this.gpuEnabled; + } + + public setIsCacheable(value: boolean) { + this.isCacheable = value; + } + + public getIsCacheable(): boolean { + return this.isCacheable; + } + + public setOnlyRetrieveFromCache(value: boolean) { + this.onlyRetrieveFromCache = value; + } + + public getOnlyRetrieveFromCache(): boolean { + return this.onlyRetrieveFromCache; + } + + public setThumbDelayTime(value: number) { + this.thumbDelayTime = value; + } + + public getThumbDelayTime(): number { + return this.thumbDelayTime; + } + + public setThumbSizeMultiplier(value: number) { + this.thumbSizeMultiplier = value; + } + + public getThumbSizeMultiplier(): number { + return this.thumbSizeMultiplier; + } + + public setDontAnimateFlag(value: boolean) { + this.dontAnimateFlag = value; + } + + public getDontAnimateFlag(): boolean { + return this.dontAnimateFlag; + } + + public setUuid(value: string) { + this.uuid = value; + } + + public getUuid(): string { + return this.uuid; + } + + public setDisplayProgress(value: boolean) { + this.displayProgress = value; + } + + public getDisplayProgress(): boolean { + return this.displayProgress; + } + + public setUsageType(value: string) { + this.usageType = value; + } + + public getUsageType(): string { + return this.usageType; + } + + public setPlaceHolderRegisterCacheKey(value: string) { + this.placeholderRegisterCacheKey = value; + } + + public getPlaceHolderRegisterCacheKey(): string { + return this.placeholderRegisterCacheKey; + } + + public setPlaceHolderRegisterMemoryCacheKey(value: string) { + this.placeholderRegisterMemoryCacheKey = value; + } + + public getPlaceHolderRegisterMemoryCacheKey(): string { + return this.placeholderRegisterMemoryCacheKey; + } +} \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/TaskParams.ets b/library/src/main/ets/components/imageknife/TaskParams.ets new file mode 100644 index 0000000..51aaa2b --- /dev/null +++ b/library/src/main/ets/components/imageknife/TaskParams.ets @@ -0,0 +1,32 @@ +/* + * 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 { ObjectKey } from './ObjectKey'; +import { Priority, Size } from '../imageknife/RequestOption' +import common from '@ohos.app.ability.common' +import { MResource } from './utils/MResource'; +import { DataFetchResult } from './networkmanage/DataFetchResult'; + +export class TaskParams { + headers: Map = new Map(); + moduleContext?: common.UIAbilityContext = undefined; + priority: Priority = Priority.MEDIUM // 优先级 + size: Size = { width: -1, height: -1 }; + loadSrc: string | PixelMap | MResource = ""; + placeholderSrc: string | PixelMap | MResource | undefined = undefined; + errorholderSrc: PixelMap | MResource | undefined = undefined; + retryholderSrc: PixelMap | MResource | undefined = undefined; + fallbackSrc: PixelMap | MResource | undefined = undefined; + customGetImage?: (context: Context, loadSrc: string) => Promise; +} \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/constants/Constants.ets b/library/src/main/ets/components/imageknife/constants/Constants.ets index e9a5b77..de09ddb 100644 --- a/library/src/main/ets/components/imageknife/constants/Constants.ets +++ b/library/src/main/ets/components/imageknife/constants/Constants.ets @@ -14,5 +14,11 @@ */ export class Constants { - public static PROJECT_TAG: string= "ImageKnife_js" + public static PROJECT_TAG: string = "ImageKnife_js" + public static PROGRESS_EMITTER: string = "progressEmitter" + public static PLACE_HOLDER: string = "placeholder" + public static RETRY_HOLDER: string = "retryholder" + public static ERROR_HOLDER: string = "errorholder" + public static MAIN_HOLDER: string = "main" + public static FALL_BACK: string = "fallback" } \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/holder/ErrorHolderManager.ets b/library/src/main/ets/components/imageknife/holder/ErrorHolderManager.ets index 885f67c..68d3fe6 100644 --- a/library/src/main/ets/components/imageknife/holder/ErrorHolderManager.ets +++ b/library/src/main/ets/components/imageknife/holder/ErrorHolderManager.ets @@ -31,21 +31,15 @@ export class ErrorHolderManager { this.options = option; } - static execute(option: RequestOption) { - let manager:ErrorHolderManager = new ErrorHolderManager(option); - return new Promise(manager.process) - .then(option.errorholderOnComplete).catch(option.errorholderOnError); - } - - process = (onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void)=>{ + process = (onComplete:(value:PixelMap)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void)=>{ this.displayErrorholder(onComplete, onError); } - private displayErrorholder(onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void) { + private displayErrorholder(onComplete:(value:PixelMap)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void) { LogUtil.log("displayErrorholder") if ((typeof (this.options.errorholderSrc as image.PixelMap).isEditable) == 'boolean') { let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, this.options.errorholderSrc as PixelMap) - onComplete(imageKnifeData); + onComplete(imageKnifeData?.drawPixelMap?.imagePixelMap as PixelMap); } else if (typeof this.options.errorholderSrc == 'string') { } else { @@ -67,6 +61,7 @@ export class ErrorHolderManager { case SupportFormat.gif: case SupportFormat.tiff: case SupportFormat.webp: + case SupportFormat.heic: this.mediaImageProcess(onComplete, onError, arraybuffer, typeValue) break; default: @@ -89,16 +84,16 @@ export class ErrorHolderManager { } } - private svgProcess(onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void, arraybuffer:ArrayBuffer, typeValue:string) { + private svgProcess(onComplete:(value:PixelMap)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void, arraybuffer:ArrayBuffer, typeValue:string) { let svgParseImpl:SVGParseImpl = new SVGParseImpl() svgParseImpl.parseSvg(this.options,arraybuffer, onComplete,onError); } - private mediaImageProcess(onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void, arraybuffer:ArrayBuffer, typeValue:string) { + private mediaImageProcess(onComplete:(value:PixelMap)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void, arraybuffer:ArrayBuffer, typeValue:string) { let parseImageUtil = new ParseImageUtil() let success = (value: PixelMap) => { let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, value) - onComplete(imageKnifeData) + onComplete(imageKnifeData?.drawPixelMap?.imagePixelMap as PixelMap) } parseImageUtil.parseImage(arraybuffer, success, onError) } diff --git a/library/src/main/ets/components/imageknife/holder/PlaceHolderManager.ets b/library/src/main/ets/components/imageknife/holder/PlaceHolderManager.ets index ad98cb5..d6769d7 100644 --- a/library/src/main/ets/components/imageknife/holder/PlaceHolderManager.ets +++ b/library/src/main/ets/components/imageknife/holder/PlaceHolderManager.ets @@ -25,6 +25,9 @@ import {LogUtil} from '../../imageknife/utils/LogUtil' import resourceManager from '@ohos.resourceManager'; import image from "@ohos.multimedia.image" import { BusinessError } from '@ohos.base' +import { GIFFrame } from '../utils/gif/GIFFrame' +import { DiskLruCache } from '../../cache/DiskLruCache' + export class PlaceHolderManager { private options: RequestOption; @@ -32,23 +35,84 @@ export class PlaceHolderManager { this.options = option; } - static execute(option: RequestOption) { - let manager:PlaceHolderManager = new PlaceHolderManager(option); - return new Promise(manager.process) - .then(option.placeholderOnComplete).catch(option.placeholderOnError); - } - process = (onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void)=>{ + process = (onComplete:(value:PixelMap)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void)=>{ this.displayPlaceholder(onComplete, onError); } - private displayPlaceholder(onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void){ - LogUtil.log("displayPlaceholder") + private displayPlaceholder(onComplete:(value:PixelMap)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void){ + LogUtil.log("ImageKnife displayPlaceholder") if ((typeof (this.options.placeholderSrc as image.PixelMap).isEditable) == 'boolean') { let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, this.options.placeholderSrc as PixelMap) - onComplete(imageKnifeData); + onComplete(imageKnifeData?.drawPixelMap?.imagePixelMap as PixelMap); } else if (typeof this.options.placeholderSrc == 'string') { - + if (typeof this.options.placeholderCacheKey == 'string' && this.options.placeholderCacheKey != '') { + return; + } + let cached = DiskLruCache.getFileCacheByFile(this.options.diskMemoryCachePath,this.options.placeholderRegisterCacheKey); + if (cached != null && cached.byteLength > 0) { + let fileTypeUtil = new FileTypeUtil(); + let typeValue = fileTypeUtil.getFileType(cached); + switch (typeValue) { + case SupportFormat.svg: + this.svgProcess(onComplete, onError, cached, typeValue); + break; + case SupportFormat.jpg: + case SupportFormat.png: + case SupportFormat.bmp: + case SupportFormat.gif: + case SupportFormat.tiff: + case SupportFormat.webp: + case SupportFormat.heic: + this.mediaImageProcess(onComplete, onError, cached, typeValue); + break; + default: + onError("PlaceHolderManager 文件类型不支持"); + break; + } + } else { + if ((typeof (this.options.fallbackSrc as image.PixelMap).isEditable) == 'boolean') { + let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, this.options.fallbackSrc as PixelMap); + onComplete(imageKnifeData?.drawPixelMap?.imagePixelMap as PixelMap); + } else { + let res = this.options.fallbackSrc as Resource; + if (typeof res.id != 'undefined' && typeof res.id != 'undefined') { + let resourceFetch = new ParseResClient(); + let suc = (arraybuffer:ArrayBuffer) => { + let fileTypeUtil: FileTypeUtil = new FileTypeUtil(); + let typeValue: string | null = fileTypeUtil.getFileType(arraybuffer); + if (typeValue != null) { + switch (typeValue) { + case SupportFormat.svg: + this.svgProcess(onComplete, onError, arraybuffer, typeValue); + break; + case SupportFormat.jpg: + case SupportFormat.png: + case SupportFormat.bmp: + case SupportFormat.gif: + case SupportFormat.tiff: + case SupportFormat.webp: + this.mediaImageProcess(onComplete, onError, arraybuffer, typeValue); + break; + default: + onError("FallBackManager 文件类型不支持"); + break; + } + }else{ + onError("FallBackManager 文件类型为null,请检查数据源arraybuffer"); + } + } + let ctx = this.options.getModuleContext(); + if(ctx != undefined){ + resourceFetch.loadResource(ctx,res, suc, onError); + }else{ + onError("FallBackManager moduleContext is undefined,please check it!"); + } + } else { + onError("FallBackManager 输入参数有问题!"); + } + } + } } else { let res = this.options.placeholderSrc as Resource; if (typeof res.id != 'undefined' && typeof res.id != 'undefined') { @@ -66,6 +130,7 @@ export class PlaceHolderManager { case SupportFormat.gif: case SupportFormat.tiff: case SupportFormat.webp: + case SupportFormat.heic: this.mediaImageProcess(onComplete, onError, arraybuffer, typeValue) break; default: @@ -87,16 +152,16 @@ export class PlaceHolderManager { - private svgProcess(onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void, arraybuffer:ArrayBuffer, typeValue:string) { + private svgProcess(onComplete:(value:PixelMap)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void, arraybuffer:ArrayBuffer, typeValue:string) { let svgParseImpl:SVGParseImpl = new SVGParseImpl() svgParseImpl.parseSvg(this.options,arraybuffer, onComplete,onError); } - private mediaImageProcess(onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void, arraybuffer:ArrayBuffer, typeValue:string) { + private mediaImageProcess(onComplete:(value:PixelMap)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void, arraybuffer:ArrayBuffer, typeValue:string) { let parseImageUtil:ParseImageUtil = new ParseImageUtil() let success = (value: PixelMap) => { let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, value) - onComplete(imageKnifeData) + onComplete(imageKnifeData?.drawPixelMap?.imagePixelMap as PixelMap) } parseImageUtil.parseImage(arraybuffer, success, onError) } diff --git a/library/src/main/ets/components/imageknife/holder/RetryHolderManager.ets b/library/src/main/ets/components/imageknife/holder/RetryHolderManager.ets index d499f7a..86abf27 100644 --- a/library/src/main/ets/components/imageknife/holder/RetryHolderManager.ets +++ b/library/src/main/ets/components/imageknife/holder/RetryHolderManager.ets @@ -32,21 +32,15 @@ export class RetryHolderManager { this.options = option; } - static execute(option: RequestOption) { - let manager:RetryHolderManager = new RetryHolderManager(option); - return new Promise(manager.process) - .then(option.retryholderOnComplete).catch(option.retryholderOnError); - } - - process = (onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void)=>{ + process = (onComplete:(value:PixelMap)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void)=>{ this.displayRetryholder(onComplete, onError); } - private displayRetryholder(onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void){ - LogUtil.log("displayRetryholder") + private displayRetryholder(onComplete:(value:PixelMap)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void){ + LogUtil.log("ImageKnife displayRetryholder") if ((typeof (this.options.retryholderSrc as image.PixelMap).isEditable) == 'boolean') { let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, this.options.placeholderSrc as PixelMap) - onComplete(imageKnifeData); + onComplete(imageKnifeData?.drawPixelMap?.imagePixelMap as PixelMap); } else if (typeof this.options.placeholderSrc == 'string') { } else { @@ -66,6 +60,7 @@ export class RetryHolderManager { case SupportFormat.gif: case SupportFormat.tiff: case SupportFormat.webp: + case SupportFormat.heic: this.mediaImageProcess(onComplete, onError, arraybuffer, typeValue) break; default: @@ -87,16 +82,16 @@ export class RetryHolderManager { - private svgProcess(onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void, arraybuffer:ArrayBuffer, typeValue:string) { + private svgProcess(onComplete:(value:PixelMap)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void, arraybuffer:ArrayBuffer, typeValue:string) { let svgParseImpl = new SVGParseImpl() svgParseImpl.parseSvg(this.options,arraybuffer, onComplete,onError); } - private mediaImageProcess(onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void, arraybuffer:ArrayBuffer, typeValue:string) { + private mediaImageProcess(onComplete:(value:PixelMap)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void, arraybuffer:ArrayBuffer, typeValue:string) { let parseImageUtil = new ParseImageUtil() let success = (value: PixelMap) => { let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, value) - onComplete(imageKnifeData) + onComplete(imageKnifeData?.drawPixelMap?.imagePixelMap as PixelMap) } parseImageUtil.parseImage(arraybuffer, success, onError) } diff --git a/library/src/main/ets/components/imageknife/networkmanage/CustomDataFetchClient.ets b/library/src/main/ets/components/imageknife/networkmanage/CustomDataFetchClient.ets new file mode 100644 index 0000000..db50dd1 --- /dev/null +++ b/library/src/main/ets/components/imageknife/networkmanage/CustomDataFetchClient.ets @@ -0,0 +1,113 @@ +/* + * 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 { IDataFetch } from '../networkmanage/IDataFetch' +import { RequestOption } from '../RequestOption' +import { RequestData } from './RequestData' +import { ImageKnifeGlobal } from '../ImageKnifeGlobal' +import common from '@ohos.app.ability.common' +import http from '@ohos.net.http' +import { DataFetchResult } from './DataFetchResult' + +@Sendable +export class CustomDataFetchClient implements IDataFetch { + async loadData(request: RequestOption) { + let result: DataFetchResult = new DataFetchResult(); + if (!request || typeof request.loadSrc !== 'string') { + result.error = 'CustomDataFetchClient request or loadSrc error.'; + return result; + } + // 自定义单个图片的网络栈 + if (request.customGetImage) { + return await request.customGetImage(ImageKnifeGlobal.getInstance() + .getHapContext() as common.UIAbilityContext, request.loadSrc); + } + // 所有图片的网络栈 + try { + let httpRequest = http.createHttp() + let arrayBuffers = new Array(); + httpRequest.on('headersReceive', (header: Object) => { + // 跟服务器连接成功准备下载 + if (request.progressFunc) { + // 进度条为0 + request.progressFunc.asyncSuccess(0) + } + }) + httpRequest.on('dataReceive', (data: ArrayBuffer) => { + // 下载数据流多次返回 + arrayBuffers.push(data); + }) + 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 (request.progressFunc) { + request.progressFunc.asyncSuccess(percent) + } + } + }) + httpRequest.on('dataEnd', () => { + // 下载完毕 + }) + const headerObj: Record = {} + request.headers.forEach((value, key) => { + headerObj[key] = value + }) + const data = await httpRequest.requestInStream(request.loadSrc as string, { + header: headerObj, + method: http.RequestMethod.GET, + expectDataType: http.HttpDataType.ARRAY_BUFFER, + connectTimeout: 60000, // 可选 默认60000ms + readTimeout: 0, // 可选, 默认为60000ms + usingProtocol: http.HttpProtocol.HTTP1_1, // 可选,协议类型默认值由系统自动指定 + usingCache: false + }).catch((err: Error) => { + result.error = 'CustomDataFetchClient has error, http code = ' + JSON.stringify(err); + }) + if (data == 200) { + result.data = this.combineArrayBuffers(arrayBuffers); + } else { + result.error = 'CustomDataFetchClient resultCode error, code = ' + data; + } + console.log('TestCustomDataFetch all onComplete, code = ' + data + ',length = ' + result.data?.byteLength); + } catch (err) { + result.error = 'CustomDataFetchClient catch err request uuid =' + request.uuid; + } + return result; + } + + 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/components/imageknife/networkmanage/DataFetchResult.ets b/library/src/main/ets/components/imageknife/networkmanage/DataFetchResult.ets new file mode 100644 index 0000000..76381a2 --- /dev/null +++ b/library/src/main/ets/components/imageknife/networkmanage/DataFetchResult.ets @@ -0,0 +1,19 @@ +/* + * 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. + */ + +export class DataFetchResult{ + data?: ArrayBuffer; + error?: string; +} \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/networkmanage/DownloadClient.ets b/library/src/main/ets/components/imageknife/networkmanage/DownloadClient.ets index 94d2092..c769b17 100644 --- a/library/src/main/ets/components/imageknife/networkmanage/DownloadClient.ets +++ b/library/src/main/ets/components/imageknife/networkmanage/DownloadClient.ets @@ -23,29 +23,42 @@ import loadRequest from '@ohos.request'; import { ImageKnifeGlobal } from '../ImageKnifeGlobal' import common from '@ohos.app.ability.common' import { NetworkDownloadClient } from './NetworkDownloadClient' +import { DataFetchResult } from './DataFetchResult' // 数据加载器 +@Sendable export class DownloadClient implements IDataFetch { - private networkDownloadClient = new NetworkDownloadClient(); - private httpDownloadClient = new HttpDownloadClient(); - private localFileClient = new LoadLocalFileClient(); - private dataShareFileClient = new LoadDataShareFileClient(); + private networkDownloadClient:NetworkDownloadClient = new NetworkDownloadClient(); + private httpDownloadClient:HttpDownloadClient = new HttpDownloadClient(); + private localFileClient:LoadLocalFileClient = new LoadLocalFileClient(); + private dataShareFileClient:LoadDataShareFileClient = new LoadDataShareFileClient(); - loadData(request: RequestOption, onCompleteFunction:(img: ArrayBuffer) => void, onErrorFunction: (err: string) => void) { + async loadData(request: RequestOption): Promise { if (typeof request.loadSrc == 'string') { - let fileDir:string = (ImageKnifeGlobal.getInstance().getHapContext() as common.UIAbilityContext).filesDir as string; - let cacheDir:string = (ImageKnifeGlobal.getInstance().getHapContext() as common.UIAbilityContext).cacheDir as string - - if (request.loadSrc.startsWith(fileDir) || - request.loadSrc.startsWith(cacheDir)) { + if (this.isLocalLoadSrc(ImageKnifeGlobal.getInstance().getHapContext(), request.loadSrc)) { // 本地沙盒 - this.localFileClient.loadData(request, onCompleteFunction, onErrorFunction) + return this.localFileClient.loadData(request) } else if (request.loadSrc.startsWith('datashare://') || request.loadSrc.startsWith('file://')) { - this.dataShareFileClient.loadData(request, onCompleteFunction, onErrorFunction) + return this.dataShareFileClient.loadData(request) } else { // 网络下载 - this.httpDownloadClient.loadData(request, onCompleteFunction, onErrorFunction) + return await this.httpDownloadClient.loadData(request); } + }else{ + let result:DataFetchResult = new DataFetchResult() + result.error ="参数错误!"; + return result; } } + + isLocalLoadSrc(context: Object | undefined, loadSrc: string): boolean { + if (context != undefined) { + let fileDir: string = (context as common.UIAbilityContext).filesDir as string; + let cacheDir: string = (context as common.UIAbilityContext).cacheDir as string + if (loadSrc.startsWith(fileDir) || loadSrc.startsWith(cacheDir)) { + return true; + } + } + return false; + } } \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/networkmanage/HttpDownloadClient.ets b/library/src/main/ets/components/imageknife/networkmanage/HttpDownloadClient.ets index 7b823c9..c54f6e5 100644 --- a/library/src/main/ets/components/imageknife/networkmanage/HttpDownloadClient.ets +++ b/library/src/main/ets/components/imageknife/networkmanage/HttpDownloadClient.ets @@ -13,76 +13,68 @@ * limitations under the License. */ -import { IDataFetch } from '../networkmanage/IDataFetch' +import { IDataFetch } from '../networkmanage/IDataFetch' import { RequestOption } from '../RequestOption' -import { SparkMD5 } from '../../3rd_party/sparkmd5/spark-md5' -import { FileUtils } from '../../cache/FileUtils' -import loadRequest from '@ohos.request'; -import { LogUtil } from '../utils/LogUtil' -import { ImageKnifeGlobal } from '../ImageKnifeGlobal' -import common from '@ohos.app.ability.common' -import { BusinessError } from '@ohos.base' import http from '@ohos.net.http' -// 数据加载器 -class RequestData{ - receiveSize: number = 2000 - totalSize: number = 2000 -} +import { RequestData } from './RequestData' +import { DataFetchResult } from './DataFetchResult' + +@Sendable export class HttpDownloadClient implements IDataFetch { - loadData(request: RequestOption, onComplete: (img: ArrayBuffer) => void, onError: (err: string) => void) { - try { - let httpRequest = http.createHttp() - let arrayBuffers = new Array(); - httpRequest.on('headersReceive', (header: Object) => { - // 跟服务器连接成功准备下载 - if (request.progressFunc) { - // 进度条为0 - request.progressFunc.asyncSuccess(0) - } - }) - httpRequest.on('dataReceive', (data: ArrayBuffer) => { - // 下载数据流多次返回 - arrayBuffers.push(data); - }) - - 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 (request.progressFunc) { - request.progressFunc.asyncSuccess(percent) - } - } - }) - - httpRequest.on('dataEnd', () => { - // 下载完毕 - let combineArray = this.combineArrayBuffers(arrayBuffers); - onComplete(combineArray) - }) - - httpRequest.requestInStream( - request.loadSrc as string, - { - method: http.RequestMethod.GET, - expectDataType: http.HttpDataType.ARRAY_BUFFER, - connectTimeout: 60000, // 可选 默认60000ms - readTimeout: 0, // 可选, 默认为60000ms - usingProtocol: http.HttpProtocol.HTTP1_1, // 可选,协议类型默认值由系统自动指定 - } - ).then((data)=>{ - if(data == 200) { - - } else { - onError(`HttpDownloadClient has error, http code = ${data}`) - } - }).catch((err:Error)=>{ - onError(`HttpDownloadClient has error, http code = ${err}`) - }) - } catch (err) { - onError('HttpDownloadClient catch err request uuid ='+request.uuid) - } + async loadData(request: RequestOption) { + let result:DataFetchResult = new DataFetchResult(); + try { + let httpRequest = http.createHttp() + let arrayBuffers = new Array(); + httpRequest.on('headersReceive', (header: Object) => { + // 跟服务器连接成功准备下载 + if (request.progressFunc) { + // 进度条为0 + request.progressFunc.asyncSuccess(0) + } + }) + httpRequest.on('dataReceive', (data: ArrayBuffer) => { + // 下载数据流多次返回 + arrayBuffers.push(data); + }) + 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 (request.progressFunc) { + request.progressFunc.asyncSuccess(percent) + } + } + }) + httpRequest.on('dataEnd', () => { + // 下载完毕 + }) + const headerObj: Record = {} + request.headers.forEach((value, key) => { + headerObj[key] = value + }) + const data = await httpRequest.requestInStream(request.loadSrc as string, { + header: headerObj, + method: http.RequestMethod.GET, + expectDataType: http.HttpDataType.ARRAY_BUFFER, + connectTimeout: 60000, // 可选 默认60000ms + readTimeout: 0, // 可选, 默认为60000ms + usingProtocol: http.HttpProtocol.HTTP1_1, // 可选,协议类型默认值由系统自动指定 + usingCache: false + }).catch((err: Error) => { + result.error = `HttpDownloadClient has error, http code = ` + JSON.stringify(err); + }) + if ( data == 200) { + result.data = this.combineArrayBuffers(arrayBuffers); + } else { + result.error = `HttpDownloadClient has error, http code = ` + JSON.stringify(data); + } + console.log('TestCustomDataFetch http onComplete, code = ' + data + ',length = ' + result.data?.byteLength); + } catch (err) { + result.error ='HttpDownloadClient catch err request uuid =' + request.uuid; + } + return result; } combineArrayBuffers(arrayBuffers: ArrayBuffer[]): ArrayBuffer { diff --git a/library/src/main/ets/components/imageknife/networkmanage/IDataFetch.ets b/library/src/main/ets/components/imageknife/networkmanage/IDataFetch.ets index 1fafeb0..4121ceb 100644 --- a/library/src/main/ets/components/imageknife/networkmanage/IDataFetch.ets +++ b/library/src/main/ets/components/imageknife/networkmanage/IDataFetch.ets @@ -14,8 +14,11 @@ */ import { RequestOption } from '../RequestOption' +import lang from '@arkts.lang'; +import { DataFetchResult } from './DataFetchResult'; +type ISendable = lang.ISendable; // 资源加载接口 -export interface IDataFetch { - loadData:(request: RequestOption, onComplete: (img: ArrayBuffer) => void, onError: (err: string) => void)=>void; +export interface IDataFetch extends ISendable{ + loadData(request: RequestOption): DataFetchResult|Promise; } \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/networkmanage/LoadDataShareFileClient.ets b/library/src/main/ets/components/imageknife/networkmanage/LoadDataShareFileClient.ets index e080a67..acde8af 100644 --- a/library/src/main/ets/components/imageknife/networkmanage/LoadDataShareFileClient.ets +++ b/library/src/main/ets/components/imageknife/networkmanage/LoadDataShareFileClient.ets @@ -16,25 +16,29 @@ import { IDataFetch } from '../networkmanage/IDataFetch' import { RequestOption } from '../RequestOption' import fs from '@ohos.file.fs'; import { BusinessError } from '@ohos.base' +import { DataFetchResult } from './DataFetchResult'; +@Sendable export class LoadDataShareFileClient implements IDataFetch { - loadData(request: RequestOption, onComplete: (img: ArrayBuffer) => void, onError: (err: string) => void) { + loadData(request: RequestOption) { + let result:DataFetchResult =new DataFetchResult() if (typeof request.loadSrc == 'string') { fs.open(request.loadSrc, fs.OpenMode.READ_ONLY).then((file) => { fs.stat(file.fd).then(stat =>{ let buf = new ArrayBuffer(stat.size); fs.read(file.fd, buf).then((readLen) => { - onComplete(buf); + result.data=buf; fs.close(file.fd); }).catch((err:BusinessError) => { - onError('LoadDataShareFileClient fs.read err happened uri=' + request.loadSrc + " err.msg=" + err?.message + " err.code=" + err?.code) + result.error = 'LoadDataShareFileClient fs.read err happened uri=' + request.loadSrc + " err.msg=" + err?.message + " err.code=" + err?.code; }) }).catch((err:BusinessError) => { - onError('LoadDataShareFileClient fs.stat err happened uri=' + request.loadSrc + " err.msg=" + err?.message + " err.code=" + err?.code) + result.error = 'LoadDataShareFileClient fs.stat err happened uri=' + request.loadSrc + " err.msg=" + err?.message + " err.code=" + err?.code; }) }).catch((err:BusinessError) => { - onError('LoadDataShareFileClient fs.open err happened uri=' + request.loadSrc + " err.msg=" + err?.message + " err.code=" + err?.code) + result.error ='LoadDataShareFileClient fs.open err happened uri=' + request.loadSrc + " err.msg=" + err?.message + " err.code=" + err?.code; }) } + return result; } } \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/networkmanage/LoadLocalFileClient.ets b/library/src/main/ets/components/imageknife/networkmanage/LoadLocalFileClient.ets index 89240af..59bdf86 100644 --- a/library/src/main/ets/components/imageknife/networkmanage/LoadLocalFileClient.ets +++ b/library/src/main/ets/components/imageknife/networkmanage/LoadLocalFileClient.ets @@ -17,19 +17,23 @@ import { IDataFetch } from '../networkmanage/IDataFetch' import { RequestOption } from '../RequestOption' import { FileUtils } from '../../cache/FileUtils' import { BusinessError } from '@ohos.base' +import { DataFetchResult } from './DataFetchResult' + +@Sendable export class LoadLocalFileClient implements IDataFetch { - loadData(request: RequestOption, onComplete: (img: ArrayBuffer) => void, onError: (err: string) => void) { + loadData(request: RequestOption) { + let result:DataFetchResult =new DataFetchResult() if (typeof request.loadSrc == 'string') { FileUtils.getInstance().readFilePicAsync(request.loadSrc).then(fileBuffer=>{ if (fileBuffer == null || fileBuffer.byteLength <= 0) { - onError('LoadLocalFileClient loadLocalFileData The File Does Not Exist!Check The File!') + result.error = 'LoadLocalFileClient loadLocalFileData The File Does Not Exist!Check The File!'; } else { - onComplete(fileBuffer); + result.data = fileBuffer } }).catch((err:BusinessError)=>{ - onError('LoadLocalFileClient loadLocalFileData Error Msg ='+err?.message) + result.error ='LoadLocalFileClient loadLocalFileData Error Msg ='+err?.message; }) - } + return result; } } \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/networkmanage/NetworkDownloadClient.ets b/library/src/main/ets/components/imageknife/networkmanage/NetworkDownloadClient.ets index ae7d9c5..1fcceaf 100644 --- a/library/src/main/ets/components/imageknife/networkmanage/NetworkDownloadClient.ets +++ b/library/src/main/ets/components/imageknife/networkmanage/NetworkDownloadClient.ets @@ -19,12 +19,16 @@ import { SparkMD5 } from '../../3rd_party/sparkmd5/spark-md5' import { FileUtils } from '../../cache/FileUtils' import loadRequest from '@ohos.request'; import { LogUtil } from '../utils/LogUtil' -import { ImageKnifeGlobal } from '../ImageKnifeGlobal' import common from '@ohos.app.ability.common' import { BusinessError } from '@ohos.base' +import { DataFetchResult } from './DataFetchResult' + // 数据加载器 + +@Sendable export class NetworkDownloadClient implements IDataFetch { - loadData(request: RequestOption, onComplete: (img: ArrayBuffer) => void, onError: (err: string) => void) { + loadData(request: RequestOption) { + let result:DataFetchResult = new DataFetchResult() let filename:string = SparkMD5.hashBinary(request.generateDataKey); let downloadFolder = request.getFilesPath() + "/" + request.networkCacheFolder; let allpath = request.getFilesPath() + "/" + request.networkCacheFolder + "/" + filename + ".img"; @@ -61,14 +65,14 @@ export class NetworkDownloadClient implements IDataFetch { let downloadPath = allpath; request.downloadFilePath = downloadPath; FileUtils.getInstance().readFilePicAsync(downloadPath).then(arraybuffer=>{ - onComplete(arraybuffer); + result.data=arraybuffer; FileUtils.getInstance().deleteFileAsync(downloadPath).then(()=>{ LogUtil.log('文件名:'+downloadPath+" 文件删除成功!") }).catch((err:BusinessError)=>{ LogUtil.log('文件名:'+downloadPath+" 文件删除失败!") }); }).catch((err:BusinessError)=>{ - onError('NetworkDownloadClient Read File Async Error Msg='+ (err as BusinessError)?.message) + result.error='NetworkDownloadClient Read File Async Error Msg='+ (err as BusinessError)?.message; }) if(loadTask != null) { @@ -86,7 +90,7 @@ export class NetworkDownloadClient implements IDataFetch { loadTask.on('remove', () => {}) loadTask.on('fail', (err) => { - onError('NetworkDownloadClient Download task fail err =' + err) + result.error='NetworkDownloadClient Download task fail err =' + err; if (loadTask!=null) { loadTask.delete().then(result => { if(loadTask != undefined) { @@ -104,11 +108,11 @@ export class NetworkDownloadClient implements IDataFetch { } }) } else { - onError('NetworkDownloadClient downloadTask dismiss!') + result.error='NetworkDownloadClient downloadTask dismiss!' } - }) - .catch((err:BusinessError)=> { - onError("下载子系统download错误捕获,error=" + err.message); + }).catch((err:BusinessError)=> { + result.error="下载子系统download错误捕获,error=" + err.message; }) + return result; } } \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/networkmanage/RequestData.ets b/library/src/main/ets/components/imageknife/networkmanage/RequestData.ets new file mode 100644 index 0000000..45b2357 --- /dev/null +++ b/library/src/main/ets/components/imageknife/networkmanage/RequestData.ets @@ -0,0 +1,20 @@ +/* + * 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. + */ +// 数据加载器 +@Sendable +export class RequestData { + receiveSize: number = 2000 + totalSize: number = 2000 +} \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/pngj/PngCallback.ts b/library/src/main/ets/components/imageknife/pngj/PngCallback.ts index 3d3b297..394b6bb 100644 --- a/library/src/main/ets/components/imageknife/pngj/PngCallback.ts +++ b/library/src/main/ets/components/imageknife/pngj/PngCallback.ts @@ -15,4 +15,9 @@ export interface PngCallback{ pngCallback: (sender:R, receover:T)=>void +} + +export interface WorkerType { + type: string + name: string } \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/pngj/Pngj.ets b/library/src/main/ets/components/imageknife/pngj/Pngj.ets new file mode 100644 index 0000000..860ed48 --- /dev/null +++ b/library/src/main/ets/components/imageknife/pngj/Pngj.ets @@ -0,0 +1,123 @@ +/* + * 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 { UPNG } from '../../3rd_party/upng/UPNG'; +import { PngCallback, WorkerType } from './PngCallback'; +import image from '@ohos.multimedia.image'; +import taskpool from '@ohos.taskpool'; +import { BusinessError } from '@ohos.base' + +export class Pngj { + readPngImageInfo(arraybuffer: ArrayBuffer, callback: PngCallback) { + let imageSource: image.ImageSource = image.createImageSource(arraybuffer); + if (imageSource != undefined) { + imageSource.getImageInfo((err: BusinessError, value: image.ImageInfo) => { + if (err) { + return; + } + callback.pngCallback(arraybuffer, value); + }); + } + + } + + /** + * + * @param pngBuffer ArrayBuffer containing the PNG file + * @param callback + * returns an image object with following properties: + * width: the width of the image + * height: the height of the image + * depth: number of bits per channel + * ctype: color type of the file (Truecolor, Grayscale, Palette ...) + * frames: additional info about frames (frame delays etc.) + * tabs: additional chunks of the PNG file + * data: pixel data of the image + */ + readPngImage(pngBuffer: ArrayBuffer, callback: PngCallback) { + let png = UPNG.decode(pngBuffer); + callback.pngCallback(pngBuffer, png) + } + + writePngWithString(addInfo: string, pngBuffer: ArrayBuffer, callback: PngCallback) { + let pngDecode = UPNG.decode(pngBuffer); + let newPng = UPNG.encodeWithString(addInfo, UPNG.toRGBA8(pngDecode), pngDecode.width, pngDecode.height, 0) + callback.pngCallback(pngBuffer, newPng); + } + + writePng(pngBuffer: ArrayBuffer, callback: PngCallback) { + let pngDecode = UPNG.decode(pngBuffer); + let newPng = UPNG.encode(UPNG.toRGBA8(pngDecode), pngDecode.width, pngDecode.height, 0) + callback.pngCallback(pngBuffer, newPng); + } + + async readPngImageAsync(e: WorkerType, source: ArrayBuffer, pngCallback: (value: ESObject) => void) { + let task = new taskpool.Task(taskPngImage, e, source) + let val1: ESObject = await taskpool.execute(task) + pngCallback(val1) + try { + taskpool.cancel(task) + } catch (e) { + console.error("taskpool.cancel occur error:" + e) + } + } + + async writePngWithStringAsync(e: WorkerType, source: ArrayBuffer, pngCallback: (value: ESObject) => void, info: string) { + let task = new taskpool.Task(taskPngImage, e, source, info) + let val1: ESObject = await taskpool.execute(task) + pngCallback(val1) + try { + taskpool.cancel(task) + } catch (e) { + console.error("taskpool.cancel occur error:" + e) + } + } + + async writePngAsync(e: WorkerType, source: ArrayBuffer, pngCallback: (value: ESObject) => void) { + let task = new taskpool.Task(taskPngImage, e, source) + let val1: ESObject = await taskpool.execute(task) + pngCallback(val1) + try { + taskpool.cancel(task) + } catch (e) { + console.error("taskpool.cancel occur error:" + e) + } + } +} + +@Concurrent +function taskPngImage(e: WorkerType, pngSource: ArrayBuffer, info?: string): Object | ArrayBufferLike | void { + let a: Object | ArrayBufferLike | undefined = undefined + let png = UPNG.decode(pngSource) + switch (e.name) { + case 'readPngImageAsync': + let array: Uint8Array = png.data; + let arrayData = array.buffer.slice(array.byteOffset, array.byteLength + array.byteOffset) + png.data = arrayData; + a = png + break; + case 'writePngWithStringAsync': + let addInfo: string | undefined = info; + let newPng = UPNG.encodeWithString(addInfo, UPNG.toRGBA8(png), png.width, png.height, 0) + a = newPng + break; + case 'writePngAsync': + let newPng3 = UPNG.encode(UPNG.toRGBA8(png), png.width, png.height, 0) + a = newPng3 + break; + default: + break + } + if(a != undefined) { return a } +} \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/pngj/Pngj.ts b/library/src/main/ets/components/imageknife/pngj/Pngj.ts deleted file mode 100644 index a0a3153..0000000 --- a/library/src/main/ets/components/imageknife/pngj/Pngj.ts +++ /dev/null @@ -1,178 +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 {UPNG} from '../../3rd_party/upng/UPNG'; -import {PngCallback} from './PngCallback'; -import image from '@ohos.multimedia.image'; -import resourceManager from '@ohos.resourceManager'; -import ArkWorker from '@ohos.worker' -import { BusinessError } from '@ohos.base' -export class Pngj { - readPngImageInfo(arraybuffer: ArrayBuffer, callback:PngCallback) { - let imageSource:image.ImageSource = image.createImageSource(arraybuffer); - if (imageSource != undefined){ - imageSource.getImageInfo((err:BusinessError, value:image.ImageInfo) => { - if (err) { - return; - } - callback.pngCallback(arraybuffer, value); - }); - } - - } - - /** - * - * @param pngBuffer ArrayBuffer containing the PNG file - * @param callback - * returns an image object with following properties: - * width: the width of the image - * height: the height of the image - * depth: number of bits per channel - * ctype: color type of the file (Truecolor, Grayscale, Palette ...) - * frames: additional info about frames (frame delays etc.) - * tabs: additional chunks of the PNG file - * data: pixel data of the image - */ - readPngImage(pngBuffer: ArrayBuffer, callback:PngCallback) { - var png = UPNG.decode(pngBuffer); - callback.pngCallback(pngBuffer, png) - } - - writePngWithString(addInfo:string, pngBuffer: ArrayBuffer,callback:PngCallback) { - var pngDecode = UPNG.decode(pngBuffer); - var newPng = UPNG.encodeWithString(addInfo, UPNG.toRGBA8(pngDecode), pngDecode.width, pngDecode.height, 0) - callback.pngCallback(pngBuffer, newPng); - } - - writePng(pngBuffer: ArrayBuffer,callback:PngCallback) { - var pngDecode = UPNG.decode(pngBuffer); - var newPng = UPNG.encode(UPNG.toRGBA8(pngDecode), pngDecode.width, pngDecode.height, 0) - callback.pngCallback(pngBuffer, newPng); - } - - readPngImageAsync(worker: any, pngBuffer: ArrayBuffer, callback: PngCallback) { - // worker.onerror = function (data) { - // - // } - // - // worker.onmessageerror = function (e) { - // - // } - // - // worker.onexit = function () { - // - // } - // - // worker.onmessage = function(e) { - // var data = e.data; - // switch (data.type) { - // case 'readPngImageAsync': - // callback.pngCallback(data.receiver, data.data) - // break; - // default: - // break - // } - // worker.terminate(); - // } - // var obj = { type: 'readPngImageAsync', data: pngBuffer } - // worker.postMessage(obj, [pngBuffer]) - let task = new taskpool.Task(taskParseImage, arrayBuffer, scale) - task.setTransferList([]) - taskpool.execute(task).then((pixelmap: Object) => { - LogUtil.log('ceshi321 : Succeeded in creating pixelmap Ui .' + (pixelmap as image.PixelMap).getPixelBytesNumber()) - onCompleteFunction(pixelmap as image.PixelMap); - }).catch((err: string) => { - LogUtil.log("ceshi321 : test occur error: " + err) - onErrorFunction(err); - }); - - - - } - - - - - - - - - - - - - - - writePngWithStringAsync(worker: any, addInfo: string, pngBuffer: ArrayBuffer, callback: PngCallback) { - worker.onerror = function (data) { - - } - - worker.onmessageerror = function (e) { - - } - - worker.onexit = function () { - - } - - worker.onmessage = function(e) { - var data = e.data; - switch (data.type) { - case 'writePngWithStringAsync': - callback.pngCallback(data.receiver, data.data) - break; - default: - break - } - worker.terminate(); - } - - var obj = { type: 'writePngWithStringAsync', data:pngBuffer, info: addInfo} - worker.postMessage(obj, [pngBuffer]) - - } - - writePngAsync(worker: any, pngBuffer: ArrayBuffer, callback: PngCallback) { - worker.onerror = function (data) { - - } - - worker.onmessageerror = function (e) { - - } - - worker.onexit = function () { - - } - - worker.onmessage = function(e) { - var data = e.data; - switch (data.type) { - case 'writePngAsync': - callback.pngCallback(data.receiver, data.data) - break; - default: - break - } - worker.terminate(); - } - - var obj = { type: 'writePngAsync', data:pngBuffer} - worker.postMessage(obj, [pngBuffer]) - - } - -} \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/requestmanage/DiskCacheProxy.ets b/library/src/main/ets/components/imageknife/requestmanage/DiskCacheProxy.ets index 62816df..552d607 100644 --- a/library/src/main/ets/components/imageknife/requestmanage/DiskCacheProxy.ets +++ b/library/src/main/ets/components/imageknife/requestmanage/DiskCacheProxy.ets @@ -14,7 +14,7 @@ */ import { ICache } from "../requestmanage/ICache" -import { DiskLruCache } from "@ohos/disklrucache" +import { DiskLruCache } from "../../cache/DiskLruCache" export class DiskCacheProxy implements ICache { private mDiskLruCache: DiskLruCache; @@ -29,7 +29,7 @@ export class DiskCacheProxy implements ICache { } getCachePath():string{ - let folderPath = this.mDiskLruCache.getPath(); + let folderPath:string = this.mDiskLruCache.getPath(); if (folderPath.endsWith('/')) { return folderPath; } else { @@ -38,7 +38,7 @@ export class DiskCacheProxy implements ICache { } getValue(key: string): ArrayBuffer{ - return this.mDiskLruCache.get(key); + return this.mDiskLruCache.get(key) as ArrayBuffer; } putValue(key: string, value: ArrayBuffer) { diff --git a/library/src/main/ets/components/imageknife/requestmanage/RequestManager.ets b/library/src/main/ets/components/imageknife/requestmanage/RequestManager.ets index 0854630..a415276 100644 --- a/library/src/main/ets/components/imageknife/requestmanage/RequestManager.ets +++ b/library/src/main/ets/components/imageknife/requestmanage/RequestManager.ets @@ -13,25 +13,26 @@ * limitations under the License. */ -import { RequestOption,Size } from '../../imageknife/RequestOption' -import { DiskLruCache } from '@ohos/disklrucache' +import { RequestOption, Size } from '../../imageknife/RequestOption' +import { DiskLruCache } from "../../cache/DiskLruCache" import { LruCache } from '../../cache/LruCache' import { SparkMD5 } from '../../3rd_party/sparkmd5/spark-md5' import { MemoryCacheProxy } from '../requestmanage/MemoryCacheProxy' import { DiskCacheProxy } from '../requestmanage/DiskCacheProxy' import { FileTypeUtil } from '../utils/FileTypeUtil' -import { IDataFetch } from '../../imageknife/networkmanage/IDataFetch' -import { IResourceFetch } from '../../imageknife/resourcemanage/IResourceFetch' +import { IDataFetch } from '../../imageknife/networkmanage/IDataFetch' +import { IResourceFetch } from '../../imageknife/resourcemanage/IResourceFetch' import { ImageKnifeData, ImageKnifeType } from '../ImageKnifeData' import { AllCacheInfo } from '../../imageknife/interface/IAllCacheInfoCallback' import { ParseImageUtil } from '../utils/ParseImageUtil' -import { IParseImage } from '../interface/IParseImage' +import { IParseImage } from '../interface/IParseImage' import image from '@ohos.multimedia.image' import { SVGParseImpl } from '../utils/svg/SVGParseImpl' import { GIFParseImpl } from '../utils/gif/GIFParseImpl' import { GIFFrame } from '../utils/gif/GIFFrame' import { LogUtil } from '../../imageknife/utils/LogUtil' import { BusinessError } from '@ohos.base' +import { DataFetchResult } from '../networkmanage/DataFetchResult' export enum Stage { @@ -62,19 +63,15 @@ export enum RunReason { export class RequestManager { private TAG: string = "RequestManager"; private options: RequestOption; - private mMemoryCacheProxy: MemoryCacheProxy; - private mDiskCacheProxy: DiskCacheProxy; private mIDataFetch: IDataFetch; private mIResourceFetch: IResourceFetch; private mParseImageUtil: IParseImage; + private diskMemoryCache: DiskLruCache; - constructor(option: RequestOption, memoryCache1: LruCache, diskMemoryCache1: DiskLruCache, dataFetch: IDataFetch, resourceFetch: IResourceFetch) { + constructor(option: RequestOption,diskMemoryCache: DiskLruCache, dataFetch: IDataFetch, resourceFetch: IResourceFetch) { this.options = option; - // 缓存部分 - this.mMemoryCacheProxy = new MemoryCacheProxy(memoryCache1); - this.mDiskCacheProxy = new DiskCacheProxy(diskMemoryCache1); - + this.diskMemoryCache = diskMemoryCache; // 网络下载能力 this.mIDataFetch = dataFetch; @@ -85,63 +82,16 @@ export class RequestManager { this.mParseImageUtil = new ParseImageUtil(); } - static execute(option: RequestOption, memoryCache1: LruCache, diskMemoryCache1: DiskLruCache, dataFetch: IDataFetch, resourceFetch: IResourceFetch) { - LogUtil.log("RequestManager execute") - let manager = new RequestManager(option, memoryCache1, diskMemoryCache1, dataFetch, resourceFetch); - return new Promise(manager.process) - .then(option.loadComplete) - .then(manager.loadCompleteAfter) - .catch(option.loadError); - } - - loadCompleteAfter =()=>{ - try { // 内部消化问题 - LogUtil.log("loadCompleteAfter!") - if (this.options.allCacheInfoCallback) { - LogUtil.log("RequestOption =" + JSON.stringify(this.options)); - - // 内存缓存 - let allCacheInfo:AllCacheInfo = { - memoryCacheInfo:{key:'', data:new ImageKnifeData()}, - resourceCacheInfo:{key:'', path:''}, - dataCacheInfo:{key:'',path:''} - }; - let memoryCache = this.mMemoryCacheProxy.getValue(this.options.generateCacheKey); - allCacheInfo.memoryCacheInfo = { - key: this.options.generateCacheKey, - data: memoryCache - } - - // 变换后缓存 - allCacheInfo.resourceCacheInfo = { - key: SparkMD5.hashBinary(this.options.generateResourceKey) as string, - path: (this.mDiskCacheProxy.getCachePath() + SparkMD5.hashBinary(this.options.generateResourceKey)) as string - }; - - // 原图缓存 - allCacheInfo.dataCacheInfo = { - key: SparkMD5.hashBinary(this.options.generateDataKey) as string, - path: (this.mDiskCacheProxy.getCachePath() + SparkMD5.hashBinary(this.options.generateDataKey)) as string - } - this.options.allCacheInfoCallback.callback(allCacheInfo) - } - } catch (err) { - LogUtil.log("after err =" + err) - } - } - - // DecodeJob work private mStage: Stage = Stage.INITIALIZE; private mRunReason: RunReason = RunReason.INITIALIZE; - - process = (onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void)=>{ - LogUtil.log("RequestManager process !"); + process = (onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) => { + LogUtil.log("ImageKnife RequestManager process !"); this.loadLeve1MemoryCache(onComplete, onError) } - private runWrapped(request: RequestOption, runReason: RunReason, onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void) { - LogUtil.log("RequestManager runWrapped") + private runWrapped(request: RequestOption, runReason: RunReason, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + LogUtil.log("ImageKnife RequestManager runWrapped") if (runReason == RunReason.INITIALIZE) { this.mStage = this.getNextStage(request, this.mStage); this.searchLoadFrom(this.options, this.mStage, onComplete, onError); @@ -150,7 +100,7 @@ export class RequestManager { } } - private getNextStage(request: RequestOption, current: Stage): Stage{ + private getNextStage(request: RequestOption, current: Stage): Stage { if (current == Stage.INITIALIZE) { return request.strategy.decodeCachedResource() ? Stage.RESOURCE_CACHE @@ -166,13 +116,13 @@ export class RequestManager { } else if (current == Stage.FINISHED) { return Stage.FINISHED; } else { - throw new Error("Unrecognized stage: " + current); + throw new Error("ImageKnife Unrecognized stage: " + current); } } // 究竟从哪里加载数据 - private searchLoadFrom(request: RequestOption, current: Stage, onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void) { - LogUtil.log("RequestManager searchLoadFrom") + private searchLoadFrom(request: RequestOption, current: Stage, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + LogUtil.log("ImageKnife RequestManager searchLoadFrom") if (current == Stage.RESOURCE_CACHE) { this.loadDiskFromTransform(request, onComplete, onError); } else if (current == Stage.DATA_CACHE) { @@ -187,85 +137,90 @@ export class RequestManager { } // 加载网络资源 - private loadSourceFromNetwork(request: RequestOption, onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void) { - let success = (arraybuffer:ArrayBuffer) => { - this.downloadSuccess(request,arraybuffer, onComplete, onError) + private async loadSourceFromNetwork(request: RequestOption, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + try { + LogUtil.log("ImageKnife RequestManager loadSourceFromNetwork") + let result: DataFetchResult = await this.mIDataFetch.loadData(request); + if(result.error){ + onError(result.error) + }else{ + if(result.data){ + this.downloadSuccess(request, result.data, onComplete, onError) + }else{ + onError("datafetch data is null") + } } - let error = (errorMsg:string) =>{ - onError(errorMsg) - } - this.mIDataFetch.loadData(request, success, error); + } catch (e) { + LogUtil.error("ImageKnife RequestManager loadSourceFromNetwork error") + } } // 加载本地资源 - private loadSourceFormNative(request: RequestOption, onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void) { - LogUtil.log("RequestManager loadSourceFormNative") + private loadSourceFormNative(request: RequestOption, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + LogUtil.log("ImageKnife RequestManager loadSourceFormNative") // 本地解析后进行一级缓存 - let success = (arrayBuffer:ArrayBuffer) => { + let success = (arrayBuffer: ArrayBuffer) => { // 使用媒体子系统 ImageSource解析文件 获取PixelMap let fileTypeUtil = new FileTypeUtil(); let typeValue = fileTypeUtil.getFileType(arrayBuffer) - LogUtil.log("RequestManager - 文件类型为= " + typeValue) - // gif处理 - if(ImageKnifeData.GIF == typeValue && !request.dontAnimateFlag){ - // 处理gif - this.gifProcess(onComplete,onError, arrayBuffer,typeValue,(imageKnifeData)=>{ - this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData) - }) - }else if(ImageKnifeData.SVG == typeValue){ + LogUtil.log("ImageKnife RequestManager - 文件类型为= " + typeValue) + // gif、webp处理 + if ((ImageKnifeData.GIF == typeValue || ImageKnifeData.WEBP == typeValue) && !request.dontAnimateFlag) { + // 处理gif、webp + this.gifProcess(onComplete, onError, arrayBuffer, typeValue) + } else if (ImageKnifeData.SVG == typeValue) { // 处理svg - this.svgProcess(request,onComplete,onError,arrayBuffer,typeValue,(imageKnifeData)=>{ - this.mMemoryCacheProxy.putValue(this.options.generateCacheKey,imageKnifeData) - }) + this.svgProcess(request, onComplete, onError, arrayBuffer, typeValue) } else { if (request.transformations[0]) { - request.transformations[0].transform(arrayBuffer, request, {asyncTransform:(error:BusinessError|string, pixelMap: PixelMap|null) => { - // 输出给Image - if (pixelMap) { - - let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, pixelMap); - this.mMemoryCacheProxy.putValue(request.generateCacheKey, imageKnifeData); - onComplete(imageKnifeData); - } else { - onError(error); + request.transformations[0].transform(arrayBuffer, request, { + asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => { + // 输出给Image + if (pixelMap) { + onComplete(pixelMap); + } else { + onError(error); + } } - }}) + }) } else { let success = (value: PixelMap) => { - let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, value); - this.mMemoryCacheProxy.putValue(request.generateCacheKey, imageKnifeData); - onComplete(imageKnifeData); + onComplete(value); } this.mParseImageUtil.parseImage(arrayBuffer, success, onError) } } } let ctx = request.getModuleContext(); - if(ctx!=undefined){ - this.mIResourceFetch.loadResource(ctx,request.loadSrc as Resource, success, onError); - }else{ - onError('RequestManager loadSourceFormNative moduleContext is undefined! please check it') + if (ctx != undefined) { + this.mIResourceFetch.loadResource(ctx, request.loadSrc as Resource, success, onError); + } else { + onError('ImageKnife RequestManager loadSourceFormNative moduleContext is undefined! please check it') } } + // 加载磁盘缓存 原图 - private loadDiskFromSource(request: RequestOption, onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void) { - LogUtil.log("RequestManager loadDiskFromSource") - let cached = this.mDiskCacheProxy.getValue(request.generateDataKey) + private loadDiskFromSource(request: RequestOption, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + LogUtil.log("ImageKnife RequestManager loadDiskFromSource") + let cached = this.diskMemoryCache.get(request.generateDataKey); if (cached != null && cached.byteLength > 0) { + LogUtil.log("ImageKnife loadDiskFromSource load resource from DiskLruCache") this.parseDiskFile2PixelMap(request, cached, onComplete, onError) } else { this.mStage = Stage.SOURCE; + this.mStage = request.onlyRetrieveFromCache? Stage.FINISHED : Stage.SOURCE this.searchLoadFrom(this.options, this.mStage, onComplete, onError); } } // 加载磁盘缓存 变换后图片 - private loadDiskFromTransform(request: RequestOption, onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void) { - LogUtil.log("RequestManager loadDiskFromTransform") - let cached = this.mDiskCacheProxy.getValue(request.generateResourceKey) + private loadDiskFromTransform(request: RequestOption, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + LogUtil.log("ImageKnife RequestManager loadDiskFromTransform") + let cached = this.diskMemoryCache.get(request.generateResourceKey); if (cached != null) { + LogUtil.log("ImageKnife loadDiskFromTransform load resource from DiskLruCache") this.parseDiskTransformFile2PixelMap(request, cached, onComplete, onError) } else { this.mStage = Stage.DATA_CACHE; @@ -273,103 +228,95 @@ export class RequestManager { } } - parseSource(request: RequestOption, onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void) { - LogUtil.log("RequestManager parseSource") + parseSource(request: RequestOption, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + LogUtil.log("ImageKnife RequestManager parseSource") + try { if ((typeof (request.loadSrc as image.PixelMap).isEditable) == 'boolean') { // PixelMap 外层捕获效率更高,不会进入这里 } else if (typeof request.loadSrc == 'string') { this.loadSourceFromNetwork(request, onComplete, onError); } else { let res = request.loadSrc as Resource; - if (typeof res.id != 'undefined' && typeof res.id != 'undefined') { + if (typeof res.id != 'undefined') { this.loadSourceFormNative(request, onComplete, onError) } else { LogUtil.log("输入参数有问题!") } } + } catch (e) { + LogUtil.error("ImageKnife RequestManager parseSource error") + } } - private loadLeve1MemoryCache(onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void) { - LogUtil.log("RequestManager loadLeve1MemoryCache") - // 一级缓存 内存获取 - let cache = this.mMemoryCacheProxy.loadMemoryCache(this.options.generateCacheKey, this.options.isCacheable); - if (cache == null || typeof cache == 'undefined') { - // 处理磁盘加载 网络加载 + private loadLeve1MemoryCache(onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + try { this.runWrapped(this.options, this.mRunReason, onComplete, onError) - } else { - // 需要清理状态 - cache.waitSaveDisk = false; - onComplete(cache); - return - + } catch (e) { + LogUtil.error("ImageKnife loadLeve1MemoryCache error") } } // 解析磁盘文件变成PixeMap - private parseDiskFile2PixelMap(request: RequestOption, source: ArrayBuffer, onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void) { + private parseDiskFile2PixelMap(request: RequestOption, source: ArrayBuffer, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + LogUtil.log("ImageKnife RequestManager parseDiskFile2PixelMap") // 步骤一:文件转为pixelMap 然后变换 给Image组件 let fileTypeUtil = new FileTypeUtil(); let typeValue = fileTypeUtil.getFileType(source); - // 解析磁盘文件 gif 和 svg - if(ImageKnifeData.GIF == typeValue && !request.dontAnimateFlag){ - // 处理gif - this.gifProcess(onComplete,onError,source,typeValue, (imageKnifeData)=>{ - this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData) - }) - }else if(ImageKnifeData.SVG == typeValue){ - this.svgProcess(request,onComplete,onError, source, typeValue, (imageKnifeData)=>{ - this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData) - }) + // 解析磁盘文件 gif、webp 和 svg + if ((ImageKnifeData.GIF == typeValue || ImageKnifeData.WEBP == typeValue) && !request.dontAnimateFlag) { + // 处理gif、webp + this.gifProcess(onComplete, onError, source, typeValue) + } else if (ImageKnifeData.SVG == typeValue) { + this.svgProcess(request, onComplete, onError, source, typeValue) } else { if (this.options.transformations[0]) { if (this.options.thumbSizeMultiplier) { - let thumbOption:RequestOption = new RequestOption(); + let thumbOption: RequestOption = new RequestOption(); thumbOption.setImageViewSize({ width: Math.round(this.options.thumbSizeMultiplier * this.options.size.width), height: Math.round(this.options.thumbSizeMultiplier * this.options.size.height) }) let ctx = this.options.getModuleContext() - if(ctx != undefined){ + if (ctx != undefined) { thumbOption.setModuleContext(ctx) - }else{ + } else { onError('RequestManager parseDiskFile2PixelMap moduleContext is undefined, please check it!') return } let thumbCallback = this.options.thumbholderOnComplete; let thumbError = this.options.thumbholderOnError; - this.options.transformations[0].transform(source, thumbOption,{asyncTransform: (error:BusinessError|string, pixelMap: PixelMap|null) => { - if (pixelMap) { - let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, pixelMap); - thumbCallback(imageKnifeData); - } else { - thumbError(error); - } - }}) - setTimeout(()=>{ - this.options.transformations[0].transform(source, request, {asyncTransform: (error:BusinessError|string, pixelMap: PixelMap|null) => { + this.options.transformations[0].transform(source, thumbOption, { + asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => { if (pixelMap) { - // 保存一份变换后的图片PixelMap到MemoryCache - let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, pixelMap); - this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData); - onComplete(imageKnifeData); + thumbCallback(pixelMap); + } else { + thumbError(error); + } + } + }) + setTimeout(() => { + this.options.transformations[0].transform(source, request, { + asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => { + if (pixelMap) { + onComplete(pixelMap); + } else { + onError(error); + } + } + }) + }, this.options.thumbDelayTime); + } + else { + this.options.transformations[0].transform(source, request, { + asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => { + if (pixelMap) { + onComplete(pixelMap); } else { onError(error); } - }}) - },this.options.thumbDelayTime); - } - else { - this.options.transformations[0].transform(source, request, {asyncTransform: (error:BusinessError|string, pixelMap: PixelMap|null) => { - if (pixelMap) { - // 保存一份变换后的图片PixelMap到MemoryCache - let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, pixelMap); - this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData); - onComplete(imageKnifeData); - } else { - onError(error); } - }}) + }) } } else { // thumbnail 缩略图部分 @@ -377,24 +324,18 @@ export class RequestManager { let thumbCallback = this.options.thumbholderOnComplete let thumbError = this.options.thumbholderOnError let thumbSuccess = (value: PixelMap) => { - let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, value); - thumbCallback(imageKnifeData); + thumbCallback(value); } this.mParseImageUtil.parseImageThumbnail(request.thumbSizeMultiplier, source, thumbSuccess, thumbError); - setTimeout(()=>{ + setTimeout(() => { let success = (value: PixelMap) => { - let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, value); - this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData); - onComplete(imageKnifeData); - } - this.mParseImageUtil.parseImage(source, success, onError) - },this.options.thumbDelayTime) - } - else { + onComplete(value); + } + this.mParseImageUtil.parseImage(source, success, onError) + }, this.options.thumbDelayTime) + } else { let success = (value: PixelMap) => { - let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, value); - this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData); - onComplete(imageKnifeData); + onComplete(value); } this.mParseImageUtil.parseImage(source, success, onError) } @@ -403,7 +344,8 @@ export class RequestManager { } // 解析磁盘变换后文件变成PixeMap - private parseDiskTransformFile2PixelMap(request: RequestOption, source: ArrayBuffer, onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void) { + private parseDiskTransformFile2PixelMap(request: RequestOption, source: ArrayBuffer, onComplete: (value: PixelMap) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { + LogUtil.log("ImageKnife RequestManager parseDiskTransformFile2PixelMap") let fileTypeUtil = new FileTypeUtil(); let typeValue = fileTypeUtil.getFileType(source); // thumbnail 缩略图部分 @@ -411,33 +353,27 @@ export class RequestManager { let thumbCallback = this.options.thumbholderOnComplete let thumbError = this.options.thumbholderOnError let thumbSuccess = (value: PixelMap) => { - let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, value); - thumbCallback(imageKnifeData); + thumbCallback(value); } this.mParseImageUtil.parseImageThumbnail(request.thumbSizeMultiplier, source, thumbSuccess, thumbError); - setTimeout(()=>{ + setTimeout(() => { let success = (value: PixelMap) => { - let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, value); - this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData); - onComplete(imageKnifeData); + onComplete(value); } this.mParseImageUtil.parseImage(source, success, onError) - },this.options.thumbDelayTime) - }else{ + }, this.options.thumbDelayTime) + } else { let success = (value: PixelMap) => { - let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, value) - this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData); - onComplete(imageKnifeData); + onComplete(value); } this.mParseImageUtil.parseImage(source, success, onError) } } - private downloadSuccess(request: RequestOption,source: ArrayBuffer, onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void) { - LogUtil.log('Download task completed.'); + private downloadSuccess(request: RequestOption, source: ArrayBuffer, onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { - if(source == null || source == undefined || source.byteLength <= 0){ - onError('Download task completed. but download file is empty!') + if (source == null || source == undefined || source.byteLength <= 0) { + onError('ImageKnife Download task completed. but download file is empty!') return } @@ -446,8 +382,8 @@ export class RequestManager { // 步骤二: 文件名保存一份全局 // 步骤三:查看文件是否支持 非支持类型直接返回 let fileTypeUtil = new FileTypeUtil(); - let filetype:string|null = fileTypeUtil.getFileType(source); - if(filetype == null){ + let filetype: string | null = fileTypeUtil.getFileType(source); + if (filetype == null) { onError("下载文件解析后类型为null,请检查数据源!"); return; } @@ -457,53 +393,51 @@ export class RequestManager { return; } - // 解析磁盘文件 gif 和 svg - if(ImageKnifeData.GIF == filetype && !this.options.dontAnimateFlag){ - // 处理gif - this.gifProcess(onComplete,onError,source,filetype, (imageKnifeData)=>{ - this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData) - }) + // 解析磁盘文件 gif、webp 和 svg + if ((ImageKnifeData.GIF == filetype || ImageKnifeData.WEBP == filetype) && !this.options.dontAnimateFlag) { + // 处理gif、webp + this.gifProcess(onComplete, onError, source, filetype) // 保存二级磁盘缓存 Promise.resolve(source) - .then((arraybuffer: ArrayBuffer)=>{ - this.mDiskCacheProxy.putValue(this.options.generateDataKey, arraybuffer) + .then((arraybuffer: ArrayBuffer) => { + this.diskMemoryCache.set(this.options.generateDataKey,arraybuffer); }) - .catch( (err:BusinessError)=>{ - LogUtil.log('download file is ='+ImageKnifeData.GIF+'and save diskLruCache error ='+ (err as BusinessError)) + .catch((err: BusinessError) => { + LogUtil.log('download file is =' + ImageKnifeData.GIF + 'and save diskLruCache error =' + (err as BusinessError)) }) - }else if(ImageKnifeData.SVG == filetype){ + } else if (ImageKnifeData.SVG == filetype) { // 处理svg - this.svgProcess(request,onComplete,onError, source, filetype, (imageKnifeData)=>{ - this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData) - }) + this.svgProcess(request, onComplete, onError, source, filetype) // 保存二级磁盘缓存 Promise.resolve(source) - .then((arraybuffer: ArrayBuffer)=>{ - this.mDiskCacheProxy.putValue(this.options.generateDataKey, arraybuffer) + .then((arraybuffer: ArrayBuffer) => { + this.diskMemoryCache.set(this.options.generateDataKey,arraybuffer); }) - .catch((err:BusinessError)=>{ - LogUtil.log('download file is ='+ImageKnifeData.SVG+'and save diskLruCache error ='+ (err as BusinessError)) + .catch((err: BusinessError) => { + LogUtil.log('download file is =' + ImageKnifeData.SVG + 'and save diskLruCache error =' + (err as BusinessError)) }) } else { // 进行变换 if (this.options.transformations[0]) { // thumbnail 缩略图部分 if (this.options.thumbSizeMultiplier) { - if(filetype != null) { + if (filetype != null) { this.thumbnailProcess(source, filetype, onComplete, onError); } } else { - this.options.transformations[0].transform(source, this.options, {asyncTransform: (error:BusinessError|string, pixelMap: PixelMap|null) => { - if (pixelMap) { - if(filetype != null) { - this.saveCacheAndDisk(pixelMap, filetype, onComplete, source); + this.options.transformations[0].transform(source, this.options, { + asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => { + if (pixelMap) { + if (filetype != null) { + this.saveCacheAndDisk(pixelMap, filetype, onComplete, source); + } + } else { + onError(error); } - } else { - onError(error); } - }}) + }) } } else { // thumbnail 缩略图部分 @@ -511,13 +445,12 @@ export class RequestManager { let thumbCallback = this.options.thumbholderOnComplete let thumbError = this.options.thumbholderOnError let thumbSuccess = (value: PixelMap) => { - let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, value); - thumbCallback(imageKnifeData); + thumbCallback(value); } this.mParseImageUtil.parseImageThumbnail(this.options.thumbSizeMultiplier, source, thumbSuccess, thumbError); setTimeout(() => { let success = (value: PixelMap) => { - if(filetype != null) { + if (filetype != null) { this.saveCacheAndDisk(value, filetype, onComplete, source); } } @@ -525,7 +458,7 @@ export class RequestManager { }, this.options.thumbDelayTime) } else { let success = (value: PixelMap) => { - if(filetype != null) { + if (filetype != null) { this.saveCacheAndDisk(value, filetype, onComplete, source); } } @@ -535,94 +468,90 @@ export class RequestManager { } } - createImagePixelMap(imageKnifeType: ImageKnifeType, imageKnifeValue: PixelMap): ImageKnifeData{ - return ImageKnifeData.createImagePixelMap(imageKnifeType,imageKnifeValue); + createImagePixelMap(imageKnifeType: ImageKnifeType, imageKnifeValue: PixelMap): ImageKnifeData { + return ImageKnifeData.createImagePixelMap(imageKnifeType, imageKnifeValue); } - createImageGIFFrame(imageKnifeType: ImageKnifeType, imageKnifeValue: GIFFrame[]): ImageKnifeData{ - return ImageKnifeData.createImageGIFFrame(imageKnifeType,imageKnifeValue); + createImageGIFFrame(imageKnifeType: ImageKnifeType, imageKnifeValue: GIFFrame[]): ImageKnifeData { + return ImageKnifeData.createImageGIFFrame(imageKnifeType, imageKnifeValue); } - - private saveCacheAndDisk(value: PixelMap, filetype:string, onComplete:(value:ImageKnifeData)=>void|PromiseLike, source:ArrayBuffer) { - let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, value); - this.mMemoryCacheProxy.putValue(this.options.generateCacheKey, imageKnifeData); - let save2DiskCache = (arraybuffer:ArrayBuffer) => { - this.mDiskCacheProxy.putValue(this.options.generateDataKey, arraybuffer) + private async saveCacheAndDisk(value: PixelMap, filetype: string, onComplete: (value: PixelMap) => void | PromiseLike, source: ArrayBuffer) { + let save2DiskCache = (arraybuffer: ArrayBuffer) => { + this.diskMemoryCache.set(this.options.generateDataKey,arraybuffer); // 落盘之后需要主动移除当前request并且调用下一个加载 - let removeCurrentAndSearchNextRun = this.options.removeCurrentAndSearchNext + let removeCurrentAndSearchNextRun = this.options.removeCurrentAndSearchNext; removeCurrentAndSearchNextRun(); } - let runSave2Disk = (resolve:(value:ArrayBuffer)=>void|PromiseLike, reject:(reason?:BusinessError|string)=>void) => { + let runSave2Disk = (resolve: (value: ArrayBuffer) => void | PromiseLike, reject: (reason?: BusinessError | string) => void) => { resolve(source); } let promise = new Promise(runSave2Disk); - promise.then(save2DiskCache); - imageKnifeData.waitSaveDisk = true; - onComplete(imageKnifeData); + await promise.then(save2DiskCache); + onComplete(value); } - thumbnailProcess(source:ArrayBuffer, filetype:string, onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void){ + thumbnailProcess(source: ArrayBuffer, filetype: string, onComplete: (value: PixelMap) => void | PromiseLike, onError: (reason?: BusinessError | string) => void) { let thumbOption = new RequestOption(); let ctx = this.options.getModuleContext() - if(ctx != undefined){ + if (ctx != undefined) { thumbOption.setModuleContext(ctx) - }else{ + } else { onError('RequestManager thumbnailProcess moduleContext is undefined, please check it!') return } thumbOption.setImageViewSize({ width: Math.round(this.options.thumbSizeMultiplier * this.options.size.width), height: Math.round(this.options.thumbSizeMultiplier * this.options.size.height) - }) + }) let thumbCallback = this.options.thumbholderOnComplete let thumbError = this.options.thumbholderOnError - this.options.transformations[0].transform(source, thumbOption, {asyncTransform: (error:BusinessError|string, pixelMap: PixelMap|null) => { - if (pixelMap) { - let imageKnifeData = this.createImagePixelMap(ImageKnifeType.PIXELMAP, pixelMap); - thumbCallback(imageKnifeData); - } else { - thumbError(error); - } - }}) - setTimeout(() => { - this.options.transformations[0].transform(source, this.options,{asyncTransform: (error:BusinessError|string, pixelMap: PixelMap|null) => { + this.options.transformations[0].transform(source, thumbOption, { + asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => { if (pixelMap) { - this.saveCacheAndDisk(pixelMap, filetype, onComplete, source); + thumbCallback(pixelMap); + } else { + thumbError(error); + } + } + }) + setTimeout(() => { + this.options.transformations[0].transform(source, this.options, { + asyncTransform: (error: BusinessError | string, pixelMap: PixelMap | null) => { + if (pixelMap) { + this.saveCacheAndDisk(pixelMap, filetype, onComplete, source); } else { onError(error); } - }}) - }, this.options.thumbDelayTime) - } - private svgProcess(option: RequestOption,onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void, arraybuffer:ArrayBuffer, typeValue:string, cacheStrategy?: (cacheData: ImageKnifeData) => void) { - let svgParseImpl = new SVGParseImpl() - svgParseImpl.parseSvg(option,arraybuffer, onComplete,onError) + } + }) + }, this.options.thumbDelayTime) } - private gifProcess(onComplete:(value:ImageKnifeData)=>void|PromiseLike, onError:(reason?:BusinessError|string)=>void, arraybuffer:ArrayBuffer, typeValue:string, cacheStrategy?: (cacheData: ImageKnifeData) => void) { + private svgProcess(option: RequestOption, onComplete: (value: PixelMap) => void | PromiseLike, onError: (reason?: BusinessError | string) => void, arraybuffer: ArrayBuffer, typeValue: string, cacheStrategy?: (cacheData: ImageKnifeData) => void) { + let svgParseImpl = new SVGParseImpl() + svgParseImpl.parseSvg(option, arraybuffer, onComplete, onError) + } + + private gifProcess(onComplete: (value: PixelMap | GIFFrame[]) => void | PromiseLike, onError: (reason?: BusinessError | string) => void, arraybuffer: ArrayBuffer, typeValue: string, cacheStrategy?: (cacheData: ImageKnifeData) => void) { let gifParseImpl = new GIFParseImpl() - gifParseImpl.parseGifs(arraybuffer, (data?:GIFFrame[],err?:BusinessError|string)=>{ - if(err){ + gifParseImpl.parseGifs(arraybuffer, (data?: GIFFrame[], err?: BusinessError | string) => { + if (err) { onError(err) } - LogUtil.log("gifProcess data is null:"+(data == null)); - if(!!data){ - let imageKnifeData = this.createImageGIFFrame(ImageKnifeType.GIFFRAME,data) - LogUtil.log('gifProcess 生成gif 返回数据类型') - if(cacheStrategy){ - LogUtil.log('gifProcess 生成gif并且存入了缓存策略') - cacheStrategy(imageKnifeData) - } - onComplete(imageKnifeData) - }else{ + LogUtil.log("gifProcess data is null:" + (data == null)); + if (!!data) { + // let imageKnifeData = this.createImageGIFFrame(ImageKnifeType.GIFFRAME, data) + // LogUtil.log('gifProcess 生成gif 返回数据类型') + // if (cacheStrategy) { + // LogUtil.log('gifProcess 生成gif并且存入了缓存策略') + // cacheStrategy(imageKnifeData) + // } + onComplete(data) + } else { onError('Parse GIF callback data is null, you need check callback data!') } }) } - - } - - diff --git a/library/src/main/ets/components/imageknife/resourcemanage/ParseResClientBase64.ets b/library/src/main/ets/components/imageknife/resourcemanage/ParseResClientBase64.ets index 2f8510f..0c43a36 100644 --- a/library/src/main/ets/components/imageknife/resourcemanage/ParseResClientBase64.ets +++ b/library/src/main/ets/components/imageknife/resourcemanage/ParseResClientBase64.ets @@ -29,7 +29,7 @@ export class ParseResClientBase64 implements IResourceFetch { if (resType == ResourceTypeEts.MEDIA) { let moduleContext = ContextCacheProxy.getInstance() .contextGetValue(res.moduleName, context); - (moduleContext.resourceManager as resourceManager.ResourceManager) + (moduleContext?.resourceManager as resourceManager.ResourceManager) .getMediaContentBase64(resId) .then(data => { let matchReg = ';base64,'; diff --git a/library/src/main/ets/components/imageknife/transform/BaseTransform.ets b/library/src/main/ets/components/imageknife/transform/BaseTransform.ets index 35847ab..e0a7f1c 100644 --- a/library/src/main/ets/components/imageknife/transform/BaseTransform.ets +++ b/library/src/main/ets/components/imageknife/transform/BaseTransform.ets @@ -20,5 +20,9 @@ export interface BaseTransform { // 实现类 返回作为key生成的一部分 getName(): string; + getClassName():string; + + getConstructorParams():string; + transform(value: ArrayBuffer, request: RequestOption, func: AsyncTransform):void; } \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/transform/BlurTransformation.ets b/library/src/main/ets/components/imageknife/transform/BlurTransformation.ets index a8c3099..3cd6547 100644 --- a/library/src/main/ets/components/imageknife/transform/BlurTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/BlurTransformation.ets @@ -26,13 +26,27 @@ import {Size} from '../../imageknife/RequestOption' export class BlurTransformation implements BaseTransform { private _mRadius: number; + private sampling: number - constructor(radius: number) { + constructor(radius: number,sampling?:number) { this._mRadius = radius; + if(sampling == undefined){ + this.sampling = 1 + } else { + this.sampling = sampling + } + } + + getClassName() { + return "BlurTransformation"; + } + + getConstructorParams() { + return JSON.stringify([this._mRadius,this.sampling]); } getName() { - return "BlurTransformation _mRadius:" + this._mRadius; + return "BlurTransformation _mRadius:" + this._mRadius +"==BlurTransformation sampling:"+ this.sampling; } transform(buf: ArrayBuffer, request: RequestOption, func?: AsyncTransform) { @@ -63,8 +77,8 @@ export class BlurTransformation implements BaseTransform { let options:image.DecodingOptions = { editable: true, desiredSize: { - width: targetWidth, - height: targetHeight + width: pixelMapWidth / this.sampling, + height: pixelMapHeight / this.sampling } } imageSource.createPixelMap(options) diff --git a/library/src/main/ets/components/imageknife/transform/BrightnessFilterTransformation.ets b/library/src/main/ets/components/imageknife/transform/BrightnessFilterTransformation.ets index a63feb8..cd9d884 100644 --- a/library/src/main/ets/components/imageknife/transform/BrightnessFilterTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/BrightnessFilterTransformation.ets @@ -33,6 +33,14 @@ export class BrightnessFilterTransformation implements BaseTransform { this._mBrightness = brightness; } + getClassName(){ + return "BrightnessFilterTransformation"; + } + + getConstructorParams(){ + return JSON.stringify([this._mBrightness]); + } + getName() { return "BrightnessFilterTransformation:" + this._mBrightness; } diff --git a/library/src/main/ets/components/imageknife/transform/ContrastFilterTransformation.ets b/library/src/main/ets/components/imageknife/transform/ContrastFilterTransformation.ets index 7aa272b..6454511 100644 --- a/library/src/main/ets/components/imageknife/transform/ContrastFilterTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/ContrastFilterTransformation.ets @@ -45,6 +45,15 @@ export class ContrastFilterTransformation implements BaseTransform { this._mContrast = contrast; } + getClassName(){ + return "ContrastFilterTransformation"; + } + + getConstructorParams(){ + return JSON.stringify([this._mContrast]); + } + + getName() { return "ContrastFilterTransformation:" + this._mContrast; } diff --git a/library/src/main/ets/components/imageknife/transform/CropCircleTransformation.ets b/library/src/main/ets/components/imageknife/transform/CropCircleTransformation.ets index a3aba0c..14a2d02 100644 --- a/library/src/main/ets/components/imageknife/transform/CropCircleTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/CropCircleTransformation.ets @@ -29,6 +29,14 @@ export class CropCircleTransformation implements BaseTransform { private mCenterY: number = 0; private mRadius: number = 0; + getClassName(){ + return "CropCircleTransformation"; + } + + getConstructorParams(){ + return "[]"; + } + getName() { return CropCircleTransformation.TAG + ";mCenterX:" + this.mCenterX + ";mCenterY:" + this.mCenterY diff --git a/library/src/main/ets/components/imageknife/transform/CropCircleWithBorderTransformation.ets b/library/src/main/ets/components/imageknife/transform/CropCircleWithBorderTransformation.ets index ea3cc38..582cb40 100644 --- a/library/src/main/ets/components/imageknife/transform/CropCircleWithBorderTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/CropCircleWithBorderTransformation.ets @@ -18,15 +18,17 @@ import { AsyncTransform } from "../transform/AsyncTransform" import { Constants } from "../constants/Constants" import { RequestOption } from "../../imageknife/RequestOption" import { TransformUtils } from "../transform/TransformUtils" -import {LogUtil} from '../../imageknife/utils/LogUtil' +import { LogUtil } from '../../imageknife/utils/LogUtil' import image from "@ohos.multimedia.image" import { BusinessError } from '@ohos.base' -import {Size} from '../../imageknife/RequestOption' -export interface rgbColor{ +import { Size } from '../../imageknife/RequestOption' + +export interface rgbColor { r_color: number, g_color: number, b_color: number, } + export class CropCircleWithBorderTransformation implements BaseTransform { private static TAG: string = "CropCircleTransformation"; private mBorderSize: number = 5; @@ -35,7 +37,7 @@ export class CropCircleWithBorderTransformation implements BaseTransform) { @@ -62,28 +76,30 @@ export class CropCircleWithBorderTransformation implements BaseTransform { - if (!size) { - func?.asyncTransform(error, null) - return; + let imageSource: image.ImageSource = image.createImageSource(buf); + TransformUtils.getPixelMapSize(imageSource, { + asyncTransform: (error: BusinessError | string, size: Size | null) => { + if (!size) { + func?.asyncTransform(error, null) + return; + } + let pixelMapWidth = size.width; + let pixelMapHeight = size.height; + let targetWidth = request.size.width; + let targetHeight = request.size.height; + if (pixelMapWidth < targetWidth) { + targetWidth = pixelMapWidth; + } + if (pixelMapHeight < targetHeight) { + targetHeight = pixelMapHeight; + } + this.updatePixelMapSize(imageSource, targetWidth, targetHeight, func); } - let pixelMapWidth = size.width; - let pixelMapHeight = size.height; - let targetWidth = request.size.width; - let targetHeight = request.size.height; - if (pixelMapWidth < targetWidth) { - targetWidth = pixelMapWidth; - } - if (pixelMapHeight < targetHeight) { - targetHeight = pixelMapHeight; - } - this.updatePixelMapSize(imageSource, targetWidth, targetHeight, func); - }}) + }) } private updatePixelMapSize(imageSource: image.ImageSource, outWith: number, outHeight: number, func?: AsyncTransform) { - let options:image.DecodingOptions = { + let options: image.DecodingOptions = { editable: true, desiredSize: { width: outWith, @@ -91,13 +107,13 @@ export class CropCircleWithBorderTransformation implements BaseTransform { + .then((pixelMap: PixelMap) => { this.transformPixelMap(pixelMap, outWith, outHeight, func); imageSource.release() }) - .catch((e:BusinessError) => { + .catch((e: BusinessError) => { LogUtil.log(Constants.PROJECT_TAG + ";CropCircleWithBorderTransformation e:" + e); - if (func!=undefined) { + if (func != undefined) { func?.asyncTransform(Constants.PROJECT_TAG + ";CropCircleWithBorderTransformation e:" + e, null); } imageSource.release() @@ -120,8 +136,8 @@ export class CropCircleWithBorderTransformation implements BaseTransform { private static TAG: string = "CropSquareTransformation"; + getClassName(){ + return "CropSquareTransformation"; + } + + getConstructorParams(){ + return '[]'; + } + getName() { return CropSquareTransformation.TAG; } @@ -60,8 +68,8 @@ export class CropSquareTransformation implements BaseTransform { editable: true, rotate: 0, desiredSize: { - width: outWidth, - height: outHeight + width: pw, + height: ph }, desiredRegion: { size: { width: targetSize, height: targetSize }, x: pw / 2 - targetSize / 2, diff --git a/library/src/main/ets/components/imageknife/transform/CropTransformation.ets b/library/src/main/ets/components/imageknife/transform/CropTransformation.ets index 416af67..9cf40b2 100644 --- a/library/src/main/ets/components/imageknife/transform/CropTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/CropTransformation.ets @@ -35,6 +35,14 @@ export class CropTransformation implements BaseTransform { this.mCropType = cropType; } + getClassName(){ + return "CropTransformation"; + } + + getConstructorParams(){ + return JSON.stringify([this.mWidth,this.mHeight ,this.mCropType]); + } + getName() { return CropTransformation.TAG + ";mWidth:" + this.mWidth + ";mHeight:" + this.mHeight diff --git a/library/src/main/ets/components/imageknife/transform/GrayscaleTransformation.ets b/library/src/main/ets/components/imageknife/transform/GrayscaleTransformation.ets index 9a07816..bc6771d 100644 --- a/library/src/main/ets/components/imageknife/transform/GrayscaleTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/GrayscaleTransformation.ets @@ -29,6 +29,14 @@ export class GrayscaleTransformation implements BaseTransform { return "GrayscaleTransformation"; } + getClassName(){ + return "GrayscaleTransformation"; + } + + getConstructorParams(){ + return '[]'; + } + async transform(buf: ArrayBuffer, request: RequestOption, func?: AsyncTransform) { if (!buf || buf.byteLength <= 0) { LogUtil.log(Constants.PROJECT_TAG + ";GrayscaleTransformation buf is empty"); diff --git a/library/src/main/ets/components/imageknife/transform/InvertFilterTransformation.ets b/library/src/main/ets/components/imageknife/transform/InvertFilterTransformation.ets index c8a3e8c..7373ae8 100644 --- a/library/src/main/ets/components/imageknife/transform/InvertFilterTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/InvertFilterTransformation.ets @@ -36,6 +36,14 @@ export class InvertFilterTransformation implements BaseTransform { return "InvertFilterTransformation"; } + getClassName(){ + return "InvertFilterTransformation"; + } + + getConstructorParams(){ + return '[]'; + } + async transform(buf: ArrayBuffer, request: RequestOption, func?: AsyncTransform) { if (!buf || buf.byteLength <= 0) { LogUtil.log(Constants.PROJECT_TAG + ";InvertFilterTransformation buf is empty"); diff --git a/library/src/main/ets/components/imageknife/transform/KuwaharaFilterTransform.ets b/library/src/main/ets/components/imageknife/transform/KuwaharaFilterTransform.ets index 2ecefa9..9b87c41 100644 --- a/library/src/main/ets/components/imageknife/transform/KuwaharaFilterTransform.ets +++ b/library/src/main/ets/components/imageknife/transform/KuwaharaFilterTransform.ets @@ -32,6 +32,14 @@ export class KuwaharaFilterTransform implements BaseTransform { this._mRadius = radius; } + getClassName(){ + return "KuwaharaFilterTransform"; + } + + getConstructorParams(){ + return JSON.stringify([this._mRadius]); + } + getName() { return "KuwaharaFilterTransform _mRadius:" + this._mRadius; } diff --git a/library/src/main/ets/components/imageknife/transform/MaskTransformation.ets b/library/src/main/ets/components/imageknife/transform/MaskTransformation.ets index ed7d7d0..8a73ce7 100644 --- a/library/src/main/ets/components/imageknife/transform/MaskTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/MaskTransformation.ets @@ -35,6 +35,14 @@ export class MaskTransformation implements BaseTransform { this._mResourceData = maskBitmap; } + getClassName(){ + return "MaskTransformation"; + } + + getConstructorParams(){ + return JSON.stringify([this._mResourceData]); + } + getName() { return "MaskTransformation:" + this._mResourceData; } diff --git a/library/src/main/ets/components/imageknife/transform/PixelationFilterTransformation.ets b/library/src/main/ets/components/imageknife/transform/PixelationFilterTransformation.ets index ff67eba..341b42a 100644 --- a/library/src/main/ets/components/imageknife/transform/PixelationFilterTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/PixelationFilterTransformation.ets @@ -37,6 +37,14 @@ export class PixelationFilterTransformation implements BaseTransform { } } + getClassName(){ + return "PixelationFilterTransformation"; + } + + getConstructorParams(){ + return JSON.stringify([this._mPixel]); + } + getName() { return "PixelationFilterTransformation" + this._mPixel; } diff --git a/library/src/main/ets/components/imageknife/transform/RotateImageTransformation.ets b/library/src/main/ets/components/imageknife/transform/RotateImageTransformation.ets index 6286720..3cc9656 100644 --- a/library/src/main/ets/components/imageknife/transform/RotateImageTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/RotateImageTransformation.ets @@ -34,6 +34,14 @@ export class RotateImageTransformation implements BaseTransform { return "RotateImageTransformation" + ";degreesToRotate:" + this.mDegreesToRotate; } + getClassName(){ + return "RotateImageTransformation"; + } + + getConstructorParams(){ + return JSON.stringify([this.mDegreesToRotate]); + } + transform(buf: ArrayBuffer, request: RequestOption, func?: AsyncTransform) { if (!buf || buf.byteLength <= 0) { LogUtil.log(Constants.PROJECT_TAG + ";RotateImageTransformation buf is empty"); diff --git a/library/src/main/ets/components/imageknife/transform/RoundedCornersTransformation.ets b/library/src/main/ets/components/imageknife/transform/RoundedCornersTransformation.ets index 74bd119..2fdebef 100644 --- a/library/src/main/ets/components/imageknife/transform/RoundedCornersTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/RoundedCornersTransformation.ets @@ -46,6 +46,19 @@ export class RoundedCornersTransformation implements BaseTransform { this.mBottom_right = value.bottom_right; } + getClassName(){ + return "RoundedCornersTransformation"; + } + + getConstructorParams(){ + return JSON.stringify([{ + top_left: this.mTop_left, + top_right: this.mTop_right, + bottom_left: this.mBottom_left, + bottom_right: this.mBottom_right + }]); + } + getName() { return "RoundedCornersTransformation" + ";mTop_left:" + this.mTop_left + ";mTop_right:" + this.mTop_right @@ -79,7 +92,7 @@ export class RoundedCornersTransformation implements BaseTransform { if (pixelMapHeight < targetHeight) { targetHeight = pixelMapHeight; } - this.transformPixelMap(imageSource, targetWidth, targetHeight, func); + this.transformPixelMap(imageSource, pixelMapWidth, pixelMapHeight, func); }}) } diff --git a/library/src/main/ets/components/imageknife/transform/SepiaFilterTransformation.ets b/library/src/main/ets/components/imageknife/transform/SepiaFilterTransformation.ets index 87380d0..ab4e99f 100644 --- a/library/src/main/ets/components/imageknife/transform/SepiaFilterTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/SepiaFilterTransformation.ets @@ -33,6 +33,14 @@ export class SepiaFilterTransformation implements BaseTransform { return "SepiaFilterTransformation"; } + getClassName(){ + return "SepiaFilterTransformation"; + } + + getConstructorParams(){ + return '[]'; + } + async transform(buf: ArrayBuffer, request: RequestOption, func?: AsyncTransform) { if (!buf || buf.byteLength <= 0) { LogUtil.log(Constants.PROJECT_TAG + ";SepiaFilterTransformation buf is empty"); diff --git a/library/src/main/ets/components/imageknife/transform/SketchFilterTransformation.ets b/library/src/main/ets/components/imageknife/transform/SketchFilterTransformation.ets index 25d6f6c..1092912 100644 --- a/library/src/main/ets/components/imageknife/transform/SketchFilterTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/SketchFilterTransformation.ets @@ -24,6 +24,15 @@ import { BusinessError } from '@ohos.base' import {Size} from '../../imageknife/RequestOption' export class SketchFilterTransformation implements BaseTransform { + + getClassName(){ + return "SketchFilterTransformation"; + } + + getConstructorParams(){ + return '[]'; + } + getName() { return 'SketchFilterTransformation'; } diff --git a/library/src/main/ets/components/imageknife/transform/SwirlFilterTransformation.ets b/library/src/main/ets/components/imageknife/transform/SwirlFilterTransformation.ets index 5681f1e..76c5a14 100644 --- a/library/src/main/ets/components/imageknife/transform/SwirlFilterTransformation.ets +++ b/library/src/main/ets/components/imageknife/transform/SwirlFilterTransformation.ets @@ -43,6 +43,14 @@ export class SwirlFilterTransformation implements BaseTransform { } } + getClassName(){ + return "SwirlFilterTransformation"; + } + + getConstructorParams(){ + return JSON.stringify([this.radius,this._angle,[this._xCenter,this._yCenter]]); + } + getName() { return 'SwirlFilterTransformation' + this.radius; } diff --git a/library/src/main/ets/components/imageknife/transform/ToonFilterTransform.ets b/library/src/main/ets/components/imageknife/transform/ToonFilterTransform.ets index cb75d92..3b5dcde 100644 --- a/library/src/main/ets/components/imageknife/transform/ToonFilterTransform.ets +++ b/library/src/main/ets/components/imageknife/transform/ToonFilterTransform.ets @@ -37,6 +37,14 @@ export class ToonFilterTransform implements BaseTransform { } } + getClassName(){ + return "ToonFilterTransform"; + } + + getConstructorParams(){ + return JSON.stringify([this.threshold,this.quantizationLevels]); + } + getName() { return "ToonFilterTransform threshold:" + this.threshold + ";quantizationLevels:" + this.quantizationLevels; } diff --git a/library/src/main/ets/components/imageknife/transform/TransformUtils.ets b/library/src/main/ets/components/imageknife/transform/TransformUtils.ets index 2b0366b..43f6283 100644 --- a/library/src/main/ets/components/imageknife/transform/TransformUtils.ets +++ b/library/src/main/ets/components/imageknife/transform/TransformUtils.ets @@ -17,6 +17,30 @@ import image from '@ohos.multimedia.image' import { Size } from '../../imageknife/RequestOption' import { BusinessError } from '@ohos.base' import { Constants } from '../constants/Constants' +import { BaseTransform } from './BaseTransform' +import { BlurTransformation } from './BlurTransformation' +import { BrightnessFilterTransformation } from './BrightnessFilterTransformation' +import { RotateImageTransformation } from './RotateImageTransformation' +import { GrayscaleTransformation } from './GrayscaleTransformation' +import { ContrastFilterTransformation } from './ContrastFilterTransformation' +import { CropCircleTransformation } from './CropCircleTransformation' +import { CropCircleWithBorderTransformation, rgbColor } from './CropCircleWithBorderTransformation' +import { CropSquareTransformation } from './CropSquareTransformation' +import { InvertFilterTransformation } from './InvertFilterTransformation' +import { KuwaharaFilterTransform } from './KuwaharaFilterTransform' +import { MaskTransformation } from './MaskTransformation' +import { PixelationFilterTransformation } from './PixelationFilterTransformation' +import { RoundCorner, RoundedCornersTransformation } from './RoundedCornersTransformation' +import { SketchFilterTransformation } from './SketchFilterTransformation' +import { SepiaFilterTransformation } from './SepiaFilterTransformation' +import { SwirlFilterTransformation } from './SwirlFilterTransformation' +import { ToonFilterTransform } from './ToonFilterTransform' +import { VignetteFilterTransform } from './VignetteFilterTransform' +import { CenterCrop } from './pixelmap/CenterCrop' +import { CenterInside } from './pixelmap/CenterInside' +import { FitCenter } from './pixelmap/FitCenter' +import { CropTransformation, CropType } from './CropTransformation' +import { collections } from '@kit.ArkTS' export class TransformUtils { static centerCrop(buf: ArrayBuffer, outWidth: number, outHeihgt: number, @@ -155,4 +179,100 @@ export class TransformUtils { func?.asyncTransform('', { width: pWidth, height: pHeight }); }) } + + static addTransformations (data: collections.Array>) { + let transformations: Array> = new Array(); + data.forEach((value:collections.Array ) => { + let className = value.shift(); + let params = value.shift() as string; + switch (className) { + case "BlurTransformation": + let paramList: number [] = JSON.parse(params); + transformations.push(new BlurTransformation(paramList[0], paramList[1])); + break; + case "BrightnessFilterTransformation": + let paramList1: number [] = JSON.parse(params); + transformations.push(new BrightnessFilterTransformation(paramList1[0])); + break; + case "RotateImageTransformation": + let paramList2: number [] = JSON.parse(params); + transformations.push(new RotateImageTransformation(paramList2[0])); + break; + case "GrayscaleTransformation": + transformations.push(new GrayscaleTransformation()); + break; + case "BlurTransformation": + let paramList3: number [] = JSON.parse(params); + transformations.push(new BlurTransformation(paramList3[0])); + break; + case "ContrastFilterTransformation": + let paramList4: number [] = JSON.parse(params); + transformations.push(new ContrastFilterTransformation(paramList4[0])); + break; + case "CropCircleTransformation": + transformations.push(new CropCircleTransformation()); + break; + case "CropCircleWithBorderTransformation": + let paramList5: (number | rgbColor) [] = JSON.parse(params); + transformations.push(new CropCircleWithBorderTransformation(paramList5[0] as number, paramList5[1] as rgbColor)); + break; + case "CropSquareTransformation": + transformations.push(new CropSquareTransformation()); + break; + case "InvertFilterTransformation": + transformations.push(new InvertFilterTransformation()); + break; + case "KuwaharaFilterTransform": + let paramList7: number [] = JSON.parse(params); + transformations.push(new KuwaharaFilterTransform(paramList7[0] as number)); + break; + case "MaskTransformation": + let paramList8: Resource [] = JSON.parse(params); + transformations.push(new MaskTransformation(paramList8[0] as Resource)); + break; + case "PixelationFilterTransformation": + let paramList9: number [] = JSON.parse(params); + transformations.push(new PixelationFilterTransformation(paramList9[0] as number)); + break; + case "RoundedCornersTransformation": + let paramList10: RoundCorner [] = JSON.parse(params); + transformations.push(new RoundedCornersTransformation(paramList10[0] as RoundCorner)); + break; + case "SepiaFilterTransformation": + transformations.push(new SepiaFilterTransformation()); + break; + case "SketchFilterTransformation": + transformations.push(new SketchFilterTransformation()); + break; + case "SwirlFilterTransformation": + let paramList11: (number | Array) [] = JSON.parse(params); + transformations.push(new SwirlFilterTransformation(paramList11[0] as number, paramList11[1] as number, paramList11[2] as Array)); + break; + case "ToonFilterTransform": + let paramList14: number [] = JSON.parse(params); + transformations.push(new ToonFilterTransform(paramList14[0], paramList14[1])); + break; + case "VignetteFilterTransform": + let paramList12: Array [] = JSON.parse(params); + transformations.push(new VignetteFilterTransform(paramList12[0], paramList12[1], paramList12[2])); + break; + case "CenterCrop": + transformations.push(new CenterCrop()); + break; + case "CenterInside": + transformations.push(new CenterInside()); + break; + case "FitCenter": + transformations.push(new FitCenter()); + break; + case "CropTransformation": + let paramList13: (number | CropType) [] = JSON.parse(params); + transformations.push(new CropTransformation(paramList13[0] as number, paramList13[1] as number, paramList13[2] as CropType)); + break; + default: + break + } + }); + return transformations; + } } \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/transform/VignetteFilterTransform.ets b/library/src/main/ets/components/imageknife/transform/VignetteFilterTransform.ets index d1937ce..c612c4d 100644 --- a/library/src/main/ets/components/imageknife/transform/VignetteFilterTransform.ets +++ b/library/src/main/ets/components/imageknife/transform/VignetteFilterTransform.ets @@ -42,6 +42,14 @@ export class VignetteFilterTransform implements BaseTransform { } } + getClassName(){ + return "VignetteFilterTransform"; + } + + getConstructorParams(){ + return JSON.stringify([this.centerPoint,this.vignetteColor,this.vignetteSpace]); + } + getName() { return "VignetteFilterTransform centerPoint:" + this.centerPoint + ";vignetteColor:" + this.vignetteColor + ";vignetteSpace:" + this.vignetteSpace; } diff --git a/library/src/main/ets/components/imageknife/transform/pixelmap/CenterCrop.ets b/library/src/main/ets/components/imageknife/transform/pixelmap/CenterCrop.ets index 01b8b5d..0c23002 100644 --- a/library/src/main/ets/components/imageknife/transform/pixelmap/CenterCrop.ets +++ b/library/src/main/ets/components/imageknife/transform/pixelmap/CenterCrop.ets @@ -20,6 +20,15 @@ import {TransformUtils} from '../TransformUtils' import {RequestOption} from '../../../imageknife/RequestOption' import { BusinessError } from '@ohos.base' export class CenterCrop implements BaseTransform { + + getClassName(){ + return "CenterCrop"; + } + + getConstructorParams(){ + return '[]'; + } + getName() { return 'CenterCrop:' + this; } diff --git a/library/src/main/ets/components/imageknife/transform/pixelmap/CenterInside.ets b/library/src/main/ets/components/imageknife/transform/pixelmap/CenterInside.ets index 94cb818..68264ae 100644 --- a/library/src/main/ets/components/imageknife/transform/pixelmap/CenterInside.ets +++ b/library/src/main/ets/components/imageknife/transform/pixelmap/CenterInside.ets @@ -23,6 +23,13 @@ export class CenterInside implements BaseTransform { getName() { return 'CenterInside:' + this; } + getClassName(){ + return "CenterInside"; + } + + getConstructorParams(){ + return '[]'; + } transform(buf: ArrayBuffer, request: RequestOption, func?: AsyncTransform) { if (!buf || buf.byteLength <= 0) { diff --git a/library/src/main/ets/components/imageknife/transform/pixelmap/FitCenter.ets b/library/src/main/ets/components/imageknife/transform/pixelmap/FitCenter.ets index 324d164..907d828 100644 --- a/library/src/main/ets/components/imageknife/transform/pixelmap/FitCenter.ets +++ b/library/src/main/ets/components/imageknife/transform/pixelmap/FitCenter.ets @@ -23,6 +23,13 @@ export class FitCenter implements BaseTransform { getName() { return 'FitCenter:' + this; } + getClassName(){ + return "FitCenter"; + } + + getConstructorParams(){ + return '[]'; + } transform(buf: ArrayBuffer, request: RequestOption, func?: AsyncTransform) { if (!buf || buf.byteLength <= 0) { diff --git a/library/src/main/ets/components/imageknife/utils/DefaultJobQueue.ets b/library/src/main/ets/components/imageknife/utils/DefaultJobQueue.ets new file mode 100644 index 0000000..0009872 --- /dev/null +++ b/library/src/main/ets/components/imageknife/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 { IJobQueue } from './IJobQueue'; +import Queue from '@ohos.util.Queue'; +import taskpool from '@ohos.taskpool'; +import { RequestOption } from '../RequestOption'; + +export class DefaultJobQueue implements IJobQueue { + highQueue: Queue = new Queue(); + normalQueue: Queue = new Queue(); + lowQueue: Queue = new Queue(); + + getQueueLength(): number { + return this.highQueue.length + this.normalQueue.length + this.lowQueue.length; + } + + add(request: RequestOption): void { + if (request.priority === undefined || request.priority === taskpool.Priority.MEDIUM) { + this.normalQueue.add(request); + } else if (request.priority === taskpool.Priority.HIGH) { + this.highQueue.add(request); + } else { + this.lowQueue.add(request); + } + } + + pop(): RequestOption | 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/components/imageknife/utils/FileTypeUtil.ets b/library/src/main/ets/components/imageknife/utils/FileTypeUtil.ets index 3bb2cfb..78e1cd7 100644 --- a/library/src/main/ets/components/imageknife/utils/FileTypeUtil.ets +++ b/library/src/main/ets/components/imageknife/utils/FileTypeUtil.ets @@ -20,12 +20,13 @@ export class FileTypeUtil { // 添加文件类型和对应的文件头部特征 '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])], + '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])], }; @@ -41,7 +42,8 @@ export class FileTypeUtil { value == SupportFormat.webp || value == SupportFormat.bmp || value == SupportFormat.gif || - value == SupportFormat.svg + value == SupportFormat.svg || + value == SupportFormat.heic ) { return true; } @@ -106,5 +108,6 @@ export enum SupportFormat { bmp = 'bmp', gif = 'gif', svg = 'svg', - tiff = 'tiff' + tiff = 'tiff', + heic = 'heic' } diff --git a/library/src/main/ets/components/imageknife/utils/IJobQueue.ets b/library/src/main/ets/components/imageknife/utils/IJobQueue.ets new file mode 100644 index 0000000..e847744 --- /dev/null +++ b/library/src/main/ets/components/imageknife/utils/IJobQueue.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. + */ +import { RequestOption } from '../../imageknife/RequestOption'; + +export interface IJobQueue { + + /** + * 获取队列长度 + * @returns + */ + getQueueLength(): number; + + /** + * 往队列中添加元素 + * @param RequestObject + */ + add(request: RequestOption): void; + + /** + * 移除并返回队列头部的元素 + * @returns + */ + pop(): RequestOption | undefined; +} diff --git a/library/src/main/ets/components/imageknife/utils/MResource.ets b/library/src/main/ets/components/imageknife/utils/MResource.ets new file mode 100644 index 0000000..0826d53 --- /dev/null +++ b/library/src/main/ets/components/imageknife/utils/MResource.ets @@ -0,0 +1,22 @@ +/* + * 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. + */ + +export class MResource { + bundleName?: string; + moduleName?: string; + id?: number; + params?: []; + type?: number; +} diff --git a/library/src/main/ets/components/imageknife/utils/ParseImageUtil.ets b/library/src/main/ets/components/imageknife/utils/ParseImageUtil.ets index 6362253..fd3cb98 100644 --- a/library/src/main/ets/components/imageknife/utils/ParseImageUtil.ets +++ b/library/src/main/ets/components/imageknife/utils/ParseImageUtil.ets @@ -24,69 +24,32 @@ export class ParseImageUtil implements IParseImage { this.parseImageThumbnail(1, imageinfo, onCompleteFunction, onErrorFunction) } - // scale(0,1) parseImageThumbnail(scale: number, imageinfo: ArrayBuffer, onCompleteFunction: (value: PixelMap) => void | PromiseLike, onErrorFunction: (reason?: BusinessError | string) => void) { - // let imageSource: image.ImageSource = image.createImageSource(imageinfo); // 步骤一:文件转为pixelMap 然后变换 给Image组件 - // imageSource.getImageInfo().then((value) => { - // let hValue = Math.round(value.size.height * scale); - // let wValue = Math.round(value.size.width * scale); - // let defaultSize: image.Size = { - // height: hValue, - // width: wValue - // }; - // let opts: image.DecodingOptions = { - // editable: true, - // desiredSize: defaultSize - // }; - // - // imageSource.createPixelMap(opts).then((pixelMap: image.PixelMap) => { - // onCompleteFunction(pixelMap); - // imageSource.release() - // }).catch((err: string) => { - // onErrorFunction(err); - // imageSource.release() - // }) - // - // }).catch((err: string) => { - // onErrorFunction(err); - // imageSource.release() - // }) + let imageSource: image.ImageSource = image.createImageSource(imageinfo); // 步骤一:文件转为pixelMap 然后变换 给Image组件 + imageSource.getImageInfo().then((value) => { + let hValue = Math.round(value.size.height * scale); + let wValue = Math.round(value.size.width * scale); + let defaultSize: image.Size = { + height: hValue, + width: wValue + }; + let opts: image.DecodingOptions = { + editable: true, + desiredSize: defaultSize + }; - taskPoolExecutePixelMap(imageinfo,scale,onCompleteFunction,onErrorFunction); //多线程接口 + imageSource.createPixelMap(opts).then((pixelMap: image.PixelMap) => { + onCompleteFunction(pixelMap); + imageSource.release() + }).catch((err: string) => { + onErrorFunction(err); + imageSource.release() + }) + + }).catch((err: string) => { + onErrorFunction(err); + imageSource.release() + }) } -} - - -@Concurrent -async function taskParseImage(arrayBuffer: ArrayBuffer, scale: number): Promise { - let imageSource: image.ImageSource = image.createImageSource(arrayBuffer); // 步骤一:文件转为pixelMap 然后变换 给Image组件 - let value = await imageSource.getImageInfo(); - let hValue = Math.round(value.size.height * scale); - let wValue = Math.round(value.size.width * scale); - let defaultSize: image.Size = { - height: hValue, - width: wValue - }; - let opts: image.DecodingOptions = { - editable: true, - desiredSize: defaultSize - }; - let pixelMap = await imageSource.createPixelMap(opts) - LogUtil.log("ceshi321 : Succeeded in creating pixelmap taskpool " + pixelMap.getPixelBytesNumber()) - imageSource.release() - return pixelMap; -} - -function taskPoolExecutePixelMap(arrayBuffer: ArrayBuffer, scale: number, onCompleteFunction: (value: PixelMap) => void | PromiseLike, onErrorFunction: (reason?: BusinessError | string) => void) { - LogUtil.log("ceshi321 : arrayBuffer长度" + arrayBuffer.byteLength) - let task = new taskpool.Task(taskParseImage, arrayBuffer, scale) - task.setTransferList([]) - taskpool.execute(task).then((pixelmap: Object) => { - LogUtil.log('ceshi321 : Succeeded in creating pixelmap Ui .' + (pixelmap as image.PixelMap).getPixelBytesNumber()) - onCompleteFunction(pixelmap as image.PixelMap); - }).catch((err: string) => { - LogUtil.log("ceshi321 : test occur error: " + err) - onErrorFunction(err); - }); } \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/utils/gif/GIFParseImpl.ets b/library/src/main/ets/components/imageknife/utils/gif/GIFParseImpl.ets index 772aadd..abe1a37 100644 --- a/library/src/main/ets/components/imageknife/utils/gif/GIFParseImpl.ets +++ b/library/src/main/ets/components/imageknife/utils/gif/GIFParseImpl.ets @@ -36,88 +36,44 @@ export interface gifBackData { export class GIFParseImpl implements IParseGif { parseGifs(imageinfo: ArrayBuffer, callback: (data?: GIFFrame[], err?: BusinessError | string) => void) { // 硬解码流程 - // let imageSource = image.createImageSource(imageinfo); - // let decodeOpts: image.DecodingOptions = { - // sampleSize: 1, - // editable: true, - // rotate: 0 - // } - // let data:GIFFrame[] = []; - // imageSource.createPixelMapList(decodeOpts).then((pixelList: Array) => { - // //sdk的api接口发生变更:从.getDelayTime() 变为.getDelayTimeList() - // imageSource.getDelayTimeList().then(delayTimes => { - // if (pixelList.length > 0) { - // let pixelmap1 = pixelList[0]; - // pixelmap1.getImageInfo().then(imageInfo => { - // for (let i = 0; i < pixelList.length; i++) { - // let frame = new GIFFrame(); - // frame.drawPixelMap = pixelList[i]; - // frame.dims = { width: imageInfo.size.width, height: imageInfo.size.height, top: 0, left: 0 } - // if (i < delayTimes.length) { - // frame.delay = delayTimes[i]; - // } else { - // frame.delay = delayTimes[delayTimes.length - 1] - // } - // data.push(frame) - // } - // callback(data,undefined) - // imageSource.release(); - // }).catch((err: string) => { - // imageSource.release(); - // callback(undefined,err) - // }) - // } - // }).catch((err: string) => { - // imageSource.release(); - // callback(undefined,err) - // }) - // }).catch((err: string) => { - // imageSource.release(); - // callback(undefined,err) - // }) - taskPoolExecutePixelMapList(imageinfo,callback); //多线程接口 - - } -} - -@Concurrent -async function taskParseGif(arrayBuffer: ArrayBuffer): Promise { - let imageSource = image.createImageSource(arrayBuffer); - let data: GIFFrame[] = []; - let decodeOpts: image.DecodingOptions = { - sampleSize: 1, - editable: true, - rotate: 0 - } - let pixelList = await imageSource.createPixelMapList(decodeOpts); - if (pixelList.length > 0) { - let pixelmap = pixelList[0]; - let imageInfo = await pixelmap.getImageInfo(); - let delayTimes = await imageSource.getDelayTimeList(); - for (let i = 0; i < pixelList.length; i++) { - let frame = new GIFFrame(); - frame.drawPixelMap = pixelList[i]; - frame.dims = { width: imageInfo.size.width, height: imageInfo.size.height, top: 0, left: 0 } - if (i < delayTimes.length) { - frame.delay = delayTimes[i]; - } else { - frame.delay = delayTimes[delayTimes.length - 1] - } - data.push(frame) + let imageSource = image.createImageSource(imageinfo); + let decodeOpts: image.DecodingOptions = { + sampleSize: 1, + editable: true, + rotate: 0 } + let data:GIFFrame[] = []; + imageSource.createPixelMapList(decodeOpts).then((pixelList: Array) => { + //sdk的api接口发生变更:从.getDelayTime() 变为.getDelayTimeList() + imageSource.getDelayTimeList().then(delayTimes => { + if (pixelList.length > 0) { + let pixelmap1 = pixelList[0]; + pixelmap1.getImageInfo().then(imageInfo => { + for (let i = 0; i < pixelList.length; i++) { + let frame = new GIFFrame(); + frame.drawPixelMap = pixelList[i]; + frame.dims = { width: imageInfo.size.width, height: imageInfo.size.height, top: 0, left: 0 } + if (i < delayTimes.length) { + frame.delay = delayTimes[i]; + } else { + frame.delay = delayTimes[delayTimes.length - 1] + } + data.push(frame) + } + callback(data,undefined) + imageSource.release(); + }).catch((err: string) => { + imageSource.release(); + callback(undefined,err) + }) + } + }).catch((err: string) => { + imageSource.release(); + callback(undefined,err) + }) + }).catch((err: string) => { + imageSource.release(); + callback(undefined,err) + }) } - return data; -} - -function taskPoolExecutePixelMapList(arrayBuffer: ArrayBuffer, callback: (data?: GIFFrame[], err?: BusinessError | string) => void) { - LogUtil.log("ceshi321 : arrayBuffer长度" + arrayBuffer.byteLength) - let task = new taskpool.Task(taskParseGif, arrayBuffer) - task.setTransferList([]) - taskpool.execute(task).then((imageFrames: Object) => { - // LogUtil.log('ceshi321 : Succeeded in creating pixelmap Ui .' + imageFrames.getPixelBytesNumber()) - callback(imageFrames as GIFFrame[],undefined) - }).catch((err: string) => { - LogUtil.log("ceshi321 : test occur error: " + err) - callback(undefined,err); - }); } \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/utils/svg/IParseSvg.ets b/library/src/main/ets/components/imageknife/utils/svg/IParseSvg.ets index 2863f30..bf7c72e 100644 --- a/library/src/main/ets/components/imageknife/utils/svg/IParseSvg.ets +++ b/library/src/main/ets/components/imageknife/utils/svg/IParseSvg.ets @@ -19,6 +19,6 @@ import { ImageKnifeData } from '../../ImageKnifeData' export interface IParseSvg { // 解析svg parseSvg:(option: RequestOption, imageInfo: ArrayBuffer | ArrayBufferLike, - onComplete: (value: ImageKnifeData) => void | PromiseLike, + onComplete: (value: PixelMap) => void | PromiseLike, onErrorFunction: (reason?: BusinessError | string) => void)=> void } \ No newline at end of file diff --git a/library/src/main/ets/components/imageknife/utils/svg/SVGParseImpl.ets b/library/src/main/ets/components/imageknife/utils/svg/SVGParseImpl.ets index ceb80d8..557214a 100644 --- a/library/src/main/ets/components/imageknife/utils/svg/SVGParseImpl.ets +++ b/library/src/main/ets/components/imageknife/utils/svg/SVGParseImpl.ets @@ -20,7 +20,7 @@ import image from '@ohos.multimedia.image' export class SVGParseImpl implements IParseSvg { parseSvg(option: RequestOption, imageInfo: ArrayBuffer, - onComplete: (value: ImageKnifeData) => void | PromiseLike, + onComplete: (value: PixelMap) => void | PromiseLike, onErrorFunction: (reason?: BusinessError | string) => void) { let imageSource: image.ImageSource = image.createImageSource(imageInfo); // 步骤一:文件转为pixelMap 然后变换 给Image组件 @@ -36,7 +36,7 @@ export class SVGParseImpl implements IParseSvg { }; imageSource.createPixelMap(opts).then((pixelMap: image.PixelMap) => { let imageKnifeData = ImageKnifeData.createImagePixelMap(ImageKnifeType.PIXELMAP, pixelMap) - onComplete(imageKnifeData); + onComplete(imageKnifeData?.drawPixelMap?.imagePixelMap as PixelMap); imageSource.release() }).catch((err: string) => { onErrorFunction(err); diff --git a/oh-package.json5 b/oh-package.json5 index b7dbed9..9e9498e 100644 --- a/oh-package.json5 +++ b/oh-package.json5 @@ -6,6 +6,6 @@ "name": "imageknife", "description": "example description", "repository": {}, - "version": "2.1.2-rc.6", + "version": "2.1.2-rc.12", "dependencies": {} } diff --git a/sharedlibrary/src/main/ets/Index.ets b/sharedlibrary/src/main/ets/Index.ets index 6e2450c..30f935f 100644 --- a/sharedlibrary/src/main/ets/Index.ets +++ b/sharedlibrary/src/main/ets/Index.ets @@ -31,6 +31,7 @@ export { NONE } from '@ohos/imageknife' export { RESOURCE } from '@ohos/imageknife' export { EngineKeyInterface } from '@ohos/imageknife' export { EngineKeyFactories } from '@ohos/imageknife' +export { DataFetchResult } from '@ohos/imageknife' /** * compress @@ -97,11 +98,11 @@ export { UPNG } from '@ohos/imageknife' */ export { ImageKnife } from '@ohos/imageknife' export { ImageKnifeGlobal } from '@ohos/imageknife' -export {RequestOption,Size} from '@ohos/imageknife' +export {RequestOption,Size,CacheType} from '@ohos/imageknife' export {ObjectKey} from '@ohos/imageknife' -export { ImageKnifeComponent, ScaleType, ScaleTypeHelper, AntiAliasing} from '@ohos/imageknife' +export { ImageKnifeComponent, ScaleType, ScaleTypeHelper, AntiAliasing, Priority} from '@ohos/imageknife' export { ImageKnifeDrawFactory } from '@ohos/imageknife' -export {ImageKnifeOption,CropCircleWithBorder,Crop,GifOptions,TransformOptions} from '@ohos/imageknife' +export {ImageKnifeOption,CropCircleWithBorder,Crop,GifOptions,TransformOptions,HeaderOptions} from '@ohos/imageknife' export { ImageKnifeData } from '@ohos/imageknife' export {IAllCacheInfoCallback,AllCacheInfo,ResourceCacheInfo,MemoryCacheInfo,DataCacheInfo} from '@ohos/imageknife' export {IParseImage} from '@ohos/imageknife' @@ -109,6 +110,8 @@ export {IDataFetch} from '@ohos/imageknife' export {ICache} from '@ohos/imageknife' export { FileTypeUtil } from '@ohos/imageknife' export { ParseImageUtil } from '@ohos/imageknife' +export { DownloadClient } from '@ohos/imageknife' +export { CustomDataFetchClient } from '@ohos/imageknife' /** * svg parse